summaryrefslogtreecommitdiffstats
path: root/src/widgets/graphicsview
diff options
context:
space:
mode:
Diffstat (limited to 'src/widgets/graphicsview')
-rw-r--r--src/widgets/graphicsview/graphicsview.pri52
-rw-r--r--src/widgets/graphicsview/qgraph_p.h286
-rw-r--r--src/widgets/graphicsview/qgraphicsanchorlayout.cpp533
-rw-r--r--src/widgets/graphicsview/qgraphicsanchorlayout.h128
-rw-r--r--src/widgets/graphicsview/qgraphicsanchorlayout_p.cpp3015
-rw-r--r--src/widgets/graphicsview/qgraphicsanchorlayout_p.h594
-rw-r--r--src/widgets/graphicsview/qgraphicsgridlayout.cpp690
-rw-r--r--src/widgets/graphicsview/qgraphicsgridlayout.h144
-rw-r--r--src/widgets/graphicsview/qgraphicsitem.cpp11597
-rw-r--r--src/widgets/graphicsview/qgraphicsitem.h1172
-rw-r--r--src/widgets/graphicsview/qgraphicsitem_p.h891
-rw-r--r--src/widgets/graphicsview/qgraphicsitemanimation.cpp599
-rw-r--r--src/widgets/graphicsview/qgraphicsitemanimation.h120
-rw-r--r--src/widgets/graphicsview/qgraphicslayout.cpp451
-rw-r--r--src/widgets/graphicsview/qgraphicslayout.h98
-rw-r--r--src/widgets/graphicsview/qgraphicslayout_p.cpp198
-rw-r--r--src/widgets/graphicsview/qgraphicslayout_p.h154
-rw-r--r--src/widgets/graphicsview/qgraphicslayoutitem.cpp935
-rw-r--r--src/widgets/graphicsview/qgraphicslayoutitem.h155
-rw-r--r--src/widgets/graphicsview/qgraphicslayoutitem_p.h103
-rw-r--r--src/widgets/graphicsview/qgraphicslinearlayout.cpp568
-rw-r--r--src/widgets/graphicsview/qgraphicslinearlayout.h119
-rw-r--r--src/widgets/graphicsview/qgraphicsproxywidget.cpp1570
-rw-r--r--src/widgets/graphicsview/qgraphicsproxywidget.h147
-rw-r--r--src/widgets/graphicsview/qgraphicsproxywidget_p.h130
-rw-r--r--src/widgets/graphicsview/qgraphicsscene.cpp6491
-rw-r--r--src/widgets/graphicsview/qgraphicsscene.h329
-rw-r--r--src/widgets/graphicsview/qgraphicsscene_bsp.cpp296
-rw-r--r--src/widgets/graphicsview/qgraphicsscene_bsp_p.h132
-rw-r--r--src/widgets/graphicsview/qgraphicsscene_p.h359
-rw-r--r--src/widgets/graphicsview/qgraphicsscenebsptreeindex.cpp719
-rw-r--r--src/widgets/graphicsview/qgraphicsscenebsptreeindex_p.h206
-rw-r--r--src/widgets/graphicsview/qgraphicssceneevent.cpp1674
-rw-r--r--src/widgets/graphicsview/qgraphicssceneevent.h326
-rw-r--r--src/widgets/graphicsview/qgraphicssceneindex.cpp648
-rw-r--r--src/widgets/graphicsview/qgraphicssceneindex_p.h182
-rw-r--r--src/widgets/graphicsview/qgraphicsscenelinearindex.cpp95
-rw-r--r--src/widgets/graphicsview/qgraphicsscenelinearindex_p.h109
-rw-r--r--src/widgets/graphicsview/qgraphicstransform.cpp594
-rw-r--r--src/widgets/graphicsview/qgraphicstransform.h159
-rw-r--r--src/widgets/graphicsview/qgraphicstransform_p.h78
-rw-r--r--src/widgets/graphicsview/qgraphicsview.cpp3880
-rw-r--r--src/widgets/graphicsview/qgraphicsview.h316
-rw-r--r--src/widgets/graphicsview/qgraphicsview_p.h233
-rw-r--r--src/widgets/graphicsview/qgraphicswidget.cpp2388
-rw-r--r--src/widgets/graphicsview/qgraphicswidget.h257
-rw-r--r--src/widgets/graphicsview/qgraphicswidget_p.cpp910
-rw-r--r--src/widgets/graphicsview/qgraphicswidget_p.h226
-rw-r--r--src/widgets/graphicsview/qgridlayoutengine.cpp1742
-rw-r--r--src/widgets/graphicsview/qgridlayoutengine_p.h458
-rw-r--r--src/widgets/graphicsview/qsimplex_p.cpp673
-rw-r--r--src/widgets/graphicsview/qsimplex_p.h208
52 files changed, 48137 insertions, 0 deletions
diff --git a/src/widgets/graphicsview/graphicsview.pri b/src/widgets/graphicsview/graphicsview.pri
new file mode 100644
index 0000000000..547d7ce7ae
--- /dev/null
+++ b/src/widgets/graphicsview/graphicsview.pri
@@ -0,0 +1,52 @@
+# Qt graphicsview module
+HEADERS += graphicsview/qgraphicsgridlayout.h \
+ graphicsview/qgraphicsitem.h \
+ graphicsview/qgraphicsitem_p.h \
+ graphicsview/qgraphicsitemanimation.h \
+ graphicsview/qgraphicslayout.h \
+ graphicsview/qgraphicslayout_p.h \
+ graphicsview/qgraphicslayoutitem.h \
+ graphicsview/qgraphicslayoutitem_p.h \
+ graphicsview/qgraphicslinearlayout.h \
+ graphicsview/qgraphicsproxywidget.h \
+ graphicsview/qgraphicsscene.h \
+ graphicsview/qgraphicsscene_bsp_p.h \
+ graphicsview/qgraphicsscene_p.h \
+ graphicsview/qgraphicsscenebsptreeindex_p.h \
+ graphicsview/qgraphicssceneevent.h \
+ graphicsview/qgraphicssceneindex_p.h \
+ graphicsview/qgraphicsscenelinearindex_p.h \
+ graphicsview/qgraphicstransform.h \
+ graphicsview/qgraphicstransform_p.h \
+ graphicsview/qgraphicsview.h \
+ graphicsview/qgraphicsview_p.h \
+ graphicsview/qgraphicswidget.h \
+ graphicsview/qgraphicswidget_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 \
+ graphicsview/qgraphicslayout.cpp \
+ graphicsview/qgraphicslayout_p.cpp \
+ graphicsview/qgraphicslayoutitem.cpp \
+ graphicsview/qgraphicslinearlayout.cpp \
+ graphicsview/qgraphicsproxywidget.cpp \
+ graphicsview/qgraphicsscene.cpp \
+ graphicsview/qgraphicsscene_bsp.cpp \
+ graphicsview/qgraphicsscenebsptreeindex.cpp \
+ graphicsview/qgraphicssceneevent.cpp \
+ graphicsview/qgraphicssceneindex.cpp \
+ graphicsview/qgraphicsscenelinearindex.cpp \
+ graphicsview/qgraphicstransform.cpp \
+ graphicsview/qgraphicsview.cpp \
+ graphicsview/qgraphicswidget.cpp \
+ graphicsview/qgraphicswidget_p.cpp \
+ graphicsview/qgridlayoutengine.cpp \
+ graphicsview/qsimplex_p.cpp \
+ graphicsview/qgraphicsanchorlayout_p.cpp \
+ graphicsview/qgraphicsanchorlayout.cpp
diff --git a/src/widgets/graphicsview/qgraph_p.h b/src/widgets/graphicsview/qgraph_p.h
new file mode 100644
index 0000000000..094fbd1c2a
--- /dev/null
+++ b/src/widgets/graphicsview/qgraph_p.h
@@ -0,0 +1,286 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** 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 Technology Preview License Agreement accompanying
+** this package.
+**
+** 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.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QGRAPH_P_H
+#define QGRAPH_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#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("%s-%s already has an edge", qPrintable(first->toString()), qPrintable(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;
+ }
+
+ 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,%6,%7]\" color=\"#000000\"] \n")
+ .arg(v->toString())
+ .arg(v1->toString())
+ .arg(data->minSize)
+ .arg(data->minPrefSize)
+ .arg(data->prefSize)
+ .arg(data->maxPrefSize)
+ .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
+
+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:
+ QHash<Vertex *, QHash<Vertex *, EdgeData *> *> m_graph;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/widgets/graphicsview/qgraphicsanchorlayout.cpp b/src/widgets/graphicsview/qgraphicsanchorlayout.cpp
new file mode 100644
index 0000000000..9bb5424641
--- /dev/null
+++ b/src/widgets/graphicsview/qgraphicsanchorlayout.cpp
@@ -0,0 +1,533 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** 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 Technology Preview License Agreement accompanying
+** this package.
+**
+** 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.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@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 allows developers to specify how widgets should be placed relative to
+ each other, and to the layout itself. The specification is made by adding anchors to the
+ layout by calling addAnchor(), addAnchors() or addCornerAnchors().
+
+ Existing anchors in the layout can be accessed with the anchor() function.
+ Items that are anchored are automatically added to the layout, and if items
+ are removed, all their anchors will be automatically removed.
+
+ \div {class="float-left"}
+ \inlineimage simpleanchorlayout-example.png Using an anchor layout to align simple colored widgets.
+ \enddiv
+
+ Anchors are always set up between edges of an item, where the "center" is also considered to
+ be an edge. Consider the following example:
+
+ \snippet examples/graphicsview/simpleanchorlayout/main.cpp adding anchors
+
+ Here, the right edge of item \c a is anchored to the left edge of item \c b and the bottom
+ edge of item \c a is anchored to the top edge of item \c b, with the result that
+ item \c b will be placed diagonally to the right and below item \c b.
+
+ The addCornerAnchors() function provides a simpler way of anchoring the corners
+ of two widgets than the two individual calls to addAnchor() shown in the code
+ above. Here, we see how a widget can be anchored to the top-left corner of the enclosing
+ layout:
+
+ \snippet examples/graphicsview/simpleanchorlayout/main.cpp adding a corner anchor
+
+ In cases where anchors are used to match the widths or heights of widgets, it is
+ convenient to use the addAnchors() function. As with the other functions for specifying
+ anchors, it can also be used to anchor a widget to a layout.
+
+ \clearfloat
+ \section1 Size Hints and Size Policies in an Anchor Layout
+
+ QGraphicsAnchorLayout respects each item's size hints and size policies.
+ Note that there are some properties of QSizePolicy that are \l{Known issues}{not respected}.
+
+ \section1 Spacing within an Anchor Layout
+
+ The layout may distribute some space between the items. If the spacing has not been
+ explicitly specified, the actual amount of space will usually be 0.
+
+ However, if the first edge is the \e opposite of the second edge (e.g., the right edge
+ of the first widget is anchored to the left edge of the second widget), the size of the
+ anchor will be queried from the style through a pixel metric:
+ \l{QStyle::}{PM_LayoutHorizontalSpacing} for horizontal anchors and
+ \l{QStyle::}{PM_LayoutVerticalSpacing} for vertical anchors.
+
+ If the spacing is negative, the items will overlap to some extent.
+
+
+ \section1 Known issues
+ There are some features that QGraphicsAnchorLayout currently does not support.
+ This might change in the future, so avoid using these features if you want to
+ avoid any future regressions in behaviour:
+ \list
+
+ \o Stretch factors are not respected.
+
+ \o QSizePolicy::ExpandFlag is not respected.
+
+ \o Height for width is not respected.
+
+ \endlist
+
+ \sa QGraphicsLinearLayout, QGraphicsGridLayout, QGraphicsLayout
+*/
+
+/*!
+ \class QGraphicsAnchor
+ \brief The QGraphicsAnchor class represents an anchor between two items in a
+ QGraphicsAnchorLayout.
+ \since 4.6
+ \ingroup appearance
+ \ingroup geomanagement
+ \ingroup graphicsview-api
+
+ The graphics anchor provides an API that enables you to query and manipulate the
+ properties an anchor has. When an anchor is added to the layout with
+ QGraphicsAnchorLayout::addAnchor(), a QGraphicsAnchor instance is returned where the properties
+ are initialized to their default values. The properties can then be further changed, and they
+ will be picked up the next time the layout is activated.
+
+ \sa QGraphicsAnchorLayout::anchor()
+
+*/
+#include "qgraphicsanchorlayout_p.h"
+#ifndef QT_NO_GRAPHICSVIEW
+QT_BEGIN_NAMESPACE
+
+QGraphicsAnchor::QGraphicsAnchor(QGraphicsAnchorLayout *parentLayout)
+ : QObject(*(new QGraphicsAnchorPrivate))
+{
+ Q_D(QGraphicsAnchor);
+ Q_ASSERT(parentLayout);
+ d->layoutPrivate = parentLayout->d_func();
+}
+
+/*!
+ Removes the QGraphicsAnchor object from the layout and destroys it.
+*/
+QGraphicsAnchor::~QGraphicsAnchor()
+{
+}
+
+/*!
+ \property QGraphicsAnchor::sizePolicy
+ \brief the size policy for the QGraphicsAnchor.
+
+ By setting the size policy on an anchor you can configure how the anchor can resize itself
+ from its preferred spacing. For instance, if the anchor has the size policy
+ QSizePolicy::Minimum, the spacing is the minimum size of the anchor. However, its size
+ can grow up to the anchors maximum size. If the default size policy is QSizePolicy::Fixed,
+ the anchor can neither grow or shrink, which means that the only size the anchor can have
+ is the spacing. QSizePolicy::Fixed is the default size policy.
+ QGraphicsAnchor always has a minimum spacing of 0 and a very large maximum spacing.
+
+ \sa QGraphicsAnchor::spacing
+*/
+
+void QGraphicsAnchor::setSizePolicy(QSizePolicy::Policy policy)
+{
+ Q_D(QGraphicsAnchor);
+ d->setSizePolicy(policy);
+}
+
+QSizePolicy::Policy QGraphicsAnchor::sizePolicy() const
+{
+ Q_D(const QGraphicsAnchor);
+ return d->sizePolicy;
+}
+
+/*!
+ \property QGraphicsAnchor::spacing
+ \brief the preferred space between items in the QGraphicsAnchorLayout.
+
+ Depending on the anchor type, the default spacing is either
+ 0 or a value returned from the style.
+
+ \sa QGraphicsAnchorLayout::addAnchor()
+*/
+void QGraphicsAnchor::setSpacing(qreal spacing)
+{
+ Q_D(QGraphicsAnchor);
+ d->setSpacing(spacing);
+}
+
+qreal QGraphicsAnchor::spacing() const
+{
+ Q_D(const QGraphicsAnchor);
+ return d->spacing();
+}
+
+void QGraphicsAnchor::unsetSpacing()
+{
+ Q_D(QGraphicsAnchor);
+ d->unsetSpacing();
+}
+
+/*!
+ Constructs a QGraphicsAnchorLayout instance. \a parent is passed to
+ QGraphicsLayout's constructor.
+ */
+QGraphicsAnchorLayout::QGraphicsAnchorLayout(QGraphicsLayoutItem *parent)
+ : QGraphicsLayout(*new QGraphicsAnchorLayoutPrivate(), parent)
+{
+ Q_D(QGraphicsAnchorLayout);
+ d->createLayoutEdges();
+}
+
+/*!
+ Destroys the QGraphicsAnchorLayout object.
+*/
+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 spacing 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 by 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 QGraphicsAnchor::setSpacing() method.
+
+ Calling this function where \a firstItem or \a secondItem are ancestors of the layout have
+ undefined behaviour.
+
+ \sa addAnchors(), addCornerAnchors()
+ */
+QGraphicsAnchor *
+QGraphicsAnchorLayout::addAnchor(QGraphicsLayoutItem *firstItem, Qt::AnchorPoint firstEdge,
+ QGraphicsLayoutItem *secondItem, Qt::AnchorPoint secondEdge)
+{
+ Q_D(QGraphicsAnchorLayout);
+ QGraphicsAnchor *a = d->addAnchor(firstItem, firstEdge, secondItem, secondEdge);
+ invalidate();
+ return a;
+}
+
+/*!
+ Returns the anchor between the anchor points defined by \a firstItem and \a firstEdge and
+ \a secondItem and \a secondEdge. If there is no such anchor, the function will return 0.
+*/
+QGraphicsAnchor *
+QGraphicsAnchorLayout::anchor(QGraphicsLayoutItem *firstItem, Qt::AnchorPoint firstEdge,
+ QGraphicsLayoutItem *secondItem, Qt::AnchorPoint secondEdge)
+{
+ Q_D(QGraphicsAnchorLayout);
+ return d->getAnchor(firstItem, firstEdge, secondItem, secondEdge);
+}
+
+/*!
+ Creates two anchors between \a firstItem and \a secondItem specified by the corners,
+ \a firstCorner and \a secondCorner, where one is for the horizontal edge and another
+ one for the vertical edge.
+
+ This is a convenience function, since anchoring corners can be expressed as anchoring
+ two edges. For instance:
+
+ \snippet examples/graphicsview/simpleanchorlayout/main.cpp adding a corner anchor in two steps
+
+ This can also be achieved with the following line of code:
+
+ \snippet examples/graphicsview/simpleanchorlayout/main.cpp adding a corner anchor
+
+ 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 by up to 2.
+
+ \sa addAnchor(), addAnchors()
+*/
+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);
+ if (d->addAnchor(firstItem, firstEdge, secondItem, secondEdge)) {
+ // Vertical anchor
+ firstEdge = (firstCorner & 2 ? Qt::AnchorBottom: Qt::AnchorTop);
+ secondEdge = (secondCorner & 2 ? Qt::AnchorBottom: Qt::AnchorTop);
+ d->addAnchor(firstItem, firstEdge, secondItem, secondEdge);
+
+ invalidate();
+ }
+}
+
+/*!
+ Anchors two or four edges of \a firstItem with the corresponding
+ edges of \a secondItem, so that \a firstItem has the same size as
+ \a secondItem in the dimensions specified by \a orientations.
+
+ For example, the following example anchors the left and right edges of two items
+ to match their widths:
+
+ \snippet examples/graphicsview/simpleanchorlayout/main.cpp adding anchors to match sizes in two steps
+
+ This can also be achieved using the following line of code:
+
+ \snippet examples/graphicsview/simpleanchorlayout/main.cpp adding anchors to match sizes
+
+ \sa addAnchor(), addCornerAnchors()
+*/
+void QGraphicsAnchorLayout::addAnchors(QGraphicsLayoutItem *firstItem,
+ QGraphicsLayoutItem *secondItem,
+ Qt::Orientations orientations)
+{
+ bool ok = true;
+ if (orientations & Qt::Horizontal) {
+ // Currently, if the first is ok, then the rest of the calls should be ok
+ ok = addAnchor(secondItem, Qt::AnchorLeft, firstItem, Qt::AnchorLeft) != 0;
+ if (ok)
+ addAnchor(firstItem, Qt::AnchorRight, secondItem, Qt::AnchorRight);
+ }
+ if (orientations & Qt::Vertical && ok) {
+ addAnchor(secondItem, Qt::AnchorTop, firstItem, Qt::AnchorTop);
+ addAnchor(firstItem, Qt::AnchorBottom, secondItem, Qt::AnchorBottom);
+ }
+}
+
+/*!
+ 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.
+
+ QGraphicsAnchorLayout does not support negative spacings. Setting a negative value will unset the
+ previous spacing and make the layout use the spacing provided by the current widget style.
+
+ \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->styleInfo().defaultSpacing(Qt::Horizontal);
+}
+
+/*!
+ Returns the default vertical spacing for the anchor layout.
+
+ \sa horizontalSpacing(), setVerticalSpacing()
+*/
+qreal QGraphicsAnchorLayout::verticalSpacing() const
+{
+ Q_D(const QGraphicsAnchorLayout);
+ return d->styleInfo().defaultSpacing(Qt::Vertical);
+}
+
+/*!
+ \reimp
+*/
+void QGraphicsAnchorLayout::setGeometry(const QRectF &geom)
+{
+ Q_D(QGraphicsAnchorLayout);
+
+ QGraphicsLayout::setGeometry(geom);
+ d->calculateVertexPositions(QGraphicsAnchorLayoutPrivate::Horizontal);
+ d->calculateVertexPositions(QGraphicsAnchorLayoutPrivate::Vertical);
+ d->setItemsGeometries(geom);
+}
+
+/*!
+ Removes the layout item at \a index without destroying it. Ownership of
+ the item is transferred to the caller.
+
+ Removing an item will also remove any of the anchors associated with it.
+
+ \sa itemAt(), count()
+*/
+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->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 = true;
+ d->styleInfoDirty = true;
+}
+
+/*!
+ \reimp
+*/
+QSizeF QGraphicsAnchorLayout::sizeHint(Qt::SizeHint which, const QSizeF &constraint) const
+{
+ 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
+#endif //QT_NO_GRAPHICSVIEW
diff --git a/src/widgets/graphicsview/qgraphicsanchorlayout.h b/src/widgets/graphicsview/qgraphicsanchorlayout.h
new file mode 100644
index 0000000000..cdb14bc3ba
--- /dev/null
+++ b/src/widgets/graphicsview/qgraphicsanchorlayout.h
@@ -0,0 +1,128 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** 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 Technology Preview License Agreement accompanying
+** this package.
+**
+** 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.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@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 QGraphicsAnchorPrivate;
+class QGraphicsAnchorLayout;
+class QGraphicsAnchorLayoutPrivate;
+
+class Q_GUI_EXPORT QGraphicsAnchor : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(qreal spacing READ spacing WRITE setSpacing RESET unsetSpacing)
+ Q_PROPERTY(QSizePolicy::Policy sizePolicy READ sizePolicy WRITE setSizePolicy)
+public:
+ void setSpacing(qreal spacing);
+ void unsetSpacing();
+ qreal spacing() const;
+ void setSizePolicy(QSizePolicy::Policy policy);
+ QSizePolicy::Policy sizePolicy() const;
+ ~QGraphicsAnchor();
+private:
+ QGraphicsAnchor(QGraphicsAnchorLayout *parent);
+
+ Q_DECLARE_PRIVATE(QGraphicsAnchor)
+
+ friend class QGraphicsAnchorLayoutPrivate;
+ friend struct AnchorData;
+};
+
+class Q_GUI_EXPORT QGraphicsAnchorLayout : public QGraphicsLayout
+{
+public:
+ QGraphicsAnchorLayout(QGraphicsLayoutItem *parent = 0);
+ virtual ~QGraphicsAnchorLayout();
+
+ QGraphicsAnchor *addAnchor(QGraphicsLayoutItem *firstItem, Qt::AnchorPoint firstEdge,
+ QGraphicsLayoutItem *secondItem, Qt::AnchorPoint secondEdge);
+ QGraphicsAnchor *anchor(QGraphicsLayoutItem *firstItem, Qt::AnchorPoint firstEdge,
+ QGraphicsLayoutItem *secondItem, Qt::AnchorPoint secondEdge);
+
+ void addCornerAnchors(QGraphicsLayoutItem *firstItem, Qt::Corner firstCorner,
+ QGraphicsLayoutItem *secondItem, Qt::Corner secondCorner);
+
+ void addAnchors(QGraphicsLayoutItem *firstItem,
+ QGraphicsLayoutItem *secondItem,
+ Qt::Orientations orientations = Qt::Horizontal | Qt::Vertical);
+
+ 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)
+
+ friend class QGraphicsAnchor;
+};
+
+#endif
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif
diff --git a/src/widgets/graphicsview/qgraphicsanchorlayout_p.cpp b/src/widgets/graphicsview/qgraphicsanchorlayout_p.cpp
new file mode 100644
index 0000000000..48cbec3d62
--- /dev/null
+++ b/src/widgets/graphicsview/qgraphicsanchorlayout_p.cpp
@@ -0,0 +1,3015 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** 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 Technology Preview License Agreement accompanying
+** this package.
+**
+** 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.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtGui/qwidget.h>
+#include <QtGui/qapplication.h>
+#include <QtCore/qlinkedlist.h>
+#include <QtCore/qstack.h>
+
+#ifdef QT_DEBUG
+#include <QtCore/qfile.h>
+#endif
+
+#include "qgraphicsanchorlayout_p.h"
+
+#ifndef QT_NO_GRAPHICSVIEW
+QT_BEGIN_NAMESPACE
+
+// To ensure that all variables inside the simplex solver are non-negative,
+// we limit the size of anchors in the interval [-limit, limit]. Then before
+// sending them to the simplex solver we add "limit" as an offset, so that
+// they are actually calculated in the interval [0, 2 * limit]
+// To avoid numerical errors in platforms where we use single precision,
+// we use a tighter limit for the variables range.
+const qreal g_offset = (sizeof(qreal) == sizeof(double)) ? QWIDGETSIZE_MAX : QWIDGETSIZE_MAX / 32;
+
+QGraphicsAnchorPrivate::QGraphicsAnchorPrivate(int version)
+ : QObjectPrivate(version), layoutPrivate(0), data(0),
+ sizePolicy(QSizePolicy::Fixed), preferredSize(0),
+ hasSize(true)
+{
+}
+
+QGraphicsAnchorPrivate::~QGraphicsAnchorPrivate()
+{
+ if (data) {
+ // The QGraphicsAnchor was already deleted at this moment. We must clean
+ // the dangling pointer to avoid double deletion in the AnchorData dtor.
+ data->graphicsAnchor = 0;
+
+ layoutPrivate->removeAnchor(data->from, data->to);
+ }
+}
+
+void QGraphicsAnchorPrivate::setSizePolicy(QSizePolicy::Policy policy)
+{
+ if (sizePolicy != policy) {
+ sizePolicy = policy;
+ layoutPrivate->q_func()->invalidate();
+ }
+}
+
+void QGraphicsAnchorPrivate::setSpacing(qreal value)
+{
+ if (!data) {
+ qWarning("QGraphicsAnchor::setSpacing: The anchor does not exist.");
+ return;
+ }
+
+ if (hasSize && (preferredSize == value))
+ return;
+
+ // The anchor has an user-defined size
+ hasSize = true;
+ preferredSize = value;
+
+ layoutPrivate->q_func()->invalidate();
+}
+
+void QGraphicsAnchorPrivate::unsetSpacing()
+{
+ if (!data) {
+ qWarning("QGraphicsAnchor::setSpacing: The anchor does not exist.");
+ return;
+ }
+
+ // Return to standard direction
+ hasSize = false;
+
+ layoutPrivate->q_func()->invalidate();
+}
+
+qreal QGraphicsAnchorPrivate::spacing() const
+{
+ if (!data) {
+ qWarning("QGraphicsAnchor::setSpacing: The anchor does not exist.");
+ return 0;
+ }
+
+ return preferredSize;
+}
+
+
+static void applySizePolicy(QSizePolicy::Policy policy,
+ qreal minSizeHint, qreal prefSizeHint, qreal maxSizeHint,
+ qreal *minSize, qreal *prefSize,
+ qreal *maxSize)
+{
+ // minSize, prefSize and maxSize are initialized
+ // with item's preferred Size: this is QSizePolicy::Fixed.
+ //
+ // Then we check each flag to find the resultant QSizePolicy,
+ // according to the following table:
+ //
+ // constant value
+ // QSizePolicy::Fixed 0
+ // QSizePolicy::Minimum GrowFlag
+ // QSizePolicy::Maximum ShrinkFlag
+ // QSizePolicy::Preferred GrowFlag | ShrinkFlag
+ // QSizePolicy::Ignored GrowFlag | ShrinkFlag | IgnoreFlag
+
+ if (policy & QSizePolicy::ShrinkFlag)
+ *minSize = minSizeHint;
+ else
+ *minSize = prefSizeHint;
+
+ if (policy & QSizePolicy::GrowFlag)
+ *maxSize = maxSizeHint;
+ else
+ *maxSize = prefSizeHint;
+
+ // Note that these two initializations are affected by the previous flags
+ if (policy & QSizePolicy::IgnoreFlag)
+ *prefSize = *minSize;
+ else
+ *prefSize = prefSizeHint;
+}
+
+AnchorData::~AnchorData()
+{
+ if (graphicsAnchor) {
+ // Remove reference to ourself to avoid double removal in
+ // QGraphicsAnchorPrivate dtor.
+ graphicsAnchor->d_func()->data = 0;
+
+ delete graphicsAnchor;
+ }
+}
+
+
+void AnchorData::refreshSizeHints(const QLayoutStyleInfo *styleInfo)
+{
+ QSizePolicy::Policy policy;
+ qreal minSizeHint;
+ qreal prefSizeHint;
+ qreal maxSizeHint;
+
+ if (item) {
+ // It is an internal anchor, fetch size information from the item
+ if (isLayoutAnchor) {
+ minSize = 0;
+ prefSize = 0;
+ maxSize = QWIDGETSIZE_MAX;
+ if (isCenterAnchor)
+ maxSize /= 2;
+
+ minPrefSize = prefSize;
+ maxPrefSize = maxSize;
+ return;
+ } else {
+ if (orientation == QGraphicsAnchorLayoutPrivate::Horizontal) {
+ policy = item->sizePolicy().horizontalPolicy();
+ minSizeHint = item->effectiveSizeHint(Qt::MinimumSize).width();
+ prefSizeHint = item->effectiveSizeHint(Qt::PreferredSize).width();
+ maxSizeHint = item->effectiveSizeHint(Qt::MaximumSize).width();
+ } else {
+ policy = item->sizePolicy().verticalPolicy();
+ minSizeHint = item->effectiveSizeHint(Qt::MinimumSize).height();
+ prefSizeHint = item->effectiveSizeHint(Qt::PreferredSize).height();
+ maxSizeHint = item->effectiveSizeHint(Qt::MaximumSize).height();
+ }
+
+ if (isCenterAnchor) {
+ minSizeHint /= 2;
+ prefSizeHint /= 2;
+ maxSizeHint /= 2;
+ }
+ }
+ } else {
+ // It is a user-created anchor, fetch size information from the associated QGraphicsAnchor
+ Q_ASSERT(graphicsAnchor);
+ QGraphicsAnchorPrivate *anchorPrivate = graphicsAnchor->d_func();
+
+ // Policy, min and max sizes are straightforward
+ policy = anchorPrivate->sizePolicy;
+ minSizeHint = 0;
+ maxSizeHint = QWIDGETSIZE_MAX;
+
+ // Preferred Size
+ if (anchorPrivate->hasSize) {
+ // Anchor has user-defined size
+ prefSizeHint = anchorPrivate->preferredSize;
+ } else {
+ // Fetch size information from style
+ const Qt::Orientation orient = Qt::Orientation(QGraphicsAnchorLayoutPrivate::edgeOrientation(from->m_edge) + 1);
+ qreal s = styleInfo->defaultSpacing(orient);
+ if (s < 0) {
+ QSizePolicy::ControlType controlTypeFrom = from->m_item->sizePolicy().controlType();
+ QSizePolicy::ControlType controlTypeTo = to->m_item->sizePolicy().controlType();
+ s = styleInfo->perItemSpacing(controlTypeFrom, controlTypeTo, orient);
+
+ // ### Currently we do not support negative anchors inside the graph.
+ // To avoid those being created by a negative style spacing, we must
+ // make this test.
+ if (s < 0)
+ s = 0;
+ }
+ prefSizeHint = s;
+ }
+ }
+
+ // Fill minSize, prefSize and maxSize based on policy and sizeHints
+ applySizePolicy(policy, minSizeHint, prefSizeHint, maxSizeHint,
+ &minSize, &prefSize, &maxSize);
+
+ minPrefSize = prefSize;
+ maxPrefSize = maxSize;
+
+ // 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;
+}
+
+void ParallelAnchorData::updateChildrenSizes()
+{
+ firstEdge->sizeAtMinimum = sizeAtMinimum;
+ firstEdge->sizeAtPreferred = sizeAtPreferred;
+ firstEdge->sizeAtMaximum = sizeAtMaximum;
+
+ if (secondForward()) {
+ secondEdge->sizeAtMinimum = sizeAtMinimum;
+ secondEdge->sizeAtPreferred = sizeAtPreferred;
+ secondEdge->sizeAtMaximum = sizeAtMaximum;
+ } else {
+ secondEdge->sizeAtMinimum = -sizeAtMinimum;
+ secondEdge->sizeAtPreferred = -sizeAtPreferred;
+ secondEdge->sizeAtMaximum = -sizeAtMaximum;
+ }
+
+ firstEdge->updateChildrenSizes();
+ secondEdge->updateChildrenSizes();
+}
+
+/*
+ \internal
+
+ Initialize the parallel anchor size hints using the sizeHint information from
+ its children.
+
+ Note that parallel groups can lead to unfeasibility, so during calculation, we can
+ find out one unfeasibility. Because of that this method return boolean. This can't
+ happen in sequential, so there the method is void.
+ */
+bool ParallelAnchorData::calculateSizeHints()
+{
+ // Normalize second child sizes.
+ // A negative anchor of sizes min, minPref, pref, maxPref and max, is equivalent
+ // to a forward anchor of sizes -max, -maxPref, -pref, -minPref, -min
+ qreal secondMin;
+ qreal secondMinPref;
+ qreal secondPref;
+ qreal secondMaxPref;
+ qreal secondMax;
+
+ if (secondForward()) {
+ secondMin = secondEdge->minSize;
+ secondMinPref = secondEdge->minPrefSize;
+ secondPref = secondEdge->prefSize;
+ secondMaxPref = secondEdge->maxPrefSize;
+ secondMax = secondEdge->maxSize;
+ } else {
+ secondMin = -secondEdge->maxSize;
+ secondMinPref = -secondEdge->maxPrefSize;
+ secondPref = -secondEdge->prefSize;
+ secondMaxPref = -secondEdge->minPrefSize;
+ secondMax = -secondEdge->minSize;
+ }
+
+ minSize = qMax(firstEdge->minSize, secondMin);
+ maxSize = qMin(firstEdge->maxSize, secondMax);
+
+ // This condition means that the maximum size of one anchor being simplified is smaller than
+ // the minimum size of the other anchor. The consequence is that there won't be a valid size
+ // for this parallel setup.
+ if (minSize > maxSize) {
+ return false;
+ }
+
+ // Preferred size calculation
+ // The calculation of preferred size is done as follows:
+ //
+ // 1) Check whether one of the child anchors is the layout structural anchor
+ // If so, we can simply copy the preferred information from the other child,
+ // after bounding it to our minimum and maximum sizes.
+ // If not, then we proceed with the actual calculations.
+ //
+ // 2) The whole algorithm for preferred size calculation is based on the fact
+ // that, if a given anchor cannot remain at its preferred size, it'd rather
+ // grow than shrink.
+ //
+ // What happens though is that while this affirmative is true for simple
+ // anchors, it may not be true for sequential anchors that have one or more
+ // reversed anchors inside it. That happens because when a sequential anchor
+ // grows, any reversed anchors inside it may be required to shrink, something
+ // we try to avoid, as said above.
+ //
+ // To overcome this, besides their actual preferred size "prefSize", each anchor
+ // exports what we call "minPrefSize" and "maxPrefSize". These two values define
+ // a surrounding interval where, if required to move, the anchor would rather
+ // remain inside.
+ //
+ // For standard anchors, this area simply represents the region between
+ // prefSize and maxSize, which makes sense since our first affirmation.
+ // For composed anchors, these values are calculated as to reduce the global
+ // "damage", that is, to reduce the total deviation and the total amount of
+ // anchors that had to shrink.
+
+ if (firstEdge->isLayoutAnchor) {
+ prefSize = qBound(minSize, secondPref, maxSize);
+ minPrefSize = qBound(minSize, secondMinPref, maxSize);
+ maxPrefSize = qBound(minSize, secondMaxPref, maxSize);
+ } else if (secondEdge->isLayoutAnchor) {
+ prefSize = qBound(minSize, firstEdge->prefSize, maxSize);
+ minPrefSize = qBound(minSize, firstEdge->minPrefSize, maxSize);
+ maxPrefSize = qBound(minSize, firstEdge->maxPrefSize, maxSize);
+ } else {
+ // Calculate the intersection between the "preferred" regions of each child
+ const qreal lowerBoundary =
+ qBound(minSize, qMax(firstEdge->minPrefSize, secondMinPref), maxSize);
+ const qreal upperBoundary =
+ qBound(minSize, qMin(firstEdge->maxPrefSize, secondMaxPref), maxSize);
+ const qreal prefMean =
+ qBound(minSize, (firstEdge->prefSize + secondPref) / 2, maxSize);
+
+ if (lowerBoundary < upperBoundary) {
+ // If there is an intersection between the two regions, this intersection
+ // will be used as the preferred region of the parallel anchor itself.
+ // The preferred size will be the bounded average between the two preferred
+ // sizes.
+ prefSize = qBound(lowerBoundary, prefMean, upperBoundary);
+ minPrefSize = lowerBoundary;
+ maxPrefSize = upperBoundary;
+ } else {
+ // If there is no intersection, we have to attribute "damage" to at least
+ // one of the children. The minimum total damage is achieved in points
+ // inside the region that extends from (1) the upper boundary of the lower
+ // region to (2) the lower boundary of the upper region.
+ // Then, we expose this region as _our_ preferred region and once again,
+ // use the bounded average as our preferred size.
+ prefSize = qBound(upperBoundary, prefMean, lowerBoundary);
+ minPrefSize = upperBoundary;
+ maxPrefSize = lowerBoundary;
+ }
+ }
+
+ // See comment in AnchorData::refreshSizeHints() about sizeAt* values
+ sizeAtMinimum = prefSize;
+ sizeAtPreferred = prefSize;
+ sizeAtMaximum = prefSize;
+
+ return true;
+}
+
+/*!
+ \internal
+ returns the factor in the interval [-1, 1].
+ -1 is at Minimum
+ 0 is at Preferred
+ 1 is at Maximum
+*/
+static QPair<QGraphicsAnchorLayoutPrivate::Interval, qreal> getFactor(qreal value, qreal min,
+ qreal minPref, qreal pref,
+ qreal maxPref, qreal max)
+{
+ QGraphicsAnchorLayoutPrivate::Interval interval;
+ qreal lower;
+ qreal upper;
+
+ if (value < minPref) {
+ interval = QGraphicsAnchorLayoutPrivate::MinimumToMinPreferred;
+ lower = min;
+ upper = minPref;
+ } else if (value < pref) {
+ interval = QGraphicsAnchorLayoutPrivate::MinPreferredToPreferred;
+ lower = minPref;
+ upper = pref;
+ } else if (value < maxPref) {
+ interval = QGraphicsAnchorLayoutPrivate::PreferredToMaxPreferred;
+ lower = pref;
+ upper = maxPref;
+ } else {
+ interval = QGraphicsAnchorLayoutPrivate::MaxPreferredToMaximum;
+ lower = maxPref;
+ upper = max;
+ }
+
+ qreal progress;
+ if (upper == lower) {
+ progress = 0;
+ } else {
+ progress = (value - lower) / (upper - lower);
+ }
+
+ return qMakePair(interval, progress);
+}
+
+static qreal interpolate(const QPair<QGraphicsAnchorLayoutPrivate::Interval, qreal> &factor,
+ qreal min, qreal minPref, qreal pref, qreal maxPref, qreal max)
+{
+ qreal lower;
+ qreal upper;
+
+ switch (factor.first) {
+ case QGraphicsAnchorLayoutPrivate::MinimumToMinPreferred:
+ lower = min;
+ upper = minPref;
+ break;
+ case QGraphicsAnchorLayoutPrivate::MinPreferredToPreferred:
+ lower = minPref;
+ upper = pref;
+ break;
+ case QGraphicsAnchorLayoutPrivate::PreferredToMaxPreferred:
+ lower = pref;
+ upper = maxPref;
+ break;
+ case QGraphicsAnchorLayoutPrivate::MaxPreferredToMaximum:
+ lower = maxPref;
+ upper = max;
+ break;
+ }
+
+ return lower + factor.second * (upper - lower);
+}
+
+void SequentialAnchorData::updateChildrenSizes()
+{
+ // Band here refers if the value is in the Minimum To Preferred
+ // band (the lower band) or the Preferred To Maximum (the upper band).
+
+ const QPair<QGraphicsAnchorLayoutPrivate::Interval, qreal> minFactor =
+ getFactor(sizeAtMinimum, minSize, minPrefSize, prefSize, maxPrefSize, maxSize);
+ const QPair<QGraphicsAnchorLayoutPrivate::Interval, qreal> prefFactor =
+ getFactor(sizeAtPreferred, minSize, minPrefSize, prefSize, maxPrefSize, maxSize);
+ const QPair<QGraphicsAnchorLayoutPrivate::Interval, qreal> maxFactor =
+ getFactor(sizeAtMaximum, minSize, minPrefSize, prefSize, maxPrefSize, maxSize);
+
+ // XXX This is not safe if Vertex simplification takes place after the sequential
+ // anchor is created. In that case, "prev" will be a group-vertex, different from
+ // "from" or "to", that _contains_ one of them.
+ AnchorVertex *prev = from;
+
+ for (int i = 0; i < m_edges.count(); ++i) {
+ AnchorData *e = m_edges.at(i);
+
+ const bool edgeIsForward = (e->from == prev);
+ if (edgeIsForward) {
+ e->sizeAtMinimum = interpolate(minFactor, e->minSize, e->minPrefSize,
+ e->prefSize, e->maxPrefSize, e->maxSize);
+ e->sizeAtPreferred = interpolate(prefFactor, e->minSize, e->minPrefSize,
+ e->prefSize, e->maxPrefSize, e->maxSize);
+ e->sizeAtMaximum = interpolate(maxFactor, e->minSize, e->minPrefSize,
+ e->prefSize, e->maxPrefSize, e->maxSize);
+ prev = e->to;
+ } else {
+ Q_ASSERT(prev == e->to);
+ e->sizeAtMinimum = interpolate(minFactor, e->maxSize, e->maxPrefSize,
+ e->prefSize, e->minPrefSize, e->minSize);
+ e->sizeAtPreferred = interpolate(prefFactor, e->maxSize, e->maxPrefSize,
+ e->prefSize, e->minPrefSize, e->minSize);
+ e->sizeAtMaximum = interpolate(maxFactor, e->maxSize, e->maxPrefSize,
+ e->prefSize, e->minPrefSize, e->minSize);
+ prev = e->from;
+ }
+
+ e->updateChildrenSizes();
+ }
+}
+
+void SequentialAnchorData::calculateSizeHints()
+{
+ minSize = 0;
+ prefSize = 0;
+ maxSize = 0;
+ minPrefSize = 0;
+ maxPrefSize = 0;
+
+ AnchorVertex *prev = from;
+
+ for (int i = 0; i < m_edges.count(); ++i) {
+ AnchorData *edge = m_edges.at(i);
+
+ const bool edgeIsForward = (edge->from == prev);
+ if (edgeIsForward) {
+ minSize += edge->minSize;
+ prefSize += edge->prefSize;
+ maxSize += edge->maxSize;
+ minPrefSize += edge->minPrefSize;
+ maxPrefSize += edge->maxPrefSize;
+ prev = edge->to;
+ } else {
+ Q_ASSERT(prev == edge->to);
+ minSize -= edge->maxSize;
+ prefSize -= edge->prefSize;
+ maxSize -= edge->minSize;
+ minPrefSize -= edge->maxPrefSize;
+ maxPrefSize -= edge->minPrefSize;
+ prev = edge->from;
+ }
+ }
+
+ // 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(true), styleInfoDirty(true)
+{
+ for (int i = 0; i < NOrientations; ++i) {
+ for (int j = 0; j < 3; ++j) {
+ sizeHints[i][j] = -1;
+ }
+ interpolationProgress[i] = -1;
+
+ spacings[i] = -1;
+ graphHasConflicts[i] = false;
+
+ layoutFirstVertex[i] = 0;
+ layoutCentralVertex[i] = 0;
+ layoutLastVertex[i] = 0;
+ }
+}
+
+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
+
+ Adds \a newAnchor to the graph.
+
+ Returns the newAnchor itself if it could be added without further changes to the graph. If a
+ new parallel anchor had to be created, then returns the new parallel anchor. If a parallel anchor
+ had to be created and it results in an unfeasible setup, \a feasible is set to false, otherwise
+ true.
+
+ Note that in the case a new parallel anchor is created, it might also take over some constraints
+ from its children anchors.
+*/
+AnchorData *QGraphicsAnchorLayoutPrivate::addAnchorMaybeParallel(AnchorData *newAnchor, bool *feasible)
+{
+ Orientation orientation = Orientation(newAnchor->orientation);
+ Graph<AnchorVertex, AnchorData> &g = graph[orientation];
+ *feasible = true;
+
+ // If already exists one anchor where newAnchor is supposed to be, we create a parallel
+ // anchor.
+ if (AnchorData *oldAnchor = g.takeEdge(newAnchor->from, newAnchor->to)) {
+ ParallelAnchorData *parallel = new ParallelAnchorData(oldAnchor, newAnchor);
+
+ // The parallel anchor will "replace" its children anchors in
+ // every center constraint that they appear.
+
+ // ### If the dependent (center) anchors had reference(s) to their constraints, we
+ // could avoid traversing all the itemCenterConstraints.
+ QList<QSimplexConstraint *> &constraints = itemCenterConstraints[orientation];
+
+ AnchorData *children[2] = { oldAnchor, newAnchor };
+ QList<QSimplexConstraint *> *childrenConstraints[2] = { &parallel->m_firstConstraints,
+ &parallel->m_secondConstraints };
+
+ for (int i = 0; i < 2; ++i) {
+ AnchorData *child = children[i];
+ QList<QSimplexConstraint *> *childConstraints = childrenConstraints[i];
+
+ // We need to fix the second child constraints if the parallel group will have the
+ // opposite direction of the second child anchor. For the point of view of external
+ // entities, this anchor was reversed. So if at some point we say that the parallel
+ // has a value of 20, this mean that the second child (when reversed) will be
+ // assigned -20.
+ const bool needsReverse = i == 1 && !parallel->secondForward();
+
+ if (!child->isCenterAnchor)
+ continue;
+
+ parallel->isCenterAnchor = true;
+
+ for (int j = 0; j < constraints.count(); ++j) {
+ QSimplexConstraint *c = constraints[j];
+ if (c->variables.contains(child)) {
+ childConstraints->append(c);
+ qreal v = c->variables.take(child);
+ if (needsReverse)
+ v *= -1;
+ c->variables.insert(parallel, v);
+ }
+ }
+ }
+
+ // At this point we can identify that the parallel anchor is not feasible, e.g. one
+ // anchor minimum size is bigger than the other anchor maximum size.
+ *feasible = parallel->calculateSizeHints();
+ newAnchor = parallel;
+ }
+
+ g.createEdge(newAnchor->from, newAnchor->to, newAnchor);
+ return newAnchor;
+}
+
+/*!
+ \internal
+
+ Takes the sequence of vertices described by (\a before, \a vertices, \a after) and removes
+ all anchors connected to the vertices in \a vertices, returning one simplified anchor between
+ \a before and \a after.
+
+ Note that this function doesn't add the created anchor to the graph. This should be done by
+ the caller.
+*/
+static AnchorData *createSequence(Graph<AnchorVertex, AnchorData> *graph,
+ AnchorVertex *before,
+ const QVector<AnchorVertex*> &vertices,
+ AnchorVertex *after)
+{
+#if defined(QT_DEBUG) && 0
+ QString strVertices;
+ for (int 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
+
+ AnchorVertex *prev = before;
+ QVector<AnchorData *> edges;
+
+ // Take from the graph, the edges that will be simplificated
+ for (int i = 0; i < vertices.count(); ++i) {
+ AnchorVertex *next = vertices.at(i);
+ AnchorData *ad = graph->takeEdge(prev, next);
+ Q_ASSERT(ad);
+ edges.append(ad);
+ prev = next;
+ }
+
+ // Take the last edge (not covered in the loop above)
+ AnchorData *ad = graph->takeEdge(vertices.last(), after);
+ Q_ASSERT(ad);
+ edges.append(ad);
+
+ // Create sequence
+ SequentialAnchorData *sequence = new SequentialAnchorData(vertices, edges);
+ sequence->from = before;
+ sequence->to = after;
+
+ sequence->calculateSizeHints();
+
+ return 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
+
+ When creating the parallel anchors, the algorithm might identify unfeasible situations. In this
+ case the simplification process stops and returns false. Otherwise returns true.
+*/
+bool QGraphicsAnchorLayoutPrivate::simplifyGraph(Orientation orientation)
+{
+ if (items.isEmpty())
+ return true;
+
+#if defined(QT_DEBUG) && 0
+ qDebug("Simplifying Graph for %s",
+ orientation == Horizontal ? "Horizontal" : "Vertical");
+
+ static int count = 0;
+ if (orientation == Horizontal) {
+ count++;
+ dumpGraph(QString::fromAscii("%1-full").arg(count));
+ }
+#endif
+
+ // Vertex simplification
+ if (!simplifyVertices(orientation)) {
+ restoreVertices(orientation);
+ return false;
+ }
+
+ // Anchor simplification
+ bool dirty;
+ bool feasible = true;
+ do {
+ dirty = simplifyGraphIteration(orientation, &feasible);
+ } while (dirty && feasible);
+
+ // Note that if we are not feasible, we fallback and make sure that the graph is fully restored
+ if (!feasible) {
+ restoreSimplifiedGraph(orientation);
+ restoreVertices(orientation);
+ return false;
+ }
+
+#if defined(QT_DEBUG) && 0
+ dumpGraph(QString::fromAscii("%1-simplified-%2").arg(count).arg(
+ QString::fromAscii(orientation == Horizontal ? "Horizontal" : "Vertical")));
+#endif
+
+ return true;
+}
+
+static AnchorVertex *replaceVertex_helper(AnchorData *data, AnchorVertex *oldV, AnchorVertex *newV)
+{
+ AnchorVertex *other;
+ if (data->from == oldV) {
+ data->from = newV;
+ other = data->to;
+ } else {
+ data->to = newV;
+ other = data->from;
+ }
+ return other;
+}
+
+bool QGraphicsAnchorLayoutPrivate::replaceVertex(Orientation orientation, AnchorVertex *oldV,
+ AnchorVertex *newV, const QList<AnchorData *> &edges)
+{
+ Graph<AnchorVertex, AnchorData> &g = graph[orientation];
+ bool feasible = true;
+
+ for (int i = 0; i < edges.count(); ++i) {
+ AnchorData *ad = edges[i];
+ AnchorVertex *otherV = replaceVertex_helper(ad, oldV, newV);
+
+#if defined(QT_DEBUG)
+ ad->name = QString::fromAscii("%1 --to--> %2").arg(ad->from->toString()).arg(ad->to->toString());
+#endif
+
+ bool newFeasible;
+ AnchorData *newAnchor = addAnchorMaybeParallel(ad, &newFeasible);
+ feasible &= newFeasible;
+
+ if (newAnchor != ad) {
+ // A parallel was created, we mark that in the list of anchors created by vertex
+ // simplification. This is needed because we want to restore them in a separate step
+ // from the restoration of anchor simplification.
+ anchorsFromSimplifiedVertices[orientation].append(newAnchor);
+ }
+
+ g.takeEdge(oldV, otherV);
+ }
+
+ return feasible;
+}
+
+/*!
+ \internal
+*/
+bool QGraphicsAnchorLayoutPrivate::simplifyVertices(Orientation orientation)
+{
+ Q_Q(QGraphicsAnchorLayout);
+ Graph<AnchorVertex, AnchorData> &g = graph[orientation];
+
+ // We'll walk through vertices
+ QStack<AnchorVertex *> stack;
+ stack.push(layoutFirstVertex[orientation]);
+ QSet<AnchorVertex *> visited;
+
+ while (!stack.isEmpty()) {
+ AnchorVertex *v = stack.pop();
+ visited.insert(v);
+
+ // Each adjacent of 'v' is a possible vertex to be merged. So we traverse all of
+ // them. Since once a merge is made, we might add new adjacents, and we don't want to
+ // pass two times through one adjacent. The 'index' is used to track our position.
+ QList<AnchorVertex *> adjacents = g.adjacentVertices(v);
+ int index = 0;
+
+ while (index < adjacents.count()) {
+ AnchorVertex *next = adjacents.at(index);
+ index++;
+
+ AnchorData *data = g.edgeData(v, next);
+ const bool bothLayoutVertices = v->m_item == q && next->m_item == q;
+ const bool zeroSized = !data->minSize && !data->maxSize;
+
+ if (!bothLayoutVertices && zeroSized) {
+
+ // Create a new vertex pair, note that we keep a list of those vertices so we can
+ // easily process them when restoring the graph.
+ AnchorVertexPair *newV = new AnchorVertexPair(v, next, data);
+ simplifiedVertices[orientation].append(newV);
+
+ // Collect the anchors of both vertices, the new vertex pair will take their place
+ // in those anchors
+ const QList<AnchorVertex *> &vAdjacents = g.adjacentVertices(v);
+ const QList<AnchorVertex *> &nextAdjacents = g.adjacentVertices(next);
+
+ for (int i = 0; i < vAdjacents.count(); ++i) {
+ AnchorVertex *adjacent = vAdjacents.at(i);
+ if (adjacent != next) {
+ AnchorData *ad = g.edgeData(v, adjacent);
+ newV->m_firstAnchors.append(ad);
+ }
+ }
+
+ for (int i = 0; i < nextAdjacents.count(); ++i) {
+ AnchorVertex *adjacent = nextAdjacents.at(i);
+ if (adjacent != v) {
+ AnchorData *ad = g.edgeData(next, adjacent);
+ newV->m_secondAnchors.append(ad);
+
+ // We'll also add new vertices to the adjacent list of the new 'v', to be
+ // created as a vertex pair and replace the current one.
+ if (!adjacents.contains(adjacent))
+ adjacents.append(adjacent);
+ }
+ }
+
+ // ### merge this loop into the ones that calculated m_firstAnchors/m_secondAnchors?
+ // Make newV take the place of v and next
+ bool feasible = replaceVertex(orientation, v, newV, newV->m_firstAnchors);
+ feasible &= replaceVertex(orientation, next, newV, newV->m_secondAnchors);
+
+ // Update the layout vertex information if one of the vertices is a layout vertex.
+ AnchorVertex *layoutVertex = 0;
+ if (v->m_item == q)
+ layoutVertex = v;
+ else if (next->m_item == q)
+ layoutVertex = next;
+
+ if (layoutVertex) {
+ // Layout vertices always have m_item == q...
+ newV->m_item = q;
+ changeLayoutVertex(orientation, layoutVertex, newV);
+ }
+
+ g.takeEdge(v, next);
+
+ // If a non-feasibility is found, we leave early and cancel the simplification
+ if (!feasible)
+ return false;
+
+ v = newV;
+ visited.insert(newV);
+
+ } else if (!visited.contains(next) && !stack.contains(next)) {
+ // If the adjacent is not fit for merge and it wasn't visited by the outermost
+ // loop, we add it to the stack.
+ stack.push(next);
+ }
+ }
+ }
+
+ return true;
+}
+
+/*!
+ \internal
+
+ One iteration of the simplification algorithm. Returns true if another iteration is needed.
+
+ 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, see
+ the function comments for more details.
+*/
+bool QGraphicsAnchorLayoutPrivate::simplifyGraphIteration(QGraphicsAnchorLayoutPrivate::Orientation orientation,
+ bool *feasible)
+{
+ Q_Q(QGraphicsAnchorLayout);
+ Graph<AnchorVertex, AnchorData> &g = graph[orientation];
+
+ QSet<AnchorVertex *> visited;
+ QStack<QPair<AnchorVertex *, AnchorVertex *> > stack;
+ stack.push(qMakePair(static_cast<AnchorVertex *>(0), layoutFirstVertex[orientation]));
+ QVector<AnchorVertex*> candidates;
+
+ // Walk depth-first, in the stack we store start of the candidate sequence (beforeSequence)
+ // and the vertex to be visited.
+ while (!stack.isEmpty()) {
+ QPair<AnchorVertex *, AnchorVertex *> pair = stack.pop();
+ AnchorVertex *beforeSequence = pair.first;
+ AnchorVertex *v = pair.second;
+
+ // The basic idea is to determine whether we found an end of sequence,
+ // if that's the case, we stop adding vertices to the candidate list
+ // and do a simplification step.
+ //
+ // A vertex can trigger an end of sequence if
+ // (a) it is a layout vertex, we don't simplify away the layout vertices;
+ // (b) it does not have exactly 2 adjacents;
+ // (c) its next adjacent is already visited (a cycle in the graph).
+ // (d) the next anchor is a center anchor.
+
+ const QList<AnchorVertex *> &adjacents = g.adjacentVertices(v);
+ const bool isLayoutVertex = v->m_item == q;
+ AnchorVertex *afterSequence = v;
+ bool endOfSequence = false;
+
+ //
+ // Identify the end cases.
+ //
+
+ // Identifies cases (a) and (b)
+ endOfSequence = isLayoutVertex || adjacents.count() != 2;
+
+ if (!endOfSequence) {
+ // This is a tricky part. We peek at the next vertex to find out whether
+ //
+ // - we already visited the next vertex (c);
+ // - the next anchor is a center (d).
+ //
+ // Those are needed to identify the remaining end of sequence cases. Note that unlike
+ // (a) and (b), we preempt the end of sequence by looking into the next vertex.
+
+ // Peek at the next vertex
+ AnchorVertex *after;
+ if (candidates.isEmpty())
+ after = (beforeSequence == adjacents.last() ? adjacents.first() : adjacents.last());
+ else
+ after = (candidates.last() == adjacents.last() ? adjacents.first() : adjacents.last());
+
+ // ### At this point we assumed that candidates will not contain 'after', this may not hold
+ // when simplifying FLOATing anchors.
+ Q_ASSERT(!candidates.contains(after));
+
+ const AnchorData *data = g.edgeData(v, after);
+ Q_ASSERT(data);
+ const bool cycleFound = visited.contains(after);
+
+ // Now cases (c) and (d)...
+ endOfSequence = cycleFound || data->isCenterAnchor;
+
+ if (!endOfSequence) {
+ // If it's not an end of sequence, then the vertex didn't trigger neither of the
+ // previously three cases, so it can be added to the candidates list.
+ candidates.append(v);
+ } else if (cycleFound && (beforeSequence != after)) {
+ afterSequence = after;
+ candidates.append(v);
+ }
+ }
+
+ //
+ // Add next non-visited vertices to the stack.
+ //
+ for (int i = 0; i < adjacents.count(); ++i) {
+ AnchorVertex *next = adjacents.at(i);
+ if (visited.contains(next))
+ continue;
+
+ // If current vertex is an end of sequence, and it'll reset the candidates list. So
+ // the next vertices will build candidates lists with the current vertex as 'before'
+ // vertex. If it's not an end of sequence, we keep the original 'before' vertex,
+ // since we are keeping the candidates list.
+ if (endOfSequence)
+ stack.push(qMakePair(v, next));
+ else
+ stack.push(qMakePair(beforeSequence, next));
+ }
+
+ visited.insert(v);
+
+ if (!endOfSequence || candidates.isEmpty())
+ continue;
+
+ //
+ // Create a sequence for (beforeSequence, candidates, afterSequence).
+ //
+
+ // One restriction we have is to not simplify half of an anchor and let the other half
+ // unsimplified. So we remove center edges before and after the sequence.
+ const AnchorData *firstAnchor = g.edgeData(beforeSequence, candidates.first());
+ if (firstAnchor->isCenterAnchor) {
+ beforeSequence = candidates.first();
+ candidates.remove(0);
+
+ // If there's not candidates to be simplified, leave.
+ if (candidates.isEmpty())
+ continue;
+ }
+
+ const AnchorData *lastAnchor = g.edgeData(candidates.last(), afterSequence);
+ if (lastAnchor->isCenterAnchor) {
+ afterSequence = candidates.last();
+ candidates.remove(candidates.count() - 1);
+
+ if (candidates.isEmpty())
+ continue;
+ }
+
+ //
+ // Add the sequence to the graph.
+ //
+
+ AnchorData *sequence = createSequence(&g, beforeSequence, candidates, afterSequence);
+
+ // If 'beforeSequence' and 'afterSequence' already had an anchor between them, we'll
+ // create a parallel anchor between the new sequence and the old anchor.
+ bool newFeasible;
+ AnchorData *newAnchor = addAnchorMaybeParallel(sequence, &newFeasible);
+
+ if (!newFeasible) {
+ *feasible = false;
+ return false;
+ }
+
+ // When a new parallel anchor is create in the graph, we finish the iteration and return
+ // true to indicate a new iteration is needed. This happens because a parallel anchor
+ // changes the number of adjacents one vertex has, possibly opening up oportunities for
+ // building candidate lists (when adjacents == 2).
+ if (newAnchor != sequence)
+ return true;
+
+ // If there was no parallel simplification, we'll keep walking the graph. So we clear the
+ // candidates list to start again.
+ candidates.clear();
+ }
+
+ return false;
+}
+
+void QGraphicsAnchorLayoutPrivate::restoreSimplifiedAnchor(AnchorData *edge)
+{
+#if 0
+ static const char *anchortypes[] = {"Normal",
+ "Sequential",
+ "Parallel"};
+ qDebug("Restoring %s edge.", anchortypes[int(edge->type)]);
+#endif
+
+ Graph<AnchorVertex, AnchorData> &g = graph[edge->orientation];
+
+ if (edge->type == AnchorData::Normal) {
+ g.createEdge(edge->from, edge->to, edge);
+
+ } else if (edge->type == AnchorData::Sequential) {
+ SequentialAnchorData *sequence = static_cast<SequentialAnchorData *>(edge);
+
+ for (int i = 0; i < sequence->m_edges.count(); ++i) {
+ AnchorData *data = sequence->m_edges.at(i);
+ restoreSimplifiedAnchor(data);
+ }
+
+ delete sequence;
+
+ } else if (edge->type == AnchorData::Parallel) {
+
+ // Skip parallel anchors that were created by vertex simplification, they will be processed
+ // later, when restoring vertex simplification.
+ // ### we could improve this check bit having a bit inside 'edge'
+ if (anchorsFromSimplifiedVertices[edge->orientation].contains(edge))
+ return;
+
+ ParallelAnchorData* parallel = static_cast<ParallelAnchorData*>(edge);
+ restoreSimplifiedConstraints(parallel);
+
+ // ### Because of the way parallel anchors are created in the anchor simplification
+ // algorithm, we know that one of these will be a sequence, so it'll be safe if the other
+ // anchor create an edge between the same vertices as the parallel.
+ Q_ASSERT(parallel->firstEdge->type == AnchorData::Sequential
+ || parallel->secondEdge->type == AnchorData::Sequential);
+ restoreSimplifiedAnchor(parallel->firstEdge);
+ restoreSimplifiedAnchor(parallel->secondEdge);
+
+ delete parallel;
+ }
+}
+
+void QGraphicsAnchorLayoutPrivate::restoreSimplifiedConstraints(ParallelAnchorData *parallel)
+{
+ if (!parallel->isCenterAnchor)
+ return;
+
+ for (int i = 0; i < parallel->m_firstConstraints.count(); ++i) {
+ QSimplexConstraint *c = parallel->m_firstConstraints.at(i);
+ qreal v = c->variables[parallel];
+ c->variables.remove(parallel);
+ c->variables.insert(parallel->firstEdge, v);
+ }
+
+ // When restoring, we might have to revert constraints back. See comments on
+ // addAnchorMaybeParallel().
+ const bool needsReverse = !parallel->secondForward();
+
+ for (int i = 0; i < parallel->m_secondConstraints.count(); ++i) {
+ QSimplexConstraint *c = parallel->m_secondConstraints.at(i);
+ qreal v = c->variables[parallel];
+ if (needsReverse)
+ v *= -1;
+ c->variables.remove(parallel);
+ c->variables.insert(parallel->secondEdge, v);
+ }
+}
+
+void QGraphicsAnchorLayoutPrivate::restoreSimplifiedGraph(Orientation orientation)
+{
+#if 0
+ qDebug("Restoring Simplified Graph for %s",
+ orientation == Horizontal ? "Horizontal" : "Vertical");
+#endif
+
+ // Restore anchor simplification
+ 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);
+
+ // We restore only sequential anchors and parallels that were not created by
+ // vertex simplification.
+ if (edge->type == AnchorData::Sequential
+ || (edge->type == AnchorData::Parallel &&
+ !anchorsFromSimplifiedVertices[orientation].contains(edge))) {
+
+ g.takeEdge(v1, v2);
+ restoreSimplifiedAnchor(edge);
+ }
+ }
+
+ restoreVertices(orientation);
+}
+
+void QGraphicsAnchorLayoutPrivate::restoreVertices(Orientation orientation)
+{
+ Q_Q(QGraphicsAnchorLayout);
+
+ Graph<AnchorVertex, AnchorData> &g = graph[orientation];
+ QList<AnchorVertexPair *> &toRestore = simplifiedVertices[orientation];
+
+ // Since we keep a list of parallel anchors and vertices that were created during vertex
+ // simplification, we can now iterate on those lists instead of traversing the graph
+ // recursively.
+
+ // First, restore the constraints changed when we created parallel anchors. Note that this
+ // works at this point because the constraints doesn't depend on vertex information and at
+ // this point it's always safe to identify whether the second child is forward or backwards.
+ // In the next step, we'll change the anchors vertices so that would not be possible anymore.
+ QList<AnchorData *> &parallelAnchors = anchorsFromSimplifiedVertices[orientation];
+
+ for (int i = parallelAnchors.count() - 1; i >= 0; --i) {
+ ParallelAnchorData *parallel = static_cast<ParallelAnchorData *>(parallelAnchors.at(i));
+ restoreSimplifiedConstraints(parallel);
+ }
+
+ // Then, we will restore the vertices in the inverse order of creation, this way we ensure that
+ // the vertex being restored was not wrapped by another simplification.
+ for (int i = toRestore.count() - 1; i >= 0; --i) {
+ AnchorVertexPair *pair = toRestore.at(i);
+ QList<AnchorVertex *> adjacents = g.adjacentVertices(pair);
+
+ // Restore the removed edge, this will also restore both vertices 'first' and 'second' to
+ // the graph structure.
+ AnchorVertex *first = pair->m_first;
+ AnchorVertex *second = pair->m_second;
+ g.createEdge(first, second, pair->m_removedAnchor);
+
+ // Restore the anchors for the first child vertex
+ for (int j = 0; j < pair->m_firstAnchors.count(); ++j) {
+ AnchorData *ad = pair->m_firstAnchors.at(j);
+ Q_ASSERT(ad->from == pair || ad->to == pair);
+
+ replaceVertex_helper(ad, pair, first);
+ g.createEdge(ad->from, ad->to, ad);
+ }
+
+ // Restore the anchors for the second child vertex
+ for (int j = 0; j < pair->m_secondAnchors.count(); ++j) {
+ AnchorData *ad = pair->m_secondAnchors.at(j);
+ Q_ASSERT(ad->from == pair || ad->to == pair);
+
+ replaceVertex_helper(ad, pair, second);
+ g.createEdge(ad->from, ad->to, ad);
+ }
+
+ for (int j = 0; j < adjacents.count(); ++j) {
+ g.takeEdge(pair, adjacents.at(j));
+ }
+
+ // The pair simplified a layout vertex, so place back the correct vertex in the variable
+ // that track layout vertices
+ if (pair->m_item == q) {
+ AnchorVertex *layoutVertex = first->m_item == q ? first : second;
+ Q_ASSERT(layoutVertex->m_item == q);
+ changeLayoutVertex(orientation, pair, layoutVertex);
+ }
+
+ delete pair;
+ }
+ qDeleteAll(parallelAnchors);
+ parallelAnchors.clear();
+ toRestore.clear();
+}
+
+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;
+ addAnchor_helper(layout, Qt::AnchorLeft, layout,
+ Qt::AnchorRight, data);
+ data->maxSize = QWIDGETSIZE_MAX;
+
+ // Save a reference to layout vertices
+ layoutFirstVertex[Horizontal] = internalVertex(layout, Qt::AnchorLeft);
+ layoutCentralVertex[Horizontal] = 0;
+ layoutLastVertex[Horizontal] = internalVertex(layout, Qt::AnchorRight);
+
+ // Vertical
+ data = new AnchorData;
+ addAnchor_helper(layout, Qt::AnchorTop, layout,
+ Qt::AnchorBottom, data);
+ data->maxSize = QWIDGETSIZE_MAX;
+
+ // Save a reference to layout vertices
+ layoutFirstVertex[Vertical] = internalVertex(layout, Qt::AnchorTop);
+ layoutCentralVertex[Vertical] = 0;
+ layoutLastVertex[Vertical] = internalVertex(layout, Qt::AnchorBottom);
+}
+
+void QGraphicsAnchorLayoutPrivate::deleteLayoutEdges()
+{
+ Q_Q(QGraphicsAnchorLayout);
+
+ Q_ASSERT(!internalVertex(q, Qt::AnchorHorizontalCenter));
+ Q_ASSERT(!internalVertex(q, Qt::AnchorVerticalCenter));
+
+ removeAnchor_helper(internalVertex(q, Qt::AnchorLeft),
+ internalVertex(q, Qt::AnchorRight));
+ removeAnchor_helper(internalVertex(q, Qt::AnchorTop),
+ internalVertex(q, Qt::AnchorBottom));
+}
+
+void QGraphicsAnchorLayoutPrivate::createItemEdges(QGraphicsLayoutItem *item)
+{
+ items.append(item);
+
+ // Create horizontal and vertical internal anchors for the item and
+ // refresh its size hint / policy values.
+ AnchorData *data = new AnchorData;
+ addAnchor_helper(item, Qt::AnchorLeft, item, Qt::AnchorRight, data);
+ data->refreshSizeHints();
+
+ data = new AnchorData;
+ addAnchor_helper(item, Qt::AnchorTop, item, Qt::AnchorBottom, data);
+ data->refreshSizeHints();
+}
+
+/*!
+ \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)
+{
+ Q_Q(QGraphicsAnchorLayout);
+
+ 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;
+ }
+
+ // 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
+ QSimplexConstraint *c = new QSimplexConstraint;
+
+ AnchorData *data = new AnchorData;
+ c->variables.insert(data, 1.0);
+ addAnchor_helper(item, firstEdge, item, centerEdge, data);
+ data->isCenterAnchor = true;
+ data->dependency = AnchorData::Master;
+ data->refreshSizeHints();
+
+ data = new AnchorData;
+ c->variables.insert(data, -1.0);
+ addAnchor_helper(item, centerEdge, item, lastEdge, data);
+ data->isCenterAnchor = true;
+ data->dependency = AnchorData::Slave;
+ data->refreshSizeHints();
+
+ itemCenterConstraints[orientation].append(c);
+
+ // Remove old one
+ removeAnchor_helper(first, last);
+
+ if (item == q) {
+ layoutCentralVertex[orientation] = internalVertex(q, centerEdge);
+ }
+}
+
+void QGraphicsAnchorLayoutPrivate::removeCenterAnchors(
+ QGraphicsLayoutItem *item, Qt::AnchorPoint centerEdge,
+ bool substitute)
+{
+ Q_Q(QGraphicsAnchorLayout);
+
+ 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;
+ }
+
+ // 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].at(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 *data = new AnchorData;
+ addAnchor_helper(item, firstEdge, item, lastEdge, data);
+ data->refreshSizeHints();
+
+ // Remove old anchors
+ removeAnchor_helper(first, center);
+ removeAnchor_helper(center, internalVertex(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_helper(center, internalVertex(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_helper(first, internalVertex(item, lastEdge));
+ }
+
+ if (item == q) {
+ layoutCentralVertex[orientation] = 0;
+ }
+}
+
+
+void QGraphicsAnchorLayoutPrivate::removeCenterConstraints(QGraphicsLayoutItem *item,
+ Orientation 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].at(i)->variables.contains(internalAnchor)) {
+ delete itemCenterConstraints[orientation].takeAt(i);
+ break;
+ }
+ }
+}
+
+/*!
+ * \internal
+ * Implements the high level "addAnchor" feature. Called by the public API
+ * addAnchor method.
+ *
+ * The optional \a spacing argument defines the size of the anchor. If not provided,
+ * the anchor size is either 0 or not-set, depending on type of anchor created (see
+ * matrix below).
+ *
+ * All anchors that remain with size not-set will assume the standard spacing,
+ * set either by the layout style or through the "setSpacing" layout API.
+ */
+QGraphicsAnchor *QGraphicsAnchorLayoutPrivate::addAnchor(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 0;
+ }
+
+ if (firstItem == secondItem) {
+ qWarning("QGraphicsAnchorLayout::addAnchor(): "
+ "Cannot anchor the item to itself");
+ return 0;
+ }
+
+ if (edgeOrientation(secondEdge) != edgeOrientation(firstEdge)) {
+ qWarning("QGraphicsAnchorLayout::addAnchor(): "
+ "Cannot anchor edges of different orientations");
+ return 0;
+ }
+
+ const QGraphicsLayoutItem *parentWidget = q->parentLayoutItem();
+ if (firstItem == parentWidget || secondItem == parentWidget) {
+ qWarning("QGraphicsAnchorLayout::addAnchor(): "
+ "You cannot add the parent of the layout to the layout.");
+ return 0;
+ }
+
+ // 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)) {
+ createItemEdges(firstItem);
+ addChildLayoutItem(firstItem);
+ }
+ if (secondItem != q && !items.contains(secondItem)) {
+ 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 = new AnchorData;
+ QGraphicsAnchor *graphicsAnchor = acquireGraphicsAnchor(data);
+
+ addAnchor_helper(firstItem, firstEdge, secondItem, secondEdge, data);
+
+ if (spacing) {
+ graphicsAnchor->setSpacing(*spacing);
+ } else {
+ // 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) {
+ graphicsAnchor->setSpacing(0);
+ } else {
+ graphicsAnchor->unsetSpacing();
+ }
+ }
+
+ return graphicsAnchor;
+}
+
+/*
+ \internal
+
+ This method adds an AnchorData to the internal graph. It is responsible for doing
+ the boilerplate part of such task.
+
+ If another AnchorData exists between the mentioned vertices, it is deleted and
+ the new one is inserted.
+*/
+void QGraphicsAnchorLayoutPrivate::addAnchor_helper(QGraphicsLayoutItem *firstItem,
+ Qt::AnchorPoint firstEdge,
+ QGraphicsLayoutItem *secondItem,
+ Qt::AnchorPoint secondEdge,
+ AnchorData *data)
+{
+ Q_Q(QGraphicsAnchorLayout);
+
+ const Orientation orientation = edgeOrientation(firstEdge);
+
+ // Create or increase the reference count for the related vertices.
+ AnchorVertex *v1 = addInternalVertex(firstItem, firstEdge);
+ AnchorVertex *v2 = addInternalVertex(secondItem, secondEdge);
+
+ // Remove previous anchor
+ if (graph[orientation].edgeData(v1, v2)) {
+ removeAnchor_helper(v1, v2);
+ }
+
+ // If its an internal anchor, set the associated item
+ if (firstItem == secondItem)
+ data->item = firstItem;
+
+ data->orientation = orientation;
+
+ // 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
+ // ### bit to track internal anchors, since inside AnchorData methods
+ // we don't have access to the 'q' pointer.
+ data->isLayoutAnchor = (data->item == q);
+
+ graph[orientation].createEdge(v1, v2, data);
+}
+
+QGraphicsAnchor *QGraphicsAnchorLayoutPrivate::getAnchor(QGraphicsLayoutItem *firstItem,
+ Qt::AnchorPoint firstEdge,
+ QGraphicsLayoutItem *secondItem,
+ Qt::AnchorPoint secondEdge)
+{
+ // Do not expose internal anchors
+ if (firstItem == secondItem)
+ return 0;
+
+ const Orientation orientation = edgeOrientation(firstEdge);
+ AnchorVertex *v1 = internalVertex(firstItem, firstEdge);
+ AnchorVertex *v2 = internalVertex(secondItem, secondEdge);
+
+ QGraphicsAnchor *graphicsAnchor = 0;
+
+ AnchorData *data = graph[orientation].edgeData(v1, v2);
+ if (data) {
+ // We could use "acquireGraphicsAnchor" here, but to avoid a regression where
+ // an internal anchor was wrongly exposed, I want to ensure no new
+ // QGraphicsAnchor instances are created by this call.
+ // This assumption must hold because anchors are either user-created (and already
+ // have their public object created), or they are internal (and must not reach
+ // this point).
+ Q_ASSERT(data->graphicsAnchor);
+ graphicsAnchor = data->graphicsAnchor;
+ }
+ return graphicsAnchor;
+}
+
+/*!
+ * \internal
+ *
+ * Implements the high level "removeAnchor" feature. Called by
+ * the QAnchorData destructor.
+ */
+void QGraphicsAnchorLayoutPrivate::removeAnchor(AnchorVertex *firstVertex,
+ AnchorVertex *secondVertex)
+{
+ Q_Q(QGraphicsAnchorLayout);
+
+ // Save references to items while it's safe to assume the vertices exist
+ QGraphicsLayoutItem *firstItem = firstVertex->m_item;
+ QGraphicsLayoutItem *secondItem = secondVertex->m_item;
+
+ // Delete the anchor (may trigger deletion of center vertices)
+ removeAnchor_helper(firstVertex, secondVertex);
+
+ // Ensure no dangling pointer is left behind
+ firstVertex = secondVertex = 0;
+
+ // Checking if the item stays in the layout or not
+ bool keepFirstItem = false;
+ bool keepSecondItem = false;
+
+ QPair<AnchorVertex *, int> v;
+ int refcount = -1;
+
+ if (firstItem != q) {
+ for (int i = Qt::AnchorLeft; i <= Qt::AnchorBottom; ++i) {
+ v = m_vertexList.value(qMakePair(firstItem, static_cast<Qt::AnchorPoint>(i)));
+ if (v.first) {
+ if (i == Qt::AnchorHorizontalCenter || i == Qt::AnchorVerticalCenter)
+ refcount = 2;
+ else
+ refcount = 1;
+
+ if (v.second > refcount) {
+ keepFirstItem = true;
+ break;
+ }
+ }
+ }
+ } else
+ keepFirstItem = true;
+
+ if (secondItem != q) {
+ for (int i = Qt::AnchorLeft; i <= Qt::AnchorBottom; ++i) {
+ v = m_vertexList.value(qMakePair(secondItem, static_cast<Qt::AnchorPoint>(i)));
+ if (v.first) {
+ if (i == Qt::AnchorHorizontalCenter || i == Qt::AnchorVerticalCenter)
+ refcount = 2;
+ else
+ refcount = 1;
+
+ if (v.second > refcount) {
+ keepSecondItem = true;
+ break;
+ }
+ }
+ }
+ } else
+ keepSecondItem = true;
+
+ if (!keepFirstItem)
+ q->removeAt(items.indexOf(firstItem));
+
+ if (!keepSecondItem)
+ q->removeAt(items.indexOf(secondItem));
+
+ // Removing anchors invalidates the layout
+ q->invalidate();
+}
+
+/*
+ \internal
+
+ Implements the low level "removeAnchor" feature. Called by
+ private methods.
+*/
+void QGraphicsAnchorLayoutPrivate::removeAnchor_helper(AnchorVertex *v1, AnchorVertex *v2)
+{
+ Q_ASSERT(v1 && v2);
+
+ // Remove edge from graph
+ const Orientation o = edgeOrientation(v1->m_edge);
+ graph[o].removeEdge(v1, v2);
+
+ // Decrease vertices reference count (may trigger a deletion)
+ removeInternalVertex(v1->m_item, v1->m_edge);
+ removeInternalVertex(v2->m_item, v2->m_edge);
+}
+
+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)
+{
+ // 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);
+
+ if ((firstItem != q) && (secondItem != q)) {
+ // If connection is between widgets (not the layout itself)
+ // Ensure that "right-edges" sit to the left of "left-edges".
+ if (firstEdge < secondEdge) {
+ qSwap(firstItem, secondItem);
+ qSwap(firstEdge, secondEdge);
+ }
+ } else if (firstItem == q) {
+ // If connection involves the right or bottom of a layout, ensure
+ // the layout is the second item.
+ if ((firstEdge == Qt::AnchorRight) || (firstEdge == Qt::AnchorBottom)) {
+ qSwap(firstItem, secondItem);
+ qSwap(firstEdge, secondEdge);
+ }
+ } else if ((secondEdge != Qt::AnchorRight) && (secondEdge != Qt::AnchorBottom)) {
+ // If connection involves the left, center or top of layout, ensure
+ // the layout is the first item.
+ qSwap(firstItem, secondItem);
+ qSwap(firstEdge, secondEdge);
+ }
+}
+
+QLayoutStyleInfo &QGraphicsAnchorLayoutPrivate::styleInfo() const
+{
+ if (styleInfoDirty) {
+ Q_Q(const QGraphicsAnchorLayout);
+ //### Fix this if QGV ever gets support for Metal style or different Aqua sizes.
+ QWidget *wid = 0;
+
+ QGraphicsLayoutItem *parent = q->parentLayoutItem();
+ while (parent && parent->isLayout()) {
+ parent = parent->parentLayoutItem();
+ }
+ QGraphicsWidget *w = 0;
+ if (parent) {
+ QGraphicsItem *parentItem = parent->graphicsItem();
+ if (parentItem && parentItem->isWidget())
+ w = static_cast<QGraphicsWidget*>(parentItem);
+ }
+
+ QStyle *style = w ? w->style() : QApplication::style();
+ cachedStyleInfo = QLayoutStyleInfo(style, wid);
+ cachedStyleInfo.setDefaultSpacing(Qt::Horizontal, spacings[0]);
+ cachedStyleInfo.setDefaultSpacing(Qt::Vertical, spacings[1]);
+
+ styleInfoDirty = false;
+ }
+ return cachedStyleInfo;
+}
+
+/*!
+ \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 = false;
+}
+
+// ### Maybe getGraphParts could return the variables when traversing, at least
+// for trunk...
+QList<AnchorData *> getVariables(QList<QSimplexConstraint *> constraints)
+{
+ QSet<AnchorData *> variableSet;
+ for (int i = 0; i < constraints.count(); ++i) {
+ const QSimplexConstraint *c = constraints.at(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) Refresh 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)
+{
+#if defined(QT_DEBUG) || defined(Q_AUTOTEST_EXPORT)
+ lastCalculationUsedSimplex[orientation] = false;
+#endif
+
+ static bool simplificationEnabled = qgetenv("QT_ANCHORLAYOUT_NO_SIMPLIFICATION").isEmpty();
+
+ // Reset the nominal sizes of each anchor based on the current item sizes
+ refreshAllSizeHints(orientation);
+
+ // Simplify the graph
+ if (simplificationEnabled && !simplifyGraph(orientation)) {
+ qWarning("QGraphicsAnchorLayout: anchor setup is not feasible.");
+ graphHasConflicts[orientation] = true;
+ return;
+ }
+
+ // 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 = 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.at(0);
+ QList<AnchorData *> trunkVariables = getVariables(trunkConstraints);
+
+ // For minimum and maximum, use the path between the two layout sides as the
+ // objective function.
+ AnchorVertex *v = layoutLastVertex[orientation];
+ GraphPath trunkPath = graphPaths[orientation].value(v);
+
+ bool feasible = calculateTrunk(orientation, trunkPath, trunkConstraints, trunkVariables);
+
+ // 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.
+
+ // Skipping the first (trunk)
+ for (int i = 1; i < parts.count(); ++i) {
+ if (!feasible)
+ break;
+
+ QList<QSimplexConstraint *> partConstraints = parts.at(i);
+ QList<AnchorData *> partVariables = getVariables(partConstraints);
+ Q_ASSERT(!partVariables.isEmpty());
+ feasible &= calculateNonTrunk(partConstraints, partVariables);
+ }
+
+ // Propagate the new sizes down the simplified graph, ie. tell the
+ // group anchors to set their children anchors sizes.
+ updateAnchorSizes(orientation);
+
+ graphHasConflicts[orientation] = !feasible;
+
+ // Clean up our data structures. They are not needed anymore since
+ // distribution uses just interpolation.
+ qDeleteAll(constraints[orientation]);
+ constraints[orientation].clear();
+ graphPaths[orientation].clear(); // ###
+
+ if (simplificationEnabled)
+ restoreSimplifiedGraph(orientation);
+}
+
+/*!
+ \internal
+
+ Shift all the constraints by a certain amount. This allows us to deal with negative values in
+ the linear program if they are bounded by a certain limit. Functions should be careful to
+ call it again with a negative amount, to shift the constraints back.
+*/
+static void shiftConstraints(const QList<QSimplexConstraint *> &constraints, qreal amount)
+{
+ for (int i = 0; i < constraints.count(); ++i) {
+ QSimplexConstraint *c = constraints.at(i);
+ qreal multiplier = 0;
+ foreach (qreal v, c->variables.values()) {
+ multiplier += v;
+ }
+ c->constant += multiplier * amount;
+ }
+}
+
+/*!
+ \internal
+
+ Calculate the sizes for all anchors which are part of the trunk. This works
+ on top of a (possibly) simplified graph.
+*/
+bool QGraphicsAnchorLayoutPrivate::calculateTrunk(Orientation orientation, const GraphPath &path,
+ const QList<QSimplexConstraint *> &constraints,
+ const QList<AnchorData *> &variables)
+{
+ bool feasible = true;
+ bool needsSimplex = !constraints.isEmpty();
+
+#if 0
+ qDebug("Simplex %s for trunk of %s", needsSimplex ? "used" : "NOT used",
+ orientation == Horizontal ? "Horizontal" : "Vertical");
+#endif
+
+ if (needsSimplex) {
+
+ QList<QSimplexConstraint *> sizeHintConstraints = constraintsFromSizeHints(variables);
+ QList<QSimplexConstraint *> allConstraints = constraints + sizeHintConstraints;
+
+ shiftConstraints(allConstraints, g_offset);
+
+ // Solve min and max size hints
+ qreal min, max;
+ feasible = solveMinMax(allConstraints, path, &min, &max);
+
+ if (feasible) {
+ solvePreferred(constraints, variables);
+
+ // 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, path.positives) {
+ pref += ad->sizeAtPreferred;
+ }
+ foreach (const AnchorData *ad, path.negatives) {
+ pref -= ad->sizeAtPreferred;
+ }
+
+ sizeHints[orientation][Qt::MinimumSize] = min;
+ sizeHints[orientation][Qt::PreferredSize] = pref;
+ sizeHints[orientation][Qt::MaximumSize] = max;
+ }
+
+ qDeleteAll(sizeHintConstraints);
+ shiftConstraints(constraints, -g_offset);
+
+ } else {
+ // No Simplex is necessary because the path was simplified all the way to a single
+ // anchor.
+ Q_ASSERT(path.positives.count() == 1);
+ Q_ASSERT(path.negatives.count() == 0);
+
+ AnchorData *ad = path.positives.toList()[0];
+ ad->sizeAtMinimum = ad->minSize;
+ ad->sizeAtPreferred = ad->prefSize;
+ ad->sizeAtMaximum = ad->maxSize;
+
+ sizeHints[orientation][Qt::MinimumSize] = ad->sizeAtMinimum;
+ sizeHints[orientation][Qt::PreferredSize] = ad->sizeAtPreferred;
+ sizeHints[orientation][Qt::MaximumSize] = ad->sizeAtMaximum;
+ }
+
+#if defined(QT_DEBUG) || defined(Q_AUTOTEST_EXPORT)
+ lastCalculationUsedSimplex[orientation] = needsSimplex;
+#endif
+
+ return feasible;
+}
+
+/*!
+ \internal
+*/
+bool QGraphicsAnchorLayoutPrivate::calculateNonTrunk(const QList<QSimplexConstraint *> &constraints,
+ const QList<AnchorData *> &variables)
+{
+ shiftConstraints(constraints, g_offset);
+ bool feasible = solvePreferred(constraints, variables);
+
+ if (feasible) {
+ // Propagate size at preferred to other sizes. Semi-floats always will be
+ // in their sizeAtPreferred.
+ for (int j = 0; j < variables.count(); ++j) {
+ AnchorData *ad = variables.at(j);
+ Q_ASSERT(ad);
+ ad->sizeAtMinimum = ad->sizeAtPreferred;
+ ad->sizeAtMaximum = ad->sizeAtPreferred;
+ }
+ }
+
+ shiftConstraints(constraints, -g_offset);
+ return feasible;
+}
+
+/*!
+ \internal
+
+ Traverse the graph refreshing the size hints. Edges will query their associated
+ item or graphicsAnchor for their size hints.
+*/
+void QGraphicsAnchorLayoutPrivate::refreshAllSizeHints(Orientation orientation)
+{
+ Graph<AnchorVertex, AnchorData> &g = graph[orientation];
+ QList<QPair<AnchorVertex *, AnchorVertex *> > vertices = g.connections();
+
+ QLayoutStyleInfo styleInf = styleInfo();
+ for (int i = 0; i < vertices.count(); ++i) {
+ AnchorData *data = g.edgeData(vertices.at(i).first, vertices.at(i).second);
+ data->refreshSizeHints(&styleInf);
+ }
+}
+
+/*!
+ \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 = layoutFirstVertex[orientation];
+
+ 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));
+ }
+ }
+
+ // We will walk through every reachable items (non-float) store them in a temporary set.
+ // We them create a set of all items and subtract the non-floating items from the set in
+ // order to get the floating items. The floating items is then stored in m_floatItems
+ identifyFloatItems(visited, orientation);
+}
+
+/*!
+ \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.at(i));
+ }
+ }
+}
+
+/*!
+ \internal
+*/
+void QGraphicsAnchorLayoutPrivate::updateAnchorSizes(Orientation orientation)
+{
+ Graph<AnchorVertex, AnchorData> &g = graph[orientation];
+ const QList<QPair<AnchorVertex *, AnchorVertex *> > &vertices = g.connections();
+
+ for (int i = 0; i < vertices.count(); ++i) {
+ AnchorData *ad = g.edgeData(vertices.at(i).first, vertices.at(i).second);
+ ad->updateChildrenSizes();
+ }
+}
+
+/*!
+ \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)
+{
+ if (anchors.isEmpty())
+ return QList<QSimplexConstraint *>();
+
+ // Look for the layout edge. That can be either the first half in case the
+ // layout is split in two, or the whole layout anchor.
+ Orientation orient = Orientation(anchors.first()->orientation);
+ AnchorData *layoutEdge = 0;
+ if (layoutCentralVertex[orient]) {
+ layoutEdge = graph[orient].edgeData(layoutFirstVertex[orient], layoutCentralVertex[orient]);
+ } else {
+ layoutEdge = graph[orient].edgeData(layoutFirstVertex[orient], layoutLastVertex[orient]);
+ }
+
+ // If maxSize is less then "infinite", that means there are other anchors
+ // grouped together with this one. We can't ignore its maximum value so we
+ // set back the variable to NULL to prevent the continue condition from being
+ // satisfied in the loop below.
+ const qreal expectedMax = layoutCentralVertex[orient] ? QWIDGETSIZE_MAX / 2 : QWIDGETSIZE_MAX;
+ qreal actualMax;
+ if (layoutEdge->from == layoutFirstVertex[orient]) {
+ actualMax = layoutEdge->maxSize;
+ } else {
+ actualMax = -layoutEdge->minSize;
+ }
+ if (actualMax != expectedMax) {
+ layoutEdge = 0;
+ }
+
+ // For each variable, create constraints based on size hints
+ QList<QSimplexConstraint *> anchorConstraints;
+ bool unboundedProblem = true;
+ for (int i = 0; i < anchors.size(); ++i) {
+ AnchorData *ad = anchors.at(i);
+
+ // Anchors that have their size directly linked to another one don't need constraints
+ // For exammple, the second half of an item has exactly the same size as the first half
+ // thus constraining the latter is enough.
+ if (ad->dependency == AnchorData::Slave)
+ continue;
+
+ // To use negative variables inside simplex, we shift them so the minimum negative value is
+ // mapped to zero before solving. To make sure that it works, we need to guarantee that the
+ // variables are all inside a certain boundary.
+ qreal boundedMin = qBound(-g_offset, ad->minSize, g_offset);
+ qreal boundedMax = qBound(-g_offset, ad->maxSize, g_offset);
+
+ if ((boundedMin == boundedMax) || qFuzzyCompare(boundedMin, boundedMax)) {
+ QSimplexConstraint *c = new QSimplexConstraint;
+ c->variables.insert(ad, 1.0);
+ c->constant = boundedMin;
+ c->ratio = QSimplexConstraint::Equal;
+ anchorConstraints += c;
+ unboundedProblem = false;
+ } else {
+ QSimplexConstraint *c = new QSimplexConstraint;
+ c->variables.insert(ad, 1.0);
+ c->constant = boundedMin;
+ c->ratio = QSimplexConstraint::MoreOrEqual;
+ anchorConstraints += c;
+
+ // We avoid adding restrictions to the layout internal anchors. That's
+ // to prevent unnecessary fair distribution from happening due to this
+ // artificial restriction.
+ if (ad == layoutEdge)
+ continue;
+
+ c = new QSimplexConstraint;
+ c->variables.insert(ad, 1.0);
+ c->constant = boundedMax;
+ c->ratio = QSimplexConstraint::LessOrEqual;
+ anchorConstraints += c;
+ unboundedProblem = false;
+ }
+ }
+
+ // If no upper boundary restriction was added, add one to avoid unbounded problem
+ if (unboundedProblem) {
+ QSimplexConstraint *c = new QSimplexConstraint;
+ c->variables.insert(layoutEdge, 1.0);
+ // The maximum size that the layout can take
+ c->constant = g_offset;
+ c->ratio = QSimplexConstraint::LessOrEqual;
+ anchorConstraints += c;
+ }
+
+ return anchorConstraints;
+}
+
+/*!
+ \internal
+*/
+QList< QList<QSimplexConstraint *> >
+QGraphicsAnchorLayoutPrivate::getGraphParts(Orientation orientation)
+{
+ Q_ASSERT(layoutFirstVertex[orientation] && layoutLastVertex[orientation]);
+
+ AnchorData *edgeL1 = 0;
+ AnchorData *edgeL2 = 0;
+
+ // The layout may have a single anchor between Left and Right or two half anchors
+ // passing through the center
+ if (layoutCentralVertex[orientation]) {
+ edgeL1 = graph[orientation].edgeData(layoutFirstVertex[orientation], layoutCentralVertex[orientation]);
+ edgeL2 = graph[orientation].edgeData(layoutCentralVertex[orientation], layoutLastVertex[orientation]);
+ } else {
+ edgeL1 = graph[orientation].edgeData(layoutFirstVertex[orientation], layoutLastVertex[orientation]);
+ }
+
+ QLinkedList<QSimplexConstraint *> remainingConstraints;
+ for (int i = 0; i < constraints[orientation].count(); ++i) {
+ remainingConstraints += constraints[orientation].at(i);
+ }
+ for (int i = 0; i < itemCenterConstraints[orientation].count(); ++i) {
+ remainingConstraints += itemCenterConstraints[orientation].at(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 all visited Anchors on findPaths() so we can identify non-float Items.
+*/
+void QGraphicsAnchorLayoutPrivate::identifyFloatItems(const QSet<AnchorData *> &visited, Orientation orientation)
+{
+ QSet<QGraphicsLayoutItem *> nonFloating;
+
+ foreach (const AnchorData *ad, visited)
+ identifyNonFloatItems_helper(ad, &nonFloating);
+
+ QSet<QGraphicsLayoutItem *> allItems;
+ foreach (QGraphicsLayoutItem *item, items)
+ allItems.insert(item);
+ m_floatItems[orientation] = allItems - nonFloating;
+}
+
+
+/*!
+ \internal
+
+ Given an anchor, if it is an internal anchor and Normal we must mark it's item as non-float.
+ If the anchor is Sequential or Parallel, we must iterate on its children recursively until we reach
+ internal anchors (items).
+*/
+void QGraphicsAnchorLayoutPrivate::identifyNonFloatItems_helper(const AnchorData *ad, QSet<QGraphicsLayoutItem *> *nonFloatingItemsIdentifiedSoFar)
+{
+ Q_Q(QGraphicsAnchorLayout);
+
+ switch(ad->type) {
+ case AnchorData::Normal:
+ if (ad->item && ad->item != q)
+ nonFloatingItemsIdentifiedSoFar->insert(ad->item);
+ break;
+ case AnchorData::Sequential:
+ foreach (const AnchorData *d, static_cast<const SequentialAnchorData *>(ad)->m_edges)
+ identifyNonFloatItems_helper(d, nonFloatingItemsIdentifiedSoFar);
+ break;
+ case AnchorData::Parallel:
+ identifyNonFloatItems_helper(static_cast<const ParallelAnchorData *>(ad)->firstEdge, nonFloatingItemsIdentifiedSoFar);
+ identifyNonFloatItems_helper(static_cast<const ParallelAnchorData *>(ad)->secondEdge, nonFloatingItemsIdentifiedSoFar);
+ break;
+ }
+}
+
+/*!
+ \internal
+
+ Use the current vertices distance to calculate and set the geometry of
+ each item.
+*/
+void QGraphicsAnchorLayoutPrivate::setItemsGeometries(const QRectF &geom)
+{
+ Q_Q(QGraphicsAnchorLayout);
+ AnchorVertex *firstH, *secondH, *firstV, *secondV;
+
+ qreal top;
+ qreal left;
+ qreal right;
+
+ q->getContentsMargins(&left, &top, &right, 0);
+ const Qt::LayoutDirection visualDir = visualDirection();
+ if (visualDir == Qt::RightToLeft)
+ qSwap(left, right);
+
+ left += geom.left();
+ top += geom.top();
+ right = geom.right() - right;
+
+ foreach (QGraphicsLayoutItem *item, items) {
+ QRectF newGeom;
+ QSizeF itemPreferredSize = item->effectiveSizeHint(Qt::PreferredSize);
+ if (m_floatItems[Horizontal].contains(item)) {
+ newGeom.setLeft(0);
+ newGeom.setRight(itemPreferredSize.width());
+ } else {
+ firstH = internalVertex(item, Qt::AnchorLeft);
+ secondH = internalVertex(item, Qt::AnchorRight);
+
+ if (visualDir == Qt::LeftToRight) {
+ newGeom.setLeft(left + firstH->distance);
+ newGeom.setRight(left + secondH->distance);
+ } else {
+ newGeom.setLeft(right - secondH->distance);
+ newGeom.setRight(right - firstH->distance);
+ }
+ }
+
+ if (m_floatItems[Vertical].contains(item)) {
+ newGeom.setTop(0);
+ newGeom.setBottom(itemPreferredSize.height());
+ } else {
+ firstV = internalVertex(item, Qt::AnchorTop);
+ secondV = internalVertex(item, Qt::AnchorBottom);
+
+ newGeom.setTop(top + firstV->distance);
+ newGeom.setBottom(top + secondV->distance);
+ }
+
+ item->setGeometry(newGeom);
+ }
+}
+
+/*!
+ \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)
+{
+ QQueue<QPair<AnchorVertex *, AnchorVertex *> > queue;
+ QSet<AnchorVertex *> visited;
+
+ // Get root vertex
+ AnchorVertex *root = layoutFirstVertex[orientation];
+
+ root->distance = 0;
+ 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
+ while (!queue.isEmpty()) {
+ QPair<AnchorVertex *, AnchorVertex *> pair = queue.dequeue();
+ AnchorData *edge = graph[orientation].edgeData(pair.first, pair.second);
+
+ if (visited.contains(pair.second))
+ continue;
+
+ visited.insert(pair.second);
+ interpolateEdge(pair.first, edge);
+
+ 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 be called once before calling "interpolateEdgeSize()" for
+ the edges.
+*/
+void QGraphicsAnchorLayoutPrivate::setupEdgesInterpolation(
+ Orientation orientation)
+{
+ Q_Q(QGraphicsAnchorLayout);
+
+ qreal current;
+ current = (orientation == Horizontal) ? q->contentsRect().width() : q->contentsRect().height();
+
+ QPair<Interval, qreal> result;
+ result = getFactor(current,
+ sizeHints[orientation][Qt::MinimumSize],
+ sizeHints[orientation][Qt::PreferredSize],
+ sizeHints[orientation][Qt::PreferredSize],
+ sizeHints[orientation][Qt::PreferredSize],
+ sizeHints[orientation][Qt::MaximumSize]);
+
+ interpolationInterval[orientation] = result.first;
+ interpolationProgress[orientation] = result.second;
+}
+
+/*!
+ \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,
+ - preferred size,
+ - 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.
+*/
+void QGraphicsAnchorLayoutPrivate::interpolateEdge(AnchorVertex *base, AnchorData *edge)
+{
+ const Orientation orientation = Orientation(edge->orientation);
+ const QPair<Interval, qreal> factor(interpolationInterval[orientation],
+ interpolationProgress[orientation]);
+
+ qreal edgeDistance = interpolate(factor, edge->sizeAtMinimum, edge->sizeAtPreferred,
+ edge->sizeAtPreferred, edge->sizeAtPreferred,
+ edge->sizeAtMaximum);
+
+ Q_ASSERT(edge->from == base || edge->to == base);
+
+ // Calculate the distance for the vertex opposite to the base
+ if (edge->from == base) {
+ edge->to->distance = base->distance + edgeDistance;
+ } else {
+ edge->from->distance = base->distance - edgeDistance;
+ }
+}
+
+bool QGraphicsAnchorLayoutPrivate::solveMinMax(const QList<QSimplexConstraint *> &constraints,
+ GraphPath path, qreal *min, qreal *max)
+{
+ QSimplex simplex;
+ bool feasible = simplex.setConstraints(constraints);
+ if (feasible) {
+ // 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);
+
+ const qreal objectiveOffset = (path.positives.count() - path.negatives.count()) * g_offset;
+ simplex.setObjective(&objective);
+
+ // Calculate minimum values
+ *min = simplex.solveMin() - objectiveOffset;
+
+ // Save sizeAtMinimum results
+ QList<AnchorData *> variables = getVariables(constraints);
+ for (int i = 0; i < variables.size(); ++i) {
+ AnchorData *ad = static_cast<AnchorData *>(variables.at(i));
+ ad->sizeAtMinimum = ad->result - g_offset;
+ }
+
+ // Calculate maximum values
+ *max = simplex.solveMax() - objectiveOffset;
+
+ // Save sizeAtMaximum results
+ for (int i = 0; i < variables.size(); ++i) {
+ AnchorData *ad = static_cast<AnchorData *>(variables.at(i));
+ ad->sizeAtMaximum = ad->result - g_offset;
+ }
+ }
+ return feasible;
+}
+
+enum slackType { Grower = -1, Shrinker = 1 };
+static QPair<QSimplexVariable *, QSimplexConstraint *> createSlack(QSimplexConstraint *sizeConstraint,
+ qreal interval, slackType type)
+{
+ QSimplexVariable *slack = new QSimplexVariable;
+ sizeConstraint->variables.insert(slack, type);
+
+ QSimplexConstraint *limit = new QSimplexConstraint;
+ limit->variables.insert(slack, 1.0);
+ limit->ratio = QSimplexConstraint::LessOrEqual;
+ limit->constant = interval;
+
+ return qMakePair(slack, limit);
+}
+
+bool QGraphicsAnchorLayoutPrivate::solvePreferred(const QList<QSimplexConstraint *> &constraints,
+ const QList<AnchorData *> &variables)
+{
+ 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_shrinker_hard + A_grower_hard + B_shrinker_hard + B_grower_hard + ...) +
+ // (A_shrinker_soft + A_grower_soft + B_shrinker_soft + B_grower_soft + ...)
+ //
+ // 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_hard + A_shrinker_soft - A_grower_hard - A_grower_soft = A_pref
+ //
+ for (int i = 0; i < variables.size(); ++i) {
+ AnchorData *ad = variables.at(i);
+
+ // The layout original structure anchors are not relevant in preferred size calculation
+ if (ad->isLayoutAnchor)
+ continue;
+
+ // By default, all variables are equal to their preferred size. If they have room to
+ // grow or shrink, such flexibility will be added by the additional variables below.
+ QSimplexConstraint *sizeConstraint = new QSimplexConstraint;
+ preferredConstraints += sizeConstraint;
+ sizeConstraint->variables.insert(ad, 1.0);
+ sizeConstraint->constant = ad->prefSize + g_offset;
+
+ // Can easily shrink
+ QPair<QSimplexVariable *, QSimplexConstraint *> slack;
+ const qreal softShrinkInterval = ad->prefSize - ad->minPrefSize;
+ if (softShrinkInterval) {
+ slack = createSlack(sizeConstraint, softShrinkInterval, Shrinker);
+ preferredVariables += slack.first;
+ preferredConstraints += slack.second;
+
+ // Add to objective with ratio == 1 (soft)
+ objective.variables.insert(slack.first, 1.0);
+ }
+
+ // Can easily grow
+ const qreal softGrowInterval = ad->maxPrefSize - ad->prefSize;
+ if (softGrowInterval) {
+ slack = createSlack(sizeConstraint, softGrowInterval, Grower);
+ preferredVariables += slack.first;
+ preferredConstraints += slack.second;
+
+ // Add to objective with ratio == 1 (soft)
+ objective.variables.insert(slack.first, 1.0);
+ }
+
+ // Can shrink if really necessary
+ const qreal hardShrinkInterval = ad->minPrefSize - ad->minSize;
+ if (hardShrinkInterval) {
+ slack = createSlack(sizeConstraint, hardShrinkInterval, Shrinker);
+ preferredVariables += slack.first;
+ preferredConstraints += slack.second;
+
+ // Add to objective with ratio == N (hard)
+ objective.variables.insert(slack.first, variables.size());
+ }
+
+ // Can grow if really necessary
+ const qreal hardGrowInterval = ad->maxSize - ad->maxPrefSize;
+ if (hardGrowInterval) {
+ slack = createSlack(sizeConstraint, hardGrowInterval, Grower);
+ preferredVariables += slack.first;
+ preferredConstraints += slack.second;
+
+ // Add to objective with ratio == N (hard)
+ objective.variables.insert(slack.first, variables.size());
+ }
+ }
+
+ QSimplex *simplex = new QSimplex;
+ bool feasible = simplex->setConstraints(constraints + preferredConstraints);
+ if (feasible) {
+ simplex->setObjective(&objective);
+
+ // Calculate minimum values
+ simplex->solveMin();
+
+ // Save sizeAtPreferred results
+ for (int i = 0; i < variables.size(); ++i) {
+ AnchorData *ad = variables.at(i);
+ ad->sizeAtPreferred = ad->result - g_offset;
+ }
+
+ // 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);
+
+ return feasible;
+}
+
+/*!
+ \internal
+ Returns true if there are no arrangement that satisfies all constraints.
+ Otherwise returns false.
+
+ \sa addAnchor()
+*/
+bool QGraphicsAnchorLayoutPrivate::hasConflicts() const
+{
+ QGraphicsAnchorLayoutPrivate *that = const_cast<QGraphicsAnchorLayoutPrivate*>(this);
+ that->calculateGraphs();
+
+ bool floatConflict = !m_floatItems[0].isEmpty() || !m_floatItems[1].isEmpty();
+
+ return graphHasConflicts[0] || graphHasConflicts[1] || floatConflict;
+}
+
+#ifdef QT_DEBUG
+void QGraphicsAnchorLayoutPrivate::dumpGraph(const QString &name)
+{
+ QFile file(QString::fromAscii("anchorlayout.%1.dot").arg(name));
+ 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
+#endif //QT_NO_GRAPHICSVIEW
diff --git a/src/widgets/graphicsview/qgraphicsanchorlayout_p.h b/src/widgets/graphicsview/qgraphicsanchorlayout_p.h
new file mode 100644
index 0000000000..9a91c3c8df
--- /dev/null
+++ b/src/widgets/graphicsview/qgraphicsanchorlayout_p.h
@@ -0,0 +1,594 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** 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 Technology Preview License Agreement accompanying
+** this package.
+**
+** 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.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QGRAPHICSANCHORLAYOUT_P_H
+#define QGRAPHICSANCHORLAYOUT_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QGraphicsWidget>
+#include <private/qobject_p.h>
+
+#include "qgraphicslayout_p.h"
+#include "qgraphicsanchorlayout.h"
+#include "qgraph_p.h"
+#include "qsimplex_p.h"
+#ifndef QT_NO_GRAPHICSVIEW
+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 {
+ enum Type {
+ Normal = 0,
+ Pair
+ };
+
+ AnchorVertex(QGraphicsLayoutItem *item, Qt::AnchorPoint edge)
+ : m_item(item), m_edge(edge), m_type(Normal) {}
+
+ AnchorVertex()
+ : m_item(0), m_edge(Qt::AnchorPoint(0)), m_type(Normal) {}
+
+#ifdef QT_DEBUG
+ inline QString toString() const;
+#endif
+
+ QGraphicsLayoutItem *m_item;
+ Qt::AnchorPoint m_edge;
+ uint m_type : 1;
+
+ // Current distance from this vertex to the layout edge (Left or Top)
+ // Value is calculated from the current anchors sizes.
+ qreal distance;
+};
+
+/*!
+ \internal
+
+ Represents an edge (anchor) in the internal graph.
+*/
+struct AnchorData : public QSimplexVariable {
+ enum Type {
+ Normal = 0,
+ Sequential,
+ Parallel
+ };
+
+ enum Dependency {
+ Independent = 0,
+ Master,
+ Slave
+ };
+
+ AnchorData()
+ : QSimplexVariable(), from(0), to(0),
+ minSize(0), prefSize(0), maxSize(0),
+ minPrefSize(0), maxPrefSize(0),
+ sizeAtMinimum(0), sizeAtPreferred(0),
+ sizeAtMaximum(0), item(0), graphicsAnchor(0),
+ type(Normal), isLayoutAnchor(false),
+ isCenterAnchor(false), orientation(0),
+ dependency(Independent) {}
+ virtual ~AnchorData();
+
+ virtual void updateChildrenSizes() {}
+ void refreshSizeHints(const QLayoutStyleInfo *styleInfo = 0);
+
+#ifdef QT_DEBUG
+ void dump(int indent = 2);
+ inline QString toString() const;
+ QString name;
+#endif
+
+ // Anchor is semantically directed
+ AnchorVertex *from;
+ AnchorVertex *to;
+
+ // Nominal sizes
+ // These are the intrinsic size restrictions for a given item. They are
+ // used as input for the calculation of the actual sizes.
+ // These values are filled by the refreshSizeHints method, based on the
+ // anchor size policy, the size hints of the item it (possibly) represents
+ // and the layout spacing information.
+ qreal minSize;
+ qreal prefSize;
+ qreal maxSize;
+
+ qreal minPrefSize;
+ qreal maxPrefSize;
+
+ // Calculated sizes
+ // 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;
+
+ // References to the classes that represent this anchor in the public world
+ // An anchor may represent a LayoutItem, it may also be acessible externally
+ // through a GraphicsAnchor "handler".
+ QGraphicsLayoutItem *item;
+ QGraphicsAnchor *graphicsAnchor;
+
+ uint type : 2; // either Normal, Sequential or Parallel
+ uint isLayoutAnchor : 1; // if this anchor is an internal layout anchor
+ uint isCenterAnchor : 1;
+ uint orientation : 1;
+ uint dependency : 2; // either Independent, Master or Slave
+};
+
+#ifdef QT_DEBUG
+inline QString AnchorData::toString() const
+{
+ return QString::fromAscii("Anchor(%1)").arg(name);
+}
+#endif
+
+struct SequentialAnchorData : public AnchorData
+{
+ SequentialAnchorData(const QVector<AnchorVertex *> &vertices, const QVector<AnchorData *> &edges)
+ : AnchorData(), m_children(vertices), m_edges(edges)
+ {
+ type = AnchorData::Sequential;
+ orientation = m_edges.at(0)->orientation;
+#ifdef QT_DEBUG
+ name = QString::fromAscii("%1 -- %2").arg(vertices.first()->toString(), vertices.last()->toString());
+#endif
+ }
+
+ virtual void updateChildrenSizes();
+ void calculateSizeHints();
+
+ 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(), firstEdge(first), secondEdge(second)
+ {
+ type = AnchorData::Parallel;
+ orientation = first->orientation;
+
+ // This assert whether the child anchors share their vertices
+ Q_ASSERT(((first->from == second->from) && (first->to == second->to)) ||
+ ((first->from == second->to) && (first->to == second->from)));
+
+ // Our convention will be that the parallel group anchor will have the same
+ // direction as the first anchor.
+ from = first->from;
+ to = first->to;
+#ifdef QT_DEBUG
+ name = QString::fromAscii("%1 | %2").arg(first->toString(), second->toString());
+#endif
+ }
+
+ virtual void updateChildrenSizes();
+ bool calculateSizeHints();
+
+ bool secondForward() const {
+ // We have the convention that the first children will define the direction of the
+ // pararell group. Note that we can't rely on 'this->from' or 'this->to' because they
+ // might be changed by vertex simplification.
+ return firstEdge->from == secondEdge->from;
+ }
+
+ AnchorData* firstEdge;
+ AnchorData* secondEdge;
+
+ QList<QSimplexConstraint *> m_firstConstraints;
+ QList<QSimplexConstraint *> m_secondConstraints;
+};
+
+struct AnchorVertexPair : public AnchorVertex {
+ AnchorVertexPair(AnchorVertex *v1, AnchorVertex *v2, AnchorData *data)
+ : AnchorVertex(), m_first(v1), m_second(v2), m_removedAnchor(data) {
+ m_type = AnchorVertex::Pair;
+ }
+
+ AnchorVertex *m_first;
+ AnchorVertex *m_second;
+
+ AnchorData *m_removedAnchor;
+ QList<AnchorData *> m_firstAnchors;
+ QList<AnchorData *> m_secondAnchors;
+};
+
+#ifdef QT_DEBUG
+inline QString AnchorVertex::toString() const
+{
+ if (!this) {
+ return QLatin1String("NULL");
+ } else if (m_type == Pair) {
+ const AnchorVertexPair *vp = static_cast<const AnchorVertexPair *>(this);
+ return QString::fromAscii("(%1, %2)").arg(vp->m_first->toString()).arg(vp->m_second->toString());
+ } else if (!m_item) {
+ return QString::fromAscii("NULL_%1").arg(quintptr(this));
+ }
+ 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
+
+ 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;
+};
+
+class QGraphicsAnchorLayoutPrivate;
+/*!
+ \internal
+*/
+class QGraphicsAnchorPrivate : public QObjectPrivate
+{
+ Q_DECLARE_PUBLIC(QGraphicsAnchor)
+
+public:
+ explicit QGraphicsAnchorPrivate(int version = QObjectPrivateVersion);
+ ~QGraphicsAnchorPrivate();
+
+ void setSpacing(qreal value);
+ void unsetSpacing();
+ qreal spacing() const;
+
+ void setSizePolicy(QSizePolicy::Policy policy);
+
+ QGraphicsAnchorLayoutPrivate *layoutPrivate;
+ AnchorData *data;
+
+ // Size information for user controlled anchor
+ QSizePolicy::Policy sizePolicy;
+ qreal preferredSize;
+
+ uint hasSize : 1; // if false, get size from style.
+};
+
+
+
+
+/*!
+ \internal
+
+ QGraphicsAnchorLayout private methods and attributes.
+*/
+class Q_AUTOTEST_EXPORT 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 {
+ MinimumToMinPreferred = 0,
+ MinPreferredToPreferred,
+ PreferredToMaxPreferred,
+ MaxPreferredToMaximum
+ };
+
+ // 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 QGraphicsAnchorLayoutPrivate *get(QGraphicsAnchorLayout *q)
+ {
+ return q ? q->d_func() : 0;
+ }
+
+ 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);
+
+ QGraphicsAnchor *acquireGraphicsAnchor(AnchorData *data)
+ {
+ Q_Q(QGraphicsAnchorLayout);
+ if (!data->graphicsAnchor) {
+ data->graphicsAnchor = new QGraphicsAnchor(q);
+ data->graphicsAnchor->d_func()->data = data;
+ }
+ return data->graphicsAnchor;
+ }
+
+ // function used by the 4 API functions
+ QGraphicsAnchor *addAnchor(QGraphicsLayoutItem *firstItem,
+ Qt::AnchorPoint firstEdge,
+ QGraphicsLayoutItem *secondItem,
+ Qt::AnchorPoint secondEdge,
+ qreal *spacing = 0);
+
+ // Helper for Anchor Manipulation methods
+ void addAnchor_helper(QGraphicsLayoutItem *firstItem,
+ Qt::AnchorPoint firstEdge,
+ QGraphicsLayoutItem *secondItem,
+ Qt::AnchorPoint secondEdge,
+ AnchorData *data);
+
+ QGraphicsAnchor *getAnchor(QGraphicsLayoutItem *firstItem, Qt::AnchorPoint firstEdge,
+ QGraphicsLayoutItem *secondItem, Qt::AnchorPoint secondEdge);
+
+ void removeAnchor(AnchorVertex *firstVertex, AnchorVertex *secondVertex);
+ void removeAnchor_helper(AnchorVertex *v1, AnchorVertex *v2);
+
+ void removeAnchors(QGraphicsLayoutItem *item);
+
+ void removeVertex(QGraphicsLayoutItem *item, Qt::AnchorPoint edge);
+
+ void correctEdgeDirection(QGraphicsLayoutItem *&firstItem,
+ Qt::AnchorPoint &firstEdge,
+ QGraphicsLayoutItem *&secondItem,
+ Qt::AnchorPoint &secondEdge);
+
+ QLayoutStyleInfo &styleInfo() const;
+
+ AnchorData *addAnchorMaybeParallel(AnchorData *newAnchor, bool *feasible);
+
+ // Activation
+ void calculateGraphs();
+ void calculateGraphs(Orientation orientation);
+
+ // Simplification
+ bool simplifyGraph(Orientation orientation);
+ bool simplifyVertices(Orientation orientation);
+ bool simplifyGraphIteration(Orientation orientation, bool *feasible);
+
+ bool replaceVertex(Orientation orientation, AnchorVertex *oldV,
+ AnchorVertex *newV, const QList<AnchorData *> &edges);
+
+
+ void restoreSimplifiedGraph(Orientation orientation);
+ void restoreSimplifiedAnchor(AnchorData *edge);
+ void restoreSimplifiedConstraints(ParallelAnchorData *parallel);
+ void restoreVertices(Orientation orientation);
+
+ bool calculateTrunk(Orientation orientation, const GraphPath &trunkPath,
+ const QList<QSimplexConstraint *> &constraints,
+ const QList<AnchorData *> &variables);
+ bool calculateNonTrunk(const QList<QSimplexConstraint *> &constraints,
+ const QList<AnchorData *> &variables);
+
+ // Support functions for calculateGraph()
+ void refreshAllSizeHints(Orientation orientation);
+ void findPaths(Orientation orientation);
+ void constraintsFromPaths(Orientation orientation);
+ void updateAnchorSizes(Orientation orientation);
+ QList<QSimplexConstraint *> constraintsFromSizeHints(const QList<AnchorData *> &anchors);
+ QList<QList<QSimplexConstraint *> > getGraphParts(Orientation orientation);
+ void identifyFloatItems(const QSet<AnchorData *> &visited, Orientation orientation);
+ void identifyNonFloatItems_helper(const AnchorData *ad, QSet<QGraphicsLayoutItem *> *nonFloatingItemsIdentifiedSoFar);
+
+ 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));
+ }
+
+ inline void changeLayoutVertex(Orientation orientation, AnchorVertex *oldV, AnchorVertex *newV)
+ {
+ if (layoutFirstVertex[orientation] == oldV)
+ layoutFirstVertex[orientation] = newV;
+ else if (layoutCentralVertex[orientation] == oldV)
+ layoutCentralVertex[orientation] = newV;
+ else if (layoutLastVertex[orientation] == oldV)
+ layoutLastVertex[orientation] = newV;
+ }
+
+
+ AnchorVertex *addInternalVertex(QGraphicsLayoutItem *item, Qt::AnchorPoint edge);
+ void removeInternalVertex(QGraphicsLayoutItem *item, Qt::AnchorPoint edge);
+
+ // Geometry interpolation methods
+ void setItemsGeometries(const QRectF &geom);
+
+ void calculateVertexPositions(Orientation orientation);
+ void setupEdgesInterpolation(Orientation orientation);
+ void interpolateEdge(AnchorVertex *base, AnchorData *edge);
+
+ // Linear Programming solver methods
+ bool solveMinMax(const QList<QSimplexConstraint *> &constraints,
+ GraphPath path, qreal *min, qreal *max);
+ bool solvePreferred(const QList<QSimplexConstraint *> &constraints,
+ const QList<AnchorData *> &variables);
+ bool hasConflicts() const;
+
+#ifdef QT_DEBUG
+ void dumpGraph(const QString &name = QString());
+#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];
+
+ AnchorVertex *layoutFirstVertex[2];
+ AnchorVertex *layoutCentralVertex[2];
+ AnchorVertex *layoutLastVertex[2];
+
+ // Combined anchors in order of creation
+ QList<AnchorVertexPair *> simplifiedVertices[2];
+ QList<AnchorData *> anchorsFromSimplifiedVertices[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 graphHasConflicts[2];
+ QSet<QGraphicsLayoutItem *> m_floatItems[2];
+
+#if defined(QT_DEBUG) || defined(Q_AUTOTEST_EXPORT)
+ bool lastCalculationUsedSimplex[2];
+#endif
+
+ uint calculateGraphCacheDirty : 1;
+ mutable uint styleInfoDirty : 1;
+ mutable QLayoutStyleInfo cachedStyleInfo;
+
+ friend class QGraphicsAnchorPrivate;
+};
+
+QT_END_NAMESPACE
+#endif //QT_NO_GRAPHICSVIEW
+
+#endif
diff --git a/src/widgets/graphicsview/qgraphicsgridlayout.cpp b/src/widgets/graphicsview/qgraphicsgridlayout.cpp
new file mode 100644
index 0000000000..f6eec1d405
--- /dev/null
+++ b/src/widgets/graphicsview/qgraphicsgridlayout.cpp
@@ -0,0 +1,690 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** 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 Technology Preview License Agreement accompanying
+** this package.
+**
+** 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.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*!
+ \class QGraphicsGridLayout
+ \brief The QGraphicsGridLayout class provides a grid layout for managing
+ widgets in Graphics View.
+ \since 4.4
+
+ \ingroup graphicsview-api
+
+ The most common way to use QGraphicsGridLayout is to construct an object
+ on the heap with no parent, add widgets and layouts by calling addItem(),
+ and finally assign the layout to a widget by calling
+ QGraphicsWidget::setLayout(). QGraphicsGridLayout automatically computes
+ the dimensions of the grid as you add items.
+
+ \snippet doc/src/snippets/code/src_gui_graphicsview_qgraphicsgridlayout.cpp 0
+
+ The layout takes ownership of the items. In some cases when the layout
+ item also inherits from QGraphicsItem (such as QGraphicsWidget) there will be a
+ ambiguity in ownership because the layout item belongs to two ownership hierarchies.
+ See the documentation of QGraphicsLayoutItem::setOwnedByLayout() how to handle
+ this.
+ You can access each item in the layout by calling count() and itemAt(). Calling
+ removeAt() will remove an item from the layout, without
+ destroying it.
+
+ \section1 Size Hints and Size Policies in QGraphicsGridLayout
+
+ QGraphicsGridLayout respects each item's size hints and size policies,
+ and when a cell in the grid has more space than the items can fill, each item
+ is arranged according to the layout's alignment for that item. You can set
+ an alignment for each item by calling setAlignment(), and check the
+ alignment for any item by calling alignment(). You can also set the alignment
+ for an entire row or column by calling setRowAlignment() and setColumnAlignment()
+ respectively. By default, items are aligned to the top left.
+
+
+ \sa QGraphicsLinearLayout, QGraphicsWidget
+*/
+
+#include "qglobal.h"
+
+#ifndef QT_NO_GRAPHICSVIEW
+
+#include "qapplication.h"
+#include "qwidget.h"
+#include "qgraphicslayout_p.h"
+#include "qgraphicslayoutitem.h"
+#include "qgraphicsgridlayout.h"
+#include "qgraphicswidget.h"
+#include "qgridlayoutengine_p.h"
+#include <QtCore/qdebug.h>
+
+QT_BEGIN_NAMESPACE
+
+class QGraphicsGridLayoutPrivate : public QGraphicsLayoutPrivate
+{
+public:
+ QGraphicsGridLayoutPrivate() { }
+ QLayoutStyleInfo styleInfo() const;
+
+ QGridLayoutEngine engine;
+#ifdef QT_DEBUG
+ void dump(int indent) const;
+#endif
+};
+
+Q_GLOBAL_STATIC(QWidget, globalStyleInfoWidget);
+
+QLayoutStyleInfo QGraphicsGridLayoutPrivate::styleInfo() const
+{
+ QGraphicsItem *item = parentItem();
+ QStyle *style = (item && item->isWidget()) ? static_cast<QGraphicsWidget*>(item)->style() : QApplication::style();
+ return QLayoutStyleInfo(style, globalStyleInfoWidget());
+}
+
+/*!
+ Constructs a QGraphicsGridLayout instance. \a parent is passed to
+ QGraphicsLayout's constructor.
+*/
+QGraphicsGridLayout::QGraphicsGridLayout(QGraphicsLayoutItem *parent)
+ : QGraphicsLayout(*new QGraphicsGridLayoutPrivate(), parent)
+{
+}
+
+/*!
+ Destroys the QGraphicsGridLayout object.
+*/
+QGraphicsGridLayout::~QGraphicsGridLayout()
+{
+ for (int i = count() - 1; i >= 0; --i) {
+ QGraphicsLayoutItem *item = itemAt(i);
+ // The following lines can be removed, but this removes the item
+ // from the layout more efficiently than the implementation of
+ // ~QGraphicsLayoutItem.
+ removeAt(i);
+ if (item) {
+ item->setParentLayoutItem(0);
+ if (item->ownedByLayout())
+ delete item;
+ }
+ }
+}
+
+/*!
+ Adds \a item to the grid on \a row and \a column. You can specify a
+ \a rowSpan and \a columnSpan and an optional \a alignment.
+*/
+void QGraphicsGridLayout::addItem(QGraphicsLayoutItem *item, int row, int column,
+ int rowSpan, int columnSpan, Qt::Alignment alignment)
+{
+ Q_D(QGraphicsGridLayout);
+ if (row < 0 || column < 0) {
+ qWarning("QGraphicsGridLayout::addItem: invalid row/column: %d",
+ row < 0 ? row : column);
+ return;
+ }
+ if (columnSpan < 1 || rowSpan < 1) {
+ qWarning("QGraphicsGridLayout::addItem: invalid row span/column span: %d",
+ rowSpan < 1 ? rowSpan : columnSpan);
+ return;
+ }
+ if (!item) {
+ qWarning("QGraphicsGridLayout::addItem: cannot add null item");
+ return;
+ }
+ if (item == this) {
+ qWarning("QGraphicsGridLayout::addItem: cannot insert itself");
+ return;
+ }
+
+ d->addChildLayoutItem(item);
+
+ new QGridLayoutItem(&d->engine, item, row, column, rowSpan, columnSpan, alignment);
+ invalidate();
+}
+
+/*!
+ \fn QGraphicsGridLayout::addItem(QGraphicsLayoutItem *item, int row, int column, Qt::Alignment alignment = 0)
+
+ Adds \a item to the grid on \a row and \a column. You can specify
+ an optional \a alignment for \a item.
+*/
+
+/*!
+ Sets the default horizontal spacing for the grid layout to \a spacing.
+*/
+void QGraphicsGridLayout::setHorizontalSpacing(qreal spacing)
+{
+ Q_D(QGraphicsGridLayout);
+ d->engine.setSpacing(spacing, Qt::Horizontal);
+ invalidate();
+}
+
+/*!
+ Returns the default horizontal spacing for the grid layout.
+*/
+qreal QGraphicsGridLayout::horizontalSpacing() const
+{
+ Q_D(const QGraphicsGridLayout);
+ return d->engine.spacing(d->styleInfo(), Qt::Horizontal);
+}
+
+/*!
+ Sets the default vertical spacing for the grid layout to \a spacing.
+*/
+void QGraphicsGridLayout::setVerticalSpacing(qreal spacing)
+{
+ Q_D(QGraphicsGridLayout);
+ d->engine.setSpacing(spacing, Qt::Vertical);
+ invalidate();
+}
+
+/*!
+ Returns the default vertical spacing for the grid layout.
+*/
+qreal QGraphicsGridLayout::verticalSpacing() const
+{
+ Q_D(const QGraphicsGridLayout);
+ return d->engine.spacing(d->styleInfo(), Qt::Vertical);
+}
+
+/*!
+ Sets the grid layout's default spacing, both vertical and
+ horizontal, to \a spacing.
+
+ \sa rowSpacing(), columnSpacing()
+*/
+void QGraphicsGridLayout::setSpacing(qreal spacing)
+{
+ Q_D(QGraphicsGridLayout);
+ d->engine.setSpacing(spacing, Qt::Horizontal | Qt::Vertical);
+ invalidate();
+}
+
+/*!
+ Sets the spacing for \a row to \a spacing.
+*/
+void QGraphicsGridLayout::setRowSpacing(int row, qreal spacing)
+{
+ Q_D(QGraphicsGridLayout);
+ d->engine.setRowSpacing(row, spacing, Qt::Vertical);
+ invalidate();
+}
+
+/*!
+ Returns the row spacing for \a row.
+*/
+qreal QGraphicsGridLayout::rowSpacing(int row) const
+{
+ Q_D(const QGraphicsGridLayout);
+ return d->engine.rowSpacing(row, Qt::Vertical);
+}
+
+/*!
+ Sets the spacing for \a column to \a spacing.
+*/
+void QGraphicsGridLayout::setColumnSpacing(int column, qreal spacing)
+{
+ Q_D(QGraphicsGridLayout);
+ d->engine.setRowSpacing(column, spacing, Qt::Horizontal);
+ invalidate();
+}
+
+/*!
+ Returns the column spacing for \a column.
+*/
+qreal QGraphicsGridLayout::columnSpacing(int column) const
+{
+ Q_D(const QGraphicsGridLayout);
+ return d->engine.rowSpacing(column, Qt::Horizontal);
+}
+
+/*!
+ Sets the stretch factor for \a row to \a stretch.
+*/
+void QGraphicsGridLayout::setRowStretchFactor(int row, int stretch)
+{
+ Q_D(QGraphicsGridLayout);
+ d->engine.setRowStretchFactor(row, stretch, Qt::Vertical);
+ invalidate();
+}
+
+/*!
+ Returns the stretch factor for \a row.
+*/
+int QGraphicsGridLayout::rowStretchFactor(int row) const
+{
+ Q_D(const QGraphicsGridLayout);
+ return d->engine.rowStretchFactor(row, Qt::Vertical);
+}
+
+/*!
+ Sets the stretch factor for \a column to \a stretch.
+*/
+void QGraphicsGridLayout::setColumnStretchFactor(int column, int stretch)
+{
+ Q_D(QGraphicsGridLayout);
+ d->engine.setRowStretchFactor(column, stretch, Qt::Horizontal);
+ invalidate();
+}
+
+/*!
+ Returns the stretch factor for \a column.
+*/
+int QGraphicsGridLayout::columnStretchFactor(int column) const
+{
+ Q_D(const QGraphicsGridLayout);
+ return d->engine.rowStretchFactor(column, Qt::Horizontal);
+}
+
+/*!
+ Sets the minimum height for row, \a row, to \a height.
+*/
+void QGraphicsGridLayout::setRowMinimumHeight(int row, qreal height)
+{
+ Q_D(QGraphicsGridLayout);
+ d->engine.setRowSizeHint(Qt::MinimumSize, row, height, Qt::Vertical);
+ invalidate();
+}
+
+/*!
+ Returns the minimum height for row, \a row.
+*/
+qreal QGraphicsGridLayout::rowMinimumHeight(int row) const
+{
+ Q_D(const QGraphicsGridLayout);
+ return d->engine.rowSizeHint(Qt::MinimumSize, row, Qt::Vertical);
+}
+
+/*!
+ Sets the preferred height for row, \a row, to \a height.
+*/
+void QGraphicsGridLayout::setRowPreferredHeight(int row, qreal height)
+{
+ Q_D(QGraphicsGridLayout);
+ d->engine.setRowSizeHint(Qt::PreferredSize, row, height, Qt::Vertical);
+ invalidate();
+}
+
+/*!
+ Returns the preferred height for row, \a row.
+*/
+qreal QGraphicsGridLayout::rowPreferredHeight(int row) const
+{
+ Q_D(const QGraphicsGridLayout);
+ return d->engine.rowSizeHint(Qt::PreferredSize, row, Qt::Vertical);
+}
+
+/*!
+ Sets the maximum height for row, \a row, to \a height.
+*/
+void QGraphicsGridLayout::setRowMaximumHeight(int row, qreal height)
+{
+ Q_D(QGraphicsGridLayout);
+ d->engine.setRowSizeHint(Qt::MaximumSize, row, height, Qt::Vertical);
+ invalidate();
+}
+
+/*!
+ Returns the maximum height for row, \a row.
+*/
+qreal QGraphicsGridLayout::rowMaximumHeight(int row) const
+{
+ Q_D(const QGraphicsGridLayout);
+ return d->engine.rowSizeHint(Qt::MaximumSize, row, Qt::Vertical);
+}
+
+/*!
+ Sets the fixed height for row, \a row, to \a height.
+*/
+void QGraphicsGridLayout::setRowFixedHeight(int row, qreal height)
+{
+ Q_D(QGraphicsGridLayout);
+ d->engine.setRowSizeHint(Qt::MinimumSize, row, height, Qt::Vertical);
+ d->engine.setRowSizeHint(Qt::MaximumSize, row, height, Qt::Vertical);
+ invalidate();
+}
+
+/*!
+ Sets the minimum width for \a column to \a width.
+*/
+void QGraphicsGridLayout::setColumnMinimumWidth(int column, qreal width)
+{
+ Q_D(QGraphicsGridLayout);
+ d->engine.setRowSizeHint(Qt::MinimumSize, column, width, Qt::Horizontal);
+ invalidate();
+}
+
+/*!
+ Returns the minimum width for \a column.
+*/
+qreal QGraphicsGridLayout::columnMinimumWidth(int column) const
+{
+ Q_D(const QGraphicsGridLayout);
+ return d->engine.rowSizeHint(Qt::MinimumSize, column, Qt::Horizontal);
+}
+
+/*!
+ Sets the preferred width for \a column to \a width.
+*/
+void QGraphicsGridLayout::setColumnPreferredWidth(int column, qreal width)
+{
+ Q_D(QGraphicsGridLayout);
+ d->engine.setRowSizeHint(Qt::PreferredSize, column, width, Qt::Horizontal);
+ invalidate();
+}
+
+/*!
+ Returns the preferred width for \a column.
+*/
+qreal QGraphicsGridLayout::columnPreferredWidth(int column) const
+{
+ Q_D(const QGraphicsGridLayout);
+ return d->engine.rowSizeHint(Qt::PreferredSize, column, Qt::Horizontal);
+}
+
+/*!
+ Sets the maximum width of \a column to \a width.
+*/
+void QGraphicsGridLayout::setColumnMaximumWidth(int column, qreal width)
+{
+ Q_D(QGraphicsGridLayout);
+ d->engine.setRowSizeHint(Qt::MaximumSize, column, width, Qt::Horizontal);
+ invalidate();
+}
+
+/*!
+ Returns the maximum width for \a column.
+*/
+qreal QGraphicsGridLayout::columnMaximumWidth(int column) const
+{
+ Q_D(const QGraphicsGridLayout);
+ return d->engine.rowSizeHint(Qt::MaximumSize, column, Qt::Horizontal);
+}
+
+/*!
+ Sets the fixed width of \a column to \a width.
+*/
+void QGraphicsGridLayout::setColumnFixedWidth(int column, qreal width)
+{
+ Q_D(QGraphicsGridLayout);
+ d->engine.setRowSizeHint(Qt::MinimumSize, column, width, Qt::Horizontal);
+ d->engine.setRowSizeHint(Qt::MaximumSize, column, width, Qt::Horizontal);
+ invalidate();
+}
+
+/*!
+ Sets the alignment of \a row to \a alignment.
+*/
+void QGraphicsGridLayout::setRowAlignment(int row, Qt::Alignment alignment)
+{
+ Q_D(QGraphicsGridLayout);
+ d->engine.setRowAlignment(row, alignment, Qt::Vertical);
+ invalidate();
+}
+
+/*!
+ Returns the alignment of \a row.
+*/
+Qt::Alignment QGraphicsGridLayout::rowAlignment(int row) const
+{
+ Q_D(const QGraphicsGridLayout);
+ return d->engine.rowAlignment(row, Qt::Vertical);
+}
+
+/*!
+ Sets the alignment for \a column to \a alignment.
+*/
+void QGraphicsGridLayout::setColumnAlignment(int column, Qt::Alignment alignment)
+{
+ Q_D(QGraphicsGridLayout);
+ d->engine.setRowAlignment(column, alignment, Qt::Horizontal);
+ invalidate();
+}
+
+/*!
+ Returns the alignment for \a column.
+*/
+Qt::Alignment QGraphicsGridLayout::columnAlignment(int column) const
+{
+ Q_D(const QGraphicsGridLayout);
+ return d->engine.rowAlignment(column, Qt::Horizontal);
+}
+
+/*!
+ Sets the alignment for \a item to \a alignment.
+*/
+void QGraphicsGridLayout::setAlignment(QGraphicsLayoutItem *item, Qt::Alignment alignment)
+{
+ Q_D(QGraphicsGridLayout);
+ d->engine.setAlignment(item, alignment);
+ invalidate();
+}
+
+/*!
+ Returns the alignment for \a item.
+*/
+Qt::Alignment QGraphicsGridLayout::alignment(QGraphicsLayoutItem *item) const
+{
+ Q_D(const QGraphicsGridLayout);
+ return d->engine.alignment(item);
+}
+
+/*!
+ Returns the number of rows in the grid layout. This is always one more
+ than the index of the last row that is occupied by a layout item (empty
+ rows are counted except for those at the end).
+*/
+int QGraphicsGridLayout::rowCount() const
+{
+ Q_D(const QGraphicsGridLayout);
+ return d->engine.effectiveLastRow(Qt::Vertical) + 1;
+}
+
+/*!
+ Returns the number of columns in the grid layout. This is always one more
+ than the index of the last column that is occupied by a layout item (empty
+ columns are counted except for those at the end).
+*/
+int QGraphicsGridLayout::columnCount() const
+{
+ Q_D(const QGraphicsGridLayout);
+ return d->engine.effectiveLastRow(Qt::Horizontal) + 1;
+}
+
+/*!
+ Returns a pointer to the layout item at (\a row, \a column).
+*/
+QGraphicsLayoutItem *QGraphicsGridLayout::itemAt(int row, int column) const
+{
+ Q_D(const QGraphicsGridLayout);
+ if (row < 0 || row >= rowCount() || column < 0 || column >= columnCount()) {
+ qWarning("QGraphicsGridLayout::itemAt: invalid row, column %d, %d", row, column);
+ return 0;
+ }
+ if (QGridLayoutItem *item = d->engine.itemAt(row, column))
+ return item->layoutItem();
+ return 0;
+}
+
+/*!
+ Returns the number of layout items in this grid layout.
+*/
+int QGraphicsGridLayout::count() const
+{
+ Q_D(const QGraphicsGridLayout);
+ return d->engine.itemCount();
+}
+
+/*!
+ Returns the layout item at \a index, or 0 if there is no layout item at
+ this index.
+*/
+QGraphicsLayoutItem *QGraphicsGridLayout::itemAt(int index) const
+{
+ Q_D(const QGraphicsGridLayout);
+ if (index < 0 || index >= d->engine.itemCount()) {
+ qWarning("QGraphicsGridLayout::itemAt: invalid index %d", index);
+ return 0;
+ }
+ QGraphicsLayoutItem *item = 0;
+ if (QGridLayoutItem *gridItem = d->engine.itemAt(index))
+ item = gridItem->layoutItem();
+ return item;
+}
+
+/*!
+ Removes the layout item at \a index without destroying it. Ownership of
+ the item is transferred to the caller.
+
+ \sa addItem()
+*/
+void QGraphicsGridLayout::removeAt(int index)
+{
+ Q_D(QGraphicsGridLayout);
+ if (index < 0 || index >= d->engine.itemCount()) {
+ qWarning("QGraphicsGridLayout::removeAt: invalid index %d", index);
+ return;
+ }
+ if (QGridLayoutItem *gridItem = d->engine.itemAt(index)) {
+ if (QGraphicsLayoutItem *layoutItem = gridItem->layoutItem())
+ layoutItem->setParentLayoutItem(0);
+ d->engine.removeItem(gridItem);
+
+ // recalculate rowInfo.count if we remove an item that is on the right/bottommost row
+ for (int j = 0; j < NOrientations; ++j) {
+ // 0: Hor, 1: Ver
+ const Qt::Orientation orient = (j == 0 ? Qt::Horizontal : Qt::Vertical);
+ const int oldCount = d->engine.rowCount(orient);
+ if (gridItem->lastRow(orient) == oldCount - 1) {
+ const int newCount = d->engine.effectiveLastRow(orient) + 1;
+ d->engine.removeRows(newCount, oldCount - newCount, orient);
+ }
+ }
+
+ delete gridItem;
+ invalidate();
+ }
+}
+
+/*!
+ Removes the layout item \a item without destroying it.
+ Ownership of the item is transferred to the caller.
+
+ \sa addItem()
+*/
+void QGraphicsGridLayout::removeItem(QGraphicsLayoutItem *item)
+{
+ Q_D(QGraphicsGridLayout);
+ int index = d->engine.indexOf(item);
+ removeAt(index);
+}
+/*!
+ \reimp
+*/
+void QGraphicsGridLayout::invalidate()
+{
+ Q_D(QGraphicsGridLayout);
+ d->engine.invalidate();
+ QGraphicsLayout::invalidate();
+}
+
+#ifdef QT_DEBUG
+void QGraphicsGridLayoutPrivate::dump(int indent) const
+{
+ if (qt_graphicsLayoutDebug()) {
+ engine.dump(indent + 1);
+ }
+}
+#endif
+
+/*!
+ Sets the bounding geometry of the grid layout to \a rect.
+*/
+void QGraphicsGridLayout::setGeometry(const QRectF &rect)
+{
+ Q_D(QGraphicsGridLayout);
+ QGraphicsLayout::setGeometry(rect);
+ QRectF effectiveRect = geometry();
+ qreal left, top, right, bottom;
+ getContentsMargins(&left, &top, &right, &bottom);
+ Qt::LayoutDirection visualDir = d->visualDirection();
+ d->engine.setVisualDirection(visualDir);
+ if (visualDir == Qt::RightToLeft)
+ qSwap(left, right);
+ effectiveRect.adjust(+left, +top, -right, -bottom);
+ d->engine.setGeometries(d->styleInfo(), effectiveRect);
+#ifdef QT_DEBUG
+ if (qt_graphicsLayoutDebug()) {
+ static int counter = 0;
+ qDebug("==== BEGIN DUMP OF QGraphicsGridLayout (%d)====", counter++);
+ d->dump(1);
+ qDebug("==== END DUMP OF QGraphicsGridLayout ====");
+ }
+#endif
+}
+
+/*!
+ \reimp
+*/
+QSizeF QGraphicsGridLayout::sizeHint(Qt::SizeHint which, const QSizeF &constraint) const
+{
+ Q_D(const QGraphicsGridLayout);
+ qreal left, top, right, bottom;
+ getContentsMargins(&left, &top, &right, &bottom);
+ const QSizeF extraMargins(left + right, top + bottom);
+ return d->engine.sizeHint(d->styleInfo(), which , constraint - extraMargins) + extraMargins;
+}
+
+
+#if 0
+// ### kill? (implement and kill?)
+QRect QGraphicsGridLayout::cellRect(int row, int column, int rowSpan, int columnSpan) const
+{
+ Q_D(const QGraphicsGridLayout);
+ return QRect();
+// return d->engine.cellRect(parentLayoutable(), contentsGeometry(), row, column, rowSpan, columnSpan);
+}
+
+QSizePolicy::ControlTypes QGraphicsGridLayout::controlTypes(LayoutSide side) const
+{
+ Q_D(const QGraphicsGridLayout);
+ return d->engine.controlTypes(side);
+}
+#endif
+
+QT_END_NAMESPACE
+
+#endif //QT_NO_GRAPHICSVIEW
diff --git a/src/widgets/graphicsview/qgraphicsgridlayout.h b/src/widgets/graphicsview/qgraphicsgridlayout.h
new file mode 100644
index 0000000000..f493eb6153
--- /dev/null
+++ b/src/widgets/graphicsview/qgraphicsgridlayout.h
@@ -0,0 +1,144 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** 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 Technology Preview License Agreement accompanying
+** this package.
+**
+** 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.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QGRAPHICSGRIDLAYOUT_H
+#define QGRAPHICSGRIDLAYOUT_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 QGraphicsGridLayoutPrivate;
+
+class Q_GUI_EXPORT QGraphicsGridLayout : public QGraphicsLayout
+{
+public:
+ QGraphicsGridLayout(QGraphicsLayoutItem *parent = 0);
+ virtual ~QGraphicsGridLayout();
+
+ void addItem(QGraphicsLayoutItem *item, int row, int column, int rowSpan, int columnSpan,
+ Qt::Alignment alignment = 0);
+ inline void addItem(QGraphicsLayoutItem *item, int row, int column, Qt::Alignment alignment = 0);
+
+ void setHorizontalSpacing(qreal spacing);
+ qreal horizontalSpacing() const;
+ void setVerticalSpacing(qreal spacing);
+ qreal verticalSpacing() const;
+ void setSpacing(qreal spacing);
+
+ void setRowSpacing(int row, qreal spacing);
+ qreal rowSpacing(int row) const;
+ void setColumnSpacing(int column, qreal spacing);
+ qreal columnSpacing(int column) const;
+
+ void setRowStretchFactor(int row, int stretch);
+ int rowStretchFactor(int row) const;
+ void setColumnStretchFactor(int column, int stretch);
+ int columnStretchFactor(int column) const;
+
+ void setRowMinimumHeight(int row, qreal height);
+ qreal rowMinimumHeight(int row) const;
+ void setRowPreferredHeight(int row, qreal height);
+ qreal rowPreferredHeight(int row) const;
+ void setRowMaximumHeight(int row, qreal height);
+ qreal rowMaximumHeight(int row) const;
+ void setRowFixedHeight(int row, qreal height);
+
+ void setColumnMinimumWidth(int column, qreal width);
+ qreal columnMinimumWidth(int column) const;
+ void setColumnPreferredWidth(int column, qreal width);
+ qreal columnPreferredWidth(int column) const;
+ void setColumnMaximumWidth(int column, qreal width);
+ qreal columnMaximumWidth(int column) const;
+ void setColumnFixedWidth(int column, qreal width);
+
+ void setRowAlignment(int row, Qt::Alignment alignment);
+ Qt::Alignment rowAlignment(int row) const;
+ void setColumnAlignment(int column, Qt::Alignment alignment);
+ Qt::Alignment columnAlignment(int column) const;
+
+ void setAlignment(QGraphicsLayoutItem *item, Qt::Alignment alignment);
+ Qt::Alignment alignment(QGraphicsLayoutItem *item) const;
+
+ int rowCount() const;
+ int columnCount() const;
+
+ QGraphicsLayoutItem *itemAt(int row, int column) const;
+
+ // inherited from QGraphicsLayout
+ int count() const;
+ QGraphicsLayoutItem *itemAt(int index) const;
+ void removeAt(int index);
+ void removeItem(QGraphicsLayoutItem *item);
+
+ void invalidate();
+
+ // inherited from QGraphicsLayoutItem
+ void setGeometry(const QRectF &rect);
+ QSizeF sizeHint(Qt::SizeHint which, const QSizeF &constraint = QSizeF()) const;
+
+ // ####
+ //QRect cellRect(int row, int column, int rowSpan = 1, int columnSpan = 1) const;
+ //QSizePolicy::ControlTypes controlTypes(LayoutSide side) const;
+
+private:
+ Q_DISABLE_COPY(QGraphicsGridLayout)
+ Q_DECLARE_PRIVATE(QGraphicsGridLayout)
+};
+
+inline void QGraphicsGridLayout::addItem(QGraphicsLayoutItem *aitem, int arow, int acolumn, Qt::Alignment aalignment)
+{ addItem(aitem, arow, acolumn, 1, 1, aalignment); }
+
+#endif
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif
+
diff --git a/src/widgets/graphicsview/qgraphicsitem.cpp b/src/widgets/graphicsview/qgraphicsitem.cpp
new file mode 100644
index 0000000000..e67fe82045
--- /dev/null
+++ b/src/widgets/graphicsview/qgraphicsitem.cpp
@@ -0,0 +1,11597 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** 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 Technology Preview License Agreement accompanying
+** this package.
+**
+** 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.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*!
+ \class QGraphicsItem
+ \brief The QGraphicsItem class is the base class for all graphical
+ items in a QGraphicsScene.
+ \since 4.2
+
+ \ingroup graphicsview-api
+
+ It provides a light-weight foundation for writing your own custom items.
+ This includes defining the item's geometry, collision detection, its
+ painting implementation and item interaction through its event handlers.
+ QGraphicsItem is part of the \l{Graphics View Framework}
+
+ \image graphicsview-items.png
+
+ For convenience, Qt provides a set of standard graphics items for the most
+ common shapes. These are:
+
+ \list
+ \o QGraphicsEllipseItem provides an ellipse item
+ \o QGraphicsLineItem provides a line item
+ \o QGraphicsPathItem provides an arbitrary path item
+ \o QGraphicsPixmapItem provides a pixmap item
+ \o QGraphicsPolygonItem provides a polygon item
+ \o QGraphicsRectItem provides a rectangular item
+ \o QGraphicsSimpleTextItem provides a simple text label item
+ \o QGraphicsTextItem provides an advanced text browser item
+ \endlist
+
+ All of an item's geometric information is based on its local coordinate
+ system. The item's position, pos(), is the only function that does not
+ operate in local coordinates, as it returns a position in parent
+ coordinates. \l {The Graphics View Coordinate System} describes the coordinate
+ system in detail.
+
+ You can set whether an item should be visible (i.e., drawn, and accepting
+ events), by calling setVisible(). Hiding an item will also hide its
+ children. Similarly, you can enable or disable an item by calling
+ setEnabled(). If you disable an item, all its children will also be
+ disabled. By default, items are both visible and enabled. To toggle
+ whether an item is selected or not, first enable selection by setting
+ the ItemIsSelectable flag, and then call setSelected(). Normally,
+ selection is toggled by the scene, as a result of user interaction.
+
+ To write your own graphics item, you first create a subclass of
+ QGraphicsItem, and then start by implementing its two pure virtual public
+ functions: boundingRect(), which returns an estimate of the area painted
+ by the item, and paint(), which implements the actual painting. For
+ example:
+
+ \snippet doc/src/snippets/code/src_gui_graphicsview_qgraphicsitem.cpp 0
+
+ The boundingRect() function has many different purposes.
+ QGraphicsScene bases its item index on boundingRect(), and
+ QGraphicsView uses it both for culling invisible items, and for
+ determining the area that needs to be recomposed when drawing
+ overlapping items. In addition, QGraphicsItem's collision
+ detection mechanisms use boundingRect() to provide an efficient
+ cut-off. The fine grained collision algorithm in
+ collidesWithItem() is based on calling shape(), which returns an
+ accurate outline of the item's shape as a QPainterPath.
+
+ QGraphicsScene expects all items boundingRect() and shape() to
+ remain unchanged unless it is notified. If you want to change an
+ item's geometry in any way, you must first call
+ prepareGeometryChange() to allow QGraphicsScene to update its
+ bookkeeping.
+
+ Collision detection can be done in two ways:
+
+ \list 1
+
+ \o Reimplement shape() to return an accurate shape for your item,
+ and rely on the default implementation of collidesWithItem() to do
+ shape-shape intersection. This can be rather expensive if the
+ shapes are complex.
+
+ \o Reimplement collidesWithItem() to provide your own custom item
+ and shape collision algorithm.
+
+ \endlist
+
+ The contains() function can be called to determine whether the item \e
+ contains a point or not. This function can also be reimplemented by the
+ item. The default behavior of contains() is based on calling shape().
+
+ Items can contain other items, and also be contained by other items. All
+ items can have a parent item and a list of children. Unless the item has
+ no parent, its position is in \e parent coordinates (i.e., the parent's
+ local coordinates). Parent items propagate both their position and their
+ transformation to all children.
+
+ \img graphicsview-parentchild.png
+
+ \target Transformations
+ \section1 Transformations
+
+ QGraphicsItem supports projective transformations in addition to its base
+ position, pos(). There are several ways to change an item's transformation.
+ For simple transformations, you can call either of the convenience
+ functions setRotation() or setScale(), or you can pass any transformation
+ matrix to setTransform(). For advanced transformation control you also have
+ the option of setting several combined transformations by calling
+ setTransformations().
+
+ Item transformations accumulate from parent to child, so if both a parent
+ and child item are rotated 90 degrees, the child's total transformation
+ will be 180 degrees. Similarly, if the item's parent is scaled to 2x its
+ original size, its children will also be twice as large. An item's
+ transformation does not affect its own local geometry; all geometry
+ functions (e.g., contains(), update(), and all the mapping functions) still
+ operate in local coordinates. For convenience, QGraphicsItem provides the
+ functions sceneTransform(), which returns the item's total transformation
+ matrix (including its position and all parents' positions and
+ transformations), and scenePos(), which returns its position in scene
+ coordinates. To reset an item's matrix, call resetTransform().
+
+ Certain transformation operations produce a different outcome depending on
+ the order in which they are applied. For example, if you scale an
+ transform, and then rotate it, you may get a different result than if the
+ transform was rotated first. However, the order you set the transformation
+ properties on QGraphicsItem does not affect the resulting transformation;
+ QGraphicsItem always applies the properties in a fixed, defined order:
+
+ \list
+ \o The item's base transform is applied (transform())
+ \o The item's transformations list is applied in order (transformations())
+ \o The item is rotated relative to its transform origin point (rotation(), transformOriginPoint())
+ \o The item is scaled relative to its transform origin point (scale(), transformOriginPoint())
+ \endlist
+
+ \section1 Painting
+
+ The paint() function is called by QGraphicsView to paint the item's
+ contents. The item has no background or default fill of its own; whatever
+ is behind the item will shine through all areas that are not explicitly
+ painted in this function. You can call update() to schedule a repaint,
+ optionally passing the rectangle that needs a repaint. Depending on
+ whether or not the item is visible in a view, the item may or may not be
+ repainted; there is no equivalent to QWidget::repaint() in QGraphicsItem.
+
+ Items are painted by the view, starting with the parent items and then
+ drawing children, in ascending stacking order. You can set an item's
+ stacking order by calling setZValue(), and test it by calling
+ zValue(), where items with low z-values are painted before items with
+ high z-values. Stacking order applies to sibling items; parents are always
+ drawn before their children.
+
+ \section1 Sorting
+
+ All items are drawn in a defined, stable order, and this same order decides
+ which items will receive mouse input first when you click on the scene.
+ Normally you don't have to worry about sorting, as the items follow a
+ "natural order", following the logical structure of the scene.
+
+ An item's children are stacked on top of the parent, and sibling items are
+ stacked by insertion order (i.e., in the same order that they were either
+ added to the scene, or added to the same parent). If you add item A, and
+ then B, then B will be on top of A. If you then add C, the items' stacking
+ order will be A, then B, then C.
+
+ \image graphicsview-zorder.png
+
+ This example shows the stacking order of all limbs of the robot from the
+ \l{graphicsview/dragdroprobot}{Drag and Drop Robot} example. The torso is
+ the root item (all other items are children or descendants of the torso),
+ so it is drawn first. Next, the head is drawn, as it is the first item in
+ the torso's list of children. Then the upper left arm is drawn. As the
+ lower arm is a child of the upper arm, the lower arm is then drawn,
+ followed by the upper arm's next sibling, which is the upper right arm, and
+ so on.
+
+ For advanced users, there are ways to alter how your items are sorted:
+
+ \list
+ \o You can call setZValue() on an item to explicitly stack it on top of, or
+ under, other sibling items. The default Z value for an item is 0. Items
+ with the same Z value are stacked by insertion order.
+
+ \o You can call stackBefore() to reorder the list of children. This will
+ directly modify the insertion order.
+
+ \o You can set the ItemStacksBehindParent flag to stack a child item behind
+ its parent.
+ \endlist
+
+ The stacking order of two sibling items also counts for each item's
+ children and descendant items. So if one item is on top of another, then
+ all its children will also be on top of all the other item's children as
+ well.
+
+ \section1 Events
+
+ QGraphicsItem receives events from QGraphicsScene through the virtual
+ function sceneEvent(). This function distributes the most common events
+ to a set of convenience event handlers:
+
+ \list
+ \o contextMenuEvent() handles context menu events
+ \o focusInEvent() and focusOutEvent() handle focus in and out events
+ \o hoverEnterEvent(), hoverMoveEvent(), and hoverLeaveEvent() handles
+ hover enter, move and leave events
+ \o inputMethodEvent() handles input events, for accessibility support
+ \o keyPressEvent() and keyReleaseEvent() handle key press and release events
+ \o mousePressEvent(), mouseMoveEvent(), mouseReleaseEvent(), and
+ mouseDoubleClickEvent() handles mouse press, move, release, click and
+ doubleclick events
+ \endlist
+
+ You can filter events for any other item by installing event filters. This
+ functionality is separate from Qt's regular event filters (see
+ QObject::installEventFilter()), which only work on subclasses of QObject. After
+ installing your item as an event filter for another item by calling
+ installSceneEventFilter(), the filtered events will be received by the virtual
+ function sceneEventFilter(). You can remove item event filters by calling
+ removeSceneEventFilter().
+
+ \section1 Custom Data
+
+ Sometimes it's useful to register custom data with an item, be it a custom
+ item, or a standard item. You can call setData() on any item to store data
+ in it using a key-value pair (the key being an integer, and the value is a
+ QVariant). To get custom data from an item, call data(). This
+ functionality is completely untouched by Qt itself; it is provided for the
+ user's convenience.
+
+ \sa QGraphicsScene, QGraphicsView, {Graphics View Framework}
+*/
+
+/*!
+ \variable QGraphicsItem::Type
+
+ The type value returned by the virtual type() function in standard
+ graphics item classes in Qt. All such standard graphics item
+ classes in Qt are associated with a unique value for Type,
+ e.g. the value returned by QGraphicsPathItem::type() is 2.
+
+ \snippet doc/src/snippets/code/src_gui_graphicsview_qgraphicsitem.cpp 18
+*/
+
+/*!
+ \variable QGraphicsItem::UserType
+
+ The lowest permitted type value for custom items (subclasses
+ of QGraphicsItem or any of the standard items). This value is
+ used in conjunction with a reimplementation of QGraphicsItem::type()
+ and declaring a Type enum value. Example:
+
+ \snippet doc/src/snippets/code/src_gui_graphicsview_qgraphicsitem.cpp 1
+
+ \note UserType = 65536
+*/
+
+/*!
+ \enum QGraphicsItem::GraphicsItemFlag
+
+ This enum describes different flags that you can set on an item to
+ toggle different features in the item's behavior.
+
+ All flags are disabled by default.
+
+ \value ItemIsMovable The item supports interactive movement using
+ the mouse. By clicking on the item and then dragging, the item
+ will move together with the mouse cursor. If the item has
+ children, all children are also moved. If the item is part of a
+ selection, all selected items are also moved. This feature is
+ provided as a convenience through the base implementation of
+ QGraphicsItem's mouse event handlers.
+
+ \value ItemIsSelectable The item supports selection. Enabling this
+ feature will enable setSelected() to toggle selection for the
+ item. It will also let the item be selected automatically as a
+ result of calling QGraphicsScene::setSelectionArea(), by clicking
+ on an item, or by using rubber band selection in QGraphicsView.
+
+ \value ItemIsFocusable The item supports keyboard input focus (i.e., it is
+ an input item). Enabling this flag will allow the item to accept focus,
+ which again allows the delivery of key events to
+ QGraphicsItem::keyPressEvent() and QGraphicsItem::keyReleaseEvent().
+
+ \value ItemClipsToShape The item clips to its own shape. The item cannot
+ draw or receive mouse, tablet, drag and drop or hover events outside its
+ shape. It is disabled by default. This behavior is enforced by
+ QGraphicsView::drawItems() or QGraphicsScene::drawItems(). This flag was
+ introduced in Qt 4.3.
+
+ \value ItemClipsChildrenToShape The item clips the painting of all its
+ descendants to its own shape. Items that are either direct or indirect
+ children of this item cannot draw outside this item's shape. By default,
+ this flag is disabled; children can draw anywhere. This behavior is
+ enforced by QGraphicsView::drawItems() or
+ QGraphicsScene::drawItems(). This flag was introduced in Qt 4.3.
+
+ \value ItemIgnoresTransformations The item ignores inherited
+ transformations (i.e., its position is still anchored to its parent, but
+ the parent or view rotation, zoom or shear transformations are ignored).
+ This flag is useful for keeping text label items horizontal and unscaled,
+ so they will still be readable if the view is transformed. When set, the
+ item's view geometry and scene geometry will be maintained separately. You
+ must call deviceTransform() to map coordinates and detect collisions in
+ the view. By default, this flag is disabled. This flag was introduced in
+ Qt 4.3. \note With this flag set you can still scale the item itself, and
+ that scale transformation will influence the item's children.
+
+ \value ItemIgnoresParentOpacity The item ignores its parent's opacity. The
+ item's effective opacity is the same as its own; it does not combine with
+ the parent's opacity. This flags allows your item to keep its absolute
+ opacity even if the parent is semitransparent. This flag was introduced in
+ Qt 4.5.
+
+ \value ItemDoesntPropagateOpacityToChildren The item doesn't propagate its
+ opacity to its children. This flag allows you to create a semitransparent
+ item that does not affect the opacity of its children. This flag was
+ introduced in Qt 4.5.
+
+ \value ItemStacksBehindParent The item is stacked behind its parent. By
+ default, child items are stacked on top of the parent item. But setting
+ this flag, the child will be stacked behind it. This flag is useful for
+ drop shadow effects and for decoration objects that follow the parent
+ item's geometry without drawing on top of it. This flag was introduced
+ in Qt 4.5.
+
+ \value ItemUsesExtendedStyleOption The item makes use of either
+ \l{QStyleOptionGraphicsItem::} {exposedRect} or
+ \l{QStyleOptionGraphicsItem::} {matrix} in
+ QStyleOptionGraphicsItem. By default, the
+ \l{QStyleOptionGraphicsItem::} {exposedRect} is initialized to the
+ item's boundingRect() and the
+ \l{QStyleOptionGraphicsItem::}{matrix} is untransformed. You can
+ enable this flag for the style options to be set up with more
+ fine-grained values. Note that
+ QStyleOptionGraphicsItem::levelOfDetail is unaffected by this flag
+ and always initialized to 1. Use
+ QStyleOptionGraphicsItem::levelOfDetailFromTransform() if you need
+ a higher value. This flag was introduced in Qt 4.6.
+
+ \value ItemHasNoContents The item does not paint anything (i.e., calling
+ paint() on the item has no effect). You should set this flag on items that
+ do not need to be painted to ensure that Graphics View avoids unnecessary
+ painting preparations. This flag was introduced in Qt 4.6.
+
+ \value ItemSendsGeometryChanges The item enables itemChange()
+ notifications for ItemPositionChange, ItemPositionHasChanged,
+ ItemMatrixChange, ItemTransformChange, ItemTransformHasChanged,
+ ItemRotationChange, ItemRotationHasChanged, ItemScaleChange, ItemScaleHasChanged,
+ ItemTransformOriginPointChange, and ItemTransformOriginPointHasChanged. For
+ performance reasons, these notifications are disabled by default. You must
+ enable this flag to receive notifications for position and transform
+ changes. This flag was introduced in Qt 4.6.
+
+ \value ItemAcceptsInputMethod The item supports input methods typically
+ used for Asian languages.
+ This flag was introduced in Qt 4.6.
+
+ \value ItemNegativeZStacksBehindParent The item automatically
+ stacks behind it's parent if it's z-value is negative. This flag
+ enables setZValue() to toggle ItemStacksBehindParent. This flag
+ was introduced in Qt 4.6.
+
+ \value ItemIsPanel The item is a panel. A panel provides activation and
+ contained focus handling. Only one panel can be active at a time (see
+ QGraphicsItem::isActive()). When no panel is active, QGraphicsScene
+ activates all non-panel items. Window items (i.e.,
+ QGraphicsItem::isWindow() returns true) are panels. This flag was
+ introduced in Qt 4.6.
+
+ \omitvalue ItemIsFocusScope \omit Internal only (for now). \endomit
+
+ \value ItemSendsScenePositionChanges The item enables itemChange()
+ notifications for ItemScenePositionHasChanged. For performance reasons,
+ these notifications are disabled by default. You must enable this flag
+ to receive notifications for scene position changes. This flag was
+ introduced in Qt 4.6.
+
+ \omitvalue ItemStopsClickFocusPropagation \omit The item stops propagating
+ click focus to items underneath when being clicked on. This flag
+ allows you create a non-focusable item that can be clicked on without
+ changing the focus. \endomit
+
+ \omitvalue ItemStopsFocusHandling \omit Same as
+ ItemStopsClickFocusPropagation, but also suppresses focus-out. This flag
+ allows you to completely take over focus handling.
+ This flag was introduced in Qt 4.7. \endomit
+*/
+
+/*!
+ \enum QGraphicsItem::GraphicsItemChange
+
+ This enum describes the state changes that are notified by
+ QGraphicsItem::itemChange(). The notifications are sent as the state
+ changes, and in some cases, adjustments can be made (see the documentation
+ for each change for details).
+
+ Note: Be careful with calling functions on the QGraphicsItem itself inside
+ itemChange(), as certain function calls can lead to unwanted
+ recursion. For example, you cannot call setPos() in itemChange() on an
+ ItemPositionChange notification, as the setPos() function will again call
+ itemChange(ItemPositionChange). Instead, you can return the new, adjusted
+ position from itemChange().
+
+ \value ItemEnabledChange The item's enabled state changes. If the item is
+ presently enabled, it will become disabled, and vice verca. The value
+ argument is the new enabled state (i.e., true or false). Do not call
+ setEnabled() in itemChange() as this notification is delivered. Instead,
+ you can return the new state from itemChange().
+
+ \value ItemEnabledHasChanged The item's enabled state has changed. The
+ value argument is the new enabled state (i.e., true or false). Do not call
+ setEnabled() in itemChange() as this notification is delivered. The return
+ value is ignored.
+
+ \value ItemMatrixChange The item's affine transformation matrix is
+ changing. This value is obsolete; you can use ItemTransformChange instead.
+
+ \value ItemPositionChange The item's position changes. This notification
+ is sent if the ItemSendsGeometryChanges flag is enabled, and when the
+ item's local position changes, relative to its parent (i.e., as a result
+ of calling setPos() or moveBy()). The value argument is the new position
+ (i.e., a QPointF). You can call pos() to get the original position. Do
+ not call setPos() or moveBy() in itemChange() as this notification is
+ delivered; instead, you can return the new, adjusted position from
+ itemChange(). After this notification, QGraphicsItem immediately sends the
+ ItemPositionHasChanged notification if the position changed.
+
+ \value ItemPositionHasChanged The item's position has changed. This
+ notification is sent if the ItemSendsGeometryChanges flag is enabled, and
+ after the item's local position, relative to its parent, has changed. The
+ value argument is the new position (the same as pos()), and QGraphicsItem
+ ignores the return value for this notification (i.e., a read-only
+ notification).
+
+ \value ItemTransformChange The item's transformation matrix changes. This
+ notification is send if the ItemSendsGeometryChanges flag is enabled, and
+ when the item's local transformation matrix changes (i.e., as a result of
+ calling setTransform(). The value argument is the new matrix (i.e., a
+ QTransform); to get the old matrix, call transform(). Do not call
+ setTransform() or set any of the transformation properties in itemChange()
+ as this notification is delivered; instead, you can return the new matrix
+ from itemChange(). This notification is not sent if you change the
+ transformation properties.
+
+ \value ItemTransformHasChanged The item's transformation matrix has
+ changed either because setTransform is called, or one of the
+ transformation properties is changed. This notification is sent if the
+ ItemSendsGeometryChanges flag is enabled, and after the item's local
+ transformation matrix has changed. The value argument is the new matrix
+ (same as transform()), and QGraphicsItem ignores the return value for this
+ notification (i.e., a read-only notification).
+
+ \value ItemRotationChange The item's rotation property changes. This
+ notification is sent if the ItemSendsGeometryChanges flag is enabled, and
+ when the item's rotation property changes (i.e., as a result of calling
+ setRotation()). The value argument is the new rotation (i.e., a double);
+ to get the old rotation, call rotation(). Do not call setRotation() in
+ itemChange() as this notification is delivered; instead, you can return
+ the new rotation from itemChange().
+
+ \value ItemRotationHasChanged The item's rotation property has changed.
+ This notification is sent if the ItemSendsGeometryChanges flag is enabled,
+ and after the item's rotation property has changed. The value argument is
+ the new rotation (i.e., a double), and QGraphicsItem ignores the return
+ value for this notification (i.e., a read-only notification). Do not call
+ setRotation() in itemChange() as this notification is delivered.
+
+ \value ItemScaleChange The item's scale property changes. This notification
+ is sent if the ItemSendsGeometryChanges flag is enabled, and when the item's
+ scale property changes (i.e., as a result of calling setScale()). The value
+ argument is the new scale (i.e., a double); to get the old scale, call
+ scale(). Do not call setScale() in itemChange() as this notification is
+ delivered; instead, you can return the new scale from itemChange().
+
+ \value ItemScaleHasChanged The item's scale property has changed. This
+ notification is sent if the ItemSendsGeometryChanges flag is enabled, and
+ after the item's scale property has changed. The value argument is the new
+ scale (i.e., a double), and QGraphicsItem ignores the return value for this
+ notification (i.e., a read-only notification). Do not call setScale() in
+ itemChange() as this notification is delivered.
+
+ \value ItemTransformOriginPointChange The item's transform origin point
+ property changes. This notification is sent if the ItemSendsGeometryChanges
+ flag is enabled, and when the item's transform origin point property changes
+ (i.e., as a result of calling setTransformOriginPoint()). The value argument
+ is the new origin point (i.e., a QPointF); to get the old origin point, call
+ transformOriginPoint(). Do not call setTransformOriginPoint() in itemChange()
+ as this notification is delivered; instead, you can return the new transform
+ origin point from itemChange().
+
+ \value ItemTransformOriginPointHasChanged The item's transform origin point
+ property has changed. This notification is sent if the ItemSendsGeometryChanges
+ flag is enabled, and after the item's transform origin point property has
+ changed. The value argument is the new origin point (i.e., a QPointF), and
+ QGraphicsItem ignores the return value for this notification (i.e., a read-only
+ notification). Do not call setTransformOriginPoint() in itemChange() as this
+ notification is delivered.
+
+ \value ItemSelectedChange The item's selected state changes. If the item is
+ presently selected, it will become unselected, and vice verca. The value
+ argument is the new selected state (i.e., true or false). Do not call
+ setSelected() in itemChange() as this notification is delivered; instead, you
+ can return the new selected state from itemChange().
+
+ \value ItemSelectedHasChanged The item's selected state has changed. The
+ value argument is the new selected state (i.e., true or false). Do not
+ call setSelected() in itemChange() as this notification is delivered. The
+ return value is ignored.
+
+ \value ItemVisibleChange The item's visible state changes. If the item is
+ presently visible, it will become invisible, and vice verca. The value
+ argument is the new visible state (i.e., true or false). Do not call
+ setVisible() in itemChange() as this notification is delivered; instead,
+ you can return the new visible state from itemChange().
+
+ \value ItemVisibleHasChanged The item's visible state has changed. The
+ value argument is the new visible state (i.e., true or false). Do not call
+ setVisible() in itemChange() as this notification is delivered. The return
+ value is ignored.
+
+ \value ItemParentChange The item's parent changes. The value argument is
+ the new parent item (i.e., a QGraphicsItem pointer). Do not call
+ setParentItem() in itemChange() as this notification is delivered;
+ instead, you can return the new parent from itemChange().
+
+ \value ItemParentHasChanged The item's parent has changed. The value
+ argument is the new parent (i.e., a pointer to a QGraphicsItem). Do not
+ call setParentItem() in itemChange() as this notification is
+ delivered. The return value is ignored.
+
+ \value ItemChildAddedChange A child is added to this item. The value
+ argument is the new child item (i.e., a QGraphicsItem pointer). Do not
+ pass this item to any item's setParentItem() function as this notification
+ is delivered. The return value is unused; you cannot adjust anything in
+ this notification. Note that the new child might not be fully constructed
+ when this notification is sent; calling pure virtual functions on
+ the child can lead to a crash.
+
+ \value ItemChildRemovedChange A child is removed from this item. The value
+ argument is the child item that is about to be removed (i.e., a
+ QGraphicsItem pointer). The return value is unused; you cannot adjust
+ anything in this notification.
+
+ \value ItemSceneChange The item is moved to a new scene. This notification is
+ also sent when the item is added to its initial scene, and when it is removed.
+ The item's scene() is the old scene (or 0 if the item has not been added to a
+ scene yet). The value argument is the new scene (i.e., a QGraphicsScene
+ pointer), or a null pointer if the item is removed from a scene. Do not
+ override this change by passing this item to QGraphicsScene::addItem() as this
+ notification is delivered; instead, you can return the new scene from
+ itemChange(). Use this feature with caution; objecting to a scene change can
+ quickly lead to unwanted recursion.
+
+ \value ItemSceneHasChanged The item's scene has changed. The item's scene() is
+ the new scene. This notification is also sent when the item is added to its
+ initial scene, and when it is removed.The value argument is the new scene
+ (i.e., a pointer to a QGraphicsScene). Do not call setScene() in itemChange()
+ as this notification is delivered. The return value is ignored.
+
+ \value ItemCursorChange The item's cursor changes. The value argument is
+ the new cursor (i.e., a QCursor). Do not call setCursor() in itemChange()
+ as this notification is delivered. Instead, you can return a new cursor
+ from itemChange().
+
+ \value ItemCursorHasChanged The item's cursor has changed. The value
+ argument is the new cursor (i.e., a QCursor). Do not call setCursor() as
+ this notification is delivered. The return value is ignored.
+
+ \value ItemToolTipChange The item's tooltip changes. The value argument is
+ the new tooltip (i.e., a QToolTip). Do not call setToolTip() in
+ itemChange() as this notification is delivered. Instead, you can return a
+ new tooltip from itemChange().
+
+ \value ItemToolTipHasChanged The item's tooltip has changed. The value
+ argument is the new tooltip (i.e., a QToolTip). Do not call setToolTip()
+ as this notification is delivered. The return value is ignored.
+
+ \value ItemFlagsChange The item's flags change. The value argument is the
+ new flags (i.e., a quint32). Do not call setFlags() in itemChange() as
+ this notification is delivered. Instead, you can return the new flags from
+ itemChange().
+
+ \value ItemFlagsHaveChanged The item's flags have changed. The value
+ argument is the new flags (i.e., a quint32). Do not call setFlags() in
+ itemChange() as this notification is delivered. The return value is
+ ignored.
+
+ \value ItemZValueChange The item's Z-value changes. The value argument is
+ the new Z-value (i.e., a double). Do not call setZValue() in itemChange()
+ as this notification is delivered. Instead, you can return a new Z-value
+ from itemChange().
+
+ \value ItemZValueHasChanged The item's Z-value has changed. The value
+ argument is the new Z-value (i.e., a double). Do not call setZValue() as
+ this notification is delivered. The return value is ignored.
+
+ \value ItemOpacityChange The item's opacity changes. The value argument is
+ the new opacity (i.e., a double). Do not call setOpacity() in itemChange()
+ as this notification is delivered. Instead, you can return a new opacity
+ from itemChange().
+
+ \value ItemOpacityHasChanged The item's opacity has changed. The value
+ argument is the new opacity (i.e., a double). Do not call setOpacity() as
+ this notification is delivered. The return value is ignored.
+
+ \value ItemScenePositionHasChanged The item's scene position has changed.
+ This notification is sent if the ItemSendsScenePositionChanges flag is
+ enabled, and after the item's scene position has changed (i.e., the
+ position or transformation of the item itself or the position or
+ transformation of any ancestor has changed). The value argument is the
+ new scene position (the same as scenePos()), and QGraphicsItem ignores
+ the return value for this notification (i.e., a read-only notification).
+*/
+
+/*!
+ \enum QGraphicsItem::CacheMode
+ \since 4.4
+
+ This enum describes QGraphicsItem's cache modes. Caching is used to speed
+ up rendering by allocating and rendering to an off-screen pixel buffer,
+ which can be reused when the item requires redrawing. For some paint
+ devices, the cache is stored directly in graphics memory, which makes
+ rendering very quick.
+
+ \value NoCache The default; all item caching is
+ disabled. QGraphicsItem::paint() is called every time the item needs
+ redrawing.
+
+ \value ItemCoordinateCache Caching is enabled for the item's logical
+ (local) coordinate system. QGraphicsItem creates an off-screen pixel
+ buffer with a configurable size / resolution that you can pass to
+ QGraphicsItem::setCacheMode(). Rendering quality will typically degrade,
+ depending on the resolution of the cache and the item transformation. The
+ first time the item is redrawn, it will render itself into the cache, and
+ the cache is then reused for every subsequent expose. The cache is also
+ reused as the item is transformed. To adjust the resolution of the cache,
+ you can call setCacheMode() again.
+
+ \value DeviceCoordinateCache Caching is enabled at the paint device level,
+ in device coordinates. This mode is for items that can move, but are not
+ rotated, scaled or sheared. If the item is transformed directly or
+ indirectly, the cache will be regenerated automatically. Unlike
+ ItemCoordinateCacheMode, DeviceCoordinateCache always renders at maximum
+ quality.
+
+ \sa QGraphicsItem::setCacheMode()
+*/
+
+/*!
+ \enum QGraphicsItem::Extension
+ \internal
+
+ Note: This is provided as a hook to avoid future problems related
+ to adding virtual functions. See also extension(),
+ supportsExtension() and setExtension().
+*/
+
+/*!
+ \enum QGraphicsItem::PanelModality
+ \since 4.6
+
+ This enum specifies the behavior of a modal panel. A modal panel
+ is one that blocks input to other panels. Note that items that
+ are children of a modal panel are not blocked.
+
+ The values are:
+
+ \value NonModal The panel is not modal and does not block input to
+ other panels. This is the default value for panels.
+
+ \value PanelModal The panel is modal to a single item hierarchy
+ and blocks input to its parent pane, all grandparent panels, and
+ all siblings of its parent and grandparent panels.
+
+ \value SceneModal The window is modal to the entire scene and
+ blocks input to all panels.
+
+ \sa QGraphicsItem::setPanelModality(), QGraphicsItem::panelModality(), QGraphicsItem::ItemIsPanel
+*/
+
+#include "qgraphicsitem.h"
+
+#ifndef QT_NO_GRAPHICSVIEW
+
+#include "qgraphicsscene.h"
+#include "qgraphicsscene_p.h"
+#include "qgraphicssceneevent.h"
+#include "qgraphicsview.h"
+#include "qgraphicswidget.h"
+#include "qgraphicsproxywidget.h"
+#include "qgraphicsscenebsptreeindex_p.h"
+#include <QtCore/qbitarray.h>
+#include <QtCore/qdebug.h>
+#include <QtCore/qpoint.h>
+#include <QtCore/qstack.h>
+#include <QtCore/qtimer.h>
+#include <QtCore/qvariant.h>
+#include <QtCore/qvarlengtharray.h>
+#include <QtCore/qnumeric.h>
+#include <QtGui/qapplication.h>
+#include <QtGui/qbitmap.h>
+#include <QtGui/qpainter.h>
+#include <QtGui/qpainterpath.h>
+#include <QtGui/qpixmapcache.h>
+#include <QtGui/qstyleoption.h>
+#include <QtGui/qevent.h>
+#include <QtGui/qinputcontext.h>
+#include <QtGui/qgraphicseffect.h>
+#ifndef QT_NO_ACCESSIBILITY
+# include "qaccessible.h"
+#endif
+
+#include <private/qgraphicsitem_p.h>
+#include <private/qgraphicswidget_p.h>
+#include <private/qtextcontrol_p.h>
+#include <private/qtextdocumentlayout_p.h>
+#include <private/qtextengine_p.h>
+#include <private/qwidget_p.h>
+#include <private/qapplication_p.h>
+
+#ifdef Q_WS_X11
+#include <private/qt_x11_p.h>
+#include <private/qpixmap_x11_p.h>
+#endif
+
+#include <private/qgesturemanager_p.h>
+
+#include <math.h>
+
+QT_BEGIN_NAMESPACE
+
+static inline void _q_adjustRect(QRect *rect)
+{
+ Q_ASSERT(rect);
+ if (!rect->width())
+ rect->adjust(0, 0, 1, 0);
+ if (!rect->height())
+ rect->adjust(0, 0, 0, 1);
+}
+
+/*
+ ### Move this into QGraphicsItemPrivate
+ */
+class QGraphicsItemCustomDataStore
+{
+public:
+ QMap<const QGraphicsItem *, QMap<int, QVariant> > data;
+};
+Q_GLOBAL_STATIC(QGraphicsItemCustomDataStore, qt_dataStore)
+
+/*!
+ \internal
+
+ Returns a QPainterPath of \a path when stroked with the \a pen.
+ Ignoring dash pattern.
+*/
+static QPainterPath qt_graphicsItem_shapeFromPath(const QPainterPath &path, const QPen &pen)
+{
+ // We unfortunately need this hack as QPainterPathStroker will set a width of 1.0
+ // if we pass a value of 0.0 to QPainterPathStroker::setWidth()
+ const qreal penWidthZero = qreal(0.00000001);
+
+ if (path == QPainterPath())
+ return path;
+ QPainterPathStroker ps;
+ ps.setCapStyle(pen.capStyle());
+ if (pen.widthF() <= 0.0)
+ ps.setWidth(penWidthZero);
+ else
+ ps.setWidth(pen.widthF());
+ ps.setJoinStyle(pen.joinStyle());
+ ps.setMiterLimit(pen.miterLimit());
+ QPainterPath p = ps.createStroke(path);
+ p.addPath(path);
+ return p;
+}
+
+/*!
+ \internal
+
+ Propagates the ancestor flag \a flag with value \a enabled to all this
+ item's children. If \a root is false, the flag is also set on this item
+ (default is true).
+*/
+void QGraphicsItemPrivate::updateAncestorFlag(QGraphicsItem::GraphicsItemFlag childFlag,
+ AncestorFlag flag, bool enabled, bool root)
+{
+ Q_Q(QGraphicsItem);
+ if (root) {
+ // For root items only. This is the item that has either enabled or
+ // disabled \a childFlag, or has been reparented.
+ switch (int(childFlag)) {
+ case -2:
+ flag = AncestorFiltersChildEvents;
+ enabled = q->filtersChildEvents();
+ break;
+ case -1:
+ flag = AncestorHandlesChildEvents;
+ enabled = q->handlesChildEvents();
+ break;
+ case QGraphicsItem::ItemClipsChildrenToShape:
+ flag = AncestorClipsChildren;
+ enabled = flags & QGraphicsItem::ItemClipsChildrenToShape;
+ break;
+ case QGraphicsItem::ItemIgnoresTransformations:
+ flag = AncestorIgnoresTransformations;
+ enabled = flags & QGraphicsItem::ItemIgnoresTransformations;
+ break;
+ default:
+ return;
+ }
+
+ if (parent) {
+ // Inherit the enabled-state from our parents.
+ if ((parent->d_ptr->ancestorFlags & flag)
+ || (int(parent->d_ptr->flags & childFlag) == childFlag)
+ || (childFlag == -1 && parent->d_ptr->handlesChildEvents)
+ || (childFlag == -2 && parent->d_ptr->filtersDescendantEvents)) {
+ enabled = true;
+ ancestorFlags |= flag;
+ } else {
+ ancestorFlags &= ~flag;
+ }
+ } else {
+ // Top-level root items don't have any ancestors, so there are no
+ // ancestor flags either.
+ ancestorFlags = 0;
+ }
+ } else {
+ // Don't set or propagate the ancestor flag if it's already correct.
+ if (((ancestorFlags & flag) && enabled) || (!(ancestorFlags & flag) && !enabled))
+ return;
+
+ // Set the flag.
+ if (enabled)
+ ancestorFlags |= flag;
+ else
+ ancestorFlags &= ~flag;
+
+ // Don't process children if the item has the main flag set on itself.
+ if ((childFlag != -1 && int(flags & childFlag) == childFlag)
+ || (int(childFlag) == -1 && handlesChildEvents)
+ || (int(childFlag) == -2 && filtersDescendantEvents))
+ return;
+ }
+
+ for (int i = 0; i < children.size(); ++i)
+ children.at(i)->d_ptr->updateAncestorFlag(childFlag, flag, enabled, false);
+}
+
+void QGraphicsItemPrivate::updateAncestorFlags()
+{
+ int flags = 0;
+ if (parent) {
+ // Inherit the parent's ancestor flags.
+ QGraphicsItemPrivate *pd = parent->d_ptr.data();
+ flags = pd->ancestorFlags;
+
+ // Add in flags from the parent.
+ if (pd->filtersDescendantEvents)
+ flags |= AncestorFiltersChildEvents;
+ if (pd->handlesChildEvents)
+ flags |= AncestorHandlesChildEvents;
+ if (pd->flags & QGraphicsItem::ItemClipsChildrenToShape)
+ flags |= AncestorClipsChildren;
+ if (pd->flags & QGraphicsItem::ItemIgnoresTransformations)
+ flags |= AncestorIgnoresTransformations;
+ }
+
+ if (ancestorFlags == flags)
+ return; // No change; stop propagation.
+ ancestorFlags = flags;
+
+ // Propagate to children recursively.
+ for (int i = 0; i < children.size(); ++i)
+ children.at(i)->d_ptr->updateAncestorFlags();
+}
+
+/*!
+ \internal
+
+ Propagates item group membership.
+*/
+void QGraphicsItemPrivate::setIsMemberOfGroup(bool enabled)
+{
+ Q_Q(QGraphicsItem);
+ isMemberOfGroup = enabled;
+ if (!qgraphicsitem_cast<QGraphicsItemGroup *>(q)) {
+ foreach (QGraphicsItem *child, children)
+ child->d_func()->setIsMemberOfGroup(enabled);
+ }
+}
+
+/*!
+ \internal
+
+ Maps any item pos properties of \a event to \a item's coordinate system.
+*/
+void QGraphicsItemPrivate::remapItemPos(QEvent *event, QGraphicsItem *item)
+{
+ Q_Q(QGraphicsItem);
+ switch (event->type()) {
+ case QEvent::GraphicsSceneMouseMove:
+ case QEvent::GraphicsSceneMousePress:
+ case QEvent::GraphicsSceneMouseRelease:
+ case QEvent::GraphicsSceneMouseDoubleClick: {
+ QGraphicsSceneMouseEvent *mouseEvent = static_cast<QGraphicsSceneMouseEvent *>(event);
+ mouseEvent->setPos(item->mapFromItem(q, mouseEvent->pos()));
+ mouseEvent->setLastPos(item->mapFromItem(q, mouseEvent->pos()));
+ for (int i = 0x1; i <= 0x10; i <<= 1) {
+ if (mouseEvent->buttons() & i) {
+ Qt::MouseButton button = Qt::MouseButton(i);
+ mouseEvent->setButtonDownPos(button, item->mapFromItem(q, mouseEvent->buttonDownPos(button)));
+ }
+ }
+ break;
+ }
+ case QEvent::GraphicsSceneWheel: {
+ QGraphicsSceneWheelEvent *wheelEvent = static_cast<QGraphicsSceneWheelEvent *>(event);
+ wheelEvent->setPos(item->mapFromItem(q, wheelEvent->pos()));
+ break;
+ }
+ case QEvent::GraphicsSceneContextMenu: {
+ QGraphicsSceneContextMenuEvent *contextEvent = static_cast<QGraphicsSceneContextMenuEvent *>(event);
+ contextEvent->setPos(item->mapFromItem(q, contextEvent->pos()));
+ break;
+ }
+ case QEvent::GraphicsSceneHoverMove: {
+ QGraphicsSceneHoverEvent *hoverEvent = static_cast<QGraphicsSceneHoverEvent *>(event);
+ hoverEvent->setPos(item->mapFromItem(q, hoverEvent->pos()));
+ break;
+ }
+ default:
+ break;
+ }
+}
+
+/*!
+ \internal
+
+ Maps the point \a pos from scene to item coordinates. If \a view is passed and the item
+ is untransformable, this function will correctly map \a pos from the scene using the
+ view's transformation.
+*/
+QPointF QGraphicsItemPrivate::genericMapFromScene(const QPointF &pos,
+ const QWidget *viewport) const
+{
+ Q_Q(const QGraphicsItem);
+ if (!itemIsUntransformable())
+ return q->mapFromScene(pos);
+ QGraphicsView *view = 0;
+ if (viewport)
+ view = qobject_cast<QGraphicsView *>(viewport->parentWidget());
+ if (!view)
+ return q->mapFromScene(pos);
+ // ### More ping pong than needed.
+ return q->deviceTransform(view->viewportTransform()).inverted().map(view->mapFromScene(pos));
+}
+
+/*!
+ \internal
+
+ Combines this item's position and transform onto \a transform.
+
+ If you need to change this function (e.g., adding more transformation
+ modes / options), make sure to change all places marked with COMBINE.
+*/
+void QGraphicsItemPrivate::combineTransformToParent(QTransform *x, const QTransform *viewTransform) const
+{
+ // COMBINE
+ if (viewTransform && itemIsUntransformable()) {
+ *x = q_ptr->deviceTransform(*viewTransform);
+ } else {
+ if (transformData)
+ *x *= transformData->computedFullTransform();
+ if (!pos.isNull())
+ *x *= QTransform::fromTranslate(pos.x(), pos.y());
+ }
+}
+
+/*!
+ \internal
+
+ Combines this item's position and transform onto \a transform.
+
+ If you need to change this function (e.g., adding more transformation
+ modes / options), make sure to change QGraphicsItem::deviceTransform() as
+ well.
+*/
+void QGraphicsItemPrivate::combineTransformFromParent(QTransform *x, const QTransform *viewTransform) const
+{
+ // COMBINE
+ if (viewTransform && itemIsUntransformable()) {
+ *x = q_ptr->deviceTransform(*viewTransform);
+ } else {
+ x->translate(pos.x(), pos.y());
+ if (transformData)
+ *x = transformData->computedFullTransform(x);
+ }
+}
+
+void QGraphicsItemPrivate::updateSceneTransformFromParent()
+{
+ if (parent) {
+ Q_ASSERT(!parent->d_ptr->dirtySceneTransform);
+ if (parent->d_ptr->sceneTransformTranslateOnly) {
+ sceneTransform = QTransform::fromTranslate(parent->d_ptr->sceneTransform.dx() + pos.x(),
+ parent->d_ptr->sceneTransform.dy() + pos.y());
+ } else {
+ sceneTransform = parent->d_ptr->sceneTransform;
+ sceneTransform.translate(pos.x(), pos.y());
+ }
+ if (transformData) {
+ sceneTransform = transformData->computedFullTransform(&sceneTransform);
+ sceneTransformTranslateOnly = (sceneTransform.type() <= QTransform::TxTranslate);
+ } else {
+ sceneTransformTranslateOnly = parent->d_ptr->sceneTransformTranslateOnly;
+ }
+ } else if (!transformData) {
+ sceneTransform = QTransform::fromTranslate(pos.x(), pos.y());
+ sceneTransformTranslateOnly = 1;
+ } else if (transformData->onlyTransform) {
+ sceneTransform = transformData->transform;
+ if (!pos.isNull())
+ sceneTransform *= QTransform::fromTranslate(pos.x(), pos.y());
+ sceneTransformTranslateOnly = (sceneTransform.type() <= QTransform::TxTranslate);
+ } else if (pos.isNull()) {
+ sceneTransform = transformData->computedFullTransform();
+ sceneTransformTranslateOnly = (sceneTransform.type() <= QTransform::TxTranslate);
+ } else {
+ sceneTransform = QTransform::fromTranslate(pos.x(), pos.y());
+ sceneTransform = transformData->computedFullTransform(&sceneTransform);
+ sceneTransformTranslateOnly = (sceneTransform.type() <= QTransform::TxTranslate);
+ }
+ dirtySceneTransform = 0;
+}
+
+/*!
+ \internal
+
+ This helper function helped us add input method query support in
+ Qt 4.4.1 without having to reimplement the inputMethodQuery()
+ function in QGraphicsProxyWidget. ### Qt 5: Remove. We cannot
+ remove it in 4.5+ even if we do reimplement the function properly,
+ because apps compiled with 4.4 will not be able to call the
+ reimplementation.
+*/
+QVariant QGraphicsItemPrivate::inputMethodQueryHelper(Qt::InputMethodQuery query) const
+{
+ Q_UNUSED(query);
+ return QVariant();
+}
+
+/*!
+ \internal
+
+ Make sure not to trigger any pure virtual function calls (e.g.,
+ prepareGeometryChange) if the item is in its destructor, i.e.
+ inDestructor is 1.
+*/
+void QGraphicsItemPrivate::setParentItemHelper(QGraphicsItem *newParent, const QVariant *newParentVariant,
+ const QVariant *thisPointerVariant)
+{
+ Q_Q(QGraphicsItem);
+ if (newParent == parent)
+ return;
+
+ if (isWidget)
+ static_cast<QGraphicsWidgetPrivate *>(this)->fixFocusChainBeforeReparenting((newParent &&
+ newParent->isWidget()) ? static_cast<QGraphicsWidget *>(newParent) : 0,
+ scene);
+ if (scene) {
+ // Deliver the change to the index
+ if (scene->d_func()->indexMethod != QGraphicsScene::NoIndex)
+ scene->d_func()->index->itemChange(q, QGraphicsItem::ItemParentChange, newParent);
+
+ // Disable scene pos notifications for old ancestors
+ if (scenePosDescendants || (flags & QGraphicsItem::ItemSendsScenePositionChanges))
+ scene->d_func()->setScenePosItemEnabled(q, false);
+ }
+
+ if (subFocusItem && parent) {
+ // Make sure none of the old parents point to this guy.
+ subFocusItem->d_ptr->clearSubFocus(parent);
+ }
+
+ // We anticipate geometry changes. If the item is deleted, it will be
+ // removed from the index at a later stage, and the whole scene will be
+ // updated.
+ if (!inDestructor)
+ q_ptr->prepareGeometryChange();
+
+ if (parent) {
+ // Remove from current parent
+ parent->d_ptr->removeChild(q);
+ if (thisPointerVariant)
+ parent->itemChange(QGraphicsItem::ItemChildRemovedChange, *thisPointerVariant);
+ }
+
+ // Update toplevelitem list. If this item is being deleted, its parent
+ // will be 0 but we don't want to register/unregister it in the TLI list.
+ if (scene && !inDestructor) {
+ if (parent && !newParent) {
+ scene->d_func()->registerTopLevelItem(q);
+ } else if (!parent && newParent) {
+ scene->d_func()->unregisterTopLevelItem(q);
+ }
+ }
+
+ // Ensure any last parent focus scope does not point to this item or any of
+ // its descendents.
+ QGraphicsItem *p = parent;
+ QGraphicsItem *parentFocusScopeItem = 0;
+ while (p) {
+ if (p->d_ptr->flags & QGraphicsItem::ItemIsFocusScope) {
+ // If this item's focus scope's focus scope item points
+ // to this item or a descendent, then clear it.
+ QGraphicsItem *fsi = p->d_ptr->focusScopeItem;
+ if (q_ptr == fsi || q_ptr->isAncestorOf(fsi)) {
+ parentFocusScopeItem = fsi;
+ p->d_ptr->focusScopeItem = 0;
+ fsi->d_ptr->focusScopeItemChange(false);
+ }
+ break;
+ }
+ p = p->d_ptr->parent;
+ }
+
+ // Update graphics effect optimization flag
+ if (newParent && (graphicsEffect || mayHaveChildWithGraphicsEffect))
+ newParent->d_ptr->updateChildWithGraphicsEffectFlagRecursively();
+
+ // Update focus scope item ptr in new scope.
+ QGraphicsItem *newFocusScopeItem = subFocusItem ? subFocusItem : parentFocusScopeItem;
+ if (newFocusScopeItem && newParent) {
+ if (subFocusItem) {
+ // Find the subFocusItem's topmost focus scope.
+ QGraphicsItem *ancestorScope = 0;
+ QGraphicsItem *p = subFocusItem->d_ptr->parent;
+ while (p) {
+ if (p->d_ptr->flags & QGraphicsItem::ItemIsFocusScope)
+ ancestorScope = p;
+ if (p->d_ptr->flags & QGraphicsItem::ItemIsPanel)
+ break;
+ p = p->d_ptr->parent;
+ }
+ if (ancestorScope)
+ newFocusScopeItem = ancestorScope;
+ }
+
+ QGraphicsItem *p = newParent;
+ while (p) {
+ if (p->d_ptr->flags & QGraphicsItem::ItemIsFocusScope) {
+ p->d_ptr->focusScopeItem = newFocusScopeItem;
+ newFocusScopeItem->d_ptr->focusScopeItemChange(true);
+ // Ensure the new item is no longer the subFocusItem. The
+ // only way to set focus on a child of a focus scope is
+ // by setting focus on the scope itself.
+ if (subFocusItem && !p->focusItem())
+ subFocusItem->d_ptr->clearSubFocus();
+ break;
+ }
+ p = p->d_ptr->parent;
+ }
+ }
+
+ // Resolve depth.
+ invalidateDepthRecursively();
+
+ if ((parent = newParent)) {
+ if (parent->d_func()->scene && parent->d_func()->scene != scene) {
+ // Move this item to its new parent's scene
+ parent->d_func()->scene->addItem(q);
+ } else if (!parent->d_func()->scene && scene) {
+ // Remove this item from its former scene
+ scene->removeItem(q);
+ }
+
+ parent->d_ptr->addChild(q);
+ if (thisPointerVariant)
+ parent->itemChange(QGraphicsItem::ItemChildAddedChange, *thisPointerVariant);
+ if (scene) {
+ // Re-enable scene pos notifications for new ancestors
+ if (scenePosDescendants || (flags & QGraphicsItem::ItemSendsScenePositionChanges))
+ scene->d_func()->setScenePosItemEnabled(q, true);
+ }
+
+ // Propagate dirty flags to the new parent
+ markParentDirty(/*updateBoundingRect=*/true);
+
+ // Inherit ancestor flags from the new parent.
+ updateAncestorFlags();
+
+ // Update item visible / enabled.
+ if (parent->d_ptr->visible != visible) {
+ if (!parent->d_ptr->visible || !explicitlyHidden)
+ setVisibleHelper(parent->d_ptr->visible, /* explicit = */ false, /* update = */ false);
+ }
+ if (parent->isEnabled() != enabled) {
+ if (!parent->d_ptr->enabled || !explicitlyDisabled)
+ setEnabledHelper(parent->d_ptr->enabled, /* explicit = */ false, /* update = */ false);
+ }
+
+ // Auto-activate if visible and the parent is active.
+ if (visible && parent->isActive())
+ q->setActive(true);
+ } else {
+ // Inherit ancestor flags from the new parent.
+ updateAncestorFlags();
+
+ if (!inDestructor) {
+ // Update item visible / enabled.
+ if (!visible && !explicitlyHidden)
+ setVisibleHelper(true, /* explicit = */ false);
+ if (!enabled && !explicitlyDisabled)
+ setEnabledHelper(true, /* explicit = */ false);
+ }
+ }
+
+ dirtySceneTransform = 1;
+ if (!inDestructor && (transformData || (newParent && newParent->d_ptr->transformData)))
+ transformChanged();
+
+ // Restore the sub focus chain.
+ if (subFocusItem) {
+ subFocusItem->d_ptr->setSubFocus(newParent);
+ if (parent && parent->isActive())
+ subFocusItem->setFocus();
+ }
+
+ // Deliver post-change notification
+ if (newParentVariant)
+ q->itemChange(QGraphicsItem::ItemParentHasChanged, *newParentVariant);
+
+ if (isObject)
+ emit static_cast<QGraphicsObject *>(q)->parentChanged();
+}
+
+/*!
+ \internal
+
+ Returns the bounding rect of this item's children (excluding itself).
+*/
+void QGraphicsItemPrivate::childrenBoundingRectHelper(QTransform *x, QRectF *rect, QGraphicsItem *topMostEffectItem)
+{
+ Q_Q(QGraphicsItem);
+
+ QRectF childrenRect;
+ QRectF *result = rect;
+ rect = &childrenRect;
+ const bool setTopMostEffectItem = !topMostEffectItem;
+
+ for (int i = 0; i < children.size(); ++i) {
+ QGraphicsItem *child = children.at(i);
+ QGraphicsItemPrivate *childd = child->d_ptr.data();
+ if (setTopMostEffectItem)
+ topMostEffectItem = child;
+ bool hasPos = !childd->pos.isNull();
+ if (hasPos || childd->transformData) {
+ // COMBINE
+ QTransform matrix = childd->transformToParent();
+ if (x)
+ matrix *= *x;
+ *rect |= matrix.mapRect(child->d_ptr->effectiveBoundingRect(topMostEffectItem));
+ if (!childd->children.isEmpty())
+ childd->childrenBoundingRectHelper(&matrix, rect, topMostEffectItem);
+ } else {
+ if (x)
+ *rect |= x->mapRect(child->d_ptr->effectiveBoundingRect(topMostEffectItem));
+ else
+ *rect |= child->d_ptr->effectiveBoundingRect(topMostEffectItem);
+ if (!childd->children.isEmpty())
+ childd->childrenBoundingRectHelper(x, rect, topMostEffectItem);
+ }
+ }
+
+ if (flags & QGraphicsItem::ItemClipsChildrenToShape){
+ if (x)
+ *rect &= x->mapRect(q->boundingRect());
+ else
+ *rect &= q->boundingRect();
+ }
+
+ *result |= *rect;
+}
+
+void QGraphicsItemPrivate::initStyleOption(QStyleOptionGraphicsItem *option, const QTransform &worldTransform,
+ const QRegion &exposedRegion, bool allItems) const
+{
+ Q_ASSERT(option);
+ Q_Q(const QGraphicsItem);
+
+ // Initialize standard QStyleOption values.
+ const QRectF brect = q->boundingRect();
+ option->state = QStyle::State_None;
+ option->rect = brect.toRect();
+ option->levelOfDetail = 1;
+ option->exposedRect = brect;
+ if (selected)
+ option->state |= QStyle::State_Selected;
+ if (enabled)
+ option->state |= QStyle::State_Enabled;
+ if (q->hasFocus())
+ option->state |= QStyle::State_HasFocus;
+ if (scene) {
+ if (scene->d_func()->hoverItems.contains(q_ptr))
+ option->state |= QStyle::State_MouseOver;
+ if (q == scene->mouseGrabberItem())
+ option->state |= QStyle::State_Sunken;
+ }
+
+ if (!(flags & QGraphicsItem::ItemUsesExtendedStyleOption))
+ return;
+
+ // Initialize QStyleOptionGraphicsItem specific values (matrix, exposedRect).
+ option->matrix = worldTransform.toAffine(); //### discards perspective
+
+ if (!allItems) {
+ // Determine the item's exposed area
+ option->exposedRect = QRectF();
+ const QTransform reverseMap = worldTransform.inverted();
+ const QVector<QRect> exposedRects(exposedRegion.rects());
+ for (int i = 0; i < exposedRects.size(); ++i) {
+ option->exposedRect |= reverseMap.mapRect(QRectF(exposedRects.at(i)));
+ if (option->exposedRect.contains(brect))
+ break;
+ }
+ option->exposedRect &= brect;
+ }
+}
+
+/*!
+ \internal
+
+ Empty all cached pixmaps from the pixmap cache.
+*/
+void QGraphicsItemCache::purge()
+{
+ QPixmapCache::remove(key);
+ key = QPixmapCache::Key();
+ QMutableMapIterator<QPaintDevice *, DeviceData> it(deviceData);
+ while (it.hasNext()) {
+ DeviceData &data = it.next().value();
+ QPixmapCache::remove(data.key);
+ data.cacheIndent = QPoint();
+ }
+ deviceData.clear();
+ allExposed = true;
+ exposed.clear();
+}
+
+/*!
+ Constructs a QGraphicsItem with the given \a parent item.
+ It does not modify the parent object returned by QObject::parent().
+
+ If \a parent is 0, you can add the item to a scene by calling
+ QGraphicsScene::addItem(). The item will then become a top-level item.
+
+ \sa QGraphicsScene::addItem(), setParentItem()
+*/
+QGraphicsItem::QGraphicsItem(QGraphicsItem *parent
+#ifndef Q_QDOC
+ // obsolete argument
+ , QGraphicsScene *scene
+#endif
+ )
+ : d_ptr(new QGraphicsItemPrivate)
+{
+ d_ptr->q_ptr = this;
+ setParentItem(parent);
+
+ if (scene && parent && parent->scene() != scene) {
+ qWarning("QGraphicsItem::QGraphicsItem: ignoring scene (%p), which is"
+ " different from parent's scene (%p)",
+ scene, parent->scene());
+ return;
+ }
+ if (scene && !parent)
+ scene->addItem(this);
+}
+
+/*!
+ \internal
+*/
+QGraphicsItem::QGraphicsItem(QGraphicsItemPrivate &dd, QGraphicsItem *parent,
+ QGraphicsScene *scene)
+ : d_ptr(&dd)
+{
+ d_ptr->q_ptr = this;
+ setParentItem(parent);
+
+ if (scene && parent && parent->scene() != scene) {
+ qWarning("QGraphicsItem::QGraphicsItem: ignoring scene (%p), which is"
+ " different from parent's scene (%p)",
+ scene, parent->scene());
+ return;
+ }
+ if (scene && !parent)
+ scene->addItem(this);
+}
+
+/*!
+ Destroys the QGraphicsItem and all its children. If this item is currently
+ associated with a scene, the item will be removed from the scene before it
+ is deleted.
+
+ \note It is more efficient to remove the item from the QGraphicsScene before
+ destroying the item.
+*/
+QGraphicsItem::~QGraphicsItem()
+{
+ if (d_ptr->isObject) {
+ QGraphicsObject *o = static_cast<QGraphicsObject *>(this);
+ QObjectPrivate *p = QObjectPrivate::get(o);
+ p->wasDeleted = true;
+ if (p->declarativeData) {
+ QAbstractDeclarativeData::destroyed(p->declarativeData, o);
+ p->declarativeData = 0;
+ }
+ }
+
+ d_ptr->inDestructor = 1;
+ d_ptr->removeExtraItemCache();
+
+#ifndef QT_NO_GESTURES
+ if (d_ptr->isObject && !d_ptr->gestureContext.isEmpty()) {
+ QGraphicsObject *o = static_cast<QGraphicsObject *>(this);
+ if (QGestureManager *manager = QGestureManager::instance()) {
+ foreach (Qt::GestureType type, d_ptr->gestureContext.keys())
+ manager->cleanupCachedGestures(o, type);
+ }
+ }
+#endif
+
+ clearFocus();
+
+ // Update focus scope item ptr.
+ QGraphicsItem *p = d_ptr->parent;
+ while (p) {
+ if (p->flags() & ItemIsFocusScope) {
+ if (p->d_ptr->focusScopeItem == this)
+ p->d_ptr->focusScopeItem = 0;
+ break;
+ }
+ p = p->d_ptr->parent;
+ }
+
+ if (!d_ptr->children.isEmpty()) {
+ while (!d_ptr->children.isEmpty())
+ delete d_ptr->children.first();
+ Q_ASSERT(d_ptr->children.isEmpty());
+ }
+
+ if (d_ptr->scene) {
+ d_ptr->scene->d_func()->removeItemHelper(this);
+ } else {
+ d_ptr->resetFocusProxy();
+ setParentItem(0);
+ }
+
+#ifndef QT_NO_GRAPHICSEFFECT
+ delete d_ptr->graphicsEffect;
+#endif //QT_NO_GRAPHICSEFFECT
+ if (d_ptr->transformData) {
+ for(int i = 0; i < d_ptr->transformData->graphicsTransforms.size(); ++i) {
+ QGraphicsTransform *t = d_ptr->transformData->graphicsTransforms.at(i);
+ static_cast<QGraphicsTransformPrivate *>(t->d_ptr.data())->item = 0;
+ delete t;
+ }
+ }
+ delete d_ptr->transformData;
+
+ if (QGraphicsItemCustomDataStore *dataStore = qt_dataStore())
+ dataStore->data.remove(this);
+}
+
+/*!
+ Returns the current scene for the item, or 0 if the item is not stored in
+ a scene.
+
+ To add or move an item to a scene, call QGraphicsScene::addItem().
+*/
+QGraphicsScene *QGraphicsItem::scene() const
+{
+ return d_ptr->scene;
+}
+
+/*!
+ Returns a pointer to this item's item group, or 0 if this item is not
+ member of a group.
+
+ \sa QGraphicsItemGroup, QGraphicsScene::createItemGroup()
+*/
+QGraphicsItemGroup *QGraphicsItem::group() const
+{
+ if (!d_ptr->isMemberOfGroup)
+ return 0;
+ QGraphicsItem *parent = const_cast<QGraphicsItem *>(this);
+ while ((parent = parent->d_ptr->parent)) {
+ if (QGraphicsItemGroup *group = qgraphicsitem_cast<QGraphicsItemGroup *>(parent))
+ return group;
+ }
+ // Unreachable; if d_ptr->isMemberOfGroup is != 0, then one parent of this
+ // item is a group item.
+ return 0;
+}
+
+/*!
+ Adds this item to the item group \a group. If \a group is 0, this item is
+ removed from any current group and added as a child of the previous
+ group's parent.
+
+ \sa group(), QGraphicsScene::createItemGroup()
+*/
+void QGraphicsItem::setGroup(QGraphicsItemGroup *group)
+{
+ if (!group) {
+ if (QGraphicsItemGroup *group = this->group())
+ group->removeFromGroup(this);
+ } else {
+ group->addToGroup(this);
+ }
+}
+
+/*!
+ Returns a pointer to this item's parent item. If this item does not have a
+ parent, 0 is returned.
+
+ \sa setParentItem(), childItems()
+*/
+QGraphicsItem *QGraphicsItem::parentItem() const
+{
+ return d_ptr->parent;
+}
+
+/*!
+ Returns this item's top-level item. The top-level item is the item's
+ topmost ancestor item whose parent is 0. If an item has no parent, its own
+ pointer is returned (i.e., a top-level item is its own top-level item).
+
+ \sa parentItem()
+*/
+QGraphicsItem *QGraphicsItem::topLevelItem() const
+{
+ QGraphicsItem *parent = const_cast<QGraphicsItem *>(this);
+ while (QGraphicsItem *grandPa = parent->parentItem())
+ parent = grandPa;
+ return parent;
+}
+
+/*!
+ \since 4.6
+
+ Returns a pointer to the item's parent, cast to a QGraphicsObject. returns 0 if the parent item
+ is not a QGraphicsObject.
+
+ \sa parentItem(), childItems()
+*/
+QGraphicsObject *QGraphicsItem::parentObject() const
+{
+ QGraphicsItem *p = d_ptr->parent;
+ return (p && p->d_ptr->isObject) ? static_cast<QGraphicsObject *>(p) : 0;
+}
+
+/*!
+ \since 4.4
+
+ Returns a pointer to the item's parent widget. The item's parent widget is
+ the closest parent item that is a widget.
+
+ \sa parentItem(), childItems()
+*/
+QGraphicsWidget *QGraphicsItem::parentWidget() const
+{
+ QGraphicsItem *p = parentItem();
+ while (p && !p->isWidget())
+ p = p->parentItem();
+ return (p && p->isWidget()) ? static_cast<QGraphicsWidget *>(p) : 0;
+}
+
+/*!
+ \since 4.4
+
+ Returns a pointer to the item's top level widget (i.e., the item's
+ ancestor whose parent is 0, or whose parent is not a widget), or 0 if this
+ item does not have a top level widget. If the item is its own top level
+ widget, this function returns a pointer to the item itself.
+*/
+QGraphicsWidget *QGraphicsItem::topLevelWidget() const
+{
+ if (const QGraphicsWidget *p = parentWidget())
+ return p->topLevelWidget();
+ return isWidget() ? static_cast<QGraphicsWidget *>(const_cast<QGraphicsItem *>(this)) : 0;
+}
+
+/*!
+ \since 4.4
+
+ Returns the item's window, or 0 if this item does not have a window. If
+ the item is a window, it will return itself. Otherwise it will return the
+ closest ancestor that is a window.
+
+ \sa QGraphicsWidget::isWindow()
+*/
+QGraphicsWidget *QGraphicsItem::window() const
+{
+ QGraphicsItem *p = panel();
+ if (p && p->isWindow())
+ return static_cast<QGraphicsWidget *>(p);
+ return 0;
+}
+
+/*!
+ \since 4.6
+
+ Returns the item's panel, or 0 if this item does not have a panel. If the
+ item is a panel, it will return itself. Otherwise it will return the
+ closest ancestor that is a panel.
+
+ \sa isPanel(), ItemIsPanel
+*/
+QGraphicsItem *QGraphicsItem::panel() const
+{
+ if (d_ptr->flags & ItemIsPanel)
+ return const_cast<QGraphicsItem *>(this);
+ return d_ptr->parent ? d_ptr->parent->panel() : 0;
+}
+
+/*!
+ \since 4.6
+
+ Return the graphics item cast to a QGraphicsObject, if the class is actually a
+ graphics object, 0 otherwise.
+*/
+QGraphicsObject *QGraphicsItem::toGraphicsObject()
+{
+ return d_ptr->isObject ? static_cast<QGraphicsObject *>(this) : 0;
+}
+
+/*!
+ \since 4.6
+
+ Return the graphics item cast to a QGraphicsObject, if the class is actually a
+ graphics object, 0 otherwise.
+*/
+const QGraphicsObject *QGraphicsItem::toGraphicsObject() const
+{
+ return d_ptr->isObject ? static_cast<const QGraphicsObject *>(this) : 0;
+}
+
+/*!
+ Sets this item's parent item to \a newParent. If this item already
+ has a parent, it is first removed from the previous parent. If \a
+ newParent is 0, this item will become a top-level item.
+
+ Note that this implicitly adds this graphics item to the scene of
+ the parent. You should not \l{QGraphicsScene::addItem()}{add} the
+ item to the scene yourself.
+
+ Calling this function on an item that is an ancestor of \a newParent
+ have undefined behaviour.
+
+ \sa parentItem(), childItems()
+*/
+void QGraphicsItem::setParentItem(QGraphicsItem *newParent)
+{
+ if (newParent == this) {
+ qWarning("QGraphicsItem::setParentItem: cannot assign %p as a parent of itself", this);
+ return;
+ }
+ if (newParent == d_ptr->parent)
+ return;
+
+ const QVariant newParentVariant(itemChange(QGraphicsItem::ItemParentChange,
+ QVariant::fromValue<QGraphicsItem *>(newParent)));
+ newParent = qvariant_cast<QGraphicsItem *>(newParentVariant);
+ if (newParent == d_ptr->parent)
+ return;
+
+ const QVariant thisPointerVariant(QVariant::fromValue<QGraphicsItem *>(this));
+ d_ptr->setParentItemHelper(newParent, &newParentVariant, &thisPointerVariant);
+}
+
+/*!
+ \obsolete
+
+ Use childItems() instead.
+
+ \sa setParentItem()
+*/
+QList<QGraphicsItem *> QGraphicsItem::children() const
+{
+ return childItems();
+}
+
+/*!
+ \since 4.4
+
+ Returns a list of this item's children.
+
+ The items are sorted by stacking order. This takes into account both the
+ items' insertion order and their Z-values.
+
+ \sa setParentItem(), zValue(), {QGraphicsItem#Sorting}{Sorting}
+*/
+QList<QGraphicsItem *> QGraphicsItem::childItems() const
+{
+ const_cast<QGraphicsItem *>(this)->d_ptr->ensureSortedChildren();
+ return d_ptr->children;
+}
+
+/*!
+ \since 4.4
+ Returns true if this item is a widget (i.e., QGraphicsWidget); otherwise,
+ returns false.
+*/
+bool QGraphicsItem::isWidget() const
+{
+ return d_ptr->isWidget;
+}
+
+/*!
+ \since 4.4
+ Returns true if the item is a QGraphicsWidget window, otherwise returns
+ false.
+
+ \sa QGraphicsWidget::windowFlags()
+*/
+bool QGraphicsItem::isWindow() const
+{
+ return d_ptr->isWidget && (static_cast<const QGraphicsWidget *>(this)->windowType() & Qt::Window);
+}
+
+/*!
+ \since 4.6
+ Returns true if the item is a panel; otherwise returns false.
+
+ \sa QGraphicsItem::panel(), ItemIsPanel
+*/
+bool QGraphicsItem::isPanel() const
+{
+ return d_ptr->flags & ItemIsPanel;
+}
+
+/*!
+ Returns this item's flags. The flags describe what configurable features
+ of the item are enabled and not. For example, if the flags include
+ ItemIsFocusable, the item can accept input focus.
+
+ By default, no flags are enabled.
+
+ \sa setFlags(), setFlag()
+*/
+QGraphicsItem::GraphicsItemFlags QGraphicsItem::flags() const
+{
+ return GraphicsItemFlags(d_ptr->flags);
+}
+
+/*!
+ If \a enabled is true, the item flag \a flag is enabled; otherwise, it is
+ disabled.
+
+ \sa flags(), setFlags()
+*/
+void QGraphicsItem::setFlag(GraphicsItemFlag flag, bool enabled)
+{
+ if (enabled)
+ setFlags(GraphicsItemFlags(d_ptr->flags) | flag);
+ else
+ setFlags(GraphicsItemFlags(d_ptr->flags) & ~flag);
+}
+
+/*!
+ \internal
+
+ Sets the flag \a flag on \a item and all its children, to \a enabled.
+*/
+static void _q_qgraphicsItemSetFlag(QGraphicsItem *item, QGraphicsItem::GraphicsItemFlag flag,
+ bool enabled)
+{
+ if (item->flags() & flag) {
+ // If this item already has the correct flag set, we don't have to
+ // propagate it.
+ return;
+ }
+ item->setFlag(flag, enabled);
+ foreach (QGraphicsItem *child, item->children())
+ _q_qgraphicsItemSetFlag(child, flag, enabled);
+}
+
+/*!
+ Sets the item flags to \a flags. All flags in \a flags are enabled; all
+ flags not in \a flags are disabled.
+
+ If the item had focus and \a flags does not enable ItemIsFocusable, the
+ item loses focus as a result of calling this function. Similarly, if the
+ item was selected, and \a flags does not enabled ItemIsSelectable, the
+ item is automatically unselected.
+
+ By default, no flags are enabled. (QGraphicsWidget enables the
+ ItemSendsGeometryChanges flag by default in order to track position
+ changes.)
+
+ \sa flags(), setFlag()
+*/
+void QGraphicsItem::setFlags(GraphicsItemFlags flags)
+{
+ // Notify change and check for adjustment.
+ if (quint32(d_ptr->flags) == quint32(flags))
+ return;
+ flags = GraphicsItemFlags(itemChange(ItemFlagsChange, quint32(flags)).toUInt());
+ if (quint32(d_ptr->flags) == quint32(flags))
+ return;
+ if (d_ptr->scene && d_ptr->scene->d_func()->indexMethod != QGraphicsScene::NoIndex)
+ d_ptr->scene->d_func()->index->itemChange(this, ItemFlagsChange, &flags);
+
+ // Flags that alter the geometry of the item (or its children).
+ const quint32 geomChangeFlagsMask = (ItemClipsChildrenToShape | ItemClipsToShape | ItemIgnoresTransformations | ItemIsSelectable);
+ bool fullUpdate = (quint32(flags) & geomChangeFlagsMask) != (d_ptr->flags & geomChangeFlagsMask);
+ if (fullUpdate)
+ d_ptr->updatePaintedViewBoundingRects(/*children=*/true);
+
+ // Keep the old flags to compare the diff.
+ GraphicsItemFlags oldFlags = GraphicsItemFlags(d_ptr->flags);
+
+ // Update flags.
+ d_ptr->flags = flags;
+
+ if (!(d_ptr->flags & ItemIsFocusable) && hasFocus()) {
+ // Clear focus on the item if it has focus when the focusable flag
+ // is unset.
+ clearFocus();
+ }
+
+ if (!(d_ptr->flags & ItemIsSelectable) && isSelected()) {
+ // Unselect the item if it is selected when the selectable flag is
+ // unset.
+ setSelected(false);
+ }
+
+ if ((flags & ItemClipsChildrenToShape) != (oldFlags & ItemClipsChildrenToShape)) {
+ // Item children clipping changes. Propagate the ancestor flag to
+ // all children.
+ d_ptr->updateAncestorFlag(ItemClipsChildrenToShape);
+ // The childrenBoundingRect is clipped to the boundingRect in case of ItemClipsChildrenToShape,
+ // which means we have to invalidate the cached childrenBoundingRect whenever this flag changes.
+ d_ptr->dirtyChildrenBoundingRect = 1;
+ d_ptr->markParentDirty(true);
+ }
+
+ if ((flags & ItemIgnoresTransformations) != (oldFlags & ItemIgnoresTransformations)) {
+ // Item children clipping changes. Propagate the ancestor flag to
+ // all children.
+ d_ptr->updateAncestorFlag(ItemIgnoresTransformations);
+ }
+
+ if ((flags & ItemNegativeZStacksBehindParent) != (oldFlags & ItemNegativeZStacksBehindParent)) {
+ // NB! We change the flags directly here, so we must also update d_ptr->flags.
+ // Note that this has do be done before the ItemStacksBehindParent check
+ // below; otherwise we will loose the change.
+
+ // Update stack-behind.
+ if (d_ptr->z < qreal(0.0))
+ flags |= ItemStacksBehindParent;
+ else
+ flags &= ~ItemStacksBehindParent;
+ d_ptr->flags = flags;
+ }
+
+ if ((flags & ItemStacksBehindParent) != (oldFlags & ItemStacksBehindParent)) {
+ // NB! This check has to come after the ItemNegativeZStacksBehindParent
+ // check above. Be careful.
+
+ // Ensure child item sorting is up to date when toggling this flag.
+ if (d_ptr->parent)
+ d_ptr->parent->d_ptr->needSortChildren = 1;
+ else if (d_ptr->scene)
+ d_ptr->scene->d_func()->needSortTopLevelItems = 1;
+ }
+
+ if ((flags & ItemAcceptsInputMethod) != (oldFlags & ItemAcceptsInputMethod)) {
+ // Update input method sensitivity in any views.
+ if (d_ptr->scene)
+ d_ptr->scene->d_func()->updateInputMethodSensitivityInViews();
+ }
+
+
+ if ((d_ptr->panelModality != NonModal)
+ && d_ptr->scene
+ && (flags & ItemIsPanel) != (oldFlags & ItemIsPanel)) {
+ // update the panel's modal state
+ if (flags & ItemIsPanel)
+ d_ptr->scene->d_func()->enterModal(this);
+ else
+ d_ptr->scene->d_func()->leaveModal(this);
+ }
+
+ if (d_ptr->scene) {
+ if ((flags & ItemSendsScenePositionChanges) != (oldFlags & ItemSendsScenePositionChanges)) {
+ if (flags & ItemSendsScenePositionChanges)
+ d_ptr->scene->d_func()->registerScenePosItem(this);
+ else
+ d_ptr->scene->d_func()->unregisterScenePosItem(this);
+ }
+ d_ptr->scene->d_func()->markDirty(this, QRectF(), /*invalidateChildren=*/true);
+ }
+
+ // Notify change.
+ itemChange(ItemFlagsHaveChanged, quint32(flags));
+}
+
+/*!
+ \since 4.4
+ Returns the cache mode for this item. The default mode is NoCache (i.e.,
+ cache is disabled and all painting is immediate).
+
+ \sa setCacheMode()
+*/
+QGraphicsItem::CacheMode QGraphicsItem::cacheMode() const
+{
+ return QGraphicsItem::CacheMode(d_ptr->cacheMode);
+}
+
+/*!
+ \since 4.4
+ Sets the item's cache mode to \a mode.
+
+ The optional \a logicalCacheSize argument is used only by
+ ItemCoordinateCache mode, and describes the resolution of the cache
+ buffer; if \a logicalCacheSize is (100, 100), QGraphicsItem will fit the
+ item into 100x100 pixels in graphics memory, regardless of the logical
+ size of the item itself. By default QGraphicsItem uses the size of
+ boundingRect(). For all other cache modes than ItemCoordinateCache, \a
+ logicalCacheSize is ignored.
+
+ Caching can speed up rendering if your item spends a significant time
+ redrawing itself. In some cases the cache can also slow down rendering, in
+ particular when the item spends less time redrawing than QGraphicsItem
+ spends redrawing from the cache. When enabled, the item's paint() function
+ will be called only once for each call to update(); for any subsequent
+ repaint requests, the Graphics View framework will redraw from the
+ cache. This approach works particularly well with QGLWidget, which stores
+ all the cache as OpenGL textures.
+
+ Be aware that QPixmapCache's cache limit may need to be changed to obtain
+ optimal performance.
+
+ You can read more about the different cache modes in the CacheMode
+ documentation.
+
+ \sa CacheMode, QPixmapCache::setCacheLimit()
+*/
+void QGraphicsItem::setCacheMode(CacheMode mode, const QSize &logicalCacheSize)
+{
+ CacheMode lastMode = CacheMode(d_ptr->cacheMode);
+ d_ptr->cacheMode = mode;
+ bool noVisualChange = (mode == NoCache && lastMode == NoCache)
+ || (mode == NoCache && lastMode == DeviceCoordinateCache)
+ || (mode == DeviceCoordinateCache && lastMode == NoCache)
+ || (mode == DeviceCoordinateCache && lastMode == DeviceCoordinateCache);
+ if (mode == NoCache) {
+ d_ptr->removeExtraItemCache();
+ } else {
+ QGraphicsItemCache *cache = d_ptr->extraItemCache();
+
+ // Reset old cache
+ cache->purge();
+
+ if (mode == ItemCoordinateCache) {
+ if (lastMode == mode && cache->fixedSize == logicalCacheSize)
+ noVisualChange = true;
+ cache->fixedSize = logicalCacheSize;
+ }
+ }
+ if (!noVisualChange)
+ update();
+}
+
+/*!
+ \since 4.6
+
+ Returns the modality for this item.
+*/
+QGraphicsItem::PanelModality QGraphicsItem::panelModality() const
+{
+ return d_ptr->panelModality;
+}
+
+/*!
+ \since 4.6
+
+ Sets the modality for this item to \a panelModality.
+
+ Changing the modality of a visible item takes effect immediately.
+*/
+void QGraphicsItem::setPanelModality(PanelModality panelModality)
+{
+ if (d_ptr->panelModality == panelModality)
+ return;
+
+ PanelModality previousModality = d_ptr->panelModality;
+ bool enterLeaveModal = (isPanel() && d_ptr->scene && isVisible());
+ if (enterLeaveModal && panelModality == NonModal)
+ d_ptr->scene->d_func()->leaveModal(this);
+ d_ptr->panelModality = panelModality;
+ if (enterLeaveModal && d_ptr->panelModality != NonModal)
+ d_ptr->scene->d_func()->enterModal(this, previousModality);
+}
+
+/*!
+ \since 4.6
+
+ Returns true if this item is blocked by a modal panel, false otherwise. If \a blockingPanel is
+ non-zero, \a blockingPanel will be set to the modal panel that is blocking this item. If this
+ item is not blocked, \a blockingPanel will not be set by this function.
+
+ This function always returns false for items not in a scene.
+
+ \sa panelModality() setPanelModality() PanelModality
+*/
+bool QGraphicsItem::isBlockedByModalPanel(QGraphicsItem **blockingPanel) const
+{
+ if (!d_ptr->scene)
+ return false;
+
+
+ QGraphicsItem *dummy = 0;
+ if (!blockingPanel)
+ blockingPanel = &dummy;
+
+ QGraphicsScenePrivate *scene_d = d_ptr->scene->d_func();
+ if (scene_d->modalPanels.isEmpty())
+ return false;
+
+ // ###
+ if (!scene_d->popupWidgets.isEmpty() && scene_d->popupWidgets.first() == this)
+ return false;
+
+ for (int i = 0; i < scene_d->modalPanels.count(); ++i) {
+ QGraphicsItem *modalPanel = scene_d->modalPanels.at(i);
+ if (modalPanel->panelModality() == QGraphicsItem::SceneModal) {
+ // Scene modal panels block all non-descendents.
+ if (modalPanel != this && !modalPanel->isAncestorOf(this)) {
+ *blockingPanel = modalPanel;
+ return true;
+ }
+ } else {
+ // Window modal panels block ancestors and siblings/cousins.
+ if (modalPanel != this
+ && !modalPanel->isAncestorOf(this)
+ && commonAncestorItem(modalPanel)) {
+ *blockingPanel = modalPanel;
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+#ifndef QT_NO_TOOLTIP
+/*!
+ Returns the item's tool tip, or an empty QString if no tool tip has been
+ set.
+
+ \sa setToolTip(), QToolTip
+*/
+QString QGraphicsItem::toolTip() const
+{
+ return d_ptr->extra(QGraphicsItemPrivate::ExtraToolTip).toString();
+}
+
+/*!
+ Sets the item's tool tip to \a toolTip. If \a toolTip is empty, the item's
+ tool tip is cleared.
+
+ \sa toolTip(), QToolTip
+*/
+void QGraphicsItem::setToolTip(const QString &toolTip)
+{
+ const QVariant toolTipVariant(itemChange(ItemToolTipChange, toolTip));
+ d_ptr->setExtra(QGraphicsItemPrivate::ExtraToolTip, toolTipVariant.toString());
+ itemChange(ItemToolTipHasChanged, toolTipVariant);
+}
+#endif // QT_NO_TOOLTIP
+
+#ifndef QT_NO_CURSOR
+/*!
+ Returns the current cursor shape for the item. The mouse cursor
+ will assume this shape when it's over this item. See the \link
+ Qt::CursorShape list of predefined cursor objects\endlink for a
+ range of useful shapes.
+
+ An editor item might want to use an I-beam cursor:
+
+ \snippet doc/src/snippets/code/src_gui_graphicsview_qgraphicsitem.cpp 2
+
+ If no cursor has been set, the cursor of the item beneath is used.
+
+ \sa setCursor(), hasCursor(), unsetCursor(), QWidget::cursor,
+ QApplication::overrideCursor()
+*/
+QCursor QGraphicsItem::cursor() const
+{
+ return qvariant_cast<QCursor>(d_ptr->extra(QGraphicsItemPrivate::ExtraCursor));
+}
+
+/*!
+ Sets the current cursor shape for the item to \a cursor. The mouse cursor
+ will assume this shape when it's over this item. See the \link
+ Qt::CursorShape list of predefined cursor objects\endlink for a range of
+ useful shapes.
+
+ An editor item might want to use an I-beam cursor:
+
+ \snippet doc/src/snippets/code/src_gui_graphicsview_qgraphicsitem.cpp 3
+
+ If no cursor has been set, the cursor of the item beneath is used.
+
+ \sa cursor(), hasCursor(), unsetCursor(), QWidget::cursor,
+ QApplication::overrideCursor()
+*/
+void QGraphicsItem::setCursor(const QCursor &cursor)
+{
+ const QVariant cursorVariant(itemChange(ItemCursorChange, QVariant::fromValue<QCursor>(cursor)));
+ d_ptr->setExtra(QGraphicsItemPrivate::ExtraCursor, qvariant_cast<QCursor>(cursorVariant));
+ d_ptr->hasCursor = 1;
+ if (d_ptr->scene) {
+ d_ptr->scene->d_func()->allItemsUseDefaultCursor = false;
+ foreach (QGraphicsView *view, d_ptr->scene->views()) {
+ view->viewport()->setMouseTracking(true);
+ // Note: Some of this logic is duplicated in QGraphicsView's mouse events.
+ if (view->underMouse()) {
+ foreach (QGraphicsItem *itemUnderCursor, view->items(view->mapFromGlobal(QCursor::pos()))) {
+ if (itemUnderCursor->hasCursor()) {
+ QMetaObject::invokeMethod(view, "_q_setViewportCursor",
+ Q_ARG(QCursor, itemUnderCursor->cursor()));
+ break;
+ }
+ }
+ break;
+ }
+ }
+ }
+ itemChange(ItemCursorHasChanged, cursorVariant);
+}
+
+/*!
+ Returns true if this item has a cursor set; otherwise, false is returned.
+
+ By default, items don't have any cursor set. cursor() will return a
+ standard pointing arrow cursor.
+
+ \sa unsetCursor()
+*/
+bool QGraphicsItem::hasCursor() const
+{
+ return d_ptr->hasCursor;
+}
+
+/*!
+ Clears the cursor from this item.
+
+ \sa hasCursor(), setCursor()
+*/
+void QGraphicsItem::unsetCursor()
+{
+ d_ptr->unsetExtra(QGraphicsItemPrivate::ExtraCursor);
+ d_ptr->hasCursor = 0;
+ if (d_ptr->scene) {
+ foreach (QGraphicsView *view, d_ptr->scene->views()) {
+ if (view->underMouse() && view->itemAt(view->mapFromGlobal(QCursor::pos())) == this) {
+ QMetaObject::invokeMethod(view, "_q_unsetViewportCursor");
+ break;
+ }
+ }
+ }
+}
+
+#endif // QT_NO_CURSOR
+
+/*!
+ Returns true if the item is visible; otherwise, false is returned.
+
+ Note that the item's general visibility is unrelated to whether or not it
+ is actually being visualized by a QGraphicsView.
+
+ \sa setVisible()
+*/
+bool QGraphicsItem::isVisible() const
+{
+ return d_ptr->visible;
+}
+
+/*!
+ \since 4.4
+ Returns true if the item is visible to \a parent; otherwise, false is
+ returned. \a parent can be 0, in which case this function will return
+ whether the item is visible to the scene or not.
+
+ An item may not be visible to its ancestors even if isVisible() is true. If
+ any ancestor is hidden, the item itself will be implicitly hidden, in which
+ case this function will return false.
+
+ \sa isVisible(), setVisible()
+*/
+bool QGraphicsItem::isVisibleTo(const QGraphicsItem *parent) const
+{
+ if (!d_ptr->visible)
+ return false;
+ if (parent == this)
+ return true;
+ if (parentItem() && parentItem()->isVisibleTo(parent))
+ return true;
+ if (!parent && !parentItem())
+ return true;
+ return false;
+}
+
+/*!
+ \internal
+
+ Sets this item's visibility to \a newVisible. If \a explicitly is true,
+ this item will be "explicitly" \a newVisible; otherwise, it.. will not be.
+*/
+void QGraphicsItemPrivate::setVisibleHelper(bool newVisible, bool explicitly, bool update)
+{
+ Q_Q(QGraphicsItem);
+
+ // Update explicit bit.
+ if (explicitly)
+ explicitlyHidden = newVisible ? 0 : 1;
+
+ // Check if there's nothing to do.
+ if (visible == quint32(newVisible))
+ return;
+
+ // Don't show child if parent is not visible
+ if (parent && newVisible && !parent->d_ptr->visible)
+ return;
+
+ // Modify the property.
+ const QVariant newVisibleVariant(q_ptr->itemChange(QGraphicsItem::ItemVisibleChange,
+ quint32(newVisible)));
+ newVisible = newVisibleVariant.toBool();
+ if (visible == quint32(newVisible))
+ return;
+ visible = newVisible;
+
+ // Schedule redrawing
+ if (update) {
+ QGraphicsItemCache *c = (QGraphicsItemCache *)qvariant_cast<void *>(extra(ExtraCacheData));
+ if (c)
+ c->purge();
+ if (scene) {
+#ifndef QT_NO_GRAPHICSEFFECT
+ invalidateParentGraphicsEffectsRecursively();
+#endif //QT_NO_GRAPHICSEFFECT
+ scene->d_func()->markDirty(q_ptr, QRectF(), /*invalidateChildren=*/false, /*force=*/true);
+ }
+ }
+
+ // Certain properties are dropped as an item becomes invisible.
+ bool hasFocus = q_ptr->hasFocus();
+ if (!newVisible) {
+ if (scene) {
+ if (scene->d_func()->mouseGrabberItems.contains(q))
+ q->ungrabMouse();
+ if (scene->d_func()->keyboardGrabberItems.contains(q))
+ q->ungrabKeyboard();
+ if (q->isPanel() && panelModality != QGraphicsItem::NonModal)
+ scene->d_func()->leaveModal(q_ptr);
+ }
+ if (hasFocus && scene) {
+ // Hiding the closest non-panel ancestor of the focus item
+ QGraphicsItem *focusItem = scene->focusItem();
+ bool clear = true;
+ if (isWidget && !focusItem->isPanel()) {
+ do {
+ if (focusItem == q_ptr) {
+ clear = !static_cast<QGraphicsWidget *>(q_ptr)->focusNextPrevChild(true);
+ break;
+ }
+ } while ((focusItem = focusItem->parentWidget()) && !focusItem->isPanel());
+ }
+ if (clear)
+ clearFocusHelper(/* giveFocusToParent = */ false);
+ }
+ if (q_ptr->isSelected())
+ q_ptr->setSelected(false);
+ } else {
+ geometryChanged = 1;
+ paintedViewBoundingRectsNeedRepaint = 1;
+ if (scene) {
+ if (isWidget) {
+ QGraphicsWidget *widget = static_cast<QGraphicsWidget *>(q_ptr);
+ if (widget->windowType() == Qt::Popup)
+ scene->d_func()->addPopup(widget);
+ }
+ if (q->isPanel() && panelModality != QGraphicsItem::NonModal) {
+ scene->d_func()->enterModal(q_ptr);
+ }
+ }
+ }
+
+ // Update children with explicitly = false.
+ const bool updateChildren = update && !(flags & QGraphicsItem::ItemClipsChildrenToShape);
+ foreach (QGraphicsItem *child, children) {
+ if (!newVisible || !child->d_ptr->explicitlyHidden)
+ child->d_ptr->setVisibleHelper(newVisible, false, updateChildren);
+ }
+
+ // Update activation
+ if (scene && q->isPanel()) {
+ if (newVisible) {
+ if (parent && parent->isActive())
+ q->setActive(true);
+ } else {
+ if (q->isActive())
+ scene->setActivePanel(parent);
+ }
+ }
+
+ // Enable subfocus
+ if (scene) {
+ if (newVisible) {
+ // Item is shown
+ QGraphicsItem *p = parent;
+ bool done = false;
+ while (p) {
+ if (p->flags() & QGraphicsItem::ItemIsFocusScope) {
+ QGraphicsItem *fsi = p->d_ptr->focusScopeItem;
+ if (q_ptr == fsi || q_ptr->isAncestorOf(fsi)) {
+ done = true;
+ while (fsi->d_ptr->focusScopeItem && fsi->d_ptr->focusScopeItem->isVisible())
+ fsi = fsi->d_ptr->focusScopeItem;
+ fsi->d_ptr->setFocusHelper(Qt::OtherFocusReason, /* climb = */ true,
+ /* focusFromHide = */ false);
+ }
+ break;
+ }
+ p = p->d_ptr->parent;
+ }
+ if (!done) {
+ QGraphicsItem *fi = subFocusItem;
+ if (fi && fi != scene->focusItem()) {
+ scene->setFocusItem(fi);
+ } else if (flags & QGraphicsItem::ItemIsFocusScope &&
+ !scene->focusItem() &&
+ q->isAncestorOf(scene->d_func()->lastFocusItem)) {
+ q_ptr->setFocus();
+ }
+ }
+ } else {
+ // Item is hidden
+ if (hasFocus) {
+ QGraphicsItem *p = parent;
+ while (p) {
+ if (p->flags() & QGraphicsItem::ItemIsFocusScope) {
+ if (p->d_ptr->visible) {
+ p->d_ptr->setFocusHelper(Qt::OtherFocusReason, /* climb = */ true,
+ /* focusFromHide = */ true);
+ }
+ break;
+ }
+ p = p->d_ptr->parent;
+ }
+ }
+ }
+ }
+
+ // Deliver post-change notification.
+ q_ptr->itemChange(QGraphicsItem::ItemVisibleHasChanged, newVisibleVariant);
+
+ if (isObject)
+ emit static_cast<QGraphicsObject *>(q_ptr)->visibleChanged();
+}
+
+/*!
+ If \a visible is true, the item is made visible. Otherwise, the item is
+ made invisible. Invisible items are not painted, nor do they receive any
+ events. In particular, mouse events pass right through invisible items,
+ and are delivered to any item that may be behind. Invisible items are also
+ unselectable, they cannot take input focus, and are not detected by
+ QGraphicsScene's item location functions.
+
+ If an item becomes invisible while grabbing the mouse, (i.e., while it is
+ receiving mouse events,) it will automatically lose the mouse grab, and
+ the grab is not regained by making the item visible again; it must receive
+ a new mouse press to regain the mouse grab.
+
+ Similarly, an invisible item cannot have focus, so if the item has focus
+ when it becomes invisible, it will lose focus, and the focus is not
+ regained by simply making the item visible again.
+
+ If you hide a parent item, all its children will also be hidden. If you
+ show a parent item, all children will be shown, unless they have been
+ explicitly hidden (i.e., if you call setVisible(false) on a child, it will
+ not be reshown even if its parent is hidden, and then shown again).
+
+ Items are visible by default; it is unnecessary to call
+ setVisible() on a new item.
+
+ \sa isVisible(), show(), hide()
+*/
+void QGraphicsItem::setVisible(bool visible)
+{
+ d_ptr->setVisibleHelper(visible, /* explicit = */ true);
+}
+
+/*!
+ \fn void QGraphicsItem::hide()
+
+ Hides the item. (Items are visible by default.)
+
+ This convenience function is equivalent to calling \c setVisible(false).
+
+ \sa show(), setVisible()
+*/
+
+/*!
+ \fn void QGraphicsItem::show()
+
+ Shows the item. (Items are visible by default.)
+
+ This convenience function is equivalent to calling \c setVisible(true).
+
+ \sa hide(), setVisible()
+*/
+
+/*!
+ Returns true if the item is enabled; otherwise, false is returned.
+
+ \sa setEnabled()
+*/
+bool QGraphicsItem::isEnabled() const
+{
+ return d_ptr->enabled;
+}
+
+/*!
+ \internal
+
+ Sets this item's visibility to \a newEnabled. If \a explicitly is true,
+ this item will be "explicitly" \a newEnabled; otherwise, it.. will not be.
+*/
+void QGraphicsItemPrivate::setEnabledHelper(bool newEnabled, bool explicitly, bool update)
+{
+ // Update explicit bit.
+ if (explicitly)
+ explicitlyDisabled = newEnabled ? 0 : 1;
+
+ // Check if there's nothing to do.
+ if (enabled == quint32(newEnabled))
+ return;
+
+ // Certain properties are dropped when an item is disabled.
+ if (!newEnabled) {
+ if (scene && scene->mouseGrabberItem() == q_ptr)
+ q_ptr->ungrabMouse();
+ if (q_ptr->hasFocus()) {
+ // Disabling the closest non-panel ancestor of the focus item
+ // causes focus to pop to the next item, otherwise it's cleared.
+ QGraphicsItem *focusItem = scene->focusItem();
+ bool clear = true;
+ if (isWidget && !focusItem->isPanel() && q_ptr->isAncestorOf(focusItem)) {
+ do {
+ if (focusItem == q_ptr) {
+ clear = !static_cast<QGraphicsWidget *>(q_ptr)->focusNextPrevChild(true);
+ break;
+ }
+ } while ((focusItem = focusItem->parentWidget()) && !focusItem->isPanel());
+ }
+ if (clear)
+ q_ptr->clearFocus();
+ }
+ if (q_ptr->isSelected())
+ q_ptr->setSelected(false);
+ }
+
+ // Modify the property.
+ const QVariant newEnabledVariant(q_ptr->itemChange(QGraphicsItem::ItemEnabledChange,
+ quint32(newEnabled)));
+ enabled = newEnabledVariant.toBool();
+
+ // Schedule redraw.
+ if (update)
+ q_ptr->update();
+
+ foreach (QGraphicsItem *child, children) {
+ if (!newEnabled || !child->d_ptr->explicitlyDisabled)
+ child->d_ptr->setEnabledHelper(newEnabled, /* explicitly = */ false);
+ }
+
+ // Deliver post-change notification.
+ q_ptr->itemChange(QGraphicsItem::ItemEnabledHasChanged, newEnabledVariant);
+
+ if (isObject)
+ emit static_cast<QGraphicsObject *>(q_ptr)->enabledChanged();
+}
+
+/*!
+ If \a enabled is true, the item is enabled; otherwise, it is disabled.
+
+ Disabled items are visible, but they do not receive any events, and cannot
+ take focus nor be selected. Mouse events are discarded; they are not
+ propagated unless the item is also invisible, or if it does not accept
+ mouse events (see acceptedMouseButtons()). A disabled item cannot become the
+ mouse grabber, and as a result of this, an item loses the grab if it
+ becomes disabled when grabbing the mouse, just like it loses focus if it
+ had focus when it was disabled.
+
+ Disabled items are traditionally drawn using grayed-out colors (see \l
+ QPalette::Disabled).
+
+ If you disable a parent item, all its children will also be disabled. If
+ you enable a parent item, all children will be enabled, unless they have
+ been explicitly disabled (i.e., if you call setEnabled(false) on a child,
+ it will not be reenabled if its parent is disabled, and then enabled
+ again).
+
+ Items are enabled by default.
+
+ \note If you install an event filter, you can still intercept events
+ before they are delivered to items; this mechanism disregards the item's
+ enabled state.
+
+ \sa isEnabled()
+*/
+void QGraphicsItem::setEnabled(bool enabled)
+{
+ d_ptr->setEnabledHelper(enabled, /* explicitly = */ true);
+}
+
+/*!
+ Returns true if this item is selected; otherwise, false is returned.
+
+ Items that are in a group inherit the group's selected state.
+
+ Items are not selected by default.
+
+ \sa setSelected(), QGraphicsScene::setSelectionArea()
+*/
+bool QGraphicsItem::isSelected() const
+{
+ if (QGraphicsItemGroup *group = this->group())
+ return group->isSelected();
+ return d_ptr->selected;
+}
+
+/*!
+ If \a selected is true and this item is selectable, this item is selected;
+ otherwise, it is unselected.
+
+ If the item is in a group, the whole group's selected state is toggled by
+ this function. If the group is selected, all items in the group are also
+ selected, and if the group is not selected, no item in the group is
+ selected.
+
+ Only visible, enabled, selectable items can be selected. If \a selected
+ is true and this item is either invisible or disabled or unselectable,
+ this function does nothing.
+
+ By default, items cannot be selected. To enable selection, set the
+ ItemIsSelectable flag.
+
+ This function is provided for convenience, allowing individual toggling of
+ the selected state of an item. However, a more common way of selecting
+ items is to call QGraphicsScene::setSelectionArea(), which will call this
+ function for all visible, enabled, and selectable items within a specified
+ area on the scene.
+
+ \sa isSelected(), QGraphicsScene::selectedItems()
+*/
+void QGraphicsItem::setSelected(bool selected)
+{
+ if (QGraphicsItemGroup *group = this->group()) {
+ group->setSelected(selected);
+ return;
+ }
+
+ if (!(d_ptr->flags & ItemIsSelectable) || !d_ptr->enabled || !d_ptr->visible)
+ selected = false;
+ if (d_ptr->selected == selected)
+ return;
+ const QVariant newSelectedVariant(itemChange(ItemSelectedChange, quint32(selected)));
+ bool newSelected = newSelectedVariant.toBool();
+ if (d_ptr->selected == newSelected)
+ return;
+ d_ptr->selected = newSelected;
+
+ update();
+ if (d_ptr->scene) {
+ QGraphicsScenePrivate *sceneD = d_ptr->scene->d_func();
+ if (selected) {
+ sceneD->selectedItems << this;
+ } else {
+ // QGraphicsScene::selectedItems() lazily pulls out all items that are
+ // no longer selected.
+ }
+ if (!sceneD->selectionChanging)
+ emit d_ptr->scene->selectionChanged();
+ }
+
+ // Deliver post-change notification.
+ itemChange(QGraphicsItem::ItemSelectedHasChanged, newSelectedVariant);
+}
+
+/*!
+ \since 4.5
+
+ Returns this item's local opacity, which is between 0.0 (transparent) and
+ 1.0 (opaque). This value is combined with parent and ancestor values into
+ the effectiveOpacity(). The effective opacity decides how the item is
+ rendered.
+
+ The opacity property decides the state of the painter passed to the
+ paint() function. If the item is cached, i.e., ItemCoordinateCache or
+ DeviceCoordinateCache, the effective property will be applied to the item's
+ cache as it is rendered.
+
+ The default opacity is 1.0; fully opaque.
+
+ \sa setOpacity(), paint(), ItemIgnoresParentOpacity,
+ ItemDoesntPropagateOpacityToChildren
+*/
+qreal QGraphicsItem::opacity() const
+{
+ return d_ptr->opacity;
+}
+
+/*!
+ \since 4.5
+
+ Returns this item's \e effective opacity, which is between 0.0
+ (transparent) and 1.0 (opaque). This value is a combination of this item's
+ local opacity, and its parent and ancestors' opacities. The effective
+ opacity decides how the item is rendered.
+
+ \sa opacity(), setOpacity(), paint(), ItemIgnoresParentOpacity,
+ ItemDoesntPropagateOpacityToChildren
+*/
+qreal QGraphicsItem::effectiveOpacity() const
+{
+ return d_ptr->effectiveOpacity();
+}
+
+/*!
+ \since 4.5
+
+ Sets this item's local \a opacity, between 0.0 (transparent) and 1.0
+ (opaque). The item's local opacity is combined with parent and ancestor
+ opacities into the effectiveOpacity().
+
+ By default, opacity propagates from parent to child, so if a parent's
+ opacity is 0.5 and the child is also 0.5, the child's effective opacity
+ will be 0.25.
+
+ The opacity property decides the state of the painter passed to the
+ paint() function. If the item is cached, i.e., ItemCoordinateCache or
+ DeviceCoordinateCache, the effective property will be applied to the
+ item's cache as it is rendered.
+
+ There are two item flags that affect how the item's opacity is combined
+ with the parent: ItemIgnoresParentOpacity and
+ ItemDoesntPropagateOpacityToChildren.
+
+ \sa opacity(), effectiveOpacity()
+*/
+void QGraphicsItem::setOpacity(qreal opacity)
+{
+ // Notify change.
+ const QVariant newOpacityVariant(itemChange(ItemOpacityChange, opacity));
+
+ // Normalized opacity
+ qreal newOpacity = qBound(qreal(0), newOpacityVariant.toReal(), qreal(1));
+
+ // No change? Done.
+ if (newOpacity == d_ptr->opacity)
+ return;
+
+ bool wasFullyTransparent = d_ptr->isOpacityNull();
+ d_ptr->opacity = newOpacity;
+
+ // Notify change.
+ itemChange(ItemOpacityHasChanged, newOpacityVariant);
+
+ // Update.
+ if (d_ptr->scene) {
+#ifndef QT_NO_GRAPHICSEFFECT
+ d_ptr->invalidateParentGraphicsEffectsRecursively();
+ if (!(d_ptr->flags & ItemDoesntPropagateOpacityToChildren))
+ d_ptr->invalidateChildGraphicsEffectsRecursively(QGraphicsItemPrivate::OpacityChanged);
+#endif //QT_NO_GRAPHICSEFFECT
+ d_ptr->scene->d_func()->markDirty(this, QRectF(),
+ /*invalidateChildren=*/true,
+ /*force=*/false,
+ /*ignoreOpacity=*/d_ptr->isOpacityNull());
+ if (wasFullyTransparent)
+ d_ptr->paintedViewBoundingRectsNeedRepaint = 1;
+ }
+
+ if (d_ptr->isObject)
+ emit static_cast<QGraphicsObject *>(this)->opacityChanged();
+}
+
+/*!
+ Returns a pointer to this item's effect if it has one; otherwise 0.
+
+ \since 4.6
+*/
+#ifndef QT_NO_GRAPHICSEFFECT
+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.
+
+ QGraphicsItem takes ownership of \a effect.
+
+ \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) {
+ delete d_ptr->graphicsEffect;
+ d_ptr->graphicsEffect = 0;
+ } else if (d_ptr->parent) {
+ d_ptr->parent->d_ptr->updateChildWithGraphicsEffectFlagRecursively();
+ }
+
+ if (effect) {
+ // Set new effect.
+ QGraphicsEffectSourcePrivate *sourced = new QGraphicsItemEffectSourcePrivate(this);
+ QGraphicsEffectSource *source = new QGraphicsEffectSource(*sourced);
+ d_ptr->graphicsEffect = effect;
+ effect->d_func()->setGraphicsEffectSource(source);
+ prepareGeometryChange();
+ }
+}
+#endif //QT_NO_GRAPHICSEFFECT
+
+void QGraphicsItemPrivate::updateChildWithGraphicsEffectFlagRecursively()
+{
+#ifndef QT_NO_GRAPHICSEFFECT
+ QGraphicsItemPrivate *itemPrivate = this;
+ do {
+ // parent chain already notified?
+ if (itemPrivate->mayHaveChildWithGraphicsEffect)
+ return;
+ itemPrivate->mayHaveChildWithGraphicsEffect = 1;
+ } while ((itemPrivate = itemPrivate->parent ? itemPrivate->parent->d_ptr.data() : 0));
+#endif
+}
+
+/*!
+ \internal
+ \since 4.6
+ Returns the effective bounding rect of the given item space rect.
+ If the item has no effect, the rect is returned unmodified.
+ If the item has an effect, the effective rect can be extend beyond the
+ item's bounding rect, depending on the effect.
+
+ \sa boundingRect()
+*/
+QRectF QGraphicsItemPrivate::effectiveBoundingRect(const QRectF &rect) const
+{
+#ifndef QT_NO_GRAPHICSEFFECT
+ Q_Q(const QGraphicsItem);
+ QGraphicsEffect *effect = graphicsEffect;
+ if (scene && effect && effect->isEnabled()) {
+ if (scene->d_func()->views.isEmpty())
+ return effect->boundingRectFor(rect);
+ QRectF sceneRect = q->mapRectToScene(rect);
+ QRectF sceneEffectRect;
+ foreach (QGraphicsView *view, scene->views()) {
+ QRectF deviceRect = view->d_func()->mapRectFromScene(sceneRect);
+ QRect deviceEffectRect = effect->boundingRectFor(deviceRect).toAlignedRect();
+ sceneEffectRect |= view->d_func()->mapRectToScene(deviceEffectRect);
+ }
+ return q->mapRectFromScene(sceneEffectRect);
+ }
+#endif //QT_NO_GRAPHICSEFFECT
+ return rect;
+}
+
+/*!
+ \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(QGraphicsItem *topMostEffectItem) const
+{
+#ifndef QT_NO_GRAPHICSEFFECT
+ Q_Q(const QGraphicsItem);
+ QRectF brect = effectiveBoundingRect(q_ptr->boundingRect());
+ if (ancestorFlags & QGraphicsItemPrivate::AncestorClipsChildren || topMostEffectItem == q)
+ return brect;
+
+ const QGraphicsItem *effectParent = parent;
+ while (effectParent) {
+ QGraphicsEffect *effect = effectParent->d_ptr->graphicsEffect;
+ if (scene && effect && effect->isEnabled()) {
+ const QRectF brectInParentSpace = q->mapRectToItem(effectParent, brect);
+ const QRectF effectRectInParentSpace = effectParent->d_ptr->effectiveBoundingRect(brectInParentSpace);
+ brect = effectParent->mapRectToItem(q, effectRectInParentSpace);
+ }
+ if (effectParent->d_ptr->ancestorFlags & QGraphicsItemPrivate::AncestorClipsChildren
+ || topMostEffectItem == effectParent) {
+ return brect;
+ }
+ effectParent = effectParent->d_ptr->parent;
+ }
+
+ return brect;
+#else //QT_NO_GRAPHICSEFFECT
+ return q_ptr->boundingRect();
+#endif //QT_NO_GRAPHICSEFFECT
+
+}
+
+/*!
+ \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.
+
+ \sa setAcceptDrops()
+*/
+bool QGraphicsItem::acceptDrops() const
+{
+ return d_ptr->acceptDrops;
+}
+
+/*!
+ If \a on is true, this item will accept drag and drop events; otherwise,
+ it is transparent for drag and drop events. By default, items do not
+ accept drag and drop events.
+
+ \sa acceptDrops()
+*/
+void QGraphicsItem::setAcceptDrops(bool on)
+{
+ d_ptr->acceptDrops = on;
+}
+
+/*!
+ Returns the mouse buttons that this item accepts mouse events for. By
+ default, all mouse buttons are accepted.
+
+ If an item accepts a mouse button, it will become the mouse
+ grabber item when a mouse press event is delivered for that mouse
+ button. However, if the item does not accept the button,
+ QGraphicsScene will forward the mouse events to the first item
+ beneath it that does.
+
+ \sa setAcceptedMouseButtons(), mousePressEvent()
+*/
+Qt::MouseButtons QGraphicsItem::acceptedMouseButtons() const
+{
+ return Qt::MouseButtons(d_ptr->acceptedMouseButtons);
+}
+
+/*!
+ Sets the mouse \a buttons that this item accepts mouse events for.
+
+ By default, all mouse buttons are accepted. If an item accepts a
+ mouse button, it will become the mouse grabber item when a mouse
+ press event is delivered for that button. However, if the item
+ does not accept the mouse button, QGraphicsScene will forward the
+ mouse events to the first item beneath it that does.
+
+ To disable mouse events for an item (i.e., make it transparent for mouse
+ events), call setAcceptedMouseButtons(0).
+
+ \sa acceptedMouseButtons(), mousePressEvent()
+*/
+void QGraphicsItem::setAcceptedMouseButtons(Qt::MouseButtons buttons)
+{
+ if (Qt::MouseButtons(d_ptr->acceptedMouseButtons) != buttons) {
+ if (buttons == 0 && d_ptr->scene && d_ptr->scene->mouseGrabberItem() == this
+ && d_ptr->scene->d_func()->lastMouseGrabberItemHasImplicitMouseGrab) {
+ ungrabMouse();
+ }
+ d_ptr->acceptedMouseButtons = quint32(buttons);
+ }
+}
+
+/*!
+ \since 4.4
+
+ Returns true if an item accepts hover events
+ (QGraphicsSceneHoverEvent); otherwise, returns false. By default,
+ items do not accept hover events.
+
+ \sa setAcceptedMouseButtons()
+*/
+bool QGraphicsItem::acceptHoverEvents() const
+{
+ return d_ptr->acceptsHover;
+}
+
+/*!
+ \obsolete
+
+ Call acceptHoverEvents() instead.
+*/
+bool QGraphicsItem::acceptsHoverEvents() const
+{
+ return d_ptr->acceptsHover;
+}
+
+/*!
+ \since 4.4
+
+ If \a enabled is true, this item will accept hover events;
+ otherwise, it will ignore them. By default, items do not accept
+ hover events.
+
+ Hover events are delivered when there is no current mouse grabber
+ item. They are sent when the mouse cursor enters an item, when it
+ moves around inside the item, and when the cursor leaves an
+ item. Hover events are commonly used to highlight an item when
+ it's entered, and for tracking the mouse cursor as it hovers over
+ the item (equivalent to QWidget::mouseTracking).
+
+ Parent items receive hover enter events before their children, and
+ leave events after their children. The parent does not receive a
+ hover leave event if the cursor enters a child, though; the parent
+ stays "hovered" until the cursor leaves its area, including its
+ children's areas.
+
+ If a parent item handles child events, it will receive hover move,
+ drag move, and drop events as the cursor passes through its
+ children, but it does not receive hover enter and hover leave, nor
+ drag enter and drag leave events on behalf of its children.
+
+ A QGraphicsWidget with window decorations will accept hover events
+ regardless of the value of acceptHoverEvents().
+
+ \sa acceptHoverEvents(), hoverEnterEvent(), hoverMoveEvent(),
+ hoverLeaveEvent()
+*/
+void QGraphicsItem::setAcceptHoverEvents(bool enabled)
+{
+ if (d_ptr->acceptsHover == quint32(enabled))
+ return;
+ d_ptr->acceptsHover = quint32(enabled);
+ if (d_ptr->acceptsHover && d_ptr->scene && d_ptr->scene->d_func()->allItemsIgnoreHoverEvents) {
+ d_ptr->scene->d_func()->allItemsIgnoreHoverEvents = false;
+ d_ptr->scene->d_func()->enableMouseTrackingOnViews();
+ }
+}
+
+/*!
+ \obsolete
+
+ Use setAcceptHoverEvents(\a enabled) instead.
+*/
+void QGraphicsItem::setAcceptsHoverEvents(bool enabled)
+{
+ setAcceptHoverEvents(enabled);
+}
+
+/*! \since 4.6
+
+ Returns true if an item accepts \l{QTouchEvent}{touch events};
+ otherwise, returns false. By default, items do not accept touch events.
+
+ \sa setAcceptTouchEvents()
+*/
+bool QGraphicsItem::acceptTouchEvents() const
+{
+ return d_ptr->acceptTouchEvents;
+}
+
+/*!
+ \since 4.6
+
+ If \a enabled is true, this item will accept \l{QTouchEvent}{touch events};
+ otherwise, it will ignore them. By default, items do not accept
+ touch events.
+*/
+void QGraphicsItem::setAcceptTouchEvents(bool enabled)
+{
+ if (d_ptr->acceptTouchEvents == quint32(enabled))
+ return;
+ d_ptr->acceptTouchEvents = quint32(enabled);
+ if (d_ptr->acceptTouchEvents && d_ptr->scene && d_ptr->scene->d_func()->allItemsIgnoreTouchEvents) {
+ d_ptr->scene->d_func()->allItemsIgnoreTouchEvents = false;
+ d_ptr->scene->d_func()->enableTouchEventsOnViews();
+ }
+}
+
+/*!
+ \since 4.6
+
+ Returns true if this item filters child events (i.e., all events
+ intended for any of its children are instead sent to this item);
+ otherwise, false is returned.
+
+ The default value is false; child events are not filtered.
+
+ \sa setFiltersChildEvents()
+*/
+bool QGraphicsItem::filtersChildEvents() const
+{
+ return d_ptr->filtersDescendantEvents;
+}
+
+/*!
+ \since 4.6
+
+ If \a enabled is true, this item is set to filter all events for
+ all its children (i.e., all events intented for any of its
+ children are instead sent to this item); otherwise, if \a enabled
+ is false, this item will only handle its own events. The default
+ value is false.
+
+ \sa filtersChildEvents()
+*/
+void QGraphicsItem::setFiltersChildEvents(bool enabled)
+{
+ if (d_ptr->filtersDescendantEvents == enabled)
+ return;
+
+ d_ptr->filtersDescendantEvents = enabled;
+ d_ptr->updateAncestorFlag(QGraphicsItem::GraphicsItemFlag(-2));
+}
+
+/*!
+ \obsolete
+
+ Returns true if this item handles child events (i.e., all events
+ intended for any of its children are instead sent to this item);
+ otherwise, false is returned.
+
+ This property is useful for item groups; it allows one item to
+ handle events on behalf of its children, as opposed to its
+ children handling their events individually.
+
+ The default is to return false; children handle their own events.
+ The exception for this is if the item is a QGraphicsItemGroup, then
+ it defaults to return true.
+
+ \sa setHandlesChildEvents()
+*/
+bool QGraphicsItem::handlesChildEvents() const
+{
+ return d_ptr->handlesChildEvents;
+}
+
+/*!
+ \obsolete
+
+ If \a enabled is true, this item is set to handle all events for
+ all its children (i.e., all events intented for any of its
+ children are instead sent to this item); otherwise, if \a enabled
+ is false, this item will only handle its own events. The default
+ value is false.
+
+ This property is useful for item groups; it allows one item to
+ handle events on behalf of its children, as opposed to its
+ children handling their events individually.
+
+ If a child item accepts hover events, its parent will receive
+ hover move events as the cursor passes through the child, but it
+ does not receive hover enter and hover leave events on behalf of
+ its child.
+
+ \sa handlesChildEvents()
+*/
+void QGraphicsItem::setHandlesChildEvents(bool enabled)
+{
+ if (d_ptr->handlesChildEvents == enabled)
+ return;
+
+ d_ptr->handlesChildEvents = enabled;
+ d_ptr->updateAncestorFlag(QGraphicsItem::GraphicsItemFlag(-1));
+}
+/*!
+ \since 4.6
+ Returns true if this item is active; otherwise returns false.
+
+ An item can only be active if the scene is active. An item is active
+ if it is, or is a descendent of, an active panel. Items in non-active
+ panels are not active.
+
+ Items that are not part of a panel follow scene activation when the
+ scene has no active panel.
+
+ Only active items can gain input focus.
+
+ \sa QGraphicsScene::isActive(), QGraphicsScene::activePanel(), panel(), isPanel()
+*/
+bool QGraphicsItem::isActive() const
+{
+ if (!d_ptr->scene || !d_ptr->scene->isActive())
+ return false;
+ return panel() == d_ptr->scene->activePanel();
+}
+
+/*!
+ \since 4.6
+
+ If \a active is true, and the scene is active, this item's panel will be
+ activated. Otherwise, the panel is deactivated.
+
+ If the item is not part of an active scene, \a active will decide what
+ happens to the panel when the scene becomes active or the item is added to
+ the scene. If true, the item's panel will be activated when the item is
+ either added to the scene or the scene is activated. Otherwise, the item
+ will stay inactive independent of the scene's activated state.
+
+ \sa isPanel(), QGraphicsScene::setActivePanel(), QGraphicsScene::isActive()
+*/
+void QGraphicsItem::setActive(bool active)
+{
+ d_ptr->explicitActivate = 1;
+ d_ptr->wantsActive = active;
+ if (d_ptr->scene) {
+ if (active) {
+ // Activate this item.
+ d_ptr->scene->setActivePanel(this);
+ } else {
+ // Deactivate this item, and reactivate the last active item
+ // (if any).
+ QGraphicsItem *lastActive = d_ptr->scene->d_func()->lastActivePanel;
+ d_ptr->scene->setActivePanel(lastActive != this ? lastActive : 0);
+ }
+ }
+}
+
+/*!
+ Returns true if this item is active, and it or its \l{focusProxy()}{focus
+ proxy} has keyboard input focus; otherwise, returns false.
+
+ \sa focusItem(), setFocus(), QGraphicsScene::setFocusItem(), isActive()
+*/
+bool QGraphicsItem::hasFocus() const
+{
+ if (!d_ptr->scene || !d_ptr->scene->isActive())
+ return false;
+
+ if (d_ptr->focusProxy)
+ return d_ptr->focusProxy->hasFocus();
+
+ if (d_ptr->scene->d_func()->focusItem != this)
+ return false;
+
+ return panel() == d_ptr->scene->d_func()->activePanel;
+}
+
+/*!
+ Gives keyboard input focus to this item. The \a focusReason argument will
+ be passed into any \l{QFocusEvent}{focus event} generated by this function;
+ it is used to give an explanation of what caused the item to get focus.
+
+ Only enabled items that set the ItemIsFocusable flag can accept keyboard
+ focus.
+
+ If this item is not visible, not active, or not associated with a scene,
+ it will not gain immediate input focus. However, it will be registered as
+ the preferred focus item for its subtree of items, should it later become
+ visible.
+
+ As a result of calling this function, this item will receive a
+ \l{focusInEvent()}{focus in event} with \a focusReason. If another item
+ already has focus, that item will first receive a \l{focusOutEvent()}
+ {focus out event} indicating that it has lost input focus.
+
+ \sa clearFocus(), hasFocus(), focusItem(), focusProxy()
+*/
+void QGraphicsItem::setFocus(Qt::FocusReason focusReason)
+{
+ d_ptr->setFocusHelper(focusReason, /* climb = */ true, /* focusFromHide = */ false);
+}
+
+/*!
+ \internal
+*/
+void QGraphicsItemPrivate::setFocusHelper(Qt::FocusReason focusReason, bool climb, bool focusFromHide)
+{
+ // Disabled / unfocusable items cannot accept focus.
+ if (!q_ptr->isEnabled() || !(flags & QGraphicsItem::ItemIsFocusable))
+ return;
+
+ // Find focus proxy.
+ QGraphicsItem *f = q_ptr;
+ while (f->d_ptr->focusProxy)
+ f = f->d_ptr->focusProxy;
+
+ // Return if it already has focus.
+ if (scene && scene->focusItem() == f)
+ return;
+
+ // Update focus scope item ptr.
+ QGraphicsItem *p = parent;
+ while (p) {
+ if (p->flags() & QGraphicsItem::ItemIsFocusScope) {
+ QGraphicsItem *oldFocusScopeItem = p->d_ptr->focusScopeItem;
+ p->d_ptr->focusScopeItem = q_ptr;
+ if (!p->focusItem() && !focusFromHide) {
+ if (oldFocusScopeItem)
+ oldFocusScopeItem->d_ptr->focusScopeItemChange(false);
+ focusScopeItemChange(true);
+ // If you call setFocus on a child of a focus scope that
+ // doesn't currently have a focus item, then stop.
+ return;
+ }
+ break;
+ }
+ p = p->d_ptr->parent;
+ }
+
+ if (climb) {
+ while (f->d_ptr->focusScopeItem && f->d_ptr->focusScopeItem->isVisible())
+ f = f->d_ptr->focusScopeItem;
+ }
+
+ // Update the child focus chain.
+ QGraphicsItem *commonAncestor = 0;
+ if (scene && scene->focusItem()) {
+ commonAncestor = scene->focusItem()->commonAncestorItem(f);
+ scene->focusItem()->d_ptr->clearSubFocus(scene->focusItem(), commonAncestor);
+ }
+
+ f->d_ptr->setSubFocus(f, commonAncestor);
+
+ // Update the scene's focus item.
+ if (scene) {
+ QGraphicsItem *p = q_ptr->panel();
+ if ((!p && scene->isActive()) || (p && p->isActive())) {
+ // Visible items immediately gain focus from scene.
+ scene->d_func()->setFocusItemHelper(f, focusReason);
+ }
+ }
+}
+
+/*!
+ Takes keyboard input focus from the item.
+
+ If it has focus, a \l{focusOutEvent()}{focus out event} is sent to this
+ item to tell it that it is about to lose the focus.
+
+ Only items that set the ItemIsFocusable flag, or widgets that set an
+ appropriate focus policy, can accept keyboard focus.
+
+ \sa setFocus(), hasFocus(), QGraphicsWidget::focusPolicy
+*/
+void QGraphicsItem::clearFocus()
+{
+ d_ptr->clearFocusHelper(/* giveFocusToParent = */ true);
+}
+
+/*!
+ \internal
+*/
+void QGraphicsItemPrivate::clearFocusHelper(bool giveFocusToParent)
+{
+ if (giveFocusToParent) {
+ // Pass focus to the closest parent focus scope
+ if (!inDestructor) {
+ QGraphicsItem *p = parent;
+ while (p) {
+ if (p->flags() & QGraphicsItem::ItemIsFocusScope) {
+ if (p->d_ptr->focusScopeItem == q_ptr) {
+ p->d_ptr->focusScopeItem = 0;
+ if (!q_ptr->hasFocus()) //if it has focus, focusScopeItemChange is called elsewhere
+ focusScopeItemChange(false);
+ }
+ if (q_ptr->hasFocus())
+ p->d_ptr->setFocusHelper(Qt::OtherFocusReason, /* climb = */ false,
+ /* focusFromHide = */ false);
+ return;
+ }
+ p = p->d_ptr->parent;
+ }
+ }
+ }
+
+ if (q_ptr->hasFocus()) {
+ // Invisible items with focus must explicitly clear subfocus.
+ clearSubFocus(q_ptr);
+
+ // If this item has the scene's input focus, clear it.
+ scene->setFocusItem(0);
+ }
+}
+
+/*!
+ \since 4.6
+
+ Returns this item's focus proxy, or 0 if this item has no
+ focus proxy.
+
+ \sa setFocusProxy(), setFocus(), hasFocus()
+*/
+QGraphicsItem *QGraphicsItem::focusProxy() const
+{
+ return d_ptr->focusProxy;
+}
+
+/*!
+ \since 4.6
+
+ Sets the item's focus proxy to \a item.
+
+ If an item has a focus proxy, the focus proxy will receive
+ input focus when the item gains input focus. The item itself
+ will still have focus (i.e., hasFocus() will return true),
+ but only the focus proxy will receive the keyboard input.
+
+ A focus proxy can itself have a focus proxy, and so on. In
+ such case, keyboard input will be handled by the outermost
+ focus proxy.
+
+ The focus proxy \a item must belong to the same scene as
+ this item.
+
+ \sa focusProxy(), setFocus(), hasFocus()
+*/
+void QGraphicsItem::setFocusProxy(QGraphicsItem *item)
+{
+ if (item == d_ptr->focusProxy)
+ return;
+ if (item == this) {
+ qWarning("QGraphicsItem::setFocusProxy: cannot assign self as focus proxy");
+ return;
+ }
+ if (item) {
+ if (item->d_ptr->scene != d_ptr->scene) {
+ qWarning("QGraphicsItem::setFocusProxy: focus proxy must be in same scene");
+ return;
+ }
+ for (QGraphicsItem *f = item->focusProxy(); f != 0; f = f->focusProxy()) {
+ if (f == this) {
+ qWarning("QGraphicsItem::setFocusProxy: %p is already in the focus proxy chain", item);
+ return;
+ }
+ }
+ }
+
+ QGraphicsItem *lastFocusProxy = d_ptr->focusProxy;
+ if (lastFocusProxy)
+ lastFocusProxy->d_ptr->focusProxyRefs.removeOne(&d_ptr->focusProxy);
+ d_ptr->focusProxy = item;
+ if (item)
+ item->d_ptr->focusProxyRefs << &d_ptr->focusProxy;
+}
+
+/*!
+ \since 4.6
+
+ If this item, a child or descendant of this item currently has input
+ focus, this function will return a pointer to that item. If
+ no descendant has input focus, 0 is returned.
+
+ \sa hasFocus(), setFocus(), QWidget::focusWidget()
+*/
+QGraphicsItem *QGraphicsItem::focusItem() const
+{
+ return d_ptr->subFocusItem;
+}
+
+/*!
+ \internal
+
+ Returns this item's focus scope item.
+*/
+QGraphicsItem *QGraphicsItem::focusScopeItem() const
+{
+ return d_ptr->focusScopeItem;
+}
+
+/*!
+ \since 4.4
+ Grabs the mouse input.
+
+ This item will receive all mouse events for the scene until any of the
+ following events occurs:
+
+ \list
+ \o The item becomes invisible
+ \o The item is removed from the scene
+ \o The item is deleted
+ \o The item call ungrabMouse()
+ \o Another item calls grabMouse(); the item will regain the mouse grab
+ when the other item calls ungrabMouse().
+ \endlist
+
+ When an item gains the mouse grab, it receives a QEvent::GrabMouse
+ event. When it loses the mouse grab, it receives a QEvent::UngrabMouse
+ event. These events can be used to detect when your item gains or loses
+ the mouse grab through other means than receiving mouse button events.
+
+ It is almost never necessary to explicitly grab the mouse in Qt, as Qt
+ grabs and releases it sensibly. In particular, Qt grabs the mouse when you
+ press a mouse button, and keeps the mouse grabbed until you release the
+ last mouse button. Also, Qt::Popup widgets implicitly call grabMouse()
+ when shown, and ungrabMouse() when hidden.
+
+ Note that only visible items can grab mouse input. Calling grabMouse() on
+ an invisible item has no effect.
+
+ Keyboard events are not affected.
+
+ \sa QGraphicsScene::mouseGrabberItem(), ungrabMouse(), grabKeyboard()
+*/
+void QGraphicsItem::grabMouse()
+{
+ if (!d_ptr->scene) {
+ qWarning("QGraphicsItem::grabMouse: cannot grab mouse without scene");
+ return;
+ }
+ if (!d_ptr->visible) {
+ qWarning("QGraphicsItem::grabMouse: cannot grab mouse while invisible");
+ return;
+ }
+ d_ptr->scene->d_func()->grabMouse(this);
+}
+
+/*!
+ \since 4.4
+ Releases the mouse grab.
+
+ \sa grabMouse(), ungrabKeyboard()
+*/
+void QGraphicsItem::ungrabMouse()
+{
+ if (!d_ptr->scene) {
+ qWarning("QGraphicsItem::ungrabMouse: cannot ungrab mouse without scene");
+ return;
+ }
+ d_ptr->scene->d_func()->ungrabMouse(this);
+}
+
+/*!
+ \since 4.4
+ Grabs the keyboard input.
+
+ The item will receive all keyboard input to the scene until one of the
+ following events occur:
+
+ \list
+ \o The item becomes invisible
+ \o The item is removed from the scene
+ \o The item is deleted
+ \o The item calls ungrabKeyboard()
+ \o Another item calls grabKeyboard(); the item will regain the keyboard grab
+ when the other item calls ungrabKeyboard().
+ \endlist
+
+ When an item gains the keyboard grab, it receives a QEvent::GrabKeyboard
+ event. When it loses the keyboard grab, it receives a
+ QEvent::UngrabKeyboard event. These events can be used to detect when your
+ item gains or loses the keyboard grab through other means than gaining
+ input focus.
+
+ It is almost never necessary to explicitly grab the keyboard in Qt, as Qt
+ grabs and releases it sensibly. In particular, Qt grabs the keyboard when
+ your item gains input focus, and releases it when your item loses input
+ focus, or when the item is hidden.
+
+ Note that only visible items can grab keyboard input. Calling
+ grabKeyboard() on an invisible item has no effect.
+
+ Keyboard events are not affected.
+
+ \sa ungrabKeyboard(), grabMouse(), setFocus()
+*/
+void QGraphicsItem::grabKeyboard()
+{
+ if (!d_ptr->scene) {
+ qWarning("QGraphicsItem::grabKeyboard: cannot grab keyboard without scene");
+ return;
+ }
+ if (!d_ptr->visible) {
+ qWarning("QGraphicsItem::grabKeyboard: cannot grab keyboard while invisible");
+ return;
+ }
+ d_ptr->scene->d_func()->grabKeyboard(this);
+}
+
+/*!
+ \since 4.4
+ Releases the keyboard grab.
+
+ \sa grabKeyboard(), ungrabMouse()
+*/
+void QGraphicsItem::ungrabKeyboard()
+{
+ if (!d_ptr->scene) {
+ qWarning("QGraphicsItem::ungrabKeyboard: cannot ungrab keyboard without scene");
+ return;
+ }
+ d_ptr->scene->d_func()->ungrabKeyboard(this);
+}
+
+/*!
+ Returns the position of the item in parent coordinates. If the item has no
+ parent, its position is given in scene coordinates.
+
+ The position of the item describes its origin (local coordinate
+ (0, 0)) in parent coordinates; this function returns the same as
+ mapToParent(0, 0).
+
+ For convenience, you can also call scenePos() to determine the
+ item's position in scene coordinates, regardless of its parent.
+
+ \sa x(), y(), setPos(), transform(), {The Graphics View Coordinate System}
+*/
+QPointF QGraphicsItem::pos() const
+{
+ return d_ptr->pos;
+}
+
+/*!
+ \fn QGraphicsItem::x() const
+
+ This convenience function is equivalent to calling pos().x().
+
+ \sa y()
+*/
+
+/*!
+ \since 4.6
+
+ Set's the \a x coordinate of the item's position. Equivalent to
+ calling setPos(x, y()).
+
+ \sa x(), setPos()
+*/
+void QGraphicsItem::setX(qreal x)
+{
+ if (d_ptr->inDestructor)
+ return;
+
+ if (qIsNaN(x))
+ return;
+
+ setPos(QPointF(x, d_ptr->pos.y()));
+}
+
+/*!
+ \fn QGraphicsItem::y() const
+
+ This convenience function is equivalent to calling pos().y().
+
+ \sa x()
+*/
+
+/*!
+ \since 4.6
+
+ Set's the \a y coordinate of the item's position. Equivalent to
+ calling setPos(x(), y).
+
+ \sa x(), setPos()
+*/
+void QGraphicsItem::setY(qreal y)
+{
+ if (d_ptr->inDestructor)
+ return;
+
+ if (qIsNaN(y))
+ return;
+
+ setPos(QPointF(d_ptr->pos.x(), y));
+}
+
+/*!
+ Returns the item's position in scene coordinates. This is
+ equivalent to calling \c mapToScene(0, 0).
+
+ \sa pos(), sceneTransform(), {The Graphics View Coordinate System}
+*/
+QPointF QGraphicsItem::scenePos() const
+{
+ return mapToScene(0, 0);
+}
+
+/*!
+ \internal
+
+ Sets the position \a pos.
+*/
+void QGraphicsItemPrivate::setPosHelper(const QPointF &pos)
+{
+ Q_Q(QGraphicsItem);
+ inSetPosHelper = 1;
+ if (scene)
+ q->prepareGeometryChange();
+ QPointF oldPos = this->pos;
+ this->pos = pos;
+ dirtySceneTransform = 1;
+ inSetPosHelper = 0;
+ if (isObject) {
+ if (pos.x() != oldPos.x())
+ emit static_cast<QGraphicsObject *>(q_ptr)->xChanged();
+ if (pos.y() != oldPos.y())
+ emit static_cast<QGraphicsObject *>(q_ptr)->yChanged();
+ }
+}
+
+/*!
+ \internal
+
+ Sets the transform \a transform.
+*/
+void QGraphicsItemPrivate::setTransformHelper(const QTransform &transform)
+{
+ q_ptr->prepareGeometryChange();
+ transformData->transform = transform;
+ dirtySceneTransform = 1;
+ transformChanged();
+}
+
+/*!
+ Sets the position of the item to \a pos, which is in parent
+ coordinates. For items with no parent, \a pos is in scene
+ coordinates.
+
+ The position of the item describes its origin (local coordinate
+ (0, 0)) in parent coordinates.
+
+ \sa pos(), scenePos(), {The Graphics View Coordinate System}
+*/
+void QGraphicsItem::setPos(const QPointF &pos)
+{
+ if (d_ptr->pos == pos)
+ return;
+
+ if (d_ptr->inDestructor)
+ return;
+
+ // Update and repositition.
+ if (!(d_ptr->flags & (ItemSendsGeometryChanges | ItemSendsScenePositionChanges))) {
+ d_ptr->setPosHelper(pos);
+ if (d_ptr->isWidget)
+ static_cast<QGraphicsWidget *>(this)->d_func()->setGeometryFromSetPos();
+ if (d_ptr->scenePosDescendants)
+ d_ptr->sendScenePosChange();
+ return;
+ }
+
+ // Notify the item that the position is changing.
+ const QVariant newPosVariant(itemChange(ItemPositionChange, QVariant::fromValue<QPointF>(pos)));
+ QPointF newPos = newPosVariant.toPointF();
+ if (newPos == d_ptr->pos)
+ return;
+
+ // Update and repositition.
+ d_ptr->setPosHelper(newPos);
+
+ // Send post-notification.
+ itemChange(QGraphicsItem::ItemPositionHasChanged, newPosVariant);
+ d_ptr->sendScenePosChange();
+}
+
+/*!
+ \fn void QGraphicsItem::setPos(qreal x, qreal y)
+ \overload
+
+ This convenience function is equivalent to calling setPos(QPointF(\a x, \a
+ y)).
+*/
+
+/*!
+ \fn void QGraphicsItem::moveBy(qreal dx, qreal dy)
+
+ Moves the item by \a dx points horizontally, and \a dy point
+ vertically. This function is equivalent to calling setPos(pos() +
+ QPointF(\a dx, \a dy)).
+*/
+
+/*!
+ If this item is part of a scene that is viewed by a QGraphicsView, this
+ convenience function will attempt to scroll the view to ensure that \a
+ rect is visible inside the view's viewport. If \a rect is a null rect (the
+ default), QGraphicsItem will default to the item's bounding rect. \a xmargin
+ and \a ymargin are the number of pixels the view should use for margins.
+
+ If the specified rect cannot be reached, the contents are scrolled to the
+ nearest valid position.
+
+ If this item is not viewed by a QGraphicsView, this function does nothing.
+
+ \sa QGraphicsView::ensureVisible()
+*/
+void QGraphicsItem::ensureVisible(const QRectF &rect, int xmargin, int ymargin)
+{
+ if (d_ptr->scene) {
+ QRectF sceneRect;
+ if (!rect.isNull())
+ sceneRect = sceneTransform().mapRect(rect);
+ else
+ sceneRect = sceneBoundingRect();
+ foreach (QGraphicsView *view, d_ptr->scene->d_func()->views)
+ view->ensureVisible(sceneRect, xmargin, ymargin);
+ }
+}
+
+/*!
+ \fn void QGraphicsItem::ensureVisible(qreal x, qreal y, qreal w, qreal h,
+ int xmargin = 50, int ymargin = 50)
+
+ This convenience function is equivalent to calling
+ ensureVisible(QRectF(\a x, \a y, \a w, \a h), \a xmargin, \a ymargin):
+*/
+
+/*!
+ \obsolete
+
+ Returns the item's affine transformation matrix. This is a subset or the
+ item's full transformation matrix, and might not represent the item's full
+ transformation.
+
+ Use transform() instead.
+
+ \sa setTransform(), sceneTransform()
+*/
+QMatrix QGraphicsItem::matrix() const
+{
+ return transform().toAffine();
+}
+
+/*!
+ \since 4.3
+
+ Returns this item's transformation matrix.
+
+ The transformation matrix is combined with the item's rotation(), scale()
+ and transformations() into a combined transformations for the item.
+
+ The default transformation matrix is an identity matrix.
+
+ \sa setTransform(), sceneTransform()
+*/
+QTransform QGraphicsItem::transform() const
+{
+ if (!d_ptr->transformData)
+ return QTransform();
+ return d_ptr->transformData->transform;
+}
+
+/*!
+ \since 4.6
+
+ Returns the clockwise rotation, in degrees, around the Z axis. The default
+ value is 0 (i.e., the item is not rotated).
+
+ The rotation is combined with the item's scale(), transform() and
+ transformations() to map the item's coordinate system to the parent item.
+
+ \sa setRotation(), transformOriginPoint(), {Transformations}
+*/
+qreal QGraphicsItem::rotation() const
+{
+ if (!d_ptr->transformData)
+ return 0;
+ return d_ptr->transformData->rotation;
+}
+
+/*!
+ \since 4.6
+
+ Sets the clockwise rotation \a angle, in degrees, around the Z axis. The
+ default value is 0 (i.e., the item is not rotated). Assigning a negative
+ value will rotate the item counter-clockwise. Normally the rotation angle
+ is in the range (-360, 360), but it's also possible to assign values
+ outside of this range (e.g., a rotation of 370 degrees is the same as a
+ rotation of 10 degrees).
+
+ The item is rotated around its transform origin point, which by default
+ is (0, 0). You can select a different transformation origin by calling
+ setTransformOriginPoint().
+
+ The rotation is combined with the item's scale(), transform() and
+ transformations() to map the item's coordinate system to the parent item.
+
+ \sa rotation(), setTransformOriginPoint(), {Transformations}
+*/
+void QGraphicsItem::setRotation(qreal angle)
+{
+ prepareGeometryChange();
+ qreal newRotation = angle;
+
+ if (d_ptr->flags & ItemSendsGeometryChanges) {
+ // Notify the item that the rotation is changing.
+ const QVariant newRotationVariant(itemChange(ItemRotationChange, angle));
+ newRotation = newRotationVariant.toReal();
+ }
+
+ if (!d_ptr->transformData)
+ d_ptr->transformData = new QGraphicsItemPrivate::TransformData;
+
+ if (d_ptr->transformData->rotation == newRotation)
+ return;
+
+ d_ptr->transformData->rotation = newRotation;
+ d_ptr->transformData->onlyTransform = false;
+ d_ptr->dirtySceneTransform = 1;
+
+ // Send post-notification.
+ if (d_ptr->flags & ItemSendsGeometryChanges)
+ itemChange(ItemRotationHasChanged, newRotation);
+
+ if (d_ptr->isObject)
+ emit static_cast<QGraphicsObject *>(this)->rotationChanged();
+
+ d_ptr->transformChanged();
+}
+
+/*!
+ \since 4.6
+
+ Returns the scale factor of the item. The default scale factor is 1.0
+ (i.e., the item is not scaled).
+
+ The scale is combined with the item's rotation(), transform() and
+ transformations() to map the item's coordinate system to the parent item.
+
+ \sa setScale(), rotation(), {Transformations}
+*/
+qreal QGraphicsItem::scale() const
+{
+ if (!d_ptr->transformData)
+ return 1.;
+ return d_ptr->transformData->scale;
+}
+
+/*!
+ \since 4.6
+
+ Sets the scale \a factor of the item. The default scale factor is 1.0
+ (i.e., the item is not scaled). A scale factor of 0.0 will collapse the
+ item to a single point. If you provide a negative scale factor, the
+ item will be flipped and mirrored (i.e., rotated 180 degrees).
+
+ The item is scaled around its transform origin point, which by default
+ is (0, 0). You can select a different transformation origin by calling
+ setTransformOriginPoint().
+
+ The scale is combined with the item's rotation(), transform() and
+ transformations() to map the item's coordinate system to the parent item.
+
+ \sa scale(), setTransformOriginPoint(), {Transformations Example}
+*/
+void QGraphicsItem::setScale(qreal factor)
+{
+ prepareGeometryChange();
+ qreal newScale = factor;
+
+ if (d_ptr->flags & ItemSendsGeometryChanges) {
+ // Notify the item that the scale is changing.
+ const QVariant newScaleVariant(itemChange(ItemScaleChange, factor));
+ newScale = newScaleVariant.toReal();
+ }
+
+ if (!d_ptr->transformData)
+ d_ptr->transformData = new QGraphicsItemPrivate::TransformData;
+
+ if (d_ptr->transformData->scale == newScale)
+ return;
+
+ d_ptr->transformData->scale = newScale;
+ d_ptr->transformData->onlyTransform = false;
+ d_ptr->dirtySceneTransform = 1;
+
+ // Send post-notification.
+ if (d_ptr->flags & ItemSendsGeometryChanges)
+ itemChange(ItemScaleHasChanged, newScale);
+
+ if (d_ptr->isObject)
+ emit static_cast<QGraphicsObject *>(this)->scaleChanged();
+
+ d_ptr->transformChanged();
+}
+
+
+/*!
+ \since 4.6
+
+ Returns a list of graphics transforms that currently apply to this item.
+
+ QGraphicsTransform is for applying and controlling a chain of individual
+ transformation operations on an item. It's particularly useful in
+ animations, where each transform operation needs to be interpolated
+ independently, or differently.
+
+ The transformations are combined with the item's rotation(), scale() and
+ transform() to map the item's coordinate system to the parent item.
+
+ \sa scale(), rotation(), transformOriginPoint(), {Transformations}
+*/
+QList<QGraphicsTransform *> QGraphicsItem::transformations() const
+{
+ if (!d_ptr->transformData)
+ return QList<QGraphicsTransform *>();
+ return d_ptr->transformData->graphicsTransforms;
+}
+
+/*!
+ \since 4.6
+
+ Sets a list of graphics \a transformations (QGraphicsTransform) that
+ currently apply to this item.
+
+ If all you want is to rotate or scale an item, you should call setRotation()
+ or setScale() instead. If you want to set an arbitrary transformation on
+ an item, you can call setTransform().
+
+ QGraphicsTransform is for applying and controlling a chain of individual
+ transformation operations on an item. It's particularly useful in
+ animations, where each transform operation needs to be interpolated
+ independently, or differently.
+
+ The transformations are combined with the item's rotation(), scale() and
+ transform() to map the item's coordinate system to the parent item.
+
+ \sa scale(), setTransformOriginPoint(), {Transformations}
+*/
+void QGraphicsItem::setTransformations(const QList<QGraphicsTransform *> &transformations)
+{
+ prepareGeometryChange();
+ if (!d_ptr->transformData)
+ d_ptr->transformData = new QGraphicsItemPrivate::TransformData;
+ d_ptr->transformData->graphicsTransforms = transformations;
+ for (int i = 0; i < transformations.size(); ++i)
+ transformations.at(i)->d_func()->setItem(this);
+ d_ptr->transformData->onlyTransform = false;
+ d_ptr->dirtySceneTransform = 1;
+ d_ptr->transformChanged();
+}
+
+/*!
+ \internal
+*/
+void QGraphicsItemPrivate::prependGraphicsTransform(QGraphicsTransform *t)
+{
+ if (!transformData)
+ transformData = new QGraphicsItemPrivate::TransformData;
+ if (!transformData->graphicsTransforms.contains(t))
+ transformData->graphicsTransforms.prepend(t);
+
+ Q_Q(QGraphicsItem);
+ t->d_func()->setItem(q);
+ transformData->onlyTransform = false;
+ dirtySceneTransform = 1;
+ transformChanged();
+}
+
+/*!
+ \internal
+*/
+void QGraphicsItemPrivate::appendGraphicsTransform(QGraphicsTransform *t)
+{
+ if (!transformData)
+ transformData = new QGraphicsItemPrivate::TransformData;
+ if (!transformData->graphicsTransforms.contains(t))
+ transformData->graphicsTransforms.append(t);
+
+ Q_Q(QGraphicsItem);
+ t->d_func()->setItem(q);
+ transformData->onlyTransform = false;
+ dirtySceneTransform = 1;
+ transformChanged();
+}
+
+/*!
+ \since 4.6
+
+ Returns the origin point for the transformation in item coordinates.
+
+ The default is QPointF(0,0).
+
+ \sa setTransformOriginPoint(), {Transformations}
+*/
+QPointF QGraphicsItem::transformOriginPoint() const
+{
+ if (!d_ptr->transformData)
+ return QPointF(0,0);
+ return QPointF(d_ptr->transformData->xOrigin, d_ptr->transformData->yOrigin);
+}
+
+/*!
+ \since 4.6
+
+ Sets the \a origin point for the transformation in item coordinates.
+
+ \sa transformOriginPoint(), {Transformations}
+*/
+void QGraphicsItem::setTransformOriginPoint(const QPointF &origin)
+{
+ prepareGeometryChange();
+ QPointF newOrigin = origin;
+
+ if (d_ptr->flags & ItemSendsGeometryChanges) {
+ // Notify the item that the origin point is changing.
+ const QVariant newOriginVariant(itemChange(ItemTransformOriginPointChange,
+ QVariant::fromValue<QPointF>(origin)));
+ newOrigin = newOriginVariant.toPointF();
+ }
+
+ if (!d_ptr->transformData)
+ d_ptr->transformData = new QGraphicsItemPrivate::TransformData;
+
+ if (d_ptr->transformData->xOrigin == newOrigin.x()
+ && d_ptr->transformData->yOrigin == newOrigin.y()) {
+ return;
+ }
+
+ d_ptr->transformData->xOrigin = newOrigin.x();
+ d_ptr->transformData->yOrigin = newOrigin.y();
+ d_ptr->transformData->onlyTransform = false;
+ d_ptr->dirtySceneTransform = 1;
+
+ // Send post-notification.
+ if (d_ptr->flags & ItemSendsGeometryChanges)
+ itemChange(ItemTransformOriginPointHasChanged, QVariant::fromValue<QPointF>(newOrigin));
+}
+
+/*!
+ \fn void QGraphicsItem::setTransformOriginPoint(qreal x, qreal y)
+
+ \since 4.6
+ \overload
+
+ Sets the origin point for the transformation in item coordinates.
+ This is equivalent to calling setTransformOriginPoint(QPointF(\a x, \a y)).
+
+ \sa setTransformOriginPoint(), {Transformations}
+*/
+
+
+/*!
+ \obsolete
+
+ Use sceneTransform() instead.
+
+ \sa transform(), setTransform(), scenePos(), {The Graphics View Coordinate System}
+*/
+QMatrix QGraphicsItem::sceneMatrix() const
+{
+ d_ptr->ensureSceneTransform();
+ return d_ptr->sceneTransform.toAffine();
+}
+
+
+/*!
+ \since 4.3
+
+ Returns this item's scene transformation matrix. This matrix can be used
+ to map coordinates and geometrical shapes from this item's local
+ coordinate system to the scene's coordinate system. To map coordinates
+ from the scene, you must first invert the returned matrix.
+
+ Example:
+
+ \snippet doc/src/snippets/code/src_gui_graphicsview_qgraphicsitem.cpp 4
+
+ Unlike transform(), which returns only an item's local transformation, this
+ function includes the item's (and any parents') position, and all the transfomation properties.
+
+ \sa transform(), setTransform(), scenePos(), {The Graphics View Coordinate System}, {Transformations}
+*/
+QTransform QGraphicsItem::sceneTransform() const
+{
+ d_ptr->ensureSceneTransform();
+ return d_ptr->sceneTransform;
+}
+
+/*!
+ \since 4.3
+
+ Returns this item's device transformation matrix, using \a
+ viewportTransform to map from scene to device coordinates. This matrix can
+ be used to map coordinates and geometrical shapes from this item's local
+ coordinate system to the viewport's (or any device's) coordinate
+ system. To map coordinates from the viewport, you must first invert the
+ returned matrix.
+
+ Example:
+
+ \snippet doc/src/snippets/code/src_gui_graphicsview_qgraphicsitem.cpp 5
+
+ This function is the same as combining this item's scene transform with
+ the view's viewport transform, but it also understands the
+ ItemIgnoresTransformations flag. The device transform can be used to do
+ accurate coordinate mapping (and collision detection) for untransformable
+ items.
+
+ \sa transform(), setTransform(), scenePos(), {The Graphics View Coordinate
+ System}, itemTransform()
+*/
+QTransform QGraphicsItem::deviceTransform(const QTransform &viewportTransform) const
+{
+ // Ensure we return the standard transform if we're not untransformable.
+ if (!d_ptr->itemIsUntransformable()) {
+ d_ptr->ensureSceneTransform();
+ return d_ptr->sceneTransform * viewportTransform;
+ }
+
+ // Find the topmost item that ignores view transformations.
+ const QGraphicsItem *untransformedAncestor = this;
+ QList<const QGraphicsItem *> parents;
+ while (untransformedAncestor && ((untransformedAncestor->d_ptr->ancestorFlags
+ & QGraphicsItemPrivate::AncestorIgnoresTransformations))) {
+ parents.prepend(untransformedAncestor);
+ untransformedAncestor = untransformedAncestor->parentItem();
+ }
+
+ if (!untransformedAncestor) {
+ // Assert in debug mode, continue in release.
+ Q_ASSERT_X(untransformedAncestor, "QGraphicsItem::deviceTransform",
+ "Invalid object structure!");
+ return QTransform();
+ }
+
+ // First translate the base untransformable item.
+ untransformedAncestor->d_ptr->ensureSceneTransform();
+ QPointF mappedPoint = (untransformedAncestor->d_ptr->sceneTransform * viewportTransform).map(QPointF(0, 0));
+
+ // COMBINE
+ QTransform matrix = QTransform::fromTranslate(mappedPoint.x(), mappedPoint.y());
+ if (untransformedAncestor->d_ptr->transformData)
+ matrix = untransformedAncestor->d_ptr->transformData->computedFullTransform(&matrix);
+
+ // Then transform and translate all children.
+ for (int i = 0; i < parents.size(); ++i) {
+ const QGraphicsItem *parent = parents.at(i);
+ parent->d_ptr->combineTransformFromParent(&matrix);
+ }
+
+ return matrix;
+}
+
+/*!
+ \since 4.5
+
+ Returns a QTransform that maps coordinates from this item to \a other. If
+ \a ok is not null, and if there is no such transform, the boolean pointed
+ to by \a ok will be set to false; otherwise it will be set to true.
+
+ This transform provides an alternative to the mapToItem() or mapFromItem()
+ functions, by returning the appropriate transform so that you can map
+ shapes and coordinates yourself. It also helps you write more efficient
+ code when repeatedly mapping between the same two items.
+
+ \note In rare circumstances, there is no transform that maps between two
+ items.
+
+ \sa mapToItem(), mapFromItem(), deviceTransform()
+*/
+QTransform QGraphicsItem::itemTransform(const QGraphicsItem *other, bool *ok) const
+{
+ // Catch simple cases first.
+ if (other == 0) {
+ qWarning("QGraphicsItem::itemTransform: null pointer passed");
+ return QTransform();
+ }
+ if (other == this) {
+ if (ok)
+ *ok = true;
+ return QTransform();
+ }
+
+ QGraphicsItem *parent = d_ptr->parent;
+ const QGraphicsItem *otherParent = other->d_ptr->parent;
+
+ // This is other's child
+ if (parent == other) {
+ if (ok)
+ *ok = true;
+ QTransform x;
+ d_ptr->combineTransformFromParent(&x);
+ return x;
+ }
+
+ // This is other's parent
+ if (otherParent == this) {
+ const QPointF &otherPos = other->d_ptr->pos;
+ if (other->d_ptr->transformData) {
+ QTransform otherToParent;
+ other->d_ptr->combineTransformFromParent(&otherToParent);
+ return otherToParent.inverted(ok);
+ }
+ if (ok)
+ *ok = true;
+ return QTransform::fromTranslate(-otherPos.x(), -otherPos.y());
+ }
+
+ // Siblings
+ if (parent == otherParent) {
+ // COMBINE
+ const QPointF &itemPos = d_ptr->pos;
+ const QPointF &otherPos = other->d_ptr->pos;
+ if (!d_ptr->transformData && !other->d_ptr->transformData) {
+ QPointF delta = itemPos - otherPos;
+ if (ok)
+ *ok = true;
+ return QTransform::fromTranslate(delta.x(), delta.y());
+ }
+
+ QTransform itemToParent;
+ d_ptr->combineTransformFromParent(&itemToParent);
+ QTransform otherToParent;
+ other->d_ptr->combineTransformFromParent(&otherToParent);
+ return itemToParent * otherToParent.inverted(ok);
+ }
+
+ // Find the closest common ancestor. If the two items don't share an
+ // ancestor, then the only way is to combine their scene transforms.
+ const QGraphicsItem *commonAncestor = commonAncestorItem(other);
+ if (!commonAncestor) {
+ d_ptr->ensureSceneTransform();
+ other->d_ptr->ensureSceneTransform();
+ return d_ptr->sceneTransform * other->d_ptr->sceneTransform.inverted(ok);
+ }
+
+ // If the two items are cousins (in sibling branches), map both to the
+ // common ancestor, and combine the two transforms.
+ bool cousins = other != commonAncestor && this != commonAncestor;
+ if (cousins) {
+ bool good = false;
+ QTransform thisToScene = itemTransform(commonAncestor, &good);
+ QTransform otherToScene(Qt::Uninitialized);
+ if (good)
+ otherToScene = other->itemTransform(commonAncestor, &good);
+ if (!good) {
+ if (ok)
+ *ok = false;
+ return QTransform();
+ }
+ return thisToScene * otherToScene.inverted(ok);
+ }
+
+ // One is an ancestor of the other; walk the chain.
+ bool parentOfOther = isAncestorOf(other);
+ const QGraphicsItem *child = parentOfOther ? other : this;
+ const QGraphicsItem *root = parentOfOther ? this : other;
+
+ QTransform x;
+ const QGraphicsItem *p = child;
+ do {
+ p->d_ptr.data()->combineTransformToParent(&x);
+ } while ((p = p->d_ptr->parent) && p != root);
+ if (parentOfOther)
+ return x.inverted(ok);
+ if (ok)
+ *ok = true;
+ return x;
+}
+
+/*!
+ \obsolete
+
+ Sets the item's affine transformation matrix. This is a subset or the
+ item's full transformation matrix, and might not represent the item's full
+ transformation.
+
+ Use setTransform() instead.
+
+ \sa transform(), {The Graphics View Coordinate System}
+*/
+void QGraphicsItem::setMatrix(const QMatrix &matrix, bool combine)
+{
+ if (!d_ptr->transformData)
+ d_ptr->transformData = new QGraphicsItemPrivate::TransformData;
+
+ QTransform newTransform(combine ? QTransform(matrix) * d_ptr->transformData->transform : QTransform(matrix));
+ if (d_ptr->transformData->transform == newTransform)
+ return;
+
+ // Update and set the new transformation.
+ if (!(d_ptr->flags & ItemSendsGeometryChanges)) {
+ d_ptr->setTransformHelper(newTransform);
+ return;
+ }
+
+ // Notify the item that the transformation matrix is changing.
+ const QVariant newMatrixVariant = QVariant::fromValue<QMatrix>(newTransform.toAffine());
+ newTransform = QTransform(qvariant_cast<QMatrix>(itemChange(ItemMatrixChange, newMatrixVariant)));
+ if (d_ptr->transformData->transform == newTransform)
+ return;
+
+ // Update and set the new transformation.
+ d_ptr->setTransformHelper(newTransform);
+
+ // Send post-notification.
+ itemChange(ItemTransformHasChanged, QVariant::fromValue<QTransform>(newTransform));
+}
+
+/*!
+ \since 4.3
+
+ Sets the item's current transformation matrix to \a matrix.
+
+ If \a combine is true, then \a matrix is combined with the current matrix;
+ otherwise, \a matrix \e replaces the current matrix. \a combine is false
+ by default.
+
+ To simplify interation with items using a transformed view, QGraphicsItem
+ provides mapTo... and mapFrom... functions that can translate between
+ items' and the scene's coordinates. For example, you can call mapToScene()
+ to map an item coordiate to a scene coordinate, or mapFromScene() to map
+ from scene coordinates to item coordinates.
+
+ The transformation matrix is combined with the item's rotation(), scale()
+ and transformations() into a combined transformation that maps the item's
+ coordinate system to its parent.
+
+ \sa transform(), setRotation(), setScale(), setTransformOriginPoint(), {The Graphics View Coordinate System}, {Transformations}
+*/
+void QGraphicsItem::setTransform(const QTransform &matrix, bool combine)
+{
+ if (!d_ptr->transformData)
+ d_ptr->transformData = new QGraphicsItemPrivate::TransformData;
+
+ QTransform newTransform(combine ? matrix * d_ptr->transformData->transform : matrix);
+ if (d_ptr->transformData->transform == newTransform)
+ return;
+
+ // Update and set the new transformation.
+ if (!(d_ptr->flags & (ItemSendsGeometryChanges | ItemSendsScenePositionChanges))) {
+ d_ptr->setTransformHelper(newTransform);
+ if (d_ptr->scenePosDescendants)
+ d_ptr->sendScenePosChange();
+ return;
+ }
+
+ // Notify the item that the transformation matrix is changing.
+ const QVariant newTransformVariant(itemChange(ItemTransformChange,
+ QVariant::fromValue<QTransform>(newTransform)));
+ newTransform = qvariant_cast<QTransform>(newTransformVariant);
+ if (d_ptr->transformData->transform == newTransform)
+ return;
+
+ // Update and set the new transformation.
+ d_ptr->setTransformHelper(newTransform);
+
+ // Send post-notification.
+ itemChange(ItemTransformHasChanged, newTransformVariant);
+ d_ptr->sendScenePosChange();
+}
+
+/*!
+ \obsolete
+
+ Use resetTransform() instead.
+*/
+void QGraphicsItem::resetMatrix()
+{
+ resetTransform();
+}
+
+/*!
+ \since 4.3
+
+ Resets this item's transformation matrix to the identity matrix or
+ all the transformation properties to their default values.
+ This is equivalent to calling \c setTransform(QTransform()).
+
+ \sa setTransform(), transform()
+*/
+void QGraphicsItem::resetTransform()
+{
+ setTransform(QTransform(), false);
+}
+
+/*!
+ \obsolete
+
+ Use
+
+ \code
+ setRotation(rotation() + angle);
+ \endcode
+
+ instead.
+
+ Rotates the current item transformation \a angle degrees clockwise around
+ its origin. To translate around an arbitrary point (x, y), you need to
+ combine translation and rotation with setTransform().
+
+ Example:
+
+ \snippet doc/src/snippets/code/src_gui_graphicsview_qgraphicsitem.cpp 6
+
+ \sa setTransform(), transform(), scale(), shear(), translate()
+*/
+void QGraphicsItem::rotate(qreal angle)
+{
+ setTransform(QTransform().rotate(angle), true);
+}
+
+/*!
+ \obsolete
+
+ Use
+
+ \code
+ setTransform(QTransform::fromScale(sx, sy), true);
+ \endcode
+
+ instead.
+
+ Scales the current item transformation by (\a sx, \a sy) around its
+ origin. To scale from an arbitrary point (x, y), you need to combine
+ translation and scaling with setTransform().
+
+ Example:
+
+ \snippet doc/src/snippets/code/src_gui_graphicsview_qgraphicsitem.cpp 7
+
+ \sa setTransform(), transform()
+*/
+void QGraphicsItem::scale(qreal sx, qreal sy)
+{
+ setTransform(QTransform::fromScale(sx, sy), true);
+}
+
+/*!
+ \obsolete
+
+ Use
+
+ \code
+ setTransform(QTransform().shear(sh, sv), true);
+ \endcode
+
+ instead.
+
+ Shears the current item transformation by (\a sh, \a sv).
+
+ \sa setTransform(), transform()
+*/
+void QGraphicsItem::shear(qreal sh, qreal sv)
+{
+ setTransform(QTransform().shear(sh, sv), true);
+}
+
+/*!
+ \obsolete
+
+ Use setPos() or setTransformOriginPoint() instead. For identical
+ behavior, use
+
+ \code
+ setTransform(QTransform::fromTranslate(dx, dy), true);
+ \endcode
+
+ Translates the current item transformation by (\a dx, \a dy).
+
+ If all you want is to move an item, you should call moveBy() or
+ setPos() instead; this function changes the item's translation,
+ which is conceptually separate from its position.
+
+ \sa setTransform(), transform()
+*/
+void QGraphicsItem::translate(qreal dx, qreal dy)
+{
+ setTransform(QTransform::fromTranslate(dx, dy), true);
+}
+
+/*!
+ This virtual function is called twice for all items by the
+ QGraphicsScene::advance() slot. In the first phase, all items are called
+ with \a phase == 0, indicating that items on the scene are about to
+ advance, and then all items are called with \a phase == 1. Reimplement
+ this function to update your item if you need simple scene-controlled
+ animation.
+
+ The default implementation does nothing.
+
+ For individual item animation, an alternative to this function is to
+ either use QGraphicsItemAnimation, or to multiple-inherit from QObject and
+ QGraphicsItem, and animate your item using QObject::startTimer() and
+ QObject::timerEvent().
+
+ \sa QGraphicsItemAnimation, QTimeLine
+*/
+void QGraphicsItem::advance(int phase)
+{
+ Q_UNUSED(phase);
+}
+
+/*!
+ Returns the Z-value of the item. The Z-value affects the stacking order of
+ sibling (neighboring) items.
+
+ The default Z-value is 0.
+
+ \sa setZValue(), {QGraphicsItem#Sorting}{Sorting}, stackBefore(), ItemStacksBehindParent
+*/
+qreal QGraphicsItem::zValue() const
+{
+ return d_ptr->z;
+}
+
+/*!
+ Sets the Z-value of the item to \a z. The Z value decides the stacking
+ order of sibling (neighboring) items. A sibling item of high Z value will
+ always be drawn on top of another sibling item with a lower Z value.
+
+ If you restore the Z value, the item's insertion order will decide its
+ stacking order.
+
+ The Z-value does not affect the item's size in any way.
+
+ The default Z-value is 0.
+
+ \sa zValue(), {QGraphicsItem#Sorting}{Sorting}, stackBefore(), ItemStacksBehindParent
+*/
+void QGraphicsItem::setZValue(qreal z)
+{
+ const QVariant newZVariant(itemChange(ItemZValueChange, z));
+ qreal newZ = newZVariant.toReal();
+ if (newZ == d_ptr->z)
+ return;
+
+ if (d_ptr->scene && d_ptr->scene->d_func()->indexMethod != QGraphicsScene::NoIndex) {
+ // Z Value has changed, we have to notify the index.
+ d_ptr->scene->d_func()->index->itemChange(this, ItemZValueChange, &newZ);
+ }
+
+ d_ptr->z = newZ;
+ if (d_ptr->parent)
+ d_ptr->parent->d_ptr->needSortChildren = 1;
+ else if (d_ptr->scene)
+ d_ptr->scene->d_func()->needSortTopLevelItems = 1;
+
+ if (d_ptr->scene)
+ d_ptr->scene->d_func()->markDirty(this, QRectF(), /*invalidateChildren=*/true);
+
+ itemChange(ItemZValueHasChanged, newZVariant);
+
+ if (d_ptr->flags & ItemNegativeZStacksBehindParent)
+ setFlag(QGraphicsItem::ItemStacksBehindParent, z < qreal(0.0));
+
+ if (d_ptr->isObject)
+ emit static_cast<QGraphicsObject *>(this)->zChanged();
+}
+
+/*!
+ \internal
+
+ Ensures that the list of children is sorted by insertion order, and that
+ the siblingIndexes are packed (no gaps), and start at 0.
+
+ ### This function is almost identical to
+ QGraphicsScenePrivate::ensureSequentialTopLevelSiblingIndexes().
+*/
+void QGraphicsItemPrivate::ensureSequentialSiblingIndex()
+{
+ if (!sequentialOrdering) {
+ qSort(children.begin(), children.end(), insertionOrder);
+ sequentialOrdering = 1;
+ needSortChildren = 1;
+ }
+ if (holesInSiblingIndex) {
+ holesInSiblingIndex = 0;
+ for (int i = 0; i < children.size(); ++i)
+ children[i]->d_ptr->siblingIndex = i;
+ }
+}
+
+/*!
+ \internal
+*/
+inline void QGraphicsItemPrivate::sendScenePosChange()
+{
+ Q_Q(QGraphicsItem);
+ if (scene) {
+ if (flags & QGraphicsItem::ItemSendsScenePositionChanges)
+ q->itemChange(QGraphicsItem::ItemScenePositionHasChanged, q->scenePos());
+ if (scenePosDescendants) {
+ foreach (QGraphicsItem *item, scene->d_func()->scenePosItems) {
+ if (q->isAncestorOf(item))
+ item->itemChange(QGraphicsItem::ItemScenePositionHasChanged, item->scenePos());
+ }
+ }
+ }
+}
+
+/*!
+ \since 4.6
+
+ Stacks this item before \a sibling, which must be a sibling item (i.e., the
+ two items must share the same parent item, or must both be toplevel items).
+ The \a sibling must have the same Z value as this item, otherwise calling
+ this function will have no effect.
+
+ By default, all sibling items are stacked by insertion order (i.e., the
+ first item you add is drawn before the next item you add). If two items' Z
+ values are different, then the item with the highest Z value is drawn on
+ top. When the Z values are the same, the insertion order will decide the
+ stacking order.
+
+ \sa setZValue(), ItemStacksBehindParent, {QGraphicsItem#Sorting}{Sorting}
+*/
+void QGraphicsItem::stackBefore(const QGraphicsItem *sibling)
+{
+ if (sibling == this)
+ return;
+ if (!sibling || d_ptr->parent != sibling->parentItem()) {
+ qWarning("QGraphicsItem::stackUnder: cannot stack under %p, which must be a sibling", sibling);
+ return;
+ }
+ QList<QGraphicsItem *> *siblings = d_ptr->parent
+ ? &d_ptr->parent->d_ptr->children
+ : (d_ptr->scene ? &d_ptr->scene->d_func()->topLevelItems : 0);
+ if (!siblings) {
+ qWarning("QGraphicsItem::stackUnder: cannot stack under %p, which must be a sibling", sibling);
+ return;
+ }
+
+ // First, make sure that the sibling indexes have no holes. This also
+ // marks the children list for sorting.
+ if (d_ptr->parent)
+ d_ptr->parent->d_ptr->ensureSequentialSiblingIndex();
+ else
+ d_ptr->scene->d_func()->ensureSequentialTopLevelSiblingIndexes();
+
+ // Only move items with the same Z value, and that need moving.
+ int siblingIndex = sibling->d_ptr->siblingIndex;
+ int myIndex = d_ptr->siblingIndex;
+ if (myIndex >= siblingIndex) {
+ siblings->move(myIndex, siblingIndex);
+ // Fixup the insertion ordering.
+ for (int i = 0; i < siblings->size(); ++i) {
+ int &index = siblings->at(i)->d_ptr->siblingIndex;
+ if (i != siblingIndex && index >= siblingIndex && index <= myIndex)
+ ++index;
+ }
+ d_ptr->siblingIndex = siblingIndex;
+ for (int i = 0; i < siblings->size(); ++i) {
+ int &index = siblings->at(i)->d_ptr->siblingIndex;
+ if (i != siblingIndex && index >= siblingIndex && index <= myIndex)
+ siblings->at(i)->d_ptr->siblingOrderChange();
+ }
+ d_ptr->siblingOrderChange();
+ }
+}
+
+/*!
+ Returns the bounding rect of this item's descendants (i.e., its
+ children, their children, etc.) in local coordinates. The
+ rectangle will contain all descendants after they have been mapped
+ to local coordinates. If the item has no children, this function
+ returns an empty QRectF.
+
+ This does not include this item's own bounding rect; it only returns
+ its descendants' accumulated bounding rect. If you need to include this
+ item's bounding rect, you can add boundingRect() to childrenBoundingRect()
+ using QRectF::operator|().
+
+ This function is linear in complexity; it determines the size of the
+ returned bounding rect by iterating through all descendants.
+
+ \sa boundingRect(), sceneBoundingRect()
+*/
+QRectF QGraphicsItem::childrenBoundingRect() const
+{
+ if (!d_ptr->dirtyChildrenBoundingRect)
+ return d_ptr->childrenBoundingRect;
+
+ d_ptr->childrenBoundingRect = QRectF();
+ d_ptr->childrenBoundingRectHelper(0, &d_ptr->childrenBoundingRect, 0);
+ d_ptr->dirtyChildrenBoundingRect = 0;
+ return d_ptr->childrenBoundingRect;
+}
+
+/*!
+ \fn virtual QRectF QGraphicsItem::boundingRect() const = 0
+
+ This pure virtual function defines the outer bounds of the item as
+ a rectangle; all painting must be restricted to inside an item's
+ bounding rect. QGraphicsView uses this to determine whether the
+ item requires redrawing.
+
+ Although the item's shape can be arbitrary, the bounding rect is
+ always rectangular, and it is unaffected by the items'
+ transformation.
+
+ If you want to change the item's bounding rectangle, you must first call
+ prepareGeometryChange(). This notifies the scene of the imminent change,
+ so that its can update its item geometry index; otherwise, the scene will
+ be unaware of the item's new geometry, and the results are undefined
+ (typically, rendering artifacts are left around in the view).
+
+ Reimplement this function to let QGraphicsView determine what
+ parts of the widget, if any, need to be redrawn.
+
+ Note: For shapes that paint an outline / stroke, it is important
+ to include half the pen width in the bounding rect. It is not
+ necessary to compensate for antialiasing, though.
+
+ Example:
+
+ \snippet doc/src/snippets/code/src_gui_graphicsview_qgraphicsitem.cpp 8
+
+ \sa boundingRegion(), shape(), contains(), {The Graphics View Coordinate
+ System}, prepareGeometryChange()
+*/
+
+/*!
+ Returns the bounding rect of this item in scene coordinates, by combining
+ sceneTransform() with boundingRect().
+
+ \sa boundingRect(), {The Graphics View Coordinate System}
+*/
+QRectF QGraphicsItem::sceneBoundingRect() const
+{
+ // Find translate-only offset
+ // COMBINE
+ QPointF offset;
+ const QGraphicsItem *parentItem = this;
+ const QGraphicsItemPrivate *itemd;
+ do {
+ itemd = parentItem->d_ptr.data();
+ if (itemd->transformData)
+ break;
+ offset += itemd->pos;
+ } while ((parentItem = itemd->parent));
+
+ QRectF br = boundingRect();
+ br.translate(offset);
+ if (!parentItem)
+ return br;
+ if (parentItem->d_ptr->hasTranslateOnlySceneTransform()) {
+ br.translate(parentItem->d_ptr->sceneTransform.dx(), parentItem->d_ptr->sceneTransform.dy());
+ return br;
+ }
+ return parentItem->d_ptr->sceneTransform.mapRect(br);
+}
+
+/*!
+ Returns the shape of this item as a QPainterPath in local
+ coordinates. The shape is used for many things, including collision
+ detection, hit tests, and for the QGraphicsScene::items() functions.
+
+ The default implementation calls boundingRect() to return a simple
+ rectangular shape, but subclasses can reimplement this function to return
+ a more accurate shape for non-rectangular items. For example, a round item
+ may choose to return an elliptic shape for better collision detection. For
+ example:
+
+ \snippet doc/src/snippets/code/src_gui_graphicsview_qgraphicsitem.cpp 9
+
+ The outline of a shape can vary depending on the width and style of the
+ pen used when drawing. If you want to include this outline in the item's
+ shape, you can create a shape from the stroke using QPainterPathStroker.
+
+ This function is called by the default implementations of contains() and
+ collidesWithPath().
+
+ \sa boundingRect(), contains(), prepareGeometryChange(), QPainterPathStroker
+*/
+QPainterPath QGraphicsItem::shape() const
+{
+ QPainterPath path;
+ path.addRect(boundingRect());
+ return path;
+}
+
+/*!
+ Returns true if this item is clipped. An item is clipped if it has either
+ set the \l ItemClipsToShape flag, or if it or any of its ancestors has set
+ the \l ItemClipsChildrenToShape flag.
+
+ Clipping affects the item's appearance (i.e., painting), as well as mouse
+ and hover event delivery.
+
+ \sa clipPath(), shape(), setFlags()
+*/
+bool QGraphicsItem::isClipped() const
+{
+ Q_D(const QGraphicsItem);
+ return (d->ancestorFlags & QGraphicsItemPrivate::AncestorClipsChildren)
+ || (d->flags & QGraphicsItem::ItemClipsToShape);
+}
+
+/*!
+ \since 4.5
+
+ Returns this item's clip path, or an empty QPainterPath if this item is
+ not clipped. The clip path constrains the item's appearance and
+ interaction (i.e., restricts the area the item can draw, and it also
+ restricts the area that the item receives events).
+
+ You can enable clipping by setting the ItemClipsToShape or
+ ItemClipsChildrenToShape flags. The item's clip path is calculated by
+ intersecting all clipping ancestors' shapes. If the item sets
+ ItemClipsToShape, the final clip is intersected with the item's own shape.
+
+ \note Clipping introduces a performance penalty for all items involved;
+ you should generally avoid using clipping if you can (e.g., if your items
+ always draw inside boundingRect() or shape() boundaries, clipping is not
+ necessary).
+
+ \sa isClipped(), shape(), setFlags()
+*/
+QPainterPath QGraphicsItem::clipPath() const
+{
+ Q_D(const QGraphicsItem);
+ if (!isClipped())
+ return QPainterPath();
+
+ const QRectF thisBoundingRect(boundingRect());
+ if (thisBoundingRect.isEmpty())
+ return QPainterPath();
+
+ QPainterPath clip;
+ // Start with the item's bounding rect.
+ clip.addRect(thisBoundingRect);
+
+ if (d->ancestorFlags & QGraphicsItemPrivate::AncestorClipsChildren) {
+ const QGraphicsItem *parent = this;
+ const QGraphicsItem *lastParent = this;
+
+ // Intersect any in-between clips starting at the top and moving downwards.
+ while ((parent = parent->d_ptr->parent)) {
+ if (parent->d_ptr->flags & ItemClipsChildrenToShape) {
+ // Map clip to the current parent and intersect with its shape/clipPath
+ clip = lastParent->itemTransform(parent).map(clip);
+ clip = clip.intersected(parent->shape());
+ if (clip.isEmpty())
+ return clip;
+ lastParent = parent;
+ }
+
+ if (!(parent->d_ptr->ancestorFlags & QGraphicsItemPrivate::AncestorClipsChildren))
+ break;
+ }
+
+ if (lastParent != this) {
+ // Map clip back to the item's transform.
+ // ### what if itemtransform fails
+ clip = lastParent->itemTransform(this).map(clip);
+ }
+ }
+
+ if (d->flags & ItemClipsToShape)
+ clip = clip.intersected(shape());
+
+ return clip;
+}
+
+/*!
+ Returns true if this item contains \a point, which is in local
+ coordinates; otherwise, false is returned. It is most often called from
+ QGraphicsView to determine what item is under the cursor, and for that
+ reason, the implementation of this function should be as light-weight as
+ possible.
+
+ By default, this function calls shape(), but you can reimplement it in a
+ subclass to provide a (perhaps more efficient) implementation.
+
+ \sa shape(), boundingRect(), collidesWithPath()
+*/
+bool QGraphicsItem::contains(const QPointF &point) const
+{
+ return isClipped() ? clipPath().contains(point) : shape().contains(point);
+}
+
+/*!
+
+ Returns true if this item collides with \a other; otherwise
+ returns false.
+
+ The \a mode is applied to \a other, and the resulting shape or
+ bounding rectangle is then compared to this item's shape. The
+ default value for \a mode is Qt::IntersectsItemShape; \a other
+ collides with this item if it either intersects, contains, or is
+ contained by this item's shape (see Qt::ItemSelectionMode for
+ details).
+
+ The default implementation is based on shape intersection, and it calls
+ shape() on both items. Because the complexity of arbitrary shape-shape
+ intersection grows with an order of magnitude when the shapes are complex,
+ this operation can be noticably time consuming. You have the option of
+ reimplementing this function in a subclass of QGraphicsItem to provide a
+ custom algorithm. This allows you to make use of natural constraints in
+ the shapes of your own items, in order to improve the performance of the
+ collision detection. For instance, two untransformed perfectly circular
+ items' collision can be determined very efficiently by comparing their
+ positions and radii.
+
+ Keep in mind that when reimplementing this function and calling shape() or
+ boundingRect() on \a other, the returned coordinates must be mapped to
+ this item's coordinate system before any intersection can take place.
+
+ \sa contains(), shape()
+*/
+bool QGraphicsItem::collidesWithItem(const QGraphicsItem *other, Qt::ItemSelectionMode mode) const
+{
+ if (other == this)
+ return true;
+ if (!other)
+ return false;
+ // The items share the same clip if their closest clipper is the same, or
+ // if one clips the other.
+ bool clips = (d_ptr->ancestorFlags & QGraphicsItemPrivate::AncestorClipsChildren);
+ bool otherClips = (other->d_ptr->ancestorFlags & QGraphicsItemPrivate::AncestorClipsChildren);
+ if (clips || otherClips) {
+ const QGraphicsItem *closestClipper = isAncestorOf(other) ? this : parentItem();
+ while (closestClipper && !(closestClipper->flags() & ItemClipsChildrenToShape))
+ closestClipper = closestClipper->parentItem();
+ const QGraphicsItem *otherClosestClipper = other->isAncestorOf(this) ? other : other->parentItem();
+ while (otherClosestClipper && !(otherClosestClipper->flags() & ItemClipsChildrenToShape))
+ otherClosestClipper = otherClosestClipper->parentItem();
+ if (closestClipper == otherClosestClipper) {
+ d_ptr->localCollisionHack = 1;
+ bool res = collidesWithPath(mapFromItem(other, other->shape()), mode);
+ d_ptr->localCollisionHack = 0;
+ return res;
+ }
+ }
+
+ QPainterPath otherShape = other->isClipped() ? other->clipPath() : other->shape();
+ return collidesWithPath(mapFromItem(other, otherShape), mode);
+}
+
+/*!
+ Returns true if this item collides with \a path.
+
+ The collision is determined by \a mode. The default value for \a mode is
+ Qt::IntersectsItemShape; \a path collides with this item if it either
+ intersects, contains, or is contained by this item's shape.
+
+ Note that this function checks whether the item's shape or
+ bounding rectangle (depending on \a mode) is contained within \a
+ path, and not whether \a path is contained within the items shape
+ or bounding rectangle.
+
+ \sa collidesWithItem(), contains(), shape()
+*/
+bool QGraphicsItem::collidesWithPath(const QPainterPath &path, Qt::ItemSelectionMode mode) const
+{
+ if (path.isEmpty()) {
+ // No collision with empty paths.
+ return false;
+ }
+
+ QRectF rectA(boundingRect());
+ _q_adjustRect(&rectA);
+ QRectF rectB(path.controlPointRect());
+ _q_adjustRect(&rectB);
+ if (!rectA.intersects(rectB)) {
+ // This we can determine efficiently. If the two rects neither
+ // intersect nor contain eachother, then the two items do not collide.
+ return false;
+ }
+
+ // For further testing, we need this item's shape or bounding rect.
+ QPainterPath thisShape;
+ if (mode == Qt::IntersectsItemShape || mode == Qt::ContainsItemShape)
+ thisShape = (isClipped() && !d_ptr->localCollisionHack) ? clipPath() : shape();
+ else
+ thisShape.addRect(rectA);
+
+ if (thisShape == QPainterPath()) {
+ // Empty shape? No collision.
+ return false;
+ }
+
+ // Use QPainterPath boolean operations to determine the collision, O(N*logN).
+ if (mode == Qt::IntersectsItemShape || mode == Qt::IntersectsItemBoundingRect)
+ return path.intersects(thisShape);
+ return path.contains(thisShape);
+}
+
+/*!
+ Returns a list of all items that collide with this item.
+
+ The way collisions are detected is determined by applying \a mode
+ to items that are compared to this item, i.e., each item's shape
+ or bounding rectangle is checked against this item's shape. The
+ default value for \a mode is Qt::IntersectsItemShape.
+
+ \sa collidesWithItem()
+*/
+QList<QGraphicsItem *> QGraphicsItem::collidingItems(Qt::ItemSelectionMode mode) const
+{
+ if (d_ptr->scene)
+ return d_ptr->scene->collidingItems(this, mode);
+ return QList<QGraphicsItem *>();
+}
+
+/*!
+ Returns true if this item's bounding rect is completely obscured by the
+ opaque shape of any of colliding items above it (i.e., with a higher Z
+ value than this item).
+
+ Its implementation is based on calling isObscuredBy(), which you can
+ reimplement to provide a custom obscurity algorithm.
+
+ \sa opaqueArea()
+*/
+bool QGraphicsItem::isObscured() const
+{
+ return isObscured(QRectF());
+}
+
+/*!
+ \internal
+
+ Item obscurity helper function.
+
+ Returns true if the subrect \a rect of \a item's bounding rect is obscured
+ by \a other (i.e., \a other's opaque area covers \a item's \a rect
+ completely. \a other is assumed to already be "on top of" \a item
+ wrt. stacking order.
+*/
+static bool qt_QGraphicsItem_isObscured(const QGraphicsItem *item,
+ const QGraphicsItem *other,
+ const QRectF &rect)
+{
+ return other->mapToItem(item, other->opaqueArea()).contains(rect);
+}
+
+/*!
+ \overload
+ \since 4.3
+
+ Returns true if \a rect is completely obscured by the opaque shape of any
+ of colliding items above it (i.e., with a higher Z value than this item).
+
+ Unlike the default isObscured() function, this function does not call
+ isObscuredBy().
+
+ \sa opaqueArea()
+*/
+bool QGraphicsItem::isObscured(const QRectF &rect) const
+{
+ Q_D(const QGraphicsItem);
+ if (!d->scene)
+ return false;
+
+ QRectF br = boundingRect();
+ QRectF testRect = rect.isNull() ? br : rect;
+
+ foreach (QGraphicsItem *item, d->scene->items(mapToScene(br), Qt::IntersectsItemBoundingRect)) {
+ if (item == this)
+ break;
+ if (qt_QGraphicsItem_isObscured(this, item, testRect))
+ return true;
+ }
+ return false;
+}
+
+/*!
+ \fn bool QGraphicsItem::isObscured(qreal x, qreal y, qreal w, qreal h) const
+ \since 4.3
+
+ This convenience function is equivalent to calling isObscured(QRectF(\a x, \a y, \a w, \a h)).
+*/
+
+/*!
+ Returns true if this item's bounding rect is completely obscured by the
+ opaque shape of \a item.
+
+ The base implementation maps \a item's opaqueArea() to this item's
+ coordinate system, and then checks if this item's boundingRect() is fully
+ contained within the mapped shape.
+
+ You can reimplement this function to provide a custom algorithm for
+ determining whether this item is obscured by \a item.
+
+ \sa opaqueArea(), isObscured()
+*/
+bool QGraphicsItem::isObscuredBy(const QGraphicsItem *item) const
+{
+ if (!item)
+ return false;
+ return qt_closestItemFirst(item, this)
+ && qt_QGraphicsItem_isObscured(this, item, boundingRect());
+}
+
+/*!
+ This virtual function returns a shape representing the area where this
+ item is opaque. An area is opaque if it is filled using an opaque brush or
+ color (i.e., not transparent).
+
+ This function is used by isObscuredBy(), which is called by underlying
+ items to determine if they are obscured by this item.
+
+ The default implementation returns an empty QPainterPath, indicating that
+ this item is completely transparent and does not obscure any other items.
+
+ \sa isObscuredBy(), isObscured(), shape()
+*/
+QPainterPath QGraphicsItem::opaqueArea() const
+{
+ return QPainterPath();
+}
+
+/*!
+ \since 4.4
+
+ Returns the bounding region for this item. The coordinate space of the
+ returned region depends on \a itemToDeviceTransform. If you pass an
+ identity QTransform as a parameter, this function will return a local
+ coordinate region.
+
+ The bounding region describes a coarse outline of the item's visual
+ contents. Although it's expensive to calculate, it's also more precise
+ than boundingRect(), and it can help to avoid unnecessary repainting when
+ an item is updated. This is particularly efficient for thin items (e.g.,
+ lines or simple polygons). You can tune the granularity for the bounding
+ region by calling setBoundingRegionGranularity(). The default granularity
+ is 0; in which the item's bounding region is the same as its bounding
+ rect.
+
+ \a itemToDeviceTransform is the transformation from item coordinates to
+ device coordinates. If you want this function to return a QRegion in scene
+ coordinates, you can pass sceneTransform() as an argument.
+
+ \sa boundingRegionGranularity()
+*/
+QRegion QGraphicsItem::boundingRegion(const QTransform &itemToDeviceTransform) const
+{
+ // ### Ideally we would have a better way to generate this region,
+ // preferably something in the lines of QPainterPath::toRegion(QTransform)
+ // coupled with a way to generate a painter path from a set of painter
+ // operations (e.g., QPicture::toPainterPath() or so). The current
+ // approach generates a bitmap with the size of the item's bounding rect
+ // in device coordinates, scaled by b.r.granularity, then paints the item
+ // into the bitmap, converts the result to a QRegion and scales the region
+ // back to device space with inverse granularity.
+ qreal granularity = boundingRegionGranularity();
+ QRect deviceRect = itemToDeviceTransform.mapRect(boundingRect()).toRect();
+ _q_adjustRect(&deviceRect);
+ if (granularity == 0.0)
+ return QRegion(deviceRect);
+
+ int pad = 1;
+ QSize bitmapSize(qMax(1, int(deviceRect.width() * granularity) + pad * 2),
+ qMax(1, int(deviceRect.height() * granularity) + pad * 2));
+ QImage mask(bitmapSize, QImage::Format_ARGB32_Premultiplied);
+ mask.fill(0);
+ QPainter p(&mask);
+ p.setRenderHints(QPainter::Antialiasing);
+
+ // Transform painter (### this code is from QGraphicsScene::drawItemHelper
+ // and doesn't work properly with perspective transformations).
+ QPointF viewOrigo = itemToDeviceTransform.map(QPointF(0, 0));
+ QPointF offset = viewOrigo - deviceRect.topLeft();
+ p.scale(granularity, granularity);
+ p.translate(offset);
+ p.translate(pad, pad);
+ p.setWorldTransform(itemToDeviceTransform, true);
+ p.translate(itemToDeviceTransform.inverted().map(QPointF(0, 0)));
+
+ // Render
+ QStyleOptionGraphicsItem option;
+ const_cast<QGraphicsItem *>(this)->paint(&p, &option, 0);
+ p.end();
+
+ // Transform QRegion back to device space
+ QTransform unscale = QTransform::fromScale(1 / granularity, 1 / granularity);
+ QRegion r;
+ QBitmap colorMask = QBitmap::fromImage(mask.createMaskFromColor(0));
+ foreach (const QRect &rect, QRegion( colorMask ).rects()) {
+ QRect xrect = unscale.mapRect(rect).translated(deviceRect.topLeft() - QPoint(pad, pad));
+ r += xrect.adjusted(-1, -1, 1, 1) & deviceRect;
+ }
+ return r;
+}
+
+/*!
+ \since 4.4
+
+ Returns the item's bounding region granularity; a value between and
+ including 0 and 1. The default value is 0 (i.e., the lowest granularity,
+ where the bounding region corresponds to the item's bounding rectangle).
+
+\omit
+### NOTE
+\endomit
+
+ \sa setBoundingRegionGranularity()
+*/
+qreal QGraphicsItem::boundingRegionGranularity() const
+{
+ return d_ptr->hasBoundingRegionGranularity
+ ? qvariant_cast<qreal>(d_ptr->extra(QGraphicsItemPrivate::ExtraBoundingRegionGranularity))
+ : 0;
+}
+
+/*!
+ \since 4.4
+ Sets the bounding region granularity to \a granularity; a value between
+ and including 0 and 1. The default value is 0 (i.e., the lowest
+ granularity, where the bounding region corresponds to the item's bounding
+ rectangle).
+
+ The granularity is used by boundingRegion() to calculate how fine the
+ bounding region of the item should be. The highest achievable granularity
+ is 1, where boundingRegion() will return the finest outline possible for
+ the respective device (e.g., for a QGraphicsView viewport, this gives you
+ a pixel-perfect bounding region). The lowest possible granularity is
+ 0. The value of \a granularity describes the ratio between device
+ resolution and the resolution of the bounding region (e.g., a value of
+ 0.25 will provide a region where each chunk corresponds to 4x4 device
+ units / pixels).
+
+ \sa boundingRegionGranularity()
+*/
+void QGraphicsItem::setBoundingRegionGranularity(qreal granularity)
+{
+ if (granularity < 0.0 || granularity > 1.0) {
+ qWarning("QGraphicsItem::setBoundingRegionGranularity: invalid granularity %g", granularity);
+ return;
+ }
+ if (granularity == 0.0) {
+ d_ptr->unsetExtra(QGraphicsItemPrivate::ExtraBoundingRegionGranularity);
+ d_ptr->hasBoundingRegionGranularity = 0;
+ return;
+ }
+ d_ptr->hasBoundingRegionGranularity = 1;
+ d_ptr->setExtra(QGraphicsItemPrivate::ExtraBoundingRegionGranularity,
+ QVariant::fromValue<qreal>(granularity));
+}
+
+/*!
+ \fn virtual void QGraphicsItem::paint(QPainter *painter, const
+ QStyleOptionGraphicsItem *option, QWidget *widget = 0) = 0
+
+ This function, which is usually called by QGraphicsView, paints the
+ contents of an item in local coordinates.
+
+ Reimplement this function in a QGraphicsItem subclass to provide the
+ item's painting implementation, using \a painter. The \a option parameter
+ provides style options for the item, such as its state, exposed area and
+ its level-of-detail hints. The \a widget argument is optional. If
+ provided, it points to the widget that is being painted on; otherwise, it
+ is 0. For cached painting, \a widget is always 0.
+
+ \snippet doc/src/snippets/code/src_gui_graphicsview_qgraphicsitem.cpp 10
+
+ The painter's pen is 0-width by default, and its pen is initialized to the
+ QPalette::Text brush from the paint device's palette. The brush is
+ initialized to QPalette::Window.
+
+ Make sure to constrain all painting inside the boundaries of
+ boundingRect() to avoid rendering artifacts (as QGraphicsView does not
+ clip the painter for you). In particular, when QPainter renders the
+ outline of a shape using an assigned QPen, half of the outline will be
+ drawn outside, and half inside, the shape you're rendering (e.g., with a
+ pen width of 2 units, you must draw outlines 1 unit inside
+ boundingRect()). QGraphicsItem does not support use of cosmetic pens with
+ a non-zero width.
+
+ All painting is done in local coordinates.
+
+ \sa setCacheMode(), QPen::width(), {Item Coordinates}, ItemUsesExtendedStyleOption
+*/
+
+/*!
+ \internal
+ Returns true if we can discard an update request; otherwise false.
+*/
+bool QGraphicsItemPrivate::discardUpdateRequest(bool ignoreVisibleBit, bool ignoreDirtyBit,
+ bool ignoreOpacity) const
+{
+ // No scene, or if the scene is updating everything, means we have nothing
+ // to do. The only exception is if the scene tracks the growing scene rect.
+ return !scene
+ || (!visible && !ignoreVisibleBit && !this->ignoreVisible)
+ || (!ignoreDirtyBit && fullUpdatePending)
+ || (!ignoreOpacity && !this->ignoreOpacity && childrenCombineOpacity() && isFullyTransparent());
+}
+
+/*!
+ \internal
+*/
+int QGraphicsItemPrivate::depth() const
+{
+ if (itemDepth == -1)
+ const_cast<QGraphicsItemPrivate *>(this)->resolveDepth();
+
+ return itemDepth;
+}
+
+/*!
+ \internal
+*/
+#ifndef QT_NO_GRAPHICSEFFECT
+void QGraphicsItemPrivate::invalidateParentGraphicsEffectsRecursively()
+{
+ QGraphicsItemPrivate *itemPrivate = this;
+ do {
+ if (itemPrivate->graphicsEffect) {
+ itemPrivate->notifyInvalidated = 1;
+
+ if (!itemPrivate->updateDueToGraphicsEffect)
+ static_cast<QGraphicsItemEffectSourcePrivate *>(itemPrivate->graphicsEffect->d_func()->source->d_func())->invalidateCache();
+ }
+ } while ((itemPrivate = itemPrivate->parent ? itemPrivate->parent->d_ptr.data() : 0));
+}
+
+void QGraphicsItemPrivate::invalidateChildGraphicsEffectsRecursively(QGraphicsItemPrivate::InvalidateReason reason)
+{
+ if (!mayHaveChildWithGraphicsEffect)
+ return;
+
+ for (int i = 0; i < children.size(); ++i) {
+ QGraphicsItemPrivate *childPrivate = children.at(i)->d_ptr.data();
+ if (reason == OpacityChanged && (childPrivate->flags & QGraphicsItem::ItemIgnoresParentOpacity))
+ continue;
+ if (childPrivate->graphicsEffect) {
+ childPrivate->notifyInvalidated = 1;
+ static_cast<QGraphicsItemEffectSourcePrivate *>(childPrivate->graphicsEffect->d_func()->source->d_func())->invalidateCache();
+ }
+
+ childPrivate->invalidateChildGraphicsEffectsRecursively(reason);
+ }
+}
+#endif //QT_NO_GRAPHICSEFFECT
+
+/*!
+ \internal
+*/
+void QGraphicsItemPrivate::invalidateDepthRecursively()
+{
+ if (itemDepth == -1)
+ return;
+
+ itemDepth = -1;
+ for (int i = 0; i < children.size(); ++i)
+ children.at(i)->d_ptr->invalidateDepthRecursively();
+}
+
+/*!
+ \internal
+
+ Resolves the stacking depth of this object and all its ancestors.
+*/
+void QGraphicsItemPrivate::resolveDepth()
+{
+ if (!parent)
+ itemDepth = 0;
+ else {
+ if (parent->d_ptr->itemDepth == -1)
+ parent->d_ptr->resolveDepth();
+ itemDepth = parent->d_ptr->itemDepth + 1;
+ }
+}
+
+/*!
+ \internal
+
+ ### This function is almost identical to
+ QGraphicsScenePrivate::registerTopLevelItem().
+*/
+void QGraphicsItemPrivate::addChild(QGraphicsItem *child)
+{
+ // Remove all holes from the sibling index list. Now the max index
+ // number is equal to the size of the children list.
+ ensureSequentialSiblingIndex();
+ needSortChildren = 1; // ### maybe 0
+ child->d_ptr->siblingIndex = children.size();
+ children.append(child);
+ if (isObject)
+ emit static_cast<QGraphicsObject *>(q_ptr)->childrenChanged();
+}
+
+/*!
+ \internal
+
+ ### This function is almost identical to
+ QGraphicsScenePrivate::unregisterTopLevelItem().
+*/
+void QGraphicsItemPrivate::removeChild(QGraphicsItem *child)
+{
+ // When removing elements in the middle of the children list,
+ // there will be a "gap" in the list of sibling indexes (0,1,3,4).
+ if (!holesInSiblingIndex)
+ holesInSiblingIndex = child->d_ptr->siblingIndex != children.size() - 1;
+ if (sequentialOrdering && !holesInSiblingIndex)
+ children.removeAt(child->d_ptr->siblingIndex);
+ else
+ children.removeOne(child);
+ // NB! Do not use children.removeAt(child->d_ptr->siblingIndex) because
+ // the child is not guaranteed to be at the index after the list is sorted.
+ // (see ensureSortedChildren()).
+ child->d_ptr->siblingIndex = -1;
+ if (isObject)
+ emit static_cast<QGraphicsObject *>(q_ptr)->childrenChanged();
+}
+
+/*!
+ \internal
+*/
+QGraphicsItemCache *QGraphicsItemPrivate::maybeExtraItemCache() const
+{
+ return (QGraphicsItemCache *)qvariant_cast<void *>(extra(ExtraCacheData));
+}
+
+/*!
+ \internal
+*/
+QGraphicsItemCache *QGraphicsItemPrivate::extraItemCache() const
+{
+ QGraphicsItemCache *c = (QGraphicsItemCache *)qvariant_cast<void *>(extra(ExtraCacheData));
+ if (!c) {
+ QGraphicsItemPrivate *that = const_cast<QGraphicsItemPrivate *>(this);
+ c = new QGraphicsItemCache;
+ that->setExtra(ExtraCacheData, QVariant::fromValue<void *>(c));
+ }
+ return c;
+}
+
+/*!
+ \internal
+*/
+void QGraphicsItemPrivate::removeExtraItemCache()
+{
+ QGraphicsItemCache *c = (QGraphicsItemCache *)qvariant_cast<void *>(extra(ExtraCacheData));
+ if (c) {
+ c->purge();
+ delete c;
+ }
+ unsetExtra(ExtraCacheData);
+}
+
+void QGraphicsItemPrivate::updatePaintedViewBoundingRects(bool updateChildren)
+{
+ if (!scene)
+ return;
+
+ for (int i = 0; i < scene->d_func()->views.size(); ++i) {
+ QGraphicsViewPrivate *viewPrivate = scene->d_func()->views.at(i)->d_func();
+ QRect rect = paintedViewBoundingRects.value(viewPrivate->viewport);
+ rect.translate(viewPrivate->dirtyScrollOffset);
+ viewPrivate->updateRect(rect);
+ }
+
+ if (updateChildren) {
+ for (int i = 0; i < children.size(); ++i)
+ children.at(i)->d_ptr->updatePaintedViewBoundingRects(true);
+ }
+}
+
+// Traverses all the ancestors up to the top-level and updates the pointer to
+// always point to the top-most item that has a dirty scene transform.
+// It then backtracks to the top-most dirty item and start calculating the
+// scene transform by combining the item's transform (+pos) with the parent's
+// cached scene transform (which we at this point know for sure is valid).
+void QGraphicsItemPrivate::ensureSceneTransformRecursive(QGraphicsItem **topMostDirtyItem)
+{
+ Q_ASSERT(topMostDirtyItem);
+
+ if (dirtySceneTransform)
+ *topMostDirtyItem = q_ptr;
+
+ if (parent)
+ parent->d_ptr->ensureSceneTransformRecursive(topMostDirtyItem);
+
+ if (*topMostDirtyItem == q_ptr) {
+ if (!dirtySceneTransform)
+ return; // OK, neither my ancestors nor I have dirty scene transforms.
+ *topMostDirtyItem = 0;
+ } else if (*topMostDirtyItem) {
+ return; // Continue backtrack.
+ }
+
+ // This item and all its descendants have dirty scene transforms.
+ // We're about to validate this item's scene transform, so we have to
+ // invalidate all the children; otherwise there's no way for the descendants
+ // to detect that the ancestor has changed.
+ invalidateChildrenSceneTransform();
+
+ // COMBINE my transform with the parent's scene transform.
+ updateSceneTransformFromParent();
+ Q_ASSERT(!dirtySceneTransform);
+}
+
+/*!
+ \internal
+*/
+void QGraphicsItemPrivate::setSubFocus(QGraphicsItem *rootItem, QGraphicsItem *stopItem)
+{
+ // Update focus child chain. Stop at panels, or if this item
+ // is hidden, stop at the first item with a visible parent.
+ QGraphicsItem *parent = rootItem ? rootItem : q_ptr;
+ if (parent->panel() != q_ptr->panel())
+ return;
+
+ do {
+ // Clear any existing ancestor's subFocusItem.
+ if (parent != q_ptr && parent->d_ptr->subFocusItem) {
+ if (parent->d_ptr->subFocusItem == q_ptr)
+ break;
+ parent->d_ptr->subFocusItem->d_ptr->clearSubFocus(0, stopItem);
+ }
+ parent->d_ptr->subFocusItem = q_ptr;
+ parent->d_ptr->subFocusItemChange();
+ } while (!parent->isPanel() && (parent = parent->d_ptr->parent) && (visible || !parent->d_ptr->visible));
+
+ if (scene && !scene->isActive()) {
+ scene->d_func()->passiveFocusItem = subFocusItem;
+ scene->d_func()->lastFocusItem = subFocusItem;
+ }
+}
+
+/*!
+ \internal
+*/
+void QGraphicsItemPrivate::clearSubFocus(QGraphicsItem *rootItem, QGraphicsItem *stopItem)
+{
+ // Reset sub focus chain.
+ QGraphicsItem *parent = rootItem ? rootItem : q_ptr;
+ do {
+ if (parent->d_ptr->subFocusItem != q_ptr)
+ break;
+ parent->d_ptr->subFocusItem = 0;
+ if (parent != stopItem && !parent->isAncestorOf(stopItem))
+ parent->d_ptr->subFocusItemChange();
+ } while (!parent->isPanel() && (parent = parent->d_ptr->parent));
+}
+
+/*!
+ \internal
+
+ Sets the focusProxy pointer to 0 for all items that have this item as their
+ focusProxy. ### Qt 5: Use QPointer instead.
+*/
+void QGraphicsItemPrivate::resetFocusProxy()
+{
+ for (int i = 0; i < focusProxyRefs.size(); ++i)
+ *focusProxyRefs.at(i) = 0;
+ focusProxyRefs.clear();
+}
+
+/*!
+ \internal
+
+ Subclasses can reimplement this function to be notified when subFocusItem
+ changes.
+*/
+void QGraphicsItemPrivate::subFocusItemChange()
+{
+}
+
+/*!
+ \internal
+
+ Subclasses can reimplement this function to be notified when an item
+ becomes a focusScopeItem (or is no longer a focusScopeItem).
+*/
+void QGraphicsItemPrivate::focusScopeItemChange(bool isSubFocusItem)
+{
+ Q_UNUSED(isSubFocusItem);
+}
+
+/*!
+ \internal
+
+ Subclasses can reimplement this function to be notified when its
+ siblingIndex order is changed.
+*/
+void QGraphicsItemPrivate::siblingOrderChange()
+{
+}
+
+/*!
+ \internal
+
+ Tells us if it is a proxy widget
+*/
+bool QGraphicsItemPrivate::isProxyWidget() const
+{
+ return false;
+}
+
+/*!
+ Schedules a redraw of the area covered by \a rect in this item. You can
+ call this function whenever your item needs to be redrawn, such as if it
+ changes appearance or size.
+
+ This function does not cause an immediate paint; instead it schedules a
+ paint request that is processed by QGraphicsView after control reaches the
+ event loop. The item will only be redrawn if it is visible in any
+ associated view.
+
+ As a side effect of the item being repainted, other items that overlap the
+ area \a rect may also be repainted.
+
+ If the item is invisible (i.e., isVisible() returns false), this function
+ does nothing.
+
+ \sa paint(), boundingRect()
+*/
+void QGraphicsItem::update(const QRectF &rect)
+{
+ if (rect.isEmpty() && !rect.isNull())
+ return;
+
+ // Make sure we notify effects about invalidated source.
+#ifndef QT_NO_GRAPHICSEFFECT
+ d_ptr->invalidateParentGraphicsEffectsRecursively();
+#endif //QT_NO_GRAPHICSEFFECT
+
+ if (CacheMode(d_ptr->cacheMode) != NoCache) {
+ // Invalidate cache.
+ QGraphicsItemCache *cache = d_ptr->extraItemCache();
+ if (!cache->allExposed) {
+ if (rect.isNull()) {
+ cache->allExposed = true;
+ cache->exposed.clear();
+ } else {
+ cache->exposed.append(rect);
+ }
+ }
+ // Only invalidate cache; item is already dirty.
+ if (d_ptr->fullUpdatePending)
+ return;
+ }
+
+ if (d_ptr->scene)
+ d_ptr->scene->d_func()->markDirty(this, rect);
+}
+
+/*!
+ \since 4.4
+ Scrolls the contents of \a rect by \a dx, \a dy. If \a rect is a null rect
+ (the default), the item's bounding rect is scrolled.
+
+ Scrolling provides a fast alternative to simply redrawing when the
+ contents of the item (or parts of the item) are shifted vertically or
+ horizontally. Depending on the current transformation and the capabilities
+ of the paint device (i.e., the viewport), this operation may consist of
+ simply moving pixels from one location to another using memmove(). In most
+ cases this is faster than rerendering the entire area.
+
+ After scrolling, the item will issue an update for the newly exposed
+ areas. If scrolling is not supported (e.g., you are rendering to an OpenGL
+ viewport, which does not benefit from scroll optimizations), this function
+ is equivalent to calling update(\a rect).
+
+ \bold{Note:} Scrolling is only supported when QGraphicsItem::ItemCoordinateCache
+ is enabled; in all other cases calling this function is equivalent to calling
+ update(\a rect). If you for sure know that the item is opaque and not overlapped
+ by other items, you can map the \a rect to viewport coordinates and scroll the
+ viewport.
+
+ \snippet doc/src/snippets/code/src_gui_graphicsview_qgraphicsitem.cpp 19
+
+ \sa boundingRect()
+*/
+void QGraphicsItem::scroll(qreal dx, qreal dy, const QRectF &rect)
+{
+ Q_D(QGraphicsItem);
+ if (dx == 0.0 && dy == 0.0)
+ return;
+ if (!d->scene)
+ return;
+
+ // Accelerated scrolling means moving pixels from one location to another
+ // and only redraw the newly exposed area. The following requirements must
+ // be fulfilled in order to do that:
+ //
+ // 1) Item is opaque.
+ // 2) Item is not overlapped by other items.
+ //
+ // There's (yet) no way to detect whether an item is opaque or not, which means
+ // we cannot do accelerated scrolling unless the cache is enabled. In case of using
+ // DeviceCoordinate cache we also have to take the device transform into account in
+ // order to determine whether we can do accelerated scrolling or not. That's left out
+ // for simplicity here, but it is definitely something we can consider in the future
+ // as a performance improvement.
+ if (d->cacheMode != QGraphicsItem::ItemCoordinateCache
+ || !qFuzzyIsNull(dx - int(dx)) || !qFuzzyIsNull(dy - int(dy))) {
+ update(rect);
+ return;
+ }
+
+ QGraphicsItemCache *cache = d->extraItemCache();
+ if (cache->allExposed || cache->fixedSize.isValid()) {
+ // Cache is either invalidated or item is scaled (see QGraphicsItem::setCacheMode).
+ update(rect);
+ return;
+ }
+
+ // Find pixmap in cache.
+ QPixmap cachedPixmap;
+ if (!QPixmapCache::find(cache->key, &cachedPixmap)) {
+ update(rect);
+ return;
+ }
+
+ QRect scrollRect = (rect.isNull() ? boundingRect() : rect).toAlignedRect();
+ if (!scrollRect.intersects(cache->boundingRect))
+ return; // Nothing to scroll.
+
+ // Remove from cache to avoid deep copy when modifying.
+ QPixmapCache::remove(cache->key);
+
+ QRegion exposed;
+ cachedPixmap.scroll(dx, dy, scrollRect.translated(-cache->boundingRect.topLeft()), &exposed);
+
+ // Reinsert into cache.
+ cache->key = QPixmapCache::insert(cachedPixmap);
+
+ // Translate the existing expose.
+ for (int i = 0; i < cache->exposed.size(); ++i) {
+ QRectF &e = cache->exposed[i];
+ if (!rect.isNull() && !e.intersects(rect))
+ continue;
+ e.translate(dx, dy);
+ }
+
+ // Append newly exposed areas. Note that the exposed region is currently
+ // in pixmap coordinates, so we have to translate it to item coordinates.
+ exposed.translate(cache->boundingRect.topLeft());
+ const QVector<QRect> exposedRects = exposed.rects();
+ for (int i = 0; i < exposedRects.size(); ++i)
+ cache->exposed += exposedRects.at(i);
+
+ // Trigger update. This will redraw the newly exposed area and make sure
+ // the pixmap is re-blitted in case there are overlapping items.
+ d->scene->d_func()->markDirty(this, rect);
+}
+
+/*!
+ \fn void QGraphicsItem::update(qreal x, qreal y, qreal width, qreal height)
+ \overload
+
+ This convenience function is equivalent to calling update(QRectF(\a x, \a
+ y, \a width, \a height)).
+*/
+
+/*!
+ Maps the point \a point, which is in this item's coordinate system, to \a
+ item's coordinate system, and returns the mapped coordinate.
+
+ If \a item is 0, this function returns the same as mapToScene().
+
+ \sa itemTransform(), mapToParent(), mapToScene(), transform(), mapFromItem(), {The Graphics
+ View Coordinate System}
+*/
+QPointF QGraphicsItem::mapToItem(const QGraphicsItem *item, const QPointF &point) const
+{
+ if (item)
+ return itemTransform(item).map(point);
+ return mapToScene(point);
+}
+
+/*!
+ \fn QPointF QGraphicsItem::mapToItem(const QGraphicsItem *item, qreal x, qreal y) const
+ \overload
+
+ This convenience function is equivalent to calling mapToItem(\a item,
+ QPointF(\a x, \a y)).
+*/
+
+/*!
+ Maps the point \a point, which is in this item's coordinate system, to its
+ parent's coordinate system, and returns the mapped coordinate. If the item
+ has no parent, \a point will be mapped to the scene's coordinate system.
+
+ \sa mapToItem(), mapToScene(), transform(), mapFromParent(), {The Graphics
+ View Coordinate System}
+*/
+QPointF QGraphicsItem::mapToParent(const QPointF &point) const
+{
+ // COMBINE
+ if (!d_ptr->transformData)
+ return point + d_ptr->pos;
+ return d_ptr->transformToParent().map(point);
+}
+
+/*!
+ \fn QPointF QGraphicsItem::mapToParent(qreal x, qreal y) const
+ \overload
+
+ This convenience function is equivalent to calling mapToParent(QPointF(\a
+ x, \a y)).
+*/
+
+/*!
+ Maps the point \a point, which is in this item's coordinate system, to the
+ scene's coordinate system, and returns the mapped coordinate.
+
+ \sa mapToItem(), mapToParent(), transform(), mapFromScene(), {The Graphics
+ View Coordinate System}
+*/
+QPointF QGraphicsItem::mapToScene(const QPointF &point) const
+{
+ if (d_ptr->hasTranslateOnlySceneTransform())
+ return QPointF(point.x() + d_ptr->sceneTransform.dx(), point.y() + d_ptr->sceneTransform.dy());
+ return d_ptr->sceneTransform.map(point);
+}
+
+/*!
+ \fn QPointF QGraphicsItem::mapToScene(qreal x, qreal y) const
+ \overload
+
+ This convenience function is equivalent to calling mapToScene(QPointF(\a
+ x, \a y)).
+*/
+
+/*!
+ Maps the rectangle \a rect, which is in this item's coordinate system, to
+ \a item's coordinate system, and returns the mapped rectangle as a polygon.
+
+ If \a item is 0, this function returns the same as mapToScene().
+
+ \sa itemTransform(), mapToParent(), mapToScene(), mapFromItem(), {The
+ Graphics View Coordinate System}
+*/
+QPolygonF QGraphicsItem::mapToItem(const QGraphicsItem *item, const QRectF &rect) const
+{
+ if (item)
+ return itemTransform(item).map(rect);
+ return mapToScene(rect);
+}
+
+/*!
+ \fn QPolygonF QGraphicsItem::mapToItem(const QGraphicsItem *item, qreal x, qreal y, qreal w, qreal h) const
+ \since 4.3
+
+ This convenience function is equivalent to calling mapToItem(item, QRectF(\a x, \a y, \a w, \a h)).
+*/
+
+/*!
+ Maps the rectangle \a rect, which is in this item's coordinate system, to
+ its parent's coordinate system, and returns the mapped rectangle as a
+ polygon. If the item has no parent, \a rect will be mapped to the scene's
+ coordinate system.
+
+ \sa mapToScene(), mapToItem(), mapFromParent(), {The Graphics View
+ Coordinate System}
+*/
+QPolygonF QGraphicsItem::mapToParent(const QRectF &rect) const
+{
+ // COMBINE
+ if (!d_ptr->transformData)
+ return rect.translated(d_ptr->pos);
+ return d_ptr->transformToParent().map(rect);
+}
+
+/*!
+ \fn QPolygonF QGraphicsItem::mapToParent(qreal x, qreal y, qreal w, qreal h) const
+ \since 4.3
+
+ This convenience function is equivalent to calling mapToParent(QRectF(\a x, \a y, \a w, \a h)).
+*/
+
+/*!
+ Maps the rectangle \a rect, which is in this item's coordinate system, to
+ the scene's coordinate system, and returns the mapped rectangle as a polygon.
+
+ \sa mapToParent(), mapToItem(), mapFromScene(), {The Graphics View
+ Coordinate System}
+*/
+QPolygonF QGraphicsItem::mapToScene(const QRectF &rect) const
+{
+ if (d_ptr->hasTranslateOnlySceneTransform())
+ return rect.translated(d_ptr->sceneTransform.dx(), d_ptr->sceneTransform.dy());
+ return d_ptr->sceneTransform.map(rect);
+}
+
+/*!
+ \fn QPolygonF QGraphicsItem::mapToScene(qreal x, qreal y, qreal w, qreal h) const
+ \since 4.3
+
+ This convenience function is equivalent to calling mapToScene(QRectF(\a x, \a y, \a w, \a h)).
+*/
+
+/*!
+ \since 4.5
+
+ Maps the rectangle \a rect, which is in this item's coordinate system, to
+ \a item's coordinate system, and returns the mapped rectangle as a new
+ rectangle (i.e., the bounding rectangle of the resulting polygon).
+
+ If \a item is 0, this function returns the same as mapRectToScene().
+
+ \sa itemTransform(), mapToParent(), mapToScene(), mapFromItem(), {The
+ Graphics View Coordinate System}
+*/
+QRectF QGraphicsItem::mapRectToItem(const QGraphicsItem *item, const QRectF &rect) const
+{
+ if (item)
+ return itemTransform(item).mapRect(rect);
+ return mapRectToScene(rect);
+}
+
+/*!
+ \fn QRectF QGraphicsItem::mapRectToItem(const QGraphicsItem *item, qreal x, qreal y, qreal w, qreal h) const
+ \since 4.5
+
+ This convenience function is equivalent to calling mapRectToItem(item, QRectF(\a x, \a y, \a w, \a h)).
+*/
+
+/*!
+ \since 4.5
+
+ Maps the rectangle \a rect, which is in this item's coordinate system, to
+ its parent's coordinate system, and returns the mapped rectangle as a new
+ rectangle (i.e., the bounding rectangle of the resulting polygon).
+
+ \sa itemTransform(), mapToParent(), mapToScene(), mapFromItem(), {The
+ Graphics View Coordinate System}
+*/
+QRectF QGraphicsItem::mapRectToParent(const QRectF &rect) const
+{
+ // COMBINE
+ if (!d_ptr->transformData)
+ return rect.translated(d_ptr->pos);
+ return d_ptr->transformToParent().mapRect(rect);
+}
+
+/*!
+ \fn QRectF QGraphicsItem::mapRectToParent(qreal x, qreal y, qreal w, qreal h) const
+ \since 4.5
+
+ This convenience function is equivalent to calling mapRectToParent(QRectF(\a x, \a y, \a w, \a h)).
+*/
+
+/*!
+ \since 4.5
+
+ Maps the rectangle \a rect, which is in this item's coordinate system, to
+ the scene coordinate system, and returns the mapped rectangle as a new
+ rectangle (i.e., the bounding rectangle of the resulting polygon).
+
+ \sa itemTransform(), mapToParent(), mapToScene(), mapFromItem(), {The
+ Graphics View Coordinate System}
+*/
+QRectF QGraphicsItem::mapRectToScene(const QRectF &rect) const
+{
+ if (d_ptr->hasTranslateOnlySceneTransform())
+ return rect.translated(d_ptr->sceneTransform.dx(), d_ptr->sceneTransform.dy());
+ return d_ptr->sceneTransform.mapRect(rect);
+}
+
+/*!
+ \fn QRectF QGraphicsItem::mapRectToScene(qreal x, qreal y, qreal w, qreal h) const
+ \since 4.5
+
+ This convenience function is equivalent to calling mapRectToScene(QRectF(\a x, \a y, \a w, \a h)).
+*/
+
+/*!
+ \since 4.5
+
+ Maps the rectangle \a rect, which is in \a item's coordinate system, to
+ this item's coordinate system, and returns the mapped rectangle as a new
+ rectangle (i.e., the bounding rectangle of the resulting polygon).
+
+ If \a item is 0, this function returns the same as mapRectFromScene().
+
+ \sa itemTransform(), mapToParent(), mapToScene(), mapFromItem(), {The
+ Graphics View Coordinate System}
+*/
+QRectF QGraphicsItem::mapRectFromItem(const QGraphicsItem *item, const QRectF &rect) const
+{
+ if (item)
+ return item->itemTransform(this).mapRect(rect);
+ return mapRectFromScene(rect);
+}
+
+/*!
+ \fn QRectF QGraphicsItem::mapRectFromItem(const QGraphicsItem *item, qreal x, qreal y, qreal w, qreal h) const
+ \since 4.5
+
+ This convenience function is equivalent to calling mapRectFromItem(item, QRectF(\a x, \a y, \a w, \a h)).
+*/
+
+/*!
+ \since 4.5
+
+ Maps the rectangle \a rect, which is in this item's parent's coordinate
+ system, to this item's coordinate system, and returns the mapped rectangle
+ as a new rectangle (i.e., the bounding rectangle of the resulting
+ polygon).
+
+ \sa itemTransform(), mapToParent(), mapToScene(), mapFromItem(), {The
+ Graphics View Coordinate System}
+*/
+QRectF QGraphicsItem::mapRectFromParent(const QRectF &rect) const
+{
+ // COMBINE
+ if (!d_ptr->transformData)
+ return rect.translated(-d_ptr->pos);
+ return d_ptr->transformToParent().inverted().mapRect(rect);
+}
+
+/*!
+ \fn QRectF QGraphicsItem::mapRectFromParent(qreal x, qreal y, qreal w, qreal h) const
+ \since 4.5
+
+ This convenience function is equivalent to calling mapRectFromParent(QRectF(\a x, \a y, \a w, \a h)).
+*/
+
+/*!
+ \since 4.5
+
+ Maps the rectangle \a rect, which is in scene coordinates, to this item's
+ coordinate system, and returns the mapped rectangle as a new rectangle
+ (i.e., the bounding rectangle of the resulting polygon).
+
+ \sa itemTransform(), mapToParent(), mapToScene(), mapFromItem(), {The
+ Graphics View Coordinate System}
+*/
+QRectF QGraphicsItem::mapRectFromScene(const QRectF &rect) const
+{
+ if (d_ptr->hasTranslateOnlySceneTransform())
+ return rect.translated(-d_ptr->sceneTransform.dx(), -d_ptr->sceneTransform.dy());
+ return d_ptr->sceneTransform.inverted().mapRect(rect);
+}
+
+/*!
+ \fn QRectF QGraphicsItem::mapRectFromScene(qreal x, qreal y, qreal w, qreal h) const
+ \since 4.5
+
+ This convenience function is equivalent to calling mapRectFromScene(QRectF(\a x, \a y, \a w, \a h)).
+*/
+
+/*!
+ Maps the polygon \a polygon, which is in this item's coordinate system, to
+ \a item's coordinate system, and returns the mapped polygon.
+
+ If \a item is 0, this function returns the same as mapToScene().
+
+ \sa itemTransform(), mapToParent(), mapToScene(), mapFromItem(), {The
+ Graphics View Coordinate System}
+*/
+QPolygonF QGraphicsItem::mapToItem(const QGraphicsItem *item, const QPolygonF &polygon) const
+{
+ if (item)
+ return itemTransform(item).map(polygon);
+ return mapToScene(polygon);
+}
+
+/*!
+ Maps the polygon \a polygon, which is in this item's coordinate system, to
+ its parent's coordinate system, and returns the mapped polygon. If the
+ item has no parent, \a polygon will be mapped to the scene's coordinate
+ system.
+
+ \sa mapToScene(), mapToItem(), mapFromParent(), {The Graphics View
+ Coordinate System}
+*/
+QPolygonF QGraphicsItem::mapToParent(const QPolygonF &polygon) const
+{
+ // COMBINE
+ if (!d_ptr->transformData)
+ return polygon.translated(d_ptr->pos);
+ return d_ptr->transformToParent().map(polygon);
+}
+
+/*!
+ Maps the polygon \a polygon, which is in this item's coordinate system, to
+ the scene's coordinate system, and returns the mapped polygon.
+
+ \sa mapToParent(), mapToItem(), mapFromScene(), {The Graphics View
+ Coordinate System}
+*/
+QPolygonF QGraphicsItem::mapToScene(const QPolygonF &polygon) const
+{
+ if (d_ptr->hasTranslateOnlySceneTransform())
+ return polygon.translated(d_ptr->sceneTransform.dx(), d_ptr->sceneTransform.dy());
+ return d_ptr->sceneTransform.map(polygon);
+}
+
+/*!
+ Maps the path \a path, which is in this item's coordinate system, to
+ \a item's coordinate system, and returns the mapped path.
+
+ If \a item is 0, this function returns the same as mapToScene().
+
+ \sa itemTransform(), mapToParent(), mapToScene(), mapFromItem(), {The
+ Graphics View Coordinate System}
+*/
+QPainterPath QGraphicsItem::mapToItem(const QGraphicsItem *item, const QPainterPath &path) const
+{
+ if (item)
+ return itemTransform(item).map(path);
+ return mapToScene(path);
+}
+
+/*!
+ Maps the path \a path, which is in this item's coordinate system, to
+ its parent's coordinate system, and returns the mapped path. If the
+ item has no parent, \a path will be mapped to the scene's coordinate
+ system.
+
+ \sa mapToScene(), mapToItem(), mapFromParent(), {The Graphics View
+ Coordinate System}
+*/
+QPainterPath QGraphicsItem::mapToParent(const QPainterPath &path) const
+{
+ // COMBINE
+ if (!d_ptr->transformData)
+ return path.translated(d_ptr->pos);
+ return d_ptr->transformToParent().map(path);
+}
+
+/*!
+ Maps the path \a path, which is in this item's coordinate system, to
+ the scene's coordinate system, and returns the mapped path.
+
+ \sa mapToParent(), mapToItem(), mapFromScene(), {The Graphics View
+ Coordinate System}
+*/
+QPainterPath QGraphicsItem::mapToScene(const QPainterPath &path) const
+{
+ if (d_ptr->hasTranslateOnlySceneTransform())
+ return path.translated(d_ptr->sceneTransform.dx(), d_ptr->sceneTransform.dy());
+ return d_ptr->sceneTransform.map(path);
+}
+
+/*!
+ Maps the point \a point, which is in \a item's coordinate system, to this
+ item's coordinate system, and returns the mapped coordinate.
+
+ If \a item is 0, this function returns the same as mapFromScene().
+
+ \sa itemTransform(), mapFromParent(), mapFromScene(), transform(), mapToItem(), {The Graphics
+ View Coordinate System}
+*/
+QPointF QGraphicsItem::mapFromItem(const QGraphicsItem *item, const QPointF &point) const
+{
+ if (item)
+ return item->itemTransform(this).map(point);
+ return mapFromScene(point);
+}
+
+/*!
+ \fn QPointF QGraphicsItem::mapFromItem(const QGraphicsItem *item, qreal x, qreal y) const
+ \overload
+
+ This convenience function is equivalent to calling mapFromItem(\a item,
+ QPointF(\a x, \a y)).
+*/
+
+/*!
+ Maps the point \a point, which is in this item's parent's coordinate
+ system, to this item's coordinate system, and returns the mapped
+ coordinate.
+
+ \sa mapFromItem(), mapFromScene(), transform(), mapToParent(), {The Graphics
+ View Coordinate System}
+*/
+QPointF QGraphicsItem::mapFromParent(const QPointF &point) const
+{
+ // COMBINE
+ if (d_ptr->transformData)
+ return d_ptr->transformToParent().inverted().map(point);
+ return point - d_ptr->pos;
+}
+
+/*!
+ \fn QPointF QGraphicsItem::mapFromParent(qreal x, qreal y) const
+ \overload
+
+ This convenience function is equivalent to calling
+ mapFromParent(QPointF(\a x, \a y)).
+*/
+
+/*!
+ Maps the point \a point, which is in this item's scene's coordinate
+ system, to this item's coordinate system, and returns the mapped
+ coordinate.
+
+ \sa mapFromItem(), mapFromParent(), transform(), mapToScene(), {The Graphics
+ View Coordinate System}
+*/
+QPointF QGraphicsItem::mapFromScene(const QPointF &point) const
+{
+ if (d_ptr->hasTranslateOnlySceneTransform())
+ return QPointF(point.x() - d_ptr->sceneTransform.dx(), point.y() - d_ptr->sceneTransform.dy());
+ return d_ptr->sceneTransform.inverted().map(point);
+}
+
+/*!
+ \fn QPointF QGraphicsItem::mapFromScene(qreal x, qreal y) const
+ \overload
+
+ This convenience function is equivalent to calling mapFromScene(QPointF(\a
+ x, \a y)).
+*/
+
+/*!
+ Maps the rectangle \a rect, which is in \a item's coordinate system, to
+ this item's coordinate system, and returns the mapped rectangle as a
+ polygon.
+
+ If \a item is 0, this function returns the same as mapFromScene()
+
+ \sa itemTransform(), mapToItem(), mapFromParent(), transform(), {The Graphics View Coordinate
+ System}
+*/
+QPolygonF QGraphicsItem::mapFromItem(const QGraphicsItem *item, const QRectF &rect) const
+{
+ if (item)
+ return item->itemTransform(this).map(rect);
+ return mapFromScene(rect);
+}
+
+/*!
+ \fn QPolygonF QGraphicsItem::mapFromItem(const QGraphicsItem *item, qreal x, qreal y, qreal w, qreal h) const
+ \since 4.3
+
+ This convenience function is equivalent to calling mapFromItem(item, QRectF(\a x, \a y, \a w, \a h)).
+*/
+
+/*!
+ Maps the rectangle \a rect, which is in this item's parent's coordinate
+ system, to this item's coordinate system, and returns the mapped rectangle
+ as a polygon.
+
+ \sa mapToParent(), mapFromItem(), transform(), {The Graphics View Coordinate
+ System}
+*/
+QPolygonF QGraphicsItem::mapFromParent(const QRectF &rect) const
+{
+ // COMBINE
+ if (!d_ptr->transformData)
+ return rect.translated(-d_ptr->pos);
+ return d_ptr->transformToParent().inverted().map(rect);
+}
+
+/*!
+ \fn QPolygonF QGraphicsItem::mapFromParent(qreal x, qreal y, qreal w, qreal h) const
+ \since 4.3
+
+ This convenience function is equivalent to calling mapFromItem(QRectF(\a x, \a y, \a w, \a h)).
+*/
+
+/*!
+ Maps the rectangle \a rect, which is in this item's scene's coordinate
+ system, to this item's coordinate system, and returns the mapped rectangle
+ as a polygon.
+
+ \sa mapToScene(), mapFromItem(), transform(), {The Graphics View Coordinate
+ System}
+*/
+QPolygonF QGraphicsItem::mapFromScene(const QRectF &rect) const
+{
+ if (d_ptr->hasTranslateOnlySceneTransform())
+ return rect.translated(-d_ptr->sceneTransform.dx(), -d_ptr->sceneTransform.dy());
+ return d_ptr->sceneTransform.inverted().map(rect);
+}
+
+/*!
+ \fn QPolygonF QGraphicsItem::mapFromScene(qreal x, qreal y, qreal w, qreal h) const
+ \since 4.3
+
+ This convenience function is equivalent to calling mapFromScene(QRectF(\a x, \a y, \a w, \a h)).
+*/
+
+/*!
+ Maps the polygon \a polygon, which is in \a item's coordinate system, to
+ this item's coordinate system, and returns the mapped polygon.
+
+ If \a item is 0, this function returns the same as mapFromScene().
+
+ \sa itemTransform(), mapToItem(), mapFromParent(), transform(), {The
+ Graphics View Coordinate System}
+*/
+QPolygonF QGraphicsItem::mapFromItem(const QGraphicsItem *item, const QPolygonF &polygon) const
+{
+ if (item)
+ return item->itemTransform(this).map(polygon);
+ return mapFromScene(polygon);
+}
+
+/*!
+ Maps the polygon \a polygon, which is in this item's parent's coordinate
+ system, to this item's coordinate system, and returns the mapped polygon.
+
+ \sa mapToParent(), mapToItem(), transform(), {The Graphics View Coordinate
+ System}
+*/
+QPolygonF QGraphicsItem::mapFromParent(const QPolygonF &polygon) const
+{
+ // COMBINE
+ if (!d_ptr->transformData)
+ return polygon.translated(-d_ptr->pos);
+ return d_ptr->transformToParent().inverted().map(polygon);
+}
+
+/*!
+ Maps the polygon \a polygon, which is in this item's scene's coordinate
+ system, to this item's coordinate system, and returns the mapped polygon.
+
+ \sa mapToScene(), mapFromParent(), transform(), {The Graphics View Coordinate
+ System}
+*/
+QPolygonF QGraphicsItem::mapFromScene(const QPolygonF &polygon) const
+{
+ if (d_ptr->hasTranslateOnlySceneTransform())
+ return polygon.translated(-d_ptr->sceneTransform.dx(), -d_ptr->sceneTransform.dy());
+ return d_ptr->sceneTransform.inverted().map(polygon);
+}
+
+/*!
+ Maps the path \a path, which is in \a item's coordinate system, to
+ this item's coordinate system, and returns the mapped path.
+
+ If \a item is 0, this function returns the same as mapFromScene().
+
+ \sa itemTransform(), mapFromParent(), mapFromScene(), mapToItem(), {The
+ Graphics View Coordinate System}
+*/
+QPainterPath QGraphicsItem::mapFromItem(const QGraphicsItem *item, const QPainterPath &path) const
+{
+ if (item)
+ return item->itemTransform(this).map(path);
+ return mapFromScene(path);
+}
+
+/*!
+ Maps the path \a path, which is in this item's parent's coordinate
+ system, to this item's coordinate system, and returns the mapped path.
+
+ \sa mapFromScene(), mapFromItem(), mapToParent(), {The Graphics View
+ Coordinate System}
+*/
+QPainterPath QGraphicsItem::mapFromParent(const QPainterPath &path) const
+{
+ // COMBINE
+ if (!d_ptr->transformData)
+ return path.translated(-d_ptr->pos);
+ return d_ptr->transformToParent().inverted().map(path);
+}
+
+/*!
+ Maps the path \a path, which is in this item's scene's coordinate
+ system, to this item's coordinate system, and returns the mapped path.
+
+ \sa mapFromParent(), mapFromItem(), mapToScene(), {The Graphics View
+ Coordinate System}
+*/
+QPainterPath QGraphicsItem::mapFromScene(const QPainterPath &path) const
+{
+ if (d_ptr->hasTranslateOnlySceneTransform())
+ return path.translated(-d_ptr->sceneTransform.dx(), -d_ptr->sceneTransform.dy());
+ return d_ptr->sceneTransform.inverted().map(path);
+}
+
+/*!
+ Returns true if this item is an ancestor of \a child (i.e., if this item
+ is \a child's parent, or one of \a child's parent's ancestors).
+
+ \sa parentItem()
+*/
+bool QGraphicsItem::isAncestorOf(const QGraphicsItem *child) const
+{
+ if (!child || child == this)
+ return false;
+ if (child->d_ptr->depth() < d_ptr->depth())
+ return false;
+ const QGraphicsItem *ancestor = child;
+ while ((ancestor = ancestor->d_ptr->parent)) {
+ if (ancestor == this)
+ return true;
+ }
+ return false;
+}
+
+/*!
+ \since 4.4
+
+ Returns the closest common ancestor item of this item and \a other, or 0
+ if either \a other is 0, or there is no common ancestor.
+
+ \sa isAncestorOf()
+*/
+QGraphicsItem *QGraphicsItem::commonAncestorItem(const QGraphicsItem *other) const
+{
+ if (!other)
+ return 0;
+ if (other == this)
+ return const_cast<QGraphicsItem *>(this);
+ const QGraphicsItem *thisw = this;
+ const QGraphicsItem *otherw = other;
+ int thisDepth = d_ptr->depth();
+ int otherDepth = other->d_ptr->depth();
+ while (thisDepth > otherDepth) {
+ thisw = thisw->d_ptr->parent;
+ --thisDepth;
+ }
+ while (otherDepth > thisDepth) {
+ otherw = otherw->d_ptr->parent;
+ --otherDepth;
+ }
+ while (thisw && thisw != otherw) {
+ thisw = thisw->d_ptr->parent;
+ otherw = otherw->d_ptr->parent;
+ }
+ return const_cast<QGraphicsItem *>(thisw);
+}
+
+/*!
+ \since 4,4
+ Returns true if this item is currently under the mouse cursor in one of
+ the views; otherwise, false is returned.
+
+ \sa QGraphicsScene::views(), QCursor::pos()
+*/
+bool QGraphicsItem::isUnderMouse() const
+{
+ Q_D(const QGraphicsItem);
+ if (!d->scene)
+ return false;
+
+ QPoint cursorPos = QCursor::pos();
+ foreach (QGraphicsView *view, d->scene->views()) {
+ if (contains(mapFromScene(view->mapToScene(view->mapFromGlobal(cursorPos)))))
+ return true;
+ }
+ return false;
+}
+
+/*!
+ Returns this item's custom data for the key \a key as a QVariant.
+
+ Custom item data is useful for storing arbitrary properties in any
+ item. Example:
+
+ \snippet doc/src/snippets/code/src_gui_graphicsview_qgraphicsitem.cpp 11
+
+ Qt does not use this feature for storing data; it is provided solely
+ for the convenience of the user.
+
+ \sa setData()
+*/
+QVariant QGraphicsItem::data(int key) const
+{
+ QGraphicsItemCustomDataStore *store = qt_dataStore();
+ if (!store->data.contains(this))
+ return QVariant();
+ return store->data.value(this).value(key);
+}
+
+/*!
+ Sets this item's custom data for the key \a key to \a value.
+
+ Custom item data is useful for storing arbitrary properties for any
+ item. Qt does not use this feature for storing data; it is provided solely
+ for the convenience of the user.
+
+ \sa data()
+*/
+void QGraphicsItem::setData(int key, const QVariant &value)
+{
+ qt_dataStore()->data[this][key] = value;
+}
+
+/*!
+ \fn T qgraphicsitem_cast(QGraphicsItem *item)
+ \relates QGraphicsItem
+ \since 4.2
+
+ Returns the given \a item cast to type T if \a item is of type T;
+ otherwise, 0 is returned.
+
+ \note To make this function work correctly with custom items, reimplement
+ the \l{QGraphicsItem::}{type()} function for each custom QGraphicsItem
+ subclass.
+
+ \sa QGraphicsItem::type(), QGraphicsItem::UserType
+*/
+
+/*!
+ Returns the type of an item as an int. All standard graphicsitem classes
+ are associated with a unique value; see QGraphicsItem::Type. This type
+ information is used by qgraphicsitem_cast() to distinguish between types.
+
+ The default implementation (in QGraphicsItem) returns UserType.
+
+ To enable use of qgraphicsitem_cast() with a custom item, reimplement this
+ function and declare a Type enum value equal to your custom item's type.
+ Custom items must return a value larger than or equal to UserType (65536).
+
+ For example:
+
+ \snippet doc/src/snippets/code/src_gui_graphicsview_qgraphicsitem.cpp QGraphicsItem type
+
+ \sa UserType
+*/
+int QGraphicsItem::type() const
+{
+ return (int)UserType;
+}
+
+/*!
+ Installs an event filter for this item on \a filterItem, causing
+ all events for this item to first pass through \a filterItem's
+ sceneEventFilter() function.
+
+ To filter another item's events, install this item as an event filter
+ for the other item. Example:
+
+ \snippet doc/src/snippets/code/src_gui_graphicsview_qgraphicsitem.cpp 12
+
+ An item can only filter events for other items in the same
+ scene. Also, an item cannot filter its own events; instead, you
+ can reimplement sceneEvent() directly.
+
+ Items must belong to a scene for scene event filters to be installed and
+ used.
+
+ \sa removeSceneEventFilter(), sceneEventFilter(), sceneEvent()
+*/
+void QGraphicsItem::installSceneEventFilter(QGraphicsItem *filterItem)
+{
+ if (!d_ptr->scene) {
+ qWarning("QGraphicsItem::installSceneEventFilter: event filters can only be installed"
+ " on items in a scene.");
+ return;
+ }
+ if (d_ptr->scene != filterItem->scene()) {
+ qWarning("QGraphicsItem::installSceneEventFilter: event filters can only be installed"
+ " on items in the same scene.");
+ return;
+ }
+ d_ptr->scene->d_func()->installSceneEventFilter(this, filterItem);
+}
+
+/*!
+ Removes an event filter on this item from \a filterItem.
+
+ \sa installSceneEventFilter()
+*/
+void QGraphicsItem::removeSceneEventFilter(QGraphicsItem *filterItem)
+{
+ if (!d_ptr->scene || d_ptr->scene != filterItem->scene())
+ return;
+ d_ptr->scene->d_func()->removeSceneEventFilter(this, filterItem);
+}
+
+/*!
+ Filters events for the item \a watched. \a event is the filtered
+ event.
+
+ Reimplementing this function in a subclass makes it possible
+ for the item to be used as an event filter for other items,
+ intercepting all the events send to those items before they are
+ able to respond.
+
+ Reimplementations must return true to prevent further processing of
+ a given event, ensuring that it will not be delivered to the watched
+ item, or return false to indicate that the event should be propagated
+ further by the event system.
+
+ \sa installSceneEventFilter()
+*/
+bool QGraphicsItem::sceneEventFilter(QGraphicsItem *watched, QEvent *event)
+{
+ Q_UNUSED(watched);
+ Q_UNUSED(event);
+ return false;
+}
+
+/*!
+ This virtual function receives events to this item. Reimplement
+ this function to intercept events before they are dispatched to
+ the specialized event handlers contextMenuEvent(), focusInEvent(),
+ focusOutEvent(), hoverEnterEvent(), hoverMoveEvent(),
+ hoverLeaveEvent(), keyPressEvent(), keyReleaseEvent(),
+ mousePressEvent(), mouseReleaseEvent(), mouseMoveEvent(), and
+ mouseDoubleClickEvent().
+
+ Returns true if the event was recognized and handled; otherwise, (e.g., if
+ the event type was not recognized,) false is returned.
+
+ \a event is the intercepted event.
+*/
+bool QGraphicsItem::sceneEvent(QEvent *event)
+{
+ if (d_ptr->ancestorFlags & QGraphicsItemPrivate::AncestorHandlesChildEvents) {
+ if (event->type() == QEvent::HoverEnter || event->type() == QEvent::HoverLeave
+ || event->type() == QEvent::DragEnter || event->type() == QEvent::DragLeave) {
+ // Hover enter and hover leave events for children are ignored;
+ // hover move events are forwarded.
+ return true;
+ }
+
+ QGraphicsItem *handler = this;
+ do {
+ handler = handler->d_ptr->parent;
+ Q_ASSERT(handler);
+ } while (handler->d_ptr->ancestorFlags & QGraphicsItemPrivate::AncestorHandlesChildEvents);
+ // Forward the event to the closest parent that handles child
+ // events, mapping existing item-local coordinates to its
+ // coordinate system.
+ d_ptr->remapItemPos(event, handler);
+ handler->sceneEvent(event);
+ return true;
+ }
+
+ if (event->type() == QEvent::FocusOut) {
+ focusOutEvent(static_cast<QFocusEvent *>(event));
+ return true;
+ }
+
+ if (!d_ptr->visible) {
+ // Eaten
+ return true;
+ }
+
+ switch (event->type()) {
+ case QEvent::FocusIn:
+ focusInEvent(static_cast<QFocusEvent *>(event));
+ break;
+ case QEvent::GraphicsSceneContextMenu:
+ contextMenuEvent(static_cast<QGraphicsSceneContextMenuEvent *>(event));
+ break;
+ case QEvent::GraphicsSceneDragEnter:
+ dragEnterEvent(static_cast<QGraphicsSceneDragDropEvent *>(event));
+ break;
+ case QEvent::GraphicsSceneDragMove:
+ dragMoveEvent(static_cast<QGraphicsSceneDragDropEvent *>(event));
+ break;
+ case QEvent::GraphicsSceneDragLeave:
+ dragLeaveEvent(static_cast<QGraphicsSceneDragDropEvent *>(event));
+ break;
+ case QEvent::GraphicsSceneDrop:
+ dropEvent(static_cast<QGraphicsSceneDragDropEvent *>(event));
+ break;
+ case QEvent::GraphicsSceneHoverEnter:
+ hoverEnterEvent(static_cast<QGraphicsSceneHoverEvent *>(event));
+ break;
+ case QEvent::GraphicsSceneHoverMove:
+ hoverMoveEvent(static_cast<QGraphicsSceneHoverEvent *>(event));
+ break;
+ case QEvent::GraphicsSceneHoverLeave:
+ hoverLeaveEvent(static_cast<QGraphicsSceneHoverEvent *>(event));
+ break;
+ case QEvent::GraphicsSceneMouseMove:
+ mouseMoveEvent(static_cast<QGraphicsSceneMouseEvent *>(event));
+ break;
+ case QEvent::GraphicsSceneMousePress:
+ mousePressEvent(static_cast<QGraphicsSceneMouseEvent *>(event));
+ break;
+ case QEvent::GraphicsSceneMouseRelease:
+ mouseReleaseEvent(static_cast<QGraphicsSceneMouseEvent *>(event));
+ break;
+ case QEvent::GraphicsSceneMouseDoubleClick:
+ mouseDoubleClickEvent(static_cast<QGraphicsSceneMouseEvent *>(event));
+ break;
+ case QEvent::GraphicsSceneWheel:
+ wheelEvent(static_cast<QGraphicsSceneWheelEvent *>(event));
+ break;
+ case QEvent::KeyPress: {
+ QKeyEvent *k = static_cast<QKeyEvent *>(event);
+ if (k->key() == Qt::Key_Tab || k->key() == Qt::Key_Backtab) {
+ if (!(k->modifiers() & (Qt::ControlModifier | Qt::AltModifier))) { //### Add MetaModifier?
+ bool res = false;
+ if (k->key() == Qt::Key_Backtab
+ || (k->key() == Qt::Key_Tab && (k->modifiers() & Qt::ShiftModifier))) {
+ if (d_ptr->isWidget) {
+ res = static_cast<QGraphicsWidget *>(this)->focusNextPrevChild(false);
+ } else if (d_ptr->scene) {
+ res = d_ptr->scene->focusNextPrevChild(false);
+ }
+ } else if (k->key() == Qt::Key_Tab) {
+ if (d_ptr->isWidget) {
+ res = static_cast<QGraphicsWidget *>(this)->focusNextPrevChild(true);
+ } else if (d_ptr->scene) {
+ res = d_ptr->scene->focusNextPrevChild(true);
+ }
+ }
+ if (!res)
+ event->ignore();
+ return true;
+ }
+ }
+ keyPressEvent(static_cast<QKeyEvent *>(event));
+ break;
+ }
+ case QEvent::KeyRelease:
+ keyReleaseEvent(static_cast<QKeyEvent *>(event));
+ break;
+ case QEvent::InputMethod:
+ inputMethodEvent(static_cast<QInputMethodEvent *>(event));
+ break;
+ case QEvent::WindowActivate:
+ case QEvent::WindowDeactivate:
+ // Propagate panel activation.
+ if (d_ptr->scene) {
+ for (int i = 0; i < d_ptr->children.size(); ++i) {
+ QGraphicsItem *child = d_ptr->children.at(i);
+ if (child->isVisible() && !child->isPanel()) {
+ if (!(child->d_ptr->ancestorFlags & QGraphicsItemPrivate::AncestorHandlesChildEvents))
+ d_ptr->scene->sendEvent(child, event);
+ }
+ }
+ }
+ break;
+ default:
+ return false;
+ }
+
+ return true;
+}
+
+/*!
+ This event handler can be reimplemented in a subclass to process context
+ menu events. The \a event parameter contains details about the event to
+ be handled.
+
+ If you ignore the event, (i.e., by calling QEvent::ignore(),) \a event
+ will propagate to any item beneath this item. If no items accept the
+ event, it will be ignored by the scene, and propagate to the view.
+
+ It's common to open a QMenu in response to receiving a context menu
+ event. Example:
+
+ \snippet doc/src/snippets/code/src_gui_graphicsview_qgraphicsitem.cpp 13
+
+ The default implementation ignores the event.
+
+ \sa sceneEvent()
+*/
+void QGraphicsItem::contextMenuEvent(QGraphicsSceneContextMenuEvent *event)
+{
+ event->ignore();
+}
+
+/*!
+ This event handler, for event \a event, can be reimplemented to receive
+ drag enter events for this item. Drag enter events are generated as the
+ cursor enters the item's area.
+
+ By accepting the event, (i.e., by calling QEvent::accept(),) the item will
+ accept drop events, in addition to receiving drag move and drag
+ leave. Otherwise, the event will be ignored and propagate to the item
+ beneath. If the event is accepted, the item will receive a drag move event
+ before control goes back to the event loop.
+
+ A common implementation of dragEnterEvent accepts or ignores \a event
+ depending on the associated mime data in \a event. Example:
+
+ \snippet doc/src/snippets/code/src_gui_graphicsview_qgraphicsitem.cpp 14
+
+ Items do not receive drag and drop events by default; to enable this
+ feature, call \c setAcceptDrops(true).
+
+ The default implementation does nothing.
+
+ \sa dropEvent(), dragMoveEvent(), dragLeaveEvent()
+*/
+void QGraphicsItem::dragEnterEvent(QGraphicsSceneDragDropEvent *event)
+{
+ Q_D(QGraphicsItem);
+ // binary compatibility workaround between 4.4 and 4.5
+ if (d->isProxyWidget())
+ static_cast<QGraphicsProxyWidget*>(this)->dragEnterEvent(event);
+}
+
+/*!
+ This event handler, for event \a event, can be reimplemented to receive
+ drag leave events for this item. Drag leave events are generated as the
+ cursor leaves the item's area. Most often you will not need to reimplement
+ this function, but it can be useful for resetting state in your item
+ (e.g., highlighting).
+
+ Calling QEvent::ignore() or QEvent::accept() on \a event has no effect.
+
+ Items do not receive drag and drop events by default; to enable this
+ feature, call \c setAcceptDrops(true).
+
+ The default implementation does nothing.
+
+ \sa dragEnterEvent(), dropEvent(), dragMoveEvent()
+*/
+void QGraphicsItem::dragLeaveEvent(QGraphicsSceneDragDropEvent *event)
+{
+ Q_D(QGraphicsItem);
+ // binary compatibility workaround between 4.4 and 4.5
+ if (d->isProxyWidget())
+ static_cast<QGraphicsProxyWidget*>(this)->dragLeaveEvent(event);
+}
+
+/*!
+ This event handler, for event \a event, can be reimplemented to receive
+ drag move events for this item. Drag move events are generated as the
+ cursor moves around inside the item's area. Most often you will not need
+ to reimplement this function; it is used to indicate that only parts of
+ the item can accept drops.
+
+ Calling QEvent::ignore() or QEvent::accept() on \a event toggles whether
+ or not the item will accept drops at the position from the event. By
+ default, \a event is accepted, indicating that the item allows drops at
+ the specified position.
+
+ Items do not receive drag and drop events by default; to enable this
+ feature, call \c setAcceptDrops(true).
+
+ The default implementation does nothing.
+
+ \sa dropEvent(), dragEnterEvent(), dragLeaveEvent()
+*/
+void QGraphicsItem::dragMoveEvent(QGraphicsSceneDragDropEvent *event)
+{
+ Q_D(QGraphicsItem);
+ // binary compatibility workaround between 4.4 and 4.5
+ if (d->isProxyWidget())
+ static_cast<QGraphicsProxyWidget*>(this)->dragMoveEvent(event);
+}
+
+/*!
+ This event handler, for event \a event, can be reimplemented to receive
+ drop events for this item. Items can only receive drop events if the last
+ drag move event was accepted.
+
+ Calling QEvent::ignore() or QEvent::accept() on \a event has no effect.
+
+ Items do not receive drag and drop events by default; to enable this
+ feature, call \c setAcceptDrops(true).
+
+ The default implementation does nothing.
+
+ \sa dragEnterEvent(), dragMoveEvent(), dragLeaveEvent()
+*/
+void QGraphicsItem::dropEvent(QGraphicsSceneDragDropEvent *event)
+{
+ Q_D(QGraphicsItem);
+ // binary compatibility workaround between 4.4 and 4.5
+ if (d->isProxyWidget())
+ static_cast<QGraphicsProxyWidget*>(this)->dropEvent(event);
+}
+
+/*!
+ This event handler, for event \a event, can be reimplemented to receive
+ focus in events for this item. The default implementation calls
+ ensureVisible().
+
+ \sa focusOutEvent(), sceneEvent(), setFocus()
+*/
+void QGraphicsItem::focusInEvent(QFocusEvent *event)
+{
+ Q_UNUSED(event);
+ update();
+}
+
+/*!
+ This event handler, for event \a event, can be reimplemented to receive
+ focus out events for this item. The default implementation does nothing.
+
+ \sa focusInEvent(), sceneEvent(), setFocus()
+*/
+void QGraphicsItem::focusOutEvent(QFocusEvent *event)
+{
+ Q_UNUSED(event);
+ update();
+}
+
+/*!
+ This event handler, for event \a event, can be reimplemented to receive
+ hover enter events for this item. The default implementation calls
+ update(); otherwise it does nothing.
+
+ Calling QEvent::ignore() or QEvent::accept() on \a event has no effect.
+
+ \sa hoverMoveEvent(), hoverLeaveEvent(), sceneEvent(), setAcceptHoverEvents()
+*/
+void QGraphicsItem::hoverEnterEvent(QGraphicsSceneHoverEvent *event)
+{
+ Q_UNUSED(event);
+ update();
+}
+
+/*!
+ This event handler, for event \a event, can be reimplemented to receive
+ hover move events for this item. The default implementation does nothing.
+
+ Calling QEvent::ignore() or QEvent::accept() on \a event has no effect.
+
+ \sa hoverEnterEvent(), hoverLeaveEvent(), sceneEvent(), setAcceptHoverEvents()
+*/
+void QGraphicsItem::hoverMoveEvent(QGraphicsSceneHoverEvent *event)
+{
+ Q_UNUSED(event);
+}
+
+/*!
+ This event handler, for event \a event, can be reimplemented to receive
+ hover leave events for this item. The default implementation calls
+ update(); otherwise it does nothing.
+
+ Calling QEvent::ignore() or QEvent::accept() on \a event has no effect.
+
+ \sa hoverEnterEvent(), hoverMoveEvent(), sceneEvent(), setAcceptHoverEvents()
+*/
+void QGraphicsItem::hoverLeaveEvent(QGraphicsSceneHoverEvent *event)
+{
+ Q_UNUSED(event);
+ update();
+}
+
+/*!
+ This event handler, for event \a event, can be reimplemented to
+ receive key press events for this item. The default implementation
+ ignores the event. If you reimplement this handler, the event will by
+ default be accepted.
+
+ Note that key events are only received for items that set the
+ ItemIsFocusable flag, and that have keyboard input focus.
+
+ \sa keyReleaseEvent(), setFocus(), QGraphicsScene::setFocusItem(),
+ sceneEvent()
+*/
+void QGraphicsItem::keyPressEvent(QKeyEvent *event)
+{
+ event->ignore();
+}
+
+/*!
+ This event handler, for event \a event, can be reimplemented to receive
+ key release events for this item. The default implementation
+ ignores the event. If you reimplement this handler, the event will by
+ default be accepted.
+
+ Note that key events are only received for items that set the
+ ItemIsFocusable flag, and that have keyboard input focus.
+
+ \sa keyPressEvent(), setFocus(), QGraphicsScene::setFocusItem(),
+ sceneEvent()
+*/
+void QGraphicsItem::keyReleaseEvent(QKeyEvent *event)
+{
+ event->ignore();
+}
+
+/*!
+ This event handler, for event \a event, can be reimplemented to
+ receive mouse press events for this item. Mouse press events are
+ only delivered to items that accept the mouse button that is
+ pressed. By default, an item accepts all mouse buttons, but you
+ can change this by calling setAcceptedMouseButtons().
+
+ The mouse press event decides which item should become the mouse
+ grabber (see QGraphicsScene::mouseGrabberItem()). If you do not
+ reimplement this function, the press event will propagate to any
+ topmost item beneath this item, and no other mouse events will be
+ delivered to this item.
+
+ If you do reimplement this function, \a event will by default be
+ accepted (see QEvent::accept()), and this item is then the mouse
+ grabber. This allows the item to receive future move, release and
+ doubleclick events. If you call QEvent::ignore() on \a event, this
+ item will lose the mouse grab, and \a event will propagate to any
+ topmost item beneath. No further mouse events will be delivered to
+ this item unless a new mouse press event is received.
+
+ The default implementation handles basic item interaction, such as
+ selection and moving. If you want to keep the base implementation
+ when reimplementing this function, call
+ QGraphicsItem::mousePressEvent() in your reimplementation.
+
+ The event is \l{QEvent::ignore()}d for items that are neither
+ \l{QGraphicsItem::ItemIsMovable}{movable} nor
+ \l{QGraphicsItem::ItemIsSelectable}{selectable}.
+
+ \sa mouseMoveEvent(), mouseReleaseEvent(),
+ mouseDoubleClickEvent(), sceneEvent()
+*/
+void QGraphicsItem::mousePressEvent(QGraphicsSceneMouseEvent *event)
+{
+ if (event->button() == Qt::LeftButton && (flags() & ItemIsSelectable)) {
+ bool multiSelect = (event->modifiers() & Qt::ControlModifier) != 0;
+ if (!multiSelect) {
+ if (!d_ptr->selected) {
+ if (QGraphicsScene *scene = d_ptr->scene) {
+ ++scene->d_func()->selectionChanging;
+ scene->clearSelection();
+ --scene->d_func()->selectionChanging;
+ }
+ setSelected(true);
+ }
+ }
+ } else if (!(flags() & ItemIsMovable)) {
+ event->ignore();
+ }
+ if (d_ptr->isWidget) {
+ // Qt::Popup closes when you click outside.
+ QGraphicsWidget *w = static_cast<QGraphicsWidget *>(this);
+ if ((w->windowFlags() & Qt::Popup) == Qt::Popup) {
+ event->accept();
+ if (!w->rect().contains(event->pos()))
+ w->close();
+ }
+ }
+}
+
+/*!
+ obsolete
+*/
+bool _qt_movableAncestorIsSelected(const QGraphicsItem *item)
+{
+ const QGraphicsItem *parent = item->parentItem();
+ return parent && (((parent->flags() & QGraphicsItem::ItemIsMovable) && parent->isSelected()) || _qt_movableAncestorIsSelected(parent));
+}
+
+bool QGraphicsItemPrivate::movableAncestorIsSelected(const QGraphicsItem *item)
+{
+ const QGraphicsItem *parent = item->d_ptr->parent;
+ return parent && (((parent->flags() & QGraphicsItem::ItemIsMovable) && parent->isSelected()) || _qt_movableAncestorIsSelected(parent));
+}
+
+/*!
+ This event handler, for event \a event, can be reimplemented to
+ receive mouse move events for this item. If you do receive this
+ event, you can be certain that this item also received a mouse
+ press event, and that this item is the current mouse grabber.
+
+ Calling QEvent::ignore() or QEvent::accept() on \a event has no
+ effect.
+
+ The default implementation handles basic item interaction, such as
+ selection and moving. If you want to keep the base implementation
+ when reimplementing this function, call
+ QGraphicsItem::mouseMoveEvent() in your reimplementation.
+
+ Please note that mousePressEvent() decides which graphics item it
+ is that receives mouse events. See the mousePressEvent()
+ description for details.
+
+ \sa mousePressEvent(), mouseReleaseEvent(),
+ mouseDoubleClickEvent(), sceneEvent()
+*/
+void QGraphicsItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
+{
+ if ((event->buttons() & Qt::LeftButton) && (flags() & ItemIsMovable)) {
+ // Determine the list of items that need to be moved.
+ QList<QGraphicsItem *> selectedItems;
+ QMap<QGraphicsItem *, QPointF> initialPositions;
+ if (d_ptr->scene) {
+ selectedItems = d_ptr->scene->selectedItems();
+ initialPositions = d_ptr->scene->d_func()->movingItemsInitialPositions;
+ if (initialPositions.isEmpty()) {
+ foreach (QGraphicsItem *item, selectedItems)
+ initialPositions[item] = item->pos();
+ initialPositions[this] = pos();
+ }
+ d_ptr->scene->d_func()->movingItemsInitialPositions = initialPositions;
+ }
+
+ // Find the active view.
+ QGraphicsView *view = 0;
+ if (event->widget())
+ view = qobject_cast<QGraphicsView *>(event->widget()->parentWidget());
+
+ // Move all selected items
+ int i = 0;
+ bool movedMe = false;
+ while (i <= selectedItems.size()) {
+ QGraphicsItem *item = 0;
+ if (i < selectedItems.size())
+ item = selectedItems.at(i);
+ else
+ item = this;
+ if (item == this) {
+ // Slightly clumsy-looking way to ensure that "this" is part
+ // of the list of items to move, this is to avoid allocations
+ // (appending this item to the list of selected items causes a
+ // detach).
+ if (movedMe)
+ break;
+ movedMe = true;
+ }
+
+ if ((item->flags() & ItemIsMovable) && !QGraphicsItemPrivate::movableAncestorIsSelected(item)) {
+ QPointF currentParentPos;
+ QPointF buttonDownParentPos;
+ if (item->d_ptr->ancestorFlags & QGraphicsItemPrivate::AncestorIgnoresTransformations) {
+ // Items whose ancestors ignore transformations need to
+ // map screen coordinates to local coordinates, then map
+ // those to the parent.
+ QTransform viewToItemTransform = (item->deviceTransform(view->viewportTransform())).inverted();
+ currentParentPos = mapToParent(viewToItemTransform.map(QPointF(view->mapFromGlobal(event->screenPos()))));
+ buttonDownParentPos = mapToParent(viewToItemTransform.map(QPointF(view->mapFromGlobal(event->buttonDownScreenPos(Qt::LeftButton)))));
+ } else if (item->flags() & ItemIgnoresTransformations) {
+ // Root items that ignore transformations need to
+ // calculate their diff by mapping viewport coordinates
+ // directly to parent coordinates.
+ // COMBINE
+ QTransform itemTransform;
+ if (item->d_ptr->transformData)
+ itemTransform = item->d_ptr->transformData->computedFullTransform();
+ itemTransform.translate(item->d_ptr->pos.x(), item->d_ptr->pos.y());
+ QTransform viewToParentTransform = itemTransform
+ * (item->sceneTransform() * view->viewportTransform()).inverted();
+ currentParentPos = viewToParentTransform.map(QPointF(view->mapFromGlobal(event->screenPos())));
+ buttonDownParentPos = viewToParentTransform.map(QPointF(view->mapFromGlobal(event->buttonDownScreenPos(Qt::LeftButton))));
+ } else {
+ // All other items simply map from the scene.
+ currentParentPos = item->mapToParent(item->mapFromScene(event->scenePos()));
+ buttonDownParentPos = item->mapToParent(item->mapFromScene(event->buttonDownScenePos(Qt::LeftButton)));
+ }
+
+ item->setPos(initialPositions.value(item) + currentParentPos - buttonDownParentPos);
+
+ if (item->flags() & ItemIsSelectable)
+ item->setSelected(true);
+ }
+ ++i;
+ }
+
+ } else {
+ event->ignore();
+ }
+}
+
+/*!
+ This event handler, for event \a event, can be reimplemented to
+ receive mouse release events for this item.
+
+ Calling QEvent::ignore() or QEvent::accept() on \a event has no
+ effect.
+
+ The default implementation handles basic item interaction, such as
+ selection and moving. If you want to keep the base implementation
+ when reimplementing this function, call
+ QGraphicsItem::mouseReleaseEvent() in your reimplementation.
+
+ Please note that mousePressEvent() decides which graphics item it
+ is that receives mouse events. See the mousePressEvent()
+ description for details.
+
+ \sa mousePressEvent(), mouseMoveEvent(), mouseDoubleClickEvent(),
+ sceneEvent()
+*/
+void QGraphicsItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
+{
+ if (flags() & ItemIsSelectable) {
+ bool multiSelect = (event->modifiers() & Qt::ControlModifier) != 0;
+ if (event->scenePos() == event->buttonDownScenePos(Qt::LeftButton)) {
+ // The item didn't move
+ if (multiSelect) {
+ setSelected(!isSelected());
+ } else {
+ bool selectionChanged = false;
+ if (QGraphicsScene *scene = d_ptr->scene) {
+ ++scene->d_func()->selectionChanging;
+ // Clear everything but this item. Bypass
+ // QGraphicsScene::clearSelection()'s default behavior by
+ // temporarily removing this item from the selection list.
+ if (d_ptr->selected) {
+ scene->d_func()->selectedItems.remove(this);
+ foreach (QGraphicsItem *item, scene->d_func()->selectedItems) {
+ if (item->isSelected()) {
+ selectionChanged = true;
+ break;
+ }
+ }
+ }
+ scene->clearSelection();
+ if (d_ptr->selected)
+ scene->d_func()->selectedItems.insert(this);
+ --scene->d_func()->selectionChanging;
+ if (selectionChanged)
+ emit d_ptr->scene->selectionChanged();
+ }
+ setSelected(true);
+ }
+ }
+ }
+ if (d_ptr->scene && !event->buttons())
+ d_ptr->scene->d_func()->movingItemsInitialPositions.clear();
+}
+
+/*!
+ This event handler, for event \a event, can be reimplemented to
+ receive mouse doubleclick events for this item.
+
+ When doubleclicking an item, the item will first receive a mouse
+ press event, followed by a release event (i.e., a click), then a
+ doubleclick event, and finally a release event.
+
+ Calling QEvent::ignore() or QEvent::accept() on \a event has no
+ effect.
+
+ The default implementation calls mousePressEvent(). If you want to
+ keep the base implementation when reimplementing this function,
+ call QGraphicsItem::mouseDoubleClickEvent() in your
+ reimplementation.
+
+ Note that an item will not receive double click events if it is
+ neither \l {QGraphicsItem::ItemIsSelectable}{selectable} nor
+ \l{QGraphicsItem::ItemIsMovable}{movable} (single mouse clicks are
+ ignored in this case, and that stops the generation of double
+ clicks).
+
+ \sa mousePressEvent(), mouseMoveEvent(), mouseReleaseEvent(), sceneEvent()
+*/
+void QGraphicsItem::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event)
+{
+ mousePressEvent(event);
+}
+
+/*!
+ This event handler, for event \a event, can be reimplemented to receive
+ wheel events for this item. If you reimplement this function, \a event
+ will be accepted by default.
+
+ If you ignore the event, (i.e., by calling QEvent::ignore(),) it will
+ propagate to any item beneath this item. If no items accept the event, it
+ will be ignored by the scene, and propagate to the view (e.g., the view's
+ vertical scroll bar).
+
+ The default implementation ignores the event.
+
+ \sa sceneEvent()
+*/
+void QGraphicsItem::wheelEvent(QGraphicsSceneWheelEvent *event)
+{
+ event->ignore();
+}
+
+/*!
+ This event handler, for event \a event, can be reimplemented to receive
+ input method events for this item. The default implementation ignores the
+ event.
+
+ \sa inputMethodQuery(), sceneEvent()
+*/
+void QGraphicsItem::inputMethodEvent(QInputMethodEvent *event)
+{
+ event->ignore();
+}
+
+/*!
+ This method is only relevant for input items. It is used by the
+ input method to query a set of properties of the item to be able
+ to support complex input method operations, such as support for
+ surrounding text and reconversions. \a query specifies which
+ property is queried.
+
+ \sa inputMethodEvent(), QInputMethodEvent, QInputContext
+*/
+QVariant QGraphicsItem::inputMethodQuery(Qt::InputMethodQuery query) const
+{
+ if (isWidget()) {
+ // ### Qt 5: Remove. The reimplementation in
+ // QGraphicsProxyWidget solves this problem (but requires a
+ // recompile to take effect).
+ return d_ptr->inputMethodQueryHelper(query);
+ }
+
+ Q_UNUSED(query);
+ return QVariant();
+}
+
+/*!
+ Returns the current input method hints of this item.
+
+ Input method hints are only relevant for input items.
+ The hints are used by the input method to indicate how it should operate.
+ For example, if the Qt::ImhNumbersOnly flag is set, the input method may change
+ its visual components to reflect that only numbers can be entered.
+
+ The effect may vary between input method implementations.
+
+ \since 4.6
+
+ \sa setInputMethodHints(), inputMethodQuery(), QInputContext
+*/
+Qt::InputMethodHints QGraphicsItem::inputMethodHints() const
+{
+ Q_D(const QGraphicsItem);
+ return d->imHints;
+}
+
+/*!
+ Sets the current input method hints of this item to \a hints.
+
+ \since 4.6
+
+ \sa inputMethodHints(), inputMethodQuery(), QInputContext
+*/
+void QGraphicsItem::setInputMethodHints(Qt::InputMethodHints hints)
+{
+ Q_D(QGraphicsItem);
+ d->imHints = hints;
+ if (!hasFocus())
+ return;
+ d->scene->d_func()->updateInputMethodSensitivityInViews();
+#if !defined(QT_NO_IM) && (defined(Q_WS_X11) || defined(Q_WS_QWS) || defined(Q_OS_SYMBIAN))
+ QWidget *fw = QApplication::focusWidget();
+ if (!fw)
+ return;
+ for (int i = 0 ; i < scene()->views().count() ; ++i)
+ if (scene()->views().at(i) == fw)
+ if (QInputContext *inputContext = fw->inputContext())
+ inputContext->update();
+#endif
+}
+
+/*!
+ Updates the item's micro focus.
+
+ \since 4.7
+
+ \sa QInputContext
+*/
+void QGraphicsItem::updateMicroFocus()
+{
+#if !defined(QT_NO_IM) && (defined(Q_WS_X11) || defined(Q_WS_QWS) || defined(Q_OS_SYMBIAN))
+ if (QWidget *fw = QApplication::focusWidget()) {
+ if (scene()) {
+ for (int i = 0 ; i < scene()->views().count() ; ++i) {
+ if (scene()->views().at(i) == fw)
+ if (QInputContext *inputContext = fw->inputContext())
+ inputContext->update();
+ }
+ }
+#ifndef QT_NO_ACCESSIBILITY
+ // ##### is this correct
+ QAccessible::updateAccessibility(fw, 0, QAccessible::StateChanged);
+#endif
+ }
+#endif
+}
+
+/*!
+ This virtual function is called by QGraphicsItem to notify custom items
+ that some part of the item's state changes. By reimplementing this
+ function, your can react to a change, and in some cases, (depending on \a
+ change,) adjustments can be made.
+
+ \a change is the parameter of the item that is changing. \a value is the
+ new value; the type of the value depends on \a change.
+
+ Example:
+
+ \snippet doc/src/snippets/code/src_gui_graphicsview_qgraphicsitem.cpp 15
+
+ The default implementation does nothing, and returns \a value.
+
+ Note: Certain QGraphicsItem functions cannot be called in a
+ reimplementation of this function; see the GraphicsItemChange
+ documentation for details.
+
+ \sa GraphicsItemChange
+*/
+QVariant QGraphicsItem::itemChange(GraphicsItemChange change, const QVariant &value)
+{
+ Q_UNUSED(change);
+ return value;
+}
+
+/*!
+ \internal
+
+ Note: This is provided as a hook to avoid future problems related
+ to adding virtual functions.
+*/
+bool QGraphicsItem::supportsExtension(Extension extension) const
+{
+ Q_UNUSED(extension);
+ return false;
+}
+
+/*!
+ \internal
+
+ Note: This is provided as a hook to avoid future problems related
+ to adding virtual functions.
+*/
+void QGraphicsItem::setExtension(Extension extension, const QVariant &variant)
+{
+ Q_UNUSED(extension);
+ Q_UNUSED(variant);
+}
+
+/*!
+ \internal
+
+ Note: This is provided as a hook to avoid future problems related
+ to adding virtual functions.
+*/
+QVariant QGraphicsItem::extension(const QVariant &variant) const
+{
+ Q_UNUSED(variant);
+ return QVariant();
+}
+
+/*!
+ \internal
+
+ Adds this item to the scene's index. Called in conjunction with
+ removeFromIndex() to ensure the index bookkeeping is correct when
+ the item's position, transformation or shape changes.
+*/
+void QGraphicsItem::addToIndex()
+{
+ if (d_ptr->ancestorFlags & QGraphicsItemPrivate::AncestorClipsChildren) {
+ // ### add to child index only if applicable
+ return;
+ }
+ if (d_ptr->scene)
+ d_ptr->scene->d_func()->index->addItem(this);
+}
+
+/*!
+ \internal
+
+ Removes this item from the scene's index. Called in conjunction
+ with addToIndex() to ensure the index bookkeeping is correct when
+ the item's position, transformation or shape changes.
+*/
+void QGraphicsItem::removeFromIndex()
+{
+ if (d_ptr->ancestorFlags & QGraphicsItemPrivate::AncestorClipsChildren) {
+ // ### remove from child index only if applicable
+ return;
+ }
+ if (d_ptr->scene)
+ d_ptr->scene->d_func()->index->removeItem(this);
+}
+
+/*!
+ Prepares the item for a geometry change. Call this function before
+ changing the bounding rect of an item to keep QGraphicsScene's index up to
+ date.
+
+ prepareGeometryChange() will call update() if this is necessary.
+
+ Example:
+
+ \snippet doc/src/snippets/code/src_gui_graphicsview_qgraphicsitem.cpp 16
+
+ \sa boundingRect()
+*/
+void QGraphicsItem::prepareGeometryChange()
+{
+ if (d_ptr->inDestructor)
+ return;
+ if (d_ptr->scene) {
+ 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);
+ scenePrivate->markDirty(this, QRectF(), /*invalidateChildren=*/true, /*force=*/false,
+ /*ignoreOpacity=*/ false, /*removingItemFromScene=*/ false,
+ /*updateBoundingRect=*/true);
+
+ // For compatibility reasons, we have to update the item's old geometry
+ // if someone is connected to the changed signal or the scene has no views.
+ // Note that this has to be done *after* markDirty to ensure that
+ // _q_processDirtyItems is called before _q_emitUpdated.
+ if (scenePrivate->isSignalConnected(scenePrivate->changedSignalIndex)
+ || scenePrivate->views.isEmpty()) {
+ if (d_ptr->hasTranslateOnlySceneTransform()) {
+ d_ptr->scene->update(boundingRect().translated(d_ptr->sceneTransform.dx(),
+ d_ptr->sceneTransform.dy()));
+ } else {
+ d_ptr->scene->update(d_ptr->sceneTransform.mapRect(boundingRect()));
+ }
+ }
+ }
+
+ d_ptr->markParentDirty(/*updateBoundingRect=*/true);
+}
+
+/*!
+ \internal
+
+ Highlights \a item as selected.
+
+ NOTE: This function is a duplicate of qt_graphicsItem_highlightSelected() in
+ qgraphicssvgitem.cpp!
+*/
+static void qt_graphicsItem_highlightSelected(
+ QGraphicsItem *item, QPainter *painter, const QStyleOptionGraphicsItem *option)
+{
+ const QRectF murect = painter->transform().mapRect(QRectF(0, 0, 1, 1));
+ if (qFuzzyIsNull(qMax(murect.width(), murect.height())))
+ return;
+
+ const QRectF mbrect = painter->transform().mapRect(item->boundingRect());
+ if (qMin(mbrect.width(), mbrect.height()) < qreal(1.0))
+ return;
+
+ qreal itemPenWidth;
+ switch (item->type()) {
+ case QGraphicsEllipseItem::Type:
+ itemPenWidth = static_cast<QGraphicsEllipseItem *>(item)->pen().widthF();
+ break;
+ case QGraphicsPathItem::Type:
+ itemPenWidth = static_cast<QGraphicsPathItem *>(item)->pen().widthF();
+ break;
+ case QGraphicsPolygonItem::Type:
+ itemPenWidth = static_cast<QGraphicsPolygonItem *>(item)->pen().widthF();
+ break;
+ case QGraphicsRectItem::Type:
+ itemPenWidth = static_cast<QGraphicsRectItem *>(item)->pen().widthF();
+ break;
+ case QGraphicsSimpleTextItem::Type:
+ itemPenWidth = static_cast<QGraphicsSimpleTextItem *>(item)->pen().widthF();
+ break;
+ case QGraphicsLineItem::Type:
+ itemPenWidth = static_cast<QGraphicsLineItem *>(item)->pen().widthF();
+ break;
+ default:
+ itemPenWidth = 1.0;
+ }
+ const qreal pad = itemPenWidth / 2;
+
+ const qreal penWidth = 0; // cosmetic pen
+
+ const QColor fgcolor = option->palette.windowText().color();
+ const QColor bgcolor( // ensure good contrast against fgcolor
+ fgcolor.red() > 127 ? 0 : 255,
+ fgcolor.green() > 127 ? 0 : 255,
+ fgcolor.blue() > 127 ? 0 : 255);
+
+ painter->setPen(QPen(bgcolor, penWidth, Qt::SolidLine));
+ painter->setBrush(Qt::NoBrush);
+ painter->drawRect(item->boundingRect().adjusted(pad, pad, -pad, -pad));
+
+ painter->setPen(QPen(option->palette.windowText(), 0, Qt::DashLine));
+ painter->setBrush(Qt::NoBrush);
+ painter->drawRect(item->boundingRect().adjusted(pad, pad, -pad, -pad));
+}
+
+/*!
+ \class QGraphicsObject
+ \brief The QGraphicsObject class provides a base class for all graphics items that
+ require signals, slots and properties.
+ \since 4.6
+ \ingroup graphicsview-api
+
+ The class extends a QGraphicsItem with QObject's signal/slot and property mechanisms.
+ It maps many of QGraphicsItem's basic setters and getters to properties and adds notification
+ signals for many of them.
+
+ \section1 Parents and Children
+
+ Each graphics object can be constructed with a parent item. This ensures that the
+ item will be destroyed when its parent item is destroyed. Although QGraphicsObject
+ inherits from both QObject and QGraphicsItem, you should use the functions provided
+ by QGraphicsItem, \e not QObject, to manage the relationships between parent and
+ child items.
+
+ The relationships between items can be explored using the parentItem() and childItems()
+ functions. In the hierarchy of items in a scene, the parentObject() and parentWidget()
+ functions are the equivalent of the QWidget::parent() and QWidget::parentWidget()
+ functions for QWidget subclasses.
+
+ \sa QGraphicsWidget
+*/
+
+/*!
+ Constructs a QGraphicsObject with \a parent.
+*/
+QGraphicsObject::QGraphicsObject(QGraphicsItem *parent)
+ : QGraphicsItem(parent)
+{
+ QGraphicsItem::d_ptr->isObject = true;
+}
+
+/*!
+ \internal
+*/
+QGraphicsObject::QGraphicsObject(QGraphicsItemPrivate &dd, QGraphicsItem *parent, QGraphicsScene *scene)
+ : QGraphicsItem(dd, parent, scene)
+{
+ QGraphicsItem::d_ptr->isObject = true;
+}
+
+#ifndef QT_NO_GESTURES
+/*!
+ Subscribes the graphics object to the given \a gesture with specific \a flags.
+
+ \sa ungrabGesture(), QGestureEvent
+*/
+void QGraphicsObject::grabGesture(Qt::GestureType gesture, Qt::GestureFlags flags)
+{
+ bool contains = QGraphicsItem::d_ptr->gestureContext.contains(gesture);
+ QGraphicsItem::d_ptr->gestureContext.insert(gesture, flags);
+ if (!contains && QGraphicsItem::d_ptr->scene)
+ QGraphicsItem::d_ptr->scene->d_func()->grabGesture(this, gesture);
+}
+
+/*!
+ Unsubscribes the graphics object from the given \a gesture.
+
+ \sa grabGesture(), QGestureEvent
+*/
+void QGraphicsObject::ungrabGesture(Qt::GestureType gesture)
+{
+ if (QGraphicsItem::d_ptr->gestureContext.remove(gesture) && QGraphicsItem::d_ptr->scene)
+ QGraphicsItem::d_ptr->scene->d_func()->ungrabGesture(this, gesture);
+}
+#endif // QT_NO_GESTURES
+
+/*!
+ Updates the item's micro focus. This is slot for convenience.
+
+ \since 4.7
+
+ \sa QInputContext
+*/
+void QGraphicsObject::updateMicroFocus()
+{
+ QGraphicsItem::updateMicroFocus();
+}
+
+void QGraphicsItemPrivate::children_append(QDeclarativeListProperty<QGraphicsObject> *list, QGraphicsObject *item)
+{
+ if (item) {
+ QGraphicsObject *graphicsObject = static_cast<QGraphicsObject *>(list->object);
+ if (QGraphicsItemPrivate::get(graphicsObject)->sendParentChangeNotification) {
+ item->setParentItem(graphicsObject);
+ } else {
+ QGraphicsItemPrivate::get(item)->setParentItemHelper(graphicsObject, 0, 0);
+ }
+ }
+}
+
+int QGraphicsItemPrivate::children_count(QDeclarativeListProperty<QGraphicsObject> *list)
+{
+ QGraphicsItemPrivate *d = QGraphicsItemPrivate::get(static_cast<QGraphicsObject *>(list->object));
+ return d->children.count();
+}
+
+QGraphicsObject *QGraphicsItemPrivate::children_at(QDeclarativeListProperty<QGraphicsObject> *list, int index)
+{
+ QGraphicsItemPrivate *d = QGraphicsItemPrivate::get(static_cast<QGraphicsObject *>(list->object));
+ if (index >= 0 && index < d->children.count())
+ return d->children.at(index)->toGraphicsObject();
+ else
+ return 0;
+}
+
+void QGraphicsItemPrivate::children_clear(QDeclarativeListProperty<QGraphicsObject> *list)
+{
+ QGraphicsItemPrivate *d = QGraphicsItemPrivate::get(static_cast<QGraphicsObject *>(list->object));
+ int childCount = d->children.count();
+ if (d->sendParentChangeNotification) {
+ for (int index = 0; index < childCount; index++)
+ d->children.at(0)->setParentItem(0);
+ } else {
+ for (int index = 0; index < childCount; index++)
+ QGraphicsItemPrivate::get(d->children.at(0))->setParentItemHelper(0, 0, 0);
+ }
+}
+
+/*!
+ Returns a list of this item's children.
+
+ The items are sorted by stacking order. This takes into account both the
+ items' insertion order and their Z-values.
+
+*/
+QDeclarativeListProperty<QGraphicsObject> QGraphicsItemPrivate::childrenList()
+{
+ Q_Q(QGraphicsItem);
+ if (isObject) {
+ QGraphicsObject *that = static_cast<QGraphicsObject *>(q);
+ return QDeclarativeListProperty<QGraphicsObject>(that, &children, children_append,
+ children_count, children_at, children_clear);
+ } else {
+ //QGraphicsItem is not supported for this property
+ return QDeclarativeListProperty<QGraphicsObject>();
+ }
+}
+
+/*!
+ \internal
+ Returns the width of the item
+ Reimplemented by QGraphicsWidget
+*/
+qreal QGraphicsItemPrivate::width() const
+{
+ return 0;
+}
+
+/*!
+ \internal
+ Set the width of the item
+ Reimplemented by QGraphicsWidget
+*/
+void QGraphicsItemPrivate::setWidth(qreal w)
+{
+ Q_UNUSED(w);
+}
+
+/*!
+ \internal
+ Reset the width of the item
+ Reimplemented by QGraphicsWidget
+*/
+void QGraphicsItemPrivate::resetWidth()
+{
+}
+
+/*!
+ \internal
+ Returns the height of the item
+ Reimplemented by QGraphicsWidget
+*/
+qreal QGraphicsItemPrivate::height() const
+{
+ return 0;
+}
+
+/*!
+ \internal
+ Set the height of the item
+ Reimplemented by QGraphicsWidget
+*/
+void QGraphicsItemPrivate::setHeight(qreal h)
+{
+ Q_UNUSED(h);
+}
+
+/*!
+ \internal
+ Reset the height of the item
+ Reimplemented by QGraphicsWidget
+*/
+void QGraphicsItemPrivate::resetHeight()
+{
+}
+
+/*!
+ \property QGraphicsObject::children
+ \since 4.7
+ \internal
+*/
+
+/*!
+ \property QGraphicsObject::width
+ \since 4.7
+ \internal
+*/
+
+/*!
+ \property QGraphicsObject::height
+ \since 4.7
+ \internal
+*/
+
+/*!
+ \property QGraphicsObject::parent
+ \brief the parent of the item
+
+ \note The item's parent is set independently of the parent object returned
+ by QObject::parent().
+
+ \sa QGraphicsItem::setParentItem(), QGraphicsItem::parentObject()
+*/
+
+/*!
+ \property QGraphicsObject::opacity
+ \brief the opacity of the item
+
+ \sa QGraphicsItem::setOpacity(), QGraphicsItem::opacity()
+*/
+
+/*!
+ \fn QGraphicsObject::opacityChanged()
+
+ This signal gets emitted whenever the opacity of the item changes
+
+ \sa QGraphicsItem::opacity()
+*/
+
+/*!
+ \fn QGraphicsObject::parentChanged()
+
+ This signal gets emitted whenever the parent of the item changes
+*/
+
+/*!
+ \property QGraphicsObject::pos
+ \brief the position of the item
+
+ Describes the items position.
+
+ \sa QGraphicsItem::setPos(), QGraphicsItem::pos()
+*/
+
+/*!
+ \property QGraphicsObject::x
+ \brief the x position of the item
+
+ Describes the items x position.
+
+ \sa QGraphicsItem::setX(), setPos(), xChanged()
+*/
+
+/*!
+ \fn QGraphicsObject::xChanged()
+
+ This signal gets emitted whenever the x position of the item changes
+
+ \sa pos()
+*/
+
+/*!
+ \property QGraphicsObject::y
+ \brief the y position of the item
+
+ Describes the items y position.
+
+ \sa QGraphicsItem::setY(), setPos(), yChanged()
+*/
+
+/*!
+ \fn QGraphicsObject::yChanged()
+
+ This signal gets emitted whenever the y position of the item changes.
+
+ \sa pos()
+*/
+
+/*!
+ \property QGraphicsObject::z
+ \brief the z value of the item
+
+ Describes the items z value.
+
+ \sa QGraphicsItem::setZValue(), zValue(), zChanged()
+*/
+
+/*!
+ \fn QGraphicsObject::zChanged()
+
+ This signal gets emitted whenever the z value of the item changes.
+
+ \sa pos()
+*/
+
+/*!
+ \property QGraphicsObject::rotation
+ This property holds the rotation of the item in degrees.
+
+ This specifies how many degrees to rotate the item around its transformOrigin.
+ The default rotation is 0 degrees (i.e. not rotated at all).
+*/
+
+/*!
+ \fn QGraphicsObject::rotationChanged()
+
+ This signal gets emitted whenever the roation of the item changes.
+*/
+
+/*!
+ \property QGraphicsObject::scale
+ This property holds the scale of the item.
+
+ A scale of less than 1 means the item will be displayed smaller than
+ normal, and a scale of greater than 1 means the item will be
+ displayed larger than normal. A negative scale means the item will
+ be mirrored.
+
+ By default, items are displayed at a scale of 1 (i.e. at their
+ normal size).
+
+ Scaling is from the item's transformOrigin.
+*/
+
+/*!
+ \fn void QGraphicsObject::scaleChanged()
+
+ This signal is emitted when the scale of the item changes.
+*/
+
+
+/*!
+ \property QGraphicsObject::enabled
+ \brief whether the item is enabled or not
+
+ This property is declared in QGraphicsItem.
+
+ By default, this property is true.
+
+ \sa QGraphicsItem::isEnabled(), QGraphicsItem::setEnabled()
+ \sa QGraphicsObject::enabledChanged()
+*/
+
+/*!
+ \fn void QGraphicsObject::enabledChanged()
+
+ This signal gets emitted whenever the item get's enabled or disabled.
+
+ \sa isEnabled()
+*/
+
+/*!
+ \property QGraphicsObject::visible
+ \brief whether the item is visible or not
+
+ This property is declared in QGraphicsItem.
+
+ By default, this property is true.
+
+ \sa QGraphicsItem::isVisible(), QGraphicsItem::setVisible(), visibleChanged()
+*/
+
+/*!
+ \fn QGraphicsObject::visibleChanged()
+
+ This signal gets emitted whenever the visibility of the item changes
+
+ \sa visible
+*/
+
+/*!
+ \fn const QObjectList &QGraphicsObject::children() const
+ \internal
+
+ This function returns the same value as QObject::children(). It's
+ provided to differentiate between the obsolete member
+ QGraphicsItem::children() and QObject::children(). QGraphicsItem now
+ provides childItems() instead.
+*/
+
+/*!
+ \property QGraphicsObject::transformOriginPoint
+ \brief the transformation origin
+
+ This property sets a specific point in the items coordiante system as the
+ origin for scale and rotation.
+
+ \sa scale, rotation, QGraphicsItem::transformOriginPoint()
+*/
+
+/*!
+ \fn void QGraphicsObject::widthChanged()
+ \internal
+*/
+
+/*!
+ \fn void QGraphicsObject::heightChanged()
+ \internal
+*/
+
+/*!
+
+ \fn QGraphicsObject::childrenChanged()
+
+ This signal gets emitted whenever the children list changes
+ \internal
+*/
+
+/*!
+ \property QGraphicsObject::effect
+ \since 4.7
+ \brief the effect attached to this item
+
+ \sa QGraphicsItem::setGraphicsEffect(), QGraphicsItem::graphicsEffect()
+*/
+
+/*!
+ \class QAbstractGraphicsShapeItem
+ \brief The QAbstractGraphicsShapeItem class provides a common base for
+ all path items.
+ \since 4.2
+ \ingroup graphicsview-api
+
+ This class does not fully implement an item by itself; in particular, it
+ does not implement boundingRect() and paint(), which are inherited by
+ QGraphicsItem.
+
+ You can subclass this item to provide a simple base implementation of
+ accessors for the item's pen and brush.
+
+ \sa QGraphicsRectItem, QGraphicsEllipseItem, QGraphicsPathItem,
+ QGraphicsPolygonItem, QGraphicsTextItem, QGraphicsLineItem,
+ QGraphicsPixmapItem, {Graphics View Framework}
+*/
+
+class QAbstractGraphicsShapeItemPrivate : public QGraphicsItemPrivate
+{
+ Q_DECLARE_PUBLIC(QAbstractGraphicsShapeItem)
+public:
+
+ QBrush brush;
+ QPen pen;
+
+ // Cached bounding rectangle
+ mutable QRectF boundingRect;
+};
+
+/*!
+ Constructs a QAbstractGraphicsShapeItem. \a parent is passed to
+ QGraphicsItem's constructor.
+*/
+QAbstractGraphicsShapeItem::QAbstractGraphicsShapeItem(QGraphicsItem *parent
+#ifndef Q_QDOC
+ // obsolete argument
+ , QGraphicsScene *scene
+#endif
+ )
+ : QGraphicsItem(*new QAbstractGraphicsShapeItemPrivate, parent, scene)
+{
+}
+
+/*!
+ \internal
+*/
+QAbstractGraphicsShapeItem::QAbstractGraphicsShapeItem(QAbstractGraphicsShapeItemPrivate &dd,
+ QGraphicsItem *parent,
+ QGraphicsScene *scene)
+ : QGraphicsItem(dd, parent, scene)
+{
+}
+
+/*!
+ Destroys a QAbstractGraphicsShapeItem.
+*/
+QAbstractGraphicsShapeItem::~QAbstractGraphicsShapeItem()
+{
+}
+
+/*!
+ Returns the item's pen. If no pen has been set, this function returns
+ QPen(), a default black solid line pen with 0 width.
+*/
+QPen QAbstractGraphicsShapeItem::pen() const
+{
+ Q_D(const QAbstractGraphicsShapeItem);
+ return d->pen;
+}
+
+/*!
+ Sets the pen for this item to \a pen.
+
+ The pen is used to draw the item's outline.
+
+ \sa pen()
+*/
+void QAbstractGraphicsShapeItem::setPen(const QPen &pen)
+{
+ Q_D(QAbstractGraphicsShapeItem);
+ if (d->pen == pen)
+ return;
+ prepareGeometryChange();
+ d->pen = pen;
+ d->boundingRect = QRectF();
+ update();
+}
+
+/*!
+ Returns the item's brush, or an empty brush if no brush has been set.
+
+ \sa setBrush()
+*/
+QBrush QAbstractGraphicsShapeItem::brush() const
+{
+ Q_D(const QAbstractGraphicsShapeItem);
+ return d->brush;
+}
+
+/*!
+ Sets the item's brush to \a brush.
+
+ The item's brush is used to fill the item.
+
+ If you use a brush with a QGradient, the gradient
+ is relative to the item's coordinate system.
+
+ \sa brush()
+*/
+void QAbstractGraphicsShapeItem::setBrush(const QBrush &brush)
+{
+ Q_D(QAbstractGraphicsShapeItem);
+ if (d->brush == brush)
+ return;
+ d->brush = brush;
+ update();
+}
+
+/*!
+ \reimp
+*/
+bool QAbstractGraphicsShapeItem::isObscuredBy(const QGraphicsItem *item) const
+{
+ return QGraphicsItem::isObscuredBy(item);
+}
+
+/*!
+ \reimp
+*/
+QPainterPath QAbstractGraphicsShapeItem::opaqueArea() const
+{
+ Q_D(const QAbstractGraphicsShapeItem);
+ if (d->brush.isOpaque())
+ return isClipped() ? clipPath() : shape();
+ return QGraphicsItem::opaqueArea();
+}
+
+/*!
+ \class QGraphicsPathItem
+ \brief The QGraphicsPathItem class provides a path item that you
+ can add to a QGraphicsScene.
+ \since 4.2
+ \ingroup graphicsview-api
+
+ To set the item's path, pass a QPainterPath to QGraphicsPathItem's
+ constructor, or call the setPath() function. The path() function
+ returns the current path.
+
+ \image graphicsview-pathitem.png
+
+ QGraphicsPathItem uses the path to provide a reasonable
+ implementation of boundingRect(), shape(), and contains(). The
+ paint() function draws the path using the item's associated pen
+ and brush, which you can set by calling the setPen() and
+ setBrush() functions.
+
+ \sa QGraphicsRectItem, QGraphicsEllipseItem, QGraphicsPolygonItem,
+ QGraphicsTextItem, QGraphicsLineItem, QGraphicsPixmapItem, {Graphics
+ View Framework}
+*/
+
+class QGraphicsPathItemPrivate : public QAbstractGraphicsShapeItemPrivate
+{
+ Q_DECLARE_PUBLIC(QGraphicsPathItem)
+public:
+ QPainterPath path;
+};
+
+/*!
+ Constructs a QGraphicsPath item using \a path as the default path. \a
+ parent is passed to QAbstractGraphicsShapeItem's constructor.
+
+ \sa QGraphicsScene::addItem()
+*/
+QGraphicsPathItem::QGraphicsPathItem(const QPainterPath &path,
+ QGraphicsItem *parent
+#ifndef Q_QDOC
+ // obsolete argument
+ , QGraphicsScene *scene
+#endif
+ )
+ : QAbstractGraphicsShapeItem(*new QGraphicsPathItemPrivate, parent, scene)
+{
+ if (!path.isEmpty())
+ setPath(path);
+}
+
+/*!
+ Constructs a QGraphicsPath. \a parent is passed to
+ QAbstractGraphicsShapeItem's constructor.
+
+ \sa QGraphicsScene::addItem()
+*/
+QGraphicsPathItem::QGraphicsPathItem(QGraphicsItem *parent
+#ifndef Q_QDOC
+ // obsolete argument
+ , QGraphicsScene *scene
+#endif
+ )
+ : QAbstractGraphicsShapeItem(*new QGraphicsPathItemPrivate, parent, scene)
+{
+}
+
+/*!
+ Destroys the QGraphicsPathItem.
+*/
+QGraphicsPathItem::~QGraphicsPathItem()
+{
+}
+
+/*!
+ Returns the item's path as a QPainterPath. If no item has been set, an
+ empty QPainterPath is returned.
+
+ \sa setPath()
+*/
+QPainterPath QGraphicsPathItem::path() const
+{
+ Q_D(const QGraphicsPathItem);
+ return d->path;
+}
+
+/*!
+ Sets the item's path to be the given \a path.
+
+ \sa path()
+*/
+void QGraphicsPathItem::setPath(const QPainterPath &path)
+{
+ Q_D(QGraphicsPathItem);
+ if (d->path == path)
+ return;
+ prepareGeometryChange();
+ d->path = path;
+ d->boundingRect = QRectF();
+ update();
+}
+
+/*!
+ \reimp
+*/
+QRectF QGraphicsPathItem::boundingRect() const
+{
+ Q_D(const QGraphicsPathItem);
+ if (d->boundingRect.isNull()) {
+ qreal pw = pen().widthF();
+ if (pw == 0.0)
+ d->boundingRect = d->path.controlPointRect();
+ else {
+ d->boundingRect = shape().controlPointRect();
+ }
+ }
+ return d->boundingRect;
+}
+
+/*!
+ \reimp
+*/
+QPainterPath QGraphicsPathItem::shape() const
+{
+ Q_D(const QGraphicsPathItem);
+ return qt_graphicsItem_shapeFromPath(d->path, d->pen);
+}
+
+/*!
+ \reimp
+*/
+bool QGraphicsPathItem::contains(const QPointF &point) const
+{
+ return QAbstractGraphicsShapeItem::contains(point);
+}
+
+/*!
+ \reimp
+*/
+void QGraphicsPathItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option,
+ QWidget *widget)
+{
+ Q_D(QGraphicsPathItem);
+ Q_UNUSED(widget);
+ painter->setPen(d->pen);
+ painter->setBrush(d->brush);
+ painter->drawPath(d->path);
+
+ if (option->state & QStyle::State_Selected)
+ qt_graphicsItem_highlightSelected(this, painter, option);
+}
+
+/*!
+ \reimp
+*/
+bool QGraphicsPathItem::isObscuredBy(const QGraphicsItem *item) const
+{
+ return QAbstractGraphicsShapeItem::isObscuredBy(item);
+}
+
+/*!
+ \reimp
+*/
+QPainterPath QGraphicsPathItem::opaqueArea() const
+{
+ return QAbstractGraphicsShapeItem::opaqueArea();
+}
+
+/*!
+ \reimp
+*/
+int QGraphicsPathItem::type() const
+{
+ return Type;
+}
+
+/*!
+ \internal
+*/
+bool QGraphicsPathItem::supportsExtension(Extension extension) const
+{
+ Q_UNUSED(extension);
+ return false;
+}
+
+/*!
+ \internal
+*/
+void QGraphicsPathItem::setExtension(Extension extension, const QVariant &variant)
+{
+ Q_UNUSED(extension);
+ Q_UNUSED(variant);
+}
+
+/*!
+ \internal
+*/
+QVariant QGraphicsPathItem::extension(const QVariant &variant) const
+{
+ Q_UNUSED(variant);
+ return QVariant();
+}
+
+/*!
+ \class QGraphicsRectItem
+ \brief The QGraphicsRectItem class provides a rectangle item that you
+ can add to a QGraphicsScene.
+ \since 4.2
+ \ingroup graphicsview-api
+
+ To set the item's rectangle, pass a QRectF to QGraphicsRectItem's
+ constructor, or call the setRect() function. The rect() function
+ returns the current rectangle.
+
+ \image graphicsview-rectitem.png
+
+ QGraphicsRectItem uses the rectangle and the pen width to provide
+ a reasonable implementation of boundingRect(), shape(), and
+ contains(). The paint() function draws the rectangle using the
+ item's associated pen and brush, which you can set by calling the
+ setPen() and setBrush() functions.
+
+ \note The rendering of invalid rectangles, such as those with negative
+ widths or heights, is undefined. If you cannot be sure that you are
+ using valid rectangles (for example, if you are creating
+ rectangles using data from an unreliable source) then you should
+ use QRectF::normalized() to create normalized rectangles, and use
+ those instead.
+
+ \sa QGraphicsPathItem, QGraphicsEllipseItem, QGraphicsPolygonItem,
+ QGraphicsTextItem, QGraphicsLineItem, QGraphicsPixmapItem, {Graphics
+ View Framework}
+*/
+
+class QGraphicsRectItemPrivate : public QAbstractGraphicsShapeItemPrivate
+{
+ Q_DECLARE_PUBLIC(QGraphicsRectItem)
+public:
+ QRectF rect;
+};
+
+/*!
+ Constructs a QGraphicsRectItem, using \a rect as the default rectangle.
+ \a parent is passed to QAbstractGraphicsShapeItem's constructor.
+
+ \sa QGraphicsScene::addItem()
+*/
+QGraphicsRectItem::QGraphicsRectItem(const QRectF &rect, QGraphicsItem *parent
+#ifndef Q_QDOC
+ // obsolete argument
+ , QGraphicsScene *scene
+#endif
+ )
+ : QAbstractGraphicsShapeItem(*new QGraphicsRectItemPrivate, parent, scene)
+{
+ setRect(rect);
+}
+
+/*!
+ \fn QGraphicsRectItem::QGraphicsRectItem(qreal x, qreal y, qreal width, qreal height,
+ QGraphicsItem *parent)
+
+ Constructs a QGraphicsRectItem with a default rectangle defined
+ by (\a x, \a y) and the given \a width and \a height.
+
+ \a parent is passed to QAbstractGraphicsShapeItem's constructor.
+
+ \sa QGraphicsScene::addItem()
+*/
+QGraphicsRectItem::QGraphicsRectItem(qreal x, qreal y, qreal w, qreal h,
+ QGraphicsItem *parent
+#ifndef Q_QDOC
+ // obsolete argument
+ , QGraphicsScene *scene
+#endif
+ )
+ : QAbstractGraphicsShapeItem(*new QGraphicsRectItemPrivate, parent, scene)
+{
+ setRect(QRectF(x, y, w, h));
+}
+
+/*!
+ Constructs a QGraphicsRectItem. \a parent is passed to
+ QAbstractGraphicsShapeItem's constructor.
+
+ \sa QGraphicsScene::addItem()
+*/
+QGraphicsRectItem::QGraphicsRectItem(QGraphicsItem *parent
+#ifndef Q_QDOC
+ // obsolete argument
+ , QGraphicsScene *scene
+#endif
+ )
+ : QAbstractGraphicsShapeItem(*new QGraphicsRectItemPrivate, parent, scene)
+{
+}
+
+/*!
+ Destroys the QGraphicsRectItem.
+*/
+QGraphicsRectItem::~QGraphicsRectItem()
+{
+}
+
+/*!
+ Returns the item's rectangle.
+
+ \sa setRect()
+*/
+QRectF QGraphicsRectItem::rect() const
+{
+ Q_D(const QGraphicsRectItem);
+ return d->rect;
+}
+
+/*!
+ \fn void QGraphicsRectItem::setRect(const QRectF &rectangle)
+
+ Sets the item's rectangle to be the given \a rectangle.
+
+ \sa rect()
+*/
+void QGraphicsRectItem::setRect(const QRectF &rect)
+{
+ Q_D(QGraphicsRectItem);
+ if (d->rect == rect)
+ return;
+ prepareGeometryChange();
+ d->rect = rect;
+ d->boundingRect = QRectF();
+ update();
+}
+
+/*!
+ \fn void QGraphicsRectItem::setRect(qreal x, qreal y, qreal width, qreal height)
+ \fn void QGraphicsEllipseItem::setRect(qreal x, qreal y, qreal width, qreal height)
+
+ Sets the item's rectangle to the rectangle defined by (\a x, \a y)
+ and the given \a width and \a height.
+
+ This convenience function is equivalent to calling \c
+ {setRect(QRectF(x, y, width, height))}
+
+ \sa rect()
+*/
+
+/*!
+ \reimp
+*/
+QRectF QGraphicsRectItem::boundingRect() const
+{
+ Q_D(const QGraphicsRectItem);
+ if (d->boundingRect.isNull()) {
+ qreal halfpw = pen().widthF() / 2;
+ d->boundingRect = d->rect;
+ if (halfpw > 0.0)
+ d->boundingRect.adjust(-halfpw, -halfpw, halfpw, halfpw);
+ }
+ return d->boundingRect;
+}
+
+/*!
+ \reimp
+*/
+QPainterPath QGraphicsRectItem::shape() const
+{
+ Q_D(const QGraphicsRectItem);
+ QPainterPath path;
+ path.addRect(d->rect);
+ return qt_graphicsItem_shapeFromPath(path, d->pen);
+}
+
+/*!
+ \reimp
+*/
+bool QGraphicsRectItem::contains(const QPointF &point) const
+{
+ return QAbstractGraphicsShapeItem::contains(point);
+}
+
+/*!
+ \reimp
+*/
+void QGraphicsRectItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option,
+ QWidget *widget)
+{
+ Q_D(QGraphicsRectItem);
+ Q_UNUSED(widget);
+ painter->setPen(d->pen);
+ painter->setBrush(d->brush);
+ painter->drawRect(d->rect);
+
+ if (option->state & QStyle::State_Selected)
+ qt_graphicsItem_highlightSelected(this, painter, option);
+}
+
+/*!
+ \reimp
+*/
+bool QGraphicsRectItem::isObscuredBy(const QGraphicsItem *item) const
+{
+ return QAbstractGraphicsShapeItem::isObscuredBy(item);
+}
+
+/*!
+ \reimp
+*/
+QPainterPath QGraphicsRectItem::opaqueArea() const
+{
+ return QAbstractGraphicsShapeItem::opaqueArea();
+}
+
+/*!
+ \reimp
+*/
+int QGraphicsRectItem::type() const
+{
+ return Type;
+}
+
+/*!
+ \internal
+*/
+bool QGraphicsRectItem::supportsExtension(Extension extension) const
+{
+ Q_UNUSED(extension);
+ return false;
+}
+
+/*!
+ \internal
+*/
+void QGraphicsRectItem::setExtension(Extension extension, const QVariant &variant)
+{
+ Q_UNUSED(extension);
+ Q_UNUSED(variant);
+}
+
+/*!
+ \internal
+*/
+QVariant QGraphicsRectItem::extension(const QVariant &variant) const
+{
+ Q_UNUSED(variant);
+ return QVariant();
+}
+
+/*!
+ \class QGraphicsEllipseItem
+ \brief The QGraphicsEllipseItem class provides an ellipse item that you
+ can add to a QGraphicsScene.
+ \since 4.2
+ \ingroup graphicsview-api
+
+ QGraphicsEllipseItem respresents an ellipse with a fill and an outline,
+ and you can also use it for ellipse segments (see startAngle(),
+ spanAngle()).
+
+ \table
+ \row
+ \o \inlineimage graphicsview-ellipseitem.png
+ \o \inlineimage graphicsview-ellipseitem-pie.png
+ \endtable
+
+ To set the item's ellipse, pass a QRectF to QGraphicsEllipseItem's
+ constructor, or call setRect(). The rect() function returns the
+ current ellipse geometry.
+
+ QGraphicsEllipseItem uses the rect and the pen width to provide a
+ reasonable implementation of boundingRect(), shape(), and contains(). The
+ paint() function draws the ellipse using the item's associated pen and
+ brush, which you can set by calling setPen() and setBrush().
+
+ \sa QGraphicsPathItem, QGraphicsRectItem, QGraphicsPolygonItem,
+ QGraphicsTextItem, QGraphicsLineItem, QGraphicsPixmapItem, {Graphics
+ View Framework}
+*/
+
+class QGraphicsEllipseItemPrivate : public QAbstractGraphicsShapeItemPrivate
+{
+ Q_DECLARE_PUBLIC(QGraphicsEllipseItem)
+public:
+ inline QGraphicsEllipseItemPrivate()
+ : startAngle(0), spanAngle(360 * 16)
+ { }
+
+ QRectF rect;
+ int startAngle;
+ int spanAngle;
+};
+
+/*!
+ Constructs a QGraphicsEllipseItem using \a rect as the default rectangle.
+ \a parent is passed to QAbstractGraphicsShapeItem's constructor.
+
+ \sa QGraphicsScene::addItem()
+*/
+QGraphicsEllipseItem::QGraphicsEllipseItem(const QRectF &rect, QGraphicsItem *parent
+#ifndef Q_QDOC
+ // obsolete argument
+ , QGraphicsScene *scene
+#endif
+ )
+ : QAbstractGraphicsShapeItem(*new QGraphicsEllipseItemPrivate, parent, scene)
+{
+ setRect(rect);
+}
+
+/*!
+ \fn QGraphicsEllipseItem::QGraphicsEllipseItem(qreal x, qreal y, qreal width, qreal height, QGraphicsItem *parent)
+ \since 4.3
+
+ Constructs a QGraphicsEllipseItem using the rectangle defined by (\a x, \a
+ y) and the given \a width and \a height, as the default rectangle. \a
+ parent is passed to QAbstractGraphicsShapeItem's constructor.
+
+ \sa QGraphicsScene::addItem()
+*/
+QGraphicsEllipseItem::QGraphicsEllipseItem(qreal x, qreal y, qreal w, qreal h,
+ QGraphicsItem *parent
+#ifndef Q_QDOC
+ // obsolete argument
+ , QGraphicsScene *scene
+#endif
+ )
+ : QAbstractGraphicsShapeItem(*new QGraphicsEllipseItemPrivate, parent, scene)
+{
+ setRect(x,y,w,h);
+}
+
+
+
+/*!
+ Constructs a QGraphicsEllipseItem. \a parent is passed to
+ QAbstractGraphicsShapeItem's constructor.
+
+ \sa QGraphicsScene::addItem()
+*/
+QGraphicsEllipseItem::QGraphicsEllipseItem(QGraphicsItem *parent
+#ifndef Q_QDOC
+ // obsolete argument
+ , QGraphicsScene *scene
+#endif
+ )
+ : QAbstractGraphicsShapeItem(*new QGraphicsEllipseItemPrivate, parent, scene)
+{
+}
+
+/*!
+ Destroys the QGraphicsEllipseItem.
+*/
+QGraphicsEllipseItem::~QGraphicsEllipseItem()
+{
+}
+
+/*!
+ Returns the item's ellipse geometry as a QRectF.
+
+ \sa setRect(), QPainter::drawEllipse()
+*/
+QRectF QGraphicsEllipseItem::rect() const
+{
+ Q_D(const QGraphicsEllipseItem);
+ return d->rect;
+}
+
+/*!
+ Sets the item's ellipse geometry to \a rect. The rectangle's left edge
+ defines the left edge of the ellipse, and the rectangle's top edge
+ describes the top of the ellipse. The height and width of the rectangle
+ describe the height and width of the ellipse.
+
+ \sa rect(), QPainter::drawEllipse()
+*/
+void QGraphicsEllipseItem::setRect(const QRectF &rect)
+{
+ Q_D(QGraphicsEllipseItem);
+ if (d->rect == rect)
+ return;
+ prepareGeometryChange();
+ d->rect = rect;
+ d->boundingRect = QRectF();
+ update();
+}
+
+/*!
+ Returns the start angle for an ellipse segment in 16ths of a degree. This
+ angle is used together with spanAngle() for representing an ellipse
+ segment (a pie). By default, the start angle is 0.
+
+ \sa setStartAngle(), spanAngle()
+*/
+int QGraphicsEllipseItem::startAngle() const
+{
+ Q_D(const QGraphicsEllipseItem);
+ return d->startAngle;
+}
+
+/*!
+ Sets the start angle for an ellipse segment to \a angle, which is in 16ths
+ of a degree. This angle is used together with spanAngle() for representing
+ an ellipse segment (a pie). By default, the start angle is 0.
+
+ \sa startAngle(), setSpanAngle(), QPainter::drawPie()
+*/
+void QGraphicsEllipseItem::setStartAngle(int angle)
+{
+ Q_D(QGraphicsEllipseItem);
+ if (angle != d->startAngle) {
+ prepareGeometryChange();
+ d->boundingRect = QRectF();
+ d->startAngle = angle;
+ update();
+ }
+}
+
+/*!
+ Returns the span angle of an ellipse segment in 16ths of a degree. This
+ angle is used together with startAngle() for representing an ellipse
+ segment (a pie). By default, this function returns 5760 (360 * 16, a full
+ ellipse).
+
+ \sa setSpanAngle(), startAngle()
+*/
+int QGraphicsEllipseItem::spanAngle() const
+{
+ Q_D(const QGraphicsEllipseItem);
+ return d->spanAngle;
+}
+
+/*!
+ Sets the span angle for an ellipse segment to \a angle, which is in 16ths
+ of a degree. This angle is used together with startAngle() to represent an
+ ellipse segment (a pie). By default, the span angle is 5760 (360 * 16, a
+ full ellipse).
+
+ \sa spanAngle(), setStartAngle(), QPainter::drawPie()
+*/
+void QGraphicsEllipseItem::setSpanAngle(int angle)
+{
+ Q_D(QGraphicsEllipseItem);
+ if (angle != d->spanAngle) {
+ prepareGeometryChange();
+ d->boundingRect = QRectF();
+ d->spanAngle = angle;
+ update();
+ }
+}
+
+/*!
+ \reimp
+*/
+QRectF QGraphicsEllipseItem::boundingRect() const
+{
+ Q_D(const QGraphicsEllipseItem);
+ if (d->boundingRect.isNull()) {
+ qreal pw = pen().widthF();
+ if (pw == 0.0 && d->spanAngle == 360 * 16)
+ d->boundingRect = d->rect;
+ else
+ d->boundingRect = shape().controlPointRect();
+ }
+ return d->boundingRect;
+}
+
+/*!
+ \reimp
+*/
+QPainterPath QGraphicsEllipseItem::shape() const
+{
+ Q_D(const QGraphicsEllipseItem);
+ QPainterPath path;
+ if (d->rect.isNull())
+ return path;
+ if (d->spanAngle != 360 * 16) {
+ path.moveTo(d->rect.center());
+ path.arcTo(d->rect, d->startAngle / 16.0, d->spanAngle / 16.0);
+ } else {
+ path.addEllipse(d->rect);
+ }
+
+ return qt_graphicsItem_shapeFromPath(path, d->pen);
+}
+
+/*!
+ \reimp
+*/
+bool QGraphicsEllipseItem::contains(const QPointF &point) const
+{
+ return QAbstractGraphicsShapeItem::contains(point);
+}
+
+/*!
+ \reimp
+*/
+void QGraphicsEllipseItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option,
+ QWidget *widget)
+{
+ Q_D(QGraphicsEllipseItem);
+ Q_UNUSED(widget);
+ painter->setPen(d->pen);
+ painter->setBrush(d->brush);
+ if ((d->spanAngle != 0) && (qAbs(d->spanAngle) % (360 * 16) == 0))
+ painter->drawEllipse(d->rect);
+ else
+ painter->drawPie(d->rect, d->startAngle, d->spanAngle);
+
+ if (option->state & QStyle::State_Selected)
+ qt_graphicsItem_highlightSelected(this, painter, option);
+}
+
+/*!
+ \reimp
+*/
+bool QGraphicsEllipseItem::isObscuredBy(const QGraphicsItem *item) const
+{
+ return QAbstractGraphicsShapeItem::isObscuredBy(item);
+}
+
+/*!
+ \reimp
+*/
+QPainterPath QGraphicsEllipseItem::opaqueArea() const
+{
+ return QAbstractGraphicsShapeItem::opaqueArea();
+}
+
+/*!
+ \reimp
+*/
+int QGraphicsEllipseItem::type() const
+{
+ return Type;
+}
+
+
+/*!
+ \internal
+*/
+bool QGraphicsEllipseItem::supportsExtension(Extension extension) const
+{
+ Q_UNUSED(extension);
+ return false;
+}
+
+/*!
+ \internal
+*/
+void QGraphicsEllipseItem::setExtension(Extension extension, const QVariant &variant)
+{
+ Q_UNUSED(extension);
+ Q_UNUSED(variant);
+}
+
+/*!
+ \internal
+*/
+QVariant QGraphicsEllipseItem::extension(const QVariant &variant) const
+{
+ Q_UNUSED(variant);
+ return QVariant();
+}
+
+/*!
+ \class QGraphicsPolygonItem
+ \brief The QGraphicsPolygonItem class provides a polygon item that you
+ can add to a QGraphicsScene.
+ \since 4.2
+ \ingroup graphicsview-api
+
+ To set the item's polygon, pass a QPolygonF to
+ QGraphicsPolygonItem's constructor, or call the setPolygon()
+ function. The polygon() function returns the current polygon.
+
+ \image graphicsview-polygonitem.png
+
+ QGraphicsPolygonItem uses the polygon and the pen width to provide
+ a reasonable implementation of boundingRect(), shape(), and
+ contains(). The paint() function draws the polygon using the
+ item's associated pen and brush, which you can set by calling the
+ setPen() and setBrush() functions.
+
+ \sa QGraphicsPathItem, QGraphicsRectItem, QGraphicsEllipseItem,
+ QGraphicsTextItem, QGraphicsLineItem, QGraphicsPixmapItem, {Graphics
+ View Framework}
+*/
+
+class QGraphicsPolygonItemPrivate : public QAbstractGraphicsShapeItemPrivate
+{
+ Q_DECLARE_PUBLIC(QGraphicsPolygonItem)
+public:
+ inline QGraphicsPolygonItemPrivate()
+ : fillRule(Qt::OddEvenFill)
+ { }
+
+ QPolygonF polygon;
+ Qt::FillRule fillRule;
+};
+
+/*!
+ Constructs a QGraphicsPolygonItem with \a polygon as the default
+ polygon. \a parent is passed to QAbstractGraphicsShapeItem's constructor.
+
+ \sa QGraphicsScene::addItem()
+*/
+QGraphicsPolygonItem::QGraphicsPolygonItem(const QPolygonF &polygon,
+ QGraphicsItem *parent
+#ifndef Q_QDOC
+ // obsolete argument
+ , QGraphicsScene *scene
+#endif
+ )
+ : QAbstractGraphicsShapeItem(*new QGraphicsPolygonItemPrivate, parent, scene)
+{
+ setPolygon(polygon);
+}
+
+/*!
+ Constructs a QGraphicsPolygonItem. \a parent is passed to
+ QAbstractGraphicsShapeItem's constructor.
+
+ \sa QGraphicsScene::addItem()
+*/
+QGraphicsPolygonItem::QGraphicsPolygonItem(QGraphicsItem *parent
+#ifndef Q_QDOC
+ // obsolete argument
+ , QGraphicsScene *scene
+#endif
+ )
+ : QAbstractGraphicsShapeItem(*new QGraphicsPolygonItemPrivate, parent, scene)
+{
+}
+
+/*!
+ Destroys the QGraphicsPolygonItem.
+*/
+QGraphicsPolygonItem::~QGraphicsPolygonItem()
+{
+}
+
+/*!
+ Returns the item's polygon, or an empty polygon if no polygon
+ has been set.
+
+ \sa setPolygon()
+*/
+QPolygonF QGraphicsPolygonItem::polygon() const
+{
+ Q_D(const QGraphicsPolygonItem);
+ return d->polygon;
+}
+
+/*!
+ Sets the item's polygon to be the given \a polygon.
+
+ \sa polygon()
+*/
+void QGraphicsPolygonItem::setPolygon(const QPolygonF &polygon)
+{
+ Q_D(QGraphicsPolygonItem);
+ if (d->polygon == polygon)
+ return;
+ prepareGeometryChange();
+ d->polygon = polygon;
+ d->boundingRect = QRectF();
+ update();
+}
+
+/*!
+ Returns the fill rule of the polygon. The default fill rule is
+ Qt::OddEvenFill.
+
+ \sa setFillRule(), QPainterPath::fillRule(), QPainter::drawPolygon()
+*/
+Qt::FillRule QGraphicsPolygonItem::fillRule() const
+{
+ Q_D(const QGraphicsPolygonItem);
+ return d->fillRule;
+}
+
+/*!
+ Sets the fill rule of the polygon to \a rule. The default fill rule is
+ Qt::OddEvenFill.
+
+ \sa fillRule(), QPainterPath::fillRule(), QPainter::drawPolygon()
+*/
+void QGraphicsPolygonItem::setFillRule(Qt::FillRule rule)
+{
+ Q_D(QGraphicsPolygonItem);
+ if (rule != d->fillRule) {
+ d->fillRule = rule;
+ update();
+ }
+}
+
+/*!
+ \reimp
+*/
+QRectF QGraphicsPolygonItem::boundingRect() const
+{
+ Q_D(const QGraphicsPolygonItem);
+ if (d->boundingRect.isNull()) {
+ qreal pw = pen().widthF();
+ if (pw == 0.0)
+ d->boundingRect = d->polygon.boundingRect();
+ else
+ d->boundingRect = shape().controlPointRect();
+ }
+ return d->boundingRect;
+}
+
+/*!
+ \reimp
+*/
+QPainterPath QGraphicsPolygonItem::shape() const
+{
+ Q_D(const QGraphicsPolygonItem);
+ QPainterPath path;
+ path.addPolygon(d->polygon);
+ return qt_graphicsItem_shapeFromPath(path, d->pen);
+}
+
+/*!
+ \reimp
+*/
+bool QGraphicsPolygonItem::contains(const QPointF &point) const
+{
+ return QAbstractGraphicsShapeItem::contains(point);
+}
+
+/*!
+ \reimp
+*/
+void QGraphicsPolygonItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
+{
+ Q_D(QGraphicsPolygonItem);
+ Q_UNUSED(widget);
+ painter->setPen(d->pen);
+ painter->setBrush(d->brush);
+ painter->drawPolygon(d->polygon, d->fillRule);
+
+ if (option->state & QStyle::State_Selected)
+ qt_graphicsItem_highlightSelected(this, painter, option);
+}
+
+/*!
+ \reimp
+*/
+bool QGraphicsPolygonItem::isObscuredBy(const QGraphicsItem *item) const
+{
+ return QAbstractGraphicsShapeItem::isObscuredBy(item);
+}
+
+/*!
+ \reimp
+*/
+QPainterPath QGraphicsPolygonItem::opaqueArea() const
+{
+ return QAbstractGraphicsShapeItem::opaqueArea();
+}
+
+/*!
+ \reimp
+*/
+int QGraphicsPolygonItem::type() const
+{
+ return Type;
+}
+
+/*!
+ \internal
+*/
+bool QGraphicsPolygonItem::supportsExtension(Extension extension) const
+{
+ Q_UNUSED(extension);
+ return false;
+}
+
+/*!
+ \internal
+*/
+void QGraphicsPolygonItem::setExtension(Extension extension, const QVariant &variant)
+{
+ Q_UNUSED(extension);
+ Q_UNUSED(variant);
+}
+
+/*!
+ \internal
+*/
+QVariant QGraphicsPolygonItem::extension(const QVariant &variant) const
+{
+ Q_UNUSED(variant);
+ return QVariant();
+}
+
+/*!
+ \class QGraphicsLineItem
+ \brief The QGraphicsLineItem class provides a line item that you can add to a
+ QGraphicsScene.
+ \since 4.2
+ \ingroup graphicsview-api
+
+ To set the item's line, pass a QLineF to QGraphicsLineItem's
+ constructor, or call the setLine() function. The line() function
+ returns the current line. By default the line is black with a
+ width of 0, but you can change this by calling setPen().
+
+ \img graphicsview-lineitem.png
+
+ QGraphicsLineItem uses the line and the pen width to provide a reasonable
+ implementation of boundingRect(), shape(), and contains(). The paint()
+ function draws the line using the item's associated pen.
+
+ \sa QGraphicsPathItem, QGraphicsRectItem, QGraphicsEllipseItem,
+ QGraphicsTextItem, QGraphicsPolygonItem, QGraphicsPixmapItem,
+ {Graphics View Framework}
+*/
+
+class QGraphicsLineItemPrivate : public QGraphicsItemPrivate
+{
+ Q_DECLARE_PUBLIC(QGraphicsLineItem)
+public:
+ QLineF line;
+ QPen pen;
+};
+
+/*!
+ Constructs a QGraphicsLineItem, using \a line as the default line. \a
+ parent is passed to QGraphicsItem's constructor.
+
+ \sa QGraphicsScene::addItem()
+*/
+QGraphicsLineItem::QGraphicsLineItem(const QLineF &line, QGraphicsItem *parent
+#ifndef Q_QDOC
+ // obsolete argument
+ , QGraphicsScene *scene
+#endif
+ )
+ : QGraphicsItem(*new QGraphicsLineItemPrivate, parent, scene)
+{
+ setLine(line);
+}
+
+/*!
+ Constructs a QGraphicsLineItem, using the line between (\a x1, \a y1) and
+ (\a x2, \a y2) as the default line. \a parent is passed to
+ QGraphicsItem's constructor.
+
+ \sa QGraphicsScene::addItem()
+*/
+QGraphicsLineItem::QGraphicsLineItem(qreal x1, qreal y1, qreal x2, qreal y2, QGraphicsItem *parent
+#ifndef Q_QDOC
+ // obsolete argument
+ , QGraphicsScene *scene
+#endif
+ )
+ : QGraphicsItem(*new QGraphicsLineItemPrivate, parent, scene)
+{
+ setLine(x1, y1, x2, y2);
+}
+
+
+
+/*!
+ Constructs a QGraphicsLineItem. \a parent is passed to QGraphicsItem's
+ constructor.
+
+ \sa QGraphicsScene::addItem()
+*/
+QGraphicsLineItem::QGraphicsLineItem(QGraphicsItem *parent
+#ifndef Q_QDOC
+ // obsolete argument
+ , QGraphicsScene *scene
+#endif
+ )
+ : QGraphicsItem(*new QGraphicsLineItemPrivate, parent, scene)
+{
+}
+
+/*!
+ Destroys the QGraphicsLineItem.
+*/
+QGraphicsLineItem::~QGraphicsLineItem()
+{
+}
+
+/*!
+ Returns the item's pen, or a black solid 0-width pen if no pen has
+ been set.
+
+ \sa setPen()
+*/
+QPen QGraphicsLineItem::pen() const
+{
+ Q_D(const QGraphicsLineItem);
+ return d->pen;
+}
+
+/*!
+ Sets the item's pen to \a pen. If no pen is set, the line will be painted
+ using a black solid 0-width pen.
+
+ \sa pen()
+*/
+void QGraphicsLineItem::setPen(const QPen &pen)
+{
+ Q_D(QGraphicsLineItem);
+ if (d->pen == pen)
+ return;
+ prepareGeometryChange();
+ d->pen = pen;
+ update();
+}
+
+/*!
+ Returns the item's line, or a null line if no line has been set.
+
+ \sa setLine()
+*/
+QLineF QGraphicsLineItem::line() const
+{
+ Q_D(const QGraphicsLineItem);
+ return d->line;
+}
+
+/*!
+ Sets the item's line to be the given \a line.
+
+ \sa line()
+*/
+void QGraphicsLineItem::setLine(const QLineF &line)
+{
+ Q_D(QGraphicsLineItem);
+ if (d->line == line)
+ return;
+ prepareGeometryChange();
+ d->line = line;
+ update();
+}
+
+/*!
+ \fn void QGraphicsLineItem::setLine(qreal x1, qreal y1, qreal x2, qreal y2)
+ \overload
+
+ Sets the item's line to be the line between (\a x1, \a y1) and (\a
+ x2, \a y2).
+
+ This is the same as calling \c {setLine(QLineF(x1, y1, x2, y2))}.
+*/
+
+/*!
+ \reimp
+*/
+QRectF QGraphicsLineItem::boundingRect() const
+{
+ Q_D(const QGraphicsLineItem);
+ if (d->pen.widthF() == 0.0) {
+ const qreal x1 = d->line.p1().x();
+ const qreal x2 = d->line.p2().x();
+ const qreal y1 = d->line.p1().y();
+ const qreal y2 = d->line.p2().y();
+ qreal lx = qMin(x1, x2);
+ qreal rx = qMax(x1, x2);
+ qreal ty = qMin(y1, y2);
+ qreal by = qMax(y1, y2);
+ return QRectF(lx, ty, rx - lx, by - ty);
+ }
+ return shape().controlPointRect();
+}
+
+/*!
+ \reimp
+*/
+QPainterPath QGraphicsLineItem::shape() const
+{
+ Q_D(const QGraphicsLineItem);
+ QPainterPath path;
+ if (d->line == QLineF())
+ return path;
+
+ path.moveTo(d->line.p1());
+ path.lineTo(d->line.p2());
+ return qt_graphicsItem_shapeFromPath(path, d->pen);
+}
+
+/*!
+ \reimp
+*/
+bool QGraphicsLineItem::contains(const QPointF &point) const
+{
+ return QGraphicsItem::contains(point);
+}
+
+/*!
+ \reimp
+*/
+void QGraphicsLineItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
+{
+ Q_D(QGraphicsLineItem);
+ Q_UNUSED(widget);
+ painter->setPen(d->pen);
+ painter->drawLine(d->line);
+
+ if (option->state & QStyle::State_Selected)
+ qt_graphicsItem_highlightSelected(this, painter, option);
+}
+
+/*!
+ \reimp
+*/
+bool QGraphicsLineItem::isObscuredBy(const QGraphicsItem *item) const
+{
+ return QGraphicsItem::isObscuredBy(item);
+}
+
+/*!
+ \reimp
+*/
+QPainterPath QGraphicsLineItem::opaqueArea() const
+{
+ return QGraphicsItem::opaqueArea();
+}
+
+/*!
+ \reimp
+*/
+int QGraphicsLineItem::type() const
+{
+ return Type;
+}
+
+/*!
+ \internal
+*/
+bool QGraphicsLineItem::supportsExtension(Extension extension) const
+{
+ Q_UNUSED(extension);
+ return false;
+}
+
+/*!
+ \internal
+*/
+void QGraphicsLineItem::setExtension(Extension extension, const QVariant &variant)
+{
+ Q_UNUSED(extension);
+ Q_UNUSED(variant);
+}
+
+/*!
+ \internal
+*/
+QVariant QGraphicsLineItem::extension(const QVariant &variant) const
+{
+ Q_UNUSED(variant);
+ return QVariant();
+}
+
+/*!
+ \class QGraphicsPixmapItem
+ \brief The QGraphicsPixmapItem class provides a pixmap item that you can add to
+ a QGraphicsScene.
+ \since 4.2
+ \ingroup graphicsview-api
+
+ To set the item's pixmap, pass a QPixmap to QGraphicsPixmapItem's
+ constructor, or call the setPixmap() function. The pixmap()
+ function returns the current pixmap.
+
+ QGraphicsPixmapItem uses pixmap's optional alpha mask to provide a
+ reasonable implementation of boundingRect(), shape(), and contains().
+
+ \image graphicsview-pixmapitem.png
+
+ The pixmap is drawn at the item's (0, 0) coordinate, as returned by
+ offset(). You can change the drawing offset by calling setOffset().
+
+ You can set the pixmap's transformation mode by calling
+ setTransformationMode(). By default, Qt::FastTransformation is used, which
+ provides fast, non-smooth scaling. Qt::SmoothTransformation enables
+ QPainter::SmoothPixmapTransform on the painter, and the quality depends on
+ the platform and viewport. The result is usually not as good as calling
+ QPixmap::scale() directly. Call transformationMode() to get the current
+ transformation mode for the item.
+
+ \sa QGraphicsPathItem, QGraphicsRectItem, QGraphicsEllipseItem,
+ QGraphicsTextItem, QGraphicsPolygonItem, QGraphicsLineItem,
+ {Graphics View Framework}
+*/
+
+/*!
+ \enum QGraphicsPixmapItem::ShapeMode
+
+ This enum describes how QGraphicsPixmapItem calculates its shape and
+ opaque area.
+
+ The default value is MaskShape.
+
+ \value MaskShape The shape is determined by calling QPixmap::mask().
+ This shape includes only the opaque pixels of the pixmap.
+ Because the shape is more complex, however, it can be slower than the other modes,
+ and uses more memory.
+
+ \value BoundingRectShape The shape is determined by tracing the outline of
+ the pixmap. This is the fastest shape mode, but it does not take into account
+ any transparent areas on the pixmap.
+
+ \value HeuristicMaskShape The shape is determine by calling
+ QPixmap::createHeuristicMask(). The performance and memory consumption
+ is similar to MaskShape.
+*/
+extern QPainterPath qt_regionToPath(const QRegion &region);
+
+class QGraphicsPixmapItemPrivate : public QGraphicsItemPrivate
+{
+ Q_DECLARE_PUBLIC(QGraphicsPixmapItem)
+public:
+ QGraphicsPixmapItemPrivate()
+ : transformationMode(Qt::FastTransformation),
+ shapeMode(QGraphicsPixmapItem::MaskShape),
+ hasShape(false)
+ {}
+
+ QPixmap pixmap;
+ Qt::TransformationMode transformationMode;
+ QPointF offset;
+ QGraphicsPixmapItem::ShapeMode shapeMode;
+ QPainterPath shape;
+ bool hasShape;
+
+ void updateShape()
+ {
+ shape = QPainterPath();
+ switch (shapeMode) {
+ case QGraphicsPixmapItem::MaskShape: {
+ QBitmap mask = pixmap.mask();
+ if (!mask.isNull()) {
+ shape = qt_regionToPath(QRegion(mask).translated(offset.toPoint()));
+ break;
+ }
+ // FALL THROUGH
+ }
+ case QGraphicsPixmapItem::BoundingRectShape:
+ shape.addRect(QRectF(offset.x(), offset.y(), pixmap.width(), pixmap.height()));
+ break;
+ case QGraphicsPixmapItem::HeuristicMaskShape:
+#ifndef QT_NO_IMAGE_HEURISTIC_MASK
+ shape = qt_regionToPath(QRegion(pixmap.createHeuristicMask()).translated(offset.toPoint()));
+#else
+ shape.addRect(QRectF(offset.x(), offset.y(), pixmap.width(), pixmap.height()));
+#endif
+ break;
+ }
+ }
+};
+
+/*!
+ Constructs a QGraphicsPixmapItem, using \a pixmap as the default pixmap.
+ \a parent is passed to QGraphicsItem's constructor.
+
+ \sa QGraphicsScene::addItem()
+*/
+QGraphicsPixmapItem::QGraphicsPixmapItem(const QPixmap &pixmap,
+ QGraphicsItem *parent
+#ifndef Q_QDOC
+ // obsolete argument
+ , QGraphicsScene *scene
+#endif
+ )
+ : QGraphicsItem(*new QGraphicsPixmapItemPrivate, parent, scene)
+{
+ setPixmap(pixmap);
+}
+
+/*!
+ Constructs a QGraphicsPixmapItem. \a parent is passed to QGraphicsItem's
+ constructor.
+
+ \sa QGraphicsScene::addItem()
+*/
+QGraphicsPixmapItem::QGraphicsPixmapItem(QGraphicsItem *parent
+#ifndef Q_QDOC
+ // obsolete argument
+ , QGraphicsScene *scene
+#endif
+ )
+ : QGraphicsItem(*new QGraphicsPixmapItemPrivate, parent, scene)
+{
+}
+
+/*!
+ Destroys the QGraphicsPixmapItem.
+*/
+QGraphicsPixmapItem::~QGraphicsPixmapItem()
+{
+}
+
+/*!
+ Sets the item's pixmap to \a pixmap.
+
+ \sa pixmap()
+*/
+void QGraphicsPixmapItem::setPixmap(const QPixmap &pixmap)
+{
+ Q_D(QGraphicsPixmapItem);
+ prepareGeometryChange();
+ d->pixmap = pixmap;
+ d->hasShape = false;
+ update();
+}
+
+/*!
+ Returns the item's pixmap, or an invalid QPixmap if no pixmap has been
+ set.
+
+ \sa setPixmap()
+*/
+QPixmap QGraphicsPixmapItem::pixmap() const
+{
+ Q_D(const QGraphicsPixmapItem);
+ return d->pixmap;
+}
+
+/*!
+ Returns the transformation mode of the pixmap. The default mode is
+ Qt::FastTransformation, which provides quick transformation with no
+ smoothing.
+
+ \sa setTransformationMode()
+*/
+Qt::TransformationMode QGraphicsPixmapItem::transformationMode() const
+{
+ Q_D(const QGraphicsPixmapItem);
+ return d->transformationMode;
+}
+
+/*!
+ Sets the pixmap item's transformation mode to \a mode, and toggles an
+ update of the item. The default mode is Qt::FastTransformation, which
+ provides quick transformation with no smoothing.
+
+ Qt::SmoothTransformation enables QPainter::SmoothPixmapTransform on the
+ painter, and the quality depends on the platform and viewport. The result
+ is usually not as good as calling QPixmap::scale() directly.
+
+ \sa transformationMode()
+*/
+void QGraphicsPixmapItem::setTransformationMode(Qt::TransformationMode mode)
+{
+ Q_D(QGraphicsPixmapItem);
+ if (mode != d->transformationMode) {
+ d->transformationMode = mode;
+ update();
+ }
+}
+
+/*!
+ Returns the pixmap item's \e offset, which defines the point of the
+ top-left corner of the pixmap, in local coordinates.
+
+ \sa setOffset()
+*/
+QPointF QGraphicsPixmapItem::offset() const
+{
+ Q_D(const QGraphicsPixmapItem);
+ return d->offset;
+}
+
+/*!
+ Sets the pixmap item's offset to \a offset. QGraphicsPixmapItem will draw
+ its pixmap using \a offset for its top-left corner.
+
+ \sa offset()
+*/
+void QGraphicsPixmapItem::setOffset(const QPointF &offset)
+{
+ Q_D(QGraphicsPixmapItem);
+ if (d->offset == offset)
+ return;
+ prepareGeometryChange();
+ d->offset = offset;
+ d->hasShape = false;
+ update();
+}
+
+/*!
+ \fn void QGraphicsPixmapItem::setOffset(qreal x, qreal y)
+ \since 4.3
+
+ This convenience function is equivalent to calling setOffset(QPointF(\a x, \a y)).
+*/
+
+/*!
+ \reimp
+*/
+QRectF QGraphicsPixmapItem::boundingRect() const
+{
+ Q_D(const QGraphicsPixmapItem);
+ if (d->pixmap.isNull())
+ return QRectF();
+ if (d->flags & ItemIsSelectable) {
+ qreal pw = 1.0;
+ return QRectF(d->offset, d->pixmap.size()).adjusted(-pw/2, -pw/2, pw/2, pw/2);
+ } else {
+ return QRectF(d->offset, d->pixmap.size());
+ }
+}
+
+/*!
+ \reimp
+*/
+QPainterPath QGraphicsPixmapItem::shape() const
+{
+ Q_D(const QGraphicsPixmapItem);
+ if (!d->hasShape) {
+ QGraphicsPixmapItemPrivate *thatD = const_cast<QGraphicsPixmapItemPrivate *>(d);
+ thatD->updateShape();
+ thatD->hasShape = true;
+ }
+ return d_func()->shape;
+}
+
+/*!
+ \reimp
+*/
+bool QGraphicsPixmapItem::contains(const QPointF &point) const
+{
+ return QGraphicsItem::contains(point);
+}
+
+/*!
+ \reimp
+*/
+void QGraphicsPixmapItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option,
+ QWidget *widget)
+{
+ Q_D(QGraphicsPixmapItem);
+ Q_UNUSED(widget);
+
+ painter->setRenderHint(QPainter::SmoothPixmapTransform,
+ (d->transformationMode == Qt::SmoothTransformation));
+
+ painter->drawPixmap(d->offset, d->pixmap);
+
+ if (option->state & QStyle::State_Selected)
+ qt_graphicsItem_highlightSelected(this, painter, option);
+}
+
+/*!
+ \reimp
+*/
+bool QGraphicsPixmapItem::isObscuredBy(const QGraphicsItem *item) const
+{
+ return QGraphicsItem::isObscuredBy(item);
+}
+
+/*!
+ \reimp
+*/
+QPainterPath QGraphicsPixmapItem::opaqueArea() const
+{
+ return shape();
+}
+
+/*!
+ \reimp
+*/
+int QGraphicsPixmapItem::type() const
+{
+ return Type;
+}
+
+/*!
+ Returns the item's shape mode. The shape mode describes how
+ QGraphicsPixmapItem calculates its shape. The default mode is MaskShape.
+
+ \sa setShapeMode(), ShapeMode
+*/
+QGraphicsPixmapItem::ShapeMode QGraphicsPixmapItem::shapeMode() const
+{
+ return d_func()->shapeMode;
+}
+
+/*!
+ Sets the item's shape mode to \a mode. The shape mode describes how
+ QGraphicsPixmapItem calculates its shape. The default mode is MaskShape.
+
+ \sa shapeMode(), ShapeMode
+*/
+void QGraphicsPixmapItem::setShapeMode(ShapeMode mode)
+{
+ Q_D(QGraphicsPixmapItem);
+ if (d->shapeMode == mode)
+ return;
+ d->shapeMode = mode;
+ d->hasShape = false;
+}
+
+/*!
+ \internal
+*/
+bool QGraphicsPixmapItem::supportsExtension(Extension extension) const
+{
+ Q_UNUSED(extension);
+ return false;
+}
+
+/*!
+ \internal
+*/
+void QGraphicsPixmapItem::setExtension(Extension extension, const QVariant &variant)
+{
+ Q_UNUSED(extension);
+ Q_UNUSED(variant);
+}
+
+/*!
+ \internal
+*/
+QVariant QGraphicsPixmapItem::extension(const QVariant &variant) const
+{
+ Q_UNUSED(variant);
+ return QVariant();
+}
+
+/*!
+ \class QGraphicsTextItem
+ \brief The QGraphicsTextItem class provides a text item that you can add to
+ a QGraphicsScene to display formatted text.
+ \since 4.2
+ \ingroup graphicsview-api
+
+ If you only need to show plain text in an item, consider using QGraphicsSimpleTextItem
+ instead.
+
+ To set the item's text, pass a QString to QGraphicsTextItem's
+ constructor, or call setHtml()/setPlainText().
+
+ QGraphicsTextItem uses the text's formatted size and the associated font
+ to provide a reasonable implementation of boundingRect(), shape(),
+ and contains(). You can set the font by calling setFont().
+
+ It is possible to make the item editable by setting the Qt::TextEditorInteraction flag
+ using setTextInteractionFlags().
+
+ The item's preferred text width can be set using setTextWidth() and obtained
+ using textWidth().
+
+ \note In order to align HTML text in the center, the item's text width must be set.
+
+ \img graphicsview-textitem.png
+
+ \note QGraphicsTextItem accepts \l{QGraphicsItem::acceptHoverEvents()}{hover events}
+ by default. You can change this with \l{QGraphicsItem::}{setAcceptHoverEvents()}.
+
+ \sa QGraphicsSimpleTextItem, QGraphicsPathItem, QGraphicsRectItem,
+ QGraphicsEllipseItem, QGraphicsPixmapItem, QGraphicsPolygonItem,
+ QGraphicsLineItem, {Graphics View Framework}
+*/
+
+class QGraphicsTextItemPrivate
+{
+public:
+ QGraphicsTextItemPrivate()
+ : control(0), pageNumber(0), useDefaultImpl(false), tabChangesFocus(false), clickCausedFocus(0)
+ { }
+
+ mutable QTextControl *control;
+ QTextControl *textControl() const;
+
+ inline QPointF controlOffset() const
+ { return QPointF(0., pageNumber * control->document()->pageSize().height()); }
+ inline void sendControlEvent(QEvent *e)
+ { if (control) control->processEvent(e, controlOffset()); }
+
+ void _q_updateBoundingRect(const QSizeF &);
+ void _q_update(QRectF);
+ void _q_ensureVisible(QRectF);
+ bool _q_mouseOnEdge(QGraphicsSceneMouseEvent *);
+
+ QRectF boundingRect;
+ int pageNumber;
+ bool useDefaultImpl;
+ bool tabChangesFocus;
+
+ uint clickCausedFocus : 1;
+
+ QGraphicsTextItem *qq;
+};
+
+
+/*!
+ Constructs a QGraphicsTextItem, using \a text as the default plain
+ text. \a parent is passed to QGraphicsItem's constructor.
+
+ \sa QGraphicsScene::addItem()
+*/
+QGraphicsTextItem::QGraphicsTextItem(const QString &text, QGraphicsItem *parent
+#ifndef Q_QDOC
+ // obsolete argument
+ , QGraphicsScene *scene
+#endif
+ )
+ : QGraphicsObject(*new QGraphicsItemPrivate, parent, scene), dd(new QGraphicsTextItemPrivate)
+{
+ dd->qq = this;
+ if (!text.isEmpty())
+ setPlainText(text);
+ setAcceptDrops(true);
+ setAcceptHoverEvents(true);
+ setFlags(ItemUsesExtendedStyleOption);
+}
+
+/*!
+ Constructs a QGraphicsTextItem. \a parent is passed to QGraphicsItem's
+ constructor.
+
+ \sa QGraphicsScene::addItem()
+*/
+QGraphicsTextItem::QGraphicsTextItem(QGraphicsItem *parent
+#ifndef Q_QDOC
+ // obsolete argument
+ , QGraphicsScene *scene
+#endif
+ )
+ : QGraphicsObject(*new QGraphicsItemPrivate, parent, scene), dd(new QGraphicsTextItemPrivate)
+{
+ dd->qq = this;
+ setAcceptDrops(true);
+ setAcceptHoverEvents(true);
+ setFlag(ItemUsesExtendedStyleOption);
+}
+
+/*!
+ Destroys the QGraphicsTextItem.
+*/
+QGraphicsTextItem::~QGraphicsTextItem()
+{
+ delete dd;
+}
+
+/*!
+ Returns the item's text converted to HTML, or an empty QString if no text has been set.
+
+ \sa setHtml()
+*/
+QString QGraphicsTextItem::toHtml() const
+{
+#ifndef QT_NO_TEXTHTMLPARSER
+ if (dd->control)
+ return dd->control->toHtml();
+#endif
+ return QString();
+}
+
+/*!
+ Sets the item's text to \a text, assuming that text is HTML formatted. If
+ the item has keyboard input focus, this function will also call
+ ensureVisible() to ensure that the text is visible in all viewports.
+
+ \sa toHtml(), hasFocus(), QGraphicsSimpleTextItem
+*/
+void QGraphicsTextItem::setHtml(const QString &text)
+{
+ dd->textControl()->setHtml(text);
+}
+
+/*!
+ Returns the item's text converted to plain text, or an empty QString if no text has been set.
+
+ \sa setPlainText()
+*/
+QString QGraphicsTextItem::toPlainText() const
+{
+ if (dd->control)
+ return dd->control->toPlainText();
+ return QString();
+}
+
+/*!
+ Sets the item's text to \a text. If the item has keyboard input focus,
+ this function will also call ensureVisible() to ensure that the text is
+ visible in all viewports.
+
+ \sa toHtml(), hasFocus()
+*/
+void QGraphicsTextItem::setPlainText(const QString &text)
+{
+ dd->textControl()->setPlainText(text);
+}
+
+/*!
+ Returns the item's font, which is used to render the text.
+
+ \sa setFont()
+*/
+QFont QGraphicsTextItem::font() const
+{
+ if (!dd->control)
+ return QFont();
+ return dd->control->document()->defaultFont();
+}
+
+/*!
+ Sets the font used to render the text item to \a font.
+
+ \sa font()
+*/
+void QGraphicsTextItem::setFont(const QFont &font)
+{
+ dd->textControl()->document()->setDefaultFont(font);
+}
+
+/*!
+ Sets the color for unformatted text to \a col.
+*/
+void QGraphicsTextItem::setDefaultTextColor(const QColor &col)
+{
+ QTextControl *c = dd->textControl();
+ QPalette pal = c->palette();
+ QColor old = pal.color(QPalette::Text);
+ pal.setColor(QPalette::Text, col);
+ c->setPalette(pal);
+ if (old != col)
+ update();
+}
+
+/*!
+ Returns the default text color that is used to for unformatted text.
+*/
+QColor QGraphicsTextItem::defaultTextColor() const
+{
+ return dd->textControl()->palette().color(QPalette::Text);
+}
+
+/*!
+ \reimp
+*/
+QRectF QGraphicsTextItem::boundingRect() const
+{
+ return dd->boundingRect;
+}
+
+/*!
+ \reimp
+*/
+QPainterPath QGraphicsTextItem::shape() const
+{
+ if (!dd->control)
+ return QPainterPath();
+ QPainterPath path;
+ path.addRect(dd->boundingRect);
+ return path;
+}
+
+/*!
+ \reimp
+*/
+bool QGraphicsTextItem::contains(const QPointF &point) const
+{
+ return dd->boundingRect.contains(point);
+}
+
+/*!
+ \reimp
+*/
+void QGraphicsTextItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option,
+ QWidget *widget)
+{
+ Q_UNUSED(widget);
+ if (dd->control) {
+ painter->save();
+ QRectF r = option->exposedRect;
+ painter->translate(-dd->controlOffset());
+ r.translate(dd->controlOffset());
+
+ QTextDocument *doc = dd->control->document();
+ QTextDocumentLayout *layout = qobject_cast<QTextDocumentLayout *>(doc->documentLayout());
+
+ // the layout might need to expand the root frame to
+ // the viewport if NoWrap is set
+ if (layout)
+ layout->setViewport(dd->boundingRect);
+
+ dd->control->drawContents(painter, r);
+
+ if (layout)
+ layout->setViewport(QRect());
+
+ painter->restore();
+ }
+
+ if (option->state & (QStyle::State_Selected | QStyle::State_HasFocus))
+ qt_graphicsItem_highlightSelected(this, painter, option);
+}
+
+/*!
+ \reimp
+*/
+bool QGraphicsTextItem::isObscuredBy(const QGraphicsItem *item) const
+{
+ return QGraphicsItem::isObscuredBy(item);
+}
+
+/*!
+ \reimp
+*/
+QPainterPath QGraphicsTextItem::opaqueArea() const
+{
+ return QGraphicsItem::opaqueArea();
+}
+
+/*!
+ \reimp
+*/
+int QGraphicsTextItem::type() const
+{
+ return Type;
+}
+
+/*!
+ Sets the preferred width for the item's text. If the actual text
+ is wider than the specified width then it will be broken into
+ multiple lines.
+
+ If \a width is set to -1 then the text will not be broken into
+ multiple lines unless it is enforced through an explicit line
+ break or a new paragraph.
+
+ The default value is -1.
+
+ Note that QGraphicsTextItem keeps a QTextDocument internally,
+ which is used to calculate the text width.
+
+ \sa textWidth(), QTextDocument::setTextWidth()
+*/
+void QGraphicsTextItem::setTextWidth(qreal width)
+{
+ dd->textControl()->setTextWidth(width);
+}
+
+/*!
+ Returns the text width.
+
+ The width is calculated with the QTextDocument that
+ QGraphicsTextItem keeps internally.
+
+ \sa setTextWidth(), QTextDocument::textWidth()
+*/
+qreal QGraphicsTextItem::textWidth() const
+{
+ if (!dd->control)
+ return -1;
+ return dd->control->textWidth();
+}
+
+/*!
+ Adjusts the text item to a reasonable size.
+*/
+void QGraphicsTextItem::adjustSize()
+{
+ if (dd->control)
+ dd->control->adjustSize();
+}
+
+/*!
+ Sets the text document \a document on the item.
+*/
+void QGraphicsTextItem::setDocument(QTextDocument *document)
+{
+ dd->textControl()->setDocument(document);
+ dd->_q_updateBoundingRect(dd->control->size());
+}
+
+/*!
+ Returns the item's text document.
+*/
+QTextDocument *QGraphicsTextItem::document() const
+{
+ return dd->textControl()->document();
+}
+
+/*!
+ \reimp
+*/
+bool QGraphicsTextItem::sceneEvent(QEvent *event)
+{
+ QEvent::Type t = event->type();
+ if (!dd->tabChangesFocus && (t == QEvent::KeyPress || t == QEvent::KeyRelease)) {
+ int k = ((QKeyEvent *)event)->key();
+ if (k == Qt::Key_Tab || k == Qt::Key_Backtab) {
+ dd->sendControlEvent(event);
+ return true;
+ }
+ }
+ bool result = QGraphicsItem::sceneEvent(event);
+
+ // Ensure input context is updated.
+ switch (event->type()) {
+ case QEvent::ContextMenu:
+ case QEvent::FocusIn:
+ case QEvent::FocusOut:
+ case QEvent::GraphicsSceneDragEnter:
+ case QEvent::GraphicsSceneDragLeave:
+ case QEvent::GraphicsSceneDragMove:
+ case QEvent::GraphicsSceneDrop:
+ case QEvent::GraphicsSceneHoverEnter:
+ case QEvent::GraphicsSceneHoverLeave:
+ case QEvent::GraphicsSceneHoverMove:
+ case QEvent::GraphicsSceneMouseDoubleClick:
+ case QEvent::GraphicsSceneMousePress:
+ case QEvent::GraphicsSceneMouseMove:
+ case QEvent::GraphicsSceneMouseRelease:
+ case QEvent::KeyPress:
+ case QEvent::KeyRelease:
+ // Reset the focus widget's input context, regardless
+ // of how this item gained or lost focus.
+ if (QWidget *fw = qApp->focusWidget()) {
+#ifndef QT_NO_IM
+ if (QInputContext *qic = fw->inputContext()) {
+ if (event->type() == QEvent::FocusIn || event->type() == QEvent::FocusOut)
+ qic->reset();
+ else
+ qic->update();
+ }
+#endif //QT_NO_IM
+ }
+ break;
+ case QEvent::ShortcutOverride:
+ dd->sendControlEvent(event);
+ return true;
+ default:
+ break;
+ }
+
+ return result;
+}
+
+/*!
+ \reimp
+*/
+void QGraphicsTextItem::mousePressEvent(QGraphicsSceneMouseEvent *event)
+{
+ if ((QGraphicsItem::d_ptr->flags & (ItemIsSelectable | ItemIsMovable))
+ && (event->buttons() & Qt::LeftButton) && dd->_q_mouseOnEdge(event)) {
+ // User left-pressed on edge of selectable/movable item, use
+ // base impl.
+ dd->useDefaultImpl = true;
+ } else if (event->buttons() == event->button()
+ && dd->control->textInteractionFlags() == Qt::NoTextInteraction) {
+ // User pressed first button on non-interactive item.
+ dd->useDefaultImpl = true;
+ }
+ if (dd->useDefaultImpl) {
+ QGraphicsItem::mousePressEvent(event);
+ if (!event->isAccepted())
+ dd->useDefaultImpl = false;
+ return;
+ }
+
+ dd->sendControlEvent(event);
+}
+
+/*!
+ \reimp
+*/
+void QGraphicsTextItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
+{
+ if (dd->useDefaultImpl) {
+ QGraphicsItem::mouseMoveEvent(event);
+ return;
+ }
+
+ dd->sendControlEvent(event);
+}
+
+/*!
+ \reimp
+*/
+void QGraphicsTextItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
+{
+ if (dd->useDefaultImpl) {
+ QGraphicsItem::mouseReleaseEvent(event);
+ if (dd->control->textInteractionFlags() == Qt::NoTextInteraction
+ && !event->buttons()) {
+ // User released last button on non-interactive item.
+ dd->useDefaultImpl = false;
+ } else if ((event->buttons() & Qt::LeftButton) == 0) {
+ // User released the left button on an interactive item.
+ dd->useDefaultImpl = false;
+ }
+ return;
+ }
+
+ QWidget *widget = event->widget();
+ if (widget && (dd->control->textInteractionFlags() & Qt::TextEditable) && boundingRect().contains(event->pos())) {
+ qt_widget_private(widget)->handleSoftwareInputPanel(event->button(), dd->clickCausedFocus);
+ }
+ dd->clickCausedFocus = 0;
+ dd->sendControlEvent(event);
+}
+
+/*!
+ \reimp
+*/
+void QGraphicsTextItem::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event)
+{
+ if (dd->useDefaultImpl) {
+ QGraphicsItem::mouseDoubleClickEvent(event);
+ return;
+ }
+
+ if (!hasFocus()) {
+ QGraphicsItem::mouseDoubleClickEvent(event);
+ return;
+ }
+
+ dd->sendControlEvent(event);
+}
+
+/*!
+ \reimp
+*/
+void QGraphicsTextItem::contextMenuEvent(QGraphicsSceneContextMenuEvent *event)
+{
+ dd->sendControlEvent(event);
+}
+
+/*!
+ \reimp
+*/
+void QGraphicsTextItem::keyPressEvent(QKeyEvent *event)
+{
+ dd->sendControlEvent(event);
+}
+
+/*!
+ \reimp
+*/
+void QGraphicsTextItem::keyReleaseEvent(QKeyEvent *event)
+{
+ dd->sendControlEvent(event);
+}
+
+/*!
+ \reimp
+*/
+void QGraphicsTextItem::focusInEvent(QFocusEvent *event)
+{
+ dd->sendControlEvent(event);
+ if (event->reason() == Qt::MouseFocusReason) {
+ dd->clickCausedFocus = 1;
+ }
+ update();
+}
+
+/*!
+ \reimp
+*/
+void QGraphicsTextItem::focusOutEvent(QFocusEvent *event)
+{
+ dd->sendControlEvent(event);
+ update();
+}
+
+/*!
+ \reimp
+*/
+void QGraphicsTextItem::dragEnterEvent(QGraphicsSceneDragDropEvent *event)
+{
+ dd->sendControlEvent(event);
+}
+
+/*!
+ \reimp
+*/
+void QGraphicsTextItem::dragLeaveEvent(QGraphicsSceneDragDropEvent *event)
+{
+ dd->sendControlEvent(event);
+}
+
+/*!
+ \reimp
+*/
+void QGraphicsTextItem::dragMoveEvent(QGraphicsSceneDragDropEvent *event)
+{
+ dd->sendControlEvent(event);
+}
+
+/*!
+ \reimp
+*/
+void QGraphicsTextItem::dropEvent(QGraphicsSceneDragDropEvent *event)
+{
+ dd->sendControlEvent(event);
+}
+
+/*!
+ \reimp
+*/
+void QGraphicsTextItem::inputMethodEvent(QInputMethodEvent *event)
+{
+ dd->sendControlEvent(event);
+}
+
+/*!
+ \reimp
+*/
+void QGraphicsTextItem::hoverEnterEvent(QGraphicsSceneHoverEvent *event)
+{
+ dd->sendControlEvent(event);
+}
+
+/*!
+ \reimp
+*/
+void QGraphicsTextItem::hoverMoveEvent(QGraphicsSceneHoverEvent *event)
+{
+ dd->sendControlEvent(event);
+}
+
+/*!
+ \reimp
+*/
+void QGraphicsTextItem::hoverLeaveEvent(QGraphicsSceneHoverEvent *event)
+{
+ dd->sendControlEvent(event);
+}
+
+/*!
+ \reimp
+*/
+QVariant QGraphicsTextItem::inputMethodQuery(Qt::InputMethodQuery query) const
+{
+ QVariant v;
+ if (dd->control)
+ v = dd->control->inputMethodQuery(query);
+ if (v.type() == QVariant::RectF)
+ v = v.toRectF().translated(-dd->controlOffset());
+ else if (v.type() == QVariant::PointF)
+ v = v.toPointF() - dd->controlOffset();
+ else if (v.type() == QVariant::Rect)
+ v = v.toRect().translated(-dd->controlOffset().toPoint());
+ else if (v.type() == QVariant::Point)
+ v = v.toPoint() - dd->controlOffset().toPoint();
+ return v;
+}
+
+/*!
+ \internal
+*/
+bool QGraphicsTextItem::supportsExtension(Extension extension) const
+{
+ Q_UNUSED(extension);
+ return false;
+}
+
+/*!
+ \internal
+*/
+void QGraphicsTextItem::setExtension(Extension extension, const QVariant &variant)
+{
+ Q_UNUSED(extension);
+ Q_UNUSED(variant);
+}
+
+/*!
+ \internal
+*/
+QVariant QGraphicsTextItem::extension(const QVariant &variant) const
+{
+ Q_UNUSED(variant);
+ return QVariant();
+}
+
+/*!
+ \internal
+*/
+void QGraphicsTextItemPrivate::_q_update(QRectF rect)
+{
+ if (rect.isValid()) {
+ rect.translate(-controlOffset());
+ } else {
+ rect = boundingRect;
+ }
+ if (rect.intersects(boundingRect))
+ qq->update(rect);
+}
+
+/*!
+ \internal
+*/
+void QGraphicsTextItemPrivate::_q_updateBoundingRect(const QSizeF &size)
+{
+ if (!control) return; // can't happen
+ const QSizeF pageSize = control->document()->pageSize();
+ // paged items have a constant (page) size
+ if (size == boundingRect.size() || pageSize.height() != -1)
+ return;
+ qq->prepareGeometryChange();
+ boundingRect.setSize(size);
+ qq->update();
+}
+
+/*!
+ \internal
+*/
+void QGraphicsTextItemPrivate::_q_ensureVisible(QRectF rect)
+{
+ if (qq->hasFocus()) {
+ rect.translate(-controlOffset());
+ qq->ensureVisible(rect, /*xmargin=*/0, /*ymargin=*/0);
+ }
+}
+
+QTextControl *QGraphicsTextItemPrivate::textControl() const
+{
+ if (!control) {
+ QGraphicsTextItem *that = const_cast<QGraphicsTextItem *>(qq);
+ control = new QTextControl(that);
+ control->setTextInteractionFlags(Qt::NoTextInteraction);
+
+ QObject::connect(control, SIGNAL(updateRequest(QRectF)),
+ qq, SLOT(_q_update(QRectF)));
+ QObject::connect(control, SIGNAL(documentSizeChanged(QSizeF)),
+ qq, SLOT(_q_updateBoundingRect(QSizeF)));
+ QObject::connect(control, SIGNAL(visibilityRequest(QRectF)),
+ qq, SLOT(_q_ensureVisible(QRectF)));
+ QObject::connect(control, SIGNAL(linkActivated(QString)),
+ qq, SIGNAL(linkActivated(QString)));
+ QObject::connect(control, SIGNAL(linkHovered(QString)),
+ qq, SIGNAL(linkHovered(QString)));
+
+ const QSizeF pgSize = control->document()->pageSize();
+ if (pgSize.height() != -1) {
+ qq->prepareGeometryChange();
+ that->dd->boundingRect.setSize(pgSize);
+ qq->update();
+ } else {
+ that->dd->_q_updateBoundingRect(control->size());
+ }
+ }
+ return control;
+}
+
+/*!
+ \internal
+*/
+bool QGraphicsTextItemPrivate::_q_mouseOnEdge(QGraphicsSceneMouseEvent *event)
+{
+ QPainterPath path;
+ path.addRect(qq->boundingRect());
+
+ QPainterPath docPath;
+ const QTextFrameFormat format = control->document()->rootFrame()->frameFormat();
+ docPath.addRect(
+ qq->boundingRect().adjusted(
+ format.leftMargin(),
+ format.topMargin(),
+ -format.rightMargin(),
+ -format.bottomMargin()));
+
+ return path.subtracted(docPath).contains(event->pos());
+}
+
+/*!
+ \fn QGraphicsTextItem::linkActivated(const QString &link)
+
+ This signal is emitted when the user clicks on a link on a text item
+ that enables Qt::LinksAccessibleByMouse or Qt::LinksAccessibleByKeyboard.
+ \a link is the link that was clicked.
+
+ \sa setTextInteractionFlags()
+*/
+
+/*!
+ \fn QGraphicsTextItem::linkHovered(const QString &link)
+
+ This signal is emitted when the user hovers over a link on a text item
+ that enables Qt::LinksAccessibleByMouse. \a link is
+ the link that was hovered over.
+
+ \sa setTextInteractionFlags()
+*/
+
+/*!
+ Sets the flags \a flags to specify how the text item should react to user
+ input.
+
+ The default for a QGraphicsTextItem is Qt::NoTextInteraction. This function
+ also affects the ItemIsFocusable QGraphicsItem flag by setting it if \a flags
+ is different from Qt::NoTextInteraction and clearing it otherwise.
+
+ By default, the text is read-only. To transform the item into an editor,
+ set the Qt::TextEditable flag.
+*/
+void QGraphicsTextItem::setTextInteractionFlags(Qt::TextInteractionFlags flags)
+{
+ if (flags == Qt::NoTextInteraction)
+ setFlags(this->flags() & ~(QGraphicsItem::ItemIsFocusable | QGraphicsItem::ItemAcceptsInputMethod));
+ else
+ setFlags(this->flags() | QGraphicsItem::ItemIsFocusable | QGraphicsItem::ItemAcceptsInputMethod);
+
+ dd->textControl()->setTextInteractionFlags(flags);
+}
+
+/*!
+ Returns the current text interaction flags.
+
+ \sa setTextInteractionFlags()
+*/
+Qt::TextInteractionFlags QGraphicsTextItem::textInteractionFlags() const
+{
+ if (!dd->control)
+ return Qt::NoTextInteraction;
+ return dd->control->textInteractionFlags();
+}
+
+/*!
+ \since 4.5
+
+ If \a b is true, the \gui Tab key will cause the widget to change focus;
+ otherwise, the tab key will insert a tab into the document.
+
+ In some occasions text edits should not allow the user to input tabulators
+ or change indentation using the \gui Tab key, as this breaks the focus
+ chain. The default is false.
+
+ \sa tabChangesFocus(), ItemIsFocusable, textInteractionFlags()
+*/
+void QGraphicsTextItem::setTabChangesFocus(bool b)
+{
+ dd->tabChangesFocus = b;
+}
+
+/*!
+ \since 4.5
+
+ Returns true if the \gui Tab key will cause the widget to change focus;
+ otherwise, false is returned.
+
+ By default, this behavior is disabled, and this function will return false.
+
+ \sa setTabChangesFocus()
+*/
+bool QGraphicsTextItem::tabChangesFocus() const
+{
+ return dd->tabChangesFocus;
+}
+
+/*!
+ \property QGraphicsTextItem::openExternalLinks
+
+ Specifies whether QGraphicsTextItem should automatically open links using
+ QDesktopServices::openUrl() instead of emitting the
+ linkActivated signal.
+
+ The default value is false.
+*/
+void QGraphicsTextItem::setOpenExternalLinks(bool open)
+{
+ dd->textControl()->setOpenExternalLinks(open);
+}
+
+bool QGraphicsTextItem::openExternalLinks() const
+{
+ if (!dd->control)
+ return false;
+ return dd->control->openExternalLinks();
+}
+
+/*!
+ \property QGraphicsTextItem::textCursor
+
+ This property represents the visible text cursor in an editable
+ text item.
+
+ By default, if the item's text has not been set, this property
+ contains a null text cursor; otherwise it contains a text cursor
+ placed at the start of the item's document.
+*/
+void QGraphicsTextItem::setTextCursor(const QTextCursor &cursor)
+{
+ dd->textControl()->setTextCursor(cursor);
+}
+
+QTextCursor QGraphicsTextItem::textCursor() const
+{
+ if (!dd->control)
+ return QTextCursor();
+ return dd->control->textCursor();
+}
+
+class QGraphicsSimpleTextItemPrivate : public QAbstractGraphicsShapeItemPrivate
+{
+ Q_DECLARE_PUBLIC(QGraphicsSimpleTextItem)
+public:
+ inline QGraphicsSimpleTextItemPrivate() {
+ pen.setStyle(Qt::NoPen);
+ brush.setStyle(Qt::SolidPattern);
+ }
+ QString text;
+ QFont font;
+ QRectF boundingRect;
+
+ void updateBoundingRect();
+};
+
+static QRectF setupTextLayout(QTextLayout *layout)
+{
+ layout->setCacheEnabled(true);
+ layout->beginLayout();
+ while (layout->createLine().isValid())
+ ;
+ layout->endLayout();
+ qreal maxWidth = 0;
+ qreal y = 0;
+ for (int i = 0; i < layout->lineCount(); ++i) {
+ QTextLine line = layout->lineAt(i);
+ maxWidth = qMax(maxWidth, line.naturalTextWidth());
+ line.setPosition(QPointF(0, y));
+ y += line.height();
+ }
+ return QRectF(0, 0, maxWidth, y);
+}
+
+void QGraphicsSimpleTextItemPrivate::updateBoundingRect()
+{
+ Q_Q(QGraphicsSimpleTextItem);
+ QRectF br;
+ if (text.isEmpty()) {
+ br = QRectF();
+ } else {
+ QString tmp = text;
+ tmp.replace(QLatin1Char('\n'), QChar::LineSeparator);
+ QStackTextEngine engine(tmp, font);
+ QTextLayout layout(&engine);
+ br = setupTextLayout(&layout);
+ }
+ if (br != boundingRect) {
+ q->prepareGeometryChange();
+ boundingRect = br;
+ q->update();
+ }
+}
+
+/*!
+ \class QGraphicsSimpleTextItem
+ \brief The QGraphicsSimpleTextItem class provides a simple text path item
+ that you can add to a QGraphicsScene.
+ \since 4.2
+ \ingroup graphicsview-api
+
+ To set the item's text, you can either pass a QString to
+ QGraphicsSimpleTextItem's constructor, or call setText() to change the
+ text later. To set the text fill color, call setBrush().
+
+ The simple text item can have both a fill and an outline; setBrush() will
+ set the text fill (i.e., text color), and setPen() sets the pen that will
+ be used to draw the text outline. (The latter can be slow, especially for
+ complex pens, and items with long text content.) If all you want is to
+ draw a simple line of text, you should call setBrush() only, and leave the
+ pen unset; QGraphicsSimpleTextItem's pen is by default Qt::NoPen.
+
+ QGraphicsSimpleTextItem uses the text's formatted size and the associated
+ font to provide a reasonable implementation of boundingRect(), shape(),
+ and contains(). You can set the font by calling setFont().
+
+ QGraphicsSimpleText does not display rich text; instead, you can use
+ QGraphicsTextItem, which provides full text control capabilities.
+
+ \img graphicsview-simpletextitem.png
+
+ \sa QGraphicsTextItem, QGraphicsPathItem, QGraphicsRectItem,
+ QGraphicsEllipseItem, QGraphicsPixmapItem, QGraphicsPolygonItem,
+ QGraphicsLineItem, {Graphics View Framework}
+*/
+
+/*!
+ Constructs a QGraphicsSimpleTextItem.
+
+ \a parent is passed to QGraphicsItem's constructor.
+
+ \sa QGraphicsScene::addItem()
+*/
+QGraphicsSimpleTextItem::QGraphicsSimpleTextItem(QGraphicsItem *parent
+#ifndef Q_QDOC
+ // obsolete argument
+ , QGraphicsScene *scene
+#endif
+ )
+ : QAbstractGraphicsShapeItem(*new QGraphicsSimpleTextItemPrivate, parent, scene)
+{
+}
+
+/*!
+ Constructs a QGraphicsSimpleTextItem, using \a text as the default plain text.
+
+ \a parent is passed to QGraphicsItem's constructor.
+
+ \sa QGraphicsScene::addItem()
+*/
+QGraphicsSimpleTextItem::QGraphicsSimpleTextItem(const QString &text, QGraphicsItem *parent
+#ifndef Q_QDOC
+ // obsolete argument
+ , QGraphicsScene *scene
+#endif
+ )
+ : QAbstractGraphicsShapeItem(*new QGraphicsSimpleTextItemPrivate, parent, scene)
+{
+ setText(text);
+}
+
+/*!
+ Destroys the QGraphicsSimpleTextItem.
+*/
+QGraphicsSimpleTextItem::~QGraphicsSimpleTextItem()
+{
+}
+
+/*!
+ Sets the item's text to \a text. The text will be displayed as
+ plain text. Newline characters ('\n') as well as characters of
+ type QChar::LineSeparator will cause item to break the text into
+ multiple lines.
+*/
+void QGraphicsSimpleTextItem::setText(const QString &text)
+{
+ Q_D(QGraphicsSimpleTextItem);
+ if (d->text == text)
+ return;
+ d->text = text;
+ d->updateBoundingRect();
+ update();
+}
+
+/*!
+ Returns the item's text.
+*/
+QString QGraphicsSimpleTextItem::text() const
+{
+ Q_D(const QGraphicsSimpleTextItem);
+ return d->text;
+}
+
+/*!
+ Sets the font that is used to draw the item's text to \a font.
+*/
+void QGraphicsSimpleTextItem::setFont(const QFont &font)
+{
+ Q_D(QGraphicsSimpleTextItem);
+ d->font = font;
+ d->updateBoundingRect();
+}
+
+/*!
+ Returns the font that is used to draw the item's text.
+*/
+QFont QGraphicsSimpleTextItem::font() const
+{
+ Q_D(const QGraphicsSimpleTextItem);
+ return d->font;
+}
+
+/*!
+ \reimp
+*/
+QRectF QGraphicsSimpleTextItem::boundingRect() const
+{
+ Q_D(const QGraphicsSimpleTextItem);
+ return d->boundingRect;
+}
+
+/*!
+ \reimp
+*/
+QPainterPath QGraphicsSimpleTextItem::shape() const
+{
+ Q_D(const QGraphicsSimpleTextItem);
+ QPainterPath path;
+ path.addRect(d->boundingRect);
+ return path;
+}
+
+/*!
+ \reimp
+*/
+bool QGraphicsSimpleTextItem::contains(const QPointF &point) const
+{
+ Q_D(const QGraphicsSimpleTextItem);
+ return d->boundingRect.contains(point);
+}
+
+/*!
+ \reimp
+*/
+void QGraphicsSimpleTextItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
+{
+ Q_UNUSED(widget);
+ Q_D(QGraphicsSimpleTextItem);
+
+ painter->setFont(d->font);
+
+ QString tmp = d->text;
+ tmp.replace(QLatin1Char('\n'), QChar::LineSeparator);
+ QStackTextEngine engine(tmp, d->font);
+ QTextLayout layout(&engine);
+ setupTextLayout(&layout);
+
+ QPen p;
+ p.setBrush(d->brush);
+ painter->setPen(p);
+ if (d->pen.style() == Qt::NoPen && d->brush.style() == Qt::SolidPattern) {
+ painter->setBrush(Qt::NoBrush);
+ } else {
+ QTextLayout::FormatRange range;
+ range.start = 0;
+ range.length = layout.text().length();
+ range.format.setTextOutline(d->pen);
+ QList<QTextLayout::FormatRange> formats;
+ formats.append(range);
+ layout.setAdditionalFormats(formats);
+ }
+
+ layout.draw(painter, QPointF(0, 0));
+
+ if (option->state & (QStyle::State_Selected | QStyle::State_HasFocus))
+ qt_graphicsItem_highlightSelected(this, painter, option);
+}
+
+/*!
+ \reimp
+*/
+bool QGraphicsSimpleTextItem::isObscuredBy(const QGraphicsItem *item) const
+{
+ return QAbstractGraphicsShapeItem::isObscuredBy(item);
+}
+
+/*!
+ \reimp
+*/
+QPainterPath QGraphicsSimpleTextItem::opaqueArea() const
+{
+ return QAbstractGraphicsShapeItem::opaqueArea();
+}
+
+/*!
+ \reimp
+*/
+int QGraphicsSimpleTextItem::type() const
+{
+ return Type;
+}
+
+/*!
+ \internal
+*/
+bool QGraphicsSimpleTextItem::supportsExtension(Extension extension) const
+{
+ Q_UNUSED(extension);
+ return false;
+}
+
+/*!
+ \internal
+*/
+void QGraphicsSimpleTextItem::setExtension(Extension extension, const QVariant &variant)
+{
+ Q_UNUSED(extension);
+ Q_UNUSED(variant);
+}
+
+/*!
+ \internal
+*/
+QVariant QGraphicsSimpleTextItem::extension(const QVariant &variant) const
+{
+ Q_UNUSED(variant);
+ return QVariant();
+}
+
+/*!
+ \class QGraphicsItemGroup
+ \brief The QGraphicsItemGroup class provides a container that treats
+ a group of items as a single item.
+ \since 4.2
+ \ingroup graphicsview-api
+
+ A QGraphicsItemGroup is a special type of compound item that
+ treats itself and all its children as one item (i.e., all events
+ and geometries for all children are merged together). It's common
+ to use item groups in presentation tools, when the user wants to
+ group several smaller items into one big item in order to simplify
+ moving and copying of items.
+
+ If all you want is to store items inside other items, you can use
+ any QGraphicsItem directly by passing a suitable parent to
+ setParentItem().
+
+ The boundingRect() function of QGraphicsItemGroup returns the
+ bounding rectangle of all items in the item group.
+ QGraphicsItemGroup ignores the ItemIgnoresTransformations flag on
+ its children (i.e., with respect to the geometry of the group
+ item, the children are treated as if they were transformable).
+
+ There are two ways to construct an item group. The easiest and
+ most common approach is to pass a list of items (e.g., all
+ selected items) to QGraphicsScene::createItemGroup(), which
+ returns a new QGraphicsItemGroup item. The other approach is to
+ manually construct a QGraphicsItemGroup item, add it to the scene
+ calling QGraphicsScene::addItem(), and then add items to the group
+ manually, one at a time by calling addToGroup(). To dismantle
+ ("ungroup") an item group, you can either call
+ QGraphicsScene::destroyItemGroup(), or you can manually remove all
+ items from the group by calling removeFromGroup().
+
+ \snippet doc/src/snippets/code/src_gui_graphicsview_qgraphicsitem.cpp 17
+
+ The operation of adding and removing items preserves the items'
+ scene-relative position and transformation, as opposed to calling
+ setParentItem(), where only the child item's parent-relative
+ position and transformation are kept.
+
+ The addtoGroup() function reparents the target item to this item
+ group, keeping the item's position and transformation intact
+ relative to the scene. Visually, this means that items added via
+ addToGroup() will remain completely unchanged as a result of this
+ operation, regardless of the item or the group's current position
+ or transformation; although the item's position and matrix are
+ likely to change.
+
+ The removeFromGroup() function has similar semantics to
+ setParentItem(); it reparents the item to the parent item of the
+ item group. As with addToGroup(), the item's scene-relative
+ position and transformation remain intact.
+
+ \sa QGraphicsItem, {Graphics View Framework}
+*/
+
+class QGraphicsItemGroupPrivate : public QGraphicsItemPrivate
+{
+public:
+ QRectF itemsBoundingRect;
+};
+
+/*!
+ Constructs a QGraphicsItemGroup. \a parent is passed to QGraphicsItem's
+ constructor.
+
+ \sa QGraphicsScene::addItem()
+*/
+QGraphicsItemGroup::QGraphicsItemGroup(QGraphicsItem *parent
+#ifndef Q_QDOC
+ // obsolete argument
+ , QGraphicsScene *scene
+#endif
+ )
+ : QGraphicsItem(*new QGraphicsItemGroupPrivate, parent, scene)
+{
+ setHandlesChildEvents(true);
+}
+
+/*!
+ Destroys the QGraphicsItemGroup.
+*/
+QGraphicsItemGroup::~QGraphicsItemGroup()
+{
+}
+
+/*!
+ Adds the given \a item to this item group. The item will be
+ reparented to this group, but its position and transformation
+ relative to the scene will stay intact.
+
+ \sa removeFromGroup(), QGraphicsScene::createItemGroup()
+*/
+void QGraphicsItemGroup::addToGroup(QGraphicsItem *item)
+{
+ Q_D(QGraphicsItemGroup);
+ if (!item) {
+ qWarning("QGraphicsItemGroup::addToGroup: cannot add null item");
+ return;
+ }
+ if (item == this) {
+ qWarning("QGraphicsItemGroup::addToGroup: cannot add a group to itself");
+ return;
+ }
+
+ // COMBINE
+ bool ok;
+ QTransform itemTransform = item->itemTransform(this, &ok);
+
+ if (!ok) {
+ qWarning("QGraphicsItemGroup::addToGroup: could not find a valid transformation from item to group coordinates");
+ return;
+ }
+
+ QTransform newItemTransform(itemTransform);
+ item->setPos(mapFromItem(item, 0, 0));
+ item->setParentItem(this);
+
+ // removing position from translation component of the new transform
+ if (!item->pos().isNull())
+ newItemTransform *= QTransform::fromTranslate(-item->x(), -item->y());
+
+ // removing additional transformations properties applied with itemTransform()
+ QPointF origin = item->transformOriginPoint();
+ QMatrix4x4 m;
+ QList<QGraphicsTransform*> transformList = item->transformations();
+ for (int i = 0; i < transformList.size(); ++i)
+ transformList.at(i)->applyTo(&m);
+ newItemTransform *= m.toTransform().inverted();
+ newItemTransform.translate(origin.x(), origin.y());
+ newItemTransform.rotate(-item->rotation());
+ newItemTransform.scale(1/item->scale(), 1/item->scale());
+ newItemTransform.translate(-origin.x(), -origin.y());
+
+ // ### Expensive, we could maybe use dirtySceneTransform bit for optimization
+
+ item->setTransform(newItemTransform);
+ item->d_func()->setIsMemberOfGroup(true);
+ prepareGeometryChange();
+ d->itemsBoundingRect |= itemTransform.mapRect(item->boundingRect() | item->childrenBoundingRect());
+ update();
+}
+
+/*!
+ Removes the specified \a item from this group. The item will be
+ reparented to this group's parent item, or to 0 if this group has
+ no parent. Its position and transformation relative to the scene
+ will stay intact.
+
+ \sa addToGroup(), QGraphicsScene::destroyItemGroup()
+*/
+void QGraphicsItemGroup::removeFromGroup(QGraphicsItem *item)
+{
+ Q_D(QGraphicsItemGroup);
+ if (!item) {
+ qWarning("QGraphicsItemGroup::removeFromGroup: cannot remove null item");
+ return;
+ }
+
+ QGraphicsItem *newParent = d_ptr->parent;
+
+ // COMBINE
+ bool ok;
+ QTransform itemTransform;
+ if (newParent)
+ itemTransform = item->itemTransform(newParent, &ok);
+ else
+ itemTransform = item->sceneTransform();
+
+ QPointF oldPos = item->mapToItem(newParent, 0, 0);
+ item->setParentItem(newParent);
+ item->setPos(oldPos);
+
+ // removing position from translation component of the new transform
+ if (!item->pos().isNull())
+ itemTransform *= QTransform::fromTranslate(-item->x(), -item->y());
+
+ // removing additional transformations properties applied
+ // with itemTransform() or sceneTransform()
+ QPointF origin = item->transformOriginPoint();
+ QMatrix4x4 m;
+ QList<QGraphicsTransform*> transformList = item->transformations();
+ for (int i = 0; i < transformList.size(); ++i)
+ transformList.at(i)->applyTo(&m);
+ itemTransform *= m.toTransform().inverted();
+ itemTransform.translate(origin.x(), origin.y());
+ itemTransform.rotate(-item->rotation());
+ itemTransform.scale(1 / item->scale(), 1 / item->scale());
+ itemTransform.translate(-origin.x(), -origin.y());
+
+ // ### Expensive, we could maybe use dirtySceneTransform bit for optimization
+
+ item->setTransform(itemTransform);
+ item->d_func()->setIsMemberOfGroup(item->group() != 0);
+
+ // ### Quite expensive. But removeFromGroup() isn't called very often.
+ prepareGeometryChange();
+ d->itemsBoundingRect = childrenBoundingRect();
+}
+
+/*!
+ \reimp
+
+ Returns the bounding rect of this group item, and all its children.
+*/
+QRectF QGraphicsItemGroup::boundingRect() const
+{
+ Q_D(const QGraphicsItemGroup);
+ return d->itemsBoundingRect;
+}
+
+/*!
+ \reimp
+*/
+void QGraphicsItemGroup::paint(QPainter *painter, const QStyleOptionGraphicsItem *option,
+ QWidget *widget)
+{
+ Q_UNUSED(widget);
+ if (option->state & QStyle::State_Selected) {
+ Q_D(QGraphicsItemGroup);
+ painter->setBrush(Qt::NoBrush);
+ painter->drawRect(d->itemsBoundingRect);
+ }
+}
+
+/*!
+ \reimp
+*/
+bool QGraphicsItemGroup::isObscuredBy(const QGraphicsItem *item) const
+{
+ return QGraphicsItem::isObscuredBy(item);
+}
+
+/*!
+ \reimp
+*/
+QPainterPath QGraphicsItemGroup::opaqueArea() const
+{
+ return QGraphicsItem::opaqueArea();
+}
+
+/*!
+ \reimp
+*/
+int QGraphicsItemGroup::type() const
+{
+ return Type;
+}
+
+#ifndef QT_NO_GRAPHICSEFFECT
+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 = info->painter->worldTransform().inverted();
+ effectTransform *= painter->worldTransform();
+ scened->draw(item, painter, info->viewTransform, info->transformPtr, info->exposedRegion,
+ info->widget, info->opacity, &effectTransform, info->wasDirtySceneTransform,
+ info->drawItem);
+ }
+}
+
+// sourceRect must be in the given coordinate system
+QRect QGraphicsItemEffectSourcePrivate::paddedEffectRect(Qt::CoordinateSystem system, QGraphicsEffect::PixmapPadMode mode, const QRectF &sourceRect, bool *unpadded) const
+{
+ QRectF effectRectF;
+
+ if (unpadded)
+ *unpadded = false;
+
+ if (mode == QGraphicsEffect::PadToEffectiveBoundingRect) {
+ if (info) {
+ QRectF deviceRect = system == Qt::DeviceCoordinates ? sourceRect : info->painter->worldTransform().mapRect(sourceRect);
+ effectRectF = item->graphicsEffect()->boundingRectFor(deviceRect);
+ if (unpadded)
+ *unpadded = (effectRectF.size() == sourceRect.size());
+ if (info && system == Qt::LogicalCoordinates)
+ effectRectF = info->painter->worldTransform().inverted().mapRect(effectRectF);
+ } else {
+ // no choice but to send a logical coordinate bounding rect to boundingRectFor
+ effectRectF = item->graphicsEffect()->boundingRectFor(sourceRect);
+ }
+ } else if (mode == QGraphicsEffect::PadToTransparentBorder) {
+ // adjust by 1.5 to account for cosmetic pens
+ effectRectF = sourceRect.adjusted(-1.5, -1.5, 1.5, 1.5);
+ } else {
+ effectRectF = sourceRect;
+ if (unpadded)
+ *unpadded = true;
+ }
+
+ return effectRectF.toAlignedRect();
+}
+
+QPixmap QGraphicsItemEffectSourcePrivate::pixmap(Qt::CoordinateSystem system, QPoint *offset,
+ QGraphicsEffect::PixmapPadMode mode) 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();
+
+ bool unpadded;
+ const QRectF sourceRect = boundingRect(system);
+ QRect effectRect = paddedEffectRect(system, mode, sourceRect, &unpadded);
+
+ if (offset)
+ *offset = effectRect.topLeft();
+
+ bool untransformed = !deviceCoordinates
+ || info->painter->worldTransform().type() <= QTransform::TxTranslate;
+ if (untransformed && unpadded && isPixmap()) {
+ if (offset)
+ *offset = boundingRect(system).topLeft().toPoint();
+ return static_cast<QGraphicsPixmapItem *>(item)->pixmap();
+ }
+
+ 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, 0,
+ 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, 0,
+ info->widget, info->opacity, &newEffectTransform, info->wasDirtySceneTransform,
+ info->drawItem);
+ }
+
+ pixmapPainter.end();
+
+ return pixmap;
+}
+#endif //QT_NO_GRAPHICSEFFECT
+
+#ifndef QT_NO_DEBUG_STREAM
+QDebug operator<<(QDebug debug, QGraphicsItem *item)
+{
+ if (!item) {
+ debug << "QGraphicsItem(0)";
+ return debug;
+ }
+
+ if (QGraphicsObject *o = item->toGraphicsObject())
+ debug << o->metaObject()->className();
+ else
+ debug << "QGraphicsItem";
+ debug << "(this =" << (void*)item
+ << ", parent =" << (void*)item->parentItem()
+ << ", pos =" << item->pos()
+ << ", z =" << item->zValue() << ", flags = "
+ << item->flags() << ")";
+ return debug;
+}
+
+QDebug operator<<(QDebug debug, QGraphicsObject *item)
+{
+ if (!item) {
+ debug << "QGraphicsObject(0)";
+ return debug;
+ }
+
+ debug.nospace() << item->metaObject()->className() << '(' << (void*)item;
+ if (!item->objectName().isEmpty())
+ debug << ", name = " << item->objectName();
+ debug.nospace() << ", parent = " << ((void*)item->parentItem())
+ << ", pos = " << item->pos()
+ << ", z = " << item->zValue() << ", flags = "
+ << item->flags() << ')';
+ return debug.space();
+}
+
+QDebug operator<<(QDebug debug, QGraphicsItem::GraphicsItemChange change)
+{
+ const char *str = "UnknownChange";
+ switch (change) {
+ case QGraphicsItem::ItemChildAddedChange:
+ str = "ItemChildAddedChange";
+ break;
+ case QGraphicsItem::ItemChildRemovedChange:
+ str = "ItemChildRemovedChange";
+ break;
+ case QGraphicsItem::ItemCursorChange:
+ str = "ItemCursorChange";
+ break;
+ case QGraphicsItem::ItemCursorHasChanged:
+ str = "ItemCursorHasChanged";
+ break;
+ case QGraphicsItem::ItemEnabledChange:
+ str = "ItemEnabledChange";
+ break;
+ case QGraphicsItem::ItemEnabledHasChanged:
+ str = "ItemEnabledHasChanged";
+ break;
+ case QGraphicsItem::ItemFlagsChange:
+ str = "ItemFlagsChange";
+ break;
+ case QGraphicsItem::ItemFlagsHaveChanged:
+ str = "ItemFlagsHaveChanged";
+ break;
+ case QGraphicsItem::ItemMatrixChange:
+ str = "ItemMatrixChange";
+ break;
+ case QGraphicsItem::ItemParentChange:
+ str = "ItemParentChange";
+ break;
+ case QGraphicsItem::ItemParentHasChanged:
+ str = "ItemParentHasChanged";
+ break;
+ case QGraphicsItem::ItemPositionChange:
+ str = "ItemPositionChange";
+ break;
+ case QGraphicsItem::ItemPositionHasChanged:
+ str = "ItemPositionHasChanged";
+ break;
+ case QGraphicsItem::ItemSceneChange:
+ str = "ItemSceneChange";
+ break;
+ case QGraphicsItem::ItemSceneHasChanged:
+ str = "ItemSceneHasChanged";
+ break;
+ case QGraphicsItem::ItemSelectedChange:
+ str = "ItemSelectedChange";
+ break;
+ case QGraphicsItem::ItemSelectedHasChanged:
+ str = "ItemSelectedHasChanged";
+ break;
+ case QGraphicsItem::ItemToolTipChange:
+ str = "ItemToolTipChange";
+ break;
+ case QGraphicsItem::ItemToolTipHasChanged:
+ str = "ItemToolTipHasChanged";
+ break;
+ case QGraphicsItem::ItemTransformChange:
+ str = "ItemTransformChange";
+ break;
+ case QGraphicsItem::ItemTransformHasChanged:
+ str = "ItemTransformHasChanged";
+ break;
+ case QGraphicsItem::ItemVisibleChange:
+ str = "ItemVisibleChange";
+ break;
+ case QGraphicsItem::ItemVisibleHasChanged:
+ str = "ItemVisibleHasChanged";
+ break;
+ case QGraphicsItem::ItemZValueChange:
+ str = "ItemZValueChange";
+ break;
+ case QGraphicsItem::ItemZValueHasChanged:
+ str = "ItemZValueHasChanged";
+ break;
+ case QGraphicsItem::ItemOpacityChange:
+ str = "ItemOpacityChange";
+ break;
+ case QGraphicsItem::ItemOpacityHasChanged:
+ str = "ItemOpacityHasChanged";
+ break;
+ case QGraphicsItem::ItemScenePositionHasChanged:
+ str = "ItemScenePositionHasChanged";
+ break;
+ case QGraphicsItem::ItemRotationChange:
+ str = "ItemRotationChange";
+ break;
+ case QGraphicsItem::ItemRotationHasChanged:
+ str = "ItemRotationHasChanged";
+ break;
+ case QGraphicsItem::ItemScaleChange:
+ str = "ItemScaleChange";
+ break;
+ case QGraphicsItem::ItemScaleHasChanged:
+ str = "ItemScaleHasChanged";
+ break;
+ case QGraphicsItem::ItemTransformOriginPointChange:
+ str = "ItemTransformOriginPointChange";
+ break;
+ case QGraphicsItem::ItemTransformOriginPointHasChanged:
+ str = "ItemTransformOriginPointHasChanged";
+ break;
+ }
+ debug << str;
+ return debug;
+}
+
+QDebug operator<<(QDebug debug, QGraphicsItem::GraphicsItemFlag flag)
+{
+ const char *str = "UnknownFlag";
+ switch (flag) {
+ case QGraphicsItem::ItemIsMovable:
+ str = "ItemIsMovable";
+ break;
+ case QGraphicsItem::ItemIsSelectable:
+ str = "ItemIsSelectable";
+ break;
+ case QGraphicsItem::ItemIsFocusable:
+ str = "ItemIsFocusable";
+ break;
+ case QGraphicsItem::ItemClipsToShape:
+ str = "ItemClipsToShape";
+ break;
+ case QGraphicsItem::ItemClipsChildrenToShape:
+ str = "ItemClipsChildrenToShape";
+ break;
+ case QGraphicsItem::ItemIgnoresTransformations:
+ str = "ItemIgnoresTransformations";
+ break;
+ case QGraphicsItem::ItemIgnoresParentOpacity:
+ str = "ItemIgnoresParentOpacity";
+ break;
+ case QGraphicsItem::ItemDoesntPropagateOpacityToChildren:
+ str = "ItemDoesntPropagateOpacityToChildren";
+ break;
+ case QGraphicsItem::ItemStacksBehindParent:
+ str = "ItemStacksBehindParent";
+ break;
+ case QGraphicsItem::ItemUsesExtendedStyleOption:
+ str = "ItemUsesExtendedStyleOption";
+ break;
+ case QGraphicsItem::ItemHasNoContents:
+ str = "ItemHasNoContents";
+ break;
+ case QGraphicsItem::ItemSendsGeometryChanges:
+ str = "ItemSendsGeometryChanges";
+ break;
+ case QGraphicsItem::ItemAcceptsInputMethod:
+ str = "ItemAcceptsInputMethod";
+ break;
+ case QGraphicsItem::ItemNegativeZStacksBehindParent:
+ str = "ItemNegativeZStacksBehindParent";
+ break;
+ case QGraphicsItem::ItemIsPanel:
+ str = "ItemIsPanel";
+ break;
+ case QGraphicsItem::ItemIsFocusScope:
+ str = "ItemIsFocusScope";
+ break;
+ case QGraphicsItem::ItemSendsScenePositionChanges:
+ str = "ItemSendsScenePositionChanges";
+ break;
+ case QGraphicsItem::ItemStopsClickFocusPropagation:
+ str = "ItemStopsClickFocusPropagation";
+ break;
+ case QGraphicsItem::ItemStopsFocusHandling:
+ str = "ItemStopsFocusHandling";
+ break;
+ }
+ debug << str;
+ return debug;
+}
+
+QDebug operator<<(QDebug debug, QGraphicsItem::GraphicsItemFlags flags)
+{
+ debug << '(';
+ bool f = false;
+ for (int i = 0; i < 17; ++i) {
+ if (flags & (1 << i)) {
+ if (f)
+ debug << '|';
+ f = true;
+ debug << QGraphicsItem::GraphicsItemFlag(int(flags & (1 << i)));
+ }
+ }
+ debug << ')';
+ return debug;
+}
+
+#endif
+
+QT_END_NAMESPACE
+
+#include "moc_qgraphicsitem.cpp"
+
+#endif // QT_NO_GRAPHICSVIEW
diff --git a/src/widgets/graphicsview/qgraphicsitem.h b/src/widgets/graphicsview/qgraphicsitem.h
new file mode 100644
index 0000000000..67c9cd3e2a
--- /dev/null
+++ b/src/widgets/graphicsview/qgraphicsitem.h
@@ -0,0 +1,1172 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** 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 Technology Preview License Agreement accompanying
+** this package.
+**
+** 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.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QGRAPHICSITEM_H
+#define QGRAPHICSITEM_H
+
+#include <QtCore/qglobal.h>
+#include <QtCore/qobject.h>
+#include <QtCore/qvariant.h>
+#include <QtCore/qrect.h>
+#include <QtCore/qscopedpointer.h>
+#include <QtGui/qpainterpath.h>
+#include <QtGui/qpixmap.h>
+
+class tst_QGraphicsItem;
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+#if !defined(QT_NO_GRAPHICSVIEW) || (QT_EDITION & QT_MODULE_GRAPHICSVIEW) != QT_MODULE_GRAPHICSVIEW
+
+class QBrush;
+class QCursor;
+class QFocusEvent;
+class QGraphicsEffect;
+class QGraphicsItemGroup;
+class QGraphicsObject;
+class QGraphicsSceneContextMenuEvent;
+class QGraphicsSceneDragDropEvent;
+class QGraphicsSceneEvent;
+class QGraphicsSceneHoverEvent;
+class QGraphicsSceneMouseEvent;
+class QGraphicsSceneWheelEvent;
+class QGraphicsScene;
+class QGraphicsTransform;
+class QGraphicsWidget;
+class QInputMethodEvent;
+class QKeyEvent;
+class QMatrix;
+class QMenu;
+class QPainter;
+class QPen;
+class QPointF;
+class QRectF;
+class QStyleOptionGraphicsItem;
+
+class QGraphicsItemPrivate;
+class Q_GUI_EXPORT QGraphicsItem
+{
+public:
+ enum GraphicsItemFlag {
+ ItemIsMovable = 0x1,
+ ItemIsSelectable = 0x2,
+ ItemIsFocusable = 0x4,
+ ItemClipsToShape = 0x8,
+ ItemClipsChildrenToShape = 0x10,
+ ItemIgnoresTransformations = 0x20,
+ ItemIgnoresParentOpacity = 0x40,
+ ItemDoesntPropagateOpacityToChildren = 0x80,
+ ItemStacksBehindParent = 0x100,
+ ItemUsesExtendedStyleOption = 0x200,
+ ItemHasNoContents = 0x400,
+ ItemSendsGeometryChanges = 0x800,
+ ItemAcceptsInputMethod = 0x1000,
+ ItemNegativeZStacksBehindParent = 0x2000,
+ ItemIsPanel = 0x4000,
+ ItemIsFocusScope = 0x8000, // internal
+ ItemSendsScenePositionChanges = 0x10000,
+ ItemStopsClickFocusPropagation = 0x20000,
+ ItemStopsFocusHandling = 0x40000
+ // NB! Don't forget to increase the d_ptr->flags bit field by 1 when adding a new flag.
+ };
+ Q_DECLARE_FLAGS(GraphicsItemFlags, GraphicsItemFlag)
+
+ enum GraphicsItemChange {
+ ItemPositionChange,
+ ItemMatrixChange,
+ ItemVisibleChange,
+ ItemEnabledChange,
+ ItemSelectedChange,
+ ItemParentChange,
+ ItemChildAddedChange,
+ ItemChildRemovedChange,
+ ItemTransformChange,
+ ItemPositionHasChanged,
+ ItemTransformHasChanged,
+ ItemSceneChange,
+ ItemVisibleHasChanged,
+ ItemEnabledHasChanged,
+ ItemSelectedHasChanged,
+ ItemParentHasChanged,
+ ItemSceneHasChanged,
+ ItemCursorChange,
+ ItemCursorHasChanged,
+ ItemToolTipChange,
+ ItemToolTipHasChanged,
+ ItemFlagsChange,
+ ItemFlagsHaveChanged,
+ ItemZValueChange,
+ ItemZValueHasChanged,
+ ItemOpacityChange,
+ ItemOpacityHasChanged,
+ ItemScenePositionHasChanged,
+ ItemRotationChange,
+ ItemRotationHasChanged,
+ ItemScaleChange,
+ ItemScaleHasChanged,
+ ItemTransformOriginPointChange,
+ ItemTransformOriginPointHasChanged
+ };
+
+ enum CacheMode {
+ NoCache,
+ ItemCoordinateCache,
+ DeviceCoordinateCache
+ };
+
+ enum PanelModality
+ {
+ NonModal,
+ PanelModal,
+ SceneModal
+ };
+
+ QGraphicsItem(QGraphicsItem *parent = 0
+#ifndef Q_QDOC
+ // ### obsolete argument
+ , QGraphicsScene *scene = 0
+#endif
+ );
+ virtual ~QGraphicsItem();
+
+ QGraphicsScene *scene() const;
+
+ QGraphicsItem *parentItem() const;
+ QGraphicsItem *topLevelItem() const;
+ QGraphicsObject *parentObject() const;
+ QGraphicsWidget *parentWidget() const;
+ QGraphicsWidget *topLevelWidget() const;
+ QGraphicsWidget *window() const;
+ QGraphicsItem *panel() const;
+ void setParentItem(QGraphicsItem *parent);
+ QList<QGraphicsItem *> children() const; // ### obsolete
+ QList<QGraphicsItem *> childItems() const;
+ bool isWidget() const;
+ bool isWindow() const;
+ bool isPanel() const;
+
+ QGraphicsObject *toGraphicsObject();
+ const QGraphicsObject *toGraphicsObject() const;
+
+ QGraphicsItemGroup *group() const;
+ void setGroup(QGraphicsItemGroup *group);
+
+ GraphicsItemFlags flags() const;
+ void setFlag(GraphicsItemFlag flag, bool enabled = true);
+ void setFlags(GraphicsItemFlags flags);
+
+ CacheMode cacheMode() const;
+ void setCacheMode(CacheMode mode, const QSize &cacheSize = QSize());
+
+ PanelModality panelModality() const;
+ void setPanelModality(PanelModality panelModality);
+ bool isBlockedByModalPanel(QGraphicsItem **blockingPanel = 0) const;
+
+#ifndef QT_NO_TOOLTIP
+ QString toolTip() const;
+ void setToolTip(const QString &toolTip);
+#endif
+
+#ifndef QT_NO_CURSOR
+ QCursor cursor() const;
+ void setCursor(const QCursor &cursor);
+ bool hasCursor() const;
+ void unsetCursor();
+#endif
+
+ bool isVisible() const;
+ bool isVisibleTo(const QGraphicsItem *parent) const;
+ void setVisible(bool visible);
+ inline void hide() { setVisible(false); }
+ inline void show() { setVisible(true); }
+
+ bool isEnabled() const;
+ void setEnabled(bool enabled);
+
+ bool isSelected() const;
+ void setSelected(bool selected);
+
+ bool acceptDrops() const;
+ void setAcceptDrops(bool on);
+
+ qreal opacity() const;
+ qreal effectiveOpacity() const;
+ void setOpacity(qreal opacity);
+
+#ifndef QT_NO_GRAPHICSEFFECT
+ // Effect
+ QGraphicsEffect *graphicsEffect() const;
+ void setGraphicsEffect(QGraphicsEffect *effect);
+#endif //QT_NO_GRAPHICSEFFECT
+
+ Qt::MouseButtons acceptedMouseButtons() const;
+ void setAcceptedMouseButtons(Qt::MouseButtons buttons);
+
+ bool acceptsHoverEvents() const; // ### obsolete
+ void setAcceptsHoverEvents(bool enabled); // ### obsolete
+ bool acceptHoverEvents() const;
+ void setAcceptHoverEvents(bool enabled);
+ bool acceptTouchEvents() const;
+ void setAcceptTouchEvents(bool enabled);
+
+ bool filtersChildEvents() const;
+ void setFiltersChildEvents(bool enabled);
+
+ bool handlesChildEvents() const;
+ void setHandlesChildEvents(bool enabled);
+
+ bool isActive() const;
+ void setActive(bool active);
+
+ bool hasFocus() const;
+ void setFocus(Qt::FocusReason focusReason = Qt::OtherFocusReason);
+ void clearFocus();
+
+ QGraphicsItem *focusProxy() const;
+ void setFocusProxy(QGraphicsItem *item);
+
+ QGraphicsItem *focusItem() const;
+ QGraphicsItem *focusScopeItem() const;
+
+ void grabMouse();
+ void ungrabMouse();
+ void grabKeyboard();
+ void ungrabKeyboard();
+
+ // Positioning in scene coordinates
+ QPointF pos() const;
+ inline qreal x() const { return pos().x(); }
+ void setX(qreal x);
+ inline qreal y() const { return pos().y(); }
+ void setY(qreal y);
+ QPointF scenePos() const;
+ void setPos(const QPointF &pos);
+ inline void setPos(qreal x, qreal y);
+ inline void moveBy(qreal dx, qreal dy) { setPos(pos().x() + dx, pos().y() + dy); }
+
+ void ensureVisible(const QRectF &rect = QRectF(), int xmargin = 50, int ymargin = 50);
+ inline void ensureVisible(qreal x, qreal y, qreal w, qreal h, int xmargin = 50, int ymargin = 50);
+
+ // Local transformation
+ QMatrix matrix() const;
+ QMatrix sceneMatrix() const;
+ void setMatrix(const QMatrix &matrix, bool combine = false);
+ void resetMatrix();
+ QTransform transform() const;
+ QTransform sceneTransform() const;
+ QTransform deviceTransform(const QTransform &viewportTransform) const;
+ QTransform itemTransform(const QGraphicsItem *other, bool *ok = 0) const;
+ void setTransform(const QTransform &matrix, bool combine = false);
+ void resetTransform();
+
+ void rotate(qreal angle); // ### obsolete
+ void scale(qreal sx, qreal sy); // ### obsolete
+ void shear(qreal sh, qreal sv); // ### obsolete
+ void translate(qreal dx, qreal dy); // ### obsolete
+
+ void setRotation(qreal angle);
+ qreal rotation() const;
+
+ void setScale(qreal scale);
+ qreal scale() const;
+
+ QList<QGraphicsTransform *> transformations() const;
+ void setTransformations(const QList<QGraphicsTransform *> &transformations);
+
+ QPointF transformOriginPoint() const;
+ void setTransformOriginPoint(const QPointF &origin);
+ inline void setTransformOriginPoint(qreal ax, qreal ay)
+ { setTransformOriginPoint(QPointF(ax,ay)); }
+
+ virtual void advance(int phase);
+
+ // Stacking order
+ qreal zValue() const;
+ void setZValue(qreal z);
+ void stackBefore(const QGraphicsItem *sibling);
+
+ // Hit test
+ virtual QRectF boundingRect() const = 0;
+ QRectF childrenBoundingRect() const;
+ QRectF sceneBoundingRect() const;
+ virtual QPainterPath shape() const;
+ bool isClipped() const;
+ QPainterPath clipPath() const;
+ virtual bool contains(const QPointF &point) const;
+ virtual bool collidesWithItem(const QGraphicsItem *other, Qt::ItemSelectionMode mode = Qt::IntersectsItemShape) const;
+ virtual bool collidesWithPath(const QPainterPath &path, Qt::ItemSelectionMode mode = Qt::IntersectsItemShape) const;
+ QList<QGraphicsItem *> collidingItems(Qt::ItemSelectionMode mode = Qt::IntersectsItemShape) const;
+ bool isObscured() const;
+ bool isObscured(const QRectF &rect) const; // ### Qt 5: merge with isObscured(), add QRectF arg to isObscuredBy()
+ inline bool isObscured(qreal x, qreal y, qreal w, qreal h) const;
+ virtual bool isObscuredBy(const QGraphicsItem *item) const;
+ virtual QPainterPath opaqueArea() const;
+
+ QRegion boundingRegion(const QTransform &itemToDeviceTransform) const;
+ qreal boundingRegionGranularity() const;
+ void setBoundingRegionGranularity(qreal granularity);
+
+ // Drawing
+ virtual void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = 0) = 0;
+ void update(const QRectF &rect = QRectF());
+ inline void update(qreal x, qreal y, qreal width, qreal height);
+ void scroll(qreal dx, qreal dy, const QRectF &rect = QRectF());
+
+ // Coordinate mapping
+ QPointF mapToItem(const QGraphicsItem *item, const QPointF &point) const;
+ QPointF mapToParent(const QPointF &point) const;
+ QPointF mapToScene(const QPointF &point) const;
+ QPolygonF mapToItem(const QGraphicsItem *item, const QRectF &rect) const;
+ QPolygonF mapToParent(const QRectF &rect) const;
+ QPolygonF mapToScene(const QRectF &rect) const;
+ QRectF mapRectToItem(const QGraphicsItem *item, const QRectF &rect) const;
+ QRectF mapRectToParent(const QRectF &rect) const;
+ QRectF mapRectToScene(const QRectF &rect) const;
+ QPolygonF mapToItem(const QGraphicsItem *item, const QPolygonF &polygon) const;
+ QPolygonF mapToParent(const QPolygonF &polygon) const;
+ QPolygonF mapToScene(const QPolygonF &polygon) const;
+ QPainterPath mapToItem(const QGraphicsItem *item, const QPainterPath &path) const;
+ QPainterPath mapToParent(const QPainterPath &path) const;
+ QPainterPath mapToScene(const QPainterPath &path) const;
+ QPointF mapFromItem(const QGraphicsItem *item, const QPointF &point) const;
+ QPointF mapFromParent(const QPointF &point) const;
+ QPointF mapFromScene(const QPointF &point) const;
+ QPolygonF mapFromItem(const QGraphicsItem *item, const QRectF &rect) const;
+ QPolygonF mapFromParent(const QRectF &rect) const;
+ QPolygonF mapFromScene(const QRectF &rect) const;
+ QRectF mapRectFromItem(const QGraphicsItem *item, const QRectF &rect) const;
+ QRectF mapRectFromParent(const QRectF &rect) const;
+ QRectF mapRectFromScene(const QRectF &rect) const;
+ QPolygonF mapFromItem(const QGraphicsItem *item, const QPolygonF &polygon) const;
+ QPolygonF mapFromParent(const QPolygonF &polygon) const;
+ QPolygonF mapFromScene(const QPolygonF &polygon) const;
+ QPainterPath mapFromItem(const QGraphicsItem *item, const QPainterPath &path) const;
+ QPainterPath mapFromParent(const QPainterPath &path) const;
+ QPainterPath mapFromScene(const QPainterPath &path) const;
+
+ inline QPointF mapToItem(const QGraphicsItem *item, qreal x, qreal y) const;
+ inline QPointF mapToParent(qreal x, qreal y) const;
+ inline QPointF mapToScene(qreal x, qreal y) const;
+ inline QPolygonF mapToItem(const QGraphicsItem *item, qreal x, qreal y, qreal w, qreal h) const;
+ inline QPolygonF mapToParent(qreal x, qreal y, qreal w, qreal h) const;
+ inline QPolygonF mapToScene(qreal x, qreal y, qreal w, qreal h) const;
+ inline QRectF mapRectToItem(const QGraphicsItem *item, qreal x, qreal y, qreal w, qreal h) const;
+ inline QRectF mapRectToParent(qreal x, qreal y, qreal w, qreal h) const;
+ inline QRectF mapRectToScene(qreal x, qreal y, qreal w, qreal h) const;
+ inline QPointF mapFromItem(const QGraphicsItem *item, qreal x, qreal y) const;
+ inline QPointF mapFromParent(qreal x, qreal y) const;
+ inline QPointF mapFromScene(qreal x, qreal y) const;
+ inline QPolygonF mapFromItem(const QGraphicsItem *item, qreal x, qreal y, qreal w, qreal h) const;
+ inline QPolygonF mapFromParent(qreal x, qreal y, qreal w, qreal h) const;
+ inline QPolygonF mapFromScene(qreal x, qreal y, qreal w, qreal h) const;
+ inline QRectF mapRectFromItem(const QGraphicsItem *item, qreal x, qreal y, qreal w, qreal h) const;
+ inline QRectF mapRectFromParent(qreal x, qreal y, qreal w, qreal h) const;
+ inline QRectF mapRectFromScene(qreal x, qreal y, qreal w, qreal h) const;
+
+ bool isAncestorOf(const QGraphicsItem *child) const;
+ QGraphicsItem *commonAncestorItem(const QGraphicsItem *other) const;
+ bool isUnderMouse() const;
+
+ // Custom data
+ QVariant data(int key) const;
+ void setData(int key, const QVariant &value);
+
+ Qt::InputMethodHints inputMethodHints() const;
+ void setInputMethodHints(Qt::InputMethodHints hints);
+
+ enum {
+ Type = 1,
+ UserType = 65536
+ };
+ virtual int type() const;
+
+ void installSceneEventFilter(QGraphicsItem *filterItem);
+ void removeSceneEventFilter(QGraphicsItem *filterItem);
+
+protected:
+ void updateMicroFocus();
+ virtual bool sceneEventFilter(QGraphicsItem *watched, QEvent *event);
+ virtual bool sceneEvent(QEvent *event);
+ virtual void contextMenuEvent(QGraphicsSceneContextMenuEvent *event);
+ virtual void dragEnterEvent(QGraphicsSceneDragDropEvent *event);
+ virtual void dragLeaveEvent(QGraphicsSceneDragDropEvent *event);
+ virtual void dragMoveEvent(QGraphicsSceneDragDropEvent *event);
+ virtual void dropEvent(QGraphicsSceneDragDropEvent *event);
+ virtual void focusInEvent(QFocusEvent *event);
+ virtual void focusOutEvent(QFocusEvent *event);
+ virtual void hoverEnterEvent(QGraphicsSceneHoverEvent *event);
+ virtual void hoverMoveEvent(QGraphicsSceneHoverEvent *event);
+ virtual void hoverLeaveEvent(QGraphicsSceneHoverEvent *event);
+ virtual void keyPressEvent(QKeyEvent *event);
+ virtual void keyReleaseEvent(QKeyEvent *event);
+ virtual void mousePressEvent(QGraphicsSceneMouseEvent *event);
+ virtual void mouseMoveEvent(QGraphicsSceneMouseEvent *event);
+ virtual void mouseReleaseEvent(QGraphicsSceneMouseEvent *event);
+ virtual void mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event);
+ virtual void wheelEvent(QGraphicsSceneWheelEvent *event);
+ virtual void inputMethodEvent(QInputMethodEvent *event);
+ virtual QVariant inputMethodQuery(Qt::InputMethodQuery query) const;
+
+ virtual QVariant itemChange(GraphicsItemChange change, const QVariant &value);
+
+ enum Extension {
+ UserExtension = 0x80000000
+ };
+ virtual bool supportsExtension(Extension extension) const;
+ virtual void setExtension(Extension extension, const QVariant &variant);
+ virtual QVariant extension(const QVariant &variant) const;
+
+protected:
+ QGraphicsItem(QGraphicsItemPrivate &dd,
+ QGraphicsItem *parent, QGraphicsScene *scene);
+ QScopedPointer<QGraphicsItemPrivate> d_ptr;
+
+ void addToIndex();
+ void removeFromIndex();
+ void prepareGeometryChange();
+
+private:
+ Q_DISABLE_COPY(QGraphicsItem)
+ Q_DECLARE_PRIVATE(QGraphicsItem)
+ friend class QGraphicsItemGroup;
+ friend class QGraphicsScene;
+ friend class QGraphicsScenePrivate;
+ friend class QGraphicsSceneFindItemBspTreeVisitor;
+ friend class QGraphicsSceneBspTree;
+ friend class QGraphicsView;
+ friend class QGraphicsViewPrivate;
+ friend class QGraphicsObject;
+ friend class QGraphicsWidget;
+ friend class QGraphicsWidgetPrivate;
+ friend class QGraphicsProxyWidgetPrivate;
+ friend class QGraphicsSceneIndex;
+ friend class QGraphicsSceneIndexPrivate;
+ friend class QGraphicsSceneBspTreeIndex;
+ friend class QGraphicsSceneBspTreeIndexPrivate;
+ friend class QGraphicsItemEffectSourcePrivate;
+ friend class QGraphicsTransformPrivate;
+#ifndef QT_NO_GESTURES
+ friend class QGestureManager;
+#endif
+ friend class ::tst_QGraphicsItem;
+ friend bool qt_closestLeaf(const QGraphicsItem *, const QGraphicsItem *);
+ friend bool qt_closestItemFirst(const QGraphicsItem *, const QGraphicsItem *);
+};
+
+Q_DECLARE_OPERATORS_FOR_FLAGS(QGraphicsItem::GraphicsItemFlags)
+Q_DECLARE_INTERFACE(QGraphicsItem, "com.trolltech.Qt.QGraphicsItem")
+
+inline void QGraphicsItem::setPos(qreal ax, qreal ay)
+{ setPos(QPointF(ax, ay)); }
+inline void QGraphicsItem::ensureVisible(qreal ax, qreal ay, qreal w, qreal h, int xmargin, int ymargin)
+{ ensureVisible(QRectF(ax, ay, w, h), xmargin, ymargin); }
+inline void QGraphicsItem::update(qreal ax, qreal ay, qreal width, qreal height)
+{ update(QRectF(ax, ay, width, height)); }
+inline bool QGraphicsItem::isObscured(qreal ax, qreal ay, qreal w, qreal h) const
+{ return isObscured(QRectF(ax, ay, w, h)); }
+inline QPointF QGraphicsItem::mapToItem(const QGraphicsItem *item, qreal ax, qreal ay) const
+{ return mapToItem(item, QPointF(ax, ay)); }
+inline QPointF QGraphicsItem::mapToParent(qreal ax, qreal ay) const
+{ return mapToParent(QPointF(ax, ay)); }
+inline QPointF QGraphicsItem::mapToScene(qreal ax, qreal ay) const
+{ return mapToScene(QPointF(ax, ay)); }
+inline QPointF QGraphicsItem::mapFromItem(const QGraphicsItem *item, qreal ax, qreal ay) const
+{ return mapFromItem(item, QPointF(ax, ay)); }
+inline QPointF QGraphicsItem::mapFromParent(qreal ax, qreal ay) const
+{ return mapFromParent(QPointF(ax, ay)); }
+inline QPointF QGraphicsItem::mapFromScene(qreal ax, qreal ay) const
+{ return mapFromScene(QPointF(ax, ay)); }
+inline QPolygonF QGraphicsItem::mapToItem(const QGraphicsItem *item, qreal ax, qreal ay, qreal w, qreal h) const
+{ return mapToItem(item, QRectF(ax, ay, w, h)); }
+inline QPolygonF QGraphicsItem::mapToParent(qreal ax, qreal ay, qreal w, qreal h) const
+{ return mapToParent(QRectF(ax, ay, w, h)); }
+inline QPolygonF QGraphicsItem::mapToScene(qreal ax, qreal ay, qreal w, qreal h) const
+{ return mapToScene(QRectF(ax, ay, w, h)); }
+inline QRectF QGraphicsItem::mapRectToItem(const QGraphicsItem *item, qreal ax, qreal ay, qreal w, qreal h) const
+{ return mapRectToItem(item, QRectF(ax, ay, w, h)); }
+inline QRectF QGraphicsItem::mapRectToParent(qreal ax, qreal ay, qreal w, qreal h) const
+{ return mapRectToParent(QRectF(ax, ay, w, h)); }
+inline QRectF QGraphicsItem::mapRectToScene(qreal ax, qreal ay, qreal w, qreal h) const
+{ return mapRectToScene(QRectF(ax, ay, w, h)); }
+inline QPolygonF QGraphicsItem::mapFromItem(const QGraphicsItem *item, qreal ax, qreal ay, qreal w, qreal h) const
+{ return mapFromItem(item, QRectF(ax, ay, w, h)); }
+inline QPolygonF QGraphicsItem::mapFromParent(qreal ax, qreal ay, qreal w, qreal h) const
+{ return mapFromParent(QRectF(ax, ay, w, h)); }
+inline QPolygonF QGraphicsItem::mapFromScene(qreal ax, qreal ay, qreal w, qreal h) const
+{ return mapFromScene(QRectF(ax, ay, w, h)); }
+inline QRectF QGraphicsItem::mapRectFromItem(const QGraphicsItem *item, qreal ax, qreal ay, qreal w, qreal h) const
+{ return mapRectFromItem(item, QRectF(ax, ay, w, h)); }
+inline QRectF QGraphicsItem::mapRectFromParent(qreal ax, qreal ay, qreal w, qreal h) const
+{ return mapRectFromParent(QRectF(ax, ay, w, h)); }
+inline QRectF QGraphicsItem::mapRectFromScene(qreal ax, qreal ay, qreal w, qreal h) const
+{ return mapRectFromScene(QRectF(ax, ay, w, h)); }
+
+
+class Q_GUI_EXPORT QGraphicsObject : public QObject, public QGraphicsItem
+{
+ Q_OBJECT
+ Q_PROPERTY(QGraphicsObject * parent READ parentObject WRITE setParentItem NOTIFY parentChanged DESIGNABLE false)
+ Q_PROPERTY(qreal opacity READ opacity WRITE setOpacity NOTIFY opacityChanged FINAL)
+ Q_PROPERTY(bool enabled READ isEnabled WRITE setEnabled NOTIFY enabledChanged)
+ Q_PROPERTY(bool visible READ isVisible WRITE setVisible NOTIFY visibleChanged FINAL)
+ Q_PROPERTY(QPointF pos READ pos WRITE setPos FINAL)
+ Q_PROPERTY(qreal x READ x WRITE setX NOTIFY xChanged FINAL)
+ Q_PROPERTY(qreal y READ y WRITE setY NOTIFY yChanged FINAL)
+ Q_PROPERTY(qreal z READ zValue WRITE setZValue NOTIFY zChanged FINAL)
+ Q_PROPERTY(qreal rotation READ rotation WRITE setRotation NOTIFY rotationChanged)
+ Q_PROPERTY(qreal scale READ scale WRITE setScale NOTIFY scaleChanged)
+ Q_PROPERTY(QPointF transformOriginPoint READ transformOriginPoint WRITE setTransformOriginPoint)
+#ifndef QT_NO_GRAPHICSEFFECT
+ Q_PROPERTY(QGraphicsEffect *effect READ graphicsEffect WRITE setGraphicsEffect)
+#endif
+ Q_PRIVATE_PROPERTY(QGraphicsItem::d_func(), QDeclarativeListProperty<QGraphicsObject> children READ childrenList DESIGNABLE false NOTIFY childrenChanged)
+ Q_PRIVATE_PROPERTY(QGraphicsItem::d_func(), qreal width READ width WRITE setWidth NOTIFY widthChanged RESET resetWidth FINAL)
+ Q_PRIVATE_PROPERTY(QGraphicsItem::d_func(), qreal height READ height WRITE setHeight NOTIFY heightChanged RESET resetHeight FINAL)
+ Q_CLASSINFO("DefaultProperty", "children")
+ Q_INTERFACES(QGraphicsItem)
+public:
+ QGraphicsObject(QGraphicsItem *parent = 0);
+
+ // ### Qt 5: Disambiguate
+#ifdef Q_NO_USING_KEYWORD
+ const QObjectList &children() const { return QObject::children(); }
+#else
+ using QObject::children;
+#endif
+
+#ifndef QT_NO_GESTURES
+ void grabGesture(Qt::GestureType type, Qt::GestureFlags flags = Qt::GestureFlags());
+ void ungrabGesture(Qt::GestureType type);
+#endif
+
+protected Q_SLOTS:
+ void updateMicroFocus();
+
+Q_SIGNALS:
+ void parentChanged();
+ void opacityChanged();
+ void visibleChanged();
+ void enabledChanged();
+ void xChanged();
+ void yChanged();
+ void zChanged();
+ void rotationChanged();
+ void scaleChanged();
+ void childrenChanged();
+ void widthChanged();
+ void heightChanged();
+
+protected:
+ QGraphicsObject(QGraphicsItemPrivate &dd, QGraphicsItem *parent, QGraphicsScene *scene);
+private:
+ friend class QGraphicsItem;
+ friend class QGraphicsItemPrivate;
+};
+
+
+class QAbstractGraphicsShapeItemPrivate;
+class Q_GUI_EXPORT QAbstractGraphicsShapeItem : public QGraphicsItem
+{
+public:
+ QAbstractGraphicsShapeItem(QGraphicsItem *parent = 0
+#ifndef Q_QDOC
+ // ### obsolete argument
+ , QGraphicsScene *scene = 0
+#endif
+ );
+ ~QAbstractGraphicsShapeItem();
+
+ QPen pen() const;
+ void setPen(const QPen &pen);
+
+ QBrush brush() const;
+ void setBrush(const QBrush &brush);
+
+ bool isObscuredBy(const QGraphicsItem *item) const;
+ QPainterPath opaqueArea() const;
+
+protected:
+ QAbstractGraphicsShapeItem(QAbstractGraphicsShapeItemPrivate &dd,
+ QGraphicsItem *parent, QGraphicsScene *scene);
+
+private:
+ Q_DISABLE_COPY(QAbstractGraphicsShapeItem)
+ Q_DECLARE_PRIVATE(QAbstractGraphicsShapeItem)
+};
+
+class QGraphicsPathItemPrivate;
+class Q_GUI_EXPORT QGraphicsPathItem : public QAbstractGraphicsShapeItem
+{
+public:
+ QGraphicsPathItem(QGraphicsItem *parent = 0
+#ifndef Q_QDOC
+ // ### obsolete argument
+ , QGraphicsScene *scene = 0
+#endif
+ );
+ QGraphicsPathItem(const QPainterPath &path, QGraphicsItem *parent = 0
+#ifndef Q_QDOC
+ // ### obsolete argument
+ , QGraphicsScene *scene = 0
+#endif
+ );
+ ~QGraphicsPathItem();
+
+ QPainterPath path() const;
+ void setPath(const QPainterPath &path);
+
+ QRectF boundingRect() const;
+ QPainterPath shape() const;
+ bool contains(const QPointF &point) const;
+
+ void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = 0);
+
+ bool isObscuredBy(const QGraphicsItem *item) const;
+ QPainterPath opaqueArea() const;
+
+ enum { Type = 2 };
+ int type() const;
+
+protected:
+ bool supportsExtension(Extension extension) const;
+ void setExtension(Extension extension, const QVariant &variant);
+ QVariant extension(const QVariant &variant) const;
+
+private:
+ Q_DISABLE_COPY(QGraphicsPathItem)
+ Q_DECLARE_PRIVATE(QGraphicsPathItem)
+};
+
+class QGraphicsRectItemPrivate;
+class Q_GUI_EXPORT QGraphicsRectItem : public QAbstractGraphicsShapeItem
+{
+public:
+ QGraphicsRectItem(QGraphicsItem *parent = 0
+#ifndef Q_QDOC
+ // ### obsolete argument
+ , QGraphicsScene *scene = 0
+#endif
+ );
+ QGraphicsRectItem(const QRectF &rect, QGraphicsItem *parent = 0
+#ifndef Q_QDOC
+ // ### obsolete argument
+ , QGraphicsScene *scene = 0
+#endif
+ );
+ QGraphicsRectItem(qreal x, qreal y, qreal w, qreal h, QGraphicsItem *parent = 0
+#ifndef Q_QDOC
+ // ### obsolete argument
+ , QGraphicsScene *scene = 0
+#endif
+ );
+ ~QGraphicsRectItem();
+
+ QRectF rect() const;
+ void setRect(const QRectF &rect);
+ inline void setRect(qreal x, qreal y, qreal w, qreal h);
+
+ QRectF boundingRect() const;
+ QPainterPath shape() const;
+ bool contains(const QPointF &point) const;
+
+ void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = 0);
+
+ bool isObscuredBy(const QGraphicsItem *item) const;
+ QPainterPath opaqueArea() const;
+
+ enum { Type = 3 };
+ int type() const;
+
+protected:
+ bool supportsExtension(Extension extension) const;
+ void setExtension(Extension extension, const QVariant &variant);
+ QVariant extension(const QVariant &variant) const;
+
+private:
+ Q_DISABLE_COPY(QGraphicsRectItem)
+ Q_DECLARE_PRIVATE(QGraphicsRectItem)
+};
+
+inline void QGraphicsRectItem::setRect(qreal ax, qreal ay, qreal w, qreal h)
+{ setRect(QRectF(ax, ay, w, h)); }
+
+class QGraphicsEllipseItemPrivate;
+class Q_GUI_EXPORT QGraphicsEllipseItem : public QAbstractGraphicsShapeItem
+{
+public:
+ QGraphicsEllipseItem(QGraphicsItem *parent = 0
+#ifndef Q_QDOC
+ // ### obsolete argument
+ , QGraphicsScene *scene = 0
+#endif
+ );
+ QGraphicsEllipseItem(const QRectF &rect, QGraphicsItem *parent = 0
+#ifndef Q_QDOC
+ // ### obsolete argument
+ , QGraphicsScene *scene = 0
+#endif
+ );
+ QGraphicsEllipseItem(qreal x, qreal y, qreal w, qreal h, QGraphicsItem *parent = 0
+#ifndef Q_QDOC
+ // ### obsolete argument
+ , QGraphicsScene *scene = 0
+#endif
+ );
+ ~QGraphicsEllipseItem();
+
+ QRectF rect() const;
+ void setRect(const QRectF &rect);
+ inline void setRect(qreal x, qreal y, qreal w, qreal h);
+
+ int startAngle() const;
+ void setStartAngle(int angle);
+
+ int spanAngle() const;
+ void setSpanAngle(int angle);
+
+ QRectF boundingRect() const;
+ QPainterPath shape() const;
+ bool contains(const QPointF &point) const;
+
+ void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = 0);
+
+ bool isObscuredBy(const QGraphicsItem *item) const;
+ QPainterPath opaqueArea() const;
+
+ enum { Type = 4 };
+ int type() const;
+
+protected:
+ bool supportsExtension(Extension extension) const;
+ void setExtension(Extension extension, const QVariant &variant);
+ QVariant extension(const QVariant &variant) const;
+
+private:
+ Q_DISABLE_COPY(QGraphicsEllipseItem)
+ Q_DECLARE_PRIVATE(QGraphicsEllipseItem)
+};
+
+inline void QGraphicsEllipseItem::setRect(qreal ax, qreal ay, qreal w, qreal h)
+{ setRect(QRectF(ax, ay, w, h)); }
+
+class QGraphicsPolygonItemPrivate;
+class Q_GUI_EXPORT QGraphicsPolygonItem : public QAbstractGraphicsShapeItem
+{
+public:
+ QGraphicsPolygonItem(QGraphicsItem *parent = 0
+#ifndef Q_QDOC
+ // ### obsolete argument
+ , QGraphicsScene *scene = 0
+#endif
+ );
+ QGraphicsPolygonItem(const QPolygonF &polygon,
+ QGraphicsItem *parent = 0
+#ifndef Q_QDOC
+ // ### obsolete argument
+ , QGraphicsScene *scene = 0
+#endif
+ );
+ ~QGraphicsPolygonItem();
+
+ QPolygonF polygon() const;
+ void setPolygon(const QPolygonF &polygon);
+
+ Qt::FillRule fillRule() const;
+ void setFillRule(Qt::FillRule rule);
+
+ QRectF boundingRect() const;
+ QPainterPath shape() const;
+ bool contains(const QPointF &point) const;
+
+ void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = 0);
+
+ bool isObscuredBy(const QGraphicsItem *item) const;
+ QPainterPath opaqueArea() const;
+
+ enum { Type = 5 };
+ int type() const;
+
+protected:
+ bool supportsExtension(Extension extension) const;
+ void setExtension(Extension extension, const QVariant &variant);
+ QVariant extension(const QVariant &variant) const;
+
+private:
+ Q_DISABLE_COPY(QGraphicsPolygonItem)
+ Q_DECLARE_PRIVATE(QGraphicsPolygonItem)
+};
+
+class QGraphicsLineItemPrivate;
+class Q_GUI_EXPORT QGraphicsLineItem : public QGraphicsItem
+{
+public:
+ QGraphicsLineItem(QGraphicsItem *parent = 0
+#ifndef Q_QDOC
+ // ### obsolete argument
+ , QGraphicsScene *scene = 0
+#endif
+ );
+ QGraphicsLineItem(const QLineF &line, QGraphicsItem *parent = 0
+#ifndef Q_QDOC
+ // ### obsolete argument
+ , QGraphicsScene *scene = 0
+#endif
+ );
+ QGraphicsLineItem(qreal x1, qreal y1, qreal x2, qreal y2, QGraphicsItem *parent = 0
+#ifndef Q_QDOC
+ // ### obsolete argument
+ , QGraphicsScene *scene = 0
+#endif
+ );
+ ~QGraphicsLineItem();
+
+ QPen pen() const;
+ void setPen(const QPen &pen);
+
+ QLineF line() const;
+ void setLine(const QLineF &line);
+ inline void setLine(qreal x1, qreal y1, qreal x2, qreal y2)
+ { setLine(QLineF(x1, y1, x2, y2)); }
+
+ QRectF boundingRect() const;
+ QPainterPath shape() const;
+ bool contains(const QPointF &point) const;
+
+ void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = 0);
+
+ bool isObscuredBy(const QGraphicsItem *item) const;
+ QPainterPath opaqueArea() const;
+
+ enum { Type = 6 };
+ int type() const;
+
+protected:
+ bool supportsExtension(Extension extension) const;
+ void setExtension(Extension extension, const QVariant &variant);
+ QVariant extension(const QVariant &variant) const;
+
+private:
+ Q_DISABLE_COPY(QGraphicsLineItem)
+ Q_DECLARE_PRIVATE(QGraphicsLineItem)
+};
+
+class QGraphicsPixmapItemPrivate;
+class Q_GUI_EXPORT QGraphicsPixmapItem : public QGraphicsItem
+{
+public:
+ enum ShapeMode {
+ MaskShape,
+ BoundingRectShape,
+ HeuristicMaskShape
+ };
+
+ QGraphicsPixmapItem(QGraphicsItem *parent = 0
+#ifndef Q_QDOC
+ // ### obsolete argument
+ , QGraphicsScene *scene = 0
+#endif
+ );
+ QGraphicsPixmapItem(const QPixmap &pixmap, QGraphicsItem *parent = 0
+#ifndef Q_QDOC
+ // ### obsolete argument
+ , QGraphicsScene *scene = 0
+#endif
+ );
+ ~QGraphicsPixmapItem();
+
+ QPixmap pixmap() const;
+ void setPixmap(const QPixmap &pixmap);
+
+ Qt::TransformationMode transformationMode() const;
+ void setTransformationMode(Qt::TransformationMode mode);
+
+ QPointF offset() const;
+ void setOffset(const QPointF &offset);
+ inline void setOffset(qreal x, qreal y);
+
+ QRectF boundingRect() const;
+ QPainterPath shape() const;
+ bool contains(const QPointF &point) const;
+
+ void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget);
+
+ bool isObscuredBy(const QGraphicsItem *item) const;
+ QPainterPath opaqueArea() const;
+
+ enum { Type = 7 };
+ int type() const;
+
+ ShapeMode shapeMode() const;
+ void setShapeMode(ShapeMode mode);
+
+protected:
+ bool supportsExtension(Extension extension) const;
+ void setExtension(Extension extension, const QVariant &variant);
+ QVariant extension(const QVariant &variant) const;
+
+private:
+ Q_DISABLE_COPY(QGraphicsPixmapItem)
+ Q_DECLARE_PRIVATE(QGraphicsPixmapItem)
+};
+
+inline void QGraphicsPixmapItem::setOffset(qreal ax, qreal ay)
+{ setOffset(QPointF(ax, ay)); }
+
+class QGraphicsTextItemPrivate;
+class QTextDocument;
+class QTextCursor;
+class Q_GUI_EXPORT QGraphicsTextItem : public QGraphicsObject
+{
+ Q_OBJECT
+ QDOC_PROPERTY(bool openExternalLinks READ openExternalLinks WRITE setOpenExternalLinks)
+ QDOC_PROPERTY(QTextCursor textCursor READ textCursor WRITE setTextCursor)
+
+public:
+ QGraphicsTextItem(QGraphicsItem *parent = 0
+#ifndef Q_QDOC
+ // ### obsolete argument
+ , QGraphicsScene *scene = 0
+#endif
+ );
+ QGraphicsTextItem(const QString &text, QGraphicsItem *parent = 0
+#ifndef Q_QDOC
+ // ### obsolete argument
+ , QGraphicsScene *scene = 0
+#endif
+ );
+ ~QGraphicsTextItem();
+
+ QString toHtml() const;
+ void setHtml(const QString &html);
+
+ QString toPlainText() const;
+ void setPlainText(const QString &text);
+
+ QFont font() const;
+ void setFont(const QFont &font);
+
+ void setDefaultTextColor(const QColor &c);
+ QColor defaultTextColor() const;
+
+ QRectF boundingRect() const;
+ QPainterPath shape() const;
+ bool contains(const QPointF &point) const;
+
+ void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget);
+
+ bool isObscuredBy(const QGraphicsItem *item) const;
+ QPainterPath opaqueArea() const;
+
+ enum { Type = 8 };
+ int type() const;
+
+ void setTextWidth(qreal width);
+ qreal textWidth() const;
+
+ void adjustSize();
+
+ void setDocument(QTextDocument *document);
+ QTextDocument *document() const;
+
+ void setTextInteractionFlags(Qt::TextInteractionFlags flags);
+ Qt::TextInteractionFlags textInteractionFlags() const;
+
+ void setTabChangesFocus(bool b);
+ bool tabChangesFocus() const;
+
+ void setOpenExternalLinks(bool open);
+ bool openExternalLinks() const;
+
+ void setTextCursor(const QTextCursor &cursor);
+ QTextCursor textCursor() const;
+
+Q_SIGNALS:
+ void linkActivated(const QString &);
+ void linkHovered(const QString &);
+
+protected:
+ bool sceneEvent(QEvent *event);
+ void mousePressEvent(QGraphicsSceneMouseEvent *event);
+ void mouseMoveEvent(QGraphicsSceneMouseEvent *event);
+ void mouseReleaseEvent(QGraphicsSceneMouseEvent *event);
+ void mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event);
+ void contextMenuEvent(QGraphicsSceneContextMenuEvent *event);
+ void keyPressEvent(QKeyEvent *event);
+ void keyReleaseEvent(QKeyEvent *event);
+ void focusInEvent(QFocusEvent *event);
+ void focusOutEvent(QFocusEvent *event);
+ void dragEnterEvent(QGraphicsSceneDragDropEvent *event);
+ void dragLeaveEvent(QGraphicsSceneDragDropEvent *event);
+ void dragMoveEvent(QGraphicsSceneDragDropEvent *event);
+ void dropEvent(QGraphicsSceneDragDropEvent *event);
+ void inputMethodEvent(QInputMethodEvent *event);
+ void hoverEnterEvent(QGraphicsSceneHoverEvent *event);
+ void hoverMoveEvent(QGraphicsSceneHoverEvent *event);
+ void hoverLeaveEvent(QGraphicsSceneHoverEvent *event);
+
+ QVariant inputMethodQuery(Qt::InputMethodQuery query) const;
+
+ bool supportsExtension(Extension extension) const;
+ void setExtension(Extension extension, const QVariant &variant);
+ QVariant extension(const QVariant &variant) const;
+
+private:
+ Q_DISABLE_COPY(QGraphicsTextItem)
+ Q_PRIVATE_SLOT(dd, void _q_updateBoundingRect(const QSizeF &))
+ Q_PRIVATE_SLOT(dd, void _q_update(QRectF))
+ Q_PRIVATE_SLOT(dd, void _q_ensureVisible(QRectF))
+ QGraphicsTextItemPrivate *dd;
+ friend class QGraphicsTextItemPrivate;
+};
+
+class QGraphicsSimpleTextItemPrivate;
+class Q_GUI_EXPORT QGraphicsSimpleTextItem : public QAbstractGraphicsShapeItem
+{
+public:
+ QGraphicsSimpleTextItem(QGraphicsItem *parent = 0
+#ifndef Q_QDOC
+ // ### obsolete argument
+ , QGraphicsScene *scene = 0
+#endif
+ );
+ QGraphicsSimpleTextItem(const QString &text, QGraphicsItem *parent = 0
+#ifndef Q_QDOC
+ // ### obsolete argument
+ , QGraphicsScene *scene = 0
+#endif
+ );
+ ~QGraphicsSimpleTextItem();
+
+ void setText(const QString &text);
+ QString text() const;
+
+ void setFont(const QFont &font);
+ QFont font() const;
+
+ QRectF boundingRect() const;
+ QPainterPath shape() const;
+ bool contains(const QPointF &point) const;
+
+ void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget);
+
+ bool isObscuredBy(const QGraphicsItem *item) const;
+ QPainterPath opaqueArea() const;
+
+ enum { Type = 9 };
+ int type() const;
+
+protected:
+ bool supportsExtension(Extension extension) const;
+ void setExtension(Extension extension, const QVariant &variant);
+ QVariant extension(const QVariant &variant) const;
+
+private:
+ Q_DISABLE_COPY(QGraphicsSimpleTextItem)
+ Q_DECLARE_PRIVATE(QGraphicsSimpleTextItem)
+};
+
+class QGraphicsItemGroupPrivate;
+class Q_GUI_EXPORT QGraphicsItemGroup : public QGraphicsItem
+{
+public:
+ QGraphicsItemGroup(QGraphicsItem *parent = 0
+#ifndef Q_QDOC
+ // ### obsolete argument
+ , QGraphicsScene *scene = 0
+#endif
+ );
+ ~QGraphicsItemGroup();
+
+ void addToGroup(QGraphicsItem *item);
+ void removeFromGroup(QGraphicsItem *item);
+
+ QRectF boundingRect() const;
+ void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = 0);
+
+ bool isObscuredBy(const QGraphicsItem *item) const;
+ QPainterPath opaqueArea() const;
+
+ enum { Type = 10 };
+ int type() const;
+
+private:
+ Q_DISABLE_COPY(QGraphicsItemGroup)
+ Q_DECLARE_PRIVATE(QGraphicsItemGroup)
+};
+
+template <class T> inline T qgraphicsitem_cast(QGraphicsItem *item)
+{
+ return int(static_cast<T>(0)->Type) == int(QGraphicsItem::Type)
+ || (item && int(static_cast<T>(0)->Type) == item->type()) ? static_cast<T>(item) : 0;
+}
+
+template <class T> inline T qgraphicsitem_cast(const QGraphicsItem *item)
+{
+ return int(static_cast<T>(0)->Type) == int(QGraphicsItem::Type)
+ || (item && int(static_cast<T>(0)->Type) == item->type()) ? static_cast<T>(item) : 0;
+}
+
+#ifndef QT_NO_DEBUG_STREAM
+Q_GUI_EXPORT QDebug operator<<(QDebug debug, QGraphicsItem *item);
+Q_GUI_EXPORT QDebug operator<<(QDebug debug, QGraphicsObject *item);
+Q_GUI_EXPORT QDebug operator<<(QDebug debug, QGraphicsItem::GraphicsItemChange change);
+Q_GUI_EXPORT QDebug operator<<(QDebug debug, QGraphicsItem::GraphicsItemFlag flag);
+Q_GUI_EXPORT QDebug operator<<(QDebug debug, QGraphicsItem::GraphicsItemFlags flags);
+#endif
+
+QT_END_NAMESPACE
+
+Q_DECLARE_METATYPE(QGraphicsItem *)
+Q_DECLARE_METATYPE(QGraphicsScene *)
+
+QT_BEGIN_NAMESPACE
+
+#endif // QT_NO_GRAPHICSVIEW
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QGRAPHICSITEM_H
diff --git a/src/widgets/graphicsview/qgraphicsitem_p.h b/src/widgets/graphicsview/qgraphicsitem_p.h
new file mode 100644
index 0000000000..90ff43f93c
--- /dev/null
+++ b/src/widgets/graphicsview/qgraphicsitem_p.h
@@ -0,0 +1,891 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** 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 Technology Preview License Agreement accompanying
+** this package.
+**
+** 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.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QGRAPHICSITEM_P_H
+#define QGRAPHICSITEM_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of other Qt classes. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qgraphicsitem.h"
+#include "qset.h"
+#include "qpixmapcache.h"
+#include <private/qgraphicsview_p.h>
+#include "qgraphicstransform.h"
+#include <private/qgraphicstransform_p.h>
+
+#include <private/qgraphicseffect_p.h>
+#include <qgraphicseffect.h>
+
+#include <QtCore/qpoint.h>
+
+#if !defined(QT_NO_GRAPHICSVIEW) || (QT_EDITION & QT_MODULE_GRAPHICSVIEW) != QT_MODULE_GRAPHICSVIEW
+
+QT_BEGIN_NAMESPACE
+
+class QGraphicsItemPrivate;
+
+#ifndef QDECLARATIVELISTPROPERTY
+#define QDECLARATIVELISTPROPERTY
+template<typename T>
+class QDeclarativeListProperty {
+public:
+ typedef void (*AppendFunction)(QDeclarativeListProperty<T> *, T*);
+ typedef int (*CountFunction)(QDeclarativeListProperty<T> *);
+ typedef T *(*AtFunction)(QDeclarativeListProperty<T> *, int);
+ typedef void (*ClearFunction)(QDeclarativeListProperty<T> *);
+
+ QDeclarativeListProperty()
+ : object(0), data(0), append(0), count(0), at(0), clear(0), dummy1(0), dummy2(0) {}
+ QDeclarativeListProperty(QObject *o, QList<T *> &list)
+ : object(o), data(&list), append(qlist_append), count(qlist_count), at(qlist_at),
+ clear(qlist_clear), dummy1(0), dummy2(0) {}
+ QDeclarativeListProperty(QObject *o, void *d, AppendFunction a, CountFunction c = 0, AtFunction t = 0,
+ ClearFunction r = 0)
+ : object(o), data(d), append(a), count(c), at(t), clear(r), dummy1(0), dummy2(0) {}
+
+ bool operator==(const QDeclarativeListProperty &o) const {
+ return object == o.object &&
+ data == o.data &&
+ append == o.append &&
+ count == o.count &&
+ at == o.at &&
+ clear == o.clear;
+ }
+
+ QObject *object;
+ void *data;
+
+ AppendFunction append;
+
+ CountFunction count;
+ AtFunction at;
+
+ ClearFunction clear;
+
+ void *dummy1;
+ void *dummy2;
+
+private:
+ static void qlist_append(QDeclarativeListProperty *p, T *v) {
+ ((QList<T *> *)p->data)->append(v);
+ }
+ static int qlist_count(QDeclarativeListProperty *p) {
+ return ((QList<T *> *)p->data)->count();
+ }
+ static T *qlist_at(QDeclarativeListProperty *p, int idx) {
+ return ((QList<T *> *)p->data)->at(idx);
+ }
+ static void qlist_clear(QDeclarativeListProperty *p) {
+ return ((QList<T *> *)p->data)->clear();
+ }
+};
+#endif
+
+class QGraphicsItemCache
+{
+public:
+ QGraphicsItemCache() : allExposed(false) { }
+
+ // ItemCoordinateCache only
+ QRect boundingRect;
+ QSize fixedSize;
+ QPixmapCache::Key key;
+
+ // DeviceCoordinateCache only
+ struct DeviceData {
+ DeviceData() {}
+ QTransform lastTransform;
+ QPoint cacheIndent;
+ QPixmapCache::Key key;
+ };
+ QMap<QPaintDevice *, DeviceData> deviceData;
+
+ // List of logical exposed rects
+ QVector<QRectF> exposed;
+ bool allExposed;
+
+ // Empty cache
+ void purge();
+};
+
+class Q_GUI_EXPORT QGraphicsItemPrivate
+{
+ Q_DECLARE_PUBLIC(QGraphicsItem)
+public:
+ enum Extra {
+ ExtraToolTip,
+ ExtraCursor,
+ ExtraCacheData,
+ ExtraMaxDeviceCoordCacheSize,
+ ExtraBoundingRegionGranularity
+ };
+
+ enum AncestorFlag {
+ NoFlag = 0,
+ AncestorHandlesChildEvents = 0x1,
+ AncestorClipsChildren = 0x2,
+ AncestorIgnoresTransformations = 0x4,
+ AncestorFiltersChildEvents = 0x8
+ };
+
+ inline QGraphicsItemPrivate()
+ : z(0),
+ opacity(1.),
+ scene(0),
+ parent(0),
+ transformData(0),
+ graphicsEffect(0),
+ index(-1),
+ siblingIndex(-1),
+ itemDepth(-1),
+ focusProxy(0),
+ subFocusItem(0),
+ focusScopeItem(0),
+ imHints(Qt::ImhNone),
+ panelModality(QGraphicsItem::NonModal),
+ acceptedMouseButtons(0x1f),
+ visible(1),
+ explicitlyHidden(0),
+ enabled(1),
+ explicitlyDisabled(0),
+ selected(0),
+ acceptsHover(0),
+ acceptDrops(0),
+ isMemberOfGroup(0),
+ handlesChildEvents(0),
+ itemDiscovered(0),
+ hasCursor(0),
+ ancestorFlags(0),
+ cacheMode(0),
+ hasBoundingRegionGranularity(0),
+ isWidget(0),
+ dirty(0),
+ dirtyChildren(0),
+ localCollisionHack(0),
+ inSetPosHelper(0),
+ needSortChildren(0),
+ allChildrenDirty(0),
+ fullUpdatePending(0),
+ dirtyChildrenBoundingRect(1),
+ flags(0),
+ paintedViewBoundingRectsNeedRepaint(0),
+ dirtySceneTransform(1),
+ geometryChanged(1),
+ inDestructor(0),
+ isObject(0),
+ ignoreVisible(0),
+ ignoreOpacity(0),
+ acceptTouchEvents(0),
+ acceptedTouchBeginEvent(0),
+ filtersDescendantEvents(0),
+ sceneTransformTranslateOnly(0),
+ notifyBoundingRectChanged(0),
+ notifyInvalidated(0),
+ mouseSetsFocus(1),
+ explicitActivate(0),
+ wantsActive(0),
+ holesInSiblingIndex(0),
+ sequentialOrdering(1),
+ updateDueToGraphicsEffect(0),
+ scenePosDescendants(0),
+ pendingPolish(0),
+ mayHaveChildWithGraphicsEffect(0),
+ isDeclarativeItem(0),
+ sendParentChangeNotification(0),
+ globalStackingOrder(-1),
+ q_ptr(0)
+ {
+ }
+
+ inline virtual ~QGraphicsItemPrivate()
+ { }
+
+ static const QGraphicsItemPrivate *get(const QGraphicsItem *item)
+ {
+ return item->d_ptr.data();
+ }
+ static QGraphicsItemPrivate *get(QGraphicsItem *item)
+ {
+ return item->d_ptr.data();
+ }
+
+ void updateChildWithGraphicsEffectFlagRecursively();
+ void updateAncestorFlag(QGraphicsItem::GraphicsItemFlag childFlag,
+ AncestorFlag flag = NoFlag, bool enabled = false, bool root = true);
+ void updateAncestorFlags();
+ void setIsMemberOfGroup(bool enabled);
+ void remapItemPos(QEvent *event, QGraphicsItem *item);
+ QPointF genericMapFromScene(const QPointF &pos, const QWidget *viewport) const;
+ inline bool itemIsUntransformable() const
+ {
+ return (flags & QGraphicsItem::ItemIgnoresTransformations)
+ || (ancestorFlags & AncestorIgnoresTransformations);
+ }
+
+ void combineTransformToParent(QTransform *x, const QTransform *viewTransform = 0) const;
+ void combineTransformFromParent(QTransform *x, const QTransform *viewTransform = 0) const;
+ virtual void updateSceneTransformFromParent();
+
+ // ### Qt 5: Remove. Workaround for reimplementation added after Qt 4.4.
+ virtual QVariant inputMethodQueryHelper(Qt::InputMethodQuery query) const;
+ static bool movableAncestorIsSelected(const QGraphicsItem *item);
+
+ virtual void setPosHelper(const QPointF &pos);
+ void setTransformHelper(const QTransform &transform);
+ void prependGraphicsTransform(QGraphicsTransform *t);
+ void appendGraphicsTransform(QGraphicsTransform *t);
+ void setVisibleHelper(bool newVisible, bool explicitly, bool update = true);
+ void setEnabledHelper(bool newEnabled, bool explicitly, bool update = true);
+ bool discardUpdateRequest(bool ignoreVisibleBit = false,
+ bool ignoreDirtyBit = false, bool ignoreOpacity = false) const;
+ virtual void transformChanged() {}
+ int depth() const;
+#ifndef QT_NO_GRAPHICSEFFECT
+ enum InvalidateReason {
+ OpacityChanged
+ };
+ void invalidateParentGraphicsEffectsRecursively();
+ void invalidateChildGraphicsEffectsRecursively(InvalidateReason reason);
+#endif //QT_NO_GRAPHICSEFFECT
+ void invalidateDepthRecursively();
+ void resolveDepth();
+ void addChild(QGraphicsItem *child);
+ void removeChild(QGraphicsItem *child);
+ QDeclarativeListProperty<QGraphicsObject> childrenList();
+ void setParentItemHelper(QGraphicsItem *parent, const QVariant *newParentVariant,
+ const QVariant *thisPointerVariant);
+ void childrenBoundingRectHelper(QTransform *x, QRectF *rect, QGraphicsItem *topMostEffectItem);
+ void initStyleOption(QStyleOptionGraphicsItem *option, const QTransform &worldTransform,
+ const QRegion &exposedRegion, bool allItems = false) const;
+ QRectF effectiveBoundingRect(QGraphicsItem *topMostEffectItem = 0) const;
+ QRectF sceneEffectiveBoundingRect() const;
+
+ QRectF effectiveBoundingRect(const QRectF &rect) const;
+
+ virtual void resolveFont(uint inheritedMask)
+ {
+ for (int i = 0; i < children.size(); ++i)
+ children.at(i)->d_ptr->resolveFont(inheritedMask);
+ }
+
+ virtual void resolvePalette(uint inheritedMask)
+ {
+ for (int i = 0; i < children.size(); ++i)
+ children.at(i)->d_ptr->resolveFont(inheritedMask);
+ }
+
+ virtual bool isProxyWidget() const;
+
+ inline QVariant extra(Extra type) const
+ {
+ for (int i = 0; i < extras.size(); ++i) {
+ const ExtraStruct &extra = extras.at(i);
+ if (extra.type == type)
+ return extra.value;
+ }
+ return QVariant();
+ }
+
+ inline void setExtra(Extra type, const QVariant &value)
+ {
+ int index = -1;
+ for (int i = 0; i < extras.size(); ++i) {
+ if (extras.at(i).type == type) {
+ index = i;
+ break;
+ }
+ }
+
+ if (index == -1) {
+ extras << ExtraStruct(type, value);
+ } else {
+ extras[index].value = value;
+ }
+ }
+
+ inline void unsetExtra(Extra type)
+ {
+ for (int i = 0; i < extras.size(); ++i) {
+ if (extras.at(i).type == type) {
+ extras.removeAt(i);
+ return;
+ }
+ }
+ }
+
+ struct ExtraStruct {
+ ExtraStruct(Extra type, QVariant value)
+ : type(type), value(value)
+ { }
+
+ Extra type;
+ QVariant value;
+
+ bool operator<(Extra extra) const
+ { return type < extra; }
+ };
+
+ QList<ExtraStruct> extras;
+
+ QGraphicsItemCache *maybeExtraItemCache() const;
+ QGraphicsItemCache *extraItemCache() const;
+ void removeExtraItemCache();
+
+ void updatePaintedViewBoundingRects(bool updateChildren);
+ void ensureSceneTransformRecursive(QGraphicsItem **topMostDirtyItem);
+ inline void ensureSceneTransform()
+ {
+ QGraphicsItem *that = q_func();
+ ensureSceneTransformRecursive(&that);
+ }
+
+ inline bool hasTranslateOnlySceneTransform()
+ {
+ ensureSceneTransform();
+ return sceneTransformTranslateOnly;
+ }
+
+ inline void invalidateChildrenSceneTransform()
+ {
+ for (int i = 0; i < children.size(); ++i)
+ children.at(i)->d_ptr->dirtySceneTransform = 1;
+ }
+
+ inline qreal calcEffectiveOpacity() const
+ {
+ qreal o = opacity;
+ QGraphicsItem *p = parent;
+ int myFlags = flags;
+ while (p) {
+ int parentFlags = p->d_ptr->flags;
+
+ // If I have a parent, and I don't ignore my parent's opacity, and my
+ // parent propagates to me, then combine my local opacity with my parent's
+ // effective opacity into my effective opacity.
+ if ((myFlags & QGraphicsItem::ItemIgnoresParentOpacity)
+ || (parentFlags & QGraphicsItem::ItemDoesntPropagateOpacityToChildren)) {
+ break;
+ }
+
+ o *= p->d_ptr->opacity;
+ p = p->d_ptr->parent;
+ myFlags = parentFlags;
+ }
+ return o;
+ }
+
+ inline bool isOpacityNull() const
+ { return (opacity < qreal(0.001)); }
+
+ static inline bool isOpacityNull(qreal opacity)
+ { return (opacity < qreal(0.001)); }
+
+ inline bool isFullyTransparent() const
+ {
+ if (isOpacityNull())
+ return true;
+ if (!parent)
+ return false;
+
+ return isOpacityNull(calcEffectiveOpacity());
+ }
+
+ inline qreal effectiveOpacity() const {
+ if (!parent || !opacity)
+ return opacity;
+
+ return calcEffectiveOpacity();
+ }
+
+ inline qreal combineOpacityFromParent(qreal parentOpacity) const
+ {
+ if (parent && !(flags & QGraphicsItem::ItemIgnoresParentOpacity)
+ && !(parent->d_ptr->flags & QGraphicsItem::ItemDoesntPropagateOpacityToChildren)) {
+ return parentOpacity * opacity;
+ }
+ return opacity;
+ }
+
+ inline bool childrenCombineOpacity() const
+ {
+ if (!children.size())
+ return true;
+ if (flags & QGraphicsItem::ItemDoesntPropagateOpacityToChildren)
+ return false;
+
+ for (int i = 0; i < children.size(); ++i) {
+ if (children.at(i)->d_ptr->flags & QGraphicsItem::ItemIgnoresParentOpacity)
+ return false;
+ }
+ return true;
+ }
+
+ inline bool childrenClippedToShape() const
+ { return (flags & QGraphicsItem::ItemClipsChildrenToShape) || children.isEmpty(); }
+
+ inline bool isInvisible() const
+ {
+ return !visible || (childrenCombineOpacity() && isFullyTransparent());
+ }
+
+ inline void markParentDirty(bool updateBoundingRect = false);
+
+ void setFocusHelper(Qt::FocusReason focusReason, bool climb, bool focusFromHide);
+ void clearFocusHelper(bool giveFocusToParent);
+ void setSubFocus(QGraphicsItem *rootItem = 0, QGraphicsItem *stopItem = 0);
+ void clearSubFocus(QGraphicsItem *rootItem = 0, QGraphicsItem *stopItem = 0);
+ void resetFocusProxy();
+ virtual void subFocusItemChange();
+ virtual void focusScopeItemChange(bool isSubFocusItem);
+
+ static void children_append(QDeclarativeListProperty<QGraphicsObject> *list, QGraphicsObject *item);
+ static int children_count(QDeclarativeListProperty<QGraphicsObject> *list);
+ static QGraphicsObject *children_at(QDeclarativeListProperty<QGraphicsObject> *list, int);
+ static void children_clear(QDeclarativeListProperty<QGraphicsObject> *list);
+
+ inline QTransform transformToParent() const;
+ inline void ensureSortedChildren();
+ static inline bool insertionOrder(QGraphicsItem *a, QGraphicsItem *b);
+ void ensureSequentialSiblingIndex();
+ inline void sendScenePosChange();
+ virtual void siblingOrderChange();
+
+ // Private Properties
+ virtual qreal width() const;
+ virtual void setWidth(qreal);
+ virtual void resetWidth();
+
+ virtual qreal height() const;
+ virtual void setHeight(qreal);
+ virtual void resetHeight();
+
+ QRectF childrenBoundingRect;
+ QRectF needsRepaint;
+ QMap<QWidget *, QRect> paintedViewBoundingRects;
+ QPointF pos;
+ qreal z;
+ qreal opacity;
+ QGraphicsScene *scene;
+ QGraphicsItem *parent;
+ QList<QGraphicsItem *> children;
+ struct TransformData;
+ TransformData *transformData;
+ QGraphicsEffect *graphicsEffect;
+ QTransform sceneTransform;
+ int index;
+ int siblingIndex;
+ int itemDepth; // Lazily calculated when calling depth().
+ QGraphicsItem *focusProxy;
+ QList<QGraphicsItem **> focusProxyRefs;
+ QGraphicsItem *subFocusItem;
+ QGraphicsItem *focusScopeItem;
+ Qt::InputMethodHints imHints;
+ QGraphicsItem::PanelModality panelModality;
+#ifndef QT_NO_GESTURES
+ QMap<Qt::GestureType, Qt::GestureFlags> gestureContext;
+#endif
+
+ // Packed 32 bits
+ quint32 acceptedMouseButtons : 5;
+ quint32 visible : 1;
+ quint32 explicitlyHidden : 1;
+ quint32 enabled : 1;
+ quint32 explicitlyDisabled : 1;
+ quint32 selected : 1;
+ quint32 acceptsHover : 1;
+ quint32 acceptDrops : 1;
+ quint32 isMemberOfGroup : 1;
+ quint32 handlesChildEvents : 1;
+ quint32 itemDiscovered : 1;
+ quint32 hasCursor : 1;
+ quint32 ancestorFlags : 4;
+ quint32 cacheMode : 2;
+ quint32 hasBoundingRegionGranularity : 1;
+ quint32 isWidget : 1;
+ quint32 dirty : 1;
+ quint32 dirtyChildren : 1;
+ quint32 localCollisionHack : 1;
+ quint32 inSetPosHelper : 1;
+ quint32 needSortChildren : 1;
+ quint32 allChildrenDirty : 1;
+ quint32 fullUpdatePending : 1;
+ quint32 dirtyChildrenBoundingRect : 1;
+
+ // Packed 32 bits
+ quint32 flags : 19;
+ quint32 paintedViewBoundingRectsNeedRepaint : 1;
+ quint32 dirtySceneTransform : 1;
+ quint32 geometryChanged : 1;
+ quint32 inDestructor : 1;
+ quint32 isObject : 1;
+ quint32 ignoreVisible : 1;
+ quint32 ignoreOpacity : 1;
+ quint32 acceptTouchEvents : 1;
+ quint32 acceptedTouchBeginEvent : 1;
+ quint32 filtersDescendantEvents : 1;
+ quint32 sceneTransformTranslateOnly : 1;
+ quint32 notifyBoundingRectChanged : 1;
+ quint32 notifyInvalidated : 1;
+
+ // New 32 bits
+ quint32 mouseSetsFocus : 1;
+ quint32 explicitActivate : 1;
+ quint32 wantsActive : 1;
+ quint32 holesInSiblingIndex : 1;
+ quint32 sequentialOrdering : 1;
+ quint32 updateDueToGraphicsEffect : 1;
+ quint32 scenePosDescendants : 1;
+ quint32 pendingPolish : 1;
+ quint32 mayHaveChildWithGraphicsEffect : 1;
+ quint32 isDeclarativeItem : 1;
+ quint32 sendParentChangeNotification : 1;
+ quint32 padding : 21;
+
+ // Optional stacking order
+ int globalStackingOrder;
+ QGraphicsItem *q_ptr;
+};
+
+struct QGraphicsItemPrivate::TransformData
+{
+ QTransform transform;
+ qreal scale;
+ qreal rotation;
+ qreal xOrigin;
+ qreal yOrigin;
+ QList<QGraphicsTransform *> graphicsTransforms;
+ bool onlyTransform;
+
+ TransformData() :
+ scale(1.0), rotation(0.0),
+ xOrigin(0.0), yOrigin(0.0),
+ onlyTransform(true)
+ { }
+
+ QTransform computedFullTransform(QTransform *postmultiplyTransform = 0) const
+ {
+ if (onlyTransform) {
+ if (!postmultiplyTransform || postmultiplyTransform->isIdentity())
+ return transform;
+ if (transform.isIdentity())
+ return *postmultiplyTransform;
+ return transform * *postmultiplyTransform;
+ }
+
+ QTransform x(transform);
+ if (!graphicsTransforms.isEmpty()) {
+ QMatrix4x4 m;
+ for (int i = 0; i < graphicsTransforms.size(); ++i)
+ graphicsTransforms.at(i)->applyTo(&m);
+ x *= m.toTransform();
+ }
+ x.translate(xOrigin, yOrigin);
+ x.rotate(rotation);
+ x.scale(scale, scale);
+ x.translate(-xOrigin, -yOrigin);
+ if (postmultiplyTransform)
+ x *= *postmultiplyTransform;
+ return x;
+ }
+};
+
+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;
+};
+
+#ifndef QT_NO_GRAPHICSEFFECT
+class QGraphicsItemEffectSourcePrivate : public QGraphicsEffectSourcePrivate
+{
+public:
+ QGraphicsItemEffectSourcePrivate(QGraphicsItem *i)
+ : QGraphicsEffectSourcePrivate(), item(i), info(0)
+ {}
+
+ inline void detach()
+ {
+ item->d_ptr->graphicsEffect = 0;
+ item->prepareGeometryChange();
+ }
+
+ inline const QGraphicsItem *graphicsItem() const
+ { return item; }
+
+ inline const QWidget *widget() const
+ { return 0; }
+
+ inline void update() {
+ item->d_ptr->updateDueToGraphicsEffect = true;
+ item->update();
+ item->d_ptr->updateDueToGraphicsEffect = false;
+ }
+
+ inline void effectBoundingRectChanged()
+ { item->prepareGeometryChange(); }
+
+ inline bool isPixmap() const
+ {
+ return item->type() == QGraphicsPixmapItem::Type
+ && !(item->flags() & QGraphicsItem::ItemIsSelectable)
+ && item->d_ptr->children.size() == 0;
+ //|| (item->d_ptr->isObject && qobject_cast<QDeclarativeImage *>(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,
+ QGraphicsEffect::PixmapPadMode mode) const;
+ QRect paddedEffectRect(Qt::CoordinateSystem system, QGraphicsEffect::PixmapPadMode mode, const QRectF &sourceRect, bool *unpadded = 0) const;
+
+ QGraphicsItem *item;
+ QGraphicsItemPaintInfo *info;
+ QTransform lastEffectTransform;
+};
+#endif //QT_NO_GRAPHICSEFFECT
+
+/*!
+ Returns true if \a item1 is on top of \a item2.
+ The items don't need to be siblings.
+
+ \internal
+*/
+inline bool qt_closestItemFirst(const QGraphicsItem *item1, const QGraphicsItem *item2)
+{
+ // Siblings? Just check their z-values.
+ const QGraphicsItemPrivate *d1 = item1->d_ptr.data();
+ const QGraphicsItemPrivate *d2 = item2->d_ptr.data();
+ if (d1->parent == d2->parent)
+ return qt_closestLeaf(item1, item2);
+
+ // Find common ancestor, and each item's ancestor closest to the common
+ // ancestor.
+ int item1Depth = d1->depth();
+ int item2Depth = d2->depth();
+ const QGraphicsItem *p = item1;
+ const QGraphicsItem *t1 = item1;
+ while (item1Depth > item2Depth && (p = p->d_ptr->parent)) {
+ if (p == item2) {
+ // item2 is one of item1's ancestors; item1 is on top
+ return !(t1->d_ptr->flags & QGraphicsItem::ItemStacksBehindParent);
+ }
+ t1 = p;
+ --item1Depth;
+ }
+ p = item2;
+ const QGraphicsItem *t2 = item2;
+ while (item2Depth > item1Depth && (p = p->d_ptr->parent)) {
+ if (p == item1) {
+ // item1 is one of item2's ancestors; item1 is not on top
+ return (t2->d_ptr->flags & QGraphicsItem::ItemStacksBehindParent);
+ }
+ t2 = p;
+ --item2Depth;
+ }
+
+ // item1Ancestor is now at the same level as item2Ancestor, but not the same.
+ const QGraphicsItem *p1 = t1;
+ const QGraphicsItem *p2 = t2;
+ while (t1 && t1 != t2) {
+ p1 = t1;
+ p2 = t2;
+ t1 = t1->d_ptr->parent;
+ t2 = t2->d_ptr->parent;
+ }
+
+ // in case we have a common ancestor, we compare the immediate children in the ancestor's path.
+ // otherwise we compare the respective items' topLevelItems directly.
+ return qt_closestLeaf(p1, p2);
+}
+
+/*!
+ Returns true if \a item2 is on top of \a item1.
+ The items don't need to be siblings.
+
+ \internal
+*/
+inline bool qt_closestItemLast(const QGraphicsItem *item1, const QGraphicsItem *item2)
+{
+ return qt_closestItemFirst(item2, item1);
+}
+
+/*!
+ \internal
+*/
+inline bool qt_closestLeaf(const QGraphicsItem *item1, const QGraphicsItem *item2)
+{
+ // Return true if sibling item1 is on top of item2.
+ const QGraphicsItemPrivate *d1 = item1->d_ptr.data();
+ const QGraphicsItemPrivate *d2 = item2->d_ptr.data();
+ bool f1 = d1->flags & QGraphicsItem::ItemStacksBehindParent;
+ bool f2 = d2->flags & QGraphicsItem::ItemStacksBehindParent;
+ if (f1 != f2)
+ return f2;
+ if (d1->z != d2->z)
+ return d1->z > d2->z;
+ return d1->siblingIndex > d2->siblingIndex;
+}
+
+/*!
+ \internal
+*/
+inline bool qt_notclosestLeaf(const QGraphicsItem *item1, const QGraphicsItem *item2)
+{ return qt_closestLeaf(item2, item1); }
+
+/*
+ return the full transform of the item to the parent. This include the position and all the transform data
+*/
+inline QTransform QGraphicsItemPrivate::transformToParent() const
+{
+ QTransform matrix;
+ combineTransformToParent(&matrix);
+ return matrix;
+}
+
+/*!
+ \internal
+*/
+inline void QGraphicsItemPrivate::ensureSortedChildren()
+{
+ if (needSortChildren) {
+ needSortChildren = 0;
+ sequentialOrdering = 1;
+ if (children.isEmpty())
+ return;
+ qSort(children.begin(), children.end(), qt_notclosestLeaf);
+ for (int i = 0; i < children.size(); ++i) {
+ if (children.at(i)->d_ptr->siblingIndex != i) {
+ sequentialOrdering = 0;
+ break;
+ }
+ }
+ }
+}
+
+/*!
+ \internal
+*/
+inline bool QGraphicsItemPrivate::insertionOrder(QGraphicsItem *a, QGraphicsItem *b)
+{
+ return a->d_ptr->siblingIndex < b->d_ptr->siblingIndex;
+}
+
+/*!
+ \internal
+*/
+inline void QGraphicsItemPrivate::markParentDirty(bool updateBoundingRect)
+{
+ QGraphicsItemPrivate *parentp = this;
+#ifndef QT_NO_GRAPHICSEFFECT
+ if (updateBoundingRect && parentp->graphicsEffect && !parentp->inSetPosHelper) {
+ parentp->notifyInvalidated = 1;
+ static_cast<QGraphicsItemEffectSourcePrivate *>(parentp->graphicsEffect->d_func()
+ ->source->d_func())->invalidateCache();
+ }
+#endif
+ while (parentp->parent) {
+ parentp = parentp->parent->d_ptr.data();
+ parentp->dirtyChildren = 1;
+
+ if (updateBoundingRect) {
+ parentp->dirtyChildrenBoundingRect = 1;
+ // ### Only do this if the parent's effect applies to the entire subtree.
+ parentp->notifyBoundingRectChanged = 1;
+ }
+#ifndef QT_NO_GRAPHICSEFFECT
+ if (parentp->graphicsEffect) {
+ if (updateBoundingRect) {
+ static_cast<QGraphicsItemEffectSourcePrivate *>(parentp->graphicsEffect->d_func()
+ ->source->d_func())->invalidateCache();
+ parentp->notifyInvalidated = 1;
+ }
+ if (parentp->scene && parentp->graphicsEffect->isEnabled()) {
+ parentp->dirty = 1;
+ parentp->fullUpdatePending = 1;
+ }
+ }
+#endif
+ }
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_GRAPHICSVIEW
+
+#endif
diff --git a/src/widgets/graphicsview/qgraphicsitemanimation.cpp b/src/widgets/graphicsview/qgraphicsitemanimation.cpp
new file mode 100644
index 0000000000..7eedc8dd3d
--- /dev/null
+++ b/src/widgets/graphicsview/qgraphicsitemanimation.cpp
@@ -0,0 +1,599 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** 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 Technology Preview License Agreement accompanying
+** this package.
+**
+** 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.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*!
+ \class QGraphicsItemAnimation
+ \brief The QGraphicsItemAnimation class provides simple animation
+ support for QGraphicsItem.
+ \since 4.2
+ \ingroup graphicsview-api
+ \deprecated
+
+ The QGraphicsItemAnimation class animates a QGraphicsItem. You can
+ schedule changes to the item's transformation matrix at
+ specified steps. The QGraphicsItemAnimation class has a
+ current step value. When this value changes the transformations
+ scheduled at that step are performed. The current step of the
+ animation is set with the \c setStep() function.
+
+ QGraphicsItemAnimation will do a simple linear interpolation
+ between the nearest adjacent scheduled changes to calculate the
+ matrix. For instance, if you set the position of an item at values
+ 0.0 and 1.0, the animation will show the item moving in a straight
+ line between these positions. The same is true for scaling and
+ rotation.
+
+ It is usual to use the class with a QTimeLine. The timeline's
+ \l{QTimeLine::}{valueChanged()} signal is then connected to the
+ \c setStep() slot. For example, you can set up an item for rotation
+ by calling \c setRotationAt() for different step values.
+ The animations timeline is set with the setTimeLine() function.
+
+ An example animation with a timeline follows:
+
+ \snippet doc/src/snippets/timeline/main.cpp 0
+
+ Note that steps lie between 0.0 and 1.0. It may be necessary to use
+ \l{QTimeLine::}{setUpdateInterval()}. The default update interval
+ is 40 ms. A scheduled transformation cannot be removed when set,
+ so scheduling several transformations of the same kind (e.g.,
+ rotations) at the same step is not recommended.
+
+ \sa QTimeLine, {Graphics View Framework}
+*/
+
+#include "qgraphicsitemanimation.h"
+
+#ifndef QT_NO_GRAPHICSVIEW
+
+#include "qgraphicsitem.h"
+
+#include <QtCore/qtimeline.h>
+#include <QtCore/qpoint.h>
+#include <QtCore/qpointer.h>
+#include <QtCore/qpair.h>
+#include <QtGui/qmatrix.h>
+
+QT_BEGIN_NAMESPACE
+
+class QGraphicsItemAnimationPrivate
+{
+public:
+ inline QGraphicsItemAnimationPrivate()
+ : q(0), timeLine(0), item(0), step(0)
+ { }
+
+ QGraphicsItemAnimation *q;
+
+ QPointer<QTimeLine> timeLine;
+ QGraphicsItem *item;
+
+ QPointF startPos;
+ QMatrix startMatrix;
+
+ qreal step;
+
+ struct Pair {
+ Pair(qreal a, qreal b) : step(a), value(b) {}
+ bool operator <(const Pair &other) const
+ { return step < other.step; }
+ bool operator==(const Pair &other) const
+ { return step == other.step; }
+ qreal step;
+ qreal value;
+ };
+ QList<Pair> xPosition;
+ QList<Pair> yPosition;
+ QList<Pair> rotation;
+ QList<Pair> verticalScale;
+ QList<Pair> horizontalScale;
+ QList<Pair> verticalShear;
+ QList<Pair> horizontalShear;
+ QList<Pair> xTranslation;
+ QList<Pair> yTranslation;
+
+ qreal linearValueForStep(qreal step, QList<Pair> *source, qreal defaultValue = 0);
+ void insertUniquePair(qreal step, qreal value, QList<Pair> *binList, const char* method);
+};
+
+qreal QGraphicsItemAnimationPrivate::linearValueForStep(qreal step, QList<Pair> *source, qreal defaultValue)
+{
+ if (source->isEmpty())
+ return defaultValue;
+ step = qMin<qreal>(qMax<qreal>(step, 0), 1);
+
+ if (step == 1)
+ return source->last().value;
+
+ qreal stepBefore = 0;
+ qreal stepAfter = 1;
+ qreal valueBefore = source->first().step == 0 ? source->first().value : defaultValue;
+ qreal valueAfter = source->last().value;
+
+ // Find the closest step and value before the given step.
+ for (int i = 0; i < source->size() && step >= source->at(i).step; ++i) {
+ stepBefore = source->at(i).step;
+ valueBefore = source->at(i).value;
+ }
+
+ // Find the closest step and value after the given step.
+ for (int j = source->size() - 1; j >= 0 && step < source->at(j).step; --j) {
+ stepAfter = source->at(j).step;
+ valueAfter = source->at(j).value;
+ }
+
+ // Do a simple linear interpolation.
+ return valueBefore + (valueAfter - valueBefore) * ((step - stepBefore) / (stepAfter - stepBefore));
+}
+
+void QGraphicsItemAnimationPrivate::insertUniquePair(qreal step, qreal value, QList<Pair> *binList, const char* method)
+{
+ if (step < 0.0 || step > 1.0) {
+ qWarning("QGraphicsItemAnimation::%s: invalid step = %f", method, step);
+ return;
+ }
+
+ Pair pair(step, value);
+
+ QList<Pair>::iterator result = qBinaryFind(binList->begin(), binList->end(), pair);
+ if (result != binList->end())
+ result->value = value;
+ else {
+ *binList << pair;
+ qSort(binList->begin(), binList->end());
+ }
+}
+
+/*!
+ Constructs an animation object with the given \a parent.
+*/
+QGraphicsItemAnimation::QGraphicsItemAnimation(QObject *parent)
+ : QObject(parent), d(new QGraphicsItemAnimationPrivate)
+{
+ d->q = this;
+}
+
+/*!
+ Destroys the animation object.
+*/
+QGraphicsItemAnimation::~QGraphicsItemAnimation()
+{
+ delete d;
+}
+
+/*!
+ Returns the item on which the animation object operates.
+
+ \sa setItem()
+*/
+QGraphicsItem *QGraphicsItemAnimation::item() const
+{
+ return d->item;
+}
+
+/*!
+ Sets the specified \a item to be used in the animation.
+
+ \sa item()
+*/
+void QGraphicsItemAnimation::setItem(QGraphicsItem *item)
+{
+ d->item = item;
+ d->startPos = d->item->pos();
+}
+
+/*!
+ Returns the timeline object used to control the rate at which the animation
+ occurs.
+
+ \sa setTimeLine()
+*/
+QTimeLine *QGraphicsItemAnimation::timeLine() const
+{
+ return d->timeLine;
+}
+
+/*!
+ Sets the timeline object used to control the rate of animation to the \a timeLine
+ specified.
+
+ \sa timeLine()
+*/
+void QGraphicsItemAnimation::setTimeLine(QTimeLine *timeLine)
+{
+ if (d->timeLine == timeLine)
+ return;
+ if (d->timeLine)
+ delete d->timeLine;
+ if (!timeLine)
+ return;
+ d->timeLine = timeLine;
+ connect(timeLine, SIGNAL(valueChanged(qreal)), this, SLOT(setStep(qreal)));
+}
+
+/*!
+ Returns the position of the item at the given \a step value.
+
+ \sa setPosAt()
+*/
+QPointF QGraphicsItemAnimation::posAt(qreal step) const
+{
+ if (step < 0.0 || step > 1.0)
+ qWarning("QGraphicsItemAnimation::posAt: invalid step = %f", step);
+
+ return QPointF(d->linearValueForStep(step, &d->xPosition, d->startPos.x()),
+ d->linearValueForStep(step, &d->yPosition, d->startPos.y()));
+}
+
+/*!
+ \fn void QGraphicsItemAnimation::setPosAt(qreal step, const QPointF &point)
+
+ Sets the position of the item at the given \a step value to the \a point specified.
+
+ \sa posAt()
+*/
+void QGraphicsItemAnimation::setPosAt(qreal step, const QPointF &pos)
+{
+ d->insertUniquePair(step, pos.x(), &d->xPosition, "setPosAt");
+ d->insertUniquePair(step, pos.y(), &d->yPosition, "setPosAt");
+}
+
+/*!
+ Returns all explicitly inserted positions.
+
+ \sa posAt(), setPosAt()
+*/
+QList<QPair<qreal, QPointF> > QGraphicsItemAnimation::posList() const
+{
+ QList<QPair<qreal, QPointF> > list;
+ for (int i = 0; i < d->xPosition.size(); ++i)
+ list << QPair<qreal, QPointF>(d->xPosition.at(i).step, QPointF(d->xPosition.at(i).value, d->yPosition.at(i).value));
+
+ return list;
+}
+
+/*!
+ Returns the matrix used to transform the item at the specified \a step value.
+*/
+QMatrix QGraphicsItemAnimation::matrixAt(qreal step) const
+{
+ if (step < 0.0 || step > 1.0)
+ qWarning("QGraphicsItemAnimation::matrixAt: invalid step = %f", step);
+
+ QMatrix matrix;
+ if (!d->rotation.isEmpty())
+ matrix.rotate(rotationAt(step));
+ if (!d->verticalScale.isEmpty())
+ matrix.scale(horizontalScaleAt(step), verticalScaleAt(step));
+ if (!d->verticalShear.isEmpty())
+ matrix.shear(horizontalShearAt(step), verticalShearAt(step));
+ if (!d->xTranslation.isEmpty())
+ matrix.translate(xTranslationAt(step), yTranslationAt(step));
+ return matrix;
+}
+
+/*!
+ Returns the angle at which the item is rotated at the specified \a step value.
+
+ \sa setRotationAt()
+*/
+qreal QGraphicsItemAnimation::rotationAt(qreal step) const
+{
+ if (step < 0.0 || step > 1.0)
+ qWarning("QGraphicsItemAnimation::rotationAt: invalid step = %f", step);
+
+ return d->linearValueForStep(step, &d->rotation);
+}
+
+/*!
+ Sets the rotation of the item at the given \a step value to the \a angle specified.
+
+ \sa rotationAt()
+*/
+void QGraphicsItemAnimation::setRotationAt(qreal step, qreal angle)
+{
+ d->insertUniquePair(step, angle, &d->rotation, "setRotationAt");
+}
+
+/*!
+ Returns all explicitly inserted rotations.
+
+ \sa rotationAt(), setRotationAt()
+*/
+QList<QPair<qreal, qreal> > QGraphicsItemAnimation::rotationList() const
+{
+ QList<QPair<qreal, qreal> > list;
+ for (int i = 0; i < d->rotation.size(); ++i)
+ list << QPair<qreal, qreal>(d->rotation.at(i).step, d->rotation.at(i).value);
+
+ return list;
+}
+
+/*!
+ Returns the horizontal translation of the item at the specified \a step value.
+
+ \sa setTranslationAt()
+*/
+qreal QGraphicsItemAnimation::xTranslationAt(qreal step) const
+{
+ if (step < 0.0 || step > 1.0)
+ qWarning("QGraphicsItemAnimation::xTranslationAt: invalid step = %f", step);
+
+ return d->linearValueForStep(step, &d->xTranslation);
+}
+
+/*!
+ Returns the vertical translation of the item at the specified \a step value.
+
+ \sa setTranslationAt()
+*/
+qreal QGraphicsItemAnimation::yTranslationAt(qreal step) const
+{
+ if (step < 0.0 || step > 1.0)
+ qWarning("QGraphicsItemAnimation::yTranslationAt: invalid step = %f", step);
+
+ return d->linearValueForStep(step, &d->yTranslation);
+}
+
+/*!
+ Sets the translation of the item at the given \a step value using the horizontal
+ and vertical coordinates specified by \a dx and \a dy.
+
+ \sa xTranslationAt(), yTranslationAt()
+*/
+void QGraphicsItemAnimation::setTranslationAt(qreal step, qreal dx, qreal dy)
+{
+ d->insertUniquePair(step, dx, &d->xTranslation, "setTranslationAt");
+ d->insertUniquePair(step, dy, &d->yTranslation, "setTranslationAt");
+}
+
+/*!
+ Returns all explicitly inserted translations.
+
+ \sa xTranslationAt(), yTranslationAt(), setTranslationAt()
+*/
+QList<QPair<qreal, QPointF> > QGraphicsItemAnimation::translationList() const
+{
+ QList<QPair<qreal, QPointF> > list;
+ for (int i = 0; i < d->xTranslation.size(); ++i)
+ list << QPair<qreal, QPointF>(d->xTranslation.at(i).step, QPointF(d->xTranslation.at(i).value, d->yTranslation.at(i).value));
+
+ return list;
+}
+
+/*!
+ Returns the vertical scale for the item at the specified \a step value.
+
+ \sa setScaleAt()
+*/
+qreal QGraphicsItemAnimation::verticalScaleAt(qreal step) const
+{
+ if (step < 0.0 || step > 1.0)
+ qWarning("QGraphicsItemAnimation::verticalScaleAt: invalid step = %f", step);
+
+ return d->linearValueForStep(step, &d->verticalScale, 1);
+}
+
+/*!
+ Returns the horizontal scale for the item at the specified \a step value.
+
+ \sa setScaleAt()
+*/
+qreal QGraphicsItemAnimation::horizontalScaleAt(qreal step) const
+{
+ if (step < 0.0 || step > 1.0)
+ qWarning("QGraphicsItemAnimation::horizontalScaleAt: invalid step = %f", step);
+
+ return d->linearValueForStep(step, &d->horizontalScale, 1);
+}
+
+/*!
+ Sets the scale of the item at the given \a step value using the horizontal and
+ vertical scale factors specified by \a sx and \a sy.
+
+ \sa verticalScaleAt(), horizontalScaleAt()
+*/
+void QGraphicsItemAnimation::setScaleAt(qreal step, qreal sx, qreal sy)
+{
+ d->insertUniquePair(step, sx, &d->horizontalScale, "setScaleAt");
+ d->insertUniquePair(step, sy, &d->verticalScale, "setScaleAt");
+}
+
+/*!
+ Returns all explicitly inserted scales.
+
+ \sa verticalScaleAt(), horizontalScaleAt(), setScaleAt()
+*/
+QList<QPair<qreal, QPointF> > QGraphicsItemAnimation::scaleList() const
+{
+ QList<QPair<qreal, QPointF> > list;
+ for (int i = 0; i < d->horizontalScale.size(); ++i)
+ list << QPair<qreal, QPointF>(d->horizontalScale.at(i).step, QPointF(d->horizontalScale.at(i).value, d->verticalScale.at(i).value));
+
+ return list;
+}
+
+/*!
+ Returns the vertical shear for the item at the specified \a step value.
+
+ \sa setShearAt()
+*/
+qreal QGraphicsItemAnimation::verticalShearAt(qreal step) const
+{
+ if (step < 0.0 || step > 1.0)
+ qWarning("QGraphicsItemAnimation::verticalShearAt: invalid step = %f", step);
+
+ return d->linearValueForStep(step, &d->verticalShear, 0);
+}
+
+/*!
+ Returns the horizontal shear for the item at the specified \a step value.
+
+ \sa setShearAt()
+*/
+qreal QGraphicsItemAnimation::horizontalShearAt(qreal step) const
+{
+ if (step < 0.0 || step > 1.0)
+ qWarning("QGraphicsItemAnimation::horizontalShearAt: invalid step = %f", step);
+
+ return d->linearValueForStep(step, &d->horizontalShear, 0);
+}
+
+/*!
+ Sets the shear of the item at the given \a step value using the horizontal and
+ vertical shear factors specified by \a sh and \a sv.
+
+ \sa verticalShearAt(), horizontalShearAt()
+*/
+void QGraphicsItemAnimation::setShearAt(qreal step, qreal sh, qreal sv)
+{
+ d->insertUniquePair(step, sh, &d->horizontalShear, "setShearAt");
+ d->insertUniquePair(step, sv, &d->verticalShear, "setShearAt");
+}
+
+/*!
+ Returns all explicitly inserted shears.
+
+ \sa verticalShearAt(), horizontalShearAt(), setShearAt()
+*/
+QList<QPair<qreal, QPointF> > QGraphicsItemAnimation::shearList() const
+{
+ QList<QPair<qreal, QPointF> > list;
+ for (int i = 0; i < d->horizontalShear.size(); ++i)
+ list << QPair<qreal, QPointF>(d->horizontalShear.at(i).step, QPointF(d->horizontalShear.at(i).value, d->verticalShear.at(i).value));
+
+ return list;
+}
+
+/*!
+ Clears the scheduled transformations used for the animation, but
+ retains the item and timeline.
+*/
+void QGraphicsItemAnimation::clear()
+{
+ d->xPosition.clear();
+ d->yPosition.clear();
+ d->rotation.clear();
+ d->verticalScale.clear();
+ d->horizontalScale.clear();
+ d->verticalShear.clear();
+ d->horizontalShear.clear();
+ d->xTranslation.clear();
+ d->yTranslation.clear();
+}
+
+/*!
+ \fn void QGraphicsItemAnimation::setStep(qreal step)
+
+ Sets the current \a step value for the animation, causing the
+ transformations scheduled at this step to be performed.
+*/
+void QGraphicsItemAnimation::setStep(qreal x)
+{
+ if (x < 0.0 || x > 1.0) {
+ qWarning("QGraphicsItemAnimation::setStep: invalid step = %f", x);
+ return;
+ }
+
+ beforeAnimationStep(x);
+
+ d->step = x;
+ if (d->item) {
+ if (!d->xPosition.isEmpty() || !d->yPosition.isEmpty())
+ d->item->setPos(posAt(x));
+ if (!d->rotation.isEmpty()
+ || !d->verticalScale.isEmpty()
+ || !d->horizontalScale.isEmpty()
+ || !d->verticalShear.isEmpty()
+ || !d->horizontalShear.isEmpty()
+ || !d->xTranslation.isEmpty()
+ || !d->yTranslation.isEmpty()) {
+ d->item->setMatrix(d->startMatrix * matrixAt(x));
+ }
+ }
+
+ afterAnimationStep(x);
+}
+
+/*!
+ Resets the item to its starting position and transformation.
+
+ \obsolete
+
+ You can call setStep(0) instead.
+*/
+void QGraphicsItemAnimation::reset()
+{
+ if (!d->item)
+ return;
+ d->startPos = d->item->pos();
+ d->startMatrix = d->item->matrix();
+}
+
+/*!
+ \fn void QGraphicsItemAnimation::beforeAnimationStep(qreal step)
+
+ This method is meant to be overridden by subclassed that needs to
+ execute additional code before a new step takes place. The
+ animation \a step is provided for use in cases where the action
+ depends on its value.
+*/
+void QGraphicsItemAnimation::beforeAnimationStep(qreal step)
+{
+ Q_UNUSED(step);
+}
+
+/*!
+ \fn void QGraphicsItemAnimation::afterAnimationStep(qreal step)
+
+ This method is meant to be overridden in subclasses that need to
+ execute additional code after a new step has taken place. The
+ animation \a step is provided for use in cases where the action
+ depends on its value.
+*/
+void QGraphicsItemAnimation::afterAnimationStep(qreal step)
+{
+ Q_UNUSED(step);
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_GRAPHICSVIEW
diff --git a/src/widgets/graphicsview/qgraphicsitemanimation.h b/src/widgets/graphicsview/qgraphicsitemanimation.h
new file mode 100644
index 0000000000..5dfbf4b81e
--- /dev/null
+++ b/src/widgets/graphicsview/qgraphicsitemanimation.h
@@ -0,0 +1,120 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** 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 Technology Preview License Agreement accompanying
+** this package.
+**
+** 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.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QGRAPHICSITEMANIMATION_H
+#define QGRAPHICSITEMANIMATION_H
+
+#include <QtCore/qobject.h>
+
+#if !defined(QT_NO_GRAPHICSVIEW) || (QT_EDITION & QT_MODULE_GRAPHICSVIEW) != QT_MODULE_GRAPHICSVIEW
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+class QGraphicsItem;
+class QMatrix;
+class QPointF;
+class QTimeLine;
+template <class T1, class T2> struct QPair;
+
+class QGraphicsItemAnimationPrivate;
+class Q_GUI_EXPORT QGraphicsItemAnimation : public QObject
+{
+ Q_OBJECT
+public:
+ QGraphicsItemAnimation(QObject *parent = 0);
+ virtual ~QGraphicsItemAnimation();
+
+ QGraphicsItem *item() const;
+ void setItem(QGraphicsItem *item);
+
+ QTimeLine *timeLine() const;
+ void setTimeLine(QTimeLine *timeLine);
+
+ QPointF posAt(qreal step) const;
+ QList<QPair<qreal, QPointF> > posList() const;
+ void setPosAt(qreal step, const QPointF &pos);
+
+ QMatrix matrixAt(qreal step) const;
+
+ qreal rotationAt(qreal step) const;
+ QList<QPair<qreal, qreal> > rotationList() const;
+ void setRotationAt(qreal step, qreal angle);
+
+ qreal xTranslationAt(qreal step) const;
+ qreal yTranslationAt(qreal step) const;
+ QList<QPair<qreal, QPointF> > translationList() const;
+ void setTranslationAt(qreal step, qreal dx, qreal dy);
+
+ qreal verticalScaleAt(qreal step) const;
+ qreal horizontalScaleAt(qreal step) const;
+ QList<QPair<qreal, QPointF> > scaleList() const;
+ void setScaleAt(qreal step, qreal sx, qreal sy);
+
+ qreal verticalShearAt(qreal step) const;
+ qreal horizontalShearAt(qreal step) const;
+ QList<QPair<qreal, QPointF> > shearList() const;
+ void setShearAt(qreal step, qreal sh, qreal sv);
+
+ void clear();
+
+public Q_SLOTS:
+ void setStep(qreal x);
+ void reset();
+
+protected:
+ virtual void beforeAnimationStep(qreal step);
+ virtual void afterAnimationStep(qreal step);
+
+private:
+ Q_DISABLE_COPY(QGraphicsItemAnimation)
+ QGraphicsItemAnimationPrivate *d;
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QT_NO_GRAPHICSVIEW
+#endif
diff --git a/src/widgets/graphicsview/qgraphicslayout.cpp b/src/widgets/graphicsview/qgraphicslayout.cpp
new file mode 100644
index 0000000000..904a3deda2
--- /dev/null
+++ b/src/widgets/graphicsview/qgraphicslayout.cpp
@@ -0,0 +1,451 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** 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 Technology Preview License Agreement accompanying
+** this package.
+**
+** 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.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qapplication.h"
+
+#ifndef QT_NO_GRAPHICSVIEW
+#include "qgraphicslayout.h"
+#include "qgraphicslayout_p.h"
+#include "qgraphicslayoutitem.h"
+#include "qgraphicslayoutitem_p.h"
+#include "qgraphicswidget.h"
+#include "qgraphicswidget_p.h"
+#include "qgraphicsscene.h"
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class QGraphicsLayout
+ \brief The QGraphicsLayout class provides the base class for all layouts
+ in Graphics View.
+ \since 4.4
+ \ingroup graphicsview-api
+
+ QGraphicsLayout is an abstract class that defines a virtual API for
+ arranging QGraphicsWidget children and other QGraphicsLayoutItem objects
+ for a QGraphicsWidget. QGraphicsWidget assigns responsibility to a
+ QGraphicsLayout through QGraphicsWidget::setLayout(). As the widget
+ is resized, the layout will automatically arrange the widget's children.
+ QGraphicsLayout inherits QGraphicsLayoutItem, so, it can be managed by
+ any layout, including its own subclasses.
+
+ \section1 Writing a Custom Layout
+
+ You can use QGraphicsLayout as a base to write your own custom layout
+ (e.g., a flowlayout), but it is more common to use one of its subclasses
+ instead - QGraphicsLinearLayout or QGraphicsGridLayout. When creating
+ a custom layout, the following functions must be reimplemented as a bare
+ minimum:
+
+ \table
+ \header \o Function \o Description
+ \row \o QGraphicsLayoutItem::setGeometry()
+ \o Notifies you when the geometry of the layout is set. You can
+ store the geometry in your own layout class in a reimplementation
+ of this function.
+ \row \o QGraphicsLayoutItem::sizeHint()
+ \o Returns the layout's size hints.
+ \row \o QGraphicsLayout::count()
+ \o Returns the number of items in your layout.
+ \row \o QGraphicsLayout::itemAt()
+ \o Returns a pointer to an item in your layout.
+ \row \o QGraphicsLayout::removeAt()
+ \o Removes an item from your layout without destroying it.
+ \endtable
+
+ For more details on how to implement each function, refer to the individual
+ function documentation.
+
+ Each layout defines its own API for arranging widgets and layout items.
+ For example, with a grid layout, you require a row and a
+ column index with optional row and column spans, alignment, spacing, and more.
+ A linear layout, however, requires a single row or column index to position its
+ items. For a grid layout, the order of insertion does not affect the layout in
+ any way, but for a linear layout, the order is essential. When writing your own
+ layout subclass, you are free to choose the API that best suits your layout.
+
+ \section1 Activating the Layout
+
+ When the layout's geometry changes, QGraphicsLayout immediately rearranges
+ all of its managed items by calling setGeometry() on each item. This
+ rearrangement is called \e activating the layout.
+
+ QGraphicsLayout updates its own geometry to match the contentsRect() of the
+ QGraphicsLayoutItem it is managing. Thus, it will automatically rearrange all
+ its items when the widget is resized. QGraphicsLayout caches the sizes of all
+ its managed items to avoid calling setGeometry() too often.
+
+ \note A QGraphicsLayout will have the same geometry as the contentsRect()
+ of the widget (not the layout) it is assigned to.
+
+ \section2 Activating the Layout Implicitly
+
+ The layout can be activated implicitly using one of two ways: by calling
+ activate() or by calling invalidate(). Calling activate() activates the layout
+ immediately. In contrast, calling invalidate() is delayed, as it posts a
+ \l{QEvent::LayoutRequest}{LayoutRequest} event to the managed widget. Due
+ to event compression, the activate() will only be called once after control has
+ returned to the event loop. This is referred to as \e invalidating the layout.
+ Invalidating the layout also invalidates any cached information. Also, the
+ invalidate() function is a virtual function. So, you can invalidate your own
+ cache in a subclass of QGraphicsLayout by reimplementing this function.
+
+ \section1 Event Handling
+
+ QGraphicsLayout listens to events for the widget it manages through the
+ virtual widgetEvent() event handler. When the layout is assigned to a
+ widget, all events delivered to the widget are first processed by
+ widgetEvent(). This allows the layout to be aware of any relevant state
+ changes on the widget such as visibility changes or layout direction changes.
+
+ \section1 Margin Handling
+
+ The margins of a QGraphicsLayout can be modified by reimplementing
+ setContentsMargins() and getContentsMargins().
+
+*/
+
+/*!
+ Contructs a QGraphicsLayout object.
+
+ \a parent is passed to QGraphicsLayoutItem's constructor and the
+ QGraphicsLayoutItem's isLayout argument is set to \e true.
+
+ If \a parent is a QGraphicsWidget the layout will be installed
+ on that widget. (Note that installing a layout will delete the old one
+ installed.)
+*/
+QGraphicsLayout::QGraphicsLayout(QGraphicsLayoutItem *parent)
+ : QGraphicsLayoutItem(*new QGraphicsLayoutPrivate)
+{
+ setParentLayoutItem(parent);
+ if (parent && !parent->isLayout()) {
+ // If a layout has a parent that is not a layout it must be a QGraphicsWidget.
+ QGraphicsItem *itemParent = parent->graphicsItem();
+ if (itemParent && itemParent->isWidget()) {
+ static_cast<QGraphicsWidget *>(itemParent)->d_func()->setLayout_helper(this);
+ } else {
+ qWarning("QGraphicsLayout::QGraphicsLayout: Attempt to create a layout with a parent that is"
+ " neither a QGraphicsWidget nor QGraphicsLayout");
+ }
+ }
+ setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding, QSizePolicy::DefaultType);
+ setOwnedByLayout(true);
+}
+
+/*!
+ \internal
+*/
+QGraphicsLayout::QGraphicsLayout(QGraphicsLayoutPrivate &dd, QGraphicsLayoutItem *parent)
+ : QGraphicsLayoutItem(dd)
+{
+ setParentLayoutItem(parent);
+ if (parent && !parent->isLayout()) {
+ // If a layout has a parent that is not a layout it must be a QGraphicsWidget.
+ QGraphicsItem *itemParent = parent->graphicsItem();
+ if (itemParent && itemParent->isWidget()) {
+ static_cast<QGraphicsWidget *>(itemParent)->d_func()->setLayout_helper(this);
+ } else {
+ qWarning("QGraphicsLayout::QGraphicsLayout: Attempt to create a layout with a parent that is"
+ " neither a QGraphicsWidget nor QGraphicsLayout");
+ }
+ }
+ setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding, QSizePolicy::DefaultType);
+ setOwnedByLayout(true);
+}
+
+/*!
+ Destroys the QGraphicsLayout object.
+*/
+QGraphicsLayout::~QGraphicsLayout()
+{
+}
+
+/*!
+ Sets the contents margins to \a left, \a top, \a right and \a bottom. The
+ default contents margins for toplevel layouts are style dependent
+ (by querying the pixelMetric for QStyle::PM_LayoutLeftMargin,
+ QStyle::PM_LayoutTopMargin, QStyle::PM_LayoutRightMargin and
+ QStyle::PM_LayoutBottomMargin).
+
+ For sublayouts the default margins are 0.
+
+ Changing the contents margins automatically invalidates the layout.
+
+ \sa invalidate()
+*/
+void QGraphicsLayout::setContentsMargins(qreal left, qreal top, qreal right, qreal bottom)
+{
+ Q_D(QGraphicsLayout);
+ if (d->left == left && d->top == top && d->right == right && d->bottom == bottom)
+ return;
+ d->left = left;
+ d->right = right;
+ d->top = top;
+ d->bottom = bottom;
+ invalidate();
+}
+
+/*!
+ \reimp
+*/
+void QGraphicsLayout::getContentsMargins(qreal *left, qreal *top, qreal *right, qreal *bottom) const
+{
+ Q_D(const QGraphicsLayout);
+ d->getMargin(left, d->left, QStyle::PM_LayoutLeftMargin);
+ d->getMargin(top, d->top, QStyle::PM_LayoutTopMargin);
+ d->getMargin(right, d->right, QStyle::PM_LayoutRightMargin);
+ d->getMargin(bottom, d->bottom, QStyle::PM_LayoutBottomMargin);
+}
+
+/*!
+ Activates the layout, causing all items in the layout to be immediately
+ rearranged. This function is based on calling count() and itemAt(), and
+ then calling setGeometry() on all items sequentially. When activated,
+ the layout will adjust its geometry to its parent's contentsRect().
+ The parent will then invalidate any layout of its own.
+
+ If called in sequence or recursively, e.g., by one of the arranged items
+ in response to being resized, this function will do nothing.
+
+ Note that the layout is free to use geometry caching to optimize this
+ process. To forcefully invalidate any such cache, you can call
+ invalidate() before calling activate().
+
+ \sa invalidate()
+*/
+void QGraphicsLayout::activate()
+{
+ Q_D(QGraphicsLayout);
+ if (d->activated)
+ return;
+
+ d->activateRecursive(this);
+
+ // we don't call activate on a sublayout, but somebody might.
+ // Therefore, we walk to the parentitem of the toplevel layout.
+ QGraphicsLayoutItem *parentItem = this;
+ while (parentItem && parentItem->isLayout())
+ parentItem = parentItem->parentLayoutItem();
+ if (!parentItem)
+ return;
+ Q_ASSERT(!parentItem->isLayout());
+
+ setGeometry(parentItem->contentsRect()); // relayout children
+
+ // ### bug, should be parentItem ?
+ parentLayoutItem()->updateGeometry(); // bubble up; will set activated to false
+ // ### too many resizes? maybe we should walk up the chain to the
+ // ### top-level layouted layoutItem and call activate there.
+}
+
+/*!
+ Returns true if the layout is currently being activated; otherwise,
+ returns false. If the layout is being activated, this means that it is
+ currently in the process of rearranging its items (i.e., the activate()
+ function has been called, and has not yet returned).
+
+ \sa activate(), invalidate()
+*/
+bool QGraphicsLayout::isActivated() const
+{
+ Q_D(const QGraphicsLayout);
+ return d->activated;
+}
+
+/*!
+ Clears any cached geometry and size hint information in the layout, and
+ posts a \l{QEvent::LayoutRequest}{LayoutRequest} event to the managed
+ parent QGraphicsLayoutItem.
+
+ \sa activate(), setGeometry()
+*/
+void QGraphicsLayout::invalidate()
+{
+ // only mark layouts as invalid (activated = false) if we can post a LayoutRequest event.
+ QGraphicsLayoutItem *layoutItem = this;
+ while (layoutItem && layoutItem->isLayout()) {
+ // we could call updateGeometry(), but what if that method
+ // does not call the base implementation? In addition, updateGeometry()
+ // does more than we need.
+ layoutItem->d_func()->sizeHintCacheDirty = true;
+ layoutItem->d_func()->sizeHintWithConstraintCacheDirty = true;
+ layoutItem = layoutItem->parentLayoutItem();
+ }
+ if (layoutItem) {
+ layoutItem->d_func()->sizeHintCacheDirty = true;
+ layoutItem->d_func()->sizeHintWithConstraintCacheDirty = true;
+ }
+
+ bool postIt = layoutItem ? !layoutItem->isLayout() : false;
+ if (postIt) {
+ layoutItem = this;
+ while (layoutItem && layoutItem->isLayout()
+ && static_cast<QGraphicsLayout*>(layoutItem)->d_func()->activated) {
+ static_cast<QGraphicsLayout*>(layoutItem)->d_func()->activated = false;
+ layoutItem = layoutItem->parentLayoutItem();
+ }
+ if (layoutItem && !layoutItem->isLayout()) {
+ // If a layout has a parent that is not a layout it must be a QGraphicsWidget.
+ QApplication::postEvent(static_cast<QGraphicsWidget *>(layoutItem), new QEvent(QEvent::LayoutRequest));
+ }
+ }
+}
+
+/*!
+ \reimp
+*/
+void QGraphicsLayout::updateGeometry()
+{
+ QGraphicsLayoutItem::updateGeometry();
+ if (QGraphicsLayoutItem *parentItem = parentLayoutItem()) {
+ if (parentItem->isLayout()) {
+ parentItem->updateGeometry();
+ } else {
+ invalidate();
+ }
+ }
+}
+
+/*!
+ This virtual event handler receives all events for the managed
+ widget. QGraphicsLayout uses this event handler to listen for layout
+ related events such as geometry changes, layout changes or layout
+ direction changes.
+
+ \a e is a pointer to the event.
+
+ You can reimplement this event handler to track similar events for your
+ own custom layout.
+
+ \sa QGraphicsWidget::event(), QGraphicsItem::sceneEvent()
+*/
+void QGraphicsLayout::widgetEvent(QEvent *e)
+{
+ switch (e->type()) {
+ case QEvent::GraphicsSceneResize:
+ if (isActivated()) {
+ setGeometry(parentLayoutItem()->contentsRect());
+ } else {
+ activate(); // relies on that activate() will call updateGeometry()
+ }
+ break;
+ case QEvent::LayoutRequest:
+ activate();
+ break;
+ case QEvent::LayoutDirectionChange:
+ invalidate();
+ break;
+ default:
+ break;
+ }
+}
+
+/*!
+ \fn virtual int QGraphicsLayout::count() const = 0
+
+ This pure virtual function must be reimplemented in a subclass of
+ QGraphicsLayout to return the number of items in the layout.
+
+ The subclass is free to decide how to store the items.
+
+ \sa itemAt(), removeAt()
+*/
+
+/*!
+ \fn virtual QGraphicsLayoutItem *QGraphicsLayout::itemAt(int i) const = 0
+
+ This pure virtual function must be reimplemented in a subclass of
+ QGraphicsLayout to return a pointer to the item at index \a i. The
+ reimplementation can assume that \a i is valid (i.e., it respects the
+ value of count()).
+ Together with count(), it is provided as a means of iterating over all items in a layout.
+
+ The subclass is free to decide how to store the items, and the visual arrangement
+ does not have to be reflected through this function.
+
+ \sa count(), removeAt()
+*/
+
+/*!
+ \fn virtual void QGraphicsLayout::removeAt(int index) = 0
+
+ This pure virtual function must be reimplemented in a subclass of
+ QGraphicsLayout to remove the item at \a index. The
+ reimplementation can assume that \a index is valid (i.e., it
+ respects the value of count()).
+
+ The implementation must ensure that the parentLayoutItem() of
+ the removed item does not point to this layout, since the item is
+ considered to be removed from the layout hierarchy.
+
+ If the layout is to be reused between applications, we recommend
+ that the layout deletes the item, but the graphics view framework
+ does not depend on this.
+
+ The subclass is free to decide how to store the items.
+
+ \sa itemAt(), count()
+*/
+
+/*!
+ \since 4.6
+
+ This function is a convenience function provided for custom layouts, and will go through
+ all items in the layout and reparent their graphics items to the closest QGraphicsWidget
+ ancestor of the layout.
+
+ If \a layoutItem is already in a different layout, it will be removed from that layout.
+
+ If custom layouts want special behaviour they can ignore to use this function, and implement
+ their own behaviour.
+
+ \sa graphicsItem()
+ */
+void QGraphicsLayout::addChildLayoutItem(QGraphicsLayoutItem *layoutItem)
+{
+ Q_D(QGraphicsLayout);
+ d->addChildLayoutItem(layoutItem);
+}
+
+QT_END_NAMESPACE
+
+#endif //QT_NO_GRAPHICSVIEW
diff --git a/src/widgets/graphicsview/qgraphicslayout.h b/src/widgets/graphicsview/qgraphicslayout.h
new file mode 100644
index 0000000000..c622fb881e
--- /dev/null
+++ b/src/widgets/graphicsview/qgraphicslayout.h
@@ -0,0 +1,98 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** 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 Technology Preview License Agreement accompanying
+** this package.
+**
+** 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.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QGRAPHICSLAYOUT_H
+#define QGRAPHICSLAYOUT_H
+
+#include <QtGui/qgraphicslayoutitem.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+#if !defined(QT_NO_GRAPHICSVIEW) || (QT_EDITION & QT_MODULE_GRAPHICSVIEW) != QT_MODULE_GRAPHICSVIEW
+
+class QGraphicsLayoutPrivate;
+class QGraphicsLayoutItem;
+class QGraphicsWidget;
+
+class Q_GUI_EXPORT QGraphicsLayout : public QGraphicsLayoutItem
+{
+public:
+ QGraphicsLayout(QGraphicsLayoutItem *parent = 0);
+ ~QGraphicsLayout();
+
+ void setContentsMargins(qreal left, qreal top, qreal right, qreal bottom);
+ void getContentsMargins(qreal *left, qreal *top, qreal *right, qreal *bottom) const;
+
+ void activate();
+ bool isActivated() const;
+ virtual void invalidate();
+ virtual void updateGeometry();
+
+ virtual void widgetEvent(QEvent *e);
+
+ virtual int count() const = 0;
+ virtual QGraphicsLayoutItem *itemAt(int i) const = 0;
+ virtual void removeAt(int index) = 0;
+
+protected:
+ QGraphicsLayout(QGraphicsLayoutPrivate &, QGraphicsLayoutItem *);
+ void addChildLayoutItem(QGraphicsLayoutItem *layoutItem);
+
+private:
+ Q_DISABLE_COPY(QGraphicsLayout)
+ Q_DECLARE_PRIVATE(QGraphicsLayout)
+ friend class QGraphicsWidget;
+};
+
+Q_DECLARE_INTERFACE(QGraphicsLayout, "com.trolltech.Qt.QGraphicsLayout")
+
+#endif
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif
+
diff --git a/src/widgets/graphicsview/qgraphicslayout_p.cpp b/src/widgets/graphicsview/qgraphicslayout_p.cpp
new file mode 100644
index 0000000000..c325602cc6
--- /dev/null
+++ b/src/widgets/graphicsview/qgraphicslayout_p.cpp
@@ -0,0 +1,198 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** 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 Technology Preview License Agreement accompanying
+** this package.
+**
+** 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.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qglobal.h"
+
+#ifndef QT_NO_GRAPHICSVIEW
+
+#include "qgraphicslayout_p.h"
+#include "qgraphicslayout.h"
+#include "qgraphicswidget.h"
+#include "qapplication.h"
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \internal
+
+ \a mw is the new parent. all items in the layout will be a child of \a mw.
+ */
+void QGraphicsLayoutPrivate::reparentChildItems(QGraphicsItem *newParent)
+{
+ Q_Q(QGraphicsLayout);
+ int n = q->count();
+ //bool mwVisible = mw && mw->isVisible();
+ for (int i = 0; i < n; ++i) {
+ QGraphicsLayoutItem *layoutChild = q->itemAt(i);
+ if (!layoutChild) {
+ // Skip stretch items
+ continue;
+ }
+ if (layoutChild->isLayout()) {
+ QGraphicsLayout *l = static_cast<QGraphicsLayout*>(layoutChild);
+ l->d_func()->reparentChildItems(newParent);
+ } else if (QGraphicsItem *itemChild = layoutChild->graphicsItem()){
+ QGraphicsItem *childParent = itemChild->parentItem();
+#ifdef QT_DEBUG
+ if (childParent && childParent != newParent && itemChild->isWidget() && qt_graphicsLayoutDebug()) {
+ QGraphicsWidget *w = static_cast<QGraphicsWidget*>(layoutChild);
+ qWarning("QGraphicsLayout::addChildLayout: widget %s \"%s\" in wrong parent; moved to correct parent",
+ w->metaObject()->className(), w->objectName().toLocal8Bit().constData());
+ }
+#endif
+ if (childParent != newParent)
+ itemChild->setParentItem(newParent);
+ }
+ }
+}
+
+void QGraphicsLayoutPrivate::getMargin(qreal *result, qreal userMargin, QStyle::PixelMetric pm) const
+{
+ if (!result)
+ return;
+ Q_Q(const QGraphicsLayout);
+
+ QGraphicsLayoutItem *parent = q->parentLayoutItem();
+ if (userMargin >= 0.0) {
+ *result = userMargin;
+ } else if (!parent) {
+ *result = 0.0;
+ } else if (parent->isLayout()) { // sublayouts have 0 margin by default
+ *result = 0.0;
+ } else {
+ *result = 0.0;
+ if (QGraphicsItem *layoutParentItem = parentItem()) {
+ if (layoutParentItem->isWidget())
+ *result = (qreal)static_cast<QGraphicsWidget*>(layoutParentItem)->style()->pixelMetric(pm, 0);
+ }
+ }
+}
+
+Qt::LayoutDirection QGraphicsLayoutPrivate::visualDirection() const
+{
+ if (QGraphicsItem *maybeWidget = parentItem()) {
+ if (maybeWidget->isWidget())
+ return static_cast<QGraphicsWidget*>(maybeWidget)->layoutDirection();
+ }
+ return QApplication::layoutDirection();
+}
+
+static bool removeLayoutItemFromLayout(QGraphicsLayout *lay, QGraphicsLayoutItem *layoutItem)
+{
+ if (!lay)
+ return false;
+
+ for (int i = lay->count() - 1; i >= 0; --i) {
+ QGraphicsLayoutItem *child = lay->itemAt(i);
+ if (child && child->isLayout()) {
+ if (removeLayoutItemFromLayout(static_cast<QGraphicsLayout*>(child), layoutItem))
+ return true;
+ } else if (child == layoutItem) {
+ lay->removeAt(i);
+ return true;
+ }
+ }
+ return false;
+}
+
+/*!
+ \internal
+
+ This function is called from subclasses to add a layout item \a layoutItem
+ to a layout.
+
+ It takes care of automatically reparenting graphics items, if needed.
+
+ If \a layoutItem is a is already in a layout, it will remove it from that layout.
+
+*/
+void QGraphicsLayoutPrivate::addChildLayoutItem(QGraphicsLayoutItem *layoutItem)
+{
+ Q_Q(QGraphicsLayout);
+ if (QGraphicsLayoutItem *maybeLayout = layoutItem->parentLayoutItem()) {
+ if (maybeLayout->isLayout())
+ removeLayoutItemFromLayout(static_cast<QGraphicsLayout*>(maybeLayout), layoutItem);
+ }
+ layoutItem->setParentLayoutItem(q);
+ if (layoutItem->isLayout()) {
+ if (QGraphicsItem *parItem = parentItem()) {
+ static_cast<QGraphicsLayout*>(layoutItem)->d_func()->reparentChildItems(parItem);
+ }
+ } else {
+ if (QGraphicsItem *item = layoutItem->graphicsItem()) {
+ QGraphicsItem *newParent = parentItem();
+ QGraphicsItem *oldParent = item->parentItem();
+ if (oldParent == newParent || !newParent)
+ return;
+
+#ifdef QT_DEBUG
+ if (oldParent && item->isWidget()) {
+ QGraphicsWidget *w = static_cast<QGraphicsWidget*>(item);
+ qWarning("QGraphicsLayout::addChildLayoutItem: %s \"%s\" in wrong parent; moved to correct parent",
+ w->metaObject()->className(), w->objectName().toLocal8Bit().constData());
+ }
+#endif
+
+ item->setParentItem(newParent);
+ }
+ }
+}
+
+void QGraphicsLayoutPrivate::activateRecursive(QGraphicsLayoutItem *item)
+{
+ if (item->isLayout()) {
+ QGraphicsLayout *layout = static_cast<QGraphicsLayout *>(item);
+ if (layout->d_func()->activated)
+ layout->invalidate();
+
+ for (int i = layout->count() - 1; i >= 0; --i) {
+ QGraphicsLayoutItem *childItem = layout->itemAt(i);
+ if (childItem)
+ activateRecursive(childItem);
+ }
+ layout->d_func()->activated = true;
+ }
+}
+
+
+QT_END_NAMESPACE
+
+#endif //QT_NO_GRAPHICSVIEW
diff --git a/src/widgets/graphicsview/qgraphicslayout_p.h b/src/widgets/graphicsview/qgraphicslayout_p.h
new file mode 100644
index 0000000000..5358ec3cf4
--- /dev/null
+++ b/src/widgets/graphicsview/qgraphicslayout_p.h
@@ -0,0 +1,154 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** 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 Technology Preview License Agreement accompanying
+** this package.
+**
+** 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.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QGRAPHICSLAYOUT_P_H
+#define QGRAPHICSLAYOUT_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of other Qt classes. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtCore/qglobal.h>
+
+#if !defined(QT_NO_GRAPHICSVIEW) || (QT_EDITION & QT_MODULE_GRAPHICSVIEW) != QT_MODULE_GRAPHICSVIEW
+
+#include "qgraphicslayout.h"
+#include "qgraphicslayoutitem_p.h"
+#include <QtGui/qstyle.h>
+#include <QtGui/qwidget.h>
+#include <QtGui/qstyleoption.h>
+
+QT_BEGIN_NAMESPACE
+
+class QGraphicsLayoutItem;
+class QGraphicsWidget;
+
+#ifdef QT_DEBUG
+inline bool qt_graphicsLayoutDebug()
+{
+ static int checked_env = -1;
+ if(checked_env == -1)
+ checked_env = !!qgetenv("QT_GRAPHICSLAYOUT_DEBUG").toInt();
+ return checked_env;
+}
+#endif
+
+
+class QLayoutStyleInfo
+{
+public:
+ inline QLayoutStyleInfo() { invalidate(); }
+ inline QLayoutStyleInfo(QStyle *style, QWidget *widget)
+ : m_valid(true), m_style(style), m_widget(widget)
+ {
+ Q_ASSERT(style);
+ if (widget) //###
+ m_styleOption.initFrom(widget);
+ m_defaultSpacing[0] = style->pixelMetric(QStyle::PM_LayoutHorizontalSpacing);
+ m_defaultSpacing[1] = style->pixelMetric(QStyle::PM_LayoutVerticalSpacing);
+ }
+
+ inline void invalidate() { m_valid = false; m_style = 0; m_widget = 0; }
+
+ inline QStyle *style() const { return m_style; }
+ inline QWidget *widget() const { return m_widget; }
+
+ inline bool operator==(const QLayoutStyleInfo &other)
+ { return m_style == other.m_style && m_widget == other.m_widget; }
+ inline bool operator!=(const QLayoutStyleInfo &other)
+ { return !(*this == other); }
+
+ inline void setDefaultSpacing(Qt::Orientation o, qreal spacing){
+ if (spacing >= 0)
+ m_defaultSpacing[o - 1] = spacing;
+ }
+
+ inline qreal defaultSpacing(Qt::Orientation o) const {
+ return m_defaultSpacing[o - 1];
+ }
+
+ inline qreal perItemSpacing(QSizePolicy::ControlType control1,
+ QSizePolicy::ControlType control2,
+ Qt::Orientation orientation) const
+ {
+ Q_ASSERT(style());
+ return style()->layoutSpacing(control1, control2, orientation, &m_styleOption, widget());
+ }
+private:
+ bool m_valid;
+ QStyle *m_style;
+ QWidget *m_widget;
+ QStyleOption m_styleOption;
+ qreal m_defaultSpacing[2];
+};
+
+class Q_AUTOTEST_EXPORT QGraphicsLayoutPrivate : public QGraphicsLayoutItemPrivate
+{
+ Q_DECLARE_PUBLIC(QGraphicsLayout)
+
+public:
+ QGraphicsLayoutPrivate() : QGraphicsLayoutItemPrivate(0, true), left(-1.0), top(-1.0), right(-1.0), bottom(-1.0),
+ activated(true) { }
+
+ void reparentChildItems(QGraphicsItem *newParent);
+ void getMargin(qreal *result, qreal userMargin, QStyle::PixelMetric pm) const;
+ Qt::LayoutDirection visualDirection() const;
+
+ void addChildLayoutItem(QGraphicsLayoutItem *item);
+ void activateRecursive(QGraphicsLayoutItem *item);
+
+ qreal left, top, right, bottom;
+ bool activated;
+};
+
+
+QT_END_NAMESPACE
+
+#endif //QT_NO_GRAPHICSVIEW
+
+#endif
diff --git a/src/widgets/graphicsview/qgraphicslayoutitem.cpp b/src/widgets/graphicsview/qgraphicslayoutitem.cpp
new file mode 100644
index 0000000000..c064e01502
--- /dev/null
+++ b/src/widgets/graphicsview/qgraphicslayoutitem.cpp
@@ -0,0 +1,935 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** 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 Technology Preview License Agreement accompanying
+** this package.
+**
+** 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.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qglobal.h"
+
+#ifndef QT_NO_GRAPHICSVIEW
+
+#include "qgraphicslayout.h"
+#include "qgraphicsscene.h"
+#include "qgraphicslayoutitem.h"
+#include "qgraphicslayoutitem_p.h"
+#include "qwidget.h"
+#include "qgraphicswidget.h"
+
+#include <QtDebug>
+
+QT_BEGIN_NAMESPACE
+
+/*
+ COMBINE_SIZE() is identical to combineSize(), except that it
+ doesn't evaluate 'size' unless necessary.
+*/
+#define COMBINE_SIZE(result, size) \
+ do { \
+ if ((result).width() < 0 || (result).height() < 0) \
+ combineSize((result), (size)); \
+ } while (false)
+
+static void combineSize(QSizeF &result, const QSizeF &size)
+{
+ if (result.width() < 0)
+ result.setWidth(size.width());
+ if (result.height() < 0)
+ result.setHeight(size.height());
+}
+
+static void boundSize(QSizeF &result, const QSizeF &size)
+{
+ if (size.width() >= 0 && size.width() < result.width())
+ result.setWidth(size.width());
+ if (size.height() >= 0 && size.height() < result.height())
+ result.setHeight(size.height());
+}
+
+static void expandSize(QSizeF &result, const QSizeF &size)
+{
+ if (size.width() >= 0 && size.width() > result.width())
+ result.setWidth(size.width());
+ if (size.height() >= 0 && size.height() > result.height())
+ result.setHeight(size.height());
+}
+
+static void normalizeHints(qreal &minimum, qreal &preferred, qreal &maximum, qreal &descent)
+{
+ if (minimum >= 0 && maximum >= 0 && minimum > maximum)
+ minimum = maximum;
+
+ if (preferred >= 0) {
+ if (minimum >= 0 && preferred < minimum) {
+ preferred = minimum;
+ } else if (maximum >= 0 && preferred > maximum) {
+ preferred = maximum;
+ }
+ }
+
+ if (minimum >= 0 && descent > minimum)
+ descent = minimum;
+}
+
+/*!
+ \internal
+*/
+QGraphicsLayoutItemPrivate::QGraphicsLayoutItemPrivate(QGraphicsLayoutItem *par, bool layout)
+ : parent(par), userSizeHints(0), isLayout(layout), ownedByLayout(false), graphicsItem(0)
+{
+}
+
+/*!
+ \internal
+*/
+QGraphicsLayoutItemPrivate::~QGraphicsLayoutItemPrivate()
+{
+ // Remove any lazily allocated data
+ delete[] userSizeHints;
+}
+
+/*!
+ \internal
+*/
+void QGraphicsLayoutItemPrivate::init()
+{
+ sizeHintCacheDirty = true;
+ sizeHintWithConstraintCacheDirty = true;
+}
+
+/*!
+ \internal
+*/
+QSizeF *QGraphicsLayoutItemPrivate::effectiveSizeHints(const QSizeF &constraint) const
+{
+ Q_Q(const QGraphicsLayoutItem);
+ QSizeF *sizeHintCache;
+ const bool hasConstraint = constraint.width() >= 0 || constraint.height() >= 0;
+ if (hasConstraint) {
+ if (!sizeHintWithConstraintCacheDirty && constraint == cachedConstraint)
+ return cachedSizeHintsWithConstraints;
+ sizeHintCache = cachedSizeHintsWithConstraints;
+ } else {
+ if (!sizeHintCacheDirty)
+ return cachedSizeHints;
+ sizeHintCache = cachedSizeHints;
+ }
+
+ for (int i = 0; i < Qt::NSizeHints; ++i) {
+ sizeHintCache[i] = constraint;
+ if (userSizeHints)
+ combineSize(sizeHintCache[i], userSizeHints[i]);
+ }
+
+ QSizeF &minS = sizeHintCache[Qt::MinimumSize];
+ QSizeF &prefS = sizeHintCache[Qt::PreferredSize];
+ QSizeF &maxS = sizeHintCache[Qt::MaximumSize];
+ QSizeF &descentS = sizeHintCache[Qt::MinimumDescent];
+
+ normalizeHints(minS.rwidth(), prefS.rwidth(), maxS.rwidth(), descentS.rwidth());
+ normalizeHints(minS.rheight(), prefS.rheight(), maxS.rheight(), descentS.rheight());
+
+ // if the minimum, preferred and maximum sizes contradict each other
+ // (e.g. the minimum is larger than the maximum) we give priority to
+ // the maximum size, then the minimum size and finally the preferred size
+ COMBINE_SIZE(maxS, q->sizeHint(Qt::MaximumSize, maxS));
+ combineSize(maxS, QSizeF(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX));
+ expandSize(maxS, prefS);
+ expandSize(maxS, minS);
+ boundSize(maxS, QSizeF(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX));
+
+ COMBINE_SIZE(minS, q->sizeHint(Qt::MinimumSize, minS));
+ expandSize(minS, QSizeF(0, 0));
+ boundSize(minS, prefS);
+ boundSize(minS, maxS);
+
+ COMBINE_SIZE(prefS, q->sizeHint(Qt::PreferredSize, prefS));
+ expandSize(prefS, minS);
+ boundSize(prefS, maxS);
+
+ // Not supported yet
+ // COMBINE_SIZE(descentS, q->sizeHint(Qt::MinimumDescent, constraint));
+
+ if (hasConstraint) {
+ cachedConstraint = constraint;
+ sizeHintWithConstraintCacheDirty = false;
+ } else {
+ sizeHintCacheDirty = false;
+ }
+ return sizeHintCache;
+}
+
+
+/*!
+ \internal
+
+ Returns the parent item of this layout, or 0 if this layout is
+ not installed on any widget.
+
+ If this is the item that the layout is installed on, it will return "itself".
+
+ If the layout is a sub-layout, this function returns the parent
+ widget of the parent layout.
+
+ Note that it will traverse up the layout item hierarchy instead of just calling
+ QGraphicsItem::parentItem(). This is on purpose.
+
+ \sa parent()
+*/
+QGraphicsItem *QGraphicsLayoutItemPrivate::parentItem() const
+{
+ Q_Q(const QGraphicsLayoutItem);
+
+ const QGraphicsLayoutItem *parent = q;
+ while (parent && parent->isLayout()) {
+ parent = parent->parentLayoutItem();
+ }
+ return parent ? parent->graphicsItem() : 0;
+}
+
+/*!
+ \internal
+
+ Ensures that userSizeHints is allocated.
+ This function must be called before any dereferencing.
+*/
+void QGraphicsLayoutItemPrivate::ensureUserSizeHints()
+{
+ if (!userSizeHints)
+ userSizeHints = new QSizeF[Qt::NSizeHints];
+}
+
+/*!
+ \internal
+
+ Sets the user size hint \a which to \a size. Use an invalid size to unset the size hint.
+ */
+void QGraphicsLayoutItemPrivate::setSize(Qt::SizeHint which, const QSizeF &size)
+{
+ Q_Q(QGraphicsLayoutItem);
+
+ if (userSizeHints) {
+ if (size == userSizeHints[which])
+ return;
+ } else if (size.width() < 0 && size.height() < 0) {
+ return;
+ }
+
+ ensureUserSizeHints();
+ userSizeHints[which] = size;
+ q->updateGeometry();
+}
+
+/*!
+ \internal
+
+ Sets the width of the user size hint \a which to \a width.
+ */
+void QGraphicsLayoutItemPrivate::setSizeComponent(
+ Qt::SizeHint which, SizeComponent component, qreal value)
+{
+ Q_Q(QGraphicsLayoutItem);
+ ensureUserSizeHints();
+ qreal &userValue = (component == Width)
+ ? userSizeHints[which].rwidth()
+ : userSizeHints[which].rheight();
+ if (value == userValue)
+ return;
+ userValue = value;
+ q->updateGeometry();
+}
+
+
+bool QGraphicsLayoutItemPrivate::hasHeightForWidth() const
+{
+ Q_Q(const QGraphicsLayoutItem);
+ if (isLayout) {
+ const QGraphicsLayout *l = static_cast<const QGraphicsLayout *>(q);
+ for (int i = l->count() - 1; i >= 0; --i) {
+ if (QGraphicsLayoutItemPrivate::get(l->itemAt(i))->hasHeightForWidth())
+ return true;
+ }
+ } else if (QGraphicsItem *item = q->graphicsItem()) {
+ if (item->isWidget()) {
+ QGraphicsWidget *w = static_cast<QGraphicsWidget *>(item);
+ if (w->layout()) {
+ return QGraphicsLayoutItemPrivate::get(w->layout())->hasHeightForWidth();
+ }
+ }
+ }
+ return q->sizePolicy().hasHeightForWidth();
+}
+
+bool QGraphicsLayoutItemPrivate::hasWidthForHeight() const
+{
+ Q_Q(const QGraphicsLayoutItem);
+ if (isLayout) {
+ const QGraphicsLayout *l = static_cast<const QGraphicsLayout *>(q);
+ for (int i = l->count() - 1; i >= 0; --i) {
+ if (QGraphicsLayoutItemPrivate::get(l->itemAt(i))->hasWidthForHeight())
+ return true;
+ }
+ } else if (QGraphicsItem *item = q->graphicsItem()) {
+ if (item->isWidget()) {
+ QGraphicsWidget *w = static_cast<QGraphicsWidget *>(item);
+ if (w->layout()) {
+ return QGraphicsLayoutItemPrivate::get(w->layout())->hasWidthForHeight();
+ }
+ }
+ }
+ return q->sizePolicy().hasWidthForHeight();
+}
+
+/*!
+ \class QGraphicsLayoutItem
+ \brief The QGraphicsLayoutItem class can be inherited to allow your custom
+ items to be managed by layouts.
+ \since 4.4
+ \ingroup graphicsview-api
+
+ QGraphicsLayoutItem is an abstract class that defines a set of virtual
+ functions describing sizes, size policies, and size hints for any object
+ arranged by QGraphicsLayout. The API contains functions relevant
+ for both the item itself and for the user of the item as most of
+ QGraphicsLayoutItem's functions are also part of the subclass' public API.
+
+ In most cases, existing layout-aware classes such as QGraphicsWidget and
+ QGraphicsLayout already provide the functionality you require. However,
+ subclassing these classes will enable you to create both graphical
+ elements that work well with layouts (QGraphicsWidget) or custom layouts
+ (QGraphicsLayout).
+
+ \section1 Subclassing QGraphicsLayoutItem
+
+ If you create a subclass of QGraphicsLayoutItem and reimplement its
+ virtual functions, you will enable the layout to resize and position your
+ item along with other QGraphicsLayoutItems including QGraphicsWidget
+ and QGraphicsLayout.
+
+ You can start by reimplementing important functions: the protected
+ sizeHint() function, as well as the public setGeometry()
+ function. If you want your items to be aware of immediate geometry
+ changes, you can also reimplement updateGeometry().
+
+ The geometry, size hint, and size policy affect the item's size and
+ position. Calling setGeometry() will always resize and reposition the item
+ immediately. Normally, this function is called by QGraphicsLayout after
+ the layout has been activated, but it can also be called by the item's user
+ at any time.
+
+ The sizeHint() function returns the item' minimum, preferred and maximum
+ size hints. You can override these properties by calling setMinimumSize(),
+ setPreferredSize() or setMaximumSize(). You can also use functions such as
+ setMinimumWidth() or setMaximumHeight() to set only the width or height
+ component if desired.
+
+ The effectiveSizeHint() function, on the other hand, returns a size hint
+ for any given Qt::SizeHint, and guarantees that the returned size is bound
+ to the minimum and maximum sizes and size hints. You can set the item's
+ vertical and horizontal size policy by calling setSizePolicy(). The
+ sizePolicy property is used by the layout system to describe how this item
+ prefers to grow or shrink.
+
+ \section1 Nesting QGraphicsLayoutItems
+
+ QGraphicsLayoutItems can be nested within other QGraphicsLayoutItems,
+ similar to layouts that can contain sublayouts. This is done either by
+ passing a QGraphicsLayoutItem pointer to QGraphicsLayoutItem's
+ protected constructor, or by calling setParentLayoutItem(). The
+ parentLayoutItem() function returns a pointer to the item's layoutItem
+ parent. If the item's parent is 0 or if the parent does not inherit
+ from QGraphicsItem, the parentLayoutItem() function then returns 0.
+ isLayout() returns true if the QGraphicsLayoutItem subclass is itself a
+ layout, or false otherwise.
+
+ Qt uses QGraphicsLayoutItem to provide layout functionality in the
+ \l{Graphics View Framework}, but in the future its use may spread
+ throughout Qt itself.
+
+ \sa QGraphicsWidget, QGraphicsLayout, QGraphicsLinearLayout,
+ QGraphicsGridLayout
+*/
+
+/*!
+ Constructs the QGraphicsLayoutItem object. \a parent becomes the object's
+ parent. If \a isLayout is true the item is a layout, otherwise
+ \a isLayout is false.
+*/
+QGraphicsLayoutItem::QGraphicsLayoutItem(QGraphicsLayoutItem *parent, bool isLayout)
+ : d_ptr(new QGraphicsLayoutItemPrivate(parent, isLayout))
+{
+ Q_D(QGraphicsLayoutItem);
+ d->init();
+ d->sizePolicy = QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
+ d->q_ptr = this;
+}
+
+/*!
+ \internal
+*/
+QGraphicsLayoutItem::QGraphicsLayoutItem(QGraphicsLayoutItemPrivate &dd)
+ : d_ptr(&dd)
+{
+ Q_D(QGraphicsLayoutItem);
+ d->init();
+ d->q_ptr = this;
+}
+
+/*!
+ Destroys the QGraphicsLayoutItem object.
+*/
+QGraphicsLayoutItem::~QGraphicsLayoutItem()
+{
+ QGraphicsLayoutItem *parentLI = parentLayoutItem();
+ if (parentLI && parentLI->isLayout()) {
+ QGraphicsLayout *lay = static_cast<QGraphicsLayout*>(parentLI);
+ // this is not optimal
+ for (int i = lay->count() - 1; i >= 0; --i) {
+ if (lay->itemAt(i) == this) {
+ lay->removeAt(i);
+ break;
+ }
+ }
+ }
+}
+
+/*!
+ \fn virtual QSizeF QGraphicsLayoutItem::sizeHint(Qt::SizeHint which, const QSizeF &constraint) const = 0;
+
+ This pure virtual function returns the size hint for \a which of the
+ QGraphicsLayoutItem, using the width or height of \a constraint to
+ constrain the output.
+
+ Reimplement this function in a subclass of QGraphicsLayoutItem to
+ provide the necessary size hints for your items.
+
+ \sa effectiveSizeHint()
+*/
+
+/*!
+ Sets the size policy to \a policy. The size policy describes how the item
+ should grow horizontally and vertically when arranged in a layout.
+
+ QGraphicsLayoutItem's default size policy is (QSizePolicy::Fixed,
+ QSizePolicy::Fixed, QSizePolicy::DefaultType), but it is common for
+ subclasses to change the default. For example, QGraphicsWidget defaults
+ to (QSizePolicy::Preferred, QSizePolicy::Preferred,
+ QSizePolicy::DefaultType).
+
+ \sa sizePolicy(), QWidget::sizePolicy()
+*/
+void QGraphicsLayoutItem::setSizePolicy(const QSizePolicy &policy)
+{
+ Q_D(QGraphicsLayoutItem);
+ if (d->sizePolicy == policy)
+ return;
+ d->sizePolicy = policy;
+ updateGeometry();
+}
+
+/*!
+ \overload
+
+ This function is equivalent to calling
+ setSizePolicy(QSizePolicy(\a hPolicy, \a vPolicy, \a controlType)).
+
+ \sa sizePolicy(), QWidget::sizePolicy()
+*/
+void QGraphicsLayoutItem::setSizePolicy(QSizePolicy::Policy hPolicy,
+ QSizePolicy::Policy vPolicy,
+ QSizePolicy::ControlType controlType)
+{
+ setSizePolicy(QSizePolicy(hPolicy, vPolicy, controlType));
+}
+
+/*!
+ Returns the current size policy.
+
+ \sa setSizePolicy(), QWidget::sizePolicy()
+*/
+QSizePolicy QGraphicsLayoutItem::sizePolicy() const
+{
+ Q_D(const QGraphicsLayoutItem);
+ return d->sizePolicy;
+}
+
+/*!
+ Sets the minimum size to \a size. This property overrides sizeHint() for
+ Qt::MinimumSize and ensures that effectiveSizeHint() will never return
+ a size smaller than \a size. In order to unset the minimum size, use an
+ invalid size.
+
+ \sa minimumSize(), maximumSize(), preferredSize(), Qt::MinimumSize,
+ sizeHint(), setMinimumWidth(), setMinimumHeight()
+*/
+void QGraphicsLayoutItem::setMinimumSize(const QSizeF &size)
+{
+ d_ptr->setSize(Qt::MinimumSize, size);
+}
+
+/*!
+ \fn QGraphicsLayoutItem::setMinimumSize(qreal w, qreal h)
+
+ This convenience function is equivalent to calling
+ setMinimumSize(QSizeF(\a w, \a h)).
+
+ \sa minimumSize(), setMaximumSize(), setPreferredSize(), sizeHint()
+*/
+
+/*!
+ Returns the minimum size.
+
+ \sa setMinimumSize(), preferredSize(), maximumSize(), Qt::MinimumSize,
+ sizeHint()
+*/
+QSizeF QGraphicsLayoutItem::minimumSize() const
+{
+ return effectiveSizeHint(Qt::MinimumSize);
+}
+
+/*!
+ Sets the minimum width to \a width.
+
+ \sa minimumWidth(), setMinimumSize(), minimumSize()
+*/
+void QGraphicsLayoutItem::setMinimumWidth(qreal width)
+{
+ d_ptr->setSizeComponent(Qt::MinimumSize, d_ptr->Width, width);
+}
+
+/*!
+ Sets the minimum height to \a height.
+
+ \sa minimumHeight(), setMinimumSize(), minimumSize()
+*/
+void QGraphicsLayoutItem::setMinimumHeight(qreal height)
+{
+ d_ptr->setSizeComponent(Qt::MinimumSize, d_ptr->Height, height);
+}
+
+
+/*!
+ Sets the preferred size to \a size. This property overrides sizeHint() for
+ Qt::PreferredSize and provides the default value for effectiveSizeHint().
+ In order to unset the preferred size, use an invalid size.
+
+ \sa preferredSize(), minimumSize(), maximumSize(), Qt::PreferredSize,
+ sizeHint()
+*/
+void QGraphicsLayoutItem::setPreferredSize(const QSizeF &size)
+{
+ d_ptr->setSize(Qt::PreferredSize, size);
+}
+
+/*!
+ \fn QGraphicsLayoutItem::setPreferredSize(qreal w, qreal h)
+
+ This convenience function is equivalent to calling
+ setPreferredSize(QSizeF(\a w, \a h)).
+
+ \sa preferredSize(), setMaximumSize(), setMinimumSize(), sizeHint()
+*/
+
+/*!
+ Returns the preferred size.
+
+ \sa setPreferredSize(), minimumSize(), maximumSize(), Qt::PreferredSize,
+ sizeHint()
+*/
+QSizeF QGraphicsLayoutItem::preferredSize() const
+{
+ return effectiveSizeHint(Qt::PreferredSize);
+}
+
+/*!
+ Sets the preferred height to \a height.
+
+ \sa preferredWidth(), setPreferredSize(), preferredSize()
+*/
+void QGraphicsLayoutItem::setPreferredHeight(qreal height)
+{
+ d_ptr->setSizeComponent(Qt::PreferredSize, d_ptr->Height, height);
+}
+
+/*!
+ Sets the preferred width to \a width.
+
+ \sa preferredHeight(), setPreferredSize(), preferredSize()
+*/
+void QGraphicsLayoutItem::setPreferredWidth(qreal width)
+{
+ d_ptr->setSizeComponent(Qt::PreferredSize, d_ptr->Width, width);
+}
+
+/*!
+ Sets the maximum size to \a size. This property overrides sizeHint() for
+ Qt::MaximumSize and ensures that effectiveSizeHint() will never return a
+ size larger than \a size. In order to unset the maximum size, use an
+ invalid size.
+
+ \sa maximumSize(), minimumSize(), preferredSize(), Qt::MaximumSize,
+ sizeHint()
+*/
+void QGraphicsLayoutItem::setMaximumSize(const QSizeF &size)
+{
+ d_ptr->setSize(Qt::MaximumSize, size);
+}
+
+/*!
+ \fn QGraphicsLayoutItem::setMaximumSize(qreal w, qreal h)
+
+ This convenience function is equivalent to calling
+ setMaximumSize(QSizeF(\a w, \a h)).
+
+ \sa maximumSize(), setMinimumSize(), setPreferredSize(), sizeHint()
+*/
+
+/*!
+ Returns the maximum size.
+
+ \sa setMaximumSize(), minimumSize(), preferredSize(), Qt::MaximumSize,
+ sizeHint()
+*/
+QSizeF QGraphicsLayoutItem::maximumSize() const
+{
+ return effectiveSizeHint(Qt::MaximumSize);
+}
+
+/*!
+ Sets the maximum width to \a width.
+
+ \sa maximumWidth(), setMaximumSize(), maximumSize()
+*/
+void QGraphicsLayoutItem::setMaximumWidth(qreal width)
+{
+ d_ptr->setSizeComponent(Qt::MaximumSize, d_ptr->Width, width);
+}
+
+/*!
+ Sets the maximum height to \a height.
+
+ \sa maximumHeight(), setMaximumSize(), maximumSize()
+*/
+void QGraphicsLayoutItem::setMaximumHeight(qreal height)
+{
+ d_ptr->setSizeComponent(Qt::MaximumSize, d_ptr->Height, height);
+}
+
+/*!
+ \fn qreal QGraphicsLayoutItem::minimumWidth() const
+
+ Returns the minimum width.
+
+ \sa setMinimumWidth(), setMinimumSize(), minimumSize()
+*/
+
+/*!
+ \fn qreal QGraphicsLayoutItem::minimumHeight() const
+
+ Returns the minimum height.
+
+ \sa setMinimumHeight(), setMinimumSize(), minimumSize()
+*/
+
+/*!
+ \fn qreal QGraphicsLayoutItem::preferredWidth() const
+
+ Returns the preferred width.
+
+ \sa setPreferredWidth(), setPreferredSize(), preferredSize()
+*/
+
+/*!
+ \fn qreal QGraphicsLayoutItem::preferredHeight() const
+
+ Returns the preferred height.
+
+ \sa setPreferredHeight(), setPreferredSize(), preferredSize()
+*/
+
+/*!
+ \fn qreal QGraphicsLayoutItem::maximumWidth() const
+
+ Returns the maximum width.
+
+ \sa setMaximumWidth(), setMaximumSize(), maximumSize()
+*/
+
+/*!
+ \fn qreal QGraphicsLayoutItem::maximumHeight() const
+
+ Returns the maximum height.
+
+ \sa setMaximumHeight(), setMaximumSize(), maximumSize()
+*/
+
+/*!
+ \fn virtual void QGraphicsLayoutItem::setGeometry(const QRectF &rect)
+
+ This virtual function sets the geometry of the QGraphicsLayoutItem to
+ \a rect, which is in parent coordinates (e.g., the top-left corner of \a rect
+ is equivalent to the item's position in parent coordinates).
+
+ You must reimplement this function in a subclass of QGraphicsLayoutItem to
+ receive geometry updates. The layout will call this function when it does a
+ rearrangement.
+
+ If \a rect is outside of the bounds of minimumSize and maximumSize, it
+ will be adjusted to its closest size so that it is within the legal
+ bounds.
+
+ \sa geometry()
+*/
+void QGraphicsLayoutItem::setGeometry(const QRectF &rect)
+{
+ Q_D(QGraphicsLayoutItem);
+ QSizeF effectiveSize = rect.size().expandedTo(effectiveSizeHint(Qt::MinimumSize))
+ .boundedTo(effectiveSizeHint(Qt::MaximumSize));
+ d->geom = QRectF(rect.topLeft(), effectiveSize);
+}
+
+/*!
+ \fn QRectF QGraphicsLayoutItem::geometry() const
+
+ Returns the item's geometry (e.g., position and size) as a
+ QRectF. This function is equivalent to QRectF(pos(), size()).
+
+ \sa setGeometry()
+*/
+QRectF QGraphicsLayoutItem::geometry() const
+{
+ Q_D(const QGraphicsLayoutItem);
+ return d->geom;
+}
+
+/*!
+ This virtual function provides the \a left, \a top, \a right and \a bottom
+ contents margins for this QGraphicsLayoutItem. The default implementation
+ assumes all contents margins are 0. The parameters point to values stored
+ in qreals. If any of the pointers is 0, that value will not be updated.
+
+ \sa QGraphicsWidget::setContentsMargins()
+*/
+void QGraphicsLayoutItem::getContentsMargins(qreal *left, qreal *top, qreal *right, qreal *bottom) const
+{
+ if (left)
+ *left = 0;
+ if (top)
+ *top = 0;
+ if (right)
+ *right = 0;
+ if (bottom)
+ *bottom = 0;
+}
+
+/*!
+ Returns the contents rect in local coordinates.
+
+ The contents rect defines the subrectangle used by an associated layout
+ when arranging subitems. This function is a convenience function that
+ adjusts the item's geometry() by its contents margins. Note that
+ getContentsMargins() is a virtual function that you can reimplement to
+ return the item's contents margins.
+
+ \sa getContentsMargins(), geometry()
+*/
+QRectF QGraphicsLayoutItem::contentsRect() const
+{
+ qreal left, top, right, bottom;
+ getContentsMargins(&left, &top, &right, &bottom);
+ return QRectF(QPointF(), geometry().size()).adjusted(+left, +top, -right, -bottom);
+}
+
+/*!
+ Returns the effective size hint for this QGraphicsLayoutItem.
+
+ \a which is the size hint in question.
+ \a constraint is an optional argument that defines a special constrain
+ when calculating the effective size hint. By default, \a constraint is
+ QSizeF(-1, -1), which means there is no constraint to the size hint.
+
+ If you want to specify the widget's size hint for a given width or height,
+ you can provide the fixed dimension in \a constraint. This is useful for
+ widgets that can grow only either vertically or horizontally, and need to
+ set either their width or their height to a special value.
+
+ For example, a text paragraph item fit into a column width of 200 may
+ grow vertically. You can pass QSizeF(200, -1) as a constraint to get a
+ suitable minimum, preferred and maximum height).
+
+ You can adjust the effective size hint by reimplementing sizeHint()
+ in a QGraphicsLayoutItem subclass, or by calling one of the following
+ functions: setMinimumSize(), setPreferredSize, or setMaximumSize()
+ (or a combination of both).
+
+ This function caches each of the size hints and guarantees that
+ sizeHint() will be called only once for each value of \a which - unless
+ \a constraint is not specified and updateGeometry() has been called.
+
+ \sa sizeHint()
+*/
+QSizeF QGraphicsLayoutItem::effectiveSizeHint(Qt::SizeHint which, const QSizeF &constraint) const
+{
+ Q_D(const QGraphicsLayoutItem);
+
+ if (!d->userSizeHints && constraint.isValid())
+ return constraint;
+
+ // ### should respect size policy???
+ return d_ptr->effectiveSizeHints(constraint)[which];
+}
+
+/*!
+ This virtual function discards any cached size hint information. You
+ should always call this function if you change the return value of the
+ sizeHint() function. Subclasses must always call the base implementation
+ when reimplementing this function.
+
+ \sa effectiveSizeHint()
+*/
+void QGraphicsLayoutItem::updateGeometry()
+{
+ Q_D(QGraphicsLayoutItem);
+ d->sizeHintCacheDirty = true;
+ d->sizeHintWithConstraintCacheDirty = true;
+}
+
+/*!
+ Returns the parent of this QGraphicsLayoutItem, or 0 if there is no parent,
+ or if the parent does not inherit from QGraphicsLayoutItem
+ (QGraphicsLayoutItem is often used through multiple inheritance with
+ QObject-derived classes).
+
+ \sa setParentLayoutItem()
+*/
+QGraphicsLayoutItem *QGraphicsLayoutItem::parentLayoutItem() const
+{
+ return d_func()->parent;
+}
+
+/*!
+ Sets the parent of this QGraphicsLayoutItem to \a parent.
+
+ \sa parentLayoutItem()
+*/
+void QGraphicsLayoutItem::setParentLayoutItem(QGraphicsLayoutItem *parent)
+{
+ d_func()->parent = parent;
+}
+
+/*!
+ Returns true if this QGraphicsLayoutItem is a layout (e.g., is inherited
+ by an object that arranges other QGraphicsLayoutItem objects); otherwise
+ returns false.
+
+ \sa QGraphicsLayout
+*/
+bool QGraphicsLayoutItem::isLayout() const
+{
+ return d_func()->isLayout;
+}
+
+/*!
+ \since 4.6
+
+ Returns whether a layout should delete this item in its destructor.
+ If its true, then the layout will delete it. If its false, then it is
+ assumed that another object has the ownership of it, and the layout won't
+ delete this item.
+
+ If the item inherits both QGraphicsItem and QGraphicsLayoutItem (such
+ as QGraphicsWidget does) the item is really part of two ownership
+ hierarchies. This property informs what the layout should do with its
+ child items when it is destructed. In the case of QGraphicsWidget, it
+ is preferred that when the layout is deleted it won't delete its children
+ (since they are also part of the graphics item hierarchy).
+
+ By default this value is initialized to false in QGraphicsLayoutItem,
+ but it is overridden by QGraphicsLayout to return true. This is because
+ QGraphicsLayout is not normally part of the QGraphicsItem hierarchy, so the
+ parent layout should delete it.
+ Subclasses might override this default behaviour by calling
+ setOwnedByLayout(true).
+
+ \sa setOwnedByLayout()
+*/
+bool QGraphicsLayoutItem::ownedByLayout() const
+{
+ return d_func()->ownedByLayout;
+}
+/*!
+ \since 4.6
+
+ Sets whether a layout should delete this item in its destructor or not.
+ \a ownership must be true to in order for the layout to delete it.
+ \sa ownedByLayout()
+*/
+void QGraphicsLayoutItem::setOwnedByLayout(bool ownership)
+{
+ d_func()->ownedByLayout = ownership;
+}
+
+/*!
+ * Returns the QGraphicsItem that this layout item represents.
+ * For QGraphicsWidget it will return itself. For custom items it can return an
+ * aggregated value.
+ *
+ * \sa setGraphicsItem()
+ */
+QGraphicsItem *QGraphicsLayoutItem::graphicsItem() const
+{
+ return d_func()->graphicsItem;
+}
+
+/*!
+ * If the QGraphicsLayoutItem represents a QGraphicsItem, and it wants to take
+ * advantage of the automatic reparenting capabilities of QGraphicsLayout it
+ * should set this value.
+ * Note that if you delete \a item and not delete the layout item, you are
+ * responsible of calling setGraphicsItem(0) in order to avoid having a
+ * dangling pointer.
+ *
+ * \sa graphicsItem()
+ */
+void QGraphicsLayoutItem::setGraphicsItem(QGraphicsItem *item)
+{
+ d_func()->graphicsItem = item;
+}
+
+QT_END_NAMESPACE
+
+#endif //QT_NO_GRAPHICSVIEW
diff --git a/src/widgets/graphicsview/qgraphicslayoutitem.h b/src/widgets/graphicsview/qgraphicslayoutitem.h
new file mode 100644
index 0000000000..7112dd06fc
--- /dev/null
+++ b/src/widgets/graphicsview/qgraphicslayoutitem.h
@@ -0,0 +1,155 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** 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 Technology Preview License Agreement accompanying
+** this package.
+**
+** 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.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QGRAPHICSLAYOUTITEM_H
+#define QGRAPHICSLAYOUTITEM_H
+
+#include <QtCore/qscopedpointer.h>
+#include <QtGui/qsizepolicy.h>
+#include <QtGui/qevent.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+#if !defined(QT_NO_GRAPHICSVIEW) || (QT_EDITION & QT_MODULE_GRAPHICSVIEW) != QT_MODULE_GRAPHICSVIEW
+
+class QGraphicsLayoutItemPrivate;
+class QGraphicsItem;
+class Q_GUI_EXPORT QGraphicsLayoutItem
+{
+public:
+ QGraphicsLayoutItem(QGraphicsLayoutItem *parent = 0, bool isLayout = false);
+ virtual ~QGraphicsLayoutItem();
+
+ void setSizePolicy(const QSizePolicy &policy);
+ void setSizePolicy(QSizePolicy::Policy hPolicy, QSizePolicy::Policy vPolicy, QSizePolicy::ControlType controlType = QSizePolicy::DefaultType);
+ QSizePolicy sizePolicy() const;
+
+ void setMinimumSize(const QSizeF &size);
+ inline void setMinimumSize(qreal w, qreal h);
+ QSizeF minimumSize() const;
+ void setMinimumWidth(qreal width);
+ inline qreal minimumWidth() const;
+ void setMinimumHeight(qreal height);
+ inline qreal minimumHeight() const;
+
+ void setPreferredSize(const QSizeF &size);
+ inline void setPreferredSize(qreal w, qreal h);
+ QSizeF preferredSize() const;
+ void setPreferredWidth(qreal width);
+ inline qreal preferredWidth() const;
+ void setPreferredHeight(qreal height);
+ inline qreal preferredHeight() const;
+
+ void setMaximumSize(const QSizeF &size);
+ inline void setMaximumSize(qreal w, qreal h);
+ QSizeF maximumSize() const;
+ void setMaximumWidth(qreal width);
+ inline qreal maximumWidth() const;
+ void setMaximumHeight(qreal height);
+ inline qreal maximumHeight() const;
+
+ virtual void setGeometry(const QRectF &rect);
+ QRectF geometry() const;
+ virtual void getContentsMargins(qreal *left, qreal *top, qreal *right, qreal *bottom) const;
+ QRectF contentsRect() const;
+
+ QSizeF effectiveSizeHint(Qt::SizeHint which, const QSizeF &constraint = QSizeF()) const;
+
+ virtual void updateGeometry(); //### rename to sizeHintChanged()
+
+ QGraphicsLayoutItem *parentLayoutItem() const;
+ void setParentLayoutItem(QGraphicsLayoutItem *parent);
+
+ bool isLayout() const;
+ // ###Qt5: Make automatic reparenting work regardless of item/object/widget type.
+ QGraphicsItem *graphicsItem() const;
+ bool ownedByLayout() const;
+
+protected:
+ void setGraphicsItem(QGraphicsItem *item);
+ void setOwnedByLayout(bool ownedByLayout);
+ QGraphicsLayoutItem(QGraphicsLayoutItemPrivate &dd);
+
+ virtual QSizeF sizeHint(Qt::SizeHint which, const QSizeF &constraint = QSizeF()) const = 0;
+ QScopedPointer<QGraphicsLayoutItemPrivate> d_ptr;
+
+private:
+ QSizeF *effectiveSizeHints(const QSizeF &constraint) const;
+ Q_DECLARE_PRIVATE(QGraphicsLayoutItem)
+
+ friend class QGraphicsLayout;
+};
+
+Q_DECLARE_INTERFACE(QGraphicsLayoutItem, "com.trolltech.Qt.QGraphicsLayoutItem")
+
+inline void QGraphicsLayoutItem::setMinimumSize(qreal aw, qreal ah)
+{ setMinimumSize(QSizeF(aw, ah)); }
+inline void QGraphicsLayoutItem::setPreferredSize(qreal aw, qreal ah)
+{ setPreferredSize(QSizeF(aw, ah)); }
+inline void QGraphicsLayoutItem::setMaximumSize(qreal aw, qreal ah)
+{ setMaximumSize(QSizeF(aw, ah)); }
+
+inline qreal QGraphicsLayoutItem::minimumWidth() const
+{ return effectiveSizeHint(Qt::MinimumSize).width(); }
+inline qreal QGraphicsLayoutItem::minimumHeight() const
+{ return effectiveSizeHint(Qt::MinimumSize).height(); }
+
+inline qreal QGraphicsLayoutItem::preferredWidth() const
+{ return effectiveSizeHint(Qt::PreferredSize).width(); }
+inline qreal QGraphicsLayoutItem::preferredHeight() const
+{ return effectiveSizeHint(Qt::PreferredSize).height(); }
+
+inline qreal QGraphicsLayoutItem::maximumWidth() const
+{ return effectiveSizeHint(Qt::MaximumSize).width(); }
+inline qreal QGraphicsLayoutItem::maximumHeight() const
+{ return effectiveSizeHint(Qt::MaximumSize).height(); }
+
+#endif
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif
diff --git a/src/widgets/graphicsview/qgraphicslayoutitem_p.h b/src/widgets/graphicsview/qgraphicslayoutitem_p.h
new file mode 100644
index 0000000000..85481b7a46
--- /dev/null
+++ b/src/widgets/graphicsview/qgraphicslayoutitem_p.h
@@ -0,0 +1,103 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** 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 Technology Preview License Agreement accompanying
+** this package.
+**
+** 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.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QGRAPHICSLAYOUTITEM_P_H
+#define QGRAPHICSLAYOUTITEM_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of other Qt classes. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtCore/QSizeF>
+#include <QtGui/QSizePolicy>
+
+QT_BEGIN_NAMESPACE
+
+class QGraphicsLayoutItem;
+class Q_AUTOTEST_EXPORT QGraphicsLayoutItemPrivate
+{
+ Q_DECLARE_PUBLIC(QGraphicsLayoutItem)
+public:
+ virtual ~QGraphicsLayoutItemPrivate();
+ QGraphicsLayoutItemPrivate(QGraphicsLayoutItem *parent, bool isLayout);
+ static QGraphicsLayoutItemPrivate *get(QGraphicsLayoutItem *q) { return q->d_func();}
+ static const QGraphicsLayoutItemPrivate *get(const QGraphicsLayoutItem *q) { return q->d_func();}
+
+ void init();
+ QSizeF *effectiveSizeHints(const QSizeF &constraint) const;
+ QGraphicsItem *parentItem() const;
+ void ensureUserSizeHints();
+ void setSize(Qt::SizeHint which, const QSizeF &size);
+ enum SizeComponent { Width, Height };
+ void setSizeComponent(Qt::SizeHint which, SizeComponent component, qreal value);
+
+ bool hasHeightForWidth() const;
+ bool hasWidthForHeight() const;
+
+ QSizePolicy sizePolicy;
+ QGraphicsLayoutItem *parent;
+
+ QSizeF *userSizeHints;
+ mutable QSizeF cachedSizeHints[Qt::NSizeHints];
+ mutable QSizeF cachedConstraint;
+ mutable QSizeF cachedSizeHintsWithConstraints[Qt::NSizeHints];
+
+ mutable quint32 sizeHintCacheDirty : 1;
+ mutable quint32 sizeHintWithConstraintCacheDirty : 1;
+ quint32 isLayout : 1;
+ quint32 ownedByLayout : 1;
+
+ QGraphicsLayoutItem *q_ptr;
+ QRectF geom;
+ QGraphicsItem *graphicsItem;
+};
+
+QT_END_NAMESPACE
+
+#endif //QGRAPHICSLAYOUTITEM_P_H
+
diff --git a/src/widgets/graphicsview/qgraphicslinearlayout.cpp b/src/widgets/graphicsview/qgraphicslinearlayout.cpp
new file mode 100644
index 0000000000..5591638395
--- /dev/null
+++ b/src/widgets/graphicsview/qgraphicslinearlayout.cpp
@@ -0,0 +1,568 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** 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 Technology Preview License Agreement accompanying
+** this package.
+**
+** 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.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*!
+ \class QGraphicsLinearLayout
+ \brief The QGraphicsLinearLayout class provides a horizontal or vertical
+ layout for managing widgets in Graphics View.
+ \since 4.4
+ \ingroup graphicsview-api
+
+ The default orientation for a linear layout is Qt::Horizontal. You can
+ choose a vertical orientation either by calling setOrientation(), or by
+ passing Qt::Vertical to QGraphicsLinearLayout's constructor.
+
+ The most common way to use QGraphicsLinearLayout is to construct an object
+ on the heap with no parent, add widgets and layouts by calling addItem(),
+ and finally assign the layout to a widget by calling
+ QGraphicsWidget::setLayout().
+
+ \snippet doc/src/snippets/code/src_gui_graphicsview_qgraphicslinearlayout.cpp 0
+
+ You can add widgets, layouts, stretches (addStretch(), insertStretch() or
+ setStretchFactor()), and spacings (setItemSpacing()) to a linear
+ layout. The layout takes ownership of the items. In some cases when the layout
+ item also inherits from QGraphicsItem (such as QGraphicsWidget) there will be a
+ ambiguity in ownership because the layout item belongs to two ownership hierarchies.
+ See the documentation of QGraphicsLayoutItem::setOwnedByLayout() how to handle
+ this.
+ You can access each item in the layout by calling count() and itemAt(). Calling
+ removeAt() or removeItem() will remove an item from the layout, without
+ destroying it.
+
+ \section1 Size Hints and Size Policies in QGraphicsLinearLayout
+
+ QGraphicsLinearLayout respects each item's size hints and size policies,
+ and when the layout contains more space than the items can fill, each item
+ is arranged according to the layout's alignment for that item. You can set
+ an alignment for each item by calling setAlignment(), and check the
+ alignment for any item by calling alignment(). By default, items are
+ aligned to the top left.
+
+ \section1 Spacing within QGraphicsLinearLayout
+
+ Between the items, the layout distributes some space. The actual amount of
+ space depends on the managed widget's current style, but the common
+ spacing is 4. You can also set your own spacing by calling setSpacing(),
+ and get the current spacing value by calling spacing(). If you want to
+ configure individual spacing for your items, you can call setItemSpacing().
+
+ \section1 Stretch Factor in QGraphicsLinearLayout
+
+ You can assign a stretch factor to each item to control how much space it
+ will get compared to the other items. By default, two identical widgets
+ arranged in a linear layout will have the same size, but if the first
+ widget has a stretch factor of 1 and the second widget has a stretch
+ factor of 2, the first widget will get 1/3 of the available space, and the
+ second will get 2/3.
+
+ QGraphicsLinearLayout calculates the distribution of sizes by adding up
+ the stretch factors of all items, and then dividing the available space
+ accordingly. The default stretch factor is 0 for all items; a factor of 0
+ means the item does not have any defined stretch factor; effectively this
+ is the same as setting the stretch factor to 1. The stretch factor only
+ applies to the available space in the lengthwise direction of the layout
+ (following its orientation). If you want to control both the item's
+ horizontal and vertical stretch, you can use QGraphicsGridLayout instead.
+
+ \section1 QGraphicsLinearLayout Compared to Other Layouts
+
+ QGraphicsLinearLayout is very similar to QVBoxLayout and QHBoxLayout, but
+ in contrast to these classes, it is used to manage QGraphicsWidget and
+ QGraphicsLayout instead of QWidget and QLayout.
+
+ \sa QGraphicsGridLayout, QGraphicsWidget
+*/
+
+#include "qapplication.h"
+
+#ifndef QT_NO_GRAPHICSVIEW
+
+#include "qwidget.h"
+#include "qgraphicslayout_p.h"
+#include "qgraphicslayoutitem.h"
+#include "qgraphicslinearlayout.h"
+#include "qgraphicswidget.h"
+#include "qgridlayoutengine_p.h"
+#ifdef QT_DEBUG
+#include <QtCore/qdebug.h>
+#endif
+
+QT_BEGIN_NAMESPACE
+
+class QGraphicsLinearLayoutPrivate : public QGraphicsLayoutPrivate
+{
+public:
+ QGraphicsLinearLayoutPrivate(Qt::Orientation orientation) : orientation(orientation) { }
+
+ void removeGridItem(QGridLayoutItem *gridItem);
+ QLayoutStyleInfo styleInfo() const;
+ void fixIndex(int *index) const;
+ int gridRow(int index) const;
+ int gridColumn(int index) const;
+
+ Qt::Orientation orientation;
+ QGridLayoutEngine engine;
+};
+
+void QGraphicsLinearLayoutPrivate::removeGridItem(QGridLayoutItem *gridItem)
+{
+ int index = gridItem->firstRow(orientation);
+ engine.removeItem(gridItem);
+ engine.removeRows(index, 1, orientation);
+}
+
+void QGraphicsLinearLayoutPrivate::fixIndex(int *index) const
+{
+ int count = engine.rowCount(orientation);
+ if (uint(*index) > uint(count))
+ *index = count;
+}
+
+int QGraphicsLinearLayoutPrivate::gridRow(int index) const
+{
+ if (orientation == Qt::Horizontal)
+ return 0;
+ return int(qMin(uint(index), uint(engine.rowCount())));
+}
+
+int QGraphicsLinearLayoutPrivate::gridColumn(int index) const
+{
+ if (orientation == Qt::Vertical)
+ return 0;
+ return int(qMin(uint(index), uint(engine.columnCount())));
+}
+
+Q_GLOBAL_STATIC(QWidget, globalStyleInfoWidget)
+
+QLayoutStyleInfo QGraphicsLinearLayoutPrivate::styleInfo() const
+{
+ QGraphicsItem *item = parentItem();
+ QStyle *style = (item && item->isWidget()) ? static_cast<QGraphicsWidget*>(item)->style() : QApplication::style();
+ return QLayoutStyleInfo(style, globalStyleInfoWidget());
+}
+
+/*!
+ Constructs a QGraphicsLinearLayout instance. You can pass the
+ \a orientation for the layout, either horizontal or vertical, and
+ \a parent is passed to QGraphicsLayout's constructor.
+*/
+QGraphicsLinearLayout::QGraphicsLinearLayout(Qt::Orientation orientation, QGraphicsLayoutItem *parent)
+ : QGraphicsLayout(*new QGraphicsLinearLayoutPrivate(orientation), parent)
+{
+}
+
+/*!
+ Constructs a QGraphicsLinearLayout instance using Qt::Horizontal
+ orientation. \a parent is passed to QGraphicsLayout's constructor.
+*/
+QGraphicsLinearLayout::QGraphicsLinearLayout(QGraphicsLayoutItem *parent)
+ : QGraphicsLayout(*new QGraphicsLinearLayoutPrivate(Qt::Horizontal), parent)
+{
+}
+
+/*!
+ Destroys the QGraphicsLinearLayout object.
+*/
+QGraphicsLinearLayout::~QGraphicsLinearLayout()
+{
+ for (int i = count() - 1; i >= 0; --i) {
+ QGraphicsLayoutItem *item = itemAt(i);
+ // The following lines can be removed, but this removes the item
+ // from the layout more efficiently than the implementation of
+ // ~QGraphicsLayoutItem.
+ removeAt(i);
+ if (item) {
+ item->setParentLayoutItem(0);
+ if (item->ownedByLayout())
+ delete item;
+ }
+ }
+}
+
+/*!
+ Change the layout orientation to \a orientation. Changing the layout
+ orientation will automatically invalidate the layout.
+
+ \sa orientation()
+*/
+void QGraphicsLinearLayout::setOrientation(Qt::Orientation orientation)
+{
+ Q_D(QGraphicsLinearLayout);
+ if (orientation != d->orientation) {
+ d->engine.transpose();
+ d->orientation = orientation;
+ invalidate();
+ }
+}
+
+/*!
+ Returns the layout orientation.
+ \sa setOrientation()
+ */
+Qt::Orientation QGraphicsLinearLayout::orientation() const
+{
+ Q_D(const QGraphicsLinearLayout);
+ return d->orientation;
+}
+
+/*!
+ \fn void QGraphicsLinearLayout::addItem(QGraphicsLayoutItem *item)
+
+ This convenience function is equivalent to calling
+ insertItem(-1, \a item).
+*/
+
+/*!
+ \fn void QGraphicsLinearLayout::addStretch(int stretch)
+
+ This convenience function is equivalent to calling
+ insertStretch(-1, \a stretch).
+*/
+
+/*!
+ Inserts \a item into the layout at \a index, or before any item that is
+ currently at \a index.
+
+ \sa addItem(), itemAt(), insertStretch(), setItemSpacing()
+*/
+void QGraphicsLinearLayout::insertItem(int index, QGraphicsLayoutItem *item)
+{
+ Q_D(QGraphicsLinearLayout);
+ if (!item) {
+ qWarning("QGraphicsLinearLayout::insertItem: cannot insert null item");
+ return;
+ }
+ if (item == this) {
+ qWarning("QGraphicsLinearLayout::insertItem: cannot insert itself");
+ return;
+ }
+ Q_ASSERT(item);
+
+ //the order of the following instructions is very important because
+ //invalidating the layout before adding the child item will make the layout happen
+ //before we try to paint the item
+ invalidate();
+ d->addChildLayoutItem(item);
+
+ d->fixIndex(&index);
+ d->engine.insertRow(index, d->orientation);
+ new QGridLayoutItem(&d->engine, item, d->gridRow(index), d->gridColumn(index), 1, 1, 0, index);
+}
+
+/*!
+ Inserts a stretch of \a stretch at \a index, or before any item that is
+ currently at \a index.
+
+ \sa addStretch(), setStretchFactor(), setItemSpacing(), insertItem()
+*/
+void QGraphicsLinearLayout::insertStretch(int index, int stretch)
+{
+ Q_D(QGraphicsLinearLayout);
+ d->fixIndex(&index);
+ d->engine.insertRow(index, d->orientation);
+ d->engine.setRowStretchFactor(index, stretch, d->orientation);
+ invalidate();
+}
+
+/*!
+ Removes \a item from the layout without destroying it. Ownership of
+ \a item is transferred to the caller.
+
+ \sa removeAt(), insertItem()
+*/
+void QGraphicsLinearLayout::removeItem(QGraphicsLayoutItem *item)
+{
+ Q_D(QGraphicsLinearLayout);
+ if (QGridLayoutItem *gridItem = d->engine.findLayoutItem(item)) {
+ item->setParentLayoutItem(0);
+ d->removeGridItem(gridItem);
+ delete gridItem;
+ invalidate();
+ }
+}
+
+/*!
+ Removes the item at \a index without destroying it. Ownership of the item
+ is transferred to the caller.
+
+ \sa removeItem(), insertItem()
+*/
+void QGraphicsLinearLayout::removeAt(int index)
+{
+ Q_D(QGraphicsLinearLayout);
+ if (index < 0 || index >= d->engine.itemCount()) {
+ qWarning("QGraphicsLinearLayout::removeAt: invalid index %d", index);
+ return;
+ }
+ if (QGridLayoutItem *gridItem = d->engine.itemAt(index)) {
+ if (QGraphicsLayoutItem *layoutItem = gridItem->layoutItem())
+ layoutItem->setParentLayoutItem(0);
+ d->removeGridItem(gridItem);
+ delete gridItem;
+ invalidate();
+ }
+}
+
+/*!
+ Sets the layout's spacing to \a spacing. Spacing refers to the
+ vertical and horizontal distances between items.
+
+ \sa setItemSpacing(), setStretchFactor(), QGraphicsGridLayout::setSpacing()
+*/
+void QGraphicsLinearLayout::setSpacing(qreal spacing)
+{
+ Q_D(QGraphicsLinearLayout);
+ if (spacing < 0) {
+ qWarning("QGraphicsLinearLayout::setSpacing: invalid spacing %g", spacing);
+ return;
+ }
+ d->engine.setSpacing(spacing, Qt::Horizontal | Qt::Vertical);
+ invalidate();
+}
+
+/*!
+ Returns the layout's spacing. Spacing refers to the
+ vertical and horizontal distances between items.
+
+ \sa setSpacing()
+ */
+qreal QGraphicsLinearLayout::spacing() const
+{
+ Q_D(const QGraphicsLinearLayout);
+ return d->engine.spacing(d->styleInfo(), d->orientation);
+}
+
+/*!
+ Sets the spacing after item at \a index to \a spacing.
+*/
+void QGraphicsLinearLayout::setItemSpacing(int index, qreal spacing)
+{
+ Q_D(QGraphicsLinearLayout);
+ d->engine.setRowSpacing(index, spacing, d->orientation);
+ invalidate();
+}
+/*!
+ Returns the spacing after item at \a index.
+*/
+qreal QGraphicsLinearLayout::itemSpacing(int index) const
+{
+ Q_D(const QGraphicsLinearLayout);
+ return d->engine.rowSpacing(index, d->orientation);
+}
+
+/*!
+ Sets the stretch factor for \a item to \a stretch. If an item's stretch
+ factor changes, this function will invalidate the layout.
+
+ Setting \a stretch to 0 removes the stretch factor from the item, and is
+ effectively equivalent to setting \a stretch to 1.
+
+ \sa stretchFactor()
+*/
+void QGraphicsLinearLayout::setStretchFactor(QGraphicsLayoutItem *item, int stretch)
+{
+ Q_D(QGraphicsLinearLayout);
+ if (!item) {
+ qWarning("QGraphicsLinearLayout::setStretchFactor: cannot assign"
+ " a stretch factor to a null item");
+ return;
+ }
+ if (stretchFactor(item) == stretch)
+ return;
+ d->engine.setStretchFactor(item, stretch, d->orientation);
+ invalidate();
+}
+
+/*!
+ Returns the stretch factor for \a item. The default stretch factor is 0,
+ meaning that the item has no assigned stretch factor.
+
+ \sa setStretchFactor()
+*/
+int QGraphicsLinearLayout::stretchFactor(QGraphicsLayoutItem *item) const
+{
+ Q_D(const QGraphicsLinearLayout);
+ if (!item) {
+ qWarning("QGraphicsLinearLayout::setStretchFactor: cannot return"
+ " a stretch factor for a null item");
+ return 0;
+ }
+ return d->engine.stretchFactor(item, d->orientation);
+}
+
+/*!
+ Sets the alignment of \a item to \a alignment. If \a item's alignment
+ changes, the layout is automatically invalidated.
+
+ \sa alignment(), invalidate()
+*/
+void QGraphicsLinearLayout::setAlignment(QGraphicsLayoutItem *item, Qt::Alignment alignment)
+{
+ Q_D(QGraphicsLinearLayout);
+ if (this->alignment(item) == alignment)
+ return;
+ d->engine.setAlignment(item, alignment);
+ invalidate();
+}
+
+/*!
+ Returns the alignment for \a item. The default alignment is
+ Qt::AlignTop | Qt::AlignLeft.
+
+ The alignment decides how the item is positioned within its assigned space
+ in the case where there's more space available in the layout than the
+ widgets can occupy.
+
+ \sa setAlignment()
+*/
+Qt::Alignment QGraphicsLinearLayout::alignment(QGraphicsLayoutItem *item) const
+{
+ Q_D(const QGraphicsLinearLayout);
+ return d->engine.alignment(item);
+}
+
+#if 0 // ###
+QSizePolicy::ControlTypes QGraphicsLinearLayout::controlTypes(LayoutSide side) const
+{
+ return d->engine.controlTypes(side);
+}
+#endif
+
+/*!
+ \reimp
+*/
+int QGraphicsLinearLayout::count() const
+{
+ Q_D(const QGraphicsLinearLayout);
+ return d->engine.itemCount();
+}
+
+/*!
+ \reimp
+ When iterating from 0 and up, it will return the items in the visual arranged order.
+*/
+QGraphicsLayoutItem *QGraphicsLinearLayout::itemAt(int index) const
+{
+ Q_D(const QGraphicsLinearLayout);
+ if (index < 0 || index >= d->engine.itemCount()) {
+ qWarning("QGraphicsLinearLayout::itemAt: invalid index %d", index);
+ return 0;
+ }
+ QGraphicsLayoutItem *item = 0;
+ if (QGridLayoutItem *gridItem = d->engine.itemAt(index))
+ item = gridItem->layoutItem();
+ return item;
+}
+
+/*!
+ \reimp
+*/
+void QGraphicsLinearLayout::setGeometry(const QRectF &rect)
+{
+ Q_D(QGraphicsLinearLayout);
+ QGraphicsLayout::setGeometry(rect);
+ QRectF effectiveRect = geometry();
+ qreal left, top, right, bottom;
+ getContentsMargins(&left, &top, &right, &bottom);
+ Qt::LayoutDirection visualDir = d->visualDirection();
+ d->engine.setVisualDirection(visualDir);
+ if (visualDir == Qt::RightToLeft)
+ qSwap(left, right);
+ effectiveRect.adjust(+left, +top, -right, -bottom);
+#ifdef QT_DEBUG
+ if (qt_graphicsLayoutDebug()) {
+ static int counter = 0;
+ qDebug() << counter++ << "QGraphicsLinearLayout::setGeometry - " << rect;
+ dump(1);
+ }
+#endif
+ d->engine.setGeometries(d->styleInfo(), effectiveRect);
+#ifdef QT_DEBUG
+ if (qt_graphicsLayoutDebug()) {
+ qDebug() << "post dump";
+ dump(1);
+ }
+#endif
+}
+
+/*!
+ \reimp
+*/
+QSizeF QGraphicsLinearLayout::sizeHint(Qt::SizeHint which, const QSizeF &constraint) const
+{
+ Q_D(const QGraphicsLinearLayout);
+ qreal left, top, right, bottom;
+ getContentsMargins(&left, &top, &right, &bottom);
+ const QSizeF extraMargins(left + right, top + bottom);
+ return d->engine.sizeHint(d->styleInfo(), which , constraint - extraMargins) + extraMargins;
+}
+
+/*!
+ \reimp
+*/
+void QGraphicsLinearLayout::invalidate()
+{
+ Q_D(QGraphicsLinearLayout);
+ d->engine.invalidate();
+ QGraphicsLayout::invalidate();
+}
+
+/*!
+ \internal
+*/
+void QGraphicsLinearLayout::dump(int indent) const
+{
+#ifdef QT_DEBUG
+ if (qt_graphicsLayoutDebug()) {
+ Q_D(const QGraphicsLinearLayout);
+ qDebug("%*s%s layout", indent, "",
+ d->orientation == Qt::Horizontal ? "Horizontal" : "Vertical");
+ d->engine.dump(indent + 1);
+ }
+#else
+ Q_UNUSED(indent);
+#endif
+}
+
+QT_END_NAMESPACE
+
+#endif //QT_NO_GRAPHICSVIEW
diff --git a/src/widgets/graphicsview/qgraphicslinearlayout.h b/src/widgets/graphicsview/qgraphicslinearlayout.h
new file mode 100644
index 0000000000..60749dbdaf
--- /dev/null
+++ b/src/widgets/graphicsview/qgraphicslinearlayout.h
@@ -0,0 +1,119 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** 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 Technology Preview License Agreement accompanying
+** this package.
+**
+** 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.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QGRAPHICSLINEARLAYOUT_H
+#define QGRAPHICSLINEARLAYOUT_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 QGraphicsLinearLayoutPrivate;
+
+class Q_GUI_EXPORT QGraphicsLinearLayout : public QGraphicsLayout
+{
+public:
+ QGraphicsLinearLayout(QGraphicsLayoutItem *parent = 0);
+ QGraphicsLinearLayout(Qt::Orientation orientation, QGraphicsLayoutItem *parent = 0);
+ virtual ~QGraphicsLinearLayout();
+
+ void setOrientation(Qt::Orientation orientation);
+ Qt::Orientation orientation() const;
+
+ inline void addItem(QGraphicsLayoutItem *item) { insertItem(-1, item); }
+ inline void addStretch(int stretch = 1) { insertStretch(-1, stretch); }
+
+ void insertItem(int index, QGraphicsLayoutItem *item);
+ void insertStretch(int index, int stretch = 1);
+
+ void removeItem(QGraphicsLayoutItem *item);
+ void removeAt(int index);
+
+ void setSpacing(qreal spacing);
+ qreal spacing() const;
+ void setItemSpacing(int index, qreal spacing);
+ qreal itemSpacing(int index) const;
+
+ void setStretchFactor(QGraphicsLayoutItem *item, int stretch);
+ int stretchFactor(QGraphicsLayoutItem *item) const;
+
+ void setAlignment(QGraphicsLayoutItem *item, Qt::Alignment alignment);
+ Qt::Alignment alignment(QGraphicsLayoutItem *item) const;
+
+ void setGeometry(const QRectF &rect);
+
+ int count() const;
+ QGraphicsLayoutItem *itemAt(int index) const;
+
+ void invalidate();
+ QSizeF sizeHint(Qt::SizeHint which, const QSizeF &constraint = QSizeF()) const;
+
+#if 0 // ###
+ Q5SizePolicy::ControlTypes controlTypes(LayoutSide side) const;
+#endif
+
+ void dump(int indent = 0) const;
+
+protected:
+#if 0
+ QSize contentsSizeHint(Qt::SizeHint which, const QSize &constraint = QSize()) const;
+#endif
+
+private:
+ Q_DISABLE_COPY(QGraphicsLinearLayout)
+ Q_DECLARE_PRIVATE(QGraphicsLinearLayout)
+};
+
+#endif
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif
+
diff --git a/src/widgets/graphicsview/qgraphicsproxywidget.cpp b/src/widgets/graphicsview/qgraphicsproxywidget.cpp
new file mode 100644
index 0000000000..e6142e66ac
--- /dev/null
+++ b/src/widgets/graphicsview/qgraphicsproxywidget.cpp
@@ -0,0 +1,1570 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** 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 Technology Preview License Agreement accompanying
+** this package.
+**
+** 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.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qglobal.h"
+
+#ifndef QT_NO_GRAPHICSVIEW
+
+#include "qgraphicslayout.h"
+#include "qgraphicsproxywidget.h"
+#include "private/qgraphicsproxywidget_p.h"
+#include "private/qwidget_p.h"
+#include "private/qapplication_p.h"
+
+#include <QtCore/qdebug.h>
+#include <QtGui/qevent.h>
+#include <QtGui/qgraphicsscene.h>
+#include <QtGui/qgraphicssceneevent.h>
+#include <QtGui/qlayout.h>
+#include <QtGui/qpainter.h>
+#include <QtGui/qstyleoption.h>
+#include <QtGui/qgraphicsview.h>
+#include <QtGui/qlistview.h>
+#include <QtGui/qlineedit.h>
+#include <QtGui/qtextedit.h>
+
+QT_BEGIN_NAMESPACE
+
+//#define GRAPHICSPROXYWIDGET_DEBUG
+
+/*!
+ \class QGraphicsProxyWidget
+ \brief The QGraphicsProxyWidget class provides a proxy layer for embedding
+ a QWidget in a QGraphicsScene.
+ \since 4.4
+ \ingroup graphicsview-api
+
+ QGraphicsProxyWidget embeds QWidget-based widgets, for example, a
+ QPushButton, QFontComboBox, or even QFileDialog, into
+ QGraphicsScene. It forwards events between the two objects and
+ translates between QWidget's integer-based geometry and
+ QGraphicsWidget's qreal-based geometry. QGraphicsProxyWidget
+ supports all core features of QWidget, including tab focus,
+ keyboard input, Drag & Drop, and popups. You can also embed
+ complex widgets, e.g., widgets with subwidgets.
+
+ Example:
+
+ \snippet doc/src/snippets/code/src_gui_graphicsview_qgraphicsproxywidget.cpp 0
+
+ QGraphicsProxyWidget takes care of automatically embedding popup children
+ of embedded widgets through creating a child proxy for each popup. This
+ means that when an embedded QComboBox shows its popup list, a new
+ QGraphicsProxyWidget is created automatically, embedding the popup, and
+ positioning it correctly. This only works if the popup is child of the
+ embedded widget (for example QToolButton::setMenu() requires the QMenu instance
+ to be child of the QToolButton).
+
+ \section1 Embedding a Widget with QGraphicsProxyWidget
+
+ There are two ways to embed a widget using QGraphicsProxyWidget. The most
+ common way is to pass a widget pointer to QGraphicsScene::addWidget()
+ together with any relevant \l Qt::WindowFlags. This function returns a
+ pointer to a QGraphicsProxyWidget. You can then choose to reparent or
+ position either the proxy, or the embedded widget itself.
+
+ For example, in the code snippet below, we embed a group box into the proxy:
+
+ \snippet doc/src/snippets/code/src_gui_graphicsview_qgraphicsproxywidget.cpp 1
+
+ The image below is the output obtained with its contents margin and
+ contents rect labeled.
+
+ \image qgraphicsproxywidget-embed.png
+
+ Alternatively, you can start by creating a new QGraphicsProxyWidget item,
+ and then call setWidget() to embed a QWidget later. The widget() function
+ returns a pointer to the embedded widget. QGraphicsProxyWidget shares
+ ownership with QWidget, so if either of the two widgets are destroyed, the
+ other widget will be automatically destroyed as well.
+
+ \section1 Synchronizing Widget States
+
+ QGraphicsProxyWidget keeps its state in sync with the embedded widget. For
+ example, if the proxy is hidden or disabled, the embedded widget will be
+ hidden or disabled as well, and vice versa. When the widget is embedded by
+ calling addWidget(), QGraphicsProxyWidget copies the state from the widget
+ into the proxy, and after that, the two will stay synchronized where
+ possible. By default, when you embed a widget into a proxy, both the widget
+ and the proxy will be visible because a QGraphicsWidget is visible when
+ created (you do not have to call show()). If you explicitly hide the
+ embedded widget, the proxy will also become invisible.
+
+ Example:
+
+ \snippet doc/src/snippets/code/src_gui_graphicsview_qgraphicsproxywidget.cpp 2
+
+ QGraphicsProxyWidget maintains symmetry for the following states:
+
+ \table
+ \header \o QWidget state \o QGraphicsProxyWidget state \o Notes
+ \row \o QWidget::enabled
+ \o QGraphicsProxyWidget::enabled
+ \o
+ \row \o QWidget::visible
+ \o QGraphicsProxyWidget::visible
+ \o The explicit state is also symmetric.
+ \row \o QWidget::geometry
+ \o QGraphicsProxyWidget::geometry
+ \o Geometry is only guaranteed to be symmetric while
+ the embedded widget is visible.
+ \row \o QWidget::layoutDirection
+ \o QGraphicsProxyWidget::layoutDirection
+ \o
+ \row \o QWidget::style
+ \o QGraphicsProxyWidget::style
+ \o
+ \row \o QWidget::palette
+ \o QGraphicsProxyWidget::palette
+ \o
+ \row \o QWidget::font
+ \o QGraphicsProxyWidget::font
+ \o
+ \row \o QWidget::cursor
+ \o QGraphicsProxyWidget::cursor
+ \o The embedded widget overrides the proxy widget
+ cursor. The proxy cursor changes depending on
+ which embedded subwidget is currently under the
+ mouse.
+ \row \o QWidget::sizeHint()
+ \o QGraphicsProxyWidget::sizeHint()
+ \o All size hint functionality from the embedded
+ widget is forwarded by the proxy.
+ \row \o QWidget::getContentsMargins()
+ \o QGraphicsProxyWidget::getContentsMargins()
+ \o Updated once by setWidget().
+ \row \o QWidget::windowTitle
+ \o QGraphicsProxyWidget::windowTitle
+ \o Updated once by setWidget().
+ \endtable
+
+ \note QGraphicsScene keeps the embedded widget in a special state that
+ prevents it from disturbing other widgets (both embedded and not embedded)
+ while the widget is embedded. In this state, the widget may differ slightly
+ in behavior from when it is not embedded.
+
+ \warning This class is provided for convenience when bridging
+ QWidgets and QGraphicsItems, it should not be used for
+ high-performance scenarios.
+
+ \sa QGraphicsScene::addWidget(), QGraphicsWidget
+*/
+
+extern bool qt_sendSpontaneousEvent(QObject *, QEvent *);
+Q_GUI_EXPORT extern bool qt_tab_all_widgets;
+
+/*!
+ \internal
+*/
+void QGraphicsProxyWidgetPrivate::init()
+{
+ Q_Q(QGraphicsProxyWidget);
+ q->setFocusPolicy(Qt::WheelFocus);
+ q->setAcceptDrops(true);
+}
+
+/*!
+ \internal
+*/
+void QGraphicsProxyWidgetPrivate::sendWidgetMouseEvent(QGraphicsSceneHoverEvent *event)
+{
+ QGraphicsSceneMouseEvent mouseEvent(QEvent::GraphicsSceneMouseMove);
+ mouseEvent.setPos(event->pos());
+ mouseEvent.setScreenPos(event->screenPos());
+ mouseEvent.setButton(Qt::NoButton);
+ mouseEvent.setButtons(0);
+ mouseEvent.setModifiers(event->modifiers());
+ sendWidgetMouseEvent(&mouseEvent);
+ event->setAccepted(mouseEvent.isAccepted());
+}
+
+/*!
+ \internal
+*/
+void QGraphicsProxyWidgetPrivate::sendWidgetMouseEvent(QGraphicsSceneMouseEvent *event)
+{
+ if (!event || !widget || !widget->isVisible())
+ return;
+ Q_Q(QGraphicsProxyWidget);
+
+ // Find widget position and receiver.
+ QPointF pos = event->pos();
+ QPointer<QWidget> alienWidget = widget->childAt(pos.toPoint());
+ QPointer<QWidget> receiver = alienWidget ? alienWidget : widget;
+
+ if (QWidgetPrivate::nearestGraphicsProxyWidget(receiver) != q)
+ return; //another proxywidget will handle the events
+
+ // Translate QGraphicsSceneMouse events to QMouseEvents.
+ QEvent::Type type = QEvent::None;
+ switch (event->type()) {
+ case QEvent::GraphicsSceneMousePress:
+ type = QEvent::MouseButtonPress;
+ if (!embeddedMouseGrabber)
+ embeddedMouseGrabber = receiver;
+ else
+ receiver = embeddedMouseGrabber;
+ break;
+ case QEvent::GraphicsSceneMouseRelease:
+ type = QEvent::MouseButtonRelease;
+ if (embeddedMouseGrabber)
+ receiver = embeddedMouseGrabber;
+ break;
+ case QEvent::GraphicsSceneMouseDoubleClick:
+ type = QEvent::MouseButtonDblClick;
+ if (!embeddedMouseGrabber)
+ embeddedMouseGrabber = receiver;
+ else
+ receiver = embeddedMouseGrabber;
+ break;
+ case QEvent::GraphicsSceneMouseMove:
+ type = QEvent::MouseMove;
+ if (embeddedMouseGrabber)
+ receiver = embeddedMouseGrabber;
+ break;
+ default:
+ Q_ASSERT_X(false, "QGraphicsProxyWidget", "internal error");
+ break;
+ }
+
+ if (!lastWidgetUnderMouse) {
+ QApplicationPrivate::dispatchEnterLeave(embeddedMouseGrabber ? embeddedMouseGrabber : receiver, 0);
+ lastWidgetUnderMouse = receiver;
+ }
+
+ // Map event position from us to the receiver
+ pos = mapToReceiver(pos, receiver);
+
+ // Send mouse event.
+ QMouseEvent *mouseEvent = QMouseEvent::createExtendedMouseEvent(type, pos,
+ receiver->mapToGlobal(pos.toPoint()), event->button(),
+ event->buttons(), event->modifiers());
+
+ QWidget *embeddedMouseGrabberPtr = (QWidget *)embeddedMouseGrabber;
+ QApplicationPrivate::sendMouseEvent(receiver, mouseEvent, alienWidget, widget,
+ &embeddedMouseGrabberPtr, lastWidgetUnderMouse, event->spontaneous());
+ embeddedMouseGrabber = embeddedMouseGrabberPtr;
+
+ // Handle enter/leave events when last button is released from mouse
+ // grabber child widget.
+ if (embeddedMouseGrabber && type == QEvent::MouseButtonRelease && !event->buttons()) {
+ Q_Q(QGraphicsProxyWidget);
+ if (q->rect().contains(event->pos()) && q->acceptsHoverEvents())
+ lastWidgetUnderMouse = alienWidget ? alienWidget : widget;
+ else // released on the frame our outside the item, or doesn't accept hover events.
+ lastWidgetUnderMouse = 0;
+
+ QApplicationPrivate::dispatchEnterLeave(lastWidgetUnderMouse, embeddedMouseGrabber);
+ embeddedMouseGrabber = 0;
+
+#ifndef QT_NO_CURSOR
+ // ### Restore the cursor, don't override it.
+ if (!lastWidgetUnderMouse)
+ q->unsetCursor();
+#endif
+ }
+
+ event->setAccepted(mouseEvent->isAccepted());
+ delete mouseEvent;
+}
+
+void QGraphicsProxyWidgetPrivate::sendWidgetKeyEvent(QKeyEvent *event)
+{
+ Q_Q(QGraphicsProxyWidget);
+ if (!event || !widget || !widget->isVisible())
+ return;
+
+ QPointer<QWidget> receiver = widget->focusWidget();
+ if (!receiver)
+ receiver = widget;
+ Q_ASSERT(receiver);
+
+ do {
+ bool res = QApplication::sendEvent(receiver, event);
+ if ((res && event->isAccepted()) || (q->isWindow() && receiver == widget))
+ break;
+ receiver = receiver->parentWidget();
+ } while (receiver);
+}
+
+/*!
+ \internal
+*/
+void QGraphicsProxyWidgetPrivate::removeSubFocusHelper(QWidget *widget, Qt::FocusReason reason)
+{
+ QFocusEvent event(QEvent::FocusOut, reason);
+ QPointer<QWidget> widgetGuard = widget;
+ QApplication::sendEvent(widget, &event);
+ if (widgetGuard && event.isAccepted())
+ QApplication::sendEvent(widget->style(), &event);
+}
+
+/*!
+ \internal
+
+ Reimplemented from QGraphicsItemPrivate. ### Qt 5: Move impl to
+ reimplementation QGraphicsProxyWidget::inputMethodQuery().
+*/
+QVariant QGraphicsProxyWidgetPrivate::inputMethodQueryHelper(Qt::InputMethodQuery query) const
+{
+ Q_Q(const QGraphicsProxyWidget);
+ if (!widget || !q->hasFocus())
+ return QVariant();
+
+ QWidget *focusWidget = widget->focusWidget();
+ if (!focusWidget)
+ focusWidget = widget;
+ QVariant v = focusWidget->inputMethodQuery(query);
+ QPointF focusWidgetPos = q->subWidgetRect(focusWidget).topLeft();
+ switch (v.type()) {
+ case QVariant::RectF:
+ v = v.toRectF().translated(focusWidgetPos);
+ break;
+ case QVariant::PointF:
+ v = v.toPointF() + focusWidgetPos;
+ break;
+ case QVariant::Rect:
+ v = v.toRect().translated(focusWidgetPos.toPoint());
+ break;
+ case QVariant::Point:
+ v = v.toPoint() + focusWidgetPos.toPoint();
+ break;
+ default:
+ break;
+ }
+ return v;
+}
+
+/*!
+ \internal
+ Some of the logic is shared with QApplicationPrivate::focusNextPrevChild_helper
+*/
+QWidget *QGraphicsProxyWidgetPrivate::findFocusChild(QWidget *child, bool next) const
+{
+ if (!widget)
+ return 0;
+
+ // Run around the focus chain until we find a widget that can take tab focus.
+ if (!child) {
+ child = next ? (QWidget *)widget : widget->d_func()->focus_prev;
+ } else {
+ child = next ? child->d_func()->focus_next : child->d_func()->focus_prev;
+ if ((next && child == widget) || (!next && child == widget->d_func()->focus_prev)) {
+ return 0;
+ }
+ }
+
+ QWidget *oldChild = child;
+ uint focus_flag = qt_tab_all_widgets ? Qt::TabFocus : Qt::StrongFocus;
+ do {
+ if (child->isEnabled()
+ && child->isVisibleTo(widget)
+ && ((child->focusPolicy() & focus_flag) == focus_flag)
+ && !(child->d_func()->extra && child->d_func()->extra->focus_proxy)) {
+ return child;
+ }
+ child = next ? child->d_func()->focus_next : child->d_func()->focus_prev;
+ } while (child != oldChild && !(next && child == widget) && !(!next && child == widget->d_func()->focus_prev));
+ return 0;
+}
+
+/*!
+ \internal
+*/
+void QGraphicsProxyWidgetPrivate::_q_removeWidgetSlot()
+{
+ Q_Q(QGraphicsProxyWidget);
+ widget = 0;
+ delete q;
+}
+
+/*!
+ \internal
+*/
+void QGraphicsProxyWidgetPrivate::updateWidgetGeometryFromProxy()
+{
+}
+
+/*!
+ \internal
+*/
+void QGraphicsProxyWidgetPrivate::updateProxyGeometryFromWidget()
+{
+ Q_Q(QGraphicsProxyWidget);
+ if (!widget)
+ return;
+
+ QRectF widgetGeometry = widget->geometry();
+ QWidget *parentWidget = widget->parentWidget();
+ if (widget->isWindow()) {
+ QGraphicsProxyWidget *proxyParent = 0;
+ if (parentWidget && (proxyParent = qobject_cast<QGraphicsProxyWidget *>(q->parentWidget()))) {
+ // Nested window proxy (e.g., combobox popup), map widget to the
+ // parent widget's global coordinates, and map that to the parent
+ // proxy's child coordinates.
+ widgetGeometry.moveTo(proxyParent->subWidgetRect(parentWidget).topLeft()
+ + parentWidget->mapFromGlobal(widget->pos()));
+ }
+ }
+
+ // Adjust to size hint if the widget has never been resized.
+ if (!widget->size().isValid())
+ widgetGeometry.setSize(widget->sizeHint());
+
+ // Assign new geometry.
+ posChangeMode = QGraphicsProxyWidgetPrivate::WidgetToProxyMode;
+ sizeChangeMode = QGraphicsProxyWidgetPrivate::WidgetToProxyMode;
+ q->setGeometry(widgetGeometry);
+ posChangeMode = QGraphicsProxyWidgetPrivate::NoMode;
+ sizeChangeMode = QGraphicsProxyWidgetPrivate::NoMode;
+}
+
+/*!
+ \internal
+*/
+void QGraphicsProxyWidgetPrivate::updateProxyInputMethodAcceptanceFromWidget()
+{
+ Q_Q(QGraphicsProxyWidget);
+ if (!widget)
+ return;
+
+ QWidget *focusWidget = widget->focusWidget();
+ if (!focusWidget)
+ focusWidget = widget;
+ q->setFlag(QGraphicsItem::ItemAcceptsInputMethod,
+ focusWidget->testAttribute(Qt::WA_InputMethodEnabled));
+}
+
+/*!
+ \internal
+
+ Embeds \a subWin as a subwindow of this proxy widget. \a subWin must be a top-level
+ widget and a descendant of the widget managed by this proxy. A separate subproxy
+ will be created as a child of this proxy widget to manage \a subWin.
+*/
+void QGraphicsProxyWidgetPrivate::embedSubWindow(QWidget *subWin)
+{
+ QWExtra *extra;
+ if (!((extra = subWin->d_func()->extra) && extra->proxyWidget)) {
+ QGraphicsProxyWidget *subProxy = new QGraphicsProxyWidget(q_func(), subWin->windowFlags());
+ subProxy->d_func()->setWidget_helper(subWin, false);
+ }
+}
+
+/*!
+ \internal
+
+ Removes ("unembeds") \a subWin and deletes the proxy holder item. This can
+ happen when QWidget::setParent() reparents the embedded window out of
+ "embedded space".
+*/
+void QGraphicsProxyWidgetPrivate::unembedSubWindow(QWidget *subWin)
+{
+ foreach (QGraphicsItem *child, children) {
+ if (child->isWidget()) {
+ if (QGraphicsProxyWidget *proxy = qobject_cast<QGraphicsProxyWidget *>(static_cast<QGraphicsWidget *>(child))) {
+ if (proxy->widget() == subWin) {
+ proxy->setWidget(0);
+ scene->removeItem(proxy);
+ delete proxy;
+ return;
+ }
+ }
+ }
+ }
+}
+
+bool QGraphicsProxyWidgetPrivate::isProxyWidget() const
+{
+ return true;
+}
+
+/*!
+ \internal
+*/
+QPointF QGraphicsProxyWidgetPrivate::mapToReceiver(const QPointF &pos, const QWidget *receiver) const
+{
+ QPointF p = pos;
+ // Map event position from us to the receiver, preserving its
+ // precision (don't use QWidget::mapFrom here).
+ while (receiver && receiver != widget) {
+ p -= QPointF(receiver->pos());
+ receiver = receiver->parentWidget();
+ }
+ return p;
+}
+
+/*!
+ Constructs a new QGraphicsProxy widget. \a parent and \a wFlags are passed
+ to QGraphicsItem's constructor.
+*/
+QGraphicsProxyWidget::QGraphicsProxyWidget(QGraphicsItem *parent, Qt::WindowFlags wFlags)
+ : QGraphicsWidget(*new QGraphicsProxyWidgetPrivate, parent, 0, wFlags)
+{
+ Q_D(QGraphicsProxyWidget);
+ d->init();
+}
+
+/*!
+ Destroys the proxy widget and any embedded widget.
+*/
+QGraphicsProxyWidget::~QGraphicsProxyWidget()
+{
+ Q_D(QGraphicsProxyWidget);
+ if (d->widget) {
+ QObject::disconnect(d->widget, SIGNAL(destroyed()), this, SLOT(_q_removeWidgetSlot()));
+ delete d->widget;
+ }
+}
+
+/*!
+ Embeds \a widget into this proxy widget. The embedded widget must reside
+ exclusively either inside or outside of Graphics View. You cannot embed a
+ widget as long as it is is visible elsewhere in the UI, at the same time.
+
+ \a widget must be a top-level widget whose parent is 0.
+
+ When the widget is embedded, its state (e.g., visible, enabled, geometry,
+ size hints) is copied into the proxy widget. If the embedded widget is
+ explicitly hidden or disabled, the proxy widget will become explicitly
+ hidden or disabled after embedding is complete. The class documentation
+ has a full overview over the shared state.
+
+ QGraphicsProxyWidget's window flags determine whether the widget, after
+ embedding, will be given window decorations or not.
+
+ After this function returns, QGraphicsProxyWidget will keep its state
+ synchronized with that of \a widget whenever possible.
+
+ If a widget is already embedded by this proxy when this function is
+ called, that widget will first be automatically unembedded. Passing 0 for
+ the \a widget argument will only unembed the widget, and the ownership of
+ the currently embedded widget will be passed on to the caller.
+ Every child widget that are embedded will also be embedded and their proxy
+ widget destroyed.
+
+ Note that widgets with the Qt::WA_PaintOnScreen widget attribute
+ set and widgets that wrap an external application or controller
+ cannot be embedded. Examples are QGLWidget and QAxWidget.
+
+ \sa widget()
+*/
+void QGraphicsProxyWidget::setWidget(QWidget *widget)
+{
+ Q_D(QGraphicsProxyWidget);
+ d->setWidget_helper(widget, true);
+}
+
+void QGraphicsProxyWidgetPrivate::setWidget_helper(QWidget *newWidget, bool autoShow)
+{
+ Q_Q(QGraphicsProxyWidget);
+ if (newWidget == widget)
+ return;
+ if (widget) {
+ QObject::disconnect(widget, SIGNAL(destroyed()), q, SLOT(_q_removeWidgetSlot()));
+ widget->removeEventFilter(q);
+ widget->setAttribute(Qt::WA_DontShowOnScreen, false);
+ widget->d_func()->extra->proxyWidget = 0;
+ resolveFont(inheritedFontResolveMask);
+ resolvePalette(inheritedPaletteResolveMask);
+ widget->update();
+
+ foreach (QGraphicsItem *child, q->childItems()) {
+ if (child->d_ptr->isProxyWidget()) {
+ QGraphicsProxyWidget *childProxy = static_cast<QGraphicsProxyWidget *>(child);
+ QWidget * parent = childProxy->widget();
+ while (parent->parentWidget() != 0) {
+ if (parent == widget)
+ break;
+ parent = parent->parentWidget();
+ }
+ if (!childProxy->widget() || parent != widget)
+ continue;
+ childProxy->setWidget(0);
+ delete childProxy;
+ }
+ }
+
+ widget = 0;
+#ifndef QT_NO_CURSOR
+ q->unsetCursor();
+#endif
+ q->setAcceptHoverEvents(false);
+ if (!newWidget)
+ q->update();
+ }
+ if (!newWidget)
+ return;
+ if (!newWidget->isWindow()) {
+ QWExtra *extra = newWidget->parentWidget()->d_func()->extra;
+ if (!extra || !extra->proxyWidget) {
+ qWarning("QGraphicsProxyWidget::setWidget: cannot embed widget %p "
+ "which is not a toplevel widget, and is not a child of an embedded widget", newWidget);
+ return;
+ }
+ }
+
+ // Register this proxy within the widget's private.
+ // ### This is a bit backdoorish
+ QWExtra *extra = newWidget->d_func()->extra;
+ if (!extra) {
+ newWidget->d_func()->createExtra();
+ extra = newWidget->d_func()->extra;
+ }
+ QGraphicsProxyWidget **proxyWidget = &extra->proxyWidget;
+ if (*proxyWidget) {
+ if (*proxyWidget != q) {
+ qWarning("QGraphicsProxyWidget::setWidget: cannot embed widget %p"
+ "; already embedded", newWidget);
+ }
+ return;
+ }
+ *proxyWidget = q;
+
+ newWidget->setAttribute(Qt::WA_DontShowOnScreen);
+ newWidget->ensurePolished();
+ // Do not wait for this widget to close before the app closes ###
+ // shouldn't this widget inherit the attribute?
+ newWidget->setAttribute(Qt::WA_QuitOnClose, false);
+ q->setAcceptHoverEvents(true);
+
+ if (newWidget->testAttribute(Qt::WA_NoSystemBackground))
+ q->setAttribute(Qt::WA_NoSystemBackground);
+ if (newWidget->testAttribute(Qt::WA_OpaquePaintEvent))
+ q->setAttribute(Qt::WA_OpaquePaintEvent);
+
+ widget = newWidget;
+
+ // Changes only go from the widget to the proxy.
+ enabledChangeMode = QGraphicsProxyWidgetPrivate::WidgetToProxyMode;
+ visibleChangeMode = QGraphicsProxyWidgetPrivate::WidgetToProxyMode;
+ posChangeMode = QGraphicsProxyWidgetPrivate::WidgetToProxyMode;
+ sizeChangeMode = QGraphicsProxyWidgetPrivate::WidgetToProxyMode;
+
+ if ((autoShow && !newWidget->testAttribute(Qt::WA_WState_ExplicitShowHide)) || !newWidget->testAttribute(Qt::WA_WState_Hidden)) {
+ newWidget->show();
+ }
+
+ // Copy the state from the widget onto the proxy.
+#ifndef QT_NO_CURSOR
+ if (newWidget->testAttribute(Qt::WA_SetCursor))
+ q->setCursor(widget->cursor());
+#endif
+ q->setEnabled(newWidget->isEnabled());
+ q->setVisible(newWidget->isVisible());
+ q->setLayoutDirection(newWidget->layoutDirection());
+ if (newWidget->testAttribute(Qt::WA_SetStyle))
+ q->setStyle(widget->style());
+
+ resolveFont(inheritedFontResolveMask);
+ resolvePalette(inheritedPaletteResolveMask);
+
+ if (!newWidget->testAttribute(Qt::WA_Resized))
+ newWidget->adjustSize();
+
+ int left, top, right, bottom;
+ newWidget->getContentsMargins(&left, &top, &right, &bottom);
+ q->setContentsMargins(left, top, right, bottom);
+ q->setWindowTitle(newWidget->windowTitle());
+
+ // size policies and constraints..
+ q->setSizePolicy(newWidget->sizePolicy());
+ QSize sz = newWidget->minimumSize();
+ q->setMinimumSize(sz.isNull() ? QSizeF() : QSizeF(sz));
+ sz = newWidget->maximumSize();
+ q->setMaximumSize(sz.isNull() ? QSizeF() : QSizeF(sz));
+
+ updateProxyGeometryFromWidget();
+
+ updateProxyInputMethodAcceptanceFromWidget();
+
+ // Hook up the event filter to keep the state up to date.
+ newWidget->installEventFilter(q);
+ QObject::connect(newWidget, SIGNAL(destroyed()), q, SLOT(_q_removeWidgetSlot()));
+
+ // Changes no longer go only from the widget to the proxy.
+ enabledChangeMode = QGraphicsProxyWidgetPrivate::NoMode;
+ visibleChangeMode = QGraphicsProxyWidgetPrivate::NoMode;
+ posChangeMode = QGraphicsProxyWidgetPrivate::NoMode;
+ sizeChangeMode = QGraphicsProxyWidgetPrivate::NoMode;
+}
+
+/*!
+ Returns a pointer to the embedded widget.
+
+ \sa setWidget()
+*/
+QWidget *QGraphicsProxyWidget::widget() const
+{
+ Q_D(const QGraphicsProxyWidget);
+ return d->widget;
+}
+
+/*!
+ Returns the rectangle for \a widget, which must be a descendant of
+ widget(), or widget() itself, in this proxy item's local coordinates.
+
+ If no widget is embedded, \a widget is 0, or \a widget is not a
+ descendant of the embedded widget, this function returns an empty QRectF.
+
+ \sa widget()
+*/
+QRectF QGraphicsProxyWidget::subWidgetRect(const QWidget *widget) const
+{
+ Q_D(const QGraphicsProxyWidget);
+ if (!widget || !d->widget)
+ return QRectF();
+ if (d->widget == widget || d->widget->isAncestorOf(widget))
+ return QRectF(widget->mapTo(d->widget, QPoint(0, 0)), widget->size());
+ return QRectF();
+}
+
+/*!
+ \reimp
+*/
+void QGraphicsProxyWidget::setGeometry(const QRectF &rect)
+{
+ Q_D(QGraphicsProxyWidget);
+ bool proxyResizesWidget = !d->posChangeMode && !d->sizeChangeMode;
+ if (proxyResizesWidget) {
+ d->posChangeMode = QGraphicsProxyWidgetPrivate::ProxyToWidgetMode;
+ d->sizeChangeMode = QGraphicsProxyWidgetPrivate::ProxyToWidgetMode;
+ }
+ QGraphicsWidget::setGeometry(rect);
+ if (proxyResizesWidget) {
+ d->posChangeMode = QGraphicsProxyWidgetPrivate::NoMode;
+ d->sizeChangeMode = QGraphicsProxyWidgetPrivate::NoMode;
+ }
+}
+
+/*!
+ \reimp
+*/
+QVariant QGraphicsProxyWidget::itemChange(GraphicsItemChange change,
+ const QVariant &value)
+{
+ Q_D(QGraphicsProxyWidget);
+
+ switch (change) {
+ case ItemPositionChange:
+ // The item's position is either changed directly on the proxy, in
+ // which case the position change should propagate to the widget,
+ // otherwise it happens as a side effect when filtering QEvent::Move.
+ if (!d->posChangeMode)
+ d->posChangeMode = QGraphicsProxyWidgetPrivate::ProxyToWidgetMode;
+ break;
+ case ItemPositionHasChanged:
+ // Move the internal widget if we're in widget-to-proxy
+ // mode. Otherwise the widget has already moved.
+ if (d->widget && d->posChangeMode != QGraphicsProxyWidgetPrivate::WidgetToProxyMode)
+ d->widget->move(value.toPoint());
+ if (d->posChangeMode == QGraphicsProxyWidgetPrivate::ProxyToWidgetMode)
+ d->posChangeMode = QGraphicsProxyWidgetPrivate::NoMode;
+ break;
+ case ItemVisibleChange:
+ if (!d->visibleChangeMode)
+ d->visibleChangeMode = QGraphicsProxyWidgetPrivate::ProxyToWidgetMode;
+ break;
+ case ItemVisibleHasChanged:
+ if (d->widget && d->visibleChangeMode != QGraphicsProxyWidgetPrivate::WidgetToProxyMode)
+ d->widget->setVisible(isVisible());
+ if (d->visibleChangeMode == QGraphicsProxyWidgetPrivate::ProxyToWidgetMode)
+ d->visibleChangeMode = QGraphicsProxyWidgetPrivate::NoMode;
+ break;
+ case ItemEnabledChange:
+ if (!d->enabledChangeMode)
+ d->enabledChangeMode = QGraphicsProxyWidgetPrivate::ProxyToWidgetMode;
+ break;
+ case ItemEnabledHasChanged:
+ if (d->widget && d->enabledChangeMode != QGraphicsProxyWidgetPrivate::WidgetToProxyMode)
+ d->widget->setEnabled(isEnabled());
+ if (d->enabledChangeMode == QGraphicsProxyWidgetPrivate::ProxyToWidgetMode)
+ d->enabledChangeMode = QGraphicsProxyWidgetPrivate::NoMode;
+ break;
+ default:
+ break;
+ }
+ return QGraphicsWidget::itemChange(change, value);
+}
+
+/*!
+ \reimp
+*/
+bool QGraphicsProxyWidget::event(QEvent *event)
+{
+ Q_D(QGraphicsProxyWidget);
+ if (!d->widget)
+ return QGraphicsWidget::event(event);
+
+ switch (event->type()) {
+ case QEvent::StyleChange:
+ // Propagate style changes to the embedded widget.
+ if (!d->styleChangeMode) {
+ d->styleChangeMode = QGraphicsProxyWidgetPrivate::ProxyToWidgetMode;
+ d->widget->setStyle(style());
+ d->styleChangeMode = QGraphicsProxyWidgetPrivate::NoMode;
+ }
+ break;
+ case QEvent::FontChange: {
+ // Propagate to widget.
+ QWidgetPrivate *wd = d->widget->d_func();
+ int mask = d->font.resolve() | d->inheritedFontResolveMask;
+ wd->inheritedFontResolveMask = mask;
+ wd->resolveFont();
+ break;
+ }
+ case QEvent::PaletteChange: {
+ // Propagate to widget.
+ QWidgetPrivate *wd = d->widget->d_func();
+ int mask = d->palette.resolve() | d->inheritedPaletteResolveMask;
+ wd->inheritedPaletteResolveMask = mask;
+ wd->resolvePalette();
+ break;
+ }
+ case QEvent::InputMethod: {
+ // Forward input method events if the focus widget enables
+ // input methods.
+ // ### Qt 4.5: this code must also go into a reimplementation
+ // of inputMethodEvent().
+ QWidget *focusWidget = d->widget->focusWidget();
+ if (focusWidget && focusWidget->testAttribute(Qt::WA_InputMethodEnabled))
+ QApplication::sendEvent(focusWidget, event);
+ break;
+ }
+ case QEvent::ShortcutOverride: {
+ QWidget *focusWidget = d->widget->focusWidget();
+ while (focusWidget) {
+ QApplication::sendEvent(focusWidget, event);
+ if (event->isAccepted())
+ return true;
+ focusWidget = focusWidget->parentWidget();
+ }
+ return false;
+ }
+ case QEvent::KeyPress: {
+ QKeyEvent *k = static_cast<QKeyEvent *>(event);
+ if (k->key() == Qt::Key_Tab || k->key() == Qt::Key_Backtab) {
+ if (!(k->modifiers() & (Qt::ControlModifier | Qt::AltModifier))) { //### Add MetaModifier?
+ QWidget *focusWidget = d->widget->focusWidget();
+ while (focusWidget) {
+ bool res = QApplication::sendEvent(focusWidget, event);
+ if ((res && event->isAccepted()) || (isWindow() && focusWidget == d->widget)) {
+ event->accept();
+ break;
+ }
+ focusWidget = focusWidget->parentWidget();
+ }
+ return true;
+ }
+ }
+ break;
+ }
+#ifndef QT_NO_TOOLTIP
+ case QEvent::GraphicsSceneHelp: {
+ // Propagate the help event (for tooltip) to the widget under mouse
+ if (d->lastWidgetUnderMouse) {
+ QGraphicsSceneHelpEvent *he = static_cast<QGraphicsSceneHelpEvent *>(event);
+ QPoint pos = d->mapToReceiver(mapFromScene(he->scenePos()), d->lastWidgetUnderMouse).toPoint();
+ QHelpEvent e(QEvent::ToolTip, pos, he->screenPos());
+ QApplication::sendEvent(d->lastWidgetUnderMouse, &e);
+ event->setAccepted(e.isAccepted());
+ return e.isAccepted();
+ }
+ break;
+ }
+ case QEvent::ToolTipChange: {
+ // Propagate tooltip change to the widget
+ if (!d->tooltipChangeMode) {
+ d->tooltipChangeMode = QGraphicsProxyWidgetPrivate::ProxyToWidgetMode;
+ d->widget->setToolTip(toolTip());
+ d->tooltipChangeMode = QGraphicsProxyWidgetPrivate::NoMode;
+ }
+ break;
+ }
+#endif
+ default:
+ break;
+ }
+ return QGraphicsWidget::event(event);
+}
+
+/*!
+ \reimp
+*/
+bool QGraphicsProxyWidget::eventFilter(QObject *object, QEvent *event)
+{
+ Q_D(QGraphicsProxyWidget);
+
+ if (object == d->widget) {
+ switch (event->type()) {
+ case QEvent::LayoutRequest:
+ updateGeometry();
+ break;
+ case QEvent::Resize:
+ // If the widget resizes itself, we resize the proxy too.
+ // Prevent feed-back by checking the geometry change mode.
+ if (!d->sizeChangeMode)
+ d->updateProxyGeometryFromWidget();
+ break;
+ case QEvent::Move:
+ // If the widget moves itself, we move the proxy too. Prevent
+ // feed-back by checking the geometry change mode.
+ if (!d->posChangeMode)
+ d->updateProxyGeometryFromWidget();
+ break;
+ case QEvent::Hide:
+ case QEvent::Show:
+ // If the widget toggles its visible state, the proxy will follow.
+ if (!d->visibleChangeMode) {
+ d->visibleChangeMode = QGraphicsProxyWidgetPrivate::WidgetToProxyMode;
+ setVisible(event->type() == QEvent::Show);
+ d->visibleChangeMode = QGraphicsProxyWidgetPrivate::NoMode;
+ }
+ break;
+ case QEvent::EnabledChange:
+ // If the widget toggles its enabled state, the proxy will follow.
+ if (!d->enabledChangeMode) {
+ d->enabledChangeMode = QGraphicsProxyWidgetPrivate::WidgetToProxyMode;
+ setEnabled(d->widget->isEnabled());
+ d->enabledChangeMode = QGraphicsProxyWidgetPrivate::NoMode;
+ }
+ break;
+ case QEvent::StyleChange:
+ // Propagate style changes to the proxy.
+ if (!d->styleChangeMode) {
+ d->styleChangeMode = QGraphicsProxyWidgetPrivate::WidgetToProxyMode;
+ setStyle(d->widget->style());
+ d->styleChangeMode = QGraphicsProxyWidgetPrivate::NoMode;
+ }
+ break;
+#ifndef QT_NO_TOOLTIP
+ case QEvent::ToolTipChange:
+ // Propagate tooltip change to the proxy.
+ if (!d->tooltipChangeMode) {
+ d->tooltipChangeMode = QGraphicsProxyWidgetPrivate::WidgetToProxyMode;
+ setToolTip(d->widget->toolTip());
+ d->tooltipChangeMode = QGraphicsProxyWidgetPrivate::NoMode;
+ }
+ break;
+#endif
+ default:
+ break;
+ }
+ }
+ return QGraphicsWidget::eventFilter(object, event);
+}
+
+/*!
+ \reimp
+*/
+void QGraphicsProxyWidget::showEvent(QShowEvent *event)
+{
+ Q_UNUSED(event);
+}
+
+/*!
+ \reimp
+*/
+void QGraphicsProxyWidget::hideEvent(QHideEvent *event)
+{
+ Q_UNUSED(event);
+}
+
+#ifndef QT_NO_CONTEXTMENU
+/*!
+ \reimp
+*/
+void QGraphicsProxyWidget::contextMenuEvent(QGraphicsSceneContextMenuEvent *event)
+{
+ Q_D(QGraphicsProxyWidget);
+ if (!event || !d->widget || !d->widget->isVisible() || !hasFocus())
+ return;
+
+ // Find widget position and receiver.
+ QPointF pos = event->pos();
+ QPointer<QWidget> alienWidget = d->widget->childAt(pos.toPoint());
+ QPointer<QWidget> receiver = alienWidget ? alienWidget : d->widget;
+
+ // Map event position from us to the receiver
+ pos = d->mapToReceiver(pos, receiver);
+
+ QPoint globalPos = receiver->mapToGlobal(pos.toPoint());
+ //If the receiver by-pass the proxy its popups
+ //will be top level QWidgets therefore they need
+ //the screen position. mapToGlobal expect the widget to
+ //have proper coordinates in regards of the windowing system
+ //but it's not true because the widget is embedded.
+ if (bypassGraphicsProxyWidget(receiver))
+ globalPos = event->screenPos();
+
+ // Send mouse event. ### Doesn't propagate the event.
+ QContextMenuEvent contextMenuEvent(QContextMenuEvent::Reason(event->reason()),
+ pos.toPoint(), globalPos, event->modifiers());
+ QApplication::sendEvent(receiver, &contextMenuEvent);
+
+ event->setAccepted(contextMenuEvent.isAccepted());
+}
+#endif // QT_NO_CONTEXTMENU
+
+#ifndef QT_NO_DRAGANDDROP
+/*!
+ \reimp
+*/
+void QGraphicsProxyWidget::dragEnterEvent(QGraphicsSceneDragDropEvent *event)
+{
+#ifdef QT_NO_DRAGANDDROP
+ Q_UNUSED(event);
+#else
+ Q_D(QGraphicsProxyWidget);
+ if (!d->widget)
+ return;
+
+ QDragEnterEvent proxyDragEnter(event->pos().toPoint(), event->dropAction(), event->mimeData(), event->buttons(), event->modifiers());
+ proxyDragEnter.setAccepted(event->isAccepted());
+ QApplication::sendEvent(d->widget, &proxyDragEnter);
+ event->setAccepted(proxyDragEnter.isAccepted());
+ if (proxyDragEnter.isAccepted()) // we discard answerRect
+ event->setDropAction(proxyDragEnter.dropAction());
+#endif
+}
+/*!
+ \reimp
+*/
+void QGraphicsProxyWidget::dragLeaveEvent(QGraphicsSceneDragDropEvent *event)
+{
+ Q_UNUSED(event);
+#ifndef QT_NO_DRAGANDDROP
+ Q_D(QGraphicsProxyWidget);
+ if (!d->widget || !d->dragDropWidget)
+ return;
+ QDragLeaveEvent proxyDragLeave;
+ QApplication::sendEvent(d->dragDropWidget, &proxyDragLeave);
+ d->dragDropWidget = 0;
+#endif
+}
+
+/*!
+ \reimp
+*/
+void QGraphicsProxyWidget::dragMoveEvent(QGraphicsSceneDragDropEvent *event)
+{
+#ifdef QT_NO_DRAGANDDROP
+ Q_UNUSED(event);
+#else
+ Q_D(QGraphicsProxyWidget);
+ if (!d->widget)
+ return;
+ QPointF p = event->pos();
+ event->ignore();
+ QPointer<QWidget> subWidget = d->widget->childAt(p.toPoint());
+ QPointer<QWidget> receiver = subWidget ? subWidget : d->widget;
+ bool eventDelivered = false;
+ for (; receiver; receiver = receiver->parentWidget()) {
+ if (!receiver->isEnabled() || !receiver->acceptDrops())
+ continue;
+ // Map event position from us to the receiver
+ QPoint receiverPos = d->mapToReceiver(p, receiver).toPoint();
+ if (receiver != d->dragDropWidget) {
+ // Try to enter before we leave
+ QDragEnterEvent dragEnter(receiverPos, event->possibleActions(), event->mimeData(), event->buttons(), event->modifiers());
+ dragEnter.setDropAction(event->proposedAction());
+ QApplication::sendEvent(receiver, &dragEnter);
+ event->setAccepted(dragEnter.isAccepted());
+ event->setDropAction(dragEnter.dropAction());
+ if (!event->isAccepted()) {
+ // propagate to the parent widget
+ continue;
+ }
+
+ d->lastDropAction = event->dropAction();
+
+ if (d->dragDropWidget) {
+ QDragLeaveEvent dragLeave;
+ QApplication::sendEvent(d->dragDropWidget, &dragLeave);
+ }
+ d->dragDropWidget = receiver;
+ }
+
+ QDragMoveEvent dragMove(receiverPos, event->possibleActions(), event->mimeData(), event->buttons(), event->modifiers());
+ event->setDropAction(d->lastDropAction);
+ QApplication::sendEvent(receiver, &dragMove);
+ event->setAccepted(dragMove.isAccepted());
+ event->setDropAction(dragMove.dropAction());
+ if (event->isAccepted())
+ d->lastDropAction = event->dropAction();
+ eventDelivered = true;
+ break;
+ }
+
+ if (!eventDelivered) {
+ if (d->dragDropWidget) {
+ // Leave the last drag drop item
+ QDragLeaveEvent dragLeave;
+ QApplication::sendEvent(d->dragDropWidget, &dragLeave);
+ d->dragDropWidget = 0;
+ }
+ // Propagate
+ event->setDropAction(Qt::IgnoreAction);
+ }
+#endif
+}
+
+/*!
+ \reimp
+*/
+void QGraphicsProxyWidget::dropEvent(QGraphicsSceneDragDropEvent *event)
+{
+#ifdef QT_NO_DRAGANDDROP
+ Q_UNUSED(event);
+#else
+ Q_D(QGraphicsProxyWidget);
+ if (d->widget && d->dragDropWidget) {
+ QPoint widgetPos = d->mapToReceiver(event->pos(), d->dragDropWidget).toPoint();
+ QDropEvent dropEvent(widgetPos, event->possibleActions(), event->mimeData(), event->buttons(), event->modifiers());
+ QApplication::sendEvent(d->dragDropWidget, &dropEvent);
+ event->setAccepted(dropEvent.isAccepted());
+ d->dragDropWidget = 0;
+ }
+#endif
+}
+#endif
+
+/*!
+ \reimp
+*/
+void QGraphicsProxyWidget::hoverEnterEvent(QGraphicsSceneHoverEvent *event)
+{
+ Q_UNUSED(event);
+}
+
+/*!
+ \reimp
+*/
+void QGraphicsProxyWidget::hoverLeaveEvent(QGraphicsSceneHoverEvent *event)
+{
+ Q_UNUSED(event);
+ Q_D(QGraphicsProxyWidget);
+ // If hoverMove was compressed away, make sure we update properly here.
+ if (d->lastWidgetUnderMouse) {
+ QApplicationPrivate::dispatchEnterLeave(0, d->lastWidgetUnderMouse);
+ d->lastWidgetUnderMouse = 0;
+ }
+}
+
+/*!
+ \reimp
+*/
+void QGraphicsProxyWidget::hoverMoveEvent(QGraphicsSceneHoverEvent *event)
+{
+ Q_D(QGraphicsProxyWidget);
+#ifdef GRAPHICSPROXYWIDGET_DEBUG
+ qDebug() << "QGraphicsProxyWidget::hoverMoveEvent";
+#endif
+ // Ignore events on the window frame.
+ if (!d->widget || !rect().contains(event->pos())) {
+ if (d->lastWidgetUnderMouse) {
+ QApplicationPrivate::dispatchEnterLeave(0, d->lastWidgetUnderMouse);
+ d->lastWidgetUnderMouse = 0;
+ }
+ return;
+ }
+
+ d->embeddedMouseGrabber = 0;
+ d->sendWidgetMouseEvent(event);
+}
+
+/*!
+ \reimp
+*/
+void QGraphicsProxyWidget::grabMouseEvent(QEvent *event)
+{
+ Q_UNUSED(event);
+}
+
+/*!
+ \reimp
+*/
+void QGraphicsProxyWidget::ungrabMouseEvent(QEvent *event)
+{
+ Q_D(QGraphicsProxyWidget);
+ Q_UNUSED(event);
+ d->embeddedMouseGrabber = 0;
+}
+
+/*!
+ \reimp
+*/
+void QGraphicsProxyWidget::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
+{
+ Q_D(QGraphicsProxyWidget);
+#ifdef GRAPHICSPROXYWIDGET_DEBUG
+ qDebug() << "QGraphicsProxyWidget::mouseMoveEvent";
+#endif
+ d->sendWidgetMouseEvent(event);
+}
+
+/*!
+ \reimp
+*/
+void QGraphicsProxyWidget::mousePressEvent(QGraphicsSceneMouseEvent *event)
+{
+ Q_D(QGraphicsProxyWidget);
+#ifdef GRAPHICSPROXYWIDGET_DEBUG
+ qDebug() << "QGraphicsProxyWidget::mousePressEvent";
+#endif
+ d->sendWidgetMouseEvent(event);
+}
+
+/*!
+ \reimp
+*/
+void QGraphicsProxyWidget::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event)
+{
+ Q_D(QGraphicsProxyWidget);
+#ifdef GRAPHICSPROXYWIDGET_DEBUG
+ qDebug() << "QGraphicsProxyWidget::mouseDoubleClickEvent";
+#endif
+ d->sendWidgetMouseEvent(event);
+}
+
+/*!
+ \reimp
+*/
+#ifndef QT_NO_WHEELEVENT
+void QGraphicsProxyWidget::wheelEvent(QGraphicsSceneWheelEvent *event)
+{
+ Q_D(QGraphicsProxyWidget);
+#ifdef GRAPHICSPROXYWIDGET_DEBUG
+ qDebug() << "QGraphicsProxyWidget::wheelEvent";
+#endif
+ if (!d->widget)
+ return;
+
+ QPointF pos = event->pos();
+ QPointer<QWidget> receiver = d->widget->childAt(pos.toPoint());
+ if (!receiver)
+ receiver = d->widget;
+
+ // Map event position from us to the receiver
+ pos = d->mapToReceiver(pos, receiver);
+
+ // Send mouse event.
+ QWheelEvent wheelEvent(pos.toPoint(), event->screenPos(), event->delta(),
+ event->buttons(), event->modifiers(), event->orientation());
+ QPointer<QWidget> focusWidget = d->widget->focusWidget();
+ extern bool qt_sendSpontaneousEvent(QObject *, QEvent *);
+ qt_sendSpontaneousEvent(receiver, &wheelEvent);
+ event->setAccepted(wheelEvent.isAccepted());
+
+ // ### Remove, this should be done by proper focusIn/focusOut events.
+ if (focusWidget && !focusWidget->hasFocus()) {
+ focusWidget->update();
+ focusWidget = d->widget->focusWidget();
+ if (focusWidget && focusWidget->hasFocus())
+ focusWidget->update();
+ }
+}
+#endif
+
+/*!
+ \reimp
+*/
+void QGraphicsProxyWidget::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
+{
+ Q_D(QGraphicsProxyWidget);
+#ifdef GRAPHICSPROXYWIDGET_DEBUG
+ qDebug() << "QGraphicsProxyWidget::mouseReleaseEvent";
+#endif
+ d->sendWidgetMouseEvent(event);
+}
+
+/*!
+ \reimp
+*/
+void QGraphicsProxyWidget::keyPressEvent(QKeyEvent *event)
+{
+ Q_D(QGraphicsProxyWidget);
+#ifdef GRAPHICSPROXYWIDGET_DEBUG
+ qDebug() << "QGraphicsProxyWidget::keyPressEvent";
+#endif
+ d->sendWidgetKeyEvent(event);
+}
+
+/*!
+ \reimp
+*/
+void QGraphicsProxyWidget::keyReleaseEvent(QKeyEvent *event)
+{
+ Q_D(QGraphicsProxyWidget);
+#ifdef GRAPHICSPROXYWIDGET_DEBUG
+ qDebug() << "QGraphicsProxyWidget::keyReleaseEvent";
+#endif
+ d->sendWidgetKeyEvent(event);
+}
+
+/*!
+ \reimp
+*/
+void QGraphicsProxyWidget::focusInEvent(QFocusEvent *event)
+{
+#ifdef GRAPHICSPROXYWIDGET_DEBUG
+ qDebug() << "QGraphicsProxyWidget::focusInEvent";
+#endif
+ Q_D(QGraphicsProxyWidget);
+
+ if (d->focusFromWidgetToProxy) {
+ // Prevent recursion when the proxy autogains focus through the
+ // embedded widget calling setFocus(). ### Could be done with event
+ // filter on FocusIn instead?
+ return;
+ }
+
+ d->proxyIsGivingFocus = true;
+
+ switch (event->reason()) {
+ case Qt::TabFocusReason: {
+ if (QWidget *focusChild = d->findFocusChild(0, true))
+ focusChild->setFocus(event->reason());
+ break;
+ }
+ case Qt::BacktabFocusReason:
+ if (QWidget *focusChild = d->findFocusChild(0, false))
+ focusChild->setFocus(event->reason());
+ break;
+ default:
+ if (d->widget && d->widget->focusWidget()) {
+ d->widget->focusWidget()->setFocus(event->reason());
+ }
+ break;
+ }
+
+ d->proxyIsGivingFocus = false;
+}
+
+/*!
+ \reimp
+*/
+void QGraphicsProxyWidget::focusOutEvent(QFocusEvent *event)
+{
+#ifdef GRAPHICSPROXYWIDGET_DEBUG
+ qDebug() << "QGraphicsProxyWidget::focusOutEvent";
+#endif
+ Q_D(QGraphicsProxyWidget);
+ if (d->widget) {
+ // We need to explicitly remove subfocus from the embedded widget's
+ // focus widget.
+ if (QWidget *focusWidget = d->widget->focusWidget())
+ d->removeSubFocusHelper(focusWidget, event->reason());
+ }
+}
+
+/*!
+ \reimp
+*/
+bool QGraphicsProxyWidget::focusNextPrevChild(bool next)
+{
+ Q_D(QGraphicsProxyWidget);
+ if (!d->widget || !d->scene)
+ return QGraphicsWidget::focusNextPrevChild(next);
+
+ Qt::FocusReason reason = next ? Qt::TabFocusReason : Qt::BacktabFocusReason;
+ QWidget *lastFocusChild = d->widget->focusWidget();
+ if (QWidget *newFocusChild = d->findFocusChild(lastFocusChild, next)) {
+ newFocusChild->setFocus(reason);
+ return true;
+ }
+
+ return QGraphicsWidget::focusNextPrevChild(next);
+}
+
+/*!
+ \reimp
+*/
+QSizeF QGraphicsProxyWidget::sizeHint(Qt::SizeHint which, const QSizeF &constraint) const
+{
+ Q_D(const QGraphicsProxyWidget);
+ if (!d->widget)
+ return QGraphicsWidget::sizeHint(which, constraint);
+
+ QSizeF sh;
+ switch (which) {
+ case Qt::PreferredSize:
+ if (QLayout *l = d->widget->layout())
+ sh = l->sizeHint();
+ else
+ sh = d->widget->sizeHint();
+ break;
+ case Qt::MinimumSize:
+ if (QLayout *l = d->widget->layout())
+ sh = l->minimumSize();
+ else
+ sh = d->widget->minimumSizeHint();
+ break;
+ case Qt::MaximumSize:
+ if (QLayout *l = d->widget->layout())
+ sh = l->maximumSize();
+ else
+ sh = QSizeF(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX);
+ break;
+ case Qt::MinimumDescent:
+ sh = constraint;
+ break;
+ default:
+ break;
+ }
+ return sh;
+}
+
+/*!
+ \reimp
+*/
+void QGraphicsProxyWidget::resizeEvent(QGraphicsSceneResizeEvent *event)
+{
+ Q_D(QGraphicsProxyWidget);
+ if (d->widget) {
+ if (d->sizeChangeMode != QGraphicsProxyWidgetPrivate::WidgetToProxyMode)
+ d->widget->resize(event->newSize().toSize());
+ }
+ QGraphicsWidget::resizeEvent(event);
+}
+
+/*!
+ \reimp
+*/
+void QGraphicsProxyWidget::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
+{
+ Q_D(QGraphicsProxyWidget);
+ Q_UNUSED(widget);
+ if (!d->widget || !d->widget->isVisible())
+ return;
+
+ // Filter out repaints on the window frame.
+ const QRect exposedWidgetRect = (option->exposedRect & rect()).toAlignedRect();
+ if (exposedWidgetRect.isEmpty())
+ return;
+
+ // Disable QPainter's default pen being cosmetic. This allows widgets and
+ // styles to follow Qt's existing defaults without getting ugly cosmetic
+ // lines when scaled.
+ bool restore = !(painter->renderHints() & QPainter::NonCosmeticDefaultPen);
+ painter->setRenderHints(QPainter::NonCosmeticDefaultPen, true);
+
+ d->widget->render(painter, exposedWidgetRect.topLeft(), exposedWidgetRect);
+
+ // Restore the render hints if necessary.
+ if (restore)
+ painter->setRenderHints(QPainter::NonCosmeticDefaultPen, false);
+}
+
+/*!
+ \reimp
+*/
+int QGraphicsProxyWidget::type() const
+{
+ return Type;
+}
+
+/*!
+ \since 4.5
+
+ Creates a proxy widget for the given \a child of the widget
+ contained in this proxy.
+
+ This function makes it possible to acquire proxies for
+ non top-level widgets. For instance, you can embed a dialog,
+ and then transform only one of its widgets.
+
+ If the widget is already embedded, return the existing proxy widget.
+
+ \sa newProxyWidget(), QGraphicsScene::addWidget()
+*/
+QGraphicsProxyWidget *QGraphicsProxyWidget::createProxyForChildWidget(QWidget *child)
+{
+ QGraphicsProxyWidget *proxy = child->graphicsProxyWidget();
+ if (proxy)
+ return proxy;
+ if (!child->parentWidget()) {
+ qWarning("QGraphicsProxyWidget::createProxyForChildWidget: top-level widget not in a QGraphicsScene");
+ return 0;
+ }
+
+ QGraphicsProxyWidget *parentProxy = createProxyForChildWidget(child->parentWidget());
+ if (!parentProxy)
+ return 0;
+
+ if (!QMetaObject::invokeMethod(parentProxy, "newProxyWidget", Qt::DirectConnection,
+ Q_RETURN_ARG(QGraphicsProxyWidget*, proxy), Q_ARG(const QWidget*, child)))
+ return 0;
+ proxy->setParent(parentProxy);
+ proxy->setWidget(child);
+ return proxy;
+}
+
+/*!
+ \fn QGraphicsProxyWidget *QGraphicsProxyWidget::newProxyWidget(const QWidget *child)
+ \since 4.5
+
+ Creates a proxy widget for the given \a child of the widget contained in this
+ proxy.
+
+ You should not call this function directly; use
+ QGraphicsProxyWidget::createProxyForChildWidget() instead.
+
+ This function is a fake virtual slot that you can reimplement in
+ your subclass in order to control how new proxy widgets are
+ created. The default implementation returns a proxy created with
+ the QGraphicsProxyWidget() constructor with this proxy widget as
+ the parent.
+
+ \sa createProxyForChildWidget()
+*/
+QGraphicsProxyWidget *QGraphicsProxyWidget::newProxyWidget(const QWidget *)
+{
+ return new QGraphicsProxyWidget(this);
+}
+
+
+
+QT_END_NAMESPACE
+
+#include "moc_qgraphicsproxywidget.cpp"
+
+#endif //QT_NO_GRAPHICSVIEW
diff --git a/src/widgets/graphicsview/qgraphicsproxywidget.h b/src/widgets/graphicsview/qgraphicsproxywidget.h
new file mode 100644
index 0000000000..b2ffe2f7d1
--- /dev/null
+++ b/src/widgets/graphicsview/qgraphicsproxywidget.h
@@ -0,0 +1,147 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** 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 Technology Preview License Agreement accompanying
+** this package.
+**
+** 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.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QGRAPHICSPROXYWIDGET_H
+#define QGRAPHICSPROXYWIDGET_H
+
+#include <QtGui/qgraphicswidget.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+#if !defined(QT_NO_GRAPHICSVIEW) || (QT_EDITION & QT_MODULE_GRAPHICSVIEW) != QT_MODULE_GRAPHICSVIEW
+
+class QGraphicsProxyWidgetPrivate;
+
+class Q_GUI_EXPORT QGraphicsProxyWidget : public QGraphicsWidget
+{
+ Q_OBJECT
+public:
+ QGraphicsProxyWidget(QGraphicsItem *parent = 0, Qt::WindowFlags wFlags = 0);
+ ~QGraphicsProxyWidget();
+
+ void setWidget(QWidget *widget);
+ QWidget *widget() const;
+
+ QRectF subWidgetRect(const QWidget *widget) const;
+
+ void setGeometry(const QRectF &rect);
+
+ void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget);
+
+ enum {
+ Type = 12
+ };
+ int type() const;
+
+ QGraphicsProxyWidget *createProxyForChildWidget(QWidget *child);
+
+protected:
+ QVariant itemChange(GraphicsItemChange change, const QVariant &value);
+
+ bool event(QEvent *event);
+ bool eventFilter(QObject *object, QEvent *event);
+
+ void showEvent(QShowEvent *event);
+ void hideEvent(QHideEvent *event);
+
+#ifndef QT_NO_CONTEXTMENU
+ void contextMenuEvent(QGraphicsSceneContextMenuEvent *event);
+#endif
+
+#ifndef QT_NO_DRAGANDDROP
+ void dragEnterEvent(QGraphicsSceneDragDropEvent *event);
+ void dragLeaveEvent(QGraphicsSceneDragDropEvent *event);
+ void dragMoveEvent(QGraphicsSceneDragDropEvent *event);
+ void dropEvent(QGraphicsSceneDragDropEvent *event);
+#endif
+
+ void hoverEnterEvent(QGraphicsSceneHoverEvent *event);
+ void hoverLeaveEvent(QGraphicsSceneHoverEvent *event);
+ void hoverMoveEvent(QGraphicsSceneHoverEvent *event);
+ void grabMouseEvent(QEvent *event);
+ void ungrabMouseEvent(QEvent *event);
+
+ void mouseMoveEvent(QGraphicsSceneMouseEvent *event);
+ void mousePressEvent(QGraphicsSceneMouseEvent *event);
+ void mouseReleaseEvent(QGraphicsSceneMouseEvent *event);
+ void mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event);
+#ifndef QT_NO_WHEELEVENT
+ void wheelEvent(QGraphicsSceneWheelEvent *event);
+#endif
+
+ void keyPressEvent(QKeyEvent *event);
+ void keyReleaseEvent(QKeyEvent *event);
+
+ void focusInEvent(QFocusEvent *event);
+ void focusOutEvent(QFocusEvent *event);
+ bool focusNextPrevChild(bool next);
+ // ### Qt 4.5:
+ // QVariant inputMethodQuery(Qt::InputMethodQuery query) const;
+ // void inputMethodEvent(QInputMethodEvent *event);
+
+ QSizeF sizeHint(Qt::SizeHint which, const QSizeF &constraint = QSizeF()) const;
+ void resizeEvent(QGraphicsSceneResizeEvent *event);
+
+protected Q_SLOTS:
+ QGraphicsProxyWidget *newProxyWidget(const QWidget *);
+
+private:
+ Q_DISABLE_COPY(QGraphicsProxyWidget)
+ Q_DECLARE_PRIVATE_D(QGraphicsItem::d_ptr.data(), QGraphicsProxyWidget)
+ Q_PRIVATE_SLOT(d_func(), void _q_removeWidgetSlot())
+
+ friend class QWidget;
+ friend class QWidgetPrivate;
+ friend class QGraphicsItem;
+};
+
+#endif
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif
+
diff --git a/src/widgets/graphicsview/qgraphicsproxywidget_p.h b/src/widgets/graphicsview/qgraphicsproxywidget_p.h
new file mode 100644
index 0000000000..e1a96b0021
--- /dev/null
+++ b/src/widgets/graphicsview/qgraphicsproxywidget_p.h
@@ -0,0 +1,130 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** 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 Technology Preview License Agreement accompanying
+** this package.
+**
+** 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.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QGRAPHICSPROXYWIDGET_P_H
+#define QGRAPHICSPROXYWIDGET_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of other Qt classes. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qgraphicsproxywidget.h"
+#include "private/qgraphicswidget_p.h"
+
+#if !defined(QT_NO_GRAPHICSVIEW) || (QT_EDITION & QT_MODULE_GRAPHICSVIEW) != QT_MODULE_GRAPHICSVIEW
+
+QT_BEGIN_NAMESPACE
+
+class QGraphicsProxyWidgetPrivate : public QGraphicsWidgetPrivate
+{
+ Q_DECLARE_PUBLIC(QGraphicsProxyWidget)
+public:
+ QGraphicsProxyWidgetPrivate()
+ : dragDropWidget(0),
+ posChangeMode(NoMode),
+ sizeChangeMode(NoMode),
+ visibleChangeMode(NoMode),
+ enabledChangeMode(NoMode),
+ styleChangeMode(NoMode),
+ paletteChangeMode(NoMode),
+ tooltipChangeMode(NoMode),
+ focusFromWidgetToProxy(0)
+ { }
+ void init();
+ void sendWidgetMouseEvent(QGraphicsSceneMouseEvent *event);
+ void sendWidgetMouseEvent(QGraphicsSceneHoverEvent *event);
+ void sendWidgetKeyEvent(QKeyEvent *event);
+ void setWidget_helper(QWidget *widget, bool autoShow);
+
+ QWidget *findFocusChild(QWidget *child, bool next) const;
+ void removeSubFocusHelper(QWidget *widget, Qt::FocusReason reason);
+
+ // ### Qt 5: Remove. Workaround for reimplementation added after Qt 4.4.
+ QVariant inputMethodQueryHelper(Qt::InputMethodQuery query) const;
+
+ void _q_removeWidgetSlot();
+
+ void embedSubWindow(QWidget *);
+ void unembedSubWindow(QWidget *);
+
+ bool isProxyWidget() const;
+
+ QPointer<QWidget> widget;
+ QPointer<QWidget> lastWidgetUnderMouse;
+ QPointer<QWidget> embeddedMouseGrabber;
+ QWidget *dragDropWidget;
+ Qt::DropAction lastDropAction;
+
+ void updateWidgetGeometryFromProxy();
+ void updateProxyGeometryFromWidget();
+
+ void updateProxyInputMethodAcceptanceFromWidget();
+
+ QPointF mapToReceiver(const QPointF &pos, const QWidget *receiver) const;
+
+ enum ChangeMode {
+ NoMode,
+ ProxyToWidgetMode,
+ WidgetToProxyMode
+ };
+ quint32 posChangeMode : 2;
+ quint32 sizeChangeMode : 2;
+ quint32 visibleChangeMode : 2;
+ quint32 enabledChangeMode : 2;
+ quint32 styleChangeMode : 2;
+ quint32 paletteChangeMode : 2;
+ quint32 tooltipChangeMode : 2;
+ quint32 focusFromWidgetToProxy : 1;
+ quint32 proxyIsGivingFocus : 1;
+};
+
+QT_END_NAMESPACE
+
+#endif
+
+#endif
diff --git a/src/widgets/graphicsview/qgraphicsscene.cpp b/src/widgets/graphicsview/qgraphicsscene.cpp
new file mode 100644
index 0000000000..0713d09296
--- /dev/null
+++ b/src/widgets/graphicsview/qgraphicsscene.cpp
@@ -0,0 +1,6491 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** 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 Technology Preview License Agreement accompanying
+** this package.
+**
+** 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.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*!
+ \class QGraphicsScene
+ \brief The QGraphicsScene class provides a surface for managing a large
+ number of 2D graphical items.
+ \since 4.2
+ \ingroup graphicsview-api
+
+
+ The class serves as a container for QGraphicsItems. It is used together
+ with QGraphicsView for visualizing graphical items, such as lines,
+ rectangles, text, or even custom items, on a 2D surface. QGraphicsScene is
+ part of the \l{Graphics View Framework}.
+
+ QGraphicsScene also provides functionality that lets you efficiently
+ determine both the location of items, and for determining what items are
+ visible within an arbitrary area on the scene. With the QGraphicsView
+ widget, you can either visualize the whole scene, or zoom in and view only
+ parts of the scene.
+
+ Example:
+
+ \snippet doc/src/snippets/code/src_gui_graphicsview_qgraphicsscene.cpp 0
+
+ Note that QGraphicsScene has no visual appearance of its own; it only
+ manages the items. You need to create a QGraphicsView widget to visualize
+ the scene.
+
+ To add items to a scene, you start off by constructing a QGraphicsScene
+ object. Then, you have two options: either add your existing QGraphicsItem
+ objects by calling addItem(), or you can call one of the convenience
+ functions addEllipse(), addLine(), addPath(), addPixmap(), addPolygon(),
+ addRect(), or addText(), which all return a pointer to the newly added item.
+ The dimensions of the items added with these functions are relative to the
+ item's coordinate system, and the items position is initialized to (0,
+ 0) in the scene.
+
+ You can then visualize the scene using QGraphicsView. When the scene
+ changes, (e.g., when an item moves or is transformed) QGraphicsScene
+ emits the changed() signal. To remove an item, call removeItem().
+
+ QGraphicsScene uses an indexing algorithm to manage the location of items
+ efficiently. By default, a BSP (Binary Space Partitioning) tree is used; an
+ algorithm suitable for large scenes where most items remain static (i.e.,
+ do not move around). You can choose to disable this index by calling
+ setItemIndexMethod(). For more information about the available indexing
+ algorithms, see the itemIndexMethod property.
+
+ The scene's bounding rect is set by calling setSceneRect(). Items can be
+ placed at any position on the scene, and the size of the scene is by
+ default unlimited. The scene rect is used only for internal bookkeeping,
+ maintaining the scene's item index. If the scene rect is unset,
+ QGraphicsScene will use the bounding area of all items, as returned by
+ itemsBoundingRect(), as the scene rect. However, itemsBoundingRect() is a
+ relatively time consuming function, as it operates by collecting
+ positional information for every item on the scene. Because of this, you
+ should always set the scene rect when operating on large scenes.
+
+ One of QGraphicsScene's greatest strengths is its ability to efficiently
+ determine the location of items. Even with millions of items on the scene,
+ the items() functions can determine the location of an item within few
+ milliseconds. There are several overloads to items(): one that finds items
+ at a certain position, one that finds items inside or intersecting with a
+ polygon or a rectangle, and more. The list of returned items is sorted by
+ stacking order, with the topmost item being the first item in the list.
+ For convenience, there is also an itemAt() function that returns the
+ topmost item at a given position.
+
+ QGraphicsScene maintains selection information for the scene. To select
+ items, call setSelectionArea(), and to clear the current selection, call
+ clearSelection(). Call selectedItems() to get the list of all selected
+ items.
+
+ \section1 Event Handling and Propagation
+
+ Another responsibility that QGraphicsScene has, is to propagate events
+ from QGraphicsView. To send an event to a scene, you construct an event
+ that inherits QEvent, and then send it using, for example,
+ QApplication::sendEvent(). event() is responsible for dispatching
+ the event to the individual items. Some common events are handled by
+ convenience event handlers. For example, key press events are handled by
+ keyPressEvent(), and mouse press events are handled by mousePressEvent().
+
+ Key events are delivered to the \e {focus item}. To set the focus item,
+ you can either call setFocusItem(), passing an item that accepts focus, or
+ the item itself can call QGraphicsItem::setFocus(). Call focusItem() to
+ get the current focus item. For compatibility with widgets, the scene also
+ maintains its own focus information. By default, the scene does not have
+ focus, and all key events are discarded. If setFocus() is called, or if an
+ item on the scene gains focus, the scene automatically gains focus. If the
+ scene has focus, hasFocus() will return true, and key events will be
+ forwarded to the focus item, if any. If the scene loses focus, (i.e.,
+ someone calls clearFocus()) while an item has focus, the scene will
+ maintain its item focus information, and once the scene regains focus, it
+ will make sure the last focus item regains focus.
+
+ For mouse-over effects, QGraphicsScene dispatches \e {hover
+ events}. If an item accepts hover events (see
+ QGraphicsItem::acceptHoverEvents()), it will receive a \l
+ {QEvent::}{GraphicsSceneHoverEnter} event when the mouse enters
+ its area. As the mouse continues moving inside the item's area,
+ QGraphicsScene will send it \l {QEvent::}{GraphicsSceneHoverMove}
+ events. When the mouse leaves the item's area, the item will
+ receive a \l {QEvent::}{GraphicsSceneHoverLeave} event.
+
+ All mouse events are delivered to the current \e {mouse grabber}
+ item. An item becomes the scene's mouse grabber if it accepts
+ mouse events (see QGraphicsItem::acceptedMouseButtons()) and it
+ receives a mouse press. It stays the mouse grabber until it
+ receives a mouse release when no other mouse buttons are
+ pressed. You can call mouseGrabberItem() to determine what item is
+ currently grabbing the mouse.
+
+ \sa QGraphicsItem, QGraphicsView
+*/
+
+/*!
+ \enum QGraphicsScene::SceneLayer
+ \since 4.3
+
+ This enum describes the rendering layers in a QGraphicsScene. When
+ QGraphicsScene draws the scene contents, it renders each of these layers
+ separately, in order.
+
+ Each layer represents a flag that can be OR'ed together when calling
+ functions such as invalidate() or QGraphicsView::invalidateScene().
+
+ \value ItemLayer The item layer. QGraphicsScene renders all items are in
+ this layer by calling the virtual function drawItems(). The item layer is
+ drawn after the background layer, but before the foreground layer.
+
+ \value BackgroundLayer The background layer. QGraphicsScene renders the
+ scene's background in this layer by calling the virtual function
+ drawBackground(). The background layer is drawn first of all layers.
+
+ \value ForegroundLayer The foreground layer. QGraphicsScene renders the
+ scene's foreground in this layer by calling the virtual function
+ drawForeground(). The foreground layer is drawn last of all layers.
+
+ \value AllLayers All layers; this value represents a combination of all
+ three layers.
+
+ \sa invalidate(), QGraphicsView::invalidateScene()
+*/
+
+/*!
+ \enum QGraphicsScene::ItemIndexMethod
+
+ This enum describes the indexing algorithms QGraphicsScene provides for
+ managing positional information about items on the scene.
+
+ \value BspTreeIndex A Binary Space Partitioning tree is applied. All
+ QGraphicsScene's item location algorithms are of an order close to
+ logarithmic complexity, by making use of binary search. Adding, moving and
+ removing items is logarithmic. This approach is best for static scenes
+ (i.e., scenes where most items do not move).
+
+ \value NoIndex No index is applied. Item location is of linear complexity,
+ as all items on the scene are searched. Adding, moving and removing items,
+ however, is done in constant time. This approach is ideal for dynamic
+ scenes, where many items are added, moved or removed continuously.
+
+ \sa setItemIndexMethod(), bspTreeDepth
+*/
+
+#include "qgraphicsscene.h"
+
+#ifndef QT_NO_GRAPHICSVIEW
+
+#include "qgraphicsitem.h"
+#include "qgraphicsitem_p.h"
+#include "qgraphicslayout.h"
+#include "qgraphicsscene_p.h"
+#include "qgraphicssceneevent.h"
+#include "qgraphicsview.h"
+#include "qgraphicsview_p.h"
+#include "qgraphicswidget.h"
+#include "qgraphicswidget_p.h"
+#include "qgraphicssceneindex_p.h"
+#include "qgraphicsscenebsptreeindex_p.h"
+#include "qgraphicsscenelinearindex_p.h"
+
+#include <QtCore/qdebug.h>
+#include <QtCore/qlist.h>
+#include <QtCore/qmath.h>
+#include <QtCore/qrect.h>
+#include <QtCore/qset.h>
+#include <QtCore/qstack.h>
+#include <QtCore/qtimer.h>
+#include <QtCore/qvarlengtharray.h>
+#include <QtCore/QMetaMethod>
+#include <QtGui/qapplication.h>
+#include <QtGui/qdesktopwidget.h>
+#include <QtGui/qevent.h>
+#include <QtGui/qgraphicslayout.h>
+#include <QtGui/qgraphicsproxywidget.h>
+#include <QtGui/qgraphicswidget.h>
+#include <QtGui/qmatrix.h>
+#include <QtGui/qpaintengine.h>
+#include <QtGui/qpainter.h>
+#include <QtGui/qpixmapcache.h>
+#include <QtGui/qpolygon.h>
+#include <QtGui/qstyleoption.h>
+#include <QtGui/qtooltip.h>
+#include <QtGui/qtransform.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>
+#include <private/qgesturemanager_p.h>
+#include <private/qpathclipper_p.h>
+
+// #define GESTURE_DEBUG
+#ifndef GESTURE_DEBUG
+# define DEBUG if (0) qDebug
+#else
+# define DEBUG qDebug
+#endif
+
+QT_BEGIN_NAMESPACE
+
+bool qt_sendSpontaneousEvent(QObject *receiver, QEvent *event);
+
+static void _q_hoverFromMouseEvent(QGraphicsSceneHoverEvent *hover, const QGraphicsSceneMouseEvent *mouseEvent)
+{
+ hover->setWidget(mouseEvent->widget());
+ hover->setPos(mouseEvent->pos());
+ hover->setScenePos(mouseEvent->scenePos());
+ hover->setScreenPos(mouseEvent->screenPos());
+ hover->setLastPos(mouseEvent->lastPos());
+ hover->setLastScenePos(mouseEvent->lastScenePos());
+ hover->setLastScreenPos(mouseEvent->lastScreenPos());
+ hover->setModifiers(mouseEvent->modifiers());
+ hover->setAccepted(mouseEvent->isAccepted());
+}
+
+/*!
+ \internal
+*/
+QGraphicsScenePrivate::QGraphicsScenePrivate()
+ : indexMethod(QGraphicsScene::BspTreeIndex),
+ index(0),
+ lastItemCount(0),
+ hasSceneRect(false),
+ dirtyGrowingItemsBoundingRect(true),
+ updateAll(false),
+ calledEmitUpdated(false),
+ processDirtyItemsEmitted(false),
+ needSortTopLevelItems(true),
+ holesInTopLevelSiblingIndex(false),
+ topLevelSequentialOrdering(true),
+ scenePosDescendantsUpdatePending(false),
+ stickyFocus(false),
+ hasFocus(false),
+ lastMouseGrabberItemHasImplicitMouseGrab(false),
+ allItemsIgnoreHoverEvents(true),
+ allItemsUseDefaultCursor(true),
+ painterStateProtection(true),
+ sortCacheEnabled(false),
+ allItemsIgnoreTouchEvents(true),
+ selectionChanging(0),
+ rectAdjust(2),
+ focusItem(0),
+ lastFocusItem(0),
+ passiveFocusItem(0),
+ tabFocusFirst(0),
+ activePanel(0),
+ lastActivePanel(0),
+ activationRefCount(0),
+ childExplicitActivation(0),
+ lastMouseGrabberItem(0),
+ dragDropItem(0),
+ enterWidget(0),
+ lastDropAction(Qt::IgnoreAction),
+ style(0)
+{
+}
+
+/*!
+ \internal
+*/
+void QGraphicsScenePrivate::init()
+{
+ Q_Q(QGraphicsScene);
+
+ index = new QGraphicsSceneBspTreeIndex(q);
+
+ // Keep this index so we can check for connected slots later on.
+ changedSignalIndex = signalIndex("changed(QList<QRectF>)");
+ processDirtyItemsIndex = q->metaObject()->indexOfSlot("_q_processDirtyItems()");
+ polishItemsIndex = q->metaObject()->indexOfSlot("_q_polishItems()");
+
+ qApp->d_func()->scene_list.append(q);
+ q->update();
+}
+
+/*!
+ \internal
+*/
+QGraphicsScenePrivate *QGraphicsScenePrivate::get(QGraphicsScene *q)
+{
+ return q->d_func();
+}
+
+void QGraphicsScenePrivate::_q_emitUpdated()
+{
+ Q_Q(QGraphicsScene);
+ calledEmitUpdated = false;
+
+ if (dirtyGrowingItemsBoundingRect) {
+ if (!hasSceneRect) {
+ const QRectF oldGrowingItemsBoundingRect = growingItemsBoundingRect;
+ growingItemsBoundingRect |= q->itemsBoundingRect();
+ if (oldGrowingItemsBoundingRect != growingItemsBoundingRect)
+ emit q->sceneRectChanged(growingItemsBoundingRect);
+ }
+ dirtyGrowingItemsBoundingRect = false;
+ }
+
+ // Ensure all views are connected if anything is connected. This disables
+ // the optimization that items send updates directly to the views, but it
+ // needs to happen in order to keep compatibility with the behavior from
+ // Qt 4.4 and backward.
+ if (isSignalConnected(changedSignalIndex)) {
+ for (int i = 0; i < views.size(); ++i) {
+ QGraphicsView *view = views.at(i);
+ if (!view->d_func()->connectedToScene) {
+ view->d_func()->connectedToScene = true;
+ q->connect(q, SIGNAL(changed(QList<QRectF>)),
+ views.at(i), SLOT(updateScene(QList<QRectF>)));
+ }
+ }
+ } else {
+ if (views.isEmpty()) {
+ updateAll = false;
+ return;
+ }
+ for (int i = 0; i < views.size(); ++i)
+ views.at(i)->d_func()->processPendingUpdates();
+ // It's important that we update all views before we dispatch, hence two for-loops.
+ for (int i = 0; i < views.size(); ++i)
+ views.at(i)->d_func()->dispatchPendingUpdateRequests();
+ return;
+ }
+
+ // Notify the changes to anybody interested.
+ QList<QRectF> oldUpdatedRects;
+ oldUpdatedRects = updateAll ? (QList<QRectF>() << q->sceneRect()) : updatedRects;
+ updateAll = false;
+ updatedRects.clear();
+ emit q->changed(oldUpdatedRects);
+}
+
+/*!
+ \internal
+
+ ### This function is almost identical to QGraphicsItemPrivate::addChild().
+*/
+void QGraphicsScenePrivate::registerTopLevelItem(QGraphicsItem *item)
+{
+ item->d_ptr->ensureSequentialSiblingIndex();
+ needSortTopLevelItems = true; // ### maybe false
+ item->d_ptr->siblingIndex = topLevelItems.size();
+ topLevelItems.append(item);
+}
+
+/*!
+ \internal
+
+ ### This function is almost identical to QGraphicsItemPrivate::removeChild().
+*/
+void QGraphicsScenePrivate::unregisterTopLevelItem(QGraphicsItem *item)
+{
+ if (!holesInTopLevelSiblingIndex)
+ holesInTopLevelSiblingIndex = item->d_ptr->siblingIndex != topLevelItems.size() - 1;
+ if (topLevelSequentialOrdering && !holesInTopLevelSiblingIndex)
+ topLevelItems.removeAt(item->d_ptr->siblingIndex);
+ else
+ topLevelItems.removeOne(item);
+ // NB! Do not use topLevelItems.removeAt(item->d_ptr->siblingIndex) because
+ // the item is not guaranteed to be at the index after the list is sorted
+ // (see ensureSortedTopLevelItems()).
+ item->d_ptr->siblingIndex = -1;
+ if (topLevelSequentialOrdering)
+ topLevelSequentialOrdering = !holesInTopLevelSiblingIndex;
+}
+
+/*!
+ \internal
+*/
+void QGraphicsScenePrivate::_q_polishItems()
+{
+ if (unpolishedItems.isEmpty())
+ return;
+
+ const QVariant booleanTrueVariant(true);
+ QGraphicsItem *item = 0;
+ QGraphicsItemPrivate *itemd = 0;
+ const int oldUnpolishedCount = unpolishedItems.count();
+
+ for (int i = 0; i < oldUnpolishedCount; ++i) {
+ item = unpolishedItems.at(i);
+ if (!item)
+ continue;
+ itemd = item->d_ptr.data();
+ itemd->pendingPolish = false;
+ if (!itemd->explicitlyHidden) {
+ item->itemChange(QGraphicsItem::ItemVisibleChange, booleanTrueVariant);
+ item->itemChange(QGraphicsItem::ItemVisibleHasChanged, booleanTrueVariant);
+ }
+ if (itemd->isWidget) {
+ QEvent event(QEvent::Polish);
+ QApplication::sendEvent((QGraphicsWidget *)item, &event);
+ }
+ }
+
+ if (unpolishedItems.count() == oldUnpolishedCount) {
+ // No new items were added to the vector.
+ unpolishedItems.clear();
+ } else {
+ // New items were appended; keep them and remove the old ones.
+ unpolishedItems.remove(0, oldUnpolishedCount);
+ unpolishedItems.squeeze();
+ QMetaObject::invokeMethod(q_ptr, "_q_polishItems", Qt::QueuedConnection);
+ }
+}
+
+void QGraphicsScenePrivate::_q_processDirtyItems()
+{
+ processDirtyItemsEmitted = false;
+
+ if (updateAll) {
+ Q_ASSERT(calledEmitUpdated);
+ // No need for further processing (except resetting the dirty states).
+ // The growingItemsBoundingRect is updated in _q_emitUpdated.
+ for (int i = 0; i < topLevelItems.size(); ++i)
+ resetDirtyItem(topLevelItems.at(i), /*recursive=*/true);
+ return;
+ }
+
+ const bool wasPendingSceneUpdate = calledEmitUpdated;
+ const QRectF oldGrowingItemsBoundingRect = growingItemsBoundingRect;
+
+ // Process items recursively.
+ for (int i = 0; i < topLevelItems.size(); ++i)
+ processDirtyItemsRecursive(topLevelItems.at(i));
+
+ dirtyGrowingItemsBoundingRect = false;
+ if (!hasSceneRect && oldGrowingItemsBoundingRect != growingItemsBoundingRect)
+ emit q_func()->sceneRectChanged(growingItemsBoundingRect);
+
+ if (wasPendingSceneUpdate)
+ return;
+
+ for (int i = 0; i < views.size(); ++i)
+ views.at(i)->d_func()->processPendingUpdates();
+
+ if (calledEmitUpdated) {
+ // We did a compatibility QGraphicsScene::update in processDirtyItemsRecursive
+ // and we cannot wait for the control to reach the eventloop before the
+ // changed signal is emitted, so we emit it now.
+ _q_emitUpdated();
+ }
+
+ // Immediately dispatch all pending update requests on the views.
+ for (int i = 0; i < views.size(); ++i)
+ views.at(i)->d_func()->dispatchPendingUpdateRequests();
+}
+
+/*!
+ \internal
+*/
+void QGraphicsScenePrivate::setScenePosItemEnabled(QGraphicsItem *item, bool enabled)
+{
+ QGraphicsItem *p = item->d_ptr->parent;
+ while (p) {
+ p->d_ptr->scenePosDescendants = enabled;
+ p = p->d_ptr->parent;
+ }
+ if (!enabled && !scenePosDescendantsUpdatePending) {
+ scenePosDescendantsUpdatePending = true;
+ QMetaObject::invokeMethod(q_func(), "_q_updateScenePosDescendants", Qt::QueuedConnection);
+ }
+}
+
+/*!
+ \internal
+*/
+void QGraphicsScenePrivate::registerScenePosItem(QGraphicsItem *item)
+{
+ scenePosItems.insert(item);
+ setScenePosItemEnabled(item, true);
+}
+
+/*!
+ \internal
+*/
+void QGraphicsScenePrivate::unregisterScenePosItem(QGraphicsItem *item)
+{
+ scenePosItems.remove(item);
+ setScenePosItemEnabled(item, false);
+}
+
+/*!
+ \internal
+*/
+void QGraphicsScenePrivate::_q_updateScenePosDescendants()
+{
+ foreach (QGraphicsItem *item, scenePosItems) {
+ QGraphicsItem *p = item->d_ptr->parent;
+ while (p) {
+ p->d_ptr->scenePosDescendants = 1;
+ p = p->d_ptr->parent;
+ }
+ }
+ scenePosDescendantsUpdatePending = false;
+}
+
+/*!
+ \internal
+
+ Schedules an item for removal. This function leaves some stale indexes
+ around in the BSP tree if called from the item's destructor; these will
+ be cleaned up the next time someone triggers purgeRemovedItems().
+
+ Note: This function might get called from QGraphicsItem's destructor. \a item is
+ being destroyed, so we cannot call any pure virtual functions on it (such
+ as boundingRect()). Also, it is unnecessary to update the item's own state
+ in any way.
+*/
+void QGraphicsScenePrivate::removeItemHelper(QGraphicsItem *item)
+{
+ Q_Q(QGraphicsScene);
+
+ // Clear focus on the item to remove any reference in the focusWidget chain.
+ item->clearFocus();
+
+ markDirty(item, QRectF(), /*invalidateChildren=*/false, /*force=*/false,
+ /*ignoreOpacity=*/false, /*removingItemFromScene=*/true);
+
+ if (item->d_ptr->inDestructor) {
+ // The item is actually in its destructor, we call the special method in the index.
+ index->deleteItem(item);
+ } else {
+ // Can potentially call item->boundingRect() (virtual function), that's why
+ // we only can call this function if the item is not in its destructor.
+ index->removeItem(item);
+ }
+
+ item->d_ptr->clearSubFocus();
+
+ if (item->flags() & QGraphicsItem::ItemSendsScenePositionChanges)
+ unregisterScenePosItem(item);
+
+ QGraphicsScene *oldScene = item->d_func()->scene;
+ item->d_func()->scene = 0;
+
+ //We need to remove all children first because they might use their parent
+ //attributes (e.g. sceneTransform).
+ if (!item->d_ptr->inDestructor) {
+ // Remove all children recursively
+ for (int i = 0; i < item->d_ptr->children.size(); ++i)
+ q->removeItem(item->d_ptr->children.at(i));
+ }
+
+ if (!item->d_ptr->inDestructor && item == tabFocusFirst) {
+ QGraphicsWidget *widget = static_cast<QGraphicsWidget *>(item);
+ widget->d_func()->fixFocusChainBeforeReparenting(0, oldScene, 0);
+ }
+
+ // Unregister focus proxy.
+ item->d_ptr->resetFocusProxy();
+
+ // Remove from parent, or unregister from toplevels.
+ if (QGraphicsItem *parentItem = item->parentItem()) {
+ if (parentItem->scene()) {
+ Q_ASSERT_X(parentItem->scene() == q, "QGraphicsScene::removeItem",
+ "Parent item's scene is different from this item's scene");
+ item->setParentItem(0);
+ }
+ } else {
+ unregisterTopLevelItem(item);
+ }
+
+ // Reset the mouse grabber and focus item data.
+ if (item == focusItem)
+ focusItem = 0;
+ if (item == lastFocusItem)
+ lastFocusItem = 0;
+ if (item == passiveFocusItem)
+ passiveFocusItem = 0;
+ if (item == activePanel) {
+ // ### deactivate...
+ activePanel = 0;
+ }
+ if (item == lastActivePanel)
+ lastActivePanel = 0;
+
+ // Cancel active touches
+ {
+ QMap<int, QGraphicsItem *>::iterator it = itemForTouchPointId.begin();
+ while (it != itemForTouchPointId.end()) {
+ if (it.value() == item) {
+ sceneCurrentTouchPoints.remove(it.key());
+ it = itemForTouchPointId.erase(it);
+ } else {
+ ++it;
+ }
+ }
+ }
+
+ // Disable selectionChanged() for individual items
+ ++selectionChanging;
+ int oldSelectedItemsSize = selectedItems.size();
+
+ // Update selected & hovered item bookkeeping
+ selectedItems.remove(item);
+ hoverItems.removeAll(item);
+ cachedItemsUnderMouse.removeAll(item);
+ if (item->d_ptr->pendingPolish) {
+ const int unpolishedIndex = unpolishedItems.indexOf(item);
+ if (unpolishedIndex != -1)
+ unpolishedItems[unpolishedIndex] = 0;
+ item->d_ptr->pendingPolish = false;
+ }
+ resetDirtyItem(item);
+
+ //We remove all references of item from the sceneEventFilter arrays
+ QMultiMap<QGraphicsItem*, QGraphicsItem*>::iterator iterator = sceneEventFilters.begin();
+ while (iterator != sceneEventFilters.end()) {
+ if (iterator.value() == item || iterator.key() == item)
+ iterator = sceneEventFilters.erase(iterator);
+ else
+ ++iterator;
+ }
+
+ if (item->isPanel() && item->isVisible() && item->panelModality() != QGraphicsItem::NonModal)
+ leaveModal(item);
+
+ // Reset the mouse grabber and focus item data.
+ if (mouseGrabberItems.contains(item))
+ ungrabMouse(item, /* item is dying */ item->d_ptr->inDestructor);
+
+ // Reset the keyboard grabber
+ if (keyboardGrabberItems.contains(item))
+ ungrabKeyboard(item, /* item is dying */ item->d_ptr->inDestructor);
+
+ // Reset the last mouse grabber item
+ if (item == lastMouseGrabberItem)
+ lastMouseGrabberItem = 0;
+
+ // Reset the current drop item
+ if (item == dragDropItem)
+ dragDropItem = 0;
+
+ // Reenable selectionChanged() for individual items
+ --selectionChanging;
+ if (!selectionChanging && selectedItems.size() != oldSelectedItemsSize)
+ emit q->selectionChanged();
+
+#ifndef QT_NO_GESTURES
+ QHash<QGesture *, QGraphicsObject *>::iterator it;
+ for (it = gestureTargets.begin(); it != gestureTargets.end();) {
+ if (it.value() == item)
+ it = gestureTargets.erase(it);
+ else
+ ++it;
+ }
+
+ QGraphicsObject *dummy = static_cast<QGraphicsObject *>(item);
+ cachedTargetItems.removeOne(dummy);
+ cachedItemGestures.remove(dummy);
+ cachedAlreadyDeliveredGestures.remove(dummy);
+
+ foreach (Qt::GestureType gesture, item->d_ptr->gestureContext.keys())
+ ungrabGesture(item, gesture);
+#endif // QT_NO_GESTURES
+}
+
+/*!
+ \internal
+*/
+void QGraphicsScenePrivate::setActivePanelHelper(QGraphicsItem *item, bool duringActivationEvent)
+{
+ Q_Q(QGraphicsScene);
+ if (item && item->scene() != q) {
+ qWarning("QGraphicsScene::setActivePanel: item %p must be part of this scene",
+ item);
+ return;
+ }
+
+ // Ensure the scene has focus when we change panel activation.
+ q->setFocus(Qt::ActiveWindowFocusReason);
+
+ // Find the item's panel.
+ QGraphicsItem *panel = item ? item->panel() : 0;
+ lastActivePanel = panel ? activePanel : 0;
+ if (panel == activePanel || (!q->isActive() && !duringActivationEvent))
+ return;
+
+ // Deactivate the last active panel.
+ if (activePanel) {
+ if (QGraphicsItem *fi = activePanel->focusItem()) {
+ // Remove focus from the current focus item.
+ if (fi == q->focusItem())
+ q->setFocusItem(0, Qt::ActiveWindowFocusReason);
+ }
+
+ QEvent event(QEvent::WindowDeactivate);
+ q->sendEvent(activePanel, &event);
+ } else if (panel && !duringActivationEvent) {
+ // Deactivate the scene if changing activation to a panel.
+ QEvent event(QEvent::WindowDeactivate);
+ foreach (QGraphicsItem *item, q->items()) {
+ if (item->isVisible() && !item->isPanel() && !item->parentItem())
+ q->sendEvent(item, &event);
+ }
+ }
+
+ // Update activate state.
+ activePanel = panel;
+ QEvent event(QEvent::ActivationChange);
+ QApplication::sendEvent(q, &event);
+
+ // Activate
+ if (panel) {
+ QEvent event(QEvent::WindowActivate);
+ q->sendEvent(panel, &event);
+
+ // Set focus on the panel's focus item.
+ if (QGraphicsItem *focusItem = panel->focusItem())
+ focusItem->setFocus(Qt::ActiveWindowFocusReason);
+ } else if (q->isActive()) {
+ // Activate the scene
+ QEvent event(QEvent::WindowActivate);
+ foreach (QGraphicsItem *item, q->items()) {
+ if (item->isVisible() && !item->isPanel() && !item->parentItem())
+ q->sendEvent(item, &event);
+ }
+ }
+}
+
+/*!
+ \internal
+*/
+void QGraphicsScenePrivate::setFocusItemHelper(QGraphicsItem *item,
+ Qt::FocusReason focusReason)
+{
+ Q_Q(QGraphicsScene);
+ if (item == focusItem)
+ return;
+
+ // Clear focus if asked to set focus on something that can't
+ // accept input focus.
+ if (item && (!(item->flags() & QGraphicsItem::ItemIsFocusable)
+ || !item->isVisible() || !item->isEnabled())) {
+ item = 0;
+ }
+
+ // Set focus on the scene if an item requests focus.
+ if (item) {
+ q->setFocus(focusReason);
+ if (item == focusItem)
+ return;
+ }
+
+ if (focusItem) {
+ lastFocusItem = focusItem;
+
+#ifndef QT_NO_IM
+ if (lastFocusItem
+ && (lastFocusItem->flags() & QGraphicsItem::ItemAcceptsInputMethod)) {
+ // Close any external input method panel. This happens
+ // automatically by removing WA_InputMethodEnabled on
+ // the views, but if we are changing focus, we have to
+ // do it ourselves.
+ for (int i = 0; i < views.size(); ++i)
+ if (views.at(i)->inputContext())
+ views.at(i)->inputContext()->reset();
+ }
+
+ focusItem = 0;
+ QFocusEvent event(QEvent::FocusOut, focusReason);
+ sendEvent(lastFocusItem, &event);
+#endif //QT_NO_IM
+ }
+
+ // This handles the case that the item has been removed from the
+ // scene in response to the FocusOut event.
+ if (item && item->scene() != q)
+ item = 0;
+
+ if (item)
+ focusItem = item;
+ updateInputMethodSensitivityInViews();
+ if (item) {
+ QFocusEvent event(QEvent::FocusIn, focusReason);
+ sendEvent(item, &event);
+ }
+}
+
+/*!
+ \internal
+*/
+void QGraphicsScenePrivate::addPopup(QGraphicsWidget *widget)
+{
+ Q_ASSERT(widget);
+ Q_ASSERT(!popupWidgets.contains(widget));
+ popupWidgets << widget;
+ if (QGraphicsWidget *focusWidget = widget->focusWidget()) {
+ focusWidget->setFocus(Qt::PopupFocusReason);
+ } else {
+ grabKeyboard((QGraphicsItem *)widget);
+ if (focusItem && popupWidgets.size() == 1) {
+ QFocusEvent event(QEvent::FocusOut, Qt::PopupFocusReason);
+ sendEvent(focusItem, &event);
+ }
+ }
+ grabMouse((QGraphicsItem *)widget);
+}
+
+/*!
+ \internal
+
+ Remove \a widget from the popup list. Important notes:
+
+ \a widget is guaranteed to be in the list of popups, but it might not be
+ the last entry; you can hide any item in the pop list before the others,
+ and this must cause all later mouse grabbers to lose the grab.
+*/
+void QGraphicsScenePrivate::removePopup(QGraphicsWidget *widget, bool itemIsDying)
+{
+ Q_ASSERT(widget);
+ int index = popupWidgets.indexOf(widget);
+ Q_ASSERT(index != -1);
+
+ for (int i = popupWidgets.size() - 1; i >= index; --i) {
+ QGraphicsWidget *widget = popupWidgets.takeLast();
+ ungrabMouse(widget, itemIsDying);
+ if (focusItem && popupWidgets.isEmpty()) {
+ QFocusEvent event(QEvent::FocusIn, Qt::PopupFocusReason);
+ sendEvent(focusItem, &event);
+ } else if (keyboardGrabberItems.contains(static_cast<QGraphicsItem *>(widget))) {
+ ungrabKeyboard(static_cast<QGraphicsItem *>(widget), itemIsDying);
+ }
+ if (!itemIsDying && widget->isVisible()) {
+ widget->QGraphicsItem::d_ptr->setVisibleHelper(false, /* explicit = */ false);
+ }
+ }
+}
+
+/*!
+ \internal
+*/
+void QGraphicsScenePrivate::grabMouse(QGraphicsItem *item, bool implicit)
+{
+ // Append to list of mouse grabber items, and send a mouse grab event.
+ if (mouseGrabberItems.contains(item)) {
+ if (mouseGrabberItems.last() == item) {
+ Q_ASSERT(!implicit);
+ if (!lastMouseGrabberItemHasImplicitMouseGrab) {
+ qWarning("QGraphicsItem::grabMouse: already a mouse grabber");
+ } else {
+ // Upgrade to an explicit mouse grab
+ lastMouseGrabberItemHasImplicitMouseGrab = false;
+ }
+ } else {
+ qWarning("QGraphicsItem::grabMouse: already blocked by mouse grabber: %p",
+ mouseGrabberItems.last());
+ }
+ return;
+ }
+
+ // Send ungrab event to the last grabber.
+ if (!mouseGrabberItems.isEmpty()) {
+ QGraphicsItem *last = mouseGrabberItems.last();
+ if (lastMouseGrabberItemHasImplicitMouseGrab) {
+ // Implicit mouse grab is immediately lost.
+ last->ungrabMouse();
+ } else {
+ // Just send ungrab event to current grabber.
+ QEvent ungrabEvent(QEvent::UngrabMouse);
+ sendEvent(last, &ungrabEvent);
+ }
+ }
+
+ mouseGrabberItems << item;
+ lastMouseGrabberItemHasImplicitMouseGrab = implicit;
+
+ // Send grab event to current grabber.
+ QEvent grabEvent(QEvent::GrabMouse);
+ sendEvent(item, &grabEvent);
+}
+
+/*!
+ \internal
+*/
+void QGraphicsScenePrivate::ungrabMouse(QGraphicsItem *item, bool itemIsDying)
+{
+ int index = mouseGrabberItems.indexOf(item);
+ if (index == -1) {
+ qWarning("QGraphicsItem::ungrabMouse: not a mouse grabber");
+ return;
+ }
+
+ if (item != mouseGrabberItems.last()) {
+ // Recursively ungrab the next mouse grabber until we reach this item
+ // to ensure state consistency.
+ ungrabMouse(mouseGrabberItems.at(index + 1), itemIsDying);
+ }
+ if (!popupWidgets.isEmpty() && item == popupWidgets.last()) {
+ // If the item is a popup, go via removePopup to ensure state
+ // consistency and that it gets hidden correctly - beware that
+ // removePopup() reenters this function to continue removing the grab.
+ removePopup((QGraphicsWidget *)item, itemIsDying);
+ return;
+ }
+
+ // Send notification about mouse ungrab.
+ if (!itemIsDying) {
+ QEvent event(QEvent::UngrabMouse);
+ sendEvent(item, &event);
+ }
+
+ // Remove the item from the list of grabbers. Whenever this happens, we
+ // reset the implicitGrab (there can be only ever be one implicit grabber
+ // in a scene, and it is always the latest grabber; if the implicit grab
+ // is lost, it is not automatically regained.
+ mouseGrabberItems.takeLast();
+ lastMouseGrabberItemHasImplicitMouseGrab = false;
+
+ // Send notification about mouse regrab. ### It's unfortunate that all the
+ // items get a GrabMouse event, but this is a rare case with a simple
+ // implementation and it does ensure a consistent state.
+ if (!itemIsDying && !mouseGrabberItems.isEmpty()) {
+ QGraphicsItem *last = mouseGrabberItems.last();
+ QEvent event(QEvent::GrabMouse);
+ sendEvent(last, &event);
+ }
+}
+
+/*!
+ \internal
+*/
+void QGraphicsScenePrivate::clearMouseGrabber()
+{
+ if (!mouseGrabberItems.isEmpty())
+ mouseGrabberItems.first()->ungrabMouse();
+ lastMouseGrabberItem = 0;
+}
+
+/*!
+ \internal
+*/
+void QGraphicsScenePrivate::grabKeyboard(QGraphicsItem *item)
+{
+ if (keyboardGrabberItems.contains(item)) {
+ if (keyboardGrabberItems.last() == item)
+ qWarning("QGraphicsItem::grabKeyboard: already a keyboard grabber");
+ else
+ qWarning("QGraphicsItem::grabKeyboard: already blocked by keyboard grabber: %p",
+ keyboardGrabberItems.last());
+ return;
+ }
+
+ // Send ungrab event to the last grabber.
+ if (!keyboardGrabberItems.isEmpty()) {
+ // Just send ungrab event to current grabber.
+ QEvent ungrabEvent(QEvent::UngrabKeyboard);
+ sendEvent(keyboardGrabberItems.last(), &ungrabEvent);
+ }
+
+ keyboardGrabberItems << item;
+
+ // Send grab event to current grabber.
+ QEvent grabEvent(QEvent::GrabKeyboard);
+ sendEvent(item, &grabEvent);
+}
+
+/*!
+ \internal
+*/
+void QGraphicsScenePrivate::ungrabKeyboard(QGraphicsItem *item, bool itemIsDying)
+{
+ int index = keyboardGrabberItems.lastIndexOf(item);
+ if (index == -1) {
+ qWarning("QGraphicsItem::ungrabKeyboard: not a keyboard grabber");
+ return;
+ }
+ if (item != keyboardGrabberItems.last()) {
+ // Recursively ungrab the topmost keyboard grabber until we reach this
+ // item to ensure state consistency.
+ ungrabKeyboard(keyboardGrabberItems.at(index + 1), itemIsDying);
+ }
+
+ // Send notification about keyboard ungrab.
+ if (!itemIsDying) {
+ QEvent event(QEvent::UngrabKeyboard);
+ sendEvent(item, &event);
+ }
+
+ // Remove the item from the list of grabbers.
+ keyboardGrabberItems.takeLast();
+
+ // Send notification about mouse regrab.
+ if (!itemIsDying && !keyboardGrabberItems.isEmpty()) {
+ QGraphicsItem *last = keyboardGrabberItems.last();
+ QEvent event(QEvent::GrabKeyboard);
+ sendEvent(last, &event);
+ }
+}
+
+/*!
+ \internal
+*/
+void QGraphicsScenePrivate::clearKeyboardGrabber()
+{
+ if (!keyboardGrabberItems.isEmpty())
+ ungrabKeyboard(keyboardGrabberItems.first());
+}
+
+void QGraphicsScenePrivate::enableMouseTrackingOnViews()
+{
+ foreach (QGraphicsView *view, views)
+ view->viewport()->setMouseTracking(true);
+}
+
+/*!
+ Returns all items for the screen position in \a event.
+*/
+QList<QGraphicsItem *> QGraphicsScenePrivate::itemsAtPosition(const QPoint &/*screenPos*/,
+ const QPointF &scenePos,
+ QWidget *widget) const
+{
+ Q_Q(const QGraphicsScene);
+ QGraphicsView *view = widget ? qobject_cast<QGraphicsView *>(widget->parentWidget()) : 0;
+ if (!view)
+ return q->items(scenePos, Qt::IntersectsItemShape, Qt::DescendingOrder, QTransform());
+
+ const QRectF pointRect(scenePos, QSizeF(1, 1));
+ if (!view->isTransformed())
+ return q->items(pointRect, Qt::IntersectsItemShape, Qt::DescendingOrder);
+
+ const QTransform viewTransform = view->viewportTransform();
+ return q->items(pointRect, Qt::IntersectsItemShape,
+ Qt::DescendingOrder, viewTransform);
+}
+
+/*!
+ \internal
+*/
+void QGraphicsScenePrivate::storeMouseButtonsForMouseGrabber(QGraphicsSceneMouseEvent *event)
+{
+ for (int i = 0x1; i <= 0x10; i <<= 1) {
+ if (event->buttons() & i) {
+ mouseGrabberButtonDownPos.insert(Qt::MouseButton(i),
+ mouseGrabberItems.last()->d_ptr->genericMapFromScene(event->scenePos(),
+ event->widget()));
+ mouseGrabberButtonDownScenePos.insert(Qt::MouseButton(i), event->scenePos());
+ mouseGrabberButtonDownScreenPos.insert(Qt::MouseButton(i), event->screenPos());
+ }
+ }
+}
+
+/*!
+ \internal
+*/
+void QGraphicsScenePrivate::installSceneEventFilter(QGraphicsItem *watched, QGraphicsItem *filter)
+{
+ sceneEventFilters.insert(watched, filter);
+}
+
+/*!
+ \internal
+*/
+void QGraphicsScenePrivate::removeSceneEventFilter(QGraphicsItem *watched, QGraphicsItem *filter)
+{
+ if (!sceneEventFilters.contains(watched))
+ return;
+
+ QMultiMap<QGraphicsItem *, QGraphicsItem *>::Iterator it = sceneEventFilters.lowerBound(watched);
+ QMultiMap<QGraphicsItem *, QGraphicsItem *>::Iterator end = sceneEventFilters.upperBound(watched);
+ do {
+ if (it.value() == filter)
+ it = sceneEventFilters.erase(it);
+ else
+ ++it;
+ } while (it != end);
+}
+
+/*!
+ \internal
+*/
+bool QGraphicsScenePrivate::filterDescendantEvent(QGraphicsItem *item, QEvent *event)
+{
+ if (item && (item->d_ptr->ancestorFlags & QGraphicsItemPrivate::AncestorFiltersChildEvents)) {
+ QGraphicsItem *parent = item->parentItem();
+ while (parent) {
+ if (parent->d_ptr->filtersDescendantEvents && parent->sceneEventFilter(item, event))
+ return true;
+ if (!(parent->d_ptr->ancestorFlags & QGraphicsItemPrivate::AncestorFiltersChildEvents))
+ return false;
+ parent = parent->parentItem();
+ }
+ }
+ return false;
+}
+
+/*!
+ \internal
+*/
+bool QGraphicsScenePrivate::filterEvent(QGraphicsItem *item, QEvent *event)
+{
+ if (item && !sceneEventFilters.contains(item))
+ return false;
+
+ QMultiMap<QGraphicsItem *, QGraphicsItem *>::Iterator it = sceneEventFilters.lowerBound(item);
+ QMultiMap<QGraphicsItem *, QGraphicsItem *>::Iterator end = sceneEventFilters.upperBound(item);
+ while (it != end) {
+ // ### The filterer and filteree might both be deleted.
+ if (it.value()->sceneEventFilter(it.key(), event))
+ return true;
+ ++it;
+ }
+ return false;
+}
+
+/*!
+ \internal
+
+ This is the final dispatch point for any events from the scene to the
+ item. It filters the event first - if the filter returns true, the event
+ is considered to have been eaten by the filter, and is therefore stopped
+ (the default filter returns false). Then/otherwise, if the item is
+ enabled, the event is sent; otherwise it is stopped.
+*/
+bool QGraphicsScenePrivate::sendEvent(QGraphicsItem *item, QEvent *event)
+{
+ if (QGraphicsObject *object = item->toGraphicsObject()) {
+#ifndef QT_NO_GESTURES
+ QGestureManager *gestureManager = QApplicationPrivate::instance()->gestureManager;
+ if (gestureManager) {
+ if (gestureManager->filterEvent(object, event))
+ return true;
+ }
+#endif // QT_NO_GESTURES
+ }
+
+ if (filterEvent(item, event))
+ return false;
+ if (filterDescendantEvent(item, event))
+ return false;
+ if (!item || !item->isEnabled())
+ return false;
+ if (QGraphicsObject *o = item->toGraphicsObject()) {
+ bool spont = event->spontaneous();
+ if (spont ? qt_sendSpontaneousEvent(o, event) : QApplication::sendEvent(o, event))
+ return true;
+ event->spont = spont;
+ }
+ return item->sceneEvent(event);
+}
+
+/*!
+ \internal
+*/
+void QGraphicsScenePrivate::cloneDragDropEvent(QGraphicsSceneDragDropEvent *dest,
+ QGraphicsSceneDragDropEvent *source)
+{
+ dest->setWidget(source->widget());
+ dest->setPos(source->pos());
+ dest->setScenePos(source->scenePos());
+ dest->setScreenPos(source->screenPos());
+ dest->setButtons(source->buttons());
+ dest->setModifiers(source->modifiers());
+ dest->setPossibleActions(source->possibleActions());
+ dest->setProposedAction(source->proposedAction());
+ dest->setDropAction(source->dropAction());
+ dest->setSource(source->source());
+ dest->setMimeData(source->mimeData());
+}
+
+/*!
+ \internal
+*/
+void QGraphicsScenePrivate::sendDragDropEvent(QGraphicsItem *item,
+ QGraphicsSceneDragDropEvent *dragDropEvent)
+{
+ dragDropEvent->setPos(item->d_ptr->genericMapFromScene(dragDropEvent->scenePos(), dragDropEvent->widget()));
+ sendEvent(item, dragDropEvent);
+}
+
+/*!
+ \internal
+*/
+void QGraphicsScenePrivate::sendHoverEvent(QEvent::Type type, QGraphicsItem *item,
+ QGraphicsSceneHoverEvent *hoverEvent)
+{
+ QGraphicsSceneHoverEvent event(type);
+ event.setWidget(hoverEvent->widget());
+ event.setPos(item->d_ptr->genericMapFromScene(hoverEvent->scenePos(), hoverEvent->widget()));
+ event.setScenePos(hoverEvent->scenePos());
+ event.setScreenPos(hoverEvent->screenPos());
+ event.setLastPos(item->d_ptr->genericMapFromScene(hoverEvent->lastScenePos(), hoverEvent->widget()));
+ event.setLastScenePos(hoverEvent->lastScenePos());
+ event.setLastScreenPos(hoverEvent->lastScreenPos());
+ event.setModifiers(hoverEvent->modifiers());
+ sendEvent(item, &event);
+}
+
+/*!
+ \internal
+*/
+void QGraphicsScenePrivate::sendMouseEvent(QGraphicsSceneMouseEvent *mouseEvent)
+{
+ if (mouseEvent->button() == 0 && mouseEvent->buttons() == 0 && lastMouseGrabberItemHasImplicitMouseGrab) {
+ // ### This is a temporary fix for until we get proper mouse
+ // grab events.
+ clearMouseGrabber();
+ return;
+ }
+
+ QGraphicsItem *item = mouseGrabberItems.last();
+ if (item->isBlockedByModalPanel())
+ return;
+
+ for (int i = 0x1; i <= 0x10; i <<= 1) {
+ Qt::MouseButton button = Qt::MouseButton(i);
+ mouseEvent->setButtonDownPos(button, mouseGrabberButtonDownPos.value(button, item->d_ptr->genericMapFromScene(mouseEvent->scenePos(), mouseEvent->widget())));
+ mouseEvent->setButtonDownScenePos(button, mouseGrabberButtonDownScenePos.value(button, mouseEvent->scenePos()));
+ mouseEvent->setButtonDownScreenPos(button, mouseGrabberButtonDownScreenPos.value(button, mouseEvent->screenPos()));
+ }
+ mouseEvent->setPos(item->d_ptr->genericMapFromScene(mouseEvent->scenePos(), mouseEvent->widget()));
+ mouseEvent->setLastPos(item->d_ptr->genericMapFromScene(mouseEvent->lastScenePos(), mouseEvent->widget()));
+ sendEvent(item, mouseEvent);
+}
+
+/*!
+ \internal
+*/
+void QGraphicsScenePrivate::mousePressEventHandler(QGraphicsSceneMouseEvent *mouseEvent)
+{
+ Q_Q(QGraphicsScene);
+
+ // Ignore by default, unless we find a mouse grabber that accepts it.
+ mouseEvent->ignore();
+
+ // Deliver to any existing mouse grabber.
+ if (!mouseGrabberItems.isEmpty()) {
+ if (mouseGrabberItems.last()->isBlockedByModalPanel())
+ return;
+ // The event is ignored by default, but we disregard the event's
+ // accepted state after delivery; the mouse is grabbed, after all.
+ sendMouseEvent(mouseEvent);
+ return;
+ }
+
+ // Start by determining the number of items at the current position.
+ // Reuse value from earlier calculations if possible.
+ if (cachedItemsUnderMouse.isEmpty()) {
+ cachedItemsUnderMouse = itemsAtPosition(mouseEvent->screenPos(),
+ mouseEvent->scenePos(),
+ mouseEvent->widget());
+ }
+
+ // Update window activation.
+ QGraphicsItem *topItem = cachedItemsUnderMouse.value(0);
+ QGraphicsWidget *newActiveWindow = topItem ? topItem->window() : 0;
+ if (newActiveWindow && newActiveWindow->isBlockedByModalPanel(&topItem)) {
+ // pass activation to the blocking modal window
+ newActiveWindow = topItem ? topItem->window() : 0;
+ }
+
+ if (newActiveWindow != q->activeWindow())
+ q->setActiveWindow(newActiveWindow);
+
+ // Set focus on the topmost enabled item that can take focus.
+ bool setFocus = false;
+
+ foreach (QGraphicsItem *item, cachedItemsUnderMouse) {
+ if (item->isBlockedByModalPanel()
+ || (item->d_ptr->flags & QGraphicsItem::ItemStopsFocusHandling)) {
+ // Make sure we don't clear focus.
+ setFocus = true;
+ break;
+ }
+ if (item->isEnabled() && ((item->flags() & QGraphicsItem::ItemIsFocusable))) {
+ if (!item->isWidget() || ((QGraphicsWidget *)item)->focusPolicy() & Qt::ClickFocus) {
+ setFocus = true;
+ if (item != q->focusItem() && item->d_ptr->mouseSetsFocus)
+ q->setFocusItem(item, Qt::MouseFocusReason);
+ break;
+ }
+ }
+ if (item->isPanel())
+ break;
+ if (item->d_ptr->flags & QGraphicsItem::ItemStopsClickFocusPropagation)
+ break;
+ }
+
+ // Check for scene modality.
+ bool sceneModality = false;
+ for (int i = 0; i < modalPanels.size(); ++i) {
+ if (modalPanels.at(i)->panelModality() == QGraphicsItem::SceneModal) {
+ sceneModality = true;
+ break;
+ }
+ }
+
+ // If nobody could take focus, clear it.
+ if (!stickyFocus && !setFocus && !sceneModality)
+ q->setFocusItem(0, Qt::MouseFocusReason);
+
+ // Any item will do.
+ if (sceneModality && cachedItemsUnderMouse.isEmpty())
+ cachedItemsUnderMouse << modalPanels.first();
+
+ // Find a mouse grabber by sending mouse press events to all mouse grabber
+ // candidates one at a time, until the event is accepted. It's accepted by
+ // default, so the receiver has to explicitly ignore it for it to pass
+ // through.
+ foreach (QGraphicsItem *item, cachedItemsUnderMouse) {
+ if (!(item->acceptedMouseButtons() & mouseEvent->button())) {
+ // Skip items that don't accept the event's mouse button.
+ continue;
+ }
+
+ // Check if this item is blocked by a modal panel and deliver the mouse event to the
+ // blocking panel instead of this item if blocked.
+ (void) item->isBlockedByModalPanel(&item);
+
+ grabMouse(item, /* implicit = */ true);
+ mouseEvent->accept();
+
+ // check if the item we are sending to are disabled (before we send the event)
+ bool disabled = !item->isEnabled();
+ bool isPanel = item->isPanel();
+ if (mouseEvent->type() == QEvent::GraphicsSceneMouseDoubleClick
+ && item != lastMouseGrabberItem && lastMouseGrabberItem) {
+ // If this item is different from the item that received the last
+ // mouse event, and mouseEvent is a doubleclick event, then the
+ // event is converted to a press. Known limitation:
+ // Triple-clicking will not generate a doubleclick, though.
+ QGraphicsSceneMouseEvent mousePress(QEvent::GraphicsSceneMousePress);
+ mousePress.spont = mouseEvent->spont;
+ mousePress.accept();
+ mousePress.setButton(mouseEvent->button());
+ mousePress.setButtons(mouseEvent->buttons());
+ mousePress.setScreenPos(mouseEvent->screenPos());
+ mousePress.setScenePos(mouseEvent->scenePos());
+ mousePress.setModifiers(mouseEvent->modifiers());
+ mousePress.setWidget(mouseEvent->widget());
+ mousePress.setButtonDownPos(mouseEvent->button(),
+ mouseEvent->buttonDownPos(mouseEvent->button()));
+ mousePress.setButtonDownScenePos(mouseEvent->button(),
+ mouseEvent->buttonDownScenePos(mouseEvent->button()));
+ mousePress.setButtonDownScreenPos(mouseEvent->button(),
+ mouseEvent->buttonDownScreenPos(mouseEvent->button()));
+ sendMouseEvent(&mousePress);
+ mouseEvent->setAccepted(mousePress.isAccepted());
+ } else {
+ sendMouseEvent(mouseEvent);
+ }
+
+ bool dontSendUngrabEvents = mouseGrabberItems.isEmpty() || mouseGrabberItems.last() != item;
+ if (disabled) {
+ ungrabMouse(item, /* itemIsDying = */ dontSendUngrabEvents);
+ break;
+ }
+ if (mouseEvent->isAccepted()) {
+ if (!mouseGrabberItems.isEmpty())
+ storeMouseButtonsForMouseGrabber(mouseEvent);
+ lastMouseGrabberItem = item;
+ return;
+ }
+ ungrabMouse(item, /* itemIsDying = */ dontSendUngrabEvents);
+
+ // Don't propagate through panels.
+ if (isPanel)
+ break;
+ }
+
+ // Is the event still ignored? Then the mouse press goes to the scene.
+ // Reset the mouse grabber, clear the selection, clear focus, and leave
+ // the event ignored so that it can propagate through the originating
+ // view.
+ if (!mouseEvent->isAccepted()) {
+ clearMouseGrabber();
+
+ QGraphicsView *view = mouseEvent->widget() ? qobject_cast<QGraphicsView *>(mouseEvent->widget()->parentWidget()) : 0;
+ bool dontClearSelection = view && view->dragMode() == QGraphicsView::ScrollHandDrag;
+ if (!dontClearSelection) {
+ // Clear the selection if the originating view isn't in scroll
+ // hand drag mode. The view will clear the selection if no drag
+ // happened.
+ q->clearSelection();
+ }
+ }
+}
+
+/*!
+ \internal
+
+ Ensures that the list of toplevels is sorted by insertion order, and that
+ the siblingIndexes are packed (no gaps), and start at 0.
+
+ ### This function is almost identical to
+ QGraphicsItemPrivate::ensureSequentialSiblingIndex().
+*/
+void QGraphicsScenePrivate::ensureSequentialTopLevelSiblingIndexes()
+{
+ if (!topLevelSequentialOrdering) {
+ qSort(topLevelItems.begin(), topLevelItems.end(), QGraphicsItemPrivate::insertionOrder);
+ topLevelSequentialOrdering = true;
+ needSortTopLevelItems = 1;
+ }
+ if (holesInTopLevelSiblingIndex) {
+ holesInTopLevelSiblingIndex = 0;
+ for (int i = 0; i < topLevelItems.size(); ++i)
+ topLevelItems[i]->d_ptr->siblingIndex = i;
+ }
+}
+
+/*!
+ \internal
+
+ Set the font and propagate the changes if the font is different from the
+ current font.
+*/
+void QGraphicsScenePrivate::setFont_helper(const QFont &font)
+{
+ if (this->font == font && this->font.resolve() == font.resolve())
+ return;
+ updateFont(font);
+}
+
+/*!
+ \internal
+
+ Resolve the scene's font against the application font, and propagate the
+ changes too all items in the scene.
+*/
+void QGraphicsScenePrivate::resolveFont()
+{
+ QFont naturalFont = QApplication::font();
+ naturalFont.resolve(0);
+ QFont resolvedFont = font.resolve(naturalFont);
+ updateFont(resolvedFont);
+}
+
+/*!
+ \internal
+
+ Update the font, and whether or not it has changed, reresolve all fonts in
+ the scene.
+*/
+void QGraphicsScenePrivate::updateFont(const QFont &font)
+{
+ Q_Q(QGraphicsScene);
+
+ // Update local font setting.
+ this->font = font;
+
+ // Resolve the fonts of all top-level widget items, or widget items
+ // whose parent is not a widget.
+ foreach (QGraphicsItem *item, q->items()) {
+ if (!item->parentItem()) {
+ // Resolvefont for an item is a noop operation, but
+ // every item can be a widget, or can have a widget
+ // childre.
+ item->d_ptr->resolveFont(font.resolve());
+ }
+ }
+
+ // Send the scene a FontChange event.
+ QEvent event(QEvent::FontChange);
+ QApplication::sendEvent(q, &event);
+}
+
+/*!
+ \internal
+
+ Set the palette and propagate the changes if the palette is different from
+ the current palette.
+*/
+void QGraphicsScenePrivate::setPalette_helper(const QPalette &palette)
+{
+ if (this->palette == palette && this->palette.resolve() == palette.resolve())
+ return;
+ updatePalette(palette);
+}
+
+/*!
+ \internal
+
+ Resolve the scene's palette against the application palette, and propagate
+ the changes too all items in the scene.
+*/
+void QGraphicsScenePrivate::resolvePalette()
+{
+ QPalette naturalPalette = QApplication::palette();
+ naturalPalette.resolve(0);
+ QPalette resolvedPalette = palette.resolve(naturalPalette);
+ updatePalette(resolvedPalette);
+}
+
+/*!
+ \internal
+
+ Update the palette, and whether or not it has changed, reresolve all
+ palettes in the scene.
+*/
+void QGraphicsScenePrivate::updatePalette(const QPalette &palette)
+{
+ Q_Q(QGraphicsScene);
+
+ // Update local palette setting.
+ this->palette = palette;
+
+ // Resolve the palettes of all top-level widget items, or widget items
+ // whose parent is not a widget.
+ foreach (QGraphicsItem *item, q->items()) {
+ if (!item->parentItem()) {
+ // Resolvefont for an item is a noop operation, but
+ // every item can be a widget, or can have a widget
+ // childre.
+ item->d_ptr->resolvePalette(palette.resolve());
+ }
+ }
+
+ // Send the scene a PaletteChange event.
+ QEvent event(QEvent::PaletteChange);
+ QApplication::sendEvent(q, &event);
+}
+
+/*!
+ Constructs a QGraphicsScene object. The \a parent parameter is
+ passed to QObject's constructor.
+*/
+QGraphicsScene::QGraphicsScene(QObject *parent)
+ : QObject(*new QGraphicsScenePrivate, parent)
+{
+ d_func()->init();
+}
+
+/*!
+ Constructs a QGraphicsScene object, using \a sceneRect for its
+ scene rectangle. The \a parent parameter is passed to QObject's
+ constructor.
+
+ \sa sceneRect
+*/
+QGraphicsScene::QGraphicsScene(const QRectF &sceneRect, QObject *parent)
+ : QObject(*new QGraphicsScenePrivate, parent)
+{
+ d_func()->init();
+ setSceneRect(sceneRect);
+}
+
+/*!
+ Constructs a QGraphicsScene object, using the rectangle specified
+ by (\a x, \a y), and the given \a width and \a height for its
+ scene rectangle. The \a parent parameter is passed to QObject's
+ constructor.
+
+ \sa sceneRect
+*/
+QGraphicsScene::QGraphicsScene(qreal x, qreal y, qreal width, qreal height, QObject *parent)
+ : QObject(*new QGraphicsScenePrivate, parent)
+{
+ d_func()->init();
+ setSceneRect(x, y, width, height);
+}
+
+/*!
+ Removes and deletes all items from the scene object
+ before destroying the scene object. The scene object
+ is removed from the application's global scene list,
+ and it is removed from all associated views.
+*/
+QGraphicsScene::~QGraphicsScene()
+{
+ Q_D(QGraphicsScene);
+
+ // Remove this scene from qApp's global scene list.
+ qApp->d_func()->scene_list.removeAll(this);
+
+ clear();
+
+ // Remove this scene from all associated views.
+ for (int j = 0; j < d->views.size(); ++j)
+ d->views.at(j)->setScene(0);
+}
+
+/*!
+ \property QGraphicsScene::sceneRect
+ \brief the scene rectangle; the bounding rectangle of the scene
+
+ The scene rectangle defines the extent of the scene. It is
+ primarily used by QGraphicsView to determine the view's default
+ scrollable area, and by QGraphicsScene to manage item indexing.
+
+ If unset, or if set to a null QRectF, sceneRect() will return the largest
+ bounding rect of all items on the scene since the scene was created (i.e.,
+ a rectangle that grows when items are added to or moved in the scene, but
+ never shrinks).
+
+ \sa width(), height(), QGraphicsView::sceneRect
+*/
+QRectF QGraphicsScene::sceneRect() const
+{
+ Q_D(const QGraphicsScene);
+ if (d->hasSceneRect)
+ return d->sceneRect;
+
+ if (d->dirtyGrowingItemsBoundingRect) {
+ // Lazily update the growing items bounding rect
+ QGraphicsScenePrivate *thatd = const_cast<QGraphicsScenePrivate *>(d);
+ QRectF oldGrowingBoundingRect = thatd->growingItemsBoundingRect;
+ thatd->growingItemsBoundingRect |= itemsBoundingRect();
+ thatd->dirtyGrowingItemsBoundingRect = false;
+ if (oldGrowingBoundingRect != thatd->growingItemsBoundingRect)
+ emit const_cast<QGraphicsScene *>(this)->sceneRectChanged(thatd->growingItemsBoundingRect);
+ }
+ return d->growingItemsBoundingRect;
+}
+void QGraphicsScene::setSceneRect(const QRectF &rect)
+{
+ Q_D(QGraphicsScene);
+ if (rect != d->sceneRect) {
+ d->hasSceneRect = !rect.isNull();
+ d->sceneRect = rect;
+ emit sceneRectChanged(d->hasSceneRect ? rect : d->growingItemsBoundingRect);
+ }
+}
+
+/*!
+ \fn qreal QGraphicsScene::width() const
+
+ This convenience function is equivalent to calling sceneRect().width().
+
+ \sa height()
+*/
+
+/*!
+ \fn qreal QGraphicsScene::height() const
+
+ This convenience function is equivalent to calling \c sceneRect().height().
+
+ \sa width()
+*/
+
+/*!
+ Renders the \a source rect from scene into \a target, using \a painter. This
+ function is useful for capturing the contents of the scene onto a paint
+ device, such as a QImage (e.g., to take a screenshot), or for printing
+ with QPrinter. For example:
+
+ \snippet doc/src/snippets/code/src_gui_graphicsview_qgraphicsscene.cpp 1
+
+ If \a source is a null rect, this function will use sceneRect() to
+ determine what to render. If \a target is a null rect, the dimensions of \a
+ painter's paint device will be used.
+
+ The source rect contents will be transformed according to \a
+ aspectRatioMode to fit into the target rect. By default, the aspect ratio
+ is kept, and \a source is scaled to fit in \a target.
+
+ \sa QGraphicsView::render()
+*/
+void QGraphicsScene::render(QPainter *painter, const QRectF &target, const QRectF &source,
+ Qt::AspectRatioMode aspectRatioMode)
+{
+ // ### Switch to using the recursive rendering algorithm instead.
+
+ // Default source rect = scene rect
+ QRectF sourceRect = source;
+ if (sourceRect.isNull())
+ sourceRect = sceneRect();
+
+ // Default target rect = device rect
+ QRectF targetRect = target;
+ if (targetRect.isNull()) {
+ if (painter->device()->devType() == QInternal::Picture)
+ targetRect = sourceRect;
+ else
+ targetRect.setRect(0, 0, painter->device()->width(), painter->device()->height());
+ }
+
+ // Find the ideal x / y scaling ratio to fit \a source into \a target.
+ qreal xratio = targetRect.width() / sourceRect.width();
+ qreal yratio = targetRect.height() / sourceRect.height();
+
+ // Scale according to the aspect ratio mode.
+ switch (aspectRatioMode) {
+ case Qt::KeepAspectRatio:
+ xratio = yratio = qMin(xratio, yratio);
+ break;
+ case Qt::KeepAspectRatioByExpanding:
+ xratio = yratio = qMax(xratio, yratio);
+ break;
+ case Qt::IgnoreAspectRatio:
+ break;
+ }
+
+ // Find all items to draw, and reverse the list (we want to draw
+ // in reverse order).
+ QList<QGraphicsItem *> itemList = items(sourceRect, Qt::IntersectsItemBoundingRect);
+ QGraphicsItem **itemArray = new QGraphicsItem *[itemList.size()];
+ int numItems = itemList.size();
+ for (int i = 0; i < numItems; ++i)
+ itemArray[numItems - i - 1] = itemList.at(i);
+ itemList.clear();
+
+ painter->save();
+
+ // Transform the painter.
+ painter->setClipRect(targetRect, Qt::IntersectClip);
+ QTransform painterTransform;
+ painterTransform *= QTransform()
+ .translate(targetRect.left(), targetRect.top())
+ .scale(xratio, yratio)
+ .translate(-sourceRect.left(), -sourceRect.top());
+ painter->setWorldTransform(painterTransform, true);
+
+ // Two unit vectors.
+ QLineF v1(0, 0, 1, 0);
+ QLineF v2(0, 0, 0, 1);
+
+ // Generate the style options
+ QStyleOptionGraphicsItem *styleOptionArray = new QStyleOptionGraphicsItem[numItems];
+ for (int i = 0; i < numItems; ++i)
+ itemArray[i]->d_ptr->initStyleOption(&styleOptionArray[i], painterTransform, targetRect.toRect());
+
+ // Render the scene.
+ drawBackground(painter, sourceRect);
+ drawItems(painter, numItems, itemArray, styleOptionArray);
+ drawForeground(painter, sourceRect);
+
+ delete [] itemArray;
+ delete [] styleOptionArray;
+
+ painter->restore();
+}
+
+/*!
+ \property QGraphicsScene::itemIndexMethod
+ \brief the item indexing method.
+
+ QGraphicsScene applies an indexing algorithm to the scene, to speed up
+ item discovery functions like items() and itemAt(). Indexing is most
+ efficient for static scenes (i.e., where items don't move around). For
+ dynamic scenes, or scenes with many animated items, the index bookkeeping
+ can outweight the fast lookup speeds.
+
+ For the common case, the default index method BspTreeIndex works fine. If
+ your scene uses many animations and you are experiencing slowness, you can
+ disable indexing by calling \c setItemIndexMethod(NoIndex).
+
+ \sa bspTreeDepth
+*/
+QGraphicsScene::ItemIndexMethod QGraphicsScene::itemIndexMethod() const
+{
+ Q_D(const QGraphicsScene);
+ return d->indexMethod;
+}
+void QGraphicsScene::setItemIndexMethod(ItemIndexMethod method)
+{
+ Q_D(QGraphicsScene);
+ if (d->indexMethod == method)
+ return;
+
+ d->indexMethod = method;
+
+ QList<QGraphicsItem *> oldItems = d->index->items(Qt::DescendingOrder);
+ delete d->index;
+ if (method == BspTreeIndex)
+ d->index = new QGraphicsSceneBspTreeIndex(this);
+ else
+ d->index = new QGraphicsSceneLinearIndex(this);
+ for (int i = oldItems.size() - 1; i >= 0; --i)
+ d->index->addItem(oldItems.at(i));
+}
+
+/*!
+ \property QGraphicsScene::bspTreeDepth
+ \brief the depth of QGraphicsScene's BSP index tree
+ \since 4.3
+
+ This property has no effect when NoIndex is used.
+
+ This value determines the depth of QGraphicsScene's BSP tree. The depth
+ directly affects QGraphicsScene's performance and memory usage; the latter
+ growing exponentially with the depth of the tree. With an optimal tree
+ depth, QGraphicsScene can instantly determine the locality of items, even
+ for scenes with thousands or millions of items. This also greatly improves
+ rendering performance.
+
+ By default, the value is 0, in which case Qt will guess a reasonable
+ default depth based on the size, location and number of items in the
+ scene. If these parameters change frequently, however, you may experience
+ slowdowns as QGraphicsScene retunes the depth internally. You can avoid
+ potential slowdowns by fixating the tree depth through setting this
+ property.
+
+ The depth of the tree and the size of the scene rectangle decide the
+ granularity of the scene's partitioning. The size of each scene segment is
+ determined by the following algorithm:
+
+ \snippet doc/src/snippets/code/src_gui_graphicsview_qgraphicsscene.cpp 2
+
+ The BSP tree has an optimal size when each segment contains between 0 and
+ 10 items.
+
+ \sa itemIndexMethod
+*/
+int QGraphicsScene::bspTreeDepth() const
+{
+ Q_D(const QGraphicsScene);
+ QGraphicsSceneBspTreeIndex *bspTree = qobject_cast<QGraphicsSceneBspTreeIndex *>(d->index);
+ return bspTree ? bspTree->bspTreeDepth() : 0;
+}
+void QGraphicsScene::setBspTreeDepth(int depth)
+{
+ Q_D(QGraphicsScene);
+ if (depth < 0) {
+ qWarning("QGraphicsScene::setBspTreeDepth: invalid depth %d ignored; must be >= 0", depth);
+ return;
+ }
+
+ QGraphicsSceneBspTreeIndex *bspTree = qobject_cast<QGraphicsSceneBspTreeIndex *>(d->index);
+ if (!bspTree) {
+ qWarning("QGraphicsScene::setBspTreeDepth: can not apply if indexing method is not BSP");
+ return;
+ }
+ bspTree->setBspTreeDepth(depth);
+}
+
+/*!
+ \property QGraphicsScene::sortCacheEnabled
+ \brief whether sort caching is enabled
+ \since 4.5
+ \obsolete
+
+ Since Qt 4.6, this property has no effect.
+*/
+bool QGraphicsScene::isSortCacheEnabled() const
+{
+ Q_D(const QGraphicsScene);
+ return d->sortCacheEnabled;
+}
+void QGraphicsScene::setSortCacheEnabled(bool enabled)
+{
+ Q_D(QGraphicsScene);
+ if (d->sortCacheEnabled == enabled)
+ return;
+ d->sortCacheEnabled = enabled;
+}
+
+/*!
+ Calculates and returns the bounding rect of all items on the scene. This
+ function works by iterating over all items, and because if this, it can
+ be slow for large scenes.
+
+ \sa sceneRect()
+*/
+QRectF QGraphicsScene::itemsBoundingRect() const
+{
+ // Does not take untransformable items into account.
+ QRectF boundingRect;
+ foreach (QGraphicsItem *item, items())
+ boundingRect |= item->sceneBoundingRect();
+ return boundingRect;
+}
+
+/*!
+ Returns a list of all items in the scene in descending stacking order.
+
+ \sa addItem(), removeItem(), {QGraphicsItem#Sorting}{Sorting}
+*/
+QList<QGraphicsItem *> QGraphicsScene::items() const
+{
+ Q_D(const QGraphicsScene);
+ return d->index->items(Qt::DescendingOrder);
+}
+
+/*!
+ Returns an ordered list of all items on the scene. \a order decides the
+ stacking order.
+
+ \sa addItem(), removeItem(), {QGraphicsItem#Sorting}{Sorting}
+*/
+QList<QGraphicsItem *> QGraphicsScene::items(Qt::SortOrder order) const
+{
+ Q_D(const QGraphicsScene);
+ return d->index->items(order);
+}
+
+/*!
+ \obsolete
+
+ Returns all visible items at position \a pos in the scene. The items are
+ listed in descending stacking order (i.e., the first item in the list is the
+ top-most item, and the last item is the bottom-most item).
+
+ This function is deprecated and returns incorrect results if the scene
+ contains items that ignore transformations. Use the overload that takes
+ a QTransform instead.
+
+ \sa itemAt(), {QGraphicsItem#Sorting}{Sorting}
+*/
+QList<QGraphicsItem *> QGraphicsScene::items(const QPointF &pos) const
+{
+ Q_D(const QGraphicsScene);
+ return d->index->items(pos, Qt::IntersectsItemShape, Qt::DescendingOrder);
+}
+
+/*!
+ \overload
+ \obsolete
+
+ Returns all visible items that, depending on \a mode, are either inside or
+ intersect with the specified \a rectangle.
+
+ The default value for \a mode is Qt::IntersectsItemShape; all items whose
+ exact shape intersects with or is contained by \a rectangle are returned.
+
+ This function is deprecated and returns incorrect results if the scene
+ contains items that ignore transformations. Use the overload that takes
+ a QTransform instead.
+
+ \sa itemAt(), {QGraphicsItem#Sorting}{Sorting}
+*/
+QList<QGraphicsItem *> QGraphicsScene::items(const QRectF &rectangle, Qt::ItemSelectionMode mode) const
+{
+ Q_D(const QGraphicsScene);
+ return d->index->items(rectangle, mode, Qt::DescendingOrder);
+}
+
+/*!
+ \fn QList<QGraphicsItem *> QGraphicsScene::items(qreal x, qreal y, qreal w, qreal h, Qt::ItemSelectionMode mode) const
+ \obsolete
+ \since 4.3
+
+ This convenience function is equivalent to calling items(QRectF(\a x, \a y, \a w, \a h), \a mode).
+
+ This function is deprecated and returns incorrect results if the scene
+ contains items that ignore transformations. Use the overload that takes
+ a QTransform instead.
+*/
+
+/*!
+ \fn QList<QGraphicsItem *> QGraphicsScene::items(qreal x, qreal y, qreal w, qreal h, Qt::ItemSelectionMode mode, Qt::SortOrder order, const QTransform &deviceTransform) const
+ \overload
+ \since 4.6
+
+ \brief Returns all visible items that, depending on \a mode, are
+ either inside or intersect with the rectangle defined by \a x, \a y,
+ \a w and \a h, in a list sorted using \a order.
+
+ \a deviceTransform is the transformation that applies to the view, and needs to
+ be provided if the scene contains items that ignore transformations.
+*/
+
+/*!
+ \fn QList<QGraphicsItem *> QGraphicsScene::items(const QPolygonF &polygon, Qt::ItemSelectionMode mode) const
+ \overload
+ \obsolete
+
+ Returns all visible items that, depending on \a mode, are either inside or
+ intersect with the polygon \a polygon.
+
+ The default value for \a mode is Qt::IntersectsItemShape; all items whose
+ exact shape intersects with or is contained by \a polygon are returned.
+
+ This function is deprecated and returns incorrect results if the scene
+ contains items that ignore transformations. Use the overload that takes
+ a QTransform instead.
+
+ \sa itemAt(), {QGraphicsItem#Sorting}{Sorting}
+*/
+QList<QGraphicsItem *> QGraphicsScene::items(const QPolygonF &polygon, Qt::ItemSelectionMode mode) const
+{
+ Q_D(const QGraphicsScene);
+ return d->index->items(polygon, mode, Qt::DescendingOrder);
+}
+
+/*!
+ \fn QList<QGraphicsItem *> QGraphicsScene::items(const QPainterPath &path, Qt::ItemSelectionMode mode) const
+ \overload
+ \obsolete
+
+ Returns all visible items that, depending on \a path, are either inside or
+ intersect with the path \a path.
+
+ The default value for \a mode is Qt::IntersectsItemShape; all items whose
+ exact shape intersects with or is contained by \a path are returned.
+
+ This function is deprecated and returns incorrect results if the scene
+ contains items that ignore transformations. Use the overload that takes
+ a QTransform instead.
+
+ \sa itemAt(), {QGraphicsItem#Sorting}{Sorting}
+*/
+QList<QGraphicsItem *> QGraphicsScene::items(const QPainterPath &path, Qt::ItemSelectionMode mode) const
+{
+ Q_D(const QGraphicsScene);
+ return d->index->items(path, mode, Qt::DescendingOrder);
+}
+
+/*!
+ \fn QList<QGraphicsItem *> QGraphicsScene::items(const QPointF &pos, Qt::ItemSelectionMode mode, Qt::SortOrder order, const QTransform &deviceTransform) const
+ \since 4.6
+
+ \brief Returns all visible items that, depending on \a mode, are at
+ the specified \a pos in a list sorted using \a order.
+
+ The default value for \a mode is Qt::IntersectsItemShape; all items whose
+ exact shape intersects with \a pos are returned.
+
+ \a deviceTransform is the transformation that applies to the view, and needs to
+ be provided if the scene contains items that ignore transformations.
+
+ \sa itemAt(), {QGraphicsItem#Sorting}{Sorting}
+*/
+QList<QGraphicsItem *> QGraphicsScene::items(const QPointF &pos, Qt::ItemSelectionMode mode,
+ Qt::SortOrder order, const QTransform &deviceTransform) const
+{
+ Q_D(const QGraphicsScene);
+ return d->index->items(pos, mode, order, deviceTransform);
+}
+
+/*!
+ \fn QList<QGraphicsItem *> QGraphicsScene::items(const QRectF &rect, Qt::ItemSelectionMode mode, Qt::SortOrder order, const QTransform &deviceTransform) const
+ \overload
+ \since 4.6
+
+ \brief Returns all visible items that, depending on \a mode, are
+ either inside or intersect with the specified \a rect and return a
+ list sorted using \a order.
+
+ The default value for \a mode is Qt::IntersectsItemShape; all items whose
+ exact shape intersects with or is contained by \a rect are returned.
+
+ \a deviceTransform is the transformation that applies to the view, and needs to
+ be provided if the scene contains items that ignore transformations.
+
+ \sa itemAt(), {QGraphicsItem#Sorting}{Sorting}
+*/
+QList<QGraphicsItem *> QGraphicsScene::items(const QRectF &rect, Qt::ItemSelectionMode mode,
+ Qt::SortOrder order, const QTransform &deviceTransform) const
+{
+ Q_D(const QGraphicsScene);
+ return d->index->items(rect, mode, order, deviceTransform);
+}
+
+/*!
+ \fn QList<QGraphicsItem *> QGraphicsScene::items(const QPolygonF &polygon, Qt::ItemSelectionMode mode, Qt::SortOrder order, const QTransform &deviceTransform) const
+ \overload
+ \since 4.6
+
+ \brief Returns all visible items that, depending on \a mode, are
+ either inside or intersect with the specified \a polygon and return
+ a list sorted using \a order.
+
+ The default value for \a mode is Qt::IntersectsItemShape; all items whose
+ exact shape intersects with or is contained by \a polygon are returned.
+
+ \a deviceTransform is the transformation that applies to the view, and needs to
+ be provided if the scene contains items that ignore transformations.
+
+ \sa itemAt(), {QGraphicsItem#Sorting}{Sorting}
+*/
+QList<QGraphicsItem *> QGraphicsScene::items(const QPolygonF &polygon, Qt::ItemSelectionMode mode,
+ Qt::SortOrder order, const QTransform &deviceTransform) const
+{
+ Q_D(const QGraphicsScene);
+ return d->index->items(polygon, mode, order, deviceTransform);
+}
+
+/*!
+ \fn QList<QGraphicsItem *> QGraphicsScene::items(const QPainterPath &path, Qt::ItemSelectionMode mode, Qt::SortOrder order, const QTransform &deviceTransform) const
+ \overload
+ \since 4.6
+
+ \brief Returns all visible items that, depending on \a mode, are
+ either inside or intersect with the specified \a path and return a
+ list sorted using \a order.
+
+ The default value for \a mode is Qt::IntersectsItemShape; all items whose
+ exact shape intersects with or is contained by \a path are returned.
+
+ \a deviceTransform is the transformation that applies to the view, and needs to
+ be provided if the scene contains items that ignore transformations.
+
+ \sa itemAt(), {QGraphicsItem#Sorting}{Sorting}
+*/
+QList<QGraphicsItem *> QGraphicsScene::items(const QPainterPath &path, Qt::ItemSelectionMode mode,
+ Qt::SortOrder order, const QTransform &deviceTransform) const
+{
+ Q_D(const QGraphicsScene);
+ return d->index->items(path, mode, order, deviceTransform);
+}
+
+/*!
+ Returns a list of all items that collide with \a item. Collisions are
+ determined by calling QGraphicsItem::collidesWithItem(); the collision
+ detection is determined by \a mode. By default, all items whose shape
+ intersects \a item or is contained inside \a item's shape are returned.
+
+ The items are returned in descending stacking order (i.e., the first item
+ in the list is the uppermost item, and the last item is the lowermost
+ item).
+
+ \sa items(), itemAt(), QGraphicsItem::collidesWithItem(), {QGraphicsItem#Sorting}{Sorting}
+*/
+QList<QGraphicsItem *> QGraphicsScene::collidingItems(const QGraphicsItem *item,
+ Qt::ItemSelectionMode mode) const
+{
+ Q_D(const QGraphicsScene);
+ if (!item) {
+ qWarning("QGraphicsScene::collidingItems: cannot find collisions for null item");
+ return QList<QGraphicsItem *>();
+ }
+
+ // Does not support ItemIgnoresTransformations.
+ QList<QGraphicsItem *> tmp;
+ foreach (QGraphicsItem *itemInVicinity, d->index->estimateItems(item->sceneBoundingRect(), Qt::DescendingOrder)) {
+ if (item != itemInVicinity && item->collidesWithItem(itemInVicinity, mode))
+ tmp << itemInVicinity;
+ }
+ return tmp;
+}
+
+/*!
+ \overload
+ \obsolete
+
+ Returns the topmost visible item at the specified \a position, or 0 if
+ there are no items at this position.
+
+ This function is deprecated and returns incorrect results if the scene
+ contains items that ignore transformations. Use the overload that takes
+ a QTransform instead.
+
+ \sa items(), collidingItems(), {QGraphicsItem#Sorting}{Sorting}
+*/
+QGraphicsItem *QGraphicsScene::itemAt(const QPointF &position) const
+{
+ QList<QGraphicsItem *> itemsAtPoint = items(position);
+ return itemsAtPoint.isEmpty() ? 0 : itemsAtPoint.first();
+}
+
+/*!
+ \since 4.6
+
+ Returns the topmost visible item at the specified \a position, or 0
+ if there are no items at this position.
+
+ \a deviceTransform is the transformation that applies to the view, and needs to
+ be provided if the scene contains items that ignore transformations.
+
+ \sa items(), collidingItems(), {QGraphicsItem#Sorting}{Sorting}
+*/
+QGraphicsItem *QGraphicsScene::itemAt(const QPointF &position, const QTransform &deviceTransform) const
+{
+ QList<QGraphicsItem *> itemsAtPoint = items(position, Qt::IntersectsItemShape,
+ Qt::DescendingOrder, deviceTransform);
+ return itemsAtPoint.isEmpty() ? 0 : itemsAtPoint.first();
+}
+
+/*!
+ \fn QGraphicsScene::itemAt(qreal x, qreal y, const QTransform &deviceTransform) const
+ \overload
+ \since 4.6
+
+ Returns the topmost item at the position specified by (\a x, \a
+ y), or 0 if there are no items at this position.
+
+ \a deviceTransform is the transformation that applies to the view, and needs to
+ be provided if the scene contains items that ignore transformations.
+
+ This convenience function is equivalent to calling \c
+ {itemAt(QPointF(x, y), deviceTransform)}.
+*/
+
+/*!
+ \fn QGraphicsScene::itemAt(qreal x, qreal y) const
+ \overload
+ \obsolete
+
+ Returns the topmost item at the position specified by (\a x, \a
+ y), or 0 if there are no items at this position.
+
+ This convenience function is equivalent to calling \c
+ {itemAt(QPointF(x, y))}.
+
+ This function is deprecated and returns incorrect results if the scene
+ contains items that ignore transformations. Use the overload that takes
+ a QTransform instead.
+*/
+
+/*!
+ Returns a list of all currently selected items. The items are
+ returned in no particular order.
+
+ \sa setSelectionArea()
+*/
+QList<QGraphicsItem *> QGraphicsScene::selectedItems() const
+{
+ Q_D(const QGraphicsScene);
+
+ // Optimization: Lazily removes items that are not selected.
+ QGraphicsScene *that = const_cast<QGraphicsScene *>(this);
+ QSet<QGraphicsItem *> actuallySelectedSet;
+ foreach (QGraphicsItem *item, that->d_func()->selectedItems) {
+ if (item->isSelected())
+ actuallySelectedSet << item;
+ }
+
+ that->d_func()->selectedItems = actuallySelectedSet;
+
+ return d->selectedItems.values();
+}
+
+/*!
+ Returns the selection area that was previously set with
+ setSelectionArea(), or an empty QPainterPath if no selection area has been
+ set.
+
+ \sa setSelectionArea()
+*/
+QPainterPath QGraphicsScene::selectionArea() const
+{
+ Q_D(const QGraphicsScene);
+ return d->selectionArea;
+}
+
+/*!
+ \since 4.6
+
+ Sets the selection area to \a path. All items within this area are
+ immediately selected, and all items outside are unselected. You can get
+ the list of all selected items by calling selectedItems().
+
+ \a deviceTransform is the transformation that applies to the view, and needs to
+ be provided if the scene contains items that ignore transformations.
+
+ For an item to be selected, it must be marked as \e selectable
+ (QGraphicsItem::ItemIsSelectable).
+
+ \sa clearSelection(), selectionArea()
+*/
+void QGraphicsScene::setSelectionArea(const QPainterPath &path, const QTransform &deviceTransform)
+{
+ setSelectionArea(path, Qt::IntersectsItemShape, deviceTransform);
+}
+
+/*!
+ \obsolete
+ \overload
+
+ Sets the selection area to \a path.
+
+ This function is deprecated and leads to incorrect results if the scene
+ contains items that ignore transformations. Use the overload that takes
+ a QTransform instead.
+*/
+void QGraphicsScene::setSelectionArea(const QPainterPath &path)
+{
+ setSelectionArea(path, Qt::IntersectsItemShape, QTransform());
+}
+
+/*!
+ \obsolete
+ \overload
+ \since 4.3
+
+ Sets the selection area to \a path using \a mode to determine if items are
+ included in the selection area.
+
+ \sa clearSelection(), selectionArea()
+*/
+void QGraphicsScene::setSelectionArea(const QPainterPath &path, Qt::ItemSelectionMode mode)
+{
+ setSelectionArea(path, mode, QTransform());
+}
+
+/*!
+ \overload
+ \since 4.6
+
+ Sets the selection area to \a path using \a mode to determine if items are
+ included in the selection area.
+
+ \a deviceTransform is the transformation that applies to the view, and needs to
+ be provided if the scene contains items that ignore transformations.
+
+ \sa clearSelection(), selectionArea()
+*/
+void QGraphicsScene::setSelectionArea(const QPainterPath &path, Qt::ItemSelectionMode mode,
+ const QTransform &deviceTransform)
+{
+ Q_D(QGraphicsScene);
+
+ // Note: with boolean path operations, we can improve performance here
+ // quite a lot by "growing" the old path instead of replacing it. That
+ // allows us to only check the intersect area for changes, instead of
+ // reevaluating the whole path over again.
+ d->selectionArea = path;
+
+ QSet<QGraphicsItem *> unselectItems = d->selectedItems;
+
+ // Disable emitting selectionChanged() for individual items.
+ ++d->selectionChanging;
+ bool changed = false;
+
+ // Set all items in path to selected.
+ foreach (QGraphicsItem *item, items(path, mode, Qt::DescendingOrder, deviceTransform)) {
+ if (item->flags() & QGraphicsItem::ItemIsSelectable) {
+ if (!item->isSelected())
+ changed = true;
+ unselectItems.remove(item);
+ item->setSelected(true);
+ }
+ }
+
+ // Unselect all items outside path.
+ foreach (QGraphicsItem *item, unselectItems) {
+ item->setSelected(false);
+ changed = true;
+ }
+
+ // Reenable emitting selectionChanged() for individual items.
+ --d->selectionChanging;
+
+ if (!d->selectionChanging && changed)
+ emit selectionChanged();
+}
+
+/*!
+ Clears the current selection.
+
+ \sa setSelectionArea(), selectedItems()
+*/
+void QGraphicsScene::clearSelection()
+{
+ Q_D(QGraphicsScene);
+
+ // Disable emitting selectionChanged
+ ++d->selectionChanging;
+ bool changed = !d->selectedItems.isEmpty();
+
+ foreach (QGraphicsItem *item, d->selectedItems)
+ item->setSelected(false);
+ d->selectedItems.clear();
+
+ // Reenable emitting selectionChanged() for individual items.
+ --d->selectionChanging;
+
+ if (!d->selectionChanging && changed)
+ emit selectionChanged();
+}
+
+/*!
+ \since 4.4
+
+ Removes and deletes all items from the scene, but otherwise leaves the
+ state of the scene unchanged.
+
+ \sa addItem()
+*/
+void QGraphicsScene::clear()
+{
+ Q_D(QGraphicsScene);
+ // NB! We have to clear the index before deleting items; otherwise the
+ // index might try to access dangling item pointers.
+ d->index->clear();
+ // NB! QGraphicsScenePrivate::unregisterTopLevelItem() removes items
+ while (!d->topLevelItems.isEmpty())
+ delete d->topLevelItems.first();
+ Q_ASSERT(d->topLevelItems.isEmpty());
+ d->lastItemCount = 0;
+ d->allItemsIgnoreHoverEvents = true;
+ d->allItemsUseDefaultCursor = true;
+ d->allItemsIgnoreTouchEvents = true;
+}
+
+/*!
+ Groups all items in \a items into a new QGraphicsItemGroup, and returns a
+ pointer to the group. The group is created with the common ancestor of \a
+ items as its parent, and with position (0, 0). The items are all
+ reparented to the group, and their positions and transformations are
+ mapped to the group. If \a items is empty, this function will return an
+ empty top-level QGraphicsItemGroup.
+
+ QGraphicsScene has ownership of the group item; you do not need to delete
+ it. To dismantle (ungroup) a group, call destroyItemGroup().
+
+ \sa destroyItemGroup(), QGraphicsItemGroup::addToGroup()
+*/
+QGraphicsItemGroup *QGraphicsScene::createItemGroup(const QList<QGraphicsItem *> &items)
+{
+ // Build a list of the first item's ancestors
+ QList<QGraphicsItem *> ancestors;
+ int n = 0;
+ if (!items.isEmpty()) {
+ QGraphicsItem *parent = items.at(n++);
+ while ((parent = parent->parentItem()))
+ ancestors.append(parent);
+ }
+
+ // Find the common ancestor for all items
+ QGraphicsItem *commonAncestor = 0;
+ if (!ancestors.isEmpty()) {
+ while (n < items.size()) {
+ int commonIndex = -1;
+ QGraphicsItem *parent = items.at(n++);
+ do {
+ int index = ancestors.indexOf(parent, qMax(0, commonIndex));
+ if (index != -1) {
+ commonIndex = index;
+ break;
+ }
+ } while ((parent = parent->parentItem()));
+
+ if (commonIndex == -1) {
+ commonAncestor = 0;
+ break;
+ }
+
+ commonAncestor = ancestors.at(commonIndex);
+ }
+ }
+
+ // Create a new group at that level
+ QGraphicsItemGroup *group = new QGraphicsItemGroup(commonAncestor);
+ if (!commonAncestor)
+ addItem(group);
+ foreach (QGraphicsItem *item, items)
+ group->addToGroup(item);
+ return group;
+}
+
+/*!
+ Reparents all items in \a group to \a group's parent item, then removes \a
+ group from the scene, and finally deletes it. The items' positions and
+ transformations are mapped from the group to the group's parent.
+
+ \sa createItemGroup(), QGraphicsItemGroup::removeFromGroup()
+*/
+void QGraphicsScene::destroyItemGroup(QGraphicsItemGroup *group)
+{
+ foreach (QGraphicsItem *item, group->children())
+ group->removeFromGroup(item);
+ removeItem(group);
+ delete group;
+}
+
+/*!
+ Adds or moves the \a item and all its childen to this scene.
+ This scene takes ownership of the \a item.
+
+ If the item is visible (i.e., QGraphicsItem::isVisible() returns
+ true), QGraphicsScene will emit changed() once control goes back
+ to the event loop.
+
+ If the item is already in a different scene, it will first be
+ removed from its old scene, and then added to this scene as a
+ top-level.
+
+ QGraphicsScene will send ItemSceneChange notifications to \a item
+ while it is added to the scene. If item does not currently belong
+ to a scene, only one notification is sent. If it does belong to
+ scene already (i.e., it is moved to this scene), QGraphicsScene
+ will send an addition notification as the item is removed from its
+ previous scene.
+
+ If the item is a panel, the scene is active, and there is no
+ active panel in the scene, then the item will be activated.
+
+ \sa removeItem(), addEllipse(), addLine(), addPath(), addPixmap(),
+ addRect(), addText(), addWidget(), {QGraphicsItem#Sorting}{Sorting}
+*/
+void QGraphicsScene::addItem(QGraphicsItem *item)
+{
+ Q_D(QGraphicsScene);
+ if (!item) {
+ qWarning("QGraphicsScene::addItem: cannot add null item");
+ return;
+ }
+ if (item->d_ptr->scene == this) {
+ qWarning("QGraphicsScene::addItem: item has already been added to this scene");
+ return;
+ }
+ // Remove this item from its existing scene
+ if (QGraphicsScene *oldScene = item->d_ptr->scene)
+ oldScene->removeItem(item);
+
+ // Notify the item that its scene is changing, and allow the item to
+ // react.
+ const QVariant newSceneVariant(item->itemChange(QGraphicsItem::ItemSceneChange,
+ QVariant::fromValue<QGraphicsScene *>(this)));
+ QGraphicsScene *targetScene = qvariant_cast<QGraphicsScene *>(newSceneVariant);
+ if (targetScene != this) {
+ if (targetScene && item->d_ptr->scene != targetScene)
+ targetScene->addItem(item);
+ return;
+ }
+
+ // QDeclarativeItems do not rely on initial itemChanged message, as the componentComplete
+ // function allows far more opportunity for delayed-construction optimization.
+ if (!item->d_ptr->isDeclarativeItem) {
+ if (d->unpolishedItems.isEmpty()) {
+ QMetaMethod method = metaObject()->method(d->polishItemsIndex);
+ method.invoke(this, Qt::QueuedConnection);
+ }
+ d->unpolishedItems.append(item);
+ item->d_ptr->pendingPolish = true;
+ }
+
+ // Detach this item from its parent if the parent's scene is different
+ // from this scene.
+ if (QGraphicsItem *itemParent = item->d_ptr->parent) {
+ if (itemParent->d_ptr->scene != this)
+ item->setParentItem(0);
+ }
+
+ // Add the item to this scene
+ item->d_func()->scene = targetScene;
+
+ // Add the item in the index
+ d->index->addItem(item);
+
+ // Add to list of toplevels if this item is a toplevel.
+ if (!item->d_ptr->parent)
+ d->registerTopLevelItem(item);
+
+ // Add to list of items that require an update. We cannot assume that the
+ // item is fully constructed, so calling item->update() can lead to a pure
+ // virtual function call to boundingRect().
+ d->markDirty(item);
+ d->dirtyGrowingItemsBoundingRect = true;
+
+ // Disable selectionChanged() for individual items
+ ++d->selectionChanging;
+ int oldSelectedItemSize = d->selectedItems.size();
+
+ // Enable mouse tracking if the item accepts hover events or has a cursor set.
+ if (d->allItemsIgnoreHoverEvents && d->itemAcceptsHoverEvents_helper(item)) {
+ d->allItemsIgnoreHoverEvents = false;
+ d->enableMouseTrackingOnViews();
+ }
+#ifndef QT_NO_CURSOR
+ if (d->allItemsUseDefaultCursor && item->d_ptr->hasCursor) {
+ d->allItemsUseDefaultCursor = false;
+ if (d->allItemsIgnoreHoverEvents) // already enabled otherwise
+ d->enableMouseTrackingOnViews();
+ }
+#endif //QT_NO_CURSOR
+
+ // Enable touch events if the item accepts touch events.
+ if (d->allItemsIgnoreTouchEvents && item->d_ptr->acceptTouchEvents) {
+ d->allItemsIgnoreTouchEvents = false;
+ d->enableTouchEventsOnViews();
+ }
+
+#ifndef QT_NO_GESTURES
+ foreach (Qt::GestureType gesture, item->d_ptr->gestureContext.keys())
+ d->grabGesture(item, gesture);
+#endif
+
+ // Update selection lists
+ if (item->isSelected())
+ d->selectedItems << item;
+ if (item->isWidget() && item->isVisible() && static_cast<QGraphicsWidget *>(item)->windowType() == Qt::Popup)
+ d->addPopup(static_cast<QGraphicsWidget *>(item));
+ if (item->isPanel() && item->isVisible() && item->panelModality() != QGraphicsItem::NonModal)
+ d->enterModal(item);
+
+ // Update creation order focus chain. Make sure to leave the widget's
+ // internal tab order intact.
+ if (item->isWidget()) {
+ QGraphicsWidget *widget = static_cast<QGraphicsWidget *>(item);
+ if (!d->tabFocusFirst) {
+ // No first tab focus widget - make this the first tab focus
+ // widget.
+ d->tabFocusFirst = widget;
+ } else if (!widget->parentWidget()) {
+ // Adding a widget that is not part of a tab focus chain.
+ QGraphicsWidget *last = d->tabFocusFirst->d_func()->focusPrev;
+ QGraphicsWidget *lastNew = widget->d_func()->focusPrev;
+ last->d_func()->focusNext = widget;
+ widget->d_func()->focusPrev = last;
+ d->tabFocusFirst->d_func()->focusPrev = lastNew;
+ lastNew->d_func()->focusNext = d->tabFocusFirst;
+ }
+ }
+
+ // Add all children recursively
+ item->d_ptr->ensureSortedChildren();
+ for (int i = 0; i < item->d_ptr->children.size(); ++i)
+ addItem(item->d_ptr->children.at(i));
+
+ // Resolve font and palette.
+ item->d_ptr->resolveFont(d->font.resolve());
+ item->d_ptr->resolvePalette(d->palette.resolve());
+
+
+ // Reenable selectionChanged() for individual items
+ --d->selectionChanging;
+ if (!d->selectionChanging && d->selectedItems.size() != oldSelectedItemSize)
+ emit selectionChanged();
+
+ // Deliver post-change notification
+ item->itemChange(QGraphicsItem::ItemSceneHasChanged, newSceneVariant);
+
+ // Update explicit activation
+ bool autoActivate = true;
+ if (!d->childExplicitActivation && item->d_ptr->explicitActivate)
+ d->childExplicitActivation = item->d_ptr->wantsActive ? 1 : 2;
+ if (d->childExplicitActivation && item->isPanel()) {
+ if (d->childExplicitActivation == 1)
+ setActivePanel(item);
+ else
+ autoActivate = false;
+ d->childExplicitActivation = 0;
+ } else if (!item->d_ptr->parent) {
+ d->childExplicitActivation = 0;
+ }
+
+ // Auto-activate this item's panel if nothing else has been activated
+ if (autoActivate) {
+ if (!d->lastActivePanel && !d->activePanel && item->isPanel()) {
+ if (isActive())
+ setActivePanel(item);
+ else
+ d->lastActivePanel = item;
+ }
+ }
+
+ if (item->d_ptr->flags & QGraphicsItem::ItemSendsScenePositionChanges)
+ d->registerScenePosItem(item);
+
+ // Ensure that newly added items that have subfocus set, gain
+ // focus automatically if there isn't a focus item already.
+ if (!d->focusItem && item != d->lastFocusItem && item->focusItem() == item)
+ item->focusItem()->setFocus();
+
+ d->updateInputMethodSensitivityInViews();
+}
+
+/*!
+ Creates and adds an ellipse item to the scene, and returns the item
+ pointer. The geometry of the ellipse is defined by \a rect, and its pen
+ and brush are initialized to \a pen and \a brush.
+
+ Note that the item's geometry is provided in item coordinates, and its
+ position is initialized to (0, 0).
+
+ If the item is visible (i.e., QGraphicsItem::isVisible() returns true),
+ QGraphicsScene will emit changed() once control goes back to the event
+ loop.
+
+ \sa addLine(), addPath(), addPixmap(), addRect(), addText(), addItem(),
+ addWidget()
+*/
+QGraphicsEllipseItem *QGraphicsScene::addEllipse(const QRectF &rect, const QPen &pen, const QBrush &brush)
+{
+ QGraphicsEllipseItem *item = new QGraphicsEllipseItem(rect);
+ item->setPen(pen);
+ item->setBrush(brush);
+ addItem(item);
+ return item;
+}
+
+/*!
+ \fn QGraphicsEllipseItem *QGraphicsScene::addEllipse(qreal x, qreal y, qreal w, qreal h, const QPen &pen, const QBrush &brush)
+ \since 4.3
+
+ This convenience function is equivalent to calling addEllipse(QRectF(\a x,
+ \a y, \a w, \a h), \a pen, \a brush).
+*/
+
+/*!
+ Creates and adds a line item to the scene, and returns the item
+ pointer. The geometry of the line is defined by \a line, and its pen
+ is initialized to \a pen.
+
+ Note that the item's geometry is provided in item coordinates, and its
+ position is initialized to (0, 0).
+
+ If the item is visible (i.e., QGraphicsItem::isVisible() returns true),
+ QGraphicsScene will emit changed() once control goes back to the event
+ loop.
+
+ \sa addEllipse(), addPath(), addPixmap(), addRect(), addText(), addItem(),
+ addWidget()
+*/
+QGraphicsLineItem *QGraphicsScene::addLine(const QLineF &line, const QPen &pen)
+{
+ QGraphicsLineItem *item = new QGraphicsLineItem(line);
+ item->setPen(pen);
+ addItem(item);
+ return item;
+}
+
+/*!
+ \fn QGraphicsLineItem *QGraphicsScene::addLine(qreal x1, qreal y1, qreal x2, qreal y2, const QPen &pen)
+ \since 4.3
+
+ This convenience function is equivalent to calling addLine(QLineF(\a x1,
+ \a y1, \a x2, \a y2), \a pen).
+*/
+
+/*!
+ Creates and adds a path item to the scene, and returns the item
+ pointer. The geometry of the path is defined by \a path, and its pen and
+ brush are initialized to \a pen and \a brush.
+
+ Note that the item's geometry is provided in item coordinates, and its
+ position is initialized to (0, 0).
+
+ If the item is visible (i.e., QGraphicsItem::isVisible() returns true),
+ QGraphicsScene will emit changed() once control goes back to the event
+ loop.
+
+ \sa addEllipse(), addLine(), addPixmap(), addRect(), addText(), addItem(),
+ addWidget()
+*/
+QGraphicsPathItem *QGraphicsScene::addPath(const QPainterPath &path, const QPen &pen, const QBrush &brush)
+{
+ QGraphicsPathItem *item = new QGraphicsPathItem(path);
+ item->setPen(pen);
+ item->setBrush(brush);
+ addItem(item);
+ return item;
+}
+
+/*!
+ Creates and adds a pixmap item to the scene, and returns the item
+ pointer. The pixmap is defined by \a pixmap.
+
+ Note that the item's geometry is provided in item coordinates, and its
+ position is initialized to (0, 0).
+
+ If the item is visible (i.e., QGraphicsItem::isVisible() returns true),
+ QGraphicsScene will emit changed() once control goes back to the event
+ loop.
+
+ \sa addEllipse(), addLine(), addPath(), addRect(), addText(), addItem(),
+ addWidget()
+*/
+QGraphicsPixmapItem *QGraphicsScene::addPixmap(const QPixmap &pixmap)
+{
+ QGraphicsPixmapItem *item = new QGraphicsPixmapItem(pixmap);
+ addItem(item);
+ return item;
+}
+
+/*!
+ Creates and adds a polygon item to the scene, and returns the item
+ pointer. The polygon is defined by \a polygon, and its pen and
+ brush are initialized to \a pen and \a brush.
+
+ Note that the item's geometry is provided in item coordinates, and its
+ position is initialized to (0, 0).
+
+ If the item is visible (i.e., QGraphicsItem::isVisible() returns true),
+ QGraphicsScene will emit changed() once control goes back to the event
+ loop.
+
+ \sa addEllipse(), addLine(), addPath(), addRect(), addText(), addItem(),
+ addWidget()
+*/
+QGraphicsPolygonItem *QGraphicsScene::addPolygon(const QPolygonF &polygon,
+ const QPen &pen, const QBrush &brush)
+{
+ QGraphicsPolygonItem *item = new QGraphicsPolygonItem(polygon);
+ item->setPen(pen);
+ item->setBrush(brush);
+ addItem(item);
+ return item;
+}
+
+/*!
+ Creates and adds a rectangle item to the scene, and returns the item
+ pointer. The geometry of the rectangle is defined by \a rect, and its pen
+ and brush are initialized to \a pen and \a brush.
+
+ Note that the item's geometry is provided in item coordinates, and its
+ position is initialized to (0, 0). For example, if a QRect(50, 50, 100,
+ 100) is added, its top-left corner will be at (50, 50) relative to the
+ origin in the items coordinate system.
+
+ If the item is visible (i.e., QGraphicsItem::isVisible() returns true),
+ QGraphicsScene will emit changed() once control goes back to the event
+ loop.
+
+ \sa addEllipse(), addLine(), addPixmap(), addPixmap(), addText(),
+ addItem(), addWidget()
+*/
+QGraphicsRectItem *QGraphicsScene::addRect(const QRectF &rect, const QPen &pen, const QBrush &brush)
+{
+ QGraphicsRectItem *item = new QGraphicsRectItem(rect);
+ item->setPen(pen);
+ item->setBrush(brush);
+ addItem(item);
+ return item;
+}
+
+/*!
+ \fn QGraphicsRectItem *QGraphicsScene::addRect(qreal x, qreal y, qreal w, qreal h, const QPen &pen, const QBrush &brush)
+ \since 4.3
+
+ This convenience function is equivalent to calling addRect(QRectF(\a x,
+ \a y, \a w, \a h), \a pen, \a brush).
+*/
+
+/*!
+ Creates and adds a text item to the scene, and returns the item
+ pointer. The text string is initialized to \a text, and its font
+ is initialized to \a font.
+
+ The item's position is initialized to (0, 0).
+
+ If the item is visible (i.e., QGraphicsItem::isVisible() returns true),
+ QGraphicsScene will emit changed() once control goes back to the event
+ loop.
+
+ \sa addEllipse(), addLine(), addPixmap(), addPixmap(), addRect(),
+ addItem(), addWidget()
+*/
+QGraphicsTextItem *QGraphicsScene::addText(const QString &text, const QFont &font)
+{
+ QGraphicsTextItem *item = new QGraphicsTextItem(text);
+ item->setFont(font);
+ addItem(item);
+ return item;
+}
+
+/*!
+ Creates and adds a QGraphicsSimpleTextItem to the scene, and returns the
+ item pointer. The text string is initialized to \a text, and its font is
+ initialized to \a font.
+
+ The item's position is initialized to (0, 0).
+
+ If the item is visible (i.e., QGraphicsItem::isVisible() returns true),
+ QGraphicsScene will emit changed() once control goes back to the event
+ loop.
+
+ \sa addEllipse(), addLine(), addPixmap(), addPixmap(), addRect(),
+ addItem(), addWidget()
+*/
+QGraphicsSimpleTextItem *QGraphicsScene::addSimpleText(const QString &text, const QFont &font)
+{
+ QGraphicsSimpleTextItem *item = new QGraphicsSimpleTextItem(text);
+ item->setFont(font);
+ addItem(item);
+ return item;
+}
+
+/*!
+ Creates a new QGraphicsProxyWidget for \a widget, adds it to the scene,
+ and returns a pointer to the proxy. \a wFlags set the default window flags
+ for the embedding proxy widget.
+
+ The item's position is initialized to (0, 0).
+
+ If the item is visible (i.e., QGraphicsItem::isVisible() returns true),
+ QGraphicsScene will emit changed() once control goes back to the event
+ loop.
+
+ Note that widgets with the Qt::WA_PaintOnScreen widget attribute
+ set and widgets that wrap an external application or controller
+ are not supported. Examples are QGLWidget and QAxWidget.
+
+ \sa addEllipse(), addLine(), addPixmap(), addPixmap(), addRect(),
+ addText(), addSimpleText(), addItem()
+*/
+QGraphicsProxyWidget *QGraphicsScene::addWidget(QWidget *widget, Qt::WindowFlags wFlags)
+{
+ QGraphicsProxyWidget *proxy = new QGraphicsProxyWidget(0, wFlags);
+ proxy->setWidget(widget);
+ addItem(proxy);
+ return proxy;
+}
+
+/*!
+ Removes the item \a item and all its children from the scene. The
+ ownership of \a item is passed on to the caller (i.e.,
+ QGraphicsScene will no longer delete \a item when destroyed).
+
+ \sa addItem()
+*/
+void QGraphicsScene::removeItem(QGraphicsItem *item)
+{
+ // ### Refactoring: This function shares much functionality with _q_removeItemLater()
+ Q_D(QGraphicsScene);
+ if (!item) {
+ qWarning("QGraphicsScene::removeItem: cannot remove 0-item");
+ return;
+ }
+ if (item->scene() != this) {
+ qWarning("QGraphicsScene::removeItem: item %p's scene (%p)"
+ " is different from this scene (%p)",
+ item, item->scene(), this);
+ return;
+ }
+
+ // Notify the item that it's scene is changing to 0, allowing the item to
+ // react.
+ const QVariant newSceneVariant(item->itemChange(QGraphicsItem::ItemSceneChange,
+ QVariant::fromValue<QGraphicsScene *>(0)));
+ QGraphicsScene *targetScene = qvariant_cast<QGraphicsScene *>(newSceneVariant);
+ if (targetScene != 0 && targetScene != this) {
+ targetScene->addItem(item);
+ return;
+ }
+
+ d->removeItemHelper(item);
+
+ // Deliver post-change notification
+ item->itemChange(QGraphicsItem::ItemSceneHasChanged, newSceneVariant);
+
+ d->updateInputMethodSensitivityInViews();
+}
+
+/*!
+ When the scene is active, this functions returns the scene's current focus
+ item, or 0 if no item currently has focus. When the scene is inactive, this
+ functions returns the item that will gain input focus when the scene becomes
+ active.
+
+ The focus item receives keyboard input when the scene receives a
+ key event.
+
+ \sa setFocusItem(), QGraphicsItem::hasFocus(), isActive()
+*/
+QGraphicsItem *QGraphicsScene::focusItem() const
+{
+ Q_D(const QGraphicsScene);
+ return isActive() ? d->focusItem : d->passiveFocusItem;
+}
+
+/*!
+ Sets the scene's focus item to \a item, with the focus reason \a
+ focusReason, after removing focus from any previous item that may have had
+ focus.
+
+ If \a item is 0, or if it either does not accept focus (i.e., it does not
+ have the QGraphicsItem::ItemIsFocusable flag enabled), or is not visible
+ or not enabled, this function only removes focus from any previous
+ focusitem.
+
+ If item is not 0, and the scene does not currently have focus (i.e.,
+ hasFocus() returns false), this function will call setFocus()
+ automatically.
+
+ \sa focusItem(), hasFocus(), setFocus()
+*/
+void QGraphicsScene::setFocusItem(QGraphicsItem *item, Qt::FocusReason focusReason)
+{
+ Q_D(QGraphicsScene);
+ if (item)
+ item->setFocus(focusReason);
+ else
+ d->setFocusItemHelper(item, focusReason);
+}
+
+/*!
+ Returns true if the scene has focus; otherwise returns false. If the scene
+ has focus, it will will forward key events from QKeyEvent to any item that
+ has focus.
+
+ \sa setFocus(), setFocusItem()
+*/
+bool QGraphicsScene::hasFocus() const
+{
+ Q_D(const QGraphicsScene);
+ return d->hasFocus;
+}
+
+/*!
+ Sets focus on the scene by sending a QFocusEvent to the scene, passing \a
+ focusReason as the reason. If the scene regains focus after having
+ previously lost it while an item had focus, the last focus item will
+ receive focus with \a focusReason as the reason.
+
+ If the scene already has focus, this function does nothing.
+
+ \sa hasFocus(), clearFocus(), setFocusItem()
+*/
+void QGraphicsScene::setFocus(Qt::FocusReason focusReason)
+{
+ Q_D(QGraphicsScene);
+ if (d->hasFocus || !isActive())
+ return;
+ QFocusEvent event(QEvent::FocusIn, focusReason);
+ QCoreApplication::sendEvent(this, &event);
+}
+
+/*!
+ Clears focus from the scene. If any item has focus when this function is
+ called, it will lose focus, and regain focus again once the scene regains
+ focus.
+
+ A scene that does not have focus ignores key events.
+
+ \sa hasFocus(), setFocus(), setFocusItem()
+*/
+void QGraphicsScene::clearFocus()
+{
+ Q_D(QGraphicsScene);
+ if (d->hasFocus) {
+ d->hasFocus = false;
+ d->passiveFocusItem = d->focusItem;
+ setFocusItem(0, Qt::OtherFocusReason);
+ }
+}
+
+/*!
+ \property QGraphicsScene::stickyFocus
+ \brief whether clicking into the scene background will clear focus
+
+ \since 4.6
+
+ In a QGraphicsScene with stickyFocus set to true, focus will remain
+ unchanged when the user clicks into the scene background or on an item
+ that does not accept focus. Otherwise, focus will be cleared.
+
+ By default, this property is false.
+
+ Focus changes in response to a mouse press. You can reimplement
+ mousePressEvent() in a subclass of QGraphicsScene to toggle this property
+ based on where the user has clicked.
+
+ \sa clearFocus(), setFocusItem()
+*/
+void QGraphicsScene::setStickyFocus(bool enabled)
+{
+ Q_D(QGraphicsScene);
+ d->stickyFocus = enabled;
+}
+bool QGraphicsScene::stickyFocus() const
+{
+ Q_D(const QGraphicsScene);
+ return d->stickyFocus;
+}
+
+/*!
+ Returns the current mouse grabber item, or 0 if no item is currently
+ grabbing the mouse. The mouse grabber item is the item that receives all
+ mouse events sent to the scene.
+
+ An item becomes a mouse grabber when it receives and accepts a
+ mouse press event, and it stays the mouse grabber until either of
+ the following events occur:
+
+ \list
+ \o If the item receives a mouse release event when there are no other
+ buttons pressed, it loses the mouse grab.
+ \o If the item becomes invisible (i.e., someone calls \c {item->setVisible(false)}),
+ or if it becomes disabled (i.e., someone calls \c {item->setEnabled(false)}),
+ it loses the mouse grab.
+ \o If the item is removed from the scene, it loses the mouse grab.
+ \endlist
+
+ If the item loses its mouse grab, the scene will ignore all mouse events
+ until a new item grabs the mouse (i.e., until a new item receives a mouse
+ press event).
+*/
+QGraphicsItem *QGraphicsScene::mouseGrabberItem() const
+{
+ Q_D(const QGraphicsScene);
+ return !d->mouseGrabberItems.isEmpty() ? d->mouseGrabberItems.last() : 0;
+}
+
+/*!
+ \property QGraphicsScene::backgroundBrush
+ \brief the background brush of the scene.
+
+ Set this property to changes the scene's background to a different color,
+ gradient or texture. The default background brush is Qt::NoBrush. The
+ background is drawn before (behind) the items.
+
+ Example:
+
+ \snippet doc/src/snippets/code/src_gui_graphicsview_qgraphicsscene.cpp 3
+
+ QGraphicsScene::render() calls drawBackground() to draw the scene
+ background. For more detailed control over how the background is drawn,
+ you can reimplement drawBackground() in a subclass of QGraphicsScene.
+*/
+QBrush QGraphicsScene::backgroundBrush() const
+{
+ Q_D(const QGraphicsScene);
+ return d->backgroundBrush;
+}
+void QGraphicsScene::setBackgroundBrush(const QBrush &brush)
+{
+ Q_D(QGraphicsScene);
+ d->backgroundBrush = brush;
+ foreach (QGraphicsView *view, d->views) {
+ view->resetCachedContent();
+ view->viewport()->update();
+ }
+ update();
+}
+
+/*!
+ \property QGraphicsScene::foregroundBrush
+ \brief the foreground brush of the scene.
+
+ Change this property to set the scene's foreground to a different
+ color, gradient or texture.
+
+ The foreground is drawn after (on top of) the items. The default
+ foreground brush is Qt::NoBrush ( i.e. the foreground is not
+ drawn).
+
+ Example:
+
+ \snippet doc/src/snippets/code/src_gui_graphicsview_qgraphicsscene.cpp 4
+
+ QGraphicsScene::render() calls drawForeground() to draw the scene
+ foreground. For more detailed control over how the foreground is
+ drawn, you can reimplement the drawForeground() function in a
+ QGraphicsScene subclass.
+*/
+QBrush QGraphicsScene::foregroundBrush() const
+{
+ Q_D(const QGraphicsScene);
+ return d->foregroundBrush;
+}
+void QGraphicsScene::setForegroundBrush(const QBrush &brush)
+{
+ Q_D(QGraphicsScene);
+ d->foregroundBrush = brush;
+ foreach (QGraphicsView *view, views())
+ view->viewport()->update();
+ update();
+}
+
+/*!
+ This method is used by input methods to query a set of properties of
+ the scene to be able to support complex input method operations as support
+ for surrounding text and reconversions.
+
+ The \a query parameter specifies which property is queried.
+
+ \sa QWidget::inputMethodQuery()
+*/
+QVariant QGraphicsScene::inputMethodQuery(Qt::InputMethodQuery query) const
+{
+ Q_D(const QGraphicsScene);
+ if (!d->focusItem || !(d->focusItem->flags() & QGraphicsItem::ItemAcceptsInputMethod))
+ return QVariant();
+ const QTransform matrix = d->focusItem->sceneTransform();
+ QVariant value = d->focusItem->inputMethodQuery(query);
+ if (value.type() == QVariant::RectF)
+ value = matrix.mapRect(value.toRectF());
+ else if (value.type() == QVariant::PointF)
+ value = matrix.map(value.toPointF());
+ else if (value.type() == QVariant::Rect)
+ value = matrix.mapRect(value.toRect());
+ else if (value.type() == QVariant::Point)
+ value = matrix.map(value.toPoint());
+ return value;
+}
+
+/*!
+ \fn void QGraphicsScene::update(const QRectF &rect)
+ Schedules a redraw of the area \a rect on the scene.
+
+ \sa sceneRect(), changed()
+*/
+void QGraphicsScene::update(const QRectF &rect)
+{
+ Q_D(QGraphicsScene);
+ if (d->updateAll || (rect.isEmpty() && !rect.isNull()))
+ return;
+
+ // Check if anyone's connected; if not, we can send updates directly to
+ // the views. Otherwise or if there are no views, use old behavior.
+ bool directUpdates = !(d->isSignalConnected(d->changedSignalIndex)) && !d->views.isEmpty();
+ if (rect.isNull()) {
+ d->updateAll = true;
+ d->updatedRects.clear();
+ if (directUpdates) {
+ // Update all views.
+ for (int i = 0; i < d->views.size(); ++i)
+ d->views.at(i)->d_func()->fullUpdatePending = true;
+ }
+ } else {
+ if (directUpdates) {
+ // Update all views.
+ for (int i = 0; i < d->views.size(); ++i) {
+ QGraphicsView *view = d->views.at(i);
+ if (view->isTransformed())
+ view->d_func()->updateRectF(view->viewportTransform().mapRect(rect));
+ else
+ view->d_func()->updateRectF(rect);
+ }
+ } else {
+ d->updatedRects << rect;
+ }
+ }
+
+ if (!d->calledEmitUpdated) {
+ d->calledEmitUpdated = true;
+ QMetaObject::invokeMethod(this, "_q_emitUpdated", Qt::QueuedConnection);
+ }
+}
+
+/*!
+ \fn void QGraphicsScene::update(qreal x, qreal y, qreal w, qreal h)
+ \overload
+ \since 4.3
+
+ This function is equivalent to calling update(QRectF(\a x, \a y, \a w,
+ \a h));
+*/
+
+/*!
+ Invalidates and schedules a redraw of the \a layers in \a rect on the
+ scene. Any cached content in \a layers is unconditionally invalidated and
+ redrawn.
+
+ You can use this function overload to notify QGraphicsScene of changes to
+ the background or the foreground of the scene. This function is commonly
+ used for scenes with tile-based backgrounds to notify changes when
+ QGraphicsView has enabled
+ \l{QGraphicsView::CacheBackground}{CacheBackground}.
+
+ Example:
+
+ \snippet doc/src/snippets/code/src_gui_graphicsview_qgraphicsscene.cpp 5
+
+ Note that QGraphicsView currently supports background caching only (see
+ QGraphicsView::CacheBackground). This function is equivalent to calling
+ update() if any layer but BackgroundLayer is passed.
+
+ \sa QGraphicsView::resetCachedContent()
+*/
+void QGraphicsScene::invalidate(const QRectF &rect, SceneLayers layers)
+{
+ foreach (QGraphicsView *view, views())
+ view->invalidateScene(rect, layers);
+ update(rect);
+}
+
+/*!
+ \fn void QGraphicsScene::invalidate(qreal x, qreal y, qreal w, qreal h, SceneLayers layers)
+ \overload
+ \since 4.3
+
+ This convenience function is equivalent to calling invalidate(QRectF(\a x, \a
+ y, \a w, \a h), \a layers);
+*/
+
+/*!
+ Returns a list of all the views that display this scene.
+
+ \sa QGraphicsView::scene()
+*/
+QList <QGraphicsView *> QGraphicsScene::views() const
+{
+ Q_D(const QGraphicsScene);
+ return d->views;
+}
+
+/*!
+ This slot \e advances the scene by one step, by calling
+ QGraphicsItem::advance() for all items on the scene. This is done in two
+ phases: in the first phase, all items are notified that the scene is about
+ to change, and in the second phase all items are notified that they can
+ move. In the first phase, QGraphicsItem::advance() is called passing a
+ value of 0 as an argument, and 1 is passed in the second phase.
+
+ \sa QGraphicsItem::advance(), QGraphicsItemAnimation, QTimeLine
+*/
+void QGraphicsScene::advance()
+{
+ for (int i = 0; i < 2; ++i) {
+ foreach (QGraphicsItem *item, items())
+ item->advance(i);
+ }
+}
+
+/*!
+ Processes the event \a event, and dispatches it to the respective
+ event handlers.
+
+ In addition to calling the convenience event handlers, this
+ function is responsible for converting mouse move events to hover
+ events for when there is no mouse grabber item. Hover events are
+ delivered directly to items; there is no convenience function for
+ them.
+
+ Unlike QWidget, QGraphicsScene does not have the convenience functions
+ \l{QWidget::}{enterEvent()} and \l{QWidget::}{leaveEvent()}. Use this
+ function to obtain those events instead.
+
+ \sa contextMenuEvent(), keyPressEvent(), keyReleaseEvent(),
+ mousePressEvent(), mouseMoveEvent(), mouseReleaseEvent(),
+ mouseDoubleClickEvent(), focusInEvent(), focusOutEvent()
+*/
+bool QGraphicsScene::event(QEvent *event)
+{
+ Q_D(QGraphicsScene);
+
+ switch (event->type()) {
+ case QEvent::GraphicsSceneMousePress:
+ case QEvent::GraphicsSceneMouseMove:
+ case QEvent::GraphicsSceneMouseRelease:
+ case QEvent::GraphicsSceneMouseDoubleClick:
+ case QEvent::GraphicsSceneHoverEnter:
+ case QEvent::GraphicsSceneHoverLeave:
+ case QEvent::GraphicsSceneHoverMove:
+ case QEvent::TouchBegin:
+ case QEvent::TouchUpdate:
+ case QEvent::TouchEnd:
+ // Reset the under-mouse list to ensure that this event gets fresh
+ // item-under-mouse data. Be careful about this list; if people delete
+ // items from inside event handlers, this list can quickly end up
+ // having stale pointers in it. We need to clear it before dispatching
+ // events that use it.
+ // ### this should only be cleared if we received a new mouse move event,
+ // which relies on us fixing the replay mechanism in QGraphicsView.
+ d->cachedItemsUnderMouse.clear();
+ default:
+ break;
+ }
+
+ switch (event->type()) {
+ case QEvent::GraphicsSceneDragEnter:
+ dragEnterEvent(static_cast<QGraphicsSceneDragDropEvent *>(event));
+ break;
+ case QEvent::GraphicsSceneDragMove:
+ dragMoveEvent(static_cast<QGraphicsSceneDragDropEvent *>(event));
+ break;
+ case QEvent::GraphicsSceneDragLeave:
+ dragLeaveEvent(static_cast<QGraphicsSceneDragDropEvent *>(event));
+ break;
+ case QEvent::GraphicsSceneDrop:
+ dropEvent(static_cast<QGraphicsSceneDragDropEvent *>(event));
+ break;
+ case QEvent::GraphicsSceneContextMenu:
+ contextMenuEvent(static_cast<QGraphicsSceneContextMenuEvent *>(event));
+ break;
+ case QEvent::KeyPress:
+ if (!d->focusItem) {
+ QKeyEvent *k = static_cast<QKeyEvent *>(event);
+ if (k->key() == Qt::Key_Tab || k->key() == Qt::Key_Backtab) {
+ if (!(k->modifiers() & (Qt::ControlModifier | Qt::AltModifier))) { //### Add MetaModifier?
+ bool res = false;
+ if (k->key() == Qt::Key_Backtab
+ || (k->key() == Qt::Key_Tab && (k->modifiers() & Qt::ShiftModifier))) {
+ res = focusNextPrevChild(false);
+ } else if (k->key() == Qt::Key_Tab) {
+ res = focusNextPrevChild(true);
+ }
+ if (!res)
+ event->ignore();
+ return true;
+ }
+ }
+ }
+ keyPressEvent(static_cast<QKeyEvent *>(event));
+ break;
+ case QEvent::KeyRelease:
+ keyReleaseEvent(static_cast<QKeyEvent *>(event));
+ break;
+ case QEvent::ShortcutOverride: {
+ QGraphicsItem *parent = focusItem();
+ while (parent) {
+ d->sendEvent(parent, event);
+ if (event->isAccepted())
+ return true;
+ parent = parent->parentItem();
+ }
+ }
+ return false;
+ case QEvent::GraphicsSceneMouseMove:
+ {
+ QGraphicsSceneMouseEvent *mouseEvent = static_cast<QGraphicsSceneMouseEvent *>(event);
+ d->lastSceneMousePos = mouseEvent->scenePos();
+ mouseMoveEvent(mouseEvent);
+ break;
+ }
+ case QEvent::GraphicsSceneMousePress:
+ mousePressEvent(static_cast<QGraphicsSceneMouseEvent *>(event));
+ break;
+ case QEvent::GraphicsSceneMouseRelease:
+ mouseReleaseEvent(static_cast<QGraphicsSceneMouseEvent *>(event));
+ break;
+ case QEvent::GraphicsSceneMouseDoubleClick:
+ mouseDoubleClickEvent(static_cast<QGraphicsSceneMouseEvent *>(event));
+ break;
+ case QEvent::GraphicsSceneWheel:
+ wheelEvent(static_cast<QGraphicsSceneWheelEvent *>(event));
+ break;
+ case QEvent::FocusIn:
+ focusInEvent(static_cast<QFocusEvent *>(event));
+ break;
+ case QEvent::FocusOut:
+ focusOutEvent(static_cast<QFocusEvent *>(event));
+ break;
+ case QEvent::GraphicsSceneHoverEnter:
+ case QEvent::GraphicsSceneHoverLeave:
+ case QEvent::GraphicsSceneHoverMove:
+ {
+ QGraphicsSceneHoverEvent *hoverEvent = static_cast<QGraphicsSceneHoverEvent *>(event);
+ d->lastSceneMousePos = hoverEvent->scenePos();
+ d->dispatchHoverEvent(hoverEvent);
+ break;
+ }
+ case QEvent::Leave:
+ // hackieshly unpacking the viewport pointer from the leave event.
+ d->leaveScene(reinterpret_cast<QWidget *>(event->d));
+ break;
+ case QEvent::GraphicsSceneHelp:
+ helpEvent(static_cast<QGraphicsSceneHelpEvent *>(event));
+ break;
+ case QEvent::InputMethod:
+ inputMethodEvent(static_cast<QInputMethodEvent *>(event));
+ break;
+ case QEvent::WindowActivate:
+ if (!d->activationRefCount++) {
+ if (d->lastActivePanel) {
+ // Activate the last panel.
+ d->setActivePanelHelper(d->lastActivePanel, true);
+ } else if (d->tabFocusFirst && d->tabFocusFirst->isPanel()) {
+ // Activate the panel of the first item in the tab focus
+ // chain.
+ d->setActivePanelHelper(d->tabFocusFirst, true);
+ } else {
+ // Activate all toplevel items.
+ QEvent event(QEvent::WindowActivate);
+ foreach (QGraphicsItem *item, items()) {
+ if (item->isVisible() && !item->isPanel() && !item->parentItem())
+ sendEvent(item, &event);
+ }
+ }
+ }
+ break;
+ case QEvent::WindowDeactivate:
+ if (!--d->activationRefCount) {
+ if (d->activePanel) {
+ // Deactivate the active panel (but keep it so we can
+ // reactivate it later).
+ QGraphicsItem *lastActivePanel = d->activePanel;
+ d->setActivePanelHelper(0, true);
+ d->lastActivePanel = lastActivePanel;
+ } else {
+ // Activate all toplevel items.
+ QEvent event(QEvent::WindowDeactivate);
+ foreach (QGraphicsItem *item, items()) {
+ if (item->isVisible() && !item->isPanel() && !item->parentItem())
+ sendEvent(item, &event);
+ }
+ }
+ }
+ break;
+ case QEvent::ApplicationFontChange: {
+ // Resolve the existing scene font.
+ d->resolveFont();
+ break;
+ }
+ case QEvent::FontChange:
+ // Update the entire scene when the font changes.
+ update();
+ break;
+ case QEvent::ApplicationPaletteChange: {
+ // Resolve the existing scene palette.
+ d->resolvePalette();
+ break;
+ }
+ case QEvent::PaletteChange:
+ // Update the entire scene when the palette changes.
+ update();
+ break;
+ case QEvent::StyleChange:
+ // Reresolve all widgets' styles. Update all top-level widgets'
+ // geometries that do not have an explicit style set.
+ update();
+ break;
+ case QEvent::TouchBegin:
+ case QEvent::TouchUpdate:
+ case QEvent::TouchEnd:
+ d->touchEventHandler(static_cast<QTouchEvent *>(event));
+ break;
+#ifndef QT_NO_GESTURES
+ case QEvent::Gesture:
+ case QEvent::GestureOverride:
+ d->gestureEventHandler(static_cast<QGestureEvent *>(event));
+ break;
+#endif // QT_NO_GESTURES
+ default:
+ return QObject::event(event);
+ }
+ return true;
+}
+
+/*!
+ \reimp
+
+ QGraphicsScene filters QApplication's events to detect palette and font
+ changes.
+*/
+bool QGraphicsScene::eventFilter(QObject *watched, QEvent *event)
+{
+ if (watched != qApp)
+ return false;
+
+ switch (event->type()) {
+ case QEvent::ApplicationPaletteChange:
+ QApplication::postEvent(this, new QEvent(QEvent::ApplicationPaletteChange));
+ break;
+ case QEvent::ApplicationFontChange:
+ QApplication::postEvent(this, new QEvent(QEvent::ApplicationFontChange));
+ break;
+ default:
+ break;
+ }
+ return false;
+}
+
+/*!
+ This event handler, for event \a contextMenuEvent, can be reimplemented in
+ a subclass to receive context menu events. The default implementation
+ forwards the event to the topmost item that accepts context menu events at
+ the position of the event. If no items accept context menu events at this
+ position, the event is ignored.
+
+ \sa QGraphicsItem::contextMenuEvent()
+*/
+void QGraphicsScene::contextMenuEvent(QGraphicsSceneContextMenuEvent *contextMenuEvent)
+{
+ Q_D(QGraphicsScene);
+ // Ignore by default.
+ contextMenuEvent->ignore();
+
+ // Send the event to all items at this position until one item accepts the
+ // event.
+ foreach (QGraphicsItem *item, d->itemsAtPosition(contextMenuEvent->screenPos(),
+ contextMenuEvent->scenePos(),
+ contextMenuEvent->widget())) {
+ contextMenuEvent->setPos(item->d_ptr->genericMapFromScene(contextMenuEvent->scenePos(),
+ contextMenuEvent->widget()));
+ contextMenuEvent->accept();
+ if (!d->sendEvent(item, contextMenuEvent))
+ break;
+
+ if (contextMenuEvent->isAccepted())
+ break;
+ }
+}
+
+/*!
+ This event handler, for event \a event, can be reimplemented in a subclass
+ to receive drag enter events for the scene.
+
+ The default implementation accepts the event and prepares the scene to
+ accept drag move events.
+
+ \sa QGraphicsItem::dragEnterEvent(), dragMoveEvent(), dragLeaveEvent(),
+ dropEvent()
+*/
+void QGraphicsScene::dragEnterEvent(QGraphicsSceneDragDropEvent *event)
+{
+ Q_D(QGraphicsScene);
+ d->dragDropItem = 0;
+ d->lastDropAction = Qt::IgnoreAction;
+ event->accept();
+}
+
+/*!
+ This event handler, for event \a event, can be reimplemented in a subclass
+ to receive drag move events for the scene.
+
+ \sa QGraphicsItem::dragMoveEvent(), dragEnterEvent(), dragLeaveEvent(),
+ dropEvent()
+*/
+void QGraphicsScene::dragMoveEvent(QGraphicsSceneDragDropEvent *event)
+{
+ Q_D(QGraphicsScene);
+ event->ignore();
+
+ if (!d->mouseGrabberItems.isEmpty()) {
+ // Mouse grabbers that start drag events lose the mouse grab.
+ d->clearMouseGrabber();
+ d->mouseGrabberButtonDownPos.clear();
+ d->mouseGrabberButtonDownScenePos.clear();
+ d->mouseGrabberButtonDownScreenPos.clear();
+ }
+
+ bool eventDelivered = false;
+
+ // Find the topmost enabled items under the cursor. They are all
+ // candidates for accepting drag & drop events.
+ foreach (QGraphicsItem *item, d->itemsAtPosition(event->screenPos(),
+ event->scenePos(),
+ event->widget())) {
+ if (!item->isEnabled() || !item->acceptDrops())
+ continue;
+
+ if (item != d->dragDropItem) {
+ // Enter the new drag drop item. If it accepts the event, we send
+ // the leave to the parent item.
+ QGraphicsSceneDragDropEvent dragEnter(QEvent::GraphicsSceneDragEnter);
+ d->cloneDragDropEvent(&dragEnter, event);
+ dragEnter.setDropAction(event->proposedAction());
+ d->sendDragDropEvent(item, &dragEnter);
+ event->setAccepted(dragEnter.isAccepted());
+ event->setDropAction(dragEnter.dropAction());
+ if (!event->isAccepted()) {
+ // Propagate to the item under
+ continue;
+ }
+
+ d->lastDropAction = event->dropAction();
+
+ if (d->dragDropItem) {
+ // Leave the last drag drop item. A perfect implementation
+ // would set the position of this event to the point where
+ // this event and the last event intersect with the item's
+ // shape, but that's not easy to do. :-)
+ QGraphicsSceneDragDropEvent dragLeave(QEvent::GraphicsSceneDragLeave);
+ d->cloneDragDropEvent(&dragLeave, event);
+ d->sendDragDropEvent(d->dragDropItem, &dragLeave);
+ }
+
+ // We've got a new drag & drop item
+ d->dragDropItem = item;
+ }
+
+ // Send the move event.
+ event->setDropAction(d->lastDropAction);
+ event->accept();
+ d->sendDragDropEvent(item, event);
+ if (event->isAccepted())
+ d->lastDropAction = event->dropAction();
+ eventDelivered = true;
+ break;
+ }
+
+ if (!eventDelivered) {
+ if (d->dragDropItem) {
+ // Leave the last drag drop item
+ QGraphicsSceneDragDropEvent dragLeave(QEvent::GraphicsSceneDragLeave);
+ d->cloneDragDropEvent(&dragLeave, event);
+ d->sendDragDropEvent(d->dragDropItem, &dragLeave);
+ d->dragDropItem = 0;
+ }
+ // Propagate
+ event->setDropAction(Qt::IgnoreAction);
+ }
+}
+
+/*!
+ This event handler, for event \a event, can be reimplemented in a subclass
+ to receive drag leave events for the scene.
+
+ \sa QGraphicsItem::dragLeaveEvent(), dragEnterEvent(), dragMoveEvent(),
+ dropEvent()
+*/
+void QGraphicsScene::dragLeaveEvent(QGraphicsSceneDragDropEvent *event)
+{
+ Q_D(QGraphicsScene);
+ if (d->dragDropItem) {
+ // Leave the last drag drop item
+ d->sendDragDropEvent(d->dragDropItem, event);
+ d->dragDropItem = 0;
+ }
+}
+
+/*!
+ This event handler, for event \a event, can be reimplemented in a subclass
+ to receive drop events for the scene.
+
+ \sa QGraphicsItem::dropEvent(), dragEnterEvent(), dragMoveEvent(),
+ dragLeaveEvent()
+*/
+void QGraphicsScene::dropEvent(QGraphicsSceneDragDropEvent *event)
+{
+ Q_UNUSED(event);
+ Q_D(QGraphicsScene);
+ if (d->dragDropItem) {
+ // Drop on the last drag drop item
+ d->sendDragDropEvent(d->dragDropItem, event);
+ d->dragDropItem = 0;
+ }
+}
+
+/*!
+ This event handler, for event \a focusEvent, can be reimplemented in a
+ subclass to receive focus in events.
+
+ The default implementation sets focus on the scene, and then on the last
+ focus item.
+
+ \sa QGraphicsItem::focusOutEvent()
+*/
+void QGraphicsScene::focusInEvent(QFocusEvent *focusEvent)
+{
+ Q_D(QGraphicsScene);
+
+ d->hasFocus = true;
+ switch (focusEvent->reason()) {
+ case Qt::TabFocusReason:
+ if (!focusNextPrevChild(true))
+ focusEvent->ignore();
+ break;
+ case Qt::BacktabFocusReason:
+ if (!focusNextPrevChild(false))
+ focusEvent->ignore();
+ break;
+ default:
+ if (d->passiveFocusItem) {
+ // Set focus on the last focus item
+ setFocusItem(d->passiveFocusItem, focusEvent->reason());
+ }
+ break;
+ }
+}
+
+/*!
+ This event handler, for event \a focusEvent, can be reimplemented in a
+ subclass to receive focus out events.
+
+ The default implementation removes focus from any focus item, then removes
+ focus from the scene.
+
+ \sa QGraphicsItem::focusInEvent()
+*/
+void QGraphicsScene::focusOutEvent(QFocusEvent *focusEvent)
+{
+ Q_D(QGraphicsScene);
+ d->hasFocus = false;
+ d->passiveFocusItem = d->focusItem;
+ setFocusItem(0, focusEvent->reason());
+
+ // Remove all popups when the scene loses focus.
+ if (!d->popupWidgets.isEmpty())
+ d->removePopup(d->popupWidgets.first());
+}
+
+/*!
+ This event handler, for event \a helpEvent, can be
+ reimplemented in a subclass to receive help events. The events
+ are of type QEvent::ToolTip, which are created when a tooltip is
+ requested.
+
+ The default implementation shows the tooltip of the topmost
+ item, i.e., the item with the highest z-value, at the mouse
+ cursor position. If no item has a tooltip set, this function
+ does nothing.
+
+ \sa QGraphicsItem::toolTip(), QGraphicsSceneHelpEvent
+*/
+void QGraphicsScene::helpEvent(QGraphicsSceneHelpEvent *helpEvent)
+{
+#ifdef QT_NO_TOOLTIP
+ Q_UNUSED(helpEvent);
+#else
+ // Find the first item that does tooltips
+ Q_D(QGraphicsScene);
+ QList<QGraphicsItem *> itemsAtPos = d->itemsAtPosition(helpEvent->screenPos(),
+ helpEvent->scenePos(),
+ helpEvent->widget());
+ QGraphicsItem *toolTipItem = 0;
+ for (int i = 0; i < itemsAtPos.size(); ++i) {
+ QGraphicsItem *tmp = itemsAtPos.at(i);
+ if (tmp->d_func()->isProxyWidget()) {
+ // if the item is a proxy widget, the event is forwarded to it
+ sendEvent(tmp, helpEvent);
+ if (helpEvent->isAccepted())
+ return;
+ }
+ if (!tmp->toolTip().isEmpty()) {
+ toolTipItem = tmp;
+ break;
+ }
+ }
+
+ // Show or hide the tooltip
+ QString text;
+ QPoint point;
+ if (toolTipItem && !toolTipItem->toolTip().isEmpty()) {
+ text = toolTipItem->toolTip();
+ point = helpEvent->screenPos();
+ }
+ QToolTip::showText(point, text, helpEvent->widget());
+ helpEvent->setAccepted(!text.isEmpty());
+#endif
+}
+
+bool QGraphicsScenePrivate::itemAcceptsHoverEvents_helper(const QGraphicsItem *item) const
+{
+ return (item->d_ptr->acceptsHover
+ || (item->d_ptr->isWidget
+ && static_cast<const QGraphicsWidget *>(item)->d_func()->hasDecoration()))
+ && !item->isBlockedByModalPanel();
+}
+
+/*!
+ This event handler, for event \a hoverEvent, can be reimplemented in a
+ subclass to receive hover enter events. The default implementation
+ forwards the event to the topmost item that accepts hover events at the
+ scene position from the event.
+
+ \sa QGraphicsItem::hoverEvent(), QGraphicsItem::setAcceptHoverEvents()
+*/
+bool QGraphicsScenePrivate::dispatchHoverEvent(QGraphicsSceneHoverEvent *hoverEvent)
+{
+ if (allItemsIgnoreHoverEvents)
+ return false;
+
+ // Find the first item that accepts hover events, reusing earlier
+ // calculated data is possible.
+ if (cachedItemsUnderMouse.isEmpty()) {
+ cachedItemsUnderMouse = itemsAtPosition(hoverEvent->screenPos(),
+ hoverEvent->scenePos(),
+ hoverEvent->widget());
+ }
+
+ QGraphicsItem *item = 0;
+ for (int i = 0; i < cachedItemsUnderMouse.size(); ++i) {
+ QGraphicsItem *tmp = cachedItemsUnderMouse.at(i);
+ if (itemAcceptsHoverEvents_helper(tmp)) {
+ item = tmp;
+ break;
+ }
+ }
+
+ // Find the common ancestor item for the new topmost hoverItem and the
+ // last item in the hoverItem list.
+ QGraphicsItem *commonAncestorItem = (item && !hoverItems.isEmpty()) ? item->commonAncestorItem(hoverItems.last()) : 0;
+ while (commonAncestorItem && !itemAcceptsHoverEvents_helper(commonAncestorItem))
+ commonAncestorItem = commonAncestorItem->parentItem();
+ if (commonAncestorItem && commonAncestorItem->panel() != item->panel()) {
+ // The common ancestor isn't in the same panel as the two hovered
+ // items.
+ commonAncestorItem = 0;
+ }
+
+ // Check if the common ancestor item is known.
+ int index = commonAncestorItem ? hoverItems.indexOf(commonAncestorItem) : -1;
+ // Send hover leaves to any existing hovered children of the common
+ // ancestor item.
+ for (int i = hoverItems.size() - 1; i > index; --i) {
+ QGraphicsItem *lastItem = hoverItems.takeLast();
+ if (itemAcceptsHoverEvents_helper(lastItem))
+ sendHoverEvent(QEvent::GraphicsSceneHoverLeave, lastItem, hoverEvent);
+ }
+
+ // Item is a child of a known item. Generate enter events for the
+ // missing links.
+ QList<QGraphicsItem *> parents;
+ QGraphicsItem *parent = item;
+ while (parent && parent != commonAncestorItem) {
+ parents.prepend(parent);
+ if (parent->isPanel()) {
+ // Stop at the panel - we don't deliver beyond this point.
+ break;
+ }
+ parent = parent->parentItem();
+ }
+ for (int i = 0; i < parents.size(); ++i) {
+ parent = parents.at(i);
+ hoverItems << parent;
+ if (itemAcceptsHoverEvents_helper(parent))
+ sendHoverEvent(QEvent::GraphicsSceneHoverEnter, parent, hoverEvent);
+ }
+
+ // Generate a move event for the item itself
+ if (item
+ && !hoverItems.isEmpty()
+ && item == hoverItems.last()) {
+ sendHoverEvent(QEvent::GraphicsSceneHoverMove, item, hoverEvent);
+ return true;
+ }
+ return false;
+}
+
+/*!
+ \internal
+
+ Handles all actions necessary to clean up the scene when the mouse leaves
+ the view.
+*/
+void QGraphicsScenePrivate::leaveScene(QWidget *viewport)
+{
+#ifndef QT_NO_TOOLTIP
+ QToolTip::hideText();
+#endif
+ QGraphicsView *view = qobject_cast<QGraphicsView *>(viewport->parent());
+ // Send HoverLeave events to all existing hover items, topmost first.
+ QGraphicsSceneHoverEvent hoverEvent;
+ hoverEvent.setWidget(viewport);
+
+ if (view) {
+ QPoint cursorPos = QCursor::pos();
+ hoverEvent.setScenePos(view->mapToScene(viewport->mapFromGlobal(cursorPos)));
+ hoverEvent.setLastScenePos(hoverEvent.scenePos());
+ hoverEvent.setScreenPos(cursorPos);
+ hoverEvent.setLastScreenPos(hoverEvent.screenPos());
+ }
+
+ while (!hoverItems.isEmpty()) {
+ QGraphicsItem *lastItem = hoverItems.takeLast();
+ if (itemAcceptsHoverEvents_helper(lastItem))
+ sendHoverEvent(QEvent::GraphicsSceneHoverLeave, lastItem, &hoverEvent);
+ }
+}
+
+/*!
+ This event handler, for event \a keyEvent, can be reimplemented in a
+ subclass to receive keypress events. The default implementation forwards
+ the event to current focus item.
+
+ \sa QGraphicsItem::keyPressEvent(), focusItem()
+*/
+void QGraphicsScene::keyPressEvent(QKeyEvent *keyEvent)
+{
+ // ### Merge this function with keyReleaseEvent; they are identical
+ // ### (except this comment).
+ Q_D(QGraphicsScene);
+ QGraphicsItem *item = !d->keyboardGrabberItems.isEmpty() ? d->keyboardGrabberItems.last() : 0;
+ if (!item)
+ item = focusItem();
+ if (item) {
+ QGraphicsItem *p = item;
+ do {
+ // Accept the event by default
+ keyEvent->accept();
+ // Send it; QGraphicsItem::keyPressEvent ignores it. If the event
+ // is filtered out, stop propagating it.
+ if (p->isBlockedByModalPanel())
+ break;
+ if (!d->sendEvent(p, keyEvent))
+ break;
+ } while (!keyEvent->isAccepted() && !p->isPanel() && (p = p->parentItem()));
+ } else {
+ keyEvent->ignore();
+ }
+}
+
+/*!
+ This event handler, for event \a keyEvent, can be reimplemented in a
+ subclass to receive key release events. The default implementation
+ forwards the event to current focus item.
+
+ \sa QGraphicsItem::keyReleaseEvent(), focusItem()
+*/
+void QGraphicsScene::keyReleaseEvent(QKeyEvent *keyEvent)
+{
+ // ### Merge this function with keyPressEvent; they are identical (except
+ // ### this comment).
+ Q_D(QGraphicsScene);
+ QGraphicsItem *item = !d->keyboardGrabberItems.isEmpty() ? d->keyboardGrabberItems.last() : 0;
+ if (!item)
+ item = focusItem();
+ if (item) {
+ QGraphicsItem *p = item;
+ do {
+ // Accept the event by default
+ keyEvent->accept();
+ // Send it; QGraphicsItem::keyPressEvent ignores it. If the event
+ // is filtered out, stop propagating it.
+ if (p->isBlockedByModalPanel())
+ break;
+ if (!d->sendEvent(p, keyEvent))
+ break;
+ } while (!keyEvent->isAccepted() && !p->isPanel() && (p = p->parentItem()));
+ } else {
+ keyEvent->ignore();
+ }
+}
+
+/*!
+ This event handler, for event \a mouseEvent, can be reimplemented
+ in a subclass to receive mouse press events for the scene.
+
+ The default implementation depends on the state of the scene. If
+ there is a mouse grabber item, then the event is sent to the mouse
+ grabber. Otherwise, it is forwarded to the topmost item that
+ accepts mouse events at the scene position from the event, and
+ that item promptly becomes the mouse grabber item.
+
+ If there is no item at the given position on the scene, the
+ selection area is reset, any focus item loses its input focus, and
+ the event is then ignored.
+
+ \sa QGraphicsItem::mousePressEvent(),
+ QGraphicsItem::setAcceptedMouseButtons()
+*/
+void QGraphicsScene::mousePressEvent(QGraphicsSceneMouseEvent *mouseEvent)
+{
+ Q_D(QGraphicsScene);
+ if (d->mouseGrabberItems.isEmpty()) {
+ // Dispatch hover events
+ QGraphicsSceneHoverEvent hover;
+ _q_hoverFromMouseEvent(&hover, mouseEvent);
+ d->dispatchHoverEvent(&hover);
+ }
+
+ d->mousePressEventHandler(mouseEvent);
+}
+
+/*!
+ This event handler, for event \a mouseEvent, can be reimplemented
+ in a subclass to receive mouse move events for the scene.
+
+ The default implementation depends on the mouse grabber state. If there is
+ a mouse grabber item, the event is sent to the mouse grabber. If there
+ are any items that accept hover events at the current position, the event
+ is translated into a hover event and accepted; otherwise it's ignored.
+
+ \sa QGraphicsItem::mousePressEvent(), QGraphicsItem::mouseReleaseEvent(),
+ QGraphicsItem::mouseDoubleClickEvent(), QGraphicsItem::setAcceptedMouseButtons()
+*/
+void QGraphicsScene::mouseMoveEvent(QGraphicsSceneMouseEvent *mouseEvent)
+{
+ Q_D(QGraphicsScene);
+ if (d->mouseGrabberItems.isEmpty()) {
+ if (mouseEvent->buttons())
+ return;
+ QGraphicsSceneHoverEvent hover;
+ _q_hoverFromMouseEvent(&hover, mouseEvent);
+ mouseEvent->setAccepted(d->dispatchHoverEvent(&hover));
+ return;
+ }
+
+ // Forward the event to the mouse grabber
+ d->sendMouseEvent(mouseEvent);
+ mouseEvent->accept();
+}
+
+/*!
+ This event handler, for event \a mouseEvent, can be reimplemented
+ in a subclass to receive mouse release events for the scene.
+
+ The default implementation depends on the mouse grabber state. If
+ there is no mouse grabber, the event is ignored. Otherwise, if
+ there is a mouse grabber item, the event is sent to the mouse
+ grabber. If this mouse release represents the last pressed button
+ on the mouse, the mouse grabber item then loses the mouse grab.
+
+ \sa QGraphicsItem::mousePressEvent(), QGraphicsItem::mouseMoveEvent(),
+ QGraphicsItem::mouseDoubleClickEvent(), QGraphicsItem::setAcceptedMouseButtons()
+*/
+void QGraphicsScene::mouseReleaseEvent(QGraphicsSceneMouseEvent *mouseEvent)
+{
+ Q_D(QGraphicsScene);
+ if (d->mouseGrabberItems.isEmpty()) {
+ mouseEvent->ignore();
+ return;
+ }
+
+ // Forward the event to the mouse grabber
+ d->sendMouseEvent(mouseEvent);
+ mouseEvent->accept();
+
+ // Reset the mouse grabber when the last mouse button has been released.
+ if (!mouseEvent->buttons()) {
+ if (!d->mouseGrabberItems.isEmpty()) {
+ d->lastMouseGrabberItem = d->mouseGrabberItems.last();
+ if (d->lastMouseGrabberItemHasImplicitMouseGrab)
+ d->mouseGrabberItems.last()->ungrabMouse();
+ } else {
+ d->lastMouseGrabberItem = 0;
+ }
+
+ // Generate a hoverevent
+ QGraphicsSceneHoverEvent hoverEvent;
+ _q_hoverFromMouseEvent(&hoverEvent, mouseEvent);
+ d->dispatchHoverEvent(&hoverEvent);
+ }
+}
+
+/*!
+ This event handler, for event \a mouseEvent, can be reimplemented
+ in a subclass to receive mouse doubleclick events for the scene.
+
+ If someone doubleclicks on the scene, the scene will first receive
+ a mouse press event, followed by a release event (i.e., a click),
+ then a doubleclick event, and finally a release event. If the
+ doubleclick event is delivered to a different item than the one
+ that received the first press and release, it will be delivered as
+ a press event. However, tripleclick events are not delivered as
+ doubleclick events in this case.
+
+ The default implementation is similar to mousePressEvent().
+
+ \sa QGraphicsItem::mousePressEvent(), QGraphicsItem::mouseMoveEvent(),
+ QGraphicsItem::mouseReleaseEvent(), QGraphicsItem::setAcceptedMouseButtons()
+*/
+void QGraphicsScene::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *mouseEvent)
+{
+ Q_D(QGraphicsScene);
+ d->mousePressEventHandler(mouseEvent);
+}
+
+/*!
+ This event handler, for event \a wheelEvent, can be reimplemented in a
+ subclass to receive mouse wheel events for the scene.
+
+ By default, the event is delivered to the topmost visible item under the
+ cursor. If ignored, the event propagates to the item beneath, and again
+ until the event is accepted, or it reaches the scene. If no items accept
+ the event, it is ignored.
+
+ \sa QGraphicsItem::wheelEvent()
+*/
+void QGraphicsScene::wheelEvent(QGraphicsSceneWheelEvent *wheelEvent)
+{
+ Q_D(QGraphicsScene);
+ QList<QGraphicsItem *> wheelCandidates = d->itemsAtPosition(wheelEvent->screenPos(),
+ wheelEvent->scenePos(),
+ wheelEvent->widget());
+
+#ifdef Q_WS_MAC
+ // On Mac, ignore the event if the first item under the mouse is not the last opened
+ // popup (or one of its descendant)
+ if (!d->popupWidgets.isEmpty() && !wheelCandidates.isEmpty() && wheelCandidates.first() != d->popupWidgets.back() && !d->popupWidgets.back()->isAncestorOf(wheelCandidates.first())) {
+ wheelEvent->accept();
+ return;
+ }
+#else
+ // Find the first popup under the mouse (including the popup's descendants) starting from the last.
+ // Remove all popups after the one found, or all or them if no popup is under the mouse.
+ // Then continue with the event.
+ QList<QGraphicsWidget *>::const_iterator iter = d->popupWidgets.end();
+ while (--iter >= d->popupWidgets.begin() && !wheelCandidates.isEmpty()) {
+ if (wheelCandidates.first() == *iter || (*iter)->isAncestorOf(wheelCandidates.first()))
+ break;
+ d->removePopup(*iter);
+ }
+#endif
+
+ bool hasSetFocus = false;
+ foreach (QGraphicsItem *item, wheelCandidates) {
+ if (!hasSetFocus && item->isEnabled()
+ && ((item->flags() & QGraphicsItem::ItemIsFocusable) && item->d_ptr->mouseSetsFocus)) {
+ if (item->isWidget() && static_cast<QGraphicsWidget *>(item)->focusPolicy() == Qt::WheelFocus) {
+ hasSetFocus = true;
+ if (item != focusItem())
+ setFocusItem(item, Qt::MouseFocusReason);
+ }
+ }
+
+ wheelEvent->setPos(item->d_ptr->genericMapFromScene(wheelEvent->scenePos(),
+ wheelEvent->widget()));
+ wheelEvent->accept();
+ bool isPanel = item->isPanel();
+ d->sendEvent(item, wheelEvent);
+ if (isPanel || wheelEvent->isAccepted())
+ break;
+ }
+}
+
+/*!
+ This event handler, for event \a event, can be reimplemented in a
+ subclass to receive input method events for the scene.
+
+ The default implementation forwards the event to the focusItem().
+ If no item currently has focus or the current focus item does not
+ accept input methods, this function does nothing.
+
+ \sa QGraphicsItem::inputMethodEvent()
+*/
+void QGraphicsScene::inputMethodEvent(QInputMethodEvent *event)
+{
+ Q_D(QGraphicsScene);
+ if (d->focusItem && (d->focusItem->flags() & QGraphicsItem::ItemAcceptsInputMethod))
+ d->sendEvent(d->focusItem, event);
+}
+
+/*!
+ Draws the background of the scene using \a painter, before any items and
+ the foreground are drawn. Reimplement this function to provide a custom
+ background for the scene.
+
+ All painting is done in \e scene coordinates. The \a rect
+ parameter is the exposed rectangle.
+
+ If all you want is to define a color, texture, or gradient for the
+ background, you can call setBackgroundBrush() instead.
+
+ \sa drawForeground(), drawItems()
+*/
+void QGraphicsScene::drawBackground(QPainter *painter, const QRectF &rect)
+{
+ Q_D(QGraphicsScene);
+
+ if (d->backgroundBrush.style() != Qt::NoBrush) {
+ if (d->painterStateProtection)
+ painter->save();
+ painter->setBrushOrigin(0, 0);
+ painter->fillRect(rect, backgroundBrush());
+ if (d->painterStateProtection)
+ painter->restore();
+ }
+}
+
+/*!
+ Draws the foreground of the scene using \a painter, after the background
+ and all items have been drawn. Reimplement this function to provide a
+ custom foreground for the scene.
+
+ All painting is done in \e scene coordinates. The \a rect
+ parameter is the exposed rectangle.
+
+ If all you want is to define a color, texture or gradient for the
+ foreground, you can call setForegroundBrush() instead.
+
+ \sa drawBackground(), drawItems()
+*/
+void QGraphicsScene::drawForeground(QPainter *painter, const QRectF &rect)
+{
+ Q_D(QGraphicsScene);
+
+ if (d->foregroundBrush.style() != Qt::NoBrush) {
+ if (d->painterStateProtection)
+ painter->save();
+ painter->setBrushOrigin(0, 0);
+ painter->fillRect(rect, foregroundBrush());
+ if (d->painterStateProtection)
+ painter->restore();
+ }
+}
+
+static void _q_paintItem(QGraphicsItem *item, QPainter *painter,
+ const QStyleOptionGraphicsItem *option, QWidget *widget,
+ bool useWindowOpacity, bool painterStateProtection)
+{
+ if (!item->isWidget()) {
+ item->paint(painter, option, widget);
+ return;
+ }
+ QGraphicsWidget *widgetItem = static_cast<QGraphicsWidget *>(item);
+ QGraphicsProxyWidget *proxy = qobject_cast<QGraphicsProxyWidget *>(widgetItem);
+ const qreal windowOpacity = (proxy && proxy->widget() && useWindowOpacity)
+ ? proxy->widget()->windowOpacity() : 1.0;
+ const qreal oldPainterOpacity = painter->opacity();
+
+ if (qFuzzyIsNull(windowOpacity))
+ return;
+ // Set new painter opacity.
+ if (windowOpacity < 1.0)
+ painter->setOpacity(oldPainterOpacity * windowOpacity);
+
+ // set layoutdirection on the painter
+ Qt::LayoutDirection oldLayoutDirection = painter->layoutDirection();
+ painter->setLayoutDirection(widgetItem->layoutDirection());
+
+ if (widgetItem->isWindow() && widgetItem->windowType() != Qt::Popup && widgetItem->windowType() != Qt::ToolTip
+ && !(widgetItem->windowFlags() & Qt::FramelessWindowHint)) {
+ if (painterStateProtection)
+ painter->save();
+ widgetItem->paintWindowFrame(painter, option, widget);
+ if (painterStateProtection)
+ painter->restore();
+ } else if (widgetItem->autoFillBackground()) {
+ painter->fillRect(option->exposedRect, widgetItem->palette().window());
+ }
+
+ widgetItem->paint(painter, option, widget);
+
+ // Restore layoutdirection on the painter.
+ painter->setLayoutDirection(oldLayoutDirection);
+ // Restore painter opacity.
+ if (windowOpacity < 1.0)
+ painter->setOpacity(oldPainterOpacity);
+}
+
+static void _q_paintIntoCache(QPixmap *pix, QGraphicsItem *item, const QRegion &pixmapExposed,
+ const QTransform &itemToPixmap, QPainter::RenderHints renderHints,
+ const QStyleOptionGraphicsItem *option, bool painterStateProtection)
+{
+ QPixmap subPix;
+ QPainter pixmapPainter;
+ QRect br = pixmapExposed.boundingRect();
+
+ // Don't use subpixmap if we get a full update.
+ if (pixmapExposed.isEmpty() || (pixmapExposed.rectCount() == 1 && br.contains(pix->rect()))) {
+ pix->fill(Qt::transparent);
+ pixmapPainter.begin(pix);
+ } else {
+ subPix = QPixmap(br.size());
+ subPix.fill(Qt::transparent);
+ pixmapPainter.begin(&subPix);
+ pixmapPainter.translate(-br.topLeft());
+ if (!pixmapExposed.isEmpty()) {
+ // Applied to subPix; paint is adjusted to the coordinate space is
+ // correct.
+ pixmapPainter.setClipRegion(pixmapExposed);
+ }
+ }
+
+ pixmapPainter.setRenderHints(pixmapPainter.renderHints(), false);
+ pixmapPainter.setRenderHints(renderHints, true);
+ pixmapPainter.setWorldTransform(itemToPixmap, true);
+
+ // Render.
+ _q_paintItem(item, &pixmapPainter, option, 0, false, painterStateProtection);
+ pixmapPainter.end();
+
+ if (!subPix.isNull()) {
+ // Blit the subpixmap into the main pixmap.
+ pixmapPainter.begin(pix);
+ pixmapPainter.setCompositionMode(QPainter::CompositionMode_Source);
+ pixmapPainter.setClipRegion(pixmapExposed);
+ pixmapPainter.drawPixmap(br.topLeft(), subPix);
+ pixmapPainter.end();
+ }
+}
+
+// Copied from qpaintengine_vg.cpp
+// Returns true for 90, 180, and 270 degree rotations.
+static inline bool transformIsSimple(const QTransform& transform)
+{
+ QTransform::TransformationType type = transform.type();
+ if (type <= QTransform::TxScale) {
+ return true;
+ } else if (type == QTransform::TxRotate) {
+ // Check for 90, and 270 degree rotations.
+ qreal m11 = transform.m11();
+ qreal m12 = transform.m12();
+ qreal m21 = transform.m21();
+ qreal m22 = transform.m22();
+ if (m11 == 0.0f && m22 == 0.0f) {
+ if (m12 == 1.0f && m21 == -1.0f)
+ return true; // 90 degrees.
+ else if (m12 == -1.0f && m21 == 1.0f)
+ return true; // 270 degrees.
+ else if (m12 == -1.0f && m21 == -1.0f)
+ return true; // 90 degrees inverted y.
+ else if (m12 == 1.0f && m21 == 1.0f)
+ return true; // 270 degrees inverted y.
+ }
+ }
+ return false;
+}
+
+/*!
+ \internal
+
+ Draws items directly, or using cache.
+*/
+void QGraphicsScenePrivate::drawItemHelper(QGraphicsItem *item, QPainter *painter,
+ const QStyleOptionGraphicsItem *option, QWidget *widget,
+ bool painterStateProtection)
+{
+ QGraphicsItemPrivate *itemd = item->d_ptr.data();
+ QGraphicsItem::CacheMode cacheMode = QGraphicsItem::CacheMode(itemd->cacheMode);
+
+ // Render directly, using no cache.
+ if (cacheMode == QGraphicsItem::NoCache
+#ifdef Q_WS_X11
+ || !X11->use_xrender
+#endif
+ ) {
+ _q_paintItem(static_cast<QGraphicsWidget *>(item), painter, option, widget, true, painterStateProtection);
+ return;
+ }
+
+ const qreal oldPainterOpacity = painter->opacity();
+ qreal newPainterOpacity = oldPainterOpacity;
+ QGraphicsProxyWidget *proxy = item->isWidget() ? qobject_cast<QGraphicsProxyWidget *>(static_cast<QGraphicsWidget *>(item)) : 0;
+ if (proxy && proxy->widget()) {
+ const qreal windowOpacity = proxy->widget()->windowOpacity();
+ if (windowOpacity < 1.0)
+ newPainterOpacity *= windowOpacity;
+ }
+
+ // Item's (local) bounding rect
+ QRectF brect = item->boundingRect();
+ QRectF adjustedBrect(brect);
+ _q_adjustRect(&adjustedBrect);
+ if (adjustedBrect.isEmpty())
+ return;
+
+ // Fetch the off-screen transparent buffer and exposed area info.
+ QPixmapCache::Key pixmapKey;
+ QPixmap pix;
+ bool pixmapFound;
+ QGraphicsItemCache *itemCache = itemd->extraItemCache();
+ if (cacheMode == QGraphicsItem::ItemCoordinateCache) {
+ pixmapKey = itemCache->key;
+ } else {
+ pixmapKey = itemCache->deviceData.value(widget).key;
+ }
+
+ // Find pixmap in cache.
+ pixmapFound = QPixmapCache::find(pixmapKey, &pix);
+
+ // Render using item coordinate cache mode.
+ if (cacheMode == QGraphicsItem::ItemCoordinateCache) {
+ QSize pixmapSize;
+ bool fixedCacheSize = false;
+ QRect br = brect.toAlignedRect();
+ if ((fixedCacheSize = itemCache->fixedSize.isValid())) {
+ pixmapSize = itemCache->fixedSize;
+ } else {
+ pixmapSize = br.size();
+ }
+
+ // Create or recreate the pixmap.
+ int adjust = itemCache->fixedSize.isValid() ? 0 : 2;
+ QSize adjustSize(adjust*2, adjust*2);
+ br.adjust(-adjust, -adjust, adjust, adjust);
+ if (pix.isNull() || (!fixedCacheSize && (pixmapSize + adjustSize) != pix.size())) {
+ pix = QPixmap(pixmapSize + adjustSize);
+ itemCache->boundingRect = br;
+ itemCache->exposed.clear();
+ itemCache->allExposed = true;
+ } else if (itemCache->boundingRect != br) {
+ itemCache->boundingRect = br;
+ itemCache->exposed.clear();
+ itemCache->allExposed = true;
+ }
+
+ // Redraw any newly exposed areas.
+ if (itemCache->allExposed || !itemCache->exposed.isEmpty()) {
+
+ //We know that we will modify the pixmap, removing it from the cache
+ //will detach the one we have and avoid a deep copy
+ if (pixmapFound)
+ QPixmapCache::remove(pixmapKey);
+
+ // Fit the item's bounding rect into the pixmap's coordinates.
+ QTransform itemToPixmap;
+ if (fixedCacheSize) {
+ const QPointF scale(pixmapSize.width() / brect.width(), pixmapSize.height() / brect.height());
+ itemToPixmap.scale(scale.x(), scale.y());
+ }
+ itemToPixmap.translate(-br.x(), -br.y());
+
+ // Generate the item's exposedRect and map its list of expose
+ // rects to device coordinates.
+ styleOptionTmp = *option;
+ QRegion pixmapExposed;
+ QRectF exposedRect;
+ if (!itemCache->allExposed) {
+ for (int i = 0; i < itemCache->exposed.size(); ++i) {
+ QRectF r = itemCache->exposed.at(i);
+ exposedRect |= r;
+ pixmapExposed += itemToPixmap.mapRect(r).toAlignedRect();
+ }
+ } else {
+ exposedRect = brect;
+ }
+ styleOptionTmp.exposedRect = exposedRect;
+
+ // Render.
+ _q_paintIntoCache(&pix, item, pixmapExposed, itemToPixmap, painter->renderHints(),
+ &styleOptionTmp, painterStateProtection);
+
+ // insert this pixmap into the cache.
+ itemCache->key = QPixmapCache::insert(pix);
+
+ // Reset expose data.
+ itemCache->allExposed = false;
+ itemCache->exposed.clear();
+ }
+
+ // Redraw the exposed area using the transformed painter. Depending on
+ // the hardware, this may be a server-side operation, or an expensive
+ // qpixmap-image-transform-pixmap roundtrip.
+ if (newPainterOpacity != oldPainterOpacity) {
+ painter->setOpacity(newPainterOpacity);
+ painter->drawPixmap(br.topLeft(), pix);
+ painter->setOpacity(oldPainterOpacity);
+ } else {
+ painter->drawPixmap(br.topLeft(), pix);
+ }
+ return;
+ }
+
+ // Render using device coordinate cache mode.
+ if (cacheMode == QGraphicsItem::DeviceCoordinateCache) {
+ // Find the item's bounds in device coordinates.
+ QRectF deviceBounds = painter->worldTransform().mapRect(brect);
+ QRect deviceRect = deviceBounds.toRect().adjusted(-1, -1, 1, 1);
+ if (deviceRect.isEmpty())
+ return;
+ QRect viewRect = widget ? widget->rect() : QRect();
+ if (widget && !viewRect.intersects(deviceRect))
+ return;
+
+ // Resort to direct rendering if the device rect exceeds the
+ // (optional) maximum bounds. (QGraphicsSvgItem uses this).
+ QSize maximumCacheSize =
+ itemd->extra(QGraphicsItemPrivate::ExtraMaxDeviceCoordCacheSize).toSize();
+ if (!maximumCacheSize.isEmpty()
+ && (deviceRect.width() > maximumCacheSize.width()
+ || deviceRect.height() > maximumCacheSize.height())) {
+ _q_paintItem(static_cast<QGraphicsWidget *>(item), painter, option, widget,
+ oldPainterOpacity != newPainterOpacity, painterStateProtection);
+ return;
+ }
+
+ // Create or reuse offscreen pixmap, possibly scroll/blit from the old one.
+ // If the world transform is rotated we always recreate the cache to avoid
+ // wrong blending.
+ bool pixModified = false;
+ QGraphicsItemCache::DeviceData *deviceData = &itemCache->deviceData[widget];
+ bool invertable = true;
+ QTransform diff = deviceData->lastTransform.inverted(&invertable);
+ if (invertable)
+ diff *= painter->worldTransform();
+ deviceData->lastTransform = painter->worldTransform();
+ bool allowPartialCacheExposure = false;
+ bool simpleTransform = invertable && diff.type() <= QTransform::TxTranslate
+ && transformIsSimple(painter->worldTransform());
+ if (!simpleTransform) {
+ pixModified = true;
+ itemCache->allExposed = true;
+ itemCache->exposed.clear();
+ deviceData->cacheIndent = QPoint();
+ pix = QPixmap();
+ } else if (!viewRect.isNull()) {
+ allowPartialCacheExposure = deviceData->cacheIndent != QPoint();
+ }
+
+ // Allow partial cache exposure if the device rect isn't fully contained and
+ // deviceRect is 20% taller or wider than the viewRect.
+ if (!allowPartialCacheExposure && !viewRect.isNull() && !viewRect.contains(deviceRect)) {
+ allowPartialCacheExposure = (viewRect.width() * 1.2 < deviceRect.width())
+ || (viewRect.height() * 1.2 < deviceRect.height());
+ }
+
+ QRegion scrollExposure;
+ if (allowPartialCacheExposure) {
+ // Part of pixmap is drawn. Either device contains viewrect (big
+ // item covers whole screen) or parts of device are outside the
+ // viewport. In either case the device rect must be the intersect
+ // between the two.
+ int dx = deviceRect.left() < viewRect.left() ? viewRect.left() - deviceRect.left() : 0;
+ int dy = deviceRect.top() < viewRect.top() ? viewRect.top() - deviceRect.top() : 0;
+ QPoint newCacheIndent(dx, dy);
+ deviceRect &= viewRect;
+
+ if (pix.isNull()) {
+ deviceData->cacheIndent = QPoint();
+ itemCache->allExposed = true;
+ itemCache->exposed.clear();
+ pixModified = true;
+ }
+
+ // Copy / "scroll" the old pixmap onto the new ole and calculate
+ // scrolled exposure.
+ if (newCacheIndent != deviceData->cacheIndent || deviceRect.size() != pix.size()) {
+ QPoint diff = newCacheIndent - deviceData->cacheIndent;
+ QPixmap newPix(deviceRect.size());
+ // ### Investigate removing this fill (test with Plasma and
+ // graphicssystem raster).
+ newPix.fill(Qt::transparent);
+ if (!pix.isNull()) {
+ QPainter newPixPainter(&newPix);
+ newPixPainter.drawPixmap(-diff, pix);
+ newPixPainter.end();
+ }
+ QRegion exposed;
+ exposed += newPix.rect();
+ if (!pix.isNull())
+ exposed -= QRect(-diff, pix.size());
+ scrollExposure = exposed;
+
+ pix = newPix;
+ pixModified = true;
+ }
+ deviceData->cacheIndent = newCacheIndent;
+ } else {
+ // Full pixmap is drawn.
+ deviceData->cacheIndent = QPoint();
+
+ // Auto-adjust the pixmap size.
+ if (deviceRect.size() != pix.size()) {
+ // exposed needs to cover the whole pixmap
+ pix = QPixmap(deviceRect.size());
+ pixModified = true;
+ itemCache->allExposed = true;
+ itemCache->exposed.clear();
+ }
+ }
+
+ // Check for newly invalidated areas.
+ if (itemCache->allExposed || !itemCache->exposed.isEmpty() || !scrollExposure.isEmpty()) {
+ //We know that we will modify the pixmap, removing it from the cache
+ //will detach the one we have and avoid a deep copy
+ if (pixmapFound)
+ QPixmapCache::remove(pixmapKey);
+
+ // Construct an item-to-pixmap transform.
+ QPointF p = deviceRect.topLeft();
+ QTransform itemToPixmap = painter->worldTransform();
+ if (!p.isNull())
+ itemToPixmap *= QTransform::fromTranslate(-p.x(), -p.y());
+
+ // Map the item's logical expose to pixmap coordinates.
+ QRegion pixmapExposed = scrollExposure;
+ if (!itemCache->allExposed) {
+ const QVector<QRectF> &exposed = itemCache->exposed;
+ for (int i = 0; i < exposed.size(); ++i)
+ pixmapExposed += itemToPixmap.mapRect(exposed.at(i)).toRect().adjusted(-1, -1, 1, 1);
+ }
+
+ // Calculate the style option's exposedRect.
+ QRectF br;
+ if (itemCache->allExposed) {
+ br = item->boundingRect();
+ } else {
+ const QVector<QRectF> &exposed = itemCache->exposed;
+ for (int i = 0; i < exposed.size(); ++i)
+ br |= exposed.at(i);
+ QTransform pixmapToItem = itemToPixmap.inverted();
+ foreach (QRect r, scrollExposure.rects())
+ br |= pixmapToItem.mapRect(r);
+ }
+ styleOptionTmp = *option;
+ styleOptionTmp.exposedRect = br.adjusted(-1, -1, 1, 1);
+
+ // Render the exposed areas.
+ _q_paintIntoCache(&pix, item, pixmapExposed, itemToPixmap, painter->renderHints(),
+ &styleOptionTmp, painterStateProtection);
+
+ // Reset expose data.
+ pixModified = true;
+ itemCache->allExposed = false;
+ itemCache->exposed.clear();
+ }
+
+ if (pixModified) {
+ // Insert this pixmap into the cache.
+ deviceData->key = QPixmapCache::insert(pix);
+ }
+
+ // Redraw the exposed area using an untransformed painter. This
+ // effectively becomes a bitblit that does not transform the cache.
+ QTransform restoreTransform = painter->worldTransform();
+ painter->setWorldTransform(QTransform());
+ if (newPainterOpacity != oldPainterOpacity) {
+ painter->setOpacity(newPainterOpacity);
+ painter->drawPixmap(deviceRect.topLeft(), pix);
+ painter->setOpacity(oldPainterOpacity);
+ } else {
+ painter->drawPixmap(deviceRect.topLeft(), pix);
+ }
+ painter->setWorldTransform(restoreTransform);
+ return;
+ }
+}
+
+void QGraphicsScenePrivate::drawItems(QPainter *painter, const QTransform *const viewTransform,
+ QRegion *exposedRegion, QWidget *widget)
+{
+ // Make sure we don't have unpolished items before we draw.
+ if (!unpolishedItems.isEmpty())
+ _q_polishItems();
+
+ updateAll = false;
+ QRectF exposedSceneRect;
+ if (exposedRegion && indexMethod != QGraphicsScene::NoIndex) {
+ exposedSceneRect = exposedRegion->boundingRect().adjusted(-1, -1, 1, 1);
+ if (viewTransform)
+ exposedSceneRect = viewTransform->inverted().mapRect(exposedSceneRect);
+ }
+ const QList<QGraphicsItem *> tli = index->estimateTopLevelItems(exposedSceneRect, Qt::AscendingOrder);
+ for (int i = 0; i < tli.size(); ++i)
+ drawSubtreeRecursive(tli.at(i), painter, viewTransform, exposedRegion, widget);
+}
+
+void QGraphicsScenePrivate::drawSubtreeRecursive(QGraphicsItem *item, QPainter *painter,
+ const QTransform *const viewTransform,
+ QRegion *exposedRegion, QWidget *widget,
+ qreal parentOpacity, const QTransform *const effectTransform)
+{
+ Q_ASSERT(item);
+
+ if (!item->d_ptr->visible)
+ return;
+
+ const bool itemHasContents = !(item->d_ptr->flags & QGraphicsItem::ItemHasNoContents);
+ const bool itemHasChildren = !item->d_ptr->children.isEmpty();
+ if (!itemHasContents && !itemHasChildren)
+ return; // Item has neither contents nor children!(?)
+
+ const qreal opacity = item->d_ptr->combineOpacityFromParent(parentOpacity);
+ const bool itemIsFullyTransparent = QGraphicsItemPrivate::isOpacityNull(opacity);
+ if (itemIsFullyTransparent && (!itemHasChildren || item->d_ptr->childrenCombineOpacity()))
+ return;
+
+ QTransform transform(Qt::Uninitialized);
+ QTransform *transformPtr = 0;
+ bool translateOnlyTransform = false;
+#define ENSURE_TRANSFORM_PTR \
+ if (!transformPtr) { \
+ Q_ASSERT(!itemIsUntransformable); \
+ if (viewTransform) { \
+ transform = item->d_ptr->sceneTransform; \
+ transform *= *viewTransform; \
+ transformPtr = &transform; \
+ } else { \
+ transformPtr = &item->d_ptr->sceneTransform; \
+ translateOnlyTransform = item->d_ptr->sceneTransformTranslateOnly; \
+ } \
+ }
+
+ // Update the item's scene transform if the item is transformable;
+ // otherwise calculate the full transform,
+ bool wasDirtyParentSceneTransform = false;
+ const bool itemIsUntransformable = item->d_ptr->itemIsUntransformable();
+ if (itemIsUntransformable) {
+ transform = item->deviceTransform(viewTransform ? *viewTransform : QTransform());
+ transformPtr = &transform;
+ } else if (item->d_ptr->dirtySceneTransform) {
+ item->d_ptr->updateSceneTransformFromParent();
+ Q_ASSERT(!item->d_ptr->dirtySceneTransform);
+ wasDirtyParentSceneTransform = true;
+ }
+
+ const bool itemClipsChildrenToShape = (item->d_ptr->flags & QGraphicsItem::ItemClipsChildrenToShape);
+ bool drawItem = itemHasContents && !itemIsFullyTransparent;
+ if (drawItem) {
+ const QRectF brect = adjustedItemEffectiveBoundingRect(item);
+ ENSURE_TRANSFORM_PTR
+ QRect viewBoundingRect = translateOnlyTransform ? brect.translated(transformPtr->dx(), transformPtr->dy()).toAlignedRect()
+ : transformPtr->mapRect(brect).toAlignedRect();
+ viewBoundingRect.adjust(-int(rectAdjust), -int(rectAdjust), rectAdjust, rectAdjust);
+ if (widget)
+ item->d_ptr->paintedViewBoundingRects.insert(widget, viewBoundingRect);
+ drawItem = exposedRegion ? exposedRegion->intersects(viewBoundingRect)
+ : !viewBoundingRect.normalized().isEmpty();
+ if (!drawItem) {
+ if (!itemHasChildren)
+ return;
+ if (itemClipsChildrenToShape) {
+ if (wasDirtyParentSceneTransform)
+ item->d_ptr->invalidateChildrenSceneTransform();
+ return;
+ }
+ }
+ } // else we know for sure this item has children we must process.
+
+ if (itemHasChildren && itemClipsChildrenToShape)
+ ENSURE_TRANSFORM_PTR;
+
+#ifndef QT_NO_GRAPHICSEFFECT
+ if (item->d_ptr->graphicsEffect && item->d_ptr->graphicsEffect->isEnabled()) {
+ ENSURE_TRANSFORM_PTR;
+ QGraphicsItemPaintInfo info(viewTransform, transformPtr, effectTransform, exposedRegion, widget, &styleOptionTmp,
+ painter, opacity, wasDirtyParentSceneTransform, itemHasContents && !itemIsFullyTransparent);
+ 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);
+ painter->setOpacity(opacity);
+
+ if (sourced->currentCachedSystem() != Qt::LogicalCoordinates
+ && sourced->lastEffectTransform != painter->worldTransform())
+ {
+ if (sourced->lastEffectTransform.type() <= QTransform::TxTranslate
+ && painter->worldTransform().type() <= QTransform::TxTranslate)
+ {
+ QRectF sourceRect = sourced->boundingRect(Qt::DeviceCoordinates);
+ QRect effectRect = sourced->paddedEffectRect(Qt::DeviceCoordinates, sourced->currentCachedMode(), sourceRect);
+
+ sourced->setCachedOffset(effectRect.topLeft());
+ } else {
+ sourced->invalidateCache(QGraphicsEffectSourcePrivate::TransformChanged);
+ }
+
+ sourced->lastEffectTransform = painter->worldTransform();
+ }
+
+ item->d_ptr->graphicsEffect->draw(painter);
+ painter->setWorldTransform(restoreTransform);
+ sourced->info = 0;
+ } else
+#endif //QT_NO_GRAPHICSEFFECT
+ {
+ draw(item, painter, viewTransform, transformPtr, exposedRegion, widget, opacity,
+ effectTransform, wasDirtyParentSceneTransform, drawItem);
+ }
+}
+
+static inline void setClip(QPainter *painter, QGraphicsItem *item)
+{
+ painter->save();
+ QRectF clipRect;
+ const QPainterPath clipPath(item->shape());
+ if (QPathClipper::pathToRect(clipPath, &clipRect))
+ painter->setClipRect(clipRect, Qt::IntersectClip);
+ else
+ painter->setClipPath(clipPath, Qt::IntersectClip);
+}
+
+static inline void setWorldTransform(QPainter *painter, const QTransform *const transformPtr,
+ const QTransform *effectTransform)
+{
+ Q_ASSERT(transformPtr);
+ if (effectTransform)
+ painter->setWorldTransform(*transformPtr * *effectTransform);
+ else
+ painter->setWorldTransform(*transformPtr);
+}
+
+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 = QGraphicsItemPrivate::isOpacityNull(opacity);
+ const bool itemClipsChildrenToShape = (item->d_ptr->flags & QGraphicsItem::ItemClipsChildrenToShape);
+ const bool itemHasChildren = !item->d_ptr->children.isEmpty();
+ bool setChildClip = itemClipsChildrenToShape;
+ bool itemHasChildrenStackedBehind = false;
+
+ int i = 0;
+ if (itemHasChildren) {
+ if (itemClipsChildrenToShape)
+ setWorldTransform(painter, transformPtr, effectTransform);
+
+ item->d_ptr->ensureSortedChildren();
+ // Items with the 'ItemStacksBehindParent' flag are put in front of the list
+ // so all we have to do is to check the first item.
+ itemHasChildrenStackedBehind = (item->d_ptr->children.at(0)->d_ptr->flags
+ & QGraphicsItem::ItemStacksBehindParent);
+
+ if (itemHasChildrenStackedBehind) {
+ if (itemClipsChildrenToShape) {
+ setClip(painter, item);
+ setChildClip = false;
+ }
+
+ // Draw children behind
+ for (i = 0; i < item->d_ptr->children.size(); ++i) {
+ QGraphicsItem *child = item->d_ptr->children.at(i);
+ if (wasDirtyParentSceneTransform)
+ child->d_ptr->dirtySceneTransform = 1;
+ if (!(child->d_ptr->flags & QGraphicsItem::ItemStacksBehindParent))
+ break;
+ if (itemIsFullyTransparent && !(child->d_ptr->flags & QGraphicsItem::ItemIgnoresParentOpacity))
+ continue;
+ drawSubtreeRecursive(child, painter, viewTransform, exposedRegion, widget, opacity, effectTransform);
+ }
+ }
+ }
+
+ // Draw item
+ if (drawItem) {
+ Q_ASSERT(!itemIsFullyTransparent);
+ Q_ASSERT(!(item->d_ptr->flags & QGraphicsItem::ItemHasNoContents));
+ Q_ASSERT(transformPtr);
+ item->d_ptr->initStyleOption(&styleOptionTmp, *transformPtr, exposedRegion
+ ? *exposedRegion : QRegion(), exposedRegion == 0);
+
+ const bool itemClipsToShape = item->d_ptr->flags & QGraphicsItem::ItemClipsToShape;
+ bool restorePainterClip = false;
+
+ if (!itemHasChildren || !itemClipsChildrenToShape) {
+ // Item does not have children or clip children to shape.
+ setWorldTransform(painter, transformPtr, effectTransform);
+ if ((restorePainterClip = itemClipsToShape))
+ setClip(painter, item);
+ } else if (itemHasChildrenStackedBehind){
+ // Item clips children to shape and has children stacked behind, which means
+ // the painter is already clipped to the item's shape.
+ if (itemClipsToShape) {
+ // The clip is already correct. Ensure correct world transform.
+ setWorldTransform(painter, transformPtr, effectTransform);
+ } else {
+ // Remove clip (this also ensures correct world transform).
+ painter->restore();
+ setChildClip = true;
+ }
+ } else if (itemClipsToShape) {
+ // Item clips children and itself to shape. It does not have hildren stacked
+ // behind, which means the clip has not yet been set. We set it now and re-use it
+ // for the children.
+ setClip(painter, item);
+ setChildClip = false;
+ }
+
+ if (painterStateProtection && !restorePainterClip)
+ painter->save();
+
+ painter->setOpacity(opacity);
+ if (!item->d_ptr->cacheMode && !item->d_ptr->isWidget)
+ item->paint(painter, &styleOptionTmp, widget);
+ else
+ drawItemHelper(item, painter, &styleOptionTmp, widget, painterStateProtection);
+
+ if (painterStateProtection || restorePainterClip)
+ painter->restore();
+
+ static int drawRect = qgetenv("QT_DRAW_SCENE_ITEM_RECTS").toInt();
+ if (drawRect) {
+ QPen oldPen = painter->pen();
+ QBrush oldBrush = painter->brush();
+ quintptr ptr = reinterpret_cast<quintptr>(item);
+ const QColor color = QColor::fromHsv(ptr % 255, 255, 255);
+ painter->setPen(color);
+ painter->setBrush(Qt::NoBrush);
+ painter->drawRect(adjustedItemBoundingRect(item));
+ painter->setPen(oldPen);
+ painter->setBrush(oldBrush);
+ }
+ }
+
+ // Draw children in front
+ if (itemHasChildren) {
+ if (setChildClip)
+ setClip(painter, item);
+
+ for (; i < item->d_ptr->children.size(); ++i) {
+ QGraphicsItem *child = item->d_ptr->children.at(i);
+ if (wasDirtyParentSceneTransform)
+ child->d_ptr->dirtySceneTransform = 1;
+ if (itemIsFullyTransparent && !(child->d_ptr->flags & QGraphicsItem::ItemIgnoresParentOpacity))
+ continue;
+ drawSubtreeRecursive(child, painter, viewTransform, exposedRegion, widget, opacity, effectTransform);
+ }
+
+ // Restore child clip
+ if (itemClipsChildrenToShape)
+ painter->restore();
+ }
+}
+
+void QGraphicsScenePrivate::markDirty(QGraphicsItem *item, const QRectF &rect, bool invalidateChildren,
+ bool force, bool ignoreOpacity, bool removingItemFromScene,
+ bool updateBoundingRect)
+{
+ Q_ASSERT(item);
+ if (updateAll)
+ return;
+
+ if (removingItemFromScene && !ignoreOpacity && !item->d_ptr->ignoreOpacity) {
+ // If any of the item's ancestors ignore opacity, it means that the opacity
+ // was set to 0 (and the update request has not yet been processed). That
+ // also means that we have to ignore the opacity for the item itself; otherwise
+ // things like: parent->setOpacity(0); scene->removeItem(child) won't work.
+ // Note that we only do this when removing items from the scene. In all other
+ // cases the ignoreOpacity bit propagates properly in processDirtyItems, but
+ // since the item is removed immediately it won't be processed there.
+ QGraphicsItem *p = item->d_ptr->parent;
+ while (p) {
+ if (p->d_ptr->ignoreOpacity) {
+ item->d_ptr->ignoreOpacity = true;
+ break;
+ }
+ p = p->d_ptr->parent;
+ }
+ }
+
+ if (item->d_ptr->discardUpdateRequest(/*ignoreVisibleBit=*/force,
+ /*ignoreDirtyBit=*/removingItemFromScene || invalidateChildren,
+ /*ignoreOpacity=*/ignoreOpacity)) {
+ if (item->d_ptr->dirty) {
+ // The item is already marked as dirty and will be processed later. However,
+ // we have to make sure ignoreVisible and ignoreOpacity are set properly;
+ // otherwise things like: item->update(); item->hide() (force is now true)
+ // won't work as expected.
+ if (force)
+ item->d_ptr->ignoreVisible = 1;
+ if (ignoreOpacity)
+ item->d_ptr->ignoreOpacity = 1;
+ }
+ return;
+ }
+
+ const bool fullItemUpdate = rect.isNull();
+ if (!fullItemUpdate && rect.isEmpty())
+ return;
+
+ if (!processDirtyItemsEmitted) {
+ QMetaMethod method = q_ptr->metaObject()->method(processDirtyItemsIndex);
+ method.invoke(q_ptr, Qt::QueuedConnection);
+// QMetaObject::invokeMethod(q_ptr, "_q_processDirtyItems", Qt::QueuedConnection);
+ processDirtyItemsEmitted = true;
+ }
+
+ if (removingItemFromScene) {
+ // Note that this function can be called from the item's destructor, so
+ // do NOT call any virtual functions on it within this block.
+ if (isSignalConnected(changedSignalIndex) || views.isEmpty()) {
+ // This block of code is kept for compatibility. Since 4.5, by default
+ // QGraphicsView does not connect the signal and we use the below
+ // method of delivering updates.
+ q_func()->update();
+ return;
+ }
+
+ for (int i = 0; i < views.size(); ++i) {
+ QGraphicsViewPrivate *viewPrivate = views.at(i)->d_func();
+ QRect rect = item->d_ptr->paintedViewBoundingRects.value(viewPrivate->viewport);
+ rect.translate(viewPrivate->dirtyScrollOffset);
+ viewPrivate->updateRect(rect);
+ }
+ return;
+ }
+
+ bool hasNoContents = item->d_ptr->flags & QGraphicsItem::ItemHasNoContents;
+ if (!hasNoContents) {
+ item->d_ptr->dirty = 1;
+ if (fullItemUpdate)
+ item->d_ptr->fullUpdatePending = 1;
+ else if (!item->d_ptr->fullUpdatePending)
+ item->d_ptr->needsRepaint |= rect;
+ } else if (item->d_ptr->graphicsEffect) {
+ invalidateChildren = true;
+ }
+
+ if (invalidateChildren) {
+ item->d_ptr->allChildrenDirty = 1;
+ item->d_ptr->dirtyChildren = 1;
+ }
+
+ if (force)
+ item->d_ptr->ignoreVisible = 1;
+ if (ignoreOpacity)
+ item->d_ptr->ignoreOpacity = 1;
+
+ if (!updateBoundingRect)
+ item->d_ptr->markParentDirty();
+}
+
+static inline bool updateHelper(QGraphicsViewPrivate *view, QGraphicsItemPrivate *item,
+ const QRectF &rect, bool itemIsUntransformable)
+{
+ Q_ASSERT(view);
+ Q_ASSERT(item);
+
+ QGraphicsItem *itemq = static_cast<QGraphicsItem *>(item->q_ptr);
+ QGraphicsView *viewq = static_cast<QGraphicsView *>(view->q_ptr);
+
+ if (itemIsUntransformable) {
+ const QTransform xform = itemq->deviceTransform(viewq->viewportTransform());
+ if (!item->hasBoundingRegionGranularity)
+ return view->updateRectF(xform.mapRect(rect));
+ return view->updateRegion(rect, xform);
+ }
+
+ if (item->sceneTransformTranslateOnly && view->identityMatrix) {
+ const qreal dx = item->sceneTransform.dx();
+ const qreal dy = item->sceneTransform.dy();
+ QRectF r(rect);
+ r.translate(dx - view->horizontalScroll(), dy - view->verticalScroll());
+ return view->updateRectF(r);
+ }
+
+ if (!viewq->isTransformed()) {
+ if (!item->hasBoundingRegionGranularity)
+ return view->updateRectF(item->sceneTransform.mapRect(rect));
+ return view->updateRegion(rect, item->sceneTransform);
+ }
+
+ QTransform xform = item->sceneTransform;
+ xform *= viewq->viewportTransform();
+ if (!item->hasBoundingRegionGranularity)
+ return view->updateRectF(xform.mapRect(rect));
+ return view->updateRegion(rect, xform);
+}
+
+void QGraphicsScenePrivate::processDirtyItemsRecursive(QGraphicsItem *item, bool dirtyAncestorContainsChildren,
+ qreal parentOpacity)
+{
+ Q_Q(QGraphicsScene);
+ Q_ASSERT(item);
+ Q_ASSERT(!updateAll);
+
+ if (!item->d_ptr->dirty && !item->d_ptr->dirtyChildren) {
+ resetDirtyItem(item);
+ return;
+ }
+
+ const bool itemIsHidden = !item->d_ptr->ignoreVisible && !item->d_ptr->visible;
+ if (itemIsHidden) {
+ resetDirtyItem(item, /*recursive=*/true);
+ return;
+ }
+
+ bool itemHasContents = !(item->d_ptr->flags & QGraphicsItem::ItemHasNoContents);
+ const bool itemHasChildren = !item->d_ptr->children.isEmpty();
+ if (!itemHasContents) {
+ if (!itemHasChildren) {
+ resetDirtyItem(item);
+ return; // Item has neither contents nor children!(?)
+ }
+ if (item->d_ptr->graphicsEffect)
+ itemHasContents = true;
+ }
+
+ const qreal opacity = item->d_ptr->combineOpacityFromParent(parentOpacity);
+ const bool itemIsFullyTransparent = !item->d_ptr->ignoreOpacity
+ && QGraphicsItemPrivate::isOpacityNull(opacity);
+ if (itemIsFullyTransparent && (!itemHasChildren || item->d_ptr->childrenCombineOpacity())) {
+ resetDirtyItem(item, /*recursive=*/itemHasChildren);
+ return;
+ }
+
+ bool wasDirtyParentSceneTransform = item->d_ptr->dirtySceneTransform;
+ const bool itemIsUntransformable = item->d_ptr->itemIsUntransformable();
+ if (wasDirtyParentSceneTransform && !itemIsUntransformable) {
+ item->d_ptr->updateSceneTransformFromParent();
+ Q_ASSERT(!item->d_ptr->dirtySceneTransform);
+ }
+
+ const bool wasDirtyParentViewBoundingRects = item->d_ptr->paintedViewBoundingRectsNeedRepaint;
+ if (itemIsFullyTransparent || !itemHasContents || dirtyAncestorContainsChildren) {
+ // Make sure we don't process invisible items or items with no content.
+ item->d_ptr->dirty = 0;
+ item->d_ptr->fullUpdatePending = 0;
+ // Might have a dirty view bounding rect otherwise.
+ if (itemIsFullyTransparent || !itemHasContents)
+ item->d_ptr->paintedViewBoundingRectsNeedRepaint = 0;
+ }
+
+ if (!hasSceneRect && item->d_ptr->geometryChanged && item->d_ptr->visible) {
+ // Update growingItemsBoundingRect.
+ if (item->d_ptr->sceneTransformTranslateOnly) {
+ growingItemsBoundingRect |= item->boundingRect().translated(item->d_ptr->sceneTransform.dx(),
+ item->d_ptr->sceneTransform.dy());
+ } else {
+ growingItemsBoundingRect |= item->d_ptr->sceneTransform.mapRect(item->boundingRect());
+ }
+ }
+
+ // Process item.
+ if (item->d_ptr->dirty || item->d_ptr->paintedViewBoundingRectsNeedRepaint) {
+ const bool useCompatUpdate = views.isEmpty() || isSignalConnected(changedSignalIndex);
+ const QRectF itemBoundingRect = adjustedItemEffectiveBoundingRect(item);
+
+ if (useCompatUpdate && !itemIsUntransformable && qFuzzyIsNull(item->boundingRegionGranularity())) {
+ // This block of code is kept for compatibility. Since 4.5, by default
+ // QGraphicsView does not connect the signal and we use the below
+ // method of delivering updates.
+ if (item->d_ptr->sceneTransformTranslateOnly) {
+ q->update(itemBoundingRect.translated(item->d_ptr->sceneTransform.dx(),
+ item->d_ptr->sceneTransform.dy()));
+ } else {
+ QRectF rect = item->d_ptr->sceneTransform.mapRect(itemBoundingRect);
+ if (!rect.isEmpty())
+ q->update(rect);
+ }
+ } else {
+ QRectF dirtyRect;
+ bool uninitializedDirtyRect = true;
+
+ for (int j = 0; j < views.size(); ++j) {
+ QGraphicsView *view = views.at(j);
+ QGraphicsViewPrivate *viewPrivate = view->d_func();
+ QRect &paintedViewBoundingRect = item->d_ptr->paintedViewBoundingRects[viewPrivate->viewport];
+ if (viewPrivate->fullUpdatePending
+ || viewPrivate->viewportUpdateMode == QGraphicsView::NoViewportUpdate) {
+ // Okay, if we have a full update pending or no viewport update, this item's
+ // paintedViewBoundingRect will be updated correctly in the next paintEvent if
+ // it is inside the viewport, but for now we can pretend that it is outside.
+ paintedViewBoundingRect = QRect(-1, -1, -1, -1);
+ continue;
+ }
+
+ if (item->d_ptr->paintedViewBoundingRectsNeedRepaint) {
+ paintedViewBoundingRect.translate(viewPrivate->dirtyScrollOffset);
+ if (!viewPrivate->updateRect(paintedViewBoundingRect))
+ paintedViewBoundingRect = QRect(-1, -1, -1, -1); // Outside viewport.
+ }
+
+ if (!item->d_ptr->dirty)
+ continue;
+
+ if (!item->d_ptr->paintedViewBoundingRectsNeedRepaint
+ && paintedViewBoundingRect.x() == -1 && paintedViewBoundingRect.y() == -1
+ && paintedViewBoundingRect.width() == -1 && paintedViewBoundingRect.height() == -1) {
+ continue; // Outside viewport.
+ }
+
+ if (uninitializedDirtyRect) {
+ dirtyRect = itemBoundingRect;
+ if (!item->d_ptr->fullUpdatePending) {
+ _q_adjustRect(&item->d_ptr->needsRepaint);
+ dirtyRect &= item->d_ptr->needsRepaint;
+ }
+ uninitializedDirtyRect = false;
+ }
+
+ if (dirtyRect.isEmpty())
+ continue; // Discard updates outside the bounding rect.
+
+ if (!updateHelper(viewPrivate, item->d_ptr.data(), dirtyRect, itemIsUntransformable)
+ && item->d_ptr->paintedViewBoundingRectsNeedRepaint) {
+ paintedViewBoundingRect = QRect(-1, -1, -1, -1); // Outside viewport.
+ }
+ }
+ }
+ }
+
+ // Process children.
+ if (itemHasChildren && item->d_ptr->dirtyChildren) {
+ const bool itemClipsChildrenToShape = item->d_ptr->flags & QGraphicsItem::ItemClipsChildrenToShape;
+ // Items with no content are threated as 'dummy' items which means they are never drawn and
+ // 'processed', so the painted view bounding rect is never up-to-date. This means that whenever
+ // such an item changes geometry, its children have to take care of the update regardless
+ // of whether the item clips children to shape or not.
+ const bool bypassUpdateClip = !itemHasContents && wasDirtyParentViewBoundingRects;
+ if (itemClipsChildrenToShape && !bypassUpdateClip) {
+ // Make sure child updates are clipped to the item's bounding rect.
+ for (int i = 0; i < views.size(); ++i)
+ views.at(i)->d_func()->setUpdateClip(item);
+ }
+ if (!dirtyAncestorContainsChildren) {
+ dirtyAncestorContainsChildren = item->d_ptr->fullUpdatePending
+ && itemClipsChildrenToShape;
+ }
+ const bool allChildrenDirty = item->d_ptr->allChildrenDirty;
+ const bool parentIgnoresVisible = item->d_ptr->ignoreVisible;
+ const bool parentIgnoresOpacity = item->d_ptr->ignoreOpacity;
+ for (int i = 0; i < item->d_ptr->children.size(); ++i) {
+ QGraphicsItem *child = item->d_ptr->children.at(i);
+ if (wasDirtyParentSceneTransform)
+ child->d_ptr->dirtySceneTransform = 1;
+ if (wasDirtyParentViewBoundingRects)
+ child->d_ptr->paintedViewBoundingRectsNeedRepaint = 1;
+ if (parentIgnoresVisible)
+ child->d_ptr->ignoreVisible = 1;
+ if (parentIgnoresOpacity)
+ child->d_ptr->ignoreOpacity = 1;
+ if (allChildrenDirty) {
+ child->d_ptr->dirty = 1;
+ child->d_ptr->fullUpdatePending = 1;
+ child->d_ptr->dirtyChildren = 1;
+ child->d_ptr->allChildrenDirty = 1;
+ }
+ processDirtyItemsRecursive(child, dirtyAncestorContainsChildren, opacity);
+ }
+
+ if (itemClipsChildrenToShape) {
+ // Reset updateClip.
+ for (int i = 0; i < views.size(); ++i)
+ views.at(i)->d_func()->setUpdateClip(0);
+ }
+ } else if (wasDirtyParentSceneTransform) {
+ item->d_ptr->invalidateChildrenSceneTransform();
+ }
+
+ resetDirtyItem(item);
+}
+
+/*!
+ \obsolete
+
+ Paints the given \a items using the provided \a painter, after the
+ background has been drawn, and before the foreground has been
+ drawn. All painting is done in \e scene coordinates. Before
+ drawing each item, the painter must be transformed using
+ QGraphicsItem::sceneTransform().
+
+ The \a options parameter is the list of style option objects for
+ each item in \a items. The \a numItems parameter is the number of
+ items in \a items and options in \a options. The \a widget
+ parameter is optional; if specified, it should point to the widget
+ that is being painted on.
+
+ The default implementation prepares the painter matrix, and calls
+ QGraphicsItem::paint() on all items. Reimplement this function to
+ provide custom painting of all items for the scene; gaining
+ complete control over how each item is drawn. In some cases this
+ can increase drawing performance significantly.
+
+ Example:
+
+ \snippet doc/src/snippets/graphicssceneadditemsnippet.cpp 0
+
+ Since Qt 4.6, this function is not called anymore unless
+ the QGraphicsView::IndirectPainting flag is given as an Optimization
+ flag.
+
+ \sa drawBackground(), drawForeground()
+*/
+void QGraphicsScene::drawItems(QPainter *painter,
+ int numItems,
+ QGraphicsItem *items[],
+ const QStyleOptionGraphicsItem options[], QWidget *widget)
+{
+ Q_D(QGraphicsScene);
+ // Make sure we don't have unpolished items before we draw.
+ if (!d->unpolishedItems.isEmpty())
+ d->_q_polishItems();
+
+ const qreal opacity = painter->opacity();
+ QTransform viewTransform = painter->worldTransform();
+ Q_UNUSED(options);
+
+ // Determine view, expose and flags.
+ QGraphicsView *view = widget ? qobject_cast<QGraphicsView *>(widget->parentWidget()) : 0;
+ QRegion *expose = 0;
+ const quint32 oldRectAdjust = d->rectAdjust;
+ if (view) {
+ d->updateAll = false;
+ expose = &view->d_func()->exposedRegion;
+ if (view->d_func()->optimizationFlags & QGraphicsView::DontAdjustForAntialiasing)
+ d->rectAdjust = 1;
+ else
+ d->rectAdjust = 2;
+ }
+
+ // Find all toplevels, they are already sorted.
+ QList<QGraphicsItem *> topLevelItems;
+ for (int i = 0; i < numItems; ++i) {
+ QGraphicsItem *item = items[i]->topLevelItem();
+ if (!item->d_ptr->itemDiscovered) {
+ topLevelItems << item;
+ item->d_ptr->itemDiscovered = 1;
+ d->drawSubtreeRecursive(item, painter, &viewTransform, expose, widget);
+ }
+ }
+
+ d->rectAdjust = oldRectAdjust;
+ // Reset discovery bits.
+ for (int i = 0; i < topLevelItems.size(); ++i)
+ topLevelItems.at(i)->d_ptr->itemDiscovered = 0;
+
+ painter->setWorldTransform(viewTransform);
+ painter->setOpacity(opacity);
+}
+
+/*!
+ \since 4.4
+
+ Finds a new widget to give the keyboard focus to, as appropriate for Tab
+ and Shift+Tab, and returns true if it can find a new widget, or false if
+ it cannot. If \a next is true, this function searches forward; if \a next
+ is false, it searches backward.
+
+ You can reimplement this function in a subclass of QGraphicsScene to
+ provide fine-grained control over how tab focus passes inside your
+ scene. The default implementation is based on the tab focus chain defined
+ by QGraphicsWidget::setTabOrder().
+*/
+bool QGraphicsScene::focusNextPrevChild(bool next)
+{
+ Q_D(QGraphicsScene);
+
+ QGraphicsItem *item = focusItem();
+ if (item && !item->isWidget()) {
+ // Tab out of the scene.
+ return false;
+ }
+ if (!item) {
+ if (d->lastFocusItem && !d->lastFocusItem->isWidget()) {
+ // Restore focus to the last focusable non-widget item that had
+ // focus.
+ setFocusItem(d->lastFocusItem, next ? Qt::TabFocusReason : Qt::BacktabFocusReason);
+ return true;
+ }
+ }
+ if (!d->tabFocusFirst) {
+ // No widgets...
+ return false;
+ }
+
+ // The item must be a widget.
+ QGraphicsWidget *widget = 0;
+ if (!item) {
+ widget = next ? d->tabFocusFirst : d->tabFocusFirst->d_func()->focusPrev;
+ } else {
+ QGraphicsWidget *test = static_cast<QGraphicsWidget *>(item);
+ widget = next ? test->d_func()->focusNext : test->d_func()->focusPrev;
+ if ((next && widget == d->tabFocusFirst) || (!next && widget == d->tabFocusFirst->d_func()->focusPrev))
+ return false;
+ }
+ QGraphicsWidget *widgetThatHadFocus = widget;
+
+ // Run around the focus chain until we find a widget that can take tab focus.
+ do {
+ if (widget->flags() & QGraphicsItem::ItemIsFocusable
+ && widget->isEnabled() && widget->isVisibleTo(0)
+ && (widget->focusPolicy() & Qt::TabFocus)
+ && (!item || !item->isPanel() || item->isAncestorOf(widget))
+ ) {
+ setFocusItem(widget, next ? Qt::TabFocusReason : Qt::BacktabFocusReason);
+ return true;
+ }
+ widget = next ? widget->d_func()->focusNext : widget->d_func()->focusPrev;
+ if ((next && widget == d->tabFocusFirst) || (!next && widget == d->tabFocusFirst->d_func()->focusPrev))
+ return false;
+ } while (widget != widgetThatHadFocus);
+
+ return false;
+}
+
+/*!
+ \fn QGraphicsScene::changed(const QList<QRectF> &region)
+
+ This signal is emitted by QGraphicsScene when control reaches the
+ event loop, if the scene content changes. The \a region parameter
+ contains a list of scene rectangles that indicate the area that
+ has been changed.
+
+ \sa QGraphicsView::updateScene()
+*/
+
+/*!
+ \fn QGraphicsScene::sceneRectChanged(const QRectF &rect)
+
+ This signal is emitted by QGraphicsScene whenever the scene rect changes.
+ The \a rect parameter is the new scene rectangle.
+
+ \sa QGraphicsView::updateSceneRect()
+*/
+
+/*!
+ \fn QGraphicsScene::selectionChanged()
+ \since 4.3
+
+ This signal is emitted by QGraphicsScene whenever the selection
+ changes. You can call selectedItems() to get the new list of selected
+ items.
+
+ The selection changes whenever an item is selected or unselected, a
+ selection area is set, cleared or otherwise changed, if a preselected item
+ is added to the scene, or if a selected item is removed from the scene.
+
+ QGraphicsScene emits this signal only once for group selection operations.
+ For example, if you set a selection area, select or unselect a
+ QGraphicsItemGroup, or if you add or remove from the scene a parent item
+ that contains several selected items, selectionChanged() is emitted only
+ once after the operation has completed (instead of once for each item).
+
+ \sa setSelectionArea(), selectedItems(), QGraphicsItem::setSelected()
+*/
+
+/*!
+ \since 4.4
+
+ Returns the scene's style, or the same as QApplication::style() if the
+ scene has not been explicitly assigned a style.
+
+ \sa setStyle()
+*/
+QStyle *QGraphicsScene::style() const
+{
+ Q_D(const QGraphicsScene);
+ // ### This function, and the use of styles in general, is non-reentrant.
+ return d->style ? d->style : QApplication::style();
+}
+
+/*!
+ \since 4.4
+
+ Sets or replaces the style of the scene to \a style, and reparents the
+ style to this scene. Any previously assigned style is deleted. The scene's
+ style defaults to QApplication::style(), and serves as the default for all
+ QGraphicsWidget items in the scene.
+
+ Changing the style, either directly by calling this function, or
+ indirectly by calling QApplication::setStyle(), will automatically update
+ the style for all widgets in the scene that do not have a style explicitly
+ assigned to them.
+
+ If \a style is 0, QGraphicsScene will revert to QApplication::style().
+
+ \sa style()
+*/
+void QGraphicsScene::setStyle(QStyle *style)
+{
+ Q_D(QGraphicsScene);
+ // ### This function, and the use of styles in general, is non-reentrant.
+ if (style == d->style)
+ return;
+
+ // Delete the old style,
+ delete d->style;
+ if ((d->style = style))
+ d->style->setParent(this);
+
+ // Notify the scene.
+ QEvent event(QEvent::StyleChange);
+ QApplication::sendEvent(this, &event);
+
+ // Notify all widgets that don't have a style explicitly set.
+ foreach (QGraphicsItem *item, items()) {
+ if (item->isWidget()) {
+ QGraphicsWidget *widget = static_cast<QGraphicsWidget *>(item);
+ if (!widget->testAttribute(Qt::WA_SetStyle))
+ QApplication::sendEvent(widget, &event);
+ }
+ }
+}
+
+/*!
+ \property QGraphicsScene::font
+ \since 4.4
+ \brief the scene's default font
+
+ This property provides the scene's font. The scene font defaults to,
+ and resolves all its entries from, QApplication::font.
+
+ If the scene's font changes, either directly through setFont() or
+ indirectly when the application font changes, QGraphicsScene first
+ sends itself a \l{QEvent::FontChange}{FontChange} event, and it then
+ sends \l{QEvent::FontChange}{FontChange} events to all top-level
+ widget items in the scene. These items respond by resolving their own
+ fonts to the scene, and they then notify their children, who again
+ notify their children, and so on, until all widget items have updated
+ their fonts.
+
+ Changing the scene font, (directly or indirectly through
+ QApplication::setFont(),) automatically schedules a redraw the entire
+ scene.
+
+ \sa QWidget::font, QApplication::setFont(), palette, style()
+*/
+QFont QGraphicsScene::font() const
+{
+ Q_D(const QGraphicsScene);
+ return d->font;
+}
+void QGraphicsScene::setFont(const QFont &font)
+{
+ Q_D(QGraphicsScene);
+ QFont naturalFont = QApplication::font();
+ naturalFont.resolve(0);
+ QFont resolvedFont = font.resolve(naturalFont);
+ d->setFont_helper(resolvedFont);
+}
+
+/*!
+ \property QGraphicsScene::palette
+ \since 4.4
+ \brief the scene's default palette
+
+ This property provides the scene's palette. The scene palette defaults to,
+ and resolves all its entries from, QApplication::palette.
+
+ If the scene's palette changes, either directly through setPalette() or
+ indirectly when the application palette changes, QGraphicsScene first
+ sends itself a \l{QEvent::PaletteChange}{PaletteChange} event, and it then
+ sends \l{QEvent::PaletteChange}{PaletteChange} events to all top-level
+ widget items in the scene. These items respond by resolving their own
+ palettes to the scene, and they then notify their children, who again
+ notify their children, and so on, until all widget items have updated
+ their palettes.
+
+ Changing the scene palette, (directly or indirectly through
+ QApplication::setPalette(),) automatically schedules a redraw the entire
+ scene.
+
+ \sa QWidget::palette, QApplication::setPalette(), font, style()
+*/
+QPalette QGraphicsScene::palette() const
+{
+ Q_D(const QGraphicsScene);
+ return d->palette;
+}
+void QGraphicsScene::setPalette(const QPalette &palette)
+{
+ Q_D(QGraphicsScene);
+ QPalette naturalPalette = QApplication::palette();
+ naturalPalette.resolve(0);
+ QPalette resolvedPalette = palette.resolve(naturalPalette);
+ d->setPalette_helper(resolvedPalette);
+}
+
+/*!
+ \since 4.6
+
+ Returns true if the scene is active (e.g., it's viewed by
+ at least one QGraphicsView that is active); otherwise returns false.
+
+ \sa QGraphicsItem::isActive(), QWidget::isActiveWindow()
+*/
+bool QGraphicsScene::isActive() const
+{
+ Q_D(const QGraphicsScene);
+ return d->activationRefCount > 0;
+}
+
+/*!
+ \since 4.6
+ Returns the current active panel, or 0 if no panel is currently active.
+
+ \sa QGraphicsScene::setActivePanel()
+*/
+QGraphicsItem *QGraphicsScene::activePanel() const
+{
+ Q_D(const QGraphicsScene);
+ return d->activePanel;
+}
+
+/*!
+ \since 4.6
+ Activates \a item, which must be an item in this scene. You
+ can also pass 0 for \a item, in which case QGraphicsScene will
+ deactivate any currently active panel.
+
+ If the scene is currently inactive, \a item remains inactive until the
+ scene becomes active (or, ir \a item is 0, no item will be activated).
+
+ \sa activePanel(), isActive(), QGraphicsItem::isActive()
+*/
+void QGraphicsScene::setActivePanel(QGraphicsItem *item)
+{
+ Q_D(QGraphicsScene);
+ d->setActivePanelHelper(item, false);
+}
+
+/*!
+ \since 4.4
+
+ Returns the current active window, or 0 if no window is currently
+ active.
+
+ \sa QGraphicsScene::setActiveWindow()
+*/
+QGraphicsWidget *QGraphicsScene::activeWindow() const
+{
+ Q_D(const QGraphicsScene);
+ if (d->activePanel && d->activePanel->isWindow())
+ return static_cast<QGraphicsWidget *>(d->activePanel);
+ return 0;
+}
+
+/*!
+ \since 4.4
+ Activates \a widget, which must be a widget in this scene. You can also
+ pass 0 for \a widget, in which case QGraphicsScene will deactivate any
+ currently active window.
+
+ \sa activeWindow(), QGraphicsWidget::isActiveWindow()
+*/
+void QGraphicsScene::setActiveWindow(QGraphicsWidget *widget)
+{
+ if (widget && widget->scene() != this) {
+ qWarning("QGraphicsScene::setActiveWindow: widget %p must be part of this scene",
+ widget);
+ return;
+ }
+
+ // Activate the widget's panel (all windows are panels).
+ QGraphicsItem *panel = widget ? widget->panel() : 0;
+ setActivePanel(panel);
+
+ // Raise
+ if (panel) {
+ QList<QGraphicsItem *> siblingWindows;
+ QGraphicsItem *parent = panel->parentItem();
+ // Raise ### inefficient for toplevels
+ foreach (QGraphicsItem *sibling, parent ? parent->children() : items()) {
+ if (sibling != panel && sibling->isWindow())
+ siblingWindows << sibling;
+ }
+
+ // Find the highest z value.
+ qreal z = panel->zValue();
+ for (int i = 0; i < siblingWindows.size(); ++i)
+ z = qMax(z, siblingWindows.at(i)->zValue());
+
+ // This will probably never overflow.
+ const qreal litt = qreal(0.001);
+ panel->setZValue(z + litt);
+ }
+}
+
+/*!
+ \since 4.6
+
+ Sends event \a event to item \a item through possible event filters.
+
+ The event is sent only if the item is enabled.
+
+ Returns \c false if the event was filtered or if the item is disabled.
+ Otherwise returns the value that was returned from the event handler.
+
+ \sa QGraphicsItem::sceneEvent(), QGraphicsItem::sceneEventFilter()
+*/
+bool QGraphicsScene::sendEvent(QGraphicsItem *item, QEvent *event)
+{
+ Q_D(QGraphicsScene);
+ if (!item) {
+ qWarning("QGraphicsScene::sendEvent: cannot send event to a null item");
+ return false;
+ }
+ if (item->scene() != this) {
+ qWarning("QGraphicsScene::sendEvent: item %p's scene (%p)"
+ " is different from this scene (%p)",
+ item, item->scene(), this);
+ return false;
+ }
+ return d->sendEvent(item, event);
+}
+
+void QGraphicsScenePrivate::addView(QGraphicsView *view)
+{
+ views << view;
+#ifndef QT_NO_GESTURES
+ foreach (Qt::GestureType gesture, grabbedGestures.keys())
+ view->viewport()->grabGesture(gesture);
+#endif
+}
+
+void QGraphicsScenePrivate::removeView(QGraphicsView *view)
+{
+ views.removeAll(view);
+}
+
+void QGraphicsScenePrivate::updateTouchPointsForItem(QGraphicsItem *item, QTouchEvent *touchEvent)
+{
+ QList<QTouchEvent::TouchPoint> touchPoints = touchEvent->touchPoints();
+ for (int i = 0; i < touchPoints.count(); ++i) {
+ QTouchEvent::TouchPoint &touchPoint = touchPoints[i];
+ touchPoint.setRect(item->mapFromScene(touchPoint.sceneRect()).boundingRect());
+ touchPoint.setStartPos(item->d_ptr->genericMapFromScene(touchPoint.startScenePos(), touchEvent->widget()));
+ touchPoint.setLastPos(item->d_ptr->genericMapFromScene(touchPoint.lastScenePos(), touchEvent->widget()));
+ }
+ touchEvent->setTouchPoints(touchPoints);
+}
+
+int QGraphicsScenePrivate::findClosestTouchPointId(const QPointF &scenePos)
+{
+ int closestTouchPointId = -1;
+ qreal closestDistance = qreal(0.);
+ foreach (const QTouchEvent::TouchPoint &touchPoint, sceneCurrentTouchPoints) {
+ qreal distance = QLineF(scenePos, touchPoint.scenePos()).length();
+ if (closestTouchPointId == -1|| distance < closestDistance) {
+ closestTouchPointId = touchPoint.id();
+ closestDistance = distance;
+ }
+ }
+ return closestTouchPointId;
+}
+
+void QGraphicsScenePrivate::touchEventHandler(QTouchEvent *sceneTouchEvent)
+{
+ typedef QPair<Qt::TouchPointStates, QList<QTouchEvent::TouchPoint> > StatesAndTouchPoints;
+ QHash<QGraphicsItem *, StatesAndTouchPoints> itemsNeedingEvents;
+
+ for (int i = 0; i < sceneTouchEvent->touchPoints().count(); ++i) {
+ const QTouchEvent::TouchPoint &touchPoint = sceneTouchEvent->touchPoints().at(i);
+
+ // update state
+ QGraphicsItem *item = 0;
+ if (touchPoint.state() == Qt::TouchPointPressed) {
+ if (sceneTouchEvent->deviceType() == QTouchEvent::TouchPad) {
+ // on touch-pad devices, send all touch points to the same item
+ item = itemForTouchPointId.isEmpty()
+ ? 0
+ : itemForTouchPointId.constBegin().value();
+ }
+
+ if (!item) {
+ // determine which item this touch point will go to
+ cachedItemsUnderMouse = itemsAtPosition(touchPoint.screenPos().toPoint(),
+ touchPoint.scenePos(),
+ sceneTouchEvent->widget());
+ item = cachedItemsUnderMouse.isEmpty() ? 0 : cachedItemsUnderMouse.first();
+ }
+
+ if (sceneTouchEvent->deviceType() == QTouchEvent::TouchScreen) {
+ // on touch-screens, combine this touch point with the closest one we find
+ int closestTouchPointId = findClosestTouchPointId(touchPoint.scenePos());
+ QGraphicsItem *closestItem = itemForTouchPointId.value(closestTouchPointId);
+ if (!item || (closestItem && cachedItemsUnderMouse.contains(closestItem)))
+ item = closestItem;
+ }
+ if (!item)
+ continue;
+
+ itemForTouchPointId.insert(touchPoint.id(), item);
+ sceneCurrentTouchPoints.insert(touchPoint.id(), touchPoint);
+ } else if (touchPoint.state() == Qt::TouchPointReleased) {
+ item = itemForTouchPointId.take(touchPoint.id());
+ if (!item)
+ continue;
+
+ sceneCurrentTouchPoints.remove(touchPoint.id());
+ } else {
+ item = itemForTouchPointId.value(touchPoint.id());
+ if (!item)
+ continue;
+ Q_ASSERT(sceneCurrentTouchPoints.contains(touchPoint.id()));
+ sceneCurrentTouchPoints[touchPoint.id()] = touchPoint;
+ }
+
+ StatesAndTouchPoints &statesAndTouchPoints = itemsNeedingEvents[item];
+ statesAndTouchPoints.first |= touchPoint.state();
+ statesAndTouchPoints.second.append(touchPoint);
+ }
+
+ if (itemsNeedingEvents.isEmpty()) {
+ sceneTouchEvent->accept();
+ return;
+ }
+
+ bool ignoreSceneTouchEvent = true;
+ QHash<QGraphicsItem *, StatesAndTouchPoints>::ConstIterator it = itemsNeedingEvents.constBegin();
+ const QHash<QGraphicsItem *, StatesAndTouchPoints>::ConstIterator end = itemsNeedingEvents.constEnd();
+ for (; it != end; ++it) {
+ QGraphicsItem *item = it.key();
+
+ (void) item->isBlockedByModalPanel(&item);
+
+ // determine event type from the state mask
+ QEvent::Type eventType;
+ switch (it.value().first) {
+ case Qt::TouchPointPressed:
+ // all touch points have pressed state
+ eventType = QEvent::TouchBegin;
+ break;
+ case Qt::TouchPointReleased:
+ // all touch points have released state
+ eventType = QEvent::TouchEnd;
+ break;
+ case Qt::TouchPointStationary:
+ // don't send the event if nothing changed
+ continue;
+ default:
+ // all other combinations
+ eventType = QEvent::TouchUpdate;
+ break;
+ }
+
+ QTouchEvent touchEvent(eventType);
+ touchEvent.setWidget(sceneTouchEvent->widget());
+ touchEvent.setDeviceType(sceneTouchEvent->deviceType());
+ touchEvent.setModifiers(sceneTouchEvent->modifiers());
+ touchEvent.setTouchPointStates(it.value().first);
+ touchEvent.setTouchPoints(it.value().second);
+
+ switch (touchEvent.type()) {
+ case QEvent::TouchBegin:
+ {
+ // if the TouchBegin handler recurses, we assume that means the event
+ // has been implicitly accepted and continue to send touch events
+ item->d_ptr->acceptedTouchBeginEvent = true;
+ bool res = sendTouchBeginEvent(item, &touchEvent)
+ && touchEvent.isAccepted();
+ if (!res) {
+ // forget about these touch points, we didn't handle them
+ for (int i = 0; i < touchEvent.touchPoints().count(); ++i) {
+ const QTouchEvent::TouchPoint &touchPoint = touchEvent.touchPoints().at(i);
+ itemForTouchPointId.remove(touchPoint.id());
+ sceneCurrentTouchPoints.remove(touchPoint.id());
+ }
+ ignoreSceneTouchEvent = false;
+ }
+ break;
+ }
+ default:
+ if (item->d_ptr->acceptedTouchBeginEvent) {
+ updateTouchPointsForItem(item, &touchEvent);
+ (void) sendEvent(item, &touchEvent);
+ ignoreSceneTouchEvent = false;
+ }
+ break;
+ }
+ }
+ sceneTouchEvent->setAccepted(ignoreSceneTouchEvent);
+}
+
+bool QGraphicsScenePrivate::sendTouchBeginEvent(QGraphicsItem *origin, QTouchEvent *touchEvent)
+{
+ Q_Q(QGraphicsScene);
+
+ if (cachedItemsUnderMouse.isEmpty() || cachedItemsUnderMouse.first() != origin) {
+ const QTouchEvent::TouchPoint &firstTouchPoint = touchEvent->touchPoints().first();
+ cachedItemsUnderMouse = itemsAtPosition(firstTouchPoint.screenPos().toPoint(),
+ firstTouchPoint.scenePos(),
+ touchEvent->widget());
+ }
+ Q_ASSERT(cachedItemsUnderMouse.first() == origin);
+
+ // Set focus on the topmost enabled item that can take focus.
+ bool setFocus = false;
+
+ foreach (QGraphicsItem *item, cachedItemsUnderMouse) {
+ if (item->isEnabled() && ((item->flags() & QGraphicsItem::ItemIsFocusable) && item->d_ptr->mouseSetsFocus)) {
+ if (!item->isWidget() || ((QGraphicsWidget *)item)->focusPolicy() & Qt::ClickFocus) {
+ setFocus = true;
+ if (item != q->focusItem())
+ q->setFocusItem(item, Qt::MouseFocusReason);
+ break;
+ }
+ }
+ if (item->isPanel())
+ break;
+ if (item->d_ptr->flags & QGraphicsItem::ItemStopsClickFocusPropagation)
+ break;
+ if (item->d_ptr->flags & QGraphicsItem::ItemStopsFocusHandling) {
+ // Make sure we don't clear focus.
+ setFocus = true;
+ break;
+ }
+ }
+
+ // If nobody could take focus, clear it.
+ if (!stickyFocus && !setFocus)
+ q->setFocusItem(0, Qt::MouseFocusReason);
+
+ bool res = false;
+ bool eventAccepted = touchEvent->isAccepted();
+ foreach (QGraphicsItem *item, cachedItemsUnderMouse) {
+ // first, try to deliver the touch event
+ updateTouchPointsForItem(item, touchEvent);
+ bool acceptTouchEvents = item->acceptTouchEvents();
+ touchEvent->setAccepted(acceptTouchEvents);
+ res = acceptTouchEvents && sendEvent(item, touchEvent);
+ eventAccepted = touchEvent->isAccepted();
+ if (itemForTouchPointId.value(touchEvent->touchPoints().first().id()) == 0) {
+ // item was deleted
+ item = 0;
+ } else {
+ item->d_ptr->acceptedTouchBeginEvent = (res && eventAccepted);
+ }
+ touchEvent->spont = false;
+ if (res && eventAccepted) {
+ // the first item to accept the TouchBegin gets an implicit grab.
+ for (int i = 0; i < touchEvent->touchPoints().count(); ++i) {
+ const QTouchEvent::TouchPoint &touchPoint = touchEvent->touchPoints().at(i);
+ itemForTouchPointId[touchPoint.id()] = item; // can be zero
+ }
+ break;
+ }
+ if (item && item->isPanel())
+ break;
+ }
+
+ touchEvent->setAccepted(eventAccepted);
+ return res;
+}
+
+void QGraphicsScenePrivate::enableTouchEventsOnViews()
+{
+ foreach (QGraphicsView *view, views)
+ view->viewport()->setAttribute(Qt::WA_AcceptTouchEvents, true);
+}
+
+void QGraphicsScenePrivate::updateInputMethodSensitivityInViews()
+{
+ for (int i = 0; i < views.size(); ++i)
+ views.at(i)->d_func()->updateInputMethodSensitivity();
+}
+
+void QGraphicsScenePrivate::enterModal(QGraphicsItem *panel, QGraphicsItem::PanelModality previousModality)
+{
+ Q_Q(QGraphicsScene);
+ Q_ASSERT(panel && panel->isPanel());
+
+ QGraphicsItem::PanelModality panelModality = panel->d_ptr->panelModality;
+ if (previousModality != QGraphicsItem::NonModal) {
+ // the panel is changing from one modality type to another... temporarily set it back so
+ // that blockedPanels is populated correctly
+ panel->d_ptr->panelModality = previousModality;
+ }
+
+ QSet<QGraphicsItem *> blockedPanels;
+ QList<QGraphicsItem *> items = q->items(); // ### store panels separately
+ for (int i = 0; i < items.count(); ++i) {
+ QGraphicsItem *item = items.at(i);
+ if (item->isPanel() && item->isBlockedByModalPanel())
+ blockedPanels.insert(item);
+ }
+ // blockedPanels contains all currently blocked panels
+
+ if (previousModality != QGraphicsItem::NonModal) {
+ // reset the modality to the proper value, since we changed it above
+ panel->d_ptr->panelModality = panelModality;
+ // remove this panel so that it will be reinserted at the front of the stack
+ modalPanels.removeAll(panel);
+ }
+
+ modalPanels.prepend(panel);
+
+ if (!hoverItems.isEmpty()) {
+ // send GraphicsSceneHoverLeave events to newly blocked hoverItems
+ QGraphicsSceneHoverEvent hoverEvent;
+ hoverEvent.setScenePos(lastSceneMousePos);
+ dispatchHoverEvent(&hoverEvent);
+ }
+
+ if (!mouseGrabberItems.isEmpty() && lastMouseGrabberItemHasImplicitMouseGrab) {
+ QGraphicsItem *item = mouseGrabberItems.last();
+ if (item->isBlockedByModalPanel())
+ ungrabMouse(item, /*itemIsDying =*/ false);
+ }
+
+ QEvent windowBlockedEvent(QEvent::WindowBlocked);
+ QEvent windowUnblockedEvent(QEvent::WindowUnblocked);
+ for (int i = 0; i < items.count(); ++i) {
+ QGraphicsItem *item = items.at(i);
+ if (item->isPanel()) {
+ if (!blockedPanels.contains(item) && item->isBlockedByModalPanel()) {
+ // send QEvent::WindowBlocked to newly blocked panels
+ sendEvent(item, &windowBlockedEvent);
+ } else if (blockedPanels.contains(item) && !item->isBlockedByModalPanel()) {
+ // send QEvent::WindowUnblocked to unblocked panels when downgrading
+ // a panel from SceneModal to PanelModal
+ sendEvent(item, &windowUnblockedEvent);
+ }
+ }
+ }
+}
+
+void QGraphicsScenePrivate::leaveModal(QGraphicsItem *panel)
+{
+ Q_Q(QGraphicsScene);
+ Q_ASSERT(panel && panel->isPanel());
+
+ QSet<QGraphicsItem *> blockedPanels;
+ QList<QGraphicsItem *> items = q->items(); // ### same as above
+ for (int i = 0; i < items.count(); ++i) {
+ QGraphicsItem *item = items.at(i);
+ if (item->isPanel() && item->isBlockedByModalPanel())
+ blockedPanels.insert(item);
+ }
+
+ modalPanels.removeAll(panel);
+
+ QEvent e(QEvent::WindowUnblocked);
+ for (int i = 0; i < items.count(); ++i) {
+ QGraphicsItem *item = items.at(i);
+ if (item->isPanel() && blockedPanels.contains(item) && !item->isBlockedByModalPanel())
+ sendEvent(item, &e);
+ }
+
+ // send GraphicsSceneHoverEnter events to newly unblocked items
+ QGraphicsSceneHoverEvent hoverEvent;
+ hoverEvent.setScenePos(lastSceneMousePos);
+ dispatchHoverEvent(&hoverEvent);
+}
+
+#ifndef QT_NO_GESTURES
+void QGraphicsScenePrivate::gestureTargetsAtHotSpots(const QSet<QGesture *> &gestures,
+ Qt::GestureFlag flag,
+ QHash<QGraphicsObject *, QSet<QGesture *> > *targets,
+ QSet<QGraphicsObject *> *itemsSet,
+ QSet<QGesture *> *normal,
+ QSet<QGesture *> *conflicts)
+{
+ QSet<QGesture *> normalGestures; // that are not in conflicted state.
+ foreach (QGesture *gesture, gestures) {
+ if (!gesture->hasHotSpot())
+ continue;
+ const Qt::GestureType gestureType = gesture->gestureType();
+ QList<QGraphicsItem *> items = itemsAtPosition(QPoint(), gesture->d_func()->sceneHotSpot, 0);
+ for (int j = 0; j < items.size(); ++j) {
+ QGraphicsItem *item = items.at(j);
+
+ // Check if the item is blocked by a modal panel and use it as
+ // a target instead of this item.
+ (void) item->isBlockedByModalPanel(&item);
+
+ if (QGraphicsObject *itemobj = item->toGraphicsObject()) {
+ QGraphicsItemPrivate *d = item->QGraphicsItem::d_func();
+ QMap<Qt::GestureType, Qt::GestureFlags>::const_iterator it =
+ d->gestureContext.find(gestureType);
+ if (it != d->gestureContext.end() && (!flag || (it.value() & flag))) {
+ if (normalGestures.contains(gesture)) {
+ normalGestures.remove(gesture);
+ if (conflicts)
+ conflicts->insert(gesture);
+ } else {
+ normalGestures.insert(gesture);
+ }
+ if (targets)
+ (*targets)[itemobj].insert(gesture);
+ if (itemsSet)
+ (*itemsSet).insert(itemobj);
+ }
+ }
+ // Don't propagate through panels.
+ if (item->isPanel())
+ break;
+ }
+ }
+ if (normal)
+ *normal = normalGestures;
+}
+
+void QGraphicsScenePrivate::gestureEventHandler(QGestureEvent *event)
+{
+ QWidget *viewport = event->widget();
+ if (!viewport)
+ return;
+ QGraphicsView *graphicsView = qobject_cast<QGraphicsView *>(viewport->parent());
+ if (!graphicsView)
+ return;
+
+ QList<QGesture *> allGestures = event->gestures();
+ DEBUG() << "QGraphicsScenePrivate::gestureEventHandler:"
+ << "Gestures:" << allGestures;
+
+ QSet<QGesture *> startedGestures;
+ QPoint delta = viewport->mapFromGlobal(QPoint());
+ QTransform toScene = QTransform::fromTranslate(delta.x(), delta.y())
+ * graphicsView->viewportTransform().inverted();
+ foreach (QGesture *gesture, allGestures) {
+ // cache scene coordinates of the hot spot
+ if (gesture->hasHotSpot()) {
+ gesture->d_func()->sceneHotSpot = toScene.map(gesture->hotSpot());
+ } else {
+ gesture->d_func()->sceneHotSpot = QPointF();
+ }
+
+ QGraphicsObject *target = gestureTargets.value(gesture, 0);
+ if (!target) {
+ // when we are not in started mode but don't have a target
+ // then the only one interested in gesture is the view/scene
+ if (gesture->state() == Qt::GestureStarted)
+ startedGestures.insert(gesture);
+ }
+ }
+
+ if (!startedGestures.isEmpty()) {
+ QSet<QGesture *> normalGestures; // that have just one target
+ QSet<QGesture *> conflictedGestures; // that have multiple possible targets
+ gestureTargetsAtHotSpots(startedGestures, Qt::GestureFlag(0), &cachedItemGestures, 0,
+ &normalGestures, &conflictedGestures);
+ cachedTargetItems = cachedItemGestures.keys();
+ qSort(cachedTargetItems.begin(), cachedTargetItems.end(), qt_closestItemFirst);
+ DEBUG() << "QGraphicsScenePrivate::gestureEventHandler:"
+ << "Normal gestures:" << normalGestures
+ << "Conflicting gestures:" << conflictedGestures;
+
+ // deliver conflicted gestures as override events AND remember
+ // initial gesture targets
+ if (!conflictedGestures.isEmpty()) {
+ for (int i = 0; i < cachedTargetItems.size(); ++i) {
+ QWeakPointer<QGraphicsObject> item = cachedTargetItems.at(i);
+
+ // get gestures to deliver to the current item
+ QSet<QGesture *> gestures = conflictedGestures & cachedItemGestures.value(item.data());
+ if (gestures.isEmpty())
+ continue;
+
+ DEBUG() << "QGraphicsScenePrivate::gestureEventHandler:"
+ << "delivering override to"
+ << item.data() << gestures;
+ // send gesture override
+ QGestureEvent ev(gestures.toList());
+ ev.t = QEvent::GestureOverride;
+ ev.setWidget(event->widget());
+ // mark event and individual gestures as ignored
+ ev.ignore();
+ foreach(QGesture *g, gestures)
+ ev.setAccepted(g, false);
+ sendEvent(item.data(), &ev);
+ // mark all accepted gestures to deliver them as normal gesture events
+ foreach (QGesture *g, gestures) {
+ if (ev.isAccepted() || ev.isAccepted(g)) {
+ conflictedGestures.remove(g);
+ // mark the item as a gesture target
+ if (item) {
+ gestureTargets.insert(g, item.data());
+ QHash<QGraphicsObject *, QSet<QGesture *> >::iterator it, e;
+ it = cachedItemGestures.begin();
+ e = cachedItemGestures.end();
+ for(; it != e; ++it)
+ it.value().remove(g);
+ cachedItemGestures[item.data()].insert(g);
+ }
+ DEBUG() << "QGraphicsScenePrivate::gestureEventHandler:"
+ << "override was accepted:"
+ << g << item.data();
+ }
+ // remember the first item that received the override event
+ // as it most likely become a target if no one else accepts
+ // the override event
+ if (!gestureTargets.contains(g) && item)
+ gestureTargets.insert(g, item.data());
+
+ }
+ if (conflictedGestures.isEmpty())
+ break;
+ }
+ }
+ // remember the initial target item for each gesture that was not in
+ // the conflicted state.
+ if (!normalGestures.isEmpty()) {
+ for (int i = 0; i < cachedTargetItems.size() && !normalGestures.isEmpty(); ++i) {
+ QGraphicsObject *item = cachedTargetItems.at(i);
+
+ // get gestures to deliver to the current item
+ foreach (QGesture *g, cachedItemGestures.value(item)) {
+ if (!gestureTargets.contains(g)) {
+ gestureTargets.insert(g, item);
+ normalGestures.remove(g);
+ }
+ }
+ }
+ }
+ }
+
+
+ // deliver all gesture events
+ QSet<QGesture *> undeliveredGestures;
+ QSet<QGesture *> parentPropagatedGestures;
+ foreach (QGesture *gesture, allGestures) {
+ if (QGraphicsObject *target = gestureTargets.value(gesture, 0)) {
+ cachedItemGestures[target].insert(gesture);
+ cachedTargetItems.append(target);
+ undeliveredGestures.insert(gesture);
+ QGraphicsItemPrivate *d = target->QGraphicsItem::d_func();
+ const Qt::GestureFlags flags = d->gestureContext.value(gesture->gestureType());
+ if (flags & Qt::IgnoredGesturesPropagateToParent)
+ parentPropagatedGestures.insert(gesture);
+ } else {
+ DEBUG() << "QGraphicsScenePrivate::gestureEventHandler:"
+ << "no target for" << gesture << "at"
+ << gesture->hotSpot() << gesture->d_func()->sceneHotSpot;
+ }
+ }
+ qSort(cachedTargetItems.begin(), cachedTargetItems.end(), qt_closestItemFirst);
+ for (int i = 0; i < cachedTargetItems.size(); ++i) {
+ QWeakPointer<QGraphicsObject> receiver = cachedTargetItems.at(i);
+ QSet<QGesture *> gestures =
+ undeliveredGestures & cachedItemGestures.value(receiver.data());
+ gestures -= cachedAlreadyDeliveredGestures.value(receiver.data());
+
+ if (gestures.isEmpty())
+ continue;
+
+ cachedAlreadyDeliveredGestures[receiver.data()] += gestures;
+ const bool isPanel = receiver.data()->isPanel();
+
+ DEBUG() << "QGraphicsScenePrivate::gestureEventHandler:"
+ << "delivering to"
+ << receiver.data() << gestures;
+ QGestureEvent ev(gestures.toList());
+ ev.setWidget(event->widget());
+ sendEvent(receiver.data(), &ev);
+ QSet<QGesture *> ignoredGestures;
+ foreach (QGesture *g, gestures) {
+ if (!ev.isAccepted() && !ev.isAccepted(g)) {
+ // if the gesture was ignored by its target, we will update the
+ // targetItems list with a possible target items (items that
+ // want to receive partial gestures).
+ // ### wont' work if the target was destroyed in the event
+ // we will just stop delivering it.
+ if (receiver && receiver.data() == gestureTargets.value(g, 0))
+ ignoredGestures.insert(g);
+ } else {
+ if (receiver && g->state() == Qt::GestureStarted) {
+ // someone accepted the propagated initial GestureStarted
+ // event, let it be the new target for all following events.
+ gestureTargets[g] = receiver.data();
+ }
+ undeliveredGestures.remove(g);
+ }
+ }
+ if (undeliveredGestures.isEmpty())
+ break;
+
+ // ignoredGestures list is only filled when delivering to the gesture
+ // target item, so it is safe to assume item == target.
+ if (!ignoredGestures.isEmpty() && !isPanel) {
+ // look for new potential targets for gestures that were ignored
+ // and should be propagated.
+
+ QSet<QGraphicsObject *> targetsSet = cachedTargetItems.toSet();
+
+ if (receiver) {
+ // first if the gesture should be propagated to parents only
+ for (QSet<QGesture *>::iterator it = ignoredGestures.begin();
+ it != ignoredGestures.end();) {
+ if (parentPropagatedGestures.contains(*it)) {
+ QGesture *gesture = *it;
+ const Qt::GestureType gestureType = gesture->gestureType();
+ QGraphicsItem *item = receiver.data();
+ while (item) {
+ if (QGraphicsObject *obj = item->toGraphicsObject()) {
+ if (item->d_func()->gestureContext.contains(gestureType)) {
+ targetsSet.insert(obj);
+ cachedItemGestures[obj].insert(gesture);
+ }
+ }
+ if (item->isPanel())
+ break;
+ item = item->parentItem();
+ }
+
+ it = ignoredGestures.erase(it);
+ continue;
+ }
+ ++it;
+ }
+ }
+
+ gestureTargetsAtHotSpots(ignoredGestures, Qt::ReceivePartialGestures,
+ &cachedItemGestures, &targetsSet, 0, 0);
+
+ cachedTargetItems = targetsSet.toList();
+ qSort(cachedTargetItems.begin(), cachedTargetItems.end(), qt_closestItemFirst);
+ DEBUG() << "QGraphicsScenePrivate::gestureEventHandler:"
+ << "new targets:" << cachedTargetItems;
+ i = -1; // start delivery again
+ continue;
+ }
+ }
+
+ foreach (QGesture *g, startedGestures) {
+ if (g->gestureCancelPolicy() == QGesture::CancelAllInContext) {
+ DEBUG() << "lets try to cancel some";
+ // find gestures in context in Qt::GestureStarted or Qt::GestureUpdated state and cancel them
+ cancelGesturesForChildren(g);
+ }
+ }
+
+ // forget about targets for gestures that have ended
+ foreach (QGesture *g, allGestures) {
+ switch (g->state()) {
+ case Qt::GestureFinished:
+ case Qt::GestureCanceled:
+ gestureTargets.remove(g);
+ break;
+ default:
+ break;
+ }
+ }
+
+ cachedTargetItems.clear();
+ cachedItemGestures.clear();
+ cachedAlreadyDeliveredGestures.clear();
+}
+
+void QGraphicsScenePrivate::cancelGesturesForChildren(QGesture *original)
+{
+ Q_ASSERT(original);
+ QGraphicsItem *originalItem = gestureTargets.value(original);
+ if (originalItem == 0) // we only act on accepted gestures, which implies it has a target.
+ return;
+
+ // iterate over all active gestures and for each find the owner
+ // if the owner is part of our sub-hierarchy, cancel it.
+
+ QSet<QGesture *> canceledGestures;
+ QHash<QGesture *, QGraphicsObject *>::Iterator iter = gestureTargets.begin();
+ while (iter != gestureTargets.end()) {
+ QGraphicsObject *item = iter.value();
+ // note that we don't touch the gestures for our originalItem
+ if (item != originalItem && originalItem->isAncestorOf(item)) {
+ DEBUG() << " found a gesture to cancel" << iter.key();
+ iter.key()->d_func()->state = Qt::GestureCanceled;
+ canceledGestures << iter.key();
+ }
+ ++iter;
+ }
+
+ // sort them per target item by cherry picking from almostCanceledGestures and delivering
+ QSet<QGesture *> almostCanceledGestures = canceledGestures;
+ QSet<QGesture *>::Iterator setIter;
+ while (!almostCanceledGestures.isEmpty()) {
+ QGraphicsObject *target = 0;
+ QSet<QGesture*> gestures;
+ setIter = almostCanceledGestures.begin();
+ // sort per target item
+ while (setIter != almostCanceledGestures.end()) {
+ QGraphicsObject *item = gestureTargets.value(*setIter);
+ if (target == 0)
+ target = item;
+ if (target == item) {
+ gestures << *setIter;
+ setIter = almostCanceledGestures.erase(setIter);
+ } else {
+ ++setIter;
+ }
+ }
+ Q_ASSERT(target);
+
+ QList<QGesture *> list = gestures.toList();
+ QGestureEvent ev(list);
+ sendEvent(target, &ev);
+
+ foreach (QGesture *g, list) {
+ if (ev.isAccepted() || ev.isAccepted(g))
+ gestures.remove(g);
+ }
+
+ foreach (QGesture *g, gestures) {
+ if (!g->hasHotSpot())
+ continue;
+
+ QList<QGraphicsItem *> items = itemsAtPosition(QPoint(), g->d_func()->sceneHotSpot, 0);
+ for (int j = 0; j < items.size(); ++j) {
+ QGraphicsObject *item = items.at(j)->toGraphicsObject();
+ if (!item)
+ continue;
+ QGraphicsItemPrivate *d = item->QGraphicsItem::d_func();
+ if (d->gestureContext.contains(g->gestureType())) {
+ QList<QGesture *> list;
+ list << g;
+ QGestureEvent ev(list);
+ sendEvent(item, &ev);
+ if (ev.isAccepted() || ev.isAccepted(g))
+ break; // successfully delivered
+ }
+ }
+ }
+ }
+
+ QGestureManager *gestureManager = QApplicationPrivate::instance()->gestureManager;
+ Q_ASSERT(gestureManager); // it would be very odd if we got called without a manager.
+ for (setIter = canceledGestures.begin(); setIter != canceledGestures.end(); ++setIter) {
+ gestureManager->recycle(*setIter);
+ gestureTargets.remove(*setIter);
+ }
+}
+
+void QGraphicsScenePrivate::grabGesture(QGraphicsItem *, Qt::GestureType gesture)
+{
+ (void)QGestureManager::instance(); // create a gesture manager
+ if (!grabbedGestures[gesture]++) {
+ foreach (QGraphicsView *view, views)
+ view->viewport()->grabGesture(gesture);
+ }
+}
+
+void QGraphicsScenePrivate::ungrabGesture(QGraphicsItem *item, Qt::GestureType gesture)
+{
+ // we know this can only be an object
+ Q_ASSERT(item->d_ptr->isObject);
+ QGraphicsObject *obj = static_cast<QGraphicsObject *>(item);
+ QGestureManager::instance()->cleanupCachedGestures(obj, gesture);
+ if (!--grabbedGestures[gesture]) {
+ foreach (QGraphicsView *view, views)
+ view->viewport()->ungrabGesture(gesture);
+ }
+}
+#endif // QT_NO_GESTURES
+
+QT_END_NAMESPACE
+
+#include "moc_qgraphicsscene.cpp"
+
+#endif // QT_NO_GRAPHICSVIEW
diff --git a/src/widgets/graphicsview/qgraphicsscene.h b/src/widgets/graphicsview/qgraphicsscene.h
new file mode 100644
index 0000000000..c17ab96074
--- /dev/null
+++ b/src/widgets/graphicsview/qgraphicsscene.h
@@ -0,0 +1,329 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** 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 Technology Preview License Agreement accompanying
+** this package.
+**
+** 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.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QGRAPHICSSCENE_H
+#define QGRAPHICSSCENE_H
+
+#include <QtCore/qobject.h>
+#include <QtCore/qpoint.h>
+#include <QtCore/qrect.h>
+#include <QtGui/qbrush.h>
+#include <QtGui/qfont.h>
+#include <QtGui/qtransform.h>
+#include <QtGui/qmatrix.h>
+#include <QtGui/qpen.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+#if !defined(QT_NO_GRAPHICSVIEW) || (QT_EDITION & QT_MODULE_GRAPHICSVIEW) != QT_MODULE_GRAPHICSVIEW
+
+template<typename T> class QList;
+class QFocusEvent;
+class QFont;
+class QFontMetrics;
+class QGraphicsEllipseItem;
+class QGraphicsItem;
+class QGraphicsItemGroup;
+class QGraphicsLineItem;
+class QGraphicsPathItem;
+class QGraphicsPixmapItem;
+class QGraphicsPolygonItem;
+class QGraphicsProxyWidget;
+class QGraphicsRectItem;
+class QGraphicsSceneContextMenuEvent;
+class QGraphicsSceneDragDropEvent;
+class QGraphicsSceneEvent;
+class QGraphicsSceneHelpEvent;
+class QGraphicsSceneHoverEvent;
+class QGraphicsSceneMouseEvent;
+class QGraphicsSceneWheelEvent;
+class QGraphicsSimpleTextItem;
+class QGraphicsTextItem;
+class QGraphicsView;
+class QGraphicsWidget;
+class QGraphicsSceneIndex;
+class QHelpEvent;
+class QInputMethodEvent;
+class QKeyEvent;
+class QLineF;
+class QPainterPath;
+class QPixmap;
+class QPointF;
+class QPolygonF;
+class QRectF;
+class QSizeF;
+class QStyle;
+class QStyleOptionGraphicsItem;
+
+class QGraphicsScenePrivate;
+class Q_GUI_EXPORT QGraphicsScene : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(QBrush backgroundBrush READ backgroundBrush WRITE setBackgroundBrush)
+ Q_PROPERTY(QBrush foregroundBrush READ foregroundBrush WRITE setForegroundBrush)
+ Q_PROPERTY(ItemIndexMethod itemIndexMethod READ itemIndexMethod WRITE setItemIndexMethod)
+ Q_PROPERTY(QRectF sceneRect READ sceneRect WRITE setSceneRect)
+ Q_PROPERTY(int bspTreeDepth READ bspTreeDepth WRITE setBspTreeDepth)
+ Q_PROPERTY(QPalette palette READ palette WRITE setPalette)
+ Q_PROPERTY(QFont font READ font WRITE setFont)
+ Q_PROPERTY(bool sortCacheEnabled READ isSortCacheEnabled WRITE setSortCacheEnabled)
+ Q_PROPERTY(bool stickyFocus READ stickyFocus WRITE setStickyFocus)
+
+public:
+ enum ItemIndexMethod {
+ BspTreeIndex,
+ NoIndex = -1
+ };
+
+ enum SceneLayer {
+ ItemLayer = 0x1,
+ BackgroundLayer = 0x2,
+ ForegroundLayer = 0x4,
+ AllLayers = 0xffff
+ };
+ Q_DECLARE_FLAGS(SceneLayers, SceneLayer)
+
+ QGraphicsScene(QObject *parent = 0);
+ QGraphicsScene(const QRectF &sceneRect, QObject *parent = 0);
+ QGraphicsScene(qreal x, qreal y, qreal width, qreal height, QObject *parent = 0);
+ virtual ~QGraphicsScene();
+
+ QRectF sceneRect() const;
+ inline qreal width() const { return sceneRect().width(); }
+ inline qreal height() const { return sceneRect().height(); }
+ void setSceneRect(const QRectF &rect);
+ inline void setSceneRect(qreal x, qreal y, qreal w, qreal h)
+ { setSceneRect(QRectF(x, y, w, h)); }
+
+ void render(QPainter *painter,
+ const QRectF &target = QRectF(), const QRectF &source = QRectF(),
+ Qt::AspectRatioMode aspectRatioMode = Qt::KeepAspectRatio);
+
+ ItemIndexMethod itemIndexMethod() const;
+ void setItemIndexMethod(ItemIndexMethod method);
+
+ bool isSortCacheEnabled() const;
+ void setSortCacheEnabled(bool enabled);
+
+ int bspTreeDepth() const;
+ void setBspTreeDepth(int depth);
+
+ QRectF itemsBoundingRect() const;
+
+ QList<QGraphicsItem *> items() const;
+ QList<QGraphicsItem *> items(Qt::SortOrder order) const; // ### Qt 5: unify
+
+ QList<QGraphicsItem *> items(const QPointF &pos, Qt::ItemSelectionMode mode, Qt::SortOrder order, const QTransform &deviceTransform = QTransform()) const;
+ QList<QGraphicsItem *> items(const QRectF &rect, Qt::ItemSelectionMode mode, Qt::SortOrder order, const QTransform &deviceTransform = QTransform()) const;
+ QList<QGraphicsItem *> items(const QPolygonF &polygon, Qt::ItemSelectionMode mode, Qt::SortOrder order, const QTransform &deviceTransform = QTransform()) const;
+ QList<QGraphicsItem *> items(const QPainterPath &path, Qt::ItemSelectionMode mode, Qt::SortOrder order, const QTransform &deviceTransform = QTransform()) const;
+
+ QList<QGraphicsItem *> items(const QPointF &pos) const; // ### obsolete
+ QList<QGraphicsItem *> items(const QRectF &rect, Qt::ItemSelectionMode mode = Qt::IntersectsItemShape) const; // ### obsolete
+ QList<QGraphicsItem *> items(const QPolygonF &polygon, Qt::ItemSelectionMode mode = Qt::IntersectsItemShape) const; // ### obsolete
+ QList<QGraphicsItem *> items(const QPainterPath &path, Qt::ItemSelectionMode mode = Qt::IntersectsItemShape) const; // ### obsolete
+
+ QList<QGraphicsItem *> collidingItems(const QGraphicsItem *item, Qt::ItemSelectionMode mode = Qt::IntersectsItemShape) const;
+
+ QGraphicsItem *itemAt(const QPointF &pos) const; // ### obsolete
+ QGraphicsItem *itemAt(const QPointF &pos, const QTransform &deviceTransform) const;
+
+ inline QList<QGraphicsItem *> items(qreal x, qreal y, qreal w, qreal h, Qt::ItemSelectionMode mode = Qt::IntersectsItemShape) const
+ { return items(QRectF(x, y, w, h), mode); } // ### obsolete
+ inline QList<QGraphicsItem *> items(qreal x, qreal y, qreal w, qreal h, Qt::ItemSelectionMode mode, Qt::SortOrder order,
+ const QTransform &deviceTransform = QTransform()) const
+ { return items(QRectF(x, y, w, h), mode, order, deviceTransform); }
+ inline QGraphicsItem *itemAt(qreal x, qreal y) const // ### obsolete
+ { return itemAt(QPointF(x, y)); }
+ inline QGraphicsItem *itemAt(qreal x, qreal y, const QTransform &deviceTransform) const
+ { return itemAt(QPointF(x, y), deviceTransform); }
+
+ QList<QGraphicsItem *> selectedItems() const;
+ QPainterPath selectionArea() const;
+ void setSelectionArea(const QPainterPath &path); // ### obsolete
+ void setSelectionArea(const QPainterPath &path, const QTransform &deviceTransform);
+ void setSelectionArea(const QPainterPath &path, Qt::ItemSelectionMode mode); // ### obsolete
+ void setSelectionArea(const QPainterPath &path, Qt::ItemSelectionMode mode, const QTransform &deviceTransform);
+
+ QGraphicsItemGroup *createItemGroup(const QList<QGraphicsItem *> &items);
+ void destroyItemGroup(QGraphicsItemGroup *group);
+
+ void addItem(QGraphicsItem *item);
+ QGraphicsEllipseItem *addEllipse(const QRectF &rect, const QPen &pen = QPen(), const QBrush &brush = QBrush());
+ QGraphicsLineItem *addLine(const QLineF &line, const QPen &pen = QPen());
+ QGraphicsPathItem *addPath(const QPainterPath &path, const QPen &pen = QPen(), const QBrush &brush = QBrush());
+ QGraphicsPixmapItem *addPixmap(const QPixmap &pixmap);
+ QGraphicsPolygonItem *addPolygon(const QPolygonF &polygon, const QPen &pen = QPen(), const QBrush &brush = QBrush());
+ QGraphicsRectItem *addRect(const QRectF &rect, const QPen &pen = QPen(), const QBrush &brush = QBrush());
+ QGraphicsTextItem *addText(const QString &text, const QFont &font = QFont());
+ QGraphicsSimpleTextItem *addSimpleText(const QString &text, const QFont &font = QFont());
+ QGraphicsProxyWidget *addWidget(QWidget *widget, Qt::WindowFlags wFlags = 0);
+ inline QGraphicsEllipseItem *addEllipse(qreal x, qreal y, qreal w, qreal h, const QPen &pen = QPen(), const QBrush &brush = QBrush())
+ { return addEllipse(QRectF(x, y, w, h), pen, brush); }
+ inline QGraphicsLineItem *addLine(qreal x1, qreal y1, qreal x2, qreal y2, const QPen &pen = QPen())
+ { return addLine(QLineF(x1, y1, x2, y2), pen); }
+ inline QGraphicsRectItem *addRect(qreal x, qreal y, qreal w, qreal h, const QPen &pen = QPen(), const QBrush &brush = QBrush())
+ { return addRect(QRectF(x, y, w, h), pen, brush); }
+ void removeItem(QGraphicsItem *item);
+
+ QGraphicsItem *focusItem() const;
+ void setFocusItem(QGraphicsItem *item, Qt::FocusReason focusReason = Qt::OtherFocusReason);
+ bool hasFocus() const;
+ void setFocus(Qt::FocusReason focusReason = Qt::OtherFocusReason);
+ void clearFocus();
+
+ void setStickyFocus(bool enabled);
+ bool stickyFocus() const;
+
+ QGraphicsItem *mouseGrabberItem() const;
+
+ QBrush backgroundBrush() const;
+ void setBackgroundBrush(const QBrush &brush);
+
+ QBrush foregroundBrush() const;
+ void setForegroundBrush(const QBrush &brush);
+
+ virtual QVariant inputMethodQuery(Qt::InputMethodQuery query) const;
+
+ QList <QGraphicsView *> views() const;
+
+ inline void update(qreal x, qreal y, qreal w, qreal h)
+ { update(QRectF(x, y, w, h)); }
+ inline void invalidate(qreal x, qreal y, qreal w, qreal h, SceneLayers layers = AllLayers)
+ { invalidate(QRectF(x, y, w, h), layers); }
+
+ QStyle *style() const;
+ void setStyle(QStyle *style);
+
+ QFont font() const;
+ void setFont(const QFont &font);
+
+ QPalette palette() const;
+ void setPalette(const QPalette &palette);
+
+ bool isActive() const;
+ QGraphicsItem *activePanel() const;
+ void setActivePanel(QGraphicsItem *item);
+ QGraphicsWidget *activeWindow() const;
+ void setActiveWindow(QGraphicsWidget *widget);
+
+ bool sendEvent(QGraphicsItem *item, QEvent *event);
+
+public Q_SLOTS:
+ void update(const QRectF &rect = QRectF());
+ void invalidate(const QRectF &rect = QRectF(), SceneLayers layers = AllLayers);
+ void advance();
+ void clearSelection();
+ void clear();
+
+protected:
+ bool event(QEvent *event);
+ bool eventFilter(QObject *watched, QEvent *event);
+ virtual void contextMenuEvent(QGraphicsSceneContextMenuEvent *event);
+ virtual void dragEnterEvent(QGraphicsSceneDragDropEvent *event);
+ virtual void dragMoveEvent(QGraphicsSceneDragDropEvent *event);
+ virtual void dragLeaveEvent(QGraphicsSceneDragDropEvent *event);
+ virtual void dropEvent(QGraphicsSceneDragDropEvent *event);
+ virtual void focusInEvent(QFocusEvent *event);
+ virtual void focusOutEvent(QFocusEvent *event);
+ virtual void helpEvent(QGraphicsSceneHelpEvent *event);
+ virtual void keyPressEvent(QKeyEvent *event);
+ virtual void keyReleaseEvent(QKeyEvent *event);
+ virtual void mousePressEvent(QGraphicsSceneMouseEvent *event);
+ virtual void mouseMoveEvent(QGraphicsSceneMouseEvent *event);
+ virtual void mouseReleaseEvent(QGraphicsSceneMouseEvent *event);
+ virtual void mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event);
+ virtual void wheelEvent(QGraphicsSceneWheelEvent *event);
+ virtual void inputMethodEvent(QInputMethodEvent *event);
+
+ virtual void drawBackground(QPainter *painter, const QRectF &rect);
+ virtual void drawForeground(QPainter *painter, const QRectF &rect);
+ virtual void drawItems(QPainter *painter, int numItems,
+ QGraphicsItem *items[],
+ const QStyleOptionGraphicsItem options[],
+ QWidget *widget = 0);
+
+protected Q_SLOTS:
+ bool focusNextPrevChild(bool next);
+
+Q_SIGNALS:
+ void changed(const QList<QRectF> &region);
+ void sceneRectChanged(const QRectF &rect);
+ void selectionChanged();
+
+private:
+ Q_DECLARE_PRIVATE(QGraphicsScene)
+ Q_DISABLE_COPY(QGraphicsScene)
+ Q_PRIVATE_SLOT(d_func(), void _q_emitUpdated())
+ Q_PRIVATE_SLOT(d_func(), void _q_polishItems())
+ Q_PRIVATE_SLOT(d_func(), void _q_processDirtyItems())
+ Q_PRIVATE_SLOT(d_func(), void _q_updateScenePosDescendants())
+ friend class QGraphicsItem;
+ friend class QGraphicsItemPrivate;
+ friend class QGraphicsObject;
+ friend class QGraphicsView;
+ 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;
+#ifndef QT_NO_GESTURES
+ friend class QGesture;
+#endif
+};
+
+Q_DECLARE_OPERATORS_FOR_FLAGS(QGraphicsScene::SceneLayers)
+
+#endif // QT_NO_GRAPHICSVIEW
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif
diff --git a/src/widgets/graphicsview/qgraphicsscene_bsp.cpp b/src/widgets/graphicsview/qgraphicsscene_bsp.cpp
new file mode 100644
index 0000000000..3716debcc4
--- /dev/null
+++ b/src/widgets/graphicsview/qgraphicsscene_bsp.cpp
@@ -0,0 +1,296 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** 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 Technology Preview License Agreement accompanying
+** this package.
+**
+** 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.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qgraphicsscene_bsp_p.h"
+
+#ifndef QT_NO_GRAPHICSVIEW
+
+#include <QtCore/qstring.h>
+#include <private/qgraphicsitem_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QGraphicsSceneInsertItemBspTreeVisitor : public QGraphicsSceneBspTreeVisitor
+{
+public:
+ QGraphicsItem *item;
+
+ void visit(QList<QGraphicsItem *> *items)
+ { items->prepend(item); }
+};
+
+class QGraphicsSceneRemoveItemBspTreeVisitor : public QGraphicsSceneBspTreeVisitor
+{
+public:
+ QGraphicsItem *item;
+
+ void visit(QList<QGraphicsItem *> *items)
+ { items->removeAll(item); }
+};
+
+class QGraphicsSceneFindItemBspTreeVisitor : public QGraphicsSceneBspTreeVisitor
+{
+public:
+ QList<QGraphicsItem *> *foundItems;
+ bool onlyTopLevelItems;
+
+ void visit(QList<QGraphicsItem *> *items)
+ {
+ for (int i = 0; i < items->size(); ++i) {
+ QGraphicsItem *item = items->at(i);
+ if (onlyTopLevelItems && item->d_ptr->parent)
+ item = item->topLevelItem();
+ if (!item->d_func()->itemDiscovered && item->d_ptr->visible) {
+ item->d_func()->itemDiscovered = 1;
+ foundItems->prepend(item);
+ }
+ }
+ }
+};
+
+QGraphicsSceneBspTree::QGraphicsSceneBspTree()
+ : leafCnt(0)
+{
+ insertVisitor = new QGraphicsSceneInsertItemBspTreeVisitor;
+ removeVisitor = new QGraphicsSceneRemoveItemBspTreeVisitor;
+ findVisitor = new QGraphicsSceneFindItemBspTreeVisitor;
+}
+
+QGraphicsSceneBspTree::~QGraphicsSceneBspTree()
+{
+ delete insertVisitor;
+ delete removeVisitor;
+ delete findVisitor;
+}
+
+void QGraphicsSceneBspTree::initialize(const QRectF &rect, int depth)
+{
+ this->rect = rect;
+ leafCnt = 0;
+ nodes.resize((1 << (depth + 1)) - 1);
+ nodes.fill(Node());
+ leaves.resize(1 << depth);
+ leaves.fill(QList<QGraphicsItem *>());
+
+ initialize(rect, depth, 0);
+}
+
+void QGraphicsSceneBspTree::clear()
+{
+ leafCnt = 0;
+ nodes.clear();
+ leaves.clear();
+}
+
+void QGraphicsSceneBspTree::insertItem(QGraphicsItem *item, const QRectF &rect)
+{
+ insertVisitor->item = item;
+ climbTree(insertVisitor, rect);
+}
+
+void QGraphicsSceneBspTree::removeItem(QGraphicsItem *item, const QRectF &rect)
+{
+ removeVisitor->item = item;
+ climbTree(removeVisitor, rect);
+}
+
+void QGraphicsSceneBspTree::removeItems(const QSet<QGraphicsItem *> &items)
+{
+ for (int i = 0; i < leaves.size(); ++i) {
+ QList<QGraphicsItem *> newItemList;
+ const QList<QGraphicsItem *> &oldItemList = leaves[i];
+ for (int j = 0; j < oldItemList.size(); ++j) {
+ QGraphicsItem *item = oldItemList.at(j);
+ if (!items.contains(item))
+ newItemList << item;
+ }
+ leaves[i] = newItemList;
+ }
+}
+
+QList<QGraphicsItem *> QGraphicsSceneBspTree::items(const QRectF &rect, bool onlyTopLevelItems) const
+{
+ QList<QGraphicsItem *> tmp;
+ findVisitor->foundItems = &tmp;
+ findVisitor->onlyTopLevelItems = onlyTopLevelItems;
+ climbTree(findVisitor, rect);
+ // Reset discovery bits.
+ for (int i = 0; i < tmp.size(); ++i)
+ tmp.at(i)->d_ptr->itemDiscovered = 0;
+ return tmp;
+}
+
+int QGraphicsSceneBspTree::leafCount() const
+{
+ return leafCnt;
+}
+
+QString QGraphicsSceneBspTree::debug(int index) const
+{
+ const Node *node = &nodes.at(index);
+
+ QString tmp;
+ if (node->type == Node::Leaf) {
+ QRectF rect = rectForIndex(index);
+ if (!leaves[node->leafIndex].isEmpty()) {
+ tmp += QString::fromLatin1("[%1, %2, %3, %4] contains %5 items\n")
+ .arg(rect.left()).arg(rect.top())
+ .arg(rect.width()).arg(rect.height())
+ .arg(leaves[node->leafIndex].size());
+ }
+ } else {
+ if (node->type == Node::Horizontal) {
+ tmp += debug(firstChildIndex(index));
+ tmp += debug(firstChildIndex(index) + 1);
+ } else {
+ tmp += debug(firstChildIndex(index));
+ tmp += debug(firstChildIndex(index) + 1);
+ }
+ }
+
+ return tmp;
+}
+
+void QGraphicsSceneBspTree::initialize(const QRectF &rect, int depth, int index)
+{
+ Node *node = &nodes[index];
+ if (index == 0) {
+ node->type = Node::Horizontal;
+ node->offset = rect.center().x();
+ }
+
+ if (depth) {
+ Node::Type type;
+ QRectF rect1, rect2;
+ qreal offset1, offset2;
+
+ if (node->type == Node::Horizontal) {
+ type = Node::Vertical;
+ rect1.setRect(rect.left(), rect.top(), rect.width(), rect.height() / 2);
+ rect2.setRect(rect1.left(), rect1.bottom(), rect1.width(), rect.height() - rect1.height());
+ offset1 = rect1.center().x();
+ offset2 = rect2.center().x();
+ } else {
+ type = Node::Horizontal;
+ rect1.setRect(rect.left(), rect.top(), rect.width() / 2, rect.height());
+ rect2.setRect(rect1.right(), rect1.top(), rect.width() - rect1.width(), rect1.height());
+ offset1 = rect1.center().y();
+ offset2 = rect2.center().y();
+ }
+
+ int childIndex = firstChildIndex(index);
+
+ Node *child = &nodes[childIndex];
+ child->offset = offset1;
+ child->type = type;
+
+ child = &nodes[childIndex + 1];
+ child->offset = offset2;
+ child->type = type;
+
+ initialize(rect1, depth - 1, childIndex);
+ initialize(rect2, depth - 1, childIndex + 1);
+ } else {
+ node->type = Node::Leaf;
+ node->leafIndex = leafCnt++;
+ }
+}
+
+void QGraphicsSceneBspTree::climbTree(QGraphicsSceneBspTreeVisitor *visitor, const QRectF &rect, int index) const
+{
+ if (nodes.isEmpty())
+ return;
+
+ const Node &node = nodes.at(index);
+ const int childIndex = firstChildIndex(index);
+
+ switch (node.type) {
+ case Node::Leaf: {
+ visitor->visit(const_cast<QList<QGraphicsItem*>*>(&leaves[node.leafIndex]));
+ break;
+ }
+ case Node::Vertical:
+ if (rect.left() < node.offset) {
+ climbTree(visitor, rect, childIndex);
+ if (rect.right() >= node.offset)
+ climbTree(visitor, rect, childIndex + 1);
+ } else {
+ climbTree(visitor, rect, childIndex + 1);
+ }
+ break;
+ case Node::Horizontal:
+ if (rect.top() < node.offset) {
+ climbTree(visitor, rect, childIndex);
+ if (rect.bottom() >= node.offset)
+ climbTree(visitor, rect, childIndex + 1);
+ } else {
+ climbTree(visitor, rect, childIndex + 1);
+ }
+ }
+}
+
+QRectF QGraphicsSceneBspTree::rectForIndex(int index) const
+{
+ if (index <= 0)
+ return rect;
+
+ int parentIdx = parentIndex(index);
+ QRectF rect = rectForIndex(parentIdx);
+ const Node *parent = &nodes.at(parentIdx);
+
+ if (parent->type == Node::Horizontal) {
+ if (index & 1)
+ rect.setRight(parent->offset);
+ else
+ rect.setLeft(parent->offset);
+ } else {
+ if (index & 1)
+ rect.setBottom(parent->offset);
+ else
+ rect.setTop(parent->offset);
+ }
+
+ return rect;
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_GRAPHICSVIEW
diff --git a/src/widgets/graphicsview/qgraphicsscene_bsp_p.h b/src/widgets/graphicsview/qgraphicsscene_bsp_p.h
new file mode 100644
index 0000000000..4f230eff79
--- /dev/null
+++ b/src/widgets/graphicsview/qgraphicsscene_bsp_p.h
@@ -0,0 +1,132 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** 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 Technology Preview License Agreement accompanying
+** this package.
+**
+** 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.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QGRAPHICSSCENEBSPTREE_P_H
+#define QGRAPHICSSCENEBSPTREE_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of other Qt classes. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtCore/qlist.h>
+
+#if !defined(QT_NO_GRAPHICSVIEW) || (QT_EDITION & QT_MODULE_GRAPHICSVIEW) != QT_MODULE_GRAPHICSVIEW
+
+#include <QtCore/qrect.h>
+#include <QtCore/qset.h>
+#include <QtCore/qvector.h>
+
+QT_BEGIN_NAMESPACE
+
+class QGraphicsItem;
+class QGraphicsSceneBspTreeVisitor;
+class QGraphicsSceneInsertItemBspTreeVisitor;
+class QGraphicsSceneRemoveItemBspTreeVisitor;
+class QGraphicsSceneFindItemBspTreeVisitor;
+
+class QGraphicsSceneBspTree
+{
+public:
+ struct Node
+ {
+ enum Type { Horizontal, Vertical, Leaf };
+ union {
+ qreal offset;
+ int leafIndex;
+ };
+ Type type;
+ };
+
+ QGraphicsSceneBspTree();
+ ~QGraphicsSceneBspTree();
+
+ void initialize(const QRectF &rect, int depth);
+ void clear();
+
+ void insertItem(QGraphicsItem *item, const QRectF &rect);
+ void removeItem(QGraphicsItem *item, const QRectF &rect);
+ void removeItems(const QSet<QGraphicsItem *> &items);
+
+ QList<QGraphicsItem *> items(const QRectF &rect, bool onlyTopLevelItems = false) const;
+ int leafCount() const;
+
+ inline int firstChildIndex(int index) const
+ { return index * 2 + 1; }
+
+ inline int parentIndex(int index) const
+ { return index > 0 ? ((index & 1) ? ((index - 1) / 2) : ((index - 2) / 2)) : -1; }
+
+ QString debug(int index) const;
+
+private:
+ void initialize(const QRectF &rect, int depth, int index);
+ void climbTree(QGraphicsSceneBspTreeVisitor *visitor, const QRectF &rect, int index = 0) const;
+ QRectF rectForIndex(int index) const;
+
+ QVector<Node> nodes;
+ QVector<QList<QGraphicsItem *> > leaves;
+ int leafCnt;
+ QRectF rect;
+
+ QGraphicsSceneInsertItemBspTreeVisitor *insertVisitor;
+ QGraphicsSceneRemoveItemBspTreeVisitor *removeVisitor;
+ QGraphicsSceneFindItemBspTreeVisitor *findVisitor;
+};
+
+class QGraphicsSceneBspTreeVisitor
+{
+public:
+ virtual ~QGraphicsSceneBspTreeVisitor() { }
+ virtual void visit(QList<QGraphicsItem *> *items) = 0;
+};
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_GRAPHICSVIEW
+
+#endif // QGRAPHICSSCENEBSPTREE_P_H
diff --git a/src/widgets/graphicsview/qgraphicsscene_p.h b/src/widgets/graphicsview/qgraphicsscene_p.h
new file mode 100644
index 0000000000..9460a4dc51
--- /dev/null
+++ b/src/widgets/graphicsview/qgraphicsscene_p.h
@@ -0,0 +1,359 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** 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 Technology Preview License Agreement accompanying
+** this package.
+**
+** 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.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QGRAPHICSSCENE_P_H
+#define QGRAPHICSSCENE_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of other Qt classes. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qgraphicsscene.h"
+
+#if !defined(QT_NO_GRAPHICSVIEW) || (QT_EDITION & QT_MODULE_GRAPHICSVIEW) != QT_MODULE_GRAPHICSVIEW
+
+#include "qgraphicssceneevent.h"
+#include "qgraphicsview.h"
+#include "qgraphicsview_p.h"
+#include "qgraphicsitem_p.h"
+
+#include <private/qobject_p.h>
+#include <QtCore/qbitarray.h>
+#include <QtCore/qlist.h>
+#include <QtCore/qmap.h>
+#include <QtCore/qset.h>
+#include <QtGui/qfont.h>
+#include <QtGui/qpalette.h>
+#include <QtGui/qstyle.h>
+#include <QtGui/qstyleoption.h>
+
+QT_BEGIN_NAMESPACE
+
+class QGraphicsSceneIndex;
+class QGraphicsView;
+class QGraphicsWidget;
+
+class Q_AUTOTEST_EXPORT QGraphicsScenePrivate : public QObjectPrivate
+{
+ Q_DECLARE_PUBLIC(QGraphicsScene)
+public:
+ QGraphicsScenePrivate();
+ void init();
+
+ static QGraphicsScenePrivate *get(QGraphicsScene *q);
+
+ int changedSignalIndex;
+ int processDirtyItemsIndex;
+ int polishItemsIndex;
+
+ QGraphicsScene::ItemIndexMethod indexMethod;
+ QGraphicsSceneIndex *index;
+
+ int lastItemCount;
+
+ QRectF sceneRect;
+
+ quint32 hasSceneRect : 1;
+ quint32 dirtyGrowingItemsBoundingRect : 1;
+ quint32 updateAll : 1;
+ quint32 calledEmitUpdated : 1;
+ quint32 processDirtyItemsEmitted : 1;
+ quint32 needSortTopLevelItems : 1;
+ quint32 holesInTopLevelSiblingIndex : 1;
+ quint32 topLevelSequentialOrdering : 1;
+ quint32 scenePosDescendantsUpdatePending : 1;
+ quint32 stickyFocus : 1;
+ quint32 hasFocus : 1;
+ quint32 lastMouseGrabberItemHasImplicitMouseGrab : 1;
+ quint32 allItemsIgnoreHoverEvents : 1;
+ quint32 allItemsUseDefaultCursor : 1;
+ quint32 painterStateProtection : 1;
+ quint32 sortCacheEnabled : 1; // for compatibility
+ quint32 allItemsIgnoreTouchEvents : 1;
+ quint32 padding : 15;
+
+ QRectF growingItemsBoundingRect;
+
+ void _q_emitUpdated();
+ QList<QRectF> updatedRects;
+
+ QPainterPath selectionArea;
+ int selectionChanging;
+ QSet<QGraphicsItem *> selectedItems;
+ QVector<QGraphicsItem *> unpolishedItems;
+ QList<QGraphicsItem *> topLevelItems;
+
+ QMap<QGraphicsItem *, QPointF> movingItemsInitialPositions;
+ void registerTopLevelItem(QGraphicsItem *item);
+ void unregisterTopLevelItem(QGraphicsItem *item);
+ void _q_updateLater();
+ void _q_polishItems();
+
+ void _q_processDirtyItems();
+
+ QSet<QGraphicsItem *> scenePosItems;
+ void setScenePosItemEnabled(QGraphicsItem *item, bool enabled);
+ void registerScenePosItem(QGraphicsItem *item);
+ void unregisterScenePosItem(QGraphicsItem *item);
+ void _q_updateScenePosDescendants();
+
+ void removeItemHelper(QGraphicsItem *item);
+
+ QBrush backgroundBrush;
+ QBrush foregroundBrush;
+
+ quint32 rectAdjust;
+ QGraphicsItem *focusItem;
+ QGraphicsItem *lastFocusItem;
+ QGraphicsItem *passiveFocusItem;
+ QGraphicsWidget *tabFocusFirst;
+ QGraphicsItem *activePanel;
+ QGraphicsItem *lastActivePanel;
+ int activationRefCount;
+ int childExplicitActivation;
+ void setActivePanelHelper(QGraphicsItem *item, bool duringActivationEvent);
+ void setFocusItemHelper(QGraphicsItem *item, Qt::FocusReason focusReason);
+
+ QList<QGraphicsWidget *> popupWidgets;
+ void addPopup(QGraphicsWidget *widget);
+ void removePopup(QGraphicsWidget *widget, bool itemIsDying = false);
+
+ QGraphicsItem *lastMouseGrabberItem;
+ QList<QGraphicsItem *> mouseGrabberItems;
+ void grabMouse(QGraphicsItem *item, bool implicit = false);
+ void ungrabMouse(QGraphicsItem *item, bool itemIsDying = false);
+ void clearMouseGrabber();
+
+ QList<QGraphicsItem *> keyboardGrabberItems;
+ void grabKeyboard(QGraphicsItem *item);
+ void ungrabKeyboard(QGraphicsItem *item, bool itemIsDying = false);
+ void clearKeyboardGrabber();
+
+ QGraphicsItem *dragDropItem;
+ QGraphicsWidget *enterWidget;
+ Qt::DropAction lastDropAction;
+ QList<QGraphicsItem *> cachedItemsUnderMouse;
+ QList<QGraphicsItem *> hoverItems;
+ QPointF lastSceneMousePos;
+ void enableMouseTrackingOnViews();
+ QMap<Qt::MouseButton, QPointF> mouseGrabberButtonDownPos;
+ QMap<Qt::MouseButton, QPointF> mouseGrabberButtonDownScenePos;
+ QMap<Qt::MouseButton, QPoint> mouseGrabberButtonDownScreenPos;
+ QList<QGraphicsItem *> itemsAtPosition(const QPoint &screenPos,
+ const QPointF &scenePos,
+ QWidget *widget) const;
+ void storeMouseButtonsForMouseGrabber(QGraphicsSceneMouseEvent *event);
+
+ QList<QGraphicsView *> views;
+ void addView(QGraphicsView *view);
+ void removeView(QGraphicsView *view);
+
+ QMultiMap<QGraphicsItem *, QGraphicsItem *> sceneEventFilters;
+ void installSceneEventFilter(QGraphicsItem *watched, QGraphicsItem *filter);
+ void removeSceneEventFilter(QGraphicsItem *watched, QGraphicsItem *filter);
+ bool filterDescendantEvent(QGraphicsItem *item, QEvent *event);
+ bool filterEvent(QGraphicsItem *item, QEvent *event);
+ bool sendEvent(QGraphicsItem *item, QEvent *event);
+
+ bool dispatchHoverEvent(QGraphicsSceneHoverEvent *hoverEvent);
+ bool itemAcceptsHoverEvents_helper(const QGraphicsItem *item) const;
+ void leaveScene(QWidget *viewport);
+
+ void cloneDragDropEvent(QGraphicsSceneDragDropEvent *dest,
+ QGraphicsSceneDragDropEvent *source);
+ void sendDragDropEvent(QGraphicsItem *item,
+ QGraphicsSceneDragDropEvent *dragDropEvent);
+ void sendHoverEvent(QEvent::Type type, QGraphicsItem *item,
+ QGraphicsSceneHoverEvent *hoverEvent);
+ void sendMouseEvent(QGraphicsSceneMouseEvent *mouseEvent);
+ void mousePressEventHandler(QGraphicsSceneMouseEvent *mouseEvent);
+ QGraphicsWidget *windowForItem(const QGraphicsItem *item) const;
+
+ void drawItemHelper(QGraphicsItem *item, QPainter *painter,
+ const QStyleOptionGraphicsItem *option, QWidget *widget,
+ bool painterStateProtection);
+
+ void drawItems(QPainter *painter, const QTransform *const viewTransform,
+ QRegion *exposedRegion, QWidget *widget);
+
+ void drawSubtreeRecursive(QGraphicsItem *item, QPainter *painter, const QTransform *const,
+ 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 force = false, bool ignoreOpacity = false, bool removingItemFromScene = false,
+ bool updateBoundingRect = false);
+ void processDirtyItemsRecursive(QGraphicsItem *item, bool dirtyAncestorContainsChildren = false,
+ qreal parentOpacity = qreal(1.0));
+
+ inline void resetDirtyItem(QGraphicsItem *item, bool recursive = false)
+ {
+ Q_ASSERT(item);
+ item->d_ptr->dirty = 0;
+ item->d_ptr->paintedViewBoundingRectsNeedRepaint = 0;
+ item->d_ptr->geometryChanged = 0;
+ if (!item->d_ptr->dirtyChildren)
+ recursive = false;
+ item->d_ptr->dirtyChildren = 0;
+ item->d_ptr->needsRepaint = QRectF();
+ item->d_ptr->allChildrenDirty = 0;
+ item->d_ptr->fullUpdatePending = 0;
+ item->d_ptr->ignoreVisible = 0;
+ item->d_ptr->ignoreOpacity = 0;
+#ifndef QT_NO_GRAPHICSEFFECT
+ 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;
+ }
+#endif //QT_NO_GRAPHICSEFFECT
+ if (recursive) {
+ for (int i = 0; i < item->d_ptr->children.size(); ++i)
+ resetDirtyItem(item->d_ptr->children.at(i), recursive);
+ }
+#ifndef QT_NO_GRAPHICSEFFECT
+ if (flags && item->d_ptr->graphicsEffect)
+ item->d_ptr->graphicsEffect->sourceChanged(flags);
+#endif //QT_NO_GRAPHICSEFFECT
+ }
+
+ inline void ensureSortedTopLevelItems()
+ {
+ if (needSortTopLevelItems) {
+ qSort(topLevelItems.begin(), topLevelItems.end(), qt_notclosestLeaf);
+ topLevelSequentialOrdering = false;
+ needSortTopLevelItems = false;
+ }
+ }
+
+ void ensureSequentialTopLevelSiblingIndexes();
+
+ QStyle *style;
+ QFont font;
+ void setFont_helper(const QFont &font);
+ void resolveFont();
+ void updateFont(const QFont &font);
+ QPalette palette;
+ void setPalette_helper(const QPalette &palette);
+ void resolvePalette();
+ void updatePalette(const QPalette &palette);
+
+ QStyleOptionGraphicsItem styleOptionTmp;
+
+ QMap<int, QTouchEvent::TouchPoint> sceneCurrentTouchPoints;
+ QMap<int, QGraphicsItem *> itemForTouchPointId;
+ static void updateTouchPointsForItem(QGraphicsItem *item, QTouchEvent *touchEvent);
+ int findClosestTouchPointId(const QPointF &scenePos);
+ void touchEventHandler(QTouchEvent *touchEvent);
+ bool sendTouchBeginEvent(QGraphicsItem *item, QTouchEvent *touchEvent);
+ void enableTouchEventsOnViews();
+
+ QList<QGraphicsObject *> cachedTargetItems;
+#ifndef QT_NO_GESTURES
+ QHash<QGraphicsObject *, QSet<QGesture *> > cachedItemGestures;
+ QHash<QGraphicsObject *, QSet<QGesture *> > cachedAlreadyDeliveredGestures;
+ QHash<QGesture *, QGraphicsObject *> gestureTargets;
+ QHash<Qt::GestureType, int> grabbedGestures;
+ void gestureEventHandler(QGestureEvent *event);
+ void gestureTargetsAtHotSpots(const QSet<QGesture *> &gestures,
+ Qt::GestureFlag flag,
+ QHash<QGraphicsObject *, QSet<QGesture *> > *targets,
+ QSet<QGraphicsObject *> *itemsSet = 0,
+ QSet<QGesture *> *normal = 0,
+ QSet<QGesture *> *conflicts = 0);
+ void cancelGesturesForChildren(QGesture *original);
+ void grabGesture(QGraphicsItem *, Qt::GestureType gesture);
+ void ungrabGesture(QGraphicsItem *, Qt::GestureType gesture);
+#endif // QT_NO_GESTURES
+
+ void updateInputMethodSensitivityInViews();
+
+ QList<QGraphicsItem *> modalPanels;
+ void enterModal(QGraphicsItem *item,
+ QGraphicsItem::PanelModality panelModality = QGraphicsItem::NonModal);
+ void leaveModal(QGraphicsItem *item);
+};
+
+// QRectF::intersects() returns false always if either the source or target
+// rectangle's width or height are 0. This works around that problem.
+static inline void _q_adjustRect(QRectF *rect)
+{
+ Q_ASSERT(rect);
+ if (!rect->width())
+ rect->adjust(qreal(-0.00001), 0, qreal(0.00001), 0);
+ if (!rect->height())
+ rect->adjust(0, qreal(-0.00001), 0, qreal(0.00001));
+}
+
+static inline QRectF adjustedItemBoundingRect(const QGraphicsItem *item)
+{
+ Q_ASSERT(item);
+ QRectF boundingRect(item->boundingRect());
+ _q_adjustRect(&boundingRect);
+ 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
+
+#endif
diff --git a/src/widgets/graphicsview/qgraphicsscenebsptreeindex.cpp b/src/widgets/graphicsview/qgraphicsscenebsptreeindex.cpp
new file mode 100644
index 0000000000..92e4a55952
--- /dev/null
+++ b/src/widgets/graphicsview/qgraphicsscenebsptreeindex.cpp
@@ -0,0 +1,719 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** 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 Technology Preview License Agreement accompanying
+** this package.
+**
+** 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.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*!
+ \class QGraphicsSceneBspTreeIndex
+ \brief The QGraphicsSceneBspTreeIndex class provides an implementation of
+ a BSP indexing algorithm for discovering items in QGraphicsScene.
+ \since 4.6
+ \ingroup graphicsview-api
+
+ \internal
+
+ QGraphicsSceneBspTreeIndex index use a BSP(Binary Space Partitioning)
+ implementation to discover items quickly. This implementation is
+ very efficient for static scene. It has a depth that you can set.
+ The depth directly affects performance and memory usage; the latter
+ growing exponentially with the depth of the tree. With an optimal tree
+ depth, the index can instantly determine the locality of items, even
+ for scenes with thousands or millions of items. This also greatly improves
+ rendering performance.
+
+ By default, the value is 0, in which case Qt will guess a reasonable
+ default depth based on the size, location and number of items in the
+ scene. If these parameters change frequently, however, you may experience
+ slowdowns as the index retunes the depth internally. You can avoid
+ potential slowdowns by fixating the tree depth through setting this
+ property.
+
+ The depth of the tree and the size of the scene rectangle decide the
+ granularity of the scene's partitioning. The size of each scene segment is
+ determined by the following algorithm:
+
+ The BSP tree has an optimal size when each segment contains between 0 and
+ 10 items.
+
+ \sa QGraphicsScene, QGraphicsView, QGraphicsSceneIndex
+*/
+
+#include <QtCore/qglobal.h>
+
+#ifndef QT_NO_GRAPHICSVIEW
+
+#include <private/qgraphicsscene_p.h>
+#include <private/qgraphicsscenebsptreeindex_p.h>
+#include <private/qgraphicssceneindex_p.h>
+
+#include <QtCore/qmath.h>
+#include <QtCore/qdebug.h>
+
+QT_BEGIN_NAMESPACE
+
+static inline int intmaxlog(int n)
+{
+ return (n > 0 ? qMax(qCeil(qLn(qreal(n)) / qLn(qreal(2))), 5) : 0);
+}
+
+/*!
+ Constructs a private scene bsp index.
+*/
+QGraphicsSceneBspTreeIndexPrivate::QGraphicsSceneBspTreeIndexPrivate(QGraphicsScene *scene)
+ : QGraphicsSceneIndexPrivate(scene),
+ bspTreeDepth(0),
+ indexTimerId(0),
+ restartIndexTimer(false),
+ regenerateIndex(true),
+ lastItemCount(0),
+ purgePending(false),
+ sortCacheEnabled(false),
+ updatingSortCache(false)
+{
+}
+
+
+/*!
+ This method will update the BSP index by removing the items from the temporary
+ unindexed list and add them in the indexedItems list. This will also
+ update the growingItemsBoundingRect if needed. This will update the BSP
+ implementation as well.
+
+ \internal
+*/
+void QGraphicsSceneBspTreeIndexPrivate::_q_updateIndex()
+{
+ Q_Q(QGraphicsSceneBspTreeIndex);
+ if (!indexTimerId)
+ return;
+
+ q->killTimer(indexTimerId);
+ indexTimerId = 0;
+
+ purgeRemovedItems();
+
+ // Add unindexedItems to indexedItems
+ for (int i = 0; i < unindexedItems.size(); ++i) {
+ if (QGraphicsItem *item = unindexedItems.at(i)) {
+ Q_ASSERT(!item->d_ptr->itemDiscovered);
+ if (!freeItemIndexes.isEmpty()) {
+ int freeIndex = freeItemIndexes.takeFirst();
+ item->d_func()->index = freeIndex;
+ indexedItems[freeIndex] = item;
+ } else {
+ item->d_func()->index = indexedItems.size();
+ indexedItems << item;
+ }
+ }
+ }
+
+ // Determine whether we should regenerate the BSP tree.
+ if (bspTreeDepth == 0) {
+ int oldDepth = intmaxlog(lastItemCount);
+ bspTreeDepth = intmaxlog(indexedItems.size());
+ static const int slack = 100;
+ if (bsp.leafCount() == 0 || (oldDepth != bspTreeDepth && qAbs(lastItemCount - indexedItems.size()) > slack)) {
+ // ### Crude algorithm.
+ regenerateIndex = true;
+ }
+ }
+
+ // Regenerate the tree.
+ if (regenerateIndex) {
+ regenerateIndex = false;
+ bsp.initialize(sceneRect, bspTreeDepth);
+ unindexedItems = indexedItems;
+ lastItemCount = indexedItems.size();
+ }
+
+ // Insert all unindexed items into the tree.
+ for (int i = 0; i < unindexedItems.size(); ++i) {
+ if (QGraphicsItem *item = unindexedItems.at(i)) {
+ if (item->d_ptr->itemIsUntransformable()) {
+ untransformableItems << item;
+ continue;
+ }
+ if (item->d_ptr->ancestorFlags & QGraphicsItemPrivate::AncestorClipsChildren)
+ continue;
+
+ bsp.insertItem(item, item->d_ptr->sceneEffectiveBoundingRect());
+ }
+ }
+ unindexedItems.clear();
+}
+
+
+/*!
+ \internal
+
+ Removes stale pointers from all data structures.
+*/
+void QGraphicsSceneBspTreeIndexPrivate::purgeRemovedItems()
+{
+ if (!purgePending && removedItems.isEmpty())
+ return;
+
+ // Remove stale items from the BSP tree.
+ bsp.removeItems(removedItems);
+ // Purge this list.
+ removedItems.clear();
+ freeItemIndexes.clear();
+ for (int i = 0; i < indexedItems.size(); ++i) {
+ if (!indexedItems.at(i))
+ freeItemIndexes << i;
+ }
+ purgePending = false;
+}
+
+/*!
+ \internal
+
+ Starts or restarts the timer used for reindexing unindexed items.
+*/
+void QGraphicsSceneBspTreeIndexPrivate::startIndexTimer(int interval)
+{
+ Q_Q(QGraphicsSceneBspTreeIndex);
+ if (indexTimerId) {
+ restartIndexTimer = true;
+ } else {
+ indexTimerId = q->startTimer(interval);
+ }
+}
+
+/*!
+ \internal
+*/
+void QGraphicsSceneBspTreeIndexPrivate::resetIndex()
+{
+ purgeRemovedItems();
+ for (int i = 0; i < indexedItems.size(); ++i) {
+ if (QGraphicsItem *item = indexedItems.at(i)) {
+ item->d_ptr->index = -1;
+ Q_ASSERT(!item->d_ptr->itemDiscovered);
+ unindexedItems << item;
+ }
+ }
+ indexedItems.clear();
+ freeItemIndexes.clear();
+ untransformableItems.clear();
+ regenerateIndex = true;
+ startIndexTimer();
+}
+
+/*!
+ \internal
+*/
+void QGraphicsSceneBspTreeIndexPrivate::climbTree(QGraphicsItem *item, int *stackingOrder)
+{
+ if (!item->d_ptr->children.isEmpty()) {
+ QList<QGraphicsItem *> childList = item->d_ptr->children;
+ qSort(childList.begin(), childList.end(), qt_closestLeaf);
+ for (int i = 0; i < childList.size(); ++i) {
+ QGraphicsItem *item = childList.at(i);
+ if (!(item->flags() & QGraphicsItem::ItemStacksBehindParent))
+ climbTree(childList.at(i), stackingOrder);
+ }
+ item->d_ptr->globalStackingOrder = (*stackingOrder)++;
+ for (int i = 0; i < childList.size(); ++i) {
+ QGraphicsItem *item = childList.at(i);
+ if (item->flags() & QGraphicsItem::ItemStacksBehindParent)
+ climbTree(childList.at(i), stackingOrder);
+ }
+ } else {
+ item->d_ptr->globalStackingOrder = (*stackingOrder)++;
+ }
+}
+
+/*!
+ \internal
+*/
+void QGraphicsSceneBspTreeIndexPrivate::_q_updateSortCache()
+{
+ Q_Q(QGraphicsSceneBspTreeIndex);
+ _q_updateIndex();
+
+ if (!sortCacheEnabled || !updatingSortCache)
+ return;
+
+ updatingSortCache = false;
+ int stackingOrder = 0;
+
+ QList<QGraphicsItem *> topLevels;
+ const QList<QGraphicsItem *> items = q->items();
+ for (int i = 0; i < items.size(); ++i) {
+ QGraphicsItem *item = items.at(i);
+ if (item && !item->d_ptr->parent)
+ topLevels << item;
+ }
+
+ qSort(topLevels.begin(), topLevels.end(), qt_closestLeaf);
+ for (int i = 0; i < topLevels.size(); ++i)
+ climbTree(topLevels.at(i), &stackingOrder);
+}
+
+/*!
+ \internal
+*/
+void QGraphicsSceneBspTreeIndexPrivate::invalidateSortCache()
+{
+ Q_Q(QGraphicsSceneBspTreeIndex);
+ if (!sortCacheEnabled || updatingSortCache)
+ return;
+
+ updatingSortCache = true;
+ QMetaObject::invokeMethod(q, "_q_updateSortCache", Qt::QueuedConnection);
+}
+
+void QGraphicsSceneBspTreeIndexPrivate::addItem(QGraphicsItem *item, bool recursive)
+{
+ if (!item)
+ return;
+
+ // Prevent reusing a recently deleted pointer: purge all removed item from our lists.
+ purgeRemovedItems();
+
+ // Invalidate any sort caching; arrival of a new item means we need to resort.
+ // Update the scene's sort cache settings.
+ item->d_ptr->globalStackingOrder = -1;
+ invalidateSortCache();
+
+ // Indexing requires sceneBoundingRect(), but because \a item might
+ // not be completely constructed at this point, we need to store it in
+ // a temporary list and schedule an indexing for later.
+ if (item->d_ptr->index == -1) {
+ Q_ASSERT(!unindexedItems.contains(item));
+ unindexedItems << item;
+ startIndexTimer(0);
+ } else {
+ Q_ASSERT(indexedItems.contains(item));
+ qWarning("QGraphicsSceneBspTreeIndex::addItem: item has already been added to this BSP");
+ }
+
+ if (recursive) {
+ for (int i = 0; i < item->d_ptr->children.size(); ++i)
+ addItem(item->d_ptr->children.at(i), recursive);
+ }
+}
+
+void QGraphicsSceneBspTreeIndexPrivate::removeItem(QGraphicsItem *item, bool recursive,
+ bool moveToUnindexedItems)
+{
+ if (!item)
+ return;
+
+ if (item->d_ptr->index != -1) {
+ Q_ASSERT(item->d_ptr->index < indexedItems.size());
+ Q_ASSERT(indexedItems.at(item->d_ptr->index) == item);
+ Q_ASSERT(!item->d_ptr->itemDiscovered);
+ freeItemIndexes << item->d_ptr->index;
+ indexedItems[item->d_ptr->index] = 0;
+ item->d_ptr->index = -1;
+
+ if (item->d_ptr->itemIsUntransformable()) {
+ untransformableItems.removeOne(item);
+ } else if (item->d_ptr->inDestructor) {
+ // Avoid virtual function calls from the destructor.
+ purgePending = true;
+ removedItems << item;
+ } else if (!(item->d_ptr->ancestorFlags & QGraphicsItemPrivate::AncestorClipsChildren)) {
+ bsp.removeItem(item, item->d_ptr->sceneEffectiveBoundingRect());
+ }
+ } else {
+ unindexedItems.removeOne(item);
+ }
+ invalidateSortCache(); // ### Only do this when removing from BSP?
+
+ Q_ASSERT(item->d_ptr->index == -1);
+ Q_ASSERT(!indexedItems.contains(item));
+ Q_ASSERT(!unindexedItems.contains(item));
+ Q_ASSERT(!untransformableItems.contains(item));
+
+ if (moveToUnindexedItems)
+ addItem(item);
+
+ if (recursive) {
+ for (int i = 0; i < item->d_ptr->children.size(); ++i)
+ removeItem(item->d_ptr->children.at(i), recursive, moveToUnindexedItems);
+ }
+}
+
+QList<QGraphicsItem *> QGraphicsSceneBspTreeIndexPrivate::estimateItems(const QRectF &rect, Qt::SortOrder order,
+ bool onlyTopLevelItems)
+{
+ Q_Q(QGraphicsSceneBspTreeIndex);
+ if (onlyTopLevelItems && rect.isNull())
+ return q->QGraphicsSceneIndex::estimateTopLevelItems(rect, order);
+
+ purgeRemovedItems();
+ _q_updateSortCache();
+ Q_ASSERT(unindexedItems.isEmpty());
+
+ QList<QGraphicsItem *> rectItems = bsp.items(rect, onlyTopLevelItems);
+ if (onlyTopLevelItems) {
+ for (int i = 0; i < untransformableItems.size(); ++i) {
+ QGraphicsItem *item = untransformableItems.at(i);
+ if (!item->d_ptr->parent) {
+ rectItems << item;
+ } else {
+ item = item->topLevelItem();
+ if (!rectItems.contains(item))
+ rectItems << item;
+ }
+ }
+ } else {
+ rectItems += untransformableItems;
+ }
+
+ sortItems(&rectItems, order, sortCacheEnabled, onlyTopLevelItems);
+ return rectItems;
+}
+
+/*!
+ Sort a list of \a itemList in a specific \a order and use the cache if requested.
+
+ \internal
+*/
+void QGraphicsSceneBspTreeIndexPrivate::sortItems(QList<QGraphicsItem *> *itemList, Qt::SortOrder order,
+ bool sortCacheEnabled, bool onlyTopLevelItems)
+{
+ if (order == Qt::SortOrder(-1))
+ return;
+
+ if (onlyTopLevelItems) {
+ if (order == Qt::DescendingOrder)
+ qSort(itemList->begin(), itemList->end(), qt_closestLeaf);
+ else if (order == Qt::AscendingOrder)
+ qSort(itemList->begin(), itemList->end(), qt_notclosestLeaf);
+ return;
+ }
+
+ if (sortCacheEnabled) {
+ if (order == Qt::DescendingOrder) {
+ qSort(itemList->begin(), itemList->end(), closestItemFirst_withCache);
+ } else if (order == Qt::AscendingOrder) {
+ qSort(itemList->begin(), itemList->end(), closestItemLast_withCache);
+ }
+ } else {
+ if (order == Qt::DescendingOrder) {
+ qSort(itemList->begin(), itemList->end(), qt_closestItemFirst);
+ } else if (order == Qt::AscendingOrder) {
+ qSort(itemList->begin(), itemList->end(), qt_closestItemLast);
+ }
+ }
+}
+
+/*!
+ Constructs a BSP scene index for the given \a scene.
+*/
+QGraphicsSceneBspTreeIndex::QGraphicsSceneBspTreeIndex(QGraphicsScene *scene)
+ : QGraphicsSceneIndex(*new QGraphicsSceneBspTreeIndexPrivate(scene), scene)
+{
+
+}
+
+QGraphicsSceneBspTreeIndex::~QGraphicsSceneBspTreeIndex()
+{
+ Q_D(QGraphicsSceneBspTreeIndex);
+ for (int i = 0; i < d->indexedItems.size(); ++i) {
+ // Ensure item bits are reset properly.
+ if (QGraphicsItem *item = d->indexedItems.at(i)) {
+ Q_ASSERT(!item->d_ptr->itemDiscovered);
+ item->d_ptr->index = -1;
+ }
+ }
+}
+
+/*!
+ \internal
+ Clear the all the BSP index.
+*/
+void QGraphicsSceneBspTreeIndex::clear()
+{
+ Q_D(QGraphicsSceneBspTreeIndex);
+ d->bsp.clear();
+ d->lastItemCount = 0;
+ d->freeItemIndexes.clear();
+ for (int i = 0; i < d->indexedItems.size(); ++i) {
+ // Ensure item bits are reset properly.
+ if (QGraphicsItem *item = d->indexedItems.at(i)) {
+ Q_ASSERT(!item->d_ptr->itemDiscovered);
+ item->d_ptr->index = -1;
+ }
+ }
+ d->indexedItems.clear();
+ d->unindexedItems.clear();
+ d->untransformableItems.clear();
+ d->regenerateIndex = true;
+}
+
+/*!
+ Add the \a item into the BSP index.
+*/
+void QGraphicsSceneBspTreeIndex::addItem(QGraphicsItem *item)
+{
+ Q_D(QGraphicsSceneBspTreeIndex);
+ d->addItem(item);
+}
+
+/*!
+ Remove the \a item from the BSP index.
+*/
+void QGraphicsSceneBspTreeIndex::removeItem(QGraphicsItem *item)
+{
+ Q_D(QGraphicsSceneBspTreeIndex);
+ d->removeItem(item);
+}
+
+/*!
+ \internal
+ Update the BSP when the \a item 's bounding rect has changed.
+*/
+void QGraphicsSceneBspTreeIndex::prepareBoundingRectChange(const QGraphicsItem *item)
+{
+ if (!item)
+ return;
+
+ if (item->d_ptr->index == -1 || item->d_ptr->itemIsUntransformable()
+ || (item->d_ptr->ancestorFlags & QGraphicsItemPrivate::AncestorClipsChildren)) {
+ return; // Item is not in BSP tree; nothing to do.
+ }
+
+ Q_D(QGraphicsSceneBspTreeIndex);
+ QGraphicsItem *thatItem = const_cast<QGraphicsItem *>(item);
+ d->removeItem(thatItem, /*recursive=*/false, /*moveToUnindexedItems=*/true);
+ for (int i = 0; i < item->d_ptr->children.size(); ++i) // ### Do we really need this?
+ prepareBoundingRectChange(item->d_ptr->children.at(i));
+}
+
+/*!
+ Returns an estimation visible items that are either inside or
+ intersect with the specified \a rect and return a list sorted using \a order.
+
+ \a deviceTransform is the transformation apply to the view.
+
+*/
+QList<QGraphicsItem *> QGraphicsSceneBspTreeIndex::estimateItems(const QRectF &rect, Qt::SortOrder order) const
+{
+ Q_D(const QGraphicsSceneBspTreeIndex);
+ return const_cast<QGraphicsSceneBspTreeIndexPrivate*>(d)->estimateItems(rect, order);
+}
+
+QList<QGraphicsItem *> QGraphicsSceneBspTreeIndex::estimateTopLevelItems(const QRectF &rect, Qt::SortOrder order) const
+{
+ Q_D(const QGraphicsSceneBspTreeIndex);
+ return const_cast<QGraphicsSceneBspTreeIndexPrivate*>(d)->estimateItems(rect, order, /*onlyTopLevels=*/true);
+}
+
+/*!
+ \fn QList<QGraphicsItem *> QGraphicsSceneBspTreeIndex::items(Qt::SortOrder order = Qt::DescendingOrder) const;
+
+ Return all items in the BSP index and sort them using \a order.
+*/
+QList<QGraphicsItem *> QGraphicsSceneBspTreeIndex::items(Qt::SortOrder order) const
+{
+ Q_D(const QGraphicsSceneBspTreeIndex);
+ const_cast<QGraphicsSceneBspTreeIndexPrivate*>(d)->purgeRemovedItems();
+ QList<QGraphicsItem *> itemList;
+
+ // If freeItemIndexes is empty, we know there are no holes in indexedItems and
+ // unindexedItems.
+ if (d->freeItemIndexes.isEmpty()) {
+ if (d->unindexedItems.isEmpty()) {
+ itemList = d->indexedItems;
+ } else {
+ itemList = d->indexedItems + d->unindexedItems;
+ }
+ } else {
+ // Rebuild the list of items to avoid holes. ### We could also just
+ // compress the item lists at this point.
+ foreach (QGraphicsItem *item, d->indexedItems + d->unindexedItems) {
+ if (item)
+ itemList << item;
+ }
+ }
+ if (order != -1) {
+ //We sort descending order
+ d->sortItems(&itemList, order, d->sortCacheEnabled);
+ }
+ return itemList;
+}
+
+/*!
+ \property QGraphicsSceneBspTreeIndex::bspTreeDepth
+ \brief the depth of the BSP index tree
+ \since 4.6
+
+ This value determines the depth of BSP tree. The depth
+ directly affects performance and memory usage; the latter
+ growing exponentially with the depth of the tree. With an optimal tree
+ depth, the index can instantly determine the locality of items, even
+ for scenes with thousands or millions of items. This also greatly improves
+ rendering performance.
+
+ By default, the value is 0, in which case Qt will guess a reasonable
+ default depth based on the size, location and number of items in the
+ scene. If these parameters change frequently, however, you may experience
+ slowdowns as the index retunes the depth internally. You can avoid
+ potential slowdowns by fixating the tree depth through setting this
+ property.
+
+ The depth of the tree and the size of the scene rectangle decide the
+ granularity of the scene's partitioning. The size of each scene segment is
+ determined by the following algorithm:
+
+ The BSP tree has an optimal size when each segment contains between 0 and
+ 10 items.
+
+*/
+int QGraphicsSceneBspTreeIndex::bspTreeDepth()
+{
+ Q_D(const QGraphicsSceneBspTreeIndex);
+ return d->bspTreeDepth;
+}
+
+void QGraphicsSceneBspTreeIndex::setBspTreeDepth(int depth)
+{
+ Q_D(QGraphicsSceneBspTreeIndex);
+ if (d->bspTreeDepth == depth)
+ return;
+ d->bspTreeDepth = depth;
+ d->resetIndex();
+}
+
+/*!
+ \internal
+
+ This method react to the \a rect change of the scene and
+ reset the BSP tree index.
+*/
+void QGraphicsSceneBspTreeIndex::updateSceneRect(const QRectF &rect)
+{
+ Q_D(QGraphicsSceneBspTreeIndex);
+ d->sceneRect = rect;
+ d->resetIndex();
+}
+
+/*!
+ \internal
+
+ This method react to the \a change of the \a item and use the \a value to
+ update the BSP tree if necessary.
+*/
+void QGraphicsSceneBspTreeIndex::itemChange(const QGraphicsItem *item, QGraphicsItem::GraphicsItemChange change, const void *const value)
+{
+ Q_D(QGraphicsSceneBspTreeIndex);
+ switch (change) {
+ case QGraphicsItem::ItemFlagsChange: {
+ // Handle ItemIgnoresTransformations
+ QGraphicsItem::GraphicsItemFlags newFlags = *static_cast<const QGraphicsItem::GraphicsItemFlags *>(value);
+ bool ignoredTransform = item->d_ptr->flags & QGraphicsItem::ItemIgnoresTransformations;
+ bool willIgnoreTransform = newFlags & QGraphicsItem::ItemIgnoresTransformations;
+ bool clipsChildren = item->d_ptr->flags & QGraphicsItem::ItemClipsChildrenToShape;
+ bool willClipChildren = newFlags & QGraphicsItem::ItemClipsChildrenToShape;
+ if ((ignoredTransform != willIgnoreTransform) || (clipsChildren != willClipChildren)) {
+ QGraphicsItem *thatItem = const_cast<QGraphicsItem *>(item);
+ // Remove item and its descendants from the index and append
+ // them to the list of unindexed items. Then, when the index
+ // is updated, they will be put into the bsp-tree or the list
+ // of untransformable items.
+ d->removeItem(thatItem, /*recursive=*/true, /*moveToUnidexedItems=*/true);
+ }
+ break;
+ }
+ case QGraphicsItem::ItemZValueChange:
+ d->invalidateSortCache();
+ break;
+ case QGraphicsItem::ItemParentChange: {
+ d->invalidateSortCache();
+ // Handle ItemIgnoresTransformations
+ const QGraphicsItem *newParent = static_cast<const QGraphicsItem *>(value);
+ bool ignoredTransform = item->d_ptr->itemIsUntransformable();
+ bool willIgnoreTransform = (item->d_ptr->flags & QGraphicsItem::ItemIgnoresTransformations)
+ || (newParent && newParent->d_ptr->itemIsUntransformable());
+ bool ancestorClippedChildren = item->d_ptr->ancestorFlags & QGraphicsItemPrivate::AncestorClipsChildren;
+ bool ancestorWillClipChildren = newParent
+ && ((newParent->d_ptr->flags & QGraphicsItem::ItemClipsChildrenToShape)
+ || (newParent->d_ptr->ancestorFlags & QGraphicsItemPrivate::AncestorClipsChildren));
+ if ((ignoredTransform != willIgnoreTransform) || (ancestorClippedChildren != ancestorWillClipChildren)) {
+ QGraphicsItem *thatItem = const_cast<QGraphicsItem *>(item);
+ // Remove item and its descendants from the index and append
+ // them to the list of unindexed items. Then, when the index
+ // is updated, they will be put into the bsp-tree or the list
+ // of untransformable items.
+ d->removeItem(thatItem, /*recursive=*/true, /*moveToUnidexedItems=*/true);
+ }
+ break;
+ }
+ default:
+ break;
+ }
+}
+/*!
+ \reimp
+
+ Used to catch the timer event.
+
+ \internal
+*/
+bool QGraphicsSceneBspTreeIndex::event(QEvent *event)
+{
+ Q_D(QGraphicsSceneBspTreeIndex);
+ switch (event->type()) {
+ case QEvent::Timer:
+ if (d->indexTimerId && static_cast<QTimerEvent *>(event)->timerId() == d->indexTimerId) {
+ if (d->restartIndexTimer) {
+ d->restartIndexTimer = false;
+ } else {
+ // this call will kill the timer
+ d->_q_updateIndex();
+ }
+ }
+ // Fallthrough intended - support timers in subclasses.
+ default:
+ return QObject::event(event);
+ }
+ return true;
+}
+
+QT_END_NAMESPACE
+
+#include "moc_qgraphicsscenebsptreeindex_p.cpp"
+
+#endif // QT_NO_GRAPHICSVIEW
+
diff --git a/src/widgets/graphicsview/qgraphicsscenebsptreeindex_p.h b/src/widgets/graphicsview/qgraphicsscenebsptreeindex_p.h
new file mode 100644
index 0000000000..fadf9d8a74
--- /dev/null
+++ b/src/widgets/graphicsview/qgraphicsscenebsptreeindex_p.h
@@ -0,0 +1,206 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtCore 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 Technology Preview License Agreement accompanying
+** this package.
+**
+** 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.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of other Qt classes. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#ifndef QGRAPHICSBSPTREEINDEX_H
+#define QGRAPHICSBSPTREEINDEX_H
+
+#include <QtCore/qglobal.h>
+
+#if !defined(QT_NO_GRAPHICSVIEW) || (QT_EDITION & QT_MODULE_GRAPHICSVIEW) != QT_MODULE_GRAPHICSVIEW
+
+#include "qgraphicssceneindex_p.h"
+#include "qgraphicsitem_p.h"
+#include "qgraphicsscene_bsp_p.h"
+
+#include <QtCore/qrect.h>
+#include <QtCore/qlist.h>
+
+QT_BEGIN_NAMESPACE
+
+static const int QGRAPHICSSCENE_INDEXTIMER_TIMEOUT = 2000;
+
+class QGraphicsScene;
+class QGraphicsSceneBspTreeIndexPrivate;
+
+class Q_AUTOTEST_EXPORT QGraphicsSceneBspTreeIndex : public QGraphicsSceneIndex
+{
+ Q_OBJECT
+ Q_PROPERTY(int bspTreeDepth READ bspTreeDepth WRITE setBspTreeDepth)
+public:
+ QGraphicsSceneBspTreeIndex(QGraphicsScene *scene = 0);
+ ~QGraphicsSceneBspTreeIndex();
+
+ QList<QGraphicsItem *> estimateItems(const QRectF &rect, Qt::SortOrder order) const;
+ QList<QGraphicsItem *> estimateTopLevelItems(const QRectF &rect, Qt::SortOrder order) const;
+ QList<QGraphicsItem *> items(Qt::SortOrder order = Qt::DescendingOrder) const;
+
+ int bspTreeDepth();
+ void setBspTreeDepth(int depth);
+
+protected Q_SLOTS:
+ void updateSceneRect(const QRectF &rect);
+
+protected:
+ bool event(QEvent *event);
+ void clear();
+
+ void addItem(QGraphicsItem *item);
+ void removeItem(QGraphicsItem *item);
+ void prepareBoundingRectChange(const QGraphicsItem *item);
+
+ void itemChange(const QGraphicsItem *item, QGraphicsItem::GraphicsItemChange change, const void *const value);
+
+private :
+ Q_DECLARE_PRIVATE(QGraphicsSceneBspTreeIndex)
+ Q_DISABLE_COPY(QGraphicsSceneBspTreeIndex)
+ Q_PRIVATE_SLOT(d_func(), void _q_updateSortCache())
+ Q_PRIVATE_SLOT(d_func(), void _q_updateIndex())
+
+ friend class QGraphicsScene;
+ friend class QGraphicsScenePrivate;
+};
+
+class QGraphicsSceneBspTreeIndexPrivate : public QGraphicsSceneIndexPrivate
+{
+ Q_DECLARE_PUBLIC(QGraphicsSceneBspTreeIndex)
+public:
+ QGraphicsSceneBspTreeIndexPrivate(QGraphicsScene *scene);
+
+ QGraphicsSceneBspTree bsp;
+ QRectF sceneRect;
+ int bspTreeDepth;
+ int indexTimerId;
+ bool restartIndexTimer;
+ bool regenerateIndex;
+ int lastItemCount;
+
+ QList<QGraphicsItem *> indexedItems;
+ QList<QGraphicsItem *> unindexedItems;
+ QList<QGraphicsItem *> untransformableItems;
+ QList<int> freeItemIndexes;
+
+ bool purgePending;
+ QSet<QGraphicsItem *> removedItems;
+ void purgeRemovedItems();
+
+ void _q_updateIndex();
+ void startIndexTimer(int interval = QGRAPHICSSCENE_INDEXTIMER_TIMEOUT);
+ void resetIndex();
+
+ void _q_updateSortCache();
+ bool sortCacheEnabled;
+ bool updatingSortCache;
+ void invalidateSortCache();
+ void addItem(QGraphicsItem *item, bool recursive = false);
+ void removeItem(QGraphicsItem *item, bool recursive = false, bool moveToUnindexedItems = false);
+ QList<QGraphicsItem *> estimateItems(const QRectF &, Qt::SortOrder, bool b = false);
+
+ static void climbTree(QGraphicsItem *item, int *stackingOrder);
+
+ static inline bool closestItemFirst_withCache(const QGraphicsItem *item1, const QGraphicsItem *item2)
+ {
+ return item1->d_ptr->globalStackingOrder < item2->d_ptr->globalStackingOrder;
+ }
+ static inline bool closestItemLast_withCache(const QGraphicsItem *item1, const QGraphicsItem *item2)
+ {
+ return item1->d_ptr->globalStackingOrder >= item2->d_ptr->globalStackingOrder;
+ }
+
+ static void sortItems(QList<QGraphicsItem *> *itemList, Qt::SortOrder order,
+ bool cached, bool onlyTopLevelItems = false);
+};
+
+static inline bool QRectF_intersects(const QRectF &s, const QRectF &r)
+{
+ qreal xp = s.left();
+ qreal yp = s.top();
+ qreal w = s.width();
+ qreal h = s.height();
+ qreal l1 = xp;
+ qreal r1 = xp;
+ if (w < 0)
+ l1 += w;
+ else
+ r1 += w;
+
+ qreal l2 = r.left();
+ qreal r2 = r.left();
+ if (w < 0)
+ l2 += r.width();
+ else
+ r2 += r.width();
+
+ if (l1 >= r2 || l2 >= r1)
+ return false;
+
+ qreal t1 = yp;
+ qreal b1 = yp;
+ if (h < 0)
+ t1 += h;
+ else
+ b1 += h;
+
+ qreal t2 = r.top();
+ qreal b2 = r.top();
+ if (r.height() < 0)
+ t2 += r.height();
+ else
+ b2 += r.height();
+
+ return !(t1 >= b2 || t2 >= b1);
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_GRAPHICSVIEW
+
+#endif // QGRAPHICSBSPTREEINDEX_H
diff --git a/src/widgets/graphicsview/qgraphicssceneevent.cpp b/src/widgets/graphicsview/qgraphicssceneevent.cpp
new file mode 100644
index 0000000000..1b1f3db0aa
--- /dev/null
+++ b/src/widgets/graphicsview/qgraphicssceneevent.cpp
@@ -0,0 +1,1674 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** 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 Technology Preview License Agreement accompanying
+** this package.
+**
+** 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.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*!
+ \class QGraphicsSceneEvent
+ \brief The QGraphicsSceneEvent class provides a base class for all
+ graphics view related events.
+ \since 4.2
+ \ingroup graphicsview-api
+
+ When a QGraphicsView receives Qt mouse, keyboard, and drag and
+ drop events (QMouseEvent, QKeyEvent, QDragEvent, etc.), it
+ translates them into instances of QGraphicsSceneEvent subclasses
+ and forwards them to the QGraphicsScene it displays. The scene
+ then forwards the events to the relevant items.
+
+ For example, when a QGraphicsView receives a QMouseEvent of type
+ MousePress as a response to a user click, the view sends a
+ QGraphicsSceneMouseEvent of type GraphicsSceneMousePress to the
+ underlying QGraphicsScene through its
+ \l{QGraphicsScene::}{mousePressEvent()} function. The default
+ QGraphicsScene::mousePressEvent() implementation determines which
+ item was clicked and forwards the event to
+ QGraphicsItem::mousePressEvent().
+
+ \omit ### Beskrive widget() \endomit
+
+ Subclasses such as QGraphicsSceneMouseEvent and
+ QGraphicsSceneContextMenuEvent provide the coordinates from the
+ original QEvent in screen, scene, and item coordinates (see
+ \l{QGraphicsSceneMouseEvent::}{screenPos()},
+ \l{QGraphicsSceneMouseEvent::}{scenePos()}, and
+ \l{QGraphicsSceneMouseEvent::}{pos()}). The item coordinates are
+ set by the QGraphicsScene before it forwards the event to the
+ event to a QGraphicsItem. The mouse events also add the
+ possibility to retrieve the coordinates from the last event
+ received by the view (see
+ \l{QGraphicsSceneMouseEvent::}{lastScreenPos()},
+ \l{QGraphicsSceneMouseEvent::}{lastScenePos()}, and
+ \l{QGraphicsSceneMouseEvent::}{lastPos()}).
+
+ \sa QEvent
+*/
+
+/*!
+ \class QGraphicsSceneMouseEvent
+ \brief The QGraphicsSceneMouseEvent class provides mouse events
+ in the graphics view framework.
+ \since 4.2
+ \ingroup graphicsview-api
+
+ When a QGraphicsView receives a QMouseEvent, it translates it to a
+ QGraphicsSceneMouseEvent. The event is then forwarded to the
+ QGraphicsScene associated with the view. If the event is not
+ handled by the scene, the view may use it, e.g., for the
+ \l{QGraphicsView::}{DragMode}.
+
+ In addition to containing the item, scene, and screen coordinates
+ of the event (as pos(), scenePos(), and screenPos()), mouse
+ events also contain the coordinates of the previous mouse
+ event received by the view. These can be retrieved with
+ lastPos(), lastScreenPos(), and lastScenePos().
+
+ \sa QGraphicsSceneContextMenuEvent,
+ QGraphicsSceneHoverEvent, QGraphicsSceneWheelEvent,
+ QMouseEvent
+*/
+
+/*!
+ \class QGraphicsSceneWheelEvent
+ \brief The QGraphicsSceneWheelEvent class provides wheel events
+ in the graphics view framework.
+ \brief The QGraphicsSceneWheelEvent class provides wheel events in the
+ graphics view framework.
+ \since 4.2
+ \ingroup graphicsview-api
+
+ \l{QWheelEvent}{QWheelEvent}s received by a QGraphicsView are translated
+ into QGraphicsSceneWheelEvents; it translates the QWheelEvent::globalPos()
+ into item, scene, and screen coordinates (pos(), scenePos(), and
+ screenPos()).
+
+ \sa QGraphicsSceneMouseEvent, QGraphicsSceneContextMenuEvent,
+ QGraphicsSceneHoverEvent, QWheelEvent
+*/
+
+/*!
+ \class QGraphicsSceneContextMenuEvent
+ \brief The QGraphicsSceneContextMenuEvent class provides context
+ menu events in the graphics view framework.
+ \since 4.2
+ \ingroup graphicsview-api
+
+ A QContextMenuEvent received by a QGraphicsView is translated
+ into a QGraphicsSceneContextMenuEvent. The
+ QContextMenuEvent::globalPos() is translated into item, scene, and
+ screen coordinates (pos(), scenePos(), and screenPos()).
+
+ \sa QGraphicsSceneMouseEvent, QGraphicsSceneWheelEvent,
+ QContextMenuEvent
+*/
+
+/*!
+ \enum QGraphicsSceneContextMenuEvent::Reason
+
+ This enum describes the reason why the context event was sent.
+
+ \value Mouse The mouse caused the event to be sent. On most
+ platforms, this means the right mouse button was clicked.
+
+ \value Keyboard The keyboard caused this event to be sent. On
+ Windows and Mac OS X, this means the menu button was pressed.
+
+ \value Other The event was sent by some other means (i.e. not
+ by the mouse or keyboard).
+*/
+
+/*!
+ \class QGraphicsSceneHoverEvent
+ \brief The QGraphicsSceneHoverEvent class provides hover events
+ in the graphics view framework.
+ \since 4.2
+ \ingroup graphicsview-api
+
+ When a QGraphicsView receives a QHoverEvent event, it translates
+ it into QGraphicsSceneHoverEvent. The event is then forwarded to
+ the QGraphicsScene associated with the view.
+
+ \sa QGraphicsSceneMouseEvent, QGraphicsSceneContextMenuEvent,
+ QGraphicsSceneWheelEvent, QHoverEvent
+*/
+
+/*!
+ \class QGraphicsSceneHelpEvent
+ \brief The QGraphicsSceneHelpEvent class provides events when a
+ tooltip is requested.
+ \since 4.2
+ \ingroup graphicsview-api
+
+ When a QGraphicsView receives a QEvent of type
+ QEvent::ToolTip, it creates a QGraphicsSceneHelpEvent, which is
+ forwarded to the scene. You can set a tooltip on a QGraphicsItem
+ with \l{QGraphicsItem::}{setToolTip()}; by default QGraphicsScene
+ displays the tooltip of the QGraphicsItem with the highest
+ z-value (i.e, the top-most item) under the mouse position.
+
+ QGraphicsView does not forward events when
+ \l{QWhatsThis}{"What's This"} and \l{QStatusTipEvent}{status tip}
+ help is requested. If you need this, you can reimplement
+ QGraphicsView::viewportEvent() and forward QStatusTipEvent
+ events and \l{QEvent}{QEvents} of type QEvent::WhatsThis to the
+ scene.
+
+ \sa QEvent
+*/
+
+/*!
+ \class QGraphicsSceneDragDropEvent
+ \brief The QGraphicsSceneDragDropEvent class provides events for
+ drag and drop in the graphics view framework.
+ \since 4.2
+ \ingroup graphicsview-api
+
+ QGraphicsView inherits the drag and drop functionality provided
+ by QWidget. When it receives a drag and drop event, it translates
+ it to a QGraphicsSceneDragDropEvent.
+
+ QGraphicsSceneDragDropEvent stores events of type
+ GraphicsSceneDragEnter, GraphicsSceneDragLeave,
+ GraphicsSceneDragMove, or GraphicsSceneDrop.
+
+ QGraphicsSceneDragDropEvent contains the position of the mouse
+ cursor in both item, scene, and screen coordinates; this can be
+ retrieved with pos(), scenePos(), and screenPos().
+
+ The scene sends the event to the first QGraphicsItem under the
+ mouse cursor that accepts drops; a graphics item is set to accept
+ drops with \l{QGraphicsItem::}{setAcceptDrops()}.
+*/
+
+/*!
+ \class QGraphicsSceneResizeEvent
+ \brief The QGraphicsSceneResizeEvent class provides events for widget
+ resizing in the graphics view framework.
+ \since 4.4
+ \ingroup graphicsview-api
+
+ A QGraphicsWidget sends itself a QGraphicsSceneResizeEvent immediately
+ when its geometry changes.
+
+ It's similar to QResizeEvent, but its sizes, oldSize() and newSize(), use
+ QSizeF instead of QSize.
+
+ \sa QGraphicsWidget::setGeometry(), QGraphicsWidget::resize()
+*/
+
+/*!
+ \class QGraphicsSceneMoveEvent
+ \brief The QGraphicsSceneMoveEvent class provides events for widget
+ moving in the graphics view framework.
+ \since 4.4
+ \ingroup graphicsview-api
+
+ A QGraphicsWidget sends itself a QGraphicsSceneMoveEvent immediately when
+ its local position changes. The delivery is implemented as part of
+ QGraphicsItem::itemChange().
+
+ It's similar to QMoveEvent, but its positions, oldPos() and newPos(), use
+ QPointF instead of QPoint.
+
+ \sa QGraphicsItem::setPos(), QGraphicsItem::ItemPositionChange,
+ QGraphicsItem::ItemPositionHasChanged
+*/
+
+#include "qgraphicssceneevent.h"
+
+#ifndef QT_NO_GRAPHICSVIEW
+
+#ifndef QT_NO_DEBUG
+#include <QtCore/qdebug.h>
+#endif
+#include <QtCore/qmap.h>
+#include <QtCore/qpoint.h>
+#include <QtCore/qsize.h>
+#include <QtCore/qstring.h>
+#include "qgraphicsview.h"
+#include "qgraphicsitem.h"
+#include <QtGui/qgesture.h>
+#include <private/qevent_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QGraphicsSceneEventPrivate
+{
+public:
+ inline QGraphicsSceneEventPrivate()
+ : widget(0),
+ q_ptr(0)
+ { }
+
+ inline virtual ~QGraphicsSceneEventPrivate()
+ { }
+
+ QWidget *widget;
+ QGraphicsSceneEvent *q_ptr;
+};
+
+/*!
+ \internal
+
+ Constructs a generic graphics scene event of the specified \a type.
+*/
+QGraphicsSceneEvent::QGraphicsSceneEvent(Type type)
+ : QEvent(type), d_ptr(new QGraphicsSceneEventPrivate)
+{
+ d_ptr->q_ptr = this;
+}
+
+/*!
+ \internal
+
+ Constructs a generic graphics scene event.
+*/
+QGraphicsSceneEvent::QGraphicsSceneEvent(QGraphicsSceneEventPrivate &dd, Type type)
+ : QEvent(type), d_ptr(&dd)
+{
+ d_ptr->q_ptr = this;
+}
+
+/*!
+ Destroys the event.
+*/
+QGraphicsSceneEvent::~QGraphicsSceneEvent()
+{
+}
+
+/*!
+ Returns the widget where the event originated, or 0 if the event
+ originates from another application.
+*/
+QWidget *QGraphicsSceneEvent::widget() const
+{
+ return d_ptr->widget;
+}
+
+/*!
+ \internal
+
+ Sets the \a widget related to this event.
+
+ \sa widget()
+*/
+void QGraphicsSceneEvent::setWidget(QWidget *widget)
+{
+ d_ptr->widget = widget;
+}
+
+class QGraphicsSceneMouseEventPrivate : public QGraphicsSceneEventPrivate
+{
+ Q_DECLARE_PUBLIC(QGraphicsSceneMouseEvent)
+public:
+ inline QGraphicsSceneMouseEventPrivate()
+ : button(Qt::NoButton),
+ buttons(0), modifiers(0)
+ { }
+
+ QPointF pos;
+ QPointF scenePos;
+ QPoint screenPos;
+ QPointF lastPos;
+ QPointF lastScenePos;
+ QPoint lastScreenPos;
+ QMap<Qt::MouseButton, QPointF> buttonDownPos;
+ QMap<Qt::MouseButton, QPointF> buttonDownScenePos;
+ QMap<Qt::MouseButton, QPoint> buttonDownScreenPos;
+ Qt::MouseButton button;
+ Qt::MouseButtons buttons;
+ Qt::KeyboardModifiers modifiers;
+};
+
+/*!
+ \internal
+
+ Constructs a generic graphics scene mouse event of the specified \a type.
+*/
+QGraphicsSceneMouseEvent::QGraphicsSceneMouseEvent(Type type)
+ : QGraphicsSceneEvent(*new QGraphicsSceneMouseEventPrivate, type)
+{
+}
+
+/*!
+ Destroys the event.
+*/
+QGraphicsSceneMouseEvent::~QGraphicsSceneMouseEvent()
+{
+}
+
+/*!
+ Returns the mouse cursor position in item coordinates.
+
+ \sa scenePos(), screenPos(), lastPos()
+*/
+QPointF QGraphicsSceneMouseEvent::pos() const
+{
+ Q_D(const QGraphicsSceneMouseEvent);
+ return d->pos;
+}
+
+/*!
+ \internal
+*/
+void QGraphicsSceneMouseEvent::setPos(const QPointF &pos)
+{
+ Q_D(QGraphicsSceneMouseEvent);
+ d->pos = pos;
+}
+
+/*!
+ Returns the mouse cursor position in scene coordinates.
+
+ \sa pos(), screenPos(), lastScenePos()
+*/
+QPointF QGraphicsSceneMouseEvent::scenePos() const
+{
+ Q_D(const QGraphicsSceneMouseEvent);
+ return d->scenePos;
+}
+
+/*!
+ \internal
+*/
+void QGraphicsSceneMouseEvent::setScenePos(const QPointF &pos)
+{
+ Q_D(QGraphicsSceneMouseEvent);
+ d->scenePos = pos;
+}
+
+/*!
+ Returns the mouse cursor position in screen coordinates.
+
+ \sa pos(), scenePos(), lastScreenPos()
+*/
+QPoint QGraphicsSceneMouseEvent::screenPos() const
+{
+ Q_D(const QGraphicsSceneMouseEvent);
+ return d->screenPos;
+}
+
+/*!
+ \internal
+*/
+void QGraphicsSceneMouseEvent::setScreenPos(const QPoint &pos)
+{
+ Q_D(QGraphicsSceneMouseEvent);
+ d->screenPos = pos;
+}
+
+/*!
+ Returns the mouse cursor position in item coordinates where the specified
+ \a button was clicked.
+
+ \sa buttonDownScenePos(), buttonDownScreenPos(), pos()
+*/
+QPointF QGraphicsSceneMouseEvent::buttonDownPos(Qt::MouseButton button) const
+{
+ Q_D(const QGraphicsSceneMouseEvent);
+ return d->buttonDownPos.value(button);
+}
+
+/*!
+ \internal
+*/
+void QGraphicsSceneMouseEvent::setButtonDownPos(Qt::MouseButton button, const QPointF &pos)
+{
+ Q_D(QGraphicsSceneMouseEvent);
+ d->buttonDownPos.insert(button, pos);
+}
+
+/*!
+ Returns the mouse cursor position in scene coordinates where the
+ specified \a button was clicked.
+
+ \sa buttonDownPos(), buttonDownScreenPos(), scenePos()
+*/
+QPointF QGraphicsSceneMouseEvent::buttonDownScenePos(Qt::MouseButton button) const
+{
+ Q_D(const QGraphicsSceneMouseEvent);
+ return d->buttonDownScenePos.value(button);
+}
+
+/*!
+ \internal
+*/
+void QGraphicsSceneMouseEvent::setButtonDownScenePos(Qt::MouseButton button, const QPointF &pos)
+{
+ Q_D(QGraphicsSceneMouseEvent);
+ d->buttonDownScenePos.insert(button, pos);
+}
+
+/*!
+ Returns the mouse cursor position in screen coordinates where the
+ specified \a button was clicked.
+
+ \sa screenPos(), buttonDownPos(), buttonDownScenePos()
+*/
+QPoint QGraphicsSceneMouseEvent::buttonDownScreenPos(Qt::MouseButton button) const
+{
+ Q_D(const QGraphicsSceneMouseEvent);
+ return d->buttonDownScreenPos.value(button);
+}
+
+/*!
+ \internal
+*/
+void QGraphicsSceneMouseEvent::setButtonDownScreenPos(Qt::MouseButton button, const QPoint &pos)
+{
+ Q_D(QGraphicsSceneMouseEvent);
+ d->buttonDownScreenPos.insert(button, pos);
+}
+
+/*!
+ Returns the last recorded mouse cursor position in item
+ coordinates.
+
+ \sa lastScenePos(), lastScreenPos(), pos()
+*/
+QPointF QGraphicsSceneMouseEvent::lastPos() const
+{
+ Q_D(const QGraphicsSceneMouseEvent);
+ return d->lastPos;
+}
+
+/*!
+ \internal
+*/
+void QGraphicsSceneMouseEvent::setLastPos(const QPointF &pos)
+{
+ Q_D(QGraphicsSceneMouseEvent);
+ d->lastPos = pos;
+}
+
+/*!
+ Returns the last recorded mouse cursor position in scene
+ coordinates. The last recorded position is the position of
+ the previous mouse event received by the view that created
+ the event.
+
+ \sa lastPos(), lastScreenPos(), scenePos()
+*/
+QPointF QGraphicsSceneMouseEvent::lastScenePos() const
+{
+ Q_D(const QGraphicsSceneMouseEvent);
+ return d->lastScenePos;
+}
+
+/*!
+ \internal
+*/
+void QGraphicsSceneMouseEvent::setLastScenePos(const QPointF &pos)
+{
+ Q_D(QGraphicsSceneMouseEvent);
+ d->lastScenePos = pos;
+}
+
+/*!
+ Returns the last recorded mouse cursor position in screen
+ coordinates. The last recorded position is the position of
+ the previous mouse event received by the view that created
+ the event.
+
+ \sa lastPos(), lastScenePos(), screenPos()
+*/
+QPoint QGraphicsSceneMouseEvent::lastScreenPos() const
+{
+ Q_D(const QGraphicsSceneMouseEvent);
+ return d->lastScreenPos;
+}
+
+/*!
+ \internal
+*/
+void QGraphicsSceneMouseEvent::setLastScreenPos(const QPoint &pos)
+{
+ Q_D(QGraphicsSceneMouseEvent);
+ d->lastScreenPos = pos;
+}
+
+/*!
+ Returns the combination of mouse buttons that were pressed at the
+ time the event was sent.
+
+ \sa button(), modifiers()
+*/
+Qt::MouseButtons QGraphicsSceneMouseEvent::buttons() const
+{
+ Q_D(const QGraphicsSceneMouseEvent);
+ return d->buttons;
+}
+
+/*!
+ \internal
+*/
+void QGraphicsSceneMouseEvent::setButtons(Qt::MouseButtons buttons)
+{
+ Q_D(QGraphicsSceneMouseEvent);
+ d->buttons = buttons;
+}
+
+/*!
+ Returns the mouse button (if any) that caused the event.
+
+ \sa buttons(), modifiers()
+*/
+Qt::MouseButton QGraphicsSceneMouseEvent::button() const
+{
+ Q_D(const QGraphicsSceneMouseEvent);
+ return d->button;
+}
+
+/*!
+ \internal
+*/
+void QGraphicsSceneMouseEvent::setButton(Qt::MouseButton button)
+{
+ Q_D(QGraphicsSceneMouseEvent);
+ d->button = button;
+}
+
+/*!
+ Returns the keyboard modifiers in use at the time the event was
+ sent.
+
+ \sa buttons(), button()
+*/
+Qt::KeyboardModifiers QGraphicsSceneMouseEvent::modifiers() const
+{
+ Q_D(const QGraphicsSceneMouseEvent);
+ return d->modifiers;
+}
+
+/*!
+ \internal
+*/
+void QGraphicsSceneMouseEvent::setModifiers(Qt::KeyboardModifiers modifiers)
+{
+ Q_D(QGraphicsSceneMouseEvent);
+ d->modifiers = modifiers;
+}
+
+class QGraphicsSceneWheelEventPrivate : public QGraphicsSceneEventPrivate
+{
+ Q_DECLARE_PUBLIC(QGraphicsSceneWheelEvent)
+public:
+ inline QGraphicsSceneWheelEventPrivate()
+ : buttons(0), modifiers(0), delta(0), orientation(Qt::Horizontal)
+ { }
+
+ QPointF pos;
+ QPointF scenePos;
+ QPoint screenPos;
+ Qt::MouseButtons buttons;
+ Qt::KeyboardModifiers modifiers;
+ int delta;
+ Qt::Orientation orientation;
+};
+
+/*!
+ \internal
+
+ Constructs a QGraphicsSceneWheelEvent of type \a type, which
+ is always QEvent::GraphicsSceneWheel.
+*/
+QGraphicsSceneWheelEvent::QGraphicsSceneWheelEvent(Type type)
+ : QGraphicsSceneEvent(*new QGraphicsSceneWheelEventPrivate, type)
+{
+}
+
+/*!
+ Destroys the QGraphicsSceneWheelEvent.
+*/
+QGraphicsSceneWheelEvent::~QGraphicsSceneWheelEvent()
+{
+}
+
+/*!
+ Returns the position of the cursor in item coordinates when the
+ wheel event occurred.
+
+ \sa scenePos(), screenPos()
+*/
+QPointF QGraphicsSceneWheelEvent::pos() const
+{
+ Q_D(const QGraphicsSceneWheelEvent);
+ return d->pos;
+}
+
+/*!
+ \internal
+*/
+void QGraphicsSceneWheelEvent::setPos(const QPointF &pos)
+{
+ Q_D(QGraphicsSceneWheelEvent);
+ d->pos = pos;
+}
+
+/*!
+ Returns the position of the cursor in scene coordinates when the wheel
+ event occurred.
+
+ \sa pos(), screenPos()
+*/
+QPointF QGraphicsSceneWheelEvent::scenePos() const
+{
+ Q_D(const QGraphicsSceneWheelEvent);
+ return d->scenePos;
+}
+
+/*!
+ \internal
+*/
+void QGraphicsSceneWheelEvent::setScenePos(const QPointF &pos)
+{
+ Q_D(QGraphicsSceneWheelEvent);
+ d->scenePos = pos;
+}
+
+/*!
+ Returns the position of the cursor in screen coordinates when the wheel
+ event occurred.
+
+ \sa pos(), scenePos()
+*/
+QPoint QGraphicsSceneWheelEvent::screenPos() const
+{
+ Q_D(const QGraphicsSceneWheelEvent);
+ return d->screenPos;
+}
+
+/*!
+ \internal
+*/
+void QGraphicsSceneWheelEvent::setScreenPos(const QPoint &pos)
+{
+ Q_D(QGraphicsSceneWheelEvent);
+ d->screenPos = pos;
+}
+
+/*!
+ Returns the mouse buttons that were pressed when the wheel event occurred.
+
+ \sa modifiers()
+*/
+Qt::MouseButtons QGraphicsSceneWheelEvent::buttons() const
+{
+ Q_D(const QGraphicsSceneWheelEvent);
+ return d->buttons;
+}
+
+/*!
+ \internal
+*/
+void QGraphicsSceneWheelEvent::setButtons(Qt::MouseButtons buttons)
+{
+ Q_D(QGraphicsSceneWheelEvent);
+ d->buttons = buttons;
+}
+
+/*!
+ Returns the keyboard modifiers that were active when the wheel event
+ occurred.
+
+ \sa buttons()
+*/
+Qt::KeyboardModifiers QGraphicsSceneWheelEvent::modifiers() const
+{
+ Q_D(const QGraphicsSceneWheelEvent);
+ return d->modifiers;
+}
+
+/*!
+ \internal
+*/
+void QGraphicsSceneWheelEvent::setModifiers(Qt::KeyboardModifiers modifiers)
+{
+ Q_D(QGraphicsSceneWheelEvent);
+ d->modifiers = modifiers;
+}
+
+/*!
+ Returns the distance that the wheel is rotated, in eighths (1/8s)
+ of a degree. A positive value indicates that the wheel was
+ rotated forwards away from the user; a negative value indicates
+ that the wheel was rotated backwards toward the user.
+
+ Most mouse types work in steps of 15 degrees, in which case the delta
+ value is a multiple of 120 (== 15 * 8).
+*/
+int QGraphicsSceneWheelEvent::delta() const
+{
+ Q_D(const QGraphicsSceneWheelEvent);
+ return d->delta;
+}
+
+/*!
+ \internal
+*/
+void QGraphicsSceneWheelEvent::setDelta(int delta)
+{
+ Q_D(QGraphicsSceneWheelEvent);
+ d->delta = delta;
+}
+
+/*!
+ Returns the wheel orientation.
+*/
+Qt::Orientation QGraphicsSceneWheelEvent::orientation() const
+{
+ Q_D(const QGraphicsSceneWheelEvent);
+ return d->orientation;
+}
+
+/*!
+ \internal
+*/
+void QGraphicsSceneWheelEvent::setOrientation(Qt::Orientation orientation)
+{
+ Q_D(QGraphicsSceneWheelEvent);
+ d->orientation = orientation;
+}
+
+class QGraphicsSceneContextMenuEventPrivate : public QGraphicsSceneEventPrivate
+{
+ Q_DECLARE_PUBLIC(QGraphicsSceneContextMenuEvent)
+ public:
+ inline QGraphicsSceneContextMenuEventPrivate()
+ : modifiers(0), reason(QGraphicsSceneContextMenuEvent::Other)
+ { }
+
+ QPointF pos;
+ QPointF scenePos;
+ QPoint screenPos;
+ Qt::KeyboardModifiers modifiers;
+ QGraphicsSceneContextMenuEvent::Reason reason;
+};
+
+/*!
+ \internal
+
+ Constructs a graphics scene context menu event of the specified \a type.
+*/
+QGraphicsSceneContextMenuEvent::QGraphicsSceneContextMenuEvent(Type type)
+ : QGraphicsSceneEvent(*new QGraphicsSceneContextMenuEventPrivate, type)
+{
+}
+
+/*!
+ Destroys the event.
+*/
+QGraphicsSceneContextMenuEvent::~QGraphicsSceneContextMenuEvent()
+{
+}
+
+/*!
+ Returns the position of the mouse cursor in item coordinates at the moment
+ the context menu was requested.
+
+ \sa scenePos(), screenPos()
+*/
+QPointF QGraphicsSceneContextMenuEvent::pos() const
+{
+ Q_D(const QGraphicsSceneContextMenuEvent);
+ return d->pos;
+}
+
+/*!
+ \fn void QGraphicsSceneContextMenuEvent::setPos(const QPointF &point)
+ \internal
+
+ Sets the position associated with the context menu to the given \a point
+ in item coordinates.
+*/
+void QGraphicsSceneContextMenuEvent::setPos(const QPointF &pos)
+{
+ Q_D(QGraphicsSceneContextMenuEvent);
+ d->pos = pos;
+}
+
+/*!
+ Returns the position of the mouse cursor in scene coordinates at the moment the
+ the context menu was requested.
+
+ \sa pos(), screenPos()
+*/
+QPointF QGraphicsSceneContextMenuEvent::scenePos() const
+{
+ Q_D(const QGraphicsSceneContextMenuEvent);
+ return d->scenePos;
+}
+
+/*!
+ \fn void QGraphicsSceneContextMenuEvent::setScenePos(const QPointF &point)
+ \internal
+
+ Sets the position associated with the context menu to the given \a point
+ in scene coordinates.
+*/
+void QGraphicsSceneContextMenuEvent::setScenePos(const QPointF &pos)
+{
+ Q_D(QGraphicsSceneContextMenuEvent);
+ d->scenePos = pos;
+}
+
+/*!
+ Returns the position of the mouse cursor in screen coordinates at the moment the
+ the context menu was requested.
+
+ \sa pos(), scenePos()
+*/
+QPoint QGraphicsSceneContextMenuEvent::screenPos() const
+{
+ Q_D(const QGraphicsSceneContextMenuEvent);
+ return d->screenPos;
+}
+
+/*!
+ \fn void QGraphicsSceneContextMenuEvent::setScreenPos(const QPoint &point)
+ \internal
+
+ Sets the position associated with the context menu to the given \a point
+ in screen coordinates.
+*/
+void QGraphicsSceneContextMenuEvent::setScreenPos(const QPoint &pos)
+{
+ Q_D(QGraphicsSceneContextMenuEvent);
+ d->screenPos = pos;
+}
+
+/*!
+ Returns the keyboard modifiers in use when the context menu was requested.
+*/
+Qt::KeyboardModifiers QGraphicsSceneContextMenuEvent::modifiers() const
+{
+ Q_D(const QGraphicsSceneContextMenuEvent);
+ return d->modifiers;
+}
+
+/*!
+ \internal
+
+ Sets the keyboard modifiers associated with the context menu to the \a
+ modifiers specified.
+*/
+void QGraphicsSceneContextMenuEvent::setModifiers(Qt::KeyboardModifiers modifiers)
+{
+ Q_D(QGraphicsSceneContextMenuEvent);
+ d->modifiers = modifiers;
+}
+
+/*!
+ Returns the reason for the context menu event.
+
+ \sa QGraphicsSceneContextMenuEvent::Reason
+*/
+QGraphicsSceneContextMenuEvent::Reason QGraphicsSceneContextMenuEvent::reason() const
+{
+ Q_D(const QGraphicsSceneContextMenuEvent);
+ return d->reason;
+}
+
+/*!
+ \internal
+ Sets the reason for the context menu event to \a reason.
+
+ \sa reason()
+*/
+void QGraphicsSceneContextMenuEvent::setReason(Reason reason)
+{
+ Q_D(QGraphicsSceneContextMenuEvent);
+ d->reason = reason;
+}
+
+class QGraphicsSceneHoverEventPrivate : public QGraphicsSceneEventPrivate
+{
+public:
+ QPointF pos;
+ QPointF scenePos;
+ QPoint screenPos;
+ QPointF lastPos;
+ QPointF lastScenePos;
+ QPoint lastScreenPos;
+ Qt::KeyboardModifiers modifiers;
+};
+
+/*!
+ \internal
+
+ Constructs a graphics scene hover event of the specified \a type.
+*/
+QGraphicsSceneHoverEvent::QGraphicsSceneHoverEvent(Type type)
+ : QGraphicsSceneEvent(*new QGraphicsSceneHoverEventPrivate, type)
+{
+}
+
+/*!
+ Destroys the event.
+*/
+QGraphicsSceneHoverEvent::~QGraphicsSceneHoverEvent()
+{
+}
+
+/*!
+ Returns the position of the mouse cursor in item coordinates at the moment
+ the hover event was sent.
+
+ \sa scenePos(), screenPos()
+*/
+QPointF QGraphicsSceneHoverEvent::pos() const
+{
+ Q_D(const QGraphicsSceneHoverEvent);
+ return d->pos;
+}
+
+/*!
+ \fn void QGraphicsSceneHoverEvent::setPos(const QPointF &point)
+ \internal
+
+ Sets the position associated with the hover event to the given \a point in
+ item coordinates.
+*/
+void QGraphicsSceneHoverEvent::setPos(const QPointF &pos)
+{
+ Q_D(QGraphicsSceneHoverEvent);
+ d->pos = pos;
+}
+
+/*!
+ Returns the position of the mouse cursor in scene coordinates at the
+ moment the hover event was sent.
+
+ \sa pos(), screenPos()
+*/
+QPointF QGraphicsSceneHoverEvent::scenePos() const
+{
+ Q_D(const QGraphicsSceneHoverEvent);
+ return d->scenePos;
+}
+
+/*!
+ \fn void QGraphicsSceneHoverEvent::setScenePos(const QPointF &point)
+ \internal
+
+ Sets the position associated with the hover event to the given \a point in
+ scene coordinates.
+*/
+void QGraphicsSceneHoverEvent::setScenePos(const QPointF &pos)
+{
+ Q_D(QGraphicsSceneHoverEvent);
+ d->scenePos = pos;
+}
+
+/*!
+ Returns the position of the mouse cursor in screen coordinates at the
+ moment the hover event was sent.
+
+ \sa pos(), scenePos()
+*/
+QPoint QGraphicsSceneHoverEvent::screenPos() const
+{
+ Q_D(const QGraphicsSceneHoverEvent);
+ return d->screenPos;
+}
+
+/*!
+ \fn void QGraphicsSceneHoverEvent::setScreenPos(const QPoint &point)
+ \internal
+
+ Sets the position associated with the hover event to the given \a point in
+ screen coordinates.
+*/
+void QGraphicsSceneHoverEvent::setScreenPos(const QPoint &pos)
+{
+ Q_D(QGraphicsSceneHoverEvent);
+ d->screenPos = pos;
+}
+
+/*!
+ \since 4.4
+
+ Returns the last recorded mouse cursor position in item coordinates.
+
+ \sa lastScenePos(), lastScreenPos(), pos()
+*/
+QPointF QGraphicsSceneHoverEvent::lastPos() const
+{
+ Q_D(const QGraphicsSceneHoverEvent);
+ return d->lastPos;
+}
+
+/*!
+ \internal
+*/
+void QGraphicsSceneHoverEvent::setLastPos(const QPointF &pos)
+{
+ Q_D(QGraphicsSceneHoverEvent);
+ d->lastPos = pos;
+}
+
+/*!
+ \since 4.4
+
+ Returns the last recorded, the scene coordinates of the previous mouse or
+ hover event received by the view, that created the event mouse cursor
+ position in scene coordinates.
+
+ \sa lastPos(), lastScreenPos(), scenePos()
+*/
+QPointF QGraphicsSceneHoverEvent::lastScenePos() const
+{
+ Q_D(const QGraphicsSceneHoverEvent);
+ return d->lastScenePos;
+}
+
+/*!
+ \internal
+*/
+void QGraphicsSceneHoverEvent::setLastScenePos(const QPointF &pos)
+{
+ Q_D(QGraphicsSceneHoverEvent);
+ d->lastScenePos = pos;
+}
+
+/*!
+ \since 4.4
+
+ Returns the last recorded mouse cursor position in screen coordinates. The
+ last recorded position is the position of the previous mouse or hover
+ event received by the view that created the event.
+
+ \sa lastPos(), lastScenePos(), screenPos()
+*/
+QPoint QGraphicsSceneHoverEvent::lastScreenPos() const
+{
+ Q_D(const QGraphicsSceneHoverEvent);
+ return d->lastScreenPos;
+}
+
+/*!
+ \internal
+*/
+void QGraphicsSceneHoverEvent::setLastScreenPos(const QPoint &pos)
+{
+ Q_D(QGraphicsSceneHoverEvent);
+ d->lastScreenPos = pos;
+}
+
+/*!
+ \since 4.4
+
+ Returns the keyboard modifiers at the moment the hover event was sent.
+*/
+Qt::KeyboardModifiers QGraphicsSceneHoverEvent::modifiers() const
+{
+ Q_D(const QGraphicsSceneHoverEvent);
+ return d->modifiers;
+}
+
+/*!
+ \fn void QGraphicsSceneHoverEvent::setModifiers(Qt::KeyboardModifiers modifiers)
+ \internal
+
+ Sets the modifiers for the current hover event to \a modifiers.
+*/
+void QGraphicsSceneHoverEvent::setModifiers(Qt::KeyboardModifiers modifiers)
+{
+ Q_D(QGraphicsSceneHoverEvent);
+ d->modifiers = modifiers;
+}
+
+class QGraphicsSceneHelpEventPrivate : public QGraphicsSceneEventPrivate
+{
+public:
+ QPointF scenePos;
+ QPoint screenPos;
+};
+
+/*!
+ \internal
+
+ Constructs a graphics scene help event of the specified \a type.
+*/
+QGraphicsSceneHelpEvent::QGraphicsSceneHelpEvent(Type type)
+ : QGraphicsSceneEvent(*new QGraphicsSceneHelpEventPrivate, type)
+{
+}
+
+/*!
+ Destroys the event.
+*/
+QGraphicsSceneHelpEvent::~QGraphicsSceneHelpEvent()
+{
+}
+
+/*!
+ Returns the position of the mouse cursor in scene coordinates at the
+ moment the help event was sent.
+
+ \sa screenPos()
+*/
+QPointF QGraphicsSceneHelpEvent::scenePos() const
+{
+ Q_D(const QGraphicsSceneHelpEvent);
+ return d->scenePos;
+}
+
+/*!
+ \fn void QGraphicsSceneHelpEvent::setScenePos(const QPointF &point)
+ \internal
+
+ Sets the position associated with the context menu to the given \a point
+ in scene coordinates.
+*/
+void QGraphicsSceneHelpEvent::setScenePos(const QPointF &pos)
+{
+ Q_D(QGraphicsSceneHelpEvent);
+ d->scenePos = pos;
+}
+
+/*!
+ Returns the position of the mouse cursor in screen coordinates at the
+ moment the help event was sent.
+
+ \sa scenePos()
+*/
+QPoint QGraphicsSceneHelpEvent::screenPos() const
+{
+ Q_D(const QGraphicsSceneHelpEvent);
+ return d->screenPos;
+}
+
+/*!
+ \fn void QGraphicsSceneHelpEvent::setScreenPos(const QPoint &point)
+ \internal
+
+ Sets the position associated with the context menu to the given \a point
+ in screen coordinates.
+*/
+void QGraphicsSceneHelpEvent::setScreenPos(const QPoint &pos)
+{
+ Q_D(QGraphicsSceneHelpEvent);
+ d->screenPos = pos;
+}
+
+class QGraphicsSceneDragDropEventPrivate : public QGraphicsSceneEventPrivate
+{
+ Q_DECLARE_PUBLIC(QGraphicsSceneDragDropEvent)
+public:
+ inline QGraphicsSceneDragDropEventPrivate()
+ : source(0), mimeData(0)
+ { }
+
+ QPointF pos;
+ QPointF scenePos;
+ QPoint screenPos;
+ Qt::MouseButtons buttons;
+ Qt::KeyboardModifiers modifiers;
+ Qt::DropActions possibleActions;
+ Qt::DropAction proposedAction;
+ Qt::DropAction dropAction;
+ QWidget *source;
+ const QMimeData *mimeData;
+};
+
+/*!
+ \internal
+
+ Constructs a new QGraphicsSceneDragDropEvent of the
+ specified \a type. The type can be either
+ QEvent::GraphicsSceneDragEnter, QEvent::GraphicsSceneDragLeave,
+ QEvent::GraphicsSceneDragMove, or QEvent::GraphicsSceneDrop.
+*/
+QGraphicsSceneDragDropEvent::QGraphicsSceneDragDropEvent(Type type)
+ : QGraphicsSceneEvent(*new QGraphicsSceneDragDropEventPrivate, type)
+{
+}
+
+/*!
+ Destroys the object.
+*/
+QGraphicsSceneDragDropEvent::~QGraphicsSceneDragDropEvent()
+{
+}
+
+/*!
+ Returns the mouse position of the event relative to the
+ view that sent the event.
+
+ \sa QGraphicsView, screenPos(), scenePos()
+*/
+QPointF QGraphicsSceneDragDropEvent::pos() const
+{
+ Q_D(const QGraphicsSceneDragDropEvent);
+ return d->pos;
+}
+
+/*!
+ \internal
+ Sets the position of the mouse to \a pos; this should be
+ relative to the widget that generated the event, which normally
+ is a QGraphicsView.
+
+ \sa pos(), setScenePos(), setScreenPos()
+*/
+
+void QGraphicsSceneDragDropEvent::setPos(const QPointF &pos)
+{
+ Q_D(QGraphicsSceneDragDropEvent);
+ d->pos = pos;
+}
+
+/*!
+ Returns the position of the mouse in scene coordinates.
+
+ \sa pos(), screenPos()
+*/
+QPointF QGraphicsSceneDragDropEvent::scenePos() const
+{
+ Q_D(const QGraphicsSceneDragDropEvent);
+ return d->scenePos;
+}
+
+/*!
+ \internal
+ Sets the scene position of the mouse to \a pos.
+
+ \sa scenePos(), setScreenPos(), setPos()
+*/
+void QGraphicsSceneDragDropEvent::setScenePos(const QPointF &pos)
+{
+ Q_D(QGraphicsSceneDragDropEvent);
+ d->scenePos = pos;
+}
+
+/*!
+ Returns the position of the mouse relative to the screen.
+
+ \sa pos(), scenePos()
+*/
+QPoint QGraphicsSceneDragDropEvent::screenPos() const
+{
+ Q_D(const QGraphicsSceneDragDropEvent);
+ return d->screenPos;
+}
+
+/*!
+ \internal
+ Sets the mouse position relative to the screen to \a pos.
+
+ \sa screenPos(), setScenePos(), setPos()
+*/
+void QGraphicsSceneDragDropEvent::setScreenPos(const QPoint &pos)
+{
+ Q_D(QGraphicsSceneDragDropEvent);
+ d->screenPos = pos;
+}
+
+/*!
+ Returns a Qt::MouseButtons value indicating which buttons
+ were pressed on the mouse when this mouse event was
+ generated.
+
+ \sa Qt::MouseButtons
+*/
+Qt::MouseButtons QGraphicsSceneDragDropEvent::buttons() const
+{
+ Q_D(const QGraphicsSceneDragDropEvent);
+ return d->buttons;
+}
+
+/*!
+ \internal
+ Sets the mouse buttons that were pressed when the event was
+ created to \a buttons.
+
+ \sa Qt::MouseButtons, buttons()
+*/
+void QGraphicsSceneDragDropEvent::setButtons(Qt::MouseButtons buttons)
+{
+ Q_D(QGraphicsSceneDragDropEvent);
+ d->buttons = buttons;
+}
+
+/*!
+ Returns the keyboard modifiers that were pressed when the drag
+ and drop event was created.
+
+ \sa Qt::KeyboardModifiers
+*/
+Qt::KeyboardModifiers QGraphicsSceneDragDropEvent::modifiers() const
+{
+ Q_D(const QGraphicsSceneDragDropEvent);
+ return d->modifiers;
+}
+
+/*!
+ \internal
+ Sets the keyboard modifiers that were pressed when the event
+ was created to \a modifiers.
+
+ \sa Qt::KeyboardModifiers, modifiers()
+*/
+
+void QGraphicsSceneDragDropEvent::setModifiers(Qt::KeyboardModifiers modifiers)
+{
+ Q_D(QGraphicsSceneDragDropEvent);
+ d->modifiers = modifiers;
+}
+
+/*!
+ Returns the possible drop actions that the drag and
+ drop can result in.
+
+ \sa Qt::DropActions
+*/
+
+Qt::DropActions QGraphicsSceneDragDropEvent::possibleActions() const
+{
+ Q_D(const QGraphicsSceneDragDropEvent);
+ return d->possibleActions;
+}
+
+/*!
+ \internal
+ Sets the possible drop actions that the drag can
+ result in to \a actions.
+
+ \sa Qt::DropActions, possibleActions()
+*/
+void QGraphicsSceneDragDropEvent::setPossibleActions(Qt::DropActions actions)
+{
+ Q_D(QGraphicsSceneDragDropEvent);
+ d->possibleActions = actions;
+}
+
+/*!
+ Returns the drop action that is proposed, i.e., preferred.
+ The action must be one of the possible actions as defined by
+ \c possibleActions().
+
+ \sa Qt::DropAction, possibleActions()
+*/
+
+Qt::DropAction QGraphicsSceneDragDropEvent::proposedAction() const
+{
+ Q_D(const QGraphicsSceneDragDropEvent);
+ return d->proposedAction;
+}
+
+/*!
+ \internal
+ Sets the proposed action to \a action. The proposed action
+ is a Qt::DropAction that is one of the possible actions as
+ given by \c possibleActions().
+
+ \sa proposedAction(), Qt::DropAction, possibleActions()
+*/
+
+void QGraphicsSceneDragDropEvent::setProposedAction(Qt::DropAction action)
+{
+ Q_D(QGraphicsSceneDragDropEvent);
+ d->proposedAction = action;
+}
+
+/*!
+ Sets the proposed action as accepted, i.e, the drop action
+ is set to the proposed action. This is equal to:
+
+ \snippet doc/src/snippets/code/src_gui_graphicsview_qgraphicssceneevent.cpp 0
+
+ When using this function, one should not call \c accept().
+
+ \sa dropAction(), setDropAction(), proposedAction()
+*/
+
+void QGraphicsSceneDragDropEvent::acceptProposedAction()
+{
+ Q_D(QGraphicsSceneDragDropEvent);
+ d->dropAction = d->proposedAction;
+}
+
+/*!
+ Returns the action that was performed in this drag and drop.
+ This should be set by the receiver of the drop and is
+ returned by QDrag::exec().
+
+ \sa setDropAction(), acceptProposedAction()
+*/
+
+Qt::DropAction QGraphicsSceneDragDropEvent::dropAction() const
+{
+ Q_D(const QGraphicsSceneDragDropEvent);
+ return d->dropAction;
+}
+
+/*!
+ This function lets the receiver of the drop set the drop
+ action that was performed to \a action, which should be one
+ of the
+ \l{QGraphicsSceneDragDropEvent::possibleActions()}{possible
+ actions}. Call \c accept() in stead of \c
+ acceptProposedAction() if you use this function.
+
+ \sa dropAction(), accept(), possibleActions()
+*/
+void QGraphicsSceneDragDropEvent::setDropAction(Qt::DropAction action)
+{
+ Q_D(QGraphicsSceneDragDropEvent);
+ d->dropAction = action;
+}
+
+/*!
+ This function returns the QGraphicsView that created the
+ QGraphicsSceneDragDropEvent.
+*/
+QWidget *QGraphicsSceneDragDropEvent::source() const
+{
+ Q_D(const QGraphicsSceneDragDropEvent);
+ return d->source;
+}
+
+/*!
+ \internal
+ This function set the source widget, i.e., the widget that
+ created the drop event, to \a source.
+*/
+void QGraphicsSceneDragDropEvent::setSource(QWidget *source)
+{
+ Q_D(QGraphicsSceneDragDropEvent);
+ d->source = source;
+}
+
+/*!
+ This function returns the MIME data of the event.
+*/
+const QMimeData *QGraphicsSceneDragDropEvent::mimeData() const
+{
+ Q_D(const QGraphicsSceneDragDropEvent);
+ return d->mimeData;
+}
+
+/*!
+ \internal
+ This function sets the MIME data for the event.
+*/
+void QGraphicsSceneDragDropEvent::setMimeData(const QMimeData *data)
+{
+ Q_D(QGraphicsSceneDragDropEvent);
+ d->mimeData = data;
+}
+
+class QGraphicsSceneResizeEventPrivate : public QGraphicsSceneEventPrivate
+{
+ Q_DECLARE_PUBLIC(QGraphicsSceneResizeEvent)
+public:
+ inline QGraphicsSceneResizeEventPrivate()
+ { }
+
+ QSizeF oldSize;
+ QSizeF newSize;
+};
+
+/*!
+ Constructs a QGraphicsSceneResizeEvent.
+*/
+QGraphicsSceneResizeEvent::QGraphicsSceneResizeEvent()
+ : QGraphicsSceneEvent(*new QGraphicsSceneResizeEventPrivate, QEvent::GraphicsSceneResize)
+{
+}
+
+/*!
+ Destroys the QGraphicsSceneResizeEvent.
+*/
+QGraphicsSceneResizeEvent::~QGraphicsSceneResizeEvent()
+{
+}
+
+/*!
+ Returns the old size (i.e., the size immediately before the widget was
+ resized).
+
+ \sa newSize(), QGraphicsWidget::resize()
+*/
+QSizeF QGraphicsSceneResizeEvent::oldSize() const
+{
+ Q_D(const QGraphicsSceneResizeEvent);
+ return d->oldSize;
+}
+
+/*!
+ \internal
+*/
+void QGraphicsSceneResizeEvent::setOldSize(const QSizeF &size)
+{
+ Q_D(QGraphicsSceneResizeEvent);
+ d->oldSize = size;
+}
+
+/*!
+ Returns the new size (i.e., the current size).
+
+ \sa oldSize(), QGraphicsWidget::resize()
+*/
+QSizeF QGraphicsSceneResizeEvent::newSize() const
+{
+ Q_D(const QGraphicsSceneResizeEvent);
+ return d->newSize;
+}
+
+/*!
+ \internal
+*/
+void QGraphicsSceneResizeEvent::setNewSize(const QSizeF &size)
+{
+ Q_D(QGraphicsSceneResizeEvent);
+ d->newSize = size;
+}
+
+class QGraphicsSceneMoveEventPrivate : public QGraphicsSceneEventPrivate
+{
+ Q_DECLARE_PUBLIC(QGraphicsSceneMoveEvent)
+public:
+ inline QGraphicsSceneMoveEventPrivate()
+ { }
+
+ QPointF oldPos;
+ QPointF newPos;
+};
+
+/*!
+ Constructs a QGraphicsSceneMoveEvent.
+*/
+QGraphicsSceneMoveEvent::QGraphicsSceneMoveEvent()
+ : QGraphicsSceneEvent(*new QGraphicsSceneMoveEventPrivate, QEvent::GraphicsSceneMove)
+{
+}
+
+/*!
+ Destroys the QGraphicsSceneMoveEvent.
+*/
+QGraphicsSceneMoveEvent::~QGraphicsSceneMoveEvent()
+{
+}
+
+/*!
+ Returns the old position (i.e., the position immediately before the widget
+ was moved).
+
+ \sa newPos(), QGraphicsItem::setPos()
+*/
+QPointF QGraphicsSceneMoveEvent::oldPos() const
+{
+ Q_D(const QGraphicsSceneMoveEvent);
+ return d->oldPos;
+}
+
+/*!
+ \internal
+*/
+void QGraphicsSceneMoveEvent::setOldPos(const QPointF &pos)
+{
+ Q_D(QGraphicsSceneMoveEvent);
+ d->oldPos = pos;
+}
+
+/*!
+ Returns the new position (i.e., the current position).
+
+ \sa oldPos(), QGraphicsItem::setPos()
+*/
+QPointF QGraphicsSceneMoveEvent::newPos() const
+{
+ Q_D(const QGraphicsSceneMoveEvent);
+ return d->newPos;
+}
+
+/*!
+ \internal
+*/
+void QGraphicsSceneMoveEvent::setNewPos(const QPointF &pos)
+{
+ Q_D(QGraphicsSceneMoveEvent);
+ d->newPos = pos;
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_GRAPHICSVIEW
diff --git a/src/widgets/graphicsview/qgraphicssceneevent.h b/src/widgets/graphicsview/qgraphicssceneevent.h
new file mode 100644
index 0000000000..afaa33c9ce
--- /dev/null
+++ b/src/widgets/graphicsview/qgraphicssceneevent.h
@@ -0,0 +1,326 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** 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 Technology Preview License Agreement accompanying
+** this package.
+**
+** 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.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QGRAPHICSSCENEEVENT_H
+#define QGRAPHICSSCENEEVENT_H
+
+#include <QtCore/qcoreevent.h>
+#include <QtCore/qpoint.h>
+#include <QtCore/qscopedpointer.h>
+#include <QtCore/qrect.h>
+#include <QtGui/qpolygon.h>
+#include <QtCore/qset.h>
+#include <QtCore/qhash.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+#if !defined(QT_NO_GRAPHICSVIEW) || (QT_EDITION & QT_MODULE_GRAPHICSVIEW) != QT_MODULE_GRAPHICSVIEW
+
+class QMimeData;
+class QPointF;
+class QSizeF;
+class QWidget;
+
+class QGraphicsSceneEventPrivate;
+class Q_GUI_EXPORT QGraphicsSceneEvent : public QEvent
+{
+public:
+ QGraphicsSceneEvent(Type type);
+ ~QGraphicsSceneEvent();
+
+ QWidget *widget() const;
+ void setWidget(QWidget *widget);
+
+protected:
+ QGraphicsSceneEvent(QGraphicsSceneEventPrivate &dd, Type type = None);
+ QScopedPointer<QGraphicsSceneEventPrivate> d_ptr;
+ Q_DECLARE_PRIVATE(QGraphicsSceneEvent)
+private:
+ Q_DISABLE_COPY(QGraphicsSceneEvent)
+};
+
+class QGraphicsSceneMouseEventPrivate;
+class Q_GUI_EXPORT QGraphicsSceneMouseEvent : public QGraphicsSceneEvent
+{
+public:
+ QGraphicsSceneMouseEvent(Type type = None);
+ ~QGraphicsSceneMouseEvent();
+
+ QPointF pos() const;
+ void setPos(const QPointF &pos);
+
+ QPointF scenePos() const;
+ void setScenePos(const QPointF &pos);
+
+ QPoint screenPos() const;
+ void setScreenPos(const QPoint &pos);
+
+ QPointF buttonDownPos(Qt::MouseButton button) const;
+ void setButtonDownPos(Qt::MouseButton button, const QPointF &pos);
+
+ QPointF buttonDownScenePos(Qt::MouseButton button) const;
+ void setButtonDownScenePos(Qt::MouseButton button, const QPointF &pos);
+
+ QPoint buttonDownScreenPos(Qt::MouseButton button) const;
+ void setButtonDownScreenPos(Qt::MouseButton button, const QPoint &pos);
+
+ QPointF lastPos() const;
+ void setLastPos(const QPointF &pos);
+
+ QPointF lastScenePos() const;
+ void setLastScenePos(const QPointF &pos);
+
+ QPoint lastScreenPos() const;
+ void setLastScreenPos(const QPoint &pos);
+
+ Qt::MouseButtons buttons() const;
+ void setButtons(Qt::MouseButtons buttons);
+
+ Qt::MouseButton button() const;
+ void setButton(Qt::MouseButton button);
+
+ Qt::KeyboardModifiers modifiers() const;
+ void setModifiers(Qt::KeyboardModifiers modifiers);
+
+private:
+ Q_DECLARE_PRIVATE(QGraphicsSceneMouseEvent)
+ Q_DISABLE_COPY(QGraphicsSceneMouseEvent)
+};
+
+class QGraphicsSceneWheelEventPrivate;
+class Q_GUI_EXPORT QGraphicsSceneWheelEvent : public QGraphicsSceneEvent
+{
+public:
+ QGraphicsSceneWheelEvent(Type type = None);
+ ~QGraphicsSceneWheelEvent();
+
+ QPointF pos() const;
+ void setPos(const QPointF &pos);
+
+ QPointF scenePos() const;
+ void setScenePos(const QPointF &pos);
+
+ QPoint screenPos() const;
+ void setScreenPos(const QPoint &pos);
+
+ Qt::MouseButtons buttons() const;
+ void setButtons(Qt::MouseButtons buttons);
+
+ Qt::KeyboardModifiers modifiers() const;
+ void setModifiers(Qt::KeyboardModifiers modifiers);
+
+ int delta() const;
+ void setDelta(int delta);
+
+ Qt::Orientation orientation() const;
+ void setOrientation(Qt::Orientation orientation);
+
+private:
+ Q_DECLARE_PRIVATE(QGraphicsSceneWheelEvent)
+ Q_DISABLE_COPY(QGraphicsSceneWheelEvent)
+};
+
+class QGraphicsSceneContextMenuEventPrivate;
+class Q_GUI_EXPORT QGraphicsSceneContextMenuEvent : public QGraphicsSceneEvent
+{
+public:
+ enum Reason { Mouse, Keyboard, Other };
+
+ QGraphicsSceneContextMenuEvent(Type type = None);
+ ~QGraphicsSceneContextMenuEvent();
+
+ QPointF pos() const;
+ void setPos(const QPointF &pos);
+
+ QPointF scenePos() const;
+ void setScenePos(const QPointF &pos);
+
+ QPoint screenPos() const;
+ void setScreenPos(const QPoint &pos);
+
+ Qt::KeyboardModifiers modifiers() const;
+ void setModifiers(Qt::KeyboardModifiers modifiers);
+
+ Reason reason() const;
+ void setReason(Reason reason);
+
+private:
+ Q_DECLARE_PRIVATE(QGraphicsSceneContextMenuEvent)
+ Q_DISABLE_COPY(QGraphicsSceneContextMenuEvent)
+};
+
+class QGraphicsSceneHoverEventPrivate;
+class Q_GUI_EXPORT QGraphicsSceneHoverEvent : public QGraphicsSceneEvent
+{
+public:
+ QGraphicsSceneHoverEvent(Type type = None);
+ ~QGraphicsSceneHoverEvent();
+
+ QPointF pos() const;
+ void setPos(const QPointF &pos);
+
+ QPointF scenePos() const;
+ void setScenePos(const QPointF &pos);
+
+ QPoint screenPos() const;
+ void setScreenPos(const QPoint &pos);
+
+ QPointF lastPos() const;
+ void setLastPos(const QPointF &pos);
+
+ QPointF lastScenePos() const;
+ void setLastScenePos(const QPointF &pos);
+
+ QPoint lastScreenPos() const;
+ void setLastScreenPos(const QPoint &pos);
+
+ Qt::KeyboardModifiers modifiers() const;
+ void setModifiers(Qt::KeyboardModifiers modifiers);
+
+private:
+ Q_DECLARE_PRIVATE(QGraphicsSceneHoverEvent)
+ Q_DISABLE_COPY(QGraphicsSceneHoverEvent)
+};
+
+class QGraphicsSceneHelpEventPrivate;
+class Q_GUI_EXPORT QGraphicsSceneHelpEvent : public QGraphicsSceneEvent
+{
+public:
+ QGraphicsSceneHelpEvent(Type type = None);
+ ~QGraphicsSceneHelpEvent();
+
+ QPointF scenePos() const;
+ void setScenePos(const QPointF &pos);
+
+ QPoint screenPos() const;
+ void setScreenPos(const QPoint &pos);
+
+private:
+ Q_DECLARE_PRIVATE(QGraphicsSceneHelpEvent)
+ Q_DISABLE_COPY(QGraphicsSceneHelpEvent)
+};
+
+class QGraphicsSceneDragDropEventPrivate;
+class Q_GUI_EXPORT QGraphicsSceneDragDropEvent : public QGraphicsSceneEvent
+{
+public:
+ QGraphicsSceneDragDropEvent(Type type = None);
+ ~QGraphicsSceneDragDropEvent();
+
+ QPointF pos() const;
+ void setPos(const QPointF &pos);
+
+ QPointF scenePos() const;
+ void setScenePos(const QPointF &pos);
+
+ QPoint screenPos() const;
+ void setScreenPos(const QPoint &pos);
+
+ Qt::MouseButtons buttons() const;
+ void setButtons(Qt::MouseButtons buttons);
+
+ Qt::KeyboardModifiers modifiers() const;
+ void setModifiers(Qt::KeyboardModifiers modifiers);
+
+ Qt::DropActions possibleActions() const;
+ void setPossibleActions(Qt::DropActions actions);
+
+ Qt::DropAction proposedAction() const;
+ void setProposedAction(Qt::DropAction action);
+ void acceptProposedAction();
+
+ Qt::DropAction dropAction() const;
+ void setDropAction(Qt::DropAction action);
+
+ QWidget *source() const;
+ void setSource(QWidget *source);
+
+ const QMimeData *mimeData() const;
+ void setMimeData(const QMimeData *data);
+
+private:
+ Q_DECLARE_PRIVATE(QGraphicsSceneDragDropEvent)
+ Q_DISABLE_COPY(QGraphicsSceneDragDropEvent)
+};
+
+class QGraphicsSceneResizeEventPrivate;
+class Q_GUI_EXPORT QGraphicsSceneResizeEvent : public QGraphicsSceneEvent
+{
+ Q_DECLARE_PRIVATE(QGraphicsSceneResizeEvent)
+ Q_DISABLE_COPY(QGraphicsSceneResizeEvent)
+public:
+ QGraphicsSceneResizeEvent();
+ ~QGraphicsSceneResizeEvent();
+
+ QSizeF oldSize() const;
+ void setOldSize(const QSizeF &size);
+
+ QSizeF newSize() const;
+ void setNewSize(const QSizeF &size);
+};
+
+class QGraphicsSceneMoveEventPrivate;
+class Q_GUI_EXPORT QGraphicsSceneMoveEvent : public QGraphicsSceneEvent
+{
+ Q_DECLARE_PRIVATE(QGraphicsSceneMoveEvent)
+ Q_DISABLE_COPY(QGraphicsSceneMoveEvent)
+public:
+ QGraphicsSceneMoveEvent();
+ ~QGraphicsSceneMoveEvent();
+
+ QPointF oldPos() const;
+ void setOldPos(const QPointF &pos);
+
+ QPointF newPos() const;
+ void setNewPos(const QPointF &pos);
+};
+
+#endif // QT_NO_GRAPHICSVIEW
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif
diff --git a/src/widgets/graphicsview/qgraphicssceneindex.cpp b/src/widgets/graphicsview/qgraphicssceneindex.cpp
new file mode 100644
index 0000000000..964e9cb0ef
--- /dev/null
+++ b/src/widgets/graphicsview/qgraphicssceneindex.cpp
@@ -0,0 +1,648 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** 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 Technology Preview License Agreement accompanying
+** this package.
+**
+** 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.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+/*!
+ \class QGraphicsSceneIndex
+ \brief The QGraphicsSceneIndex class provides a base class to implement
+ a custom indexing algorithm for discovering items in QGraphicsScene.
+ \since 4.6
+ \ingroup graphicsview-api
+
+ \internal
+
+ The QGraphicsSceneIndex class provides a base class to implement
+ a custom indexing algorithm for discovering items in QGraphicsScene. You
+ need to subclass it and reimplement addItem, removeItem, estimateItems
+ and items in order to have an functional indexing.
+
+ \sa QGraphicsScene, QGraphicsView
+*/
+
+#include "qdebug.h"
+#include "qgraphicsscene.h"
+#include "qgraphicsitem_p.h"
+#include "qgraphicsscene_p.h"
+#include "qgraphicswidget.h"
+#include "qgraphicssceneindex_p.h"
+#include "qgraphicsscenebsptreeindex_p.h"
+
+#ifndef QT_NO_GRAPHICSVIEW
+
+QT_BEGIN_NAMESPACE
+
+class QGraphicsSceneIndexRectIntersector : public QGraphicsSceneIndexIntersector
+{
+public:
+ bool intersect(const QGraphicsItem *item, const QRectF &exposeRect, Qt::ItemSelectionMode mode,
+ const QTransform &deviceTransform) const
+ {
+ QRectF brect = item->boundingRect();
+ _q_adjustRect(&brect);
+
+ // ### Add test for this (without making things slower?)
+ Q_UNUSED(exposeRect);
+
+ bool keep = true;
+ const QGraphicsItemPrivate *itemd = QGraphicsItemPrivate::get(item);
+ if (itemd->itemIsUntransformable()) {
+ // Untransformable items; map the scene rect to item coordinates.
+ const QTransform transform = item->deviceTransform(deviceTransform);
+ QRectF itemRect = (deviceTransform * transform.inverted()).mapRect(sceneRect);
+ if (mode == Qt::ContainsItemShape || mode == Qt::ContainsItemBoundingRect)
+ keep = itemRect.contains(brect) && itemRect != brect;
+ else
+ keep = itemRect.intersects(brect);
+ if (keep && (mode == Qt::ContainsItemShape || mode == Qt::IntersectsItemShape)) {
+ QPainterPath itemPath;
+ itemPath.addRect(itemRect);
+ keep = QGraphicsSceneIndexPrivate::itemCollidesWithPath(item, itemPath, mode);
+ }
+ } else {
+ Q_ASSERT(!itemd->dirtySceneTransform);
+ const QRectF itemSceneBoundingRect = itemd->sceneTransformTranslateOnly
+ ? brect.translated(itemd->sceneTransform.dx(),
+ itemd->sceneTransform.dy())
+ : itemd->sceneTransform.mapRect(brect);
+ if (mode == Qt::ContainsItemShape || mode == Qt::ContainsItemBoundingRect)
+ keep = sceneRect != brect && sceneRect.contains(itemSceneBoundingRect);
+ else
+ keep = sceneRect.intersects(itemSceneBoundingRect);
+ if (keep && (mode == Qt::ContainsItemShape || mode == Qt::IntersectsItemShape)) {
+ QPainterPath rectPath;
+ rectPath.addRect(sceneRect);
+ if (itemd->sceneTransformTranslateOnly)
+ rectPath.translate(-itemd->sceneTransform.dx(), -itemd->sceneTransform.dy());
+ else
+ rectPath = itemd->sceneTransform.inverted().map(rectPath);
+ keep = QGraphicsSceneIndexPrivate::itemCollidesWithPath(item, rectPath, mode);
+ }
+ }
+ return keep;
+ }
+
+ QRectF sceneRect;
+};
+
+class QGraphicsSceneIndexPointIntersector : public QGraphicsSceneIndexIntersector
+{
+public:
+ bool intersect(const QGraphicsItem *item, const QRectF &exposeRect, Qt::ItemSelectionMode mode,
+ const QTransform &deviceTransform) const
+ {
+ QRectF brect = item->boundingRect();
+ _q_adjustRect(&brect);
+
+ // ### Add test for this (without making things slower?)
+ Q_UNUSED(exposeRect);
+
+ bool keep = false;
+ const QGraphicsItemPrivate *itemd = QGraphicsItemPrivate::get(item);
+ if (itemd->itemIsUntransformable()) {
+ // Untransformable items; map the scene point to item coordinates.
+ const QTransform transform = item->deviceTransform(deviceTransform);
+ QPointF itemPoint = (deviceTransform * transform.inverted()).map(scenePoint);
+ keep = brect.contains(itemPoint);
+ if (keep && (mode == Qt::ContainsItemShape || mode == Qt::IntersectsItemShape)) {
+ QPainterPath pointPath;
+ pointPath.addRect(QRectF(itemPoint, QSizeF(1, 1)));
+ keep = QGraphicsSceneIndexPrivate::itemCollidesWithPath(item, pointPath, mode);
+ }
+ } else {
+ Q_ASSERT(!itemd->dirtySceneTransform);
+ QRectF sceneBoundingRect = itemd->sceneTransformTranslateOnly
+ ? brect.translated(itemd->sceneTransform.dx(),
+ itemd->sceneTransform.dy())
+ : itemd->sceneTransform.mapRect(brect);
+ keep = sceneBoundingRect.intersects(QRectF(scenePoint, QSizeF(1, 1)));
+ if (keep) {
+ QPointF p = itemd->sceneTransformTranslateOnly
+ ? QPointF(scenePoint.x() - itemd->sceneTransform.dx(),
+ scenePoint.y() - itemd->sceneTransform.dy())
+ : itemd->sceneTransform.inverted().map(scenePoint);
+ keep = item->contains(p);
+ }
+ }
+
+ return keep;
+ }
+
+ QPointF scenePoint;
+};
+
+class QGraphicsSceneIndexPathIntersector : public QGraphicsSceneIndexIntersector
+{
+public:
+ bool intersect(const QGraphicsItem *item, const QRectF &exposeRect, Qt::ItemSelectionMode mode,
+ const QTransform &deviceTransform) const
+ {
+ QRectF brect = item->boundingRect();
+ _q_adjustRect(&brect);
+
+ // ### Add test for this (without making things slower?)
+ Q_UNUSED(exposeRect);
+
+ bool keep = true;
+ const QGraphicsItemPrivate *itemd = QGraphicsItemPrivate::get(item);
+ if (itemd->itemIsUntransformable()) {
+ // Untransformable items; map the scene rect to item coordinates.
+ const QTransform transform = item->deviceTransform(deviceTransform);
+ QPainterPath itemPath = (deviceTransform * transform.inverted()).map(scenePath);
+ if (mode == Qt::ContainsItemShape || mode == Qt::ContainsItemBoundingRect)
+ keep = itemPath.contains(brect);
+ else
+ keep = itemPath.intersects(brect);
+ if (keep && (mode == Qt::ContainsItemShape || mode == Qt::IntersectsItemShape))
+ keep = QGraphicsSceneIndexPrivate::itemCollidesWithPath(item, itemPath, mode);
+ } else {
+ Q_ASSERT(!itemd->dirtySceneTransform);
+ const QRectF itemSceneBoundingRect = itemd->sceneTransformTranslateOnly
+ ? brect.translated(itemd->sceneTransform.dx(),
+ itemd->sceneTransform.dy())
+ : itemd->sceneTransform.mapRect(brect);
+ if (mode == Qt::ContainsItemShape || mode == Qt::ContainsItemBoundingRect)
+ keep = scenePath.contains(itemSceneBoundingRect);
+ else
+ keep = scenePath.intersects(itemSceneBoundingRect);
+ if (keep && (mode == Qt::ContainsItemShape || mode == Qt::IntersectsItemShape)) {
+ QPainterPath itemPath = itemd->sceneTransformTranslateOnly
+ ? scenePath.translated(-itemd->sceneTransform.dx(),
+ -itemd->sceneTransform.dy())
+ : itemd->sceneTransform.inverted().map(scenePath);
+ keep = QGraphicsSceneIndexPrivate::itemCollidesWithPath(item, itemPath, mode);
+ }
+ }
+ return keep;
+ }
+
+ QPainterPath scenePath;
+};
+
+/*!
+ Constructs a private scene index.
+*/
+QGraphicsSceneIndexPrivate::QGraphicsSceneIndexPrivate(QGraphicsScene *scene) : scene(scene)
+{
+ pointIntersector = new QGraphicsSceneIndexPointIntersector;
+ rectIntersector = new QGraphicsSceneIndexRectIntersector;
+ pathIntersector = new QGraphicsSceneIndexPathIntersector;
+}
+
+/*!
+ Destructor of private scene index.
+*/
+QGraphicsSceneIndexPrivate::~QGraphicsSceneIndexPrivate()
+{
+ delete pointIntersector;
+ delete rectIntersector;
+ delete pathIntersector;
+}
+
+/*!
+ \internal
+
+ Checks if item collides with the path and mode, but also checks that if it
+ doesn't collide, maybe its frame rect will.
+*/
+bool QGraphicsSceneIndexPrivate::itemCollidesWithPath(const QGraphicsItem *item,
+ const QPainterPath &path,
+ Qt::ItemSelectionMode mode)
+{
+ if (item->collidesWithPath(path, mode))
+ return true;
+ if (item->isWidget()) {
+ // Check if this is a window, and if its frame rect collides.
+ const QGraphicsWidget *widget = static_cast<const QGraphicsWidget *>(item);
+ if (widget->isWindow()) {
+ QRectF frameRect = widget->windowFrameRect();
+ QPainterPath framePath;
+ framePath.addRect(frameRect);
+ bool intersects = path.intersects(frameRect);
+ if (mode == Qt::IntersectsItemShape || mode == Qt::IntersectsItemBoundingRect)
+ return intersects || path.contains(frameRect.topLeft())
+ || framePath.contains(path.elementAt(0));
+ return !intersects && path.contains(frameRect.topLeft());
+ }
+ }
+ return false;
+}
+
+/*!
+ \internal
+ This function returns the items in ascending order.
+*/
+void QGraphicsSceneIndexPrivate::recursive_items_helper(QGraphicsItem *item, QRectF exposeRect,
+ QGraphicsSceneIndexIntersector *intersector,
+ QList<QGraphicsItem *> *items,
+ const QTransform &viewTransform,
+ Qt::ItemSelectionMode mode,
+ qreal parentOpacity) const
+{
+ Q_ASSERT(item);
+ if (!item->d_ptr->visible)
+ return;
+
+ const qreal opacity = item->d_ptr->combineOpacityFromParent(parentOpacity);
+ const bool itemIsFullyTransparent = QGraphicsItemPrivate::isOpacityNull(opacity);
+ const bool itemHasChildren = !item->d_ptr->children.isEmpty();
+ if (itemIsFullyTransparent && (!itemHasChildren || item->d_ptr->childrenCombineOpacity()))
+ return;
+
+ // Update the item's scene transform if dirty.
+ const bool itemIsUntransformable = item->d_ptr->itemIsUntransformable();
+ const bool wasDirtyParentSceneTransform = item->d_ptr->dirtySceneTransform && !itemIsUntransformable;
+ if (wasDirtyParentSceneTransform) {
+ item->d_ptr->updateSceneTransformFromParent();
+ Q_ASSERT(!item->d_ptr->dirtySceneTransform);
+ }
+
+ const bool itemClipsChildrenToShape = (item->d_ptr->flags & QGraphicsItem::ItemClipsChildrenToShape);
+ bool processItem = !itemIsFullyTransparent;
+ if (processItem) {
+ processItem = intersector->intersect(item, exposeRect, mode, viewTransform);
+ if (!processItem && (!itemHasChildren || itemClipsChildrenToShape)) {
+ if (wasDirtyParentSceneTransform)
+ item->d_ptr->invalidateChildrenSceneTransform();
+ return;
+ }
+ } // else we know for sure this item has children we must process.
+
+ int i = 0;
+ if (itemHasChildren) {
+ // Sort children.
+ item->d_ptr->ensureSortedChildren();
+
+ // Clip to shape.
+ if (itemClipsChildrenToShape && !itemIsUntransformable) {
+ QPainterPath mappedShape = item->d_ptr->sceneTransformTranslateOnly
+ ? item->shape().translated(item->d_ptr->sceneTransform.dx(),
+ item->d_ptr->sceneTransform.dy())
+ : item->d_ptr->sceneTransform.map(item->shape());
+ exposeRect &= mappedShape.controlPointRect();
+ }
+
+ // Process children behind
+ for (i = 0; i < item->d_ptr->children.size(); ++i) {
+ QGraphicsItem *child = item->d_ptr->children.at(i);
+ if (wasDirtyParentSceneTransform)
+ child->d_ptr->dirtySceneTransform = 1;
+ if (!(child->d_ptr->flags & QGraphicsItem::ItemStacksBehindParent))
+ break;
+ if (itemIsFullyTransparent && !(child->d_ptr->flags & QGraphicsItem::ItemIgnoresParentOpacity))
+ continue;
+ recursive_items_helper(child, exposeRect, intersector, items, viewTransform,
+ mode, opacity);
+ }
+ }
+
+ // Process item
+ if (processItem)
+ items->append(item);
+
+ // Process children in front
+ if (itemHasChildren) {
+ for (; i < item->d_ptr->children.size(); ++i) {
+ QGraphicsItem *child = item->d_ptr->children.at(i);
+ if (wasDirtyParentSceneTransform)
+ child->d_ptr->dirtySceneTransform = 1;
+ if (itemIsFullyTransparent && !(child->d_ptr->flags & QGraphicsItem::ItemIgnoresParentOpacity))
+ continue;
+ recursive_items_helper(child, exposeRect, intersector, items, viewTransform,
+ mode, opacity);
+ }
+ }
+}
+
+void QGraphicsSceneIndexPrivate::init()
+{
+ if (!scene)
+ return;
+
+ QObject::connect(scene, SIGNAL(sceneRectChanged(QRectF)),
+ q_func(), SLOT(updateSceneRect(QRectF)));
+}
+
+/*!
+ Constructs an abstract scene index for a given \a scene.
+*/
+QGraphicsSceneIndex::QGraphicsSceneIndex(QGraphicsScene *scene)
+: QObject(*new QGraphicsSceneIndexPrivate(scene), scene)
+{
+ d_func()->init();
+}
+
+/*!
+ \internal
+*/
+QGraphicsSceneIndex::QGraphicsSceneIndex(QGraphicsSceneIndexPrivate &dd, QGraphicsScene *scene)
+ : QObject(dd, scene)
+{
+ d_func()->init();
+}
+
+/*!
+ Destroys the scene index.
+*/
+QGraphicsSceneIndex::~QGraphicsSceneIndex()
+{
+
+}
+
+/*!
+ Returns the scene of this index.
+*/
+QGraphicsScene* QGraphicsSceneIndex::scene() const
+{
+ Q_D(const QGraphicsSceneIndex);
+ return d->scene;
+}
+
+/*!
+ \fn QList<QGraphicsItem *> QGraphicsSceneIndex::items(const QPointF &pos,
+ Qt::ItemSelectionMode mode, Qt::SortOrder order, const QTransform
+ &deviceTransform) const
+
+ Returns all visible items that, depending on \a mode, are at the specified
+ \a pos and return a list sorted using \a order.
+
+ The default value for \a mode is Qt::IntersectsItemShape; all items whose
+ exact shape intersects with \a pos are returned.
+
+ \a deviceTransform is the transformation apply to the view.
+
+ This method use the estimation of the index (estimateItems) and refine the
+ list to get an exact result. If you want to implement your own refinement
+ algorithm you can reimplement this method.
+
+ \sa estimateItems()
+
+*/
+QList<QGraphicsItem *> QGraphicsSceneIndex::items(const QPointF &pos, Qt::ItemSelectionMode mode,
+ Qt::SortOrder order, const QTransform &deviceTransform) const
+{
+
+ Q_D(const QGraphicsSceneIndex);
+ QList<QGraphicsItem *> itemList;
+ d->pointIntersector->scenePoint = pos;
+ d->items_helper(QRectF(pos, QSizeF(1, 1)), d->pointIntersector, &itemList, deviceTransform, mode, order);
+ return itemList;
+}
+
+/*!
+ \fn QList<QGraphicsItem *> QGraphicsSceneIndex::items(const QRectF &rect,
+ Qt::ItemSelectionMode mode, Qt::SortOrder order, const QTransform
+ &deviceTransform) const
+
+ \overload
+
+ Returns all visible items that, depending on \a mode, are either inside or
+ intersect with the specified \a rect and return a list sorted using \a order.
+
+ The default value for \a mode is Qt::IntersectsItemShape; all items whose
+ exact shape intersects with or is contained by \a rect are returned.
+
+ \a deviceTransform is the transformation apply to the view.
+
+ This method use the estimation of the index (estimateItems) and refine
+ the list to get an exact result. If you want to implement your own
+ refinement algorithm you can reimplement this method.
+
+ \sa estimateItems()
+
+*/
+QList<QGraphicsItem *> QGraphicsSceneIndex::items(const QRectF &rect, Qt::ItemSelectionMode mode,
+ Qt::SortOrder order, const QTransform &deviceTransform) const
+{
+ Q_D(const QGraphicsSceneIndex);
+ QRectF exposeRect = rect;
+ _q_adjustRect(&exposeRect);
+ QList<QGraphicsItem *> itemList;
+ d->rectIntersector->sceneRect = rect;
+ d->items_helper(exposeRect, d->rectIntersector, &itemList, deviceTransform, mode, order);
+ return itemList;
+}
+
+/*!
+ \fn QList<QGraphicsItem *> QGraphicsSceneIndex::items(const QPolygonF
+ &polygon, Qt::ItemSelectionMode mode, Qt::SortOrder order, const
+ QTransform &deviceTransform) const
+
+ \overload
+
+ Returns all visible items that, depending on \a mode, are either inside or
+ intersect with the specified \a polygon and return a list sorted using \a order.
+
+ The default value for \a mode is Qt::IntersectsItemShape; all items whose
+ exact shape intersects with or is contained by \a polygon are returned.
+
+ \a deviceTransform is the transformation apply to the view.
+
+ This method use the estimation of the index (estimateItems) and refine
+ the list to get an exact result. If you want to implement your own
+ refinement algorithm you can reimplement this method.
+
+ \sa estimateItems()
+
+*/
+QList<QGraphicsItem *> QGraphicsSceneIndex::items(const QPolygonF &polygon, Qt::ItemSelectionMode mode,
+ Qt::SortOrder order, const QTransform &deviceTransform) const
+{
+ Q_D(const QGraphicsSceneIndex);
+ QList<QGraphicsItem *> itemList;
+ QRectF exposeRect = polygon.boundingRect();
+ _q_adjustRect(&exposeRect);
+ QPainterPath path;
+ path.addPolygon(polygon);
+ d->pathIntersector->scenePath = path;
+ d->items_helper(exposeRect, d->pathIntersector, &itemList, deviceTransform, mode, order);
+ return itemList;
+}
+
+/*!
+ \fn QList<QGraphicsItem *> QGraphicsSceneIndex::items(const QPainterPath
+ &path, Qt::ItemSelectionMode mode, Qt::SortOrder order, const QTransform
+ &deviceTransform) const
+
+ \overload
+
+ Returns all visible items that, depending on \a mode, are either inside or
+ intersect with the specified \a path and return a list sorted using \a order.
+
+ The default value for \a mode is Qt::IntersectsItemShape; all items whose
+ exact shape intersects with or is contained by \a path are returned.
+
+ \a deviceTransform is the transformation apply to the view.
+
+ This method use the estimation of the index (estimateItems) and refine
+ the list to get an exact result. If you want to implement your own
+ refinement algorithm you can reimplement this method.
+
+ \sa estimateItems()
+
+*/
+QList<QGraphicsItem *> QGraphicsSceneIndex::items(const QPainterPath &path, Qt::ItemSelectionMode mode,
+ Qt::SortOrder order, const QTransform &deviceTransform) const
+{
+ Q_D(const QGraphicsSceneIndex);
+ QList<QGraphicsItem *> itemList;
+ QRectF exposeRect = path.controlPointRect();
+ _q_adjustRect(&exposeRect);
+ d->pathIntersector->scenePath = path;
+ d->items_helper(exposeRect, d->pathIntersector, &itemList, deviceTransform, mode, order);
+ return itemList;
+}
+
+/*!
+ This virtual function return an estimation of items at position \a point.
+ This method return a list sorted using \a order.
+*/
+QList<QGraphicsItem *> QGraphicsSceneIndex::estimateItems(const QPointF &point, Qt::SortOrder order) const
+{
+ return estimateItems(QRectF(point, QSize(1, 1)), order);
+}
+
+QList<QGraphicsItem *> QGraphicsSceneIndex::estimateTopLevelItems(const QRectF &rect, Qt::SortOrder order) const
+{
+ Q_D(const QGraphicsSceneIndex);
+ Q_UNUSED(rect);
+ QGraphicsScenePrivate *scened = d->scene->d_func();
+ scened->ensureSortedTopLevelItems();
+ if (order == Qt::DescendingOrder) {
+ QList<QGraphicsItem *> sorted;
+ for (int i = scened->topLevelItems.size() - 1; i >= 0; --i)
+ sorted << scened->topLevelItems.at(i);
+ return sorted;
+ }
+ return scened->topLevelItems;
+}
+
+/*!
+ \fn QList<QGraphicsItem *> QGraphicsSceneIndex::items(Qt::SortOrder order = Qt::DescendingOrder) const
+
+ This pure virtual function all items in the index and sort them using
+ \a order.
+*/
+
+
+/*!
+ Notifies the index that the scene's scene rect has changed. \a rect
+ is thew new scene rect.
+
+ \sa QGraphicsScene::sceneRect()
+*/
+void QGraphicsSceneIndex::updateSceneRect(const QRectF &rect)
+{
+ Q_UNUSED(rect);
+}
+
+/*!
+ This virtual function removes all items in the scene index.
+*/
+void QGraphicsSceneIndex::clear()
+{
+ const QList<QGraphicsItem *> allItems = items();
+ for (int i = 0 ; i < allItems.size(); ++i)
+ removeItem(allItems.at(i));
+}
+
+/*!
+ \fn virtual void QGraphicsSceneIndex::addItem(QGraphicsItem *item) = 0
+
+ This pure virtual function inserts an \a item to the scene index.
+
+ \sa removeItem(), deleteItem()
+*/
+
+/*!
+ \fn virtual void QGraphicsSceneIndex::removeItem(QGraphicsItem *item) = 0
+
+ This pure virtual function removes an \a item to the scene index.
+
+ \sa addItem(), deleteItem()
+*/
+
+/*!
+ This method is called when an \a item has been deleted.
+ The default implementation call removeItem. Be carefull,
+ if your implementation of removeItem use pure virtual method
+ of QGraphicsItem like boundingRect(), then you should reimplement
+ this method.
+
+ \sa addItem(), removeItem()
+*/
+void QGraphicsSceneIndex::deleteItem(QGraphicsItem *item)
+{
+ removeItem(item);
+}
+
+/*!
+ This virtual function is called by QGraphicsItem to notify the index
+ that some part of the \a item 's state changes. By reimplementing this
+ function, your can react to a change, and in some cases, (depending on \a
+ change,) adjustments in the index can be made.
+
+ \a change is the parameter of the item that is changing. \a value is the
+ value that changed; the type of the value depends on \a change.
+
+ The default implementation does nothing.
+
+ \sa QGraphicsItem::GraphicsItemChange
+*/
+void QGraphicsSceneIndex::itemChange(const QGraphicsItem *item, QGraphicsItem::GraphicsItemChange change, const void *const value)
+{
+ Q_UNUSED(item);
+ Q_UNUSED(change);
+ Q_UNUSED(value);
+}
+
+/*!
+ Notify the index for a geometry change of an \a item.
+
+ \sa QGraphicsItem::prepareGeometryChange()
+*/
+void QGraphicsSceneIndex::prepareBoundingRectChange(const QGraphicsItem *item)
+{
+ Q_UNUSED(item);
+}
+
+QT_END_NAMESPACE
+
+#include "moc_qgraphicssceneindex_p.cpp"
+
+#endif // QT_NO_GRAPHICSVIEW
diff --git a/src/widgets/graphicsview/qgraphicssceneindex_p.h b/src/widgets/graphicsview/qgraphicssceneindex_p.h
new file mode 100644
index 0000000000..e498e822fa
--- /dev/null
+++ b/src/widgets/graphicsview/qgraphicssceneindex_p.h
@@ -0,0 +1,182 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtCore 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 Technology Preview License Agreement accompanying
+** this package.
+**
+** 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.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QGRAPHICSSCENEINDEX_H
+#define QGRAPHICSSCENEINDEX_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of other Qt classes. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qgraphicsscene_p.h"
+#include "qgraphicsscene.h"
+#include <private/qobject_p.h>
+
+#include <QtCore/qnamespace.h>
+#include <QtCore/qobject.h>
+#include <QtGui/qtransform.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+#if !defined(QT_NO_GRAPHICSVIEW) || (QT_EDITION & QT_MODULE_GRAPHICSVIEW) != QT_MODULE_GRAPHICSVIEW
+
+class QGraphicsSceneIndexIntersector;
+class QGraphicsSceneIndexPointIntersector;
+class QGraphicsSceneIndexRectIntersector;
+class QGraphicsSceneIndexPathIntersector;
+class QGraphicsSceneIndexPrivate;
+class QPointF;
+class QRectF;
+template<typename T> class QList;
+
+class Q_AUTOTEST_EXPORT QGraphicsSceneIndex : public QObject
+{
+ Q_OBJECT
+
+public:
+ QGraphicsSceneIndex(QGraphicsScene *scene = 0);
+ virtual ~QGraphicsSceneIndex();
+
+ QGraphicsScene *scene() const;
+
+ virtual QList<QGraphicsItem *> items(Qt::SortOrder order = Qt::DescendingOrder) const = 0;
+ virtual QList<QGraphicsItem *> items(const QPointF &pos, Qt::ItemSelectionMode mode,
+ Qt::SortOrder order, const QTransform &deviceTransform = QTransform()) const;
+ virtual QList<QGraphicsItem *> items(const QRectF &rect, Qt::ItemSelectionMode mode,
+ Qt::SortOrder order, const QTransform &deviceTransform = QTransform()) const;
+ virtual QList<QGraphicsItem *> items(const QPolygonF &polygon, Qt::ItemSelectionMode mode,
+ Qt::SortOrder order, const QTransform &deviceTransform = QTransform()) const;
+ virtual QList<QGraphicsItem *> items(const QPainterPath &path, Qt::ItemSelectionMode mode,
+ Qt::SortOrder order, const QTransform &deviceTransform = QTransform()) const;
+ virtual QList<QGraphicsItem *> estimateItems(const QPointF &point, Qt::SortOrder order) const;
+ virtual QList<QGraphicsItem *> estimateItems(const QRectF &rect, Qt::SortOrder order) const = 0;
+ virtual QList<QGraphicsItem *> estimateTopLevelItems(const QRectF &, Qt::SortOrder order) const;
+
+protected Q_SLOTS:
+ virtual void updateSceneRect(const QRectF &rect);
+
+protected:
+ virtual void clear();
+ virtual void addItem(QGraphicsItem *item) = 0;
+ virtual void removeItem(QGraphicsItem *item) = 0;
+ virtual void deleteItem(QGraphicsItem *item);
+
+ virtual void itemChange(const QGraphicsItem *item, QGraphicsItem::GraphicsItemChange, const void *const value);
+ virtual void prepareBoundingRectChange(const QGraphicsItem *item);
+
+ QGraphicsSceneIndex(QGraphicsSceneIndexPrivate &dd, QGraphicsScene *scene);
+
+ friend class QGraphicsScene;
+ friend class QGraphicsScenePrivate;
+ friend class QGraphicsItem;
+ friend class QGraphicsItemPrivate;
+ friend class QGraphicsSceneBspTreeIndex;
+private:
+ Q_DISABLE_COPY(QGraphicsSceneIndex)
+ Q_DECLARE_PRIVATE(QGraphicsSceneIndex)
+};
+
+class QGraphicsSceneIndexPrivate : public QObjectPrivate
+{
+ Q_DECLARE_PUBLIC(QGraphicsSceneIndex)
+public:
+ QGraphicsSceneIndexPrivate(QGraphicsScene *scene);
+ ~QGraphicsSceneIndexPrivate();
+
+ void init();
+ static bool itemCollidesWithPath(const QGraphicsItem *item, const QPainterPath &path, Qt::ItemSelectionMode mode);
+
+ void recursive_items_helper(QGraphicsItem *item, QRectF exposeRect,
+ QGraphicsSceneIndexIntersector *intersector, QList<QGraphicsItem *> *items,
+ const QTransform &viewTransform,
+ Qt::ItemSelectionMode mode, qreal parentOpacity = 1.0) const;
+ inline void items_helper(const QRectF &rect, QGraphicsSceneIndexIntersector *intersector,
+ QList<QGraphicsItem *> *items, const QTransform &viewTransform,
+ Qt::ItemSelectionMode mode, Qt::SortOrder order) const;
+
+ QGraphicsScene *scene;
+ QGraphicsSceneIndexPointIntersector *pointIntersector;
+ QGraphicsSceneIndexRectIntersector *rectIntersector;
+ QGraphicsSceneIndexPathIntersector *pathIntersector;
+};
+
+inline void QGraphicsSceneIndexPrivate::items_helper(const QRectF &rect, QGraphicsSceneIndexIntersector *intersector,
+ QList<QGraphicsItem *> *items, const QTransform &viewTransform,
+ Qt::ItemSelectionMode mode, Qt::SortOrder order) const
+{
+ Q_Q(const QGraphicsSceneIndex);
+ const QList<QGraphicsItem *> tli = q->estimateTopLevelItems(rect, Qt::AscendingOrder);
+ for (int i = 0; i < tli.size(); ++i)
+ recursive_items_helper(tli.at(i), rect, intersector, items, viewTransform, mode);
+ if (order == Qt::DescendingOrder) {
+ const int n = items->size();
+ for (int i = 0; i < n / 2; ++i)
+ items->swap(i, n - i - 1);
+ }
+}
+
+class QGraphicsSceneIndexIntersector
+{
+public:
+ QGraphicsSceneIndexIntersector() { }
+ virtual ~QGraphicsSceneIndexIntersector() { }
+ virtual bool intersect(const QGraphicsItem *item, const QRectF &exposeRect, Qt::ItemSelectionMode mode,
+ const QTransform &deviceTransform) const = 0;
+};
+
+#endif // QT_NO_GRAPHICSVIEW
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QGRAPHICSSCENEINDEX_H
diff --git a/src/widgets/graphicsview/qgraphicsscenelinearindex.cpp b/src/widgets/graphicsview/qgraphicsscenelinearindex.cpp
new file mode 100644
index 0000000000..074d43961a
--- /dev/null
+++ b/src/widgets/graphicsview/qgraphicsscenelinearindex.cpp
@@ -0,0 +1,95 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** 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 Technology Preview License Agreement accompanying
+** this package.
+**
+** 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.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*!
+ \class QGraphicsSceneLinearIndex
+ \brief The QGraphicsSceneLinearIndex class provides an implementation of
+ a linear indexing algorithm for discovering items in QGraphicsScene.
+ \since 4.6
+ \ingroup graphicsview-api
+ \internal
+
+ QGraphicsSceneLinearIndex index is default linear implementation to discover items.
+ It basically store all items in a list and return them to the scene.
+
+ \sa QGraphicsScene, QGraphicsView, QGraphicsSceneIndex, QGraphicsSceneBspTreeIndex
+*/
+
+#include <private/qgraphicsscenelinearindex_p.h>
+
+/*!
+ \fn QGraphicsSceneLinearIndex::QGraphicsSceneLinearIndex(QGraphicsScene *scene = 0):
+
+ Construct a linear index for the given \a scene.
+*/
+
+/*!
+ \fn QList<QGraphicsItem *> QGraphicsSceneLinearIndex::items(Qt::SortOrder order = Qt::DescendingOrder) const;
+
+ Return all items in the index and sort them using \a order.
+*/
+
+
+/*!
+ \fn virtual QList<QGraphicsItem *> QGraphicsSceneLinearIndex::estimateItems(const QRectF &rect, Qt::SortOrder order) const
+
+ Returns an estimation visible items that are either inside or
+ intersect with the specified \a rect and return a list sorted using \a order.
+*/
+
+/*!
+ \fn void QGraphicsSceneLinearIndex::clear()
+ \internal
+ Clear the all the BSP index.
+*/
+
+/*!
+ \fn virtual void QGraphicsSceneLinearIndex::addItem(QGraphicsItem *item)
+
+ Add the \a item into the index.
+*/
+
+/*!
+ \fn virtual void QGraphicsSceneLinearIndex::removeItem(QGraphicsItem *item)
+
+ Add the \a item from the index.
+*/
+
diff --git a/src/widgets/graphicsview/qgraphicsscenelinearindex_p.h b/src/widgets/graphicsview/qgraphicsscenelinearindex_p.h
new file mode 100644
index 0000000000..ef72f57faf
--- /dev/null
+++ b/src/widgets/graphicsview/qgraphicsscenelinearindex_p.h
@@ -0,0 +1,109 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtCore 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 Technology Preview License Agreement accompanying
+** this package.
+**
+** 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.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QGRAPHICSSCENELINEARINDEX_H
+#define QGRAPHICSSCENELINEARINDEX_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of other Qt classes. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtCore/qglobal.h>
+
+#if !defined(QT_NO_GRAPHICSVIEW) || (QT_EDITION & QT_MODULE_GRAPHICSVIEW) != QT_MODULE_GRAPHICSVIEW
+
+#include <QtCore/qrect.h>
+#include <QtCore/qlist.h>
+#include <QtGui/qgraphicsitem.h>
+#include <private/qgraphicssceneindex_p.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+class Q_AUTOTEST_EXPORT QGraphicsSceneLinearIndex : public QGraphicsSceneIndex
+{
+ Q_OBJECT
+
+public:
+ QGraphicsSceneLinearIndex(QGraphicsScene *scene = 0) : QGraphicsSceneIndex(scene)
+ { }
+
+ QList<QGraphicsItem *> items(Qt::SortOrder order = Qt::DescendingOrder) const
+ { Q_UNUSED(order); return m_items; }
+
+ virtual QList<QGraphicsItem *> estimateItems(const QRectF &rect, Qt::SortOrder order) const
+ {
+ Q_UNUSED(rect);
+ Q_UNUSED(order);
+ return m_items;
+ }
+
+protected :
+ virtual void clear()
+ { m_items.clear(); }
+
+ virtual void addItem(QGraphicsItem *item)
+ { m_items << item; }
+
+ virtual void removeItem(QGraphicsItem *item)
+ { m_items.removeOne(item); }
+
+private:
+ QList<QGraphicsItem*> m_items;
+};
+
+#endif // QT_NO_GRAPHICSVIEW
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QGRAPHICSSCENELINEARINDEX_H
diff --git a/src/widgets/graphicsview/qgraphicstransform.cpp b/src/widgets/graphicsview/qgraphicstransform.cpp
new file mode 100644
index 0000000000..513c41f46f
--- /dev/null
+++ b/src/widgets/graphicsview/qgraphicstransform.cpp
@@ -0,0 +1,594 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtDeclarative 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 Technology Preview License Agreement accompanying
+** this package.
+**
+** 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.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*!
+ \class QGraphicsTransform
+ \brief The QGraphicsTransform class is an abstract base class for building
+ advanced transformations on QGraphicsItems.
+ \since 4.6
+ \ingroup graphicsview-api
+
+ As an alternative to QGraphicsItem::transform, QGraphicsTransform lets you
+ create and control advanced transformations that can be configured
+ independently using specialized properties.
+
+ QGraphicsItem allows you to assign any number of QGraphicsTransform
+ instances to one QGraphicsItem. Each QGraphicsTransform is applied in
+ order, one at a time, to the QGraphicsItem it's assigned to.
+
+ QGraphicsTransform is particularly useful for animations. Whereas
+ QGraphicsItem::setTransform() lets you assign any transform directly to an
+ item, there is no direct way to interpolate between two different
+ transformations (e.g., when transitioning between two states, each for
+ which the item has a different arbitrary transform assigned). Using
+ QGraphicsTransform you can interpolate the property values of each
+ 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.
+ The exception to this is QGraphicsRotation, which projects back to
+ 2D after each rotation to preserve the perspective effect around
+ the X and Y axes.
+
+ 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
+ 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.
+
+ QGraphicsTransform can be used together with QGraphicsItem::setTransform(),
+ QGraphicsItem::setRotation(), and QGraphicsItem::setScale().
+
+ \sa QGraphicsItem::transform(), QGraphicsScale, QGraphicsRotation
+*/
+
+#include "qgraphicstransform.h"
+#include "qgraphicsitem_p.h"
+#include "qgraphicstransform_p.h"
+#include <QDebug>
+#include <QtCore/qmath.h>
+#include <QtCore/qnumeric.h>
+
+#ifndef QT_NO_GRAPHICSVIEW
+QT_BEGIN_NAMESPACE
+void QGraphicsTransformPrivate::setItem(QGraphicsItem *i)
+{
+ if (item == i)
+ return;
+
+ if (item) {
+ Q_Q(QGraphicsTransform);
+ QGraphicsItemPrivate *d_ptr = item->d_ptr.data();
+
+ item->prepareGeometryChange();
+ Q_ASSERT(d_ptr->transformData);
+ d_ptr->transformData->graphicsTransforms.removeAll(q);
+ d_ptr->dirtySceneTransform = 1;
+ item = 0;
+ }
+
+ item = i;
+}
+
+void QGraphicsTransformPrivate::updateItem(QGraphicsItem *item)
+{
+ item->prepareGeometryChange();
+ item->d_ptr->dirtySceneTransform = 1;
+}
+
+/*!
+ Constructs a new QGraphicsTransform with the given \a parent.
+*/
+QGraphicsTransform::QGraphicsTransform(QObject *parent)
+ : QObject(*new QGraphicsTransformPrivate, parent)
+{
+}
+
+/*!
+ Destroys the graphics transform.
+*/
+QGraphicsTransform::~QGraphicsTransform()
+{
+ Q_D(QGraphicsTransform);
+ d->setItem(0);
+}
+
+/*!
+ \internal
+*/
+QGraphicsTransform::QGraphicsTransform(QGraphicsTransformPrivate &p, QObject *parent)
+ : QObject(p, parent)
+{
+}
+
+/*!
+ \fn void QGraphicsTransform::applyTo(QMatrix4x4 *matrix) const
+
+ This pure virtual method has to be reimplemented in derived classes.
+
+ It applies this transformation to \a matrix.
+
+ \sa QGraphicsItem::transform(), QMatrix4x4::toTransform()
+*/
+
+/*!
+ Notifies that this transform operation has changed its parameters in such a
+ way that applyTo() will return a different result than before.
+
+ When implementing you own custom graphics transform, you must call this
+ function every time you change a parameter, to let QGraphicsItem know that
+ its transformation needs to be updated.
+
+ \sa applyTo()
+*/
+void QGraphicsTransform::update()
+{
+ Q_D(QGraphicsTransform);
+ if (d->item)
+ d->updateItem(d->item);
+}
+
+/*!
+ \class QGraphicsScale
+ \brief The QGraphicsScale class provides a scale transformation.
+ \since 4.6
+
+ QGraphicsScene provides certain parameters to help control how the scale
+ should be applied.
+
+ The origin is the point that the item is scaled from (i.e., it stays fixed
+ relative to the parent as the rest of the item grows). By default the
+ origin is QPointF(0, 0).
+
+ 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()
+*/
+
+class QGraphicsScalePrivate : public QGraphicsTransformPrivate
+{
+public:
+ QGraphicsScalePrivate()
+ : xScale(1), yScale(1), zScale(1) {}
+ QVector3D origin;
+ qreal xScale;
+ qreal yScale;
+ qreal zScale;
+};
+
+/*!
+ Constructs an empty QGraphicsScale object with the given \a parent.
+*/
+QGraphicsScale::QGraphicsScale(QObject *parent)
+ : QGraphicsTransform(*new QGraphicsScalePrivate, parent)
+{
+}
+
+/*!
+ Destroys the graphics scale.
+*/
+QGraphicsScale::~QGraphicsScale()
+{
+}
+
+/*!
+ \property QGraphicsScale::origin
+ \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, zScale
+*/
+QVector3D QGraphicsScale::origin() const
+{
+ Q_D(const QGraphicsScale);
+ return d->origin;
+}
+void QGraphicsScale::setOrigin(const QVector3D &point)
+{
+ Q_D(QGraphicsScale);
+ if (d->origin == point)
+ return;
+ d->origin = point;
+ update();
+ emit originChanged();
+}
+
+/*!
+ \property QGraphicsScale::xScale
+ \brief the horizontal 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 mirrored horizontally around its
+ origin.
+
+ \sa yScale, zScale, origin
+*/
+qreal QGraphicsScale::xScale() const
+{
+ Q_D(const QGraphicsScale);
+ return d->xScale;
+}
+void QGraphicsScale::setXScale(qreal scale)
+{
+ Q_D(QGraphicsScale);
+ if (d->xScale == scale)
+ return;
+ d->xScale = scale;
+ update();
+ emit xScaleChanged();
+ emit scaleChanged();
+}
+
+/*!
+ \property QGraphicsScale::yScale
+ \brief the vertical 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 vertically around its
+ origin.
+
+ \sa xScale, zScale, origin
+*/
+qreal QGraphicsScale::yScale() const
+{
+ Q_D(const QGraphicsScale);
+ return d->yScale;
+}
+void QGraphicsScale::setYScale(qreal scale)
+{
+ Q_D(QGraphicsScale);
+ if (d->yScale == scale)
+ return;
+ d->yScale = scale;
+ update();
+ emit yScaleChanged();
+ emit scaleChanged();
+}
+
+/*!
+ \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 zScaleChanged();
+ emit scaleChanged();
+}
+
+/*!
+ \reimp
+*/
+void QGraphicsScale::applyTo(QMatrix4x4 *matrix) const
+{
+ Q_D(const QGraphicsScale);
+ matrix->translate(d->origin);
+ matrix->scale(d->xScale, d->yScale, d->zScale);
+ matrix->translate(-d->origin);
+}
+
+/*!
+ \fn QGraphicsScale::originChanged()
+
+ QGraphicsScale emits this signal when its origin changes.
+
+ \sa QGraphicsScale::origin
+*/
+
+/*!
+ \fn QGraphicsScale::xScaleChanged()
+ \since 4.7
+
+ This signal is emitted whenever the \l xScale property changes.
+*/
+
+/*!
+ \fn QGraphicsScale::yScaleChanged()
+ \since 4.7
+
+ This signal is emitted whenever the \l yScale property changes.
+*/
+
+/*!
+ \fn QGraphicsScale::zScaleChanged()
+ \since 4.7
+
+ This signal is emitted whenever the \l zScale property changes.
+*/
+
+/*!
+ \fn QGraphicsScale::scaleChanged()
+
+ This signal is emitted whenever the xScale, yScale, or zScale
+ of the object changes.
+
+ \sa QGraphicsScale::xScale, QGraphicsScale::yScale
+ \sa QGraphicsScale::zScale
+*/
+
+/*!
+ \class QGraphicsRotation
+ \brief The QGraphicsRotation class provides a rotation transformation around
+ a given axis.
+ \since 4.6
+
+ You can provide the desired axis by assigning a QVector3D to the axis property
+ or by passing a member if Qt::Axis to the setAxis convenience function.
+ By default the axis is (0, 0, 1) i.e., rotation around the Z axis.
+
+ The angle property, which is provided by QGraphicsRotation, now
+ describes the number of degrees to rotate around this axis.
+
+ QGraphicsRotation provides certain parameters to help control how the
+ rotation should be applied.
+
+ The origin is the point that the item is rotated around (i.e., it stays
+ fixed relative to the parent as the rest of the item is rotated). By
+ default the origin is QPointF(0, 0).
+
+ The angle property provides the number of degrees to rotate the item
+ clockwise around the origin. This value also be negative, indicating a
+ counter-clockwise rotation. For animation purposes it may also be useful to
+ provide rotation angles exceeding (-360, 360) degrees, for instance to
+ animate how an item rotates several times.
+
+ Note: the final rotation is the combined effect of a rotation in
+ 3D space followed by a projection back to 2D. If several rotations
+ are performed in succession, they will not behave as expected unless
+ they were all around the Z axis.
+
+ \sa QGraphicsTransform, QGraphicsItem::setRotation(), QTransform::rotate()
+*/
+
+class QGraphicsRotationPrivate : public QGraphicsTransformPrivate
+{
+public:
+ QGraphicsRotationPrivate()
+ : angle(0), axis(0, 0, 1) {}
+ QVector3D origin;
+ qreal angle;
+ QVector3D axis;
+};
+
+/*!
+ Constructs a new QGraphicsRotation with the given \a parent.
+*/
+QGraphicsRotation::QGraphicsRotation(QObject *parent)
+ : QGraphicsTransform(*new QGraphicsRotationPrivate, parent)
+{
+}
+
+/*!
+ Destroys the graphics rotation.
+*/
+QGraphicsRotation::~QGraphicsRotation()
+{
+}
+
+/*!
+ \property QGraphicsRotation::origin
+ \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
+*/
+QVector3D QGraphicsRotation::origin() const
+{
+ Q_D(const QGraphicsRotation);
+ return d->origin;
+}
+void QGraphicsRotation::setOrigin(const QVector3D &point)
+{
+ Q_D(QGraphicsRotation);
+ if (d->origin == point)
+ return;
+ d->origin = point;
+ update();
+ emit originChanged();
+}
+
+/*!
+ \property QGraphicsRotation::angle
+ \brief the angle for clockwise rotation, in degrees.
+
+ The angle can be any real number; the default value is 0.0. A value of 180
+ will rotate 180 degrees, clockwise. If you provide a negative number, the
+ item will be rotated counter-clockwise. Normally the rotation angle will be
+ in the range (-360, 360), but you can also provide numbers outside of this
+ range (e.g., a angle of 370 degrees gives the same result as 10 degrees).
+ Setting the angle to NaN results in no rotation.
+
+ \sa origin
+*/
+qreal QGraphicsRotation::angle() const
+{
+ Q_D(const QGraphicsRotation);
+ return d->angle;
+}
+void QGraphicsRotation::setAngle(qreal angle)
+{
+ Q_D(QGraphicsRotation);
+ if (d->angle == angle)
+ return;
+ d->angle = angle;
+ update();
+ emit angleChanged();
+}
+
+/*!
+ \fn QGraphicsRotation::originChanged()
+
+ This signal is emitted whenever the origin has changed.
+
+ \sa QGraphicsRotation::origin
+*/
+
+/*!
+ \fn void QGraphicsRotation::angleChanged()
+
+ This signal is emitted whenever the angle has changed.
+
+ \sa QGraphicsRotation::angle
+*/
+
+/*!
+ \property QGraphicsRotation::axis
+ \brief a rotation axis, specified by a vector in 3D space.
+
+ This can be any axis in 3D space. By default the axis is (0, 0, 1),
+ which is aligned with the Z axis. If you provide another axis,
+ QGraphicsRotation will provide a transformation that rotates
+ around this axis. For example, if you would like to rotate an item
+ around its X axis, you could pass (1, 0, 0) as the axis.
+
+ \sa QTransform, QGraphicsRotation::angle
+*/
+QVector3D QGraphicsRotation::axis() const
+{
+ Q_D(const QGraphicsRotation);
+ return d->axis;
+}
+void QGraphicsRotation::setAxis(const QVector3D &axis)
+{
+ Q_D(QGraphicsRotation);
+ if (d->axis == axis)
+ return;
+ d->axis = axis;
+ update();
+ emit axisChanged();
+}
+
+/*!
+ \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(QVector3D(1, 0, 0));
+ break;
+ case Qt::YAxis:
+ setAxis(QVector3D(0, 1, 0));
+ break;
+ case Qt::ZAxis:
+ setAxis(QVector3D(0, 0, 1));
+ break;
+ }
+}
+
+/*!
+ \reimp
+*/
+void QGraphicsRotation::applyTo(QMatrix4x4 *matrix) const
+{
+ Q_D(const QGraphicsRotation);
+
+ if (d->angle == 0. || d->axis.isNull() || qIsNaN(d->angle))
+ return;
+
+ matrix->translate(d->origin);
+ matrix->projectedRotate(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"
+
+QT_END_NAMESPACE
+#endif //QT_NO_GRAPHICSVIEW
diff --git a/src/widgets/graphicsview/qgraphicstransform.h b/src/widgets/graphicsview/qgraphicstransform.h
new file mode 100644
index 0000000000..e2dd05b5be
--- /dev/null
+++ b/src/widgets/graphicsview/qgraphicstransform.h
@@ -0,0 +1,159 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** 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 Technology Preview License Agreement accompanying
+** this package.
+**
+** 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.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QGRAPHICSTRANSFORM_H
+#define QGRAPHICSTRANSFORM_H
+
+#include <QtCore/QObject>
+#include <QtGui/QVector3D>
+#include <QtGui/QTransform>
+#include <QtGui/QMatrix4x4>
+
+#ifndef QT_NO_GRAPHICSVIEW
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+class QGraphicsItem;
+class QGraphicsTransformPrivate;
+
+class Q_GUI_EXPORT QGraphicsTransform : public QObject
+{
+ Q_OBJECT
+public:
+ QGraphicsTransform(QObject *parent = 0);
+ ~QGraphicsTransform();
+
+ virtual void applyTo(QMatrix4x4 *matrix) const = 0;
+
+protected Q_SLOTS:
+ void update();
+
+protected:
+ QGraphicsTransform(QGraphicsTransformPrivate &p, QObject *parent);
+
+private:
+ friend class QGraphicsItem;
+ friend class QGraphicsItemPrivate;
+ Q_DECLARE_PRIVATE(QGraphicsTransform)
+};
+
+class QGraphicsScalePrivate;
+
+class Q_GUI_EXPORT QGraphicsScale : public QGraphicsTransform
+{
+ Q_OBJECT
+
+ Q_PROPERTY(QVector3D origin READ origin WRITE setOrigin NOTIFY originChanged)
+ Q_PROPERTY(qreal xScale READ xScale WRITE setXScale NOTIFY xScaleChanged)
+ Q_PROPERTY(qreal yScale READ yScale WRITE setYScale NOTIFY yScaleChanged)
+ Q_PROPERTY(qreal zScale READ zScale WRITE setZScale NOTIFY zScaleChanged)
+public:
+ QGraphicsScale(QObject *parent = 0);
+ ~QGraphicsScale();
+
+ QVector3D origin() const;
+ void setOrigin(const QVector3D &point);
+
+ qreal xScale() const;
+ void setXScale(qreal);
+
+ qreal yScale() const;
+ void setYScale(qreal);
+
+ qreal zScale() const;
+ void setZScale(qreal);
+
+ void applyTo(QMatrix4x4 *matrix) const;
+
+Q_SIGNALS:
+ void originChanged();
+ void xScaleChanged();
+ void yScaleChanged();
+ void zScaleChanged();
+ void scaleChanged();
+
+private:
+ Q_DECLARE_PRIVATE(QGraphicsScale)
+};
+
+class QGraphicsRotationPrivate;
+
+class Q_GUI_EXPORT QGraphicsRotation : public QGraphicsTransform
+{
+ Q_OBJECT
+
+ 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();
+
+ QVector3D origin() const;
+ void setOrigin(const QVector3D &point);
+
+ qreal angle() const;
+ void setAngle(qreal);
+
+ QVector3D axis() const;
+ void setAxis(const QVector3D &axis);
+ void setAxis(Qt::Axis axis);
+
+ void applyTo(QMatrix4x4 *matrix) const;
+
+Q_SIGNALS:
+ void originChanged();
+ void angleChanged();
+ void axisChanged();
+
+private:
+ Q_DECLARE_PRIVATE(QGraphicsRotation)
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+#endif //QT_NO_GRAPHICSVIEW
+
+#endif // QFXTRANSFORM_H
diff --git a/src/widgets/graphicsview/qgraphicstransform_p.h b/src/widgets/graphicsview/qgraphicstransform_p.h
new file mode 100644
index 0000000000..2a0bf859e3
--- /dev/null
+++ b/src/widgets/graphicsview/qgraphicstransform_p.h
@@ -0,0 +1,78 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** 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 Technology Preview License Agreement accompanying
+** this package.
+**
+** 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.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QGRAPHICSTRANSFORM_P_H
+#define QGRAPHICSTRANSFORM_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of other Qt classes. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "private/qobject_p.h"
+#ifndef QT_NO_GRAPHICSVIEW
+QT_BEGIN_NAMESPACE
+
+class QGraphicsItem;
+
+class QGraphicsTransformPrivate : public QObjectPrivate {
+public:
+ Q_DECLARE_PUBLIC(QGraphicsTransform)
+
+ QGraphicsTransformPrivate()
+ : QObjectPrivate(), item(0) {}
+
+ QGraphicsItem *item;
+
+ void setItem(QGraphicsItem *item);
+ static void updateItem(QGraphicsItem *item);
+};
+
+QT_END_NAMESPACE
+#endif //QT_NO_GRAPHCISVIEW
+
+#endif // QGRAPHICSTRANSFORM_P_H
diff --git a/src/widgets/graphicsview/qgraphicsview.cpp b/src/widgets/graphicsview/qgraphicsview.cpp
new file mode 100644
index 0000000000..488a36af49
--- /dev/null
+++ b/src/widgets/graphicsview/qgraphicsview.cpp
@@ -0,0 +1,3880 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** 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 Technology Preview License Agreement accompanying
+** this package.
+**
+** 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.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+static const int QGRAPHICSVIEW_REGION_RECT_THRESHOLD = 50;
+
+static const int QGRAPHICSVIEW_PREALLOC_STYLE_OPTIONS = 503; // largest prime < 2^9
+
+/*!
+ \class QGraphicsView
+ \brief The QGraphicsView class provides a widget for displaying the
+ contents of a QGraphicsScene.
+ \since 4.2
+ \ingroup graphicsview-api
+
+
+ QGraphicsView visualizes the contents of a QGraphicsScene in a scrollable
+ viewport. To create a scene with geometrical items, see QGraphicsScene's
+ documentation. QGraphicsView is part of the \l{Graphics View Framework}.
+
+ To visualize a scene, you start by constructing a QGraphicsView object,
+ passing the address of the scene you want to visualize to QGraphicsView's
+ constructor. Alternatively, you can call setScene() to set the scene at a
+ later point. After you call show(), the view will by default scroll to the
+ center of the scene and display any items that are visible at this
+ point. For example:
+
+ \snippet doc/src/snippets/code/src_gui_graphicsview_qgraphicsview.cpp 0
+
+ You can explicitly scroll to any position on the scene by using the
+ scroll bars, or by calling centerOn(). By passing a point to centerOn(),
+ QGraphicsView will scroll its viewport to ensure that the point is
+ centered in the view. An overload is provided for scrolling to a
+ QGraphicsItem, in which case QGraphicsView will see to that the center of
+ the item is centered in the view. If all you want is to ensure that a
+ certain area is visible, (but not necessarily centered,) you can call
+ ensureVisible() instead.
+
+ QGraphicsView can be used to visualize a whole scene, or only parts of it.
+ The visualized area is by default detected automatically when the view is
+ displayed for the first time (by calling
+ QGraphicsScene::itemsBoundingRect()). To set the visualized area rectangle
+ yourself, you can call setSceneRect(). This will adjust the scroll bars'
+ ranges appropriately. Note that although the scene supports a virtually
+ unlimited size, the range of the scroll bars will never exceed the range of
+ an integer (INT_MIN, INT_MAX).
+
+ QGraphicsView visualizes the scene by calling render(). By default, the
+ items are drawn onto the viewport by using a regular QPainter, and using
+ default render hints. To change the default render hints that
+ QGraphicsView passes to QPainter when painting items, you can call
+ setRenderHints().
+
+ By default, QGraphicsView provides a regular QWidget for the viewport
+ widget. You can access this widget by calling viewport(), or you can
+ replace it by calling setViewport(). To render using OpenGL, simply call
+ setViewport(new QGLWidget). QGraphicsView takes ownership of the viewport
+ widget.
+
+ QGraphicsView supports affine transformations, using QTransform. You can
+ either pass a matrix to setTransform(), or you can call one of the
+ convenience functions rotate(), scale(), translate() or shear(). The most
+ two common transformations are scaling, which is used to implement
+ zooming, and rotation. QGraphicsView keeps the center of the view fixed
+ during a transformation. Because of the scene alignment (setAligment()),
+ translating the view will have no visual impact.
+
+ You can interact with the items on the scene by using the mouse and
+ keyboard. QGraphicsView translates the mouse and key events into \e scene
+ events, (events that inherit QGraphicsSceneEvent,), and forward them to
+ the visualized scene. In the end, it's the individual item that handles
+ the events and reacts to them. For example, if you click on a selectable
+ item, the item will typically let the scene know that it has been
+ selected, and it will also redraw itself to display a selection
+ rectangle. Similiary, if you click and drag the mouse to move a movable
+ item, it's the item that handles the mouse moves and moves itself. Item
+ interaction is enabled by default, and you can toggle it by calling
+ setInteractive().
+
+ You can also provide your own custom scene interaction, by creating a
+ subclass of QGraphicsView, and reimplementing the mouse and key event
+ handlers. To simplify how you programmatically interact with items in the
+ view, QGraphicsView provides the mapping functions mapToScene() and
+ mapFromScene(), and the item accessors items() and itemAt(). These
+ functions allow you to map points, rectangles, polygons and paths between
+ view coordinates and scene coordinates, and to find items on the scene
+ using view coordinates.
+
+ \img graphicsview-view.png
+
+ \sa QGraphicsScene, QGraphicsItem, QGraphicsSceneEvent
+*/
+
+/*!
+ \enum QGraphicsView::ViewportAnchor
+
+ This enums describe the possible anchors that QGraphicsView can
+ use when the user resizes the view or when the view is
+ transformed.
+
+ \value NoAnchor No anchor, i.e. the view leaves the scene's
+ position unchanged.
+ \value AnchorViewCenter The scene point at the center of the view
+ is used as the anchor.
+ \value AnchorUnderMouse The point under the mouse is used as the anchor.
+
+ \sa resizeAnchor, transformationAnchor
+*/
+
+/*!
+ \enum QGraphicsView::ViewportUpdateMode
+
+ \since 4.3
+
+ This enum describes how QGraphicsView updates its viewport when the scene
+ contents change or are exposed.
+
+ \value FullViewportUpdate When any visible part of the scene changes or is
+ reexposed, QGraphicsView will update the entire viewport. This approach is
+ fastest when QGraphicsView spends more time figuring out what to draw than
+ it would spend drawing (e.g., when very many small items are repeatedly
+ updated). This is the preferred update mode for viewports that do not
+ support partial updates, such as QGLWidget, and for viewports that need to
+ disable scroll optimization.
+
+ \value MinimalViewportUpdate QGraphicsView will determine the minimal
+ viewport region that requires a redraw, minimizing the time spent drawing
+ by avoiding a redraw of areas that have not changed. This is
+ QGraphicsView's default mode. Although this approach provides the best
+ performance in general, if there are many small visible changes on the
+ scene, QGraphicsView might end up spending more time finding the minimal
+ approach than it will spend drawing.
+
+ \value SmartViewportUpdate QGraphicsView will attempt to find an optimal
+ update mode by analyzing the areas that require a redraw.
+
+ \value BoundingRectViewportUpdate The bounding rectangle of all changes in
+ the viewport will be redrawn. This mode has the advantage that
+ QGraphicsView searches only one region for changes, minimizing time spent
+ determining what needs redrawing. The disadvantage is that areas that have
+ not changed also need to be redrawn.
+
+ \value NoViewportUpdate QGraphicsView will never update its viewport when
+ the scene changes; the user is expected to control all updates. This mode
+ disables all (potentially slow) item visibility testing in QGraphicsView,
+ and is suitable for scenes that either require a fixed frame rate, or where
+ the viewport is otherwise updated externally.
+
+ \sa viewportUpdateMode
+*/
+
+/*!
+ \enum QGraphicsView::OptimizationFlag
+
+ \since 4.3
+
+ This enum describes flags that you can enable to improve rendering
+ performance in QGraphicsView. By default, none of these flags are set.
+ Note that setting a flag usually imposes a side effect, and this effect
+ can vary between paint devices and platforms.
+
+ \value DontClipPainter This value is obsolete and has no effect.
+
+ \value DontSavePainterState When rendering, QGraphicsView protects the
+ painter state (see QPainter::save()) when rendering the background or
+ foreground, and when rendering each item. This allows you to leave the
+ painter in an altered state (i.e., you can call QPainter::setPen() or
+ QPainter::setBrush() without restoring the state after painting). However,
+ if the items consistently do restore the state, you should enable this
+ flag to prevent QGraphicsView from doing the same.
+
+ \value DontAdjustForAntialiasing Disables QGraphicsView's antialiasing
+ auto-adjustment of exposed areas. Items that render antialiased lines on
+ the boundaries of their QGraphicsItem::boundingRect() can end up rendering
+ parts of the line outside. To prevent rendering artifacts, QGraphicsView
+ expands all exposed regions by 2 pixels in all directions. If you enable
+ this flag, QGraphicsView will no longer perform these adjustments,
+ minimizing the areas that require redrawing, which improves performance. A
+ common side effect is that items that do draw with antialiasing can leave
+ painting traces behind on the scene as they are moved.
+
+ \value IndirectPainting Since Qt 4.6, restore the old painting algorithm
+ that calls QGraphicsView::drawItems() and QGraphicsScene::drawItems().
+ To be used only for compatibility with old code.
+*/
+
+/*!
+ \enum QGraphicsView::CacheModeFlag
+
+ This enum describes the flags that you can set for a QGraphicsView's cache
+ mode.
+
+ \value CacheNone All painting is done directly onto the viewport.
+
+ \value CacheBackground The background is cached. This affects both custom
+ backgrounds, and backgrounds based on the backgroundBrush property. When
+ this flag is enabled, QGraphicsView will allocate one pixmap with the full
+ size of the viewport.
+
+ \sa cacheMode
+*/
+
+/*!
+ \enum QGraphicsView::DragMode
+
+ This enum describes the default action for the view when pressing and
+ dragging the mouse over the viewport.
+
+ \value NoDrag Nothing happens; the mouse event is ignored.
+
+ \value ScrollHandDrag The cursor changes into a pointing hand, and
+ dragging the mouse around will scroll the scrolbars. This mode works both
+ in \l{QGraphicsView::interactive}{interactive} and non-interactive mode.
+
+ \value RubberBandDrag A rubber band will appear. Dragging the mouse will
+ set the rubber band geometry, and all items covered by the rubber band are
+ selected. This mode is disabled for non-interactive views.
+
+ \sa dragMode, QGraphicsScene::setSelectionArea()
+*/
+
+#include "qgraphicsview.h"
+#include "qgraphicsview_p.h"
+
+#ifndef QT_NO_GRAPHICSVIEW
+
+#include "qgraphicsitem.h"
+#include "qgraphicsitem_p.h"
+#include "qgraphicsscene.h"
+#include "qgraphicsscene_p.h"
+#include "qgraphicssceneevent.h"
+#include "qgraphicswidget.h"
+
+#include <QtCore/qdatetime.h>
+#include <QtCore/qdebug.h>
+#include <QtCore/qmath.h>
+#include <QtGui/qapplication.h>
+#include <QtGui/qdesktopwidget.h>
+#include <QtGui/qevent.h>
+#include <QtGui/qlayout.h>
+#include <QtGui/qtransform.h>
+#include <QtGui/qmatrix.h>
+#include <QtGui/qpainter.h>
+#include <QtGui/qscrollbar.h>
+#include <QtGui/qstyleoption.h>
+#include <QtGui/qinputcontext.h>
+#ifdef Q_WS_X11
+#include <QtGui/qpaintengine.h>
+#include <private/qt_x11_p.h>
+#endif
+
+#include <private/qevent_p.h>
+
+QT_BEGIN_NAMESPACE
+
+bool qt_sendSpontaneousEvent(QObject *receiver, QEvent *event);
+
+inline int q_round_bound(qreal d) //### (int)(qreal) INT_MAX != INT_MAX for single precision
+{
+ if (d <= (qreal) INT_MIN)
+ return INT_MIN;
+ else if (d >= (qreal) INT_MAX)
+ return INT_MAX;
+ return d >= 0.0 ? int(d + 0.5) : int(d - int(d-1) + 0.5) + int(d-1);
+}
+
+void QGraphicsViewPrivate::translateTouchEvent(QGraphicsViewPrivate *d, QTouchEvent *touchEvent)
+{
+ QList<QTouchEvent::TouchPoint> touchPoints = touchEvent->touchPoints();
+ for (int i = 0; i < touchPoints.count(); ++i) {
+ QTouchEvent::TouchPoint &touchPoint = touchPoints[i];
+ // the scene will set the item local pos, startPos, lastPos, and rect before delivering to
+ // an item, but for now those functions are returning the view's local coordinates
+ touchPoint.setSceneRect(d->mapToScene(touchPoint.rect()));
+ touchPoint.setStartScenePos(d->mapToScene(touchPoint.startPos()));
+ touchPoint.setLastScenePos(d->mapToScene(touchPoint.lastPos()));
+
+ // screenPos, startScreenPos, lastScreenPos, and screenRect are already set
+ }
+
+ touchEvent->setTouchPoints(touchPoints);
+}
+
+/*!
+ \internal
+*/
+QGraphicsViewPrivate::QGraphicsViewPrivate()
+ : renderHints(QPainter::TextAntialiasing),
+ dragMode(QGraphicsView::NoDrag),
+ sceneInteractionAllowed(true), hasSceneRect(false),
+ connectedToScene(false),
+ useLastMouseEvent(false),
+ identityMatrix(true),
+ dirtyScroll(true),
+ accelerateScrolling(true),
+ keepLastCenterPoint(true),
+ transforming(false),
+ handScrolling(false),
+ mustAllocateStyleOptions(false),
+ mustResizeBackgroundPixmap(true),
+ fullUpdatePending(true),
+ hasUpdateClip(false),
+ mousePressButton(Qt::NoButton),
+ leftIndent(0), topIndent(0),
+ lastMouseEvent(QEvent::None, QPoint(), Qt::NoButton, 0, 0),
+ alignment(Qt::AlignCenter),
+ transformationAnchor(QGraphicsView::AnchorViewCenter), resizeAnchor(QGraphicsView::NoAnchor),
+ viewportUpdateMode(QGraphicsView::MinimalViewportUpdate),
+ optimizationFlags(0),
+ scene(0),
+#ifndef QT_NO_RUBBERBAND
+ rubberBanding(false),
+ rubberBandSelectionMode(Qt::IntersectsItemShape),
+#endif
+ handScrollMotions(0), cacheMode(0),
+#ifndef QT_NO_CURSOR
+ hasStoredOriginalCursor(false),
+#endif
+ lastDragDropEvent(0),
+ updateSceneSlotReimplementedChecked(false)
+{
+ styleOptions.reserve(QGRAPHICSVIEW_PREALLOC_STYLE_OPTIONS);
+}
+
+/*!
+ \internal
+*/
+void QGraphicsViewPrivate::recalculateContentSize()
+{
+ Q_Q(QGraphicsView);
+
+ QSize maxSize = q->maximumViewportSize();
+ int width = maxSize.width();
+ int height = maxSize.height();
+ QRectF viewRect = matrix.mapRect(q->sceneRect());
+
+ bool frameOnlyAround = (q->style()->styleHint(QStyle::SH_ScrollView_FrameOnlyAroundContents, 0, q));
+ if (frameOnlyAround) {
+ if (hbarpolicy == Qt::ScrollBarAlwaysOn)
+ height -= frameWidth * 2;
+ if (vbarpolicy == Qt::ScrollBarAlwaysOn)
+ width -= frameWidth * 2;
+ }
+
+ // Adjust the maximum width and height of the viewport based on the width
+ // of visible scroll bars.
+ int scrollBarExtent = q->style()->pixelMetric(QStyle::PM_ScrollBarExtent, 0, q);
+ if (frameOnlyAround)
+ scrollBarExtent += frameWidth * 2;
+
+ bool useHorizontalScrollBar = (viewRect.width() > width) && hbarpolicy != Qt::ScrollBarAlwaysOff;
+ bool useVerticalScrollBar = (viewRect.height() > height) && vbarpolicy != Qt::ScrollBarAlwaysOff;
+ if (useHorizontalScrollBar && !useVerticalScrollBar) {
+ if (viewRect.height() > height - scrollBarExtent)
+ useVerticalScrollBar = true;
+ }
+ if (useVerticalScrollBar && !useHorizontalScrollBar) {
+ if (viewRect.width() > width - scrollBarExtent)
+ useHorizontalScrollBar = true;
+ }
+ if (useHorizontalScrollBar && hbarpolicy != Qt::ScrollBarAlwaysOn)
+ height -= scrollBarExtent;
+ if (useVerticalScrollBar && vbarpolicy != Qt::ScrollBarAlwaysOn)
+ width -= scrollBarExtent;
+
+ // Setting the ranges of these scroll bars can/will cause the values to
+ // change, and scrollContentsBy() will be called correspondingly. This
+ // will reset the last center point.
+ QPointF savedLastCenterPoint = lastCenterPoint;
+
+ // Remember the former indent settings
+ qreal oldLeftIndent = leftIndent;
+ qreal oldTopIndent = topIndent;
+
+ // If the whole scene fits horizontally, we center the scene horizontally,
+ // and ignore the horizontal scroll bars.
+ int left = q_round_bound(viewRect.left());
+ int right = q_round_bound(viewRect.right() - width);
+ if (left >= right) {
+ hbar->setRange(0, 0);
+
+ switch (alignment & Qt::AlignHorizontal_Mask) {
+ case Qt::AlignLeft:
+ leftIndent = -viewRect.left();
+ break;
+ case Qt::AlignRight:
+ leftIndent = width - viewRect.width() - viewRect.left() - 1;
+ break;
+ case Qt::AlignHCenter:
+ default:
+ leftIndent = width / 2 - (viewRect.left() + viewRect.right()) / 2;
+ break;
+ }
+ } else {
+ hbar->setRange(left, right);
+ hbar->setPageStep(width);
+ hbar->setSingleStep(width / 20);
+ leftIndent = 0;
+ }
+
+ // If the whole scene fits vertically, we center the scene vertically, and
+ // ignore the vertical scroll bars.
+ int top = q_round_bound(viewRect.top());
+ int bottom = q_round_bound(viewRect.bottom() - height);
+ if (top >= bottom) {
+ vbar->setRange(0, 0);
+
+ switch (alignment & Qt::AlignVertical_Mask) {
+ case Qt::AlignTop:
+ topIndent = -viewRect.top();
+ break;
+ case Qt::AlignBottom:
+ topIndent = height - viewRect.height() - viewRect.top() - 1;
+ break;
+ case Qt::AlignVCenter:
+ default:
+ topIndent = height / 2 - (viewRect.top() + viewRect.bottom()) / 2;
+ break;
+ }
+ } else {
+ vbar->setRange(top, bottom);
+ vbar->setPageStep(height);
+ vbar->setSingleStep(height / 20);
+ topIndent = 0;
+ }
+
+ // Restorethe center point from before the ranges changed.
+ lastCenterPoint = savedLastCenterPoint;
+
+ // Issue a full update if the indents change.
+ // ### If the transform is still the same, we can get away with just a
+ // scroll instead.
+ if (oldLeftIndent != leftIndent || oldTopIndent != topIndent) {
+ dirtyScroll = true;
+ updateAll();
+ } else if (q->isRightToLeft() && !leftIndent) {
+ // In reverse mode, the horizontal scroll always changes after the content
+ // size has changed, as the scroll is calculated by summing the min and
+ // max values of the range and subtracting the current value. In normal
+ // mode the scroll remains unchanged unless the indent has changed.
+ dirtyScroll = true;
+ }
+
+ if (cacheMode & QGraphicsView::CacheBackground) {
+ // Invalidate the background pixmap
+ mustResizeBackgroundPixmap = true;
+ }
+}
+
+/*!
+ \internal
+*/
+void QGraphicsViewPrivate::centerView(QGraphicsView::ViewportAnchor anchor)
+{
+ Q_Q(QGraphicsView);
+ switch (anchor) {
+ case QGraphicsView::AnchorUnderMouse: {
+ if (q->underMouse()) {
+ // Last scene pos: lastMouseMoveScenePoint
+ // Current mouse pos:
+ QPointF transformationDiff = q->mapToScene(viewport->rect().center())
+ - q->mapToScene(viewport->mapFromGlobal(QCursor::pos()));
+ q->centerOn(lastMouseMoveScenePoint + transformationDiff);
+ } else {
+ q->centerOn(lastCenterPoint);
+ }
+ break;
+ }
+ case QGraphicsView::AnchorViewCenter:
+ q->centerOn(lastCenterPoint);
+ break;
+ case QGraphicsView::NoAnchor:
+ break;
+ }
+}
+
+/*!
+ \internal
+*/
+void QGraphicsViewPrivate::updateLastCenterPoint()
+{
+ Q_Q(QGraphicsView);
+ lastCenterPoint = q->mapToScene(viewport->rect().center());
+}
+
+/*!
+ \internal
+
+ Returns the horizontal scroll value (the X value of the left edge of the
+ viewport).
+*/
+qint64 QGraphicsViewPrivate::horizontalScroll() const
+{
+ if (dirtyScroll)
+ const_cast<QGraphicsViewPrivate *>(this)->updateScroll();
+ return scrollX;
+}
+
+/*!
+ \internal
+
+ Returns the vertical scroll value (the X value of the top edge of the
+ viewport).
+*/
+qint64 QGraphicsViewPrivate::verticalScroll() const
+{
+ if (dirtyScroll)
+ const_cast<QGraphicsViewPrivate *>(this)->updateScroll();
+ return scrollY;
+}
+
+/*!
+ \internal
+
+ Maps the given rectangle to the scene using QTransform::mapRect()
+*/
+QRectF QGraphicsViewPrivate::mapRectToScene(const QRect &rect) const
+{
+ if (dirtyScroll)
+ const_cast<QGraphicsViewPrivate *>(this)->updateScroll();
+ QRectF scrolled = QRectF(rect.translated(scrollX, scrollY));
+ return identityMatrix ? scrolled : matrix.inverted().mapRect(scrolled);
+}
+
+
+/*!
+ \internal
+
+ Maps the given rectangle from the scene using QTransform::mapRect()
+*/
+QRectF QGraphicsViewPrivate::mapRectFromScene(const QRectF &rect) const
+{
+ if (dirtyScroll)
+ const_cast<QGraphicsViewPrivate *>(this)->updateScroll();
+ return (identityMatrix ? rect : matrix.mapRect(rect)).translated(-scrollX, -scrollY);
+}
+
+/*!
+ \internal
+*/
+void QGraphicsViewPrivate::updateScroll()
+{
+ Q_Q(QGraphicsView);
+ scrollX = qint64(-leftIndent);
+ if (q->isRightToLeft()) {
+ if (!leftIndent) {
+ scrollX += hbar->minimum();
+ scrollX += hbar->maximum();
+ scrollX -= hbar->value();
+ }
+ } else {
+ scrollX += hbar->value();
+ }
+
+ scrollY = qint64(vbar->value() - topIndent);
+
+ dirtyScroll = false;
+}
+
+/*!
+ \internal
+*/
+void QGraphicsViewPrivate::replayLastMouseEvent()
+{
+ if (!useLastMouseEvent || !scene)
+ return;
+ mouseMoveEventHandler(&lastMouseEvent);
+}
+
+/*!
+ \internal
+*/
+void QGraphicsViewPrivate::storeMouseEvent(QMouseEvent *event)
+{
+ useLastMouseEvent = true;
+ lastMouseEvent = QMouseEvent(QEvent::MouseMove, event->pos(), event->globalPos(),
+ event->button(), event->buttons(), event->modifiers());
+}
+
+void QGraphicsViewPrivate::mouseMoveEventHandler(QMouseEvent *event)
+{
+ Q_Q(QGraphicsView);
+
+ storeMouseEvent(event);
+ lastMouseEvent.setAccepted(false);
+
+ if (!sceneInteractionAllowed)
+ return;
+ if (handScrolling)
+ return;
+ if (!scene)
+ return;
+
+ QGraphicsSceneMouseEvent mouseEvent(QEvent::GraphicsSceneMouseMove);
+ mouseEvent.setWidget(viewport);
+ mouseEvent.setButtonDownScenePos(mousePressButton, mousePressScenePoint);
+ mouseEvent.setButtonDownScreenPos(mousePressButton, mousePressScreenPoint);
+ mouseEvent.setScenePos(q->mapToScene(event->pos()));
+ mouseEvent.setScreenPos(event->globalPos());
+ mouseEvent.setLastScenePos(lastMouseMoveScenePoint);
+ mouseEvent.setLastScreenPos(lastMouseMoveScreenPoint);
+ mouseEvent.setButtons(event->buttons());
+ mouseEvent.setButton(event->button());
+ mouseEvent.setModifiers(event->modifiers());
+ lastMouseMoveScenePoint = mouseEvent.scenePos();
+ lastMouseMoveScreenPoint = mouseEvent.screenPos();
+ mouseEvent.setAccepted(false);
+ if (event->spontaneous())
+ qt_sendSpontaneousEvent(scene, &mouseEvent);
+ else
+ QApplication::sendEvent(scene, &mouseEvent);
+
+ // Remember whether the last event was accepted or not.
+ lastMouseEvent.setAccepted(mouseEvent.isAccepted());
+
+ if (mouseEvent.isAccepted() && mouseEvent.buttons() != 0) {
+ // The event was delivered to a mouse grabber; the press is likely to
+ // have set a cursor, and we must not change it.
+ return;
+ }
+
+#ifndef QT_NO_CURSOR
+ // If all the items ignore hover events, we don't look-up any items
+ // in QGraphicsScenePrivate::dispatchHoverEvent, hence the
+ // cachedItemsUnderMouse list will be empty. We therefore do the look-up
+ // for cursor items here if not all items use the default cursor.
+ if (scene->d_func()->allItemsIgnoreHoverEvents && !scene->d_func()->allItemsUseDefaultCursor
+ && scene->d_func()->cachedItemsUnderMouse.isEmpty()) {
+ scene->d_func()->cachedItemsUnderMouse = scene->d_func()->itemsAtPosition(mouseEvent.screenPos(),
+ mouseEvent.scenePos(),
+ mouseEvent.widget());
+ }
+ // Find the topmost item under the mouse with a cursor.
+ foreach (QGraphicsItem *item, scene->d_func()->cachedItemsUnderMouse) {
+ if (item->hasCursor()) {
+ _q_setViewportCursor(item->cursor());
+ return;
+ }
+ }
+
+ // No items with cursors found; revert to the view cursor.
+ if (hasStoredOriginalCursor) {
+ // Restore the original viewport cursor.
+ hasStoredOriginalCursor = false;
+ viewport->setCursor(originalCursor);
+ }
+#endif
+}
+
+/*!
+ \internal
+*/
+#ifndef QT_NO_RUBBERBAND
+QRegion QGraphicsViewPrivate::rubberBandRegion(const QWidget *widget, const QRect &rect) const
+{
+ QStyleHintReturnMask mask;
+ QStyleOptionRubberBand option;
+ option.initFrom(widget);
+ option.rect = rect;
+ option.opaque = false;
+ option.shape = QRubberBand::Rectangle;
+
+ QRegion tmp;
+ tmp += rect;
+ if (widget->style()->styleHint(QStyle::SH_RubberBand_Mask, &option, widget, &mask))
+ tmp &= mask.region;
+ return tmp;
+}
+#endif
+
+/*!
+ \internal
+*/
+#ifndef QT_NO_CURSOR
+void QGraphicsViewPrivate::_q_setViewportCursor(const QCursor &cursor)
+{
+ if (!hasStoredOriginalCursor) {
+ hasStoredOriginalCursor = true;
+ originalCursor = viewport->cursor();
+ }
+ viewport->setCursor(cursor);
+}
+#endif
+
+/*!
+ \internal
+*/
+#ifndef QT_NO_CURSOR
+void QGraphicsViewPrivate::_q_unsetViewportCursor()
+{
+ Q_Q(QGraphicsView);
+ foreach (QGraphicsItem *item, q->items(lastMouseEvent.pos())) {
+ if (item->hasCursor()) {
+ _q_setViewportCursor(item->cursor());
+ return;
+ }
+ }
+
+ // Restore the original viewport cursor.
+ if (hasStoredOriginalCursor) {
+ hasStoredOriginalCursor = false;
+ if (dragMode == QGraphicsView::ScrollHandDrag)
+ viewport->setCursor(Qt::OpenHandCursor);
+ else
+ viewport->setCursor(originalCursor);
+ }
+}
+#endif
+
+/*!
+ \internal
+*/
+void QGraphicsViewPrivate::storeDragDropEvent(const QGraphicsSceneDragDropEvent *event)
+{
+ delete lastDragDropEvent;
+ lastDragDropEvent = new QGraphicsSceneDragDropEvent(event->type());
+ lastDragDropEvent->setScenePos(event->scenePos());
+ lastDragDropEvent->setScreenPos(event->screenPos());
+ lastDragDropEvent->setButtons(event->buttons());
+ lastDragDropEvent->setModifiers(event->modifiers());
+ lastDragDropEvent->setPossibleActions(event->possibleActions());
+ lastDragDropEvent->setProposedAction(event->proposedAction());
+ lastDragDropEvent->setDropAction(event->dropAction());
+ lastDragDropEvent->setMimeData(event->mimeData());
+ lastDragDropEvent->setWidget(event->widget());
+ lastDragDropEvent->setSource(event->source());
+}
+
+/*!
+ \internal
+*/
+void QGraphicsViewPrivate::populateSceneDragDropEvent(QGraphicsSceneDragDropEvent *dest,
+ QDropEvent *source)
+{
+#ifndef QT_NO_DRAGANDDROP
+ Q_Q(QGraphicsView);
+ dest->setScenePos(q->mapToScene(source->pos()));
+ dest->setScreenPos(q->mapToGlobal(source->pos()));
+ dest->setButtons(source->mouseButtons());
+ dest->setModifiers(source->keyboardModifiers());
+ dest->setPossibleActions(source->possibleActions());
+ dest->setProposedAction(source->proposedAction());
+ dest->setDropAction(source->dropAction());
+ dest->setMimeData(source->mimeData());
+ dest->setWidget(viewport);
+ dest->setSource(source->source());
+#else
+ Q_UNUSED(dest)
+ Q_UNUSED(source)
+#endif
+}
+
+/*!
+ \internal
+*/
+QRect QGraphicsViewPrivate::mapToViewRect(const QGraphicsItem *item, const QRectF &rect) const
+{
+ Q_Q(const QGraphicsView);
+ if (dirtyScroll)
+ const_cast<QGraphicsViewPrivate *>(this)->updateScroll();
+
+ if (item->d_ptr->itemIsUntransformable()) {
+ QTransform itv = item->deviceTransform(q->viewportTransform());
+ return itv.mapRect(rect).toAlignedRect();
+ }
+
+ // Translate-only
+ // COMBINE
+ QPointF offset;
+ const QGraphicsItem *parentItem = item;
+ const QGraphicsItemPrivate *itemd;
+ do {
+ itemd = parentItem->d_ptr.data();
+ if (itemd->transformData)
+ break;
+ offset += itemd->pos;
+ } while ((parentItem = itemd->parent));
+
+ QRectF baseRect = rect.translated(offset.x(), offset.y());
+ if (!parentItem) {
+ if (identityMatrix) {
+ baseRect.translate(-scrollX, -scrollY);
+ return baseRect.toAlignedRect();
+ }
+ return matrix.mapRect(baseRect).translated(-scrollX, -scrollY).toAlignedRect();
+ }
+
+ QTransform tr = parentItem->sceneTransform();
+ if (!identityMatrix)
+ tr *= matrix;
+ QRectF r = tr.mapRect(baseRect);
+ r.translate(-scrollX, -scrollY);
+ return r.toAlignedRect();
+}
+
+/*!
+ \internal
+*/
+QRegion QGraphicsViewPrivate::mapToViewRegion(const QGraphicsItem *item, const QRectF &rect) const
+{
+ Q_Q(const QGraphicsView);
+ if (dirtyScroll)
+ const_cast<QGraphicsViewPrivate *>(this)->updateScroll();
+
+ // Accurate bounding region
+ QTransform itv = item->deviceTransform(q->viewportTransform());
+ return item->boundingRegion(itv) & itv.mapRect(rect).toAlignedRect();
+}
+
+/*!
+ \internal
+*/
+void QGraphicsViewPrivate::processPendingUpdates()
+{
+ if (!scene)
+ return;
+
+ if (fullUpdatePending) {
+ viewport->update();
+ } else if (viewportUpdateMode == QGraphicsView::BoundingRectViewportUpdate) {
+ viewport->update(dirtyBoundingRect);
+ } else {
+ viewport->update(dirtyRegion); // Already adjusted in updateRect/Region.
+ }
+
+ dirtyBoundingRect = QRect();
+ dirtyRegion = QRegion();
+}
+
+static inline bool intersectsViewport(const QRect &r, int width, int height)
+{ return !(r.left() > width) && !(r.right() < 0) && !(r.top() >= height) && !(r.bottom() < 0); }
+
+static inline bool containsViewport(const QRect &r, int width, int height)
+{ return r.left() <= 0 && r.top() <= 0 && r.right() >= width - 1 && r.bottom() >= height - 1; }
+
+static inline void QRect_unite(QRect *rect, const QRect &other)
+{
+ if (rect->isEmpty()) {
+ *rect = other;
+ } else {
+ rect->setCoords(qMin(rect->left(), other.left()), qMin(rect->top(), other.top()),
+ qMax(rect->right(), other.right()), qMax(rect->bottom(), other.bottom()));
+ }
+}
+
+/*
+ Calling this function results in update rects being clipped to the item's
+ bounding rect. Note that updates prior to this function call is not clipped.
+ The clip is removed by passing 0.
+*/
+void QGraphicsViewPrivate::setUpdateClip(QGraphicsItem *item)
+{
+ Q_Q(QGraphicsView);
+ // We simply ignore the request if the update mode is either FullViewportUpdate
+ // or NoViewportUpdate; in that case there's no point in clipping anything.
+ if (!item || viewportUpdateMode == QGraphicsView::NoViewportUpdate
+ || viewportUpdateMode == QGraphicsView::FullViewportUpdate) {
+ hasUpdateClip = false;
+ return;
+ }
+
+ // Calculate the clip (item's bounding rect in view coordinates).
+ // Optimized version of:
+ // QRect clip = item->deviceTransform(q->viewportTransform())
+ // .mapRect(item->boundingRect()).toAlignedRect();
+ QRect clip;
+ if (item->d_ptr->itemIsUntransformable()) {
+ QTransform xform = item->deviceTransform(q->viewportTransform());
+ clip = xform.mapRect(item->boundingRect()).toAlignedRect();
+ } else if (item->d_ptr->sceneTransformTranslateOnly && identityMatrix) {
+ QRectF r(item->boundingRect());
+ r.translate(item->d_ptr->sceneTransform.dx() - horizontalScroll(),
+ item->d_ptr->sceneTransform.dy() - verticalScroll());
+ clip = r.toAlignedRect();
+ } else if (!q->isTransformed()) {
+ clip = item->d_ptr->sceneTransform.mapRect(item->boundingRect()).toAlignedRect();
+ } else {
+ QTransform xform = item->d_ptr->sceneTransform;
+ xform *= q->viewportTransform();
+ clip = xform.mapRect(item->boundingRect()).toAlignedRect();
+ }
+
+ if (hasUpdateClip) {
+ // Intersect with old clip.
+ updateClip &= clip;
+ } else {
+ updateClip = clip;
+ hasUpdateClip = true;
+ }
+}
+
+bool QGraphicsViewPrivate::updateRegion(const QRectF &rect, const QTransform &xform)
+{
+ if (rect.isEmpty())
+ return false;
+
+ if (viewportUpdateMode != QGraphicsView::MinimalViewportUpdate
+ && viewportUpdateMode != QGraphicsView::SmartViewportUpdate) {
+ // No point in updating with QRegion granularity; use the rect instead.
+ return updateRectF(xform.mapRect(rect));
+ }
+
+ // Update mode is either Minimal or Smart, so we have to do a potentially slow operation,
+ // which is clearly documented here: QGraphicsItem::setBoundingRegionGranularity.
+ const QRegion region = xform.map(QRegion(rect.toAlignedRect()));
+ QRect viewRect = region.boundingRect();
+ const bool dontAdjustForAntialiasing = optimizationFlags & QGraphicsView::DontAdjustForAntialiasing;
+ if (dontAdjustForAntialiasing)
+ viewRect.adjust(-1, -1, 1, 1);
+ else
+ viewRect.adjust(-2, -2, 2, 2);
+ if (!intersectsViewport(viewRect, viewport->width(), viewport->height()))
+ return false; // Update region for sure outside viewport.
+
+ const QVector<QRect> &rects = region.rects();
+ for (int i = 0; i < rects.size(); ++i) {
+ viewRect = rects.at(i);
+ if (dontAdjustForAntialiasing)
+ viewRect.adjust(-1, -1, 1, 1);
+ else
+ viewRect.adjust(-2, -2, 2, 2);
+ if (hasUpdateClip)
+ viewRect &= updateClip;
+ dirtyRegion += viewRect;
+ }
+
+ return true;
+}
+
+// NB! Assumes the rect 'r' is already aligned and adjusted for antialiasing.
+// For QRectF use updateRectF(const QRectF &) to ensure proper adjustments.
+bool QGraphicsViewPrivate::updateRect(const QRect &r)
+{
+ if (fullUpdatePending || viewportUpdateMode == QGraphicsView::NoViewportUpdate
+ || !intersectsViewport(r, viewport->width(), viewport->height())) {
+ return false;
+ }
+
+ switch (viewportUpdateMode) {
+ case QGraphicsView::FullViewportUpdate:
+ fullUpdatePending = true;
+ viewport->update();
+ break;
+ case QGraphicsView::BoundingRectViewportUpdate:
+ if (hasUpdateClip)
+ QRect_unite(&dirtyBoundingRect, r & updateClip);
+ else
+ QRect_unite(&dirtyBoundingRect, r);
+ if (containsViewport(dirtyBoundingRect, viewport->width(), viewport->height())) {
+ fullUpdatePending = true;
+ viewport->update();
+ }
+ break;
+ case QGraphicsView::SmartViewportUpdate: // ### DEPRECATE
+ case QGraphicsView::MinimalViewportUpdate:
+ if (hasUpdateClip)
+ dirtyRegion += r & updateClip;
+ else
+ dirtyRegion += r;
+ break;
+ default:
+ break;
+ }
+
+ return true;
+}
+
+QStyleOptionGraphicsItem *QGraphicsViewPrivate::allocStyleOptionsArray(int numItems)
+{
+ if (mustAllocateStyleOptions || (numItems > styleOptions.capacity()))
+ // too many items, let's allocate on-the-fly
+ return new QStyleOptionGraphicsItem[numItems];
+
+ // expand only whenever necessary
+ if (numItems > styleOptions.size())
+ styleOptions.resize(numItems);
+
+ mustAllocateStyleOptions = true;
+ return styleOptions.data();
+}
+
+void QGraphicsViewPrivate::freeStyleOptionsArray(QStyleOptionGraphicsItem *array)
+{
+ mustAllocateStyleOptions = false;
+ if (array != styleOptions.data())
+ delete [] array;
+}
+
+extern QPainterPath qt_regionToPath(const QRegion &region);
+
+/*!
+ ### Adjustments in findItems: mapToScene(QRect) forces us to adjust the
+ input rectangle by (0, 0, 1, 1), because it uses QRect::bottomRight()
+ (etc) when mapping the rectangle to a polygon (which is _wrong_). In
+ addition, as QGraphicsItem::boundingRect() is defined in logical space,
+ but the default pen for QPainter is cosmetic with a width of 0, QPainter
+ is at risk of painting 1 pixel outside the bounding rect. Therefore we
+ must search for items with an adjustment of (-1, -1, 1, 1).
+*/
+QList<QGraphicsItem *> QGraphicsViewPrivate::findItems(const QRegion &exposedRegion, bool *allItems,
+ const QTransform &viewTransform) const
+{
+ Q_Q(const QGraphicsView);
+
+ // Step 1) If all items are contained within the expose region, then
+ // return a list of all visible items. ### the scene's growing bounding
+ // rect does not take into account untransformable items.
+ const QRectF exposedRegionSceneBounds = q->mapToScene(exposedRegion.boundingRect().adjusted(-1, -1, 1, 1))
+ .boundingRect();
+ if (exposedRegionSceneBounds.contains(scene->sceneRect())) {
+ Q_ASSERT(allItems);
+ *allItems = true;
+
+ // All items are guaranteed within the exposed region.
+ return scene->items(Qt::AscendingOrder);
+ }
+
+ // Step 2) If the expose region is a simple rect and the view is only
+ // translated or scaled, search for items using
+ // QGraphicsScene::items(QRectF).
+ bool simpleRectLookup = exposedRegion.rectCount() == 1 && matrix.type() <= QTransform::TxScale;
+ if (simpleRectLookup) {
+ return scene->items(exposedRegionSceneBounds,
+ Qt::IntersectsItemBoundingRect,
+ Qt::AscendingOrder, viewTransform);
+ }
+
+ // If the region is complex or the view has a complex transform, adjust
+ // the expose region, convert it to a path, and then search for items
+ // using QGraphicsScene::items(QPainterPath);
+ QRegion adjustedRegion;
+ foreach (const QRect &r, exposedRegion.rects())
+ adjustedRegion += r.adjusted(-1, -1, 1, 1);
+
+ const QPainterPath exposedScenePath(q->mapToScene(qt_regionToPath(adjustedRegion)));
+ return scene->items(exposedScenePath, Qt::IntersectsItemBoundingRect,
+ Qt::AscendingOrder, viewTransform);
+}
+
+/*!
+ \internal
+
+ Enables input methods for the view if and only if the current focus item of
+ the scene accepts input methods. Call function whenever that condition has
+ potentially changed.
+*/
+void QGraphicsViewPrivate::updateInputMethodSensitivity()
+{
+ Q_Q(QGraphicsView);
+ QGraphicsItem *focusItem = 0;
+ bool enabled = scene && (focusItem = scene->focusItem())
+ && (focusItem->d_ptr->flags & QGraphicsItem::ItemAcceptsInputMethod);
+ q->setAttribute(Qt::WA_InputMethodEnabled, enabled);
+ q->viewport()->setAttribute(Qt::WA_InputMethodEnabled, enabled);
+
+ if (!enabled) {
+ q->setInputMethodHints(0);
+ return;
+ }
+
+ QGraphicsProxyWidget *proxy = focusItem->d_ptr->isWidget && focusItem->d_ptr->isProxyWidget()
+ ? static_cast<QGraphicsProxyWidget *>(focusItem) : 0;
+ if (!proxy) {
+ q->setInputMethodHints(focusItem->inputMethodHints());
+ } else if (QWidget *widget = proxy->widget()) {
+ if (QWidget *fw = widget->focusWidget())
+ widget = fw;
+ q->setInputMethodHints(widget->inputMethodHints());
+ } else {
+ q->setInputMethodHints(0);
+ }
+}
+
+/*!
+ Constructs a QGraphicsView. \a parent is passed to QWidget's constructor.
+*/
+QGraphicsView::QGraphicsView(QWidget *parent)
+ : QAbstractScrollArea(*new QGraphicsViewPrivate, parent)
+{
+ setViewport(0);
+ setAcceptDrops(true);
+ setBackgroundRole(QPalette::Base);
+ // Investigate leaving these disabled by default.
+ setAttribute(Qt::WA_InputMethodEnabled);
+ viewport()->setAttribute(Qt::WA_InputMethodEnabled);
+}
+
+/*!
+ Constructs a QGraphicsView and sets the visualized scene to \a
+ scene. \a parent is passed to QWidget's constructor.
+*/
+QGraphicsView::QGraphicsView(QGraphicsScene *scene, QWidget *parent)
+ : QAbstractScrollArea(*new QGraphicsViewPrivate, parent)
+{
+ setScene(scene);
+ setViewport(0);
+ setAcceptDrops(true);
+ setBackgroundRole(QPalette::Base);
+ // Investigate leaving these disabled by default.
+ setAttribute(Qt::WA_InputMethodEnabled);
+ viewport()->setAttribute(Qt::WA_InputMethodEnabled);
+}
+
+/*!
+ \internal
+ */
+QGraphicsView::QGraphicsView(QGraphicsViewPrivate &dd, QWidget *parent)
+ : QAbstractScrollArea(dd, parent)
+{
+ setViewport(0);
+ setAcceptDrops(true);
+ setBackgroundRole(QPalette::Base);
+ // Investigate leaving these disabled by default.
+ setAttribute(Qt::WA_InputMethodEnabled);
+ viewport()->setAttribute(Qt::WA_InputMethodEnabled);
+}
+
+/*!
+ Destructs the QGraphicsView object.
+*/
+QGraphicsView::~QGraphicsView()
+{
+ Q_D(QGraphicsView);
+ if (d->scene)
+ d->scene->d_func()->views.removeAll(this);
+ delete d->lastDragDropEvent;
+}
+
+/*!
+ \reimp
+*/
+QSize QGraphicsView::sizeHint() const
+{
+ Q_D(const QGraphicsView);
+ if (d->scene) {
+ QSizeF baseSize = d->matrix.mapRect(sceneRect()).size();
+ baseSize += QSizeF(d->frameWidth * 2, d->frameWidth * 2);
+ return baseSize.boundedTo((3 * QApplication::desktop()->size()) / 4).toSize();
+ }
+ return QAbstractScrollArea::sizeHint();
+}
+
+/*!
+ \property QGraphicsView::renderHints
+ \brief the default render hints for the view
+
+ These hints are
+ used to initialize QPainter before each visible item is drawn. QPainter
+ uses render hints to toggle rendering features such as antialiasing and
+ smooth pixmap transformation.
+
+ QPainter::TextAntialiasing is enabled by default.
+
+ Example:
+
+ \snippet doc/src/snippets/code/src_gui_graphicsview_qgraphicsview.cpp 1
+*/
+QPainter::RenderHints QGraphicsView::renderHints() const
+{
+ Q_D(const QGraphicsView);
+ return d->renderHints;
+}
+void QGraphicsView::setRenderHints(QPainter::RenderHints hints)
+{
+ Q_D(QGraphicsView);
+ if (hints == d->renderHints)
+ return;
+ d->renderHints = hints;
+ d->updateAll();
+}
+
+/*!
+ If \a enabled is true, the render hint \a hint is enabled; otherwise it
+ is disabled.
+
+ \sa renderHints
+*/
+void QGraphicsView::setRenderHint(QPainter::RenderHint hint, bool enabled)
+{
+ Q_D(QGraphicsView);
+ QPainter::RenderHints oldHints = d->renderHints;
+ if (enabled)
+ d->renderHints |= hint;
+ else
+ d->renderHints &= ~hint;
+ if (oldHints != d->renderHints)
+ d->updateAll();
+}
+
+/*!
+ \property QGraphicsView::alignment
+ \brief the alignment of the scene in the view when the whole
+ scene is visible.
+
+ If the whole scene is visible in the view, (i.e., there are no visible
+ scroll bars,) the view's alignment will decide where the scene will be
+ rendered in the view. For example, if the alignment is Qt::AlignCenter,
+ which is default, the scene will be centered in the view, and if the
+ alignment is (Qt::AlignLeft | Qt::AlignTop), the scene will be rendered in
+ the top-left corner of the view.
+*/
+Qt::Alignment QGraphicsView::alignment() const
+{
+ Q_D(const QGraphicsView);
+ return d->alignment;
+}
+void QGraphicsView::setAlignment(Qt::Alignment alignment)
+{
+ Q_D(QGraphicsView);
+ if (d->alignment != alignment) {
+ d->alignment = alignment;
+ d->recalculateContentSize();
+ }
+}
+
+/*!
+ \property QGraphicsView::transformationAnchor
+ \brief how the view should position the scene during transformations.
+
+ QGraphicsView uses this property to decide how to position the scene in
+ the viewport when the transformation matrix changes, and the coordinate
+ system of the view is transformed. The default behavior, AnchorViewCenter,
+ ensures that the scene point at the center of the view remains unchanged
+ during transformations (e.g., when rotating, the scene will appear to
+ rotate around the center of the view).
+
+ Note that the effect of this property is noticeable when only a part of the
+ scene is visible (i.e., when there are scroll bars). Otherwise, if the
+ whole scene fits in the view, QGraphicsScene uses the view \l alignment to
+ position the scene in the view.
+
+ \sa alignment, resizeAnchor
+*/
+QGraphicsView::ViewportAnchor QGraphicsView::transformationAnchor() const
+{
+ Q_D(const QGraphicsView);
+ return d->transformationAnchor;
+}
+void QGraphicsView::setTransformationAnchor(ViewportAnchor anchor)
+{
+ Q_D(QGraphicsView);
+ d->transformationAnchor = anchor;
+
+ // Ensure mouse tracking is enabled in the case we are using AnchorUnderMouse
+ // in order to have up-to-date information for centering the view.
+ if (d->transformationAnchor == AnchorUnderMouse)
+ d->viewport->setMouseTracking(true);
+}
+
+/*!
+ \property QGraphicsView::resizeAnchor
+ \brief how the view should position the scene when the view is resized.
+
+ QGraphicsView uses this property to decide how to position the scene in
+ the viewport when the viewport widget's size changes. The default
+ behavior, NoAnchor, leaves the scene's position unchanged during a resize;
+ the top-left corner of the view will appear to be anchored while resizing.
+
+ Note that the effect of this property is noticeable when only a part of the
+ scene is visible (i.e., when there are scroll bars). Otherwise, if the
+ whole scene fits in the view, QGraphicsScene uses the view \l alignment to
+ position the scene in the view.
+
+ \sa alignment, transformationAnchor, Qt::WNorthWestGravity
+*/
+QGraphicsView::ViewportAnchor QGraphicsView::resizeAnchor() const
+{
+ Q_D(const QGraphicsView);
+ return d->resizeAnchor;
+}
+void QGraphicsView::setResizeAnchor(ViewportAnchor anchor)
+{
+ Q_D(QGraphicsView);
+ d->resizeAnchor = anchor;
+
+ // Ensure mouse tracking is enabled in the case we are using AnchorUnderMouse
+ // in order to have up-to-date information for centering the view.
+ if (d->resizeAnchor == AnchorUnderMouse)
+ d->viewport->setMouseTracking(true);
+}
+
+/*!
+ \property QGraphicsView::viewportUpdateMode
+ \brief how the viewport should update its contents.
+
+ \since 4.3
+
+ QGraphicsView uses this property to decide how to update areas of the
+ scene that have been reexposed or changed. Usually you do not need to
+ modify this property, but there are some cases where doing so can improve
+ rendering performance. See the ViewportUpdateMode documentation for
+ specific details.
+
+ The default value is MinimalViewportUpdate, where QGraphicsView will
+ update as small an area of the viewport as possible when the contents
+ change.
+
+ \sa ViewportUpdateMode, cacheMode
+*/
+QGraphicsView::ViewportUpdateMode QGraphicsView::viewportUpdateMode() const
+{
+ Q_D(const QGraphicsView);
+ return d->viewportUpdateMode;
+}
+void QGraphicsView::setViewportUpdateMode(ViewportUpdateMode mode)
+{
+ Q_D(QGraphicsView);
+ d->viewportUpdateMode = mode;
+}
+
+/*!
+ \property QGraphicsView::optimizationFlags
+ \brief flags that can be used to tune QGraphicsView's performance.
+
+ \since 4.3
+
+ QGraphicsView uses clipping, extra bounding rect adjustments, and certain
+ other aids to improve rendering quality and performance for the common
+ case graphics scene. However, depending on the target platform, the scene,
+ and the viewport in use, some of these operations can degrade performance.
+
+ The effect varies from flag to flag; see the OptimizationFlags
+ documentation for details.
+
+ By default, no optimization flags are enabled.
+
+ \sa setOptimizationFlag()
+*/
+QGraphicsView::OptimizationFlags QGraphicsView::optimizationFlags() const
+{
+ Q_D(const QGraphicsView);
+ return d->optimizationFlags;
+}
+void QGraphicsView::setOptimizationFlags(OptimizationFlags flags)
+{
+ Q_D(QGraphicsView);
+ d->optimizationFlags = flags;
+}
+
+/*!
+ Enables \a flag if \a enabled is true; otherwise disables \a flag.
+
+ \sa optimizationFlags
+*/
+void QGraphicsView::setOptimizationFlag(OptimizationFlag flag, bool enabled)
+{
+ Q_D(QGraphicsView);
+ if (enabled)
+ d->optimizationFlags |= flag;
+ else
+ d->optimizationFlags &= ~flag;
+}
+
+/*!
+ \property QGraphicsView::dragMode
+ \brief the behavior for dragging the mouse over the scene while
+ the left mouse button is pressed.
+
+ This property defines what should happen when the user clicks on the scene
+ background and drags the mouse (e.g., scrolling the viewport contents
+ using a pointing hand cursor, or selecting multiple items with a rubber
+ band). The default value, NoDrag, does nothing.
+
+ This behavior only affects mouse clicks that are not handled by any item.
+ You can define a custom behavior by creating a subclass of QGraphicsView
+ and reimplementing mouseMoveEvent().
+*/
+QGraphicsView::DragMode QGraphicsView::dragMode() const
+{
+ Q_D(const QGraphicsView);
+ return d->dragMode;
+}
+void QGraphicsView::setDragMode(DragMode mode)
+{
+ Q_D(QGraphicsView);
+ if (d->dragMode == mode)
+ return;
+
+#ifndef QT_NO_CURSOR
+ if (d->dragMode == ScrollHandDrag)
+ viewport()->unsetCursor();
+#endif
+
+ // If dragMode is unset while dragging, e.g. via a keyEvent, we
+ // don't unset the handScrolling state. When enabling scrolling
+ // again the mouseMoveEvent will automatically start scrolling,
+ // without a mousePress
+ if (d->dragMode == ScrollHandDrag && mode == NoDrag && d->handScrolling)
+ d->handScrolling = false;
+
+ d->dragMode = mode;
+
+#ifndef QT_NO_CURSOR
+ if (d->dragMode == ScrollHandDrag) {
+ // Forget the stored viewport cursor when we enter scroll hand drag mode.
+ d->hasStoredOriginalCursor = false;
+ viewport()->setCursor(Qt::OpenHandCursor);
+ }
+#endif
+}
+
+#ifndef QT_NO_RUBBERBAND
+/*!
+ \property QGraphicsView::rubberBandSelectionMode
+ \brief the behavior for selecting items with a rubber band selection rectangle.
+ \since 4.3
+
+ This property defines how items are selected when using the RubberBandDrag
+ drag mode.
+
+ The default value is Qt::IntersectsItemShape; all items whose shape
+ intersects with or is contained by the rubber band are selected.
+
+ \sa dragMode, items()
+*/
+Qt::ItemSelectionMode QGraphicsView::rubberBandSelectionMode() const
+{
+ Q_D(const QGraphicsView);
+ return d->rubberBandSelectionMode;
+}
+void QGraphicsView::setRubberBandSelectionMode(Qt::ItemSelectionMode mode)
+{
+ Q_D(QGraphicsView);
+ d->rubberBandSelectionMode = mode;
+}
+#endif
+
+/*!
+ \property QGraphicsView::cacheMode
+ \brief which parts of the view are cached
+
+ QGraphicsView can cache pre-rendered content in a QPixmap, which is then
+ drawn onto the viewport. The purpose of such caching is to speed up the
+ total rendering time for areas that are slow to render. Texture, gradient
+ and alpha blended backgrounds, for example, can be notibly slow to render;
+ especially with a transformed view. The CacheBackground flag enables
+ caching of the view's background. For example:
+
+ \snippet doc/src/snippets/code/src_gui_graphicsview_qgraphicsview.cpp 2
+
+ The cache is invalidated every time the view is transformed. However, when
+ scrolling, only partial invalidation is required.
+
+ By default, nothing is cached.
+
+ \sa resetCachedContent(), QPixmapCache
+*/
+QGraphicsView::CacheMode QGraphicsView::cacheMode() const
+{
+ Q_D(const QGraphicsView);
+ return d->cacheMode;
+}
+void QGraphicsView::setCacheMode(CacheMode mode)
+{
+ Q_D(QGraphicsView);
+ if (mode == d->cacheMode)
+ return;
+ d->cacheMode = mode;
+ resetCachedContent();
+}
+
+/*!
+ Resets any cached content. Calling this function will clear
+ QGraphicsView's cache. If the current cache mode is \l CacheNone, this
+ function does nothing.
+
+ This function is called automatically for you when the backgroundBrush or
+ QGraphicsScene::backgroundBrush properties change; you only need to call
+ this function if you have reimplemented QGraphicsScene::drawBackground()
+ or QGraphicsView::drawBackground() to draw a custom background, and need
+ to trigger a full redraw.
+
+ \sa cacheMode()
+*/
+void QGraphicsView::resetCachedContent()
+{
+ Q_D(QGraphicsView);
+ if (d->cacheMode == CacheNone)
+ return;
+
+ if (d->cacheMode & CacheBackground) {
+ // Background caching is enabled.
+ d->mustResizeBackgroundPixmap = true;
+ d->updateAll();
+ } else if (d->mustResizeBackgroundPixmap) {
+ // Background caching is disabled.
+ // Cleanup, free some resources.
+ d->mustResizeBackgroundPixmap = false;
+ d->backgroundPixmap = QPixmap();
+ d->backgroundPixmapExposed = QRegion();
+ }
+}
+
+/*!
+ Invalidates and schedules a redraw of \a layers inside \a rect. \a rect is
+ in scene coordinates. Any cached content for \a layers inside \a rect is
+ unconditionally invalidated and redrawn.
+
+ You can call this function to notify QGraphicsView of changes to the
+ background or the foreground of the scene. It is commonly used for scenes
+ with tile-based backgrounds to notify changes when QGraphicsView has
+ enabled background caching.
+
+ Note that QGraphicsView currently supports background caching only (see
+ QGraphicsView::CacheBackground). This function is equivalent to calling update() if any
+ layer but QGraphicsScene::BackgroundLayer is passed.
+
+ \sa QGraphicsScene::invalidate(), update()
+*/
+void QGraphicsView::invalidateScene(const QRectF &rect, QGraphicsScene::SceneLayers layers)
+{
+ Q_D(QGraphicsView);
+ if ((layers & QGraphicsScene::BackgroundLayer) && !d->mustResizeBackgroundPixmap) {
+ QRect viewRect = mapFromScene(rect).boundingRect();
+ if (viewport()->rect().intersects(viewRect)) {
+ // The updated background area is exposed; schedule this area for
+ // redrawing.
+ d->backgroundPixmapExposed += viewRect;
+ if (d->scene)
+ d->scene->update(rect);
+ }
+ }
+}
+
+/*!
+ \property QGraphicsView::interactive
+ \brief whether the view allowed scene interaction.
+
+ If enabled, this view is set to allow scene interaction. Otherwise, this
+ view will not allow interaction, and any mouse or key events are ignored
+ (i.e., it will act as a read-only view).
+
+ By default, this property is true.
+*/
+bool QGraphicsView::isInteractive() const
+{
+ Q_D(const QGraphicsView);
+ return d->sceneInteractionAllowed;
+}
+void QGraphicsView::setInteractive(bool allowed)
+{
+ Q_D(QGraphicsView);
+ d->sceneInteractionAllowed = allowed;
+}
+
+/*!
+ Returns a pointer to the scene that is currently visualized in the
+ view. If no scene is currently visualized, 0 is returned.
+
+ \sa setScene()
+*/
+QGraphicsScene *QGraphicsView::scene() const
+{
+ Q_D(const QGraphicsView);
+ return d->scene;
+}
+
+/*!
+ Sets the current scene to \a scene. If \a scene is already being
+ viewed, this function does nothing.
+
+ When a scene is set on a view, the QGraphicsScene::changed() signal
+ is automatically connected to this view's updateScene() slot, and the
+ view's scroll bars are adjusted to fit the size of the scene.
+*/
+void QGraphicsView::setScene(QGraphicsScene *scene)
+{
+ Q_D(QGraphicsView);
+ if (d->scene == scene)
+ return;
+
+ // Always update the viewport when the scene changes.
+ d->updateAll();
+
+ // Remove the previously assigned scene.
+ if (d->scene) {
+ disconnect(d->scene, SIGNAL(changed(QList<QRectF>)),
+ this, SLOT(updateScene(QList<QRectF>)));
+ disconnect(d->scene, SIGNAL(sceneRectChanged(QRectF)),
+ this, SLOT(updateSceneRect(QRectF)));
+ d->scene->d_func()->removeView(this);
+ d->connectedToScene = false;
+
+ if (isActiveWindow() && isVisible()) {
+ QEvent windowDeactivate(QEvent::WindowDeactivate);
+ QApplication::sendEvent(d->scene, &windowDeactivate);
+ }
+ if(hasFocus())
+ d->scene->clearFocus();
+ }
+
+ // Assign the new scene and update the contents (scrollbars, etc.)).
+ if ((d->scene = scene)) {
+ connect(d->scene, SIGNAL(sceneRectChanged(QRectF)),
+ this, SLOT(updateSceneRect(QRectF)));
+ d->updateSceneSlotReimplementedChecked = false;
+ d->scene->d_func()->addView(this);
+ d->recalculateContentSize();
+ d->lastCenterPoint = sceneRect().center();
+ d->keepLastCenterPoint = true;
+ // We are only interested in mouse tracking if items accept
+ // hover events or use non-default cursors.
+ if (!d->scene->d_func()->allItemsIgnoreHoverEvents
+ || !d->scene->d_func()->allItemsUseDefaultCursor) {
+ d->viewport->setMouseTracking(true);
+ }
+
+ // enable touch events if any items is interested in them
+ if (!d->scene->d_func()->allItemsIgnoreTouchEvents)
+ d->viewport->setAttribute(Qt::WA_AcceptTouchEvents);
+
+ if (isActiveWindow() && isVisible()) {
+ QEvent windowActivate(QEvent::WindowActivate);
+ QApplication::sendEvent(d->scene, &windowActivate);
+ }
+ } else {
+ d->recalculateContentSize();
+ }
+
+ d->updateInputMethodSensitivity();
+
+ if (d->scene && hasFocus())
+ d->scene->setFocus();
+}
+
+/*!
+ \property QGraphicsView::sceneRect
+ \brief the area of the scene visualized by this view.
+
+ The scene rectangle defines the extent of the scene, and in the view's case,
+ this means the area of the scene that you can navigate using the scroll
+ bars.
+
+ If unset, or if a null QRectF is set, this property has the same value as
+ QGraphicsScene::sceneRect, and it changes with
+ QGraphicsScene::sceneRect. Otherwise, the view's scene rect is unaffected
+ by the scene.
+
+ Note that, although the scene supports a virtually unlimited size, the
+ range of the scroll bars will never exceed the range of an integer
+ (INT_MIN, INT_MAX). When the scene is larger than the scroll bars' values,
+ you can choose to use translate() to navigate the scene instead.
+
+ By default, this property contains a rectangle at the origin with zero
+ width and height.
+
+ \sa QGraphicsScene::sceneRect
+*/
+QRectF QGraphicsView::sceneRect() const
+{
+ Q_D(const QGraphicsView);
+ if (d->hasSceneRect)
+ return d->sceneRect;
+ if (d->scene)
+ return d->scene->sceneRect();
+ return QRectF();
+}
+void QGraphicsView::setSceneRect(const QRectF &rect)
+{
+ Q_D(QGraphicsView);
+ d->hasSceneRect = !rect.isNull();
+ d->sceneRect = rect;
+ d->recalculateContentSize();
+}
+
+/*!
+ Returns the current transformation matrix for the view. If no current
+ transformation is set, the identity matrix is returned.
+
+ \sa setMatrix(), transform(), rotate(), scale(), shear(), translate()
+*/
+QMatrix QGraphicsView::matrix() const
+{
+ Q_D(const QGraphicsView);
+ return d->matrix.toAffine();
+}
+
+/*!
+ Sets the view's current transformation matrix to \a matrix.
+
+ If \a combine is true, then \a matrix is combined with the current matrix;
+ otherwise, \a matrix \e replaces the current matrix. \a combine is false
+ by default.
+
+ The transformation matrix tranforms the scene into view coordinates. Using
+ the default transformation, provided by the identity matrix, one pixel in
+ the view represents one unit in the scene (e.g., a 10x10 rectangular item
+ is drawn using 10x10 pixels in the view). If a 2x2 scaling matrix is
+ applied, the scene will be drawn in 1:2 (e.g., a 10x10 rectangular item is
+ then drawn using 20x20 pixels in the view).
+
+ Example:
+
+ \snippet doc/src/snippets/code/src_gui_graphicsview_qgraphicsview.cpp 3
+
+ To simplify interation with items using a transformed view, QGraphicsView
+ provides mapTo... and mapFrom... functions that can translate between
+ scene and view coordinates. For example, you can call mapToScene() to map
+ a view coordinate to a floating point scene coordinate, or mapFromScene()
+ to map from floating point scene coordinates to view coordinates.
+
+ \sa matrix(), setTransform(), rotate(), scale(), shear(), translate()
+*/
+void QGraphicsView::setMatrix(const QMatrix &matrix, bool combine)
+{
+ setTransform(QTransform(matrix), combine);
+}
+
+/*!
+ Resets the view transformation matrix to the identity matrix.
+
+ \sa resetTransform()
+*/
+void QGraphicsView::resetMatrix()
+{
+ resetTransform();
+}
+
+/*!
+ Rotates the current view transformation \a angle degrees clockwise.
+
+ \sa setTransform(), transform(), scale(), shear(), translate()
+*/
+void QGraphicsView::rotate(qreal angle)
+{
+ Q_D(QGraphicsView);
+ QTransform matrix = d->matrix;
+ matrix.rotate(angle);
+ setTransform(matrix);
+}
+
+/*!
+ Scales the current view transformation by (\a sx, \a sy).
+
+ \sa setTransform(), transform(), rotate(), shear(), translate()
+*/
+void QGraphicsView::scale(qreal sx, qreal sy)
+{
+ Q_D(QGraphicsView);
+ QTransform matrix = d->matrix;
+ matrix.scale(sx, sy);
+ setTransform(matrix);
+}
+
+/*!
+ Shears the current view transformation by (\a sh, \a sv).
+
+ \sa setTransform(), transform(), rotate(), scale(), translate()
+*/
+void QGraphicsView::shear(qreal sh, qreal sv)
+{
+ Q_D(QGraphicsView);
+ QTransform matrix = d->matrix;
+ matrix.shear(sh, sv);
+ setTransform(matrix);
+}
+
+/*!
+ Translates the current view transformation by (\a dx, \a dy).
+
+ \sa setTransform(), transform(), rotate(), shear()
+*/
+void QGraphicsView::translate(qreal dx, qreal dy)
+{
+ Q_D(QGraphicsView);
+ QTransform matrix = d->matrix;
+ matrix.translate(dx, dy);
+ setTransform(matrix);
+}
+
+/*!
+ Scrolls the contents of the viewport to ensure that the scene
+ coordinate \a pos, is centered in the view.
+
+ Because \a pos is a floating point coordinate, and the scroll bars operate
+ on integer coordinates, the centering is only an approximation.
+
+ \note If the item is close to or outside the border, it will be visible
+ in the view, but not centered.
+
+ \sa ensureVisible()
+*/
+void QGraphicsView::centerOn(const QPointF &pos)
+{
+ Q_D(QGraphicsView);
+ qreal width = viewport()->width();
+ qreal height = viewport()->height();
+ QPointF viewPoint = d->matrix.map(pos);
+ QPointF oldCenterPoint = pos;
+
+ if (!d->leftIndent) {
+ if (isRightToLeft()) {
+ qint64 horizontal = 0;
+ horizontal += horizontalScrollBar()->minimum();
+ horizontal += horizontalScrollBar()->maximum();
+ horizontal -= int(viewPoint.x() - width / 2.0);
+ horizontalScrollBar()->setValue(horizontal);
+ } else {
+ horizontalScrollBar()->setValue(int(viewPoint.x() - width / 2.0));
+ }
+ }
+ if (!d->topIndent)
+ verticalScrollBar()->setValue(int(viewPoint.y() - height / 2.0));
+ d->lastCenterPoint = oldCenterPoint;
+}
+
+/*!
+ \fn QGraphicsView::centerOn(qreal x, qreal y)
+ \overload
+
+ This function is provided for convenience. It's equivalent to calling
+ centerOn(QPointF(\a x, \a y)).
+*/
+
+/*!
+ \overload
+
+ Scrolls the contents of the viewport to ensure that \a item
+ is centered in the view.
+
+ \sa ensureVisible()
+*/
+void QGraphicsView::centerOn(const QGraphicsItem *item)
+{
+ centerOn(item->sceneBoundingRect().center());
+}
+
+/*!
+ Scrolls the contents of the viewport so that the scene rectangle \a rect
+ is visible, with margins specified in pixels by \a xmargin and \a
+ ymargin. If the specified rect cannot be reached, the contents are
+ scrolled to the nearest valid position. The default value for both margins
+ is 50 pixels.
+
+ \sa centerOn()
+*/
+void QGraphicsView::ensureVisible(const QRectF &rect, int xmargin, int ymargin)
+{
+ Q_D(QGraphicsView);
+ qreal width = viewport()->width();
+ qreal height = viewport()->height();
+ QRectF viewRect = d->matrix.mapRect(rect);
+
+ qreal left = d->horizontalScroll();
+ qreal right = left + width;
+ qreal top = d->verticalScroll();
+ qreal bottom = top + height;
+
+ if (viewRect.left() <= left + xmargin) {
+ // need to scroll from the left
+ if (!d->leftIndent)
+ horizontalScrollBar()->setValue(int(viewRect.left() - xmargin - 0.5));
+ }
+ if (viewRect.right() >= right - xmargin) {
+ // need to scroll from the right
+ if (!d->leftIndent)
+ horizontalScrollBar()->setValue(int(viewRect.right() - width + xmargin + 0.5));
+ }
+ if (viewRect.top() <= top + ymargin) {
+ // need to scroll from the top
+ if (!d->topIndent)
+ verticalScrollBar()->setValue(int(viewRect.top() - ymargin - 0.5));
+ }
+ if (viewRect.bottom() >= bottom - ymargin) {
+ // need to scroll from the bottom
+ if (!d->topIndent)
+ verticalScrollBar()->setValue(int(viewRect.bottom() - height + ymargin + 0.5));
+ }
+}
+
+/*!
+ \fn QGraphicsView::ensureVisible(qreal x, qreal y, qreal w, qreal h,
+ int xmargin, int ymargin)
+ \overload
+
+ This function is provided for convenience. It's equivalent to calling
+ ensureVisible(QRectF(\a x, \a y, \a w, \a h), \a xmargin, \a ymargin).
+*/
+
+/*!
+ \overload
+
+ Scrolls the contents of the viewport so that the center of item \a item is
+ visible, with margins specified in pixels by \a xmargin and \a ymargin. If
+ the specified point cannot be reached, the contents are scrolled to the
+ nearest valid position. The default value for both margins is 50 pixels.
+
+ \sa centerOn()
+*/
+void QGraphicsView::ensureVisible(const QGraphicsItem *item, int xmargin, int ymargin)
+{
+ ensureVisible(item->sceneBoundingRect(), xmargin, ymargin);
+}
+
+/*!
+ Scales the view matrix and scrolls the scroll bars to ensure that the
+ scene rectangle \a rect fits inside the viewport. \a rect must be inside
+ the scene rect; otherwise, fitInView() cannot guarantee that the whole
+ rect is visible.
+
+ This function keeps the view's rotation, translation, or shear. The view
+ is scaled according to \a aspectRatioMode. \a rect will be centered in the
+ view if it does not fit tightly.
+
+ It's common to call fitInView() from inside a reimplementation of
+ resizeEvent(), to ensure that the whole scene, or parts of the scene,
+ scales automatically to fit the new size of the viewport as the view is
+ resized. Note though, that calling fitInView() from inside resizeEvent()
+ can lead to unwanted resize recursion, if the new transformation toggles
+ the automatic state of the scrollbars. You can toggle the scrollbar
+ policies to always on or always off to prevent this (see
+ horizontalScrollBarPolicy() and verticalScrollBarPolicy()).
+
+ If \a rect is empty, or if the viewport is too small, this
+ function will do nothing.
+
+ \sa setTransform(), ensureVisible(), centerOn()
+*/
+void QGraphicsView::fitInView(const QRectF &rect, Qt::AspectRatioMode aspectRatioMode)
+{
+ Q_D(QGraphicsView);
+ if (!d->scene || rect.isNull())
+ return;
+
+ // Reset the view scale to 1:1.
+ QRectF unity = d->matrix.mapRect(QRectF(0, 0, 1, 1));
+ if (unity.isEmpty())
+ return;
+ scale(1 / unity.width(), 1 / unity.height());
+
+ // Find the ideal x / y scaling ratio to fit \a rect in the view.
+ int margin = 2;
+ QRectF viewRect = viewport()->rect().adjusted(margin, margin, -margin, -margin);
+ if (viewRect.isEmpty())
+ return;
+ QRectF sceneRect = d->matrix.mapRect(rect);
+ if (sceneRect.isEmpty())
+ return;
+ qreal xratio = viewRect.width() / sceneRect.width();
+ qreal yratio = viewRect.height() / sceneRect.height();
+
+ // Respect the aspect ratio mode.
+ switch (aspectRatioMode) {
+ case Qt::KeepAspectRatio:
+ xratio = yratio = qMin(xratio, yratio);
+ break;
+ case Qt::KeepAspectRatioByExpanding:
+ xratio = yratio = qMax(xratio, yratio);
+ break;
+ case Qt::IgnoreAspectRatio:
+ break;
+ }
+
+ // Scale and center on the center of \a rect.
+ scale(xratio, yratio);
+ centerOn(rect.center());
+}
+
+/*!
+ \fn void QGraphicsView::fitInView(qreal x, qreal y, qreal w, qreal h,
+ Qt::AspectRatioMode aspectRatioMode = Qt::IgnoreAspectRatio)
+
+ \overload
+
+ This convenience function is equivalent to calling
+ fitInView(QRectF(\a x, \a y, \a w, \a h), \a aspectRatioMode).
+
+ \sa ensureVisible(), centerOn()
+*/
+
+/*!
+ \overload
+
+ Ensures that \a item fits tightly inside the view, scaling the view
+ according to \a aspectRatioMode.
+
+ \sa ensureVisible(), centerOn()
+*/
+void QGraphicsView::fitInView(const QGraphicsItem *item, Qt::AspectRatioMode aspectRatioMode)
+{
+ QPainterPath path = item->isClipped() ? item->clipPath() : item->shape();
+ if (item->d_ptr->hasTranslateOnlySceneTransform()) {
+ path.translate(item->d_ptr->sceneTransform.dx(), item->d_ptr->sceneTransform.dy());
+ fitInView(path.boundingRect(), aspectRatioMode);
+ } else {
+ fitInView(item->d_ptr->sceneTransform.map(path).boundingRect(), aspectRatioMode);
+ }
+}
+
+/*!
+ Renders the \a source rect, which is in view coordinates, from the scene
+ into \a target, which is in paint device coordinates, using \a
+ painter. This function is useful for capturing the contents of the view
+ onto a paint device, such as a QImage (e.g., to take a screenshot), or for
+ printing to QPrinter. For example:
+
+ \snippet doc/src/snippets/code/src_gui_graphicsview_qgraphicsview.cpp 4
+
+ If \a source is a null rect, this function will use viewport()->rect() to
+ determine what to draw. If \a target is a null rect, the full dimensions
+ of \a painter's paint device (e.g., for a QPrinter, the page size) will be
+ used.
+
+ The source rect contents will be transformed according to \a
+ aspectRatioMode to fit into the target rect. By default, the aspect ratio
+ is kept, and \a source is scaled to fit in \a target.
+
+ \sa QGraphicsScene::render()
+*/
+void QGraphicsView::render(QPainter *painter, const QRectF &target, const QRect &source,
+ Qt::AspectRatioMode aspectRatioMode)
+{
+ // ### Switch to using the recursive rendering algorithm instead.
+
+ Q_D(QGraphicsView);
+ if (!d->scene || !(painter && painter->isActive()))
+ return;
+
+ // Default source rect = viewport rect
+ QRect sourceRect = source;
+ if (source.isNull())
+ sourceRect = viewport()->rect();
+
+ // Default target rect = device rect
+ QRectF targetRect = target;
+ if (target.isNull()) {
+ if (painter->device()->devType() == QInternal::Picture)
+ targetRect = sourceRect;
+ else
+ targetRect.setRect(0, 0, painter->device()->width(), painter->device()->height());
+ }
+
+ // Find the ideal x / y scaling ratio to fit \a source into \a target.
+ qreal xratio = targetRect.width() / sourceRect.width();
+ qreal yratio = targetRect.height() / sourceRect.height();
+
+ // Scale according to the aspect ratio mode.
+ switch (aspectRatioMode) {
+ case Qt::KeepAspectRatio:
+ xratio = yratio = qMin(xratio, yratio);
+ break;
+ case Qt::KeepAspectRatioByExpanding:
+ xratio = yratio = qMax(xratio, yratio);
+ break;
+ case Qt::IgnoreAspectRatio:
+ break;
+ }
+
+ // Find all items to draw, and reverse the list (we want to draw
+ // in reverse order).
+ QPolygonF sourceScenePoly = mapToScene(sourceRect.adjusted(-1, -1, 1, 1));
+ QList<QGraphicsItem *> itemList = d->scene->items(sourceScenePoly,
+ Qt::IntersectsItemBoundingRect);
+ QGraphicsItem **itemArray = new QGraphicsItem *[itemList.size()];
+ int numItems = itemList.size();
+ for (int i = 0; i < numItems; ++i)
+ itemArray[numItems - i - 1] = itemList.at(i);
+ itemList.clear();
+
+ // Setup painter matrix.
+ QTransform moveMatrix = QTransform::fromTranslate(-d->horizontalScroll(), -d->verticalScroll());
+ QTransform painterMatrix = d->matrix * moveMatrix;
+ painterMatrix *= QTransform()
+ .translate(targetRect.left(), targetRect.top())
+ .scale(xratio, yratio)
+ .translate(-sourceRect.left(), -sourceRect.top());
+
+ // Generate the style options
+ QStyleOptionGraphicsItem *styleOptionArray = d->allocStyleOptionsArray(numItems);
+ for (int i = 0; i < numItems; ++i)
+ itemArray[i]->d_ptr->initStyleOption(&styleOptionArray[i], painterMatrix, targetRect.toRect());
+
+ painter->save();
+
+ // Clip in device coordinates to avoid QRegion transformations.
+ painter->setClipRect(targetRect);
+ QPainterPath path;
+ path.addPolygon(sourceScenePoly);
+ path.closeSubpath();
+ painter->setClipPath(painterMatrix.map(path), Qt::IntersectClip);
+
+ // Transform the painter.
+ painter->setTransform(painterMatrix, true);
+
+ // Render the scene.
+ QRectF sourceSceneRect = sourceScenePoly.boundingRect();
+ drawBackground(painter, sourceSceneRect);
+ drawItems(painter, numItems, itemArray, styleOptionArray);
+ drawForeground(painter, sourceSceneRect);
+
+ delete [] itemArray;
+ d->freeStyleOptionsArray(styleOptionArray);
+
+ painter->restore();
+}
+
+/*!
+ Returns a list of all the items in the associated scene, in descending
+ stacking order (i.e., the first item in the returned list is the uppermost
+ item).
+
+ \sa QGraphicsScene::items(), {QGraphicsItem#Sorting}{Sorting}
+*/
+QList<QGraphicsItem *> QGraphicsView::items() const
+{
+ Q_D(const QGraphicsView);
+ if (!d->scene)
+ return QList<QGraphicsItem *>();
+ return d->scene->items();
+}
+
+/*!
+ Returns a list of all the items at the position \a pos in the view. The
+ items are listed in descending stacking order (i.e., the first item in the
+ list is the uppermost item, and the last item is the lowermost item). \a
+ pos is in viewport coordinates.
+
+ This function is most commonly called from within mouse event handlers in
+ a subclass in QGraphicsView. \a pos is in untransformed viewport
+ coordinates, just like QMouseEvent::pos().
+
+ \snippet doc/src/snippets/code/src_gui_graphicsview_qgraphicsview.cpp 5
+
+ \sa QGraphicsScene::items(), {QGraphicsItem#Sorting}{Sorting}
+*/
+QList<QGraphicsItem *> QGraphicsView::items(const QPoint &pos) const
+{
+ Q_D(const QGraphicsView);
+ if (!d->scene)
+ return QList<QGraphicsItem *>();
+ // ### Unify these two, and use the items(QPointF) version in
+ // QGraphicsScene instead. The scene items function could use the viewport
+ // transform to map the point to a rect/polygon.
+ if ((d->identityMatrix || d->matrix.type() <= QTransform::TxScale)) {
+ // Use the rect version
+ QTransform xinv = viewportTransform().inverted();
+ return d->scene->items(xinv.mapRect(QRectF(pos.x(), pos.y(), 1, 1)),
+ Qt::IntersectsItemShape,
+ Qt::DescendingOrder,
+ viewportTransform());
+ }
+ // Use the polygon version
+ return d->scene->items(mapToScene(pos.x(), pos.y(), 1, 1),
+ Qt::IntersectsItemShape,
+ Qt::DescendingOrder,
+ viewportTransform());
+}
+
+/*!
+ \fn QGraphicsView::items(int x, int y) const
+
+ This function is provided for convenience. It's equivalent to calling
+ items(QPoint(\a x, \a y)).
+*/
+
+/*!
+ \overload
+
+ Returns a list of all the items that, depending on \a mode, are either
+ contained by or intersect with \a rect. \a rect is in viewport
+ coordinates.
+
+ The default value for \a mode is Qt::IntersectsItemShape; all items whose
+ exact shape intersects with or is contained by \a rect are returned.
+
+ The items are sorted in descending stacking order (i.e., the first item in
+ the returned list is the uppermost item).
+
+ \sa itemAt(), items(), mapToScene(), {QGraphicsItem#Sorting}{Sorting}
+*/
+QList<QGraphicsItem *> QGraphicsView::items(const QRect &rect, Qt::ItemSelectionMode mode) const
+{
+ Q_D(const QGraphicsView);
+ if (!d->scene)
+ return QList<QGraphicsItem *>();
+ return d->scene->items(mapToScene(rect), mode, Qt::DescendingOrder, viewportTransform());
+}
+
+/*!
+ \fn QList<QGraphicsItem *> QGraphicsView::items(int x, int y, int w, int h, Qt::ItemSelectionMode mode) const
+ \since 4.3
+
+ This convenience function is equivalent to calling items(QRectF(\a x, \a
+ y, \a w, \a h), \a mode).
+*/
+
+/*!
+ \overload
+
+ Returns a list of all the items that, depending on \a mode, are either
+ contained by or intersect with \a polygon. \a polygon is in viewport
+ coordinates.
+
+ The default value for \a mode is Qt::IntersectsItemShape; all items whose
+ exact shape intersects with or is contained by \a polygon are returned.
+
+ The items are sorted by descending stacking order (i.e., the first item in
+ the returned list is the uppermost item).
+
+ \sa itemAt(), items(), mapToScene(), {QGraphicsItem#Sorting}{Sorting}
+*/
+QList<QGraphicsItem *> QGraphicsView::items(const QPolygon &polygon, Qt::ItemSelectionMode mode) const
+{
+ Q_D(const QGraphicsView);
+ if (!d->scene)
+ return QList<QGraphicsItem *>();
+ return d->scene->items(mapToScene(polygon), mode, Qt::DescendingOrder, viewportTransform());
+}
+
+/*!
+ \overload
+
+ Returns a list of all the items that, depending on \a mode, are either
+ contained by or intersect with \a path. \a path is in viewport
+ coordinates.
+
+ The default value for \a mode is Qt::IntersectsItemShape; all items whose
+ exact shape intersects with or is contained by \a path are returned.
+
+ \sa itemAt(), items(), mapToScene(), {QGraphicsItem#Sorting}{Sorting}
+*/
+QList<QGraphicsItem *> QGraphicsView::items(const QPainterPath &path, Qt::ItemSelectionMode mode) const
+{
+ Q_D(const QGraphicsView);
+ if (!d->scene)
+ return QList<QGraphicsItem *>();
+ return d->scene->items(mapToScene(path), mode, Qt::DescendingOrder, viewportTransform());
+}
+
+/*!
+ Returns the item at position \a pos, which is in viewport coordinates.
+ If there are several items at this position, this function returns
+ the topmost item.
+
+ Example:
+
+ \snippet doc/src/snippets/code/src_gui_graphicsview_qgraphicsview.cpp 6
+
+ \sa items(), {QGraphicsItem#Sorting}{Sorting}
+*/
+QGraphicsItem *QGraphicsView::itemAt(const QPoint &pos) const
+{
+ Q_D(const QGraphicsView);
+ if (!d->scene)
+ return 0;
+ QList<QGraphicsItem *> itemsAtPos = items(pos);
+ return itemsAtPos.isEmpty() ? 0 : itemsAtPos.first();
+}
+
+/*!
+ \overload
+ \fn QGraphicsItem *QGraphicsView::itemAt(int x, int y) const
+
+ This function is provided for convenience. It's equivalent to
+ calling itemAt(QPoint(\a x, \a y)).
+*/
+
+/*!
+ Returns the viewport coordinate \a point mapped to scene coordinates.
+
+ Note: It can be useful to map the whole rectangle covered by the pixel at
+ \a point instead of the point itself. To do this, you can call
+ mapToScene(QRect(\a point, QSize(2, 2))).
+
+ \sa mapFromScene()
+*/
+QPointF QGraphicsView::mapToScene(const QPoint &point) const
+{
+ Q_D(const QGraphicsView);
+ QPointF p = point;
+ p.rx() += d->horizontalScroll();
+ p.ry() += d->verticalScroll();
+ return d->identityMatrix ? p : d->matrix.inverted().map(p);
+}
+
+/*!
+ \fn QGraphicsView::mapToScene(int x, int y) const
+
+ This function is provided for convenience. It's equivalent to calling
+ mapToScene(QPoint(\a x, \a y)).
+*/
+
+/*!
+ Returns the viewport rectangle \a rect mapped to a scene coordinate
+ polygon.
+
+ \sa mapFromScene()
+*/
+QPolygonF QGraphicsView::mapToScene(const QRect &rect) const
+{
+ Q_D(const QGraphicsView);
+ if (!rect.isValid())
+ return QPolygonF();
+
+ QPointF scrollOffset(d->horizontalScroll(), d->verticalScroll());
+ QRect r = rect.adjusted(0, 0, 1, 1);
+ QPointF tl = scrollOffset + r.topLeft();
+ QPointF tr = scrollOffset + r.topRight();
+ QPointF br = scrollOffset + r.bottomRight();
+ QPointF bl = scrollOffset + r.bottomLeft();
+
+ QPolygonF poly(4);
+ if (!d->identityMatrix) {
+ QTransform x = d->matrix.inverted();
+ poly[0] = x.map(tl);
+ poly[1] = x.map(tr);
+ poly[2] = x.map(br);
+ poly[3] = x.map(bl);
+ } else {
+ poly[0] = tl;
+ poly[1] = tr;
+ poly[2] = br;
+ poly[3] = bl;
+ }
+ return poly;
+}
+
+/*!
+ \fn QGraphicsView::mapToScene(int x, int y, int w, int h) const
+
+ This function is provided for convenience. It's equivalent to calling
+ mapToScene(QRect(\a x, \a y, \a w, \a h)).
+*/
+
+/*!
+ Returns the viewport polygon \a polygon mapped to a scene coordinate
+ polygon.
+
+ \sa mapFromScene()
+*/
+QPolygonF QGraphicsView::mapToScene(const QPolygon &polygon) const
+{
+ QPolygonF poly;
+ foreach (const QPoint &point, polygon)
+ poly << mapToScene(point);
+ return poly;
+}
+
+/*!
+ Returns the viewport painter path \a path mapped to a scene coordinate
+ painter path.
+
+ \sa mapFromScene()
+*/
+QPainterPath QGraphicsView::mapToScene(const QPainterPath &path) const
+{
+ Q_D(const QGraphicsView);
+ QTransform matrix = QTransform::fromTranslate(d->horizontalScroll(), d->verticalScroll());
+ matrix *= d->matrix.inverted();
+ return matrix.map(path);
+}
+
+/*!
+ Returns the scene coordinate \a point to viewport coordinates.
+
+ \sa mapToScene()
+*/
+QPoint QGraphicsView::mapFromScene(const QPointF &point) const
+{
+ Q_D(const QGraphicsView);
+ QPointF p = d->identityMatrix ? point : d->matrix.map(point);
+ p.rx() -= d->horizontalScroll();
+ p.ry() -= d->verticalScroll();
+ return p.toPoint();
+}
+
+/*!
+ \fn QGraphicsView::mapFromScene(qreal x, qreal y) const
+
+ This function is provided for convenience. It's equivalent to
+ calling mapFromScene(QPointF(\a x, \a y)).
+*/
+
+/*!
+ Returns the scene rectangle \a rect to a viewport coordinate
+ polygon.
+
+ \sa mapToScene()
+*/
+QPolygon QGraphicsView::mapFromScene(const QRectF &rect) const
+{
+ Q_D(const QGraphicsView);
+ QPointF tl;
+ QPointF tr;
+ QPointF br;
+ QPointF bl;
+ if (!d->identityMatrix) {
+ const QTransform &x = d->matrix;
+ tl = x.map(rect.topLeft());
+ tr = x.map(rect.topRight());
+ br = x.map(rect.bottomRight());
+ bl = x.map(rect.bottomLeft());
+ } else {
+ tl = rect.topLeft();
+ tr = rect.topRight();
+ br = rect.bottomRight();
+ bl = rect.bottomLeft();
+ }
+ QPointF scrollOffset(d->horizontalScroll(), d->verticalScroll());
+ tl -= scrollOffset;
+ tr -= scrollOffset;
+ br -= scrollOffset;
+ bl -= scrollOffset;
+
+ QPolygon poly(4);
+ poly[0] = tl.toPoint();
+ poly[1] = tr.toPoint();
+ poly[2] = br.toPoint();
+ poly[3] = bl.toPoint();
+ return poly;
+}
+
+/*!
+ \fn QGraphicsView::mapFromScene(qreal x, qreal y, qreal w, qreal h) const
+
+ This function is provided for convenience. It's equivalent to
+ calling mapFromScene(QRectF(\a x, \a y, \a w, \a h)).
+*/
+
+/*!
+ Returns the scene coordinate polygon \a polygon to a viewport coordinate
+ polygon.
+
+ \sa mapToScene()
+*/
+QPolygon QGraphicsView::mapFromScene(const QPolygonF &polygon) const
+{
+ QPolygon poly;
+ foreach (const QPointF &point, polygon)
+ poly << mapFromScene(point);
+ return poly;
+}
+
+/*!
+ Returns the scene coordinate painter path \a path to a viewport coordinate
+ painter path.
+
+ \sa mapToScene()
+*/
+QPainterPath QGraphicsView::mapFromScene(const QPainterPath &path) const
+{
+ Q_D(const QGraphicsView);
+ QTransform matrix = d->matrix;
+ matrix *= QTransform::fromTranslate(-d->horizontalScroll(), -d->verticalScroll());
+ return matrix.map(path);
+}
+
+/*!
+ \reimp
+*/
+QVariant QGraphicsView::inputMethodQuery(Qt::InputMethodQuery query) const
+{
+ Q_D(const QGraphicsView);
+ if (!d->scene)
+ return QVariant();
+
+ QVariant value = d->scene->inputMethodQuery(query);
+ if (value.type() == QVariant::RectF)
+ value = d->mapRectFromScene(value.toRectF());
+ else if (value.type() == QVariant::PointF)
+ value = mapFromScene(value.toPointF());
+ else if (value.type() == QVariant::Rect)
+ value = d->mapRectFromScene(value.toRect()).toRect();
+ else if (value.type() == QVariant::Point)
+ value = mapFromScene(value.toPoint());
+ return value;
+}
+
+/*!
+ \property QGraphicsView::backgroundBrush
+ \brief the background brush of the scene.
+
+ This property sets the background brush for the scene in this view. It is
+ used to override the scene's own background, and defines the behavior of
+ drawBackground(). To provide custom background drawing for this view, you
+ can reimplement drawBackground() instead.
+
+ By default, this property contains a brush with the Qt::NoBrush pattern.
+
+ \sa QGraphicsScene::backgroundBrush, foregroundBrush
+*/
+QBrush QGraphicsView::backgroundBrush() const
+{
+ Q_D(const QGraphicsView);
+ return d->backgroundBrush;
+}
+void QGraphicsView::setBackgroundBrush(const QBrush &brush)
+{
+ Q_D(QGraphicsView);
+ d->backgroundBrush = brush;
+ d->updateAll();
+
+ if (d->cacheMode & CacheBackground) {
+ // Invalidate the background pixmap
+ d->mustResizeBackgroundPixmap = true;
+ }
+}
+
+/*!
+ \property QGraphicsView::foregroundBrush
+ \brief the foreground brush of the scene.
+
+ This property sets the foreground brush for the scene in this view. It is
+ used to override the scene's own foreground, and defines the behavior of
+ drawForeground(). To provide custom foreground drawing for this view, you
+ can reimplement drawForeground() instead.
+
+ By default, this property contains a brush with the Qt::NoBrush pattern.
+
+ \sa QGraphicsScene::foregroundBrush, backgroundBrush
+*/
+QBrush QGraphicsView::foregroundBrush() const
+{
+ Q_D(const QGraphicsView);
+ return d->foregroundBrush;
+}
+void QGraphicsView::setForegroundBrush(const QBrush &brush)
+{
+ Q_D(QGraphicsView);
+ d->foregroundBrush = brush;
+ d->updateAll();
+}
+
+/*!
+ Schedules an update of the scene rectangles \a rects.
+
+ \sa QGraphicsScene::changed()
+*/
+void QGraphicsView::updateScene(const QList<QRectF> &rects)
+{
+ // ### Note: Since 4.5, this slot is only called if the user explicitly
+ // establishes a connection between the scene and the view, as the scene
+ // and view are no longer connected. We need to keep it working (basically
+ // leave it as it is), but the new delivery path is through
+ // QGraphicsScenePrivate::itemUpdate().
+ Q_D(QGraphicsView);
+ if (d->fullUpdatePending || d->viewportUpdateMode == QGraphicsView::NoViewportUpdate)
+ return;
+
+ // Extract and reset dirty scene rect info.
+ QVector<QRect> dirtyViewportRects;
+ const QVector<QRect> &dirtyRects = d->dirtyRegion.rects();
+ for (int i = 0; i < dirtyRects.size(); ++i)
+ dirtyViewportRects += dirtyRects.at(i);
+ d->dirtyRegion = QRegion();
+ d->dirtyBoundingRect = QRect();
+
+ bool fullUpdate = !d->accelerateScrolling || d->viewportUpdateMode == QGraphicsView::FullViewportUpdate;
+ bool boundingRectUpdate = (d->viewportUpdateMode == QGraphicsView::BoundingRectViewportUpdate)
+ || (d->viewportUpdateMode == QGraphicsView::SmartViewportUpdate
+ && ((dirtyViewportRects.size() + rects.size()) >= QGRAPHICSVIEW_REGION_RECT_THRESHOLD));
+
+ QRegion updateRegion;
+ QRect boundingRect;
+ QRect viewportRect = viewport()->rect();
+ bool redraw = false;
+ QTransform transform = viewportTransform();
+
+ // Convert scene rects to viewport rects.
+ foreach (const QRectF &rect, rects) {
+ QRect xrect = transform.mapRect(rect).toAlignedRect();
+ if (!(d->optimizationFlags & DontAdjustForAntialiasing))
+ xrect.adjust(-2, -2, 2, 2);
+ else
+ xrect.adjust(-1, -1, 1, 1);
+ if (!viewportRect.intersects(xrect))
+ continue;
+ dirtyViewportRects << xrect;
+ }
+
+ foreach (const QRect &rect, dirtyViewportRects) {
+ // Add the exposed rect to the update region. In rect update
+ // mode, we only count the bounding rect of items.
+ if (!boundingRectUpdate) {
+ updateRegion += rect;
+ } else {
+ boundingRect |= rect;
+ }
+ redraw = true;
+ if (fullUpdate) {
+ // If fullUpdate is true and we found a visible dirty rect,
+ // we're done.
+ break;
+ }
+ }
+
+ if (!redraw)
+ return;
+
+ if (fullUpdate)
+ viewport()->update();
+ else if (boundingRectUpdate)
+ viewport()->update(boundingRect);
+ else
+ viewport()->update(updateRegion);
+}
+
+/*!
+ Notifies QGraphicsView that the scene's scene rect has changed. \a rect
+ is the new scene rect. If the view already has an explicitly set scene
+ rect, this function does nothing.
+
+ \sa sceneRect, QGraphicsScene::sceneRectChanged()
+*/
+void QGraphicsView::updateSceneRect(const QRectF &rect)
+{
+ Q_D(QGraphicsView);
+ if (!d->hasSceneRect) {
+ d->sceneRect = rect;
+ d->recalculateContentSize();
+ }
+}
+
+/*!
+ This slot is called by QAbstractScrollArea after setViewport() has been
+ called. Reimplement this function in a subclass of QGraphicsView to
+ initialize the new viewport \a widget before it is used.
+
+ \sa setViewport()
+*/
+void QGraphicsView::setupViewport(QWidget *widget)
+{
+ Q_D(QGraphicsView);
+
+ if (!widget) {
+ qWarning("QGraphicsView::setupViewport: cannot initialize null widget");
+ return;
+ }
+
+ const bool isGLWidget = widget->inherits("QGLWidget");
+
+ d->accelerateScrolling = !(isGLWidget);
+
+ widget->setFocusPolicy(Qt::StrongFocus);
+
+ if (!isGLWidget) {
+ // autoFillBackground enables scroll acceleration.
+ widget->setAutoFillBackground(true);
+ }
+
+ // We are only interested in mouse tracking if items
+ // accept hover events or use non-default cursors or if
+ // AnchorUnderMouse is used as transformation or resize anchor.
+ if ((d->scene && (!d->scene->d_func()->allItemsIgnoreHoverEvents
+ || !d->scene->d_func()->allItemsUseDefaultCursor))
+ || d->transformationAnchor == AnchorUnderMouse
+ || d->resizeAnchor == AnchorUnderMouse) {
+ widget->setMouseTracking(true);
+ }
+
+ // enable touch events if any items is interested in them
+ if (d->scene && !d->scene->d_func()->allItemsIgnoreTouchEvents)
+ widget->setAttribute(Qt::WA_AcceptTouchEvents);
+
+#ifndef QT_NO_GESTURES
+ if (d->scene) {
+ foreach (Qt::GestureType gesture, d->scene->d_func()->grabbedGestures.keys())
+ widget->grabGesture(gesture);
+ }
+#endif
+
+ widget->setAcceptDrops(acceptDrops());
+}
+
+/*!
+ \reimp
+*/
+bool QGraphicsView::event(QEvent *event)
+{
+ Q_D(QGraphicsView);
+
+ if (d->sceneInteractionAllowed) {
+ switch (event->type()) {
+ case QEvent::ShortcutOverride:
+ if (d->scene)
+ return QApplication::sendEvent(d->scene, event);
+ break;
+ case QEvent::KeyPress:
+ if (d->scene) {
+ QKeyEvent *k = static_cast<QKeyEvent *>(event);
+ if (k->key() == Qt::Key_Tab || k->key() == Qt::Key_Backtab) {
+ // Send the key events to the scene. This will invoke the
+ // scene's tab focus handling, and if the event is
+ // accepted, we return (prevent further event delivery),
+ // and the base implementation will call QGraphicsView's
+ // focusNextPrevChild() function. If the event is ignored,
+ // we fall back to standard tab focus handling.
+ QApplication::sendEvent(d->scene, event);
+ if (event->isAccepted())
+ return true;
+ // Ensure the event doesn't propagate just because the
+ // scene ignored it. If the event propagates, then tab
+ // handling will be called twice (this and parent).
+ event->accept();
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+ return QAbstractScrollArea::event(event);
+}
+
+/*!
+ \reimp
+*/
+bool QGraphicsView::viewportEvent(QEvent *event)
+{
+ Q_D(QGraphicsView);
+ if (!d->scene)
+ return QAbstractScrollArea::viewportEvent(event);
+
+ switch (event->type()) {
+ case QEvent::Enter:
+ QApplication::sendEvent(d->scene, event);
+ break;
+ case QEvent::WindowActivate:
+ QApplication::sendEvent(d->scene, event);
+ break;
+ case QEvent::WindowDeactivate:
+ // ### This is a temporary fix for until we get proper mouse
+ // grab events. mouseGrabberItem should be set to 0 if we lose
+ // the mouse grab.
+ // Remove all popups when the scene loses focus.
+ if (!d->scene->d_func()->popupWidgets.isEmpty())
+ d->scene->d_func()->removePopup(d->scene->d_func()->popupWidgets.first());
+ QApplication::sendEvent(d->scene, event);
+ break;
+ case QEvent::Show:
+ if (d->scene && isActiveWindow()) {
+ QEvent windowActivate(QEvent::WindowActivate);
+ QApplication::sendEvent(d->scene, &windowActivate);
+ }
+ break;
+ case QEvent::Hide:
+ // spontaneous event will generate a WindowDeactivate.
+ if (!event->spontaneous() && d->scene && isActiveWindow()) {
+ QEvent windowDeactivate(QEvent::WindowDeactivate);
+ QApplication::sendEvent(d->scene, &windowDeactivate);
+ }
+ break;
+ case QEvent::Leave:
+ // ### This is a temporary fix for until we get proper mouse grab
+ // events. activeMouseGrabberItem should be set to 0 if we lose the
+ // mouse grab.
+ if ((QApplication::activePopupWidget() && QApplication::activePopupWidget() != window())
+ || (QApplication::activeModalWidget() && QApplication::activeModalWidget() != window())
+ || (QApplication::activeWindow() != window())) {
+ if (!d->scene->d_func()->popupWidgets.isEmpty())
+ d->scene->d_func()->removePopup(d->scene->d_func()->popupWidgets.first());
+ }
+ d->useLastMouseEvent = false;
+ // a hack to pass a viewport pointer to the scene inside the leave event
+ Q_ASSERT(event->d == 0);
+ event->d = reinterpret_cast<QEventPrivate *>(viewport());
+ QApplication::sendEvent(d->scene, event);
+ break;
+#ifndef QT_NO_TOOLTIP
+ case QEvent::ToolTip: {
+ QHelpEvent *toolTip = static_cast<QHelpEvent *>(event);
+ QGraphicsSceneHelpEvent helpEvent(QEvent::GraphicsSceneHelp);
+ helpEvent.setWidget(viewport());
+ helpEvent.setScreenPos(toolTip->globalPos());
+ helpEvent.setScenePos(mapToScene(toolTip->pos()));
+ QApplication::sendEvent(d->scene, &helpEvent);
+ toolTip->setAccepted(helpEvent.isAccepted());
+ return true;
+ }
+#endif
+ case QEvent::Paint:
+ // Reset full update
+ d->fullUpdatePending = false;
+ d->dirtyScrollOffset = QPoint();
+ if (d->scene) {
+ // Check if this view reimplements the updateScene slot; if it
+ // does, we can't do direct update delivery and have to fall back
+ // to connecting the changed signal.
+ if (!d->updateSceneSlotReimplementedChecked) {
+ d->updateSceneSlotReimplementedChecked = true;
+ const QMetaObject *mo = metaObject();
+ if (mo != &QGraphicsView::staticMetaObject) {
+ if (mo->indexOfSlot("updateScene(QList<QRectF>)")
+ != QGraphicsView::staticMetaObject.indexOfSlot("updateScene(QList<QRectF>)")) {
+ connect(d->scene, SIGNAL(changed(QList<QRectF>)),
+ this, SLOT(updateScene(QList<QRectF>)));
+ }
+ }
+ }
+ }
+ break;
+ case QEvent::TouchBegin:
+ case QEvent::TouchUpdate:
+ case QEvent::TouchEnd:
+ {
+ if (!isEnabled())
+ return false;
+
+ if (d->scene && d->sceneInteractionAllowed) {
+ // Convert and deliver the touch event to the scene.
+ QTouchEvent *touchEvent = static_cast<QTouchEvent *>(event);
+ touchEvent->setWidget(viewport());
+ QGraphicsViewPrivate::translateTouchEvent(d, touchEvent);
+ (void) QApplication::sendEvent(d->scene, touchEvent);
+ }
+
+ return true;
+ }
+#ifndef QT_NO_GESTURES
+ case QEvent::Gesture:
+ case QEvent::GestureOverride:
+ {
+ if (!isEnabled())
+ return false;
+
+ if (d->scene && d->sceneInteractionAllowed) {
+ QGestureEvent *gestureEvent = static_cast<QGestureEvent *>(event);
+ gestureEvent->setWidget(viewport());
+ (void) QApplication::sendEvent(d->scene, gestureEvent);
+ }
+ return true;
+ }
+#endif // QT_NO_GESTURES
+ default:
+ break;
+ }
+
+ return QAbstractScrollArea::viewportEvent(event);
+}
+
+#ifndef QT_NO_CONTEXTMENU
+/*!
+ \reimp
+*/
+void QGraphicsView::contextMenuEvent(QContextMenuEvent *event)
+{
+ Q_D(QGraphicsView);
+ if (!d->scene || !d->sceneInteractionAllowed)
+ return;
+
+ d->mousePressViewPoint = event->pos();
+ d->mousePressScenePoint = mapToScene(d->mousePressViewPoint);
+ d->mousePressScreenPoint = event->globalPos();
+ d->lastMouseMoveScenePoint = d->mousePressScenePoint;
+ d->lastMouseMoveScreenPoint = d->mousePressScreenPoint;
+
+ QGraphicsSceneContextMenuEvent contextEvent(QEvent::GraphicsSceneContextMenu);
+ contextEvent.setWidget(viewport());
+ contextEvent.setScenePos(d->mousePressScenePoint);
+ contextEvent.setScreenPos(d->mousePressScreenPoint);
+ contextEvent.setModifiers(event->modifiers());
+ contextEvent.setReason((QGraphicsSceneContextMenuEvent::Reason)(event->reason()));
+ contextEvent.setAccepted(event->isAccepted());
+ QApplication::sendEvent(d->scene, &contextEvent);
+ event->setAccepted(contextEvent.isAccepted());
+}
+#endif // QT_NO_CONTEXTMENU
+
+/*!
+ \reimp
+*/
+void QGraphicsView::dropEvent(QDropEvent *event)
+{
+#ifndef QT_NO_DRAGANDDROP
+ Q_D(QGraphicsView);
+ if (!d->scene || !d->sceneInteractionAllowed)
+ return;
+
+ // Generate a scene event.
+ QGraphicsSceneDragDropEvent sceneEvent(QEvent::GraphicsSceneDrop);
+ d->populateSceneDragDropEvent(&sceneEvent, event);
+
+ // Send it to the scene.
+ QApplication::sendEvent(d->scene, &sceneEvent);
+
+ // Accept the originating event if the scene accepted the scene event.
+ event->setAccepted(sceneEvent.isAccepted());
+ if (sceneEvent.isAccepted())
+ event->setDropAction(sceneEvent.dropAction());
+
+ delete d->lastDragDropEvent;
+ d->lastDragDropEvent = 0;
+
+#else
+ Q_UNUSED(event)
+#endif
+}
+
+/*!
+ \reimp
+*/
+void QGraphicsView::dragEnterEvent(QDragEnterEvent *event)
+{
+#ifndef QT_NO_DRAGANDDROP
+ Q_D(QGraphicsView);
+ if (!d->scene || !d->sceneInteractionAllowed)
+ return;
+
+ // Disable replaying of mouse move events.
+ d->useLastMouseEvent = false;
+
+ // Generate a scene event.
+ QGraphicsSceneDragDropEvent sceneEvent(QEvent::GraphicsSceneDragEnter);
+ d->populateSceneDragDropEvent(&sceneEvent, event);
+
+ // Store it for later use.
+ d->storeDragDropEvent(&sceneEvent);
+
+ // Send it to the scene.
+ QApplication::sendEvent(d->scene, &sceneEvent);
+
+ // Accept the originating event if the scene accepted the scene event.
+ if (sceneEvent.isAccepted()) {
+ event->setAccepted(true);
+ event->setDropAction(sceneEvent.dropAction());
+ }
+#else
+ Q_UNUSED(event)
+#endif
+}
+
+/*!
+ \reimp
+*/
+void QGraphicsView::dragLeaveEvent(QDragLeaveEvent *event)
+{
+#ifndef QT_NO_DRAGANDDROP
+ Q_D(QGraphicsView);
+ if (!d->scene || !d->sceneInteractionAllowed)
+ return;
+ if (!d->lastDragDropEvent) {
+ qWarning("QGraphicsView::dragLeaveEvent: drag leave received before drag enter");
+ return;
+ }
+
+ // Generate a scene event.
+ QGraphicsSceneDragDropEvent sceneEvent(QEvent::GraphicsSceneDragLeave);
+ sceneEvent.setScenePos(d->lastDragDropEvent->scenePos());
+ sceneEvent.setScreenPos(d->lastDragDropEvent->screenPos());
+ sceneEvent.setButtons(d->lastDragDropEvent->buttons());
+ sceneEvent.setModifiers(d->lastDragDropEvent->modifiers());
+ sceneEvent.setPossibleActions(d->lastDragDropEvent->possibleActions());
+ sceneEvent.setProposedAction(d->lastDragDropEvent->proposedAction());
+ sceneEvent.setDropAction(d->lastDragDropEvent->dropAction());
+ sceneEvent.setMimeData(d->lastDragDropEvent->mimeData());
+ sceneEvent.setWidget(d->lastDragDropEvent->widget());
+ sceneEvent.setSource(d->lastDragDropEvent->source());
+ delete d->lastDragDropEvent;
+ d->lastDragDropEvent = 0;
+
+ // Send it to the scene.
+ QApplication::sendEvent(d->scene, &sceneEvent);
+
+ // Accept the originating event if the scene accepted the scene event.
+ if (sceneEvent.isAccepted())
+ event->setAccepted(true);
+#else
+ Q_UNUSED(event)
+#endif
+}
+
+/*!
+ \reimp
+*/
+void QGraphicsView::dragMoveEvent(QDragMoveEvent *event)
+{
+#ifndef QT_NO_DRAGANDDROP
+ Q_D(QGraphicsView);
+ if (!d->scene || !d->sceneInteractionAllowed)
+ return;
+
+ // Generate a scene event.
+ QGraphicsSceneDragDropEvent sceneEvent(QEvent::GraphicsSceneDragMove);
+ d->populateSceneDragDropEvent(&sceneEvent, event);
+
+ // Store it for later use.
+ d->storeDragDropEvent(&sceneEvent);
+
+ // Send it to the scene.
+ QApplication::sendEvent(d->scene, &sceneEvent);
+
+ // Ignore the originating event if the scene ignored the scene event.
+ event->setAccepted(sceneEvent.isAccepted());
+ if (sceneEvent.isAccepted())
+ event->setDropAction(sceneEvent.dropAction());
+#else
+ Q_UNUSED(event)
+#endif
+}
+
+/*!
+ \reimp
+*/
+void QGraphicsView::focusInEvent(QFocusEvent *event)
+{
+ Q_D(QGraphicsView);
+ d->updateInputMethodSensitivity();
+ QAbstractScrollArea::focusInEvent(event);
+ if (d->scene)
+ QApplication::sendEvent(d->scene, event);
+ // Pass focus on if the scene cannot accept focus.
+ if (!d->scene || !event->isAccepted())
+ QAbstractScrollArea::focusInEvent(event);
+}
+
+/*!
+ \reimp
+*/
+bool QGraphicsView::focusNextPrevChild(bool next)
+{
+ return QAbstractScrollArea::focusNextPrevChild(next);
+}
+
+/*!
+ \reimp
+*/
+void QGraphicsView::focusOutEvent(QFocusEvent *event)
+{
+ Q_D(QGraphicsView);
+ QAbstractScrollArea::focusOutEvent(event);
+ if (d->scene)
+ QApplication::sendEvent(d->scene, event);
+}
+
+/*!
+ \reimp
+*/
+void QGraphicsView::keyPressEvent(QKeyEvent *event)
+{
+ Q_D(QGraphicsView);
+ if (!d->scene || !d->sceneInteractionAllowed) {
+ QAbstractScrollArea::keyPressEvent(event);
+ return;
+ }
+ QApplication::sendEvent(d->scene, event);
+ if (!event->isAccepted())
+ QAbstractScrollArea::keyPressEvent(event);
+}
+
+/*!
+ \reimp
+*/
+void QGraphicsView::keyReleaseEvent(QKeyEvent *event)
+{
+ Q_D(QGraphicsView);
+ if (!d->scene || !d->sceneInteractionAllowed)
+ return;
+ QApplication::sendEvent(d->scene, event);
+ if (!event->isAccepted())
+ QAbstractScrollArea::keyReleaseEvent(event);
+}
+
+/*!
+ \reimp
+*/
+void QGraphicsView::mouseDoubleClickEvent(QMouseEvent *event)
+{
+ Q_D(QGraphicsView);
+ if (!d->scene || !d->sceneInteractionAllowed)
+ return;
+
+ d->storeMouseEvent(event);
+ d->mousePressViewPoint = event->pos();
+ d->mousePressScenePoint = mapToScene(d->mousePressViewPoint);
+ d->mousePressScreenPoint = event->globalPos();
+ d->lastMouseMoveScenePoint = d->mousePressScenePoint;
+ d->lastMouseMoveScreenPoint = d->mousePressScreenPoint;
+ d->mousePressButton = event->button();
+
+ QGraphicsSceneMouseEvent mouseEvent(QEvent::GraphicsSceneMouseDoubleClick);
+ mouseEvent.setWidget(viewport());
+ mouseEvent.setButtonDownScenePos(d->mousePressButton, d->mousePressScenePoint);
+ mouseEvent.setButtonDownScreenPos(d->mousePressButton, d->mousePressScreenPoint);
+ mouseEvent.setScenePos(mapToScene(d->mousePressViewPoint));
+ mouseEvent.setScreenPos(d->mousePressScreenPoint);
+ mouseEvent.setLastScenePos(d->lastMouseMoveScenePoint);
+ mouseEvent.setLastScreenPos(d->lastMouseMoveScreenPoint);
+ mouseEvent.setButtons(event->buttons());
+ mouseEvent.setButtons(event->buttons());
+ mouseEvent.setAccepted(false);
+ mouseEvent.setButton(event->button());
+ mouseEvent.setModifiers(event->modifiers());
+ if (event->spontaneous())
+ qt_sendSpontaneousEvent(d->scene, &mouseEvent);
+ else
+ QApplication::sendEvent(d->scene, &mouseEvent);
+}
+
+/*!
+ \reimp
+*/
+void QGraphicsView::mousePressEvent(QMouseEvent *event)
+{
+ Q_D(QGraphicsView);
+
+ // Store this event for replaying, finding deltas, and for
+ // scroll-dragging; even in non-interactive mode, scroll hand dragging is
+ // allowed, so we store the event at the very top of this function.
+ d->storeMouseEvent(event);
+ d->lastMouseEvent.setAccepted(false);
+
+ if (d->sceneInteractionAllowed) {
+ // Store some of the event's button-down data.
+ d->mousePressViewPoint = event->pos();
+ d->mousePressScenePoint = mapToScene(d->mousePressViewPoint);
+ d->mousePressScreenPoint = event->globalPos();
+ d->lastMouseMoveScenePoint = d->mousePressScenePoint;
+ d->lastMouseMoveScreenPoint = d->mousePressScreenPoint;
+ d->mousePressButton = event->button();
+
+ if (d->scene) {
+ // Convert and deliver the mouse event to the scene.
+ QGraphicsSceneMouseEvent mouseEvent(QEvent::GraphicsSceneMousePress);
+ mouseEvent.setWidget(viewport());
+ mouseEvent.setButtonDownScenePos(d->mousePressButton, d->mousePressScenePoint);
+ mouseEvent.setButtonDownScreenPos(d->mousePressButton, d->mousePressScreenPoint);
+ mouseEvent.setScenePos(d->mousePressScenePoint);
+ mouseEvent.setScreenPos(d->mousePressScreenPoint);
+ mouseEvent.setLastScenePos(d->lastMouseMoveScenePoint);
+ mouseEvent.setLastScreenPos(d->lastMouseMoveScreenPoint);
+ mouseEvent.setButtons(event->buttons());
+ mouseEvent.setButton(event->button());
+ mouseEvent.setModifiers(event->modifiers());
+ mouseEvent.setAccepted(false);
+ if (event->spontaneous())
+ qt_sendSpontaneousEvent(d->scene, &mouseEvent);
+ else
+ QApplication::sendEvent(d->scene, &mouseEvent);
+
+ // Update the original mouse event accepted state.
+ bool isAccepted = mouseEvent.isAccepted();
+ event->setAccepted(isAccepted);
+
+ // Update the last mouse event accepted state.
+ d->lastMouseEvent.setAccepted(isAccepted);
+
+ if (isAccepted)
+ return;
+ }
+ }
+
+#ifndef QT_NO_RUBBERBAND
+ if (d->dragMode == QGraphicsView::RubberBandDrag && !d->rubberBanding) {
+ if (d->sceneInteractionAllowed) {
+ // Rubberbanding is only allowed in interactive mode.
+ event->accept();
+ d->rubberBanding = true;
+ d->rubberBandRect = QRect();
+ if (d->scene) {
+ // Initiating a rubber band always clears the selection.
+ d->scene->clearSelection();
+ }
+ }
+ } else
+#endif
+ if (d->dragMode == QGraphicsView::ScrollHandDrag && event->button() == Qt::LeftButton) {
+ // Left-button press in scroll hand mode initiates hand scrolling.
+ event->accept();
+ d->handScrolling = true;
+ d->handScrollMotions = 0;
+#ifndef QT_NO_CURSOR
+ viewport()->setCursor(Qt::ClosedHandCursor);
+#endif
+ }
+}
+
+/*!
+ \reimp
+*/
+void QGraphicsView::mouseMoveEvent(QMouseEvent *event)
+{
+ Q_D(QGraphicsView);
+
+#ifndef QT_NO_RUBBERBAND
+ if (d->dragMode == QGraphicsView::RubberBandDrag && d->sceneInteractionAllowed) {
+ d->storeMouseEvent(event);
+ if (d->rubberBanding) {
+ // Check for enough drag distance
+ if ((d->mousePressViewPoint - event->pos()).manhattanLength()
+ < QApplication::startDragDistance()) {
+ return;
+ }
+
+ // Update old rubberband
+ if (d->viewportUpdateMode != QGraphicsView::NoViewportUpdate && !d->rubberBandRect.isEmpty()) {
+ if (d->viewportUpdateMode != FullViewportUpdate)
+ viewport()->update(d->rubberBandRegion(viewport(), d->rubberBandRect));
+ else
+ d->updateAll();
+ }
+
+ // Stop rubber banding if the user has let go of all buttons (even
+ // if we didn't get the release events).
+ if (!event->buttons()) {
+ d->rubberBanding = false;
+ d->rubberBandRect = QRect();
+ return;
+ }
+
+ // Update rubberband position
+ const QPoint &mp = d->mousePressViewPoint;
+ QPoint ep = event->pos();
+ d->rubberBandRect = QRect(qMin(mp.x(), ep.x()), qMin(mp.y(), ep.y()),
+ qAbs(mp.x() - ep.x()) + 1, qAbs(mp.y() - ep.y()) + 1);
+
+ // Update new rubberband
+ if (d->viewportUpdateMode != QGraphicsView::NoViewportUpdate){
+ if (d->viewportUpdateMode != FullViewportUpdate)
+ viewport()->update(d->rubberBandRegion(viewport(), d->rubberBandRect));
+ else
+ d->updateAll();
+ }
+ // Set the new selection area
+ QPainterPath selectionArea;
+ selectionArea.addPolygon(mapToScene(d->rubberBandRect));
+ selectionArea.closeSubpath();
+ if (d->scene)
+ d->scene->setSelectionArea(selectionArea, d->rubberBandSelectionMode,
+ viewportTransform());
+ return;
+ }
+ } else
+#endif // QT_NO_RUBBERBAND
+ if (d->dragMode == QGraphicsView::ScrollHandDrag) {
+ if (d->handScrolling) {
+ QScrollBar *hBar = horizontalScrollBar();
+ QScrollBar *vBar = verticalScrollBar();
+ QPoint delta = event->pos() - d->lastMouseEvent.pos();
+ hBar->setValue(hBar->value() + (isRightToLeft() ? delta.x() : -delta.x()));
+ vBar->setValue(vBar->value() - delta.y());
+
+ // Detect how much we've scrolled to disambiguate scrolling from
+ // clicking.
+ ++d->handScrollMotions;
+ }
+ }
+
+ d->mouseMoveEventHandler(event);
+}
+
+/*!
+ \reimp
+*/
+void QGraphicsView::mouseReleaseEvent(QMouseEvent *event)
+{
+ Q_D(QGraphicsView);
+
+#ifndef QT_NO_RUBBERBAND
+ if (d->dragMode == QGraphicsView::RubberBandDrag && d->sceneInteractionAllowed && !event->buttons()) {
+ if (d->rubberBanding) {
+ if (d->viewportUpdateMode != QGraphicsView::NoViewportUpdate){
+ if (d->viewportUpdateMode != FullViewportUpdate)
+ viewport()->update(d->rubberBandRegion(viewport(), d->rubberBandRect));
+ else
+ d->updateAll();
+ }
+ d->rubberBanding = false;
+ d->rubberBandRect = QRect();
+ }
+ } else
+#endif
+ if (d->dragMode == QGraphicsView::ScrollHandDrag && event->button() == Qt::LeftButton) {
+#ifndef QT_NO_CURSOR
+ // Restore the open hand cursor. ### There might be items
+ // under the mouse that have a valid cursor at this time, so
+ // we could repeat the steps from mouseMoveEvent().
+ viewport()->setCursor(Qt::OpenHandCursor);
+#endif
+ d->handScrolling = false;
+
+ if (d->scene && d->sceneInteractionAllowed && !d->lastMouseEvent.isAccepted() && d->handScrollMotions <= 6) {
+ // If we've detected very little motion during the hand drag, and
+ // no item accepted the last event, we'll interpret that as a
+ // click to the scene, and reset the selection.
+ d->scene->clearSelection();
+ }
+ }
+
+ d->storeMouseEvent(event);
+
+ if (!d->sceneInteractionAllowed)
+ return;
+
+ if (!d->scene)
+ return;
+
+ QGraphicsSceneMouseEvent mouseEvent(QEvent::GraphicsSceneMouseRelease);
+ mouseEvent.setWidget(viewport());
+ mouseEvent.setButtonDownScenePos(d->mousePressButton, d->mousePressScenePoint);
+ mouseEvent.setButtonDownScreenPos(d->mousePressButton, d->mousePressScreenPoint);
+ mouseEvent.setScenePos(mapToScene(event->pos()));
+ mouseEvent.setScreenPos(event->globalPos());
+ mouseEvent.setLastScenePos(d->lastMouseMoveScenePoint);
+ mouseEvent.setLastScreenPos(d->lastMouseMoveScreenPoint);
+ mouseEvent.setButtons(event->buttons());
+ mouseEvent.setButton(event->button());
+ mouseEvent.setModifiers(event->modifiers());
+ mouseEvent.setAccepted(false);
+ if (event->spontaneous())
+ qt_sendSpontaneousEvent(d->scene, &mouseEvent);
+ else
+ QApplication::sendEvent(d->scene, &mouseEvent);
+
+ // Update the last mouse event selected state.
+ d->lastMouseEvent.setAccepted(mouseEvent.isAccepted());
+
+#ifndef QT_NO_CURSOR
+ if (mouseEvent.isAccepted() && mouseEvent.buttons() == 0 && viewport()->testAttribute(Qt::WA_SetCursor)) {
+ // The last mouse release on the viewport will trigger clearing the cursor.
+ d->_q_unsetViewportCursor();
+ }
+#endif
+}
+
+#ifndef QT_NO_WHEELEVENT
+/*!
+ \reimp
+*/
+void QGraphicsView::wheelEvent(QWheelEvent *event)
+{
+ Q_D(QGraphicsView);
+ if (!d->scene || !d->sceneInteractionAllowed) {
+ QAbstractScrollArea::wheelEvent(event);
+ return;
+ }
+
+ event->ignore();
+
+ QGraphicsSceneWheelEvent wheelEvent(QEvent::GraphicsSceneWheel);
+ wheelEvent.setWidget(viewport());
+ wheelEvent.setScenePos(mapToScene(event->pos()));
+ wheelEvent.setScreenPos(event->globalPos());
+ wheelEvent.setButtons(event->buttons());
+ wheelEvent.setModifiers(event->modifiers());
+ wheelEvent.setDelta(event->delta());
+ wheelEvent.setOrientation(event->orientation());
+ wheelEvent.setAccepted(false);
+ QApplication::sendEvent(d->scene, &wheelEvent);
+ event->setAccepted(wheelEvent.isAccepted());
+ if (!event->isAccepted())
+ QAbstractScrollArea::wheelEvent(event);
+}
+#endif // QT_NO_WHEELEVENT
+
+/*!
+ \reimp
+*/
+void QGraphicsView::paintEvent(QPaintEvent *event)
+{
+ Q_D(QGraphicsView);
+ if (!d->scene) {
+ QAbstractScrollArea::paintEvent(event);
+ return;
+ }
+
+ // Set up painter state protection.
+ d->scene->d_func()->painterStateProtection = !(d->optimizationFlags & DontSavePainterState);
+
+ // Determine the exposed region
+ d->exposedRegion = event->region();
+ QRectF exposedSceneRect = mapToScene(d->exposedRegion.boundingRect()).boundingRect();
+
+ // Set up the painter
+ QPainter painter(viewport());
+#ifndef QT_NO_RUBBERBAND
+ if (d->rubberBanding && !d->rubberBandRect.isEmpty())
+ painter.save();
+#endif
+ // Set up render hints
+ painter.setRenderHints(painter.renderHints(), false);
+ painter.setRenderHints(d->renderHints, true);
+
+ // Set up viewport transform
+ const bool viewTransformed = isTransformed();
+ if (viewTransformed)
+ painter.setWorldTransform(viewportTransform());
+ const QTransform viewTransform = painter.worldTransform();
+
+ // Draw background
+ if ((d->cacheMode & CacheBackground)
+#ifdef Q_WS_X11
+ && X11->use_xrender
+#endif
+ ) {
+ // Recreate the background pixmap, and flag the whole background as
+ // exposed.
+ if (d->mustResizeBackgroundPixmap) {
+ d->backgroundPixmap = QPixmap(viewport()->size());
+ QBrush bgBrush = viewport()->palette().brush(viewport()->backgroundRole());
+ if (!bgBrush.isOpaque())
+ d->backgroundPixmap.fill(Qt::transparent);
+ QPainter p(&d->backgroundPixmap);
+ p.fillRect(0, 0, d->backgroundPixmap.width(), d->backgroundPixmap.height(), bgBrush);
+ d->backgroundPixmapExposed = QRegion(viewport()->rect());
+ d->mustResizeBackgroundPixmap = false;
+ }
+
+ // Redraw exposed areas
+ if (!d->backgroundPixmapExposed.isEmpty()) {
+ QPainter backgroundPainter(&d->backgroundPixmap);
+ backgroundPainter.setClipRegion(d->backgroundPixmapExposed, Qt::ReplaceClip);
+ if (viewTransformed)
+ backgroundPainter.setTransform(viewTransform);
+ QRectF backgroundExposedSceneRect = mapToScene(d->backgroundPixmapExposed.boundingRect()).boundingRect();
+ drawBackground(&backgroundPainter, backgroundExposedSceneRect);
+ d->backgroundPixmapExposed = QRegion();
+ }
+
+ // Blit the background from the background pixmap
+ if (viewTransformed) {
+ painter.setWorldTransform(QTransform());
+ painter.drawPixmap(QPoint(), d->backgroundPixmap);
+ painter.setWorldTransform(viewTransform);
+ } else {
+ painter.drawPixmap(QPoint(), d->backgroundPixmap);
+ }
+ } else {
+ if (!(d->optimizationFlags & DontSavePainterState))
+ painter.save();
+ drawBackground(&painter, exposedSceneRect);
+ if (!(d->optimizationFlags & DontSavePainterState))
+ painter.restore();
+ }
+
+ // Items
+ if (!(d->optimizationFlags & IndirectPainting)) {
+ const quint32 oldRectAdjust = d->scene->d_func()->rectAdjust;
+ if (d->optimizationFlags & QGraphicsView::DontAdjustForAntialiasing)
+ d->scene->d_func()->rectAdjust = 1;
+ else
+ d->scene->d_func()->rectAdjust = 2;
+ d->scene->d_func()->drawItems(&painter, viewTransformed ? &viewTransform : 0,
+ &d->exposedRegion, viewport());
+ d->scene->d_func()->rectAdjust = oldRectAdjust;
+ // Make sure the painter's world transform is restored correctly when
+ // drawing without painter state protection (DontSavePainterState).
+ // We only change the worldTransform() so there's no need to do a full-blown
+ // save() and restore(). Also note that we don't have to do this in case of
+ // IndirectPainting (the else branch), because in that case we always save()
+ // and restore() in QGraphicsScene::drawItems().
+ if (!d->scene->d_func()->painterStateProtection)
+ painter.setOpacity(1.0);
+ painter.setWorldTransform(viewTransform);
+ } else {
+ // Make sure we don't have unpolished items before we draw
+ if (!d->scene->d_func()->unpolishedItems.isEmpty())
+ d->scene->d_func()->_q_polishItems();
+ // We reset updateAll here (after we've issued polish events)
+ // so that we can discard update requests coming from polishEvent().
+ d->scene->d_func()->updateAll = false;
+
+ // Find all exposed items
+ bool allItems = false;
+ QList<QGraphicsItem *> itemList = d->findItems(d->exposedRegion, &allItems, viewTransform);
+ if (!itemList.isEmpty()) {
+ // Generate the style options.
+ const int numItems = itemList.size();
+ QGraphicsItem **itemArray = &itemList[0]; // Relies on QList internals, but is perfectly valid.
+ QStyleOptionGraphicsItem *styleOptionArray = d->allocStyleOptionsArray(numItems);
+ QTransform transform(Qt::Uninitialized);
+ for (int i = 0; i < numItems; ++i) {
+ QGraphicsItem *item = itemArray[i];
+ QGraphicsItemPrivate *itemd = item->d_ptr.data();
+ itemd->initStyleOption(&styleOptionArray[i], viewTransform, d->exposedRegion, allItems);
+ // Cache the item's area in view coordinates.
+ // Note that we have to do this here in case the base class implementation
+ // (QGraphicsScene::drawItems) is not called. If it is, we'll do this
+ // operation twice, but that's the price one has to pay for using indirect
+ // painting :-/.
+ const QRectF brect = adjustedItemEffectiveBoundingRect(item);
+ if (!itemd->itemIsUntransformable()) {
+ transform = item->sceneTransform();
+ if (viewTransformed)
+ transform *= viewTransform;
+ } else {
+ transform = item->deviceTransform(viewTransform);
+ }
+ itemd->paintedViewBoundingRects.insert(d->viewport, transform.mapRect(brect).toRect());
+ }
+ // Draw the items.
+ drawItems(&painter, numItems, itemArray, styleOptionArray);
+ d->freeStyleOptionsArray(styleOptionArray);
+ }
+ }
+
+ // Foreground
+ drawForeground(&painter, exposedSceneRect);
+
+#ifndef QT_NO_RUBBERBAND
+ // Rubberband
+ if (d->rubberBanding && !d->rubberBandRect.isEmpty()) {
+ painter.restore();
+ QStyleOptionRubberBand option;
+ option.initFrom(viewport());
+ option.rect = d->rubberBandRect;
+ option.shape = QRubberBand::Rectangle;
+
+ QStyleHintReturnMask mask;
+ if (viewport()->style()->styleHint(QStyle::SH_RubberBand_Mask, &option, viewport(), &mask)) {
+ // painter clipping for masked rubberbands
+ painter.setClipRegion(mask.region, Qt::IntersectClip);
+ }
+
+ viewport()->style()->drawControl(QStyle::CE_RubberBand, &option, &painter, viewport());
+ }
+#endif
+
+ painter.end();
+
+ // Restore painter state protection.
+ d->scene->d_func()->painterStateProtection = true;
+}
+
+/*!
+ \reimp
+*/
+void QGraphicsView::resizeEvent(QResizeEvent *event)
+{
+ Q_D(QGraphicsView);
+ // Save the last center point - the resize may scroll the view, which
+ // changes the center point.
+ QPointF oldLastCenterPoint = d->lastCenterPoint;
+
+ QAbstractScrollArea::resizeEvent(event);
+ d->recalculateContentSize();
+
+ // Restore the center point again.
+ if (d->resizeAnchor == NoAnchor && !d->keepLastCenterPoint) {
+ d->updateLastCenterPoint();
+ } else {
+ d->lastCenterPoint = oldLastCenterPoint;
+ }
+ d->centerView(d->resizeAnchor);
+ d->keepLastCenterPoint = false;
+
+ if (d->cacheMode & CacheBackground) {
+ // Invalidate the background pixmap
+ d->mustResizeBackgroundPixmap = true;
+ }
+}
+
+/*!
+ \reimp
+*/
+void QGraphicsView::scrollContentsBy(int dx, int dy)
+{
+ Q_D(QGraphicsView);
+ d->dirtyScroll = true;
+ if (d->transforming)
+ return;
+ if (isRightToLeft())
+ dx = -dx;
+
+ if (d->viewportUpdateMode != QGraphicsView::NoViewportUpdate) {
+ if (d->viewportUpdateMode != QGraphicsView::FullViewportUpdate) {
+ if (d->accelerateScrolling) {
+#ifndef QT_NO_RUBBERBAND
+ // Update new and old rubberband regions
+ if (!d->rubberBandRect.isEmpty()) {
+ QRegion rubberBandRegion(d->rubberBandRegion(viewport(), d->rubberBandRect));
+ rubberBandRegion += rubberBandRegion.translated(-dx, -dy);
+ viewport()->update(rubberBandRegion);
+ }
+#endif
+ d->dirtyScrollOffset.rx() += dx;
+ d->dirtyScrollOffset.ry() += dy;
+ d->dirtyRegion.translate(dx, dy);
+ viewport()->scroll(dx, dy);
+ } else {
+ d->updateAll();
+ }
+ } else {
+ d->updateAll();
+ }
+ }
+
+ d->updateLastCenterPoint();
+
+ if ((d->cacheMode & CacheBackground)
+#ifdef Q_WS_X11
+ && X11->use_xrender
+#endif
+ ) {
+ // Scroll the background pixmap
+ QRegion exposed;
+ if (!d->backgroundPixmap.isNull())
+ d->backgroundPixmap.scroll(dx, dy, d->backgroundPixmap.rect(), &exposed);
+
+ // Invalidate the background pixmap
+ d->backgroundPixmapExposed.translate(dx, dy);
+ d->backgroundPixmapExposed += exposed;
+ }
+
+ // Always replay on scroll.
+ if (d->sceneInteractionAllowed)
+ d->replayLastMouseEvent();
+}
+
+/*!
+ \reimp
+*/
+void QGraphicsView::showEvent(QShowEvent *event)
+{
+ Q_D(QGraphicsView);
+ d->recalculateContentSize();
+ d->centerView(d->transformationAnchor);
+ QAbstractScrollArea::showEvent(event);
+}
+
+/*!
+ \reimp
+*/
+void QGraphicsView::inputMethodEvent(QInputMethodEvent *event)
+{
+ Q_D(QGraphicsView);
+ if (d->scene)
+ QApplication::sendEvent(d->scene, event);
+}
+
+/*!
+ Draws the background of the scene using \a painter, before any items and
+ the foreground are drawn. Reimplement this function to provide a custom
+ background for this view.
+
+ If all you want is to define a color, texture or gradient for the
+ background, you can call setBackgroundBrush() instead.
+
+ All painting is done in \e scene coordinates. \a rect is the exposed
+ rectangle.
+
+ The default implementation fills \a rect using the view's backgroundBrush.
+ If no such brush is defined (the default), the scene's drawBackground()
+ function is called instead.
+
+ \sa drawForeground(), QGraphicsScene::drawBackground()
+*/
+void QGraphicsView::drawBackground(QPainter *painter, const QRectF &rect)
+{
+ Q_D(QGraphicsView);
+ if (d->scene && d->backgroundBrush.style() == Qt::NoBrush) {
+ d->scene->drawBackground(painter, rect);
+ return;
+ }
+
+ painter->fillRect(rect, d->backgroundBrush);
+}
+
+/*!
+ Draws the foreground of the scene using \a painter, after the background
+ and all items are drawn. Reimplement this function to provide a custom
+ foreground for this view.
+
+ If all you want is to define a color, texture or gradient for the
+ foreground, you can call setForegroundBrush() instead.
+
+ All painting is done in \e scene coordinates. \a rect is the exposed
+ rectangle.
+
+ The default implementation fills \a rect using the view's foregroundBrush.
+ If no such brush is defined (the default), the scene's drawForeground()
+ function is called instead.
+
+ \sa drawBackground(), QGraphicsScene::drawForeground()
+*/
+void QGraphicsView::drawForeground(QPainter *painter, const QRectF &rect)
+{
+ Q_D(QGraphicsView);
+ if (d->scene && d->foregroundBrush.style() == Qt::NoBrush) {
+ d->scene->drawForeground(painter, rect);
+ return;
+ }
+
+ painter->fillRect(rect, d->foregroundBrush);
+}
+
+/*!
+ \obsolete
+
+ Draws the items \a items in the scene using \a painter, after the
+ background and before the foreground are drawn. \a numItems is the number
+ of items in \a items and options in \a options. \a options is a list of
+ styleoptions; one for each item. Reimplement this function to provide
+ custom item drawing for this view.
+
+ The default implementation calls the scene's drawItems() function.
+
+ Since Qt 4.6, this function is not called anymore unless
+ the QGraphicsView::IndirectPainting flag is given as an Optimization
+ flag.
+
+ \sa drawForeground(), drawBackground(), QGraphicsScene::drawItems()
+*/
+void QGraphicsView::drawItems(QPainter *painter, int numItems,
+ QGraphicsItem *items[],
+ const QStyleOptionGraphicsItem options[])
+{
+ Q_D(QGraphicsView);
+ if (d->scene) {
+ QWidget *widget = painter->device() == viewport() ? viewport() : 0;
+ d->scene->drawItems(painter, numItems, items, options, widget);
+ }
+}
+
+/*!
+ Returns the current transformation matrix for the view. If no current
+ transformation is set, the identity matrix is returned.
+
+ \sa setTransform(), rotate(), scale(), shear(), translate()
+*/
+QTransform QGraphicsView::transform() const
+{
+ Q_D(const QGraphicsView);
+ return d->matrix;
+}
+
+/*!
+ Returns a matrix that maps viewport coordinates to scene coordinates.
+
+ \sa mapToScene(), mapFromScene()
+*/
+QTransform QGraphicsView::viewportTransform() const
+{
+ Q_D(const QGraphicsView);
+ QTransform moveMatrix = QTransform::fromTranslate(-d->horizontalScroll(), -d->verticalScroll());
+ return d->identityMatrix ? moveMatrix : d->matrix * moveMatrix;
+}
+
+/*!
+ \since 4.6
+
+ Returns true if the view is transformed (i.e., a non-identity transform
+ has been assigned, or the scrollbars are adjusted).
+
+ \sa setTransform(), horizontalScrollBar(), verticalScrollBar()
+*/
+bool QGraphicsView::isTransformed() const
+{
+ Q_D(const QGraphicsView);
+ return !d->identityMatrix || d->horizontalScroll() || d->verticalScroll();
+}
+
+/*!
+ Sets the view's current transformation matrix to \a matrix.
+
+ If \a combine is true, then \a matrix is combined with the current matrix;
+ otherwise, \a matrix \e replaces the current matrix. \a combine is false
+ by default.
+
+ The transformation matrix tranforms the scene into view coordinates. Using
+ the default transformation, provided by the identity matrix, one pixel in
+ the view represents one unit in the scene (e.g., a 10x10 rectangular item
+ is drawn using 10x10 pixels in the view). If a 2x2 scaling matrix is
+ applied, the scene will be drawn in 1:2 (e.g., a 10x10 rectangular item is
+ then drawn using 20x20 pixels in the view).
+
+ Example:
+
+ \snippet doc/src/snippets/code/src_gui_graphicsview_qgraphicsview.cpp 7
+
+ To simplify interation with items using a transformed view, QGraphicsView
+ provides mapTo... and mapFrom... functions that can translate between
+ scene and view coordinates. For example, you can call mapToScene() to map
+ a view coordiate to a floating point scene coordinate, or mapFromScene()
+ to map from floating point scene coordinates to view coordinates.
+
+ \sa transform(), rotate(), scale(), shear(), translate()
+*/
+void QGraphicsView::setTransform(const QTransform &matrix, bool combine )
+{
+ Q_D(QGraphicsView);
+ QTransform oldMatrix = d->matrix;
+ if (!combine)
+ d->matrix = matrix;
+ else
+ d->matrix = matrix * d->matrix;
+ if (oldMatrix == d->matrix)
+ return;
+
+ d->identityMatrix = d->matrix.isIdentity();
+ d->transforming = true;
+ if (d->scene) {
+ d->recalculateContentSize();
+ d->centerView(d->transformationAnchor);
+ } else {
+ d->updateLastCenterPoint();
+ }
+
+ if (d->sceneInteractionAllowed)
+ d->replayLastMouseEvent();
+ d->transforming = false;
+
+ // Any matrix operation requires a full update.
+ d->updateAll();
+}
+
+/*!
+ Resets the view transformation to the identity matrix.
+
+ \sa transform(), setTransform()
+*/
+void QGraphicsView::resetTransform()
+{
+ setTransform(QTransform());
+}
+
+QPointF QGraphicsViewPrivate::mapToScene(const QPointF &point) const
+{
+ QPointF p = point;
+ p.rx() += horizontalScroll();
+ p.ry() += verticalScroll();
+ return identityMatrix ? p : matrix.inverted().map(p);
+}
+
+QRectF QGraphicsViewPrivate::mapToScene(const QRectF &rect) const
+{
+ QPointF scrollOffset(horizontalScroll(), verticalScroll());
+ QPointF tl = scrollOffset + rect.topLeft();
+ QPointF tr = scrollOffset + rect.topRight();
+ QPointF br = scrollOffset + rect.bottomRight();
+ QPointF bl = scrollOffset + rect.bottomLeft();
+
+ QPolygonF poly(4);
+ if (!identityMatrix) {
+ QTransform x = matrix.inverted();
+ poly[0] = x.map(tl);
+ poly[1] = x.map(tr);
+ poly[2] = x.map(br);
+ poly[3] = x.map(bl);
+ } else {
+ poly[0] = tl;
+ poly[1] = tr;
+ poly[2] = br;
+ poly[3] = bl;
+ }
+ return poly.boundingRect();
+}
+
+QT_END_NAMESPACE
+
+#include "moc_qgraphicsview.cpp"
+
+#endif // QT_NO_GRAPHICSVIEW
diff --git a/src/widgets/graphicsview/qgraphicsview.h b/src/widgets/graphicsview/qgraphicsview.h
new file mode 100644
index 0000000000..b2a7bdb226
--- /dev/null
+++ b/src/widgets/graphicsview/qgraphicsview.h
@@ -0,0 +1,316 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** 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 Technology Preview License Agreement accompanying
+** this package.
+**
+** 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.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QGRAPHICSVIEW_H
+#define QGRAPHICSVIEW_H
+
+#include <QtCore/qmetatype.h>
+#include <QtGui/qpainter.h>
+#include <QtGui/qscrollarea.h>
+#include <QtGui/qgraphicsscene.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+#if !defined(QT_NO_GRAPHICSVIEW) || (QT_EDITION & QT_MODULE_GRAPHICSVIEW) != QT_MODULE_GRAPHICSVIEW
+
+class QGraphicsItem;
+class QPainterPath;
+class QPolygonF;
+class QStyleOptionGraphicsItem;
+
+class QGraphicsViewPrivate;
+class Q_GUI_EXPORT QGraphicsView : public QAbstractScrollArea
+{
+ Q_OBJECT
+ Q_FLAGS(QPainter::RenderHints CacheMode OptimizationFlags)
+ Q_ENUMS(ViewportAnchor DragMode ViewportUpdateMode)
+ Q_PROPERTY(QBrush backgroundBrush READ backgroundBrush WRITE setBackgroundBrush)
+ Q_PROPERTY(QBrush foregroundBrush READ foregroundBrush WRITE setForegroundBrush)
+ Q_PROPERTY(bool interactive READ isInteractive WRITE setInteractive)
+ Q_PROPERTY(QRectF sceneRect READ sceneRect WRITE setSceneRect)
+ Q_PROPERTY(Qt::Alignment alignment READ alignment WRITE setAlignment)
+ Q_PROPERTY(QPainter::RenderHints renderHints READ renderHints WRITE setRenderHints)
+ Q_PROPERTY(DragMode dragMode READ dragMode WRITE setDragMode)
+ Q_PROPERTY(CacheMode cacheMode READ cacheMode WRITE setCacheMode)
+ Q_PROPERTY(ViewportAnchor transformationAnchor READ transformationAnchor WRITE setTransformationAnchor)
+ Q_PROPERTY(ViewportAnchor resizeAnchor READ resizeAnchor WRITE setResizeAnchor)
+ Q_PROPERTY(ViewportUpdateMode viewportUpdateMode READ viewportUpdateMode WRITE setViewportUpdateMode)
+#ifndef QT_NO_RUBBERBAND
+ Q_PROPERTY(Qt::ItemSelectionMode rubberBandSelectionMode READ rubberBandSelectionMode WRITE setRubberBandSelectionMode)
+#endif
+ Q_PROPERTY(OptimizationFlags optimizationFlags READ optimizationFlags WRITE setOptimizationFlags)
+
+public:
+ enum ViewportAnchor {
+ NoAnchor,
+ AnchorViewCenter,
+ AnchorUnderMouse
+ };
+
+ enum CacheModeFlag {
+ CacheNone = 0x0,
+ CacheBackground = 0x1
+ };
+ Q_DECLARE_FLAGS(CacheMode, CacheModeFlag)
+
+ enum DragMode {
+ NoDrag,
+ ScrollHandDrag,
+ RubberBandDrag
+ };
+
+ enum ViewportUpdateMode {
+ FullViewportUpdate,
+ MinimalViewportUpdate,
+ SmartViewportUpdate,
+ NoViewportUpdate,
+ BoundingRectViewportUpdate
+ };
+
+ enum OptimizationFlag {
+ DontClipPainter = 0x1, // obsolete
+ DontSavePainterState = 0x2,
+ DontAdjustForAntialiasing = 0x4,
+ IndirectPainting = 0x8
+ };
+ Q_DECLARE_FLAGS(OptimizationFlags, OptimizationFlag)
+
+ QGraphicsView(QWidget *parent = 0);
+ QGraphicsView(QGraphicsScene *scene, QWidget *parent = 0);
+ ~QGraphicsView();
+
+ QSize sizeHint() const;
+
+ QPainter::RenderHints renderHints() const;
+ void setRenderHint(QPainter::RenderHint hint, bool enabled = true);
+ void setRenderHints(QPainter::RenderHints hints);
+
+ Qt::Alignment alignment() const;
+ void setAlignment(Qt::Alignment alignment);
+
+ ViewportAnchor transformationAnchor() const;
+ void setTransformationAnchor(ViewportAnchor anchor);
+
+ ViewportAnchor resizeAnchor() const;
+ void setResizeAnchor(ViewportAnchor anchor);
+
+ ViewportUpdateMode viewportUpdateMode() const;
+ void setViewportUpdateMode(ViewportUpdateMode mode);
+
+ OptimizationFlags optimizationFlags() const;
+ void setOptimizationFlag(OptimizationFlag flag, bool enabled = true);
+ void setOptimizationFlags(OptimizationFlags flags);
+
+ DragMode dragMode() const;
+ void setDragMode(DragMode mode);
+
+#ifndef QT_NO_RUBBERBAND
+ Qt::ItemSelectionMode rubberBandSelectionMode() const;
+ void setRubberBandSelectionMode(Qt::ItemSelectionMode mode);
+#endif
+
+ CacheMode cacheMode() const;
+ void setCacheMode(CacheMode mode);
+ void resetCachedContent();
+
+ bool isInteractive() const;
+ void setInteractive(bool allowed);
+
+ QGraphicsScene *scene() const;
+ void setScene(QGraphicsScene *scene);
+
+ QRectF sceneRect() const;
+ void setSceneRect(const QRectF &rect);
+ inline void setSceneRect(qreal x, qreal y, qreal w, qreal h);
+
+ QMatrix matrix() const;
+ void setMatrix(const QMatrix &matrix, bool combine = false);
+ void resetMatrix();
+ QTransform transform() const;
+ QTransform viewportTransform() const;
+ bool isTransformed() const;
+ void setTransform(const QTransform &matrix, bool combine = false);
+ void resetTransform();
+ void rotate(qreal angle);
+ void scale(qreal sx, qreal sy);
+ void shear(qreal sh, qreal sv);
+ void translate(qreal dx, qreal dy);
+
+ void centerOn(const QPointF &pos);
+ inline void centerOn(qreal x, qreal y);
+ void centerOn(const QGraphicsItem *item);
+ void ensureVisible(const QRectF &rect, int xmargin = 50, int ymargin = 50);
+ inline void ensureVisible(qreal x, qreal y, qreal w, qreal h, int xmargin = 50, int ymargin = 50);
+ void ensureVisible(const QGraphicsItem *item, int xmargin = 50, int ymargin = 50);
+ void fitInView(const QRectF &rect, Qt::AspectRatioMode aspectRadioMode = Qt::IgnoreAspectRatio);
+ inline void fitInView(qreal x, qreal y, qreal w, qreal h,
+ Qt::AspectRatioMode aspectRadioMode = Qt::IgnoreAspectRatio);
+ void fitInView(const QGraphicsItem *item,
+ Qt::AspectRatioMode aspectRadioMode = Qt::IgnoreAspectRatio);
+
+ void render(QPainter *painter, const QRectF &target = QRectF(), const QRect &source = QRect(),
+ Qt::AspectRatioMode aspectRatioMode = Qt::KeepAspectRatio);
+
+ QList<QGraphicsItem *> items() const;
+ QList<QGraphicsItem *> items(const QPoint &pos) const;
+ inline QList<QGraphicsItem *> items(int x, int y) const;
+ QList<QGraphicsItem *> items(const QRect &rect, Qt::ItemSelectionMode mode = Qt::IntersectsItemShape) const;
+ inline QList<QGraphicsItem *> items(int x, int y, int w, int h, Qt::ItemSelectionMode mode = Qt::IntersectsItemShape) const;
+ QList<QGraphicsItem *> items(const QPolygon &polygon, Qt::ItemSelectionMode mode = Qt::IntersectsItemShape) const;
+ QList<QGraphicsItem *> items(const QPainterPath &path, Qt::ItemSelectionMode mode = Qt::IntersectsItemShape) const;
+ QGraphicsItem *itemAt(const QPoint &pos) const;
+ inline QGraphicsItem *itemAt(int x, int y) const;
+
+ QPointF mapToScene(const QPoint &point) const;
+ QPolygonF mapToScene(const QRect &rect) const;
+ QPolygonF mapToScene(const QPolygon &polygon) const;
+ QPainterPath mapToScene(const QPainterPath &path) const;
+ QPoint mapFromScene(const QPointF &point) const;
+ QPolygon mapFromScene(const QRectF &rect) const;
+ QPolygon mapFromScene(const QPolygonF &polygon) const;
+ QPainterPath mapFromScene(const QPainterPath &path) const;
+ inline QPointF mapToScene(int x, int y) const;
+ inline QPolygonF mapToScene(int x, int y, int w, int h) const;
+ inline QPoint mapFromScene(qreal x, qreal y) const;
+ inline QPolygon mapFromScene(qreal x, qreal y, qreal w, qreal h) const;
+
+ QVariant inputMethodQuery(Qt::InputMethodQuery query) const;
+
+ QBrush backgroundBrush() const;
+ void setBackgroundBrush(const QBrush &brush);
+
+ QBrush foregroundBrush() const;
+ void setForegroundBrush(const QBrush &brush);
+
+public Q_SLOTS:
+ void updateScene(const QList<QRectF> &rects);
+ void invalidateScene(const QRectF &rect = QRectF(), QGraphicsScene::SceneLayers layers = QGraphicsScene::AllLayers);
+ void updateSceneRect(const QRectF &rect);
+
+protected Q_SLOTS:
+ void setupViewport(QWidget *widget);
+
+protected:
+ QGraphicsView(QGraphicsViewPrivate &, QWidget *parent = 0);
+ bool event(QEvent *event);
+ bool viewportEvent(QEvent *event);
+
+#ifndef QT_NO_CONTEXTMENU
+ void contextMenuEvent(QContextMenuEvent *event);
+#endif
+ void dragEnterEvent(QDragEnterEvent *event);
+ void dragLeaveEvent(QDragLeaveEvent *event);
+ void dragMoveEvent(QDragMoveEvent *event);
+ void dropEvent(QDropEvent *event);
+ void focusInEvent(QFocusEvent *event);
+ bool focusNextPrevChild(bool next);
+ void focusOutEvent(QFocusEvent *event);
+ void keyPressEvent(QKeyEvent *event);
+ void keyReleaseEvent(QKeyEvent *event);
+ void mouseDoubleClickEvent(QMouseEvent *event);
+ void mousePressEvent(QMouseEvent *event);
+ void mouseMoveEvent(QMouseEvent *event);
+ void mouseReleaseEvent(QMouseEvent *event);
+#ifndef QT_NO_WHEELEVENT
+ void wheelEvent(QWheelEvent *event);
+#endif
+ void paintEvent(QPaintEvent *event);
+ void resizeEvent(QResizeEvent *event);
+ void scrollContentsBy(int dx, int dy);
+ void showEvent(QShowEvent *event);
+ void inputMethodEvent(QInputMethodEvent *event);
+
+ virtual void drawBackground(QPainter *painter, const QRectF &rect);
+ virtual void drawForeground(QPainter *painter, const QRectF &rect);
+ virtual void drawItems(QPainter *painter, int numItems,
+ QGraphicsItem *items[],
+ const QStyleOptionGraphicsItem options[]);
+
+private:
+ Q_DECLARE_PRIVATE(QGraphicsView)
+ Q_DISABLE_COPY(QGraphicsView)
+#ifndef QT_NO_CURSOR
+ Q_PRIVATE_SLOT(d_func(), void _q_setViewportCursor(const QCursor &))
+ Q_PRIVATE_SLOT(d_func(), void _q_unsetViewportCursor())
+#endif
+ friend class QGraphicsSceneWidget;
+ friend class QGraphicsScene;
+ friend class QGraphicsScenePrivate;
+ friend class QGraphicsItemPrivate;
+};
+
+Q_DECLARE_OPERATORS_FOR_FLAGS(QGraphicsView::CacheMode)
+Q_DECLARE_OPERATORS_FOR_FLAGS(QGraphicsView::OptimizationFlags)
+
+inline void QGraphicsView::setSceneRect(qreal ax, qreal ay, qreal aw, qreal ah)
+{ setSceneRect(QRectF(ax, ay, aw, ah)); }
+inline void QGraphicsView::centerOn(qreal ax, qreal ay)
+{ centerOn(QPointF(ax, ay)); }
+inline void QGraphicsView::ensureVisible(qreal ax, qreal ay, qreal aw, qreal ah, int xmargin, int ymargin)
+{ ensureVisible(QRectF(ax, ay, aw, ah), xmargin, ymargin); }
+inline void QGraphicsView::fitInView(qreal ax, qreal ay, qreal w, qreal h, Qt::AspectRatioMode mode)
+{ fitInView(QRectF(ax, ay, w, h), mode); }
+inline QList<QGraphicsItem *> QGraphicsView::items(int ax, int ay) const
+{ return items(QPoint(ax, ay)); }
+inline QList<QGraphicsItem *> QGraphicsView::items(int ax, int ay, int w, int h, Qt::ItemSelectionMode mode) const
+{ return items(QRect(ax, ay, w, h), mode); }
+inline QGraphicsItem *QGraphicsView::itemAt(int ax, int ay) const
+{ return itemAt(QPoint(ax, ay)); }
+inline QPointF QGraphicsView::mapToScene(int ax, int ay) const
+{ return mapToScene(QPoint(ax, ay)); }
+inline QPolygonF QGraphicsView::mapToScene(int ax, int ay, int w, int h) const
+{ return mapToScene(QRect(ax, ay, w, h)); }
+inline QPoint QGraphicsView::mapFromScene(qreal ax, qreal ay) const
+{ return mapFromScene(QPointF(ax, ay)); }
+inline QPolygon QGraphicsView::mapFromScene(qreal ax, qreal ay, qreal w, qreal h) const
+{ return mapFromScene(QRectF(ax, ay, w, h)); }
+
+#endif // QT_NO_GRAPHICSVIEW
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QGRAPHICSVIEW_H
diff --git a/src/widgets/graphicsview/qgraphicsview_p.h b/src/widgets/graphicsview/qgraphicsview_p.h
new file mode 100644
index 0000000000..5572b7e770
--- /dev/null
+++ b/src/widgets/graphicsview/qgraphicsview_p.h
@@ -0,0 +1,233 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** 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 Technology Preview License Agreement accompanying
+** this package.
+**
+** 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.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QGRAPHICSVIEW_P_H
+#define QGRAPHICSVIEW_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of other Qt classes. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qgraphicsview.h"
+
+#if !defined(QT_NO_GRAPHICSVIEW) || (QT_EDITION & QT_MODULE_GRAPHICSVIEW) != QT_MODULE_GRAPHICSVIEW
+
+#include <QtGui/qevent.h>
+#include <QtCore/qcoreapplication.h>
+#include "qgraphicssceneevent.h"
+#include <QtGui/qstyleoption.h>
+#include <private/qabstractscrollarea_p.h>
+#include <private/qapplication_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class Q_GUI_EXPORT QGraphicsViewPrivate : public QAbstractScrollAreaPrivate
+{
+ Q_DECLARE_PUBLIC(QGraphicsView)
+public:
+ QGraphicsViewPrivate();
+
+ void recalculateContentSize();
+ void centerView(QGraphicsView::ViewportAnchor anchor);
+
+ QPainter::RenderHints renderHints;
+
+ QGraphicsView::DragMode dragMode;
+
+ quint32 sceneInteractionAllowed : 1;
+ quint32 hasSceneRect : 1;
+ quint32 connectedToScene : 1;
+ quint32 useLastMouseEvent : 1;
+ quint32 identityMatrix : 1;
+ quint32 dirtyScroll : 1;
+ quint32 accelerateScrolling : 1;
+ quint32 keepLastCenterPoint : 1;
+ quint32 transforming : 1;
+ quint32 handScrolling : 1;
+ quint32 mustAllocateStyleOptions : 1;
+ quint32 mustResizeBackgroundPixmap : 1;
+ quint32 fullUpdatePending : 1;
+ quint32 hasUpdateClip : 1;
+ quint32 padding : 18;
+
+ QRectF sceneRect;
+ void updateLastCenterPoint();
+
+ qint64 horizontalScroll() const;
+ qint64 verticalScroll() const;
+
+ QRectF mapRectToScene(const QRect &rect) const;
+ QRectF mapRectFromScene(const QRectF &rect) const;
+
+ QRect updateClip;
+ QPointF mousePressItemPoint;
+ QPointF mousePressScenePoint;
+ QPoint mousePressViewPoint;
+ QPoint mousePressScreenPoint;
+ QPointF lastMouseMoveScenePoint;
+ QPoint lastMouseMoveScreenPoint;
+ QPoint dirtyScrollOffset;
+ Qt::MouseButton mousePressButton;
+ QTransform matrix;
+ qint64 scrollX, scrollY;
+ void updateScroll();
+
+ qreal leftIndent;
+ qreal topIndent;
+
+ // Replaying mouse events
+ QMouseEvent lastMouseEvent;
+ void replayLastMouseEvent();
+ void storeMouseEvent(QMouseEvent *event);
+ void mouseMoveEventHandler(QMouseEvent *event);
+
+ QPointF lastCenterPoint;
+ Qt::Alignment alignment;
+
+ QGraphicsView::ViewportAnchor transformationAnchor;
+ QGraphicsView::ViewportAnchor resizeAnchor;
+ QGraphicsView::ViewportUpdateMode viewportUpdateMode;
+ QGraphicsView::OptimizationFlags optimizationFlags;
+
+ QPointer<QGraphicsScene> scene;
+#ifndef QT_NO_RUBBERBAND
+ QRect rubberBandRect;
+ QRegion rubberBandRegion(const QWidget *widget, const QRect &rect) const;
+ bool rubberBanding;
+ Qt::ItemSelectionMode rubberBandSelectionMode;
+#endif
+ int handScrollMotions;
+
+ QGraphicsView::CacheMode cacheMode;
+
+ QVector<QStyleOptionGraphicsItem> styleOptions;
+ QStyleOptionGraphicsItem *allocStyleOptionsArray(int numItems);
+ void freeStyleOptionsArray(QStyleOptionGraphicsItem *array);
+
+ QBrush backgroundBrush;
+ QBrush foregroundBrush;
+ QPixmap backgroundPixmap;
+ QRegion backgroundPixmapExposed;
+
+#ifndef QT_NO_CURSOR
+ QCursor originalCursor;
+ bool hasStoredOriginalCursor;
+ void _q_setViewportCursor(const QCursor &cursor);
+ void _q_unsetViewportCursor();
+#endif
+
+ QGraphicsSceneDragDropEvent *lastDragDropEvent;
+ void storeDragDropEvent(const QGraphicsSceneDragDropEvent *event);
+ void populateSceneDragDropEvent(QGraphicsSceneDragDropEvent *dest,
+ QDropEvent *source);
+
+ QRect mapToViewRect(const QGraphicsItem *item, const QRectF &rect) const;
+ QRegion mapToViewRegion(const QGraphicsItem *item, const QRectF &rect) const;
+ QRegion dirtyRegion;
+ QRect dirtyBoundingRect;
+ void processPendingUpdates();
+ inline void updateAll()
+ {
+ viewport->update();
+ fullUpdatePending = true;
+ dirtyBoundingRect = QRect();
+ dirtyRegion = QRegion();
+ }
+
+ inline void dispatchPendingUpdateRequests()
+ {
+#ifdef Q_WS_MAC
+ // QWidget::update() works slightly different on the Mac without the raster engine;
+ // it's not part of our backing store so it needs special threatment.
+ if (QApplicationPrivate::graphics_system_name != QLatin1String("raster")) {
+ // At this point either HIViewSetNeedsDisplay (Carbon) or setNeedsDisplay: YES (Cocoa)
+ // is called, which means there's a pending update request. We want to dispatch it
+ // now because otherwise graphics view updates would require two
+ // round-trips in the event loop before the item is painted.
+ extern void qt_mac_dispatchPendingUpdateRequests(QWidget *);
+ qt_mac_dispatchPendingUpdateRequests(viewport->window());
+ } else
+#endif // !Q_WS_MAC
+ {
+ if (qt_widget_private(viewport)->paintOnScreen())
+ QCoreApplication::sendPostedEvents(viewport, QEvent::UpdateRequest);
+ else
+ QCoreApplication::sendPostedEvents(viewport->window(), QEvent::UpdateRequest);
+ }
+ }
+
+ void setUpdateClip(QGraphicsItem *);
+
+ inline bool updateRectF(const QRectF &rect)
+ {
+ if (rect.isEmpty())
+ return false;
+ if (optimizationFlags & QGraphicsView::DontAdjustForAntialiasing)
+ return updateRect(rect.toAlignedRect().adjusted(-1, -1, 1, 1));
+ return updateRect(rect.toAlignedRect().adjusted(-2, -2, 2, 2));
+ }
+
+ bool updateRect(const QRect &rect);
+ bool updateRegion(const QRectF &rect, const QTransform &xform);
+ bool updateSceneSlotReimplementedChecked;
+ QRegion exposedRegion;
+
+ QList<QGraphicsItem *> findItems(const QRegion &exposedRegion, bool *allItems,
+ const QTransform &viewTransform) const;
+
+ QPointF mapToScene(const QPointF &point) const;
+ QRectF mapToScene(const QRectF &rect) const;
+ static void translateTouchEvent(QGraphicsViewPrivate *d, QTouchEvent *touchEvent);
+ void updateInputMethodSensitivity();
+};
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_GRAPHICSVIEW
+
+#endif
diff --git a/src/widgets/graphicsview/qgraphicswidget.cpp b/src/widgets/graphicsview/qgraphicswidget.cpp
new file mode 100644
index 0000000000..675a5c5c77
--- /dev/null
+++ b/src/widgets/graphicsview/qgraphicswidget.cpp
@@ -0,0 +1,2388 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** 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 Technology Preview License Agreement accompanying
+** this package.
+**
+** 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.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qglobal.h"
+
+#ifndef QT_NO_GRAPHICSVIEW
+
+#include "qgraphicswidget.h"
+#include "qgraphicswidget_p.h"
+#include "qgraphicslayout.h"
+#include "qgraphicslayout_p.h"
+#include "qgraphicsscene.h"
+#include "qgraphicssceneevent.h"
+
+#ifndef QT_NO_ACTION
+#include <private/qaction_p.h>
+#endif
+#include <private/qapplication_p.h>
+#include <private/qgraphicsscene_p.h>
+#ifndef QT_NO_SHORTCUT
+#include <private/qshortcutmap_p.h>
+#endif
+#include <QtCore/qmutex.h>
+#include <QtGui/qapplication.h>
+#include <QtGui/qgraphicsview.h>
+#include <QtGui/qgraphicsproxywidget.h>
+#include <QtGui/qpalette.h>
+#include <QtGui/qstyleoption.h>
+
+#include <qdebug.h>
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class QGraphicsWidget
+ \brief The QGraphicsWidget class is the base class for all widget
+ items in a QGraphicsScene.
+ \since 4.4
+ \ingroup graphicsview-api
+
+ QGraphicsWidget is an extended base item that provides extra functionality
+ over QGraphicsItem. It is similar to QWidget in many ways:
+
+ \list
+ \o Provides a \l palette, a \l font and a \l style().
+ \o Has a defined geometry().
+ \o Supports layouts with setLayout() and layout().
+ \o Supports shortcuts and actions with grabShortcut() and insertAction()
+ \endlist
+
+ Unlike QGraphicsItem, QGraphicsWidget is not an abstract class; you can
+ create instances of a QGraphicsWidget without having to subclass it.
+ This approach is useful for widgets that only serve the purpose of
+ organizing child widgets into a layout.
+
+ QGraphicsWidget can be used as a base item for your own custom item if
+ you require advanced input focus handling, e.g., tab focus and activation, or
+ layouts.
+
+ Since QGraphicsWidget resembles QWidget and has similar API, it is
+ easier to port a widget from QWidget to QGraphicsWidget, instead of
+ QGraphicsItem.
+
+ \note QWidget-based widgets can be directly embedded into a
+ QGraphicsScene using QGraphicsProxyWidget.
+
+ Noticeable differences between QGraphicsWidget and QWidget are:
+
+ \table
+ \header \o QGraphicsWidget
+ \o QWidget
+ \row \o Coordinates and geometry are defined with qreals (doubles or
+ floats, depending on the platform).
+ \o QWidget uses integer geometry (QPoint, QRect).
+ \row \o The widget is already visible by default; you do not have to
+ call show() to display the widget.
+ \o QWidget is hidden by default until you call show().
+ \row \o A subset of widget attributes are supported.
+ \o All widget attributes are supported.
+ \row \o A top-level item's style defaults to QGraphicsScene::style
+ \o A top-level widget's style defaults to QApplication::style
+ \row \o Graphics View provides a custom drag and drop framework, different
+ from QWidget.
+ \o Standard drag and drop framework.
+ \row \o Widget items do not support modality.
+ \o Full modality support.
+ \endtable
+
+ QGraphicsWidget supports a subset of Qt's widget attributes,
+ (Qt::WidgetAttribute), as shown in the table below. Any attributes not
+ listed in this table are unsupported, or otherwise unused.
+
+ \table
+ \header \o Widget Attribute \o Usage
+ \row \o Qt::WA_SetLayoutDirection
+ \o Set by setLayoutDirection(), cleared by
+ unsetLayoutDirection(). You can test this attribute to
+ check if the widget has been explicitly assigned a
+ \l{QGraphicsWidget::layoutDirection()}
+ {layoutDirection}. If the attribute is not set, the
+ \l{QGraphicsWidget::layoutDirection()}
+ {layoutDirection()} is inherited.
+ \row \o Qt::WA_RightToLeft
+ \o Toggled by setLayoutDirection(). Inherited from the
+ parent/scene. If set, the widget's layout will order
+ horizontally arranged widgets from right to left.
+ \row \o Qt::WA_SetStyle
+ \o Set and cleared by setStyle(). If this attribute is
+ set, the widget has been explicitly assigned a style.
+ If it is unset, the widget will use the scene's or the
+ application's style.
+ \row \o Qt::WA_Resized
+ \o Set by setGeometry() and resize().
+ \row \o Qt::WA_SetPalette
+ \o Set by setPalette().
+ \row \o Qt::WA_SetFont
+ \o Set by setPalette().
+ \row \o Qt::WA_WindowPropagation
+ \o Enables propagation to window widgets.
+ \endtable
+
+ Although QGraphicsWidget inherits from both QObject and QGraphicsItem,
+ you should use the functions provided by QGraphicsItem, \e not QObject, to
+ manage the relationships between parent and child items. These functions
+ control the stacking order of items as well as their ownership.
+
+ \note The QObject::parent() should always return 0 for QGraphicsWidgets,
+ but this policy is not strictly defined.
+
+ \sa QGraphicsProxyWidget, QGraphicsItem, {Widgets and Layouts}
+*/
+
+/*!
+ Constructs a QGraphicsWidget instance. The optional \a parent argument is
+ passed to QGraphicsItem's constructor. The optional \a wFlags argument
+ specifies the widget's window flags (e.g., whether the widget should be a
+ window, a tool, a popup, etc).
+*/
+QGraphicsWidget::QGraphicsWidget(QGraphicsItem *parent, Qt::WindowFlags wFlags)
+ : QGraphicsObject(*new QGraphicsWidgetPrivate, 0, 0), QGraphicsLayoutItem(0, false)
+{
+ Q_D(QGraphicsWidget);
+ d->init(parent, wFlags);
+}
+
+/*!
+ \internal
+
+ Constructs a new QGraphicsWidget, using \a dd as parent.
+*/
+QGraphicsWidget::QGraphicsWidget(QGraphicsWidgetPrivate &dd, QGraphicsItem *parent, QGraphicsScene *scene, Qt::WindowFlags wFlags)
+ : QGraphicsObject(dd, 0, scene), QGraphicsLayoutItem(0, false)
+{
+ Q_D(QGraphicsWidget);
+ d->init(parent, wFlags);
+}
+
+/*
+ \internal
+ \class QGraphicsWidgetStyles
+
+ We use this thread-safe class to maintain a hash of styles for widgets
+ styles. Note that QApplication::style() itself isn't thread-safe, QStyle
+ isn't thread-safe, and we don't have a thread-safe factory for creating
+ the default style, nor cloning a style.
+*/
+class QGraphicsWidgetStyles
+{
+public:
+ QStyle *styleForWidget(const QGraphicsWidget *widget) const
+ {
+ QMutexLocker locker(&mutex);
+ return styles.value(widget, 0);
+ }
+
+ void setStyleForWidget(QGraphicsWidget *widget, QStyle *style)
+ {
+ QMutexLocker locker(&mutex);
+ if (style)
+ styles[widget] = style;
+ else
+ styles.remove(widget);
+ }
+
+private:
+ QMap<const QGraphicsWidget *, QStyle *> styles;
+ mutable QMutex mutex;
+};
+Q_GLOBAL_STATIC(QGraphicsWidgetStyles, widgetStyles)
+
+/*!
+ Destroys the QGraphicsWidget instance.
+*/
+QGraphicsWidget::~QGraphicsWidget()
+{
+ Q_D(QGraphicsWidget);
+#ifndef QT_NO_ACTION
+ // Remove all actions from this widget
+ for (int i = 0; i < d->actions.size(); ++i) {
+ QActionPrivate *apriv = d->actions.at(i)->d_func();
+ apriv->graphicsWidgets.removeAll(this);
+ }
+ d->actions.clear();
+#endif
+
+ if (QGraphicsScene *scn = scene()) {
+ QGraphicsScenePrivate *sceneD = scn->d_func();
+ if (sceneD->tabFocusFirst == this)
+ sceneD->tabFocusFirst = (d->focusNext == this ? 0 : d->focusNext);
+ }
+ d->focusPrev->d_func()->focusNext = d->focusNext;
+ d->focusNext->d_func()->focusPrev = d->focusPrev;
+
+ // Play it really safe
+ d->focusNext = this;
+ d->focusPrev = this;
+
+ clearFocus();
+
+ //we check if we have a layout previously
+ if (d->layout) {
+ QGraphicsLayout *temp = d->layout;
+ foreach (QGraphicsItem * item, childItems()) {
+ // In case of a custom layout which doesn't remove and delete items, we ensure that
+ // the parent layout item does not point to the deleted layout. This code is here to
+ // avoid regression from 4.4 to 4.5, because according to 4.5 docs it is not really needed.
+ if (item->isWidget()) {
+ QGraphicsWidget *widget = static_cast<QGraphicsWidget *>(item);
+ if (widget->parentLayoutItem() == d->layout)
+ widget->setParentLayoutItem(0);
+ }
+ }
+ d->layout = 0;
+ delete temp;
+ }
+
+ // Remove this graphics widget from widgetStyles
+ widgetStyles()->setStyleForWidget(this, 0);
+}
+
+/*!
+ \property QGraphicsWidget::size
+ \brief the size of the widget
+
+ Calling resize() resizes the widget to a \a size bounded by minimumSize()
+ and maximumSize(). This property only affects the widget's width and
+ height (e.g., its right and bottom edges); the widget's position and
+ top-left corner remains unaffected.
+
+ Resizing a widget triggers the widget to immediately receive a
+ \l{QEvent::GraphicsSceneResize}{GraphicsSceneResize} event with the
+ widget's old and new size. If the widget has a layout assigned when this
+ event arrives, the layout will be activated and it will automatically
+ update any child widgets's geometry.
+
+ This property does not affect any layout of the parent widget. If the
+ widget itself is managed by a parent layout; e.g., it has a parent widget
+ with a layout assigned, that layout will not activate.
+
+ By default, this property contains a size with zero width and height.
+
+ \sa setGeometry(), QGraphicsSceneResizeEvent, QGraphicsLayout
+*/
+QSizeF QGraphicsWidget::size() const
+{
+ return QGraphicsLayoutItem::geometry().size();
+}
+
+void QGraphicsWidget::resize(const QSizeF &size)
+{
+ setGeometry(QRectF(pos(), size));
+}
+
+/*!
+ \fn void QGraphicsWidget::resize(qreal w, qreal h)
+
+ This convenience function is equivalent to calling resize(QSizeF(w, h)).
+
+ \sa setGeometry(), setTransform()
+*/
+
+/*!
+ \property QGraphicsWidget::sizePolicy
+ \brief the size policy for the widget
+ \sa sizePolicy(), setSizePolicy(), QWidget::sizePolicy()
+*/
+
+/*!
+ \fn QGraphicsWidget::geometryChanged()
+
+ This signal gets emitted whenever the geometry is changed in setGeometry().
+*/
+
+/*!
+ \property QGraphicsWidget::geometry
+ \brief the geometry of the widget
+
+ Sets the item's geometry to \a rect. The item's position and size are
+ modified as a result of calling this function. The item is first moved,
+ then resized.
+
+ A side effect of calling this function is that the widget will receive
+ a move event and a resize event. Also, if the widget has a layout
+ assigned, the layout will activate.
+
+ \sa geometry(), resize()
+*/
+void QGraphicsWidget::setGeometry(const QRectF &rect)
+{
+ QGraphicsWidgetPrivate *wd = QGraphicsWidget::d_func();
+ QGraphicsLayoutItemPrivate *d = QGraphicsLayoutItem::d_ptr.data();
+ QRectF newGeom;
+ QPointF oldPos = d->geom.topLeft();
+ if (!wd->inSetPos) {
+ setAttribute(Qt::WA_Resized);
+ newGeom = rect;
+ newGeom.setSize(rect.size().expandedTo(effectiveSizeHint(Qt::MinimumSize))
+ .boundedTo(effectiveSizeHint(Qt::MaximumSize)));
+ if (newGeom == d->geom)
+ return;
+
+ // setPos triggers ItemPositionChange, which can adjust position
+ wd->inSetGeometry = 1;
+ setPos(newGeom.topLeft());
+ wd->inSetGeometry = 0;
+ newGeom.moveTopLeft(pos());
+
+ if (newGeom == d->geom)
+ return;
+
+ // Update and prepare to change the geometry (remove from index) if the size has changed.
+ if (wd->scene) {
+ if (rect.topLeft() == d->geom.topLeft()) {
+ prepareGeometryChange();
+ }
+ }
+ }
+
+ // Update the layout item geometry
+ bool moved = oldPos != pos();
+ if (moved) {
+ // Send move event.
+ QGraphicsSceneMoveEvent event;
+ event.setOldPos(oldPos);
+ event.setNewPos(pos());
+ QApplication::sendEvent(this, &event);
+ if (wd->inSetPos) {
+ //set the new pos
+ d->geom.moveTopLeft(pos());
+ emit geometryChanged();
+ return;
+ }
+ }
+ QSizeF oldSize = size();
+ QGraphicsLayoutItem::setGeometry(newGeom);
+ // Send resize event
+ bool resized = newGeom.size() != oldSize;
+ if (resized) {
+ QGraphicsSceneResizeEvent re;
+ re.setOldSize(oldSize);
+ re.setNewSize(newGeom.size());
+ if (oldSize.width() != newGeom.size().width())
+ emit widthChanged();
+ if (oldSize.height() != newGeom.size().height())
+ emit heightChanged();
+ QApplication::sendEvent(this, &re);
+ }
+ emit geometryChanged();
+}
+
+/*!
+ \fn QRectF QGraphicsWidget::rect() const
+
+ Returns the item's local rect as a QRectF. This function is equivalent
+ to QRectF(QPointF(), size()).
+
+ \sa setGeometry(), resize()
+*/
+
+/*!
+ \fn void QGraphicsWidget::setGeometry(qreal x, qreal y, qreal w, qreal h)
+
+ This convenience function is equivalent to calling setGeometry(QRectF(
+ \a x, \a y, \a w, \a h)).
+
+ \sa geometry(), resize()
+*/
+
+/*!
+ \property QGraphicsWidget::minimumSize
+ \brief the minimum size of the widget
+
+ \sa setMinimumSize(), minimumSize(), preferredSize, maximumSize
+*/
+
+/*!
+ \property QGraphicsWidget::preferredSize
+ \brief the preferred size of the widget
+
+ \sa setPreferredSize(), preferredSize(), minimumSize, maximumSize
+*/
+
+/*!
+ \property QGraphicsWidget::maximumSize
+ \brief the maximum size of the widget
+
+ \sa setMaximumSize(), maximumSize(), minimumSize, preferredSize
+*/
+
+/*!
+ Sets the widget's contents margins to \a left, \a top, \a right and \a
+ bottom.
+
+ Contents margins are used by the assigned layout to define the placement
+ of subwidgets and layouts. Margins are particularly useful for widgets
+ that constrain subwidgets to only a section of its own geometry. For
+ example, a group box with a layout will place subwidgets inside its frame,
+ but below the title.
+
+ Changing a widget's contents margins will always trigger an update(), and
+ any assigned layout will be activated automatically. The widget will then
+ receive a \l{QEvent::ContentsRectChange}{ContentsRectChange} event.
+
+ \sa getContentsMargins(), setGeometry()
+*/
+void QGraphicsWidget::setContentsMargins(qreal left, qreal top, qreal right, qreal bottom)
+{
+ Q_D(QGraphicsWidget);
+
+ if (!d->margins && left == 0 && top == 0 && right == 0 && bottom == 0)
+ return;
+ d->ensureMargins();
+ if (left == d->margins[d->Left]
+ && top == d->margins[d->Top]
+ && right == d->margins[d->Right]
+ && bottom == d->margins[d->Bottom])
+ return;
+
+ d->margins[d->Left] = left;
+ d->margins[d->Top] = top;
+ d->margins[d->Right] = right;
+ d->margins[d->Bottom] = bottom;
+
+ if (QGraphicsLayout *l = d->layout)
+ l->invalidate();
+ else
+ updateGeometry();
+
+ QEvent e(QEvent::ContentsRectChange);
+ QApplication::sendEvent(this, &e);
+}
+
+/*!
+ Gets the widget's contents margins. The margins are stored in \a left, \a
+ top, \a right and \a bottom, as pointers to qreals. Each argument can
+ be \e {omitted} by passing 0.
+
+ \sa setContentsMargins()
+*/
+void QGraphicsWidget::getContentsMargins(qreal *left, qreal *top, qreal *right, qreal *bottom) const
+{
+ Q_D(const QGraphicsWidget);
+ if (left || top || right || bottom)
+ d->ensureMargins();
+ if (left)
+ *left = d->margins[d->Left];
+ if (top)
+ *top = d->margins[d->Top];
+ if (right)
+ *right = d->margins[d->Right];
+ if (bottom)
+ *bottom = d->margins[d->Bottom];
+}
+
+/*!
+ Sets the widget's window frame margins to \a left, \a top, \a right and
+ \a bottom. The default frame margins are provided by the style, and they
+ depend on the current window flags.
+
+ If you would like to draw your own window decoration, you can set your
+ own frame margins to override the default margins.
+
+ \sa unsetWindowFrameMargins(), getWindowFrameMargins(), windowFrameRect()
+*/
+void QGraphicsWidget::setWindowFrameMargins(qreal left, qreal top, qreal right, qreal bottom)
+{
+ Q_D(QGraphicsWidget);
+
+ if (!d->windowFrameMargins && left == 0 && top == 0 && right == 0 && bottom == 0)
+ return;
+ d->ensureWindowFrameMargins();
+ bool unchanged =
+ d->windowFrameMargins[d->Left] == left
+ && d->windowFrameMargins[d->Top] == top
+ && d->windowFrameMargins[d->Right] == right
+ && d->windowFrameMargins[d->Bottom] == bottom;
+ if (d->setWindowFrameMargins && unchanged)
+ return;
+ if (!unchanged)
+ prepareGeometryChange();
+ d->windowFrameMargins[d->Left] = left;
+ d->windowFrameMargins[d->Top] = top;
+ d->windowFrameMargins[d->Right] = right;
+ d->windowFrameMargins[d->Bottom] = bottom;
+ d->setWindowFrameMargins = true;
+}
+
+/*!
+ Gets the widget's window frame margins. The margins are stored in \a left,
+ \a top, \a right and \a bottom as pointers to qreals. Each argument can
+ be \e {omitted} by passing 0.
+
+ \sa setWindowFrameMargins(), windowFrameRect()
+*/
+void QGraphicsWidget::getWindowFrameMargins(qreal *left, qreal *top, qreal *right, qreal *bottom) const
+{
+ Q_D(const QGraphicsWidget);
+ if (left || top || right || bottom)
+ d->ensureWindowFrameMargins();
+ if (left)
+ *left = d->windowFrameMargins[d->Left];
+ if (top)
+ *top = d->windowFrameMargins[d->Top];
+ if (right)
+ *right = d->windowFrameMargins[d->Right];
+ if (bottom)
+ *bottom = d->windowFrameMargins[d->Bottom];
+}
+
+/*!
+ Resets the window frame margins to the default value, provided by the style.
+
+ \sa setWindowFrameMargins(), getWindowFrameMargins(), windowFrameRect()
+*/
+void QGraphicsWidget::unsetWindowFrameMargins()
+{
+ Q_D(QGraphicsWidget);
+ if ((d->windowFlags & Qt::Window) && (d->windowFlags & Qt::WindowType_Mask) != Qt::Popup &&
+ (d->windowFlags & Qt::WindowType_Mask) != Qt::ToolTip && !(d->windowFlags & Qt::FramelessWindowHint)) {
+ QStyleOptionTitleBar bar;
+ d->initStyleOptionTitleBar(&bar);
+ QStyle *style = this->style();
+ qreal margin = style->pixelMetric(QStyle::PM_MdiSubWindowFrameWidth);
+ qreal titleBarHeight = d->titleBarHeight(bar);
+ setWindowFrameMargins(margin, titleBarHeight, margin, margin);
+ } else {
+ setWindowFrameMargins(0, 0, 0, 0);
+ }
+ d->setWindowFrameMargins = false;
+}
+
+/*!
+ Returns the widget's geometry in parent coordinates including any window
+ frame.
+
+ \sa windowFrameRect(), getWindowFrameMargins(), setWindowFrameMargins()
+*/
+QRectF QGraphicsWidget::windowFrameGeometry() const
+{
+ Q_D(const QGraphicsWidget);
+ return d->windowFrameMargins
+ ? geometry().adjusted(-d->windowFrameMargins[d->Left], -d->windowFrameMargins[d->Top],
+ d->windowFrameMargins[d->Right], d->windowFrameMargins[d->Bottom])
+ : geometry();
+}
+
+/*!
+ Returns the widget's local rect including any window frame.
+
+ \sa windowFrameGeometry(), getWindowFrameMargins(), setWindowFrameMargins()
+*/
+QRectF QGraphicsWidget::windowFrameRect() const
+{
+ Q_D(const QGraphicsWidget);
+ return d->windowFrameMargins
+ ? rect().adjusted(-d->windowFrameMargins[d->Left], -d->windowFrameMargins[d->Top],
+ d->windowFrameMargins[d->Right], d->windowFrameMargins[d->Bottom])
+ : rect();
+}
+
+/*!
+ Populates a style option object for this widget based on its current
+ state, and stores the output in \a option. The default implementation
+ populates \a option with the following properties.
+
+ \table
+ \header
+ \o Style Option Property
+ \o Value
+ \row
+ \o state & QStyle::State_Enabled
+ \o Corresponds to QGraphicsItem::isEnabled().
+ \row
+ \o state & QStyle::State_HasFocus
+ \o Corresponds to QGraphicsItem::hasFocus().
+ \row
+ \o state & QStyle::State_MouseOver
+ \o Corresponds to QGraphicsItem::isUnderMouse().
+ \row
+ \o direction
+ \o Corresponds to QGraphicsWidget::layoutDirection().
+ \row
+ \o rect
+ \o Corresponds to QGraphicsWidget::rect().toRect().
+ \row
+ \o palette
+ \o Corresponds to QGraphicsWidget::palette().
+ \row
+ \o fontMetrics
+ \o Corresponds to QFontMetrics(QGraphicsWidget::font()).
+ \endtable
+
+ Subclasses of QGraphicsWidget should call the base implementation, and
+ then test the type of \a option using qstyleoption_cast<>() or test
+ QStyleOption::Type before storing widget-specific options.
+
+ For example:
+
+ \snippet doc/src/snippets/code/src_gui_graphicsview_qgraphicswidget.cpp 0
+
+ \sa QStyleOption::initFrom()
+*/
+void QGraphicsWidget::initStyleOption(QStyleOption *option) const
+{
+ Q_ASSERT(option);
+
+ option->state = QStyle::State_None;
+ if (isEnabled())
+ option->state |= QStyle::State_Enabled;
+ if (hasFocus())
+ option->state |= QStyle::State_HasFocus;
+ // if (window->testAttribute(Qt::WA_KeyboardFocusChange)) // ### Window
+ // option->state |= QStyle::State_KeyboardFocusChange;
+ if (isUnderMouse())
+ option->state |= QStyle::State_MouseOver;
+ if (QGraphicsWidget *w = window()) {
+ if (w->isActiveWindow())
+ option->state |= QStyle::State_Active;
+ }
+ if (isWindow())
+ option->state |= QStyle::State_Window;
+ /*
+ ###
+#ifdef Q_WS_MAC
+ extern bool qt_mac_can_clickThrough(const QGraphicsWidget *w); //qwidget_mac.cpp
+ if (!(option->state & QStyle::State_Active) && !qt_mac_can_clickThrough(widget))
+ option->state &= ~QStyle::State_Enabled;
+
+ switch (QMacStyle::widgetSizePolicy(widget)) {
+ case QMacStyle::SizeSmall:
+ option->state |= QStyle::State_Small;
+ break;
+ case QMacStyle::SizeMini:
+ option->state |= QStyle::State_Mini;
+ break;
+ default:
+ ;
+ }
+#endif
+#ifdef QT_KEYPAD_NAVIGATION
+ if (widget->hasEditFocus())
+ state |= QStyle::State_HasEditFocus;
+#endif
+ */
+ option->direction = layoutDirection();
+ option->rect = rect().toRect(); // ### truncation!
+ option->palette = palette();
+ if (!isEnabled()) {
+ option->palette.setCurrentColorGroup(QPalette::Disabled);
+ } else if (isActiveWindow()) {
+ option->palette.setCurrentColorGroup(QPalette::Active);
+ } else {
+ option->palette.setCurrentColorGroup(QPalette::Inactive);
+ }
+ option->fontMetrics = QFontMetrics(font());
+}
+
+/*!
+ \reimp
+*/
+QSizeF QGraphicsWidget::sizeHint(Qt::SizeHint which, const QSizeF &constraint) const
+{
+ Q_D(const QGraphicsWidget);
+ QSizeF sh;
+ if (d->layout) {
+ QSizeF marginSize(0,0);
+ if (d->margins) {
+ marginSize = QSizeF(d->margins[d->Left] + d->margins[d->Right],
+ d->margins[d->Top] + d->margins[d->Bottom]);
+ }
+ sh = d->layout->effectiveSizeHint(which, constraint - marginSize);
+ sh += marginSize;
+ } else {
+ switch (which) {
+ case Qt::MinimumSize:
+ sh = QSizeF(0, 0);
+ break;
+ case Qt::PreferredSize:
+ sh = QSizeF(50, 50); //rather arbitrary
+ break;
+ case Qt::MaximumSize:
+ sh = QSizeF(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX);
+ break;
+ default:
+ qWarning("QGraphicsWidget::sizeHint(): Don't know how to handle the value of 'which'");
+ break;
+ }
+ }
+ return sh;
+}
+
+/*!
+ \property QGraphicsWidget::layout
+ \brief The layout of the widget
+
+ Any existing layout manager is deleted before the new layout is assigned. If
+ \a layout is 0, the widget is left without a layout. Existing subwidgets'
+ geometries will remain unaffected.
+
+ QGraphicsWidget takes ownership of \a layout.
+
+ All widgets that are currently managed by \a layout or all of its
+ sublayouts, are automatically reparented to this item. The layout is then
+ invalidated, and the child widget geometries are adjusted according to
+ this item's geometry() and contentsMargins(). Children who are not
+ explicitly managed by \a layout remain unaffected by the layout after
+ it has been assigned to this widget.
+
+ If no layout is currently managing this widget, layout() will return 0.
+
+*/
+
+/*!
+ \fn void QGraphicsWidget::layoutChanged()
+ This signal gets emitted whenever the layout of the item changes
+ \internal
+*/
+
+/*!
+ Returns this widget's layout, or 0 if no layout is currently managing this
+ widget.
+
+ \sa setLayout()
+*/
+QGraphicsLayout *QGraphicsWidget::layout() const
+{
+ Q_D(const QGraphicsWidget);
+ return d->layout;
+}
+
+/*!
+ \fn void QGraphicsWidget::setLayout(QGraphicsLayout *layout)
+
+ Sets the layout for this widget to \a layout. Any existing layout manager
+ is deleted before the new layout is assigned. If \a layout is 0, the
+ widget is left without a layout. Existing subwidgets' geometries will
+ remain unaffected.
+
+ All widgets that are currently managed by \a layout or all of its
+ sublayouts, are automatically reparented to this item. The layout is then
+ invalidated, and the child widget geometries are adjusted according to
+ this item's geometry() and contentsMargins(). Children who are not
+ explicitly managed by \a layout remain unaffected by the layout after
+ it has been assigned to this widget.
+
+ QGraphicsWidget takes ownership of \a layout.
+
+ \sa layout(), QGraphicsLinearLayout::addItem(), QGraphicsLayout::invalidate()
+*/
+void QGraphicsWidget::setLayout(QGraphicsLayout *l)
+{
+ Q_D(QGraphicsWidget);
+ if (d->layout == l)
+ return;
+ d->setLayout_helper(l);
+ if (!l)
+ return;
+
+ // Prevent assigning a layout that is already assigned to another widget.
+ QGraphicsLayoutItem *oldParent = l->parentLayoutItem();
+ if (oldParent && oldParent != this) {
+ qWarning("QGraphicsWidget::setLayout: Attempting to set a layout on %s"
+ " \"%s\", when the layout already has a parent",
+ metaObject()->className(), qPrintable(objectName()));
+ return;
+ }
+
+ // Install and activate the layout.
+ l->setParentLayoutItem(this);
+ l->d_func()->reparentChildItems(this);
+ l->invalidate();
+ emit layoutChanged();
+}
+
+/*!
+ Adjusts the size of the widget to its effective preferred size hint.
+
+ This function is called implicitly when the item is shown for the first
+ time.
+
+ \sa effectiveSizeHint(), Qt::MinimumSize
+*/
+void QGraphicsWidget::adjustSize()
+{
+ QSizeF sz = effectiveSizeHint(Qt::PreferredSize);
+ // What if sz is not valid?!
+ if (sz.isValid())
+ resize(sz);
+}
+
+/*!
+ \property QGraphicsWidget::layoutDirection
+ \brief the layout direction for this widget.
+
+ This property modifies this widget's and all of its descendants'
+ Qt::WA_RightToLeft attribute. It also sets this widget's
+ Qt::WA_SetLayoutDirection attribute.
+
+ The widget's layout direction determines the order in which the layout
+ manager horizontally arranges subwidgets of this widget. The default
+ value depends on the language and locale of the application, and is
+ typically in the same direction as words are read and written. With
+ Qt::LeftToRight, the layout starts placing subwidgets from the left
+ side of this widget towards the right. Qt::RightToLeft does the opposite -
+ the layout will place widgets starting from the right edge moving towards
+ the left.
+
+ Subwidgets inherit their layout direction from the parent. Top-level
+ widget items inherit their layout direction from
+ QGraphicsScene::layoutDirection. If you change a widget's layout direction
+ by calling setLayoutDirection(), the widget will send itself a
+ \l{QEvent::LayoutDirectionChange}{LayoutDirectionChange} event, and then
+ propagate the new layout direction to all its descendants.
+
+ \sa QWidget::layoutDirection, QApplication::layoutDirection
+*/
+Qt::LayoutDirection QGraphicsWidget::layoutDirection() const
+{
+ return testAttribute(Qt::WA_RightToLeft) ? Qt::RightToLeft : Qt::LeftToRight;
+}
+void QGraphicsWidget::setLayoutDirection(Qt::LayoutDirection direction)
+{
+ Q_D(QGraphicsWidget);
+ setAttribute(Qt::WA_SetLayoutDirection, true);
+ d->setLayoutDirection_helper(direction);
+}
+void QGraphicsWidget::unsetLayoutDirection()
+{
+ Q_D(QGraphicsWidget);
+ setAttribute(Qt::WA_SetLayoutDirection, false);
+ d->resolveLayoutDirection();
+}
+
+/*!
+ Returns a pointer to the widget's style. If this widget does not have any
+ explicitly assigned style, the scene's style is returned instead. In turn,
+ if the scene does not have any assigned style, this function returns
+ QApplication::style().
+
+ \sa setStyle()
+*/
+QStyle *QGraphicsWidget::style() const
+{
+ if (QStyle *style = widgetStyles()->styleForWidget(this))
+ return style;
+ // ### This is not thread-safe. QApplication::style() is not thread-safe.
+ return scene() ? scene()->style() : QApplication::style();
+}
+
+/*!
+ Sets the widget's style to \a style. QGraphicsWidget does \e not take
+ ownership of \a style.
+
+ If no style is assigned, or \a style is 0, the widget will use
+ QGraphicsScene::style() (if this has been set). Otherwise the widget will
+ use QApplication::style().
+
+ This function sets the Qt::WA_SetStyle attribute if \a style is not 0;
+ otherwise it clears the attribute.
+
+ \sa style()
+*/
+void QGraphicsWidget::setStyle(QStyle *style)
+{
+ setAttribute(Qt::WA_SetStyle, style != 0);
+ widgetStyles()->setStyleForWidget(this, style);
+
+ // Deliver StyleChange to the widget itself (doesn't propagate).
+ QEvent event(QEvent::StyleChange);
+ QApplication::sendEvent(this, &event);
+}
+
+/*!
+ \property QGraphicsWidget::font
+ \brief the widgets' font
+
+ This property provides the widget's font.
+
+ QFont consists of font properties that have been explicitly defined and
+ properties implicitly inherited from the widget's parent. Hence, font()
+ can return a different font compared to the one set with setFont().
+ This scheme allows you to define single entries in a font without
+ affecting the font's inherited entries.
+
+ When a widget's font changes, it resolves its entries against its
+ parent widget. If the widget does not have a parent widget, it resolves
+ its entries against the scene. The widget then sends itself a
+ \l{QEvent::FontChange}{FontChange} event and notifies all its
+ descendants so that they can resolve their fonts as well.
+
+ By default, this property contains the application's default font.
+
+ \sa QApplication::font(), QGraphicsScene::font, QFont::resolve()
+*/
+QFont QGraphicsWidget::font() const
+{
+ Q_D(const QGraphicsWidget);
+ QFont fnt = d->font;
+ fnt.resolve(fnt.resolve() | d->inheritedFontResolveMask);
+ return fnt;
+}
+void QGraphicsWidget::setFont(const QFont &font)
+{
+ Q_D(QGraphicsWidget);
+ setAttribute(Qt::WA_SetFont, font.resolve() != 0);
+
+ QFont naturalFont = d->naturalWidgetFont();
+ QFont resolvedFont = font.resolve(naturalFont);
+ d->setFont_helper(resolvedFont);
+}
+
+/*!
+ \property QGraphicsWidget::palette
+ \brief the widget's palette
+
+ This property provides the widget's palette. The palette provides colors
+ and brushes for color groups (e.g., QPalette::Button) and states (e.g.,
+ QPalette::Inactive), loosely defining the general look of the widget and
+ its children.
+
+ QPalette consists of color groups that have been explicitly defined, and
+ groups that are implicitly inherited from the widget's parent. Because of
+ this, palette() can return a different palette than what has been set with
+ setPalette(). This scheme allows you to define single entries in a palette
+ without affecting the palette's inherited entries.
+
+ When a widget's palette changes, it resolves its entries against its
+ parent widget, or if it doesn't have a parent widget, it resolves against
+ the scene. It then sends itself a \l{QEvent::PaletteChange}{PaletteChange}
+ event, and notifies all its descendants so they can resolve their palettes
+ as well.
+
+ By default, this property contains the application's default palette.
+
+ \sa QApplication::palette(), QGraphicsScene::palette, QPalette::resolve()
+*/
+QPalette QGraphicsWidget::palette() const
+{
+ Q_D(const QGraphicsWidget);
+ return d->palette;
+}
+void QGraphicsWidget::setPalette(const QPalette &palette)
+{
+ Q_D(QGraphicsWidget);
+ setAttribute(Qt::WA_SetPalette, palette.resolve() != 0);
+
+ QPalette naturalPalette = d->naturalWidgetPalette();
+ QPalette resolvedPalette = palette.resolve(naturalPalette);
+ d->setPalette_helper(resolvedPalette);
+}
+
+/*!
+ \property QGraphicsWidget::autoFillBackground
+ \brief whether the widget background is filled automatically
+ \since 4.7
+
+ If enabled, this property will cause Qt to fill the background of the
+ widget before invoking the paint() method. The color used is defined by the
+ QPalette::Window color role from the widget's \l{QPalette}{palette}.
+
+ In addition, Windows are always filled with QPalette::Window, unless the
+ WA_OpaquePaintEvent or WA_NoSystemBackground attributes are set.
+
+ By default, this property is false.
+
+ \sa Qt::WA_OpaquePaintEvent, Qt::WA_NoSystemBackground,
+*/
+bool QGraphicsWidget::autoFillBackground() const
+{
+ Q_D(const QGraphicsWidget);
+ return d->autoFillBackground;
+}
+void QGraphicsWidget::setAutoFillBackground(bool enabled)
+{
+ Q_D(QGraphicsWidget);
+ if (d->autoFillBackground != enabled) {
+ d->autoFillBackground = enabled;
+ update();
+ }
+}
+
+/*!
+ If this widget is currently managed by a layout, this function notifies
+ the layout that the widget's size hints have changed and the layout
+ may need to resize and reposition the widget accordingly.
+
+ Call this function if the widget's sizeHint() has changed.
+
+ \sa QGraphicsLayout::invalidate()
+*/
+void QGraphicsWidget::updateGeometry()
+{
+ QGraphicsLayoutItem::updateGeometry();
+ QGraphicsLayoutItem *parentItem = parentLayoutItem();
+
+ if (parentItem && parentItem->isLayout()) {
+ parentItem->updateGeometry();
+ } else {
+ if (parentItem) {
+ QGraphicsWidget *parentWid = parentWidget(); //###
+ if (parentWid->isVisible())
+ QApplication::postEvent(parentWid, new QEvent(QEvent::LayoutRequest));
+ }
+ bool wasResized = testAttribute(Qt::WA_Resized);
+ resize(size()); // this will restrict the size
+ setAttribute(Qt::WA_Resized, wasResized);
+ }
+}
+
+/*!
+ \reimp
+
+ QGraphicsWidget uses the base implementation of this function to catch and
+ deliver events related to state changes in the item. Because of this, it is
+ very important that subclasses call the base implementation.
+
+ \a change specifies the type of change, and \a value is the new value.
+
+ For example, QGraphicsWidget uses ItemVisibleChange to deliver
+ \l{QEvent::Show} {Show} and \l{QEvent::Hide}{Hide} events,
+ ItemPositionHasChanged to deliver \l{QEvent::Move}{Move} events,
+ and ItemParentChange both to deliver \l{QEvent::ParentChange}
+ {ParentChange} events, and for managing the focus chain.
+
+ QGraphicsWidget enables the ItemSendsGeometryChanges flag by default in
+ order to track position changes.
+
+ \sa QGraphicsItem::itemChange()
+*/
+QVariant QGraphicsWidget::itemChange(GraphicsItemChange change, const QVariant &value)
+{
+ Q_D(QGraphicsWidget);
+ switch (change) {
+ case ItemEnabledHasChanged: {
+ // Send EnabledChange after the enabled state has changed.
+ QEvent event(QEvent::EnabledChange);
+ QApplication::sendEvent(this, &event);
+ break;
+ }
+ case ItemVisibleChange:
+ if (value.toBool()) {
+ // Send Show event before the item has been shown.
+ QShowEvent event;
+ QApplication::sendEvent(this, &event);
+ bool resized = testAttribute(Qt::WA_Resized);
+ if (!resized) {
+ adjustSize();
+ setAttribute(Qt::WA_Resized, false);
+ }
+ }
+ break;
+ case ItemVisibleHasChanged:
+ if (!value.toBool()) {
+ // Send Hide event after the item has been hidden.
+ QHideEvent event;
+ QApplication::sendEvent(this, &event);
+ }
+ break;
+ case ItemPositionHasChanged:
+ d->setGeometryFromSetPos();
+ break;
+ case ItemParentChange: {
+ // Deliver ParentAboutToChange.
+ QEvent event(QEvent::ParentAboutToChange);
+ QApplication::sendEvent(this, &event);
+ break;
+ }
+ case ItemParentHasChanged: {
+ // Deliver ParentChange.
+ QEvent event(QEvent::ParentChange);
+ QApplication::sendEvent(this, &event);
+ break;
+ }
+ case ItemCursorHasChanged: {
+ // Deliver CursorChange.
+ QEvent event(QEvent::CursorChange);
+ QApplication::sendEvent(this, &event);
+ break;
+ }
+ case ItemToolTipHasChanged: {
+ // Deliver ToolTipChange.
+ QEvent event(QEvent::ToolTipChange);
+ QApplication::sendEvent(this, &event);
+ break;
+ }
+ default:
+ break;
+ }
+ return QGraphicsItem::itemChange(change, value);
+}
+
+/*!
+ \internal
+
+ This virtual function is used to notify changes to any property (both
+ dynamic properties, and registered with Q_PROPERTY) in the
+ widget. Depending on the property itself, the notification can be
+ delivered before or after the value has changed.
+
+ \a propertyName is the name of the property (e.g., "size" or "font"), and
+ \a value is the (proposed) new value of the property. The function returns
+ the new value, which may be different from \a value if the notification
+ supports adjusting the property value. The base implementation simply
+ returns \a value for any \a propertyName.
+
+ QGraphicsWidget delivers notifications for the following properties:
+
+ \table \o propertyName \o Property
+ \row \o layoutDirection \o QGraphicsWidget::layoutDirection
+ \row \o size \o QGraphicsWidget::size
+ \row \o font \o QGraphicsWidget::font
+ \row \o palette \o QGraphicsWidget::palette
+ \endtable
+
+ \sa itemChange()
+*/
+QVariant QGraphicsWidget::propertyChange(const QString &propertyName, const QVariant &value)
+{
+ Q_UNUSED(propertyName);
+ return value;
+}
+
+/*!
+ QGraphicsWidget's implementation of sceneEvent() simply passes \a event to
+ QGraphicsWidget::event(). You can handle all events for your widget in
+ event() or in any of the convenience functions; you should not have to
+ reimplement this function in a subclass of QGraphicsWidget.
+
+ \sa QGraphicsItem::sceneEvent()
+*/
+bool QGraphicsWidget::sceneEvent(QEvent *event)
+{
+ return QGraphicsItem::sceneEvent(event);
+}
+
+/*!
+ This event handler, for \a event, receives events for the window frame if
+ this widget is a window. Its base implementation provides support for
+ default window frame interaction such as moving, resizing, etc.
+
+ You can reimplement this handler in a subclass of QGraphicsWidget to
+ provide your own custom window frame interaction support.
+
+ Returns true if \a event has been recognized and processed; otherwise,
+ returns false.
+
+ \sa event()
+*/
+bool QGraphicsWidget::windowFrameEvent(QEvent *event)
+{
+ Q_D(QGraphicsWidget);
+ switch (event->type()) {
+ case QEvent::GraphicsSceneMousePress:
+ d->windowFrameMousePressEvent(static_cast<QGraphicsSceneMouseEvent *>(event));
+ break;
+ case QEvent::GraphicsSceneMouseMove:
+ d->ensureWindowData();
+ if (d->windowData->grabbedSection != Qt::NoSection) {
+ d->windowFrameMouseMoveEvent(static_cast<QGraphicsSceneMouseEvent *>(event));
+ event->accept();
+ }
+ break;
+ case QEvent::GraphicsSceneMouseRelease:
+ d->windowFrameMouseReleaseEvent(static_cast<QGraphicsSceneMouseEvent *>(event));
+ break;
+ case QEvent::GraphicsSceneHoverMove:
+ d->windowFrameHoverMoveEvent(static_cast<QGraphicsSceneHoverEvent *>(event));
+ break;
+ case QEvent::GraphicsSceneHoverLeave:
+ d->windowFrameHoverLeaveEvent(static_cast<QGraphicsSceneHoverEvent *>(event));
+ break;
+ default:
+ break;
+ }
+ return event->isAccepted();
+}
+
+/*!
+ \since 4.4
+
+ Returns the window frame section at position \a pos, or
+ Qt::NoSection if there is no window frame section at this
+ position.
+
+ This function is used in QGraphicsWidget's base implementation for window
+ frame interaction.
+
+ You can reimplement this function if you want to customize how a window
+ can be interactively moved or resized. For instance, if you only want to
+ allow a window to be resized by the bottom right corner, you can
+ reimplement this function to return Qt::NoSection for all sections except
+ Qt::BottomRightSection.
+
+ \sa windowFrameEvent(), paintWindowFrame(), windowFrameGeometry()
+*/
+Qt::WindowFrameSection QGraphicsWidget::windowFrameSectionAt(const QPointF &pos) const
+{
+ Q_D(const QGraphicsWidget);
+
+ const QRectF r = windowFrameRect();
+ if (!r.contains(pos))
+ return Qt::NoSection;
+
+ const qreal left = r.left();
+ const qreal top = r.top();
+ const qreal right = r.right();
+ const qreal bottom = r.bottom();
+ const qreal x = pos.x();
+ const qreal y = pos.y();
+
+ const qreal cornerMargin = 20;
+ //### Not sure of this one, it should be the same value for all edges.
+ const qreal windowFrameWidth = d->windowFrameMargins
+ ? d->windowFrameMargins[d->Left] : 0;
+
+ Qt::WindowFrameSection s = Qt::NoSection;
+ if (x <= left + cornerMargin) {
+ if (y <= top + windowFrameWidth || (x <= left + windowFrameWidth && y <= top + cornerMargin)) {
+ s = Qt::TopLeftSection;
+ } else if (y >= bottom - windowFrameWidth || (x <= left + windowFrameWidth && y >= bottom - windowFrameWidth)) {
+ s = Qt::BottomLeftSection;
+ } else if (x <= left + windowFrameWidth) {
+ s = Qt::LeftSection;
+ }
+ } else if (x >= right - cornerMargin) {
+ if (y <= top + windowFrameWidth || (x >= right - windowFrameWidth && y <= top + cornerMargin)) {
+ s = Qt::TopRightSection;
+ } else if (y >= bottom - windowFrameWidth || (x >= right - windowFrameWidth && y >= bottom - windowFrameWidth)) {
+ s = Qt::BottomRightSection;
+ } else if (x >= right - windowFrameWidth) {
+ s = Qt::RightSection;
+ }
+ } else if (y <= top + windowFrameWidth) {
+ s = Qt::TopSection;
+ } else if (y >= bottom - windowFrameWidth) {
+ s = Qt::BottomSection;
+ }
+ if (s == Qt::NoSection) {
+ QRectF r1 = r;
+ r1.setHeight(d->windowFrameMargins
+ ? d->windowFrameMargins[d->Top] : 0);
+ if (r1.contains(pos))
+ s = Qt::TitleBarArea;
+ }
+ return s;
+}
+
+/*!
+ \reimp
+
+ Handles the \a event. QGraphicsWidget handles the following
+ events:
+
+ \table \o Event \o Usage
+ \row \o Polish
+ \o Delivered to the widget some time after it has been
+ shown.
+ \row \o GraphicsSceneMove
+ \o Delivered to the widget after its local position has
+ changed.
+ \row \o GraphicsSceneResize
+ \o Delivered to the widget after its size has changed.
+ \row \o Show
+ \o Delivered to the widget before it has been shown.
+ \row \o Hide
+ \o Delivered to the widget after it has been hidden.
+ \row \o PaletteChange
+ \o Delivered to the widget after its palette has changed.
+ \row \o FontChange
+ \o Delivered to the widget after its font has changed.
+ \row \o EnabledChange
+ \o Delivered to the widget after its enabled state has
+ changed.
+ \row \o StyleChange
+ \o Delivered to the widget after its style has changed.
+ \row \o LayoutDirectionChange
+ \o Delivered to the widget after its layout direction has
+ changed.
+ \row \o ContentsRectChange
+ \o Delivered to the widget after its contents margins/
+ contents rect has changed.
+ \endtable
+*/
+bool QGraphicsWidget::event(QEvent *event)
+{
+ Q_D(QGraphicsWidget);
+ // Forward the event to the layout first.
+ if (d->layout)
+ d->layout->widgetEvent(event);
+
+ // Handle the event itself.
+ switch (event->type()) {
+ case QEvent::GraphicsSceneMove:
+ moveEvent(static_cast<QGraphicsSceneMoveEvent *>(event));
+ break;
+ case QEvent::GraphicsSceneResize:
+ resizeEvent(static_cast<QGraphicsSceneResizeEvent *>(event));
+ break;
+ case QEvent::Show:
+ showEvent(static_cast<QShowEvent *>(event));
+ break;
+ case QEvent::Hide:
+ hideEvent(static_cast<QHideEvent *>(event));
+ break;
+ case QEvent::Polish:
+ polishEvent();
+ d->polished = true;
+ if (!d->font.isCopyOf(QApplication::font()))
+ d->updateFont(d->font);
+ break;
+ case QEvent::WindowActivate:
+ case QEvent::WindowDeactivate:
+ update();
+ break;
+ // Taken from QWidget::event
+ case QEvent::ActivationChange:
+ case QEvent::EnabledChange:
+ case QEvent::FontChange:
+ case QEvent::StyleChange:
+ case QEvent::PaletteChange:
+ case QEvent::ParentChange:
+ case QEvent::ContentsRectChange:
+ case QEvent::LayoutDirectionChange:
+ changeEvent(event);
+ break;
+ case QEvent::Close:
+ closeEvent((QCloseEvent *)event);
+ break;
+ case QEvent::GrabMouse:
+ grabMouseEvent(event);
+ break;
+ case QEvent::UngrabMouse:
+ ungrabMouseEvent(event);
+ break;
+ case QEvent::GrabKeyboard:
+ grabKeyboardEvent(event);
+ break;
+ case QEvent::UngrabKeyboard:
+ ungrabKeyboardEvent(event);
+ break;
+ case QEvent::GraphicsSceneMousePress:
+ if (d->hasDecoration() && windowFrameEvent(event))
+ return true;
+ case QEvent::GraphicsSceneMouseMove:
+ case QEvent::GraphicsSceneMouseRelease:
+ case QEvent::GraphicsSceneMouseDoubleClick:
+ d->ensureWindowData();
+ if (d->hasDecoration() && d->windowData->grabbedSection != Qt::NoSection)
+ return windowFrameEvent(event);
+ break;
+ case QEvent::GraphicsSceneHoverEnter:
+ case QEvent::GraphicsSceneHoverMove:
+ case QEvent::GraphicsSceneHoverLeave:
+ if (d->hasDecoration()) {
+ windowFrameEvent(event);
+ // Filter out hover events if they were sent to us only because of the
+ // decoration (special case in QGraphicsScenePrivate::dispatchHoverEvent).
+ if (!acceptsHoverEvents())
+ return true;
+ }
+ break;
+ default:
+ break;
+ }
+ return QObject::event(event);
+}
+
+/*!
+ This event handler can be reimplemented to handle state changes.
+
+ The state being changed in this event can be retrieved through \a event.
+
+ Change events include: QEvent::ActivationChange, QEvent::EnabledChange,
+ QEvent::FontChange, QEvent::StyleChange, QEvent::PaletteChange,
+ QEvent::ParentChange, QEvent::LayoutDirectionChange, and
+ QEvent::ContentsRectChange.
+*/
+void QGraphicsWidget::changeEvent(QEvent *event)
+{
+ Q_D(QGraphicsWidget);
+ switch (event->type()) {
+ case QEvent::StyleChange:
+ // ### Don't unset if the margins are explicitly set.
+ unsetWindowFrameMargins();
+ if (d->layout)
+ d->layout->invalidate();
+ case QEvent::FontChange:
+ update();
+ updateGeometry();
+ break;
+ case QEvent::PaletteChange:
+ update();
+ break;
+ case QEvent::ParentChange:
+ d->resolveFont(d->inheritedFontResolveMask);
+ d->resolvePalette(d->inheritedPaletteResolveMask);
+ break;
+ default:
+ break;
+ }
+}
+
+/*!
+ This event handler, for \a event, can be reimplemented in a subclass to
+ receive widget close events. The default implementation accepts the
+ event.
+
+ \sa close(), QCloseEvent
+*/
+void QGraphicsWidget::closeEvent(QCloseEvent *event)
+{
+ event->accept();
+}
+
+/*!
+ \reimp
+*/
+void QGraphicsWidget::focusInEvent(QFocusEvent *event)
+{
+ Q_UNUSED(event);
+ if (focusPolicy() != Qt::NoFocus)
+ update();
+}
+
+/*!
+ Finds a new widget to give the keyboard focus to, as appropriate for Tab
+ and Shift+Tab, and returns true if it can find a new widget; returns false
+ otherwise. If \a next is true, this function searches forward; if \a next
+ is false, it searches backward.
+
+ Sometimes, you will want to reimplement this function to provide special
+ focus handling for your widget and its subwidgets. For example, a web
+ browser might reimplement it to move its current active link forward or
+ backward, and call the base implementation only when it reaches the last
+ or first link on the page.
+
+ Child widgets call focusNextPrevChild() on their parent widgets, but only
+ the window that contains the child widgets decides where to redirect
+ focus. By reimplementing this function for an object, you gain control of
+ focus traversal for all child widgets.
+
+ \sa focusPolicy()
+*/
+bool QGraphicsWidget::focusNextPrevChild(bool next)
+{
+ Q_D(QGraphicsWidget);
+ // Let the parent's focusNextPrevChild implementation decide what to do.
+ QGraphicsWidget *parent = 0;
+ if (!isWindow() && (parent = parentWidget()))
+ return parent->focusNextPrevChild(next);
+ if (!d->scene)
+ return false;
+ if (d->scene->focusNextPrevChild(next))
+ return true;
+ if (isWindow()) {
+ setFocus(next ? Qt::TabFocusReason : Qt::BacktabFocusReason);
+ if (hasFocus())
+ return true;
+ }
+ return false;
+}
+
+/*!
+ \reimp
+*/
+void QGraphicsWidget::focusOutEvent(QFocusEvent *event)
+{
+ Q_UNUSED(event);
+ if (focusPolicy() != Qt::NoFocus)
+ update();
+}
+
+/*!
+ This event handler, for \l{QEvent::Hide}{Hide} events, is delivered after
+ the widget has been hidden, for example, setVisible(false) has been called
+ for the widget or one of its ancestors when the widget was previously
+ shown.
+
+ You can reimplement this event handler to detect when your widget is
+ hidden. Calling QEvent::accept() or QEvent::ignore() on \a event has no
+ effect.
+
+ \sa showEvent(), QWidget::hideEvent(), ItemVisibleChange
+*/
+void QGraphicsWidget::hideEvent(QHideEvent *event)
+{
+ ///### focusNextPrevChild(true), don't lose focus when the focus widget
+ // is hidden.
+ Q_UNUSED(event);
+}
+
+/*!
+ This event handler, for \l{QEvent::GraphicsSceneMove}{GraphicsSceneMove}
+ events, is delivered after the widget has moved (e.g., its local position
+ has changed).
+
+ This event is only delivered when the item is moved locally. Calling
+ setTransform() or moving any of the item's ancestors does not affect the
+ item's local position.
+
+ You can reimplement this event handler to detect when your widget has
+ moved. Calling QEvent::accept() or QEvent::ignore() on \a event has no
+ effect.
+
+ \sa ItemPositionChange, ItemPositionHasChanged
+*/
+void QGraphicsWidget::moveEvent(QGraphicsSceneMoveEvent *event)
+{
+ // ### Last position is always == current position
+ Q_UNUSED(event);
+}
+
+/*!
+ This event is delivered to the item by the scene at some point after it
+ has been constructed, but before it is shown or otherwise accessed through
+ the scene. You can use this event handler to do last-minute initializations
+ of the widget which require the item to be fully constructed.
+
+ The base implementation does nothing.
+*/
+void QGraphicsWidget::polishEvent()
+{
+}
+
+/*!
+ This event handler, for
+ \l{QEvent::GraphicsSceneResize}{GraphicsSceneResize} events, is
+ delivered after the widget has been resized (i.e., its local size has
+ changed). \a event contains both the old and the new size.
+
+ This event is only delivered when the widget is resized locally; calling
+ setTransform() on the widget or any of its ancestors or view, does not
+ affect the widget's local size.
+
+ You can reimplement this event handler to detect when your widget has been
+ resized. Calling QEvent::accept() or QEvent::ignore() on \a event has no
+ effect.
+
+ \sa geometry(), setGeometry()
+*/
+void QGraphicsWidget::resizeEvent(QGraphicsSceneResizeEvent *event)
+{
+ Q_UNUSED(event);
+}
+
+/*!
+ This event handler, for \l{QEvent::Show}{Show} events, is delivered before
+ the widget has been shown, for example, setVisible(true) has been called
+ for the widget or one of its ancestors when the widget was previously
+ hidden.
+
+ You can reimplement this event handler to detect when your widget is
+ shown. Calling QEvent::accept() or QEvent::ignore() on \a event has no
+ effect.
+
+ \sa hideEvent(), QWidget::showEvent(), ItemVisibleChange
+*/
+void QGraphicsWidget::showEvent(QShowEvent *event)
+{
+ Q_UNUSED(event);
+}
+
+/*!
+ \reimp
+*/
+void QGraphicsWidget::hoverMoveEvent(QGraphicsSceneHoverEvent *event)
+{
+ Q_UNUSED(event);
+}
+
+/*!
+ \reimp
+*/
+void QGraphicsWidget::hoverLeaveEvent(QGraphicsSceneHoverEvent *event)
+{
+ Q_UNUSED(event);
+}
+
+/*!
+ This event handler, for \a event, can be reimplemented in a subclass to
+ receive notifications for Qt::GrabMouse events.
+
+ \sa grabMouse(), grabKeyboard()
+*/
+void QGraphicsWidget::grabMouseEvent(QEvent *event)
+{
+ Q_UNUSED(event);
+}
+
+/*!
+ This event handler, for \a event, can be reimplemented in a subclass to
+ receive notifications for Qt::UngrabMouse events.
+
+ \sa ungrabMouse(), ungrabKeyboard()
+*/
+void QGraphicsWidget::ungrabMouseEvent(QEvent *event)
+{
+ Q_UNUSED(event);
+}
+
+/*!
+ This event handler, for \a event, can be reimplemented in a subclass to
+ receive notifications for Qt::GrabKeyboard events.
+
+ \sa grabKeyboard(), grabMouse()
+*/
+void QGraphicsWidget::grabKeyboardEvent(QEvent *event)
+{
+ Q_UNUSED(event);
+}
+
+/*!
+ This event handler, for \a event, can be reimplemented in a subclass to
+ receive notifications for Qt::UngrabKeyboard events.
+
+ \sa ungrabKeyboard(), ungrabMouse()
+*/
+void QGraphicsWidget::ungrabKeyboardEvent(QEvent *event)
+{
+ Q_UNUSED(event);
+}
+
+/*!
+ Returns the widgets window type.
+
+ \sa windowFlags(), isWindow(), isPanel()
+*/
+Qt::WindowType QGraphicsWidget::windowType() const
+{
+ return Qt::WindowType(int(windowFlags()) & Qt::WindowType_Mask);
+}
+
+/*!
+ \property QGraphicsWidget::windowFlags
+ \brief the widget's window flags
+
+ Window flags are a combination of a window type (e.g., Qt::Dialog) and
+ several flags giving hints on the behavior of the window. The behavior
+ is platform-dependent.
+
+ By default, this property contains no window flags.
+
+ Windows are panels. If you set the Qt::Window flag, the ItemIsPanel flag
+ will be set automatically. If you clear the Qt::Window flag, the
+ ItemIsPanel flag is also cleared. Note that the ItemIsPanel flag can be
+ set independently of Qt::Window.
+
+ \sa isWindow(), isPanel()
+*/
+Qt::WindowFlags QGraphicsWidget::windowFlags() const
+{
+ Q_D(const QGraphicsWidget);
+ return d->windowFlags;
+}
+void QGraphicsWidget::setWindowFlags(Qt::WindowFlags wFlags)
+{
+ Q_D(QGraphicsWidget);
+ if (d->windowFlags == wFlags)
+ return;
+ bool wasPopup = (d->windowFlags & Qt::WindowType_Mask) == Qt::Popup;
+
+ d->adjustWindowFlags(&wFlags);
+ d->windowFlags = wFlags;
+ if (!d->setWindowFrameMargins)
+ unsetWindowFrameMargins();
+
+ setFlag(ItemIsPanel, d->windowFlags & Qt::Window);
+
+ bool isPopup = (d->windowFlags & Qt::WindowType_Mask) == Qt::Popup;
+ if (d->scene && isVisible() && wasPopup != isPopup) {
+ // Popup state changed; update implicit mouse grab.
+ if (!isPopup)
+ d->scene->d_func()->removePopup(this);
+ else
+ d->scene->d_func()->addPopup(this);
+ }
+
+ if (d->scene && d->scene->d_func()->allItemsIgnoreHoverEvents && d->hasDecoration()) {
+ d->scene->d_func()->allItemsIgnoreHoverEvents = false;
+ d->scene->d_func()->enableMouseTrackingOnViews();
+ }
+}
+
+/*!
+ Returns true if this widget's window is in the active window, or if the
+ widget does not have a window but is in an active scene (i.e., a scene
+ that currently has focus).
+
+ The active window is the window that either contains a child widget that
+ currently has input focus, or that itself has input focus.
+
+ \sa QGraphicsScene::activeWindow(), QGraphicsScene::setActiveWindow(), isActive()
+*/
+bool QGraphicsWidget::isActiveWindow() const
+{
+ return isActive();
+}
+
+/*!
+ \property QGraphicsWidget::windowTitle
+ \brief This property holds the window title (caption).
+
+ This property is only used for windows.
+
+ By default, if no title has been set, this property contains an
+ empty string.
+*/
+void QGraphicsWidget::setWindowTitle(const QString &title)
+{
+ Q_D(QGraphicsWidget);
+ d->ensureWindowData();
+ d->windowData->windowTitle = title;
+}
+QString QGraphicsWidget::windowTitle() const
+{
+ Q_D(const QGraphicsWidget);
+ return d->windowData ? d->windowData->windowTitle : QString();
+}
+
+/*!
+ \property QGraphicsWidget::focusPolicy
+ \brief the way the widget accepts keyboard focus
+
+ The focus policy is Qt::TabFocus if the widget accepts keyboard focus by
+ tabbing, Qt::ClickFocus if the widget accepts focus by clicking,
+ Qt::StrongFocus if it accepts both, and Qt::NoFocus (the default) if it
+ does not accept focus at all.
+
+ You must enable keyboard focus for a widget if it processes keyboard
+ events. This is normally done from the widget's constructor. For instance,
+ the QLineEdit constructor calls setFocusPolicy(Qt::StrongFocus).
+
+ If you enable a focus policy (i.e., not Qt::NoFocus), QGraphicsWidget will
+ automatically enable the ItemIsFocusable flag. Setting Qt::NoFocus on a
+ widget will clear the ItemIsFocusable flag. If the widget currently has
+ keyboard focus, the widget will automatically lose focus.
+
+ \sa focusInEvent(), focusOutEvent(), keyPressEvent(), keyReleaseEvent(), enabled
+*/
+Qt::FocusPolicy QGraphicsWidget::focusPolicy() const
+{
+ Q_D(const QGraphicsWidget);
+ return d->focusPolicy;
+}
+void QGraphicsWidget::setFocusPolicy(Qt::FocusPolicy policy)
+{
+ Q_D(QGraphicsWidget);
+ if (d->focusPolicy == policy)
+ return;
+ d->focusPolicy = policy;
+ if (hasFocus() && policy == Qt::NoFocus)
+ clearFocus();
+ setFlag(ItemIsFocusable, policy != Qt::NoFocus);
+}
+
+/*!
+ If this widget, a child or descendant of this widget currently has input
+ focus, this function will return a pointer to that widget. If
+ no descendant widget has input focus, 0 is returned.
+
+ \sa QGraphicsItem::focusItem(), QWidget::focusWidget()
+*/
+QGraphicsWidget *QGraphicsWidget::focusWidget() const
+{
+ Q_D(const QGraphicsWidget);
+ if (d->subFocusItem && d->subFocusItem->d_ptr->isWidget)
+ return static_cast<QGraphicsWidget *>(d->subFocusItem);
+ return 0;
+}
+
+#ifndef QT_NO_SHORTCUT
+/*!
+ \since 4.5
+
+ Adds a shortcut to Qt's shortcut system that watches for the given key \a
+ sequence in the given \a context. If the \a context is
+ Qt::ApplicationShortcut, the shortcut applies to the application as a
+ whole. Otherwise, it is either local to this widget, Qt::WidgetShortcut,
+ or to the window itself, Qt::WindowShortcut. For widgets that are not part
+ of a window (i.e., top-level widgets and their children),
+ Qt::WindowShortcut shortcuts apply to the scene.
+
+ If the same key \a sequence has been grabbed by several widgets,
+ when the key \a sequence occurs a QEvent::Shortcut event is sent
+ to all the widgets to which it applies in a non-deterministic
+ order, but with the ``ambiguous'' flag set to true.
+
+ \warning You should not normally need to use this function;
+ instead create \l{QAction}s with the shortcut key sequences you
+ require (if you also want equivalent menu options and toolbar
+ buttons), or create \l{QShortcut}s if you just need key sequences.
+ Both QAction and QShortcut handle all the event filtering for you,
+ and provide signals which are triggered when the user triggers the
+ key sequence, so are much easier to use than this low-level
+ function.
+
+ \sa releaseShortcut() setShortcutEnabled() QWidget::grabShortcut()
+*/
+int QGraphicsWidget::grabShortcut(const QKeySequence &sequence, Qt::ShortcutContext context)
+{
+ Q_ASSERT(qApp);
+ if (sequence.isEmpty())
+ return 0;
+ // ### setAttribute(Qt::WA_GrabbedShortcut);
+ return qApp->d_func()->shortcutMap.addShortcut(this, sequence, context);
+}
+
+/*!
+ \since 4.5
+
+ Removes the shortcut with the given \a id from Qt's shortcut
+ system. The widget will no longer receive QEvent::Shortcut events
+ for the shortcut's key sequence (unless it has other shortcuts
+ with the same key sequence).
+
+ \warning You should not normally need to use this function since
+ Qt's shortcut system removes shortcuts automatically when their
+ parent widget is destroyed. It is best to use QAction or
+ QShortcut to handle shortcuts, since they are easier to use than
+ this low-level function. Note also that this is an expensive
+ operation.
+
+ \sa grabShortcut() setShortcutEnabled() , QWidget::releaseShortcut()
+*/
+void QGraphicsWidget::releaseShortcut(int id)
+{
+ Q_ASSERT(qApp);
+ if (id)
+ qApp->d_func()->shortcutMap.removeShortcut(id, this, 0);
+}
+
+/*!
+ \since 4.5
+
+ If \a enabled is true, the shortcut with the given \a id is
+ enabled; otherwise the shortcut is disabled.
+
+ \warning You should not normally need to use this function since
+ Qt's shortcut system enables/disables shortcuts automatically as
+ widgets become hidden/visible and gain or lose focus. It is best
+ to use QAction or QShortcut to handle shortcuts, since they are
+ easier to use than this low-level function.
+
+ \sa grabShortcut() releaseShortcut(), QWidget::setShortcutEnabled()
+*/
+void QGraphicsWidget::setShortcutEnabled(int id, bool enabled)
+{
+ Q_ASSERT(qApp);
+ if (id)
+ qApp->d_func()->shortcutMap.setShortcutEnabled(enabled, id, this, 0);
+}
+
+/*!
+ \since 4.5
+
+ If \a enabled is true, auto repeat of the shortcut with the
+ given \a id is enabled; otherwise it is disabled.
+
+ \sa grabShortcut() releaseShortcut() QWidget::setShortcutAutoRepeat()
+*/
+void QGraphicsWidget::setShortcutAutoRepeat(int id, bool enabled)
+{
+ Q_ASSERT(qApp);
+ if (id)
+ qApp->d_func()->shortcutMap.setShortcutAutoRepeat(enabled, id, this, 0);
+}
+#endif
+
+#ifndef QT_NO_ACTION
+/*!
+ \since 4.5
+
+ Appends the action \a action to this widget's list of actions.
+
+ All QGraphicsWidgets have a list of \l{QAction}s, however they can be
+ represented graphically in many different ways. The default use of the
+ QAction list (as returned by actions()) is to create a context QMenu.
+
+ A QGraphicsWidget should only have one of each action and adding an action
+ it already has will not cause the same action to be in the widget twice.
+
+ \sa removeAction(), insertAction(), actions(), QWidget::addAction()
+*/
+void QGraphicsWidget::addAction(QAction *action)
+{
+ insertAction(0, action);
+}
+
+/*!
+ \since 4.5
+
+ Appends the actions \a actions to this widget's list of actions.
+
+ \sa removeAction(), QMenu, addAction(), QWidget::addActions()
+*/
+void QGraphicsWidget::addActions(QList<QAction *> actions)
+{
+ for (int i = 0; i < actions.count(); ++i)
+ insertAction(0, actions.at(i));
+}
+
+/*!
+ \since 4.5
+
+ Inserts the action \a action to this widget's list of actions,
+ before the action \a before. It appends the action if \a before is 0 or
+ \a before is not a valid action for this widget.
+
+ A QGraphicsWidget should only have one of each action.
+
+ \sa removeAction(), addAction(), QMenu, actions(),
+ QWidget::insertActions()
+*/
+void QGraphicsWidget::insertAction(QAction *before, QAction *action)
+{
+ if (!action) {
+ qWarning("QWidget::insertAction: Attempt to insert null action");
+ return;
+ }
+
+ Q_D(QGraphicsWidget);
+ int index = d->actions.indexOf(action);
+ if (index != -1)
+ d->actions.removeAt(index);
+
+ int pos = d->actions.indexOf(before);
+ if (pos < 0) {
+ before = 0;
+ pos = d->actions.size();
+ }
+ d->actions.insert(pos, action);
+
+ if (index == -1) {
+ QActionPrivate *apriv = action->d_func();
+ apriv->graphicsWidgets.append(this);
+ }
+
+ QActionEvent e(QEvent::ActionAdded, action, before);
+ QApplication::sendEvent(this, &e);
+}
+
+/*!
+ \since 4.5
+
+ Inserts the actions \a actions to this widget's list of actions,
+ before the action \a before. It appends the action if \a before is 0 or
+ \a before is not a valid action for this widget.
+
+ A QGraphicsWidget can have at most one of each action.
+
+ \sa removeAction(), QMenu, insertAction(), QWidget::insertActions()
+*/
+void QGraphicsWidget::insertActions(QAction *before, QList<QAction *> actions)
+{
+ for (int i = 0; i < actions.count(); ++i)
+ insertAction(before, actions.at(i));
+}
+
+/*!
+ \since 4.5
+
+ Removes the action \a action from this widget's list of actions.
+
+ \sa insertAction(), actions(), insertAction(), QWidget::removeAction()
+*/
+void QGraphicsWidget::removeAction(QAction *action)
+{
+ if (!action)
+ return;
+
+ Q_D(QGraphicsWidget);
+
+ QActionPrivate *apriv = action->d_func();
+ apriv->graphicsWidgets.removeAll(this);
+
+ if (d->actions.removeAll(action)) {
+ QActionEvent e(QEvent::ActionRemoved, action);
+ QApplication::sendEvent(this, &e);
+ }
+}
+
+/*!
+ \since 4.5
+
+ Returns the (possibly empty) list of this widget's actions.
+
+ \sa insertAction(), removeAction(), QWidget::actions(),
+ QAction::associatedWidgets(), QAction::associatedGraphicsWidgets()
+*/
+QList<QAction *> QGraphicsWidget::actions() const
+{
+ Q_D(const QGraphicsWidget);
+ return d->actions;
+}
+#endif
+
+/*!
+ Moves the \a second widget around the ring of focus widgets so that
+ keyboard focus moves from the \a first widget to the \a second widget when
+ the Tab key is pressed.
+
+ Note that since the tab order of the \a second widget is changed, you
+ should order a chain like this:
+
+ \snippet doc/src/snippets/code/src_gui_graphicsview_qgraphicswidget.cpp 1
+
+ \e not like this:
+
+ \snippet doc/src/snippets/code/src_gui_graphicsview_qgraphicswidget.cpp 2
+
+ If \a first is 0, this indicates that \a second should be the first widget
+ to receive input focus should the scene gain Tab focus (i.e., the user
+ hits Tab so that focus passes into the scene). If \a second is 0, this
+ indicates that \a first should be the first widget to gain focus if the
+ scene gained BackTab focus.
+
+ By default, tab order is defined implicitly using widget creation order.
+
+ \sa focusPolicy, {Keyboard Focus}
+*/
+void QGraphicsWidget::setTabOrder(QGraphicsWidget *first, QGraphicsWidget *second)
+{
+ if (!first && !second) {
+ qWarning("QGraphicsWidget::setTabOrder(0, 0) is undefined");
+ return;
+ }
+ if ((first && second) && first->scene() != second->scene()) {
+ qWarning("QGraphicsWidget::setTabOrder: scenes %p and %p are different",
+ first->scene(), second->scene());
+ return;
+ }
+ QGraphicsScene *scene = first ? first->scene() : second->scene();
+ if (!scene && (!first || !second)) {
+ qWarning("QGraphicsWidget::setTabOrder: assigning tab order from/to the"
+ " scene requires the item to be in a scene.");
+ return;
+ }
+
+ // If either first or second are 0, the scene's tabFocusFirst is updated
+ // to point to the first item in the scene's focus chain. Then first or
+ // second are set to point to tabFocusFirst.
+ QGraphicsScenePrivate *sceneD = scene->d_func();
+ if (!first) {
+ sceneD->tabFocusFirst = second;
+ return;
+ }
+ if (!second) {
+ sceneD->tabFocusFirst = first->d_func()->focusNext;
+ return;
+ }
+
+ // Both first and second are != 0.
+ QGraphicsWidget *firstFocusNext = first->d_func()->focusNext;
+ if (firstFocusNext == second) {
+ // Nothing to do.
+ return;
+ }
+
+ // Update the focus chain.
+ QGraphicsWidget *secondFocusPrev = second->d_func()->focusPrev;
+ QGraphicsWidget *secondFocusNext = second->d_func()->focusNext;
+ firstFocusNext->d_func()->focusPrev = second;
+ first->d_func()->focusNext = second;
+ second->d_func()->focusNext = firstFocusNext;
+ second->d_func()->focusPrev = first;
+ secondFocusPrev->d_func()->focusNext = secondFocusNext;
+ secondFocusNext->d_func()->focusPrev = secondFocusPrev;
+
+ Q_ASSERT(first->d_func()->focusNext->d_func()->focusPrev == first);
+ Q_ASSERT(first->d_func()->focusPrev->d_func()->focusNext == first);
+
+ Q_ASSERT(second->d_func()->focusNext->d_func()->focusPrev == second);
+ Q_ASSERT(second->d_func()->focusPrev->d_func()->focusNext == second);
+
+}
+
+/*!
+ If \a on is true, this function enables \a attribute; otherwise
+ \a attribute is disabled.
+
+ See the class documentation for QGraphicsWidget for a complete list of
+ which attributes are supported, and what they are for.
+
+ \sa testAttribute(), QWidget::setAttribute()
+*/
+void QGraphicsWidget::setAttribute(Qt::WidgetAttribute attribute, bool on)
+{
+ Q_D(QGraphicsWidget);
+ // ### most flags require some immediate action
+ // ### we might want to qWarn use of unsupported attributes
+ // ### we might want to not use Qt::WidgetAttribute, but roll our own instead
+ d->setAttribute(attribute, on);
+}
+
+/*!
+ Returns true if \a attribute is enabled for this widget; otherwise,
+ returns false.
+
+ \sa setAttribute()
+*/
+bool QGraphicsWidget::testAttribute(Qt::WidgetAttribute attribute) const
+{
+ Q_D(const QGraphicsWidget);
+ return d->testAttribute(attribute);
+}
+
+/*!
+ \reimp
+*/
+int QGraphicsWidget::type() const
+{
+ return Type;
+}
+
+/*!
+ \reimp
+*/
+void QGraphicsWidget::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
+{
+ Q_UNUSED(painter);
+ Q_UNUSED(option);
+ Q_UNUSED(widget);
+}
+
+/*!
+ This virtual function is called by QGraphicsScene to draw the window frame
+ for windows using \a painter, \a option, and \a widget, in local
+ coordinates. The base implementation uses the current style to render the
+ frame and title bar.
+
+ You can reimplement this function in a subclass of QGraphicsWidget to
+ provide custom rendering of the widget's window frame.
+
+ \sa QGraphicsItem::paint()
+*/
+void QGraphicsWidget::paintWindowFrame(QPainter *painter, const QStyleOptionGraphicsItem *option,
+ QWidget *widget)
+{
+ const bool fillBackground = !testAttribute(Qt::WA_OpaquePaintEvent)
+ && !testAttribute(Qt::WA_NoSystemBackground);
+ QGraphicsProxyWidget *proxy = qobject_cast<QGraphicsProxyWidget *>(this);
+ const bool embeddedWidgetFillsOwnBackground = proxy && proxy->widget();
+
+ if (rect().contains(option->exposedRect)) {
+ if (fillBackground && !embeddedWidgetFillsOwnBackground)
+ painter->fillRect(option->exposedRect, palette().window());
+ return;
+ }
+
+ Q_D(QGraphicsWidget);
+
+ QRect windowFrameRect = QRect(QPoint(), windowFrameGeometry().size().toSize());
+ QStyleOptionTitleBar bar;
+ bar.QStyleOption::operator=(*option);
+ d->initStyleOptionTitleBar(&bar); // this clear flags in bar.state
+ d->ensureWindowData();
+ if (d->windowData->buttonMouseOver)
+ bar.state |= QStyle::State_MouseOver;
+ else
+ bar.state &= ~QStyle::State_MouseOver;
+ if (d->windowData->buttonSunken)
+ bar.state |= QStyle::State_Sunken;
+ else
+ bar.state &= ~QStyle::State_Sunken;
+
+ bar.rect = windowFrameRect;
+
+ // translate painter to make the style happy
+ const QPointF styleOrigin = this->windowFrameRect().topLeft();
+ painter->translate(styleOrigin);
+
+#ifdef Q_WS_MAC
+ const QSize pixmapSize = windowFrameRect.size();
+ if (pixmapSize.width() <= 0 || pixmapSize.height() <= 0)
+ return;
+ QPainter *realPainter = painter;
+ QPixmap pm(pixmapSize);
+ painter = new QPainter(&pm);
+#endif
+
+ // Fill background
+ QStyleHintReturnMask mask;
+ bool setMask = style()->styleHint(QStyle::SH_WindowFrame_Mask, &bar, widget, &mask) && !mask.region.isEmpty();
+ bool hasBorder = !style()->styleHint(QStyle::SH_TitleBar_NoBorder, &bar, widget);
+ int frameWidth = style()->pixelMetric(QStyle::PM_MDIFrameWidth, &bar, widget);
+ if (setMask) {
+ painter->save();
+ painter->setClipRegion(mask.region, Qt::IntersectClip);
+ }
+ if (fillBackground) {
+ if (embeddedWidgetFillsOwnBackground) {
+ // Don't fill the background twice.
+ QPainterPath windowFrameBackground;
+ windowFrameBackground.addRect(windowFrameRect);
+ // Adjust with 0.5 to avoid border artifacts between
+ // widget background and frame background.
+ windowFrameBackground.addRect(rect().translated(-styleOrigin).adjusted(0.5, 0.5, -0.5, -0.5));
+ painter->fillPath(windowFrameBackground, palette().window());
+ } else {
+ painter->fillRect(windowFrameRect, palette().window());
+ }
+ }
+ painter->setRenderHint(QPainter::NonCosmeticDefaultPen);
+
+ // Draw title
+ int height = (int)d->titleBarHeight(bar);
+ bar.rect.setHeight(height);
+ if (hasBorder) // Frame is painted by PE_FrameWindow
+ bar.rect.adjust(frameWidth, frameWidth, -frameWidth, 0);
+
+ painter->save();
+ painter->setFont(QApplication::font("QWorkspaceTitleBar"));
+ style()->drawComplexControl(QStyle::CC_TitleBar, &bar, painter, widget);
+ painter->restore();
+ if (setMask)
+ painter->restore();
+ // Draw window frame
+ QStyleOptionFrame frameOptions;
+ frameOptions.QStyleOption::operator=(*option);
+ initStyleOption(&frameOptions);
+ if (!hasBorder)
+ painter->setClipRect(windowFrameRect.adjusted(0, +height, 0, 0), Qt::IntersectClip);
+ if (hasFocus()) {
+ frameOptions.state |= QStyle::State_HasFocus;
+ } else {
+ frameOptions.state &= ~QStyle::State_HasFocus;
+ }
+ bool isActive = isActiveWindow();
+ if (isActive) {
+ frameOptions.state |= QStyle::State_Active;
+ } else {
+ frameOptions.state &= ~QStyle::State_Active;
+ }
+
+ frameOptions.palette.setCurrentColorGroup(isActive ? QPalette::Active : QPalette::Normal);
+ frameOptions.rect = windowFrameRect;
+ frameOptions.lineWidth = style()->pixelMetric(QStyle::PM_MdiSubWindowFrameWidth, 0, widget);
+ frameOptions.midLineWidth = 1;
+ style()->drawPrimitive(QStyle::PE_FrameWindow, &frameOptions, painter, widget);
+
+#ifdef Q_WS_MAC
+ realPainter->drawPixmap(QPoint(), pm);
+ delete painter;
+#endif
+}
+
+/*!
+ \reimp
+*/
+QRectF QGraphicsWidget::boundingRect() const
+{
+ return windowFrameRect();
+}
+
+/*!
+ \reimp
+*/
+QPainterPath QGraphicsWidget::shape() const
+{
+ QPainterPath path;
+ path.addRect(rect());
+ return path;
+}
+
+/*!
+ Call this function to close the widget.
+
+ Returns true if the widget was closed; otherwise returns false.
+ This slot will first send a QCloseEvent to the widget, which may or may
+ not accept the event. If the event was ignored, nothing happens. If the
+ event was accepted, it will hide() the widget.
+
+ If the widget has the Qt::WA_DeleteOnClose attribute set it will be
+ deleted.
+*/
+bool QGraphicsWidget::close()
+{
+ QCloseEvent closeEvent;
+ QApplication::sendEvent(this, &closeEvent);
+ if (!closeEvent.isAccepted()) {
+ return false;
+ }
+ // hide
+ if (isVisible()) {
+ hide();
+ }
+ if (testAttribute(Qt::WA_DeleteOnClose)) {
+ deleteLater();
+ }
+ return true;
+}
+
+#ifdef Q_NO_USING_KEYWORD
+/*!
+ \fn const QObjectList &QGraphicsWidget::children() const
+ \internal
+
+ This function returns the same value as QObject::children(). It's
+ provided to differentiate between the obsolete member
+ QGraphicsItem::children() and QObject::children(). QGraphicsItem now
+ provides childItems() instead.
+*/
+#endif
+
+#if 0
+void QGraphicsWidget::dumpFocusChain()
+{
+ qDebug() << "=========== Dumping focus chain ==============";
+ int i = 0;
+ QGraphicsWidget *next = this;
+ QSet<QGraphicsWidget*> visited;
+ do {
+ if (!next) {
+ qWarning("Found a focus chain that is not circular, (next == 0)");
+ break;
+ }
+ qDebug() << i++ << QString::number(uint(next), 16) << next->className() << next->data(0) << QString::fromAscii("focusItem:%1").arg(next->hasFocus() ? '1' : '0') << QLatin1String("next:") << next->d_func()->focusNext->data(0) << QLatin1String("prev:") << next->d_func()->focusPrev->data(0);
+ if (visited.contains(next)) {
+ qWarning("Already visited this node. However, I expected to dump until I found myself.");
+ break;
+ }
+ visited << next;
+ next = next->d_func()->focusNext;
+ } while (next != this);
+}
+#endif
+
+QT_END_NAMESPACE
+
+#endif //QT_NO_GRAPHICSVIEW
diff --git a/src/widgets/graphicsview/qgraphicswidget.h b/src/widgets/graphicsview/qgraphicswidget.h
new file mode 100644
index 0000000000..6cbe579608
--- /dev/null
+++ b/src/widgets/graphicsview/qgraphicswidget.h
@@ -0,0 +1,257 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** 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 Technology Preview License Agreement accompanying
+** this package.
+**
+** 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.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QGRAPHICSWIDGET_H
+#define QGRAPHICSWIDGET_H
+
+#include <QtGui/qfont.h>
+#include <QtGui/qgraphicslayoutitem.h>
+#include <QtGui/qgraphicsitem.h>
+#include <QtGui/qpalette.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+class QFont;
+class QFontMetrics;
+class QGraphicsLayout;
+class QGraphicsSceneMoveEvent;
+class QGraphicsWidgetPrivate;
+class QGraphicsSceneResizeEvent;
+class QStyle;
+class QStyleOption;
+
+#if !defined(QT_NO_GRAPHICSVIEW) || (QT_EDITION & QT_MODULE_GRAPHICSVIEW) != QT_MODULE_GRAPHICSVIEW
+
+class QGraphicsWidgetPrivate;
+
+class Q_GUI_EXPORT QGraphicsWidget : public QGraphicsObject, public QGraphicsLayoutItem
+{
+ Q_OBJECT
+ Q_INTERFACES(QGraphicsItem QGraphicsLayoutItem)
+ Q_PROPERTY(QPalette palette READ palette WRITE setPalette)
+ Q_PROPERTY(QFont font READ font WRITE setFont)
+ Q_PROPERTY(Qt::LayoutDirection layoutDirection READ layoutDirection WRITE setLayoutDirection RESET unsetLayoutDirection)
+ Q_PROPERTY(QSizeF size READ size WRITE resize NOTIFY geometryChanged)
+ Q_PROPERTY(QSizeF minimumSize READ minimumSize WRITE setMinimumSize)
+ Q_PROPERTY(QSizeF preferredSize READ preferredSize WRITE setPreferredSize)
+ Q_PROPERTY(QSizeF maximumSize READ maximumSize WRITE setMaximumSize)
+ Q_PROPERTY(QSizePolicy sizePolicy READ sizePolicy WRITE setSizePolicy)
+ Q_PROPERTY(Qt::FocusPolicy focusPolicy READ focusPolicy WRITE setFocusPolicy)
+ Q_PROPERTY(Qt::WindowFlags windowFlags READ windowFlags WRITE setWindowFlags)
+ Q_PROPERTY(QString windowTitle READ windowTitle WRITE setWindowTitle)
+ Q_PROPERTY(QRectF geometry READ geometry WRITE setGeometry NOTIFY geometryChanged)
+ Q_PROPERTY(bool autoFillBackground READ autoFillBackground WRITE setAutoFillBackground)
+ Q_PROPERTY(QGraphicsLayout* layout READ layout WRITE setLayout NOTIFY layoutChanged)
+public:
+ QGraphicsWidget(QGraphicsItem *parent = 0, Qt::WindowFlags wFlags = 0);
+ ~QGraphicsWidget();
+ QGraphicsLayout *layout() const;
+ void setLayout(QGraphicsLayout *layout);
+ void adjustSize();
+
+ Qt::LayoutDirection layoutDirection() const;
+ void setLayoutDirection(Qt::LayoutDirection direction);
+ void unsetLayoutDirection();
+
+ QStyle *style() const;
+ void setStyle(QStyle *style);
+
+ QFont font() const;
+ void setFont(const QFont &font);
+
+ QPalette palette() const;
+ void setPalette(const QPalette &palette);
+
+ bool autoFillBackground() const;
+ void setAutoFillBackground(bool enabled);
+
+ void resize(const QSizeF &size);
+ inline void resize(qreal w, qreal h) { resize(QSizeF(w, h)); }
+ QSizeF size() const;
+
+ void setGeometry(const QRectF &rect);
+ inline void setGeometry(qreal x, qreal y, qreal w, qreal h);
+ inline QRectF rect() const { return QRectF(QPointF(), size()); }
+
+ void setContentsMargins(qreal left, qreal top, qreal right, qreal bottom);
+ void getContentsMargins(qreal *left, qreal *top, qreal *right, qreal *bottom) const;
+
+ void setWindowFrameMargins(qreal left, qreal top, qreal right, qreal bottom);
+ void getWindowFrameMargins(qreal *left, qreal *top, qreal *right, qreal *bottom) const;
+ void unsetWindowFrameMargins();
+ QRectF windowFrameGeometry() const;
+ QRectF windowFrameRect() const;
+
+ // Window handling
+ Qt::WindowFlags windowFlags() const;
+ Qt::WindowType windowType() const;
+ void setWindowFlags(Qt::WindowFlags wFlags);
+ bool isActiveWindow() const;
+ void setWindowTitle(const QString &title);
+ QString windowTitle() const;
+
+ // Focus handling
+ Qt::FocusPolicy focusPolicy() const;
+ void setFocusPolicy(Qt::FocusPolicy policy);
+ static void setTabOrder(QGraphicsWidget *first, QGraphicsWidget *second);
+ QGraphicsWidget *focusWidget() const;
+
+#ifndef QT_NO_SHORTCUT
+ int grabShortcut(const QKeySequence &sequence, Qt::ShortcutContext context = Qt::WindowShortcut);
+ void releaseShortcut(int id);
+ void setShortcutEnabled(int id, bool enabled = true);
+ void setShortcutAutoRepeat(int id, bool enabled = true);
+#endif
+
+#ifndef QT_NO_ACTION
+ //actions
+ void addAction(QAction *action);
+ void addActions(QList<QAction*> actions);
+ void insertAction(QAction *before, QAction *action);
+ void insertActions(QAction *before, QList<QAction*> actions);
+ void removeAction(QAction *action);
+ QList<QAction*> actions() const;
+#endif
+
+ void setAttribute(Qt::WidgetAttribute attribute, bool on = true);
+ bool testAttribute(Qt::WidgetAttribute attribute) const;
+
+ enum {
+ Type = 11
+ };
+ int type() const;
+
+ void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = 0);
+ virtual void paintWindowFrame(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = 0);
+ QRectF boundingRect() const;
+ QPainterPath shape() const;
+
+#if 0
+ void dumpFocusChain();
+#endif
+
+ // ### Qt 5: Disambiguate
+#ifdef Q_NO_USING_KEYWORD
+ const QObjectList &children() const { return QObject::children(); }
+#else
+ using QObject::children;
+#endif
+
+Q_SIGNALS:
+ void geometryChanged();
+ void layoutChanged();
+
+public Q_SLOTS:
+ bool close();
+
+protected:
+ virtual void initStyleOption(QStyleOption *option) const;
+
+ QSizeF sizeHint(Qt::SizeHint which, const QSizeF &constraint = QSizeF()) const;
+ void updateGeometry();
+
+ // Notification
+ QVariant itemChange(GraphicsItemChange change, const QVariant &value);
+ virtual QVariant propertyChange(const QString &propertyName, const QVariant &value);
+
+ // Scene events
+ bool sceneEvent(QEvent *event);
+ virtual bool windowFrameEvent(QEvent *e);
+ virtual Qt::WindowFrameSection windowFrameSectionAt(const QPointF& pos) const;
+
+ // Base event handlers
+ bool event(QEvent *event);
+ //virtual void actionEvent(QActionEvent *event);
+ virtual void changeEvent(QEvent *event);
+ virtual void closeEvent(QCloseEvent *event);
+ //void create(WId window = 0, bool initializeWindow = true, bool destroyOldWindow = true);
+ //void destroy(bool destroyWindow = true, bool destroySubWindows = true);
+ void focusInEvent(QFocusEvent *event);
+ virtual bool focusNextPrevChild(bool next);
+ void focusOutEvent(QFocusEvent *event);
+ virtual void hideEvent(QHideEvent *event);
+ //virtual bool macEvent(EventHandlerCallRef caller, EventRef event);
+ //virtual int metric(PaintDeviceMetric m ) const;
+ virtual void moveEvent(QGraphicsSceneMoveEvent *event);
+ virtual void polishEvent();
+ //virtual bool qwsEvent(QWSEvent *event);
+ //void resetInputContext ();
+ virtual void resizeEvent(QGraphicsSceneResizeEvent *event);
+ virtual void showEvent(QShowEvent *event);
+ //virtual void tabletEvent(QTabletEvent *event);
+ //virtual bool winEvent(MSG *message, long *result);
+ //virtual bool x11Event(XEvent *event);
+ virtual void hoverMoveEvent(QGraphicsSceneHoverEvent *event);
+ virtual void hoverLeaveEvent(QGraphicsSceneHoverEvent *event);
+ virtual void grabMouseEvent(QEvent *event);
+ virtual void ungrabMouseEvent(QEvent *event);
+ virtual void grabKeyboardEvent(QEvent *event);
+ virtual void ungrabKeyboardEvent(QEvent *event);
+ QGraphicsWidget(QGraphicsWidgetPrivate &, QGraphicsItem *parent, QGraphicsScene *, Qt::WindowFlags wFlags = 0);
+
+private:
+ Q_DISABLE_COPY(QGraphicsWidget)
+ Q_DECLARE_PRIVATE_D(QGraphicsItem::d_ptr.data(), QGraphicsWidget)
+ friend class QGraphicsScene;
+ friend class QGraphicsScenePrivate;
+ friend class QGraphicsView;
+ friend class QGraphicsItem;
+ friend class QGraphicsItemPrivate;
+ friend class QGraphicsLayout;
+ friend class QWidget;
+ friend class QApplication;
+};
+
+inline void QGraphicsWidget::setGeometry(qreal ax, qreal ay, qreal aw, qreal ah)
+{ setGeometry(QRectF(ax, ay, aw, ah)); }
+
+#endif
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif
+
diff --git a/src/widgets/graphicsview/qgraphicswidget_p.cpp b/src/widgets/graphicsview/qgraphicswidget_p.cpp
new file mode 100644
index 0000000000..45800551cb
--- /dev/null
+++ b/src/widgets/graphicsview/qgraphicswidget_p.cpp
@@ -0,0 +1,910 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** 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 Technology Preview License Agreement accompanying
+** this package.
+**
+** 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.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qglobal.h"
+
+#ifndef QT_NO_GRAPHICSVIEW
+
+#include <QtCore/qdebug.h>
+#include <QtCore/qnumeric.h>
+#include "qgraphicswidget_p.h"
+#include "qgraphicslayoutitem_p.h"
+#include "qgraphicslayout.h"
+#include "qgraphicsscene_p.h"
+#include <QtGui/qapplication.h>
+#include <QtGui/qgraphicsscene.h>
+#include <QtGui/qstyleoption.h>
+#include <QtGui/QStyleOptionTitleBar>
+#include <QtGui/QGraphicsSceneMouseEvent>
+#if defined(Q_WS_MAC) && !defined(QT_NO_STYLE_MAC)
+# include <QMacStyle>
+#endif
+
+QT_BEGIN_NAMESPACE
+
+void QGraphicsWidgetPrivate::init(QGraphicsItem *parentItem, Qt::WindowFlags wFlags)
+{
+ Q_Q(QGraphicsWidget);
+
+ attributes = 0;
+ isWidget = 1; // QGraphicsItem::isWidget() returns true.
+ focusNext = focusPrev = q;
+ focusPolicy = Qt::NoFocus;
+
+ adjustWindowFlags(&wFlags);
+ windowFlags = wFlags;
+
+ if (parentItem)
+ setParentItemHelper(parentItem, 0, 0);
+
+ q->setSizePolicy(QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred, QSizePolicy::DefaultType));
+ q->setGraphicsItem(q);
+
+ resolveLayoutDirection();
+ q->unsetWindowFrameMargins();
+ flags |= QGraphicsItem::ItemUsesExtendedStyleOption;
+ flags |= QGraphicsItem::ItemSendsGeometryChanges;
+ if (windowFlags & Qt::Window)
+ flags |= QGraphicsItem::ItemIsPanel;
+}
+
+qreal QGraphicsWidgetPrivate::titleBarHeight(const QStyleOptionTitleBar &options) const
+{
+ Q_Q(const QGraphicsWidget);
+ int height = q->style()->pixelMetric(QStyle::PM_TitleBarHeight, &options);
+#if defined(Q_WS_MAC) && !defined(QT_NO_STYLE_MAC)
+ if (qobject_cast<QMacStyle*>(q->style())) {
+ height -=4;
+ }
+#endif
+ return (qreal)height;
+}
+
+/*!
+ \internal
+*/
+QGraphicsWidgetPrivate::~QGraphicsWidgetPrivate()
+{
+ // Remove any lazily allocated data
+ delete[] margins;
+ delete[] windowFrameMargins;
+ delete windowData;
+}
+
+/*!
+ \internal
+
+ Ensures that margins is allocated.
+ This function must be called before any dereferencing.
+*/
+void QGraphicsWidgetPrivate::ensureMargins() const
+{
+ if (!margins) {
+ margins = new qreal[4];
+ for (int i = 0; i < 4; ++i)
+ margins[i] = 0;
+ }
+}
+
+/*!
+ \internal
+
+ Ensures that windowFrameMargins is allocated.
+ This function must be called before any dereferencing.
+*/
+void QGraphicsWidgetPrivate::ensureWindowFrameMargins() const
+{
+ if (!windowFrameMargins) {
+ windowFrameMargins = new qreal[4];
+ for (int i = 0; i < 4; ++i)
+ windowFrameMargins[i] = 0;
+ }
+}
+
+/*!
+ \internal
+
+ Ensures that windowData is allocated.
+ This function must be called before any dereferencing.
+*/
+void QGraphicsWidgetPrivate::ensureWindowData()
+{
+ if (!windowData)
+ windowData = new WindowData;
+}
+
+void QGraphicsWidgetPrivate::setPalette_helper(const QPalette &palette)
+{
+ if (this->palette == palette && this->palette.resolve() == palette.resolve())
+ return;
+ updatePalette(palette);
+}
+
+void QGraphicsWidgetPrivate::resolvePalette(uint inheritedMask)
+{
+ inheritedPaletteResolveMask = inheritedMask;
+ QPalette naturalPalette = naturalWidgetPalette();
+ QPalette resolvedPalette = palette.resolve(naturalPalette);
+ updatePalette(resolvedPalette);
+}
+
+void QGraphicsWidgetPrivate::updatePalette(const QPalette &palette)
+{
+ Q_Q(QGraphicsWidget);
+ // Update local palette setting.
+ this->palette = palette;
+
+ // Calculate new mask.
+ if (q->isWindow() && !q->testAttribute(Qt::WA_WindowPropagation))
+ inheritedPaletteResolveMask = 0;
+ int mask = palette.resolve() | inheritedPaletteResolveMask;
+
+ // Propagate to children.
+ for (int i = 0; i < children.size(); ++i) {
+ QGraphicsItem *item = children.at(i);
+ if (item->isWidget()) {
+ QGraphicsWidget *w = static_cast<QGraphicsWidget *>(item);
+ if (!w->isWindow() || w->testAttribute(Qt::WA_WindowPropagation))
+ w->d_func()->resolvePalette(mask);
+ } else {
+ item->d_ptr->resolvePalette(mask);
+ }
+ }
+
+ // Notify change.
+ QEvent event(QEvent::PaletteChange);
+ QApplication::sendEvent(q, &event);
+}
+
+void QGraphicsWidgetPrivate::setLayoutDirection_helper(Qt::LayoutDirection direction)
+{
+ Q_Q(QGraphicsWidget);
+ if ((direction == Qt::RightToLeft) == (testAttribute(Qt::WA_RightToLeft)))
+ return;
+ q->setAttribute(Qt::WA_RightToLeft, (direction == Qt::RightToLeft));
+
+ // Propagate this change to all children.
+ for (int i = 0; i < children.size(); ++i) {
+ QGraphicsItem *item = children.at(i);
+ if (item->isWidget()) {
+ QGraphicsWidget *widget = static_cast<QGraphicsWidget *>(item);
+ if (widget->parentWidget() && !widget->testAttribute(Qt::WA_SetLayoutDirection))
+ widget->d_func()->setLayoutDirection_helper(direction);
+ }
+ }
+
+ // Send the notification event to this widget item.
+ QEvent e(QEvent::LayoutDirectionChange);
+ QApplication::sendEvent(q, &e);
+}
+
+void QGraphicsWidgetPrivate::resolveLayoutDirection()
+{
+ Q_Q(QGraphicsWidget);
+ if (q->testAttribute(Qt::WA_SetLayoutDirection)) {
+ return;
+ }
+ if (QGraphicsWidget *parentWidget = q->parentWidget()) {
+ setLayoutDirection_helper(parentWidget->layoutDirection());
+ } else if (scene) {
+ // ### shouldn't the scene have a layoutdirection really? how does
+ // ### QGraphicsWidget get changes from QApplication::layoutDirection?
+ setLayoutDirection_helper(QApplication::layoutDirection());
+ } else {
+ setLayoutDirection_helper(QApplication::layoutDirection());
+ }
+}
+
+QPalette QGraphicsWidgetPrivate::naturalWidgetPalette() const
+{
+ Q_Q(const QGraphicsWidget);
+ QPalette palette;
+ if (QGraphicsWidget *parent = q->parentWidget()) {
+ palette = parent->palette();
+ } else if (scene) {
+ palette = scene->palette();
+ }
+ palette.resolve(0);
+ return palette;
+}
+
+void QGraphicsWidgetPrivate::setFont_helper(const QFont &font)
+{
+ if (this->font == font && this->font.resolve() == font.resolve())
+ return;
+ updateFont(font);
+}
+
+void QGraphicsWidgetPrivate::resolveFont(uint inheritedMask)
+{
+ Q_Q(QGraphicsWidget);
+ inheritedFontResolveMask = inheritedMask;
+ if (QGraphicsWidget *p = q->parentWidget())
+ inheritedFontResolveMask |= p->d_func()->inheritedFontResolveMask;
+ QFont naturalFont = naturalWidgetFont();
+ QFont resolvedFont = font.resolve(naturalFont);
+ updateFont(resolvedFont);
+}
+
+void QGraphicsWidgetPrivate::updateFont(const QFont &font)
+{
+ Q_Q(QGraphicsWidget);
+ // Update the local font setting.
+ this->font = font;
+
+ // Calculate new mask.
+ if (q->isWindow() && !q->testAttribute(Qt::WA_WindowPropagation))
+ inheritedFontResolveMask = 0;
+ int mask = font.resolve() | inheritedFontResolveMask;
+
+ // Propagate to children.
+ for (int i = 0; i < children.size(); ++i) {
+ QGraphicsItem *item = children.at(i);
+ if (item->isWidget()) {
+ QGraphicsWidget *w = static_cast<QGraphicsWidget *>(item);
+ if (!w->isWindow() || w->testAttribute(Qt::WA_WindowPropagation))
+ w->d_func()->resolveFont(mask);
+ } else {
+ item->d_ptr->resolveFont(mask);
+ }
+ }
+
+ if (!polished)
+ return;
+ // Notify change.
+ QEvent event(QEvent::FontChange);
+ QApplication::sendEvent(q, &event);
+}
+
+QFont QGraphicsWidgetPrivate::naturalWidgetFont() const
+{
+ Q_Q(const QGraphicsWidget);
+ QFont naturalFont; // ### no application font support
+ if (QGraphicsWidget *parent = q->parentWidget()) {
+ naturalFont = parent->font();
+ } else if (scene) {
+ naturalFont = scene->font();
+ }
+ naturalFont.resolve(0);
+ return naturalFont;
+}
+
+void QGraphicsWidgetPrivate::initStyleOptionTitleBar(QStyleOptionTitleBar *option)
+{
+ Q_Q(QGraphicsWidget);
+ ensureWindowData();
+ q->initStyleOption(option);
+ option->rect.setHeight(titleBarHeight(*option));
+ option->titleBarFlags = windowFlags;
+ option->subControls = QStyle::SC_TitleBarCloseButton | QStyle::SC_TitleBarLabel | QStyle::SC_TitleBarSysMenu;
+ option->activeSubControls = windowData->hoveredSubControl;
+ bool isActive = q->isActiveWindow();
+ if (isActive) {
+ option->state |= QStyle::State_Active;
+ option->titleBarState = Qt::WindowActive;
+ option->titleBarState |= QStyle::State_Active;
+ } else {
+ option->state &= ~QStyle::State_Active;
+ option->titleBarState = Qt::WindowNoState;
+ }
+ QFont windowTitleFont = QApplication::font("QWorkspaceTitleBar");
+ QRect textRect = q->style()->subControlRect(QStyle::CC_TitleBar, option, QStyle::SC_TitleBarLabel, 0);
+ option->text = QFontMetrics(windowTitleFont).elidedText(
+ windowData->windowTitle, Qt::ElideRight, textRect.width());
+}
+
+void QGraphicsWidgetPrivate::adjustWindowFlags(Qt::WindowFlags *flags)
+{
+ bool customize = (*flags & (Qt::CustomizeWindowHint
+ | Qt::FramelessWindowHint
+ | Qt::WindowTitleHint
+ | Qt::WindowSystemMenuHint
+ | Qt::WindowMinimizeButtonHint
+ | Qt::WindowMaximizeButtonHint
+ | Qt::WindowContextHelpButtonHint));
+
+ uint type = (*flags & Qt::WindowType_Mask);
+ if (customize)
+ ;
+ else if (type == Qt::Dialog || type == Qt::Sheet)
+ *flags |= Qt::WindowTitleHint | Qt::WindowSystemMenuHint | Qt::WindowContextHelpButtonHint;
+ else if (type == Qt::Tool)
+ *flags |= Qt::WindowTitleHint | Qt::WindowSystemMenuHint;
+ else if (type == Qt::Window || type == Qt::SubWindow)
+ *flags |= Qt::WindowTitleHint | Qt::WindowSystemMenuHint
+ | Qt::WindowMinimizeButtonHint | Qt::WindowMaximizeButtonHint;
+}
+
+void QGraphicsWidgetPrivate::windowFrameMouseReleaseEvent(QGraphicsSceneMouseEvent *event)
+{
+ Q_Q(QGraphicsWidget);
+ ensureWindowData();
+ if (windowData->grabbedSection != Qt::NoSection) {
+ if (windowData->grabbedSection == Qt::TitleBarArea) {
+ windowData->buttonSunken = false;
+ QStyleOptionTitleBar bar;
+ initStyleOptionTitleBar(&bar);
+ // make sure that the coordinates (rect and pos) we send to the style are positive.
+ bar.rect = q->windowFrameRect().toRect();
+ bar.rect.moveTo(0,0);
+ bar.rect.setHeight(q->style()->pixelMetric(QStyle::PM_TitleBarHeight, &bar));
+ QPointF pos = event->pos();
+ if (windowFrameMargins) {
+ pos.rx() += windowFrameMargins[Left];
+ pos.ry() += windowFrameMargins[Top];
+ }
+ bar.subControls = QStyle::SC_TitleBarCloseButton;
+ if (q->style()->subControlRect(QStyle::CC_TitleBar, &bar,
+ QStyle::SC_TitleBarCloseButton,
+ event->widget()).contains(pos.toPoint())) {
+ q->close();
+ }
+ }
+ if (!(static_cast<QGraphicsSceneMouseEvent *>(event)->buttons()))
+ windowData->grabbedSection = Qt::NoSection;
+ event->accept();
+ }
+}
+
+void QGraphicsWidgetPrivate::windowFrameMousePressEvent(QGraphicsSceneMouseEvent *event)
+{
+ Q_Q(QGraphicsWidget);
+ if (event->button() != Qt::LeftButton)
+ return;
+
+ ensureWindowData();
+ windowData->startGeometry = q->geometry();
+ windowData->grabbedSection = q->windowFrameSectionAt(event->pos());
+ ensureWindowData();
+ if (windowData->grabbedSection == Qt::TitleBarArea
+ && windowData->hoveredSubControl == QStyle::SC_TitleBarCloseButton) {
+ windowData->buttonSunken = true;
+ q->update();
+ }
+ event->setAccepted(windowData->grabbedSection != Qt::NoSection);
+}
+
+/*!
+ Used to calculate the
+ Precondition:
+ \a widget should support either hfw or wfh
+
+ If \a heightForWidth is set to false, this function will query the width for height
+ instead. \a width will then be interpreted as height, \a minh and \a maxh will be interpreted
+ as minimum width and maximum width.
+ */
+static qreal minimumHeightForWidth(qreal width, qreal minh, qreal maxh,
+ const QGraphicsWidget *widget,
+ bool heightForWidth = true)
+{
+ qreal minimumHeightForWidth = -1;
+ const bool hasHFW = QGraphicsLayoutItemPrivate::get(widget)->hasHeightForWidth();
+ if (hasHFW == heightForWidth) {
+ minimumHeightForWidth = hasHFW
+ ? widget->effectiveSizeHint(Qt::MinimumSize, QSizeF(width, -1)).height()
+ : widget->effectiveSizeHint(Qt::MinimumSize, QSizeF(-1, width)).width(); //"width" is here height!
+ } else {
+ // widthForHeight
+ const qreal constraint = width;
+ while (maxh - minh > 0.1) {
+ qreal middle = minh + (maxh - minh)/2;
+ // ### really bad, if we are a widget with a layout it will call
+ // layout->effectiveSizeHint(Qt::MiniumumSize), which again will call
+ // sizeHint three times because of how the cache works
+ qreal hfw = hasHFW
+ ? widget->effectiveSizeHint(Qt::MinimumSize, QSizeF(middle, -1)).height()
+ : widget->effectiveSizeHint(Qt::MinimumSize, QSizeF(-1, middle)).width();
+ if (hfw > constraint) {
+ minh = middle;
+ } else if (hfw <= constraint) {
+ maxh = middle;
+ }
+ }
+ minimumHeightForWidth = maxh;
+ }
+ return minimumHeightForWidth;
+}
+
+static qreal minimumWidthForHeight(qreal height, qreal minw, qreal maxw,
+ const QGraphicsWidget *widget)
+{
+ return minimumHeightForWidth(height, minw, maxw, widget, false);
+}
+
+static QSizeF closestAcceptableSize(const QSizeF &proposed,
+ const QGraphicsWidget *widget)
+{
+ const QSizeF current = widget->size();
+
+ qreal minw = proposed.width();
+ qreal maxw = current.width();
+ qreal minh = proposed.height();
+ qreal maxh = current.height();
+
+ qreal middlew = maxw;
+ qreal middleh = maxh;
+ qreal min_hfw;
+ min_hfw = minimumHeightForWidth(maxw, minh, maxh, widget);
+
+ do {
+ if (maxw - minw < 0.1) {
+ // we still havent found anything, cut off binary search
+ minw = maxw;
+ minh = maxh;
+ }
+ middlew = minw + (maxw - minw)/2.0;
+ middleh = minh + (maxh - minh)/2.0;
+
+ min_hfw = minimumHeightForWidth(middlew, minh, maxh, widget);
+
+ if (min_hfw > middleh) {
+ minw = middlew;
+ minh = middleh;
+ } else if (min_hfw <= middleh) {
+ maxw = middlew;
+ maxh = middleh;
+ }
+ } while (maxw != minw);
+
+ min_hfw = minimumHeightForWidth(middlew, minh, maxh, widget);
+
+ QSizeF result;
+ if (min_hfw < maxh) {
+ result = QSizeF(middlew, min_hfw);
+ } else {
+ // Needed because of the cut-off we do above.
+ result = QSizeF(minimumWidthForHeight(maxh, proposed.width(), current.width(), widget), maxh);
+ }
+ return result;
+}
+
+static void _q_boundGeometryToSizeConstraints(const QRectF &startGeometry,
+ QRectF *rect, Qt::WindowFrameSection section,
+ const QSizeF &min, const QSizeF &max,
+ const QGraphicsWidget *widget)
+{
+ const QRectF proposedRect = *rect;
+ qreal width = qBound(min.width(), proposedRect.width(), max.width());
+ qreal height = qBound(min.height(), proposedRect.height(), max.height());
+
+ const bool hasHFW = QGraphicsLayoutItemPrivate::get(widget)->hasHeightForWidth();
+ const bool hasWFH = QGraphicsLayoutItemPrivate::get(widget)->hasWidthForHeight();
+
+ const bool widthChanged = proposedRect.width() != widget->size().width();
+ const bool heightChanged = proposedRect.height() != widget->size().height();
+
+ if (hasHFW || hasWFH) {
+ if (widthChanged || heightChanged) {
+ qreal minExtent;
+ qreal maxExtent;
+ qreal constraint;
+ qreal proposed;
+ if (hasHFW) {
+ minExtent = min.height();
+ maxExtent = max.height();
+ constraint = width;
+ proposed = proposedRect.height();
+ } else {
+ // width for height
+ minExtent = min.width();
+ maxExtent = max.width();
+ constraint = height;
+ proposed = proposedRect.width();
+ }
+ if (minimumHeightForWidth(constraint, minExtent, maxExtent, widget, hasHFW) > proposed) {
+ QSizeF effectiveSize = closestAcceptableSize(QSizeF(width, height), widget);
+ width = effectiveSize.width();
+ height = effectiveSize.height();
+ }
+ }
+ }
+
+ switch (section) {
+ case Qt::LeftSection:
+ rect->setRect(startGeometry.right() - qRound(width), startGeometry.top(),
+ qRound(width), startGeometry.height());
+ break;
+ case Qt::TopLeftSection:
+ rect->setRect(startGeometry.right() - qRound(width), startGeometry.bottom() - qRound(height),
+ qRound(width), qRound(height));
+ break;
+ case Qt::TopSection:
+ rect->setRect(startGeometry.left(), startGeometry.bottom() - qRound(height),
+ startGeometry.width(), qRound(height));
+ break;
+ case Qt::TopRightSection:
+ rect->setTop(rect->bottom() - qRound(height));
+ rect->setWidth(qRound(width));
+ break;
+ case Qt::RightSection:
+ rect->setWidth(qRound(width));
+ break;
+ case Qt::BottomRightSection:
+ rect->setWidth(qRound(width));
+ rect->setHeight(qRound(height));
+ break;
+ case Qt::BottomSection:
+ rect->setHeight(qRound(height));
+ break;
+ case Qt::BottomLeftSection:
+ rect->setRect(startGeometry.right() - qRound(width), startGeometry.top(),
+ qRound(width), qRound(height));
+ break;
+ default:
+ break;
+ }
+}
+
+void QGraphicsWidgetPrivate::windowFrameMouseMoveEvent(QGraphicsSceneMouseEvent *event)
+{
+ Q_Q(QGraphicsWidget);
+ ensureWindowData();
+ if (!(event->buttons() & Qt::LeftButton) || windowData->hoveredSubControl != QStyle::SC_TitleBarLabel)
+ return;
+
+ QLineF delta(q->mapFromScene(event->buttonDownScenePos(Qt::LeftButton)), event->pos());
+ QLineF parentDelta(q->mapToParent(delta.p1()), q->mapToParent(delta.p2()));
+ QLineF parentXDelta(q->mapToParent(QPointF(delta.p1().x(), 0)), q->mapToParent(QPointF(delta.p2().x(), 0)));
+ QLineF parentYDelta(q->mapToParent(QPointF(0, delta.p1().y())), q->mapToParent(QPointF(0, delta.p2().y())));
+
+ QRectF newGeometry;
+ switch (windowData->grabbedSection) {
+ case Qt::LeftSection:
+ newGeometry = QRectF(windowData->startGeometry.topLeft()
+ + QPointF(parentXDelta.dx(), parentXDelta.dy()),
+ windowData->startGeometry.size() - QSizeF(delta.dx(), delta.dy()));
+ break;
+ case Qt::TopLeftSection:
+ newGeometry = QRectF(windowData->startGeometry.topLeft()
+ + QPointF(parentDelta.dx(), parentDelta.dy()),
+ windowData->startGeometry.size() - QSizeF(delta.dx(), delta.dy()));
+ break;
+ case Qt::TopSection:
+ newGeometry = QRectF(windowData->startGeometry.topLeft()
+ + QPointF(parentYDelta.dx(), parentYDelta.dy()),
+ windowData->startGeometry.size() - QSizeF(0, delta.dy()));
+ break;
+ case Qt::TopRightSection:
+ newGeometry = QRectF(windowData->startGeometry.topLeft()
+ + QPointF(parentYDelta.dx(), parentYDelta.dy()),
+ windowData->startGeometry.size() - QSizeF(-delta.dx(), delta.dy()));
+ break;
+ case Qt::RightSection:
+ newGeometry = QRectF(windowData->startGeometry.topLeft(),
+ windowData->startGeometry.size() + QSizeF(delta.dx(), 0));
+ break;
+ case Qt::BottomRightSection:
+ newGeometry = QRectF(windowData->startGeometry.topLeft(),
+ windowData->startGeometry.size() + QSizeF(delta.dx(), delta.dy()));
+ break;
+ case Qt::BottomSection:
+ newGeometry = QRectF(windowData->startGeometry.topLeft(),
+ windowData->startGeometry.size() + QSizeF(0, delta.dy()));
+ break;
+ case Qt::BottomLeftSection:
+ newGeometry = QRectF(windowData->startGeometry.topLeft()
+ + QPointF(parentXDelta.dx(), parentXDelta.dy()),
+ windowData->startGeometry.size() - QSizeF(delta.dx(), -delta.dy()));
+ break;
+ case Qt::TitleBarArea:
+ newGeometry = QRectF(windowData->startGeometry.topLeft()
+ + QPointF(parentDelta.dx(), parentDelta.dy()),
+ windowData->startGeometry.size());
+ break;
+ case Qt::NoSection:
+ break;
+ }
+
+ if (windowData->grabbedSection != Qt::NoSection) {
+ _q_boundGeometryToSizeConstraints(windowData->startGeometry, &newGeometry,
+ windowData->grabbedSection,
+ q->effectiveSizeHint(Qt::MinimumSize),
+ q->effectiveSizeHint(Qt::MaximumSize),
+ q);
+ q->setGeometry(newGeometry);
+ }
+}
+
+void QGraphicsWidgetPrivate::windowFrameHoverMoveEvent(QGraphicsSceneHoverEvent *event)
+{
+ Q_Q(QGraphicsWidget);
+ if (!hasDecoration())
+ return;
+
+ ensureWindowData();
+
+ if (q->rect().contains(event->pos())) {
+ if (windowData->buttonMouseOver || windowData->hoveredSubControl != QStyle::SC_None)
+ windowFrameHoverLeaveEvent(event);
+ return;
+ }
+
+ bool wasMouseOver = windowData->buttonMouseOver;
+ QRect oldButtonRect = windowData->buttonRect;
+ windowData->buttonRect = QRect();
+ windowData->buttonMouseOver = false;
+ QPointF pos = event->pos();
+ QStyleOptionTitleBar bar;
+ // make sure that the coordinates (rect and pos) we send to the style are positive.
+ if (windowFrameMargins) {
+ pos.rx() += windowFrameMargins[Left];
+ pos.ry() += windowFrameMargins[Top];
+ }
+ initStyleOptionTitleBar(&bar);
+ bar.rect = q->windowFrameRect().toRect();
+ bar.rect.moveTo(0,0);
+ bar.rect.setHeight(int(titleBarHeight(bar)));
+
+ Qt::CursorShape cursorShape = Qt::ArrowCursor;
+ bool needsSetCursorCall = true;
+ switch (q->windowFrameSectionAt(event->pos())) {
+ case Qt::TopLeftSection:
+ case Qt::BottomRightSection:
+ cursorShape = Qt::SizeFDiagCursor;
+ break;
+ case Qt::TopRightSection:
+ case Qt::BottomLeftSection:
+ cursorShape = Qt::SizeBDiagCursor;
+ break;
+ case Qt::LeftSection:
+ case Qt::RightSection:
+ cursorShape = Qt::SizeHorCursor;
+ break;
+ case Qt::TopSection:
+ case Qt::BottomSection:
+ cursorShape = Qt::SizeVerCursor;
+ break;
+ case Qt::TitleBarArea:
+ windowData->buttonRect = q->style()->subControlRect(
+ QStyle::CC_TitleBar, &bar, QStyle::SC_TitleBarCloseButton, 0);
+#ifdef Q_WS_MAC
+ // On mac we should hover if we are in the 'area' of the buttons
+ windowData->buttonRect |= q->style()->subControlRect(
+ QStyle::CC_TitleBar, &bar, QStyle::SC_TitleBarMinButton, 0);
+ windowData->buttonRect |= q->style()->subControlRect(
+ QStyle::CC_TitleBar, &bar, QStyle::SC_TitleBarMaxButton, 0);
+#endif
+ if (windowData->buttonRect.contains(pos.toPoint()))
+ windowData->buttonMouseOver = true;
+ event->ignore();
+ break;
+ default:
+ needsSetCursorCall = false;
+ event->ignore();
+ }
+#ifndef QT_NO_CURSOR
+ if (needsSetCursorCall)
+ q->setCursor(cursorShape);
+#endif
+ // update buttons if we hover over them
+ windowData->hoveredSubControl = q->style()->hitTestComplexControl(QStyle::CC_TitleBar, &bar, pos.toPoint(), 0);
+ if (windowData->hoveredSubControl != QStyle::SC_TitleBarCloseButton)
+ windowData->hoveredSubControl = QStyle::SC_TitleBarLabel;
+
+ if (windowData->buttonMouseOver != wasMouseOver) {
+ if (!oldButtonRect.isNull())
+ q->update(QRectF(oldButtonRect).translated(q->windowFrameRect().topLeft()));
+ if (!windowData->buttonRect.isNull())
+ q->update(QRectF(windowData->buttonRect).translated(q->windowFrameRect().topLeft()));
+ }
+}
+
+void QGraphicsWidgetPrivate::windowFrameHoverLeaveEvent(QGraphicsSceneHoverEvent *event)
+{
+ Q_UNUSED(event);
+ Q_Q(QGraphicsWidget);
+ if (hasDecoration()) {
+ // ### restore the cursor, don't override it
+#ifndef QT_NO_CURSOR
+ q->unsetCursor();
+#endif
+
+ ensureWindowData();
+
+ bool needsUpdate = false;
+ if (windowData->hoveredSubControl == QStyle::SC_TitleBarCloseButton
+ || windowData->buttonMouseOver)
+ needsUpdate = true;
+
+ // update the hover state (of buttons etc...)
+ windowData->hoveredSubControl = QStyle::SC_None;
+ windowData->buttonMouseOver = false;
+ windowData->buttonRect = QRect();
+ if (needsUpdate)
+ q->update(windowData->buttonRect);
+ }
+}
+
+bool QGraphicsWidgetPrivate::hasDecoration() const
+{
+ return (windowFlags & Qt::Window) && (windowFlags & Qt::WindowTitleHint);
+}
+
+/**
+ * is called after a reparent has taken place to fix up the focus chain(s)
+ */
+void QGraphicsWidgetPrivate::fixFocusChainBeforeReparenting(QGraphicsWidget *newParent, QGraphicsScene *oldScene, QGraphicsScene *newScene)
+{
+ Q_Q(QGraphicsWidget);
+
+ Q_ASSERT(focusNext && focusPrev);
+
+ QGraphicsWidget *n = q; //last one in 'new' list
+ QGraphicsWidget *o = 0; //last one in 'old' list
+
+ QGraphicsWidget *w = focusNext;
+
+ QGraphicsWidget *firstOld = 0;
+ bool wasPreviousNew = true;
+
+ while (w != q) {
+ bool isCurrentNew = q->isAncestorOf(w);
+ if (isCurrentNew) {
+ if (!wasPreviousNew) {
+ n->d_func()->focusNext = w;
+ w->d_func()->focusPrev = n;
+ }
+ n = w;
+ } else /*if (!isCurrentNew)*/ {
+ if (wasPreviousNew) {
+ if (o) {
+ o->d_func()->focusNext = w;
+ w->d_func()->focusPrev = o;
+ } else {
+ firstOld = w;
+ }
+ }
+ o = w;
+ }
+ w = w->d_func()->focusNext;
+ wasPreviousNew = isCurrentNew;
+ }
+
+ // repair the 'old' chain
+ if (firstOld) {
+ o->d_func()->focusNext = firstOld;
+ firstOld->d_func()->focusPrev = o;
+ }
+
+ // update tabFocusFirst for oldScene if the item is going to be removed from oldScene
+ if (newParent)
+ newScene = newParent->scene();
+
+ if (oldScene && newScene != oldScene)
+ oldScene->d_func()->tabFocusFirst = (firstOld && firstOld->scene() == oldScene) ? firstOld : 0;
+
+ QGraphicsItem *topLevelItem = newParent ? newParent->topLevelItem() : 0;
+ QGraphicsWidget *topLevel = 0;
+ if (topLevelItem && topLevelItem->isWidget())
+ topLevel = static_cast<QGraphicsWidget *>(topLevelItem);
+
+ if (topLevel && newParent) {
+ QGraphicsWidget *last = topLevel->d_func()->focusPrev;
+ // link last with new chain
+ last->d_func()->focusNext = q;
+ focusPrev = last;
+
+ // link last in chain with
+ topLevel->d_func()->focusPrev = n;
+ n->d_func()->focusNext = topLevel;
+ } else {
+ // q is the start of the focus chain
+ n->d_func()->focusNext = q;
+ focusPrev = n;
+ }
+
+}
+
+void QGraphicsWidgetPrivate::setLayout_helper(QGraphicsLayout *l)
+{
+ delete (this->layout);
+ layout = l;
+ if (!l) {
+ Q_Q(QGraphicsWidget);
+ q->updateGeometry();
+ }
+}
+
+qreal QGraphicsWidgetPrivate::width() const
+{
+ Q_Q(const QGraphicsWidget);
+ return q->geometry().width();
+}
+
+void QGraphicsWidgetPrivate::setWidth(qreal w)
+{
+ if (qIsNaN(w))
+ return;
+ Q_Q(QGraphicsWidget);
+ if (q->geometry().width() == w)
+ return;
+
+ QRectF oldGeom = q->geometry();
+
+ q->setGeometry(QRectF(q->x(), q->y(), w, height()));
+}
+
+void QGraphicsWidgetPrivate::resetWidth()
+{
+ Q_Q(QGraphicsWidget);
+ q->setGeometry(QRectF(q->x(), q->y(), 0, height()));
+}
+
+qreal QGraphicsWidgetPrivate::height() const
+{
+ Q_Q(const QGraphicsWidget);
+ return q->geometry().height();
+}
+
+void QGraphicsWidgetPrivate::setHeight(qreal h)
+{
+ if (qIsNaN(h))
+ return;
+ Q_Q(QGraphicsWidget);
+ if (q->geometry().height() == h)
+ return;
+
+ QRectF oldGeom = q->geometry();
+
+ q->setGeometry(QRectF(q->x(), q->y(), width(), h));
+}
+
+void QGraphicsWidgetPrivate::resetHeight()
+{
+ Q_Q(QGraphicsWidget);
+ q->setGeometry(QRectF(q->x(), q->y(), width(), 0));
+}
+
+void QGraphicsWidgetPrivate::setGeometryFromSetPos()
+{
+ if (inSetGeometry)
+ return;
+ Q_Q(QGraphicsWidget);
+ inSetPos = 1;
+ // Ensure setGeometry is called (avoid recursion when setPos is
+ // called from within setGeometry).
+ q->setGeometry(QRectF(pos, q->size()));
+ inSetPos = 0 ;
+}
+
+QT_END_NAMESPACE
+
+#endif //QT_NO_GRAPHICSVIEW
diff --git a/src/widgets/graphicsview/qgraphicswidget_p.h b/src/widgets/graphicsview/qgraphicswidget_p.h
new file mode 100644
index 0000000000..4379eafbde
--- /dev/null
+++ b/src/widgets/graphicsview/qgraphicswidget_p.h
@@ -0,0 +1,226 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** 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 Technology Preview License Agreement accompanying
+** this package.
+**
+** 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.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QGRAPHICSWIDGET_P_H
+#define QGRAPHICSWIDGET_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of other Qt classes. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <private/qobject_p.h>
+#include "qgraphicsitem_p.h"
+#include "qgraphicswidget.h"
+#include <QtGui/qfont.h>
+#include <QtGui/qpalette.h>
+#include <QtGui/qsizepolicy.h>
+#include <QtGui/qstyle.h>
+
+QT_BEGIN_NAMESPACE
+
+class QGraphicsLayout;
+class QStyleOptionTitleBar;
+
+#if !defined(QT_NO_GRAPHICSVIEW) || (QT_EDITION & QT_MODULE_GRAPHICSVIEW) != QT_MODULE_GRAPHICSVIEW
+
+class QGraphicsWidgetPrivate : public QGraphicsItemPrivate
+{
+ Q_DECLARE_PUBLIC(QGraphicsWidget)
+public:
+ QGraphicsWidgetPrivate()
+ : margins(0),
+ layout(0),
+ inheritedPaletteResolveMask(0),
+ inheritedFontResolveMask(0),
+ inSetGeometry(0),
+ polished(0),
+ inSetPos(0),
+ autoFillBackground(0),
+ focusPolicy(Qt::NoFocus),
+ focusNext(0),
+ focusPrev(0),
+ windowFlags(0),
+ windowData(0),
+ setWindowFrameMargins(false),
+ windowFrameMargins(0)
+ { }
+ virtual ~QGraphicsWidgetPrivate();
+
+ void init(QGraphicsItem *parentItem, Qt::WindowFlags wFlags);
+ qreal titleBarHeight(const QStyleOptionTitleBar &options) const;
+
+ // Margins
+ enum {Left, Top, Right, Bottom};
+ mutable qreal *margins;
+ void ensureMargins() const;
+
+ void fixFocusChainBeforeReparenting(QGraphicsWidget *newParent, QGraphicsScene *oldScene, QGraphicsScene *newScene = 0);
+ void setLayout_helper(QGraphicsLayout *l);
+
+ // Layouts
+ QGraphicsLayout *layout;
+ void setLayoutDirection_helper(Qt::LayoutDirection direction);
+ void resolveLayoutDirection();
+
+ // Style
+ QPalette palette;
+ uint inheritedPaletteResolveMask;
+ void setPalette_helper(const QPalette &palette);
+ void resolvePalette(uint inheritedMask);
+ void updatePalette(const QPalette &palette);
+ QPalette naturalWidgetPalette() const;
+ QFont font;
+ uint inheritedFontResolveMask;
+ void setFont_helper(const QFont &font);
+ void resolveFont(uint inheritedMask);
+ void updateFont(const QFont &font);
+ QFont naturalWidgetFont() const;
+
+ // Window specific
+ void initStyleOptionTitleBar(QStyleOptionTitleBar *option);
+ void adjustWindowFlags(Qt::WindowFlags *wFlags);
+ void windowFrameMouseReleaseEvent(QGraphicsSceneMouseEvent *event);
+ void windowFrameMousePressEvent(QGraphicsSceneMouseEvent *event);
+ void windowFrameMouseMoveEvent(QGraphicsSceneMouseEvent *event);
+ void windowFrameHoverMoveEvent(QGraphicsSceneHoverEvent *event);
+ void windowFrameHoverLeaveEvent(QGraphicsSceneHoverEvent *event);
+ bool hasDecoration() const;
+
+ // Private Properties
+ qreal width() const;
+ void setWidth(qreal);
+ void resetWidth();
+
+ qreal height() const;
+ void setHeight(qreal);
+ void resetHeight();
+ void setGeometryFromSetPos();
+
+ // State
+ inline int attributeToBitIndex(Qt::WidgetAttribute att) const
+ {
+ int bit = -1;
+ switch (att) {
+ case Qt::WA_SetLayoutDirection: bit = 0; break;
+ case Qt::WA_RightToLeft: bit = 1; break;
+ case Qt::WA_SetStyle: bit = 2; break;
+ case Qt::WA_Resized: bit = 3; break;
+ case Qt::WA_DeleteOnClose: bit = 4; break;
+ case Qt::WA_NoSystemBackground: bit = 5; break;
+ case Qt::WA_OpaquePaintEvent: bit = 6; break;
+ case Qt::WA_SetPalette: bit = 7; break;
+ case Qt::WA_SetFont: bit = 8; break;
+ case Qt::WA_WindowPropagation: bit = 9; break;
+ default: break;
+ }
+ return bit;
+ }
+ inline void setAttribute(Qt::WidgetAttribute att, bool value)
+ {
+ int bit = attributeToBitIndex(att);
+ if (bit == -1) {
+ qWarning("QGraphicsWidget::setAttribute: unsupported attribute %d", int(att));
+ return;
+ }
+ if (value)
+ attributes |= (1 << bit);
+ else
+ attributes &= ~(1 << bit);
+ }
+ inline bool testAttribute(Qt::WidgetAttribute att) const
+ {
+ int bit = attributeToBitIndex(att);
+ if (bit == -1)
+ return false;
+ return (attributes & (1 << bit)) != 0;
+ }
+ quint32 attributes : 10;
+ quint32 inSetGeometry : 1;
+ quint32 polished: 1;
+ quint32 inSetPos : 1;
+ quint32 autoFillBackground : 1;
+
+ // Focus
+ Qt::FocusPolicy focusPolicy;
+ QGraphicsWidget *focusNext;
+ QGraphicsWidget *focusPrev;
+
+ // Windows
+ Qt::WindowFlags windowFlags;
+ struct WindowData {
+ QString windowTitle;
+ QStyle::SubControl hoveredSubControl;
+ Qt::WindowFrameSection grabbedSection;
+ uint buttonMouseOver : 1;
+ uint buttonSunken : 1;
+ QRectF startGeometry;
+ QRect buttonRect;
+ WindowData()
+ : hoveredSubControl(QStyle::SC_None)
+ , grabbedSection(Qt::NoSection)
+ , buttonMouseOver(false)
+ , buttonSunken(false)
+ {}
+ } *windowData;
+ void ensureWindowData();
+
+ bool setWindowFrameMargins;
+ mutable qreal *windowFrameMargins;
+ void ensureWindowFrameMargins() const;
+
+#ifndef QT_NO_ACTION
+ QList<QAction *> actions;
+#endif
+};
+
+#endif //!defined(QT_NO_GRAPHICSVIEW) || (QT_EDITION & QT_MODULE_GRAPHICSVIEW) != QT_MODULE_GRAPHICSVIEW
+
+QT_END_NAMESPACE
+
+#endif //QGRAPHICSWIDGET_P_H
+
diff --git a/src/widgets/graphicsview/qgridlayoutengine.cpp b/src/widgets/graphicsview/qgridlayoutengine.cpp
new file mode 100644
index 0000000000..b8586cef21
--- /dev/null
+++ b/src/widgets/graphicsview/qgridlayoutengine.cpp
@@ -0,0 +1,1742 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** 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 Technology Preview License Agreement accompanying
+** this package.
+**
+** 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.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qglobal.h"
+
+#ifndef QT_NO_GRAPHICSVIEW
+
+#include <math.h>
+
+#include "qgraphicslayoutitem.h"
+#include "qgridlayoutengine_p.h"
+#include "qstyleoption.h"
+#include "qvarlengtharray.h"
+
+#include <QtDebug>
+#include <QtCore/qmath.h>
+
+QT_BEGIN_NAMESPACE
+
+template <typename T>
+static void insertOrRemoveItems(QVector<T> &items, int index, int delta)
+{
+ int count = items.count();
+ if (index < count) {
+ if (delta > 0) {
+ items.insert(index, delta, T());
+ } else if (delta < 0) {
+ items.remove(index, qMin(-delta, count - index));
+ }
+ }
+}
+
+static qreal growthFactorBelowPreferredSize(qreal desired, qreal sumAvailable, qreal sumDesired)
+{
+ Q_ASSERT(sumDesired != 0.0);
+ return desired * qPow(sumAvailable / sumDesired, desired / sumDesired);
+}
+
+static qreal fixedDescent(qreal descent, qreal ascent, qreal targetSize)
+{
+ if (descent < 0.0)
+ return -1.0;
+
+ Q_ASSERT(descent >= 0.0);
+ Q_ASSERT(ascent >= 0.0);
+ Q_ASSERT(targetSize >= ascent + descent);
+
+ qreal extra = targetSize - (ascent + descent);
+ return descent + (extra / 2.0);
+}
+
+static qreal compare(const QGridLayoutBox &box1, const QGridLayoutBox &box2, int which)
+{
+ qreal size1 = box1.q_sizes(which);
+ qreal size2 = box2.q_sizes(which);
+
+ if (which == MaximumSize) {
+ return size2 - size1;
+ } else {
+ return size1 - size2;
+ }
+}
+
+void QGridLayoutBox::add(const QGridLayoutBox &other, int stretch, qreal spacing)
+{
+ Q_ASSERT(q_minimumDescent < 0.0);
+
+ q_minimumSize += other.q_minimumSize + spacing;
+ q_preferredSize += other.q_preferredSize + spacing;
+ q_maximumSize += ((stretch == 0) ? other.q_preferredSize : other.q_maximumSize) + spacing;
+}
+
+void QGridLayoutBox::combine(const QGridLayoutBox &other)
+{
+ q_minimumDescent = qMax(q_minimumDescent, other.q_minimumDescent);
+ q_minimumAscent = qMax(q_minimumAscent, other.q_minimumAscent);
+
+ q_minimumSize = qMax(q_minimumAscent + q_minimumDescent,
+ qMax(q_minimumSize, other.q_minimumSize));
+ qreal maxMax;
+ if (q_maximumSize == FLT_MAX && other.q_maximumSize != FLT_MAX)
+ maxMax = other.q_maximumSize;
+ else if (other.q_maximumSize == FLT_MAX && q_maximumSize != FLT_MAX)
+ maxMax = q_maximumSize;
+ else
+ maxMax = qMax(q_maximumSize, other.q_maximumSize);
+
+ q_maximumSize = qMax(q_minimumSize, maxMax);
+ q_preferredSize = qBound(q_minimumSize, qMax(q_preferredSize, other.q_preferredSize),
+ q_maximumSize);
+}
+
+void QGridLayoutBox::normalize()
+{
+ q_maximumSize = qMax(qreal(0.0), q_maximumSize);
+ q_minimumSize = qBound(qreal(0.0), q_minimumSize, q_maximumSize);
+ q_preferredSize = qBound(q_minimumSize, q_preferredSize, q_maximumSize);
+ q_minimumDescent = qMin(q_minimumDescent, q_minimumSize);
+
+ Q_ASSERT((q_minimumDescent < 0.0) == (q_minimumAscent < 0.0));
+}
+
+#ifdef QT_DEBUG
+void QGridLayoutBox::dump(int indent) const
+{
+ qDebug("%*sBox (%g <= %g <= %g [%g/%g])", indent, "", q_minimumSize, q_preferredSize,
+ q_maximumSize, q_minimumAscent, q_minimumDescent);
+}
+#endif
+
+bool operator==(const QGridLayoutBox &box1, const QGridLayoutBox &box2)
+{
+ for (int i = 0; i < NSizes; ++i) {
+ if (box1.q_sizes(i) != box2.q_sizes(i))
+ return false;
+ }
+ return box1.q_minimumDescent == box2.q_minimumDescent
+ && box1.q_minimumAscent == box2.q_minimumAscent;
+}
+
+void QGridLayoutRowData::reset(int count)
+{
+ ignore.fill(false, count);
+ boxes.fill(QGridLayoutBox(), count);
+ multiCellMap.clear();
+ stretches.fill(0, count);
+ spacings.fill(0.0, count);
+ hasIgnoreFlag = false;
+}
+
+void QGridLayoutRowData::distributeMultiCells(const QGridLayoutRowInfo &rowInfo)
+{
+ MultiCellMap::const_iterator i = multiCellMap.constBegin();
+ for (; i != multiCellMap.constEnd(); ++i) {
+ int start = i.key().first;
+ int span = i.key().second;
+ int end = start + span;
+ const QGridLayoutBox &box = i.value().q_box;
+ int stretch = i.value().q_stretch;
+
+ QGridLayoutBox totalBox = this->totalBox(start, end);
+ QVarLengthArray<QGridLayoutBox> extras(span);
+ QVarLengthArray<qreal> dummy(span);
+ QVarLengthArray<qreal> newSizes(span);
+
+ for (int j = 0; j < NSizes; ++j) {
+ qreal extra = compare(box, totalBox, j);
+ if (extra > 0.0) {
+ calculateGeometries(start, end, box.q_sizes(j), dummy.data(), newSizes.data(),
+ 0, totalBox, rowInfo);
+
+ for (int k = 0; k < span; ++k)
+ extras[k].q_sizes(j) = newSizes[k];
+ }
+ }
+
+ for (int k = 0; k < span; ++k) {
+ boxes[start + k].combine(extras[k]);
+ if (stretch != 0)
+ stretches[start + k] = qMax(stretches[start + k], stretch);
+ }
+ }
+ multiCellMap.clear();
+}
+
+void QGridLayoutRowData::calculateGeometries(int start, int end, qreal targetSize, qreal *positions,
+ qreal *sizes, qreal *descents,
+ const QGridLayoutBox &totalBox,
+ const QGridLayoutRowInfo &rowInfo)
+{
+ Q_ASSERT(end > start);
+
+ targetSize = qMax(totalBox.q_minimumSize, targetSize);
+
+ int n = end - start;
+ QVarLengthArray<qreal> newSizes(n);
+ QVarLengthArray<qreal> factors(n);
+ qreal sumFactors = 0.0;
+ int sumStretches = 0;
+ qreal sumAvailable;
+
+ for (int i = 0; i < n; ++i) {
+ if (stretches[start + i] > 0)
+ sumStretches += stretches[start + i];
+ }
+
+ if (targetSize < totalBox.q_preferredSize) {
+ stealBox(start, end, MinimumSize, positions, sizes);
+
+ sumAvailable = targetSize - totalBox.q_minimumSize;
+ if (sumAvailable > 0.0) {
+ qreal sumDesired = totalBox.q_preferredSize - totalBox.q_minimumSize;
+
+ for (int i = 0; i < n; ++i) {
+ if (ignore.testBit(start + i)) {
+ factors[i] = 0.0;
+ continue;
+ }
+
+ const QGridLayoutBox &box = boxes.at(start + i);
+ qreal desired = box.q_preferredSize - box.q_minimumSize;
+ factors[i] = growthFactorBelowPreferredSize(desired, sumAvailable, sumDesired);
+ sumFactors += factors[i];
+ }
+
+ for (int i = 0; i < n; ++i) {
+ Q_ASSERT(sumFactors > 0.0);
+ qreal delta = sumAvailable * factors[i] / sumFactors;
+ newSizes[i] = sizes[i] + delta;
+ }
+ }
+ } else {
+ bool isLargerThanMaximum = (targetSize > totalBox.q_maximumSize);
+ if (isLargerThanMaximum) {
+ stealBox(start, end, MaximumSize, positions, sizes);
+ sumAvailable = targetSize - totalBox.q_maximumSize;
+ } else {
+ stealBox(start, end, PreferredSize, positions, sizes);
+ sumAvailable = targetSize - totalBox.q_preferredSize;
+ }
+
+ if (sumAvailable > 0.0) {
+ qreal sumCurrentAvailable = sumAvailable;
+ bool somethingHasAMaximumSize = false;
+
+ qreal sumSizes = 0.0;
+ for (int i = 0; i < n; ++i)
+ sumSizes += sizes[i];
+
+ for (int i = 0; i < n; ++i) {
+ if (ignore.testBit(start + i)) {
+ newSizes[i] = 0.0;
+ factors[i] = 0.0;
+ continue;
+ }
+
+ const QGridLayoutBox &box = boxes.at(start + i);
+ qreal boxSize;
+
+ qreal desired;
+ if (isLargerThanMaximum) {
+ boxSize = box.q_maximumSize;
+ desired = rowInfo.boxes.value(start + i).q_maximumSize - boxSize;
+ } else {
+ boxSize = box.q_preferredSize;
+ desired = box.q_maximumSize - boxSize;
+ }
+ if (desired == 0.0) {
+ newSizes[i] = sizes[i];
+ factors[i] = 0.0;
+ } else {
+ Q_ASSERT(desired > 0.0);
+
+ int stretch = stretches[start + i];
+ if (sumStretches == 0) {
+ if (hasIgnoreFlag) {
+ factors[i] = (stretch < 0) ? 1.0 : 0.0;
+ } else {
+ factors[i] = (stretch < 0) ? sizes[i] : 0.0;
+ }
+ } else if (stretch == sumStretches) {
+ factors[i] = 1.0;
+ } else if (stretch <= 0) {
+ factors[i] = 0.0;
+ } else {
+ qreal ultimateSize;
+ qreal ultimateSumSizes;
+ qreal x = ((stretch * sumSizes)
+ - (sumStretches * boxSize))
+ / (sumStretches - stretch);
+ if (x >= 0.0) {
+ ultimateSize = boxSize + x;
+ ultimateSumSizes = sumSizes + x;
+ } else {
+ ultimateSize = boxSize;
+ ultimateSumSizes = (sumStretches * boxSize)
+ / stretch;
+ }
+
+ /*
+ We multiply these by 1.5 to give some space for a smooth transition
+ (at the expense of the stretch factors, which are not fully respected
+ during the transition).
+ */
+ ultimateSize = ultimateSize * 3 / 2;
+ ultimateSumSizes = ultimateSumSizes * 3 / 2;
+
+ qreal beta = ultimateSumSizes - sumSizes;
+ if (!beta) {
+ factors[i] = 1;
+ } else {
+ qreal alpha = qMin(sumCurrentAvailable, beta);
+ qreal ultimateFactor = (stretch * ultimateSumSizes / sumStretches)
+ - (boxSize);
+ qreal transitionalFactor = sumCurrentAvailable * (ultimateSize - boxSize) / beta;
+
+ factors[i] = ((alpha * ultimateFactor)
+ + ((beta - alpha) * transitionalFactor)) / beta;
+ }
+
+ }
+ sumFactors += factors[i];
+ if (desired < sumCurrentAvailable)
+ somethingHasAMaximumSize = true;
+
+ newSizes[i] = -1.0;
+ }
+ }
+
+ bool keepGoing = somethingHasAMaximumSize;
+ while (keepGoing) {
+ keepGoing = false;
+
+ for (int i = 0; i < n; ++i) {
+ if (newSizes[i] >= 0.0)
+ continue;
+
+ qreal maxBoxSize;
+ if (isLargerThanMaximum)
+ maxBoxSize = rowInfo.boxes.value(start + i).q_maximumSize;
+ else
+ maxBoxSize = boxes.at(start + i).q_maximumSize;
+
+ qreal avail = sumCurrentAvailable * factors[i] / sumFactors;
+ if (sizes[i] + avail >= maxBoxSize) {
+ newSizes[i] = maxBoxSize;
+ sumCurrentAvailable -= maxBoxSize - sizes[i];
+ sumFactors -= factors[i];
+ keepGoing = (sumCurrentAvailable > 0.0);
+ if (!keepGoing)
+ break;
+ }
+ }
+ }
+
+ for (int i = 0; i < n; ++i) {
+ if (newSizes[i] < 0.0) {
+ qreal delta = (sumFactors == 0.0) ? 0.0
+ : sumCurrentAvailable * factors[i] / sumFactors;
+ newSizes[i] = sizes[i] + delta;
+ }
+ }
+ }
+ }
+
+ if (sumAvailable > 0) {
+ qreal offset = 0;
+ for (int i = 0; i < n; ++i) {
+ qreal delta = newSizes[i] - sizes[i];
+ positions[i] += offset;
+ sizes[i] += delta;
+ offset += delta;
+ }
+
+#if 0 // some "pixel allocation"
+ int surplus = targetSize - (positions[n - 1] + sizes[n - 1]);
+ Q_ASSERT(surplus >= 0 && surplus <= n);
+
+ int prevSurplus = -1;
+ while (surplus > 0 && surplus != prevSurplus) {
+ prevSurplus = surplus;
+
+ int offset = 0;
+ for (int i = 0; i < n; ++i) {
+ const QGridLayoutBox &box = boxes.at(start + i);
+ int delta = (!ignore.testBit(start + i) && surplus > 0
+ && factors[i] > 0 && sizes[i] < box.q_maximumSize)
+ ? 1 : 0;
+
+ positions[i] += offset;
+ sizes[i] += delta;
+ offset += delta;
+ surplus -= delta;
+ }
+ }
+ Q_ASSERT(surplus == 0);
+#endif
+ }
+
+ if (descents) {
+ for (int i = 0; i < n; ++i) {
+ if (ignore.testBit(start + i))
+ continue;
+ const QGridLayoutBox &box = boxes.at(start + i);
+ descents[i] = fixedDescent(box.q_minimumDescent, box.q_minimumAscent, sizes[i]);
+ }
+ }
+}
+
+QGridLayoutBox QGridLayoutRowData::totalBox(int start, int end) const
+{
+ QGridLayoutBox result;
+ if (start < end) {
+ result.q_maximumSize = 0.0;
+ qreal nextSpacing = 0.0;
+ for (int i = start; i < end; ++i) {
+ result.add(boxes.at(i), stretches.at(i), nextSpacing);
+ nextSpacing = spacings.at(i);
+ }
+ }
+ return result;
+}
+
+void QGridLayoutRowData::stealBox(int start, int end, int which, qreal *positions, qreal *sizes)
+{
+ qreal offset = 0.0;
+ qreal nextSpacing = 0.0;
+
+ for (int i = start; i < end; ++i) {
+ qreal avail = 0.0;
+
+ if (!ignore.testBit(i)) {
+ const QGridLayoutBox &box = boxes.at(i);
+ avail = box.q_sizes(which);
+ offset += nextSpacing;
+ nextSpacing = spacings.at(i);
+ }
+
+ *positions++ = offset;
+ *sizes++ = avail;
+ offset += avail;
+ }
+}
+
+#ifdef QT_DEBUG
+void QGridLayoutRowData::dump(int indent) const
+{
+ qDebug("%*sData", indent, "");
+
+ for (int i = 0; i < ignore.count(); ++i) {
+ qDebug("%*s Row %d (stretch %d, spacing %g)", indent, "", i, stretches.at(i),
+ spacings.at(i));
+ if (ignore.testBit(i))
+ qDebug("%*s Ignored", indent, "");
+ boxes.at(i).dump(indent + 2);
+ }
+
+ MultiCellMap::const_iterator it = multiCellMap.constBegin();
+ while (it != multiCellMap.constEnd()) {
+ qDebug("%*s Multi-cell entry <%d, %d> (stretch %d)", indent, "", it.key().first,
+ it.key().second, it.value().q_stretch);
+ it.value().q_box.dump(indent + 2);
+ }
+}
+#endif
+
+QGridLayoutItem::QGridLayoutItem(QGridLayoutEngine *engine, QGraphicsLayoutItem *layoutItem,
+ int row, int column, int rowSpan, int columnSpan,
+ Qt::Alignment alignment, int itemAtIndex)
+ : q_engine(engine), q_layoutItem(layoutItem), q_alignment(alignment)
+{
+ q_firstRows[Hor] = column;
+ q_firstRows[Ver] = row;
+ q_rowSpans[Hor] = columnSpan;
+ q_rowSpans[Ver] = rowSpan;
+ q_stretches[Hor] = -1;
+ q_stretches[Ver] = -1;
+
+ q_engine->insertItem(this, itemAtIndex);
+}
+
+int QGridLayoutItem::firstRow(Qt::Orientation orientation) const
+{
+ return q_firstRows[orientation == Qt::Vertical];
+}
+
+int QGridLayoutItem::firstColumn(Qt::Orientation orientation) const
+{
+ return q_firstRows[orientation == Qt::Horizontal];
+}
+
+int QGridLayoutItem::lastRow(Qt::Orientation orientation) const
+{
+ return firstRow(orientation) + rowSpan(orientation) - 1;
+}
+
+int QGridLayoutItem::lastColumn(Qt::Orientation orientation) const
+{
+ return firstColumn(orientation) + columnSpan(orientation) - 1;
+}
+
+int QGridLayoutItem::rowSpan(Qt::Orientation orientation) const
+{
+ return q_rowSpans[orientation == Qt::Vertical];
+}
+
+int QGridLayoutItem::columnSpan(Qt::Orientation orientation) const
+{
+ return q_rowSpans[orientation == Qt::Horizontal];
+}
+
+void QGridLayoutItem::setFirstRow(int row, Qt::Orientation orientation)
+{
+ q_firstRows[orientation == Qt::Vertical] = row;
+}
+
+void QGridLayoutItem::setRowSpan(int rowSpan, Qt::Orientation orientation)
+{
+ q_rowSpans[orientation == Qt::Vertical] = rowSpan;
+}
+
+int QGridLayoutItem::stretchFactor(Qt::Orientation orientation) const
+{
+ int stretch = q_stretches[orientation == Qt::Vertical];
+ if (stretch >= 0)
+ return stretch;
+
+ QSizePolicy::Policy policy = sizePolicy(orientation);
+
+ if (policy & QSizePolicy::ExpandFlag) {
+ return 1;
+ } else if (policy & QSizePolicy::GrowFlag) {
+ return -1; // because we max it up
+ } else {
+ return 0;
+ }
+}
+
+void QGridLayoutItem::setStretchFactor(int stretch, Qt::Orientation orientation)
+{
+ Q_ASSERT(stretch >= 0); // ### deal with too big stretches
+ q_stretches[orientation == Qt::Vertical] = stretch;
+}
+
+QSizePolicy::Policy QGridLayoutItem::sizePolicy(Qt::Orientation orientation) const
+{
+ QSizePolicy sizePolicy(q_layoutItem->sizePolicy());
+ return (orientation == Qt::Horizontal) ? sizePolicy.horizontalPolicy()
+ : sizePolicy.verticalPolicy();
+}
+
+/*
+ returns true if the size policy returns true for either hasHeightForWidth()
+ or hasWidthForHeight()
+ */
+bool QGridLayoutItem::hasDynamicConstraint() const
+{
+ return QGraphicsLayoutItemPrivate::get(q_layoutItem)->hasHeightForWidth()
+ || QGraphicsLayoutItemPrivate::get(q_layoutItem)->hasWidthForHeight();
+}
+
+Qt::Orientation QGridLayoutItem::dynamicConstraintOrientation() const
+{
+ if (QGraphicsLayoutItemPrivate::get(q_layoutItem)->hasHeightForWidth())
+ return Qt::Vertical;
+ else //if (QGraphicsLayoutItemPrivate::get(q_layoutItem)->hasWidthForHeight())
+ return Qt::Horizontal;
+}
+
+QSizePolicy::ControlTypes QGridLayoutItem::controlTypes(LayoutSide /* side */) const
+{
+ return q_layoutItem->sizePolicy().controlType();
+}
+
+QSizeF QGridLayoutItem::sizeHint(Qt::SizeHint which, const QSizeF &constraint) const
+{
+ return q_layoutItem->effectiveSizeHint(which, constraint);
+}
+
+QGridLayoutBox QGridLayoutItem::box(Qt::Orientation orientation, qreal constraint) const
+{
+ QGridLayoutBox result;
+ QSizePolicy::Policy policy = sizePolicy(orientation);
+
+ if (orientation == Qt::Horizontal) {
+ QSizeF constraintSize(-1.0, constraint);
+
+ result.q_preferredSize = sizeHint(Qt::PreferredSize, constraintSize).width();
+
+ if (policy & QSizePolicy::ShrinkFlag) {
+ result.q_minimumSize = sizeHint(Qt::MinimumSize, constraintSize).width();
+ } else {
+ result.q_minimumSize = result.q_preferredSize;
+ }
+
+ if (policy & (QSizePolicy::GrowFlag | QSizePolicy::ExpandFlag)) {
+ result.q_maximumSize = sizeHint(Qt::MaximumSize, constraintSize).width();
+ } else {
+ result.q_maximumSize = result.q_preferredSize;
+ }
+ } else {
+ QSizeF constraintSize(constraint, -1.0);
+
+ result.q_preferredSize = sizeHint(Qt::PreferredSize, constraintSize).height();
+
+ if (policy & QSizePolicy::ShrinkFlag) {
+ result.q_minimumSize = sizeHint(Qt::MinimumSize, constraintSize).height();
+ } else {
+ result.q_minimumSize = result.q_preferredSize;
+ }
+
+ if (policy & (QSizePolicy::GrowFlag | QSizePolicy::ExpandFlag)) {
+ result.q_maximumSize = sizeHint(Qt::MaximumSize, constraintSize).height();
+ } else {
+ result.q_maximumSize = result.q_preferredSize;
+ }
+
+ result.q_minimumDescent = sizeHint(Qt::MinimumDescent, constraintSize).height();
+ if (result.q_minimumDescent >= 0.0)
+ result.q_minimumAscent = result.q_minimumSize - result.q_minimumDescent;
+ }
+ if (policy & QSizePolicy::IgnoreFlag)
+ result.q_preferredSize = result.q_minimumSize;
+
+ return result;
+}
+
+QRectF QGridLayoutItem::geometryWithin(qreal x, qreal y, qreal width, qreal height,
+ qreal rowDescent) const
+{
+ rowDescent = -1.0; // ### This disables the descent
+
+ QGridLayoutBox vBox = box(Qt::Vertical);
+ if (vBox.q_minimumDescent < 0.0 || rowDescent < 0.0) {
+ qreal cellWidth = width;
+ qreal cellHeight = height;
+
+
+ QSizeF size = effectiveMaxSize(QSizeF(-1,-1));
+ if (hasDynamicConstraint()) {
+ if (dynamicConstraintOrientation() == Qt::Vertical) {
+ if (size.width() > cellWidth)
+ size = effectiveMaxSize(QSizeF(cellWidth, -1));
+ } else if (size.height() > cellHeight) {
+ size = effectiveMaxSize(QSizeF(-1, cellHeight));
+ }
+ }
+ size = size.boundedTo(QSizeF(cellWidth, cellHeight));
+ width = size.width();
+ height = size.height();
+
+ Qt::Alignment align = q_engine->effectiveAlignment(this);
+ switch (align & Qt::AlignHorizontal_Mask) {
+ case Qt::AlignHCenter:
+ x += (cellWidth - width)/2;
+ break;
+ case Qt::AlignRight:
+ x += cellWidth - width;
+ break;
+ default:
+ break;
+ }
+ switch (align & Qt::AlignVertical_Mask) {
+ case Qt::AlignVCenter:
+ y += (cellHeight - height)/2;
+ break;
+ case Qt::AlignBottom:
+ y += cellHeight - height;
+ break;
+ default:
+ break;
+ }
+ return QRectF(x, y, width, height);
+ } else {
+ qreal descent = vBox.q_minimumDescent;
+ qreal ascent = vBox.q_minimumSize - descent;
+ return QRectF(x, y + height - rowDescent - ascent, width, ascent + descent);
+ }
+}
+
+void QGridLayoutItem::setGeometry(const QRectF &rect)
+{
+ q_layoutItem->setGeometry(rect);
+}
+
+void QGridLayoutItem::transpose()
+{
+ qSwap(q_firstRows[Hor], q_firstRows[Ver]);
+ qSwap(q_rowSpans[Hor], q_rowSpans[Ver]);
+ qSwap(q_stretches[Hor], q_stretches[Ver]);
+}
+
+void QGridLayoutItem::insertOrRemoveRows(int row, int delta, Qt::Orientation orientation)
+{
+ int oldFirstRow = firstRow(orientation);
+ if (oldFirstRow >= row) {
+ setFirstRow(oldFirstRow + delta, orientation);
+ } else if (lastRow(orientation) >= row) {
+ setRowSpan(rowSpan(orientation) + delta, orientation);
+ }
+}
+/*!
+ \internal
+ returns the effective maximumSize, will take the sizepolicy into
+ consideration. (i.e. if sizepolicy does not have QSizePolicy::Grow, then
+ maxSizeHint will be the preferredSize)
+ Note that effectiveSizeHint does not take sizePolicy into consideration,
+ (since it only evaluates the hints, as the name implies)
+*/
+QSizeF QGridLayoutItem::effectiveMaxSize(const QSizeF &constraint) const
+{
+ QSizeF size = constraint;
+ bool vGrow = (sizePolicy(Qt::Vertical) & QSizePolicy::GrowFlag) == QSizePolicy::GrowFlag;
+ bool hGrow = (sizePolicy(Qt::Horizontal) & QSizePolicy::GrowFlag) == QSizePolicy::GrowFlag;
+ if (!vGrow || !hGrow) {
+ QSizeF pref = layoutItem()->effectiveSizeHint(Qt::PreferredSize, constraint);
+ if (!vGrow)
+ size.setHeight(pref.height());
+ if (!hGrow)
+ size.setWidth(pref.width());
+ }
+
+ if (!size.isValid()) {
+ QSizeF maxSize = layoutItem()->effectiveSizeHint(Qt::MaximumSize, size);
+ if (size.width() == -1)
+ size.setWidth(maxSize.width());
+ if (size.height() == -1)
+ size.setHeight(maxSize.height());
+ }
+ return size;
+}
+
+#ifdef QT_DEBUG
+void QGridLayoutItem::dump(int indent) const
+{
+ qDebug("%*s%p (%d, %d) %d x %d", indent, "", q_layoutItem, firstRow(), firstColumn(),
+ rowSpan(), columnSpan());
+
+ if (q_stretches[Hor] >= 0)
+ qDebug("%*s Horizontal stretch: %d", indent, "", q_stretches[Hor]);
+ if (q_stretches[Ver] >= 0)
+ qDebug("%*s Vertical stretch: %d", indent, "", q_stretches[Ver]);
+ if (q_alignment != 0)
+ qDebug("%*s Alignment: %x", indent, "", uint(q_alignment));
+ qDebug("%*s Horizontal size policy: %x Vertical size policy: %x",
+ indent, "", sizePolicy(Qt::Horizontal), sizePolicy(Qt::Vertical));
+}
+#endif
+
+void QGridLayoutRowInfo::insertOrRemoveRows(int row, int delta)
+{
+ count += delta;
+
+ insertOrRemoveItems(stretches, row, delta);
+ insertOrRemoveItems(spacings, row, delta);
+ insertOrRemoveItems(alignments, row, delta);
+ insertOrRemoveItems(boxes, row, delta);
+}
+
+#ifdef QT_DEBUG
+void QGridLayoutRowInfo::dump(int indent) const
+{
+ qDebug("%*sInfo (count: %d)", indent, "", count);
+ for (int i = 0; i < count; ++i) {
+ QString message;
+
+ if (stretches.value(i).value() >= 0)
+ message += QString::fromAscii(" stretch %1").arg(stretches.value(i).value());
+ if (spacings.value(i).value() >= 0.0)
+ message += QString::fromAscii(" spacing %1").arg(spacings.value(i).value());
+ if (alignments.value(i) != 0)
+ message += QString::fromAscii(" alignment %1").arg(int(alignments.value(i)), 16);
+
+ if (!message.isEmpty() || boxes.value(i) != QGridLayoutBox()) {
+ qDebug("%*s Row %d:%s", indent, "", i, qPrintable(message));
+ if (boxes.value(i) != QGridLayoutBox())
+ boxes.value(i).dump(indent + 1);
+ }
+ }
+}
+#endif
+
+QGridLayoutEngine::QGridLayoutEngine()
+{
+ m_visualDirection = Qt::LeftToRight;
+ invalidate();
+}
+
+int QGridLayoutEngine::rowCount(Qt::Orientation orientation) const
+{
+ return q_infos[orientation == Qt::Vertical].count;
+}
+
+int QGridLayoutEngine::columnCount(Qt::Orientation orientation) const
+{
+ return q_infos[orientation == Qt::Horizontal].count;
+}
+
+int QGridLayoutEngine::itemCount() const
+{
+ return q_items.count();
+}
+
+QGridLayoutItem *QGridLayoutEngine::itemAt(int index) const
+{
+ Q_ASSERT(index >= 0 && index < itemCount());
+ return q_items.at(index);
+}
+
+int QGridLayoutEngine::indexOf(QGraphicsLayoutItem *item) const
+{
+ for (int i = 0; i < q_items.size(); ++i) {
+ if (item == q_items.at(i)->layoutItem())
+ return i;
+ }
+ return -1;
+}
+
+int QGridLayoutEngine::effectiveFirstRow(Qt::Orientation orientation) const
+{
+ ensureEffectiveFirstAndLastRows();
+ return q_cachedEffectiveFirstRows[orientation == Qt::Vertical];
+}
+
+int QGridLayoutEngine::effectiveLastRow(Qt::Orientation orientation) const
+{
+ ensureEffectiveFirstAndLastRows();
+ return q_cachedEffectiveLastRows[orientation == Qt::Vertical];
+}
+
+void QGridLayoutEngine::setSpacing(qreal spacing, Qt::Orientations orientations)
+{
+ Q_ASSERT(spacing >= 0.0);
+ if (orientations & Qt::Horizontal)
+ q_defaultSpacings[Hor].setUserValue(spacing);
+ if (orientations & Qt::Vertical)
+ q_defaultSpacings[Ver].setUserValue(spacing);
+
+ invalidate();
+}
+
+qreal QGridLayoutEngine::spacing(const QLayoutStyleInfo &styleInfo, Qt::Orientation orientation) const
+{
+ if (q_defaultSpacings[orientation == Qt::Vertical].isDefault()) {
+ QStyle *style = styleInfo.style();
+ QStyleOption option;
+ option.initFrom(styleInfo.widget());
+ qreal defaultSpacing = (qreal)style->pixelMetric(orientation == Qt::Vertical ? QStyle::PM_LayoutVerticalSpacing
+ : QStyle::PM_LayoutHorizontalSpacing, &option, styleInfo.widget());
+ q_defaultSpacings[orientation == Qt::Vertical].setCachedValue(defaultSpacing);
+ }
+ return q_defaultSpacings[orientation == Qt::Vertical].value();
+}
+
+void QGridLayoutEngine::setRowSpacing(int row, qreal spacing, Qt::Orientation orientation)
+{
+ Q_ASSERT(row >= 0);
+
+ QGridLayoutRowInfo &rowInfo = q_infos[orientation == Qt::Vertical];
+ if (row >= rowInfo.spacings.count())
+ rowInfo.spacings.resize(row + 1);
+ if (spacing >= 0)
+ rowInfo.spacings[row].setUserValue(spacing);
+ else
+ rowInfo.spacings[row] = QLayoutParameter<qreal>();
+ invalidate();
+}
+
+qreal QGridLayoutEngine::rowSpacing(int row, Qt::Orientation orientation) const
+{
+ QLayoutParameter<qreal> spacing = q_infos[orientation == Qt::Vertical].spacings.value(row);
+ if (!spacing.isDefault())
+ return spacing.value();
+ return q_defaultSpacings[orientation == Qt::Vertical].value();
+}
+
+void QGridLayoutEngine::setRowStretchFactor(int row, int stretch, Qt::Orientation orientation)
+{
+ Q_ASSERT(row >= 0);
+ Q_ASSERT(stretch >= 0);
+
+ maybeExpandGrid(row, -1, orientation);
+
+ QGridLayoutRowInfo &rowInfo = q_infos[orientation == Qt::Vertical];
+ if (row >= rowInfo.stretches.count())
+ rowInfo.stretches.resize(row + 1);
+ rowInfo.stretches[row].setUserValue(stretch);
+}
+
+int QGridLayoutEngine::rowStretchFactor(int row, Qt::Orientation orientation) const
+{
+ QStretchParameter stretch = q_infos[orientation == Qt::Vertical].stretches.value(row);
+ if (!stretch.isDefault())
+ return stretch.value();
+ return 0;
+}
+
+void QGridLayoutEngine::setStretchFactor(QGraphicsLayoutItem *layoutItem, int stretch,
+ Qt::Orientation orientation)
+{
+ Q_ASSERT(stretch >= 0);
+
+ if (QGridLayoutItem *item = findLayoutItem(layoutItem))
+ item->setStretchFactor(stretch, orientation);
+}
+
+int QGridLayoutEngine::stretchFactor(QGraphicsLayoutItem *layoutItem, Qt::Orientation orientation) const
+{
+ if (QGridLayoutItem *item = findLayoutItem(layoutItem))
+ return item->stretchFactor(orientation);
+ return 0;
+}
+
+void QGridLayoutEngine::setRowSizeHint(Qt::SizeHint which, int row, qreal size,
+ Qt::Orientation orientation)
+{
+ Q_ASSERT(row >= 0);
+ Q_ASSERT(size >= 0.0);
+
+ maybeExpandGrid(row, -1, orientation);
+
+ QGridLayoutRowInfo &rowInfo = q_infos[orientation == Qt::Vertical];
+ if (row >= rowInfo.boxes.count())
+ rowInfo.boxes.resize(row + 1);
+ rowInfo.boxes[row].q_sizes(which) = size;
+}
+
+qreal QGridLayoutEngine::rowSizeHint(Qt::SizeHint which, int row, Qt::Orientation orientation) const
+{
+ return q_infos[orientation == Qt::Vertical].boxes.value(row).q_sizes(which);
+}
+
+void QGridLayoutEngine::setRowAlignment(int row, Qt::Alignment alignment,
+ Qt::Orientation orientation)
+{
+ Q_ASSERT(row >= 0);
+
+ maybeExpandGrid(row, -1, orientation);
+
+ QGridLayoutRowInfo &rowInfo = q_infos[orientation == Qt::Vertical];
+ if (row >= rowInfo.alignments.count())
+ rowInfo.alignments.resize(row + 1);
+ rowInfo.alignments[row] = alignment;
+}
+
+Qt::Alignment QGridLayoutEngine::rowAlignment(int row, Qt::Orientation orientation) const
+{
+ Q_ASSERT(row >= 0);
+ return q_infos[orientation == Qt::Vertical].alignments.value(row);
+}
+
+void QGridLayoutEngine::setAlignment(QGraphicsLayoutItem *layoutItem, Qt::Alignment alignment)
+{
+ if (QGridLayoutItem *item = findLayoutItem(layoutItem))
+ item->setAlignment(alignment);
+ invalidate();
+}
+
+Qt::Alignment QGridLayoutEngine::alignment(QGraphicsLayoutItem *layoutItem) const
+{
+ if (QGridLayoutItem *item = findLayoutItem(layoutItem))
+ return item->alignment();
+ return 0;
+}
+
+Qt::Alignment QGridLayoutEngine::effectiveAlignment(const QGridLayoutItem *layoutItem) const
+{
+ Qt::Alignment align = layoutItem->alignment();
+ if (!(align & Qt::AlignVertical_Mask)) {
+ // no vertical alignment, respect the row alignment
+ int y = layoutItem->firstRow();
+ align |= (rowAlignment(y, Qt::Vertical) & Qt::AlignVertical_Mask);
+ }
+ if (!(align & Qt::AlignHorizontal_Mask)) {
+ // no horizontal alignment, respect the column alignment
+ int x = layoutItem->firstColumn();
+ align |= (rowAlignment(x, Qt::Horizontal) & Qt::AlignHorizontal_Mask);
+ }
+ return align;
+}
+
+/*!
+ \internal
+ The \a index is only used by QGraphicsLinearLayout to ensure that itemAt() reflects the order
+ of visual arrangement. Strictly speaking it does not have to, but most people expect it to.
+ (And if it didn't we would have to add itemArrangedAt(int index) or something..)
+ */
+void QGridLayoutEngine::insertItem(QGridLayoutItem *item, int index)
+{
+ maybeExpandGrid(item->lastRow(), item->lastColumn());
+
+ if (index == -1)
+ q_items.append(item);
+ else
+ q_items.insert(index, item);
+
+ for (int i = item->firstRow(); i <= item->lastRow(); ++i) {
+ for (int j = item->firstColumn(); j <= item->lastColumn(); ++j) {
+ if (itemAt(i, j))
+ qWarning("QGridLayoutEngine::addItem: Cell (%d, %d) already taken", i, j);
+ setItemAt(i, j, item);
+ }
+ }
+}
+
+void QGridLayoutEngine::addItem(QGridLayoutItem *item)
+{
+ insertItem(item, -1);
+}
+
+void QGridLayoutEngine::removeItem(QGridLayoutItem *item)
+{
+ Q_ASSERT(q_items.contains(item));
+
+ invalidate();
+
+ for (int i = item->firstRow(); i <= item->lastRow(); ++i) {
+ for (int j = item->firstColumn(); j <= item->lastColumn(); ++j) {
+ if (itemAt(i, j) == item)
+ setItemAt(i, j, 0);
+ }
+ }
+
+ q_items.removeAll(item);
+}
+
+QGridLayoutItem *QGridLayoutEngine::findLayoutItem(QGraphicsLayoutItem *layoutItem) const
+{
+ for (int i = q_items.count() - 1; i >= 0; --i) {
+ QGridLayoutItem *item = q_items.at(i);
+ if (item->layoutItem() == layoutItem)
+ return item;
+ }
+ return 0;
+}
+
+QGridLayoutItem *QGridLayoutEngine::itemAt(int row, int column, Qt::Orientation orientation) const
+{
+ if (orientation == Qt::Horizontal)
+ qSwap(row, column);
+ if (uint(row) >= uint(rowCount()) || uint(column) >= uint(columnCount()))
+ return 0;
+ return q_grid.at((row * internalGridColumnCount()) + column);
+}
+
+void QGridLayoutEngine::invalidate()
+{
+ q_cachedEffectiveFirstRows[Hor] = -1;
+ q_cachedEffectiveFirstRows[Ver] = -1;
+ q_cachedEffectiveLastRows[Hor] = -1;
+ q_cachedEffectiveLastRows[Ver] = -1;
+ q_cachedDataForStyleInfo.invalidate();
+ q_cachedSize = QSizeF();
+ q_cachedConstraintOrientation = UnknownConstraint;
+}
+
+static void visualRect(QRectF *geom, Qt::LayoutDirection dir, const QRectF &contentsRect)
+{
+ if (dir == Qt::RightToLeft)
+ geom->moveRight(contentsRect.right() - (geom->left() - contentsRect.left()));
+}
+
+void QGridLayoutEngine::setGeometries(const QLayoutStyleInfo &styleInfo,
+ const QRectF &contentsGeometry)
+{
+ if (rowCount() < 1 || columnCount() < 1)
+ return;
+
+ ensureGeometries(styleInfo, contentsGeometry.size());
+
+ for (int i = q_items.count() - 1; i >= 0; --i) {
+ QGridLayoutItem *item = q_items.at(i);
+
+ qreal x = q_xx[item->firstColumn()];
+ qreal y = q_yy[item->firstRow()];
+ qreal width = q_widths[item->lastColumn()];
+ qreal height = q_heights[item->lastRow()];
+
+ if (item->columnSpan() != 1)
+ width += q_xx[item->lastColumn()] - x;
+ if (item->rowSpan() != 1)
+ height += q_yy[item->lastRow()] - y;
+
+ QRectF geom = item->geometryWithin(contentsGeometry.x() + x, contentsGeometry.y() + y,
+ width, height, q_descents[item->lastRow()]);
+ visualRect(&geom, visualDirection(), contentsGeometry);
+ item->setGeometry(geom);
+ }
+}
+
+// ### candidate for deletion
+QRectF QGridLayoutEngine::cellRect(const QLayoutStyleInfo &styleInfo,
+ const QRectF &contentsGeometry, int row, int column, int rowSpan,
+ int columnSpan) const
+{
+ if (uint(row) >= uint(rowCount()) || uint(column) >= uint(columnCount())
+ || rowSpan < 1 || columnSpan < 1)
+ return QRectF();
+
+ ensureGeometries(styleInfo, contentsGeometry.size());
+
+ int lastColumn = qMax(column + columnSpan, columnCount()) - 1;
+ int lastRow = qMax(row + rowSpan, rowCount()) - 1;
+
+ qreal x = q_xx[column];
+ qreal y = q_yy[row];
+ qreal width = q_widths[lastColumn];
+ qreal height = q_heights[lastRow];
+
+ if (columnSpan != 1)
+ width += q_xx[lastColumn] - x;
+ if (rowSpan != 1)
+ height += q_yy[lastRow] - y;
+
+ return QRectF(contentsGeometry.x() + x, contentsGeometry.y() + y, width, height);
+}
+
+QSizeF QGridLayoutEngine::sizeHint(const QLayoutStyleInfo &styleInfo, Qt::SizeHint which,
+ const QSizeF &constraint) const
+{
+ QGridLayoutBox sizehint_totalBoxes[NOrientations];
+
+ bool sizeHintCalculated = false;
+
+ if (hasDynamicConstraint() && rowCount() > 0 && columnCount() > 0) {
+ if (constraintOrientation() == Qt::Vertical) {
+ //We have items whose height depends on their width
+ if (constraint.width() >= 0) {
+ if (q_cachedDataForStyleInfo != styleInfo)
+ ensureColumnAndRowData(&q_columnData, &sizehint_totalBoxes[Hor], styleInfo, NULL, NULL, Qt::Horizontal);
+ else
+ sizehint_totalBoxes[Hor] = q_totalBoxes[Hor];
+ QVector<qreal> sizehint_xx;
+ QVector<qreal> sizehint_widths;
+
+ sizehint_xx.resize(columnCount());
+ sizehint_widths.resize(columnCount());
+ qreal width = constraint.width();
+ //Calculate column widths and positions, and put results in q_xx.data() and q_widths.data() so that we can use this information as
+ //constraints to find the row heights
+ q_columnData.calculateGeometries(0, columnCount(), width, sizehint_xx.data(), sizehint_widths.data(),
+ 0, sizehint_totalBoxes[Hor], q_infos[Hor]);
+ ensureColumnAndRowData(&q_rowData, &sizehint_totalBoxes[Ver], styleInfo, sizehint_xx.data(), sizehint_widths.data(), Qt::Vertical);
+ sizeHintCalculated = true;
+ }
+ } else {
+ if (constraint.height() >= 0) {
+ //We have items whose width depends on their height
+ ensureColumnAndRowData(&q_rowData, &sizehint_totalBoxes[Ver], styleInfo, NULL, NULL, Qt::Vertical);
+ QVector<qreal> sizehint_yy;
+ QVector<qreal> sizehint_heights;
+
+ sizehint_yy.resize(rowCount());
+ sizehint_heights.resize(rowCount());
+ qreal height = constraint.height();
+ //Calculate row heights and positions, and put results in q_yy.data() and q_heights.data() so that we can use this information as
+ //constraints to find the column widths
+ q_rowData.calculateGeometries(0, rowCount(), height, sizehint_yy.data(), sizehint_heights.data(),
+ 0, sizehint_totalBoxes[Ver], q_infos[Ver]);
+ ensureColumnAndRowData(&q_columnData, &sizehint_totalBoxes[Hor], styleInfo, sizehint_yy.data(), sizehint_heights.data(), Qt::Horizontal);
+ sizeHintCalculated = true;
+ }
+ }
+ }
+
+ if (!sizeHintCalculated) {
+ //No items with height for width, so it doesn't matter which order we do these in
+ if (q_cachedDataForStyleInfo != styleInfo) {
+ ensureColumnAndRowData(&q_columnData, &sizehint_totalBoxes[Hor], styleInfo, NULL, NULL, Qt::Horizontal);
+ ensureColumnAndRowData(&q_rowData, &sizehint_totalBoxes[Ver], styleInfo, NULL, NULL, Qt::Vertical);
+ } else {
+ sizehint_totalBoxes[Hor] = q_totalBoxes[Hor];
+ sizehint_totalBoxes[Ver] = q_totalBoxes[Ver];
+ }
+ }
+
+ switch (which) {
+ case Qt::MinimumSize:
+ return QSizeF(sizehint_totalBoxes[Hor].q_minimumSize, sizehint_totalBoxes[Ver].q_minimumSize);
+ case Qt::PreferredSize:
+ return QSizeF(sizehint_totalBoxes[Hor].q_preferredSize, sizehint_totalBoxes[Ver].q_preferredSize);
+ case Qt::MaximumSize:
+ return QSizeF(sizehint_totalBoxes[Hor].q_maximumSize, sizehint_totalBoxes[Ver].q_maximumSize);
+ case Qt::MinimumDescent:
+ return QSizeF(-1.0, sizehint_totalBoxes[Hor].q_minimumDescent); // ### doesn't work
+ default:
+ break;
+ }
+ return QSizeF();
+}
+
+QSizePolicy::ControlTypes QGridLayoutEngine::controlTypes(LayoutSide side) const
+{
+ Qt::Orientation orientation = (side == Top || side == Bottom) ? Qt::Vertical : Qt::Horizontal;
+ int row = (side == Top || side == Left) ? effectiveFirstRow(orientation)
+ : effectiveLastRow(orientation);
+ QSizePolicy::ControlTypes result = 0;
+
+ for (int column = columnCount(orientation) - 1; column >= 0; --column) {
+ if (QGridLayoutItem *item = itemAt(row, column, orientation))
+ result |= item->controlTypes(side);
+ }
+ return result;
+}
+
+void QGridLayoutEngine::transpose()
+{
+ invalidate();
+
+ for (int i = q_items.count() - 1; i >= 0; --i)
+ q_items.at(i)->transpose();
+
+ qSwap(q_defaultSpacings[Hor], q_defaultSpacings[Ver]);
+ qSwap(q_infos[Hor], q_infos[Ver]);
+
+ regenerateGrid();
+}
+
+void QGridLayoutEngine::setVisualDirection(Qt::LayoutDirection direction)
+{
+ m_visualDirection = direction;
+}
+
+Qt::LayoutDirection QGridLayoutEngine::visualDirection() const
+{
+ return m_visualDirection;
+}
+
+#ifdef QT_DEBUG
+void QGridLayoutEngine::dump(int indent) const
+{
+ qDebug("%*sEngine", indent, "");
+
+ qDebug("%*s Items (%d)", indent, "", q_items.count());
+ int i;
+ for (i = 0; i < q_items.count(); ++i)
+ q_items.at(i)->dump(indent + 2);
+
+ qDebug("%*s Grid (%d x %d)", indent, "", internalGridRowCount(),
+ internalGridColumnCount());
+ for (int row = 0; row < internalGridRowCount(); ++row) {
+ QString message = QLatin1String("[ ");
+ for (int column = 0; column < internalGridColumnCount(); ++column) {
+ message += QString::number(q_items.indexOf(itemAt(row, column))).rightJustified(3);
+ message += QLatin1Char(' ');
+ }
+ message += QLatin1Char(']');
+ qDebug("%*s %s", indent, "", qPrintable(message));
+ }
+
+ if (q_defaultSpacings[Hor].value() >= 0.0 || q_defaultSpacings[Ver].value() >= 0.0)
+ qDebug("%*s Default spacings: %g %g", indent, "", q_defaultSpacings[Hor].value(),
+ q_defaultSpacings[Ver].value());
+
+ qDebug("%*s Column and row info", indent, "");
+ q_infos[Hor].dump(indent + 2);
+ q_infos[Ver].dump(indent + 2);
+
+ qDebug("%*s Column and row data", indent, "");
+ q_columnData.dump(indent + 2);
+ q_rowData.dump(indent + 2);
+
+ qDebug("%*s Geometries output", indent, "");
+ QVector<qreal> *cellPos = &q_yy;
+ for (int pass = 0; pass < 2; ++pass) {
+ QString message;
+ for (i = 0; i < cellPos->count(); ++i) {
+ message += QLatin1String((message.isEmpty() ? "[" : ", "));
+ message += QString::number(cellPos->at(i));
+ }
+ message += QLatin1Char(']');
+ qDebug("%*s %s %s", indent, "", (pass == 0 ? "rows:" : "columns:"), qPrintable(message));
+ cellPos = &q_xx;
+ }
+}
+#endif
+
+void QGridLayoutEngine::maybeExpandGrid(int row, int column, Qt::Orientation orientation)
+{
+ invalidate(); // ### move out of here?
+
+ if (orientation == Qt::Horizontal)
+ qSwap(row, column);
+
+ if (row < rowCount() && column < columnCount())
+ return;
+
+ int oldGridRowCount = internalGridRowCount();
+ int oldGridColumnCount = internalGridColumnCount();
+
+ q_infos[Ver].count = qMax(row + 1, rowCount());
+ q_infos[Hor].count = qMax(column + 1, columnCount());
+
+ int newGridRowCount = internalGridRowCount();
+ int newGridColumnCount = internalGridColumnCount();
+
+ int newGridSize = newGridRowCount * newGridColumnCount;
+ if (newGridSize != q_grid.count()) {
+ q_grid.resize(newGridSize);
+
+ if (newGridColumnCount != oldGridColumnCount) {
+ for (int i = oldGridRowCount - 1; i >= 1; --i) {
+ for (int j = oldGridColumnCount - 1; j >= 0; --j) {
+ int oldIndex = (i * oldGridColumnCount) + j;
+ int newIndex = (i * newGridColumnCount) + j;
+
+ Q_ASSERT(newIndex > oldIndex);
+ q_grid[newIndex] = q_grid[oldIndex];
+ q_grid[oldIndex] = 0;
+ }
+ }
+ }
+ }
+}
+
+void QGridLayoutEngine::regenerateGrid()
+{
+ q_grid.fill(0);
+
+ for (int i = q_items.count() - 1; i >= 0; --i) {
+ QGridLayoutItem *item = q_items.at(i);
+
+ for (int j = item->firstRow(); j <= item->lastRow(); ++j) {
+ for (int k = item->firstColumn(); k <= item->lastColumn(); ++k) {
+ setItemAt(j, k, item);
+ }
+ }
+ }
+}
+
+void QGridLayoutEngine::setItemAt(int row, int column, QGridLayoutItem *item)
+{
+ Q_ASSERT(row >= 0 && row < rowCount());
+ Q_ASSERT(column >= 0 && column < columnCount());
+ q_grid[(row * internalGridColumnCount()) + column] = item;
+}
+
+void QGridLayoutEngine::insertOrRemoveRows(int row, int delta, Qt::Orientation orientation)
+{
+ int oldRowCount = rowCount(orientation);
+ Q_ASSERT(uint(row) <= uint(oldRowCount));
+
+ invalidate();
+
+ // appending rows (or columns) is easy
+ if (row == oldRowCount && delta > 0) {
+ maybeExpandGrid(oldRowCount + delta - 1, -1, orientation);
+ return;
+ }
+
+ q_infos[orientation == Qt::Vertical].insertOrRemoveRows(row, delta);
+
+ for (int i = q_items.count() - 1; i >= 0; --i)
+ q_items.at(i)->insertOrRemoveRows(row, delta, orientation);
+
+ q_grid.resize(internalGridRowCount() * internalGridColumnCount());
+ regenerateGrid();
+}
+
+void QGridLayoutEngine::fillRowData(QGridLayoutRowData *rowData, const QLayoutStyleInfo &styleInfo,
+ qreal *colPositions, qreal *colSizes,
+ Qt::Orientation orientation) const
+{
+ const int ButtonMask = QSizePolicy::ButtonBox | QSizePolicy::PushButton;
+ const QGridLayoutRowInfo &rowInfo = q_infos[orientation == Qt::Vertical];
+ const QGridLayoutRowInfo &columnInfo = q_infos[orientation == Qt::Horizontal];
+ LayoutSide top = (orientation == Qt::Vertical) ? Top : Left;
+ LayoutSide bottom = (orientation == Qt::Vertical) ? Bottom : Right;
+
+ QStyle *style = styleInfo.style();
+ QStyleOption option;
+ option.initFrom(styleInfo.widget());
+
+ const QLayoutParameter<qreal> &defaultSpacing = q_defaultSpacings[orientation == Qt::Vertical];
+ qreal innerSpacing = 0.0;
+ if (style)
+ innerSpacing = (qreal)style->pixelMetric(orientation == Qt::Vertical ? QStyle::PM_LayoutVerticalSpacing
+ : QStyle::PM_LayoutHorizontalSpacing,
+ &option, styleInfo.widget());
+ if (innerSpacing >= 0.0)
+ defaultSpacing.setCachedValue(innerSpacing);
+
+ for (int row = 0; row < rowInfo.count; ++row) {
+ bool rowIsEmpty = true;
+ bool rowIsIdenticalToPrevious = (row > 0);
+
+ for (int column = 0; column < columnInfo.count; ++column) {
+ QGridLayoutItem *item = itemAt(row, column, orientation);
+
+ if (rowIsIdenticalToPrevious && item != itemAt(row - 1, column, orientation))
+ rowIsIdenticalToPrevious = false;
+
+ if (item)
+ rowIsEmpty = false;
+ }
+
+ if ((rowIsEmpty || rowIsIdenticalToPrevious)
+ && rowInfo.spacings.value(row).isDefault()
+ && rowInfo.stretches.value(row).isDefault()
+ && rowInfo.boxes.value(row) == QGridLayoutBox())
+ rowData->ignore.setBit(row, true);
+
+ if (rowInfo.spacings.value(row).isUser()) {
+ rowData->spacings[row] = rowInfo.spacings.at(row).value();
+ } else if (!defaultSpacing.isDefault()) {
+ rowData->spacings[row] = defaultSpacing.value();
+ }
+
+ rowData->stretches[row] = rowInfo.stretches.value(row).value();
+ }
+
+ struct RowAdHocData {
+ int q_row;
+ unsigned int q_hasButtons : 8;
+ unsigned int q_hasNonButtons : 8;
+
+ inline RowAdHocData() : q_row(-1), q_hasButtons(false), q_hasNonButtons(false) {}
+ inline void init(int row) {
+ this->q_row = row;
+ q_hasButtons = false;
+ q_hasNonButtons = false;
+ }
+ inline bool hasOnlyButtons() const { return q_hasButtons && !q_hasNonButtons; }
+ inline bool hasOnlyNonButtons() const { return q_hasNonButtons && !q_hasButtons; }
+ };
+ RowAdHocData lastRowAdHocData;
+ RowAdHocData nextToLastRowAdHocData;
+ RowAdHocData nextToNextToLastRowAdHocData;
+
+ rowData->hasIgnoreFlag = false;
+ for (int row = 0; row < rowInfo.count; ++row) {
+ if (rowData->ignore.testBit(row))
+ continue;
+
+ QGridLayoutBox &rowBox = rowData->boxes[row];
+ if (option.state & QStyle::State_Window) {
+ nextToNextToLastRowAdHocData = nextToLastRowAdHocData;
+ nextToLastRowAdHocData = lastRowAdHocData;
+ lastRowAdHocData.init(row);
+ }
+
+ bool userRowStretch = rowInfo.stretches.value(row).isUser();
+ int &rowStretch = rowData->stretches[row];
+
+ bool hasIgnoreFlag = true;
+ for (int column = 0; column < columnInfo.count; ++column) {
+ QGridLayoutItem *item = itemAt(row, column, orientation);
+ if (item) {
+ int itemRow = item->firstRow(orientation);
+ int itemColumn = item->firstColumn(orientation);
+
+ if (itemRow == row && itemColumn == column) {
+ int itemStretch = item->stretchFactor(orientation);
+ if (!(item->sizePolicy(orientation) & QSizePolicy::IgnoreFlag))
+ hasIgnoreFlag = false;
+ int itemRowSpan = item->rowSpan(orientation);
+
+ int effectiveRowSpan = 1;
+ for (int i = 1; i < itemRowSpan; ++i) {
+ if (!rowData->ignore.testBit(i))
+ ++effectiveRowSpan;
+ }
+
+ QGridLayoutBox *box;
+ if (effectiveRowSpan == 1) {
+ box = &rowBox;
+ if (!userRowStretch && itemStretch != 0)
+ rowStretch = qMax(rowStretch, itemStretch);
+ } else {
+ QGridLayoutMultiCellData &multiCell =
+ rowData->multiCellMap[qMakePair(row, effectiveRowSpan)];
+ box = &multiCell.q_box;
+ multiCell.q_stretch = itemStretch;
+ }
+ // Items with constraints need to be passed the constraint
+ if (colSizes && colPositions && item->hasDynamicConstraint() && orientation == item->dynamicConstraintOrientation()) {
+ /* Get the width of the item by summing up the widths of the columns that it spans.
+ * We need to have already calculated the widths of the columns by calling
+ * q_columns->calculateGeometries() before hand and passing the value in the colSizes
+ * and colPositions parameters.
+ * The variable name is still colSizes even when it actually has the row sizes
+ */
+ qreal length = colSizes[item->lastColumn(orientation)];
+ if (item->columnSpan(orientation) != 1)
+ length += colPositions[item->lastColumn(orientation)] - colPositions[item->firstColumn(orientation)];
+ box->combine(item->box(orientation, length));
+ } else {
+ box->combine(item->box(orientation));
+ }
+
+ if (effectiveRowSpan == 1) {
+ QSizePolicy::ControlTypes controls = item->controlTypes(top);
+ if (controls & ButtonMask)
+ lastRowAdHocData.q_hasButtons = true;
+ if (controls & ~ButtonMask)
+ lastRowAdHocData.q_hasNonButtons = true;
+ }
+ }
+ }
+ }
+ if (row < rowInfo.boxes.count()) {
+ QGridLayoutBox rowBoxInfo = rowInfo.boxes.at(row);
+ rowBoxInfo.normalize();
+ rowBox.q_minimumSize = qMax(rowBox.q_minimumSize, rowBoxInfo.q_minimumSize);
+ rowBox.q_maximumSize = qMax(rowBox.q_minimumSize,
+ (rowBoxInfo.q_maximumSize != FLT_MAX ?
+ rowBoxInfo.q_maximumSize : rowBox.q_maximumSize));
+ rowBox.q_preferredSize = qBound(rowBox.q_minimumSize,
+ qMax(rowBox.q_preferredSize, rowBoxInfo.q_preferredSize),
+ rowBox.q_maximumSize);
+ }
+ if (hasIgnoreFlag)
+ rowData->hasIgnoreFlag = true;
+ }
+
+ /*
+ Heuristic: Detect button boxes that don't use QSizePolicy::ButtonBox.
+ This is somewhat ad hoc but it usually does the trick.
+ */
+ bool lastRowIsButtonBox = (lastRowAdHocData.hasOnlyButtons()
+ && nextToLastRowAdHocData.hasOnlyNonButtons());
+ bool lastTwoRowsIsButtonBox = (lastRowAdHocData.hasOnlyButtons()
+ && nextToLastRowAdHocData.hasOnlyButtons()
+ && nextToNextToLastRowAdHocData.hasOnlyNonButtons()
+ && orientation == Qt::Vertical);
+
+ if (defaultSpacing.isDefault()) {
+ int prevRow = -1;
+ for (int row = 0; row < rowInfo.count; ++row) {
+ if (rowData->ignore.testBit(row))
+ continue;
+
+ if (prevRow != -1 && !rowInfo.spacings.value(prevRow).isUser()) {
+ qreal &rowSpacing = rowData->spacings[prevRow];
+ for (int column = 0; column < columnInfo.count; ++column) {
+ QGridLayoutItem *item1 = itemAt(prevRow, column, orientation);
+ QGridLayoutItem *item2 = itemAt(row, column, orientation);
+
+ if (item1 && item2 && item1 != item2) {
+ QSizePolicy::ControlTypes controls1 = item1->controlTypes(bottom);
+ QSizePolicy::ControlTypes controls2 = item2->controlTypes(top);
+
+ if (controls2 & QSizePolicy::PushButton) {
+ if ((row == nextToLastRowAdHocData.q_row && lastTwoRowsIsButtonBox)
+ || (row == lastRowAdHocData.q_row && lastRowIsButtonBox)) {
+ controls2 &= ~QSizePolicy::PushButton;
+ controls2 |= QSizePolicy::ButtonBox;
+ }
+ }
+
+ qreal spacing = style->combinedLayoutSpacing(controls1, controls2,
+ orientation, &option,
+ styleInfo.widget());
+ if (orientation == Qt::Horizontal) {
+ qreal width1 = rowData->boxes.at(prevRow).q_minimumSize;
+ qreal width2 = rowData->boxes.at(row).q_minimumSize;
+ QRectF rect1 = item1->geometryWithin(0.0, 0.0, width1, FLT_MAX, -1.0);
+ QRectF rect2 = item2->geometryWithin(0.0, 0.0, width2, FLT_MAX, -1.0);
+ spacing -= (width1 - (rect1.x() + rect1.width())) + rect2.x();
+ } else {
+ const QGridLayoutBox &box1 = rowData->boxes.at(prevRow);
+ const QGridLayoutBox &box2 = rowData->boxes.at(row);
+ qreal height1 = box1.q_minimumSize;
+ qreal height2 = box2.q_minimumSize;
+ qreal rowDescent1 = fixedDescent(box1.q_minimumDescent,
+ box1.q_minimumAscent, height1);
+ qreal rowDescent2 = fixedDescent(box2.q_minimumDescent,
+ box2.q_minimumAscent, height2);
+ QRectF rect1 = item1->geometryWithin(0.0, 0.0, FLT_MAX, height1,
+ rowDescent1);
+ QRectF rect2 = item2->geometryWithin(0.0, 0.0, FLT_MAX, height2,
+ rowDescent2);
+ spacing -= (height1 - (rect1.y() + rect1.height())) + rect2.y();
+ }
+ rowSpacing = qMax(spacing, rowSpacing);
+ }
+ }
+ }
+ prevRow = row;
+ }
+ } else if (lastRowIsButtonBox || lastTwoRowsIsButtonBox) {
+ /*
+ Even for styles that define a uniform spacing, we cheat a
+ bit and use the window margin as the spacing. This
+ significantly improves the look of dialogs.
+ */
+ int prevRow = lastRowIsButtonBox ? nextToLastRowAdHocData.q_row
+ : nextToNextToLastRowAdHocData.q_row;
+ if (!defaultSpacing.isUser() && !rowInfo.spacings.value(prevRow).isUser()) {
+ qreal windowMargin = style->pixelMetric(orientation == Qt::Vertical
+ ? QStyle::PM_LayoutBottomMargin
+ : QStyle::PM_LayoutRightMargin,
+ &option, styleInfo.widget());
+
+ qreal &rowSpacing = rowData->spacings[prevRow];
+ rowSpacing = qMax(windowMargin, rowSpacing);
+ }
+ }
+}
+
+void QGridLayoutEngine::ensureEffectiveFirstAndLastRows() const
+{
+ if (q_cachedEffectiveFirstRows[Hor] == -1 && !q_items.isEmpty()) {
+ int rowCount = this->rowCount();
+ int columnCount = this->columnCount();
+
+ q_cachedEffectiveFirstRows[Ver] = rowCount;
+ q_cachedEffectiveFirstRows[Hor] = columnCount;
+ q_cachedEffectiveLastRows[Ver] = -1;
+ q_cachedEffectiveLastRows[Hor] = -1;
+
+ for (int i = q_items.count() - 1; i >= 0; --i) {
+ const QGridLayoutItem *item = q_items.at(i);
+
+ for (int j = 0; j < NOrientations; ++j) {
+ Qt::Orientation orientation = (j == Hor) ? Qt::Horizontal : Qt::Vertical;
+ if (item->firstRow(orientation) < q_cachedEffectiveFirstRows[j])
+ q_cachedEffectiveFirstRows[j] = item->firstRow(orientation);
+ if (item->lastRow(orientation) > q_cachedEffectiveLastRows[j])
+ q_cachedEffectiveLastRows[j] = item->lastRow(orientation);
+ }
+ }
+ }
+}
+
+void QGridLayoutEngine::ensureColumnAndRowData(QGridLayoutRowData *rowData, QGridLayoutBox *totalBox,
+ const QLayoutStyleInfo &styleInfo,
+ qreal *colPositions, qreal *colSizes,
+ Qt::Orientation orientation) const
+{
+ rowData->reset(rowCount(orientation));
+ fillRowData(rowData, styleInfo, colPositions, colSizes, orientation);
+ const QGridLayoutRowInfo &rowInfo = q_infos[orientation == Qt::Vertical];
+ rowData->distributeMultiCells(rowInfo);
+ *totalBox = rowData->totalBox(0, rowCount(orientation));
+ //We have items whose width depends on their height
+}
+
+/**
+ returns false if the layout has contradicting constraints (i.e. some items with a horizontal
+ constraint and other items with a vertical constraint)
+ */
+bool QGridLayoutEngine::ensureDynamicConstraint() const
+{
+ if (q_cachedConstraintOrientation == UnknownConstraint) {
+ for (int i = q_items.count() - 1; i >= 0; --i) {
+ QGridLayoutItem *item = q_items.at(i);
+ if (item->hasDynamicConstraint()) {
+ Qt::Orientation itemConstraintOrientation = item->dynamicConstraintOrientation();
+ if (q_cachedConstraintOrientation == UnknownConstraint) {
+ q_cachedConstraintOrientation = itemConstraintOrientation;
+ } else if (q_cachedConstraintOrientation != itemConstraintOrientation) {
+ q_cachedConstraintOrientation = UnfeasibleConstraint;
+ qWarning("QGridLayoutEngine: Unfeasible, cannot mix horizontal and"
+ " vertical constraint in the same layout");
+ return false;
+ }
+ }
+ }
+ if (q_cachedConstraintOrientation == UnknownConstraint)
+ q_cachedConstraintOrientation = NoConstraint;
+ }
+ return true;
+}
+
+bool QGridLayoutEngine::hasDynamicConstraint() const
+{
+ if (!ensureDynamicConstraint())
+ return false;
+ return q_cachedConstraintOrientation != NoConstraint;
+}
+
+/*
+ * return value is only valid if hasConstraint() returns true
+ */
+Qt::Orientation QGridLayoutEngine::constraintOrientation() const
+{
+ (void)ensureDynamicConstraint();
+ return (Qt::Orientation)q_cachedConstraintOrientation;
+}
+
+void QGridLayoutEngine::ensureGeometries(const QLayoutStyleInfo &styleInfo,
+ const QSizeF &size) const
+{
+ if (q_cachedDataForStyleInfo == styleInfo && q_cachedSize == size)
+ return;
+
+ q_cachedDataForStyleInfo = styleInfo;
+ q_cachedSize = size;
+
+ q_xx.resize(columnCount());
+ q_widths.resize(columnCount());
+ q_yy.resize(rowCount());
+ q_heights.resize(rowCount());
+ q_descents.resize(rowCount());
+
+ if (constraintOrientation() != Qt::Horizontal) {
+ //We might have items whose width depends on their height
+ ensureColumnAndRowData(&q_columnData, &q_totalBoxes[Hor], styleInfo, NULL, NULL, Qt::Horizontal);
+ //Calculate column widths and positions, and put results in q_xx.data() and q_widths.data() so that we can use this information as
+ //constraints to find the row heights
+ q_columnData.calculateGeometries(0, columnCount(), size.width(), q_xx.data(), q_widths.data(),
+ 0, q_totalBoxes[Hor], q_infos[Hor] );
+ ensureColumnAndRowData(&q_rowData, &q_totalBoxes[Ver], styleInfo, q_xx.data(), q_widths.data(), Qt::Vertical);
+ //Calculate row heights and positions, and put results in q_yy.data() and q_heights.data()
+ q_rowData.calculateGeometries(0, rowCount(), size.height(), q_yy.data(), q_heights.data(),
+ q_descents.data(), q_totalBoxes[Ver], q_infos[Ver]);
+ } else {
+ //We have items whose height depends on their width
+ ensureColumnAndRowData(&q_rowData, &q_totalBoxes[Ver], styleInfo, NULL, NULL, Qt::Vertical);
+ //Calculate row heights and positions, and put results in q_yy.data() and q_heights.data() so that we can use this information as
+ //constraints to find the column widths
+ q_rowData.calculateGeometries(0, rowCount(), size.height(), q_yy.data(), q_heights.data(),
+ q_descents.data(), q_totalBoxes[Ver], q_infos[Ver]);
+ ensureColumnAndRowData(&q_columnData, &q_totalBoxes[Hor], styleInfo, q_yy.data(), q_heights.data(), Qt::Horizontal);
+ //Calculate row heights and positions, and put results in q_yy.data() and q_heights.data()
+ q_columnData.calculateGeometries(0, columnCount(), size.width(), q_xx.data(), q_widths.data(),
+ 0, q_totalBoxes[Hor], q_infos[Hor]);
+ }
+}
+
+QT_END_NAMESPACE
+
+#endif //QT_NO_GRAPHICSVIEW
diff --git a/src/widgets/graphicsview/qgridlayoutengine_p.h b/src/widgets/graphicsview/qgridlayoutengine_p.h
new file mode 100644
index 0000000000..c5b35f59e8
--- /dev/null
+++ b/src/widgets/graphicsview/qgridlayoutengine_p.h
@@ -0,0 +1,458 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** 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 Technology Preview License Agreement accompanying
+** this package.
+**
+** 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.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QGRIDLAYOUTENGINE_P_H
+#define QGRIDLAYOUTENGINE_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of the graphics view layout classes. This header
+// file may change from version to version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qalgorithms.h"
+#include "qbitarray.h"
+#include "qlist.h"
+#include "qmap.h"
+#include "qpair.h"
+#include "qvector.h"
+#include "qgraphicslayout_p.h"
+#include <float.h>
+
+QT_BEGIN_NAMESPACE
+
+class QGraphicsLayoutItem;
+class QStyle;
+class QWidget;
+
+// ### deal with Descent in a similar way
+enum {
+ MinimumSize = Qt::MinimumSize,
+ PreferredSize = Qt::PreferredSize,
+ MaximumSize = Qt::MaximumSize,
+ NSizes
+};
+
+// do not reorder
+enum {
+ Hor,
+ Ver,
+ NOrientations
+};
+
+// do not reorder
+enum LayoutSide {
+ Left,
+ Top,
+ Right,
+ Bottom
+};
+
+enum {
+ NoConstraint,
+ HorizontalConstraint, // Width depends on the height
+ VerticalConstraint, // Height depends on the width
+ UnknownConstraint, // need to update cache
+ UnfeasibleConstraint // not feasible, it be has some items with Vertical and others with Horizontal constraints
+};
+
+template <typename T>
+class QLayoutParameter
+{
+public:
+ enum State { Default, User, Cached };
+
+ inline QLayoutParameter() : q_value(T()), q_state(Default) {}
+ inline QLayoutParameter(T value, State state = Default) : q_value(value), q_state(state) {}
+
+ inline void setUserValue(T value) {
+ q_value = value;
+ q_state = User;
+ }
+ inline void setCachedValue(T value) const {
+ if (q_state != User) {
+ q_value = value;
+ q_state = Cached;
+ }
+ }
+ inline T value() const { return q_value; }
+ inline T value(T defaultValue) const { return isUser() ? q_value : defaultValue; }
+ inline bool isDefault() const { return q_state == Default; }
+ inline bool isUser() const { return q_state == User; }
+ inline bool isCached() const { return q_state == Cached; }
+
+private:
+ mutable T q_value;
+ mutable State q_state;
+};
+
+class QStretchParameter : public QLayoutParameter<int>
+{
+public:
+ QStretchParameter() : QLayoutParameter<int>(-1) {}
+
+};
+
+class QGridLayoutBox
+{
+public:
+ inline QGridLayoutBox()
+ : q_minimumSize(0), q_preferredSize(0), q_maximumSize(FLT_MAX),
+ q_minimumDescent(-1), q_minimumAscent(-1) {}
+
+ void add(const QGridLayoutBox &other, int stretch, qreal spacing);
+ void combine(const QGridLayoutBox &other);
+ void normalize();
+
+#ifdef QT_DEBUG
+ void dump(int indent = 0) const;
+#endif
+ // This code could use the union-struct-array trick, but a compiler
+ // bug prevents this from working.
+ qreal q_minimumSize;
+ qreal q_preferredSize;
+ qreal q_maximumSize;
+ qreal q_minimumDescent;
+ qreal q_minimumAscent;
+ inline qreal &q_sizes(int which)
+ {
+ qreal *t;
+ switch (which) {
+ case Qt::MinimumSize:
+ t = &q_minimumSize;
+ break;
+ case Qt::PreferredSize:
+ t = &q_preferredSize;
+ break;
+ case Qt::MaximumSize:
+ t = &q_maximumSize;
+ break;
+ case Qt::MinimumDescent:
+ t = &q_minimumDescent;
+ break;
+ case (Qt::MinimumDescent + 1):
+ t = &q_minimumAscent;
+ break;
+ default:
+ t = 0;
+ break;
+ }
+ return *t;
+ }
+ inline const qreal &q_sizes(int which) const
+ {
+ const qreal *t;
+ switch (which) {
+ case Qt::MinimumSize:
+ t = &q_minimumSize;
+ break;
+ case Qt::PreferredSize:
+ t = &q_preferredSize;
+ break;
+ case Qt::MaximumSize:
+ t = &q_maximumSize;
+ break;
+ case Qt::MinimumDescent:
+ t = &q_minimumDescent;
+ break;
+ case (Qt::MinimumDescent + 1):
+ t = &q_minimumAscent;
+ break;
+ default:
+ t = 0;
+ break;
+ }
+ return *t;
+ }
+};
+
+bool operator==(const QGridLayoutBox &box1, const QGridLayoutBox &box2);
+inline bool operator!=(const QGridLayoutBox &box1, const QGridLayoutBox &box2)
+ { return !operator==(box1, box2); }
+
+class QGridLayoutMultiCellData
+{
+public:
+ inline QGridLayoutMultiCellData() : q_stretch(-1) {}
+
+ QGridLayoutBox q_box;
+ int q_stretch;
+};
+
+typedef QMap<QPair<int, int>, QGridLayoutMultiCellData> MultiCellMap;
+
+class QGridLayoutRowInfo;
+
+class QGridLayoutRowData
+{
+public:
+ void reset(int count);
+ void distributeMultiCells(const QGridLayoutRowInfo &rowInfo);
+ void calculateGeometries(int start, int end, qreal targetSize, qreal *positions, qreal *sizes,
+ qreal *descents, const QGridLayoutBox &totalBox,
+ const QGridLayoutRowInfo &rowInfo);
+ QGridLayoutBox totalBox(int start, int end) const;
+ void stealBox(int start, int end, int which, qreal *positions, qreal *sizes);
+
+#ifdef QT_DEBUG
+ void dump(int indent = 0) const;
+#endif
+
+ QBitArray ignore; // ### rename q_
+ QVector<QGridLayoutBox> boxes;
+ MultiCellMap multiCellMap;
+ QVector<int> stretches;
+ QVector<qreal> spacings;
+ bool hasIgnoreFlag;
+};
+
+class QGridLayoutEngine;
+
+class QGridLayoutItem
+{
+public:
+ QGridLayoutItem(QGridLayoutEngine *engine, QGraphicsLayoutItem *layoutItem, int row, int column,
+ int rowSpan = 1, int columnSpan = 1, Qt::Alignment alignment = 0,
+ int itemAtIndex = -1);
+
+ inline int firstRow() const { return q_firstRows[Ver]; }
+ inline int firstColumn() const { return q_firstRows[Hor]; }
+ inline int rowSpan() const { return q_rowSpans[Ver]; }
+ inline int columnSpan() const { return q_rowSpans[Hor]; }
+ inline int lastRow() const { return firstRow() + rowSpan() - 1; }
+ inline int lastColumn() const { return firstColumn() + columnSpan() - 1; }
+
+ int firstRow(Qt::Orientation orientation) const;
+ int firstColumn(Qt::Orientation orientation) const;
+ int lastRow(Qt::Orientation orientation) const;
+ int lastColumn(Qt::Orientation orientation) const;
+ int rowSpan(Qt::Orientation orientation) const;
+ int columnSpan(Qt::Orientation orientation) const;
+ void setFirstRow(int row, Qt::Orientation orientation = Qt::Vertical);
+ void setRowSpan(int rowSpan, Qt::Orientation orientation = Qt::Vertical);
+
+ int stretchFactor(Qt::Orientation orientation) const;
+ void setStretchFactor(int stretch, Qt::Orientation orientation);
+
+ inline Qt::Alignment alignment() const { return q_alignment; }
+ inline void setAlignment(Qt::Alignment alignment) { q_alignment = alignment; }
+
+ QSizePolicy::Policy sizePolicy(Qt::Orientation orientation) const;
+
+ bool hasDynamicConstraint() const;
+ Qt::Orientation dynamicConstraintOrientation() const;
+
+ QSizePolicy::ControlTypes controlTypes(LayoutSide side) const;
+ QSizeF sizeHint(Qt::SizeHint which, const QSizeF &constraint = QSizeF()) const;
+ QGridLayoutBox box(Qt::Orientation orientation, qreal constraint = -1.0) const;
+ QRectF geometryWithin(qreal x, qreal y, qreal width, qreal height, qreal rowDescent) const;
+
+ QGraphicsLayoutItem *layoutItem() const { return q_layoutItem; }
+
+ void setGeometry(const QRectF &rect);
+ void transpose();
+ void insertOrRemoveRows(int row, int delta, Qt::Orientation orientation = Qt::Vertical);
+ QSizeF effectiveMaxSize(const QSizeF &constraint) const;
+
+#ifdef QT_DEBUG
+ void dump(int indent = 0) const;
+#endif
+
+private:
+ QGridLayoutEngine *q_engine; // ### needed?
+ QGraphicsLayoutItem *q_layoutItem;
+ int q_firstRows[NOrientations];
+ int q_rowSpans[NOrientations];
+ int q_stretches[NOrientations];
+ Qt::Alignment q_alignment;
+};
+
+class QGridLayoutRowInfo
+{
+public:
+ inline QGridLayoutRowInfo() : count(0) {}
+
+ void insertOrRemoveRows(int row, int delta);
+
+#ifdef QT_DEBUG
+ void dump(int indent = 0) const;
+#endif
+
+ int count;
+ QVector<QStretchParameter> stretches;
+ QVector<QLayoutParameter<qreal> > spacings;
+ QVector<Qt::Alignment> alignments;
+ QVector<QGridLayoutBox> boxes;
+};
+
+class QGridLayoutEngine
+{
+public:
+ QGridLayoutEngine();
+ inline ~QGridLayoutEngine() { qDeleteAll(q_items); }
+
+ int rowCount(Qt::Orientation orientation) const;
+ int columnCount(Qt::Orientation orientation) const;
+ inline int rowCount() const { return q_infos[Ver].count; }
+ inline int columnCount() const { return q_infos[Hor].count; }
+ // returns the number of items inserted, which may be less than (rowCount * columnCount)
+ int itemCount() const;
+ QGridLayoutItem *itemAt(int index) const;
+ int indexOf(QGraphicsLayoutItem *item) const;
+
+ int effectiveFirstRow(Qt::Orientation orientation = Qt::Vertical) const;
+ int effectiveLastRow(Qt::Orientation orientation = Qt::Vertical) const;
+
+ void setSpacing(qreal spacing, Qt::Orientations orientations);
+ qreal spacing(const QLayoutStyleInfo &styleInfo, Qt::Orientation orientation) const;
+ // ### setSpacingAfterRow(), spacingAfterRow()
+ void setRowSpacing(int row, qreal spacing, Qt::Orientation orientation = Qt::Vertical);
+ qreal rowSpacing(int row, Qt::Orientation orientation = Qt::Vertical) const;
+
+ void setRowStretchFactor(int row, int stretch, Qt::Orientation orientation = Qt::Vertical);
+ int rowStretchFactor(int row, Qt::Orientation orientation = Qt::Vertical) const;
+
+ void setStretchFactor(QGraphicsLayoutItem *layoutItem, int stretch,
+ Qt::Orientation orientation);
+ int stretchFactor(QGraphicsLayoutItem *layoutItem, Qt::Orientation orientation) const;
+
+ void setRowSizeHint(Qt::SizeHint which, int row, qreal size,
+ Qt::Orientation orientation = Qt::Vertical);
+ qreal rowSizeHint(Qt::SizeHint which, int row,
+ Qt::Orientation orientation = Qt::Vertical) const;
+
+ void setRowAlignment(int row, Qt::Alignment alignment, Qt::Orientation orientation);
+ Qt::Alignment rowAlignment(int row, Qt::Orientation orientation) const;
+
+ void setAlignment(QGraphicsLayoutItem *layoutItem, Qt::Alignment alignment);
+ Qt::Alignment alignment(QGraphicsLayoutItem *layoutItem) const;
+ Qt::Alignment effectiveAlignment(const QGridLayoutItem *layoutItem) const;
+
+
+ void insertItem(QGridLayoutItem *item, int index);
+ void addItem(QGridLayoutItem *item);
+ void removeItem(QGridLayoutItem *item);
+ QGridLayoutItem *findLayoutItem(QGraphicsLayoutItem *layoutItem) const;
+ QGridLayoutItem *itemAt(int row, int column, Qt::Orientation orientation = Qt::Vertical) const;
+ inline void insertRow(int row, Qt::Orientation orientation = Qt::Vertical)
+ { insertOrRemoveRows(row, +1, orientation); }
+ inline void removeRows(int row, int count, Qt::Orientation orientation)
+ { insertOrRemoveRows(row, -count, orientation); }
+
+ void invalidate();
+ void setGeometries(const QLayoutStyleInfo &styleInfo, const QRectF &contentsGeometry);
+ QRectF cellRect(const QLayoutStyleInfo &styleInfo, const QRectF &contentsGeometry, int row,
+ int column, int rowSpan, int columnSpan) const;
+ QSizeF sizeHint(const QLayoutStyleInfo &styleInfo, Qt::SizeHint which,
+ const QSizeF &constraint) const;
+
+ // heightForWidth / widthForHeight support
+ QSizeF dynamicallyConstrainedSizeHint(Qt::SizeHint which, const QSizeF &constraint) const;
+ bool ensureDynamicConstraint() const;
+ bool hasDynamicConstraint() const;
+ Qt::Orientation constraintOrientation() const;
+
+
+ QSizePolicy::ControlTypes controlTypes(LayoutSide side) const;
+ void transpose();
+ void setVisualDirection(Qt::LayoutDirection direction);
+ Qt::LayoutDirection visualDirection() const;
+#ifdef QT_DEBUG
+ void dump(int indent = 0) const;
+#endif
+
+private:
+ static int grossRoundUp(int n) { return ((n + 2) | 0x3) - 2; }
+
+ void maybeExpandGrid(int row, int column, Qt::Orientation orientation = Qt::Vertical);
+ void regenerateGrid();
+ inline int internalGridRowCount() const { return grossRoundUp(rowCount()); }
+ inline int internalGridColumnCount() const { return grossRoundUp(columnCount()); }
+ void setItemAt(int row, int column, QGridLayoutItem *item);
+ void insertOrRemoveRows(int row, int delta, Qt::Orientation orientation = Qt::Vertical);
+ void fillRowData(QGridLayoutRowData *rowData, const QLayoutStyleInfo &styleInfo,
+ qreal *colPositions, qreal *colSizes,
+ Qt::Orientation orientation = Qt::Vertical) const;
+ void ensureEffectiveFirstAndLastRows() const;
+ void ensureColumnAndRowData(QGridLayoutRowData *rowData, QGridLayoutBox *totalBox,
+ const QLayoutStyleInfo &styleInfo,
+ qreal *colPositions, qreal *colSizes,
+ Qt::Orientation orientation) const;
+
+ void ensureGeometries(const QLayoutStyleInfo &styleInfo, const QSizeF &size) const;
+
+ // User input
+ QVector<QGridLayoutItem *> q_grid;
+ QList<QGridLayoutItem *> q_items;
+ QLayoutParameter<qreal> q_defaultSpacings[NOrientations];
+ QGridLayoutRowInfo q_infos[NOrientations];
+ Qt::LayoutDirection m_visualDirection;
+
+ // Lazily computed from the above user input
+ mutable int q_cachedEffectiveFirstRows[NOrientations];
+ mutable int q_cachedEffectiveLastRows[NOrientations];
+ mutable quint8 q_cachedConstraintOrientation : 3;
+
+ // Layout item input
+ mutable QLayoutStyleInfo q_cachedDataForStyleInfo;
+ mutable QGridLayoutRowData q_columnData;
+ mutable QGridLayoutRowData q_rowData;
+ mutable QGridLayoutBox q_totalBoxes[NOrientations];
+
+ // Output
+ mutable QSizeF q_cachedSize;
+ mutable QVector<qreal> q_xx;
+ mutable QVector<qreal> q_yy;
+ mutable QVector<qreal> q_widths;
+ mutable QVector<qreal> q_heights;
+ mutable QVector<qreal> q_descents;
+
+ friend class QGridLayoutItem;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/widgets/graphicsview/qsimplex_p.cpp b/src/widgets/graphicsview/qsimplex_p.cpp
new file mode 100644
index 0000000000..d2d9646b90
--- /dev/null
+++ b/src/widgets/graphicsview/qsimplex_p.cpp
@@ -0,0 +1,673 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** 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 Technology Preview License Agreement accompanying
+** this package.
+**
+** 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.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qsimplex_p.h"
+
+#include <QtCore/qset.h>
+#include <QtCore/qdebug.h>
+
+#include <stdlib.h>
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \internal
+ \class QSimplex
+
+ The QSimplex class is a Linear Programming problem solver based on the two-phase
+ simplex method.
+
+ It takes a set of QSimplexConstraints as its restrictive constraints and an
+ additional QSimplexConstraint as its objective function. Then methods to maximize
+ and minimize the problem solution are provided.
+
+ The two-phase simplex method is based on the following steps:
+ First phase:
+ 1.a) Modify the original, complex, and possibly not feasible problem, into a new,
+ easy to solve problem.
+ 1.b) Set as the objective of the new problem, a feasible solution for the original
+ complex problem.
+ 1.c) Run simplex to optimize the modified problem and check whether a solution for
+ the original problem exists.
+
+ Second phase:
+ 2.a) Go back to the original problem with the feasibl (but not optimal) solution
+ found in the first phase.
+ 2.b) Set the original objective.
+ 3.c) Run simplex to optimize the original problem towards its optimal solution.
+*/
+
+/*!
+ \internal
+*/
+QSimplex::QSimplex() : objective(0), rows(0), columns(0), firstArtificial(0), matrix(0)
+{
+}
+
+/*!
+ \internal
+*/
+QSimplex::~QSimplex()
+{
+ clearDataStructures();
+}
+
+/*!
+ \internal
+*/
+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;
+ delete constraints[i]->artificial;
+ delete constraints[i];
+ }
+ constraints.clear();
+
+ // Other
+ variables.clear();
+ objective = 0;
+}
+
+/*!
+ \internal
+ Sets the new constraints in the simplex solver and returns whether the problem
+ is feasible.
+
+ This method sets the new constraints, normalizes them, creates the simplex matrix
+ and runs the first simplex phase.
+*/
+bool QSimplex::setConstraints(const QList<QSimplexConstraint *> newConstraints)
+{
+ ////////////////////////////
+ // Reset to initial state //
+ ////////////////////////////
+ clearDataStructures();
+
+ if (newConstraints.isEmpty())
+ return true; // we are ok with no constraints
+
+ // Make deep copy of constraints. We need this copy because we may change
+ // them in the simplification method.
+ for (int i = 0; i < newConstraints.size(); ++i) {
+ QSimplexConstraint *c = new QSimplexConstraint;
+ c->constant = newConstraints[i]->constant;
+ c->ratio = newConstraints[i]->ratio;
+ c->variables = newConstraints[i]->variables;
+ constraints << c;
+ }
+
+ // Remove constraints of type Var == K and replace them for their value.
+ if (!simplifyConstraints(&constraints)) {
+ qWarning() << "QSimplex: No feasible solution!";
+ clearDataStructures();
+ return false;
+ }
+
+ ///////////////////////////////////////
+ // Prepare variables and constraints //
+ ///////////////////////////////////////
+
+ // Set Variables direct mapping.
+ // "variables" is a list that provides a stable, indexed list of all variables
+ // used in this problem.
+ 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
+ // We also need to be able to find the index for a given variable, to do that
+ // we store in each variable its index.
+ for (int i = 0; i < variables.size(); ++i) {
+ // The variable "0" goes at the column "1", etc...
+ variables[i]->index = i + 1;
+ }
+
+ // Normalize Constraints
+ // In this step, we prepare the constraints in two ways:
+ // Firstly, we modify all constraints of type "LessOrEqual" or "MoreOrEqual"
+ // by the adding slack or surplus variables and making them "Equal" constraints.
+ // Secondly, we need every single constraint to have a direct, easy feasible
+ // solution. Constraints that have slack variables are already easy to solve,
+ // to all the others we add artificial variables.
+ //
+ // At the end we modify the constraints as follows:
+ // - LessOrEqual: SLACK variable is added.
+ // - Equal: ARTIFICIAL variable is added.
+ // - More or Equal: ARTIFICIAL and SURPLUS variables are added.
+ 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;
+ }
+ }
+
+ // All original, slack and surplus have already had its index set
+ // at this point. We now set the index of the artificial variables
+ // as to ensure they are at the end of the variable list and therefore
+ // can be easily removed at the end of this method.
+ firstArtificial = variableIndex + 1;
+ for (int i = 0; i < artificialList.size(); ++i)
+ artificialList[i]->index = ++variableIndex;
+ artificialList.clear();
+
+ /////////////////////////////
+ // Fill the Simplex 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 false;
+ }
+ 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 objective for the first-phase Simplex.
+ // Z = -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 there is a solution where the sum of all artificial
+ // variables is zero, then all of them can be removed and yet
+ // we will have a feasible (but not optimal) solution for the
+ // original problem.
+ // Otherwise, we clean up our structures and report there is
+ // no feasible solution.
+ if ((valueAt(0, columns - 1) != 0.0) && (qAbs(valueAt(0, columns - 1)) > 0.00001)) {
+ qWarning() << "QSimplex: No feasible solution!";
+ clearDataStructures();
+ return false;
+ }
+
+ // Remove artificial variables. We already have a feasible
+ // solution for the first problem, thus we don't need them
+ // anymore.
+ clearColumns(firstArtificial, columns - 2);
+
+ return true;
+}
+
+/*!
+ \internal
+
+ Run simplex on the current matrix with the current objective.
+
+ This is the iterative method. The matrix lines are combined
+ as to modify the variable values towards the best solution possible.
+ The method returns when the matrix is in the optimal state.
+*/
+void QSimplex::solveMaxHelper()
+{
+ reducedRowEchelon();
+ while (iterate()) ;
+}
+
+/*!
+ \internal
+*/
+void QSimplex::setObjective(QSimplexConstraint *newObjective)
+{
+ objective = newObjective;
+}
+
+/*!
+ \internal
+*/
+void QSimplex::clearRow(int rowIndex)
+{
+ qreal *item = matrix + rowIndex * columns;
+ for (int i = 0; i < columns; ++i)
+ item[i] = 0.0;
+}
+
+/*!
+ \internal
+*/
+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;
+ }
+}
+
+/*!
+ \internal
+*/
+void QSimplex::dumpMatrix()
+{
+ qDebug("---- Simplex Matrix ----\n");
+
+ QString str(QLatin1String(" "));
+ for (int j = 0; j < columns; ++j)
+ str += QString::fromAscii(" <%1 >").arg(j, 2);
+ qDebug("%s", qPrintable(str));
+ for (int i = 0; i < rows; ++i) {
+ str = QString::fromAscii("Row %1:").arg(i, 2);
+
+ qreal *row = matrix + i * columns;
+ for (int j = 0; j < columns; ++j)
+ str += QString::fromAscii("%1").arg(row[j], 7, 'f', 2);
+ qDebug("%s", qPrintable(str));
+ }
+ qDebug("------------------------\n");
+}
+
+/*!
+ \internal
+*/
+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;
+ }
+}
+
+/*!
+ \internal
+*/
+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;
+}
+
+/*!
+ \internal
+
+ For a given pivot column, find the pivot row. That is, the row with the
+ minimum associated "quotient" where:
+
+ - quotient is the division of the value in the last column by the value
+ in the pivot column.
+ - rows with value less or equal to zero are ignored
+ - if two rows have the same quotient, lines are chosen based on the
+ highest variable index (value in the first column)
+
+ The last condition avoids a bug where artificial variables would be
+ left behind for the second-phase simplex, and with 'good'
+ constraints would be removed before it, what would lead to incorrect
+ results.
+*/
+int QSimplex::pivotRowForColumn(int column)
+{
+ qreal min = qreal(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;
+ } else if ((quotient == min) && (valueAt(i, 0) > valueAt(minIndex, 0))) {
+ minIndex = i;
+ }
+ }
+
+ return minIndex;
+}
+
+/*!
+ \internal
+*/
+void QSimplex::reducedRowEchelon()
+{
+ for (int i = 1; i < rows; ++i) {
+ int factorInObjectiveRow = valueAt(i, 0);
+ combineRows(0, i, -1 * valueAt(0, factorInObjectiveRow));
+ }
+}
+
+/*!
+ \internal
+
+ Does one iteration towards a better solution for the problem.
+ See 'solveMaxHelper'.
+*/
+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();
+ // qDebug("------------ 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).
+
+ This method sets the original objective and runs the second phase
+ Simplex to obtain the optimal solution for the problem. As the internal
+ simplex solver is only able to _maximize_ objectives, we handle the
+ minimization case by inverting the original objective and then
+ maximizing it.
+*/
+qreal QSimplex::solver(solverFactor factor)
+{
+ // Remove old objective
+ clearRow(0);
+
+ // Set new objective in the first row of the simplex matrix
+ qreal resultOffset = 0;
+ QHash<QSimplexVariable *, qreal>::const_iterator iter;
+ for (iter = objective->variables.constBegin();
+ iter != objective->variables.constEnd();
+ ++iter) {
+
+ // Check if the variable was removed in the simplification process.
+ // If so, we save its offset to the objective function and skip adding
+ // it to the matrix.
+ if (iter.key()->index == -1) {
+ resultOffset += iter.value() * iter.key()->result;
+ continue;
+ }
+
+ setValueAt(0, iter.key()->index, -1 * factor * iter.value());
+ }
+
+ solveMaxHelper();
+ collectResults();
+
+#ifdef QT_DEBUG
+ for (int i = 0; i < constraints.size(); ++i) {
+ Q_ASSERT(constraints[i]->isSatisfied());
+ }
+#endif
+
+ // Return the value calculated by the simplex plus the value of the
+ // fixed variables.
+ return (factor * valueAt(0, columns - 1)) + resultOffset;
+}
+
+/*!
+ \internal
+ Minimize the original objective.
+*/
+qreal QSimplex::solveMin()
+{
+ return solver(Minimum);
+}
+
+/*!
+ \internal
+ Maximize the original objective.
+*/
+qreal QSimplex::solveMax()
+{
+ return solver(Maximum);
+}
+
+/*!
+ \internal
+
+ Reads results from the simplified matrix and saves them in the
+ "result" member of each QSimplexVariable.
+*/
+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);
+ }
+}
+
+/*!
+ \internal
+
+ Looks for single-valued variables and remove them from the constraints list.
+*/
+bool QSimplex::simplifyConstraints(QList<QSimplexConstraint *> *constraints)
+{
+ QHash<QSimplexVariable *, qreal> results; // List of single-valued variables
+ bool modified = true; // Any chance more optimization exists?
+
+ while (modified) {
+ modified = false;
+
+ // For all constraints
+ QList<QSimplexConstraint *>::iterator iter = constraints->begin();
+ while (iter != constraints->end()) {
+ QSimplexConstraint *c = *iter;
+ if ((c->ratio == QSimplexConstraint::Equal) && (c->variables.count() == 1)) {
+ // Check whether this is a constraint of type Var == K
+ // If so, save its value to "results".
+ QSimplexVariable *variable = c->variables.constBegin().key();
+ qreal result = c->constant / c->variables.value(variable);
+
+ results.insert(variable, result);
+ variable->result = result;
+ variable->index = -1;
+ modified = true;
+
+ }
+
+ // Replace known values among their variables
+ QHash<QSimplexVariable *, qreal>::const_iterator r;
+ for (r = results.constBegin(); r != results.constEnd(); ++r) {
+ if (c->variables.contains(r.key())) {
+ c->constant -= r.value() * c->variables.take(r.key());
+ modified = true;
+ }
+ }
+
+ // Keep it normalized
+ if (c->constant < 0)
+ c->invert();
+
+ if (c->variables.isEmpty()) {
+ // If constraint became empty due to substitution, delete it.
+ if (c->isSatisfied() == false)
+ // We must ensure that the constraint soon to be deleted would not
+ // make the problem unfeasible if left behind. If that's the case,
+ // we return false so the simplex solver can properly report that.
+ return false;
+
+ delete c;
+ iter = constraints->erase(iter);
+ } else {
+ ++iter;
+ }
+ }
+ }
+
+ return true;
+}
+
+void QSimplexConstraint::invert()
+{
+ constant = -constant;
+ ratio = Ratio(2 - ratio);
+
+ QHash<QSimplexVariable *, qreal>::iterator iter;
+ for (iter = variables.begin(); iter != variables.end(); ++iter) {
+ iter.value() = -iter.value();
+ }
+}
+
+QT_END_NAMESPACE
diff --git a/src/widgets/graphicsview/qsimplex_p.h b/src/widgets/graphicsview/qsimplex_p.h
new file mode 100644
index 0000000000..e6eced311e
--- /dev/null
+++ b/src/widgets/graphicsview/qsimplex_p.h
@@ -0,0 +1,208 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** 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 Technology Preview License Agreement accompanying
+** this package.
+**
+** 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.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSIMPLEX_P_H
+#define QSIMPLEX_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtCore/qhash.h>
+#include <QtCore/qpair.h>
+
+QT_BEGIN_NAMESPACE
+
+struct QSimplexVariable
+{
+ QSimplexVariable() : result(0), index(0) {}
+
+ qreal result;
+ int 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;
+
+ void invert();
+
+ bool isSatisfied() {
+ qreal leftHandSide(0);
+
+ QHash<QSimplexVariable *, qreal>::const_iterator iter;
+ for (iter = variables.constBegin(); iter != variables.constEnd(); ++iter) {
+ leftHandSide += iter.value() * iter.key()->result;
+ }
+
+ Q_ASSERT(constant > 0 || qFuzzyCompare(1, 1 + constant));
+
+ if ((leftHandSide == constant) || qAbs(leftHandSide - constant) < 0.0000001)
+ return true;
+
+ switch (ratio) {
+ case LessOrEqual:
+ return leftHandSide < constant;
+ case MoreOrEqual:
+ return leftHandSide > constant;
+ default:
+ return false;
+ }
+ }
+
+#ifdef QT_DEBUG
+ QString toString() {
+ QString result;
+ result += QString::fromAscii("-- QSimplexConstraint %1 --").arg(quintptr(this), 0, 16);
+
+ QHash<QSimplexVariable *, qreal>::const_iterator iter;
+ for (iter = variables.constBegin(); iter != variables.constEnd(); ++iter) {
+ result += QString::fromAscii(" %1 x %2").arg(iter.value()).arg(quintptr(iter.key()), 0, 16);
+ }
+
+ switch (ratio) {
+ case LessOrEqual:
+ result += QString::fromAscii(" (less) <= %1").arg(constant);
+ break;
+ case MoreOrEqual:
+ result += QString::fromAscii(" (more) >= %1").arg(constant);
+ break;
+ default:
+ result += QString::fromAscii(" (eqal) == %1").arg(constant);
+ }
+
+ return result;
+ }
+#endif
+};
+
+class QSimplex
+{
+public:
+ QSimplex();
+ virtual ~QSimplex();
+
+ qreal solveMin();
+ qreal solveMax();
+
+ bool 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
+ bool simplifyConstraints(QList<QSimplexConstraint *> *constraints);
+ 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 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