diff options
Diffstat (limited to 'src/gui/graphicsview')
52 files changed, 6469 insertions, 1257 deletions
diff --git a/src/gui/graphicsview/graphicsview.pri b/src/gui/graphicsview/graphicsview.pri index 9d232e4..547d7ce 100644 --- a/src/gui/graphicsview/graphicsview.pri +++ b/src/gui/graphicsview/graphicsview.pri @@ -22,7 +22,12 @@ HEADERS += graphicsview/qgraphicsgridlayout.h \ graphicsview/qgraphicsview_p.h \ graphicsview/qgraphicswidget.h \ graphicsview/qgraphicswidget_p.h \ - graphicsview/qgridlayoutengine_p.h + graphicsview/qgridlayoutengine_p.h \ + graphicsview/qgraph_p.h \ + graphicsview/qsimplex_p.h \ + graphicsview/qgraphicsanchorlayout_p.h \ + graphicsview/qgraphicsanchorlayout.h + SOURCES += graphicsview/qgraphicsgridlayout.cpp \ graphicsview/qgraphicsitem.cpp \ graphicsview/qgraphicsitemanimation.cpp \ @@ -41,4 +46,7 @@ SOURCES += graphicsview/qgraphicsgridlayout.cpp \ graphicsview/qgraphicsview.cpp \ graphicsview/qgraphicswidget.cpp \ graphicsview/qgraphicswidget_p.cpp \ - graphicsview/qgridlayoutengine.cpp + graphicsview/qgridlayoutengine.cpp \ + graphicsview/qsimplex_p.cpp \ + graphicsview/qgraphicsanchorlayout_p.cpp \ + graphicsview/qgraphicsanchorlayout.cpp diff --git a/src/gui/graphicsview/qgraph_p.h b/src/gui/graphicsview/qgraph_p.h new file mode 100644 index 0000000..f1fa185 --- /dev/null +++ b/src/gui/graphicsview/qgraph_p.h @@ -0,0 +1,296 @@ +/**************************************************************************** +** +** Copyright (C) 2009 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; + } + + void setRootVertex(Vertex *vertex) + { + userVertex = vertex; + } + + QSet<Vertex*> vertices() const { + QSet<Vertex *> setOfVertices; + for (const_iterator it = constBegin(); it != constEnd(); ++it) { + setOfVertices.insert(*it); + } + return setOfVertices; + } + + QList<QPair<Vertex*, Vertex*> > connections() const { + QList<QPair<Vertex*, Vertex*> > conns; + for (const_iterator it = constBegin(); it != constEnd(); ++it) { + Vertex *from = it.from(); + Vertex *to = it.to(); + // do not return (from,to) *and* (to,from) + if (from < to) { + conns.append(qMakePair(from, to)); + } + } + return conns; + } + +#if defined(QT_DEBUG) + QString serializeToDot() { // traversal + QString strVertices; + QString edges; + + QSet<Vertex *> setOfVertices = vertices(); + for (Q_TYPENAME QSet<Vertex*>::const_iterator it = setOfVertices.begin(); it != setOfVertices.end(); ++it) { + Vertex *v = *it; + QList<Vertex*> adjacents = adjacentVertices(v); + for (int i = 0; i < adjacents.count(); ++i) { + Vertex *v1 = adjacents.at(i); + EdgeData *data = edgeData(v, v1); + bool forward = data->from == v; + if (forward) { + edges += QString::fromAscii("%1->%2 [label=\"[%3,%4,%5]\" dir=both color=\"#000000:#a0a0a0\"] \n") + .arg(v->toString()) + .arg(v1->toString()) + .arg(data->minSize) + .arg(data->prefSize) + .arg(data->maxSize) + ; + } + } + strVertices += QString::fromAscii("%1 [label=\"%2\"]\n").arg(v->toString()).arg(v->toString()); + } + return QString::fromAscii("%1\n%2\n").arg(strVertices).arg(edges); + } +#endif + + Vertex *rootVertex() const + { + return userVertex; + } + +protected: + void createDirectedEdge(Vertex *from, Vertex *to, EdgeData *data) + { + QHash<Vertex *, EdgeData *> *adjacentToFirst = m_graph.value(from); + if (!adjacentToFirst) { + adjacentToFirst = new QHash<Vertex *, EdgeData *>(); + m_graph.insert(from, adjacentToFirst); + } + adjacentToFirst->insert(to, data); + } + + void removeDirectedEdge(Vertex *from, Vertex *to) + { + QHash<Vertex *, EdgeData *> *adjacentToFirst = m_graph.value(from); + Q_ASSERT(adjacentToFirst); + + adjacentToFirst->remove(to); + if (adjacentToFirst->isEmpty()) { + //nobody point to 'from' so we can remove it from the graph + m_graph.remove(from); + delete adjacentToFirst; + } + } + +private: + Vertex *userVertex; + + QHash<Vertex *, QHash<Vertex *, EdgeData *> *> m_graph; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/gui/graphicsview/qgraphicsanchorlayout.cpp b/src/gui/graphicsview/qgraphicsanchorlayout.cpp new file mode 100644 index 0000000..78b6b53 --- /dev/null +++ b/src/gui/graphicsview/qgraphicsanchorlayout.cpp @@ -0,0 +1,462 @@ +/**************************************************************************** +** +** Copyright (C) 2009 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 is a layout where one can specify how widgets should be placed relative to + each other. The specification is called an anchor, and it is set up by calling anchor(). + Anchors are always set up between edges of an item, where the "center" is also considered to + be an edge. Considering this example: + \code + QGraphicsAnchorLayout *l = new QGraphicsAnchorLayout; + QGraphicsWidget *a = new QGraphicsWidget; + QGraphicsWidget *b = new QGraphicsWidget; + l->anchor(a, Qt::AnchorRight, b, Qt::AnchorLeft); + \endcode + + Here is the right edge of item A anchored to the left edge of item B, with the result that + item B will be placed to the right of item A, with a spacing between A and B. If the + spacing is negative, the items will overlap to some extent. Items that are anchored are + automatically added to the layout, and if items are removed, all their anchors will be + automatically removed + + \section1 Size Hints and Size Policies in QGraphicsAnchorLayout + + QGraphicsAnchorLayout respects each item's size hints and size policies. However it does + not respect stretch factors currently. This might change in the future, so please refrain + from using stretch factors in anchor layout to avoid any future regressions. + + \section1 Spacing within QGraphicsAnchorLayout + + Between the items, the layout can distribute some space. If the spacing has not been + explicitly specified, the actual amount of space will usually be 0, but if the first edge + is the "opposite" of the second edge (i.e. Right is anchored to Left or vice-versa), the + size of the anchor will be queried from the style through the pixelMetric + PM_LayoutHorizontalSpacing (or PM_LayoutVerticalSpacing for vertical anchors). +*/ + +#include "qgraphicsanchorlayout_p.h" + +QT_BEGIN_NAMESPACE + +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() +{ +} + +/*! + Set the spacing for the anchor to \a spacing. + + \sa spacing(), unsetSpacing() +*/ +void QGraphicsAnchor::setSpacing(qreal spacing) +{ + Q_D(QGraphicsAnchor); + d->setSpacing(spacing); +} + +/*! + Returns the spacing for the anchor + + \sa setSpacing() +*/ +qreal QGraphicsAnchor::spacing() const +{ + Q_D(const QGraphicsAnchor); + return d->spacing(); +} + +/*! + Resets the spacing of the anchor point to be the default spacing. Depending on the anchor type, + the default spacing is either 0 or a value returned from the style. + + \sa setSpacing(), spacing(), QGraphicsAnchorLayout::anchor() +*/ +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 magnitude of the anchor is picked up from the style. Anchors + * between a layout edge and an item edge will have a size of 0. + * If there is already an anchor between the edges, the the new anchor will replace the old one. + * + * \a firstItem and \a secondItem are automatically added to the layout if they are not part + * of the layout. This means that count() can increase with up to 2. + * + * The spacing an anchor will get depends on the type of anchor. For instance, anchors from the + * Right edge of one item to the Left edge of another (or vice versa) will use the default + * horizontal spacing. The same behaviour applies to Bottom to Top anchors, (but they will use + * the default vertical spacing). For all other anchor combinations, the spacing will be 0. + * All anchoring functions will follow this rule. + * + * The spacing can also be set manually by using QGraphicsAnchor::setSpacing() method. + * + * \sa addCornerAnchors(), addAnchors() + */ +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, where one is for the horizontal + * edge and another one for the vertical edge that the corners \a firstCorner and \a + * secondCorner specifies. + * The magnitude of the anchors is picked up from the style. + * + * This is a convenience function, since anchoring corners can be expressed as anchoring two edges. + * For instance, + * \code + * layout->addAnchor(layout, Qt::AnchorTop, b, Qt::AnchorTop); + * layout->addAnchor(layout, Qt::AnchorLeft, b, Qt::AnchorLeft); + * \endcode + * + * has the same effect as + * + * \code + * layout->addCornerAnchors(layout, Qt::TopLeft, b, Qt::TopLeft); + * \endcode + * + * If there is already an anchor between the edge pairs, it will be replaced by the anchors that + * this function specifies. + * + * \a firstItem and \a secondItem are automatically added to the layout if they are not part + * of the layout. This means that count() can increase with up to 2. + */ +void QGraphicsAnchorLayout::addCornerAnchors(QGraphicsLayoutItem *firstItem, + Qt::Corner firstCorner, + QGraphicsLayoutItem *secondItem, + Qt::Corner secondCorner) +{ + Q_D(QGraphicsAnchorLayout); + + // Horizontal anchor + Qt::AnchorPoint firstEdge = (firstCorner & 1 ? Qt::AnchorRight: Qt::AnchorLeft); + Qt::AnchorPoint secondEdge = (secondCorner & 1 ? Qt::AnchorRight: Qt::AnchorLeft); + d->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. + + Calling this convenience function with the following arguments + \code + l->addAnchors(firstItem, secondItem, Qt::Horizontal) + \endcode + + is the same as + + \code + l->addAnchor(firstItem, Qt::AnchorLeft, secondItem, Qt::AnchorLeft); + l->addAnchor(firstItem, Qt::AnchorRight, secondItem, Qt::AnchorRight); + \endcode +*/ +void QGraphicsAnchorLayout::addAnchors(QGraphicsLayoutItem *firstItem, + QGraphicsLayoutItem *secondItem, + Qt::Orientations orientations) +{ + if (orientations & Qt::Horizontal) { + addAnchor(secondItem, Qt::AnchorLeft, firstItem, Qt::AnchorLeft); + addAnchor(firstItem, Qt::AnchorRight, secondItem, Qt::AnchorRight); + } + if (orientations & Qt::Vertical) { + addAnchor(secondItem, Qt::AnchorTop, firstItem, Qt::AnchorTop); + addAnchor(firstItem, Qt::AnchorBottom, secondItem, Qt::AnchorBottom); + } +} + +/*! + Returns true if there are no arrangement that satisfies all constraints. + Otherwise returns false. + + \sa addAnchor() +*/ +bool QGraphicsAnchorLayout::hasConflicts() const +{ + Q_D(const QGraphicsAnchorLayout); + return d->hasConflicts(); +} + +/*! + Sets the default horizontal spacing for the anchor layout to \a spacing. + + \sa horizontalSpacing(), setVerticalSpacing(), setSpacing() +*/ +void QGraphicsAnchorLayout::setHorizontalSpacing(qreal spacing) +{ + Q_D(QGraphicsAnchorLayout); + d->spacings[0] = spacing; + invalidate(); +} + +/*! + Sets the default vertical spacing for the anchor layout to \a spacing. + + \sa verticalSpacing(), setHorizontalSpacing(), setSpacing() +*/ +void QGraphicsAnchorLayout::setVerticalSpacing(qreal spacing) +{ + Q_D(QGraphicsAnchorLayout); + d->spacings[1] = spacing; + invalidate(); +} + +/*! + Sets the default horizontal and the default vertical spacing for the anchor layout to \a spacing. + + If an item is anchored with no spacing associated with the anchor, it will use the default + spacing. + \sa setHorizontalSpacing(), setVerticalSpacing() +*/ +void QGraphicsAnchorLayout::setSpacing(qreal spacing) +{ + Q_D(QGraphicsAnchorLayout); + d->spacings[0] = d->spacings[1] = spacing; + invalidate(); +} + +/*! + Returns the default horizontal spacing for the anchor layout. + + \sa verticalSpacing(), setHorizontalSpacing() +*/ +qreal QGraphicsAnchorLayout::horizontalSpacing() const +{ + Q_D(const QGraphicsAnchorLayout); + return d->effectiveSpacing(QGraphicsAnchorLayoutPrivate::Horizontal); +} + +/*! + Returns the default vertical spacing for the anchor layout. + + \sa horizontalSpacing(), setVerticalSpacing() +*/ +qreal QGraphicsAnchorLayout::verticalSpacing() const +{ + Q_D(const QGraphicsAnchorLayout); + return d->effectiveSpacing(QGraphicsAnchorLayoutPrivate::Vertical); +} + +/*! + \reimp +*/ +void QGraphicsAnchorLayout::setGeometry(const QRectF &geom) +{ + Q_D(QGraphicsAnchorLayout); + + QGraphicsLayout::setGeometry(geom); + d->calculateVertexPositions(QGraphicsAnchorLayoutPrivate::Horizontal); + d->calculateVertexPositions(QGraphicsAnchorLayoutPrivate::Vertical); + d->setItemsGeometries(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->restoreSimplifiedGraph(QGraphicsAnchorLayoutPrivate::Horizontal); + d->restoreSimplifiedGraph(QGraphicsAnchorLayoutPrivate::Vertical); + + d->removeCenterConstraints(item, QGraphicsAnchorLayoutPrivate::Horizontal); + d->removeCenterConstraints(item, QGraphicsAnchorLayoutPrivate::Vertical); + d->removeAnchors(item); + d->items.remove(index); + + item->setParentLayoutItem(0); + invalidate(); +} + +/*! + \reimp +*/ +int QGraphicsAnchorLayout::count() const +{ + Q_D(const QGraphicsAnchorLayout); + return d->items.size(); +} + +/*! + \reimp +*/ +QGraphicsLayoutItem *QGraphicsAnchorLayout::itemAt(int index) const +{ + Q_D(const QGraphicsAnchorLayout); + return d->items.value(index); +} + +/*! + \reimp +*/ +void QGraphicsAnchorLayout::invalidate() +{ + Q_D(QGraphicsAnchorLayout); + QGraphicsLayout::invalidate(); + d->calculateGraphCacheDirty = 1; +} + +/*! + \reimp +*/ +QSizeF QGraphicsAnchorLayout::sizeHint(Qt::SizeHint which, const QSizeF &constraint) const +{ + Q_UNUSED(constraint); + Q_D(const QGraphicsAnchorLayout); + + // Some setup calculations are delayed until the information is + // actually needed, avoiding unnecessary recalculations when + // adding multiple anchors. + + // sizeHint() / effectiveSizeHint() already have a cache + // mechanism, using invalidate() to force recalculation. However + // sizeHint() is called three times after invalidation (for max, + // min and pref), but we just need do our setup once. + + const_cast<QGraphicsAnchorLayoutPrivate *>(d)->calculateGraphs(); + + // ### apply constraint! + QSizeF engineSizeHint( + d->sizeHints[QGraphicsAnchorLayoutPrivate::Horizontal][which], + d->sizeHints[QGraphicsAnchorLayoutPrivate::Vertical][which]); + + qreal left, top, right, bottom; + getContentsMargins(&left, &top, &right, &bottom); + + return engineSizeHint + QSizeF(left + right, top + bottom); +} + +QT_END_NAMESPACE diff --git a/src/gui/graphicsview/qgraphicsanchorlayout.h b/src/gui/graphicsview/qgraphicsanchorlayout.h new file mode 100644 index 0000000..44074d1 --- /dev/null +++ b/src/gui/graphicsview/qgraphicsanchorlayout.h @@ -0,0 +1,125 @@ +/**************************************************************************** +** +** Copyright (C) 2009 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) +public: + void setSpacing(qreal spacing); + void unsetSpacing(); + qreal spacing() const; + ~QGraphicsAnchor(); +private: + QGraphicsAnchor(QGraphicsAnchorLayout *parent); + + Q_DECLARE_PRIVATE(QGraphicsAnchor) + + friend class QGraphicsAnchorLayoutPrivate; +}; + +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); + + bool hasConflicts() const; + 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/gui/graphicsview/qgraphicsanchorlayout_p.cpp b/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp new file mode 100644 index 0000000..7041d58 --- /dev/null +++ b/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp @@ -0,0 +1,2313 @@ +/**************************************************************************** +** +** Copyright (C) 2009 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 <QtCore/qlinkedlist.h> +#include <QtCore/qstack.h> + +#ifdef QT_DEBUG +#include <QtCore/qfile.h> +#endif + +#include "qgraphicsanchorlayout_p.h" + +QT_BEGIN_NAMESPACE + + +QGraphicsAnchorPrivate::QGraphicsAnchorPrivate(int version) + : QObjectPrivate(version), layoutPrivate(0), data(0) +{ +} + +QGraphicsAnchorPrivate::~QGraphicsAnchorPrivate() +{ + layoutPrivate->removeAnchor(data->from, data->to); +} + +void QGraphicsAnchorPrivate::setSpacing(qreal value) +{ + if (data) { + layoutPrivate->setAnchorSize(data, &value); + } else { + qWarning("QGraphicsAnchor::setSpacing: The anchor does not exist."); + } +} + +void QGraphicsAnchorPrivate::unsetSpacing() +{ + if (data) { + layoutPrivate->setAnchorSize(data, 0); + } else { + qWarning("QGraphicsAnchor::setSpacing: The anchor does not exist."); + } +} + +qreal QGraphicsAnchorPrivate::spacing() const +{ + qreal size = 0; + if (data) { + layoutPrivate->anchorSize(data, 0, &size, 0); + } else { + qWarning("QGraphicsAnchor::setSpacing: The anchor does not exist."); + } + return size; +} + + +void AnchorData::refreshSizeHints(qreal effectiveSpacing) +{ + if (!isLayoutAnchor && from->m_item == to->m_item) { + QGraphicsLayoutItem *item = from->m_item; + + const QGraphicsAnchorLayoutPrivate::Orientation orient = QGraphicsAnchorLayoutPrivate::edgeOrientation(from->m_edge); + const Qt::AnchorPoint centerEdge = QGraphicsAnchorLayoutPrivate::pickEdge(Qt::AnchorHorizontalCenter, orient); + + QSizePolicy::Policy policy; + qreal minSizeHint, prefSizeHint, maxSizeHint; + if (orient == 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(); + } + // 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 + prefSize = prefSizeHint; + minSize = prefSize; + maxSize = prefSize; + + if (policy & QSizePolicy::GrowFlag) + maxSize = maxSizeHint; + + if (policy & QSizePolicy::ShrinkFlag) + minSize = minSizeHint; + + if (policy & QSizePolicy::IgnoreFlag) + prefSize = minSize; + + bool hasCenter = (from->m_edge == centerEdge || to->m_edge == centerEdge); + + if (hasCenter) { + minSize /= 2; + prefSize /= 2; + maxSize /= 2; + } + + // Set the anchor effective sizes to preferred. + // + // Note: The idea here is that all items should remain at their + // preferred size unless where that's impossible. In cases where + // the item is subject to restrictions (anchored to the layout + // edges, for instance), the simplex solver will be run to + // recalculate and override the values we set here. + sizeAtMinimum = prefSize; + sizeAtPreferred = prefSize; + sizeAtMaximum = prefSize; + + } else if (!hasSize) { + // Anchor has no size defined, use given default information + minSize = effectiveSpacing; + prefSize = effectiveSpacing; + maxSize = effectiveSpacing; + + sizeAtMinimum = prefSize; + sizeAtPreferred = prefSize; + sizeAtMaximum = prefSize; + } +} + +void ParallelAnchorData::updateChildrenSizes() +{ + firstEdge->sizeAtMinimum = secondEdge->sizeAtMinimum = sizeAtMinimum; + firstEdge->sizeAtPreferred = secondEdge->sizeAtPreferred = sizeAtPreferred; + firstEdge->sizeAtMaximum = secondEdge->sizeAtMaximum = sizeAtMaximum; + + firstEdge->updateChildrenSizes(); + secondEdge->updateChildrenSizes(); +} + +void ParallelAnchorData::refreshSizeHints(qreal effectiveSpacing) +{ + // First refresh children information + firstEdge->refreshSizeHints(effectiveSpacing); + secondEdge->refreshSizeHints(effectiveSpacing); + + // ### should we warn if the parallel connection is invalid? + // e.g. 1-2-3 with 10-20-30, the minimum of the latter is + // bigger than the maximum of the former. + + minSize = qMax(firstEdge->minSize, secondEdge->minSize); + maxSize = qMin(firstEdge->maxSize, secondEdge->maxSize); + + prefSize = qMax(firstEdge->prefSize, secondEdge->prefSize); + prefSize = qMin(prefSize, maxSize); + + // See comment in AnchorData::refreshSizeHints() about sizeAt* values + sizeAtMinimum = prefSize; + sizeAtPreferred = prefSize; + sizeAtMaximum = prefSize; +} + +/*! + \internal + returns the factor in the interval [-1, 1]. + -1 is at Minimum + 0 is at Preferred + 1 is at Maximum +*/ +static qreal getFactor(qreal value, qreal min, qreal pref, qreal max) +{ + // ### Maybe remove some of the assertions? (since outside is asserting us) + Q_ASSERT(value > min || qFuzzyCompare(value, min)); + Q_ASSERT(value < max || qFuzzyCompare(value, max)); + + if (qFuzzyCompare(value, min)) { + return -1.0; + } else if (qFuzzyCompare(value, pref)) { + return 0.0; + } else if (qFuzzyCompare(value, max)) { + return 1.0; + } else if (value < pref) { + // Since value < pref and value != pref and min <= value, + // we can assert that min < pref. + Q_ASSERT(min < pref); + return (value - min) / (pref - min) - 1; + } else { + // Since value > pref and value != pref and max >= value, + // we can assert that max > pref. + Q_ASSERT(max > pref); + return (value - pref) / (max - pref); + } +} + +void SequentialAnchorData::updateChildrenSizes() +{ + // ### REMOVE ME + // ### check whether we are guarantee to get those or we need to warn stuff at this + // point. + Q_ASSERT(sizeAtMinimum > minSize || qFuzzyCompare(sizeAtMinimum, minSize)); + Q_ASSERT(sizeAtMinimum < maxSize || qFuzzyCompare(sizeAtMinimum, maxSize)); + Q_ASSERT(sizeAtPreferred > minSize || qFuzzyCompare(sizeAtPreferred, minSize)); + Q_ASSERT(sizeAtPreferred < maxSize || qFuzzyCompare(sizeAtPreferred, maxSize)); + Q_ASSERT(sizeAtMaximum > minSize || qFuzzyCompare(sizeAtMaximum, minSize)); + Q_ASSERT(sizeAtMaximum < maxSize || qFuzzyCompare(sizeAtMaximum, maxSize)); + + // Band here refers if the value is in the Minimum To Preferred + // band (the lower band) or the Preferred To Maximum (the upper band). + + qreal minFactor = getFactor(sizeAtMinimum, minSize, prefSize, maxSize); + qreal prefFactor = getFactor(sizeAtPreferred, minSize, prefSize, maxSize); + qreal maxFactor = getFactor(sizeAtMaximum, minSize, prefSize, maxSize); + + for (int i = 0; i < m_edges.count(); ++i) { + AnchorData *e = m_edges.at(i); + + qreal bandSize = minFactor > 0 ? e->maxSize - e->prefSize : e->prefSize - e->minSize; + e->sizeAtMinimum = e->prefSize + bandSize * minFactor; + + bandSize = prefFactor > 0 ? e->maxSize - e->prefSize : e->prefSize - e->minSize; + e->sizeAtPreferred = e->prefSize + bandSize * prefFactor; + + bandSize = maxFactor > 0 ? e->maxSize - e->prefSize : e->prefSize - e->minSize; + e->sizeAtMaximum = e->prefSize + bandSize * maxFactor; + + e->updateChildrenSizes(); + } +} + +void SequentialAnchorData::refreshSizeHints(qreal effectiveSpacing) +{ + minSize = 0; + prefSize = 0; + maxSize = 0; + + for (int i = 0; i < m_edges.count(); ++i) { + AnchorData *edge = m_edges.at(i); + + // First refresh children information + edge->refreshSizeHints(effectiveSpacing); + + minSize += edge->minSize; + prefSize += edge->prefSize; + maxSize += edge->maxSize; + } + + // See comment in AnchorData::refreshSizeHints() about sizeAt* values + sizeAtMinimum = prefSize; + sizeAtPreferred = prefSize; + sizeAtMaximum = prefSize; +} + +#ifdef QT_DEBUG +void AnchorData::dump(int indent) { + if (type == Parallel) { + qDebug("%*s type: parallel:", indent, ""); + ParallelAnchorData *p = static_cast<ParallelAnchorData *>(this); + p->firstEdge->dump(indent+2); + p->secondEdge->dump(indent+2); + } else if (type == Sequential) { + SequentialAnchorData *s = static_cast<SequentialAnchorData *>(this); + int kids = s->m_edges.count(); + qDebug("%*s type: sequential(%d):", indent, "", kids); + for (int i = 0; i < kids; ++i) { + s->m_edges.at(i)->dump(indent+2); + } + } else { + qDebug("%*s type: Normal:", indent, ""); + } +} + +#endif + +QSimplexConstraint *GraphPath::constraint(const GraphPath &path) const +{ + // Calculate + QSet<AnchorData *> cPositives; + QSet<AnchorData *> cNegatives; + QSet<AnchorData *> intersection; + + cPositives = positives + path.negatives; + cNegatives = negatives + path.positives; + + intersection = cPositives & cNegatives; + + cPositives -= intersection; + cNegatives -= intersection; + + // Fill + QSimplexConstraint *c = new QSimplexConstraint; + QSet<AnchorData *>::iterator i; + for (i = cPositives.begin(); i != cPositives.end(); ++i) + c->variables.insert(*i, 1.0); + + for (i = cNegatives.begin(); i != cNegatives.end(); ++i) + c->variables.insert(*i, -1.0); + + return c; +} + +#ifdef QT_DEBUG +QString GraphPath::toString() const +{ + QString string(QLatin1String("Path: ")); + foreach(AnchorData *edge, positives) + string += QString::fromAscii(" (+++) %1").arg(edge->toString()); + + foreach(AnchorData *edge, negatives) + string += QString::fromAscii(" (---) %1").arg(edge->toString()); + + return string; +} +#endif + +QGraphicsAnchorLayoutPrivate::QGraphicsAnchorLayoutPrivate() + : calculateGraphCacheDirty(1) +{ + for (int i = 0; i < NOrientations; ++i) { + spacings[i] = -1; + graphSimplified[i] = false; + graphHasConflicts[i] = false; + } +} + +Qt::AnchorPoint QGraphicsAnchorLayoutPrivate::oppositeEdge(Qt::AnchorPoint edge) +{ + switch (edge) { + case Qt::AnchorLeft: + edge = Qt::AnchorRight; + break; + case Qt::AnchorRight: + edge = Qt::AnchorLeft; + break; + case Qt::AnchorTop: + edge = Qt::AnchorBottom; + break; + case Qt::AnchorBottom: + edge = Qt::AnchorTop; + break; + default: + break; + } + return edge; +} + + +/*! + * \internal + * + * helper function in order to avoid overflowing anchor sizes + * the returned size will never be larger than FLT_MAX + * + */ +inline static qreal checkAdd(qreal a, qreal b) +{ + if (FLT_MAX - b < a) + return FLT_MAX; + return a + b; +} + +/*! + * \internal + * + * Takes the sequence of vertices described by (\a before, \a vertices, \a after) and replaces + * all anchors connected to the vertices in \a vertices with one simplified anchor between + * \a before and \a after. The simplified anchor will be a placeholder for all the previous + * anchors between \a before and \a after, and can be restored back to the anchors it is a + * placeholder for. + */ +static bool simplifySequentialChunk(Graph<AnchorVertex, AnchorData> *graph, + AnchorVertex *before, + const QVector<AnchorVertex*> &vertices, + AnchorVertex *after) +{ + int i; +#if defined(QT_DEBUG) && 0 + QString strVertices; + for (i = 0; i < vertices.count(); ++i) + strVertices += QString::fromAscii("%1 - ").arg(vertices.at(i)->toString()); + QString strPath = QString::fromAscii("%1 - %2%3").arg(before->toString(), strVertices, after->toString()); + qDebug("simplifying [%s] to [%s - %s]", qPrintable(strPath), qPrintable(before->toString()), qPrintable(after->toString())); +#endif + + qreal min = 0; + qreal pref = 0; + qreal max = 0; + + SequentialAnchorData *sequence = new SequentialAnchorData; + AnchorVertex *prev = before; + AnchorData *data; + for (i = 0; i <= vertices.count(); ++i) { + AnchorVertex *next = (i < vertices.count()) ? vertices.at(i) : after; + data = graph->takeEdge(prev, next); + min += data->minSize; + pref += data->prefSize; + max = checkAdd(max, data->maxSize); + sequence->m_edges.append(data); + prev = next; + } + + // insert new + sequence->minSize = min; + sequence->prefSize = pref; + sequence->maxSize = max; + + // Unless these values are overhidden by the simplex solver later-on, + // anchors will keep their own preferred size. + sequence->sizeAtMinimum = pref; + sequence->sizeAtPreferred = pref; + sequence->sizeAtMaximum = pref; + + sequence->setVertices(vertices); + + sequence->from = before; + sequence->to = after; + + // data here is the last edge in the sequence + // ### this seems to be here for supporting reverse order sequences, + // but doesnt seem to be used right now + if (data->from != vertices.last()) + qSwap(sequence->from, sequence->to); + + // Note that since layout 'edges' can't be simplified away from + // the graph, it's safe to assume that if there's a layout + // 'edge', it'll be in the boundaries of the sequence. + sequence->isLayoutAnchor = (sequence->m_edges.first()->isLayoutAnchor + || sequence->m_edges.last()->isLayoutAnchor); + + AnchorData *newAnchor = sequence; + if (AnchorData *oldAnchor = graph->takeEdge(before, after)) { + newAnchor = new ParallelAnchorData(oldAnchor, sequence); + + newAnchor->isLayoutAnchor = (oldAnchor->isLayoutAnchor + || sequence->isLayoutAnchor); + + min = qMax(oldAnchor->minSize, sequence->minSize); + max = qMin(oldAnchor->maxSize, sequence->maxSize); + + pref = qMax(oldAnchor->prefSize, sequence->prefSize); + pref = qMin(pref, max); + + newAnchor->minSize = min; + newAnchor->prefSize = pref; + newAnchor->maxSize = max; + + // Same as above, by default, keep preferred size. + newAnchor->sizeAtMinimum = pref; + newAnchor->sizeAtPreferred = pref; + newAnchor->sizeAtMaximum = pref; + } + graph->createEdge(before, after, newAnchor); + + // True if we created a parallel anchor + return newAnchor != sequence; +} + +/*! + \internal + + The purpose of this function is to simplify the graph. + Simplification serves two purposes: + 1. Reduce the number of edges in the graph, (thus the number of variables to the equation + solver is reduced, and the solver performs better). + 2. Be able to do distribution of sequences of edges more intelligently (esp. with sequential + anchors) + + It is essential that it must be possible to restore simplified anchors back to their "original" + form. This is done by restoreSimplifiedAnchor(). + + There are two types of simplification that can be done: + 1. Sequential simplification + Sequential simplification means that all sequences of anchors will be merged into one single + anchor. Only anhcors that points in the same direction will be merged. + 2. Parallel simplification + If a simplified sequential anchor is about to be inserted between two vertices in the graph + and there already exist an anchor between those two vertices, a parallel anchor will be + created that serves as a placeholder for the sequential anchor and the anchor that was + already between the two vertices. + + The process of simplification can be described as: + + 1. Simplify all sequences of anchors into one anchor. + If no further simplification was done, go to (3) + - If there already exist an anchor where the sequential anchor is supposed to be inserted, + take that anchor out of the graph + - Then create a parallel anchor that holds the sequential anchor and the anchor just taken + out of the graph. + 2. Go to (1) + 3. Done + + + * Gathering sequential anchors * + The algorithm walks the graph in depth-first order, and only collects vertices that has two + edges connected to it. If the vertex does not have two edges or if it is a layout edge, + it will take all the previously collected vertices and try to create a simplified sequential + anchor representing all the previously collected vertices. + Once the simplified anchor is inserted, the collected list is cleared in order to find the next + sequence to simplify. + Note that there are some catches to this that are not covered by the above explanation. +*/ +void QGraphicsAnchorLayoutPrivate::simplifyGraph(Orientation orientation) +{ + static bool noSimplification = !qgetenv("QT_ANCHORLAYOUT_NO_SIMPLIFICATION").isEmpty(); + if (noSimplification) + return; + + if (graphSimplified[orientation]) + return; + graphSimplified[orientation] = true; + +#if 0 + qDebug("Simplifying Graph for %s", + orientation == Horizontal ? "Horizontal" : "Vertical"); +#endif + + AnchorVertex *rootVertex = graph[orientation].rootVertex(); + + if (!rootVertex) + return; + + bool dirty; + do { + dirty = simplifyGraphIteration(orientation); + } while (dirty); +} + +bool QGraphicsAnchorLayoutPrivate::simplifyGraphIteration(QGraphicsAnchorLayoutPrivate::Orientation orientation) +{ + Q_Q(QGraphicsAnchorLayout); + Graph<AnchorVertex, AnchorData> &g = graph[orientation]; + AnchorVertex *v = g.rootVertex(); + + QSet<AnchorVertex *> visited; + QStack<AnchorVertex *> stack; + stack.push(v); + QVector<AnchorVertex*> candidates; + + const Qt::AnchorPoint centerEdge = pickEdge(Qt::AnchorHorizontalCenter, orientation); + const Qt::AnchorPoint layoutEdge = oppositeEdge(v->m_edge); + + bool dirty = false; + + // walk depth-first. + while (!stack.isEmpty()) { + v = stack.pop(); + QList<AnchorVertex *> vertices = g.adjacentVertices(v); + const int count = vertices.count(); + bool endOfSequence = (v->m_item == q && v->m_edge == layoutEdge) || count != 2; + if (count == 2 && v->m_item != q) { + candidates.append(v); + if (visited.contains(vertices.first()) && visited.contains(vertices.last())) { + // in case of a cycle + endOfSequence = true; + } + } + if (endOfSequence && candidates.count() >= 1) { + int i; + AnchorVertex *afterSequence= 0; + AnchorVertex *beforeSequence = 0; + // find the items before and after the valid sequence + if (candidates.count() == 1) { + QList<AnchorVertex *> beforeAndAfterVertices = g.adjacentVertices(candidates.at(0)); + Q_ASSERT(beforeAndAfterVertices.count() == 2); + // Since we only have one vertex, we can pick + // any of the two vertices to become before/after. + afterSequence = beforeAndAfterVertices.last(); + beforeSequence = beforeAndAfterVertices.first(); + } else { + QList<AnchorVertex *> adjacentOfSecondLastVertex = g.adjacentVertices(candidates.last()); + Q_ASSERT(adjacentOfSecondLastVertex.count() == 2); + if (adjacentOfSecondLastVertex.first() == candidates.at(candidates.count() - 2)) + afterSequence = adjacentOfSecondLastVertex.last(); + else + afterSequence = adjacentOfSecondLastVertex.first(); + + QList<AnchorVertex *> adjacentOfSecondVertex = g.adjacentVertices(candidates.first()); + Q_ASSERT(adjacentOfSecondVertex.count() == 2); + if (adjacentOfSecondVertex.first() == candidates.at(1)) + beforeSequence = adjacentOfSecondVertex.last(); + else + beforeSequence = adjacentOfSecondVertex.first(); + } + // The complete path of the sequence to simplify is: beforeSequence, <candidates>, afterSequence + // where beforeSequence and afterSequence are the endpoints where the anchor is inserted + // between. +#if defined(QT_DEBUG) && 0 + // ### DEBUG + QString strCandidates; + for (i = 0; i < candidates.count(); ++i) + strCandidates += QString::fromAscii("%1 - ").arg(candidates.at(i)->toString()); + QString strPath = QString::fromAscii("%1 - %2%3").arg(beforeSequence->toString(), strCandidates, afterSequence->toString()); + qDebug("candidate list for sequential simplification:\n[%s]", qPrintable(strPath)); +#endif + + bool forward = true; + AnchorVertex *prev = beforeSequence; + int intervalFrom = 0; + + // Check for directionality (from). We don't want to destroy that information, + // thus we only combine anchors with the same direction. + + // "i" is the index *including* the beforeSequence and afterSequence vertices. + for (i = 1; i <= candidates.count() + 1; ++i) { + bool atVertexAfter = i > candidates.count(); + AnchorVertex *v1 = atVertexAfter ? afterSequence : candidates.at(i - 1); + AnchorData *data = g.edgeData(prev, v1); + Q_ASSERT(data); + if (i == 1) { + forward = (prev == data->from ? true : false); + } else if (forward != (prev == data->from) || atVertexAfter) { + int intervalTo = i; + if (forward != (prev == data->from)) + --intervalTo; + + // intervalFrom and intervalTo should now be indices to the vertex before and + // after the sequential anchor. + if (intervalTo - intervalFrom >= 2) { + // simplify in the range [intervalFrom, intervalTo] + + // Trim off internal center anchors (Left-Center/Center-Right) from the + // start and the end of the sequence. We never want to simplify internal + // center anchors where there is an external anchor connected to the center. + AnchorVertex *intervalVertexFrom = intervalFrom == 0 ? beforeSequence : candidates.at(intervalFrom - 1); + int effectiveIntervalFrom = intervalFrom; + if (intervalVertexFrom->m_edge == centerEdge + && intervalVertexFrom->m_item == candidates.at(effectiveIntervalFrom)->m_item) { + ++effectiveIntervalFrom; + intervalVertexFrom = candidates.at(effectiveIntervalFrom - 1); + } + AnchorVertex *intervalVertexTo = intervalTo <= candidates.count() ? candidates.at(intervalTo - 1) : afterSequence; + int effectiveIntervalTo = intervalTo; + if (intervalVertexTo->m_edge == centerEdge + && intervalVertexTo->m_item == candidates.at(effectiveIntervalTo - 2)->m_item) { + --effectiveIntervalTo; + intervalVertexTo = candidates.at(effectiveIntervalTo - 1); + } + if (effectiveIntervalTo - effectiveIntervalFrom >= 2) { + QVector<AnchorVertex*> subCandidates; + if (forward) { + subCandidates = candidates.mid(effectiveIntervalFrom, effectiveIntervalTo - effectiveIntervalFrom - 1); + } else { + // reverse the order of the candidates. + qSwap(intervalVertexFrom, intervalVertexTo); + do { + ++effectiveIntervalFrom; + subCandidates.prepend(candidates.at(effectiveIntervalFrom - 1)); + } while (effectiveIntervalFrom < effectiveIntervalTo - 1); + } + if (simplifySequentialChunk(&g, intervalVertexFrom, subCandidates, intervalVertexTo)) { + dirty = true; + break; + } + // finished simplification of chunk with same direction + } + } + if (forward == (prev == data->from)) + --intervalTo; + intervalFrom = intervalTo; + + forward = !forward; + } + prev = v1; + } + + if (dirty) + break; + } + + if (endOfSequence) + candidates.clear(); + + for (int i = 0; i < count; ++i) { + AnchorVertex *next = vertices.at(i); + if (next->m_item == q && next->m_edge == centerEdge) + continue; + if (visited.contains(next)) + continue; + stack.push(next); + } + + visited.insert(v); + } + + return dirty; +} + +static void restoreSimplifiedAnchor(Graph<AnchorVertex, AnchorData> &g, + AnchorData *edge, + AnchorVertex *before, + AnchorVertex *after) +{ + Q_ASSERT(edge->type != AnchorData::Normal); +#if 0 + static const char *anchortypes[] = {"Normal", + "Sequential", + "Parallel"}; + qDebug("Restoring %s edge.", anchortypes[int(edge->type)]); +#endif + if (edge->type == AnchorData::Sequential) { + SequentialAnchorData* seqEdge = static_cast<SequentialAnchorData*>(edge); + // restore the sequential anchor + AnchorVertex *prev = before; + AnchorVertex *last = after; + if (edge->from != prev) + qSwap(last, prev); + + for (int i = 0; i < seqEdge->m_edges.count(); ++i) { + AnchorVertex *v1 = (i < seqEdge->m_children.count()) ? seqEdge->m_children.at(i) : last; + AnchorData *data = seqEdge->m_edges.at(i); + if (data->type != AnchorData::Normal) { + restoreSimplifiedAnchor(g, data, prev, v1); + } else { + g.createEdge(prev, v1, data); + } + prev = v1; + } + } else if (edge->type == AnchorData::Parallel) { + ParallelAnchorData* parallelEdge = static_cast<ParallelAnchorData*>(edge); + AnchorData *parallelEdges[2] = {parallelEdge->firstEdge, + parallelEdge->secondEdge}; + for (int i = 0; i < 2; ++i) { + AnchorData *data = parallelEdges[i]; + if (data->type == AnchorData::Normal) { + g.createEdge(before, after, data); + } else { + restoreSimplifiedAnchor(g, data, before, after); + } + } + } +} + +void QGraphicsAnchorLayoutPrivate::restoreSimplifiedGraph(Orientation orientation) +{ + if (!graphSimplified[orientation]) + return; + graphSimplified[orientation] = false; + +#if 0 + qDebug("Restoring Simplified Graph for %s", + orientation == Horizontal ? "Horizontal" : "Vertical"); +#endif + + Graph<AnchorVertex, AnchorData> &g = graph[orientation]; + + QList<QPair<AnchorVertex*, AnchorVertex*> > connections = g.connections(); + for (int i = 0; i < connections.count(); ++i) { + AnchorVertex *v1 = connections.at(i).first; + AnchorVertex *v2 = connections.at(i).second; + AnchorData *edge = g.edgeData(v1, v2); + if (edge->type != AnchorData::Normal) { + AnchorData *oldEdge = g.takeEdge(v1, v2); + restoreSimplifiedAnchor(g, edge, v1, v2); + delete oldEdge; + } + } +} + +QGraphicsAnchorLayoutPrivate::Orientation +QGraphicsAnchorLayoutPrivate::edgeOrientation(Qt::AnchorPoint edge) +{ + return edge > Qt::AnchorRight ? Vertical : Horizontal; +} + +/*! + \internal + + Create internal anchors to connect the layout edges (Left to Right and + Top to Bottom). + + These anchors doesn't have size restrictions, that will be enforced by + other anchors and items in the layout. +*/ +void QGraphicsAnchorLayoutPrivate::createLayoutEdges() +{ + Q_Q(QGraphicsAnchorLayout); + QGraphicsLayoutItem *layout = q; + + // Horizontal + AnchorData *data = new AnchorData(0, 0, QWIDGETSIZE_MAX); + addAnchor_helper(layout, Qt::AnchorLeft, layout, + Qt::AnchorRight, data); + data->skipInPreferred = 1; + + // Set the Layout Left edge as the root of the horizontal graph. + AnchorVertex *v = internalVertex(layout, Qt::AnchorLeft); + graph[Horizontal].setRootVertex(v); + + // Vertical + data = new AnchorData(0, 0, QWIDGETSIZE_MAX); + addAnchor_helper(layout, Qt::AnchorTop, layout, + Qt::AnchorBottom, data); + data->skipInPreferred = 1; + + // Set the Layout Top edge as the root of the vertical graph. + v = internalVertex(layout, Qt::AnchorTop); + graph[Vertical].setRootVertex(v); +} + +void QGraphicsAnchorLayoutPrivate::deleteLayoutEdges() +{ + Q_Q(QGraphicsAnchorLayout); + + Q_ASSERT(internalVertex(q, Qt::AnchorHorizontalCenter) == NULL); + Q_ASSERT(internalVertex(q, Qt::AnchorVerticalCenter) == NULL); + + removeAnchor_helper(internalVertex(q, Qt::AnchorLeft), + internalVertex(q, Qt::AnchorRight)); + removeAnchor_helper(internalVertex(q, Qt::AnchorTop), + internalVertex(q, Qt::AnchorBottom)); +} + +void QGraphicsAnchorLayoutPrivate::createItemEdges(QGraphicsLayoutItem *item) +{ + Q_ASSERT(!graphSimplified[Horizontal] && !graphSimplified[Vertical]); + + items.append(item); + + QSizeF minSize = item->effectiveSizeHint(Qt::MinimumSize); + QSizeF prefSize = item->effectiveSizeHint(Qt::PreferredSize); + QSizeF maxSize = item->effectiveSizeHint(Qt::MaximumSize); + + // Horizontal + AnchorData *data = new AnchorData(minSize.width(), prefSize.width(), maxSize.width()); + addAnchor_helper(item, Qt::AnchorLeft, item, + Qt::AnchorRight, data); + + // Vertical + data = new AnchorData(minSize.height(), prefSize.height(), maxSize.height()); + addAnchor_helper(item, Qt::AnchorTop, item, + Qt::AnchorBottom, data); +} + +/*! + \internal + + By default, each item in the layout is represented internally as + a single anchor in each direction. For instance, from Left to Right. + + However, to support anchorage of items to the center of items, we + must split this internal anchor into two half-anchors. From Left + to Center and then from Center to Right, with the restriction that + these anchors must have the same time at all times. +*/ +void QGraphicsAnchorLayoutPrivate::createCenterAnchors( + QGraphicsLayoutItem *item, Qt::AnchorPoint centerEdge) +{ + Orientation orientation; + switch (centerEdge) { + case Qt::AnchorHorizontalCenter: + orientation = Horizontal; + break; + case Qt::AnchorVerticalCenter: + orientation = Vertical; + break; + default: + // Don't create center edges unless needed + return; + } + + Q_ASSERT(!graphSimplified[orientation]); + + // Check if vertex already exists + if (internalVertex(item, centerEdge)) + return; + + // Orientation code + Qt::AnchorPoint firstEdge; + Qt::AnchorPoint lastEdge; + + if (orientation == Horizontal) { + firstEdge = Qt::AnchorLeft; + lastEdge = Qt::AnchorRight; + } else { + firstEdge = Qt::AnchorTop; + lastEdge = Qt::AnchorBottom; + } + + AnchorVertex *first = internalVertex(item, firstEdge); + AnchorVertex *last = internalVertex(item, lastEdge); + Q_ASSERT(first && last); + + // Create new anchors + AnchorData *oldData = graph[orientation].edgeData(first, last); + + qreal minimumSize = oldData->minSize / 2; + qreal preferredSize = oldData->prefSize / 2; + qreal maximumSize = oldData->maxSize / 2; + + QSimplexConstraint *c = new QSimplexConstraint; + AnchorData *data = new AnchorData(minimumSize, preferredSize, maximumSize); + c->variables.insert(data, 1.0); + addAnchor_helper(item, firstEdge, item, centerEdge, data); + + data = new AnchorData(minimumSize, preferredSize, maximumSize); + c->variables.insert(data, -1.0); + addAnchor_helper(item, centerEdge, item, lastEdge, data); + + itemCenterConstraints[orientation].append(c); + + // Remove old one + removeAnchor_helper(first, last); +} + +void QGraphicsAnchorLayoutPrivate::removeCenterAnchors( + QGraphicsLayoutItem *item, Qt::AnchorPoint centerEdge, + bool substitute) +{ + Orientation orientation; + switch (centerEdge) { + case Qt::AnchorHorizontalCenter: + orientation = Horizontal; + break; + case Qt::AnchorVerticalCenter: + orientation = Vertical; + break; + default: + // Don't remove edges that not the center ones + return; + } + + Q_ASSERT(!graphSimplified[orientation]); + + // Orientation code + Qt::AnchorPoint firstEdge; + Qt::AnchorPoint lastEdge; + + if (orientation == Horizontal) { + firstEdge = Qt::AnchorLeft; + lastEdge = Qt::AnchorRight; + } else { + firstEdge = Qt::AnchorTop; + lastEdge = Qt::AnchorBottom; + } + + AnchorVertex *center = internalVertex(item, centerEdge); + if (!center) + return; + AnchorVertex *first = internalVertex(item, firstEdge); + + Q_ASSERT(first); + Q_ASSERT(center); + + Graph<AnchorVertex, AnchorData> &g = graph[orientation]; + + + AnchorData *oldData = g.edgeData(first, center); + // Remove center constraint + for (int i = itemCenterConstraints[orientation].count() - 1; i >= 0; --i) { + if (itemCenterConstraints[orientation][i]->variables.contains(oldData)) { + delete itemCenterConstraints[orientation].takeAt(i); + break; + } + } + + if (substitute) { + // Create the new anchor that should substitute the left-center-right anchors. + AnchorData *oldData = g.edgeData(first, center); + + qreal minimumSize = oldData->minSize * 2; + qreal preferredSize = oldData->prefSize * 2; + qreal maximumSize = oldData->maxSize * 2; + + AnchorData *data = new AnchorData(minimumSize, preferredSize, maximumSize); + addAnchor_helper(item, firstEdge, item, lastEdge, data); + + // 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)); + } +} + + +void QGraphicsAnchorLayoutPrivate::removeCenterConstraints(QGraphicsLayoutItem *item, + Orientation orientation) +{ + Q_ASSERT(!graphSimplified[orientation]); + + // Remove the item center constraints associated to this item + // ### This is a temporary solution. We should probably use a better + // data structure to hold items and/or their associated constraints + // so that we can remove those easily + + AnchorVertex *first = internalVertex(item, orientation == Horizontal ? + Qt::AnchorLeft : + Qt::AnchorTop); + AnchorVertex *center = internalVertex(item, orientation == Horizontal ? + Qt::AnchorHorizontalCenter : + Qt::AnchorVerticalCenter); + + // Skip if no center constraints exist + if (!center) + return; + + Q_ASSERT(first); + AnchorData *internalAnchor = graph[orientation].edgeData(first, center); + + // Look for our anchor in all item center constraints, then remove it + for (int i = 0; i < itemCenterConstraints[orientation].size(); ++i) { + if (itemCenterConstraints[orientation][i]->variables.contains(internalAnchor)) { + delete itemCenterConstraints[orientation].takeAt(i); + break; + } + } +} + +/*! + * \internal + * + * Helper function that is called from the anchor functions in the public API. + * If \a spacing is 0, it will pick up the spacing defined by the style. + */ +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; + } + + // Guarantee that the graph is no simplified when adding this anchor, + // anchor manipulation always happen in the full graph + restoreSimplifiedGraph(edgeOrientation(firstEdge)); + + // In QGraphicsAnchorLayout, items are represented in its internal + // graph as four anchors that connect: + // - Left -> HCenter + // - HCenter-> Right + // - Top -> VCenter + // - VCenter -> Bottom + + // Ensure that the internal anchors have been created for both items. + if (firstItem != q && !items.contains(firstItem)) { + restoreSimplifiedGraph(edgeOrientation(firstEdge) == Horizontal ? Vertical : Horizontal); + createItemEdges(firstItem); + addChildLayoutItem(firstItem); + } + if (secondItem != q && !items.contains(secondItem)) { + restoreSimplifiedGraph(edgeOrientation(firstEdge) == Horizontal ? Vertical : Horizontal); + createItemEdges(secondItem); + addChildLayoutItem(secondItem); + } + + // Create center edges if needed + createCenterAnchors(firstItem, firstEdge); + createCenterAnchors(secondItem, secondEdge); + + // Use heuristics to find out what the user meant with this anchor. + correctEdgeDirection(firstItem, firstEdge, secondItem, secondEdge); + + AnchorData *data; + if (!spacing) { + // If firstItem or secondItem is the layout itself, the spacing will default to 0. + // Otherwise, the following matrix is used (questionmark means that the spacing + // is queried from the style): + // from + // to Left HCenter Right + // Left 0 0 ? + // HCenter 0 0 0 + // Right ? 0 0 + if (firstItem != q + && secondItem != q + && pickEdge(firstEdge, Horizontal) != Qt::AnchorHorizontalCenter + && oppositeEdge(firstEdge) == secondEdge) { + data = new AnchorData; // ask the style later + } else { + data = new AnchorData(0); // spacing should be 0 + } + addAnchor_helper(firstItem, firstEdge, secondItem, secondEdge, data); + } else if (*spacing >= 0) { + data = new AnchorData(*spacing); + addAnchor_helper(firstItem, firstEdge, secondItem, secondEdge, data); + } else { + data = new AnchorData(-*spacing); + addAnchor_helper(secondItem, secondEdge, firstItem, firstEdge, data); + } + return acquireGraphicsAnchor(data); +} + +void QGraphicsAnchorLayoutPrivate::addAnchor_helper(QGraphicsLayoutItem *firstItem, + Qt::AnchorPoint firstEdge, + QGraphicsLayoutItem *secondItem, + Qt::AnchorPoint secondEdge, + AnchorData *data) +{ + Q_Q(QGraphicsAnchorLayout); + + // Guarantee that the graph is no simplified when adding this anchor, + // anchor manipulation always happen in the full graph + restoreSimplifiedGraph(edgeOrientation(firstEdge)); + + // Is the Vertex (firstItem, firstEdge) already represented in our + // internal structure? + AnchorVertex *v1 = addInternalVertex(firstItem, firstEdge); + AnchorVertex *v2 = addInternalVertex(secondItem, secondEdge); + + // Remove previous anchor + // ### Could we update the existing edgeData rather than creating a new one? + if (graph[edgeOrientation(firstEdge)].edgeData(v1, v2)) { + removeAnchor_helper(v1, v2); + } + + // Create a bi-directional edge in the sense it can be transversed both + // from v1 or v2. "data" however is shared between the two references + // so we still know that the anchor direction is from 1 to 2. + data->from = v1; + data->to = v2; +#ifdef QT_DEBUG + data->name = QString::fromAscii("%1 --to--> %2").arg(v1->toString()).arg(v2->toString()); +#endif + // Keep track of anchors that are connected to the layout 'edges' + data->isLayoutAnchor = (v1->m_item == q || v2->m_item == q); + + graph[edgeOrientation(firstEdge)].createEdge(v1, v2, data); +} + +QGraphicsAnchor *QGraphicsAnchorLayoutPrivate::getAnchor(QGraphicsLayoutItem *firstItem, + Qt::AnchorPoint firstEdge, + QGraphicsLayoutItem *secondItem, + Qt::AnchorPoint secondEdge) +{ + Orientation orient = edgeOrientation(firstEdge); + restoreSimplifiedGraph(orient); + + AnchorVertex *v1 = internalVertex(firstItem, firstEdge); + AnchorVertex *v2 = internalVertex(secondItem, secondEdge); + + QGraphicsAnchor *graphicsAnchor = 0; + + AnchorData *data = graph[orient].edgeData(v1, v2); + if (data) + graphicsAnchor = acquireGraphicsAnchor(data); + return graphicsAnchor; +} + +/*! + * \internal + * + * Implements the high level "removeAnchor" feature. Called by + * the QAnchorData destructor. + */ +void QGraphicsAnchorLayoutPrivate::removeAnchor(AnchorVertex *firstVertex, + AnchorVertex *secondVertex) +{ + Q_Q(QGraphicsAnchorLayout); + + // Actually delete the anchor + removeAnchor_helper(firstVertex, secondVertex); + + QGraphicsLayoutItem *firstItem = firstVertex->m_item; + QGraphicsLayoutItem *secondItem = secondVertex->m_item; + + // 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); + // Guarantee that the graph is no simplified when removing this anchor, + // anchor manipulation always happen in the full graph + Orientation o = edgeOrientation(v1->m_edge); + restoreSimplifiedGraph(o); + + // Remove edge from graph + 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); +} + +/*! + \internal + Only called from outside. (calls invalidate()) +*/ +void QGraphicsAnchorLayoutPrivate::setAnchorSize(AnchorData *data, const qreal *anchorSize) +{ + Q_Q(QGraphicsAnchorLayout); + // ### we can avoid restoration if we really want to, but we would have to + // search recursively through all composite anchors + Q_ASSERT(data); + restoreSimplifiedGraph(edgeOrientation(data->from->m_edge)); + + QGraphicsLayoutItem *firstItem = data->from->m_item; + QGraphicsLayoutItem *secondItem = data->to->m_item; + Qt::AnchorPoint firstEdge = data->from->m_edge; + Qt::AnchorPoint secondEdge = data->to->m_edge; + + // Use heuristics to find out what the user meant with this anchor. + correctEdgeDirection(firstItem, firstEdge, secondItem, secondEdge); + if (data->from->m_item != firstItem) + qSwap(data->from, data->to); + + if (anchorSize) { + // ### The current implementation makes "setAnchorSize" behavior + // dependent on the argument order for cases where we have + // no heuristic. Ie. two widgets, same anchor point. + + // We cannot have negative sizes inside the graph. This would cause + // the simplex solver to fail because all simplex variables are + // positive by definition. + // "negative spacing" is handled by inverting the standard item order. + if (*anchorSize >= 0) { + data->setFixedSize(*anchorSize); + } else { + data->setFixedSize(-*anchorSize); + qSwap(data->from, data->to); + } + } else { + data->unsetSize(); + } + q->invalidate(); +} + +void QGraphicsAnchorLayoutPrivate::anchorSize(const AnchorData *data, + qreal *minSize, + qreal *prefSize, + qreal *maxSize) const +{ + Q_ASSERT(minSize || prefSize || maxSize); + Q_ASSERT(data); + QGraphicsAnchorLayoutPrivate *that = const_cast<QGraphicsAnchorLayoutPrivate *>(this); + that->restoreSimplifiedGraph(edgeOrientation(data->from->m_edge)); + + if (minSize) + *minSize = data->minSize; + if (prefSize) + *prefSize = data->prefSize; + if (maxSize) + *maxSize = data->maxSize; +} + +AnchorVertex *QGraphicsAnchorLayoutPrivate::addInternalVertex(QGraphicsLayoutItem *item, + Qt::AnchorPoint edge) +{ + QPair<QGraphicsLayoutItem *, Qt::AnchorPoint> pair(item, edge); + QPair<AnchorVertex *, int> v = m_vertexList.value(pair); + + if (!v.first) { + Q_ASSERT(v.second == 0); + v.first = new AnchorVertex(item, edge); + } + v.second++; + m_vertexList.insert(pair, v); + return v.first; +} + +/** + * \internal + * + * returns the AnchorVertex that was dereferenced, also when it was removed. + * returns 0 if it did not exist. + */ +void QGraphicsAnchorLayoutPrivate::removeInternalVertex(QGraphicsLayoutItem *item, + Qt::AnchorPoint edge) +{ + QPair<QGraphicsLayoutItem *, Qt::AnchorPoint> pair(item, edge); + QPair<AnchorVertex *, int> v = m_vertexList.value(pair); + + if (!v.first) { + qWarning("This item with this edge is not in the graph"); + return; + } + + v.second--; + if (v.second == 0) { + // Remove reference and delete vertex + m_vertexList.remove(pair); + delete v.first; + } else { + // Update reference count + m_vertexList.insert(pair, v); + + if ((v.second == 2) && + ((edge == Qt::AnchorHorizontalCenter) || + (edge == Qt::AnchorVerticalCenter))) { + removeCenterAnchors(item, edge, true); + } + } +} + +void QGraphicsAnchorLayoutPrivate::removeVertex(QGraphicsLayoutItem *item, Qt::AnchorPoint edge) +{ + if (AnchorVertex *v = internalVertex(item, edge)) { + Graph<AnchorVertex, AnchorData> &g = graph[edgeOrientation(edge)]; + const QList<AnchorVertex *> allVertices = graph[edgeOrientation(edge)].adjacentVertices(v); + AnchorVertex *v2; + foreach (v2, allVertices) { + g.removeEdge(v, v2); + removeInternalVertex(item, edge); + removeInternalVertex(v2->m_item, v2->m_edge); + } + } +} + +void QGraphicsAnchorLayoutPrivate::removeAnchors(QGraphicsLayoutItem *item) +{ + Q_ASSERT(!graphSimplified[Horizontal] && !graphSimplified[Vertical]); + + // remove the center anchor first!! + removeCenterAnchors(item, Qt::AnchorHorizontalCenter, false); + removeVertex(item, Qt::AnchorLeft); + removeVertex(item, Qt::AnchorRight); + + removeCenterAnchors(item, Qt::AnchorVerticalCenter, false); + removeVertex(item, Qt::AnchorTop); + removeVertex(item, Qt::AnchorBottom); +} + +/*! + \internal + + Use heuristics to determine the correct orientation of a given anchor. + + After API discussions, we decided we would like expressions like + anchor(A, Left, B, Right) to mean the same as anchor(B, Right, A, Left). + The problem with this is that anchors could become ambiguous, for + instance, what does the anchor A, B of size X mean? + + "pos(B) = pos(A) + X" or "pos(A) = pos(B) + X" ? + + To keep the API user friendly and at the same time, keep our algorithm + deterministic, we use an heuristic to determine a direction for each + added anchor and then keep it. The heuristic is based on the fact + that people usually avoid overlapping items, therefore: + + "A, RIGHT to B, LEFT" means that B is to the LEFT of A. + "B, LEFT to A, RIGHT" is corrected to the above anchor. + + Special correction is also applied when one of the items is the + layout. We handle Layout Left as if it was another items's Right + and Layout Right as another item's Left. +*/ +void QGraphicsAnchorLayoutPrivate::correctEdgeDirection(QGraphicsLayoutItem *&firstItem, + Qt::AnchorPoint &firstEdge, + QGraphicsLayoutItem *&secondItem, + Qt::AnchorPoint &secondEdge) +{ + Q_Q(QGraphicsAnchorLayout); + + 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); + } +} + +qreal QGraphicsAnchorLayoutPrivate::effectiveSpacing(Orientation orientation) const +{ + Q_Q(const QGraphicsAnchorLayout); + qreal s = spacings[orientation]; + if (s < 0) { + // ### make sure behaviour is the same as in QGraphicsGridLayout + QGraphicsLayoutItem *parent = q->parentLayoutItem(); + while (parent && parent->isLayout()) { + parent = parent->parentLayoutItem(); + } + if (parent) { + QGraphicsItem *parentItem = parent->graphicsItem(); + if (parentItem && parentItem->isWidget()) { + QGraphicsWidget *w = static_cast<QGraphicsWidget*>(parentItem); + s = w->style()->pixelMetric(orientation == Horizontal + ? QStyle::PM_LayoutHorizontalSpacing + : QStyle::PM_LayoutVerticalSpacing); + } + } + } + return s; +} + +/*! + \internal + + Called on activation. Uses Linear Programming to define minimum, preferred + and maximum sizes for the layout. Also calculates the sizes that each item + should assume when the layout is in one of such situations. +*/ +void QGraphicsAnchorLayoutPrivate::calculateGraphs() +{ + if (!calculateGraphCacheDirty) + return; + + calculateGraphs(Horizontal); + calculateGraphs(Vertical); + + calculateGraphCacheDirty = 0; +} + +// ### remove me: +QList<AnchorData *> getVariables(QList<QSimplexConstraint *> constraints) +{ + QSet<AnchorData *> variableSet; + for (int i = 0; i < constraints.count(); ++i) { + const QSimplexConstraint *c = constraints[i]; + foreach (QSimplexVariable *var, c->variables.keys()) { + variableSet += static_cast<AnchorData *>(var); + } + } + return variableSet.toList(); +} + +/*! + \internal + + Calculate graphs is the method that puts together all the helper routines + so that the AnchorLayout can calculate the sizes of each item. + + In a nutshell it should do: + + 1) Update anchor nominal sizes, that is, the size that each anchor would + have if no other restrictions applied. This is done by quering the + layout style and the sizeHints of the items belonging to the layout. + + 2) Simplify the graph by grouping together parallel and sequential anchors + into "group anchors". These have equivalent minimum, preferred and maximum + sizeHints as the anchors they replace. + + 3) Check if we got to a trivial case. In some cases, the whole graph can be + simplified into a single anchor. If so, use this information. If not, + then call the Simplex solver to calculate the anchors sizes. + + 4) Once the root anchors had its sizes calculated, propagate that to the + anchors they represent. +*/ +void QGraphicsAnchorLayoutPrivate::calculateGraphs( + QGraphicsAnchorLayoutPrivate::Orientation orientation) +{ + Q_Q(QGraphicsAnchorLayout); + + // Simplify the graph + simplifyGraph(orientation); + + // Reset the nominal sizes of each anchor based on the current item sizes + setAnchorSizeHintsFromItems(orientation); + + // Traverse all graph edges and store the possible paths to each vertex + findPaths(orientation); + + // From the paths calculated above, extract the constraints that the current + // anchor setup impose, to our Linear Programming problem. + constraintsFromPaths(orientation); + + // Split the constraints and anchors into groups that should be fed to the + // simplex solver independently. Currently we find two groups: + // + // 1) The "trunk", that is, the set of anchors (items) that are connected + // to the two opposite sides of our layout, and thus need to stretch in + // order to fit in the current layout size. + // + // 2) The floating or semi-floating anchors (items) that are those which + // are connected to only one (or none) of the layout sides, thus are not + // influenced by the layout size. + QList<QList<QSimplexConstraint *> > parts; + parts = getGraphParts(orientation); + + // Now run the simplex solver to calculate Minimum, Preferred and Maximum sizes + // of the "trunk" set of constraints and variables. + // ### does trunk always exist? empty = trunk is the layout left->center->right + QList<QSimplexConstraint *> trunkConstraints = parts[0]; + QList<QSimplexConstraint *> sizeHintConstraints; + sizeHintConstraints = constraintsFromSizeHints(getVariables(trunkConstraints)); + trunkConstraints += sizeHintConstraints; + + // For minimum and maximum, use the path between the two layout sides as the + // objective function. + + // Retrieve that path + AnchorVertex *v = internalVertex(q, pickEdge(Qt::AnchorRight, orientation)); + GraphPath trunkPath = graphPaths[orientation].value(v); + + bool feasible = true; + if (!trunkConstraints.isEmpty()) { +#if 0 + qDebug("Simplex used for trunk of %s", + orientation == Horizontal ? "Horizontal" : "Vertical"); +#endif + + // Solve min and max size hints for trunk + qreal min, max; + feasible = solveMinMax(trunkConstraints, trunkPath, &min, &max); + + // Solve for preferred. The objective function is calculated from the constraints + // and variables internally. + feasible &= solvePreferred(trunkConstraints); + + if (feasible) { + // Propagate the new sizes down the simplified graph, ie. tell the + // group anchors to set their children anchors sizes. + + // ### we calculated variables already a few times, can't we reuse that? + QList<AnchorData *> trunkVariables = getVariables(trunkConstraints); + + for (int i = 0; i < trunkVariables.count(); ++i) + trunkVariables.at(i)->updateChildrenSizes(); + + // Calculate and set the preferred size for the layout from the edge sizes that + // were calculated above. + qreal pref(0.0); + foreach (const AnchorData *ad, trunkPath.positives) { + pref += ad->sizeAtPreferred; + } + foreach (const AnchorData *ad, trunkPath.negatives) { + pref -= ad->sizeAtPreferred; + } + sizeHints[orientation][Qt::MinimumSize] = min; + sizeHints[orientation][Qt::PreferredSize] = pref; + sizeHints[orientation][Qt::MaximumSize] = max; + + } + } else { +#if 0 + qDebug("Simplex NOT used for trunk of %s", + orientation == Horizontal ? "Horizontal" : "Vertical"); +#endif + + // No Simplex is necessary because the path was simplified all the way to a single + // anchor. + Q_ASSERT(trunkPath.positives.count() == 1); + Q_ASSERT(trunkPath.negatives.count() == 0); + + AnchorData *ad = trunkPath.positives.toList()[0]; + ad->sizeAtMinimum = ad->minSize; + ad->sizeAtPreferred = ad->prefSize; + ad->sizeAtMaximum = ad->maxSize; + + // Propagate + ad->updateChildrenSizes(); + + sizeHints[orientation][Qt::MinimumSize] = ad->sizeAtMinimum; + sizeHints[orientation][Qt::PreferredSize] = ad->sizeAtPreferred; + sizeHints[orientation][Qt::MaximumSize] = ad->sizeAtMaximum; + } + + // Delete the constraints, we won't use them anymore. + qDeleteAll(sizeHintConstraints); + sizeHintConstraints.clear(); + + // For the other parts that not the trunk, solve only for the preferred size + // that is the size they will remain at, since they are not stretched by the + // layout. + + // Solve the other only for preferred, skip trunk + if (feasible) { + for (int i = 1; i < parts.count(); ++i) { + QList<QSimplexConstraint *> partConstraints = parts[i]; + QList<AnchorData *> partVariables = getVariables(partConstraints); + Q_ASSERT(!partVariables.isEmpty()); + + sizeHintConstraints = constraintsFromSizeHints(partVariables); + partConstraints += sizeHintConstraints; + feasible &= solvePreferred(partConstraints); + if (!feasible) + break; + + // Propagate size at preferred to other sizes. Semi-floats + // always will be in their sizeAtPreferred. + for (int j = 0; j < partVariables.count(); ++j) { + AnchorData *ad = partVariables[j]; + Q_ASSERT(ad); + ad->sizeAtMinimum = ad->sizeAtPreferred; + ad->sizeAtMaximum = ad->sizeAtPreferred; + ad->updateChildrenSizes(); + } + + // Delete the constraints, we won't use them anymore. + qDeleteAll(sizeHintConstraints); + sizeHintConstraints.clear(); + } + } + 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(); // ### +} + +/*! + \internal + + For graph edges ("anchors") that represent items, this method updates their + intrinsic size restrictions, based on the item size hints. +*/ +void QGraphicsAnchorLayoutPrivate::setAnchorSizeHintsFromItems(Orientation orientation) +{ + Graph<AnchorVertex, AnchorData> &g = graph[orientation]; + QList<QPair<AnchorVertex *, AnchorVertex *> > vertices = g.connections(); + + qreal spacing = effectiveSpacing(orientation); + + for (int i = 0; i < vertices.count(); ++i) { + AnchorData *data = g.edgeData(vertices.at(i).first, vertices.at(i).second);; + Q_ASSERT(data->from && data->to); + data->refreshSizeHints(spacing); + } +} + +/*! + \internal + + This method walks the graph using a breadth-first search to find paths + between the root vertex and each vertex on the graph. The edges + directions in each path are considered and they are stored as a + positive edge (left-to-right) or negative edge (right-to-left). + + The list of paths is used later to generate a list of constraints. + */ +void QGraphicsAnchorLayoutPrivate::findPaths(Orientation orientation) +{ + QQueue<QPair<AnchorVertex *, AnchorVertex *> > queue; + + QSet<AnchorData *> visited; + + AnchorVertex *root = graph[orientation].rootVertex(); + + graphPaths[orientation].insert(root, GraphPath()); + + foreach (AnchorVertex *v, graph[orientation].adjacentVertices(root)) { + queue.enqueue(qMakePair(root, v)); + } + + while(!queue.isEmpty()) { + QPair<AnchorVertex *, AnchorVertex *> pair = queue.dequeue(); + AnchorData *edge = graph[orientation].edgeData(pair.first, pair.second); + + if (visited.contains(edge)) + continue; + + visited.insert(edge); + GraphPath current = graphPaths[orientation].value(pair.first); + + if (edge->from == pair.first) + current.positives.insert(edge); + else + current.negatives.insert(edge); + + graphPaths[orientation].insert(pair.second, current); + + foreach (AnchorVertex *v, + graph[orientation].adjacentVertices(pair.second)) { + queue.enqueue(qMakePair(pair.second, v)); + } + } +} + +/*! + \internal + + Each vertex on the graph that has more than one path to it + represents a contra int to the sizes of the items in these paths. + + This method walks the list of paths to each vertex, generate + the constraints and store them in a list so they can be used later + by the Simplex solver. +*/ +void QGraphicsAnchorLayoutPrivate::constraintsFromPaths(Orientation orientation) +{ + foreach (AnchorVertex *vertex, graphPaths[orientation].uniqueKeys()) + { + int valueCount = graphPaths[orientation].count(vertex); + if (valueCount == 1) + continue; + + QList<GraphPath> pathsToVertex = graphPaths[orientation].values(vertex); + for (int i = 1; i < valueCount; ++i) { + constraints[orientation] += \ + pathsToVertex[0].constraint(pathsToVertex[i]); + } + } +} + +/*! + \internal + + Create LP constraints for each anchor based on its minimum and maximum + sizes, as specified in its size hints +*/ +QList<QSimplexConstraint *> QGraphicsAnchorLayoutPrivate::constraintsFromSizeHints( + const QList<AnchorData *> &anchors) +{ + QList<QSimplexConstraint *> anchorConstraints; + for (int i = 0; i < anchors.size(); ++i) { + QSimplexConstraint *c = new QSimplexConstraint; + c->variables.insert(anchors[i], 1.0); + c->constant = anchors[i]->minSize; + c->ratio = QSimplexConstraint::MoreOrEqual; + anchorConstraints += c; + + c = new QSimplexConstraint; + c->variables.insert(anchors[i], 1.0); + c->constant = anchors[i]->maxSize; + c->ratio = QSimplexConstraint::LessOrEqual; + anchorConstraints += c; + } + + return anchorConstraints; +} + +/*! + \internal +*/ +QList< QList<QSimplexConstraint *> > +QGraphicsAnchorLayoutPrivate::getGraphParts(Orientation orientation) +{ + Q_Q(QGraphicsAnchorLayout); + + // Find layout vertices and edges for the current orientation. + AnchorVertex *layoutFirstVertex = \ + internalVertex(q, pickEdge(Qt::AnchorLeft, orientation)); + + AnchorVertex *layoutCentralVertex = \ + internalVertex(q, pickEdge(Qt::AnchorHorizontalCenter, orientation)); + + AnchorVertex *layoutLastVertex = \ + internalVertex(q, pickEdge(Qt::AnchorRight, orientation)); + + Q_ASSERT(layoutFirstVertex && layoutLastVertex); + + AnchorData *edgeL1 = NULL; + AnchorData *edgeL2 = NULL; + + // The layout may have a single anchor between Left and Right or two half anchors + // passing through the center + if (layoutCentralVertex) { + edgeL1 = graph[orientation].edgeData(layoutFirstVertex, layoutCentralVertex); + edgeL2 = graph[orientation].edgeData(layoutCentralVertex, layoutLastVertex); + } else { + edgeL1 = graph[orientation].edgeData(layoutFirstVertex, layoutLastVertex); + } + + QLinkedList<QSimplexConstraint *> remainingConstraints; + for (int i = 0; i < constraints[orientation].count(); ++i) { + remainingConstraints += constraints[orientation][i]; + } + for (int i = 0; i < itemCenterConstraints[orientation].count(); ++i) { + remainingConstraints += itemCenterConstraints[orientation][i]; + } + + QList<QSimplexConstraint *> trunkConstraints; + QSet<QSimplexVariable *> trunkVariables; + + trunkVariables += edgeL1; + if (edgeL2) + trunkVariables += edgeL2; + + bool dirty; + do { + dirty = false; + + QLinkedList<QSimplexConstraint *>::iterator it = remainingConstraints.begin(); + while (it != remainingConstraints.end()) { + QSimplexConstraint *c = *it; + bool match = false; + + // Check if this constraint have some overlap with current + // trunk variables... + foreach (QSimplexVariable *ad, trunkVariables) { + if (c->variables.contains(ad)) { + match = true; + break; + } + } + + // If so, we add it to trunk, and erase it from the + // remaining constraints. + if (match) { + trunkConstraints += c; + trunkVariables += QSet<QSimplexVariable *>::fromList(c->variables.keys()); + it = remainingConstraints.erase(it); + dirty = true; + } else { + // Note that we don't erase the constraint if it's not + // a match, since in a next iteration of a do-while we + // can pass on it again and it will be a match. + // + // For example: if trunk share a variable with + // remainingConstraints[1] and it shares with + // remainingConstraints[0], we need a second iteration + // of the do-while loop to match both. + ++it; + } + } + } while (dirty); + + QList< QList<QSimplexConstraint *> > result; + result += trunkConstraints; + + if (!remainingConstraints.isEmpty()) { + QList<QSimplexConstraint *> nonTrunkConstraints; + QLinkedList<QSimplexConstraint *>::iterator it = remainingConstraints.begin(); + while (it != remainingConstraints.end()) { + nonTrunkConstraints += *it; + ++it; + } + result += nonTrunkConstraints; + } + + return result; +} + +/*! + \internal + + Use the current vertices distance to calculate and set the geometry of + each item. +*/ +void QGraphicsAnchorLayoutPrivate::setItemsGeometries(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) { + firstH = internalVertex(item, Qt::AnchorLeft); + secondH = internalVertex(item, Qt::AnchorRight); + firstV = internalVertex(item, Qt::AnchorTop); + secondV = internalVertex(item, Qt::AnchorBottom); + + QRectF newGeom; + newGeom.setTop(top + firstV->distance); + newGeom.setBottom(top + secondV->distance); + + 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); + } + 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 = graph[orientation].rootVertex(); + + 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, we need to + // visit all pairs since each of them could have a sequential + // anchor inside, which hides more vertices. + while (!queue.isEmpty()) { + QPair<AnchorVertex *, AnchorVertex *> pair = queue.dequeue(); + AnchorData *edge = graph[orientation].edgeData(pair.first, pair.second); + + // Both vertices were interpolated, and the anchor itself can't have other + // anchors inside (it's not a complex anchor). + if (edge->type == AnchorData::Normal && visited.contains(pair.second)) + continue; + + visited.insert(pair.second); + interpolateEdge(pair.first, edge, orientation); + + QList<AnchorVertex *> adjacents = graph[orientation].adjacentVertices(pair.second); + for (int i = 0; i < adjacents.count(); ++i) { + if (!visited.contains(adjacents.at(i))) + queue.enqueue(qMakePair(pair.second, adjacents.at(i))); + } + } +} + +/*! + \internal + + Calculate interpolation parameters based on current Layout Size. + Must once before calling "interpolateEdgeSize()" for each edge. +*/ +void QGraphicsAnchorLayoutPrivate::setupEdgesInterpolation( + Orientation orientation) +{ + Q_Q(QGraphicsAnchorLayout); + qreal lower, upper, current; + + if (orientation == Horizontal) { + current = q->contentsRect().width(); + } else { + current = q->contentsRect().height(); + } + + if (current < sizeHints[orientation][Qt::PreferredSize]) { + interpolationInterval[orientation] = MinToPreferred; + lower = sizeHints[orientation][Qt::MinimumSize]; + upper = sizeHints[orientation][Qt::PreferredSize]; + } else { + interpolationInterval[orientation] = PreferredToMax; + lower = sizeHints[orientation][Qt::PreferredSize]; + upper = sizeHints[orientation][Qt::MaximumSize]; + } + + if (upper == lower) { + interpolationProgress[orientation] = 0; + } else { + interpolationProgress[orientation] = (current - lower) / (upper - lower); + } +} + +/*! + \internal + + Calculate the current Edge size based on the current Layout size and the + size the edge is supposed to have when: + + - the layout is at its minimum size. + - the layout is at its preferred size. + - the layout is at its maximum size. + + These three key values are calculated in advance using linear + programming (more expensive) or the simplification algorithm, then + subsequential resizes of the parent layout require a simple + interpolation. + + If the edge is sequential or parallel, it's possible to have more + vertices to be initalized, so it calls specialized functions that + will recurse back to interpolateEdge(). + */ +void QGraphicsAnchorLayoutPrivate::interpolateEdge(AnchorVertex *base, + AnchorData *edge, + Orientation orientation) +{ + qreal lower, upper; + + if (interpolationInterval[orientation] == MinToPreferred) { + lower = edge->sizeAtMinimum; + upper = edge->sizeAtPreferred; + } else { + lower = edge->sizeAtPreferred; + upper = edge->sizeAtMaximum; + } + + qreal edgeDistance = (interpolationProgress[orientation] * (upper - lower)) + lower; + + Q_ASSERT(edge->from == base || edge->to == base); + + if (edge->from == base) + edge->to->distance = base->distance + edgeDistance; + else + edge->from->distance = base->distance - edgeDistance; + + // Process child anchors + if (edge->type == AnchorData::Sequential) + interpolateSequentialEdges(edge->from, + static_cast<SequentialAnchorData *>(edge), + orientation); + else if (edge->type == AnchorData::Parallel) + interpolateParallelEdges(edge->from, + static_cast<ParallelAnchorData *>(edge), + orientation); +} + +void QGraphicsAnchorLayoutPrivate::interpolateParallelEdges( + AnchorVertex *base, ParallelAnchorData *data, Orientation orientation) +{ + // In parallels the boundary vertices are already calculate, we + // just need to look for sequential groups inside, because only + // them may have new vertices associated. + + // First edge + if (data->firstEdge->type == AnchorData::Sequential) + interpolateSequentialEdges(base, + static_cast<SequentialAnchorData *>(data->firstEdge), + orientation); + else if (data->firstEdge->type == AnchorData::Parallel) + interpolateParallelEdges(base, + static_cast<ParallelAnchorData *>(data->firstEdge), + orientation); + + // Second edge + if (data->secondEdge->type == AnchorData::Sequential) + interpolateSequentialEdges(base, + static_cast<SequentialAnchorData *>(data->secondEdge), + orientation); + else if (data->secondEdge->type == AnchorData::Parallel) + interpolateParallelEdges(base, + static_cast<ParallelAnchorData *>(data->secondEdge), + orientation); +} + +void QGraphicsAnchorLayoutPrivate::interpolateSequentialEdges( + AnchorVertex *base, SequentialAnchorData *data, Orientation orientation) +{ + AnchorVertex *prev = base; + + // ### I'm not sure whether this assumption is safe. If not, + // consider that m_edges.last() could be used instead (so + // at(0) would be the one to be treated specially). + Q_ASSERT(base == data->m_edges.at(0)->to || base == data->m_edges.at(0)->from); + + // Skip the last + for (int i = 0; i < data->m_edges.count() - 1; ++i) { + AnchorData *child = data->m_edges.at(i); + interpolateEdge(prev, child, orientation); + prev = child->to; + } + + // Treat the last specially, since we already calculated it's end + // vertex, so it's only interesting if it's a complex one + if (data->m_edges.last()->type != AnchorData::Normal) + interpolateEdge(prev, data->m_edges.last(), orientation); +} + +bool QGraphicsAnchorLayoutPrivate::solveMinMax(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); + + simplex.setObjective(&objective); + + // Calculate minimum values + *min = simplex.solveMin(); + + // Save sizeAtMinimum results + QList<QSimplexVariable *> variables = simplex.constraintsVariables(); + for (int i = 0; i < variables.size(); ++i) { + AnchorData *ad = static_cast<AnchorData *>(variables[i]); + Q_ASSERT(ad->result >= ad->minSize || qFuzzyCompare(ad->result, ad->minSize)); + ad->sizeAtMinimum = ad->result; + } + + // Calculate maximum values + *max = simplex.solveMax(); + + // Save sizeAtMaximum results + for (int i = 0; i < variables.size(); ++i) { + AnchorData *ad = static_cast<AnchorData *>(variables[i]); + Q_ASSERT(ad->result <= ad->maxSize || qFuzzyCompare(ad->result, ad->maxSize)); + ad->sizeAtMaximum = ad->result; + } + } + return feasible; +} + +bool QGraphicsAnchorLayoutPrivate::solvePreferred(QList<QSimplexConstraint *> constraints) +{ + QList<AnchorData *> variables = getVariables(constraints); + QList<QSimplexConstraint *> preferredConstraints; + QList<QSimplexVariable *> preferredVariables; + QSimplexConstraint objective; + + // Fill the objective coefficients for this variable. In the + // end the objective function will be + // + // z = n * (A_shrink + B_shrink + ...) + (A_grower + B_grower + ...) + // + // where n is the number of variables that have + // slacks. Note that here we use the number of variables + // as coefficient, this is to mark the "shrinker slack + // variable" less likely to get value than the "grower + // slack variable". + + // This will fill the values for the structural constraints + // and we now fill the values for the slack constraints (one per variable), + // which have this form (the constant A_pref was set when creating the slacks): + // + // A + A_shrinker - A_grower = A_pref + // + for (int i = 0; i < variables.size(); ++i) { + AnchorData *ad = static_cast<AnchorData *>(variables[i]); + if (ad->skipInPreferred) + continue; + + QSimplexVariable *grower = new QSimplexVariable; + QSimplexVariable *shrinker = new QSimplexVariable; + QSimplexConstraint *c = new QSimplexConstraint; + c->variables.insert(ad, 1.0); + c->variables.insert(shrinker, 1.0); + c->variables.insert(grower, -1.0); + c->constant = ad->prefSize; + + preferredConstraints += c; + preferredVariables += grower; + preferredVariables += shrinker; + + objective.variables.insert(grower, 1.0); + objective.variables.insert(shrinker, variables.size()); + } + + + QSimplex *simplex = new QSimplex; + 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 = static_cast<AnchorData *>(variables[i]); + ad->sizeAtPreferred = ad->result; + } + + // Make sure we delete the simplex solver -before- we delete the + // constraints used by it. + delete simplex; + } + // Delete constraints and variables we created. + qDeleteAll(preferredConstraints); + qDeleteAll(preferredVariables); + + return feasible; +} + +bool QGraphicsAnchorLayoutPrivate::hasConflicts() const +{ + QGraphicsAnchorLayoutPrivate *that = const_cast<QGraphicsAnchorLayoutPrivate*>(this); + that->calculateGraphs(); + return graphHasConflicts[0] || graphHasConflicts[1]; +} + +#ifdef QT_DEBUG +void QGraphicsAnchorLayoutPrivate::dumpGraph() +{ + QFile file(QString::fromAscii("anchorlayout.dot")); + if (!file.open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate)) + qWarning("Could not write to %s", file.fileName().toLocal8Bit().constData()); + + QString str = QString::fromAscii("digraph anchorlayout {\nnode [shape=\"rect\"]\n%1}"); + QString dotContents = graph[0].serializeToDot(); + dotContents += graph[1].serializeToDot(); + file.write(str.arg(dotContents).toLocal8Bit()); + + file.close(); +} +#endif + +QT_END_NAMESPACE diff --git a/src/gui/graphicsview/qgraphicsanchorlayout_p.h b/src/gui/graphicsview/qgraphicsanchorlayout_p.h new file mode 100644 index 0000000..4e1bcd4 --- /dev/null +++ b/src/gui/graphicsview/qgraphicsanchorlayout_p.h @@ -0,0 +1,525 @@ +/**************************************************************************** +** +** Copyright (C) 2009 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" + +QT_BEGIN_NAMESPACE + +/* + The public QGraphicsAnchorLayout interface represents an anchorage point + as a pair of a <QGraphicsLayoutItem *> and a <Qt::AnchorPoint>. + + Internally though, it has a graph of anchorage points (vertices) and + anchors (edges), represented by the AnchorVertex and AnchorData structs + respectively. +*/ + +/*! + \internal + + Represents a vertex (anchorage point) in the internal graph +*/ +struct AnchorVertex { + AnchorVertex(QGraphicsLayoutItem *item, Qt::AnchorPoint edge) + : m_item(item), m_edge(edge) {} + + AnchorVertex() + : m_item(0), m_edge(Qt::AnchorPoint(0)) {} + +#ifdef QT_DEBUG + inline QString toString() const; +#endif + QGraphicsLayoutItem *m_item; + Qt::AnchorPoint m_edge; + + // Current distance from this vertex to the layout edge (Left or Top) + // Value is calculated from the current anchors sizes. + qreal distance; +}; + +#ifdef QT_DEBUG +inline QString AnchorVertex::toString() const +{ + if (!this || !m_item) { + return QLatin1String("NULL"); + } + QString edge; + switch (m_edge) { + case Qt::AnchorLeft: + edge = QLatin1String("Left"); + break; + case Qt::AnchorHorizontalCenter: + edge = QLatin1String("HorizontalCenter"); + break; + case Qt::AnchorRight: + edge = QLatin1String("Right"); + break; + case Qt::AnchorTop: + edge = QLatin1String("Top"); + break; + case Qt::AnchorVerticalCenter: + edge = QLatin1String("VerticalCenter"); + break; + case Qt::AnchorBottom: + edge = QLatin1String("Bottom"); + break; + default: + edge = QLatin1String("None"); + break; + } + QString itemName; + if (m_item->isLayout()) { + itemName = QLatin1String("layout"); + } else { + if (QGraphicsItem *item = m_item->graphicsItem()) { + itemName = item->data(0).toString(); + } + } + edge.insert(0, QLatin1String("%1_")); + return edge.arg(itemName); +} +#endif + +/*! + \internal + + Represents an edge (anchor) in the internal graph. +*/ +struct AnchorData : public QSimplexVariable { + enum Type { + Normal = 0, + Sequential, + Parallel + }; + AnchorData(qreal minimumSize, qreal preferredSize, qreal maximumSize) + : QSimplexVariable(), from(0), to(0), + minSize(minimumSize), prefSize(preferredSize), + maxSize(maximumSize), sizeAtMinimum(preferredSize), + sizeAtPreferred(preferredSize), sizeAtMaximum(preferredSize), + graphicsAnchor(0), + skipInPreferred(0), type(Normal), hasSize(true), + isLayoutAnchor(false) {} + + AnchorData(qreal size) + : QSimplexVariable(), from(0), to(0), + minSize(size), prefSize(size), maxSize(size), + sizeAtMinimum(size), sizeAtPreferred(size), sizeAtMaximum(size), + graphicsAnchor(0), + skipInPreferred(0), type(Normal), hasSize(true), + isLayoutAnchor(false) {} + + AnchorData() + : QSimplexVariable(), from(0), to(0), + minSize(0), prefSize(0), maxSize(0), + sizeAtMinimum(0), sizeAtPreferred(0), sizeAtMaximum(0), + graphicsAnchor(0), + skipInPreferred(0), type(Normal), hasSize(false), + isLayoutAnchor(false) {} + + virtual void updateChildrenSizes() {} + virtual void refreshSizeHints(qreal effectiveSpacing); + + virtual ~AnchorData() {} + +#ifdef QT_DEBUG + void dump(int indent = 2); + inline QString toString() const; + QString name; +#endif + + inline void setFixedSize(qreal size) + { + minSize = size; + prefSize = size; + maxSize = size; + sizeAtMinimum = size; + sizeAtPreferred = size; + sizeAtMaximum = size; + hasSize = true; + } + + inline void unsetSize() + { + hasSize = false; + } + + // Anchor is semantically directed + AnchorVertex *from; + AnchorVertex *to; + + // Size restrictions of this edge. For anchors internal to items, these + // values are derived from the respective item size hints. For anchors + // that were added by users, these values are equal to the specified anchor + // size. + qreal minSize; + qreal prefSize; + qreal maxSize; + + // These attributes define which sizes should that anchor be in when the + // layout is at its minimum, preferred or maximum sizes. Values are + // calculated by the Simplex solver based on the current layout setup. + qreal sizeAtMinimum; + qreal sizeAtPreferred; + qreal sizeAtMaximum; + QGraphicsAnchor *graphicsAnchor; + + uint skipInPreferred : 1; + uint type : 2; // either Normal, Sequential or Parallel + uint hasSize : 1; // if false, get size from style. + uint isLayoutAnchor : 1; // if this anchor is connected to a layout 'edge' +protected: + AnchorData(Type type, qreal size = 0) + : QSimplexVariable(), from(0), to(0), + minSize(size), prefSize(size), + maxSize(size), sizeAtMinimum(size), + sizeAtPreferred(size), sizeAtMaximum(size), + graphicsAnchor(0), + skipInPreferred(0), type(type), hasSize(true), + isLayoutAnchor(false) {} +}; + +#ifdef QT_DEBUG +inline QString AnchorData::toString() const +{ + return QString::fromAscii("Anchor(%1)").arg(name); +} +#endif + +struct SequentialAnchorData : public AnchorData +{ + SequentialAnchorData() : AnchorData(AnchorData::Sequential) + { +#ifdef QT_DEBUG + name = QLatin1String("SequentialAnchorData"); +#endif + } + + virtual void updateChildrenSizes(); + virtual void refreshSizeHints(qreal effectiveSpacing); + + void setVertices(const QVector<AnchorVertex*> &vertices) + { + m_children = vertices; +#ifdef QT_DEBUG + name = QString::fromAscii("%1 -- %2").arg(vertices.first()->toString(), vertices.last()->toString()); +#endif + } + + QVector<AnchorVertex*> m_children; // list of vertices in the sequence + QVector<AnchorData*> m_edges; // keep the list of edges too. +}; + +struct ParallelAnchorData : public AnchorData +{ + ParallelAnchorData(AnchorData *first, AnchorData *second) + : AnchorData(AnchorData::Parallel), + firstEdge(first), secondEdge(second) + { + // ### Those asserts force that both child anchors have the same direction, + // but can't we simplify a pair of anchors in opposite directions? + Q_ASSERT(first->from == second->from); + Q_ASSERT(first->to == second->to); + from = first->from; + to = first->to; +#ifdef QT_DEBUG + name = QString::fromAscii("%1 | %2").arg(first->toString(), second->toString()); +#endif + } + + virtual void updateChildrenSizes(); + virtual void refreshSizeHints(qreal effectiveSpacing); + + AnchorData* firstEdge; + AnchorData* secondEdge; +}; + +/*! + \internal + + Representation of a valid path for a given vertex in the graph. + In this struct, "positives" is the set of anchors that have been + traversed in the forward direction, while "negatives" is the set + with the ones walked backwards. + + This paths are compared against each other to produce LP Constraints, + the exact order in which the anchors were traversed is not relevant. +*/ +class GraphPath +{ +public: + GraphPath() {} + + QSimplexConstraint *constraint(const GraphPath &path) const; +#ifdef QT_DEBUG + QString toString() const; +#endif + QSet<AnchorData *> positives; + QSet<AnchorData *> negatives; +}; + +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; + + QGraphicsAnchorLayoutPrivate *layoutPrivate; + AnchorData *data; +}; + + + + +/*! + \internal + + QGraphicsAnchorLayout private methods and attributes. +*/ +class QGraphicsAnchorLayoutPrivate : public QGraphicsLayoutPrivate +{ + Q_DECLARE_PUBLIC(QGraphicsAnchorLayout) + +public: + // When the layout geometry is different from its Minimum, Preferred + // or Maximum values, interpolation is used to calculate the geometries + // of the items. + // + // Interval represents which interpolation interval are we operating in. + enum Interval { + MinToPreferred = 0, + PreferredToMax + }; + + // Several structures internal to the layout are duplicated to handle + // both Horizontal and Vertical restrictions. + // + // Orientation is used to reference the right structure in each context + enum Orientation { + Horizontal = 0, + Vertical, + NOrientations + }; + + QGraphicsAnchorLayoutPrivate(); + + static Qt::AnchorPoint oppositeEdge( + Qt::AnchorPoint edge); + + static Orientation edgeOrientation(Qt::AnchorPoint edge); + + static Qt::AnchorPoint pickEdge(Qt::AnchorPoint edge, Orientation orientation) + { + if (orientation == Vertical && int(edge) <= 2) + return (Qt::AnchorPoint)(edge + 3); + else if (orientation == Horizontal && int(edge) >= 3) { + return (Qt::AnchorPoint)(edge - 3); + } + return edge; + } + + // Init methods + void createLayoutEdges(); + void deleteLayoutEdges(); + void createItemEdges(QGraphicsLayoutItem *item); + void createCenterAnchors(QGraphicsLayoutItem *item, Qt::AnchorPoint centerEdge); + void removeCenterAnchors(QGraphicsLayoutItem *item, Qt::AnchorPoint centerEdge, bool substitute = true); + void removeCenterConstraints(QGraphicsLayoutItem *item, Orientation orientation); + + 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 setAnchorSize(AnchorData *data, const qreal *anchorSize); + void anchorSize(const AnchorData *data, + qreal *minSize = 0, + qreal *prefSize = 0, + qreal *maxSize = 0) const; + + void removeAnchors(QGraphicsLayoutItem *item); + + void removeVertex(QGraphicsLayoutItem *item, Qt::AnchorPoint edge); + + void correctEdgeDirection(QGraphicsLayoutItem *&firstItem, + Qt::AnchorPoint &firstEdge, + QGraphicsLayoutItem *&secondItem, + Qt::AnchorPoint &secondEdge); + // for getting the actual spacing (will query the style if the + // spacing is not explicitly set). + qreal effectiveSpacing(Orientation orientation) const; + + // Activation methods + void simplifyGraph(Orientation orientation); + bool simplifyGraphIteration(Orientation orientation); + void restoreSimplifiedGraph(Orientation orientation); + + void calculateGraphs(); + void calculateGraphs(Orientation orientation); + void setAnchorSizeHintsFromItems(Orientation orientation); + void findPaths(Orientation orientation); + void constraintsFromPaths(Orientation orientation); + QList<QSimplexConstraint *> constraintsFromSizeHints(const QList<AnchorData *> &anchors); + QList<QList<QSimplexConstraint *> > getGraphParts(Orientation orientation); + + inline AnchorVertex *internalVertex(const QPair<QGraphicsLayoutItem*, Qt::AnchorPoint> &itemEdge) const + { + return m_vertexList.value(itemEdge).first; + } + + inline AnchorVertex *internalVertex(const QGraphicsLayoutItem *item, Qt::AnchorPoint edge) const + { + return internalVertex(qMakePair(const_cast<QGraphicsLayoutItem *>(item), edge)); + } + + AnchorVertex *addInternalVertex(QGraphicsLayoutItem *item, Qt::AnchorPoint edge); + void removeInternalVertex(QGraphicsLayoutItem *item, Qt::AnchorPoint edge); + + // Geometry interpolation methods + void setItemsGeometries(const QRectF &geom); + + void calculateVertexPositions(Orientation orientation); + void setupEdgesInterpolation(Orientation orientation); + void interpolateEdge(AnchorVertex *base, AnchorData *edge, Orientation orientation); + void interpolateSequentialEdges(AnchorVertex *base, SequentialAnchorData *edge, + Orientation orientation); + void interpolateParallelEdges(AnchorVertex *base, ParallelAnchorData *edge, + Orientation orientation); + + // Linear Programming solver methods + bool solveMinMax(QList<QSimplexConstraint *> constraints, + GraphPath path, qreal *min, qreal *max); + bool solvePreferred(QList<QSimplexConstraint *> constraints); + bool hasConflicts() const; + +#ifdef QT_DEBUG + void dumpGraph(); +#endif + + + qreal spacings[NOrientations]; + // Size hints from simplex engine + qreal sizeHints[2][3]; + + // Items + QVector<QGraphicsLayoutItem *> items; + + // Mapping between high level anchorage points (Item, Edge) to low level + // ones (Graph Vertices) + + QHash<QPair<QGraphicsLayoutItem*, Qt::AnchorPoint>, QPair<AnchorVertex *, int> > m_vertexList; + + // Internal graph of anchorage points and anchors, for both orientations + Graph<AnchorVertex, AnchorData> graph[2]; + + // Graph paths and constraints, for both orientations + QMultiHash<AnchorVertex *, GraphPath> graphPaths[2]; + QList<QSimplexConstraint *> constraints[2]; + QList<QSimplexConstraint *> itemCenterConstraints[2]; + + // The interpolation interval and progress based on the current size + // as well as the key values (minimum, preferred and maximum) + Interval interpolationInterval[2]; + qreal interpolationProgress[2]; + + // ### + bool graphSimplified[2]; + bool graphHasConflicts[2]; + + uint calculateGraphCacheDirty : 1; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/gui/graphicsview/qgraphicsgridlayout.cpp b/src/gui/graphicsview/qgraphicsgridlayout.cpp index 0fb284f..d1d91db 100644 --- a/src/gui/graphicsview/qgraphicsgridlayout.cpp +++ b/src/gui/graphicsview/qgraphicsgridlayout.cpp @@ -1,6 +1,7 @@ /**************************************************************************** ** ** Copyright (C) 2009 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. @@ -9,8 +10,8 @@ ** No Commercial Usage ** This file contains pre-release code and may not be distributed. ** You may use this file in accordance with the terms and conditions -** contained in the either Technology Preview License Agreement or the -** Beta Release License Agreement. +** 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 @@ -20,21 +21,20 @@ ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** -** In addition, as a special exception, Nokia gives you certain -** additional rights. These rights are described in the Nokia Qt LGPL -** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this -** package. +** 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. +** +** +** +** +** +** ** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. ** -** If you are unsure which license is appropriate for your use, please -** contact the sales department at http://qt.nokia.com/contact. ** $QT_END_LICENSE$ ** ****************************************************************************/ diff --git a/src/gui/graphicsview/qgraphicsgridlayout.h b/src/gui/graphicsview/qgraphicsgridlayout.h index 9551e87..978a6ba 100644 --- a/src/gui/graphicsview/qgraphicsgridlayout.h +++ b/src/gui/graphicsview/qgraphicsgridlayout.h @@ -1,6 +1,7 @@ /**************************************************************************** ** ** Copyright (C) 2009 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. @@ -9,8 +10,8 @@ ** No Commercial Usage ** This file contains pre-release code and may not be distributed. ** You may use this file in accordance with the terms and conditions -** contained in the either Technology Preview License Agreement or the -** Beta Release License Agreement. +** 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 @@ -20,21 +21,20 @@ ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** -** In addition, as a special exception, Nokia gives you certain -** additional rights. These rights are described in the Nokia Qt LGPL -** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this -** package. +** 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. +** +** +** +** +** +** ** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. ** -** If you are unsure which license is appropriate for your use, please -** contact the sales department at http://qt.nokia.com/contact. ** $QT_END_LICENSE$ ** ****************************************************************************/ diff --git a/src/gui/graphicsview/qgraphicsitem.cpp b/src/gui/graphicsview/qgraphicsitem.cpp index 4f64c2a..3249bb1 100644 --- a/src/gui/graphicsview/qgraphicsitem.cpp +++ b/src/gui/graphicsview/qgraphicsitem.cpp @@ -1,6 +1,7 @@ /**************************************************************************** ** ** Copyright (C) 2009 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. @@ -9,8 +10,8 @@ ** No Commercial Usage ** This file contains pre-release code and may not be distributed. ** You may use this file in accordance with the terms and conditions -** contained in the either Technology Preview License Agreement or the -** Beta Release License Agreement. +** 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 @@ -20,21 +21,20 @@ ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** -** In addition, as a special exception, Nokia gives you certain -** additional rights. These rights are described in the Nokia Qt LGPL -** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this -** package. +** 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. +** +** +** +** +** +** ** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. ** -** If you are unsure which license is appropriate for your use, please -** contact the sales department at http://qt.nokia.com/contact. ** $QT_END_LICENSE$ ** ****************************************************************************/ @@ -198,20 +198,19 @@ \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 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 functionaly is separate from 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(). + 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 @@ -304,13 +303,17 @@ drop shadow effects and for decoration objects that follow the parent item's geometry without drawing on top of it. - \value ItemUsesExtendedStyleOption The item makes use of either the - exposedRect or matrix member of the QStyleOptionGraphicsItem. Implementers - of QGraphicsItem subclasses should set that flag if this data is required. - By default, the exposedRect is initialized to the item's boundingRect and - the matrix is untransformed. Enable this flag for more fine-grained values. - Use QStyleOptionGraphicsItem::levelOfDetailFromTransform() for a more - fine-grained value. + \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. \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 @@ -332,9 +335,14 @@ it's parent if it's z-value is negative. This flag enables setZValue() to toggle ItemStacksBehindParent. - \value ItemAutoDetectsFocusProxy The item will assign any child that - gains input focus as its focus proxy. See also focusProxy(). - 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 Internal only (for now). */ /*! @@ -407,11 +415,11 @@ (same as transform()), and QGraphicsItem ignores the return value for this notification (i.e., a read-only notification). - \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 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 @@ -582,6 +590,7 @@ #include <QtGui/qstyleoption.h> #include <QtGui/qevent.h> #include <QtGui/qinputcontext.h> +#include <QtGui/qgraphicseffect.h> #include <private/qgraphicsitem_p.h> #include <private/qgraphicswidget_p.h> @@ -922,12 +931,9 @@ void QGraphicsItemPrivate::setParentItemHelper(QGraphicsItem *newParent) scene->d_func()->index->itemChange(q, QGraphicsItem::ItemParentChange, newParentVariant); } - QGraphicsItem *lastSubFocusItem = subFocusItem; - if (subFocusItem) { - // Update the child focus chain; when reparenting an item that has a - // focus child, ensure that that focus child clears its focus child - // chain from our parents before it's reparented. - subFocusItem->clearFocus(); + 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 @@ -943,17 +949,6 @@ void QGraphicsItemPrivate::setParentItemHelper(QGraphicsItem *newParent) parent->itemChange(QGraphicsItem::ItemChildRemovedChange, thisPointerVariant); } - // Auto-update focus proxy. Any ancestor that has this as focus proxy - //needs to be nulled. - QGraphicsItem *p = parent; - while (p) { - if ((p->d_ptr->flags & QGraphicsItem::ItemAutoDetectsFocusProxy) && - (p->focusProxy() == q)) { - p->setFocusProxy(0); - } - p = p->d_ptr->parent; - } - // 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) { @@ -964,6 +959,39 @@ void QGraphicsItemPrivate::setParentItemHelper(QGraphicsItem *newParent) } } + // 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->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; + } + break; + } + p = p->d_ptr->parent; + } + + // Update focus scope item ptr in new scope. + if (newParent) { + QGraphicsItem *p = newParent; + while (p) { + if (p->flags() & QGraphicsItem::ItemIsFocusScope) { + p->d_ptr->focusScopeItem = subFocusItem ? subFocusItem : parentFocusScopeItem; + // ### The below line might not make sense... + if (subFocusItem) + subFocusItem->d_ptr->clearSubFocus(); + break; + } + p = p->d_ptr->parent; + } + } + if ((parent = newParent)) { bool implicitUpdate = false; if (parent->d_func()->scene && parent->d_func()->scene != scene) { @@ -999,6 +1027,9 @@ void QGraphicsItemPrivate::setParentItemHelper(QGraphicsItem *newParent) setEnabledHelper(parent->isEnabled(), /* explicit = */ false, /* update = */ !implicitUpdate); } + // Auto-activate if visible and the parent is active. + if (q->isVisible() && parent->isActive()) + q->setActive(true); } else { // Inherit ancestor flags from the new parent. updateAncestorFlag(QGraphicsItem::GraphicsItemFlag(-2)); @@ -1023,22 +1054,14 @@ void QGraphicsItemPrivate::setParentItemHelper(QGraphicsItem *newParent) } // Resolve depth. - resolveDepth(parent ? parent->d_ptr->depth : -1); + invalidateDepthRecursively(); dirtySceneTransform = 1; // Restore the sub focus chain. - if (lastSubFocusItem) - lastSubFocusItem->d_ptr->setSubFocus(); - - // Auto-update focus proxy. The closest parent that detects - // focus proxies is updated as the proxy gains or loses focus. - p = newParent; - while (p) { - if (p->d_ptr->flags & QGraphicsItem::ItemAutoDetectsFocusProxy) { - p->setFocusProxy(q); - break; - } - p = p->d_ptr->parent; + if (subFocusItem) { + subFocusItem->d_ptr->setSubFocus(newParent); + if (parent && parent->isActive()) + subFocusItem->setFocus(); } // Deliver post-change notification @@ -1055,11 +1078,6 @@ void QGraphicsItemPrivate::setParentItemHelper(QGraphicsItem *newParent) */ void QGraphicsItemPrivate::childrenBoundingRectHelper(QTransform *x, QRectF *rect) { - if (!dirtyChildrenBoundingRect) { - *rect |= x->mapRect(childrenBoundingRect); - return; - } - for (int i = 0; i < children.size(); ++i) { QGraphicsItem *child = children.at(i); QGraphicsItemPrivate *childd = child->d_ptr.data(); @@ -1067,19 +1085,20 @@ void QGraphicsItemPrivate::childrenBoundingRectHelper(QTransform *x, QRectF *rec if (hasPos || childd->transformData) { // COMBINE QTransform matrix = childd->transformToParent(); - matrix *= *x; + if (x) + matrix *= *x; *rect |= matrix.mapRect(child->boundingRect()); if (!childd->children.isEmpty()) childd->childrenBoundingRectHelper(&matrix, rect); } else { - *rect |= x->mapRect(child->boundingRect()); + if (x) + *rect |= x->mapRect(child->boundingRect()); + else + *rect |= child->boundingRect(); if (!childd->children.isEmpty()) childd->childrenBoundingRectHelper(x, rect); } } - - childrenBoundingRect = *rect; - dirtyChildrenBoundingRect = 0; } void QGraphicsItemPrivate::initStyleOption(QStyleOptionGraphicsItem *option, const QTransform &worldTransform, @@ -1210,6 +1229,18 @@ QGraphicsItem::~QGraphicsItem() d_ptr->removeExtraItemCache(); 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()) { QList<QGraphicsItem *> oldChildren = d_ptr->children; qDeleteAll(oldChildren); @@ -1223,6 +1254,7 @@ QGraphicsItem::~QGraphicsItem() d_ptr->setParentItemHelper(0); } + delete d_ptr->graphicsEffect; if (d_ptr->transformData) { for(int i = 0; i < d_ptr->transformData->graphicsTransforms.size(); ++i) { QGraphicsTransform *t = d_ptr->transformData->graphicsTransforms.at(i); @@ -1365,14 +1397,29 @@ QGraphicsWidget *QGraphicsItem::topLevelWidget() const */ QGraphicsWidget *QGraphicsItem::window() const { - if (isWidget() && static_cast<const QGraphicsWidget *>(this)->isWindow()) - return static_cast<QGraphicsWidget *>(const_cast<QGraphicsItem *>(this)); - if (QGraphicsWidget *parent = parentWidget()) - return parent->window(); + 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 @@ -1458,6 +1505,17 @@ bool QGraphicsItem::isWindow() const } /*! + \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. @@ -1520,6 +1578,9 @@ static void _q_qgraphicsItemSetFlag(QGraphicsItem *item, QGraphicsItem::Graphics */ void QGraphicsItem::setFlags(GraphicsItemFlags flags) { + if (isWindow()) + flags |= ItemIsPanel; + // Notify change and check for adjustment. if (quint32(d_ptr->flags) == quint32(flags)) return; @@ -1874,16 +1935,16 @@ void QGraphicsItemPrivate::setVisibleHelper(bool newVisible, bool explicitly, bo q->ungrabKeyboard(); } if (q_ptr->hasFocus() && scene) { - // Hiding the closest non-window ancestor of the focus item + // Hiding the closest non-panel ancestor of the focus item QGraphicsItem *focusItem = scene->focusItem(); bool clear = true; - if (isWidget && !focusItem->isWindow()) { + if (isWidget && !focusItem->isPanel()) { do { if (focusItem == q_ptr) { clear = !static_cast<QGraphicsWidget *>(q_ptr)->focusNextPrevChild(true); break; } - } while ((focusItem = focusItem->parentWidget()) && !focusItem->isWindow()); + } while ((focusItem = focusItem->parentWidget()) && !focusItem->isPanel()); } if (clear) q_ptr->clearFocus(); @@ -1907,12 +1968,40 @@ void QGraphicsItemPrivate::setVisibleHelper(bool newVisible, bool explicitly, bo 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 (newVisible && isWidget) { - QGraphicsWidget *widget = static_cast<QGraphicsWidget *>(q_ptr); - QGraphicsWidget *fw = widget->focusWidget(); - if (fw && fw != scene->focusItem()) - scene->setFocusItem(fw); + if (newVisible) { + 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; + scene->setFocusItem(fsi); + } + break; + } + p = p->d_ptr->parent; + } + if (!done) { + QGraphicsItem *fi = subFocusItem; + if (fi && fi != scene->focusItem()) { + scene->setFocusItem(fi); + } + } } // Deliver post-change notification. @@ -2005,17 +2094,17 @@ void QGraphicsItemPrivate::setEnabledHelper(bool newEnabled, bool explicitly, bo if (scene && scene->mouseGrabberItem() == q_ptr) q_ptr->ungrabMouse(); if (q_ptr->hasFocus()) { - // Disabling the closest non-window ancestor of the focus item + // 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->isWindow() && q_ptr->isAncestorOf(focusItem)) { + 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->isWindow()); + } while ((focusItem = focusItem->parentWidget()) && !focusItem->isPanel()); } if (clear) q_ptr->clearFocus(); @@ -2030,8 +2119,8 @@ void QGraphicsItemPrivate::setEnabledHelper(bool newEnabled, bool explicitly, bo enabled = newEnabledVariant.toBool(); // Schedule redraw. - if (update && scene) - scene->d_func()->markDirty(q_ptr); + if (update) + q_ptr->update(); foreach (QGraphicsItem *child, children) { if (!newEnabled || !child->d_ptr->explicitlyDisabled) @@ -2135,8 +2224,8 @@ void QGraphicsItem::setSelected(bool selected) return; d_ptr->selected = newSelected; + update(); if (d_ptr->scene) { - d_ptr->scene->d_func()->markDirty(this); QGraphicsScenePrivate *sceneD = d_ptr->scene->d_func(); if (selected) { sceneD->selectedItems << this; @@ -2243,6 +2332,118 @@ void QGraphicsItem::setOpacity(qreal opacity) } /*! + Returns a pointer to this item's effect if it has one; otherwise 0. + + \since 4.6 +*/ +QGraphicsEffect *QGraphicsItem::graphicsEffect() const +{ + return d_ptr->graphicsEffect; +} + +/*! + Sets \a effect as the item's effect. If there already is an effect installed + on this item, QGraphicsItem will delete the existing effect before installing + the new \a effect. + + If \a effect is the installed on a different item, setGraphicsEffect() will remove + the effect from the item and install it on this item. + + \note This function will apply the effect on itself and all its children. + + \since 4.6 +*/ +void QGraphicsItem::setGraphicsEffect(QGraphicsEffect *effect) +{ + if (d_ptr->graphicsEffect == effect) + return; + + if (d_ptr->graphicsEffect && effect) { + delete d_ptr->graphicsEffect; + d_ptr->graphicsEffect = 0; + } + + if (!effect) { + // Unset current effect. + QGraphicsEffectPrivate *oldEffectPrivate = d_ptr->graphicsEffect->d_func(); + d_ptr->graphicsEffect = 0; + if (oldEffectPrivate) { + oldEffectPrivate->setGraphicsEffectSource(0); // deletes the current source. + if (d_ptr->scene) // Update the views directly. + d_ptr->scene->d_func()->markDirty(this, QRectF(), false, false, false, false, true); + } + } else { + // Set new effect. + QGraphicsEffectSourcePrivate *sourced = new QGraphicsItemEffectSourcePrivate(this); + QGraphicsEffectSource *source = new QGraphicsEffectSource(*sourced); + d_ptr->graphicsEffect = effect; + effect->d_func()->setGraphicsEffectSource(source); + } + + prepareGeometryChange(); +} + +/*! + \internal + \since 4.6 + Returns the effective bounding rect of the item. + If the item has no effect, this is the same as the item's bounding rect. + If the item has an effect, the effective rect can be larger than the item's + bouding rect, depending on the effect. + + \sa boundingRect() +*/ +QRectF QGraphicsItemPrivate::effectiveBoundingRect() const +{ + QGraphicsEffect *effect = graphicsEffect; + QRectF brect = effect && effect->isEnabled() ? effect->boundingRect() : q_ptr->boundingRect(); + if (ancestorFlags & QGraphicsItemPrivate::AncestorClipsChildren) + return brect; + + const QGraphicsItem *effectParent = parent; + while (effectParent) { + effect = effectParent->d_ptr->graphicsEffect; + if (effect && effect->isEnabled()) + brect = effect->boundingRectFor(brect); + if (effectParent->d_ptr->ancestorFlags & QGraphicsItemPrivate::AncestorClipsChildren) + return brect; + effectParent = effectParent->d_ptr->parent; + } + + return brect; +} + +/*! + \internal + \since 4.6 + Returns the effective bounding rect of this item in scene coordinates, + by combining sceneTransform() with boundingRect(), taking into account + the effect that the item might have. + + If the item has no effect, this is the same as sceneBoundingRect(). + + \sa effectiveBoundingRect(), sceneBoundingRect() +*/ +QRectF QGraphicsItemPrivate::sceneEffectiveBoundingRect() const +{ + // Find translate-only offset + // COMBINE + QPointF offset; + const QGraphicsItem *parentItem = q_ptr; + const QGraphicsItemPrivate *itemd; + do { + itemd = parentItem->d_ptr.data(); + if (itemd->transformData) + break; + offset += itemd->pos; + } while ((parentItem = itemd->parent)); + + QRectF br = effectiveBoundingRect(); + br.translate(offset); + return !parentItem ? br : parentItem->sceneTransform().mapRect(br); +} + +/*! Returns true if this item can accept drag and drop events; otherwise, returns false. By default, items do not accept drag and drop events; items are transparent to drag and drop. @@ -2500,12 +2701,64 @@ void QGraphicsItem::setHandlesChildEvents(bool enabled) 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 or its \l{focusProxy()}{focus proxy} has keyboard - input focus; otherwise, returns false. + 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() + \sa focusItem(), setFocus(), QGraphicsScene::setFocusItem(), isActive() */ bool QGraphicsItem::hasFocus() const { @@ -2522,9 +2775,10 @@ bool QGraphicsItem::hasFocus() const Only enabled items that set the ItemIsFocusable flag can accept keyboard focus. - If this item is not visible, 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. + 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 @@ -2535,28 +2789,53 @@ bool QGraphicsItem::hasFocus() const */ void QGraphicsItem::setFocus(Qt::FocusReason focusReason) { + d_ptr->setFocusHelper(focusReason, /* climb = */ true); +} + +/*! + \internal +*/ +void QGraphicsItemPrivate::setFocusHelper(Qt::FocusReason focusReason, bool climb) +{ // Disabled / unfocusable items cannot accept focus. - if (!isEnabled() || !(d_ptr->flags & QGraphicsItem::ItemIsFocusable)) + if (!q_ptr->isEnabled() || !(flags & QGraphicsItem::ItemIsFocusable)) return; // Find focus proxy. - QGraphicsItem *f = this; + QGraphicsItem *f = q_ptr; while (f->d_ptr->focusProxy) f = f->d_ptr->focusProxy; // Return if it already has focus. - if (d_ptr->scene && d_ptr->scene->focusItem() == f) + if (scene && scene->focusItem() == f) return; + // Update focus scope item ptr. + QGraphicsItem *p = parent; + while (p) { + if (p->flags() & QGraphicsItem::ItemIsFocusScope) { + p->d_ptr->focusScopeItem = q_ptr; + if (!q_ptr->isActive() || !p->focusItem()) + 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. - d_ptr->setSubFocus(); + f->d_ptr->setSubFocus(); // Update the scene's focus item. - if (d_ptr->scene) { - QGraphicsWidget *w = window(); - if (!w || w->isActiveWindow()) { + if (scene) { + QGraphicsItem *p = q_ptr->panel(); + if ((!p && scene->isActive()) || (p && p->isActive())) { // Visible items immediately gain focus from scene. - d_ptr->scene->d_func()->setFocusItemHelper(f, focusReason); + scene->d_func()->setFocusItemHelper(f, focusReason); } } } @@ -2564,8 +2843,8 @@ void QGraphicsItem::setFocus(Qt::FocusReason 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. + 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. @@ -2574,10 +2853,21 @@ void QGraphicsItem::setFocus(Qt::FocusReason focusReason) */ void QGraphicsItem::clearFocus() { - if (!d_ptr->scene) - return; + // Pass focus to the closest parent focus scope. + if (!d_ptr->inDestructor) { + QGraphicsItem *p = d_ptr->parent; + while (p) { + if (p->flags() & ItemIsFocusScope) { + p->d_ptr->setFocusHelper(Qt::OtherFocusReason, /* climb = */ false); + return; + } + p = p->d_ptr->parent; + } + } + // Invisible items with focus must explicitly clear subfocus. - d_ptr->clearSubFocus(); + d_ptr->clearSubFocus(this); + if (hasFocus()) { // If this item has the scene's input focus, clear it. d_ptr->scene->setFocusItem(0); @@ -2590,7 +2880,7 @@ void QGraphicsItem::clearFocus() Returns this item's focus proxy, or 0 if this item has no focus proxy. - \sa setFocusProxy(), ItemAutoDetectsFocusProxy, setFocus(), hasFocus() + \sa setFocusProxy(), setFocus(), hasFocus() */ QGraphicsItem *QGraphicsItem::focusProxy() const { @@ -2614,7 +2904,7 @@ QGraphicsItem *QGraphicsItem::focusProxy() const The focus proxy \a item must belong to the same scene as this item. - \sa focusProxy(), ItemAutoDetectsFocusProxy, setFocus(), hasFocus() + \sa focusProxy(), setFocus(), hasFocus() */ void QGraphicsItem::setFocusProxy(QGraphicsItem *item) { @@ -2660,6 +2950,16 @@ QGraphicsItem *QGraphicsItem::focusItem() const } /*! + \internal + + Returns this item's focus scope item. +*/ +QGraphicsItem *QGraphicsItem::focusScopeItem() const +{ + return d_ptr->focusScopeItem; +} + +/*! \since 4.4 Grabs the mouse input. @@ -3769,10 +4069,10 @@ QRectF QGraphicsItem::childrenBoundingRect() const if (!d_ptr->dirtyChildrenBoundingRect) return d_ptr->childrenBoundingRect; - QRectF childRect; - QTransform x; - d_ptr->childrenBoundingRectHelper(&x, &childRect); - return childRect; + d_ptr->childrenBoundingRect = QRectF(); + d_ptr->childrenBoundingRectHelper(0, &d_ptr->childrenBoundingRect); + d_ptr->dirtyChildrenBoundingRect = 0; + return d_ptr->childrenBoundingRect; } /*! @@ -4413,14 +4713,42 @@ bool QGraphicsItemPrivate::discardUpdateRequest(bool ignoreClipping, bool ignore /*! \internal +*/ +int QGraphicsItemPrivate::depth() const +{ + if (itemDepth == -1) + const_cast<QGraphicsItemPrivate *>(this)->resolveDepth(); - Resolves the stacking depth of this object and all its children. + return itemDepth; +} + +/*! + \internal */ -void QGraphicsItemPrivate::resolveDepth(int parentDepth) +void QGraphicsItemPrivate::invalidateDepthRecursively() { - depth = parentDepth + 1; + if (itemDepth == -1) + return; + + itemDepth = -1; for (int i = 0; i < children.size(); ++i) - children.at(i)->d_ptr->resolveDepth(depth); + 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; + } } /*! @@ -4526,7 +4854,7 @@ void QGraphicsItemPrivate::updateCachedClipPathFromSetPosHelper(const QPointF &n { Q_ASSERT(inSetPosHelper); - if (!(ancestorFlags & QGraphicsItemPrivate::AncestorClipsChildren)) + if (inDestructor || !(ancestorFlags & QGraphicsItemPrivate::AncestorClipsChildren)) return; // Not clipped by any ancestor. // Find closest clip ancestor and transform. @@ -4536,15 +4864,19 @@ void QGraphicsItemPrivate::updateCachedClipPathFromSetPosHelper(const QPointF &n if (transformData) thisToParentTransform = transformData->computedFullTransform(&thisToParentTransform); QGraphicsItem *clipParent = parent; - while (clipParent && !(clipParent->d_ptr->flags & QGraphicsItem::ItemClipsChildrenToShape)) { + while (clipParent && !clipParent->d_ptr->inDestructor && !(clipParent->d_ptr->flags & QGraphicsItem::ItemClipsChildrenToShape)) { thisToParentTransform *= clipParent->d_ptr->transformToParent(); clipParent = clipParent->d_ptr->parent; } - // thisToParentTransform is now the same as q->itemTransform(clipParent), except - // that the new position (which is not yet set on the item) is taken into account. - Q_ASSERT(clipParent); - Q_ASSERT(clipParent->d_ptr->flags & QGraphicsItem::ItemClipsChildrenToShape); + // Ensure no parents are currently being deleted. This can only + // happen if the item is moved by a dying ancestor. + QGraphicsItem *p = clipParent; + while (p) { + if (p->d_ptr->inDestructor) + return; + p = p->d_ptr->parent; + } // From here everything is calculated in clip parent's coordinates. const QRectF parentBoundingRect(clipParent->boundingRect()); @@ -4602,51 +4934,53 @@ void QGraphicsItemPrivate::ensureSceneTransformRecursive(QGraphicsItem **topMost 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); } -void QGraphicsItemPrivate::ensureSceneTransform() -{ - if (dirtySceneTransform) { - // 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(); - } - - QGraphicsItem *that = q_func(); - ensureSceneTransformRecursive(&that); -} - /*! \internal */ -void QGraphicsItemPrivate::setSubFocus() +void QGraphicsItemPrivate::setSubFocus(QGraphicsItem *rootItem) { - // Update focus child chain. - QGraphicsItem *item = q_ptr; - QGraphicsItem *parent = item; - bool hidden = !visible; + // 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; do { - parent->d_func()->subFocusItem = item; - } while (!parent->isWindow() && (parent = parent->d_ptr->parent) && (!hidden || !parent->d_func()->visible)); + // 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(); + } + 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()->lastFocusItem = subFocusItem; } /*! \internal */ -void QGraphicsItemPrivate::clearSubFocus() +void QGraphicsItemPrivate::clearSubFocus(QGraphicsItem *rootItem) { - // Reset focus child chain. - QGraphicsItem *parent = q_ptr; + // Reset sub focus chain. + QGraphicsItem *parent = rootItem ? rootItem : q_ptr; do { if (parent->d_ptr->subFocusItem != q_ptr) break; parent->d_ptr->subFocusItem = 0; - } while (!parent->isWindow() && (parent = parent->d_ptr->parent)); + parent->d_ptr->subFocusItemChange(); + } while (!parent->isPanel() && (parent = parent->d_ptr->parent)); } /*! @@ -4665,6 +4999,16 @@ void QGraphicsItemPrivate::resetFocusProxy() /*! \internal + Subclasses can reimplement this function to be notified when subFocusItem + changes. +*/ +void QGraphicsItemPrivate::subFocusItemChange() +{ +} + +/*! + \internal + Tells us if it is a proxy widget */ bool QGraphicsItemPrivate::isProxyWidget() const @@ -4695,6 +5039,13 @@ void QGraphicsItem::update(const QRectF &rect) if (rect.isEmpty() && !rect.isNull()) return; + // Make sure we notify effects about invalidated source. + QGraphicsItem *item = this; + do { + if (item->d_ptr->graphicsEffect) + item->d_ptr->notifyInvalidated = 1; + } while ((item = item->d_ptr->parent)); + if (CacheMode(d_ptr->cacheMode) != NoCache) { QGraphicsItemCache *cache = d_ptr->extraItemCache(); if (d_ptr->discardUpdateRequest(/* ignoreVisibleBit = */ false, @@ -4722,46 +5073,6 @@ void QGraphicsItem::update(const QRectF &rect) } /*! - \internal - - Scrolls \a rect in \a pix by \a dx, \a dy. - - ### This can be done much more efficiently by using XCopyArea on X11 with - the same dst and src, and through moving pixels in the raster engine. It - can probably also be done much better on the other paint engines. -*/ -void _q_scrollPixmap(QPixmap *pix, const QRect &rect, int dx, int dy) -{ -#if 0 - QPainter painter(pix); - painter.setClipRect(rect); - painter.drawPixmap(rect.translated(dx, dy), *pix, rect); - painter.end(); -#elif defined Q_WS_X11 - GC gc = XCreateGC(X11->display, pix->handle(), 0, 0); - - XRectangle xrect; - xrect.x = rect.x(); - xrect.y = rect.y(); - xrect.width = rect.width(); - xrect.height = rect.height(); - XSetClipRectangles(X11->display, gc, 0, 0, &xrect, 1, YXBanded); - - XCopyArea(X11->display, pix->handle(), pix->handle(), gc, - rect.x(), rect.y(), rect.width(), rect.height(), - rect.x()+dx, rect.y()+dy); - XFreeGC(X11->display, gc); -#else - QPixmap newPix = *pix; - QPainter painter(&newPix); - painter.setClipRect(rect); - painter.drawPixmap(rect.translated(dx, dy), *pix, rect); - painter.end(); - *pix = newPix; -#endif -} - -/*! \since 4.4 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. @@ -4801,7 +5112,7 @@ void QGraphicsItem::scroll(qreal dx, qreal dy, const QRectF &rect) QRectF br = boundingRect().adjusted(-adjust, -adjust, adjust, adjust); QRect irect = rect.toRect().translated(-br.x(), -br.y()); - _q_scrollPixmap(&pix, irect, dx, dy); + pix.scroll(dx, dy, irect); QPixmapCache::replace(c->key, pix); @@ -5566,6 +5877,8 @@ 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) @@ -5590,8 +5903,8 @@ QGraphicsItem *QGraphicsItem::commonAncestorItem(const QGraphicsItem *other) con return const_cast<QGraphicsItem *>(this); const QGraphicsItem *thisw = this; const QGraphicsItem *otherw = other; - int thisDepth = d_ptr->depth; - int otherDepth = other->d_ptr->depth; + int thisDepth = d_ptr->depth(); + int otherDepth = other->d_ptr->depth(); while (thisDepth > otherDepth) { thisw = thisw->d_ptr->parent; --thisDepth; @@ -5890,6 +6203,19 @@ bool QGraphicsItem::sceneEvent(QEvent *event) 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; } @@ -6034,6 +6360,7 @@ void QGraphicsItem::dropEvent(QGraphicsSceneDragDropEvent *event) void QGraphicsItem::focusInEvent(QFocusEvent *event) { Q_UNUSED(event); + update(); } /*! @@ -6045,6 +6372,7 @@ void QGraphicsItem::focusInEvent(QFocusEvent *event) void QGraphicsItem::focusOutEvent(QFocusEvent *event) { Q_UNUSED(event); + update(); } /*! @@ -6059,8 +6387,7 @@ void QGraphicsItem::focusOutEvent(QFocusEvent *event) void QGraphicsItem::hoverEnterEvent(QGraphicsSceneHoverEvent *event) { Q_UNUSED(event); - if (d_ptr->scene) - d_ptr->scene->d_func()->markDirty(this); + update(); } /*! @@ -6088,8 +6415,7 @@ void QGraphicsItem::hoverMoveEvent(QGraphicsSceneHoverEvent *event) void QGraphicsItem::hoverLeaveEvent(QGraphicsSceneHoverEvent *event) { Q_UNUSED(event); - if (d_ptr->scene) - d_ptr->scene->d_func()->markDirty(this); + update(); } /*! @@ -6586,10 +6912,13 @@ void QGraphicsItem::removeFromIndex() */ 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); @@ -6601,7 +6930,7 @@ void QGraphicsItem::prepareGeometryChange() // 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->connectedSignals[0] & scenePrivate->changedSignalMask) + if (scenePrivate->isSignalConnected(scenePrivate->changedSignalIndex) || scenePrivate->views.isEmpty()) { if (d_ptr->hasTranslateOnlySceneTransform()) { d_ptr->scene->update(boundingRect().translated(d_ptr->sceneTransform.dx(), @@ -6613,16 +6942,21 @@ void QGraphicsItem::prepareGeometryChange() } QGraphicsItem *parent = this; - while ((parent = parent->d_ptr->parent)) + while ((parent = parent->d_ptr->parent)) { parent->d_ptr->dirtyChildrenBoundingRect = 1; + // ### Only do this if the parent's effect applies to the entire subtree. + parent->d_ptr->notifyBoundingRectChanged = 1; + } if (d_ptr->inSetPosHelper) return; - if (d_ptr->flags & ItemClipsChildrenToShape) + if (d_ptr->flags & ItemClipsChildrenToShape + || d_ptr->ancestorFlags & QGraphicsItemPrivate::AncestorClipsChildren) { d_ptr->invalidateCachedClipPathRecursively(); - else + } else { d_ptr->invalidateCachedClipPath(); + } } /*! @@ -6727,7 +7061,7 @@ QGraphicsObject::QGraphicsObject(QGraphicsItemPrivate &dd, QGraphicsItem *parent \property QGraphicsObject::id \brief the id of of the item - \sa QGraphicsItem::opacity(), QGraphicsItem::setOpacity() + \sa QObject::objectName(), QObject::setObjectName() */ /*! @@ -10017,6 +10351,129 @@ int QGraphicsItemGroup::type() const return Type; } +QRectF QGraphicsItemEffectSourcePrivate::boundingRect(Qt::CoordinateSystem system) const +{ + const bool deviceCoordinates = (system == Qt::DeviceCoordinates); + if (!info && deviceCoordinates) { + // Device coordinates without info not yet supported. + qWarning("QGraphicsEffectSource::boundingRect: Not yet implemented, lacking device context"); + return QRectF(); + } + + QRectF rect = item->boundingRect(); + if (!item->d_ptr->children.isEmpty()) + rect |= item->childrenBoundingRect(); + + if (deviceCoordinates) { + Q_ASSERT(info->painter); + rect = info->painter->worldTransform().mapRect(rect); + } + + return rect; +} + +void QGraphicsItemEffectSourcePrivate::draw(QPainter *painter) +{ + if (!info) { + qWarning("QGraphicsEffectSource::draw: Can only begin as a result of QGraphicsEffect::draw"); + return; + } + + Q_ASSERT(item->d_ptr->scene); + QGraphicsScenePrivate *scened = item->d_ptr->scene->d_func(); + if (painter == info->painter) { + scened->draw(item, painter, info->viewTransform, info->transformPtr, info->exposedRegion, + info->widget, info->opacity, info->effectTransform, info->wasDirtySceneTransform, + info->drawItem); + } else { + QTransform effectTransform = 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); + } +} + +QPixmap QGraphicsItemEffectSourcePrivate::pixmap(Qt::CoordinateSystem system, QPoint *offset) const +{ + const bool deviceCoordinates = (system == Qt::DeviceCoordinates); + if (!info && deviceCoordinates) { + // Device coordinates without info not yet supported. + qWarning("QGraphicsEffectSource::pixmap: Not yet implemented, lacking device context"); + return QPixmap(); + } + + if (!item->d_ptr->scene) + return QPixmap(); + QGraphicsScenePrivate *scened = item->d_ptr->scene->d_func(); + + const QRectF sourceRect = boundingRect(system); + QRect effectRect = item->graphicsEffect()->boundingRectFor(sourceRect).toAlignedRect(); + if (offset) + *offset = effectRect.topLeft(); + + if (deviceCoordinates) { + // Clip to viewport rect. + int left, top, right, bottom; + effectRect.getCoords(&left, &top, &right, &bottom); + if (left < 0) { + if (offset) + offset->rx() += -left; + effectRect.setX(0); + } + if (top < 0) { + if (offset) + offset->ry() += -top; + effectRect.setY(0); + } + // NB! We use +-1 for historical reasons (see QRect documentation). + QPaintDevice *device = info->painter->device(); + const int deviceWidth = device->width(); + const int deviceHeight = device->height(); + if (right + 1 > deviceWidth) + effectRect.setRight(deviceWidth - 1); + if (bottom + 1 > deviceHeight) + effectRect.setBottom(deviceHeight -1); + + } + + if (effectRect.isEmpty()) + return QPixmap(); + + QPixmap pixmap(effectRect.size()); + pixmap.fill(Qt::transparent); + QPainter pixmapPainter(&pixmap); + pixmapPainter.setRenderHints(info ? info->painter->renderHints() : QPainter::TextAntialiasing); + + QTransform effectTransform = QTransform::fromTranslate(-effectRect.x(), -effectRect.y()); + if (deviceCoordinates && info->effectTransform) + effectTransform *= *info->effectTransform; + + if (!info) { + // Logical coordinates without info. + QTransform sceneTransform = item->sceneTransform(); + QTransform newEffectTransform = sceneTransform.inverted(); + newEffectTransform *= effectTransform; + scened->draw(item, &pixmapPainter, 0, &sceneTransform, 0, 0, qreal(1.0), + &newEffectTransform, false, true); + } else if (deviceCoordinates) { + // Device coordinates with info. + scened->draw(item, &pixmapPainter, info->viewTransform, info->transformPtr, info->exposedRegion, + info->widget, info->opacity, &effectTransform, info->wasDirtySceneTransform, + info->drawItem); + } else { + // Item coordinates with info. + QTransform newEffectTransform = info->transformPtr->inverted(); + newEffectTransform *= effectTransform; + scened->draw(item, &pixmapPainter, info->viewTransform, info->transformPtr, info->exposedRegion, + info->widget, info->opacity, &newEffectTransform, info->wasDirtySceneTransform, + info->drawItem); + } + + pixmapPainter.end(); + return pixmap; +} + #ifndef QT_NO_DEBUG_STREAM QDebug operator<<(QDebug debug, QGraphicsItem *item) { @@ -10169,8 +10626,11 @@ QDebug operator<<(QDebug debug, QGraphicsItem::GraphicsItemFlag flag) case QGraphicsItem::ItemNegativeZStacksBehindParent: str = "ItemNegativeZStacksBehindParent"; break; - case QGraphicsItem::ItemAutoDetectsFocusProxy: - str = "ItemAutoDetectsFocusProxy"; + case QGraphicsItem::ItemIsPanel: + str = "ItemIsPanel"; + break; + case QGraphicsItem::ItemIsFocusScope: + str = "ItemIsFocusScope"; break; } debug << str; @@ -10181,7 +10641,7 @@ QDebug operator<<(QDebug debug, QGraphicsItem::GraphicsItemFlags flags) { debug << '('; bool f = false; - for (int i = 0; i < 9; ++i) { + for (int i = 0; i < 16; ++i) { if (flags & (1 << i)) { if (f) debug << '|'; diff --git a/src/gui/graphicsview/qgraphicsitem.h b/src/gui/graphicsview/qgraphicsitem.h index d34787d..089d6fe 100644 --- a/src/gui/graphicsview/qgraphicsitem.h +++ b/src/gui/graphicsview/qgraphicsitem.h @@ -1,6 +1,7 @@ /**************************************************************************** ** ** Copyright (C) 2009 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. @@ -9,8 +10,8 @@ ** No Commercial Usage ** This file contains pre-release code and may not be distributed. ** You may use this file in accordance with the terms and conditions -** contained in the either Technology Preview License Agreement or the -** Beta Release License Agreement. +** 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 @@ -20,21 +21,20 @@ ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** -** In addition, as a special exception, Nokia gives you certain -** additional rights. These rights are described in the Nokia Qt LGPL -** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this -** package. +** 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. +** +** +** +** +** +** ** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. ** -** If you are unsure which license is appropriate for your use, please -** contact the sales department at http://qt.nokia.com/contact. ** $QT_END_LICENSE$ ** ****************************************************************************/ @@ -63,6 +63,7 @@ QT_MODULE(Gui) class QBrush; class QCursor; class QFocusEvent; +class QGraphicsEffect; class QGraphicsItemGroup; class QGraphicsObject; class QGraphicsSceneContextMenuEvent; @@ -102,8 +103,9 @@ public: ItemHasNoContents = 0x400, ItemSendsGeometryChanges = 0x800, ItemAcceptsInputMethod = 0x1000, - ItemAutoDetectsFocusProxy = 0x2000, - ItemNegativeZStacksBehindParent = 0x4000 + ItemNegativeZStacksBehindParent = 0x2000, + ItemIsPanel = 0x4000, + ItemIsFocusScope = 0x8000 // internal // NB! Don't forget to increase the d_ptr->flags bit field by 1 when adding a new flag. }; Q_DECLARE_FLAGS(GraphicsItemFlags, GraphicsItemFlag) @@ -160,11 +162,13 @@ public: 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; @@ -210,6 +214,10 @@ public: qreal effectiveOpacity() const; void setOpacity(qreal opacity); + // Effect + QGraphicsEffect *graphicsEffect() const; + void setGraphicsEffect(QGraphicsEffect *effect); + Qt::MouseButtons acceptedMouseButtons() const; void setAcceptedMouseButtons(Qt::MouseButtons buttons); @@ -226,6 +234,9 @@ public: 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(); @@ -234,6 +245,7 @@ public: void setFocusProxy(QGraphicsItem *item); QGraphicsItem *focusItem() const; + QGraphicsItem *focusScopeItem() const; void grabMouse(); void ungrabMouse(); @@ -282,8 +294,8 @@ public: QPointF transformOriginPoint() const; void setTransformOriginPoint(const QPointF &origin); - inline void setTransformOriginPoint(qreal x, qreal y) - { setTransformOriginPoint(QPointF(x,y)); } + inline void setTransformOriginPoint(qreal ax, qreal ay) + { setTransformOriginPoint(QPointF(ax,ay)); } virtual void advance(int phase); @@ -447,6 +459,7 @@ private: friend class QGraphicsSceneIndexPrivate; friend class QGraphicsSceneBspTreeIndex; friend class QGraphicsSceneBspTreeIndexPrivate; + friend class QGraphicsItemEffectSourcePrivate; friend class QGraphicsTransformPrivate; friend class ::tst_QGraphicsItem; friend bool qt_closestLeaf(const QGraphicsItem *, const QGraphicsItem *); @@ -517,6 +530,7 @@ class Q_GUI_EXPORT QGraphicsObject : public QObject, public QGraphicsItem 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) + Q_INTERFACES(QGraphicsItem) public: QGraphicsObject(QGraphicsItem *parent = 0); diff --git a/src/gui/graphicsview/qgraphicsitem_p.h b/src/gui/graphicsview/qgraphicsitem_p.h index 24326f6..fd2ff34 100644 --- a/src/gui/graphicsview/qgraphicsitem_p.h +++ b/src/gui/graphicsview/qgraphicsitem_p.h @@ -1,6 +1,7 @@ /**************************************************************************** ** ** Copyright (C) 2009 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. @@ -9,8 +10,8 @@ ** No Commercial Usage ** This file contains pre-release code and may not be distributed. ** You may use this file in accordance with the terms and conditions -** contained in the either Technology Preview License Agreement or the -** Beta Release License Agreement. +** 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 @@ -20,21 +21,20 @@ ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** -** In addition, as a special exception, Nokia gives you certain -** additional rights. These rights are described in the Nokia Qt LGPL -** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this -** package. +** 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. +** +** +** +** +** +** ** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. ** -** If you are unsure which license is appropriate for your use, please -** contact the sales department at http://qt.nokia.com/contact. ** $QT_END_LICENSE$ ** ****************************************************************************/ @@ -47,8 +47,8 @@ // ------------- // // This file is not part of the Qt API. It exists for the convenience -// of qapplication_*.cpp, qwidget*.cpp and qfiledialog.cpp. This header -// file may change from version to version without notice, or even be removed. +// of other Qt classes. This header file may change from version to +// version without notice, or even be removed. // // We mean it. // @@ -60,6 +60,8 @@ #include "qgraphicstransform.h" #include <private/qgraphicstransform_p.h> +#include <private/qgraphicseffect_p.h> + #include <QtCore/qpoint.h> #if !defined(QT_NO_GRAPHICSVIEW) || (QT_EDITION & QT_MODULE_GRAPHICSVIEW) != QT_MODULE_GRAPHICSVIEW @@ -121,11 +123,13 @@ public: scene(0), parent(0), transformData(0), + graphicsEffect(0), index(-1), siblingIndex(-1), - depth(0), + itemDepth(-1), focusProxy(0), subFocusItem(0), + focusScopeItem(0), imHints(Qt::ImhNone), acceptedMouseButtons(0x1f), visible(1), @@ -165,7 +169,11 @@ public: acceptedTouchBeginEvent(0), filtersDescendantEvents(0), sceneTransformTranslateOnly(0), + notifyBoundingRectChanged(0), + notifyInvalidated(0), mouseSetsFocus(1), + explicitActivate(0), + wantsActive(0), globalStackingOrder(-1), q_ptr(0) { @@ -209,13 +217,17 @@ public: void setEnabledHelper(bool newEnabled, bool explicitly, bool update = true); bool discardUpdateRequest(bool ignoreClipping = false, bool ignoreVisibleBit = false, bool ignoreDirtyBit = false, bool ignoreOpacity = false) const; - void resolveDepth(int parentDepth); + int depth() const; + void invalidateDepthRecursively(); + void resolveDepth(); void addChild(QGraphicsItem *child); void removeChild(QGraphicsItem *child); void setParentItemHelper(QGraphicsItem *parent); void childrenBoundingRectHelper(QTransform *x, QRectF *rect); void initStyleOption(QStyleOptionGraphicsItem *option, const QTransform &worldTransform, const QRegion &exposedRegion, bool allItems = false) const; + QRectF effectiveBoundingRect() const; + QRectF sceneEffectiveBoundingRect() const; virtual void resolveFont(uint inheritedMask) { @@ -307,7 +319,11 @@ public: void invalidateCachedClipPathRecursively(bool childrenOnly = false, const QRectF &emptyIfOutsideThisRect = QRectF()); void updateCachedClipPathFromSetPosHelper(const QPointF &newPos); void ensureSceneTransformRecursive(QGraphicsItem **topMostDirtyItem); - void ensureSceneTransform(); + inline void ensureSceneTransform() + { + QGraphicsItem *that = q_func(); + ensureSceneTransformRecursive(&that); + } inline bool hasTranslateOnlySceneTransform() { @@ -397,9 +413,11 @@ public: || (childrenCombineOpacity() && isFullyTransparent()); } - void setSubFocus(); - void clearSubFocus(); + void setFocusHelper(Qt::FocusReason focusReason, bool climb); + void setSubFocus(QGraphicsItem *rootItem = 0); + void clearSubFocus(QGraphicsItem *rootItem = 0); void resetFocusProxy(); + virtual void subFocusItemChange(); inline QTransform transformToParent() const; inline void ensureSortedChildren(); @@ -416,13 +434,15 @@ public: QList<QGraphicsItem *> children; struct TransformData; TransformData *transformData; + QGraphicsEffect *graphicsEffect; QTransform sceneTransform; int index; int siblingIndex; - int depth; + int itemDepth; // Lazily calculated when calling depth(). QGraphicsItem *focusProxy; QList<QGraphicsItem **> focusProxyRefs; QGraphicsItem *subFocusItem; + QGraphicsItem *focusScopeItem; Qt::InputMethodHints imHints; // Packed 32 bits @@ -451,9 +471,9 @@ public: quint32 needSortChildren : 1; quint32 allChildrenDirty : 1; - // New 32 bits + // Packed 32 bits quint32 fullUpdatePending : 1; - quint32 flags : 15; + quint32 flags : 16; quint32 dirtyChildrenBoundingRect : 1; quint32 paintedViewBoundingRectsNeedRepaint : 1; quint32 dirtySceneTransform : 1; @@ -466,8 +486,13 @@ public: quint32 acceptedTouchBeginEvent : 1; quint32 filtersDescendantEvents : 1; quint32 sceneTransformTranslateOnly : 1; + quint32 notifyBoundingRectChanged : 1; + quint32 notifyInvalidated : 1; quint32 mouseSetsFocus : 1; - quint32 unused : 3; // feel free to use + + // New 32 bits + quint32 explicitActivate : 1; + quint32 wantsActive : 1; // Optional stacking order int globalStackingOrder; @@ -501,10 +526,14 @@ struct QGraphicsItemPrivate::TransformData } QTransform x(transform); - for (int i = 0; i < graphicsTransforms.size(); ++i) - graphicsTransforms.at(i)->applyTo(&x); + 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, Qt::ZAxis); + x.rotate(rotation); x.scale(scale, scale); x.translate(-xOrigin, -yOrigin); if (postmultiplyTransform) @@ -513,6 +542,77 @@ struct QGraphicsItemPrivate::TransformData } }; +struct QGraphicsItemPaintInfo +{ + inline QGraphicsItemPaintInfo(const QTransform *const xform1, const QTransform *const xform2, + const QTransform *const xform3, + QRegion *r, QWidget *w, QStyleOptionGraphicsItem *opt, + QPainter *p, qreal o, bool b1, bool b2) + : viewTransform(xform1), transformPtr(xform2), effectTransform(xform3), exposedRegion(r), widget(w), + option(opt), painter(p), opacity(o), wasDirtySceneTransform(b1), drawItem(b2) + {} + + const QTransform *viewTransform; + const QTransform *transformPtr; + const QTransform *effectTransform; + QRegion *exposedRegion; + QWidget *widget; + QStyleOptionGraphicsItem *option; + QPainter *painter; + qreal opacity; + quint32 wasDirtySceneTransform : 1; + quint32 drawItem : 1; +}; + +class QGraphicsItemEffectSourcePrivate : public QGraphicsEffectSourcePrivate +{ +public: + QGraphicsItemEffectSourcePrivate(QGraphicsItem *i) + : QGraphicsEffectSourcePrivate(), item(i), info(0) + {} + + inline void detach() + { item->setGraphicsEffect(0); } + + inline const QGraphicsItem *graphicsItem() const + { return item; } + + inline const QWidget *widget() const + { return 0; } + + inline void update() + { item->update(); } + + inline void effectBoundingRectChanged() + { item->prepareGeometryChange(); } + + inline bool isPixmap() const + { + return (item->type() == QGraphicsPixmapItem::Type); + //|| (item->d_ptr->isObject && qobject_cast<QFxImage *>(q_func())); + } + + inline const QStyleOption *styleOption() const + { return info ? info->option : 0; } + + inline QRect deviceRect() const + { + if (!info || !info->widget) { + qWarning("QGraphicsEffectSource::deviceRect: Not yet implemented, lacking device context"); + return QRect(); + } + return info->widget->rect(); + } + + QRectF boundingRect(Qt::CoordinateSystem system) const; + void draw(QPainter *); + QPixmap pixmap(Qt::CoordinateSystem system, QPoint *offset) const; + + QGraphicsItem *item; + QGraphicsItemPaintInfo *info; +}; + + /*! \internal */ diff --git a/src/gui/graphicsview/qgraphicsitemanimation.cpp b/src/gui/graphicsview/qgraphicsitemanimation.cpp index c9b95ad..be2f300 100644 --- a/src/gui/graphicsview/qgraphicsitemanimation.cpp +++ b/src/gui/graphicsview/qgraphicsitemanimation.cpp @@ -1,6 +1,7 @@ /**************************************************************************** ** ** Copyright (C) 2009 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. @@ -9,8 +10,8 @@ ** No Commercial Usage ** This file contains pre-release code and may not be distributed. ** You may use this file in accordance with the terms and conditions -** contained in the either Technology Preview License Agreement or the -** Beta Release License Agreement. +** 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 @@ -20,21 +21,20 @@ ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** -** In addition, as a special exception, Nokia gives you certain -** additional rights. These rights are described in the Nokia Qt LGPL -** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this -** package. +** 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. +** +** +** +** +** +** ** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. ** -** If you are unsure which license is appropriate for your use, please -** contact the sales department at http://qt.nokia.com/contact. ** $QT_END_LICENSE$ ** ****************************************************************************/ diff --git a/src/gui/graphicsview/qgraphicsitemanimation.h b/src/gui/graphicsview/qgraphicsitemanimation.h index 29221e5..8936f4b 100644 --- a/src/gui/graphicsview/qgraphicsitemanimation.h +++ b/src/gui/graphicsview/qgraphicsitemanimation.h @@ -1,6 +1,7 @@ /**************************************************************************** ** ** Copyright (C) 2009 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. @@ -9,8 +10,8 @@ ** No Commercial Usage ** This file contains pre-release code and may not be distributed. ** You may use this file in accordance with the terms and conditions -** contained in the either Technology Preview License Agreement or the -** Beta Release License Agreement. +** 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 @@ -20,21 +21,20 @@ ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** -** In addition, as a special exception, Nokia gives you certain -** additional rights. These rights are described in the Nokia Qt LGPL -** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this -** package. +** 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. +** +** +** +** +** +** ** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. ** -** If you are unsure which license is appropriate for your use, please -** contact the sales department at http://qt.nokia.com/contact. ** $QT_END_LICENSE$ ** ****************************************************************************/ diff --git a/src/gui/graphicsview/qgraphicslayout.cpp b/src/gui/graphicsview/qgraphicslayout.cpp index 58c174c..f9800bc 100644 --- a/src/gui/graphicsview/qgraphicslayout.cpp +++ b/src/gui/graphicsview/qgraphicslayout.cpp @@ -1,6 +1,7 @@ /**************************************************************************** ** ** Copyright (C) 2009 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. @@ -9,8 +10,8 @@ ** No Commercial Usage ** This file contains pre-release code and may not be distributed. ** You may use this file in accordance with the terms and conditions -** contained in the either Technology Preview License Agreement or the -** Beta Release License Agreement. +** 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 @@ -20,21 +21,20 @@ ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** -** In addition, as a special exception, Nokia gives you certain -** additional rights. These rights are described in the Nokia Qt LGPL -** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this -** package. +** 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. +** +** +** +** +** +** ** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. ** -** If you are unsure which license is appropriate for your use, please -** contact the sales department at http://qt.nokia.com/contact. ** $QT_END_LICENSE$ ** ****************************************************************************/ @@ -390,8 +390,10 @@ void QGraphicsLayout::widgetEvent(QEvent *e) 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. + 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() */ @@ -417,6 +419,24 @@ void QGraphicsLayout::widgetEvent(QEvent *e) \sa itemAt(), count() */ +/*! + 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/gui/graphicsview/qgraphicslayout.h b/src/gui/graphicsview/qgraphicslayout.h index 1a21e53..e214a62 100644 --- a/src/gui/graphicsview/qgraphicslayout.h +++ b/src/gui/graphicsview/qgraphicslayout.h @@ -1,6 +1,7 @@ /**************************************************************************** ** ** Copyright (C) 2009 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. @@ -9,8 +10,8 @@ ** No Commercial Usage ** This file contains pre-release code and may not be distributed. ** You may use this file in accordance with the terms and conditions -** contained in the either Technology Preview License Agreement or the -** Beta Release License Agreement. +** 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 @@ -20,21 +21,20 @@ ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** -** In addition, as a special exception, Nokia gives you certain -** additional rights. These rights are described in the Nokia Qt LGPL -** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this -** package. +** 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. +** +** +** +** +** +** ** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. ** -** If you are unsure which license is appropriate for your use, please -** contact the sales department at http://qt.nokia.com/contact. ** $QT_END_LICENSE$ ** ****************************************************************************/ @@ -78,6 +78,7 @@ public: protected: QGraphicsLayout(QGraphicsLayoutPrivate &, QGraphicsLayoutItem *); + void addChildLayoutItem(QGraphicsLayoutItem *layoutItem); private: Q_DISABLE_COPY(QGraphicsLayout) diff --git a/src/gui/graphicsview/qgraphicslayout_p.cpp b/src/gui/graphicsview/qgraphicslayout_p.cpp index b93e2d8..6411ca0 100644 --- a/src/gui/graphicsview/qgraphicslayout_p.cpp +++ b/src/gui/graphicsview/qgraphicslayout_p.cpp @@ -1,6 +1,7 @@ /**************************************************************************** ** ** Copyright (C) 2009 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. @@ -9,8 +10,8 @@ ** No Commercial Usage ** This file contains pre-release code and may not be distributed. ** You may use this file in accordance with the terms and conditions -** contained in the either Technology Preview License Agreement or the -** Beta Release License Agreement. +** 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 @@ -20,21 +21,20 @@ ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** -** In addition, as a special exception, Nokia gives you certain -** additional rights. These rights are described in the Nokia Qt LGPL -** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this -** package. +** 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. +** +** +** +** +** +** ** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. ** -** If you are unsure which license is appropriate for your use, please -** contact the sales department at http://qt.nokia.com/contact. ** $QT_END_LICENSE$ ** ****************************************************************************/ diff --git a/src/gui/graphicsview/qgraphicslayout_p.h b/src/gui/graphicsview/qgraphicslayout_p.h index 2ad853b..59c6dba 100644 --- a/src/gui/graphicsview/qgraphicslayout_p.h +++ b/src/gui/graphicsview/qgraphicslayout_p.h @@ -1,6 +1,7 @@ /**************************************************************************** ** ** Copyright (C) 2009 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. @@ -9,8 +10,8 @@ ** No Commercial Usage ** This file contains pre-release code and may not be distributed. ** You may use this file in accordance with the terms and conditions -** contained in the either Technology Preview License Agreement or the -** Beta Release License Agreement. +** 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 @@ -20,21 +21,20 @@ ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** -** In addition, as a special exception, Nokia gives you certain -** additional rights. These rights are described in the Nokia Qt LGPL -** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this -** package. +** 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. +** +** +** +** +** +** ** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. ** -** If you are unsure which license is appropriate for your use, please -** contact the sales department at http://qt.nokia.com/contact. ** $QT_END_LICENSE$ ** ****************************************************************************/ @@ -47,8 +47,8 @@ // ------------- // // This file is not part of the Qt API. It exists for the convenience -// of qapplication_*.cpp, qwidget*.cpp and qfiledialog.cpp. This header -// file may change from version to version without notice, or even be removed. +// of other Qt classes. This header file may change from version to +// version without notice, or even be removed. // // We mean it. // diff --git a/src/gui/graphicsview/qgraphicslayoutitem.cpp b/src/gui/graphicsview/qgraphicslayoutitem.cpp index d33beaf..a04cc88 100644 --- a/src/gui/graphicsview/qgraphicslayoutitem.cpp +++ b/src/gui/graphicsview/qgraphicslayoutitem.cpp @@ -1,6 +1,7 @@ /**************************************************************************** ** ** Copyright (C) 2009 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. @@ -9,8 +10,8 @@ ** No Commercial Usage ** This file contains pre-release code and may not be distributed. ** You may use this file in accordance with the terms and conditions -** contained in the either Technology Preview License Agreement or the -** Beta Release License Agreement. +** 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 @@ -20,21 +21,20 @@ ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** -** In addition, as a special exception, Nokia gives you certain -** additional rights. These rights are described in the Nokia Qt LGPL -** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this -** package. +** 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. +** +** +** +** +** +** ** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. ** -** If you are unsure which license is appropriate for your use, please -** contact the sales department at http://qt.nokia.com/contact. ** $QT_END_LICENSE$ ** ****************************************************************************/ diff --git a/src/gui/graphicsview/qgraphicslayoutitem.h b/src/gui/graphicsview/qgraphicslayoutitem.h index fb25e0c..c02ee87 100644 --- a/src/gui/graphicsview/qgraphicslayoutitem.h +++ b/src/gui/graphicsview/qgraphicslayoutitem.h @@ -1,6 +1,7 @@ /**************************************************************************** ** ** Copyright (C) 2009 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. @@ -9,8 +10,8 @@ ** No Commercial Usage ** This file contains pre-release code and may not be distributed. ** You may use this file in accordance with the terms and conditions -** contained in the either Technology Preview License Agreement or the -** Beta Release License Agreement. +** 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 @@ -20,21 +21,20 @@ ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** -** In addition, as a special exception, Nokia gives you certain -** additional rights. These rights are described in the Nokia Qt LGPL -** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this -** package. +** 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. +** +** +** +** +** +** ** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. ** -** If you are unsure which license is appropriate for your use, please -** contact the sales department at http://qt.nokia.com/contact. ** $QT_END_LICENSE$ ** ****************************************************************************/ diff --git a/src/gui/graphicsview/qgraphicslayoutitem_p.h b/src/gui/graphicsview/qgraphicslayoutitem_p.h index 5bda3ca..7e92154 100644 --- a/src/gui/graphicsview/qgraphicslayoutitem_p.h +++ b/src/gui/graphicsview/qgraphicslayoutitem_p.h @@ -1,6 +1,7 @@ /**************************************************************************** ** ** Copyright (C) 2009 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. @@ -9,8 +10,8 @@ ** No Commercial Usage ** This file contains pre-release code and may not be distributed. ** You may use this file in accordance with the terms and conditions -** contained in the either Technology Preview License Agreement or the -** Beta Release License Agreement. +** 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 @@ -20,21 +21,20 @@ ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** -** In addition, as a special exception, Nokia gives you certain -** additional rights. These rights are described in the Nokia Qt LGPL -** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this -** package. +** 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. +** +** +** +** +** +** ** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. ** -** If you are unsure which license is appropriate for your use, please -** contact the sales department at http://qt.nokia.com/contact. ** $QT_END_LICENSE$ ** ****************************************************************************/ @@ -47,8 +47,8 @@ // ------------- // // This file is not part of the Qt API. It exists for the convenience -// of qapplication_*.cpp, qwidget*.cpp and qfiledialog.cpp. This header -// file may change from version to version without notice, or even be removed. +// of other Qt classes. This header file may change from version to +// version without notice, or even be removed. // // We mean it. // diff --git a/src/gui/graphicsview/qgraphicslinearlayout.cpp b/src/gui/graphicsview/qgraphicslinearlayout.cpp index 185780a..0aa68df 100644 --- a/src/gui/graphicsview/qgraphicslinearlayout.cpp +++ b/src/gui/graphicsview/qgraphicslinearlayout.cpp @@ -1,6 +1,7 @@ /**************************************************************************** ** ** Copyright (C) 2009 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. @@ -9,8 +10,8 @@ ** No Commercial Usage ** This file contains pre-release code and may not be distributed. ** You may use this file in accordance with the terms and conditions -** contained in the either Technology Preview License Agreement or the -** Beta Release License Agreement. +** 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 @@ -20,21 +21,20 @@ ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** -** In addition, as a special exception, Nokia gives you certain -** additional rights. These rights are described in the Nokia Qt LGPL -** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this -** package. +** 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. +** +** +** +** +** +** ** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. ** -** If you are unsure which license is appropriate for your use, please -** contact the sales department at http://qt.nokia.com/contact. ** $QT_END_LICENSE$ ** ****************************************************************************/ @@ -277,7 +277,7 @@ void QGraphicsLinearLayout::insertItem(int index, QGraphicsLayoutItem *item) Q_ASSERT(item); d->fixIndex(&index); d->engine.insertRow(index, d->orientation); - new QGridLayoutItem(&d->engine, item, d->gridRow(index), d->gridColumn(index)); + new QGridLayoutItem(&d->engine, item, d->gridRow(index), d->gridColumn(index), 1, 1, 0, index); invalidate(); } @@ -471,6 +471,7 @@ int QGraphicsLinearLayout::count() const /*! \reimp + When iterating from 0 and up, it will return the items in the visual arranged order. */ QGraphicsLayoutItem *QGraphicsLinearLayout::itemAt(int index) const { diff --git a/src/gui/graphicsview/qgraphicslinearlayout.h b/src/gui/graphicsview/qgraphicslinearlayout.h index 4d6c944..742392e 100644 --- a/src/gui/graphicsview/qgraphicslinearlayout.h +++ b/src/gui/graphicsview/qgraphicslinearlayout.h @@ -1,6 +1,7 @@ /**************************************************************************** ** ** Copyright (C) 2009 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. @@ -9,8 +10,8 @@ ** No Commercial Usage ** This file contains pre-release code and may not be distributed. ** You may use this file in accordance with the terms and conditions -** contained in the either Technology Preview License Agreement or the -** Beta Release License Agreement. +** 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 @@ -20,21 +21,20 @@ ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** -** In addition, as a special exception, Nokia gives you certain -** additional rights. These rights are described in the Nokia Qt LGPL -** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this -** package. +** 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. +** +** +** +** +** +** ** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. ** -** If you are unsure which license is appropriate for your use, please -** contact the sales department at http://qt.nokia.com/contact. ** $QT_END_LICENSE$ ** ****************************************************************************/ diff --git a/src/gui/graphicsview/qgraphicsproxywidget.cpp b/src/gui/graphicsview/qgraphicsproxywidget.cpp index 4f626a2..15b9ff3 100644 --- a/src/gui/graphicsview/qgraphicsproxywidget.cpp +++ b/src/gui/graphicsview/qgraphicsproxywidget.cpp @@ -1,6 +1,7 @@ /**************************************************************************** ** ** Copyright (C) 2009 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. @@ -9,8 +10,8 @@ ** No Commercial Usage ** This file contains pre-release code and may not be distributed. ** You may use this file in accordance with the terms and conditions -** contained in the either Technology Preview License Agreement or the -** Beta Release License Agreement. +** 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 @@ -20,21 +21,20 @@ ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** -** In addition, as a special exception, Nokia gives you certain -** additional rights. These rights are described in the Nokia Qt LGPL -** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this -** package. +** 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. +** +** +** +** +** +** ** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. ** -** If you are unsure which license is appropriate for your use, please -** contact the sales department at http://qt.nokia.com/contact. ** $QT_END_LICENSE$ ** ****************************************************************************/ @@ -390,7 +390,7 @@ QWidget *QGraphicsProxyWidgetPrivate::findFocusChild(QWidget *child, bool next) if (child->isEnabled() && child->isVisibleTo(widget) && (child->focusPolicy() & Qt::TabFocus)) { - return child; + 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)); diff --git a/src/gui/graphicsview/qgraphicsproxywidget.h b/src/gui/graphicsview/qgraphicsproxywidget.h index 02e1335..ea65b4d 100644 --- a/src/gui/graphicsview/qgraphicsproxywidget.h +++ b/src/gui/graphicsview/qgraphicsproxywidget.h @@ -1,6 +1,7 @@ /**************************************************************************** ** ** Copyright (C) 2009 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. @@ -9,8 +10,8 @@ ** No Commercial Usage ** This file contains pre-release code and may not be distributed. ** You may use this file in accordance with the terms and conditions -** contained in the either Technology Preview License Agreement or the -** Beta Release License Agreement. +** 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 @@ -20,21 +21,20 @@ ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** -** In addition, as a special exception, Nokia gives you certain -** additional rights. These rights are described in the Nokia Qt LGPL -** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this -** package. +** 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. +** +** +** +** +** +** ** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. ** -** If you are unsure which license is appropriate for your use, please -** contact the sales department at http://qt.nokia.com/contact. ** $QT_END_LICENSE$ ** ****************************************************************************/ diff --git a/src/gui/graphicsview/qgraphicsproxywidget_p.h b/src/gui/graphicsview/qgraphicsproxywidget_p.h index e2be89c..e51c4d3 100644 --- a/src/gui/graphicsview/qgraphicsproxywidget_p.h +++ b/src/gui/graphicsview/qgraphicsproxywidget_p.h @@ -1,6 +1,7 @@ /**************************************************************************** ** ** Copyright (C) 2009 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. @@ -9,8 +10,8 @@ ** No Commercial Usage ** This file contains pre-release code and may not be distributed. ** You may use this file in accordance with the terms and conditions -** contained in the either Technology Preview License Agreement or the -** Beta Release License Agreement. +** 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 @@ -20,21 +21,20 @@ ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** -** In addition, as a special exception, Nokia gives you certain -** additional rights. These rights are described in the Nokia Qt LGPL -** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this -** package. +** 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. +** +** +** +** +** +** ** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. ** -** If you are unsure which license is appropriate for your use, please -** contact the sales department at http://qt.nokia.com/contact. ** $QT_END_LICENSE$ ** ****************************************************************************/ @@ -47,8 +47,8 @@ // ------------- // // This file is not part of the Qt API. It exists for the convenience -// of qapplication_*.cpp, qwidget*.cpp and qfiledialog.cpp. This header -// file may change from version to version without notice, or even be removed. +// of other Qt classes. This header file may change from version to +// version without notice, or even be removed. // // We mean it. // diff --git a/src/gui/graphicsview/qgraphicsscene.cpp b/src/gui/graphicsview/qgraphicsscene.cpp index a2a92b8..0655ecc 100644 --- a/src/gui/graphicsview/qgraphicsscene.cpp +++ b/src/gui/graphicsview/qgraphicsscene.cpp @@ -1,6 +1,7 @@ /**************************************************************************** ** ** Copyright (C) 2009 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. @@ -9,8 +10,8 @@ ** No Commercial Usage ** This file contains pre-release code and may not be distributed. ** You may use this file in accordance with the terms and conditions -** contained in the either Technology Preview License Agreement or the -** Beta Release License Agreement. +** 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 @@ -20,21 +21,20 @@ ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** -** In addition, as a special exception, Nokia gives you certain -** additional rights. These rights are described in the Nokia Qt LGPL -** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this -** package. +** 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. +** +** +** +** +** +** ** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. ** -** If you are unsure which license is appropriate for your use, please -** contact the sales department at http://qt.nokia.com/contact. ** $QT_END_LICENSE$ ** ****************************************************************************/ @@ -244,11 +244,13 @@ #include <QtGui/qtransform.h> #include <QtGui/qgesture.h> #include <QtGui/qinputcontext.h> +#include <QtGui/qgraphicseffect.h> #include <private/qapplication_p.h> #include <private/qobject_p.h> #ifdef Q_WS_X11 #include <private/qt_x11_p.h> #endif +#include <private/qgraphicseffect_p.h> QT_BEGIN_NAMESPACE @@ -265,12 +267,13 @@ static void _q_hoverFromMouseEvent(QGraphicsSceneHoverEvent *hover, const QGraph hover->setAccepted(mouseEvent->isAccepted()); } +int QGraphicsScenePrivate::changedSignalIndex; + /*! \internal */ QGraphicsScenePrivate::QGraphicsScenePrivate() - : changedSignalMask(0), - indexMethod(QGraphicsScene::BspTreeIndex), + : indexMethod(QGraphicsScene::BspTreeIndex), index(0), lastItemCount(0), hasSceneRect(false), @@ -285,8 +288,10 @@ QGraphicsScenePrivate::QGraphicsScenePrivate() focusItem(0), lastFocusItem(0), tabFocusFirst(0), - activeWindow(0), + activePanel(0), + lastActivePanel(0), activationRefCount(0), + childExplicitActivation(0), lastMouseGrabberItem(0), lastMouseGrabberItemHasImplicitMouseGrab(false), dragDropItem(0), @@ -311,7 +316,9 @@ void QGraphicsScenePrivate::init() index = new QGraphicsSceneBspTreeIndex(q); // Keep this index so we can check for connected slots later on. - changedSignalMask = (1 << q->metaObject()->indexOfSignal("changed(QList<QRectF>)")); + if (!changedSignalIndex) { + changedSignalIndex = signalIndex("changed(QList<QRectF>)"); + } qApp->d_func()->scene_list.append(q); q->update(); } @@ -343,7 +350,7 @@ void QGraphicsScenePrivate::_q_emitUpdated() // 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 (connectedSignals[0] & changedSignalMask) { + if (isSignalConnected(changedSignalIndex)) { for (int i = 0; i < views.size(); ++i) { QGraphicsView *view = views.at(i); if (!view->d_func()->connectedToScene) { @@ -511,10 +518,12 @@ void QGraphicsScenePrivate::removeItemHelper(QGraphicsItem *item) focusItem = 0; if (item == lastFocusItem) lastFocusItem = 0; - if (item == activeWindow) { + if (item == activePanel) { // ### deactivate... - activeWindow = 0; + activePanel = 0; } + if (item == lastActivePanel) + lastActivePanel = 0; // Disable selectionChanged() for individual items ++selectionChanging; @@ -563,6 +572,69 @@ void QGraphicsScenePrivate::removeItemHelper(QGraphicsItem *item) /*! \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) { @@ -584,19 +656,6 @@ void QGraphicsScenePrivate::setFocusItemHelper(QGraphicsItem *item, return; } - // Auto-update focus proxy. The closest parent that detects - // focus proxies is updated as the proxy gains or loses focus. - if (item) { - QGraphicsItem *p = item->d_ptr->parent; - while (p) { - if (p->d_ptr->flags & QGraphicsItem::ItemAutoDetectsFocusProxy) { - p->setFocusProxy(item); - break; - } - p = p->d_ptr->parent; - } - } - if (focusItem) { QFocusEvent event(QEvent::FocusOut, focusReason); lastFocusItem = focusItem; @@ -609,9 +668,14 @@ void QGraphicsScenePrivate::setFocusItemHelper(QGraphicsItem *item, QInputMethodEvent imEvent; sendEvent(lastFocusItem, &imEvent); - // Close any external input method panel - for (int i = 0; i < views.size(); ++i) - views.at(i)->inputContext()->reset(); + // 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. + if (item) { + for (int i = 0; i < views.size(); ++i) + views.at(i)->inputContext()->reset(); + } } } @@ -665,8 +729,8 @@ void QGraphicsScenePrivate::removePopup(QGraphicsWidget *widget, bool itemIsDyin if (focusItem && popupWidgets.isEmpty()) { QFocusEvent event(QEvent::FocusIn, Qt::PopupFocusReason); sendEvent(focusItem, &event); - } else { - ungrabKeyboard((QGraphicsItem *)widget, itemIsDying); + } else if (keyboardGrabberItems.contains(static_cast<QGraphicsItem *>(widget))) { + ungrabKeyboard(static_cast<QGraphicsItem *>(widget), itemIsDying); } if (!itemIsDying && widget->isVisible()) { widget->hide(); @@ -861,19 +925,19 @@ QList<QGraphicsItem *> QGraphicsScenePrivate::itemsAtPosition(const QPoint &scre Q_Q(const QGraphicsScene); QGraphicsView *view = widget ? qobject_cast<QGraphicsView *>(widget->parentWidget()) : 0; if (!view) - return q->items(scenePos, Qt::IntersectsItemShape, Qt::AscendingOrder, QTransform()); + return q->items(scenePos, Qt::IntersectsItemShape, Qt::DescendingOrder, QTransform()); const QRectF pointRect(QPointF(widget->mapFromGlobal(screenPos)), QSizeF(1, 1)); if (!view->isTransformed()) - return q->items(pointRect, Qt::IntersectsItemShape, Qt::AscendingOrder); + return q->items(pointRect, Qt::IntersectsItemShape, Qt::DescendingOrder); const QTransform viewTransform = view->viewportTransform(); if (viewTransform.type() <= QTransform::TxScale) { return q->items(viewTransform.inverted().mapRect(pointRect), Qt::IntersectsItemShape, - Qt::AscendingOrder, viewTransform); + Qt::DescendingOrder, viewTransform); } return q->items(viewTransform.inverted().map(pointRect), Qt::IntersectsItemShape, - Qt::AscendingOrder, viewTransform); + Qt::DescendingOrder, viewTransform); } /*! @@ -1071,8 +1135,9 @@ void QGraphicsScenePrivate::mousePressEventHandler(QGraphicsSceneMouseEvent *mou } // Update window activation. - QGraphicsWidget *newActiveWindow = windowForItem(cachedItemsUnderMouse.value(0)); - if (newActiveWindow != activeWindow) + QGraphicsItem *topItem = cachedItemsUnderMouse.value(0); + QGraphicsWidget *newActiveWindow = topItem ? topItem->window() : 0; + if (newActiveWindow != q->activeWindow()) q->setActiveWindow(newActiveWindow); // Set focus on the topmost enabled item that can take focus. @@ -1107,7 +1172,7 @@ void QGraphicsScenePrivate::mousePressEventHandler(QGraphicsSceneMouseEvent *mou // check if the item we are sending to are disabled (before we send the event) bool disabled = !item->isEnabled(); - bool isWindow = item->isWindow(); + bool isPanel = item->isPanel(); if (mouseEvent->type() == QEvent::GraphicsSceneMouseDoubleClick && item != lastMouseGrabberItem && lastMouseGrabberItem) { // If this item is different from the item that received the last @@ -1148,8 +1213,8 @@ void QGraphicsScenePrivate::mousePressEventHandler(QGraphicsSceneMouseEvent *mou } ungrabMouse(item, /* itemIsDying = */ dontSendUngrabEvents); - // Don't propagate through windows. - if (isWindow) + // Don't propagate through panels. + if (isPanel) break; } @@ -1171,18 +1236,6 @@ void QGraphicsScenePrivate::mousePressEventHandler(QGraphicsSceneMouseEvent *mou } } -QGraphicsWidget *QGraphicsScenePrivate::windowForItem(const QGraphicsItem *item) const -{ - if (!item) - return 0; - do { - if (item->isWidget()) - return static_cast<const QGraphicsWidget *>(item)->window(); - item = item->parentItem(); - } while (item); - return 0; -} - /*! \internal @@ -1340,6 +1393,7 @@ QGraphicsScene::QGraphicsScene(qreal x, qreal y, qreal width, qreal height, QObj QGraphicsScene::~QGraphicsScene() { Q_D(QGraphicsScene); + // Remove this scene from qApp's global scene list. qApp->d_func()->scene_list.removeAll(this); @@ -1530,7 +1584,7 @@ void QGraphicsScene::setItemIndexMethod(ItemIndexMethod method) d->indexMethod = method; - QList<QGraphicsItem *> oldItems = d->index->items(Qt::AscendingOrder); + QList<QGraphicsItem *> oldItems = d->index->items(Qt::DescendingOrder); delete d->index; if (method == BspTreeIndex) d->index = new QGraphicsSceneBspTreeIndex(this); @@ -1639,7 +1693,7 @@ QRectF QGraphicsScene::itemsBoundingRect() const QList<QGraphicsItem *> QGraphicsScene::items() const { Q_D(const QGraphicsScene); - return d->index->items(Qt::AscendingOrder); + return d->index->items(Qt::DescendingOrder); } /*! @@ -1670,7 +1724,7 @@ QList<QGraphicsItem *> QGraphicsScene::items(Qt::SortOrder order) const QList<QGraphicsItem *> QGraphicsScene::items(const QPointF &pos) const { Q_D(const QGraphicsScene); - return d->index->items(pos, Qt::IntersectsItemShape, Qt::AscendingOrder); + return d->index->items(pos, Qt::IntersectsItemShape, Qt::DescendingOrder); } /*! @@ -1692,7 +1746,7 @@ QList<QGraphicsItem *> QGraphicsScene::items(const QPointF &pos) const QList<QGraphicsItem *> QGraphicsScene::items(const QRectF &rectangle, Qt::ItemSelectionMode mode) const { Q_D(const QGraphicsScene); - return d->index->items(rectangle, mode, Qt::AscendingOrder); + return d->index->items(rectangle, mode, Qt::DescendingOrder); } /*! @@ -1740,7 +1794,7 @@ QList<QGraphicsItem *> QGraphicsScene::items(const QRectF &rectangle, Qt::ItemSe QList<QGraphicsItem *> QGraphicsScene::items(const QPolygonF &polygon, Qt::ItemSelectionMode mode) const { Q_D(const QGraphicsScene); - return d->index->items(polygon, mode, Qt::AscendingOrder); + return d->index->items(polygon, mode, Qt::DescendingOrder); } /*! @@ -1762,7 +1816,7 @@ QList<QGraphicsItem *> QGraphicsScene::items(const QPolygonF &polygon, Qt::ItemS QList<QGraphicsItem *> QGraphicsScene::items(const QPainterPath &path, Qt::ItemSelectionMode mode) const { Q_D(const QGraphicsScene); - return d->index->items(path, mode, Qt::AscendingOrder); + return d->index->items(path, mode, Qt::DescendingOrder); } /*! @@ -1874,7 +1928,7 @@ QList<QGraphicsItem *> QGraphicsScene::collidingItems(const QGraphicsItem *item, // Does not support ItemIgnoresTransformations. QList<QGraphicsItem *> tmp; - foreach (QGraphicsItem *itemInVicinity, d->index->estimateItems(item->sceneBoundingRect(), Qt::AscendingOrder)) { + foreach (QGraphicsItem *itemInVicinity, d->index->estimateItems(item->sceneBoundingRect(), Qt::DescendingOrder)) { if (item != itemInVicinity && item->collidesWithItem(itemInVicinity, mode)) tmp << itemInVicinity; } @@ -1918,7 +1972,7 @@ QGraphicsItem *QGraphicsScene::itemAt(const QPointF &position) const QGraphicsItem *QGraphicsScene::itemAt(const QPointF &position, const QTransform &deviceTransform) const { QList<QGraphicsItem *> itemsAtPoint = items(position, Qt::IntersectsItemShape, - Qt::AscendingOrder, deviceTransform); + Qt::DescendingOrder, deviceTransform); return itemsAtPoint.isEmpty() ? 0 : itemsAtPoint.first(); } @@ -2073,7 +2127,7 @@ void QGraphicsScene::setSelectionArea(const QPainterPath &path, Qt::ItemSelectio bool changed = false; // Set all items in path to selected. - foreach (QGraphicsItem *item, items(path, mode, Qt::AscendingOrder, deviceTransform)) { + foreach (QGraphicsItem *item, items(path, mode, Qt::DescendingOrder, deviceTransform)) { if (item->flags() & QGraphicsItem::ItemIsSelectable) { if (!item->isSelected()) changed = true; @@ -2229,6 +2283,9 @@ void QGraphicsScene::destroyItemGroup(QGraphicsItemGroup *group) 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() */ @@ -2351,13 +2408,33 @@ void QGraphicsScene::addItem(QGraphicsItem *item) // Deliver post-change notification item->itemChange(QGraphicsItem::ItemSceneHasChanged, newSceneVariant); - // Auto-activate the first inactive window if the scene is active. - if (d->activationRefCount > 0 && !d->activeWindow && item->isWindow()) - setActiveWindow(static_cast<QGraphicsWidget *>(item)); + // 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; + } + } // Ensure that newly added items that have subfocus set, gain // focus automatically if there isn't a focus item already. - if (!d->focusItem && item->focusItem()) + if (!d->focusItem && item != d->lastFocusItem && item->focusItem() == item) item->focusItem()->setFocus(); d->updateInputMethodSensitivityInViews(); @@ -2706,7 +2783,7 @@ bool QGraphicsScene::hasFocus() const void QGraphicsScene::setFocus(Qt::FocusReason focusReason) { Q_D(QGraphicsScene); - if (d->hasFocus) + if (d->hasFocus || !isActive()) return; QFocusEvent event(QEvent::FocusIn, focusReason); QCoreApplication::sendEvent(this, &event); @@ -2894,7 +2971,7 @@ void QGraphicsScene::update(const QRectF &rect) // 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->connectedSignals[0] & d->changedSignalMask) && !d->views.isEmpty(); + bool directUpdates = !(d->isSignalConnected(d->changedSignalIndex)) && !d->views.isEmpty(); if (rect.isNull()) { d->updateAll = true; d->updatedRects.clear(); @@ -3125,40 +3202,43 @@ bool QGraphicsScene::event(QEvent *event) case QEvent::InputMethod: inputMethodEvent(static_cast<QInputMethodEvent *>(event)); break; - case QEvent::WindowActivate: { + case QEvent::WindowActivate: if (!d->activationRefCount++) { - // Notify all non-window widgets. - foreach (QGraphicsItem *item, items()) { - if (item->isWidget() && item->isVisible() && !item->isWindow() && !item->parentWidget()) { - QEvent event(QEvent::WindowActivate); - QApplication::sendEvent(static_cast<QGraphicsWidget *>(item), &event); + 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); } } - - // Restore window activation. - QGraphicsItem *nextFocusItem = d->focusItem ? d->focusItem : d->lastFocusItem; - if (nextFocusItem && nextFocusItem->window()) - setActiveWindow(static_cast<QGraphicsWidget *>(nextFocusItem)); - else if (d->tabFocusFirst && d->tabFocusFirst->isWindow()) - setActiveWindow(d->tabFocusFirst); } break; - } - case QEvent::WindowDeactivate: { + case QEvent::WindowDeactivate: if (!--d->activationRefCount) { - // Remove window activation. - setActiveWindow(0); - - // Notify all non-window widgets. - foreach (QGraphicsItem *item, items()) { - if (item->isWidget() && item->isVisible() && !item->isWindow() && !item->parentWidget()) { - QEvent event(QEvent::WindowDeactivate); - QApplication::sendEvent(static_cast<QGraphicsWidget *>(item), &event); + 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(); @@ -3521,8 +3601,8 @@ bool QGraphicsScenePrivate::dispatchHoverEvent(QGraphicsSceneHoverEvent *hoverEv QGraphicsItem *commonAncestorItem = (item && !hoverItems.isEmpty()) ? item->commonAncestorItem(hoverItems.last()) : 0; while (commonAncestorItem && !itemAcceptsHoverEvents_helper(commonAncestorItem)) commonAncestorItem = commonAncestorItem->parentItem(); - if (commonAncestorItem && commonAncestorItem->window() != item->window()) { - // The common ancestor isn't in the same window as the two hovered + if (commonAncestorItem && commonAncestorItem->panel() != item->panel()) { + // The common ancestor isn't in the same panel as the two hovered // items. commonAncestorItem = 0; } @@ -3543,8 +3623,8 @@ bool QGraphicsScenePrivate::dispatchHoverEvent(QGraphicsSceneHoverEvent *hoverEv QGraphicsItem *parent = item; while (parent && parent != commonAncestorItem) { parents.prepend(parent); - if (parent->isWindow()) { - // Stop at the window - we don't deliver beyond this point. + if (parent->isPanel()) { + // Stop at the panel - we don't deliver beyond this point. break; } parent = parent->parentItem(); @@ -3621,7 +3701,7 @@ void QGraphicsScene::keyPressEvent(QKeyEvent *keyEvent) // is filtered out, stop propagating it. if (!d->sendEvent(p, keyEvent)) break; - } while (!keyEvent->isAccepted() && !p->isWindow() && (p = p->parentItem())); + } while (!keyEvent->isAccepted() && !p->isPanel() && (p = p->parentItem())); } else { keyEvent->ignore(); } @@ -3651,7 +3731,7 @@ void QGraphicsScene::keyReleaseEvent(QKeyEvent *keyEvent) // is filtered out, stop propagating it. if (!d->sendEvent(p, keyEvent)) break; - } while (!keyEvent->isAccepted() && !p->isWindow() && (p = p->parentItem())); + } while (!keyEvent->isAccepted() && !p->isPanel() && (p = p->parentItem())); } else { keyEvent->ignore(); } @@ -3813,9 +3893,9 @@ void QGraphicsScene::wheelEvent(QGraphicsSceneWheelEvent *wheelEvent) wheelEvent->setPos(item->d_ptr->genericMapFromScene(wheelEvent->scenePos(), wheelEvent->widget())); wheelEvent->accept(); - bool isWindow = item->isWindow(); + bool isPanel = item->isPanel(); d->sendEvent(item, wheelEvent); - if (isWindow || wheelEvent->isAccepted()) + if (isPanel || wheelEvent->isAccepted()) break; } } @@ -4291,7 +4371,7 @@ void QGraphicsScenePrivate::drawItems(QPainter *painter, const QTransform *const if (viewTransform) exposedSceneRect = viewTransform->inverted().mapRect(exposedSceneRect); } - const QList<QGraphicsItem *> tli = index->estimateTopLevelItems(exposedSceneRect, Qt::DescendingOrder); + 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); } @@ -4299,7 +4379,7 @@ void QGraphicsScenePrivate::drawItems(QPainter *painter, const QTransform *const void QGraphicsScenePrivate::drawSubtreeRecursive(QGraphicsItem *item, QPainter *painter, const QTransform *const viewTransform, QRegion *exposedRegion, QWidget *widget, - qreal parentOpacity) + qreal parentOpacity, const QTransform *const effectTransform) { Q_ASSERT(item); @@ -4348,11 +4428,12 @@ void QGraphicsScenePrivate::drawSubtreeRecursive(QGraphicsItem *item, QPainter * const bool itemClipsChildrenToShape = (item->d_ptr->flags & QGraphicsItem::ItemClipsChildrenToShape); bool drawItem = itemHasContents && !itemIsFullyTransparent; if (drawItem) { - const QRectF brect = adjustedItemBoundingRect(item); + const QRectF brect = adjustedItemEffectiveBoundingRect(item); ENSURE_TRANSFORM_PTR QRect viewBoundingRect = translateOnlyTransform ? brect.translated(transformPtr->dx(), transformPtr->dy()).toRect() : transformPtr->mapRect(brect).toRect(); - item->d_ptr->paintedViewBoundingRects.insert(widget, viewBoundingRect); + if (widget) + item->d_ptr->paintedViewBoundingRects.insert(widget, viewBoundingRect); viewBoundingRect.adjust(-1, -1, 1, 1); drawItem = exposedRegion ? exposedRegion->intersects(viewBoundingRect) : !viewBoundingRect.isEmpty(); if (!drawItem) { @@ -4366,14 +4447,52 @@ void QGraphicsScenePrivate::drawSubtreeRecursive(QGraphicsItem *item, QPainter * } } // else we know for sure this item has children we must process. + if (itemHasChildren && itemClipsChildrenToShape) + ENSURE_TRANSFORM_PTR; + + if (item->d_ptr->graphicsEffect && item->d_ptr->graphicsEffect->isEnabled()) { + ENSURE_TRANSFORM_PTR; + QGraphicsItemPaintInfo info(viewTransform, transformPtr, effectTransform, exposedRegion, widget, &styleOptionTmp, + painter, opacity, wasDirtyParentSceneTransform, drawItem); + QGraphicsEffectSource *source = item->d_ptr->graphicsEffect->d_func()->source; + QGraphicsItemEffectSourcePrivate *sourced = static_cast<QGraphicsItemEffectSourcePrivate *> + (source->d_func()); + sourced->info = &info; + const QTransform restoreTransform = painter->worldTransform(); + if (effectTransform) + painter->setWorldTransform(*transformPtr * *effectTransform); + else + painter->setWorldTransform(*transformPtr); + painter->setOpacity(opacity); + item->d_ptr->graphicsEffect->draw(painter, source); + painter->setWorldTransform(restoreTransform); + sourced->info = 0; + } else { + draw(item, painter, viewTransform, transformPtr, exposedRegion, widget, opacity, + effectTransform, wasDirtyParentSceneTransform, drawItem); + } +} + +void QGraphicsScenePrivate::draw(QGraphicsItem *item, QPainter *painter, const QTransform *const viewTransform, + const QTransform *const transformPtr, QRegion *exposedRegion, QWidget *widget, + qreal opacity, const QTransform *effectTransform, + bool wasDirtyParentSceneTransform, bool drawItem) +{ + const bool itemIsFullyTransparent = (opacity < 0.0001); + const bool itemClipsChildrenToShape = (item->d_ptr->flags & QGraphicsItem::ItemClipsChildrenToShape); + const bool itemHasChildren = !item->d_ptr->children.isEmpty(); + int i = 0; if (itemHasChildren) { item->d_ptr->ensureSortedChildren(); if (itemClipsChildrenToShape) { painter->save(); - ENSURE_TRANSFORM_PTR - painter->setWorldTransform(*transformPtr); + Q_ASSERT(transformPtr); + if (effectTransform) + painter->setWorldTransform(*transformPtr * *effectTransform); + else + painter->setWorldTransform(*transformPtr); painter->setClipPath(item->shape(), Qt::IntersectClip); } @@ -4386,15 +4505,15 @@ void QGraphicsScenePrivate::drawSubtreeRecursive(QGraphicsItem *item, QPainter * break; if (itemIsFullyTransparent && !(child->d_ptr->flags & QGraphicsItem::ItemIgnoresParentOpacity)) continue; - drawSubtreeRecursive(child, painter, viewTransform, exposedRegion, widget, opacity); + drawSubtreeRecursive(child, painter, viewTransform, exposedRegion, widget, opacity, effectTransform); } } // Draw item if (drawItem) { Q_ASSERT(!itemIsFullyTransparent); - Q_ASSERT(itemHasContents); - ENSURE_TRANSFORM_PTR + Q_ASSERT(!(item->d_ptr->flags & QGraphicsItem::ItemHasNoContents)); + Q_ASSERT(transformPtr); item->d_ptr->initStyleOption(&styleOptionTmp, *transformPtr, exposedRegion ? *exposedRegion : QRegion(), exposedRegion == 0); @@ -4403,8 +4522,12 @@ void QGraphicsScenePrivate::drawSubtreeRecursive(QGraphicsItem *item, QPainter * if (savePainter) painter->save(); - if (!itemHasChildren || !itemClipsChildrenToShape) - painter->setWorldTransform(*transformPtr); + if (!itemHasChildren || !itemClipsChildrenToShape) { + if (effectTransform) + painter->setWorldTransform(*transformPtr * *effectTransform); + else + painter->setWorldTransform(*transformPtr); + } if (itemClipsToShape) painter->setClipPath(item->shape(), Qt::IntersectClip); @@ -4427,7 +4550,7 @@ void QGraphicsScenePrivate::drawSubtreeRecursive(QGraphicsItem *item, QPainter * child->d_ptr->dirtySceneTransform = 1; if (itemIsFullyTransparent && !(child->d_ptr->flags & QGraphicsItem::ItemIgnoresParentOpacity)) continue; - drawSubtreeRecursive(child, painter, viewTransform, exposedRegion, widget, opacity); + drawSubtreeRecursive(child, painter, viewTransform, exposedRegion, widget, opacity, effectTransform); } } @@ -4473,7 +4596,7 @@ void QGraphicsScenePrivate::markDirty(QGraphicsItem *item, const QRectF &rect, b 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 ((connectedSignals[0] & changedSignalMask) || views.isEmpty()) { + 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. @@ -4490,7 +4613,8 @@ void QGraphicsScenePrivate::markDirty(QGraphicsItem *item, const QRectF &rect, b return; } - bool hasNoContents = item->d_ptr->flags & QGraphicsItem::ItemHasNoContents; + bool hasNoContents = item->d_ptr->flags & QGraphicsItem::ItemHasNoContents + && !item->d_ptr->graphicsEffect; if (!hasNoContents) { item->d_ptr->dirty = 1; if (fullItemUpdate) @@ -4510,8 +4634,12 @@ void QGraphicsScenePrivate::markDirty(QGraphicsItem *item, const QRectF &rect, b item->d_ptr->ignoreOpacity = 1; QGraphicsItem *p = item->d_ptr->parent; - while (p && !p->d_ptr->dirtyChildren) { + while (p) { p->d_ptr->dirtyChildren = 1; + if (p->d_ptr->graphicsEffect && p->d_ptr->graphicsEffect->isEnabled()) { + p->d_ptr->dirty = 1; + p->d_ptr->fullUpdatePending = 1; + } p = p->d_ptr->parent; } } @@ -4576,11 +4704,15 @@ void QGraphicsScenePrivate::processDirtyItemsRecursive(QGraphicsItem *item, bool return; } - const bool itemHasContents = !(item->d_ptr->flags & QGraphicsItem::ItemHasNoContents); + bool itemHasContents = !(item->d_ptr->flags & QGraphicsItem::ItemHasNoContents); const bool itemHasChildren = !item->d_ptr->children.isEmpty(); - if (!itemHasContents && !itemHasChildren) { - resetDirtyItem(item); - return; // Item has neither contents nor children!(?) + 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); @@ -4619,8 +4751,8 @@ void QGraphicsScenePrivate::processDirtyItemsRecursive(QGraphicsItem *item, bool // Process item. if (item->d_ptr->dirty || item->d_ptr->paintedViewBoundingRectsNeedRepaint) { - const bool useCompatUpdate = views.isEmpty() || (connectedSignals[0] & changedSignalMask); - const QRectF itemBoundingRect = adjustedItemBoundingRect(item); + 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 @@ -4828,7 +4960,7 @@ bool QGraphicsScene::focusNextPrevChild(bool next) if (widget->flags() & QGraphicsItem::ItemIsFocusable && widget->isEnabled() && widget->isVisibleTo(0) && (widget->focusPolicy() & Qt::TabFocus) - && (!item || !item->isWindow() || item->isAncestorOf(widget)) + && (!item || !item->isPanel() || item->isAncestorOf(widget)) ) { setFocusItem(widget, next ? Qt::TabFocusReason : Qt::BacktabFocusReason); return true; @@ -5015,6 +5147,49 @@ void QGraphicsScene::setPalette(const QPalette &palette) } /*! + \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 there is no window is currently @@ -5025,7 +5200,9 @@ void QGraphicsScene::setPalette(const QPalette &palette) QGraphicsWidget *QGraphicsScene::activeWindow() const { Q_D(const QGraphicsScene); - return d->activeWindow; + if (d->activePanel && d->activePanel->isWindow()) + return static_cast<QGraphicsWidget *>(d->activePanel); + return 0; } /*! @@ -5038,61 +5215,34 @@ QGraphicsWidget *QGraphicsScene::activeWindow() const */ void QGraphicsScene::setActiveWindow(QGraphicsWidget *widget) { - Q_D(QGraphicsScene); if (widget && widget->scene() != this) { qWarning("QGraphicsScene::setActiveWindow: widget %p must be part of this scene", widget); return; } - // Activate the widget's window. - QGraphicsWidget *window = widget ? widget->window() : 0; - if (window == d->activeWindow) - return; - - // Deactivate the last active window. - if (d->activeWindow) { - if (QGraphicsWidget *fw = d->activeWindow->focusWidget()) { - // Remove focus from the current focus item. - if (fw == focusItem()) - setFocusItem(0, Qt::ActiveWindowFocusReason); - } - - QEvent event(QEvent::WindowDeactivate); - QApplication::sendEvent(d->activeWindow, &event); - } - - // Update activate state. - d->activeWindow = window; - QEvent event(QEvent::ActivationChange); - QApplication::sendEvent(this, &event); - - // Activate - if (window) { - QEvent event(QEvent::WindowActivate); - QApplication::sendEvent(window, &event); + // 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 = window->parentItem(); + QGraphicsItem *parent = panel->parentItem(); // Raise ### inefficient for toplevels foreach (QGraphicsItem *sibling, parent ? parent->children() : items()) { - if (sibling != window && sibling->isWidget() - && static_cast<QGraphicsWidget *>(sibling)->isWindow()) { + if (sibling != panel && sibling->isWindow()) siblingWindows << sibling; - } } // Find the highest z value. - qreal z = window->zValue(); + 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); - window->setZValue(z + litt); - - if (QGraphicsWidget *focusChild = window->focusWidget()) - focusChild->setFocus(Qt::ActiveWindowFocusReason); + panel->setZValue(z + litt); } } diff --git a/src/gui/graphicsview/qgraphicsscene.h b/src/gui/graphicsview/qgraphicsscene.h index 0ef9f04..ba47530 100644 --- a/src/gui/graphicsview/qgraphicsscene.h +++ b/src/gui/graphicsview/qgraphicsscene.h @@ -1,6 +1,7 @@ /**************************************************************************** ** ** Copyright (C) 2009 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. @@ -9,8 +10,8 @@ ** No Commercial Usage ** This file contains pre-release code and may not be distributed. ** You may use this file in accordance with the terms and conditions -** contained in the either Technology Preview License Agreement or the -** Beta Release License Agreement. +** 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 @@ -20,21 +21,20 @@ ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** -** In addition, as a special exception, Nokia gives you certain -** additional rights. These rights are described in the Nokia Qt LGPL -** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this -** package. +** 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. +** +** +** +** +** +** ** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. ** -** If you are unsure which license is appropriate for your use, please -** contact the sales department at http://qt.nokia.com/contact. ** $QT_END_LICENSE$ ** ****************************************************************************/ @@ -243,6 +243,9 @@ public: 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); @@ -302,10 +305,12 @@ private: friend class QGraphicsViewPrivate; friend class QGraphicsWidget; friend class QGraphicsWidgetPrivate; + friend class QGraphicsEffect; friend class QGraphicsSceneIndex; friend class QGraphicsSceneIndexPrivate; friend class QGraphicsSceneBspTreeIndex; friend class QGraphicsSceneBspTreeIndexPrivate; + friend class QGraphicsItemEffectSourcePrivate; }; Q_DECLARE_OPERATORS_FOR_FLAGS(QGraphicsScene::SceneLayers) diff --git a/src/gui/graphicsview/qgraphicsscene_bsp.cpp b/src/gui/graphicsview/qgraphicsscene_bsp.cpp index d554b7b..34d16d1 100644 --- a/src/gui/graphicsview/qgraphicsscene_bsp.cpp +++ b/src/gui/graphicsview/qgraphicsscene_bsp.cpp @@ -1,6 +1,7 @@ /**************************************************************************** ** ** Copyright (C) 2009 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. @@ -9,8 +10,8 @@ ** No Commercial Usage ** This file contains pre-release code and may not be distributed. ** You may use this file in accordance with the terms and conditions -** contained in the either Technology Preview License Agreement or the -** Beta Release License Agreement. +** 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 @@ -20,21 +21,20 @@ ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** -** In addition, as a special exception, Nokia gives you certain -** additional rights. These rights are described in the Nokia Qt LGPL -** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this -** package. +** 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. +** +** +** +** +** +** ** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. ** -** If you are unsure which license is appropriate for your use, please -** contact the sales department at http://qt.nokia.com/contact. ** $QT_END_LICENSE$ ** ****************************************************************************/ diff --git a/src/gui/graphicsview/qgraphicsscene_bsp_p.h b/src/gui/graphicsview/qgraphicsscene_bsp_p.h index e0e4791..06e1929 100644 --- a/src/gui/graphicsview/qgraphicsscene_bsp_p.h +++ b/src/gui/graphicsview/qgraphicsscene_bsp_p.h @@ -1,6 +1,7 @@ /**************************************************************************** ** ** Copyright (C) 2009 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. @@ -9,8 +10,8 @@ ** No Commercial Usage ** This file contains pre-release code and may not be distributed. ** You may use this file in accordance with the terms and conditions -** contained in the either Technology Preview License Agreement or the -** Beta Release License Agreement. +** 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 @@ -20,21 +21,20 @@ ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** -** In addition, as a special exception, Nokia gives you certain -** additional rights. These rights are described in the Nokia Qt LGPL -** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this -** package. +** 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. +** +** +** +** +** +** ** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. ** -** If you are unsure which license is appropriate for your use, please -** contact the sales department at http://qt.nokia.com/contact. ** $QT_END_LICENSE$ ** ****************************************************************************/ diff --git a/src/gui/graphicsview/qgraphicsscene_p.h b/src/gui/graphicsview/qgraphicsscene_p.h index 685f534..3b03624 100644 --- a/src/gui/graphicsview/qgraphicsscene_p.h +++ b/src/gui/graphicsview/qgraphicsscene_p.h @@ -1,6 +1,7 @@ /**************************************************************************** ** ** Copyright (C) 2009 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. @@ -9,8 +10,8 @@ ** No Commercial Usage ** This file contains pre-release code and may not be distributed. ** You may use this file in accordance with the terms and conditions -** contained in the either Technology Preview License Agreement or the -** Beta Release License Agreement. +** 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 @@ -20,21 +21,20 @@ ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** -** In addition, as a special exception, Nokia gives you certain -** additional rights. These rights are described in the Nokia Qt LGPL -** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this -** package. +** 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. +** +** +** +** +** +** ** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. ** -** If you are unsure which license is appropriate for your use, please -** contact the sales department at http://qt.nokia.com/contact. ** $QT_END_LICENSE$ ** ****************************************************************************/ @@ -47,8 +47,8 @@ // ------------- // // This file is not part of the Qt API. It exists for the convenience -// of qapplication_*.cpp, qwidget*.cpp and qfiledialog.cpp. This header -// file may change from version to version without notice, or even be removed. +// of other Qt classes. This header file may change from version to +// version without notice, or even be removed. // // We mean it. // @@ -87,7 +87,7 @@ public: static QGraphicsScenePrivate *get(QGraphicsScene *q); - quint32 changedSignalMask; + static int changedSignalIndex; QGraphicsScene::ItemIndexMethod indexMethod; QGraphicsSceneIndex *index; @@ -129,8 +129,11 @@ public: QGraphicsItem *focusItem; QGraphicsItem *lastFocusItem; QGraphicsWidget *tabFocusFirst; - QGraphicsWidget *activeWindow; + QGraphicsItem *activePanel; + QGraphicsItem *lastActivePanel; int activationRefCount; + int childExplicitActivation; + void setActivePanelHelper(QGraphicsItem *item, bool duringActivationEvent); void setFocusItemHelper(QGraphicsItem *item, Qt::FocusReason focusReason); QList<QGraphicsWidget *> popupWidgets; @@ -202,7 +205,11 @@ public: QRegion *exposedRegion, QWidget *widget); void drawSubtreeRecursive(QGraphicsItem *item, QPainter *painter, const QTransform *const, - QRegion *exposedRegion, QWidget *widget, qreal parentOpacity = qreal(1.0)); + QRegion *exposedRegion, QWidget *widget, qreal parentOpacity = qreal(1.0), + const QTransform *const effectTransform = 0); + void draw(QGraphicsItem *, QPainter *, const QTransform *const, const QTransform *const, + QRegion *, QWidget *, qreal, const QTransform *const, bool, bool); + void markDirty(QGraphicsItem *item, const QRectF &rect = QRectF(), bool invalidateChildren = false, bool maybeDirtyClipPath = false, bool force = false, bool ignoreOpacity = false, bool removingItemFromScene = false); @@ -223,10 +230,21 @@ public: item->d_ptr->fullUpdatePending = 0; item->d_ptr->ignoreVisible = 0; item->d_ptr->ignoreOpacity = 0; + QGraphicsEffect::ChangeFlags flags; + if (item->d_ptr->notifyBoundingRectChanged) { + flags |= QGraphicsEffect::SourceBoundingRectChanged; + item->d_ptr->notifyBoundingRectChanged = 0; + } + if (item->d_ptr->notifyInvalidated) { + flags |= QGraphicsEffect::SourceInvalidated; + item->d_ptr->notifyInvalidated = 0; + } if (recursive) { for (int i = 0; i < item->d_ptr->children.size(); ++i) resetDirtyItem(item->d_ptr->children.at(i), recursive); } + if (flags && item->d_ptr->graphicsEffect) + item->d_ptr->graphicsEffect->sourceChanged(flags); } inline void ensureSortedTopLevelItems() @@ -280,6 +298,14 @@ static inline QRectF adjustedItemBoundingRect(const QGraphicsItem *item) return boundingRect; } +static inline QRectF adjustedItemEffectiveBoundingRect(const QGraphicsItem *item) +{ + Q_ASSERT(item); + QRectF boundingRect(QGraphicsItemPrivate::get(item)->effectiveBoundingRect()); + _q_adjustRect(&boundingRect); + return boundingRect; +} + QT_END_NAMESPACE #endif // QT_NO_GRAPHICSVIEW diff --git a/src/gui/graphicsview/qgraphicsscenebsptreeindex.cpp b/src/gui/graphicsview/qgraphicsscenebsptreeindex.cpp index 433d0a8..e21183a 100644 --- a/src/gui/graphicsview/qgraphicsscenebsptreeindex.cpp +++ b/src/gui/graphicsview/qgraphicsscenebsptreeindex.cpp @@ -1,6 +1,7 @@ /**************************************************************************** ** ** Copyright (C) 2009 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. @@ -9,8 +10,8 @@ ** No Commercial Usage ** This file contains pre-release code and may not be distributed. ** You may use this file in accordance with the terms and conditions -** contained in the either Technology Preview License Agreement or the -** Beta Release License Agreement. +** 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 @@ -20,21 +21,20 @@ ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** -** In addition, as a special exception, Nokia gives you certain -** additional rights. These rights are described in the Nokia Qt LGPL -** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this -** package. +** 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. +** +** +** +** +** +** ** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. ** -** If you are unsure which license is appropriate for your use, please -** contact the sales department at http://qt.nokia.com/contact. ** $QT_END_LICENSE$ ** ****************************************************************************/ @@ -172,7 +172,7 @@ void QGraphicsSceneBspTreeIndexPrivate::_q_updateIndex() if (item->d_ptr->ancestorFlags & QGraphicsItemPrivate::AncestorClipsChildren) continue; - bsp.insertItem(item, item->sceneBoundingRect()); + bsp.insertItem(item, item->d_ptr->sceneEffectiveBoundingRect()); } } unindexedItems.clear(); @@ -352,7 +352,7 @@ void QGraphicsSceneBspTreeIndexPrivate::removeItem(QGraphicsItem *item, bool rec purgePending = true; removedItems << item; } else if (!(item->d_ptr->ancestorFlags & QGraphicsItemPrivate::AncestorClipsChildren)) { - bsp.removeItem(item, item->sceneBoundingRect()); + bsp.removeItem(item, item->d_ptr->sceneEffectiveBoundingRect()); } } else { unindexedItems.removeOne(item); @@ -419,8 +419,8 @@ bool QGraphicsSceneBspTreeIndexPrivate::closestItemFirst_withoutCache(const QGra // Find common ancestor, and each item's ancestor closest to the common // ancestor. - int item1Depth = d1->depth; - int item2Depth = d2->depth; + int item1Depth = d1->depth(); + int item2Depth = d2->depth(); const QGraphicsItem *p = item1; const QGraphicsItem *t1 = item1; while (item1Depth > item2Depth && (p = p->d_ptr->parent)) { @@ -480,23 +480,23 @@ void QGraphicsSceneBspTreeIndexPrivate::sortItems(QList<QGraphicsItem *> *itemLi return; if (onlyTopLevelItems) { - if (order == Qt::AscendingOrder) + if (order == Qt::DescendingOrder) qSort(itemList->begin(), itemList->end(), qt_closestLeaf); - else if (order == Qt::DescendingOrder) + else if (order == Qt::AscendingOrder) qSort(itemList->begin(), itemList->end(), qt_notclosestLeaf); return; } if (sortCacheEnabled) { - if (order == Qt::AscendingOrder) { + if (order == Qt::DescendingOrder) { qSort(itemList->begin(), itemList->end(), closestItemFirst_withCache); - } else if (order == Qt::DescendingOrder) { + } else if (order == Qt::AscendingOrder) { qSort(itemList->begin(), itemList->end(), closestItemLast_withCache); } } else { - if (order == Qt::AscendingOrder) { + if (order == Qt::DescendingOrder) { qSort(itemList->begin(), itemList->end(), closestItemFirst_withoutCache); - } else if (order == Qt::DescendingOrder) { + } else if (order == Qt::AscendingOrder) { qSort(itemList->begin(), itemList->end(), closestItemLast_withoutCache); } } @@ -605,7 +605,7 @@ QList<QGraphicsItem *> QGraphicsSceneBspTreeIndex::estimateTopLevelItems(const Q } /*! - \fn QList<QGraphicsItem *> QGraphicsSceneBspTreeIndex::items(Qt::SortOrder order = Qt::AscendingOrder) const; + \fn QList<QGraphicsItem *> QGraphicsSceneBspTreeIndex::items(Qt::SortOrder order = Qt::DescendingOrder) const; Return all items in the BSP index and sort them using \a order. */ diff --git a/src/gui/graphicsview/qgraphicsscenebsptreeindex_p.h b/src/gui/graphicsview/qgraphicsscenebsptreeindex_p.h index 27c499d..0a86bb7 100644 --- a/src/gui/graphicsview/qgraphicsscenebsptreeindex_p.h +++ b/src/gui/graphicsview/qgraphicsscenebsptreeindex_p.h @@ -1,6 +1,7 @@ /**************************************************************************** ** ** Copyright (C) 2009 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. @@ -9,8 +10,8 @@ ** No Commercial Usage ** This file contains pre-release code and may not be distributed. ** You may use this file in accordance with the terms and conditions -** contained in the either Technology Preview License Agreement or the -** Beta Release License Agreement. +** 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 @@ -20,21 +21,20 @@ ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** -** In addition, as a special exception, Nokia gives you certain -** additional rights. These rights are described in the Nokia Qt LGPL -** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this -** package. +** 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. +** +** +** +** +** +** ** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. ** -** If you are unsure which license is appropriate for your use, please -** contact the sales department at http://qt.nokia.com/contact. ** $QT_END_LICENSE$ ** ****************************************************************************/ @@ -44,8 +44,8 @@ // ------------- // // This file is not part of the Qt API. It exists for the convenience -// of qapplication_*.cpp, qwidget*.cpp and qfiledialog.cpp. This header -// file may change from version to version without notice, or even be removed. +// of other Qt classes. This header file may change from version to +// version without notice, or even be removed. // // We mean it. // @@ -81,7 +81,7 @@ public: 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::AscendingOrder) const; + QList<QGraphicsItem *> items(Qt::SortOrder order = Qt::DescendingOrder) const; int bspTreeDepth(); void setBspTreeDepth(int depth); diff --git a/src/gui/graphicsview/qgraphicssceneevent.cpp b/src/gui/graphicsview/qgraphicssceneevent.cpp index 3ec14c3..842d06f 100644 --- a/src/gui/graphicsview/qgraphicssceneevent.cpp +++ b/src/gui/graphicsview/qgraphicssceneevent.cpp @@ -1,6 +1,7 @@ /**************************************************************************** ** ** Copyright (C) 2009 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. @@ -9,8 +10,8 @@ ** No Commercial Usage ** This file contains pre-release code and may not be distributed. ** You may use this file in accordance with the terms and conditions -** contained in the either Technology Preview License Agreement or the -** Beta Release License Agreement. +** 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 @@ -20,21 +21,20 @@ ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** -** In addition, as a special exception, Nokia gives you certain -** additional rights. These rights are described in the Nokia Qt LGPL -** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this -** package. +** 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. +** +** +** +** +** +** ** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. ** -** If you are unsure which license is appropriate for your use, please -** contact the sales department at http://qt.nokia.com/contact. ** $QT_END_LICENSE$ ** ****************************************************************************/ @@ -87,9 +87,11 @@ \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. + 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 diff --git a/src/gui/graphicsview/qgraphicssceneevent.h b/src/gui/graphicsview/qgraphicssceneevent.h index 39fbbab..5bc651f 100644 --- a/src/gui/graphicsview/qgraphicssceneevent.h +++ b/src/gui/graphicsview/qgraphicssceneevent.h @@ -1,6 +1,7 @@ /**************************************************************************** ** ** Copyright (C) 2009 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. @@ -9,8 +10,8 @@ ** No Commercial Usage ** This file contains pre-release code and may not be distributed. ** You may use this file in accordance with the terms and conditions -** contained in the either Technology Preview License Agreement or the -** Beta Release License Agreement. +** 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 @@ -20,21 +21,20 @@ ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** -** In addition, as a special exception, Nokia gives you certain -** additional rights. These rights are described in the Nokia Qt LGPL -** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this -** package. +** 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. +** +** +** +** +** +** ** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. ** -** If you are unsure which license is appropriate for your use, please -** contact the sales department at http://qt.nokia.com/contact. ** $QT_END_LICENSE$ ** ****************************************************************************/ diff --git a/src/gui/graphicsview/qgraphicssceneindex.cpp b/src/gui/graphicsview/qgraphicssceneindex.cpp index feb44bb..3ea957f 100644 --- a/src/gui/graphicsview/qgraphicssceneindex.cpp +++ b/src/gui/graphicsview/qgraphicssceneindex.cpp @@ -1,6 +1,7 @@ /**************************************************************************** ** ** Copyright (C) 2009 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. @@ -9,8 +10,8 @@ ** No Commercial Usage ** This file contains pre-release code and may not be distributed. ** You may use this file in accordance with the terms and conditions -** contained in the either Technology Preview License Agreement or the -** Beta Release License Agreement. +** 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 @@ -20,21 +21,20 @@ ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** -** In addition, as a special exception, Nokia gives you certain -** additional rights. These rights are described in the Nokia Qt LGPL -** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this -** package. +** 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. +** +** +** +** +** +** ** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. ** -** If you are unsure which license is appropriate for your use, please -** contact the sales department at http://qt.nokia.com/contact. ** $QT_END_LICENSE$ ** ****************************************************************************/ @@ -542,7 +542,7 @@ QList<QGraphicsItem *> QGraphicsSceneIndex::estimateTopLevelItems(const QRectF & Q_UNUSED(rect); QGraphicsScenePrivate *scened = d->scene->d_func(); scened->ensureSortedTopLevelItems(); - if (order == Qt::AscendingOrder) { + if (order == Qt::DescendingOrder) { QList<QGraphicsItem *> sorted; for (int i = scened->topLevelItems.size() - 1; i >= 0; --i) sorted << scened->topLevelItems.at(i); @@ -552,7 +552,7 @@ QList<QGraphicsItem *> QGraphicsSceneIndex::estimateTopLevelItems(const QRectF & } /*! - \fn QList<QGraphicsItem *> QGraphicsSceneIndex::items(Qt::SortOrder order = Qt::AscendingOrder) const + \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. diff --git a/src/gui/graphicsview/qgraphicssceneindex_p.h b/src/gui/graphicsview/qgraphicssceneindex_p.h index d922036..768c724 100644 --- a/src/gui/graphicsview/qgraphicssceneindex_p.h +++ b/src/gui/graphicsview/qgraphicssceneindex_p.h @@ -1,6 +1,7 @@ /**************************************************************************** ** ** Copyright (C) 2009 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. @@ -9,8 +10,8 @@ ** No Commercial Usage ** This file contains pre-release code and may not be distributed. ** You may use this file in accordance with the terms and conditions -** contained in the either Technology Preview License Agreement or the -** Beta Release License Agreement. +** 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 @@ -20,21 +21,20 @@ ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** -** In addition, as a special exception, Nokia gives you certain -** additional rights. These rights are described in the Nokia Qt LGPL -** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this -** package. +** 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. +** +** +** +** +** +** ** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. ** -** If you are unsure which license is appropriate for your use, please -** contact the sales department at http://qt.nokia.com/contact. ** $QT_END_LICENSE$ ** ****************************************************************************/ @@ -47,8 +47,8 @@ // ------------- // // This file is not part of the Qt API. It exists for the convenience -// of qapplication_*.cpp, qwidget*.cpp and qfiledialog.cpp. This header -// file may change from version to version without notice, or even be removed. +// of other Qt classes. This header file may change from version to +// version without notice, or even be removed. // // We mean it. // @@ -88,7 +88,7 @@ public: QGraphicsScene *scene() const; - virtual QList<QGraphicsItem *> items(Qt::SortOrder order = Qt::AscendingOrder) const = 0; + 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, @@ -154,10 +154,10 @@ inline void QGraphicsSceneIndexPrivate::items_helper(const QRectF &rect, QGraphi Qt::ItemSelectionMode mode, Qt::SortOrder order) const { Q_Q(const QGraphicsSceneIndex); - const QList<QGraphicsItem *> tli = q->estimateTopLevelItems(rect, Qt::DescendingOrder); + 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, order); - if (order == Qt::AscendingOrder) { + if (order == Qt::DescendingOrder) { const int n = items->size(); for (int i = 0; i < n / 2; ++i) items->swap(i, n - i - 1); diff --git a/src/gui/graphicsview/qgraphicsscenelinearindex.cpp b/src/gui/graphicsview/qgraphicsscenelinearindex.cpp index 132b1a6..d6faf05 100644 --- a/src/gui/graphicsview/qgraphicsscenelinearindex.cpp +++ b/src/gui/graphicsview/qgraphicsscenelinearindex.cpp @@ -1,6 +1,7 @@ /**************************************************************************** ** ** Copyright (C) 2009 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. @@ -9,8 +10,8 @@ ** No Commercial Usage ** This file contains pre-release code and may not be distributed. ** You may use this file in accordance with the terms and conditions -** contained in the either Technology Preview License Agreement or the -** Beta Release License Agreement. +** 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 @@ -20,21 +21,20 @@ ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** -** In addition, as a special exception, Nokia gives you certain -** additional rights. These rights are described in the Nokia Qt LGPL -** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this -** package. +** 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. +** +** +** +** +** +** ** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. ** -** If you are unsure which license is appropriate for your use, please -** contact the sales department at http://qt.nokia.com/contact. ** $QT_END_LICENSE$ ** ****************************************************************************/ @@ -62,7 +62,7 @@ */ /*! - \fn QList<QGraphicsItem *> QGraphicsSceneLinearIndex::items(Qt::SortOrder order = Qt::AscendingOrder) const; + \fn QList<QGraphicsItem *> QGraphicsSceneLinearIndex::items(Qt::SortOrder order = Qt::DescendingOrder) const; Return all items in the index and sort them using \a order. */ diff --git a/src/gui/graphicsview/qgraphicsscenelinearindex_p.h b/src/gui/graphicsview/qgraphicsscenelinearindex_p.h index 6181bf0..0aaee14 100644 --- a/src/gui/graphicsview/qgraphicsscenelinearindex_p.h +++ b/src/gui/graphicsview/qgraphicsscenelinearindex_p.h @@ -1,6 +1,7 @@ /**************************************************************************** ** ** Copyright (C) 2009 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. @@ -9,8 +10,8 @@ ** No Commercial Usage ** This file contains pre-release code and may not be distributed. ** You may use this file in accordance with the terms and conditions -** contained in the either Technology Preview License Agreement or the -** Beta Release License Agreement. +** 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 @@ -20,21 +21,20 @@ ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** -** In addition, as a special exception, Nokia gives you certain -** additional rights. These rights are described in the Nokia Qt LGPL -** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this -** package. +** 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. +** +** +** +** +** +** ** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. ** -** If you are unsure which license is appropriate for your use, please -** contact the sales department at http://qt.nokia.com/contact. ** $QT_END_LICENSE$ ** ****************************************************************************/ @@ -76,7 +76,7 @@ public: QGraphicsSceneLinearIndex(QGraphicsScene *scene = 0) : QGraphicsSceneIndex(scene) { } - QList<QGraphicsItem *> items(Qt::SortOrder order = Qt::AscendingOrder) const + 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 diff --git a/src/gui/graphicsview/qgraphicstransform.cpp b/src/gui/graphicsview/qgraphicstransform.cpp index edfcf8a..ec1a2f5 100644 --- a/src/gui/graphicsview/qgraphicstransform.cpp +++ b/src/gui/graphicsview/qgraphicstransform.cpp @@ -1,6 +1,7 @@ /**************************************************************************** ** ** Copyright (C) 2009 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. @@ -9,8 +10,8 @@ ** No Commercial Usage ** This file contains pre-release code and may not be distributed. ** You may use this file in accordance with the terms and conditions -** contained in the either Technology Preview License Agreement or the -** Beta Release License Agreement. +** 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 @@ -20,21 +21,20 @@ ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** -** In addition, as a special exception, Nokia gives you certain -** additional rights. These rights are described in the Nokia Qt LGPL -** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this -** package. +** 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. +** +** +** +** +** +** ** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. ** -** If you are unsure which license is appropriate for your use, please -** contact the sales department at http://qt.nokia.com/contact. ** $QT_END_LICENSE$ ** ****************************************************************************/ @@ -63,10 +63,17 @@ independent transformation. The resulting operation is then combined into a single transform which is applied to QGraphicsItem. + Transformations are computed in true 3D space using QMatrix4x4. + When the transformation is applied to a QGraphicsItem, it will be + projected back to a 2D QTransform. When multiple QGraphicsTransform + objects are applied to a QGraphicsItem, all of the transformations + are computed in true 3D space, with the projection back to 2D + only occurring after the last QGraphicsTransform is applied. + If you want to create your own configurable transformation, you can create a subclass of QGraphicsTransform (or any or the existing subclasses), and reimplement the pure virtual applyTo() function, which takes a pointer to a - QTransform. Each operation you would like to apply should be exposed as + QMatrix4x4. Each operation you would like to apply should be exposed as properties (e.g., customTransform->setVerticalShear(2.5)). Inside you reimplementation of applyTo(), you can modify the provided transform respectively. @@ -136,28 +143,13 @@ QGraphicsTransform::QGraphicsTransform(QGraphicsTransformPrivate &p, QObject *pa } /*! - Applies this transformation to an identity transform, and returns the - resulting transform. - - This is equivalent to passing an identity transform to applyTo(). - - \sa applyTo() -*/ -QTransform QGraphicsTransform::transform() const -{ - QTransform t; - applyTo(&t); - return t; -} - -/*! - \fn void QGraphicsTransform::applyTo(QTransform *transform) const + \fn void QGraphicsTransform::applyTo(QMatrix4x4 *matrix) const This pure virtual method has to be reimplemented in derived classes. - It applies this transformation to \a transform. + It applies this transformation to \a matrix. - \sa QGraphicsItem::transform() + \sa QGraphicsItem::transform(), QMatrix4x4::toTransform() */ /*! @@ -189,11 +181,12 @@ void QGraphicsTransform::update() relative to the parent as the rest of the item grows). By default the origin is QPointF(0, 0). - The two parameters xScale and yScale describe the scale factors to apply in - horizontal and vertical direction. They can take on any value, including 0 - (to collapse the item to a point) or negativate value. A negative xScale - value will mirror the item horizontally. A negative yScale value will flip - the item vertically. + The parameters xScale, yScale, and zScale describe the scale factors to + apply in horizontal, vertical, and depth directions. They can take on any + value, including 0 (to collapse the item to a point) or negative value. + A negative xScale value will mirror the item horizontally. A negative yScale + value will flip the item vertically. A negative zScale will flip the + item end for end. \sa QGraphicsTransform, QGraphicsItem::setScale(), QTransform::scale() */ @@ -202,10 +195,11 @@ class QGraphicsScalePrivate : public QGraphicsTransformPrivate { public: QGraphicsScalePrivate() - : xScale(1), yScale(1) {} - QPointF origin; + : xScale(1), yScale(1), zScale(1) {} + QVector3D origin; qreal xScale; qreal yScale; + qreal zScale; }; /*! @@ -225,21 +219,23 @@ QGraphicsScale::~QGraphicsScale() /*! \property QGraphicsScale::origin - \brief The QGraphicsScene class provides the origin of the scale. + \brief the origin of the scale in 3D space. All scaling will be done relative to this point (i.e., this point will stay fixed, relative to the parent, when the item is scaled). - \sa xScale, yScale + \sa xScale, yScale, zScale */ -QPointF QGraphicsScale::origin() const +QVector3D QGraphicsScale::origin() const { Q_D(const QGraphicsScale); return d->origin; } -void QGraphicsScale::setOrigin(const QPointF &point) +void QGraphicsScale::setOrigin(const QVector3D &point) { Q_D(QGraphicsScale); + if (d->origin == point) + return; d->origin = point; update(); emit originChanged(); @@ -254,7 +250,7 @@ void QGraphicsScale::setOrigin(const QPointF &point) provide a negative value, the item will be mirrored horizontally around its origin. - \sa yScale, origin + \sa yScale, zScale, origin */ qreal QGraphicsScale::xScale() const { @@ -280,7 +276,7 @@ void QGraphicsScale::setXScale(qreal scale) provide a negative value, the item will be flipped vertically around its origin. - \sa xScale, origin + \sa xScale, zScale, origin */ qreal QGraphicsScale::yScale() const { @@ -298,14 +294,40 @@ void QGraphicsScale::setYScale(qreal scale) } /*! + \property QGraphicsScale::zScale + \brief the depth scale factor. + + The scale factor can be any real number; the default value is 1.0. If you + set the factor to 0.0, the item will be collapsed to a single point. If you + provide a negative value, the item will be flipped end for end around its + origin. + + \sa xScale, yScale, origin +*/ +qreal QGraphicsScale::zScale() const +{ + Q_D(const QGraphicsScale); + return d->zScale; +} +void QGraphicsScale::setZScale(qreal scale) +{ + Q_D(QGraphicsScale); + if (d->zScale == scale) + return; + d->zScale = scale; + update(); + emit scaleChanged(); +} + +/*! \reimp */ -void QGraphicsScale::applyTo(QTransform *transform) const +void QGraphicsScale::applyTo(QMatrix4x4 *matrix) const { Q_D(const QGraphicsScale); - transform->translate(d->origin.x(), d->origin.y()); - transform->scale(d->xScale, d->yScale); - transform->translate(-d->origin.x(), -d->origin.y()); + matrix->translate(d->origin); + matrix->scale(d->xScale, d->yScale, d->zScale); + matrix->translate(-d->origin); } /*! @@ -319,10 +341,11 @@ void QGraphicsScale::applyTo(QTransform *transform) const /*! \fn QGraphicsScale::scaleChanged() - This signal is emitted whenever the xScale or yScale of the object - changes. + This signal is emitted whenever the xScale, yScale, or zScale + of the object changes. \sa QGraphicsScale::xScale, QGraphicsScale::yScale + \sa QGraphicsScale::zScale */ /*! @@ -359,20 +382,14 @@ void QGraphicsScale::applyTo(QTransform *transform) const \sa QGraphicsTransform, QGraphicsItem::setRotation(), QTransform::rotate() */ -#define VECTOR_FOR_AXIS_X QVector3D(1, 0, 0) -#define VECTOR_FOR_AXIS_Y QVector3D(0, 1, 0) -#define VECTOR_FOR_AXIS_Z QVector3D(0, 0, 1) - - class QGraphicsRotationPrivate : public QGraphicsTransformPrivate { public: QGraphicsRotationPrivate() - : angle(0), axis(VECTOR_FOR_AXIS_Z), simpleAxis(Qt::ZAxis) {} - QPointF origin; + : angle(0), axis(0, 0, 1) {} + QVector3D origin; qreal angle; QVector3D axis; - int simpleAxis; }; /*! @@ -392,21 +409,23 @@ QGraphicsRotation::~QGraphicsRotation() /*! \property QGraphicsRotation::origin - \brief the origin of the rotation. + \brief the origin of the rotation in 3D space. All rotations will be done relative to this point (i.e., this point will stay fixed, relative to the parent, when the item is rotated). \sa angle */ -QPointF QGraphicsRotation::origin() const +QVector3D QGraphicsRotation::origin() const { Q_D(const QGraphicsRotation); return d->origin; } -void QGraphicsRotation::setOrigin(const QPointF &point) +void QGraphicsRotation::setOrigin(const QVector3D &point) { Q_D(QGraphicsRotation); + if (d->origin == point) + return; d->origin = point; update(); emit originChanged(); @@ -448,11 +467,11 @@ void QGraphicsRotation::setAngle(qreal angle) */ /*! - \fn void QGraphicsRotation::angleChanged() + \fn void QGraphicsRotation::angleChanged() - This signal is emitted whenever the angle has changed. + This signal is emitted whenever the angle has changed. - \sa QGraphicsRotation::angle + \sa QGraphicsRotation::angle */ /*! @@ -475,18 +494,9 @@ QVector3D QGraphicsRotation::axis() const void QGraphicsRotation::setAxis(const QVector3D &axis) { Q_D(QGraphicsRotation); - if (d->axis == axis) + if (d->axis == axis) return; - d->axis = axis; - if (axis == VECTOR_FOR_AXIS_X) { - d->simpleAxis = Qt::XAxis; - } else if (axis == VECTOR_FOR_AXIS_Y) { - d->simpleAxis = Qt::YAxis; - } else if (axis == VECTOR_FOR_AXIS_Z) { - d->simpleAxis = Qt::ZAxis; - } else { - d->simpleAxis = -1; // no predefined axis - } + d->axis = axis; update(); emit axisChanged(); } @@ -495,90 +505,58 @@ void QGraphicsRotation::setAxis(const QVector3D &axis) \fn void QGraphicsRotation::setAxis(Qt::Axis axis) Convenience function to set the axis to \a axis. -*/ + Note: the Qt::YAxis rotation for QTransform is inverted from the + correct mathematical rotation in 3D space. The QGraphicsRotation + class implements a correct mathematical rotation. The following + two sequences of code will perform the same transformation: + + \code + QTransform t; + t.rotate(45, Qt::YAxis); + + QGraphicsRotation r; + r.setAxis(Qt::YAxis); + r.setAngle(-45); + \endcode +*/ void QGraphicsRotation::setAxis(Qt::Axis axis) { switch (axis) { case Qt::XAxis: - setAxis(VECTOR_FOR_AXIS_X); + setAxis(QVector3D(1, 0, 0)); break; case Qt::YAxis: - setAxis(VECTOR_FOR_AXIS_Y); + setAxis(QVector3D(0, 1, 0)); break; case Qt::ZAxis: - setAxis(VECTOR_FOR_AXIS_Z); + setAxis(QVector3D(0, 0, 1)); break; } } - -const qreal deg2rad = qreal(0.017453292519943295769); // pi/180 -static const qreal inv_dist_to_plane = 1. / 1024.; - /*! \reimp */ -void QGraphicsRotation::applyTo(QTransform *t) const +void QGraphicsRotation::applyTo(QMatrix4x4 *matrix) const { Q_D(const QGraphicsRotation); - qreal a = d->angle; - - if (a == 0.) - return; - - if (d->simpleAxis != -1) { - //that's an optimization for simple axis - t->translate(d->origin.x(), d->origin.y()); - t->rotate(a, Qt::Axis(d->simpleAxis)); - t->translate(-d->origin.x(), -d->origin.y()); - return; - } - - qreal x = d->axis.x(); - qreal y = d->axis.y(); - qreal z = d->axis.z(); - - if (x == 0. && y == 0 && z == 0) + if (d->angle == 0. || d->axis.isNull()) return; - qreal c, s; - if (a == 90. || a == -270.) { - s = 1.; - c = 0.; - } else if (a == 270. || a == -90.) { - s = -1.; - c = 0.; - } else if (a == 180.) { - s = 0.; - c = -1.; - } else { - qreal b = deg2rad*a; - s = qSin(b); - c = qCos(b); - } - - qreal len = x * x + y * y + z * z; - if (len != 1.) { - len = 1. / qSqrt(len); - x *= len; - y *= len; - z *= len; - } - - t->translate(d->origin.x(), d->origin.y()); - *t = QTransform(x*x*(1-c)+c, x*y*(1-c)+z*s, x*z*(1-c)-y*s*inv_dist_to_plane, - y*x*(1-c)-z*s, y*y*(1-c)+c, y*z*(1-c)-x*s*inv_dist_to_plane, - 0, 0, 1) * *t; - t->translate(-d->origin.x(), -d->origin.y()); + matrix->translate(d->origin); + matrix->rotate(d->angle, d->axis.x(), d->axis.y(), d->axis.z()); + matrix->translate(-d->origin); } /*! \fn void QGraphicsRotation::axisChanged() This signal is emitted whenever the axis of the object changes. + + \sa QGraphicsRotation::axis */ #include "moc_qgraphicstransform.cpp" diff --git a/src/gui/graphicsview/qgraphicstransform.h b/src/gui/graphicsview/qgraphicstransform.h index 8ccc258..58075aa 100644 --- a/src/gui/graphicsview/qgraphicstransform.h +++ b/src/gui/graphicsview/qgraphicstransform.h @@ -1,6 +1,7 @@ /**************************************************************************** ** ** Copyright (C) 2009 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. @@ -9,8 +10,8 @@ ** No Commercial Usage ** This file contains pre-release code and may not be distributed. ** You may use this file in accordance with the terms and conditions -** contained in the either Technology Preview License Agreement or the -** Beta Release License Agreement. +** 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 @@ -20,21 +21,20 @@ ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** -** In addition, as a special exception, Nokia gives you certain -** additional rights. These rights are described in the Nokia Qt LGPL -** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this -** package. +** 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. +** +** +** +** +** +** ** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. ** -** If you are unsure which license is appropriate for your use, please -** contact the sales department at http://qt.nokia.com/contact. ** $QT_END_LICENSE$ ** ****************************************************************************/ @@ -43,8 +43,9 @@ #define QGRAPHICSTRANSFORM_H #include <QtCore/QObject> -#include <QtGui/QTransform> #include <QtGui/QVector3D> +#include <QtGui/QTransform> +#include <QtGui/QMatrix4x4> QT_BEGIN_HEADER @@ -62,8 +63,7 @@ public: QGraphicsTransform(QObject *parent = 0); ~QGraphicsTransform(); - QTransform transform() const; - virtual void applyTo(QTransform *transform) const = 0; + virtual void applyTo(QMatrix4x4 *matrix) const = 0; protected Q_SLOTS: void update(); @@ -83,15 +83,16 @@ class Q_GUI_EXPORT QGraphicsScale : public QGraphicsTransform { Q_OBJECT - Q_PROPERTY(QPointF origin READ origin WRITE setOrigin NOTIFY originChanged) + Q_PROPERTY(QVector3D origin READ origin WRITE setOrigin NOTIFY originChanged) Q_PROPERTY(qreal xScale READ xScale WRITE setXScale NOTIFY scaleChanged) Q_PROPERTY(qreal yScale READ yScale WRITE setYScale NOTIFY scaleChanged) + Q_PROPERTY(qreal zScale READ zScale WRITE setZScale NOTIFY scaleChanged) public: QGraphicsScale(QObject *parent = 0); ~QGraphicsScale(); - QPointF origin() const; - void setOrigin(const QPointF &point); + QVector3D origin() const; + void setOrigin(const QVector3D &point); qreal xScale() const; void setXScale(qreal); @@ -99,7 +100,10 @@ public: qreal yScale() const; void setYScale(qreal); - void applyTo(QTransform *transform) const; + qreal zScale() const; + void setZScale(qreal); + + void applyTo(QMatrix4x4 *matrix) const; Q_SIGNALS: void originChanged(); @@ -115,15 +119,15 @@ class Q_GUI_EXPORT QGraphicsRotation : public QGraphicsTransform { Q_OBJECT - Q_PROPERTY(QPointF origin READ origin WRITE setOrigin NOTIFY originChanged) + Q_PROPERTY(QVector3D origin READ origin WRITE setOrigin NOTIFY originChanged) Q_PROPERTY(qreal angle READ angle WRITE setAngle NOTIFY angleChanged) Q_PROPERTY(QVector3D axis READ axis WRITE setAxis NOTIFY axisChanged) public: QGraphicsRotation(QObject *parent = 0); ~QGraphicsRotation(); - QPointF origin() const; - void setOrigin(const QPointF &point); + QVector3D origin() const; + void setOrigin(const QVector3D &point); qreal angle() const; void setAngle(qreal); @@ -132,7 +136,7 @@ public: void setAxis(const QVector3D &axis); void setAxis(Qt::Axis axis); - void applyTo(QTransform *transform) const; + void applyTo(QMatrix4x4 *matrix) const; Q_SIGNALS: void originChanged(); diff --git a/src/gui/graphicsview/qgraphicstransform_p.h b/src/gui/graphicsview/qgraphicstransform_p.h index 2c563e4..9e708b2 100644 --- a/src/gui/graphicsview/qgraphicstransform_p.h +++ b/src/gui/graphicsview/qgraphicstransform_p.h @@ -1,6 +1,7 @@ /**************************************************************************** ** ** Copyright (C) 2009 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. @@ -9,8 +10,8 @@ ** No Commercial Usage ** This file contains pre-release code and may not be distributed. ** You may use this file in accordance with the terms and conditions -** contained in the either Technology Preview License Agreement or the -** Beta Release License Agreement. +** 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 @@ -20,21 +21,20 @@ ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** -** In addition, as a special exception, Nokia gives you certain -** additional rights. These rights are described in the Nokia Qt LGPL -** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this -** package. +** 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. +** +** +** +** +** +** ** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. ** -** If you are unsure which license is appropriate for your use, please -** contact the sales department at http://qt.nokia.com/contact. ** $QT_END_LICENSE$ ** ****************************************************************************/ @@ -47,8 +47,8 @@ // ------------- // // This file is not part of the Qt API. It exists for the convenience -// of qapplication_*.cpp, qwidget*.cpp and qfiledialog.cpp. This header -// file may change from version to version without notice, or even be removed. +// of other Qt classes. This header file may change from version to +// version without notice, or even be removed. // // We mean it. // diff --git a/src/gui/graphicsview/qgraphicsview.cpp b/src/gui/graphicsview/qgraphicsview.cpp index 07dc6ad..98b2c9c 100644 --- a/src/gui/graphicsview/qgraphicsview.cpp +++ b/src/gui/graphicsview/qgraphicsview.cpp @@ -1,6 +1,7 @@ /**************************************************************************** ** ** Copyright (C) 2009 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. @@ -9,8 +10,8 @@ ** No Commercial Usage ** This file contains pre-release code and may not be distributed. ** You may use this file in accordance with the terms and conditions -** contained in the either Technology Preview License Agreement or the -** Beta Release License Agreement. +** 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 @@ -20,21 +21,20 @@ ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** -** In addition, as a special exception, Nokia gives you certain -** additional rights. These rights are described in the Nokia Qt LGPL -** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this -** package. +** 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. +** +** +** +** +** +** ** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. ** -** If you are unsure which license is appropriate for your use, please -** contact the sales department at http://qt.nokia.com/contact. ** $QT_END_LICENSE$ ** ****************************************************************************/ @@ -971,7 +971,7 @@ QList<QGraphicsItem *> QGraphicsViewPrivate::findItems(const QRegion &exposedReg *allItems = true; // All items are guaranteed within the exposed region. - return scene->items(Qt::DescendingOrder); + return scene->items(Qt::AscendingOrder); } // Step 2) If the expose region is a simple rect and the view is only @@ -981,7 +981,7 @@ QList<QGraphicsItem *> QGraphicsViewPrivate::findItems(const QRegion &exposedReg if (simpleRectLookup) { return scene->items(exposedRegionSceneBounds, Qt::IntersectsItemBoundingRect, - Qt::DescendingOrder, viewTransform); + Qt::AscendingOrder, viewTransform); } // If the region is complex or the view has a complex transform, adjust @@ -993,7 +993,7 @@ QList<QGraphicsItem *> QGraphicsViewPrivate::findItems(const QRegion &exposedReg const QPainterPath exposedScenePath(q->mapToScene(qt_regionToPath(adjustedRegion))); return scene->items(exposedScenePath, Qt::IntersectsItemBoundingRect, - Qt::DescendingOrder, viewTransform); + Qt::AscendingOrder, viewTransform); } /*! @@ -1537,6 +1537,9 @@ void QGraphicsView::setScene(QGraphicsScene *scene) } d->updateInputMethodSensitivity(); + + if (d->scene && hasFocus()) + d->scene->setFocus(); } /*! @@ -2052,13 +2055,13 @@ QList<QGraphicsItem *> QGraphicsView::items(const QPoint &pos) const QTransform xinv = viewportTransform().inverted(); return d->scene->items(xinv.mapRect(QRectF(pos.x(), pos.y(), 1, 1)), Qt::IntersectsItemShape, - Qt::AscendingOrder, + Qt::DescendingOrder, viewportTransform()); } // Use the polygon version return d->scene->items(mapToScene(pos.x(), pos.y(), 1, 1), Qt::IntersectsItemShape, - Qt::AscendingOrder, + Qt::DescendingOrder, viewportTransform()); } @@ -2086,7 +2089,7 @@ QList<QGraphicsItem *> QGraphicsView::items(const QRect &rect, Qt::ItemSelection Q_D(const QGraphicsView); if (!d->scene) return QList<QGraphicsItem *>(); - return d->scene->items(mapToScene(rect), mode, Qt::AscendingOrder, viewportTransform()); + return d->scene->items(mapToScene(rect), mode, Qt::DescendingOrder, viewportTransform()); } /*! @@ -2114,7 +2117,7 @@ QList<QGraphicsItem *> QGraphicsView::items(const QPolygon &polygon, Qt::ItemSel Q_D(const QGraphicsView); if (!d->scene) return QList<QGraphicsItem *>(); - return d->scene->items(mapToScene(polygon), mode, Qt::AscendingOrder, viewportTransform()); + return d->scene->items(mapToScene(polygon), mode, Qt::DescendingOrder, viewportTransform()); } /*! @@ -2134,7 +2137,7 @@ QList<QGraphicsItem *> QGraphicsView::items(const QPainterPath &path, Qt::ItemSe Q_D(const QGraphicsView); if (!d->scene) return QList<QGraphicsItem *>(); - return d->scene->items(mapToScene(path), mode, Qt::AscendingOrder, viewportTransform()); + return d->scene->items(mapToScene(path), mode, Qt::DescendingOrder, viewportTransform()); } /*! @@ -2607,7 +2610,6 @@ bool QGraphicsView::event(QEvent *event) bool QGraphicsView::viewportEvent(QEvent *event) { Q_D(QGraphicsView); - if (!d->scene) return QAbstractScrollArea::viewportEvent(event); diff --git a/src/gui/graphicsview/qgraphicsview.h b/src/gui/graphicsview/qgraphicsview.h index 0558051..2aed0e6 100644 --- a/src/gui/graphicsview/qgraphicsview.h +++ b/src/gui/graphicsview/qgraphicsview.h @@ -1,6 +1,7 @@ /**************************************************************************** ** ** Copyright (C) 2009 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. @@ -9,8 +10,8 @@ ** No Commercial Usage ** This file contains pre-release code and may not be distributed. ** You may use this file in accordance with the terms and conditions -** contained in the either Technology Preview License Agreement or the -** Beta Release License Agreement. +** 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 @@ -20,21 +21,20 @@ ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** -** In addition, as a special exception, Nokia gives you certain -** additional rights. These rights are described in the Nokia Qt LGPL -** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this -** package. +** 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. +** +** +** +** +** +** ** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. ** -** If you are unsure which license is appropriate for your use, please -** contact the sales department at http://qt.nokia.com/contact. ** $QT_END_LICENSE$ ** ****************************************************************************/ diff --git a/src/gui/graphicsview/qgraphicsview_p.h b/src/gui/graphicsview/qgraphicsview_p.h index bdf5ddd..762cad1 100644 --- a/src/gui/graphicsview/qgraphicsview_p.h +++ b/src/gui/graphicsview/qgraphicsview_p.h @@ -1,6 +1,7 @@ /**************************************************************************** ** ** Copyright (C) 2009 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. @@ -9,8 +10,8 @@ ** No Commercial Usage ** This file contains pre-release code and may not be distributed. ** You may use this file in accordance with the terms and conditions -** contained in the either Technology Preview License Agreement or the -** Beta Release License Agreement. +** 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 @@ -20,21 +21,20 @@ ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** -** In addition, as a special exception, Nokia gives you certain -** additional rights. These rights are described in the Nokia Qt LGPL -** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this -** package. +** 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. +** +** +** +** +** +** ** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. ** -** If you are unsure which license is appropriate for your use, please -** contact the sales department at http://qt.nokia.com/contact. ** $QT_END_LICENSE$ ** ****************************************************************************/ @@ -47,8 +47,8 @@ // ------------- // // This file is not part of the Qt API. It exists for the convenience -// of qapplication_*.cpp, qwidget*.cpp and qfiledialog.cpp. This header -// file may change from version to version without notice, or even be removed. +// of other Qt classes. This header file may change from version to +// version without notice, or even be removed. // // We mean it. // diff --git a/src/gui/graphicsview/qgraphicswidget.cpp b/src/gui/graphicsview/qgraphicswidget.cpp index 157fbe5..224f50b 100644 --- a/src/gui/graphicsview/qgraphicswidget.cpp +++ b/src/gui/graphicsview/qgraphicswidget.cpp @@ -1,6 +1,7 @@ /**************************************************************************** ** ** Copyright (C) 2009 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. @@ -9,8 +10,8 @@ ** No Commercial Usage ** This file contains pre-release code and may not be distributed. ** You may use this file in accordance with the terms and conditions -** contained in the either Technology Preview License Agreement or the -** Beta Release License Agreement. +** 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 @@ -20,21 +21,20 @@ ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** -** In addition, as a special exception, Nokia gives you certain -** additional rights. These rights are described in the Nokia Qt LGPL -** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this -** package. +** 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. +** +** +** +** +** +** ** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. ** -** If you are unsure which license is appropriate for your use, please -** contact the sales department at http://qt.nokia.com/contact. ** $QT_END_LICENSE$ ** ****************************************************************************/ @@ -168,6 +168,8 @@ QT_BEGIN_NAMESPACE \sa QGraphicsProxyWidget, QGraphicsItem, {Widgets and Layouts} */ +bool qt_sendSpontaneousEvent(QObject *receiver, QEvent *event); + /*! Constructs a QGraphicsWidget instance. The optional \a parent argument is passed to QGraphicsItem's constructor. The optional \a wFlags argument @@ -379,6 +381,8 @@ void QGraphicsWidget::setGeometry(const QRectF &rect) QSizeF oldSize = size(); QGraphicsLayoutItem::setGeometry(newGeom); + wd->invalidateCachedClipPathRecursively(); + // Send resize event bool resized = newGeom.size() != oldSize; if (resized) { @@ -683,11 +687,13 @@ QSizeF QGraphicsWidget::sizeHint(Qt::SizeHint which, const QSizeF &constraint) c Q_D(const QGraphicsWidget); QSizeF sh; if (d->layout) { - sh = d->layout->effectiveSizeHint(which, constraint); + QSizeF marginSize(0,0); if (d->margins) { - sh += QSizeF(d->margins[d->Left] + d->margins[d->Right], + 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: @@ -1097,7 +1103,11 @@ QVariant QGraphicsWidget::propertyChange(const QString &propertyName, const QVar */ bool QGraphicsWidget::sceneEvent(QEvent *event) { - return QCoreApplication::sendEvent(this, event) || QGraphicsItem::sceneEvent(event); + bool spont = event->spontaneous(); + if (spont ? qt_sendSpontaneousEvent(this, event) : QApplication::sendEvent(this, event)) + return true; + event->spont = spont; + return QGraphicsItem::sceneEvent(event); } /*! @@ -1277,10 +1287,6 @@ bool QGraphicsWidget::event(QEvent *event) case QEvent::WindowActivate: case QEvent::WindowDeactivate: update(); - foreach (QGraphicsItem *child, childItems()) { - if (child->isWidget()) - QApplication::sendEvent(static_cast<QGraphicsWidget *>(child), event); - } break; // Taken from QWidget::event case QEvent::ActivationChange: @@ -1590,6 +1596,8 @@ void QGraphicsWidget::ungrabKeyboardEvent(QEvent *event) /*! Returns the widgets window type. + + \sa windowFlags(), isWindow(), isPanel() */ Qt::WindowType QGraphicsWidget::windowType() const { @@ -1605,6 +1613,13 @@ Qt::WindowType QGraphicsWidget::windowType() const 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 { @@ -1623,6 +1638,8 @@ void QGraphicsWidget::setWindowFlags(Qt::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. @@ -1646,15 +1663,11 @@ void QGraphicsWidget::setWindowFlags(Qt::WindowFlags wFlags) 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() + \sa QGraphicsScene::activeWindow(), QGraphicsScene::setActiveWindow(), isActive() */ bool QGraphicsWidget::isActiveWindow() const { - Q_D(const QGraphicsWidget); - if (!d->scene) - return false; - const QGraphicsWidget *w = window(); - return (!w && d->scene->d_func()->activationRefCount) || (w && d->scene->activeWindow() == w); + return isActive(); } /*! diff --git a/src/gui/graphicsview/qgraphicswidget.h b/src/gui/graphicsview/qgraphicswidget.h index ea8e78e..9c71140 100644 --- a/src/gui/graphicsview/qgraphicswidget.h +++ b/src/gui/graphicsview/qgraphicswidget.h @@ -1,6 +1,7 @@ /**************************************************************************** ** ** Copyright (C) 2009 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. @@ -9,8 +10,8 @@ ** No Commercial Usage ** This file contains pre-release code and may not be distributed. ** You may use this file in accordance with the terms and conditions -** contained in the either Technology Preview License Agreement or the -** Beta Release License Agreement. +** 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 @@ -20,21 +21,20 @@ ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** -** In addition, as a special exception, Nokia gives you certain -** additional rights. These rights are described in the Nokia Qt LGPL -** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this -** package. +** 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. +** +** +** +** +** +** ** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. ** -** If you are unsure which license is appropriate for your use, please -** contact the sales department at http://qt.nokia.com/contact. ** $QT_END_LICENSE$ ** ****************************************************************************/ diff --git a/src/gui/graphicsview/qgraphicswidget_p.cpp b/src/gui/graphicsview/qgraphicswidget_p.cpp index 6d7e44a..b747a30 100644 --- a/src/gui/graphicsview/qgraphicswidget_p.cpp +++ b/src/gui/graphicsview/qgraphicswidget_p.cpp @@ -1,6 +1,7 @@ /**************************************************************************** ** ** Copyright (C) 2009 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. @@ -9,8 +10,8 @@ ** No Commercial Usage ** This file contains pre-release code and may not be distributed. ** You may use this file in accordance with the terms and conditions -** contained in the either Technology Preview License Agreement or the -** Beta Release License Agreement. +** 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 @@ -20,21 +21,20 @@ ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** -** In addition, as a special exception, Nokia gives you certain -** additional rights. These rights are described in the Nokia Qt LGPL -** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this -** package. +** 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. +** +** +** +** +** +** ** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. ** -** If you are unsure which license is appropriate for your use, please -** contact the sales department at http://qt.nokia.com/contact. ** $QT_END_LICENSE$ ** ****************************************************************************/ @@ -393,49 +393,162 @@ void QGraphicsWidgetPrivate::windowFrameMousePressEvent(QGraphicsSceneMouseEvent 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 QSizePolicy sp = widget->layout() ? widget->layout()->sizePolicy() : widget->sizePolicy(); + const bool hasHFW = sp.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 QSizeF &min, const QSizeF &max, + const QGraphicsWidget *widget) { - int height; - int width; + const QRectF proposedRect = *rect; + qreal width = qBound(min.width(), proposedRect.width(), max.width()); + qreal height = qBound(min.height(), proposedRect.height(), max.height()); + + QSizePolicy sp = widget->sizePolicy(); + if (const QGraphicsLayout *l = widget->layout()) { + sp = l->sizePolicy(); + } + const bool hasHFW = sp.hasHeightForWidth(); // || sp.hasWidthForHeight(); + + const bool widthChanged = proposedRect.width() < widget->size().width(); + const bool heightChanged = proposedRect.height() < widget->size().height(); + + if (hasHFW) { + if (widthChanged || heightChanged) { + const qreal minh = min.height(); + const qreal maxh = max.height(); + const qreal proposedHFW = minimumHeightForWidth(width, minh, maxh, widget); + if (proposedHFW > proposedRect.height()) { + QSizeF effectiveSize = closestAcceptableSize(QSizeF(width, height), widget); + width = effectiveSize.width(); + height = effectiveSize.height(); + } + } + } + switch (section) { case Qt::LeftSection: - width = qRound(qBound(min.width(), rect->width(), max.width())); - rect->setRect(startGeometry.right() - width, startGeometry.top(), - width, startGeometry.height()); + rect->setRect(startGeometry.right() - qRound(width), startGeometry.top(), + qRound(width), startGeometry.height()); break; case Qt::TopLeftSection: - width = qRound(qBound(min.width(), rect->width(), max.width())); - height = qRound(qBound(min.height(), rect->height(), max.height())); - rect->setRect(startGeometry.right() - width, startGeometry.bottom() - height, - width, height); + rect->setRect(startGeometry.right() - qRound(width), startGeometry.bottom() - qRound(height), + qRound(width), qRound(height)); break; case Qt::TopSection: - height = qRound(qBound(min.height(), rect->height(), max.height())); - rect->setRect(startGeometry.left(), startGeometry.bottom() - height, - startGeometry.width(), height); + rect->setRect(startGeometry.left(), startGeometry.bottom() - qRound(height), + startGeometry.width(), qRound(height)); break; case Qt::TopRightSection: - height = qRound(qBound(min.height(), rect->height(), max.height())); - rect->setTop(rect->bottom() - height); - rect->setWidth(qBound(min.width(), rect->width(), max.width())); + rect->setTop(rect->bottom() - qRound(height)); + rect->setWidth(qRound(width)); break; case Qt::RightSection: - rect->setWidth(qBound(min.width(), rect->width(), max.width())); + rect->setWidth(qRound(width)); break; case Qt::BottomRightSection: - rect->setWidth(qBound(min.width(), rect->width(), max.width())); - rect->setHeight(qBound(min.height(), rect->height(), max.height())); + rect->setWidth(qRound(width)); + rect->setHeight(qRound(height)); break; case Qt::BottomSection: - rect->setHeight(qBound(min.height(), rect->height(), max.height())); + rect->setHeight(qRound(height)); break; case Qt::BottomLeftSection: - height = qRound(qBound(min.height(), rect->height(), max.height())); - width = qRound(qBound(min.width(), rect->width(), max.width())); - rect->setRect(startGeometry.right() - width, startGeometry.top(), - width, height); + rect->setRect(startGeometry.right() - qRound(width), startGeometry.top(), + qRound(width), qRound(height)); break; default: break; @@ -506,7 +619,8 @@ void QGraphicsWidgetPrivate::windowFrameMouseMoveEvent(QGraphicsSceneMouseEvent _q_boundGeometryToSizeConstraints(windowData->startGeometry, &newGeometry, windowData->grabbedSection, q->effectiveSizeHint(Qt::MinimumSize), - q->effectiveSizeHint(Qt::MaximumSize)); + q->effectiveSizeHint(Qt::MaximumSize), + q); q->setGeometry(newGeometry); } } diff --git a/src/gui/graphicsview/qgraphicswidget_p.h b/src/gui/graphicsview/qgraphicswidget_p.h index 0e1fe46..eb53649 100644 --- a/src/gui/graphicsview/qgraphicswidget_p.h +++ b/src/gui/graphicsview/qgraphicswidget_p.h @@ -1,6 +1,7 @@ /**************************************************************************** ** ** Copyright (C) 2009 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. @@ -9,8 +10,8 @@ ** No Commercial Usage ** This file contains pre-release code and may not be distributed. ** You may use this file in accordance with the terms and conditions -** contained in the either Technology Preview License Agreement or the -** Beta Release License Agreement. +** 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 @@ -20,21 +21,20 @@ ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** -** In addition, as a special exception, Nokia gives you certain -** additional rights. These rights are described in the Nokia Qt LGPL -** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this -** package. +** 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. +** +** +** +** +** +** ** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. ** -** If you are unsure which license is appropriate for your use, please -** contact the sales department at http://qt.nokia.com/contact. ** $QT_END_LICENSE$ ** ****************************************************************************/ @@ -47,8 +47,8 @@ // ------------- // // This file is not part of the Qt API. It exists for the convenience -// of qapplication_*.cpp, qwidget*.cpp and qfiledialog.cpp. This header -// file may change from version to version without notice, or even be removed. +// of other Qt classes. This header file may change from version to +// version without notice, or even be removed. // // We mean it. // diff --git a/src/gui/graphicsview/qgridlayoutengine.cpp b/src/gui/graphicsview/qgridlayoutengine.cpp index 5ad6ac9..f61360a 100644 --- a/src/gui/graphicsview/qgridlayoutengine.cpp +++ b/src/gui/graphicsview/qgridlayoutengine.cpp @@ -1,6 +1,7 @@ /**************************************************************************** ** ** Copyright (C) 2009 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. @@ -9,8 +10,8 @@ ** No Commercial Usage ** This file contains pre-release code and may not be distributed. ** You may use this file in accordance with the terms and conditions -** contained in the either Technology Preview License Agreement or the -** Beta Release License Agreement. +** 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 @@ -20,21 +21,20 @@ ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** -** In addition, as a special exception, Nokia gives you certain -** additional rights. These rights are described in the Nokia Qt LGPL -** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this -** package. +** 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. +** +** +** +** +** +** ** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. ** -** If you are unsure which license is appropriate for your use, please -** contact the sales department at http://qt.nokia.com/contact. ** $QT_END_LICENSE$ ** ****************************************************************************/ @@ -461,7 +461,7 @@ void QGridLayoutRowData::dump(int indent) const QGridLayoutItem::QGridLayoutItem(QGridLayoutEngine *engine, QGraphicsLayoutItem *layoutItem, int row, int column, int rowSpan, int columnSpan, - Qt::Alignment alignment) + Qt::Alignment alignment, int itemAtIndex) : q_engine(engine), q_layoutItem(layoutItem), q_alignment(alignment) { q_firstRows[Hor] = column; @@ -471,7 +471,7 @@ QGridLayoutItem::QGridLayoutItem(QGridLayoutEngine *engine, QGraphicsLayoutItem q_stretches[Hor] = -1; q_stretches[Ver] = -1; - q_engine->addItem(this); + q_engine->insertItem(this, itemAtIndex); } int QGridLayoutItem::firstRow(Qt::Orientation orientation) const @@ -937,11 +937,20 @@ Qt::Alignment QGridLayoutEngine::effectiveAlignment(const QGridLayoutItem *layou return align; } -void QGridLayoutEngine::addItem(QGridLayoutItem *item) +/*! + \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()); - q_items.append(item); + 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) { @@ -952,6 +961,11 @@ void QGridLayoutEngine::addItem(QGridLayoutItem *item) } } +void QGridLayoutEngine::addItem(QGridLayoutItem *item) +{ + insertItem(item, -1); +} + void QGridLayoutEngine::removeItem(QGridLayoutItem *item) { Q_ASSERT(q_items.contains(item)); diff --git a/src/gui/graphicsview/qgridlayoutengine_p.h b/src/gui/graphicsview/qgridlayoutengine_p.h index 8f3eb13..a42a537 100644 --- a/src/gui/graphicsview/qgridlayoutengine_p.h +++ b/src/gui/graphicsview/qgridlayoutengine_p.h @@ -1,6 +1,7 @@ /**************************************************************************** ** ** Copyright (C) 2009 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. @@ -9,8 +10,8 @@ ** No Commercial Usage ** This file contains pre-release code and may not be distributed. ** You may use this file in accordance with the terms and conditions -** contained in the either Technology Preview License Agreement or the -** Beta Release License Agreement. +** 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 @@ -20,21 +21,20 @@ ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** -** In addition, as a special exception, Nokia gives you certain -** additional rights. These rights are described in the Nokia Qt LGPL -** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this -** package. +** 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. +** +** +** +** +** +** ** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. ** -** If you are unsure which license is appropriate for your use, please -** contact the sales department at http://qt.nokia.com/contact. ** $QT_END_LICENSE$ ** ****************************************************************************/ @@ -267,7 +267,8 @@ class QGridLayoutItem { public: QGridLayoutItem(QGridLayoutEngine *engine, QGraphicsLayoutItem *layoutItem, int row, int column, - int rowSpan = 1, int columnSpan = 1, Qt::Alignment alignment = 0); + 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]; } @@ -378,6 +379,7 @@ public: 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; diff --git a/src/gui/graphicsview/qsimplex_p.cpp b/src/gui/graphicsview/qsimplex_p.cpp new file mode 100644 index 0000000..e3a991e --- /dev/null +++ b/src/gui/graphicsview/qsimplex_p.cpp @@ -0,0 +1,413 @@ +/**************************************************************************** +** +** Copyright (C) 2009 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 + +QSimplex::QSimplex() : objective(0), rows(0), columns(0), firstArtificial(0), matrix(0) +{ +} + +QSimplex::~QSimplex() +{ + clearDataStructures(); +} + +void QSimplex::clearDataStructures() +{ + if (matrix == 0) + return; + + // Matrix + rows = 0; + columns = 0; + firstArtificial = 0; + free(matrix); + matrix = 0; + + // Constraints + for (int i = 0; i < constraints.size(); ++i) { + delete constraints[i]->helper.first; + constraints[i]->helper.first = 0; + constraints[i]->helper.second = 0.0; + delete constraints[i]->artificial; + constraints[i]->artificial = 0; + } + constraints.clear(); + + // Other + variables.clear(); + objective = 0; +} + +bool QSimplex::setConstraints(const QList<QSimplexConstraint *> newConstraints) +{ + clearDataStructures(); + + if (newConstraints.isEmpty()) + return true; // we are ok with no constraints + constraints = newConstraints; + + // Set Variables direct mapping + QSet<QSimplexVariable *> variablesSet; + for (int i = 0; i < constraints.size(); ++i) + variablesSet += \ + QSet<QSimplexVariable *>::fromList(constraints[i]->variables.keys()); + variables = variablesSet.toList(); + + // Set Variables reverse mapping + for (int i = 0; i < variables.size(); ++i) { + // The variable "0" goes at the column "1", etc... + variables[i]->index = i + 1; + } + + // Normalize Constraints + int variableIndex = variables.size(); + QList <QSimplexVariable *> artificialList; + + for (int i = 0; i < constraints.size(); ++i) { + QSimplexVariable *slack; + QSimplexVariable *surplus; + QSimplexVariable *artificial; + + Q_ASSERT(constraints[i]->helper.first == 0); + Q_ASSERT(constraints[i]->artificial == 0); + + switch(constraints[i]->ratio) { + case QSimplexConstraint::LessOrEqual: + slack = new QSimplexVariable; + slack->index = ++variableIndex; + constraints[i]->helper.first = slack; + constraints[i]->helper.second = 1.0; + break; + case QSimplexConstraint::MoreOrEqual: + surplus = new QSimplexVariable; + surplus->index = ++variableIndex; + constraints[i]->helper.first = surplus; + constraints[i]->helper.second = -1.0; + // fall through + case QSimplexConstraint::Equal: + artificial = new QSimplexVariable; + constraints[i]->artificial = artificial; + artificialList += constraints[i]->artificial; + break; + } + } + + firstArtificial = variableIndex + 1; + for (int i = 0; i < artificialList.size(); ++i) + artificialList[i]->index = ++variableIndex; + artificialList.clear(); + + // Matrix + + // One for each variable plus the Basic and BFS columns (first and last) + columns = variableIndex + 2; + // One for each constraint plus the objective function + rows = constraints.size() + 1; + + matrix = (qreal *)malloc(sizeof(qreal) * columns * rows); + if (!matrix) { + qWarning() << "QSimplex: Unable to allocate memory!"; + return 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 temporary objective: -1 * sum_of_artificial_vars + for (int j = firstArtificial; j < columns - 1; ++j) + setValueAt(0, j, 1.0); + + // Maximize our objective (artificial vars go to zero) + solveMaxHelper(); + + if (valueAt(0, columns - 1) != 0.0) { + qWarning() << "QSimplex: No feasible solution!"; + clearDataStructures(); + return false; + } + + // Remove artificial variables + clearColumns(firstArtificial, columns - 2); + return true; +} + +void QSimplex::solveMaxHelper() +{ + reducedRowEchelon(); + while (iterate()) ; +} + +void QSimplex::setObjective(QSimplexConstraint *newObjective) +{ + objective = newObjective; +} + +void QSimplex::clearRow(int rowIndex) +{ + qreal *item = matrix + rowIndex * columns; + for (int i = 0; i < columns; ++i) + item[i] = 0.0; +} + +void QSimplex::clearColumns(int first, int last) +{ + for (int i = 0; i < rows; ++i) { + qreal *row = matrix + i * columns; + for (int j = first; j <= last; ++j) + row[j] = 0.0; + } +} + +void QSimplex::dumpMatrix() +{ + printf("---- Simplex Matrix ----\n"); + + printf(" "); + for (int j = 0; j < columns; ++j) + printf(" <% 2d >", j); + printf("\n"); + + for (int i = 0; i < rows; ++i) { + printf("Row %2d:", i); + + qreal *row = matrix + i * columns; + for (int j = 0; j < columns; ++j) { + printf(" % 2.2f", row[j]); + } + printf("\n"); + } + printf("------------------------\n\n"); +} + +void QSimplex::combineRows(int toIndex, int fromIndex, qreal factor) +{ + if (!factor) + return; + + qreal *from = matrix + fromIndex * columns; + qreal *to = matrix + toIndex * columns; + + for (int j = 1; j < columns; ++j) { + qreal value = from[j]; + + // skip to[j] = to[j] + factor*0.0 + if (value == 0.0) + continue; + + to[j] += factor * value; + + // ### Avoid Numerical errors + if (qAbs(to[j]) < 0.0000000001) + to[j] = 0.0; + } +} + +int QSimplex::findPivotColumn() +{ + qreal min = 0; + int minIndex = -1; + + for (int j = 0; j < columns-1; ++j) { + if (valueAt(0, j) < min) { + min = valueAt(0, j); + minIndex = j; + } + } + + return minIndex; +} + +int QSimplex::pivotRowForColumn(int column) +{ + qreal min = 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; + } + } + + return minIndex; +} + +void QSimplex::reducedRowEchelon() +{ + for (int i = 1; i < rows; ++i) { + int factorInObjectiveRow = valueAt(i, 0); + combineRows(0, i, -1 * valueAt(0, factorInObjectiveRow)); + } +} + +bool QSimplex::iterate() +{ + // Find Pivot column + int pivotColumn = findPivotColumn(); + if (pivotColumn == -1) + return false; + + // Find Pivot row for column + int pivotRow = pivotRowForColumn(pivotColumn); + if (pivotRow == -1) { + qWarning() << "QSimplex: Unbounded problem!"; + return false; + } + + // Normalize Pivot Row + qreal pivot = valueAt(pivotRow, pivotColumn); + if (pivot != 1.0) + combineRows(pivotRow, pivotRow, (1.0 - pivot) / pivot); + + // Update other rows + for (int row=0; row < rows; ++row) { + if (row == pivotRow) + continue; + + combineRows(row, pivotRow, -1 * valueAt(row, pivotColumn)); + } + + // Update first column + setValueAt(pivotRow, 0, pivotColumn); + + // dumpMatrix(); + // printf("------------ end of iteration --------------\n"); + return true; +} + +/*! + \internal + + Both solveMin and solveMax are interfaces to this method. + + The enum solverFactor admits 2 values: Minimum (-1) and Maximum (+1). + */ +qreal QSimplex::solver(solverFactor factor) +{ + // Remove old objective + clearRow(0); + + // Set new objective + QHash<QSimplexVariable *, qreal>::const_iterator iter; + for (iter = objective->variables.constBegin(); + iter != objective->variables.constEnd(); + ++iter) { + setValueAt(0, iter.key()->index, -1 * factor * iter.value()); + } + + solveMaxHelper(); + collectResults(); + + return factor * valueAt(0, columns - 1); +} + +qreal QSimplex::solveMin() +{ + return solver(Minimum); +} + +qreal QSimplex::solveMax() +{ + return solver(Maximum); +} + +void QSimplex::collectResults() +{ + // All variables are zero unless overridden below. + + // ### Is this really needed? Is there any chance that an + // important variable remains as non-basic at the end of simplex? + for (int i = 0; i < variables.size(); ++i) + variables[i]->result = 0; + + // Basic variables + // Update the variable indicated in the first column with the value + // in the last column. + for (int i = 1; i < rows; ++i) { + int index = valueAt(i, 0) - 1; + if (index < variables.size()) + variables[index]->result = valueAt(i, columns - 1); + } +} + +QT_END_NAMESPACE diff --git a/src/gui/graphicsview/qsimplex_p.h b/src/gui/graphicsview/qsimplex_p.h new file mode 100644 index 0000000..54b080d --- /dev/null +++ b/src/gui/graphicsview/qsimplex_p.h @@ -0,0 +1,164 @@ +/**************************************************************************** +** +** Copyright (C) 2009 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; + uint index; +}; + + +/*! + \internal + + Representation of a LP constraint like: + + (c1 * X1) + (c2 * X2) + ... = K + or <= K + or >= K + + Where (ci, Xi) are the pairs in "variables" and K the real "constant". +*/ +struct QSimplexConstraint +{ + QSimplexConstraint() : constant(0), ratio(Equal), artificial(0) {} + + enum Ratio { + LessOrEqual = 0, + Equal, + MoreOrEqual + }; + + QHash<QSimplexVariable *, qreal> variables; + qreal constant; + Ratio ratio; + + QPair<QSimplexVariable *, qreal> helper; + QSimplexVariable * artificial; +}; + + +class QSimplex +{ +public: + QSimplex(); + virtual ~QSimplex(); + + qreal solveMin(); + qreal solveMax(); + QList<QSimplexVariable *> constraintsVariables(); + + 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 + int findPivotColumn(); + int pivotRowForColumn(int column); + void reducedRowEchelon(); + bool iterate(); + + // Helpers + void clearDataStructures(); + void solveMaxHelper(); + enum solverFactor { Minimum = -1, Maximum = 1 }; + qreal solver(solverFactor factor); + void collectResults(); + + QList<QSimplexConstraint *> constraints; + QList<QSimplexVariable *> variables; + QSimplexConstraint *objective; + + int rows; + int columns; + int firstArtificial; + + qreal *matrix; +}; + +inline QList<QSimplexVariable *> QSimplex::constraintsVariables() +{ + return variables; +} + +inline qreal QSimplex::valueAt(int rowIndex, int columnIndex) +{ + return matrix[rowIndex * columns + columnIndex]; +} + +inline void QSimplex::setValueAt(int rowIndex, int columnIndex, qreal value) +{ + matrix[rowIndex * columns + columnIndex] = value; +} + +QT_END_NAMESPACE + +#endif // QSIMPLEX_P_H |