diff options
Diffstat (limited to 'src/gui')
85 files changed, 5239 insertions, 1061 deletions
diff --git a/src/gui/animation/qguivariantanimation.cpp b/src/gui/animation/qguivariantanimation.cpp index 9badc82..de9363c 100644 --- a/src/gui/animation/qguivariantanimation.cpp +++ b/src/gui/animation/qguivariantanimation.cpp @@ -45,6 +45,10 @@ #include <private/qvariantanimation_p.h> #include <QtGui/qcolor.h> +#include <QtGui/qvector2d.h> +#include <QtGui/qvector3d.h> +#include <QtGui/qvector4d.h> +#include <QtGui/qquaternion.h> QT_BEGIN_NAMESPACE @@ -56,17 +60,35 @@ template<> Q_INLINE_TEMPLATE QColor _q_interpolate(const QColor &f,const QColor _q_interpolate(f.alpha(), t.alpha(), progress)); } +template<> Q_INLINE_TEMPLATE QQuaternion _q_interpolate(const QQuaternion &f,const QQuaternion &t, qreal progress) +{ + return QQuaternion::slerp(f, t, progress); +} + static int qRegisterGuiGetInterpolator() { qRegisterAnimationInterpolator<QColor>(_q_interpolateVariant<QColor>); + qRegisterAnimationInterpolator<QVector2D>(_q_interpolateVariant<QVector2D>); + qRegisterAnimationInterpolator<QVector3D>(_q_interpolateVariant<QVector3D>); + qRegisterAnimationInterpolator<QVector4D>(_q_interpolateVariant<QVector4D>); + qRegisterAnimationInterpolator<QQuaternion>(_q_interpolateVariant<QQuaternion>); return 1; } Q_CONSTRUCTOR_FUNCTION(qRegisterGuiGetInterpolator) static int qUnregisterGuiGetInterpolator() { + // casts required by Sun CC 5.5 qRegisterAnimationInterpolator<QColor>( - (QVariant (*)(const QColor &, const QColor &, qreal))0); // cast required by Sun CC 5.5 + (QVariant (*)(const QColor &, const QColor &, qreal))0); + qRegisterAnimationInterpolator<QVector2D>( + (QVariant (*)(const QVector2D &, const QVector2D &, qreal))0); + qRegisterAnimationInterpolator<QVector3D>( + (QVariant (*)(const QVector3D &, const QVector3D &, qreal))0); + qRegisterAnimationInterpolator<QVector4D>( + (QVariant (*)(const QVector4D &, const QVector4D &, qreal))0); + qRegisterAnimationInterpolator<QQuaternion>( + (QVariant (*)(const QQuaternion &, const QQuaternion &, qreal))0); return 1; } diff --git a/src/gui/dialogs/qfileinfogatherer_p.h b/src/gui/dialogs/qfileinfogatherer_p.h index cb980d2..bab4a29 100644 --- a/src/gui/dialogs/qfileinfogatherer_p.h +++ b/src/gui/dialogs/qfileinfogatherer_p.h @@ -184,7 +184,7 @@ private: QMutex mutex; QWaitCondition condition; - bool abort; + volatile bool abort; QStack<QString> path; QStack<QStringList> files; diff --git a/src/gui/egl/qegl.cpp b/src/gui/egl/qegl.cpp index 9d0ead1..f0df0c9 100644 --- a/src/gui/egl/qegl.cpp +++ b/src/gui/egl/qegl.cpp @@ -402,7 +402,7 @@ void QEglContext::dumpAllConfigs() QString QEglContext::extensions() { - const char* exts = eglQueryString(dpy, EGL_EXTENSIONS); + const char* exts = eglQueryString(QEglContext::defaultDisplay(0), EGL_EXTENSIONS); return QString(QLatin1String(exts)); } diff --git a/src/gui/egl/qegl_p.h b/src/gui/egl/qegl_p.h index bba5e59..97730d8 100644 --- a/src/gui/egl/qegl_p.h +++ b/src/gui/egl/qegl_p.h @@ -122,8 +122,8 @@ public: void dumpAllConfigs(); - QString extensions(); - bool hasExtension(const char* extensionName); + static QString extensions(); + static bool hasExtension(const char* extensionName); private: QEgl::API apiType; diff --git a/src/gui/egl/qeglproperties.cpp b/src/gui/egl/qeglproperties.cpp index 22b55fe..7b582c4 100644 --- a/src/gui/egl/qeglproperties.cpp +++ b/src/gui/egl/qeglproperties.cpp @@ -257,6 +257,20 @@ static void addTag(QString& str, const QString& tag) str += tag; } +void QEglProperties::dumpAllConfigs() +{ + EGLint count = 0; + eglGetConfigs(QEglContext::defaultDisplay(0), 0, 0, &count); + if (count < 1) + return; + + EGLConfig *configs = new EGLConfig [count]; + eglGetConfigs(QEglContext::defaultDisplay(0), configs, count, &count); + for (EGLint index = 0; index < count; ++index) + qWarning() << QEglProperties(configs[index]).toString(); + delete [] configs; +} + // Convert a property list to a string suitable for debug output. QString QEglProperties::toString() const { diff --git a/src/gui/egl/qeglproperties_p.h b/src/gui/egl/qeglproperties_p.h index 4ef3814..383a873 100644 --- a/src/gui/egl/qeglproperties_p.h +++ b/src/gui/egl/qeglproperties_p.h @@ -130,6 +130,8 @@ public: QString toString() const; + static void dumpAllConfigs(); + private: QVarLengthArray<int> props; }; diff --git a/src/gui/embedded/qscreen_qws.cpp b/src/gui/embedded/qscreen_qws.cpp index 1d49bd1..cee4a21 100644 --- a/src/gui/embedded/qscreen_qws.cpp +++ b/src/gui/embedded/qscreen_qws.cpp @@ -181,7 +181,27 @@ void QScreenCursor::set(const QImage &image, int hotx, int hoty) const QRect r = boundingRect(); hotspot = QPoint(hotx, hoty); - cursor = image; + // These are in almost all cases the fastest formats to blend + QImage::Format f; + switch (qt_screen->depth()) { + case 12: + f = QImage::Format_ARGB4444_Premultiplied; + break; + case 15: + f = QImage::Format_ARGB8555_Premultiplied; + break; + case 16: + f = QImage::Format_ARGB8565_Premultiplied; + break; + case 18: + f = QImage::Format_ARGB6666_Premultiplied; + break; + default: + f = QImage::Format_ARGB32_Premultiplied; + } + + cursor = image.convertToFormat(f); + size = image.size(); if (enable && !hwaccel) diff --git a/src/gui/embedded/qscreenlinuxfb_qws.cpp b/src/gui/embedded/qscreenlinuxfb_qws.cpp index 34fc54c..69e5808 100644 --- a/src/gui/embedded/qscreenlinuxfb_qws.cpp +++ b/src/gui/embedded/qscreenlinuxfb_qws.cpp @@ -271,15 +271,13 @@ bool QLinuxFbScreen::connect(const QString &displaySpec) QScreen::setFrameBufferLittleEndian(true); #endif - // Check for explicitly specified device - const int len = 8; // "/dev/fbx" - int m = displaySpec.indexOf(QLatin1String("/dev/fb")); - - QString dev; - if (m > 0) - dev = displaySpec.mid(m, len); - else - dev = QLatin1String("/dev/fb0"); + QString dev = QLatin1String("/dev/fb0"); + foreach(QString d, args) { + if (d.startsWith(QLatin1Char('/'))) { + dev = d; + break; + } + } if (access(dev.toLatin1().constData(), R_OK|W_OK) == 0) d_ptr->fd = QT_OPEN(dev.toLatin1().constData(), O_RDWR); diff --git a/src/gui/graphicsview/graphicsview.pri b/src/gui/graphicsview/graphicsview.pri index 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..7130003 --- /dev/null +++ b/src/gui/graphicsview/qgraph_p.h @@ -0,0 +1,240 @@ +#include <QtCore/QHash> +#include <QtCore/QQueue> +#include <QtCore/QString> +#include <QtCore/QDebug> + +#include <float.h> + +QT_BEGIN_NAMESPACE + +template <typename Vertex, typename EdgeData> +class Graph +{ +public: + Graph() {} + + class const_iterator { + public: + const_iterator(const Graph *graph, bool begin) : g(graph){ + if (begin) { + row = g->m_graph.constBegin(); + //test if the graph is empty + if (row != g->m_graph.constEnd()) + { + column = (*row)->constBegin(); + } + } else { + row = g->m_graph.constEnd(); + } + } + + inline Vertex *operator*() { + return column.key(); + } + + inline Vertex *from() const { + return row.key(); + } + + inline Vertex *to() const { + return column.key(); + } + + inline bool operator==(const const_iterator &o) const { return !(*this != o); } + inline bool operator!=(const const_iterator &o) const { + if (row == g->m_graph.end()) { + return row != o.row; + } else { + return row != o.row || column != o.column; + } + } + inline const_iterator& operator=(const const_iterator &o) const { row = o.row; column = o.column; return *this;} + + // prefix + const_iterator &operator++() { + if (row != g->m_graph.constEnd()) { + ++column; + if (column == (*row)->constEnd()) { + ++row; + if (row != g->m_graph.constEnd()) { + column = (*row)->constBegin(); + } + } + } + return *this; + } + + private: + const Graph *g; + Q_TYPENAME QHash<Vertex *, QHash<Vertex *, EdgeData *> * >::const_iterator row; + Q_TYPENAME QHash<Vertex *, EdgeData *>::const_iterator column; + }; + + const_iterator constBegin() const { + return const_iterator(this,true); + } + + const_iterator constEnd() const { + return const_iterator(this,false); + } + + /*! + * \internal + * + * If there is an edge between \a first and \a second, it will return a structure + * containing the data associated with the edge, otherwise it will return 0. + * + */ + EdgeData *edgeData(Vertex* first, Vertex* second) { + QHash<Vertex *, EdgeData *> *row = m_graph.value(first); + return row ? row->value(second) : 0; + } + + void createEdge(Vertex *first, Vertex *second, EdgeData *data) + { + // Creates a bidirectional edge +#if defined(QT_DEBUG) && 0 + qDebug("Graph::createEdge(): %s", + qPrintable(QString::fromAscii("%1-%2") + .arg(first->toString()).arg(second->toString()))); +#endif + if (edgeData(first, second)) { +#ifdef QT_DEBUG + qWarning(qPrintable(QString::fromAscii("%1-%2 already has an edge") + .arg(first->toString()).arg(second->toString()))); +#endif + } + createDirectedEdge(first, second, data); + createDirectedEdge(second, first, data); + } + + void removeEdge(Vertex *first, Vertex *second) + { + // Removes a bidirectional edge +#if defined(QT_DEBUG) && 0 + qDebug("Graph::removeEdge(): %s", + qPrintable(QString::fromAscii("%1-%2") + .arg(first->toString()).arg(second->toString()))); +#endif + EdgeData *data = edgeData(first, second); + removeDirectedEdge(first, second); + removeDirectedEdge(second, first); + if (data) delete data; + } + + EdgeData *takeEdge(Vertex* first, Vertex* second) + { +#if defined(QT_DEBUG) && 0 + qDebug("Graph::takeEdge(): %s", + qPrintable(QString::fromAscii("%1-%2") + .arg(first->toString()).arg(second->toString()))); +#endif + // Removes a bidirectional edge + EdgeData *data = edgeData(first, second); + if (data) { + removeDirectedEdge(first, second); + removeDirectedEdge(second, first); + } + return data; + } + + QList<Vertex *> adjacentVertices(Vertex *vertex) const + { + QHash<Vertex *, EdgeData *> *row = m_graph.value(vertex); + QList<Vertex *> l; + if (row) + l = row->keys(); + return l; + } + + void setRootVertex(Vertex *vertex) + { + userVertex = vertex; + } + + QSet<Vertex*> vertices() const { + QSet<Vertex *> setOfVertices; + for (const_iterator it = constBegin(); it != constEnd(); ++it) { + setOfVertices.insert(*it); + } + return setOfVertices; + } + + QList<QPair<Vertex*, Vertex*> > connections() const { + QList<QPair<Vertex*, Vertex*> > conns; + for (const_iterator it = constBegin(); it != constEnd(); ++it) { + Vertex *from = it.from(); + Vertex *to = it.to(); + // do not return (from,to) *and* (to,from) + if (from < to) { + conns.append(qMakePair(from, to)); + } + } + return conns; + } + +#if defined(QT_DEBUG) + QString serializeToDot() { // traversal + QString strVertices; + QString edges; + + QSet<Vertex *> setOfVertices = vertices(); + for (Q_TYPENAME QSet<Vertex*>::const_iterator it = setOfVertices.begin(); it != setOfVertices.end(); ++it) { + Vertex *v = *it; + QList<Vertex*> adjacents = adjacentVertices(v); + for (int i = 0; i < adjacents.count(); ++i) { + Vertex *v1 = adjacents.at(i); + EdgeData *data = edgeData(v, v1); + bool forward = data->from == v; + if (forward) { + edges += QString::fromAscii("%1->%2 [label=\"[%3,%4,%5]\" dir=both color=\"#000000:#a0a0a0\"] \n") + .arg(v->toString()) + .arg(v1->toString()) + .arg(data->minSize) + .arg(data->prefSize) + .arg(data->maxSize) + ; + } + } + strVertices += QString::fromAscii("%1 [label=\"%2\"]\n").arg(v->toString()).arg(v->toString()); + } + return QString::fromAscii("%1\n%2\n").arg(strVertices).arg(edges); + } +#endif + + Vertex *rootVertex() const + { + return userVertex; + } + +protected: + void createDirectedEdge(Vertex *from, Vertex *to, EdgeData *data) + { + QHash<Vertex *, EdgeData *> *adjacentToFirst = m_graph.value(from); + if (!adjacentToFirst) { + adjacentToFirst = new QHash<Vertex *, EdgeData *>(); + m_graph.insert(from, adjacentToFirst); + } + adjacentToFirst->insert(to, data); + } + + void removeDirectedEdge(Vertex *from, Vertex *to) + { + QHash<Vertex *, EdgeData *> *adjacentToFirst = m_graph.value(from); + Q_ASSERT(adjacentToFirst); + + adjacentToFirst->remove(to); + if (adjacentToFirst->isEmpty()) { + //nobody point to 'from' so we can remove it from the graph + m_graph.remove(from); + delete adjacentToFirst; + } + } + +private: + Vertex *userVertex; + + QHash<Vertex *, QHash<Vertex *, EdgeData *> *> m_graph; +}; + +QT_END_NAMESPACE diff --git a/src/gui/graphicsview/qgraphicsanchorlayout.cpp b/src/gui/graphicsview/qgraphicsanchorlayout.cpp new file mode 100644 index 0000000..3c2ea37 --- /dev/null +++ b/src/gui/graphicsview/qgraphicsanchorlayout.cpp @@ -0,0 +1,454 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! + \class QGraphicsAnchorLayout + \brief The QGraphicsAnchorLayout class provides a layout where one can anchor widgets + together in Graphics View. + \since 4.6 + \ingroup appearance + \ingroup geomanagement + \ingroup graphicsview-api + + The anchor layout is a layout where one can specify how widgets should be placed relative to + each other. The specification is called an anchor, and it is set up by calling anchor(). + Anchors are always set up between edges of an item, where the "center" is also considered to + be an edge. Considering this example: + \code + QGraphicsAnchorLayout *l = new QGraphicsAnchorLayout; + QGraphicsWidget *a = new QGraphicsWidget; + QGraphicsWidget *b = new QGraphicsWidget; + l->anchor(a, Qt::AnchorRight, b, Qt::AnchorLeft); + \endcode + + Here is the right edge of item A anchored to the left edge of item B, with the result that + item B will be placed to the right of item A, with a spacing between A and B. If the + spacing is negative, the items will overlap to some extent. Items that are anchored are + automatically added to the layout, and if items are removed, all their anchors will be + automatically removed + + \section1 Size Hints and Size Policies in QGraphicsLinearLayout + QGraphicsLinearLayout respects each item's size hints and size policies. However it does + not respect stretch factors currently. This might change in the future, so please refrain + from using stretch factors in anchor layout to avoid any future regressions. + + \section1 Spacing within QGraphicsAnchorLayout + + Between the items, the layout can distribute some space. If the spacing has not been + explicitly specified, the actual amount of space will usually be 0, but if the first edge + is the "opposite" of the second edge (i.e. Right is anchored to Left or vice-versa), the + size of the anchor will be queried from the style through the pixelMetric + PM_LayoutHorizontalSpacing (or PM_LayoutVerticalSpacing for vertical anchors). +*/ + +#include "qgraphicsanchorlayout_p.h" + +QT_BEGIN_NAMESPACE + +QGraphicsAnchorLayout::QGraphicsAnchorLayout(QGraphicsLayoutItem *parent) + : QGraphicsLayout(*new QGraphicsAnchorLayoutPrivate(), parent) +{ + Q_D(QGraphicsAnchorLayout); + d->createLayoutEdges(); +} + +QGraphicsAnchorLayout::~QGraphicsAnchorLayout() +{ + Q_D(QGraphicsAnchorLayout); + + for (int i = count() - 1; i >= 0; --i) { + QGraphicsLayoutItem *item = d->items.at(i); + removeAt(i); + if (item) { + if (item->ownedByLayout()) + delete item; + } + } + + d->removeCenterConstraints(this, QGraphicsAnchorLayoutPrivate::Horizontal); + d->removeCenterConstraints(this, QGraphicsAnchorLayoutPrivate::Vertical); + d->deleteLayoutEdges(); + + Q_ASSERT(d->itemCenterConstraints[0].isEmpty()); + Q_ASSERT(d->itemCenterConstraints[1].isEmpty()); + Q_ASSERT(d->items.isEmpty()); + Q_ASSERT(d->m_vertexList.isEmpty()); +} + +/*! + * Creates an anchor between the edge \a firstEdge of item \a firstItem and the edge \a secondEdge + * of item \a secondItem. The magnitude of the anchor is picked up from the style. Anchors + * between a layout edge and an item edge will have a size of 0. + * If there is already an anchor between the edges, the the new anchor will replace the old one. + * + * \a firstItem and \a secondItem are automatically added to the layout if they are not part + * of the layout. This means that count() can increase with up to 2. + * + * The spacing an anchor will get depends on the type of anchor. For instance, anchors from the + * Right edge of one item to the Left edge of another (or vice versa) will use the default + * horizontal spacing. The same behaviour applies to Bottom to Top anchors, (but they will use + * the default vertical spacing). For all other anchor combinations, the spacing will be 0. + * All anchoring functions will follow this rule. + * + * The spacing can also be set manually by using setAnchorSpacing() method. + * + * \sa removeAnchor(), addCornerAnchors(), addLeftAndRightAnchors(), addTopAndBottomAnchors(), + * addAllAnchors() + */ +void QGraphicsAnchorLayout::addAnchor(QGraphicsLayoutItem *firstItem, Qt::AnchorPoint firstEdge, + QGraphicsLayoutItem *secondItem, Qt::AnchorPoint secondEdge) +{ + Q_D(QGraphicsAnchorLayout); + d->anchor(firstItem, firstEdge, secondItem, secondEdge); + invalidate(); +} + +/*! + * Creates two anchors between \a firstItem and \a secondItem, where one is for the horizontal + * edge and another one for the vertical edge that the corners \a firstCorner and \a + * secondCorner specifies. + * The magnitude of the anchors is picked up from the style. + * + * This is a convenience function, since anchoring corners can be expressed as anchoring two edges. + * For instance, + * \code + * layout->addAnchor(layout, Qt::AnchorTop, b, Qt::AnchorTop); + * layout->addAnchor(layout, Qt::AnchorLeft, b, Qt::AnchorLeft); + * \endcode + * + * has the same effect as + * + * \code + * layout->addCornerAnchors(layout, Qt::TopLeft, b, Qt::TopLeft); + * \endcode + * + * If there is already an anchor between the edge pairs, it will be replaced by the anchors that + * this function specifies. + * + * \a firstItem and \a secondItem are automatically added to the layout if they are not part + * of the layout. This means that count() can increase with up to 2. + */ +void QGraphicsAnchorLayout::addCornerAnchors(QGraphicsLayoutItem *firstItem, + Qt::Corner firstCorner, + QGraphicsLayoutItem *secondItem, + Qt::Corner secondCorner) +{ + Q_D(QGraphicsAnchorLayout); + + // Horizontal anchor + Qt::AnchorPoint firstEdge = (firstCorner & 1 ? Qt::AnchorRight: Qt::AnchorLeft); + Qt::AnchorPoint secondEdge = (secondCorner & 1 ? Qt::AnchorRight: Qt::AnchorLeft); + d->anchor(firstItem, firstEdge, secondItem, secondEdge); + + // Vertical anchor + firstEdge = (firstCorner & 2 ? Qt::AnchorBottom: Qt::AnchorTop); + secondEdge = (secondCorner & 2 ? Qt::AnchorBottom: Qt::AnchorTop); + d->anchor(firstItem, firstEdge, secondItem, secondEdge); + + invalidate(); +} + +/*! + \fn QGraphicsAnchorLayout::addLeftAndRightAnchors(QGraphicsLayoutItem *firstEdge, QGraphicsLayoutItem *secondEdge) + + This convenience function is equivalent to calling + \code + l->addAnchor(firstEdge, Qt::AnchorLeft, secondEdge, Qt::AnchorLeft); + l->addAnchor(firstEdge, Qt::AnchorRight, secondEdge, Qt::AnchorRight); + \endcode +*/ + +/*! + \fn QGraphicsAnchorLayout::addTopAndBottomAnchors(QGraphicsLayoutItem *firstEdge, QGraphicsLayoutItem *secondEdge) + + This convenience function is equivalent to calling + \code + l->addAnchor(firstEdge, Qt::AnchorTop, secondEdge, Qt::AnchorTop); + l->addAnchor(firstEdge, Qt::AnchorBottom, secondEdge, Qt::AnchorBottom); + \endcode +*/ + +/*! + \fn QGraphicsAnchorLayout::addAllAnchors(QGraphicsLayoutItem *firstEdge, QGraphicsLayoutItem *secondEdge) + + This convenience function is equivalent to calling + \code + l->addLeftAndRightAnchors(firstEdge, secondEdge); + l->addTopAndBottomAnchors(firstEdge, secondEdge); + \endcode +*/ + +/*! + Set the spacing between the anchor point defined by \a firstItem and \a firstEdge and + \a secondItem and \a secondEdge to be \a spacing. +*/ +void QGraphicsAnchorLayout::setAnchorSpacing(const QGraphicsLayoutItem *firstItem, Qt::AnchorPoint firstEdge, + const QGraphicsLayoutItem *secondItem, Qt::AnchorPoint secondEdge, + qreal spacing) +{ + Q_D(QGraphicsAnchorLayout); + + if (!d->setAnchorSize(firstItem, firstEdge, secondItem, secondEdge, &spacing)) { + qWarning("setAnchorSpacing: The anchor does not exist."); + } + invalidate(); +} + +/*! + Returns the spacing between the anchor point defined by \a firstItem and \a firstEdge and + \a secondItem and \a secondEdge. The anchor must exist. +*/ +qreal QGraphicsAnchorLayout::anchorSpacing(const QGraphicsLayoutItem *firstItem, Qt::AnchorPoint firstEdge, + const QGraphicsLayoutItem *secondItem, Qt::AnchorPoint secondEdge) const +{ + Q_D(const QGraphicsAnchorLayout); + qreal size = 0; + if (!d->anchorSize(firstItem, firstEdge, secondItem, secondEdge, 0, &size)) { + qWarning("anchorSpacing: The anchor does not exist."); + } + return size; +} + +/*! + Resets the spacing between the anchor point defined by \a firstItem and \a firstEdge and + \a secondItem and \a secondEdge to be the default spacing. Depending on the anchor type, the + default spacing is either 0 or a value returned from the style. + + \sa setAnchorSpacing(), anchorSpacing(), addAnchor() +*/ +void QGraphicsAnchorLayout::unsetAnchorSpacing(const QGraphicsLayoutItem *firstItem, Qt::AnchorPoint firstEdge, + const QGraphicsLayoutItem *secondItem, Qt::AnchorPoint secondEdge) +{ + Q_D(QGraphicsAnchorLayout); + + if (!d->setAnchorSize(firstItem, firstEdge, secondItem, secondEdge, 0)) { + qWarning("unsetAnchorSpacing: The anchor does not exist."); + } + invalidate(); +} + +/*! + Removes the anchor between the edge \a firstEdge of item \a firstItem and the edge \a secondEdge + of item \a secondItem. If such an anchor does not exist, the layout will be left unchanged. +*/ +void QGraphicsAnchorLayout::removeAnchor(QGraphicsLayoutItem *firstItem, Qt::AnchorPoint firstEdge, + QGraphicsLayoutItem *secondItem, Qt::AnchorPoint secondEdge) +{ + Q_D(QGraphicsAnchorLayout); + if ((firstItem == 0) || (secondItem == 0)) { + qWarning("QGraphicsAnchorLayout::removeAnchor: " + "Cannot remove anchor between NULL items"); + return; + } + + if (firstItem == secondItem) { + qWarning("QGraphicsAnchorLayout::removeAnchor: " + "Cannot remove anchor from the item to itself"); + return; + } + + if (d->edgeOrientation(secondEdge) != d->edgeOrientation(firstEdge)) { + qWarning("QGraphicsAnchorLayout::removeAnchor: " + "Cannot remove anchor from edges of different orientations"); + return; + } + + d->removeAnchor(firstItem, firstEdge, secondItem, secondEdge); + invalidate(); +} + +/*! + Sets the default horizontal spacing for the anchor layout to \a spacing. + + \sa horizontalSpacing(), setVerticalSpacing(), setSpacing() +*/ +void QGraphicsAnchorLayout::setHorizontalSpacing(qreal spacing) +{ + Q_D(QGraphicsAnchorLayout); + d->spacings[0] = spacing; + invalidate(); +} + +/*! + Sets the default vertical spacing for the anchor layout to \a spacing. + + \sa verticalSpacing(), setHorizontalSpacing(), setSpacing() +*/ +void QGraphicsAnchorLayout::setVerticalSpacing(qreal spacing) +{ + Q_D(QGraphicsAnchorLayout); + d->spacings[1] = spacing; + invalidate(); +} + +/*! + Sets the default horizontal and the default vertical spacing for the anchor layout to \a spacing. + + If an item is anchored with no spacing associated with the anchor, it will use the default + spacing. + \sa setHorizontalSpacing(), setVerticalSpacing() +*/ +void QGraphicsAnchorLayout::setSpacing(qreal spacing) +{ + Q_D(QGraphicsAnchorLayout); + d->spacings[0] = d->spacings[1] = spacing; + invalidate(); +} + +/*! + Returns the default horizontal spacing for the anchor layout. + + \sa verticalSpacing(), setHorizontalSpacing() +*/ +qreal QGraphicsAnchorLayout::horizontalSpacing() const +{ + Q_D(const QGraphicsAnchorLayout); + return d->effectiveSpacing(QGraphicsAnchorLayoutPrivate::Horizontal); +} + +/*! + Returns the default vertical spacing for the anchor layout. + + \sa horizontalSpacing(), setVerticalSpacing() +*/ +qreal QGraphicsAnchorLayout::verticalSpacing() const +{ + Q_D(const QGraphicsAnchorLayout); + return d->effectiveSpacing(QGraphicsAnchorLayoutPrivate::Vertical); +} + +/*! + \reimp +*/ +void QGraphicsAnchorLayout::setGeometry(const QRectF &geom) +{ + Q_D(QGraphicsAnchorLayout); + + QGraphicsLayout::setGeometry(geom); + d->calculateVertexPositions(QGraphicsAnchorLayoutPrivate::Horizontal); + d->calculateVertexPositions(QGraphicsAnchorLayoutPrivate::Vertical); + d->setItemsGeometries(); +} + +/*! + Removing an item will also remove any of the anchors associated with it. +*/ +void QGraphicsAnchorLayout::removeAt(int index) +{ + Q_D(QGraphicsAnchorLayout); + QGraphicsLayoutItem *item = d->items.value(index); + + if (!item) + return; + + // Removing an item affects both horizontal and vertical graphs + d->restoreSimplifiedGraph(QGraphicsAnchorLayoutPrivate::Horizontal); + d->restoreSimplifiedGraph(QGraphicsAnchorLayoutPrivate::Vertical); + + d->removeCenterConstraints(item, QGraphicsAnchorLayoutPrivate::Horizontal); + d->removeCenterConstraints(item, QGraphicsAnchorLayoutPrivate::Vertical); + d->removeAnchors(item); + d->items.remove(index); + + item->setParentLayoutItem(0); + invalidate(); +} + +/*! + \reimp +*/ +int QGraphicsAnchorLayout::count() const +{ + Q_D(const QGraphicsAnchorLayout); + return d->items.size(); +} + +/*! + \reimp +*/ +QGraphicsLayoutItem *QGraphicsAnchorLayout::itemAt(int index) const +{ + Q_D(const QGraphicsAnchorLayout); + return d->items.value(index); +} + +/*! + \reimp +*/ +void QGraphicsAnchorLayout::invalidate() +{ + Q_D(QGraphicsAnchorLayout); + QGraphicsLayout::invalidate(); + d->calculateGraphCacheDirty = 1; +} + +/*! + \reimp +*/ +QSizeF QGraphicsAnchorLayout::sizeHint(Qt::SizeHint which, const QSizeF &constraint) const +{ + Q_UNUSED(which); + Q_UNUSED(constraint); + Q_D(const QGraphicsAnchorLayout); + + // Some setup calculations are delayed until the information is + // actually needed, avoiding unnecessary recalculations when + // adding multiple anchors. + + // sizeHint() / effectiveSizeHint() already have a cache + // mechanism, using invalidate() to force recalculation. However + // sizeHint() is called three times after invalidation (for max, + // min and pref), but we just need do our setup once. + + const_cast<QGraphicsAnchorLayoutPrivate *>(d)->calculateGraphs(); + + // ### apply constraint! + QSizeF engineSizeHint( + d->sizeHints[QGraphicsAnchorLayoutPrivate::Horizontal][which], + d->sizeHints[QGraphicsAnchorLayoutPrivate::Vertical][which]); + + qreal left, top, right, bottom; + getContentsMargins(&left, &top, &right, &bottom); + + return engineSizeHint + QSizeF(left + right, top + bottom); +} + +QT_END_NAMESPACE diff --git a/src/gui/graphicsview/qgraphicsanchorlayout.h b/src/gui/graphicsview/qgraphicsanchorlayout.h new file mode 100644 index 0000000..3de9ae5 --- /dev/null +++ b/src/gui/graphicsview/qgraphicsanchorlayout.h @@ -0,0 +1,141 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGRAPHICSANCHORLAYOUT_H +#define QGRAPHICSANCHORLAYOUT_H + +#include <QtGui/qgraphicsitem.h> +#include <QtGui/qgraphicslayout.h> + + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Gui) + +#if !defined(QT_NO_GRAPHICSVIEW) || (QT_EDITION & QT_MODULE_GRAPHICSVIEW) != QT_MODULE_GRAPHICSVIEW + +class QGraphicsAnchorLayoutPrivate; + +class Q_GUI_EXPORT QGraphicsAnchorLayout : public QGraphicsLayout +{ +public: + QGraphicsAnchorLayout(QGraphicsLayoutItem *parent = 0); + virtual ~QGraphicsAnchorLayout(); + + void addAnchor(QGraphicsLayoutItem *firstItem, Qt::AnchorPoint firstEdge, + QGraphicsLayoutItem *secondItem, Qt::AnchorPoint secondEdge); + + void addCornerAnchors(QGraphicsLayoutItem *firstItem, Qt::Corner firstCorner, + QGraphicsLayoutItem *secondItem, Qt::Corner secondCorner); + + inline void addLeftAndRightAnchors(QGraphicsLayoutItem *firstItem, + QGraphicsLayoutItem *secondItem); + + inline void addTopAndBottomAnchors(QGraphicsLayoutItem *firstItem, + QGraphicsLayoutItem *secondItem); + + inline void addAllAnchors(QGraphicsLayoutItem *firstItem, + QGraphicsLayoutItem *secondItem); + + void setAnchorSpacing(const QGraphicsLayoutItem *firstItem, Qt::AnchorPoint firstEdge, + const QGraphicsLayoutItem *secondItem, Qt::AnchorPoint secondEdge, + qreal spacing); + + qreal anchorSpacing(const QGraphicsLayoutItem *firstItem, Qt::AnchorPoint firstEdge, + const QGraphicsLayoutItem *secondItem, Qt::AnchorPoint secondEdge) const; + + void unsetAnchorSpacing(const QGraphicsLayoutItem *firstItem, Qt::AnchorPoint firstEdge, + const QGraphicsLayoutItem *secondItem, Qt::AnchorPoint secondEdge); + + void removeAnchor(QGraphicsLayoutItem *firstItem, Qt::AnchorPoint firstEdge, + QGraphicsLayoutItem *secondItem, Qt::AnchorPoint secondEdge); + + void setHorizontalSpacing(qreal spacing); + void setVerticalSpacing(qreal spacing); + void setSpacing(qreal spacing); + qreal horizontalSpacing() const; + qreal verticalSpacing() const; + + void removeAt(int index); + void setGeometry(const QRectF &rect); + int count() const; + QGraphicsLayoutItem *itemAt(int index) const; + + void invalidate(); +protected: + QSizeF sizeHint(Qt::SizeHint which, const QSizeF &constraint = QSizeF()) const; + +private: + Q_DISABLE_COPY(QGraphicsAnchorLayout) + Q_DECLARE_PRIVATE(QGraphicsAnchorLayout) +}; + + +void QGraphicsAnchorLayout::addLeftAndRightAnchors(QGraphicsLayoutItem *firstItem, + QGraphicsLayoutItem *secondItem) +{ + addAnchor(secondItem, Qt::AnchorLeft, firstItem, Qt::AnchorLeft); + addAnchor(firstItem, Qt::AnchorRight, secondItem, Qt::AnchorRight); +} + +void QGraphicsAnchorLayout::addTopAndBottomAnchors(QGraphicsLayoutItem *firstItem, + QGraphicsLayoutItem *secondItem) +{ + addAnchor(secondItem, Qt::AnchorTop, firstItem, Qt::AnchorTop); + addAnchor(firstItem, Qt::AnchorBottom, secondItem, Qt::AnchorBottom); +} + +void QGraphicsAnchorLayout::addAllAnchors(QGraphicsLayoutItem *firstItem, + QGraphicsLayoutItem *secondItem) +{ + addLeftAndRightAnchors(firstItem, secondItem); + addTopAndBottomAnchors(firstItem, secondItem); +} + +#endif + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp b/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp new file mode 100644 index 0000000..9a34ad5 --- /dev/null +++ b/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp @@ -0,0 +1,2104 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtGui/qwidget.h> +#include <QtCore/qlinkedlist.h> +#include <QtCore/qstack.h> + +#ifdef QT_DEBUG +#include <QtCore/qfile.h> +#endif + +#include "qgraphicsanchorlayout_p.h" + +QT_BEGIN_NAMESPACE + +void AnchorData::refreshSizeHints(qreal effectiveSpacing) +{ + if (!isLayoutAnchor && from->m_item == to->m_item) { + bool hasCenter = false; + QGraphicsLayoutItem *item = from->m_item; + + if (QGraphicsAnchorLayoutPrivate::edgeOrientation(from->m_edge) + == QGraphicsAnchorLayoutPrivate::Horizontal) { + minSize = item->minimumWidth(); + prefSize = item->preferredWidth(); + maxSize = item->maximumWidth(); + hasCenter = (from->m_edge == Qt::AnchorHorizontalCenter + || to->m_edge == Qt::AnchorHorizontalCenter); + } else { + minSize = item->minimumHeight(); + prefSize = item->preferredHeight(); + maxSize = item->maximumHeight(); + hasCenter = (from->m_edge == Qt::AnchorVerticalCenter + || to->m_edge == Qt::AnchorVerticalCenter); + } + + if (hasCenter) { + minSize /= 2; + prefSize /= 2; + maxSize /= 2; + } + + // Set the anchor effective sizes to preferred. + // + // Note: The idea here is that all items should remain at their + // preferred size unless where that's impossible. In cases where + // the item is subject to restrictions (anchored to the layout + // edges, for instance), the simplex solver will be run to + // recalculate and override the values we set here. + sizeAtMinimum = prefSize; + sizeAtPreferred = prefSize; + sizeAtMaximum = prefSize; + + } else if (!hasSize) { + // Anchor has no size defined, use given default information + minSize = effectiveSpacing; + prefSize = effectiveSpacing; + maxSize = effectiveSpacing; + + sizeAtMinimum = prefSize; + sizeAtPreferred = prefSize; + sizeAtMaximum = prefSize; + } +} + +void ParallelAnchorData::updateChildrenSizes() +{ + firstEdge->sizeAtMinimum = secondEdge->sizeAtMinimum = sizeAtMinimum; + firstEdge->sizeAtPreferred = secondEdge->sizeAtPreferred = sizeAtPreferred; + firstEdge->sizeAtMaximum = secondEdge->sizeAtMaximum = sizeAtMaximum; + + firstEdge->updateChildrenSizes(); + secondEdge->updateChildrenSizes(); +} + +void ParallelAnchorData::refreshSizeHints(qreal effectiveSpacing) +{ + // First refresh children information + firstEdge->refreshSizeHints(effectiveSpacing); + secondEdge->refreshSizeHints(effectiveSpacing); + + // ### should we warn if the parallel connection is invalid? + // e.g. 1-2-3 with 10-20-30, the minimum of the latter is + // bigger than the maximum of the former. + + minSize = qMax(firstEdge->minSize, secondEdge->minSize); + maxSize = qMin(firstEdge->maxSize, secondEdge->maxSize); + + prefSize = qMax(firstEdge->prefSize, secondEdge->prefSize); + prefSize = qMin(prefSize, maxSize); + + // See comment in AnchorData::refreshSizeHints() about sizeAt* values + sizeAtMinimum = prefSize; + sizeAtPreferred = prefSize; + sizeAtMaximum = prefSize; +} + +/*! + \internal + returns the factor in the interval [-1, 1]. + -1 is at Minimum + 0 is at Preferred + 1 is at Maximum +*/ +static qreal getFactor(qreal value, qreal min, qreal pref, qreal max) +{ + // ### Maybe remove some of the assertions? (since outside is asserting us) + Q_ASSERT(value > min || qFuzzyCompare(value, min)); + Q_ASSERT(value < max || qFuzzyCompare(value, max)); + + if (qFuzzyCompare(value, min)) { + return -1.0; + } else if (qFuzzyCompare(value, pref)) { + return 0.0; + } else if (qFuzzyCompare(value, max)) { + return 1.0; + } else if (value < pref) { + // Since value < pref and value != pref and min <= value, + // we can assert that min < pref. + Q_ASSERT(min < pref); + return (value - min) / (pref - min) - 1; + } else { + // Since value > pref and value != pref and max >= value, + // we can assert that max > pref. + Q_ASSERT(max > pref); + return (value - pref) / (max - pref); + } +} + +void SequentialAnchorData::updateChildrenSizes() +{ + // ### REMOVE ME + // ### check whether we are guarantee to get those or we need to warn stuff at this + // point. + Q_ASSERT(sizeAtMinimum > minSize || qFuzzyCompare(sizeAtMinimum, minSize)); + Q_ASSERT(sizeAtMinimum < maxSize || qFuzzyCompare(sizeAtMinimum, maxSize)); + Q_ASSERT(sizeAtPreferred > minSize || qFuzzyCompare(sizeAtPreferred, minSize)); + Q_ASSERT(sizeAtPreferred < maxSize || qFuzzyCompare(sizeAtPreferred, maxSize)); + Q_ASSERT(sizeAtMaximum > minSize || qFuzzyCompare(sizeAtMaximum, minSize)); + Q_ASSERT(sizeAtMaximum < maxSize || qFuzzyCompare(sizeAtMaximum, maxSize)); + + // Band here refers if the value is in the Minimum To Preferred + // band (the lower band) or the Preferred To Maximum (the upper band). + + qreal minFactor = getFactor(sizeAtMinimum, minSize, prefSize, maxSize); + qreal prefFactor = getFactor(sizeAtPreferred, minSize, prefSize, maxSize); + qreal maxFactor = getFactor(sizeAtMaximum, minSize, prefSize, maxSize); + + for (int i = 0; i < m_edges.count(); ++i) { + AnchorData *e = m_edges.at(i); + + qreal bandSize = minFactor > 0 ? e->maxSize - e->prefSize : e->prefSize - e->minSize; + e->sizeAtMinimum = e->prefSize + bandSize * minFactor; + + bandSize = prefFactor > 0 ? e->maxSize - e->prefSize : e->prefSize - e->minSize; + e->sizeAtPreferred = e->prefSize + bandSize * prefFactor; + + bandSize = maxFactor > 0 ? e->maxSize - e->prefSize : e->prefSize - e->minSize; + e->sizeAtMaximum = e->prefSize + bandSize * maxFactor; + + e->updateChildrenSizes(); + } +} + +void SequentialAnchorData::refreshSizeHints(qreal effectiveSpacing) +{ + minSize = 0; + prefSize = 0; + maxSize = 0; + + for (int i = 0; i < m_edges.count(); ++i) { + AnchorData *edge = m_edges.at(i); + + // First refresh children information + edge->refreshSizeHints(effectiveSpacing); + + minSize += edge->minSize; + prefSize += edge->prefSize; + maxSize += edge->maxSize; + } + + // See comment in AnchorData::refreshSizeHints() about sizeAt* values + sizeAtMinimum = prefSize; + sizeAtPreferred = prefSize; + sizeAtMaximum = prefSize; +} + +#ifdef QT_DEBUG +void AnchorData::dump(int indent) { + if (type == Parallel) { + qDebug("%*s type: parallel:", indent, ""); + ParallelAnchorData *p = static_cast<ParallelAnchorData *>(this); + p->firstEdge->dump(indent+2); + p->secondEdge->dump(indent+2); + } else if (type == Sequential) { + SequentialAnchorData *s = static_cast<SequentialAnchorData *>(this); + int kids = s->m_edges.count(); + qDebug("%*s type: sequential(%d):", indent, "", kids); + for (int i = 0; i < kids; ++i) { + s->m_edges.at(i)->dump(indent+2); + } + } else { + qDebug("%*s type: Normal:", indent, ""); + } +} + +#endif + +QSimplexConstraint *GraphPath::constraint(const GraphPath &path) const +{ + // Calculate + QSet<AnchorData *> cPositives; + QSet<AnchorData *> cNegatives; + QSet<AnchorData *> intersection; + + cPositives = positives + path.negatives; + cNegatives = negatives + path.positives; + + intersection = cPositives & cNegatives; + + cPositives -= intersection; + cNegatives -= intersection; + + // Fill + QSimplexConstraint *c = new QSimplexConstraint; + QSet<AnchorData *>::iterator i; + for (i = cPositives.begin(); i != cPositives.end(); ++i) + c->variables.insert(*i, 1.0); + + for (i = cNegatives.begin(); i != cNegatives.end(); ++i) + c->variables.insert(*i, -1.0); + + return c; +} + +#ifdef QT_DEBUG +QString GraphPath::toString() const +{ + QString string(QLatin1String("Path: ")); + foreach(AnchorData *edge, positives) + string += QString::fromAscii(" (+++) %1").arg(edge->toString()); + + foreach(AnchorData *edge, negatives) + string += QString::fromAscii(" (---) %1").arg(edge->toString()); + + return string; +} +#endif + +QGraphicsAnchorLayoutPrivate::QGraphicsAnchorLayoutPrivate() + : calculateGraphCacheDirty(1) +{ + for (int i = 0; i < NOrientations; ++i) { + spacings[i] = -1; + graphSimplified[i] = false; + } +} + +Qt::AnchorPoint QGraphicsAnchorLayoutPrivate::oppositeEdge(Qt::AnchorPoint edge) +{ + switch (edge) { + case Qt::AnchorLeft: + edge = Qt::AnchorRight; + break; + case Qt::AnchorRight: + edge = Qt::AnchorLeft; + break; + case Qt::AnchorTop: + edge = Qt::AnchorBottom; + break; + case Qt::AnchorBottom: + edge = Qt::AnchorTop; + break; + default: + break; + } + return edge; +} + + +/*! + * \internal + * + * helper function in order to avoid overflowing anchor sizes + * the returned size will never be larger than FLT_MAX + * + */ +inline static qreal checkAdd(qreal a, qreal b) +{ + if (FLT_MAX - b < a) + return FLT_MAX; + return a + b; +} + +/*! + * \internal + * + * Takes the sequence of vertices described by (\a before, \a vertices, \a after) and replaces + * all anchors connected to the vertices in \a vertices with one simplified anchor between + * \a before and \a after. The simplified anchor will be a placeholder for all the previous + * anchors between \a before and \a after, and can be restored back to the anchors it is a + * placeholder for. + */ +static bool simplifySequentialChunk(Graph<AnchorVertex, AnchorData> *graph, + AnchorVertex *before, + const QVector<AnchorVertex*> &vertices, + AnchorVertex *after) +{ + int i; +#if defined(QT_DEBUG) && 0 + QString strVertices; + for (i = 0; i < vertices.count(); ++i) + strVertices += QString::fromAscii("%1 - ").arg(vertices.at(i)->toString()); + QString strPath = QString::fromAscii("%1 - %2%3").arg(before->toString(), strVertices, after->toString()); + qDebug("simplifying [%s] to [%s - %s]", qPrintable(strPath), qPrintable(before->toString()), qPrintable(after->toString())); +#endif + + qreal min = 0; + qreal pref = 0; + qreal max = 0; + + SequentialAnchorData *sequence = new SequentialAnchorData; + AnchorVertex *prev = before; + AnchorData *data; + for (i = 0; i <= vertices.count(); ++i) { + AnchorVertex *next = (i < vertices.count()) ? vertices.at(i) : after; + data = graph->takeEdge(prev, next); + min += data->minSize; + pref += data->prefSize; + max = checkAdd(max, data->maxSize); + sequence->m_edges.append(data); + prev = next; + } + + // insert new + sequence->minSize = min; + sequence->prefSize = pref; + sequence->maxSize = max; + + // Unless these values are overhidden by the simplex solver later-on, + // anchors will keep their own preferred size. + sequence->sizeAtMinimum = pref; + sequence->sizeAtPreferred = pref; + sequence->sizeAtMaximum = pref; + + sequence->setVertices(vertices); + + sequence->from = before; + sequence->to = after; + + // data here is the last edge in the sequence + // ### this seems to be here for supporting reverse order sequences, + // but doesnt seem to be used right now + if (data->from != vertices.last()) + qSwap(sequence->from, sequence->to); + + // Note that since layout 'edges' can't be simplified away from + // the graph, it's safe to assume that if there's a layout + // 'edge', it'll be in the boundaries of the sequence. + sequence->isLayoutAnchor = (sequence->m_edges.first()->isLayoutAnchor + || sequence->m_edges.last()->isLayoutAnchor); + + AnchorData *newAnchor = sequence; + if (AnchorData *oldAnchor = graph->takeEdge(before, after)) { + newAnchor = new ParallelAnchorData(oldAnchor, sequence); + + newAnchor->isLayoutAnchor = (oldAnchor->isLayoutAnchor + || sequence->isLayoutAnchor); + + min = qMax(oldAnchor->minSize, sequence->minSize); + max = qMin(oldAnchor->maxSize, sequence->maxSize); + + pref = qMax(oldAnchor->prefSize, sequence->prefSize); + pref = qMin(pref, max); + + newAnchor->minSize = min; + newAnchor->prefSize = pref; + newAnchor->maxSize = max; + + // Same as above, by default, keep preferred size. + newAnchor->sizeAtMinimum = pref; + newAnchor->sizeAtPreferred = pref; + newAnchor->sizeAtMaximum = pref; + } + graph->createEdge(before, after, newAnchor); + + // True if we created a parallel anchor + return newAnchor != sequence; +} + +/*! + \internal + + The purpose of this function is to simplify the graph. + Simplification serves two purposes: + 1. Reduce the number of edges in the graph, (thus the number of variables to the equation + solver is reduced, and the solver performs better). + 2. Be able to do distribution of sequences of edges more intelligently (esp. with sequential + anchors) + + It is essential that it must be possible to restore simplified anchors back to their "original" + form. This is done by restoreSimplifiedAnchor(). + + There are two types of simplification that can be done: + 1. Sequential simplification + Sequential simplification means that all sequences of anchors will be merged into one single + anchor. Only anhcors that points in the same direction will be merged. + 2. Parallel simplification + If a simplified sequential anchor is about to be inserted between two vertices in the graph + and there already exist an anchor between those two vertices, a parallel anchor will be + created that serves as a placeholder for the sequential anchor and the anchor that was + already between the two vertices. + + The process of simplification can be described as: + + 1. Simplify all sequences of anchors into one anchor. + If no further simplification was done, go to (3) + - If there already exist an anchor where the sequential anchor is supposed to be inserted, + take that anchor out of the graph + - Then create a parallel anchor that holds the sequential anchor and the anchor just taken + out of the graph. + 2. Go to (1) + 3. Done + + + * Gathering sequential anchors * + The algorithm walks the graph in depth-first order, and only collects vertices that has two + edges connected to it. If the vertex does not have two edges or if it is a layout edge, + it will take all the previously collected vertices and try to create a simplified sequential + anchor representing all the previously collected vertices. + Once the simplified anchor is inserted, the collected list is cleared in order to find the next + sequence to simplify. + Note that there are some catches to this that are not covered by the above explanation. +*/ +void QGraphicsAnchorLayoutPrivate::simplifyGraph(Orientation orientation) +{ + static bool noSimplification = !qgetenv("QT_ANCHORLAYOUT_NO_SIMPLIFICATION").isEmpty(); + if (noSimplification) + return; + + if (graphSimplified[orientation]) + return; + graphSimplified[orientation] = true; + +#if 0 + qDebug("Simplifying Graph for %s", + orientation == Horizontal ? "Horizontal" : "Vertical"); +#endif + + AnchorVertex *rootVertex = graph[orientation].rootVertex(); + + if (!rootVertex) + return; + + bool dirty; + do { + dirty = simplifyGraphIteration(orientation); + } while (dirty); +} + +bool QGraphicsAnchorLayoutPrivate::simplifyGraphIteration(QGraphicsAnchorLayoutPrivate::Orientation orientation) +{ + Q_Q(QGraphicsAnchorLayout); + Graph<AnchorVertex, AnchorData> &g = graph[orientation]; + AnchorVertex *v = g.rootVertex(); + + QSet<AnchorVertex *> visited; + QStack<AnchorVertex *> stack; + stack.push(v); + QVector<AnchorVertex*> candidates; + + const Qt::AnchorPoint centerEdge = pickEdge(Qt::AnchorHorizontalCenter, orientation); + const Qt::AnchorPoint layoutEdge = oppositeEdge(v->m_edge); + + bool dirty = false; + + // walk depth-first. + while (!stack.isEmpty()) { + v = stack.pop(); + QList<AnchorVertex *> vertices = g.adjacentVertices(v); + const int count = vertices.count(); + bool endOfSequence = (v->m_item == q && v->m_edge == layoutEdge) || count != 2; + if (count == 2 && v->m_item != q) { + candidates.append(v); + if (visited.contains(vertices.first()) && visited.contains(vertices.last())) { + // in case of a cycle + endOfSequence = true; + } + } + if (endOfSequence && candidates.count() >= 2) { + int i; + AnchorVertex *afterSequence= 0; + QList<AnchorVertex *> adjacentOfSecondLastVertex = g.adjacentVertices(candidates.last()); + Q_ASSERT(adjacentOfSecondLastVertex.count() == 2); + if (adjacentOfSecondLastVertex.first() == candidates.at(candidates.count() - 2)) + afterSequence = adjacentOfSecondLastVertex.last(); + else + afterSequence = adjacentOfSecondLastVertex.first(); + + AnchorVertex *beforeSequence = 0; + QList<AnchorVertex *> adjacentOfSecondVertex = g.adjacentVertices(candidates.first()); + Q_ASSERT(adjacentOfSecondVertex.count() == 2); + if (adjacentOfSecondVertex.first() == candidates.at(1)) + beforeSequence = adjacentOfSecondVertex.last(); + else + beforeSequence = adjacentOfSecondVertex.first(); + // The complete path of the sequence to simplify is: beforeSequence, <candidates>, afterSequence + // where beforeSequence and afterSequence are the endpoints where the anchor is inserted + // between. +#if defined(QT_DEBUG) && 0 + // ### DEBUG + QString strCandidates; + for (i = 0; i < candidates.count(); ++i) + strCandidates += QString::fromAscii("%1 - ").arg(candidates.at(i)->toString()); + QString strPath = QString::fromAscii("%1 - %2%3").arg(beforeSequence->toString(), strCandidates, afterSequence->toString()); + qDebug("candidate list for sequential simplification:\n[%s]", qPrintable(strPath)); +#endif + + bool forward; + AnchorVertex *prev = beforeSequence; + int intervalFrom = 0; + + // Check for directionality (from). We don't want to destroy that information, + // thus we only combine anchors with the same direction. + + // "i" is the index *including* the beforeSequence and afterSequence vertices. + for (i = 1; i <= candidates.count() + 1; ++i) { + bool atVertexAfter = i > candidates.count(); + AnchorVertex *v1 = atVertexAfter ? afterSequence : candidates.at(i - 1); + AnchorData *data = g.edgeData(prev, v1); + Q_ASSERT(data); + if (i == 1) { + forward = (prev == data->from ? true : false); + } else if (forward != (prev == data->from) || atVertexAfter) { + int intervalTo = i; + if (forward != (prev == data->from)) + --intervalTo; + + // intervalFrom and intervalTo should now be indices to the vertex before and + // after the sequential anchor. + if (intervalTo - intervalFrom >= 2) { + // simplify in the range [intervalFrom, intervalTo] + + // Trim off internal center anchors (Left-Center/Center-Right) from the + // start and the end of the sequence. We never want to simplify internal + // center anchors where there is an external anchor connected to the center. + AnchorVertex *intervalVertexFrom = intervalFrom == 0 ? beforeSequence : candidates.at(intervalFrom - 1); + if (intervalVertexFrom->m_edge == centerEdge + && intervalVertexFrom->m_item == candidates.at(intervalFrom)->m_item) { + ++intervalFrom; + intervalVertexFrom = candidates.at(intervalFrom - 1); + } + AnchorVertex *intervalVertexTo = intervalTo <= candidates.count() ? candidates.at(intervalTo - 1) : afterSequence; + if (intervalVertexTo->m_edge == centerEdge + && intervalVertexTo->m_item == candidates.at(intervalTo - 2)->m_item) { + --intervalTo; + intervalVertexTo = candidates.at(intervalTo - 1); + } + + QVector<AnchorVertex*> subCandidates; + if (forward) { + subCandidates = candidates.mid(intervalFrom, intervalTo - intervalFrom - 1); + } else { + // reverse the order of the candidates. + qSwap(intervalVertexFrom, intervalVertexTo); + do { + ++intervalFrom; + subCandidates.prepend(candidates.at(intervalFrom - 1)); + } while (intervalFrom < intervalTo - 1); + } + if (simplifySequentialChunk(&g, intervalVertexFrom, subCandidates, intervalVertexTo)) { + dirty = true; + break; + } + // finished simplification of chunk with same direction + } + if (forward == (prev == data->from)) + --intervalTo; + intervalFrom = intervalTo; + + forward = !forward; + } + prev = v1; + } + + if (dirty) + break; + } + + if (endOfSequence) + candidates.clear(); + + for (int i = 0; i < count; ++i) { + AnchorVertex *next = vertices.at(i); + if (next->m_item == q && next->m_edge == centerEdge) + continue; + if (visited.contains(next)) + continue; + stack.push(next); + } + + visited.insert(v); + } + + return dirty; +} + +static void restoreSimplifiedAnchor(Graph<AnchorVertex, AnchorData> &g, + AnchorData *edge, + AnchorVertex *before, + AnchorVertex *after) +{ + Q_ASSERT(edge->type != AnchorData::Normal); +#if 0 + static const char *anchortypes[] = {"Normal", + "Sequential", + "Parallel"}; + qDebug("Restoring %s edge.", anchortypes[int(edge->type)]); +#endif + if (edge->type == AnchorData::Sequential) { + SequentialAnchorData* seqEdge = static_cast<SequentialAnchorData*>(edge); + // restore the sequential anchor + AnchorVertex *prev = before; + AnchorVertex *last = after; + if (edge->from != prev) + qSwap(last, prev); + + for (int i = 0; i < seqEdge->m_edges.count(); ++i) { + AnchorVertex *v1 = (i < seqEdge->m_children.count()) ? seqEdge->m_children.at(i) : last; + AnchorData *data = seqEdge->m_edges.at(i); + if (data->type != AnchorData::Normal) { + restoreSimplifiedAnchor(g, data, prev, v1); + } else { + g.createEdge(prev, v1, data); + } + prev = v1; + } + } else if (edge->type == AnchorData::Parallel) { + ParallelAnchorData* parallelEdge = static_cast<ParallelAnchorData*>(edge); + AnchorData *parallelEdges[2] = {parallelEdge->firstEdge, + parallelEdge->secondEdge}; + for (int i = 0; i < 2; ++i) { + AnchorData *data = parallelEdges[i]; + if (data->type == AnchorData::Normal) { + g.createEdge(before, after, data); + } else { + restoreSimplifiedAnchor(g, data, before, after); + } + } + } +} + +void QGraphicsAnchorLayoutPrivate::restoreSimplifiedGraph(Orientation orientation) +{ + if (!graphSimplified[orientation]) + return; + graphSimplified[orientation] = false; + +#if 0 + qDebug("Restoring Simplified Graph for %s", + orientation == Horizontal ? "Horizontal" : "Vertical"); +#endif + + Graph<AnchorVertex, AnchorData> &g = graph[orientation]; + + QList<QPair<AnchorVertex*, AnchorVertex*> > connections = g.connections(); + for (int i = 0; i < connections.count(); ++i) { + AnchorVertex *v1 = connections.at(i).first; + AnchorVertex *v2 = connections.at(i).second; + AnchorData *edge = g.edgeData(v1, v2); + if (edge->type != AnchorData::Normal) { + AnchorData *oldEdge = g.takeEdge(v1, v2); + restoreSimplifiedAnchor(g, edge, v1, v2); + delete oldEdge; + } + } +} + +QGraphicsAnchorLayoutPrivate::Orientation +QGraphicsAnchorLayoutPrivate::edgeOrientation(Qt::AnchorPoint edge) +{ + return edge > Qt::AnchorRight ? Vertical : Horizontal; +} + +/*! + \internal + + Create internal anchors to connect the layout edges (Left to Right and + Top to Bottom). + + These anchors doesn't have size restrictions, that will be enforced by + other anchors and items in the layout. +*/ +void QGraphicsAnchorLayoutPrivate::createLayoutEdges() +{ + Q_Q(QGraphicsAnchorLayout); + QGraphicsLayoutItem *layout = q; + + // Horizontal + AnchorData *data = new AnchorData(0, 0, QWIDGETSIZE_MAX); + addAnchor(layout, Qt::AnchorLeft, layout, + Qt::AnchorRight, data); + data->skipInPreferred = 1; + + // Set the Layout Left edge as the root of the horizontal graph. + AnchorVertex *v = internalVertex(layout, Qt::AnchorLeft); + graph[Horizontal].setRootVertex(v); + + // Vertical + data = new AnchorData(0, 0, QWIDGETSIZE_MAX); + addAnchor(layout, Qt::AnchorTop, layout, + Qt::AnchorBottom, data); + data->skipInPreferred = 1; + + // Set the Layout Top edge as the root of the vertical graph. + v = internalVertex(layout, Qt::AnchorTop); + graph[Vertical].setRootVertex(v); +} + +void QGraphicsAnchorLayoutPrivate::deleteLayoutEdges() +{ + Q_Q(QGraphicsAnchorLayout); + + Q_ASSERT(internalVertex(q, Qt::AnchorHorizontalCenter) == NULL); + Q_ASSERT(internalVertex(q, Qt::AnchorVerticalCenter) == NULL); + + removeAnchor(q, Qt::AnchorLeft, q, Qt::AnchorRight); + removeAnchor(q, Qt::AnchorTop, q, Qt::AnchorBottom); +} + +void QGraphicsAnchorLayoutPrivate::createItemEdges(QGraphicsLayoutItem *item) +{ + Q_ASSERT(!graphSimplified[Horizontal] && !graphSimplified[Vertical]); + + items.append(item); + + // Horizontal + int minimumSize = item->minimumWidth(); + int preferredSize = item->preferredWidth(); + int maximumSize = item->maximumWidth(); + + AnchorData *data = new AnchorData(minimumSize, preferredSize, maximumSize); + addAnchor(item, Qt::AnchorLeft, item, + Qt::AnchorRight, data); + + // Vertical + minimumSize = item->minimumHeight(); + preferredSize = item->preferredHeight(); + maximumSize = item->maximumHeight(); + + data = new AnchorData(minimumSize, preferredSize, maximumSize); + addAnchor(item, Qt::AnchorTop, item, + Qt::AnchorBottom, data); +} + +/*! + \internal + + By default, each item in the layout is represented internally as + a single anchor in each direction. For instance, from Left to Right. + + However, to support anchorage of items to the center of items, we + must split this internal anchor into two half-anchors. From Left + to Center and then from Center to Right, with the restriction that + these anchors must have the same time at all times. +*/ +void QGraphicsAnchorLayoutPrivate::createCenterAnchors( + QGraphicsLayoutItem *item, Qt::AnchorPoint centerEdge) +{ + Orientation orientation; + switch (centerEdge) { + case Qt::AnchorHorizontalCenter: + orientation = Horizontal; + break; + case Qt::AnchorVerticalCenter: + orientation = Vertical; + break; + default: + // Don't create center edges unless needed + return; + } + + Q_ASSERT(!graphSimplified[orientation]); + + // Check if vertex already exists + if (internalVertex(item, centerEdge)) + return; + + // Orientation code + Qt::AnchorPoint firstEdge; + Qt::AnchorPoint lastEdge; + + if (orientation == Horizontal) { + firstEdge = Qt::AnchorLeft; + lastEdge = Qt::AnchorRight; + } else { + firstEdge = Qt::AnchorTop; + lastEdge = Qt::AnchorBottom; + } + + AnchorVertex *first = internalVertex(item, firstEdge); + AnchorVertex *last = internalVertex(item, lastEdge); + Q_ASSERT(first && last); + + // Create new anchors + AnchorData *oldData = graph[orientation].edgeData(first, last); + + int minimumSize = oldData->minSize / 2; + int preferredSize = oldData->prefSize / 2; + int maximumSize = oldData->maxSize / 2; + + QSimplexConstraint *c = new QSimplexConstraint; + AnchorData *data = new AnchorData(minimumSize, preferredSize, maximumSize); + c->variables.insert(data, 1.0); + addAnchor(item, firstEdge, item, centerEdge, data); + + data = new AnchorData(minimumSize, preferredSize, maximumSize); + c->variables.insert(data, -1.0); + addAnchor(item, centerEdge, item, lastEdge, data); + + itemCenterConstraints[orientation].append(c); + + // Remove old one + removeAnchor(item, firstEdge, item, lastEdge); +} + +void QGraphicsAnchorLayoutPrivate::removeCenterAnchors( + QGraphicsLayoutItem *item, Qt::AnchorPoint centerEdge, + bool substitute) +{ + Orientation orientation; + switch (centerEdge) { + case Qt::AnchorHorizontalCenter: + orientation = Horizontal; + break; + case Qt::AnchorVerticalCenter: + orientation = Vertical; + break; + default: + // Don't remove edges that not the center ones + return; + } + + Q_ASSERT(!graphSimplified[orientation]); + + // Orientation code + Qt::AnchorPoint firstEdge; + Qt::AnchorPoint lastEdge; + + if (orientation == Horizontal) { + firstEdge = Qt::AnchorLeft; + lastEdge = Qt::AnchorRight; + } else { + firstEdge = Qt::AnchorTop; + lastEdge = Qt::AnchorBottom; + } + + AnchorVertex *center = internalVertex(item, centerEdge); + if (!center) + return; + AnchorVertex *first = internalVertex(item, firstEdge); + + Q_ASSERT(first); + Q_ASSERT(center); + + Graph<AnchorVertex, AnchorData> &g = graph[orientation]; + + + AnchorData *oldData = g.edgeData(first, center); + // Remove center constraint + for (int i = itemCenterConstraints[orientation].count() - 1; i >= 0; --i) { + if (itemCenterConstraints[orientation][i]->variables.contains(oldData)) { + delete itemCenterConstraints[orientation].takeAt(i); + break; + } + } + + if (substitute) { + // Create the new anchor that should substitute the left-center-right anchors. + AnchorData *oldData = g.edgeData(first, center); + + int minimumSize = oldData->minSize * 2; + int preferredSize = oldData->prefSize * 2; + int maximumSize = oldData->maxSize * 2; + + AnchorData *data = new AnchorData(minimumSize, preferredSize, maximumSize); + addAnchor(item, firstEdge, item, lastEdge, data); + + // Remove old anchors + removeAnchor(item, firstEdge, item, centerEdge); + removeAnchor(item, centerEdge, item, lastEdge); + + } else { + // this is only called from removeAnchors() + // first, remove all non-internal anchors + QList<AnchorVertex*> adjacents = g.adjacentVertices(center); + for (int i = 0; i < adjacents.count(); ++i) { + AnchorVertex *v = adjacents.at(i); + if (v->m_item != item) { + removeAnchor(item, centerEdge, v->m_item, v->m_edge); + } + } + // when all non-internal anchors is removed it will automatically merge the + // center anchor into a left-right (or top-bottom) anchor. We must also delete that. + // by this time, the center vertex is deleted and merged into a non-centered internal anchor + removeAnchor(item, firstEdge, item, lastEdge); + } +} + + +void QGraphicsAnchorLayoutPrivate::removeCenterConstraints(QGraphicsLayoutItem *item, + Orientation orientation) +{ + Q_ASSERT(!graphSimplified[orientation]); + + // Remove the item center constraints associated to this item + // ### This is a temporary solution. We should probably use a better + // data structure to hold items and/or their associated constraints + // so that we can remove those easily + + AnchorVertex *first = internalVertex(item, orientation == Horizontal ? + Qt::AnchorLeft : + Qt::AnchorTop); + AnchorVertex *center = internalVertex(item, orientation == Horizontal ? + Qt::AnchorHorizontalCenter : + Qt::AnchorVerticalCenter); + + // Skip if no center constraints exist + if (!center) + return; + + Q_ASSERT(first); + AnchorData *internalAnchor = graph[orientation].edgeData(first, center); + + // Look for our anchor in all item center constraints, then remove it + for (int i = 0; i < itemCenterConstraints[orientation].size(); ++i) { + if (itemCenterConstraints[orientation][i]->variables.contains(internalAnchor)) { + delete itemCenterConstraints[orientation].takeAt(i); + break; + } + } +} + +/*! + * \internal + * + * Helper function that is called from the anchor functions in the public API. + * If \a spacing is 0, it will pick up the spacing defined by the style. + */ +void QGraphicsAnchorLayoutPrivate::anchor(QGraphicsLayoutItem *firstItem, + Qt::AnchorPoint firstEdge, + QGraphicsLayoutItem *secondItem, + Qt::AnchorPoint secondEdge, + qreal *spacing) +{ + Q_Q(QGraphicsAnchorLayout); + if ((firstItem == 0) || (secondItem == 0)) { + qWarning("QGraphicsAnchorLayout::addAnchor(): " + "Cannot anchor NULL items"); + return; + } + + if (firstItem == secondItem) { + qWarning("QGraphicsAnchorLayout::addAnchor(): " + "Cannot anchor the item to itself"); + return; + } + + if (edgeOrientation(secondEdge) != edgeOrientation(firstEdge)) { + qWarning("QGraphicsAnchorLayout::addAnchor(): " + "Cannot anchor edges of different orientations"); + return; + } + + // Guarantee that the graph is no simplified when adding this anchor, + // anchor manipulation always happen in the full graph + restoreSimplifiedGraph(edgeOrientation(firstEdge)); + + // In QGraphicsAnchorLayout, items are represented in its internal + // graph as four anchors that connect: + // - Left -> HCenter + // - HCenter-> Right + // - Top -> VCenter + // - VCenter -> Bottom + + // Ensure that the internal anchors have been created for both items. + if (firstItem != q && !items.contains(firstItem)) { + restoreSimplifiedGraph(edgeOrientation(firstEdge) == Horizontal ? Vertical : Horizontal); + createItemEdges(firstItem); + addChildLayoutItem(firstItem); + } + if (secondItem != q && !items.contains(secondItem)) { + restoreSimplifiedGraph(edgeOrientation(firstEdge) == Horizontal ? Vertical : Horizontal); + createItemEdges(secondItem); + addChildLayoutItem(secondItem); + } + + // Create center edges if needed + createCenterAnchors(firstItem, firstEdge); + createCenterAnchors(secondItem, secondEdge); + + // Use heuristics to find out what the user meant with this anchor. + correctEdgeDirection(firstItem, firstEdge, secondItem, secondEdge); + + AnchorData *data; + if (!spacing) { + // If firstItem or secondItem is the layout itself, the spacing will default to 0. + // Otherwise, the following matrix is used (questionmark means that the spacing + // is queried from the style): + // from + // to Left HCenter Right + // Left 0 0 ? + // HCenter 0 0 0 + // Right ? 0 0 + if (firstItem != q + && secondItem != q + && pickEdge(firstEdge, Horizontal) != Qt::AnchorHorizontalCenter + && oppositeEdge(firstEdge) == secondEdge) { + data = new AnchorData; // ask the style later + } else { + data = new AnchorData(0); // spacing should be 0 + } + addAnchor(firstItem, firstEdge, secondItem, secondEdge, data); + } else if (*spacing >= 0) { + data = new AnchorData(*spacing); + addAnchor(firstItem, firstEdge, secondItem, secondEdge, data); + } else { + data = new AnchorData(-*spacing); + addAnchor(secondItem, secondEdge, firstItem, firstEdge, data); + } +} + +void QGraphicsAnchorLayoutPrivate::addAnchor(QGraphicsLayoutItem *firstItem, + Qt::AnchorPoint firstEdge, + QGraphicsLayoutItem *secondItem, + Qt::AnchorPoint secondEdge, + AnchorData *data) +{ + Q_Q(QGraphicsAnchorLayout); + + // Guarantee that the graph is no simplified when adding this anchor, + // anchor manipulation always happen in the full graph + restoreSimplifiedGraph(edgeOrientation(firstEdge)); + + // Is the Vertex (firstItem, firstEdge) already represented in our + // internal structure? + AnchorVertex *v1 = addInternalVertex(firstItem, firstEdge); + AnchorVertex *v2 = addInternalVertex(secondItem, secondEdge); + + // Remove previous anchor + // ### Could we update the existing edgeData rather than creating a new one? + if (graph[edgeOrientation(firstEdge)].edgeData(v1, v2)) + removeAnchor(firstItem, firstEdge, secondItem, secondEdge); + + // Create a bi-directional edge in the sense it can be transversed both + // from v1 or v2. "data" however is shared between the two references + // so we still know that the anchor direction is from 1 to 2. + data->from = v1; + data->to = v2; +#ifdef QT_DEBUG + data->name = QString::fromAscii("%1 --to--> %2").arg(v1->toString()).arg(v2->toString()); +#endif + // Keep track of anchors that are connected to the layout 'edges' + data->isLayoutAnchor = (v1->m_item == q || v2->m_item == q); + + graph[edgeOrientation(firstEdge)].createEdge(v1, v2, data); +} + +void QGraphicsAnchorLayoutPrivate::removeAnchor(QGraphicsLayoutItem *firstItem, + Qt::AnchorPoint firstEdge, + QGraphicsLayoutItem *secondItem, + Qt::AnchorPoint secondEdge) +{ + // Guarantee that the graph is no simplified when adding this anchor, + // anchor manipulation always happen in the full graph + restoreSimplifiedGraph(edgeOrientation(firstEdge)); + + // Look for both vertices + AnchorVertex *v1 = internalVertex(firstItem, firstEdge); + AnchorVertex *v2 = internalVertex(secondItem, secondEdge); + + Q_ASSERT(v1 && v2); + + // Remove edge from graph + graph[edgeOrientation(firstEdge)].removeEdge(v1, v2); + + // Decrease vertices reference count (may trigger a deletion) + removeInternalVertex(firstItem, firstEdge); + removeInternalVertex(secondItem, secondEdge); +} + +bool QGraphicsAnchorLayoutPrivate::setAnchorSize(const QGraphicsLayoutItem *firstItem, + Qt::AnchorPoint firstEdge, + const QGraphicsLayoutItem *secondItem, + Qt::AnchorPoint secondEdge, + const qreal *anchorSize) +{ + // ### we can avoid restoration if we really want to, but we would have to + // search recursively through all composite anchors + restoreSimplifiedGraph(edgeOrientation(firstEdge)); + AnchorVertex *v1 = internalVertex(firstItem, firstEdge); + AnchorVertex *v2 = internalVertex(secondItem, secondEdge); + + AnchorData *data = graph[edgeOrientation(firstEdge)].edgeData(v1, v2); + if (data) { + if (anchorSize) { + data->setFixedSize(*anchorSize); + } else { + data->unsetSize(); + } + } + + return data; +} + +bool QGraphicsAnchorLayoutPrivate::anchorSize(const QGraphicsLayoutItem *firstItem, + Qt::AnchorPoint firstEdge, + const QGraphicsLayoutItem *secondItem, + Qt::AnchorPoint secondEdge, + qreal *minSize, + qreal *prefSize, + qreal *maxSize) const +{ + Q_ASSERT(minSize || prefSize || maxSize); + QGraphicsAnchorLayoutPrivate *that = const_cast<QGraphicsAnchorLayoutPrivate *>(this); + that->restoreSimplifiedGraph(edgeOrientation(firstEdge)); + AnchorVertex *v1 = internalVertex(firstItem, firstEdge); + AnchorVertex *v2 = internalVertex(secondItem, secondEdge); + + AnchorData *data = that->graph[edgeOrientation(firstEdge)].edgeData(v1, v2); + if (data) { + if (minSize) + *minSize = data->minSize; + if (prefSize) + *prefSize = data->prefSize; + if (maxSize) + *maxSize = data->maxSize; + } + return data; +} + +AnchorVertex *QGraphicsAnchorLayoutPrivate::addInternalVertex(QGraphicsLayoutItem *item, + Qt::AnchorPoint edge) +{ + QPair<QGraphicsLayoutItem *, Qt::AnchorPoint> pair(item, edge); + QPair<AnchorVertex *, int> v = m_vertexList.value(pair); + + if (!v.first) { + Q_ASSERT(v.second == 0); + v.first = new AnchorVertex(item, edge); + } + v.second++; + m_vertexList.insert(pair, v); + return v.first; +} + +/** + * \internal + * + * returns the AnchorVertex that was dereferenced, also when it was removed. + * returns 0 if it did not exist. + */ +void QGraphicsAnchorLayoutPrivate::removeInternalVertex(QGraphicsLayoutItem *item, + Qt::AnchorPoint edge) +{ + QPair<QGraphicsLayoutItem *, Qt::AnchorPoint> pair(item, edge); + QPair<AnchorVertex *, int> v = m_vertexList.value(pair); + + if (!v.first) { + qWarning("This item with this edge is not in the graph"); + return; + } + + v.second--; + if (v.second == 0) { + // Remove reference and delete vertex + m_vertexList.remove(pair); + delete v.first; + } else { + // Update reference count + m_vertexList.insert(pair, v); + + if ((v.second == 2) && + ((edge == Qt::AnchorHorizontalCenter) || + (edge == Qt::AnchorVerticalCenter))) { + removeCenterAnchors(item, edge, true); + } + } +} + +void QGraphicsAnchorLayoutPrivate::removeVertex(QGraphicsLayoutItem *item, Qt::AnchorPoint edge) +{ + if (AnchorVertex *v = internalVertex(item, edge)) { + Graph<AnchorVertex, AnchorData> &g = graph[edgeOrientation(edge)]; + const QList<AnchorVertex *> allVertices = graph[edgeOrientation(edge)].adjacentVertices(v); + AnchorVertex *v2; + foreach (v2, allVertices) { + g.removeEdge(v, v2); + removeInternalVertex(item, edge); + removeInternalVertex(v2->m_item, v2->m_edge); + } + } +} + +void QGraphicsAnchorLayoutPrivate::removeAnchors(QGraphicsLayoutItem *item) +{ + Q_ASSERT(!graphSimplified[Horizontal] && !graphSimplified[Vertical]); + + // remove the center anchor first!! + removeCenterAnchors(item, Qt::AnchorHorizontalCenter, false); + removeVertex(item, Qt::AnchorLeft); + removeVertex(item, Qt::AnchorRight); + + removeCenterAnchors(item, Qt::AnchorVerticalCenter, false); + removeVertex(item, Qt::AnchorTop); + removeVertex(item, Qt::AnchorBottom); +} + +/*! + \internal + + Use heuristics to determine the correct orientation of a given anchor. + + After API discussions, we decided we would like expressions like + anchor(A, Left, B, Right) to mean the same as anchor(B, Right, A, Left). + The problem with this is that anchors could become ambiguous, for + instance, what does the anchor A, B of size X mean? + + "pos(B) = pos(A) + X" or "pos(A) = pos(B) + X" ? + + To keep the API user friendly and at the same time, keep our algorithm + deterministic, we use an heuristic to determine a direction for each + added anchor and then keep it. The heuristic is based on the fact + that people usually avoid overlapping items, therefore: + + "A, RIGHT to B, LEFT" means that B is to the LEFT of A. + "B, LEFT to A, RIGHT" is corrected to the above anchor. + + Special correction is also applied when one of the items is the + layout. We handle Layout Left as if it was another items's Right + and Layout Right as another item's Left. +*/ +void QGraphicsAnchorLayoutPrivate::correctEdgeDirection(QGraphicsLayoutItem *&firstItem, + Qt::AnchorPoint &firstEdge, + QGraphicsLayoutItem *&secondItem, + Qt::AnchorPoint &secondEdge) +{ + Q_Q(QGraphicsAnchorLayout); + + Qt::AnchorPoint effectiveFirst = firstEdge; + Qt::AnchorPoint effectiveSecond = secondEdge; + + if (firstItem == q) + effectiveFirst = QGraphicsAnchorLayoutPrivate::oppositeEdge(firstEdge); + if (secondItem == q) + effectiveSecond = QGraphicsAnchorLayoutPrivate::oppositeEdge(secondEdge); + + if (effectiveFirst < effectiveSecond) { + + // ### DEBUG + /* printf("Swapping Anchor from %s %d --to--> %s %d\n", + firstItem->isLayout() ? "<layout>" : + qPrintable(static_cast<QGraphicsWidget *>(firstItem)->data(0).toString()), + firstEdge, + secondItem->isLayout() ? "<layout>" : + qPrintable(static_cast<QGraphicsWidget *>(secondItem)->data(0).toString()), + secondEdge); + */ + qSwap(firstItem, secondItem); + qSwap(firstEdge, secondEdge); + } +} + +qreal QGraphicsAnchorLayoutPrivate::effectiveSpacing(Orientation orientation) const +{ + Q_Q(const QGraphicsAnchorLayout); + qreal s = spacings[orientation]; + if (s < 0) { + // ### make sure behaviour is the same as in QGraphicsGridLayout + QGraphicsLayoutItem *parent = q->parentLayoutItem(); + while (parent && parent->isLayout()) { + parent = parent->parentLayoutItem(); + } + if (parent) { + QGraphicsItem *parentItem = parent->graphicsItem(); + if (parentItem && parentItem->isWidget()) { + QGraphicsWidget *w = static_cast<QGraphicsWidget*>(parentItem); + s = w->style()->pixelMetric(orientation == Horizontal + ? QStyle::PM_LayoutHorizontalSpacing + : QStyle::PM_LayoutVerticalSpacing); + } + } + } + return s; +} + +/*! + \internal + + Called on activation. Uses Linear Programming to define minimum, preferred + and maximum sizes for the layout. Also calculates the sizes that each item + should assume when the layout is in one of such situations. +*/ +void QGraphicsAnchorLayoutPrivate::calculateGraphs() +{ + if (!calculateGraphCacheDirty) + return; + + calculateGraphs(Horizontal); + calculateGraphs(Vertical); + + calculateGraphCacheDirty = 0; +} + +// ### remove me: +QList<AnchorData *> getVariables(QList<QSimplexConstraint *> constraints) +{ + QSet<AnchorData *> variableSet; + for (int i = 0; i < constraints.count(); ++i) { + const QSimplexConstraint *c = constraints[i]; + foreach (QSimplexVariable *var, c->variables.keys()) { + variableSet += static_cast<AnchorData *>(var); + } + } + return variableSet.toList(); +} + +/*! + \internal + + Calculate graphs is the method that puts together all the helper routines + so that the AnchorLayout can calculate the sizes of each item. + + In a nutshell it should do: + + 1) Update anchor nominal sizes, that is, the size that each anchor would + have if no other restrictions applied. This is done by quering the + layout style and the sizeHints of the items belonging to the layout. + + 2) Simplify the graph by grouping together parallel and sequential anchors + into "group anchors". These have equivalent minimum, preferred and maximum + sizeHints as the anchors they replace. + + 3) Check if we got to a trivial case. In some cases, the whole graph can be + simplified into a single anchor. If so, use this information. If not, + then call the Simplex solver to calculate the anchors sizes. + + 4) Once the root anchors had its sizes calculated, propagate that to the + anchors they represent. +*/ +void QGraphicsAnchorLayoutPrivate::calculateGraphs( + QGraphicsAnchorLayoutPrivate::Orientation orientation) +{ + Q_Q(QGraphicsAnchorLayout); + + // Simplify the graph + simplifyGraph(orientation); + + // Reset the nominal sizes of each anchor based on the current item sizes + setAnchorSizeHintsFromItems(orientation); + + // Traverse all graph edges and store the possible paths to each vertex + findPaths(orientation); + + // From the paths calculated above, extract the constraints that the current + // anchor setup impose, to our Linear Programming problem. + constraintsFromPaths(orientation); + + // Split the constraints and anchors into groups that should be fed to the + // simplex solver independently. Currently we find two groups: + // + // 1) The "trunk", that is, the set of anchors (items) that are connected + // to the two opposite sides of our layout, and thus need to stretch in + // order to fit in the current layout size. + // + // 2) The floating or semi-floating anchors (items) that are those which + // are connected to only one (or none) of the layout sides, thus are not + // influenced by the layout size. + QList<QList<QSimplexConstraint *> > parts; + parts = getGraphParts(orientation); + + // Now run the simplex solver to calculate Minimum, Preferred and Maximum sizes + // of the "trunk" set of constraints and variables. + // ### does trunk always exist? empty = trunk is the layout left->center->right + QList<QSimplexConstraint *> trunkConstraints = parts[0]; + QList<QSimplexConstraint *> sizeHintConstraints; + sizeHintConstraints = constraintsFromSizeHints(getVariables(trunkConstraints)); + trunkConstraints += sizeHintConstraints; + + // For minimum and maximum, use the path between the two layout sides as the + // objective function. + + // Retrieve that path + AnchorVertex *v = internalVertex(q, pickEdge(Qt::AnchorRight, orientation)); + GraphPath trunkPath = graphPaths[orientation].value(v); + + if (!trunkConstraints.isEmpty()) { +#if 0 + qDebug("Simplex used for trunk of %s", + orientation == Horizontal ? "Horizontal" : "Vertical"); +#endif + + // Solve min and max size hints for trunk + QPair<qreal, qreal> minMax = solveMinMax(trunkConstraints, trunkPath); + sizeHints[orientation][Qt::MinimumSize] = minMax.first; + sizeHints[orientation][Qt::MaximumSize] = minMax.second; + + // Solve for preferred. The objective function is calculated from the constraints + // and variables internally. + solvePreferred(trunkConstraints); + + // Propagate the new sizes down the simplified graph, ie. tell the + // group anchors to set their children anchors sizes. + + // ### we calculated variables already a few times, can't we reuse that? + QList<AnchorData *> trunkVariables = getVariables(trunkConstraints); + + for (int i = 0; i < trunkVariables.count(); ++i) + trunkVariables.at(i)->updateChildrenSizes(); + + // Calculate and set the preferred size for the layout from the edge sizes that + // were calculated above. + qreal pref(0.0); + foreach (const AnchorData *ad, trunkPath.positives) { + pref += ad->sizeAtPreferred; + } + foreach (const AnchorData *ad, trunkPath.negatives) { + pref -= ad->sizeAtPreferred; + } + sizeHints[orientation][Qt::PreferredSize] = pref; + } else { +#if 0 + qDebug("Simplex NOT used for trunk of %s", + orientation == Horizontal ? "Horizontal" : "Vertical"); +#endif + + // No Simplex is necessary because the path was simplified all the way to a single + // anchor. + Q_ASSERT(trunkPath.positives.count() == 1); + Q_ASSERT(trunkPath.negatives.count() == 0); + + AnchorData *ad = trunkPath.positives.toList()[0]; + ad->sizeAtMinimum = ad->minSize; + ad->sizeAtPreferred = ad->prefSize; + ad->sizeAtMaximum = ad->maxSize; + + // Propagate + ad->updateChildrenSizes(); + + sizeHints[orientation][Qt::MinimumSize] = ad->sizeAtMinimum; + sizeHints[orientation][Qt::PreferredSize] = ad->sizeAtPreferred; + sizeHints[orientation][Qt::MaximumSize] = ad->sizeAtMaximum; + } + + // Delete the constraints, we won't use them anymore. + qDeleteAll(sizeHintConstraints); + sizeHintConstraints.clear(); + + // For the other parts that not the trunk, solve only for the preferred size + // that is the size they will remain at, since they are not stretched by the + // layout. + + // Solve the other only for preferred, skip trunk + for (int i = 1; i < parts.count(); ++i) { + QList<QSimplexConstraint *> partConstraints = parts[i]; + QList<AnchorData *> partVariables = getVariables(partConstraints); + Q_ASSERT(!partVariables.isEmpty()); + + sizeHintConstraints = constraintsFromSizeHints(partVariables); + partConstraints += sizeHintConstraints; + solvePreferred(partConstraints); + + // Propagate size at preferred to other sizes. Semi-floats + // always will be in their sizeAtPreferred. + for (int j = 0; j < partVariables.count(); ++j) { + AnchorData *ad = partVariables[j]; + Q_ASSERT(ad); + ad->sizeAtMinimum = ad->sizeAtPreferred; + ad->sizeAtMaximum = ad->sizeAtPreferred; + ad->updateChildrenSizes(); + } + + // Delete the constraints, we won't use them anymore. + qDeleteAll(sizeHintConstraints); + sizeHintConstraints.clear(); + } + + // Clean up our data structures. They are not needed anymore since + // distribution uses just interpolation. + qDeleteAll(constraints[orientation]); + constraints[orientation].clear(); + graphPaths[orientation].clear(); // ### +} + +/*! + \internal + + For graph edges ("anchors") that represent items, this method updates their + intrinsic size restrictions, based on the item size hints. +*/ +void QGraphicsAnchorLayoutPrivate::setAnchorSizeHintsFromItems(Orientation orientation) +{ + Graph<AnchorVertex, AnchorData> &g = graph[orientation]; + QList<QPair<AnchorVertex *, AnchorVertex *> > vertices = g.connections(); + + qreal spacing = effectiveSpacing(orientation); + + for (int i = 0; i < vertices.count(); ++i) { + AnchorData *data = g.edgeData(vertices.at(i).first, vertices.at(i).second);; + Q_ASSERT(data->from && data->to); + data->refreshSizeHints(spacing); + } +} + +/*! + \internal + + This method walks the graph using a breadth-first search to find paths + between the root vertex and each vertex on the graph. The edges + directions in each path are considered and they are stored as a + positive edge (left-to-right) or negative edge (right-to-left). + + The list of paths is used later to generate a list of constraints. + */ +void QGraphicsAnchorLayoutPrivate::findPaths(Orientation orientation) +{ + QQueue<QPair<AnchorVertex *, AnchorVertex *> > queue; + + QSet<AnchorData *> visited; + + AnchorVertex *root = graph[orientation].rootVertex(); + + graphPaths[orientation].insert(root, GraphPath()); + + foreach (AnchorVertex *v, graph[orientation].adjacentVertices(root)) { + queue.enqueue(qMakePair(root, v)); + } + + while(!queue.isEmpty()) { + QPair<AnchorVertex *, AnchorVertex *> pair = queue.dequeue(); + AnchorData *edge = graph[orientation].edgeData(pair.first, pair.second); + + if (visited.contains(edge)) + continue; + + visited.insert(edge); + GraphPath current = graphPaths[orientation].value(pair.first); + + if (edge->from == pair.first) + current.positives.insert(edge); + else + current.negatives.insert(edge); + + graphPaths[orientation].insert(pair.second, current); + + foreach (AnchorVertex *v, + graph[orientation].adjacentVertices(pair.second)) { + queue.enqueue(qMakePair(pair.second, v)); + } + } +} + +/*! + \internal + + Each vertex on the graph that has more than one path to it + represents a contra int to the sizes of the items in these paths. + + This method walks the list of paths to each vertex, generate + the constraints and store them in a list so they can be used later + by the Simplex solver. +*/ +void QGraphicsAnchorLayoutPrivate::constraintsFromPaths(Orientation orientation) +{ + foreach (AnchorVertex *vertex, graphPaths[orientation].uniqueKeys()) + { + int valueCount = graphPaths[orientation].count(vertex); + if (valueCount == 1) + continue; + + QList<GraphPath> pathsToVertex = graphPaths[orientation].values(vertex); + for (int i = 1; i < valueCount; ++i) { + constraints[orientation] += \ + pathsToVertex[0].constraint(pathsToVertex[i]); + } + } +} + +/*! + \internal + + Create LP constraints for each anchor based on its minimum and maximum + sizes, as specified in its size hints +*/ +QList<QSimplexConstraint *> QGraphicsAnchorLayoutPrivate::constraintsFromSizeHints( + const QList<AnchorData *> &anchors) +{ + QList<QSimplexConstraint *> anchorConstraints; + for (int i = 0; i < anchors.size(); ++i) { + QSimplexConstraint *c = new QSimplexConstraint; + c->variables.insert(anchors[i], 1.0); + c->constant = anchors[i]->minSize; + c->ratio = QSimplexConstraint::MoreOrEqual; + anchorConstraints += c; + + c = new QSimplexConstraint; + c->variables.insert(anchors[i], 1.0); + c->constant = anchors[i]->maxSize; + c->ratio = QSimplexConstraint::LessOrEqual; + anchorConstraints += c; + } + + return anchorConstraints; +} + +/*! + \internal +*/ +QList< QList<QSimplexConstraint *> > +QGraphicsAnchorLayoutPrivate::getGraphParts(Orientation orientation) +{ + Q_Q(QGraphicsAnchorLayout); + + // Find layout vertices and edges for the current orientation. + AnchorVertex *layoutFirstVertex = \ + internalVertex(q, pickEdge(Qt::AnchorLeft, orientation)); + + AnchorVertex *layoutCentralVertex = \ + internalVertex(q, pickEdge(Qt::AnchorHorizontalCenter, orientation)); + + AnchorVertex *layoutLastVertex = \ + internalVertex(q, pickEdge(Qt::AnchorRight, orientation)); + + Q_ASSERT(layoutFirstVertex && layoutLastVertex); + + AnchorData *edgeL1 = NULL; + AnchorData *edgeL2 = NULL; + + // The layout may have a single anchor between Left and Right or two half anchors + // passing through the center + if (layoutCentralVertex) { + edgeL1 = graph[orientation].edgeData(layoutFirstVertex, layoutCentralVertex); + edgeL2 = graph[orientation].edgeData(layoutCentralVertex, layoutLastVertex); + } else { + edgeL1 = graph[orientation].edgeData(layoutFirstVertex, layoutLastVertex); + } + + QLinkedList<QSimplexConstraint *> remainingConstraints; + for (int i = 0; i < constraints[orientation].count(); ++i) { + remainingConstraints += constraints[orientation][i]; + } + for (int i = 0; i < itemCenterConstraints[orientation].count(); ++i) { + remainingConstraints += itemCenterConstraints[orientation][i]; + } + + QList<QSimplexConstraint *> trunkConstraints; + QSet<QSimplexVariable *> trunkVariables; + + trunkVariables += edgeL1; + if (edgeL2) + trunkVariables += edgeL2; + + bool dirty; + do { + dirty = false; + + QLinkedList<QSimplexConstraint *>::iterator it = remainingConstraints.begin(); + while (it != remainingConstraints.end()) { + QSimplexConstraint *c = *it; + bool match = false; + + // Check if this constraint have some overlap with current + // trunk variables... + foreach (QSimplexVariable *ad, trunkVariables) { + if (c->variables.contains(ad)) { + match = true; + break; + } + } + + // If so, we add it to trunk, and erase it from the + // remaining constraints. + if (match) { + trunkConstraints += c; + trunkVariables += QSet<QSimplexVariable *>::fromList(c->variables.keys()); + it = remainingConstraints.erase(it); + dirty = true; + } else { + // Note that we don't erase the constraint if it's not + // a match, since in a next iteration of a do-while we + // can pass on it again and it will be a match. + // + // For example: if trunk share a variable with + // remainingConstraints[1] and it shares with + // remainingConstraints[0], we need a second iteration + // of the do-while loop to match both. + ++it; + } + } + } while (dirty); + + QList< QList<QSimplexConstraint *> > result; + result += trunkConstraints; + + if (!remainingConstraints.isEmpty()) { + QList<QSimplexConstraint *> nonTrunkConstraints; + QLinkedList<QSimplexConstraint *>::iterator it = remainingConstraints.begin(); + while (it != remainingConstraints.end()) { + nonTrunkConstraints += *it; + ++it; + } + result += nonTrunkConstraints; + } + + return result; +} + +/*! + \internal + + Use the current vertices distance to calculate and set the geometry of + each item. +*/ +void QGraphicsAnchorLayoutPrivate::setItemsGeometries() +{ + AnchorVertex *firstH, *secondH, *firstV, *secondV; + + foreach (QGraphicsLayoutItem *item, items) { + firstH = internalVertex(item, Qt::AnchorLeft); + secondH = internalVertex(item, Qt::AnchorRight); + firstV = internalVertex(item, Qt::AnchorTop); + secondV = internalVertex(item, Qt::AnchorBottom); + + QPointF topLeft(firstH->distance, firstV->distance); + QPointF bottomRight(secondH->distance, secondV->distance); + + item->setGeometry(QRectF(topLeft, bottomRight)); + } +} + +/*! + \internal + + Calculate the position of each vertex based on the paths to each of + them as well as the current edges sizes. +*/ +void QGraphicsAnchorLayoutPrivate::calculateVertexPositions( + QGraphicsAnchorLayoutPrivate::Orientation orientation) +{ + Q_Q(QGraphicsAnchorLayout); + QQueue<QPair<AnchorVertex *, AnchorVertex *> > queue; + QSet<AnchorVertex *> visited; + + // Get root vertex + AnchorVertex *root = graph[orientation].rootVertex(); + + qreal widgetMargin; + qreal layoutMargin; + + // Initialize the first vertex + if (orientation == Horizontal) { + widgetMargin = q->geometry().x(); + q->getContentsMargins(&layoutMargin, 0, 0, 0); + } else { + // Root position is equal to the top margin + widgetMargin = q->geometry().y(); + q->getContentsMargins(0, &layoutMargin, 0, 0); + } + root->distance = widgetMargin + layoutMargin; + visited.insert(root); + + // Add initial edges to the queue + foreach (AnchorVertex *v, graph[orientation].adjacentVertices(root)) { + queue.enqueue(qMakePair(root, v)); + } + + // Do initial calculation required by "interpolateEdge()" + setupEdgesInterpolation(orientation); + + // Traverse the graph and calculate vertex positions, we need to + // visit all pairs since each of them could have a sequential + // anchor inside, which hides more vertices. + while (!queue.isEmpty()) { + QPair<AnchorVertex *, AnchorVertex *> pair = queue.dequeue(); + AnchorData *edge = graph[orientation].edgeData(pair.first, pair.second); + + // Both vertices were interpolated, and the anchor itself can't have other + // anchors inside (it's not a complex anchor). + if (edge->type == AnchorData::Normal && visited.contains(pair.second)) + continue; + + visited.insert(pair.second); + interpolateEdge(pair.first, edge, orientation); + + QList<AnchorVertex *> adjacents = graph[orientation].adjacentVertices(pair.second); + for (int i = 0; i < adjacents.count(); ++i) { + if (!visited.contains(adjacents.at(i))) + queue.enqueue(qMakePair(pair.second, adjacents.at(i))); + } + } +} + +/*! + \internal + + Calculate interpolation parameters based on current Layout Size. + Must once before calling "interpolateEdgeSize()" for each edge. +*/ +void QGraphicsAnchorLayoutPrivate::setupEdgesInterpolation( + Orientation orientation) +{ + Q_Q(QGraphicsAnchorLayout); + qreal lower, upper, current; + + if (orientation == Horizontal) { + current = q->contentsRect().width(); + } else { + current = q->contentsRect().height(); + } + + if (current < sizeHints[orientation][Qt::PreferredSize]) { + interpolationInterval[orientation] = MinToPreferred; + lower = sizeHints[orientation][Qt::MinimumSize]; + upper = sizeHints[orientation][Qt::PreferredSize]; + } else { + interpolationInterval[orientation] = PreferredToMax; + lower = sizeHints[orientation][Qt::PreferredSize]; + upper = sizeHints[orientation][Qt::MaximumSize]; + } + + if (upper == lower) { + interpolationProgress[orientation] = 0; + } else { + interpolationProgress[orientation] = (current - lower) / (upper - lower); + } +} + +/*! + \internal + + Calculate the current Edge size based on the current Layout size and the + size the edge is supposed to have when: + + - the layout is at its minimum size. + - the layout is at its preferred size. + - the layout is at its maximum size. + + These three key values are calculated in advance using linear + programming (more expensive) or the simplification algorithm, then + subsequential resizes of the parent layout require a simple + interpolation. + + If the edge is sequential or parallel, it's possible to have more + vertices to be initalized, so it calls specialized functions that + will recurse back to interpolateEdge(). + */ +void QGraphicsAnchorLayoutPrivate::interpolateEdge(AnchorVertex *base, + AnchorData *edge, + Orientation orientation) +{ + qreal lower, upper; + + if (interpolationInterval[orientation] == MinToPreferred) { + lower = edge->sizeAtMinimum; + upper = edge->sizeAtPreferred; + } else { + lower = edge->sizeAtPreferred; + upper = edge->sizeAtMaximum; + } + + qreal edgeDistance = (interpolationProgress[orientation] * (upper - lower)) + lower; + + Q_ASSERT(edge->from == base || edge->to == base); + + if (edge->from == base) + edge->to->distance = base->distance + edgeDistance; + else + edge->from->distance = base->distance - edgeDistance; + + // Process child anchors + if (edge->type == AnchorData::Sequential) + interpolateSequentialEdges(edge->from, + static_cast<SequentialAnchorData *>(edge), + orientation); + else if (edge->type == AnchorData::Parallel) + interpolateParallelEdges(edge->from, + static_cast<ParallelAnchorData *>(edge), + orientation); +} + +void QGraphicsAnchorLayoutPrivate::interpolateParallelEdges( + AnchorVertex *base, ParallelAnchorData *data, Orientation orientation) +{ + // In parallels the boundary vertices are already calculate, we + // just need to look for sequential groups inside, because only + // them may have new vertices associated. + + // First edge + if (data->firstEdge->type == AnchorData::Sequential) + interpolateSequentialEdges(base, + static_cast<SequentialAnchorData *>(data->firstEdge), + orientation); + else if (data->firstEdge->type == AnchorData::Parallel) + interpolateParallelEdges(base, + static_cast<ParallelAnchorData *>(data->firstEdge), + orientation); + + // Second edge + if (data->secondEdge->type == AnchorData::Sequential) + interpolateSequentialEdges(base, + static_cast<SequentialAnchorData *>(data->secondEdge), + orientation); + else if (data->secondEdge->type == AnchorData::Parallel) + interpolateParallelEdges(base, + static_cast<ParallelAnchorData *>(data->secondEdge), + orientation); +} + +void QGraphicsAnchorLayoutPrivate::interpolateSequentialEdges( + AnchorVertex *base, SequentialAnchorData *data, Orientation orientation) +{ + AnchorVertex *prev = base; + + // ### I'm not sure whether this assumption is safe. If not, + // consider that m_edges.last() could be used instead (so + // at(0) would be the one to be treated specially). + Q_ASSERT(base == data->m_edges.at(0)->to || base == data->m_edges.at(0)->from); + + // Skip the last + for (int i = 0; i < data->m_edges.count() - 1; ++i) { + AnchorData *child = data->m_edges.at(i); + interpolateEdge(prev, child, orientation); + prev = child->to; + } + + // Treat the last specially, since we already calculated it's end + // vertex, so it's only interesting if it's a complex one + if (data->m_edges.last()->type != AnchorData::Normal) + interpolateEdge(prev, data->m_edges.last(), orientation); +} + +QPair<qreal, qreal> +QGraphicsAnchorLayoutPrivate::solveMinMax(QList<QSimplexConstraint *> constraints, + GraphPath path) +{ + QSimplex simplex; + simplex.setConstraints(constraints); + + // Obtain the objective constraint + QSimplexConstraint objective; + QSet<AnchorData *>::const_iterator iter; + for (iter = path.positives.constBegin(); iter != path.positives.constEnd(); ++iter) + objective.variables.insert(*iter, 1.0); + + for (iter = path.negatives.constBegin(); iter != path.negatives.constEnd(); ++iter) + objective.variables.insert(*iter, -1.0); + + simplex.setObjective(&objective); + + // Calculate minimum values + qreal min = simplex.solveMin(); + + // Save sizeAtMinimum results + QList<QSimplexVariable *> variables = simplex.constraintsVariables(); + for (int i = 0; i < variables.size(); ++i) { + AnchorData *ad = static_cast<AnchorData *>(variables[i]); + ad->sizeAtMinimum = ad->result; + } + + // Calculate maximum values + qreal max = simplex.solveMax(); + + // Save sizeAtMaximum results + for (int i = 0; i < variables.size(); ++i) { + AnchorData *ad = static_cast<AnchorData *>(variables[i]); + ad->sizeAtMaximum = ad->result; + } + + return qMakePair<qreal, qreal>(min, max); +} + +void QGraphicsAnchorLayoutPrivate::solvePreferred(QList<QSimplexConstraint *> constraints) +{ + QList<AnchorData *> variables = getVariables(constraints); + QList<QSimplexConstraint *> preferredConstraints; + QList<QSimplexVariable *> preferredVariables; + QSimplexConstraint objective; + + // Fill the objective coefficients for this variable. In the + // end the objective function will be + // + // z = n * (A_shrink + B_shrink + ...) + (A_grower + B_grower + ...) + // + // where n is the number of variables that have + // slacks. Note that here we use the number of variables + // as coefficient, this is to mark the "shrinker slack + // variable" less likely to get value than the "grower + // slack variable". + + // This will fill the values for the structural constraints + // and we now fill the values for the slack constraints (one per variable), + // which have this form (the constant A_pref was set when creating the slacks): + // + // A + A_shrinker - A_grower = A_pref + // + for (int i = 0; i < variables.size(); ++i) { + AnchorData *ad = static_cast<AnchorData *>(variables[i]); + if (ad->skipInPreferred) + continue; + + QSimplexVariable *grower = new QSimplexVariable; + QSimplexVariable *shrinker = new QSimplexVariable; + QSimplexConstraint *c = new QSimplexConstraint; + c->variables.insert(ad, 1.0); + c->variables.insert(shrinker, 1.0); + c->variables.insert(grower, -1.0); + c->constant = ad->prefSize; + + preferredConstraints += c; + preferredVariables += grower; + preferredVariables += shrinker; + + objective.variables.insert(grower, 1.0); + objective.variables.insert(shrinker, variables.size()); + } + + + QSimplex *simplex = new QSimplex; + simplex->setConstraints(constraints + preferredConstraints); + simplex->setObjective(&objective); + + // Calculate minimum values + simplex->solveMin(); + + // Save sizeAtPreferred results + for (int i = 0; i < variables.size(); ++i) { + AnchorData *ad = static_cast<AnchorData *>(variables[i]); + ad->sizeAtPreferred = ad->result; + } + + // Make sure we delete the simplex solver -before- we delete the + // constraints used by it. + delete simplex; + + // Delete constraints and variables we created. + qDeleteAll(preferredConstraints); + qDeleteAll(preferredVariables); +} + +#ifdef QT_DEBUG +void QGraphicsAnchorLayoutPrivate::dumpGraph() +{ + QFile file(QString::fromAscii("anchorlayout.dot")); + if (!file.open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate)) + qWarning("Could not write to %s", file.fileName().toLocal8Bit().constData()); + + QString str = QString::fromAscii("digraph anchorlayout {\nnode [shape=\"rect\"]\n%1}"); + QString dotContents = graph[0].serializeToDot(); + dotContents += graph[1].serializeToDot(); + file.write(str.arg(dotContents).toLocal8Bit()); + + file.close(); +} +#endif + +QT_END_NAMESPACE diff --git a/src/gui/graphicsview/qgraphicsanchorlayout_p.h b/src/gui/graphicsview/qgraphicsanchorlayout_p.h new file mode 100644 index 0000000..15a1b44 --- /dev/null +++ b/src/gui/graphicsview/qgraphicsanchorlayout_p.h @@ -0,0 +1,477 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QGraphicsWidget> + +#include "qgraphicslayout_p.h" +#include "qgraphicsanchorlayout.h" +#include "qgraph_p.h" +#include "qsimplex_p.h" + +QT_BEGIN_NAMESPACE + +/* + The public QGraphicsAnchorLayout interface represents an anchorage point + as a pair of a <QGraphicsLayoutItem *> and a <Qt::AnchorPoint>. + + Internally though, it has a graph of anchorage points (vertices) and + anchors (edges), represented by the AnchorVertex and AnchorData structs + respectively. +*/ + +/*! + \internal + + Represents a vertex (anchorage point) in the internal graph +*/ +struct AnchorVertex { + AnchorVertex(QGraphicsLayoutItem *item, Qt::AnchorPoint edge) + : m_item(item), m_edge(edge) {} + + AnchorVertex() + : m_item(0), m_edge(Qt::AnchorPoint(0)) {} + +#ifdef QT_DEBUG + inline QString toString() const; +#endif + QGraphicsLayoutItem *m_item; + Qt::AnchorPoint m_edge; + + // Current distance from this vertex to the layout edge (Left or Top) + // Value is calculated from the current anchors sizes. + qreal distance; +}; + +#ifdef QT_DEBUG +inline QString AnchorVertex::toString() const +{ + if (!this || !m_item) { + return QLatin1String("NULL"); + } + QString edge; + switch (m_edge) { + case Qt::AnchorLeft: + edge = QLatin1String("Left"); + break; + case Qt::AnchorHorizontalCenter: + edge = QLatin1String("HorizontalCenter"); + break; + case Qt::AnchorRight: + edge = QLatin1String("Right"); + break; + case Qt::AnchorTop: + edge = QLatin1String("Top"); + break; + case Qt::AnchorVerticalCenter: + edge = QLatin1String("VerticalCenter"); + break; + case Qt::AnchorBottom: + edge = QLatin1String("Bottom"); + break; + default: + edge = QLatin1String("None"); + break; + } + QString itemName; + if (m_item->isLayout()) { + itemName = QLatin1String("layout"); + } else { + if (QGraphicsItem *item = m_item->graphicsItem()) { + itemName = item->data(0).toString(); + } + } + edge.insert(0, QLatin1String("%1_")); + return edge.arg(itemName); +} +#endif + +/*! + \internal + + Represents an edge (anchor) in the internal graph. +*/ +struct AnchorData : public QSimplexVariable { + enum Type { + Normal = 0, + Sequential, + Parallel + }; + AnchorData(qreal minimumSize, qreal preferredSize, qreal maximumSize) + : QSimplexVariable(), from(0), to(0), + minSize(minimumSize), prefSize(preferredSize), + maxSize(maximumSize), sizeAtMinimum(preferredSize), + sizeAtPreferred(preferredSize), sizeAtMaximum(preferredSize), + skipInPreferred(0), type(Normal), hasSize(true), + isLayoutAnchor(false) {} + + AnchorData(qreal size) + : QSimplexVariable(), from(0), to(0), + minSize(size), prefSize(size), maxSize(size), + sizeAtMinimum(size), sizeAtPreferred(size), sizeAtMaximum(size), + skipInPreferred(0), type(Normal), hasSize(true), + isLayoutAnchor(false) {} + + AnchorData() + : QSimplexVariable(), from(0), to(0), + minSize(0), prefSize(0), maxSize(0), + sizeAtMinimum(0), sizeAtPreferred(0), sizeAtMaximum(0), + skipInPreferred(0), type(Normal), hasSize(false), + isLayoutAnchor(false) {} + + virtual void updateChildrenSizes() { }; + virtual void refreshSizeHints(qreal effectiveSpacing); + + virtual ~AnchorData() {} + +#ifdef QT_DEBUG + void dump(int indent = 2); + inline QString toString() const; + QString name; +#endif + + inline void setFixedSize(qreal size) + { + minSize = size; + prefSize = size; + maxSize = size; + sizeAtMinimum = size; + sizeAtPreferred = size; + sizeAtMaximum = size; + hasSize = true; + } + + inline void unsetSize() + { + hasSize = false; + } + + // Anchor is semantically directed + AnchorVertex *from; + AnchorVertex *to; + + // Size restrictions of this edge. For anchors internal to items, these + // values are derived from the respective item size hints. For anchors + // that were added by users, these values are equal to the specified anchor + // size. + qreal minSize; + qreal prefSize; + qreal maxSize; + + // These attributes define which sizes should that anchor be in when the + // layout is at its minimum, preferred or maximum sizes. Values are + // calculated by the Simplex solver based on the current layout setup. + qreal sizeAtMinimum; + qreal sizeAtPreferred; + qreal sizeAtMaximum; + + uint skipInPreferred : 1; + uint type : 2; // either Normal, Sequential or Parallel + uint hasSize : 1; // if false, get size from style. + uint isLayoutAnchor : 1; // if this anchor is connected to a layout 'edge' +protected: + AnchorData(Type type, qreal size = 0) + : QSimplexVariable(), from(0), to(0), + minSize(size), prefSize(size), + maxSize(size), sizeAtMinimum(size), + sizeAtPreferred(size), sizeAtMaximum(size), + skipInPreferred(0), type(type), hasSize(true), + isLayoutAnchor(false) {} +}; + +#ifdef QT_DEBUG +inline QString AnchorData::toString() const +{ + return QString::fromAscii("Anchor(%1)").arg(name); +} +#endif + +struct SequentialAnchorData : public AnchorData +{ + SequentialAnchorData() : AnchorData(AnchorData::Sequential) + { +#ifdef QT_DEBUG + name = QLatin1String("SequentialAnchorData"); +#endif + } + + virtual void updateChildrenSizes(); + virtual void refreshSizeHints(qreal effectiveSpacing); + + void setVertices(const QVector<AnchorVertex*> &vertices) + { + m_children = vertices; +#ifdef QT_DEBUG + name = QString::fromAscii("%1 -- %2").arg(vertices.first()->toString(), vertices.last()->toString()); +#endif + } + + QVector<AnchorVertex*> m_children; // list of vertices in the sequence + QVector<AnchorData*> m_edges; // keep the list of edges too. +}; + +struct ParallelAnchorData : public AnchorData +{ + ParallelAnchorData(AnchorData *first, AnchorData *second) + : AnchorData(AnchorData::Parallel), + firstEdge(first), secondEdge(second) + { + // ### Those asserts force that both child anchors have the same direction, + // but can't we simplify a pair of anchors in opposite directions? + Q_ASSERT(first->from == second->from); + Q_ASSERT(first->to == second->to); + from = first->from; + to = first->to; +#ifdef QT_DEBUG + name = QString::fromAscii("%1 | %2").arg(first->toString(), second->toString()); +#endif + } + + virtual void updateChildrenSizes(); + virtual void refreshSizeHints(qreal effectiveSpacing); + + AnchorData* firstEdge; + AnchorData* secondEdge; +}; + +/*! + \internal + + Representation of a valid path for a given vertex in the graph. + In this struct, "positives" is the set of anchors that have been + traversed in the forward direction, while "negatives" is the set + with the ones walked backwards. + + This paths are compared against each other to produce LP Constraints, + the exact order in which the anchors were traversed is not relevant. +*/ +class GraphPath +{ +public: + GraphPath() {}; + + QSimplexConstraint *constraint(const GraphPath &path) const; +#ifdef QT_DEBUG + QString toString() const; +#endif + QSet<AnchorData *> positives; + QSet<AnchorData *> negatives; +}; + + +/*! + \internal + + QGraphicsAnchorLayout private methods and attributes. +*/ +class QGraphicsAnchorLayoutPrivate : public QGraphicsLayoutPrivate +{ + Q_DECLARE_PUBLIC(QGraphicsAnchorLayout) + +public: + // When the layout geometry is different from its Minimum, Preferred + // or Maximum values, interpolation is used to calculate the geometries + // of the items. + // + // Interval represents which interpolation interval are we operating in. + enum Interval { + MinToPreferred = 0, + PreferredToMax + }; + + // Several structures internal to the layout are duplicated to handle + // both Horizontal and Vertical restrictions. + // + // Orientation is used to reference the right structure in each context + enum Orientation { + Horizontal = 0, + Vertical, + NOrientations + }; + + QGraphicsAnchorLayoutPrivate(); + + static Qt::AnchorPoint oppositeEdge( + Qt::AnchorPoint edge); + + static Orientation edgeOrientation(Qt::AnchorPoint edge); + + static Qt::AnchorPoint pickEdge(Qt::AnchorPoint edge, Orientation orientation) + { + if (orientation == Vertical && int(edge) <= 2) + return (Qt::AnchorPoint)(edge + 3); + else if (orientation == Horizontal && int(edge) >= 3) { + return (Qt::AnchorPoint)(edge - 3); + } + return edge; + } + + // Init methods + void createLayoutEdges(); + void deleteLayoutEdges(); + void createItemEdges(QGraphicsLayoutItem *item); + void createCenterAnchors(QGraphicsLayoutItem *item, Qt::AnchorPoint centerEdge); + void removeCenterAnchors(QGraphicsLayoutItem *item, Qt::AnchorPoint centerEdge, bool substitute = true); + void removeCenterConstraints(QGraphicsLayoutItem *item, Orientation orientation); + + // helper function used by the 4 API functions + void anchor(QGraphicsLayoutItem *firstItem, + Qt::AnchorPoint firstEdge, + QGraphicsLayoutItem *secondItem, + Qt::AnchorPoint secondEdge, + qreal *spacing = 0); + + // Anchor Manipulation methods + void addAnchor(QGraphicsLayoutItem *firstItem, + Qt::AnchorPoint firstEdge, + QGraphicsLayoutItem *secondItem, + Qt::AnchorPoint secondEdge, + AnchorData *data); + + void removeAnchor(QGraphicsLayoutItem *firstItem, + Qt::AnchorPoint firstEdge, + QGraphicsLayoutItem *secondItem, + Qt::AnchorPoint secondEdge); + + bool setAnchorSize(const QGraphicsLayoutItem *firstItem, + Qt::AnchorPoint firstEdge, + const QGraphicsLayoutItem *secondItem, + Qt::AnchorPoint secondEdge, + const qreal *anchorSize); + + bool anchorSize(const QGraphicsLayoutItem *firstItem, + Qt::AnchorPoint firstEdge, + const QGraphicsLayoutItem *secondItem, + Qt::AnchorPoint secondEdge, + qreal *minSize = 0, + qreal *prefSize = 0, + qreal *maxSize = 0) const; + + void removeAnchors(QGraphicsLayoutItem *item); + + void removeVertex(QGraphicsLayoutItem *item, Qt::AnchorPoint edge); + + void correctEdgeDirection(QGraphicsLayoutItem *&firstItem, + Qt::AnchorPoint &firstEdge, + QGraphicsLayoutItem *&secondItem, + Qt::AnchorPoint &secondEdge); + // for getting the actual spacing (will query the style if the + // spacing is not explicitly set). + qreal effectiveSpacing(Orientation orientation) const; + + // Activation methods + void simplifyGraph(Orientation orientation); + bool simplifyGraphIteration(Orientation orientation); + void restoreSimplifiedGraph(Orientation orientation); + + void calculateGraphs(); + void calculateGraphs(Orientation orientation); + void setAnchorSizeHintsFromItems(Orientation orientation); + void findPaths(Orientation orientation); + void constraintsFromPaths(Orientation orientation); + QList<QSimplexConstraint *> constraintsFromSizeHints(const QList<AnchorData *> &anchors); + QList<QList<QSimplexConstraint *> > getGraphParts(Orientation orientation); + + inline AnchorVertex *internalVertex(const QPair<QGraphicsLayoutItem*, Qt::AnchorPoint> &itemEdge) const + { + return m_vertexList.value(itemEdge).first; + } + + inline AnchorVertex *internalVertex(const QGraphicsLayoutItem *item, Qt::AnchorPoint edge) const + { + return internalVertex(qMakePair(const_cast<QGraphicsLayoutItem *>(item), edge)); + } + + AnchorVertex *addInternalVertex(QGraphicsLayoutItem *item, Qt::AnchorPoint edge); + void removeInternalVertex(QGraphicsLayoutItem *item, Qt::AnchorPoint edge); + + // Geometry interpolation methods + void setItemsGeometries(); + + void calculateVertexPositions(Orientation orientation); + void setupEdgesInterpolation(Orientation orientation); + void interpolateEdge(AnchorVertex *base, AnchorData *edge, Orientation orientation); + void interpolateSequentialEdges(AnchorVertex *base, SequentialAnchorData *edge, + Orientation orientation); + void interpolateParallelEdges(AnchorVertex *base, ParallelAnchorData *edge, + Orientation orientation); + + // Linear Programming solver methods + QPair<qreal, qreal> solveMinMax(QList<QSimplexConstraint *> constraints, + GraphPath path); + void solvePreferred(QList<QSimplexConstraint *> constraints); + +#ifdef QT_DEBUG + void dumpGraph(); +#endif + + + qreal spacings[NOrientations]; + // Size hints from simplex engine + qreal sizeHints[2][3]; + + // Items + QVector<QGraphicsLayoutItem *> items; + + // Mapping between high level anchorage points (Item, Edge) to low level + // ones (Graph Vertices) + + QHash<QPair<QGraphicsLayoutItem*, Qt::AnchorPoint>, QPair<AnchorVertex *, int> > m_vertexList; + + // Internal graph of anchorage points and anchors, for both orientations + Graph<AnchorVertex, AnchorData> graph[2]; + + // Graph paths and constraints, for both orientations + QMultiHash<AnchorVertex *, GraphPath> graphPaths[2]; + QList<QSimplexConstraint *> constraints[2]; + QList<QSimplexConstraint *> itemCenterConstraints[2]; + + // The interpolation interval and progress based on the current size + // as well as the key values (minimum, preferred and maximum) + Interval interpolationInterval[2]; + qreal interpolationProgress[2]; + + // ### + bool graphSimplified[2]; + + uint calculateGraphCacheDirty : 1; +}; + +QT_END_NAMESPACE diff --git a/src/gui/graphicsview/qgraphicsitem.cpp b/src/gui/graphicsview/qgraphicsitem.cpp index 4f64c2a..88fac14 100644 --- a/src/gui/graphicsview/qgraphicsitem.cpp +++ b/src/gui/graphicsview/qgraphicsitem.cpp @@ -1023,7 +1023,7 @@ void QGraphicsItemPrivate::setParentItemHelper(QGraphicsItem *newParent) } // Resolve depth. - resolveDepth(parent ? parent->d_ptr->depth : -1); + invalidateDepthRecursively(); dirtySceneTransform = 1; // Restore the sub focus chain. @@ -4413,14 +4413,42 @@ bool QGraphicsItemPrivate::discardUpdateRequest(bool ignoreClipping, bool ignore /*! \internal +*/ +int QGraphicsItemPrivate::depth() const +{ + if (itemDepth == -1) + const_cast<QGraphicsItemPrivate *>(this)->resolveDepth(); + + return itemDepth; +} - Resolves the stacking depth of this object and all its children. +/*! + \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; + } } /*! @@ -5590,8 +5618,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; @@ -6601,7 +6629,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(), diff --git a/src/gui/graphicsview/qgraphicsitem_p.h b/src/gui/graphicsview/qgraphicsitem_p.h index 24326f6..fea82ae 100644 --- a/src/gui/graphicsview/qgraphicsitem_p.h +++ b/src/gui/graphicsview/qgraphicsitem_p.h @@ -123,7 +123,7 @@ public: transformData(0), index(-1), siblingIndex(-1), - depth(0), + itemDepth(-1), focusProxy(0), subFocusItem(0), imHints(Qt::ImhNone), @@ -209,7 +209,9 @@ 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); @@ -419,7 +421,7 @@ public: QTransform sceneTransform; int index; int siblingIndex; - int depth; + int itemDepth; // Lazily calculated when calling depth(). QGraphicsItem *focusProxy; QList<QGraphicsItem **> focusProxyRefs; QGraphicsItem *subFocusItem; @@ -500,16 +502,17 @@ struct QGraphicsItemPrivate::TransformData return transform * *postmultiplyTransform; } - QTransform x(transform); + QMatrix4x4 x(transform); for (int i = 0; i < graphicsTransforms.size(); ++i) graphicsTransforms.at(i)->applyTo(&x); x.translate(xOrigin, yOrigin); - x.rotate(rotation, Qt::ZAxis); - x.scale(scale, scale); + x.rotate(rotation, 0, 0, 1); + x.scale(scale); x.translate(-xOrigin, -yOrigin); + QTransform t = x.toTransform(); // project the 3D matrix back to 2D. if (postmultiplyTransform) - x *= *postmultiplyTransform; - return x; + t *= *postmultiplyTransform; + return t; } }; diff --git a/src/gui/graphicsview/qgraphicsscene.cpp b/src/gui/graphicsview/qgraphicsscene.cpp index a2a92b8..a8db74d 100644 --- a/src/gui/graphicsview/qgraphicsscene.cpp +++ b/src/gui/graphicsview/qgraphicsscene.cpp @@ -265,12 +265,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), @@ -311,7 +312,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 +346,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) { @@ -2894,7 +2897,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(); @@ -4473,7 +4476,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. @@ -4619,7 +4622,7 @@ 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 bool useCompatUpdate = views.isEmpty() || isSignalConnected(changedSignalIndex); const QRectF itemBoundingRect = adjustedItemBoundingRect(item); if (useCompatUpdate && !itemIsUntransformable && qFuzzyIsNull(item->boundingRegionGranularity())) { diff --git a/src/gui/graphicsview/qgraphicsscene_p.h b/src/gui/graphicsview/qgraphicsscene_p.h index 685f534..f1ddb5a 100644 --- a/src/gui/graphicsview/qgraphicsscene_p.h +++ b/src/gui/graphicsview/qgraphicsscene_p.h @@ -87,7 +87,7 @@ public: static QGraphicsScenePrivate *get(QGraphicsScene *q); - quint32 changedSignalMask; + static int changedSignalIndex; QGraphicsScene::ItemIndexMethod indexMethod; QGraphicsSceneIndex *index; diff --git a/src/gui/graphicsview/qgraphicsscenebsptreeindex.cpp b/src/gui/graphicsview/qgraphicsscenebsptreeindex.cpp index 433d0a8..7483bc1 100644 --- a/src/gui/graphicsview/qgraphicsscenebsptreeindex.cpp +++ b/src/gui/graphicsview/qgraphicsscenebsptreeindex.cpp @@ -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)) { diff --git a/src/gui/graphicsview/qgraphicstransform.cpp b/src/gui/graphicsview/qgraphicstransform.cpp index edfcf8a..c5653d7 100644 --- a/src/gui/graphicsview/qgraphicstransform.cpp +++ b/src/gui/graphicsview/qgraphicstransform.cpp @@ -63,10 +63,17 @@ independent transformation. The resulting operation is then combined into a single transform which is applied to QGraphicsItem. + Transformations are computed in true 3D space using QMatrix4x4. + When the transformation is applied to a QGraphicsItem, it will be + projected back to a 2D QTransform. When multiple QGraphicsTransform + objects are applied to a QGraphicsItem, all of the transformations + are computed in true 3D space, with the projection back to 2D + only occurring after the last QGraphicsTransform is applied. + If you want to create your own configurable transformation, you can create a subclass of QGraphicsTransform (or any or the existing subclasses), and reimplement the pure virtual applyTo() function, which takes a pointer to a - QTransform. Each operation you would like to apply should be exposed as + QMatrix4x4. Each operation you would like to apply should be exposed as properties (e.g., customTransform->setVerticalShear(2.5)). Inside you reimplementation of applyTo(), you can modify the provided transform respectively. @@ -136,28 +143,13 @@ QGraphicsTransform::QGraphicsTransform(QGraphicsTransformPrivate &p, QObject *pa } /*! - Applies this transformation to an identity transform, and returns the - resulting transform. - - This is equivalent to passing an identity transform to applyTo(). - - \sa applyTo() -*/ -QTransform QGraphicsTransform::transform() const -{ - QTransform t; - applyTo(&t); - return t; -} - -/*! - \fn void QGraphicsTransform::applyTo(QTransform *transform) const + \fn void QGraphicsTransform::applyTo(QMatrix4x4 *matrix) const This pure virtual method has to be reimplemented in derived classes. - It applies this transformation to \a transform. + It applies this transformation to \a matrix. - \sa QGraphicsItem::transform() + \sa QGraphicsItem::transform(), QMatrix4x4::toTransform() */ /*! @@ -189,11 +181,12 @@ void QGraphicsTransform::update() relative to the parent as the rest of the item grows). By default the origin is QPointF(0, 0). - The two parameters xScale and yScale describe the scale factors to apply in - horizontal and vertical direction. They can take on any value, including 0 - (to collapse the item to a point) or negativate value. A negative xScale - value will mirror the item horizontally. A negative yScale value will flip - the item vertically. + The parameters xScale, yScale, and zScale describe the scale factors to + apply in horizontal, vertical, and depth directions. They can take on any + value, including 0 (to collapse the item to a point) or negative value. + A negative xScale value will mirror the item horizontally. A negative yScale + value will flip the item vertically. A negative zScale will flip the + item end for end. \sa QGraphicsTransform, QGraphicsItem::setScale(), QTransform::scale() */ @@ -202,10 +195,11 @@ class QGraphicsScalePrivate : public QGraphicsTransformPrivate { public: QGraphicsScalePrivate() - : xScale(1), yScale(1) {} - QPointF origin; + : xScale(1), yScale(1), zScale(1) {} + QVector3D origin; qreal xScale; qreal yScale; + qreal zScale; }; /*! @@ -225,21 +219,23 @@ QGraphicsScale::~QGraphicsScale() /*! \property QGraphicsScale::origin - \brief The QGraphicsScene class provides the origin of the scale. + \brief the origin of the scale in 3D space. All scaling will be done relative to this point (i.e., this point will stay fixed, relative to the parent, when the item is scaled). - \sa xScale, yScale + \sa xScale, yScale, zScale */ -QPointF QGraphicsScale::origin() const +QVector3D QGraphicsScale::origin() const { Q_D(const QGraphicsScale); return d->origin; } -void QGraphicsScale::setOrigin(const QPointF &point) +void QGraphicsScale::setOrigin(const QVector3D &point) { Q_D(QGraphicsScale); + if (d->origin == point) + return; d->origin = point; update(); emit originChanged(); @@ -254,7 +250,7 @@ void QGraphicsScale::setOrigin(const QPointF &point) provide a negative value, the item will be mirrored horizontally around its origin. - \sa yScale, origin + \sa yScale, zScale, origin */ qreal QGraphicsScale::xScale() const { @@ -280,7 +276,7 @@ void QGraphicsScale::setXScale(qreal scale) provide a negative value, the item will be flipped vertically around its origin. - \sa xScale, origin + \sa xScale, zScale, origin */ qreal QGraphicsScale::yScale() const { @@ -298,14 +294,40 @@ void QGraphicsScale::setYScale(qreal scale) } /*! + \property QGraphicsScale::zScale + \brief the depth scale factor. + + The scale factor can be any real number; the default value is 1.0. If you + set the factor to 0.0, the item will be collapsed to a single point. If you + provide a negative value, the item will be flipped end for end around its + origin. + + \sa xScale, yScale, origin +*/ +qreal QGraphicsScale::zScale() const +{ + Q_D(const QGraphicsScale); + return d->zScale; +} +void QGraphicsScale::setZScale(qreal scale) +{ + Q_D(QGraphicsScale); + if (d->zScale == scale) + return; + d->zScale = scale; + update(); + emit scaleChanged(); +} + +/*! \reimp */ -void QGraphicsScale::applyTo(QTransform *transform) const +void QGraphicsScale::applyTo(QMatrix4x4 *matrix) const { Q_D(const QGraphicsScale); - transform->translate(d->origin.x(), d->origin.y()); - transform->scale(d->xScale, d->yScale); - transform->translate(-d->origin.x(), -d->origin.y()); + matrix->translate(d->origin); + matrix->scale(d->xScale, d->yScale, d->zScale); + matrix->translate(-d->origin); } /*! @@ -319,10 +341,11 @@ void QGraphicsScale::applyTo(QTransform *transform) const /*! \fn QGraphicsScale::scaleChanged() - This signal is emitted whenever the xScale or yScale of the object - changes. + This signal is emitted whenever the xScale, yScale, or zScale + of the object changes. \sa QGraphicsScale::xScale, QGraphicsScale::yScale + \sa QGraphicsScale::zScale */ /*! @@ -359,20 +382,14 @@ void QGraphicsScale::applyTo(QTransform *transform) const \sa QGraphicsTransform, QGraphicsItem::setRotation(), QTransform::rotate() */ -#define VECTOR_FOR_AXIS_X QVector3D(1, 0, 0) -#define VECTOR_FOR_AXIS_Y QVector3D(0, 1, 0) -#define VECTOR_FOR_AXIS_Z QVector3D(0, 0, 1) - - class QGraphicsRotationPrivate : public QGraphicsTransformPrivate { public: QGraphicsRotationPrivate() - : angle(0), axis(VECTOR_FOR_AXIS_Z), simpleAxis(Qt::ZAxis) {} - QPointF origin; + : angle(0), axis(0, 0, 1) {} + QVector3D origin; qreal angle; QVector3D axis; - int simpleAxis; }; /*! @@ -392,21 +409,23 @@ QGraphicsRotation::~QGraphicsRotation() /*! \property QGraphicsRotation::origin - \brief the origin of the rotation. + \brief the origin of the rotation in 3D space. All rotations will be done relative to this point (i.e., this point will stay fixed, relative to the parent, when the item is rotated). \sa angle */ -QPointF QGraphicsRotation::origin() const +QVector3D QGraphicsRotation::origin() const { Q_D(const QGraphicsRotation); return d->origin; } -void QGraphicsRotation::setOrigin(const QPointF &point) +void QGraphicsRotation::setOrigin(const QVector3D &point) { Q_D(QGraphicsRotation); + if (d->origin == point) + return; d->origin = point; update(); emit originChanged(); @@ -448,11 +467,11 @@ void QGraphicsRotation::setAngle(qreal angle) */ /*! - \fn void QGraphicsRotation::angleChanged() + \fn void QGraphicsRotation::angleChanged() - This signal is emitted whenever the angle has changed. + This signal is emitted whenever the angle has changed. - \sa QGraphicsRotation::angle + \sa QGraphicsRotation::angle */ /*! @@ -475,18 +494,9 @@ QVector3D QGraphicsRotation::axis() const void QGraphicsRotation::setAxis(const QVector3D &axis) { Q_D(QGraphicsRotation); - if (d->axis == axis) + if (d->axis == axis) return; - d->axis = axis; - if (axis == VECTOR_FOR_AXIS_X) { - d->simpleAxis = Qt::XAxis; - } else if (axis == VECTOR_FOR_AXIS_Y) { - d->simpleAxis = Qt::YAxis; - } else if (axis == VECTOR_FOR_AXIS_Z) { - d->simpleAxis = Qt::ZAxis; - } else { - d->simpleAxis = -1; // no predefined axis - } + d->axis = axis; update(); emit axisChanged(); } @@ -495,90 +505,58 @@ void QGraphicsRotation::setAxis(const QVector3D &axis) \fn void QGraphicsRotation::setAxis(Qt::Axis axis) Convenience function to set the axis to \a axis. -*/ + Note: the Qt::YAxis rotation for QTransform is inverted from the + correct mathematical rotation in 3D space. The QGraphicsRotation + class implements a correct mathematical rotation. The following + two sequences of code will perform the same transformation: + + \code + QTransform t; + t.rotate(45, Qt::YAxis); + + QGraphicsRotation r; + r.setAxis(Qt::YAxis); + r.setAngle(-45); + \endcode +*/ void QGraphicsRotation::setAxis(Qt::Axis axis) { switch (axis) { case Qt::XAxis: - setAxis(VECTOR_FOR_AXIS_X); + setAxis(QVector3D(1, 0, 0)); break; case Qt::YAxis: - setAxis(VECTOR_FOR_AXIS_Y); + setAxis(QVector3D(0, 1, 0)); break; case Qt::ZAxis: - setAxis(VECTOR_FOR_AXIS_Z); + setAxis(QVector3D(0, 0, 1)); break; } } - -const qreal deg2rad = qreal(0.017453292519943295769); // pi/180 -static const qreal inv_dist_to_plane = 1. / 1024.; - /*! \reimp */ -void QGraphicsRotation::applyTo(QTransform *t) const +void QGraphicsRotation::applyTo(QMatrix4x4 *matrix) const { Q_D(const QGraphicsRotation); - qreal a = d->angle; - - if (a == 0.) - return; - - if (d->simpleAxis != -1) { - //that's an optimization for simple axis - t->translate(d->origin.x(), d->origin.y()); - t->rotate(a, Qt::Axis(d->simpleAxis)); - t->translate(-d->origin.x(), -d->origin.y()); - return; - } - - qreal x = d->axis.x(); - qreal y = d->axis.y(); - qreal z = d->axis.z(); - - if (x == 0. && y == 0 && z == 0) + if (d->angle == 0. || d->axis.isNull()) return; - qreal c, s; - if (a == 90. || a == -270.) { - s = 1.; - c = 0.; - } else if (a == 270. || a == -90.) { - s = -1.; - c = 0.; - } else if (a == 180.) { - s = 0.; - c = -1.; - } else { - qreal b = deg2rad*a; - s = qSin(b); - c = qCos(b); - } - - qreal len = x * x + y * y + z * z; - if (len != 1.) { - len = 1. / qSqrt(len); - x *= len; - y *= len; - z *= len; - } - - t->translate(d->origin.x(), d->origin.y()); - *t = QTransform(x*x*(1-c)+c, x*y*(1-c)+z*s, x*z*(1-c)-y*s*inv_dist_to_plane, - y*x*(1-c)-z*s, y*y*(1-c)+c, y*z*(1-c)-x*s*inv_dist_to_plane, - 0, 0, 1) * *t; - t->translate(-d->origin.x(), -d->origin.y()); + matrix->translate(d->origin); + matrix->rotate(d->angle, d->axis.x(), d->axis.y(), d->axis.z()); + matrix->translate(-d->origin); } /*! \fn void QGraphicsRotation::axisChanged() This signal is emitted whenever the axis of the object changes. + + \sa QGraphicsRotation::axis */ #include "moc_qgraphicstransform.cpp" diff --git a/src/gui/graphicsview/qgraphicstransform.h b/src/gui/graphicsview/qgraphicstransform.h index 8ccc258..d6d5b79 100644 --- a/src/gui/graphicsview/qgraphicstransform.h +++ b/src/gui/graphicsview/qgraphicstransform.h @@ -43,8 +43,9 @@ #define QGRAPHICSTRANSFORM_H #include <QtCore/QObject> -#include <QtGui/QTransform> #include <QtGui/QVector3D> +#include <QtGui/QTransform> +#include <QtGui/QMatrix4x4> QT_BEGIN_HEADER @@ -62,8 +63,7 @@ public: QGraphicsTransform(QObject *parent = 0); ~QGraphicsTransform(); - QTransform transform() const; - virtual void applyTo(QTransform *transform) const = 0; + virtual void applyTo(QMatrix4x4 *matrix) const = 0; protected Q_SLOTS: void update(); @@ -83,15 +83,16 @@ class Q_GUI_EXPORT QGraphicsScale : public QGraphicsTransform { Q_OBJECT - Q_PROPERTY(QPointF origin READ origin WRITE setOrigin NOTIFY originChanged) + Q_PROPERTY(QVector3D origin READ origin WRITE setOrigin NOTIFY originChanged) Q_PROPERTY(qreal xScale READ xScale WRITE setXScale NOTIFY scaleChanged) Q_PROPERTY(qreal yScale READ yScale WRITE setYScale NOTIFY scaleChanged) + Q_PROPERTY(qreal zScale READ zScale WRITE setZScale NOTIFY scaleChanged) public: QGraphicsScale(QObject *parent = 0); ~QGraphicsScale(); - QPointF origin() const; - void setOrigin(const QPointF &point); + QVector3D origin() const; + void setOrigin(const QVector3D &point); qreal xScale() const; void setXScale(qreal); @@ -99,7 +100,10 @@ public: qreal yScale() const; void setYScale(qreal); - void applyTo(QTransform *transform) const; + qreal zScale() const; + void setZScale(qreal); + + void applyTo(QMatrix4x4 *matrix) const; Q_SIGNALS: void originChanged(); @@ -115,15 +119,15 @@ class Q_GUI_EXPORT QGraphicsRotation : public QGraphicsTransform { Q_OBJECT - Q_PROPERTY(QPointF origin READ origin WRITE setOrigin NOTIFY originChanged) + Q_PROPERTY(QVector3D origin READ origin WRITE setOrigin NOTIFY originChanged) Q_PROPERTY(qreal angle READ angle WRITE setAngle NOTIFY angleChanged) Q_PROPERTY(QVector3D axis READ axis WRITE setAxis NOTIFY axisChanged) public: QGraphicsRotation(QObject *parent = 0); ~QGraphicsRotation(); - QPointF origin() const; - void setOrigin(const QPointF &point); + QVector3D origin() const; + void setOrigin(const QVector3D &point); qreal angle() const; void setAngle(qreal); @@ -132,7 +136,7 @@ public: void setAxis(const QVector3D &axis); void setAxis(Qt::Axis axis); - void applyTo(QTransform *transform) const; + void applyTo(QMatrix4x4 *matrix) const; Q_SIGNALS: void originChanged(); diff --git a/src/gui/graphicsview/qsimplex_p.cpp b/src/gui/graphicsview/qsimplex_p.cpp new file mode 100644 index 0000000..7fa5ab0 --- /dev/null +++ b/src/gui/graphicsview/qsimplex_p.cpp @@ -0,0 +1,371 @@ +#include "qsimplex_p.h" + +#include <QtCore/qset.h> +#include <QtCore/qdebug.h> + +#include <stdlib.h> + +QT_BEGIN_NAMESPACE + +QSimplex::QSimplex() : objective(0), rows(0), columns(0), firstArtificial(0), matrix(0) +{ +} + +QSimplex::~QSimplex() +{ + clearDataStructures(); +} + +void QSimplex::clearDataStructures() +{ + if (matrix == 0) + return; + + // Matrix + rows = 0; + columns = 0; + firstArtificial = 0; + free(matrix); + matrix = 0; + + // Constraints + for (int i = 0; i < constraints.size(); ++i) { + delete constraints[i]->helper.first; + constraints[i]->helper.first = 0; + constraints[i]->helper.second = 0.0; + delete constraints[i]->artificial; + constraints[i]->artificial = 0; + } + constraints.clear(); + + // Other + variables.clear(); + objective = 0; +} + +void QSimplex::setConstraints(const QList<QSimplexConstraint *> newConstraints) +{ + clearDataStructures(); + + if (newConstraints.isEmpty()) + return; + constraints = newConstraints; + + // Set Variables direct mapping + QSet<QSimplexVariable *> variablesSet; + for (int i = 0; i < constraints.size(); ++i) + variablesSet += \ + QSet<QSimplexVariable *>::fromList(constraints[i]->variables.keys()); + variables = variablesSet.toList(); + + // Set Variables reverse mapping + for (int i = 0; i < variables.size(); ++i) { + // The variable "0" goes at the column "1", etc... + variables[i]->index = i + 1; + } + + // Normalize Constraints + int variableIndex = variables.size(); + QList <QSimplexVariable *> artificialList; + + for (int i = 0; i < constraints.size(); ++i) { + QSimplexVariable *slack; + QSimplexVariable *surplus; + QSimplexVariable *artificial; + + Q_ASSERT(constraints[i]->helper.first == 0); + Q_ASSERT(constraints[i]->artificial == 0); + + switch(constraints[i]->ratio) { + case QSimplexConstraint::LessOrEqual: + slack = new QSimplexVariable; + slack->index = ++variableIndex; + constraints[i]->helper.first = slack; + constraints[i]->helper.second = 1.0; + break; + case QSimplexConstraint::MoreOrEqual: + surplus = new QSimplexVariable; + surplus->index = ++variableIndex; + constraints[i]->helper.first = surplus; + constraints[i]->helper.second = -1.0; + // fall through + case QSimplexConstraint::Equal: + artificial = new QSimplexVariable; + constraints[i]->artificial = artificial; + artificialList += constraints[i]->artificial; + break; + } + } + + firstArtificial = variableIndex + 1; + for (int i = 0; i < artificialList.size(); ++i) + artificialList[i]->index = ++variableIndex; + artificialList.clear(); + + // Matrix + + // One for each variable plus the Basic and BFS columns (first and last) + columns = variableIndex + 2; + // One for each constraint plus the objective function + rows = constraints.size() + 1; + + matrix = (qreal *)malloc(sizeof(qreal) * columns * rows); + if (!matrix) { + qWarning() << "QSimplex: Unable to allocate memory!"; + return; + } + for (int i = columns * rows - 1; i >= 0; --i) + matrix[i] = 0.0; + + // Fill Matrix + for (int i = 1; i <= constraints.size(); ++i) { + QSimplexConstraint *c = constraints[i - 1]; + + if (c->artificial) { + // Will use artificial basic variable + setValueAt(i, 0, c->artificial->index); + setValueAt(i, c->artificial->index, 1.0); + + if (c->helper.second != 0.0) { + // Surplus variable + setValueAt(i, c->helper.first->index, c->helper.second); + } + } else { + // Slack is used as the basic variable + Q_ASSERT(c->helper.second == 1.0); + setValueAt(i, 0, c->helper.first->index); + setValueAt(i, c->helper.first->index, 1.0); + } + + QHash<QSimplexVariable *, qreal>::const_iterator iter; + for (iter = c->variables.constBegin(); + iter != c->variables.constEnd(); + ++iter) { + setValueAt(i, iter.key()->index, iter.value()); + } + + setValueAt(i, columns - 1, c->constant); + } + + // Set temporary objective: -1 * sum_of_artificial_vars + for (int j = firstArtificial; j < columns - 1; ++j) + setValueAt(0, j, 1.0); + + // Maximize our objective (artificial vars go to zero) + solveMaxHelper(); + + if (valueAt(0, columns - 1) != 0.0) { + qWarning() << "QSimplex: No feasible solution!"; + clearDataStructures(); + return; + } + + // Remove artificial variables + clearColumns(firstArtificial, columns - 2); +} + +void QSimplex::solveMaxHelper() +{ + reducedRowEchelon(); + while (iterate()) ; +} + +void QSimplex::setObjective(QSimplexConstraint *newObjective) +{ + objective = newObjective; +} + +void QSimplex::clearRow(int rowIndex) +{ + qreal *item = matrix + rowIndex * columns; + for (int i = 0; i < columns; ++i) + item[i] = 0.0; +} + +void QSimplex::clearColumns(int first, int last) +{ + for (int i = 0; i < rows; ++i) { + qreal *row = matrix + i * columns; + for (int j = first; j <= last; ++j) + row[j] = 0.0; + } +} + +void QSimplex::dumpMatrix() +{ + printf("---- Simplex Matrix ----\n"); + + printf(" "); + for (int j = 0; j < columns; ++j) + printf(" <% 2d >", j); + printf("\n"); + + for (int i = 0; i < rows; ++i) { + printf("Row %2d:", i); + + qreal *row = matrix + i * columns; + for (int j = 0; j < columns; ++j) { + printf(" % 2.2f", row[j]); + } + printf("\n"); + } + printf("------------------------\n\n"); +} + +void QSimplex::combineRows(int toIndex, int fromIndex, qreal factor) +{ + if (!factor) + return; + + qreal *from = matrix + fromIndex * columns; + qreal *to = matrix + toIndex * columns; + + for (int j = 1; j < columns; ++j) { + qreal value = from[j]; + + // skip to[j] = to[j] + factor*0.0 + if (value == 0.0) + continue; + + to[j] += factor * value; + + // ### Avoid Numerical errors + if (qAbs(to[j]) < 0.0000000001) + to[j] = 0.0; + } +} + +int QSimplex::findPivotColumn() +{ + qreal min = 0; + int minIndex = -1; + + for (int j = 0; j < columns-1; ++j) { + if (valueAt(0, j) < min) { + min = valueAt(0, j); + minIndex = j; + } + } + + return minIndex; +} + +int QSimplex::pivotRowForColumn(int column) +{ + qreal min = 999999999999.0; // ### + int minIndex = -1; + + for (int i = 1; i < rows; ++i) { + qreal divisor = valueAt(i, column); + if (divisor <= 0) + continue; + + qreal quotient = valueAt(i, columns - 1) / divisor; + if (quotient < min) { + min = quotient; + minIndex = i; + } + } + + return minIndex; +} + +void QSimplex::reducedRowEchelon() +{ + for (int i = 1; i < rows; ++i) { + int factorInObjectiveRow = valueAt(i, 0); + combineRows(0, i, -1 * valueAt(0, factorInObjectiveRow)); + } +} + +bool QSimplex::iterate() +{ + // Find Pivot column + int pivotColumn = findPivotColumn(); + if (pivotColumn == -1) + return false; + + // Find Pivot row for column + int pivotRow = pivotRowForColumn(pivotColumn); + if (pivotRow == -1) { + qWarning() << "QSimplex: Unbounded problem!"; + return false; + } + + // Normalize Pivot Row + qreal pivot = valueAt(pivotRow, pivotColumn); + if (pivot != 1.0) + combineRows(pivotRow, pivotRow, (1.0 - pivot) / pivot); + + // Update other rows + for (int row=0; row < rows; ++row) { + if (row == pivotRow) + continue; + + combineRows(row, pivotRow, -1 * valueAt(row, pivotColumn)); + } + + // Update first column + setValueAt(pivotRow, 0, pivotColumn); + + // dumpMatrix(); + // printf("------------ end of iteration --------------\n"); + return true; +} + +/*! + \internal + + Both solveMin and solveMax are interfaces to this method. + + The enum solverFactor admits 2 values: Minimum (-1) and Maximum (+1). + */ +qreal QSimplex::solver(solverFactor factor) +{ + // Remove old objective + clearRow(0); + + // Set new objective + QHash<QSimplexVariable *, qreal>::const_iterator iter; + for (iter = objective->variables.constBegin(); + iter != objective->variables.constEnd(); + ++iter) { + setValueAt(0, iter.key()->index, -1 * factor * iter.value()); + } + + solveMaxHelper(); + collectResults(); + + return factor * valueAt(0, columns - 1); +} + +qreal QSimplex::solveMin() +{ + return solver(Minimum); +} + +qreal QSimplex::solveMax() +{ + return solver(Maximum); +} + +void QSimplex::collectResults() +{ + // All variables are zero unless overridden below. + + // ### Is this really needed? Is there any chance that an + // important variable remains as non-basic at the end of simplex? + for (int i = 0; i < variables.size(); ++i) + variables[i]->result = 0; + + // Basic variables + // Update the variable indicated in the first column with the value + // in the last column. + for (int i = 1; i < rows; ++i) { + int index = valueAt(i, 0) - 1; + if (index < variables.size()) + variables[index]->result = valueAt(i, columns - 1); + } +} + +QT_END_NAMESPACE diff --git a/src/gui/graphicsview/qsimplex_p.h b/src/gui/graphicsview/qsimplex_p.h new file mode 100644 index 0000000..e3629df --- /dev/null +++ b/src/gui/graphicsview/qsimplex_p.h @@ -0,0 +1,125 @@ +/**************************************************************************** +** +** Copyright (C) 1992-$THISYEAR$ $TROLLTECH$. All rights reserved. +** +** This file is part of the $MODULE$ of the Qt Toolkit. +** +** $TROLLTECH_DUAL_LICENSE$ +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +****************************************************************************/ + +#ifndef QSIMPLEX_P_H +#define QSIMPLEX_P_H + +#include <QtCore/qhash.h> +#include <QtCore/qpair.h> + +QT_BEGIN_NAMESPACE + +struct QSimplexVariable +{ + QSimplexVariable() : result(0), index(0) {}; + + qreal result; + uint index; +}; + + +/*! + \internal + + Representation of a LP constraint like: + + (c1 * X1) + (c2 * X2) + ... = K + or <= K + or >= K + + Where (ci, Xi) are the pairs in "variables" and K the real "constant". +*/ +struct QSimplexConstraint +{ + QSimplexConstraint() : constant(0), ratio(Equal), artificial(0) {}; + + enum Ratio { + LessOrEqual = 0, + Equal, + MoreOrEqual + }; + + QHash<QSimplexVariable *, qreal> variables; + qreal constant; + Ratio ratio; + + QPair<QSimplexVariable *, qreal> helper; + QSimplexVariable * artificial; +}; + + +class QSimplex +{ +public: + QSimplex(); + virtual ~QSimplex(); + + qreal solveMin(); + qreal solveMax(); + QList<QSimplexVariable *> constraintsVariables(); + + void setConstraints(const QList<QSimplexConstraint *> constraints); + void setObjective(QSimplexConstraint *objective); + + void dumpMatrix(); + +private: + // Matrix handling + qreal valueAt(int row, int column); + void setValueAt(int row, int column, qreal value); + void clearRow(int rowIndex); + void clearColumns(int first, int last); + void combineRows(int toIndex, int fromIndex, qreal factor); + + // Simplex + int findPivotColumn(); + int pivotRowForColumn(int column); + void reducedRowEchelon(); + bool iterate(); + + // Helpers + void clearDataStructures(); + void solveMaxHelper(); + enum solverFactor { Minimum = -1, Maximum = 1 }; + qreal solver(solverFactor factor); + void collectResults(); + + QList<QSimplexConstraint *> constraints; + QList<QSimplexVariable *> variables; + QSimplexConstraint *objective; + + int rows; + int columns; + int firstArtificial; + + qreal *matrix; +}; + +inline QList<QSimplexVariable *> QSimplex::constraintsVariables() +{ + return variables; +} + +inline qreal QSimplex::valueAt(int rowIndex, int columnIndex) +{ + return matrix[rowIndex * columns + columnIndex]; +} + +inline void QSimplex::setValueAt(int rowIndex, int columnIndex, qreal value) +{ + matrix[rowIndex * columns + columnIndex] = value; +} + +QT_END_NAMESPACE + +#endif // QSIMPLEX_P_H diff --git a/src/gui/image/qbitmap.h b/src/gui/image/qbitmap.h index 8738d65..89d3171 100644 --- a/src/gui/image/qbitmap.h +++ b/src/gui/image/qbitmap.h @@ -82,6 +82,8 @@ public: QT3_SUPPORT_CONSTRUCTOR QBitmap(const QImage &image) { *this = fromImage(image); } QT3_SUPPORT QBitmap &operator=(const QImage &image) { *this = fromImage(image); return *this; } #endif + + typedef QExplicitlySharedDataPointer<QPixmapData> DataPtr; }; Q_DECLARE_SHARED(QBitmap) diff --git a/src/gui/image/qbmphandler.cpp b/src/gui/image/qbmphandler.cpp index ccf8457..19701b4 100644 --- a/src/gui/image/qbmphandler.cpp +++ b/src/gui/image/qbmphandler.cpp @@ -81,14 +81,14 @@ static void swapPixel01(QImage *image) // 1-bpp: swap 0 and 1 pixels const int BMP_FILEHDR_SIZE = 14; // size of BMP_FILEHDR data -QDataStream &operator>>(QDataStream &s, BMP_FILEHDR &bf) +static QDataStream &operator>>(QDataStream &s, BMP_FILEHDR &bf) { // read file header s.readRawData(bf.bfType, 2); s >> bf.bfSize >> bf.bfReserved1 >> bf.bfReserved2 >> bf.bfOffBits; return s; } -QDataStream &operator<<(QDataStream &s, const BMP_FILEHDR &bf) +static QDataStream &operator<<(QDataStream &s, const BMP_FILEHDR &bf) { // write file header s.writeRawData(bf.bfType, 2); s << bf.bfSize << bf.bfReserved1 << bf.bfReserved2 << bf.bfOffBits; @@ -106,7 +106,7 @@ const int BMP_RLE4 = 2; // run-length encoded, 4 const int BMP_BITFIELDS = 3; // RGB values encoded in data as bit-fields -QDataStream &operator>>(QDataStream &s, BMP_INFOHDR &bi) +static QDataStream &operator>>(QDataStream &s, BMP_INFOHDR &bi) { s >> bi.biSize; if (bi.biSize == BMP_WIN || bi.biSize == BMP_OS2) { @@ -128,7 +128,7 @@ QDataStream &operator>>(QDataStream &s, BMP_INFOHDR &bi) return s; } -QDataStream &operator<<(QDataStream &s, const BMP_INFOHDR &bi) +static QDataStream &operator<<(QDataStream &s, const BMP_INFOHDR &bi) { s << bi.biSize; s << bi.biWidth << bi.biHeight; diff --git a/src/gui/image/qiconloader_p.h b/src/gui/image/qiconloader_p.h index b2944ef..5d5c211 100644 --- a/src/gui/image/qiconloader_p.h +++ b/src/gui/image/qiconloader_p.h @@ -85,6 +85,7 @@ struct QIconDirInfo class QIconLoaderEngineEntry { public: + virtual ~QIconLoaderEngineEntry() {} virtual QPixmap pixmap(const QSize &size, QIcon::Mode mode, QIcon::State state) = 0; diff --git a/src/gui/image/qpicture.cpp b/src/gui/image/qpicture.cpp index 1136a97..d821773 100644 --- a/src/gui/image/qpicture.cpp +++ b/src/gui/image/qpicture.cpp @@ -131,7 +131,6 @@ QPicture::QPicture(int formatVersion) d_ptr(new QPicturePrivate) { Q_D(QPicture); - d_ptr->q_ptr = this; if (formatVersion == 0) qWarning("QPicture: invalid format version 0"); @@ -141,8 +140,7 @@ QPicture::QPicture(int formatVersion) d->formatMajor = formatVersion; d->formatMinor = 0; d->formatOk = false; - } - else { + } else { d->resetFormat(); } } @@ -154,9 +152,8 @@ QPicture::QPicture(int formatVersion) */ QPicture::QPicture(const QPicture &pic) - : QPaintDevice(), d_ptr(pic.d_ptr.data()) + : QPaintDevice(), d_ptr(pic.d_ptr) { - d_func()->ref.ref(); } /*! \internal */ @@ -164,7 +161,6 @@ QPicture::QPicture(QPicturePrivate &dptr) : QPaintDevice(), d_ptr(&dptr) { - d_ptr->q_ptr = this; } /*! @@ -225,8 +221,7 @@ const char* QPicture::data() const void QPicture::detach() { - if (d_func()->ref != 1) - detach_helper(); + d_ptr.detach(); } bool QPicture::isDetached() const @@ -1012,22 +1007,16 @@ int QPicture::metric(PaintDeviceMetric m) const /*! \fn bool QPicture::isDetached() const \internal */ + +/*! \internal +### Qt 5 - remove me + */ void QPicture::detach_helper() { - Q_D(QPicture); - QPicturePrivate *x = new QPicturePrivate; - int pictsize = size(); - x->pictb.setData(data(), pictsize); - if (d->pictb.isOpen()) { - x->pictb.open(d->pictb.openMode()); - x->pictb.seek(d->pictb.pos()); - } - x->trecs = d->trecs; - x->formatOk = d->formatOk; - x->formatMinor = d->formatMinor; - x->brect = d->brect; - x->override_rect = d->override_rect; - d_ptr.reset(x); + // QExplicitelySharedDataPointer takes care of cloning using + // QPicturePrivate's copy constructor. Do not call detach_helper() anymore + // and remove in Qt 5, please. + Q_ASSERT_X(false, "QPicture::detach_helper()", "Do not call this function"); } /*! @@ -1036,7 +1025,7 @@ void QPicture::detach_helper() */ QPicture& QPicture::operator=(const QPicture &p) { - d_ptr.assign(p.d_ptr.data()); + d_ptr = p.d_ptr; return *this; } @@ -1046,10 +1035,28 @@ QPicture& QPicture::operator=(const QPicture &p) Constructs a QPicturePrivate */ QPicturePrivate::QPicturePrivate() - : in_memory_only(false), - q_ptr(0) + : in_memory_only(false) { - ref = 1; +} + +/*! + \internal + + Copy-Constructs a QPicturePrivate. Needed when detaching. +*/ +QPicturePrivate::QPicturePrivate(const QPicturePrivate &other) + : trecs(other.trecs), + formatOk(other.formatOk), + formatMinor(other.formatMinor), + brect(other.brect), + override_rect(other.override_rect), + in_memory_only(false) +{ + pictb.setData(other.pictb.data(), other.pictb.size()); + if (other.pictb.isOpen()) { + pictb.open(other.pictb.openMode()); + pictb.seek(other.pictb.pos()); + } } /*! diff --git a/src/gui/image/qpicture.h b/src/gui/image/qpicture.h index 1e80ab7..6effae7 100644 --- a/src/gui/image/qpicture.h +++ b/src/gui/image/qpicture.h @@ -42,8 +42,9 @@ #ifndef QPICTURE_H #define QPICTURE_H -#include <QtGui/qpaintdevice.h> #include <QtCore/qstringlist.h> +#include <QtCore/qsharedpointer.h> +#include <QtGui/qpaintdevice.h> QT_BEGIN_HEADER @@ -106,15 +107,15 @@ private: bool exec(QPainter *p, QDataStream &ds, int i); void detach_helper(); - QScopedSharedPointer<QPicturePrivate> d_ptr; + QExplicitlySharedDataPointer<QPicturePrivate> d_ptr; friend class QPicturePaintEngine; friend class Q3Picture; friend class QAlphaPaintEngine; friend class QPreviewPaintEngine; public: - typedef QPicturePrivate* DataPtr; - inline DataPtr &data_ptr() { return d_ptr.data_ptr(); } + typedef QExplicitlySharedDataPointer<QPicturePrivate> DataPtr; + inline DataPtr &data_ptr() { return d_ptr; } }; Q_DECLARE_SHARED(QPicture) diff --git a/src/gui/image/qpicture_p.h b/src/gui/image/qpicture_p.h index f405d7f..0ab181c 100644 --- a/src/gui/image/qpicture_p.h +++ b/src/gui/image/qpicture_p.h @@ -71,7 +71,6 @@ extern const char *qt_mfhdr_tag; class QPicturePrivate { - Q_DECLARE_PUBLIC(QPicture) friend class QPicturePaintEngine; friend Q_GUI_EXPORT QDataStream &operator<<(QDataStream &s, const QPicture &r); friend Q_GUI_EXPORT QDataStream &operator>>(QDataStream &s, QPicture &r); @@ -144,6 +143,7 @@ public: }; QPicturePrivate(); + QPicturePrivate(const QPicturePrivate &other); QAtomicInt ref; bool checkFormat(); @@ -162,8 +162,6 @@ public: QList<QPixmap> pixmap_list; QList<QBrush> brush_list; QList<QPen> pen_list; - - QPicture *q_ptr; }; QT_END_NAMESPACE diff --git a/src/gui/image/qpixmap.cpp b/src/gui/image/qpixmap.cpp index 79b1f17..00e78ad 100644 --- a/src/gui/image/qpixmap.cpp +++ b/src/gui/image/qpixmap.cpp @@ -120,7 +120,6 @@ void QPixmap::init(int w, int h, int type) data = QGraphicsSystem::createDefaultPixmapData(static_cast<QPixmapData::PixelType>(type)); data->resize(w, h); - data->ref.ref(); } /*! @@ -222,7 +221,6 @@ QPixmap::QPixmap(const QSize &s, int type) QPixmap::QPixmap(QPixmapData *d) : QPaintDevice(), data(d) { - data->ref.ref(); } /*! @@ -261,12 +259,7 @@ QPixmap::QPixmap(const QString& fileName, const char *format, Qt::ImageConversio if (!qt_pixmap_thread_test()) return; - QT_TRY { - load(fileName, format, flags); - } QT_CATCH(...) { - deref(); - QT_RETHROW; - } + load(fileName, format, flags); } /*! @@ -283,11 +276,9 @@ QPixmap::QPixmap(const QPixmap &pixmap) return; } if (pixmap.paintingActive()) { // make a deep copy - data = 0; operator=(pixmap.copy()); } else { data = pixmap.data; - data->ref.ref(); } } @@ -314,17 +305,12 @@ QPixmap::QPixmap(const char * const xpm[]) if (!xpm) return; - QT_TRY { - QImage image(xpm); - if (!image.isNull()) { - if (data->pixelType() == QPixmapData::BitmapType) - *this = QBitmap::fromImage(image); - else - *this = fromImage(image); - } - } QT_CATCH(...) { - deref(); - QT_RETHROW; + QImage image(xpm); + if (!image.isNull()) { + if (data->pixelType() == QPixmapData::BitmapType) + *this = QBitmap::fromImage(image); + else + *this = fromImage(image); } } #endif @@ -336,7 +322,6 @@ QPixmap::QPixmap(const char * const xpm[]) QPixmap::~QPixmap() { - deref(); } /*! @@ -381,7 +366,7 @@ QPixmap QPixmap::copy(const QRect &rect) const else d = QGraphicsSystem::createDefaultPixmapData(data->pixelType()); - d->copy(data, r); + d->copy(data.data(), r); return QPixmap(d); } @@ -454,8 +439,6 @@ QPixmap &QPixmap::operator=(const QPixmap &pixmap) if (pixmap.paintingActive()) { // make a deep copy *this = pixmap.copy(); } else { - pixmap.data->ref.ref(); // avoid 'x = x' - deref(); data = pixmap.data; } return *this; @@ -660,13 +643,13 @@ void QPixmap::resize_helper(const QSize &s) QPixmap pm(QSize(w, h), data->type); bool uninit = false; #if defined(Q_WS_X11) - QX11PixmapData *x11Data = data->classId() == QPixmapData::X11Class ? static_cast<QX11PixmapData*>(data) : 0; + QX11PixmapData *x11Data = data->classId() == QPixmapData::X11Class ? static_cast<QX11PixmapData*>(data.data()) : 0; if (x11Data) { pm.x11SetScreen(x11Data->xinfo.screen()); uninit = x11Data->flags & QX11PixmapData::Uninitialized; } #elif defined(Q_WS_MAC) - QMacPixmapData *macData = data->classId() == QPixmapData::MacClass ? static_cast<QMacPixmapData*>(data) : 0; + QMacPixmapData *macData = data->classId() == QPixmapData::MacClass ? static_cast<QMacPixmapData*>(data.data()) : 0; if (macData) uninit = macData->uninit; #endif @@ -680,7 +663,7 @@ void QPixmap::resize_helper(const QSize &s) #if defined(Q_WS_X11) if (x11Data && x11Data->x11_mask) { - QX11PixmapData *pmData = static_cast<QX11PixmapData*>(pm.data); + QX11PixmapData *pmData = static_cast<QX11PixmapData*>(pm.data.data()); pmData->x11_mask = (Qt::HANDLE)XCreatePixmap(X11->display, RootWindow(x11Data->xinfo.display(), x11Data->xinfo.screen()), @@ -1175,7 +1158,7 @@ Qt::HANDLE QPixmap::handle() const { #if defined(Q_WS_X11) if (data->classId() == QPixmapData::X11Class) - return static_cast<QX11PixmapData*>(data)->handle(); + return static_cast<const QX11PixmapData*>(data.constData())->handle(); #endif return 0; } @@ -1362,14 +1345,12 @@ bool QPixmap::isDetached() const return data->ref == 1; } +/*! \internal + ### Qt5 - remove me. +*/ void QPixmap::deref() { - if (data && !data->ref.deref()) { // Destroy image if last ref - if (data->is_cached) - QImagePixmapCleanupHooks::executePixmapHooks(this); - delete data; - data = 0; - } + Q_ASSERT_X(false, "QPixmap::deref()", "Do not call this function anymore!"); } /*! @@ -1928,7 +1909,7 @@ void QPixmap::detach() { QPixmapData::ClassId id = data->classId(); if (id == QPixmapData::RasterClass) { - QRasterPixmapData *rasterData = static_cast<QRasterPixmapData*>(data); + QRasterPixmapData *rasterData = static_cast<QRasterPixmapData*>(data.data()); rasterData->image.detach(); } @@ -1936,7 +1917,7 @@ void QPixmap::detach() QImagePixmapCleanupHooks::executePixmapHooks(this); #if defined(Q_WS_MAC) - QMacPixmapData *macData = id == QPixmapData::MacClass ? static_cast<QMacPixmapData*>(data) : 0; + QMacPixmapData *macData = id == QPixmapData::MacClass ? static_cast<QMacPixmapData*>(data.data()) : 0; if (macData) { if (macData->cg_mask) { CGImageRelease(macData->cg_mask); @@ -1952,7 +1933,7 @@ void QPixmap::detach() #if defined(Q_WS_X11) if (data->classId() == QPixmapData::X11Class) { - QX11PixmapData *d = static_cast<QX11PixmapData*>(data); + QX11PixmapData *d = static_cast<QX11PixmapData*>(data.data()); d->flags &= ~QX11PixmapData::Uninitialized; // reset the cache data @@ -2038,7 +2019,7 @@ QPixmap QPixmap::fromImage(const QImage &image, Qt::ImageConversionFlags flags) */ QPixmapData* QPixmap::pixmapData() const { - return data; + return data.data(); } /*! diff --git a/src/gui/image/qpixmap.h b/src/gui/image/qpixmap.h index bce1f5e..961008f 100644 --- a/src/gui/image/qpixmap.h +++ b/src/gui/image/qpixmap.h @@ -46,6 +46,7 @@ #include <QtGui/qcolor.h> #include <QtCore/qnamespace.h> #include <QtCore/qstring.h> // char*->QString conversion +#include <QtCore/qsharedpointer.h> #include <QtGui/qimage.h> #include <QtGui/qtransform.h> @@ -227,7 +228,7 @@ public: #endif private: - QPixmapData *data; + QExplicitlySharedDataPointer<QPixmapData> data; bool doImageIO(QImageWriter *io, int quality) const; @@ -272,7 +273,7 @@ public: QPixmapData* pixmapData() const; public: - typedef QPixmapData * DataPtr; + typedef QExplicitlySharedDataPointer<QPixmapData> DataPtr; inline DataPtr &data_ptr() { return data; } }; diff --git a/src/gui/image/qpixmap_mac.cpp b/src/gui/image/qpixmap_mac.cpp index 5959da1..4aa74f7 100644 --- a/src/gui/image/qpixmap_mac.cpp +++ b/src/gui/image/qpixmap_mac.cpp @@ -73,12 +73,12 @@ static int qt_pixmap_serial = 0; Q_GUI_EXPORT quint32 *qt_mac_pixmap_get_base(const QPixmap *pix) { - return static_cast<QMacPixmapData*>(pix->data)->pixels; + return static_cast<QMacPixmapData*>(pix->data.data())->pixels; } Q_GUI_EXPORT int qt_mac_pixmap_get_bytes_per_line(const QPixmap *pix) { - return static_cast<QMacPixmapData*>(pix->data)->bytesPerRow; + return static_cast<QMacPixmapData*>(pix->data.data())->bytesPerRow; } void qt_mac_cgimage_data_free(void *info, const void *memoryToFree, size_t) @@ -421,7 +421,7 @@ QPixmap QMacPixmapData::alphaChannel() const void QMacPixmapData::setAlphaChannel(const QPixmap &alpha) { has_mask = true; - QMacPixmapData *alphaData = static_cast<QMacPixmapData*>(alpha.data); + QMacPixmapData *alphaData = static_cast<QMacPixmapData*>(alpha.data.data()); macSetAlphaChannel(alphaData, false); } @@ -449,7 +449,7 @@ void QMacPixmapData::setMask(const QBitmap &mask) has_alpha = false; has_mask = true; - QMacPixmapData *maskData = static_cast<QMacPixmapData*>(mask.data); + QMacPixmapData *maskData = static_cast<QMacPixmapData*>(mask.data.data()); macSetAlphaChannel(maskData, true); } @@ -961,14 +961,14 @@ Qt::HANDLE QPixmap::macQDAlphaHandle() const Qt::HANDLE QPixmap::macCGHandle() const { if (data->classId() == QPixmapData::MacClass) { - QMacPixmapData *d = static_cast<QMacPixmapData *>(data); + QMacPixmapData *d = static_cast<QMacPixmapData *>(data.data()); if (!d->cg_data) d->macCreateCGImageRef(); CGImageRef ret = d->cg_data; CGImageRetain(ret); return ret; } else if (data->classId() == QPixmapData::RasterClass) { - return qt_mac_image_to_cgimage(static_cast<QRasterPixmapData *>(data)->image); + return qt_mac_image_to_cgimage(static_cast<QRasterPixmapData *>(data.data())->image); } return 0; } @@ -980,7 +980,7 @@ bool QMacPixmapData::hasAlphaChannel() const CGImageRef qt_mac_create_imagemask(const QPixmap &pixmap, const QRectF &sr) { - QMacPixmapData *px = static_cast<QMacPixmapData*>(pixmap.data); + QMacPixmapData *px = static_cast<QMacPixmapData*>(pixmap.data.data()); if (px->cg_mask) { if (px->cg_mask_rect == sr) { CGImageRetain(px->cg_mask); //reference for the caller diff --git a/src/gui/image/qpixmap_qws.cpp b/src/gui/image/qpixmap_qws.cpp index b106610..e549900 100644 --- a/src/gui/image/qpixmap_qws.cpp +++ b/src/gui/image/qpixmap_qws.cpp @@ -112,7 +112,7 @@ QPixmap QPixmap::grabWindow(WId window, int x, int y, int w, int h) QRgb* QPixmap::clut() const { if (data->classId() == QPixmapData::RasterClass) { - const QRasterPixmapData *d = static_cast<const QRasterPixmapData*>(data); + const QRasterPixmapData *d = static_cast<const QRasterPixmapData*>(data.data()); return d->image.colorTable().data(); } @@ -122,7 +122,7 @@ QRgb* QPixmap::clut() const int QPixmap::numCols() const { if (data->classId() == QPixmapData::RasterClass) { - const QRasterPixmapData *d = static_cast<const QRasterPixmapData*>(data); + const QRasterPixmapData *d = static_cast<const QRasterPixmapData*>(data.data()); return d->image.numColors(); } @@ -132,7 +132,7 @@ int QPixmap::numCols() const const uchar* QPixmap::qwsBits() const { if (data->classId() == QPixmapData::RasterClass) { - const QRasterPixmapData *d = static_cast<const QRasterPixmapData*>(data); + const QRasterPixmapData *d = static_cast<const QRasterPixmapData*>(data.data()); return d->image.bits(); } @@ -142,7 +142,7 @@ const uchar* QPixmap::qwsBits() const int QPixmap::qwsBytesPerLine() const { if (data->classId() == QPixmapData::RasterClass) { - const QRasterPixmapData *d = static_cast<const QRasterPixmapData*>(data); + const QRasterPixmapData *d = static_cast<const QRasterPixmapData*>(data.data()); return d->image.bytesPerLine(); } diff --git a/src/gui/image/qpixmap_win.cpp b/src/gui/image/qpixmap_win.cpp index b0f17ba..f12088d 100644 --- a/src/gui/image/qpixmap_win.cpp +++ b/src/gui/image/qpixmap_win.cpp @@ -123,7 +123,7 @@ HBITMAP QPixmap::toWinHBITMAP(HBitmapFormat format) const { HBITMAP bitmap = 0; if (data->classId() == QPixmapData::RasterClass) { - QRasterPixmapData* d = static_cast<QRasterPixmapData*>(data); + QRasterPixmapData* d = static_cast<QRasterPixmapData*>(data.data()); int w = d->image.width(); int h = d->image.height(); diff --git a/src/gui/image/qpixmap_x11.cpp b/src/gui/image/qpixmap_x11.cpp index 047bd33..a75cadc 100644 --- a/src/gui/image/qpixmap_x11.cpp +++ b/src/gui/image/qpixmap_x11.cpp @@ -1875,7 +1875,7 @@ QPixmap QX11PixmapData::transformed(const QTransform &transform, return bm; } else { // color pixmap QPixmap pm; - QX11PixmapData *x11Data = static_cast<QX11PixmapData*>(pm.data); + QX11PixmapData *x11Data = static_cast<QX11PixmapData*>(pm.data.data()); x11Data->flags &= ~QX11PixmapData::Uninitialized; x11Data->xinfo = xinfo; x11Data->d = d; @@ -1940,7 +1940,7 @@ void QPixmap::x11SetScreen(int screen) if (screen < 0) screen = QX11Info::appScreen(); - QX11PixmapData *x11Data = static_cast<QX11PixmapData*>(data); + QX11PixmapData *x11Data = static_cast<QX11PixmapData*>(data.data()); if (screen == x11Data->xinfo.screen()) return; // nothing to do @@ -2037,7 +2037,7 @@ bool QX11PixmapData::hasAlphaChannel() const const QX11Info &QPixmap::x11Info() const { if (data->classId() == QPixmapData::X11Class) - return static_cast<QX11PixmapData*>(data)->xinfo; + return static_cast<QX11PixmapData*>(data.data())->xinfo; else { static QX11Info nullX11Info; return nullX11Info; @@ -2094,7 +2094,7 @@ Qt::HANDLE QPixmap::x11PictureHandle() const { #ifndef QT_NO_XRENDER if (data->classId() == QPixmapData::X11Class) - return static_cast<QX11PixmapData*>(data)->picture; + return static_cast<const QX11PixmapData*>(data.data())->picture; else return 0; #else diff --git a/src/gui/image/qpixmapcache_p.h b/src/gui/image/qpixmapcache_p.h index cbfc3e8..511c852 100644 --- a/src/gui/image/qpixmapcache_p.h +++ b/src/gui/image/qpixmapcache_p.h @@ -82,7 +82,7 @@ public: QDetachedPixmap(const QPixmap &pix) : QPixmap(pix) { if (data && data->classId() == QPixmapData::RasterClass) { - QRasterPixmapData *d = static_cast<QRasterPixmapData*>(data); + QRasterPixmapData *d = static_cast<QRasterPixmapData*>(data.data()); if (!d->image.isNull() && d->image.d->paintEngine && !d->image.d->paintEngine->isActive()) { diff --git a/src/gui/image/qpixmapdata_p.h b/src/gui/image/qpixmapdata_p.h index 1c3e422..70890c0 100644 --- a/src/gui/image/qpixmapdata_p.h +++ b/src/gui/image/qpixmapdata_p.h @@ -117,6 +117,7 @@ private: friend class QGLContextPrivate; friend class QX11PixmapData; friend class QGLTextureCache; //Needs to check the reference count + friend class QExplicitlySharedDataPointer<QPixmapData>; QAtomicInt ref; int detach_no; diff --git a/src/gui/itemviews/qabstractitemview.cpp b/src/gui/itemviews/qabstractitemview.cpp index 8f8eae5..0671304 100644 --- a/src/gui/itemviews/qabstractitemview.cpp +++ b/src/gui/itemviews/qabstractitemview.cpp @@ -3382,11 +3382,7 @@ QPoint QAbstractItemView::dirtyRegionOffset() const */ void QAbstractItemView::startAutoScroll() { - Q_D(QAbstractItemView); - // ### it would be nice to make this into a style hint one day - int scrollInterval = (verticalScrollMode() == QAbstractItemView::ScrollPerItem) ? 150 : 50; - d->autoScrollTimer.start(scrollInterval, this); - d->autoScrollCount = 0; + d_func()->startAutoScroll(); } /*! @@ -3394,9 +3390,7 @@ void QAbstractItemView::startAutoScroll() */ void QAbstractItemView::stopAutoScroll() { - Q_D(QAbstractItemView); - d->autoScrollTimer.stop(); - d->autoScrollCount = 0; + d_func()->stopAutoScroll(); } /*! diff --git a/src/gui/itemviews/qabstractitemview_p.h b/src/gui/itemviews/qabstractitemview_p.h index 0d55a2e..07edd4a 100644 --- a/src/gui/itemviews/qabstractitemview_p.h +++ b/src/gui/itemviews/qabstractitemview_p.h @@ -126,6 +126,15 @@ public: void doDelayedItemsLayout(int delay = 0); void interruptDelayedItemsLayout() const; + void startAutoScroll() + { // ### it would be nice to make this into a style hint one day + int scrollInterval = (verticalScrollMode == QAbstractItemView::ScrollPerItem) ? 150 : 50; + autoScrollTimer.start(scrollInterval, q_func()); + autoScrollCount = 0; + } + void stopAutoScroll() { autoScrollTimer.stop(); autoScrollCount = 0;} + + bool dropOn(QDropEvent *event, int *row, int *col, QModelIndex *index); bool droppingOnItself(QDropEvent *event, const QModelIndex &index); diff --git a/src/gui/itemviews/qlistview.cpp b/src/gui/itemviews/qlistview.cpp index e9e365f..3e9db3b 100644 --- a/src/gui/itemviews/qlistview.cpp +++ b/src/gui/itemviews/qlistview.cpp @@ -1,4 +1,4 @@ -/*************************************************************************** +/**************************************************************************** ** ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). ** Contact: Nokia Corporation (qt-info@nokia.com) @@ -458,15 +458,13 @@ QSize QListView::gridSize() const void QListView::setViewMode(ViewMode mode) { Q_D(QListView); - if (d->viewMode == mode) + if (d->commonListView && d->viewMode == mode) return; d->viewMode = mode; + delete d->commonListView; if (mode == ListMode) { - delete d->dynamicListView; - d->dynamicListView = 0; - if (!d->staticListView) - d->staticListView = new QStaticListViewBase(this, d); + d->commonListView = new QListModeViewBase(this, d); if (!(d->modeProperties & QListViewPrivate::Wrap)) d->setWrapping(false); if (!(d->modeProperties & QListViewPrivate::Spacing)) @@ -482,10 +480,7 @@ void QListView::setViewMode(ViewMode mode) if (!(d->modeProperties & QListViewPrivate::SelectionRectVisible)) d->showElasticBand = false; } else { - delete d->staticListView; - d->staticListView = 0; - if (!d->dynamicListView) - d->dynamicListView = new QDynamicListViewBase(this, d); + d->commonListView = new QIconModeViewBase(this, d); if (!(d->modeProperties & QListViewPrivate::Wrap)) d->setWrapping(true); if (!(d->modeProperties & QListViewPrivate::Spacing)) @@ -549,24 +544,12 @@ void QListView::setRowHidden(int row, bool hide) { Q_D(QListView); const bool hidden = d->isHidden(row); - if (d->viewMode == ListMode) { - if (hide && !hidden) - d->hiddenRows.append(d->model->index(row, 0)); - else if (!hide && hidden) - d->hiddenRows.remove(d->hiddenRows.indexOf(d->model->index(row, 0))); - d->doDelayedItemsLayout(); - } else { - if (hide && !hidden) { - d->dynamicListView->removeItem(row); - d->hiddenRows.append(d->model->index(row, 0)); - } else if (!hide && hidden) { - d->hiddenRows.remove(d->hiddenRows.indexOf(d->model->index(row, 0))); - d->dynamicListView->insertItem(row); - } - if (d->resizeMode == Adjust) - d->doDelayedItemsLayout(); - d->viewport->update(); - } + if (hide && !hidden) + d->commonListView->appendHiddenRow(row); + else if (!hide && hidden) + d->commonListView->removeHiddenRow(row); + d->doDelayedItemsLayout(); + d->viewport->update(); } /*! @@ -575,7 +558,7 @@ void QListView::setRowHidden(int row, bool hide) QRect QListView::visualRect(const QModelIndex &index) const { Q_D(const QListView); - return d->mapToViewport(rectForIndex(index), d->viewMode == QListView::ListMode); + return d->mapToViewport(rectForIndex(index)); } /*! @@ -612,69 +595,17 @@ int QListViewPrivate::horizontalScrollToValue(const QModelIndex &index, const QR const bool rightOf = q->isRightToLeft() ? rect.right() > area.right() : (rect.right() > area.right()) && (rect.left() > area.left()); - int horizontalValue = hbar->value(); - - // ScrollPerItem - if (q->horizontalScrollMode() == QAbstractItemView::ScrollPerItem && viewMode == QListView::ListMode) { - const QListViewItem item = indexToListViewItem(index); - const QRect rect = q->visualRect(index); - horizontalValue = staticListView->horizontalPerItemValue(itemIndex(item), - horizontalValue, area.width(), - leftOf, rightOf, isWrapping(), hint, rect.width()); - } else { // ScrollPerPixel - if (q->isRightToLeft()) { - if (hint == QListView::PositionAtCenter) { - horizontalValue += ((area.width() - rect.width()) / 2) - rect.left(); - } else { - if (leftOf) - horizontalValue -= rect.left(); - else if (rightOf) - horizontalValue += qMin(rect.left(), area.width() - rect.right()); - } - } else { - if (hint == QListView::PositionAtCenter) { - horizontalValue += rect.left() - ((area.width()- rect.width()) / 2); - } else { - if (leftOf) - horizontalValue += rect.left(); - else if (rightOf) - horizontalValue += qMin(rect.left(), rect.right() - area.width()); - } - } - } - return horizontalValue; + return commonListView->horizontalScrollToValue(q->visualIndex(index), hint, leftOf, rightOf, area, rect); } int QListViewPrivate::verticalScrollToValue(const QModelIndex &index, const QRect &rect, QListView::ScrollHint hint) const { Q_Q(const QListView); - const QRect area = viewport->rect(); const bool above = (hint == QListView::EnsureVisible && rect.top() < area.top()); const bool below = (hint == QListView::EnsureVisible && rect.bottom() > area.bottom()); - - int verticalValue = vbar->value(); - - // ScrollPerItem - if (verticalScrollMode == QAbstractItemView::ScrollPerItem && viewMode == QListView::ListMode) { - const QListViewItem item = indexToListViewItem(index); - const QRect rect = q->visualRect(index); - verticalValue = staticListView->verticalPerItemValue(itemIndex(item), - verticalValue, area.height(), - above, below, isWrapping(), hint, rect.height()); - - } else { // ScrollPerPixel - QRect adjusted = rect.adjusted(-spacing(), -spacing(), spacing(), spacing()); - if (hint == QListView::PositionAtTop || above) - verticalValue += adjusted.top(); - else if (hint == QListView::PositionAtBottom || below) - verticalValue += qMin(adjusted.top(), adjusted.bottom() - area.height() + 1); - else if (hint == QListView::PositionAtCenter) - verticalValue += adjusted.top() - ((area.height() - adjusted.height()) / 2); - } - - return verticalValue; + return commonListView->verticalScrollToValue(q->visualIndex(index), hint, above, below, area, rect); } void QListViewPrivate::selectAll(QItemSelectionModel::SelectionFlags command) @@ -766,23 +697,12 @@ void QListView::setRootIndex(const QModelIndex &index) Scroll the view contents by \a dx and \a dy. */ + void QListView::scrollContentsBy(int dx, int dy) { Q_D(QListView); - d->delayedAutoScroll.stop(); // auto scroll was canceled by the user scrolling - - if (d->viewMode == ListMode) - d->staticListView->scrollContentsBy(dx, dy); - else if (state() == DragSelectingState) - d->scrollElasticBandBy(isRightToLeft() ? -dx : dx, dy); - - d->scrollContentsBy(isRightToLeft() ? -dx : dx, dy); - - // update the dragged items - if (d->viewMode == IconMode) // ### move to dynamic class - if (!d->dynamicListView->draggedItems.isEmpty()) - d->viewport->update(d->dynamicListView->draggedItemsRect().translated(dx, dy)); + d->commonListView->scrollContentsBy(dx, dy, d->state == QListView::DragSelectingState); } /*! @@ -811,9 +731,7 @@ QSize QListView::contentsSize() const */ void QListView::dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight) { - Q_D(QListView); - if (d->viewMode == IconMode) - d->dynamicListView->dataChanged(topLeft, bottomRight); + d_func()->commonListView->dataChanged(topLeft, bottomRight); QAbstractItemView::dataChanged(topLeft, bottomRight); } @@ -864,7 +782,7 @@ void QListView::mouseMoveEvent(QMouseEvent *e) && d->selectionMode != NoSelection) { QRect rect(d->pressedPosition, e->pos() + QPoint(horizontalOffset(), verticalOffset())); rect = rect.normalized(); - d->viewport->update(d->mapToViewport(rect.united(d->elasticBand), d->viewMode == QListView::ListMode)); + d->viewport->update(d->mapToViewport(rect.united(d->elasticBand))); d->elasticBand = rect; } } @@ -878,7 +796,7 @@ void QListView::mouseReleaseEvent(QMouseEvent *e) QAbstractItemView::mouseReleaseEvent(e); // #### move this implementation into a dynamic class if (d->showElasticBand && d->elasticBand.isValid()) { - d->viewport->update(d->mapToViewport(d->elasticBand, d->viewMode == QListView::ListMode)); + d->viewport->update(d->mapToViewport(d->elasticBand)); d->elasticBand = QRect(); } } @@ -935,69 +853,27 @@ void QListView::resizeEvent(QResizeEvent *e) */ void QListView::dragMoveEvent(QDragMoveEvent *e) { - // ### move implementation to dynamic - Q_D(QListView); - if (e->source() == this && d->viewMode == IconMode) { - // the ignore by default - e->ignore(); - if (d->canDecode(e)) { - // get old dragged items rect - QRect itemsRect = d->dynamicListView->itemsRect(d->dynamicListView->draggedItems); - d->viewport->update(itemsRect.translated(d->dynamicListView->draggedItemsDelta())); - // update position - d->dynamicListView->draggedItemsPos = e->pos(); - // get new items rect - d->viewport->update(itemsRect.translated(d->dynamicListView->draggedItemsDelta())); - // set the item under the cursor to current - QModelIndex index; - if (d->movement == Snap) { - QRect rect(d->dynamicListView->snapToGrid(e->pos() + d->offset()), d->gridSize()); - const QVector<QModelIndex> intersectVector = d->intersectingSet(rect); - index = intersectVector.count() > 0 - ? intersectVector.last() : QModelIndex(); - } else { - index = indexAt(e->pos()); - } - // check if we allow drops here - if (e->source() == this && d->dynamicListView->draggedItems.contains(index)) - e->accept(); // allow changing item position - else if (d->model->flags(index) & Qt::ItemIsDropEnabled) - e->accept(); // allow dropping on dropenabled items - else if (!index.isValid()) - e->accept(); // allow dropping in empty areas - } - // do autoscrolling - if (d->shouldAutoScroll(e->pos())) - startAutoScroll(); - } else { // not internal + if (!d_func()->commonListView->filterDragMoveEvent(e)) QAbstractItemView::dragMoveEvent(e); - } } + /*! \reimp */ void QListView::dragLeaveEvent(QDragLeaveEvent *e) { - // ### move implementation to dynamic - Q_D(QListView); - if (d->viewMode == IconMode) { - d->viewport->update(d->dynamicListView->draggedItemsRect()); // erase the area - d->dynamicListView->draggedItemsPos = QPoint(-1, -1); // don't draw the dragged items - } - QAbstractItemView::dragLeaveEvent(e); + if (!d_func()->commonListView->filterDragLeaveEvent(e)) + QAbstractItemView::dragLeaveEvent(e); } /*! \reimp */ -void QListView::dropEvent(QDropEvent *event) +void QListView::dropEvent(QDropEvent *e) { - Q_D(QListView); - if (event->source() == this && d->viewMode == IconMode) - internalDrop(event); // ### move to dynamic - else - QAbstractItemView::dropEvent(event); + if (!d_func()->commonListView->filterDropEvent(e)) + QAbstractItemView::dropEvent(e); } /*! @@ -1005,10 +881,7 @@ void QListView::dropEvent(QDropEvent *event) */ void QListView::startDrag(Qt::DropActions supportedActions) { - Q_D(QListView); - if (d->viewMode == IconMode) // ### move to dynamic - internalDrag(supportedActions); - else + if (!d_func()->commonListView->filterStartDrag(supportedActions)) QAbstractItemView::startDrag(supportedActions); } @@ -1020,41 +893,8 @@ void QListView::startDrag(Qt::DropActions supportedActions) */ void QListView::internalDrop(QDropEvent *event) { - Q_D(QListView); - if (d->viewMode == QListView::ListMode) - return; - - // ### move to dynamic class - QPoint offset(horizontalOffset(), verticalOffset()); - QPoint end = event->pos() + offset; - QPoint start = d->pressedPosition; - QPoint delta = (d->movement == Snap ? - d->dynamicListView->snapToGrid(end) - - d->dynamicListView->snapToGrid(start) : end - start); - QSize contents = d->contentsSize(); - QList<QModelIndex> indexes = d->selectionModel->selectedIndexes(); - for (int i = 0; i < indexes.count(); ++i) { - QModelIndex index = indexes.at(i); - QRect rect = rectForIndex(index); - d->viewport->update(d->mapToViewport(rect, d->viewMode == QListView::ListMode)); - QPoint dest = rect.topLeft() + delta; - if (isRightToLeft()) - dest.setX(d->flipX(dest.x()) - rect.width()); - d->dynamicListView->moveItem(index.row(), dest); - update(index); - } - stopAutoScroll(); - d->dynamicListView->draggedItems.clear(); - emit indexesMoved(indexes); - event->accept(); // we have handled the event - // if the size has not grown, we need to check if it has shrinked - if (d->dynamicListView - && (d->contentsSize().width() <= contents.width() - || d->contentsSize().height() <= contents.height())) { - d->dynamicListView->updateContentsSize(); - } - if (d->contentsSize() != contents) - updateGeometries(); + // ### Qt5: remove that function + Q_UNUSED(event); } /*! @@ -1065,31 +905,8 @@ void QListView::internalDrop(QDropEvent *event) */ void QListView::internalDrag(Qt::DropActions supportedActions) { - Q_D(QListView); - if (d->viewMode == QListView::ListMode) - return; - - // #### move to dynamic class - - // This function does the same thing as in QAbstractItemView::startDrag(), - // plus adding viewitems to the draggedItems list. - // We need these items to draw the drag items - QModelIndexList indexes = d->selectionModel->selectedIndexes(); - if (indexes.count() > 0 ) { - if (d->viewport->acceptDrops()) { - QModelIndexList::ConstIterator it = indexes.constBegin(); - for (; it != indexes.constEnd(); ++it) - if (d->model->flags(*it) & Qt::ItemIsDragEnabled - && (*it).column() == d->column) - d->dynamicListView->draggedItems.push_back(*it); - } - QDrag *drag = new QDrag(this); - drag->setMimeData(d->model->mimeData(indexes)); - Qt::DropAction action = drag->exec(supportedActions, Qt::CopyAction); - d->dynamicListView->draggedItems.clear(); - if (action == Qt::MoveAction) - d->clearOrRemove(); - } + // ### Qt5: remove that function + Q_UNUSED(supportedActions); } #endif // QT_NO_DRAGANDDROP @@ -1117,6 +934,7 @@ QStyleOptionViewItem QListView::viewOptions() const return option; } + /*! \reimp */ @@ -1213,18 +1031,7 @@ void QListView::paintEvent(QPaintEvent *e) } #ifndef QT_NO_DRAGANDDROP - // #### move this implementation into a dynamic class - if (d->viewMode == IconMode) - if (!d->dynamicListView->draggedItems.isEmpty() - && d->viewport->rect().contains(d->dynamicListView->draggedItemsPos)) { - QPoint delta = d->dynamicListView->draggedItemsDelta(); - painter.translate(delta.x(), delta.y()); - d->dynamicListView->drawItems(&painter, d->dynamicListView->draggedItems); - } - // FIXME: Until the we can provide a proper drop indicator - // in IconMode, it makes no sense to show it - if (d->viewMode == ListMode) - d->paintDropIndicator(&painter); + d->commonListView->paintDragDrop(&painter); #endif #ifndef QT_NO_RUBBERBAND @@ -1263,31 +1070,7 @@ QModelIndex QListView::indexAt(const QPoint &p) const */ int QListView::horizontalOffset() const { - Q_D(const QListView); - // ### split into static and dynamic - if (horizontalScrollMode() == QAbstractItemView::ScrollPerItem && d->viewMode == ListMode) { - if (d->isWrapping()) { - if (d->flow == TopToBottom && !d->staticListView->segmentPositions.isEmpty()) { - const int max = d->staticListView->segmentPositions.count() - 1; - int currentValue = qBound(0, horizontalScrollBar()->value(), max); - int position = d->staticListView->segmentPositions.at(currentValue); - int maximumValue = qBound(0, horizontalScrollBar()->maximum(), max); - int maximum = d->staticListView->segmentPositions.at(maximumValue); - return (isRightToLeft() ? maximum - position : position); - } - //return 0; - } else { - if (d->flow == LeftToRight && !d->staticListView->flowPositions.isEmpty()) { - int position = d->staticListView->flowPositions.at(horizontalScrollBar()->value()); - int maximum = d->staticListView->flowPositions.at(horizontalScrollBar()->maximum()); - return (isRightToLeft() ? maximum - position : position); - } - //return 0; - } - } - return (isRightToLeft() - ? horizontalScrollBar()->maximum() - horizontalScrollBar()->value() - : horizontalScrollBar()->value()); + return d_func()->commonListView->horizontalOffset(); } /*! @@ -1295,30 +1078,7 @@ int QListView::horizontalOffset() const */ int QListView::verticalOffset() const { - // ## split into static and dynamic - Q_D(const QListView); - if (verticalScrollMode() == QAbstractItemView::ScrollPerItem && d->viewMode == ListMode) { - if (d->isWrapping()) { - if (d->flow == LeftToRight && !d->staticListView->segmentPositions.isEmpty()) { - int value = verticalScrollBar()->value(); - if (value >= d->staticListView->segmentPositions.count()) { - //qWarning("QListView: Vertical scroll bar is out of bounds"); - return 0; - } - return d->staticListView->segmentPositions.at(value); - } - } else { - if (d->flow == TopToBottom && !d->staticListView->flowPositions.isEmpty()) { - int value = verticalScrollBar()->value(); - if (value > d->staticListView->flowPositions.count()) { - //qWarning("QListView: Vertical scroll bar is out of bounds"); - return 0; - } - return d->staticListView->flowPositions.at(value) - d->spacing(); - } - } - } - return verticalScrollBar()->value(); + return d_func()->commonListView->verticalOffset(); } /*! @@ -1444,15 +1204,7 @@ QModelIndex QListView::moveCursor(CursorAction cursorAction, Qt::KeyboardModifie */ QRect QListView::rectForIndex(const QModelIndex &index) const { - Q_D(const QListView); - if (!d->isIndexValid(index) - || index.parent() != d->root - || index.column() != d->column - || isIndexHidden(index)) - return QRect(); - d->executePostedLayout(); - QListViewItem item = d->indexToListViewItem(index); - return d->viewItemRect(item); + return d_func()->rectForIndex(index); } /*! @@ -1460,8 +1212,8 @@ QRect QListView::rectForIndex(const QModelIndex &index) const Sets the contents position of the item at \a index in the model to the given \a position. - If the list view's movement mode is Static, this function will have no - effect. + If the list view's movement mode is Static or its view mode is ListView, + this function will have no effect. */ void QListView::setPositionForIndex(const QPoint &position, const QModelIndex &index) { @@ -1473,15 +1225,7 @@ void QListView::setPositionForIndex(const QPoint &position, const QModelIndex &i return; d->executePostedLayout(); - if (index.row() >= d->dynamicListView->items.count()) - return; - const QSize oldContents = d->contentsSize(); - update(index); // update old position - d->dynamicListView->moveItem(index.row(), position); - update(index); // update new position - - if (d->contentsSize() != oldContents) - updateGeometries(); // update the scroll bars + d->commonListView->setPositionForIndex(position, index); } /*! @@ -1717,99 +1461,8 @@ void QListView::updateGeometries() QModelIndex index = d->model->index(0, d->column, d->root); QStyleOptionViewItemV4 option = d->viewOptionsV4(); QSize step = d->itemSize(option, index); - - QSize csize = d->contentsSize(); - QSize vsize = d->viewport->size(); - QSize max = maximumViewportSize(); - if (max.width() >= d->contentsSize().width() && max.height() >= d->contentsSize().height()) - vsize = max; - - // ### reorder the logic - - // ### split into static and dynamic - - const bool vertical = verticalScrollMode() == QAbstractItemView::ScrollPerItem; - const bool horizontal = horizontalScrollMode() == QAbstractItemView::ScrollPerItem; - - if (d->flow == TopToBottom) { - if (horizontal && d->isWrapping() && d->viewMode == ListMode) { - const QVector<int> segmentPositions = d->staticListView->segmentPositions; - const int steps = segmentPositions.count() - 1; - if (steps > 0) { - int pageSteps = d->staticListView->perItemScrollingPageSteps(vsize.width(), - csize.width(), - isWrapping()); - horizontalScrollBar()->setSingleStep(1); - horizontalScrollBar()->setPageStep(pageSteps); - horizontalScrollBar()->setRange(0, steps - pageSteps); - } else { - horizontalScrollBar()->setRange(0, 0); - } - } else { - horizontalScrollBar()->setSingleStep(step.width() + d->spacing()); - horizontalScrollBar()->setPageStep(vsize.width()); - horizontalScrollBar()->setRange(0, d->contentsSize().width() - vsize.width()); - } - if (vertical && !d->isWrapping() && d->viewMode == ListMode) { - const QVector<int> flowPositions = d->staticListView->flowPositions; - const int steps = flowPositions.count() - 1; - if (steps > 0) { - int pageSteps = d->staticListView->perItemScrollingPageSteps(vsize.height(), - csize.height(), - isWrapping()); - verticalScrollBar()->setSingleStep(1); - verticalScrollBar()->setPageStep(pageSteps); - verticalScrollBar()->setRange(0, steps - pageSteps); - } else { - verticalScrollBar()->setRange(0, 0); - } - // } else if (vertical && d->isWrapping() && d->movement == Static) { - // ### wrapped scrolling in flow direction - } else { - verticalScrollBar()->setSingleStep(step.height() + d->spacing()); - verticalScrollBar()->setPageStep(vsize.height()); - verticalScrollBar()->setRange(0, d->contentsSize().height() - vsize.height()); - } - } else { // LeftToRight - if (horizontal && !d->isWrapping() && d->viewMode == ListMode) { - const QVector<int> flowPositions = d->staticListView->flowPositions; - int steps = flowPositions.count() - 1; - if (steps > 0) { - int pageSteps = d->staticListView->perItemScrollingPageSteps(vsize.width(), - csize.width(), - isWrapping()); - horizontalScrollBar()->setSingleStep(1); - horizontalScrollBar()->setPageStep(pageSteps); - horizontalScrollBar()->setRange(0, steps - pageSteps); - } else { - horizontalScrollBar()->setRange(0, 0); - } - // } else if (horizontal && d->isWrapping() && d->movement == Static) { - // ### wrapped scrolling in flow direction - } else { - horizontalScrollBar()->setSingleStep(step.width() + d->spacing()); - horizontalScrollBar()->setPageStep(vsize.width()); - horizontalScrollBar()->setRange(0, d->contentsSize().width() - vsize.width()); - } - if (vertical && d->isWrapping() && d->viewMode == ListMode) { - const QVector<int> segmentPositions = d->staticListView->segmentPositions; - int steps = segmentPositions.count() - 1; - if (steps > 0) { - int pageSteps = d->staticListView->perItemScrollingPageSteps(vsize.height(), - csize.height(), - isWrapping()); - verticalScrollBar()->setSingleStep(1); - verticalScrollBar()->setPageStep(pageSteps); - verticalScrollBar()->setRange(0, steps - pageSteps); - } else { - verticalScrollBar()->setRange(0, 0); - } - } else { - verticalScrollBar()->setSingleStep(step.height() + d->spacing()); - verticalScrollBar()->setPageStep(vsize.height()); - verticalScrollBar()->setRange(0, d->contentsSize().height() - vsize.height()); - } - } + d->commonListView->updateHorizontalScrollBar(step); + d->commonListView->updateVerticalScrollBar(step); } QAbstractItemView::updateGeometries(); @@ -1958,14 +1611,14 @@ bool QListView::event(QEvent *e) QListViewPrivate::QListViewPrivate() : QAbstractItemViewPrivate(), - dynamicListView(0), + commonListView(0), wrap(false), space(0), flow(QListView::TopToBottom), movement(QListView::Static), resizeMode(QListView::Fixed), layoutMode(QListView::SinglePass), - viewMode(QListView::IconMode), //this will ensure the first initialization to ListView + viewMode(QListView::ListMode), modeProperties(0), column(0), uniformItemSizes(false), @@ -1976,21 +1629,14 @@ QListViewPrivate::QListViewPrivate() QListViewPrivate::~QListViewPrivate() { - if (viewMode == QListView::ListMode) - delete staticListView; - else - delete dynamicListView; + delete commonListView; } void QListViewPrivate::clear() { - // ### split into dynamic and static // initialization of data structs cachedItemSize = QSize(); - if (viewMode == QListView::ListMode) - staticListView->clear(); - else - dynamicListView->clear(); + commonListView->clear(); } void QListViewPrivate::prepareItemsLayout() @@ -1999,7 +1645,7 @@ void QListViewPrivate::prepareItemsLayout() clear(); //take the size as if there were scrollbar in order to prevent scrollbar to blink - layoutBounds = QRect(QPoint(0,0), q->maximumViewportSize()); + layoutBounds = QRect(QPoint(), q->maximumViewportSize()); int frameAroundContents = 0; if (q->style()->styleHint(QStyle::SH_ScrollView_FrameOnlyAroundContents)) @@ -2017,15 +1663,8 @@ void QListViewPrivate::prepareItemsLayout() layoutBounds.adjust(0, 0, -verticalMargin, -horizontalMargin); - int rowCount = model->rowCount(root); - int colCount = model->columnCount(root); - if (colCount <= 0) - rowCount = 0; // no contents - if (viewMode == QListView::ListMode) { - staticListView->flowPositions.resize(rowCount); - } else { - dynamicListView->tree.create(qMax(rowCount - hiddenRows.count(), 0)); - } + int rowCount = model->columnCount(root) <= 0 ? 0 : model->rowCount(root); + commonListView->setRowCount(rowCount); } /*! @@ -2033,7 +1672,6 @@ void QListViewPrivate::prepareItemsLayout() */ bool QListViewPrivate::doItemsLayout(int delta) { - // ### split into static and dynamic int max = model->rowCount(root) - 1; int first = batchStartRow(); int last = qMin(first + delta - 1, max); @@ -2057,9 +1695,7 @@ bool QListViewPrivate::doItemsLayout(int delta) info.flow = flow; info.max = max; - if (viewMode == QListView::ListMode) - return staticListView->doBatchedItemLayout(info, max); - return dynamicListView->doBatchedItemLayout(info, max); + return commonListView->doBatchedItemLayout(info, max); } QListViewItem QListViewPrivate::indexToListViewItem(const QModelIndex &index) const @@ -2067,29 +1703,16 @@ QListViewItem QListViewPrivate::indexToListViewItem(const QModelIndex &index) co if (!index.isValid() || isHidden(index.row())) return QListViewItem(); - if (viewMode == QListView::ListMode) - return staticListView->indexToListViewItem(index); - return dynamicListView->indexToListViewItem(index); + return commonListView->indexToListViewItem(index); } - -int QListViewPrivate::itemIndex(const QListViewItem &item) const -{ - if (viewMode == QListView::ListMode) - return staticListView->itemIndex(item); - return dynamicListView->itemIndex(item); -} - -QRect QListViewPrivate::mapToViewport(const QRect &rect, bool greedy) const +QRect QListViewPrivate::mapToViewport(const QRect &rect, bool extend) const { Q_Q(const QListView); if (!rect.isValid()) return rect; - QRect result = rect; - if (greedy) - result = staticListView->mapToViewport(rect); - + QRect result = extend ? commonListView->mapToViewport(rect) : rect; int dx = -q->horizontalOffset(); int dy = -q->verticalOffset(); result.adjust(dx, dy, dx, dy); @@ -2177,46 +1800,217 @@ QItemSelection QListViewPrivate::selection(const QRect &rect) const } /* - * Static ListView Implementation + * Common ListView Implementation */ -int QStaticListViewBase::verticalPerItemValue(int itemIndex, int verticalValue, int areaHeight, - bool above, bool below, bool wrap, - QListView::ScrollHint hint, int itemHeight) const +void QCommonListViewBase::appendHiddenRow(int row) +{ + dd->hiddenRows.append(dd->model->index(row, 0)); +} + +void QCommonListViewBase::removeHiddenRow(int row) { - int value = qBound(0, verticalValue, flowPositions.count() - 1); - if (above) - return perItemScrollToValue(itemIndex, value, areaHeight, QListView::PositionAtTop, - Qt::Vertical,wrap, itemHeight); - else if (below) - return perItemScrollToValue(itemIndex, value, areaHeight, QListView::PositionAtBottom, - Qt::Vertical, wrap, itemHeight); - else if (hint != QListView::EnsureVisible) - return perItemScrollToValue(itemIndex, value, areaHeight, hint, Qt::Vertical, wrap, itemHeight); - return value; + dd->hiddenRows.remove(dd->hiddenRows.indexOf(dd->model->index(row, 0))); } -int QStaticListViewBase::horizontalPerItemValue(int itemIndex, int horizontalValue, int areaWidth, - bool leftOf, bool rightOf, bool wrap, - QListView::ScrollHint hint, int itemWidth) const +void QCommonListViewBase::updateHorizontalScrollBar(const QSize &step) { - int value = qBound(0, horizontalValue, flowPositions.count() - 1); + horizontalScrollBar()->setSingleStep(step.width() + spacing()); + horizontalScrollBar()->setPageStep(viewport()->width()); + horizontalScrollBar()->setRange(0, contentsSize.width() - viewport()->width()); +} + +void QCommonListViewBase::updateVerticalScrollBar(const QSize &step) +{ + verticalScrollBar()->setSingleStep(step.height() + spacing()); + verticalScrollBar()->setPageStep(viewport()->height()); + verticalScrollBar()->setRange(0, contentsSize.height() - viewport()->height()); +} + +void QCommonListViewBase::scrollContentsBy(int dx, int dy, bool /*scrollElasticBand*/) +{ + dd->scrollContentsBy(isRightToLeft() ? -dx : dx, dy); +} + +int QCommonListViewBase::verticalScrollToValue(int /*index*/, QListView::ScrollHint hint, + bool above, bool below, const QRect &area, const QRect &rect) const +{ + int verticalValue = verticalScrollBar()->value(); + QRect adjusted = rect.adjusted(-spacing(), -spacing(), spacing(), spacing()); + if (hint == QListView::PositionAtTop || above) + verticalValue += adjusted.top(); + else if (hint == QListView::PositionAtBottom || below) + verticalValue += qMin(adjusted.top(), adjusted.bottom() - area.height() + 1); + else if (hint == QListView::PositionAtCenter) + verticalValue += adjusted.top() - ((area.height() - adjusted.height()) / 2); + return verticalValue; +} + +int QCommonListViewBase::horizontalOffset() const +{ + return (isRightToLeft() ? horizontalScrollBar()->maximum() - horizontalScrollBar()->value() : horizontalScrollBar()->value()); +} + +int QCommonListViewBase::horizontalScrollToValue(const int /*index*/, QListView::ScrollHint hint, + bool leftOf, bool rightOf, const QRect &area, const QRect &rect) const +{ + int horizontalValue = horizontalScrollBar()->value(); + if (isRightToLeft()) { + if (hint == QListView::PositionAtCenter) { + horizontalValue += ((area.width() - rect.width()) / 2) - rect.left(); + } else { + if (leftOf) + horizontalValue -= rect.left(); + else if (rightOf) + horizontalValue += qMin(rect.left(), area.width() - rect.right()); + } + } else { + if (hint == QListView::PositionAtCenter) { + horizontalValue += rect.left() - ((area.width()- rect.width()) / 2); + } else { + if (leftOf) + horizontalValue += rect.left(); + else if (rightOf) + horizontalValue += qMin(rect.left(), rect.right() - area.width()); + } + } + return horizontalValue; +} + +/* + * ListMode ListView Implementation +*/ + +#ifndef QT_NO_DRAGANDDROP +void QListModeViewBase::paintDragDrop(QPainter *painter) +{ + // FIXME: Until the we can provide a proper drop indicator + // in IconMode, it makes no sense to show it + dd->paintDropIndicator(painter); +} +#endif //QT_NO_DRAGANDDROP + +void QListModeViewBase::updateVerticalScrollBar(const QSize &step) +{ + if (verticalScrollMode() == QAbstractItemView::ScrollPerItem + && ((flow() == QListView::TopToBottom && !isWrapping()) + || (flow() == QListView::LeftToRight && isWrapping()))) { + const int steps = (flow() == QListView::TopToBottom ? flowPositions : segmentPositions).count() - 1; + if (steps > 0) { + const int pageSteps = perItemScrollingPageSteps(viewport()->height(), contentsSize.height(), isWrapping()); + verticalScrollBar()->setSingleStep(1); + verticalScrollBar()->setPageStep(pageSteps); + verticalScrollBar()->setRange(0, steps - pageSteps); + } else { + verticalScrollBar()->setRange(0, 0); + } + // } else if (vertical && d->isWrapping() && d->movement == Static) { + // ### wrapped scrolling in flow direction + } else { + QCommonListViewBase::updateVerticalScrollBar(step); + } +} + +void QListModeViewBase::updateHorizontalScrollBar(const QSize &step) +{ + if (horizontalScrollMode() == QAbstractItemView::ScrollPerItem + && ((flow() == QListView::TopToBottom && isWrapping()) + || (flow() == QListView::LeftToRight && !isWrapping()))) { + int steps = (flow() == QListView::TopToBottom ? segmentPositions : flowPositions).count() - 1; + if (steps > 0) { + const int pageSteps = perItemScrollingPageSteps(viewport()->width(), contentsSize.width(), isWrapping()); + horizontalScrollBar()->setSingleStep(1); + horizontalScrollBar()->setPageStep(pageSteps); + horizontalScrollBar()->setRange(0, steps - pageSteps); + } else { + horizontalScrollBar()->setRange(0, 0); + } + } else { + QCommonListViewBase::updateHorizontalScrollBar(step); + } +} + +int QListModeViewBase::verticalScrollToValue(int index, QListView::ScrollHint hint, + bool above, bool below, const QRect &area, const QRect &rect) const +{ + if (verticalScrollMode() == QAbstractItemView::ScrollPerItem) { + int value = qBound(0, verticalScrollBar()->value(), flowPositions.count() - 1); + if (above) + hint = QListView::PositionAtTop; + else if (below) + hint = QListView::PositionAtBottom; + if (hint == QListView::EnsureVisible) + return value; + + return perItemScrollToValue(index, value, area.height(), hint, Qt::Vertical, isWrapping(), rect.height()); + } + + return QCommonListViewBase::verticalScrollToValue(index, hint, above, below, area, rect); +} + +int QListModeViewBase::horizontalOffset() const +{ + if (horizontalScrollMode() == QAbstractItemView::ScrollPerItem) { + if (isWrapping()) { + if (flow() == QListView::TopToBottom && !segmentPositions.isEmpty()) { + const int max = segmentPositions.count() - 1; + int currentValue = qBound(0, horizontalScrollBar()->value(), max); + int position = segmentPositions.at(currentValue); + int maximumValue = qBound(0, horizontalScrollBar()->maximum(), max); + int maximum = segmentPositions.at(maximumValue); + return (isRightToLeft() ? maximum - position : position); + } + } else if (flow() == QListView::LeftToRight && !flowPositions.isEmpty()) { + int position = flowPositions.at(horizontalScrollBar()->value()); + int maximum = flowPositions.at(horizontalScrollBar()->maximum()); + return (isRightToLeft() ? maximum - position : position); + } + } + return QCommonListViewBase::horizontalOffset(); +} + +int QListModeViewBase::verticalOffset() const +{ + if (verticalScrollMode() == QAbstractItemView::ScrollPerItem) { + if (isWrapping()) { + if (flow() == QListView::LeftToRight && !segmentPositions.isEmpty()) { + int value = verticalScrollBar()->value(); + if (value >= segmentPositions.count()) + return 0; + return segmentPositions.at(value); + } + } else if (flow() == QListView::TopToBottom && !flowPositions.isEmpty()) { + int value = verticalScrollBar()->value(); + if (value > flowPositions.count()) + return 0; + return flowPositions.at(value) - spacing(); + } + } + return QCommonListViewBase::verticalOffset(); +} + +int QListModeViewBase::horizontalScrollToValue(int index, QListView::ScrollHint hint, + bool leftOf, bool rightOf, const QRect &area, const QRect &rect) const +{ + if (horizontalScrollMode() != QAbstractItemView::ScrollPerItem) + return QCommonListViewBase::horizontalScrollToValue(index, hint, leftOf, rightOf, area, rect); + + int value = qBound(0, horizontalScrollBar()->value(), flowPositions.count() - 1); if (leftOf) - return perItemScrollToValue(itemIndex, value, areaWidth, QListView::PositionAtTop, - Qt::Horizontal, wrap, itemWidth); + hint = QListView::PositionAtTop; else if (rightOf) - return perItemScrollToValue(itemIndex, value, areaWidth, QListView::PositionAtBottom, - Qt::Horizontal, wrap, itemWidth); - else if (hint != QListView::EnsureVisible) - return perItemScrollToValue(itemIndex, value, areaWidth, hint, Qt::Horizontal, wrap, itemWidth); - return value; + hint = QListView::PositionAtBottom; + if (hint == QListView::EnsureVisible) + return value; + + return perItemScrollToValue(index, value, area.width(), hint, Qt::Horizontal, isWrapping(), rect.width()); } -void QStaticListViewBase::scrollContentsBy(int &dx, int &dy) +void QListModeViewBase::scrollContentsBy(int dx, int dy, bool scrollElasticBand) { // ### reorder this logic - const int verticalValue = verticalScrollBarValue(); - const int horizontalValue = horizontalScrollBarValue(); + const int verticalValue = verticalScrollBar()->value(); + const int horizontalValue = horizontalScrollBar()->value(); const bool vertical = (verticalScrollMode() == QAbstractItemView::ScrollPerItem); const bool horizontal = (horizontalScrollMode() == QAbstractItemView::ScrollPerItem); @@ -2255,9 +2049,10 @@ void QStaticListViewBase::scrollContentsBy(int &dx, int &dy) dx = previousCoordinate - currentCoordinate; } } + QCommonListViewBase::scrollContentsBy(dx, dy, scrollElasticBand); } -bool QStaticListViewBase::doBatchedItemLayout(const QListViewLayoutInfo &info, int max) +bool QListModeViewBase::doBatchedItemLayout(const QListViewLayoutInfo &info, int max) { doStaticLayout(info); if (batchStartRow > max) { // stop items layout @@ -2269,7 +2064,7 @@ bool QStaticListViewBase::doBatchedItemLayout(const QListViewLayoutInfo &info, i return false; // not done } -QListViewItem QStaticListViewBase::indexToListViewItem(const QModelIndex &index) const +QListViewItem QListModeViewBase::indexToListViewItem(const QModelIndex &index) const { if (flowPositions.isEmpty() || segmentPositions.isEmpty() @@ -2305,7 +2100,7 @@ QListViewItem QStaticListViewBase::indexToListViewItem(const QModelIndex &index) return QListViewItem(QRect(pos, size), index.row()); } -QPoint QStaticListViewBase::initStaticLayout(const QListViewLayoutInfo &info) +QPoint QListModeViewBase::initStaticLayout(const QListViewLayoutInfo &info) { int x, y; if (info.first == 0) { @@ -2340,7 +2135,7 @@ QPoint QStaticListViewBase::initStaticLayout(const QListViewLayoutInfo &info) /*! \internal */ -void QStaticListViewBase::doStaticLayout(const QListViewLayoutInfo &info) +void QListModeViewBase::doStaticLayout(const QListViewLayoutInfo &info) { const bool useItemSize = !info.grid.isValid(); const QPoint topLeft = initStaticLayout(info); @@ -2443,7 +2238,7 @@ void QStaticListViewBase::doStaticLayout(const QListViewLayoutInfo &info) Finds the set of items intersecting with \a area. In this function, itemsize is counted from topleft to the start of the next item. */ -QVector<QModelIndex> QStaticListViewBase::intersectingStaticSet(const QRect &area) const +QVector<QModelIndex> QListModeViewBase::intersectingSet(const QRect &area) const { QVector<QModelIndex> ret; int segStartPosition; @@ -2480,19 +2275,14 @@ QVector<QModelIndex> QStaticListViewBase::intersectingStaticSet(const QRect &are ret += index; #if 0 // for debugging else - qWarning("intersectingStaticSet: row %d was invalid", row); + qWarning("intersectingSet: row %d was invalid", row); #endif } } return ret; } -int QStaticListViewBase::itemIndex(const QListViewItem &item) const -{ - return item.indexHint; -} - -QRect QStaticListViewBase::mapToViewport(const QRect &rect) const +QRect QListModeViewBase::mapToViewport(const QRect &rect) const { if (isWrapping()) return rect; @@ -2510,7 +2300,7 @@ QRect QStaticListViewBase::mapToViewport(const QRect &rect) const return result; } -int QStaticListViewBase::perItemScrollingPageSteps(int length, int bounds, bool wrap) const +int QListModeViewBase::perItemScrollingPageSteps(int length, int bounds, bool wrap) const { const QVector<int> positions = (wrap ? segmentPositions : flowPositions); if (positions.isEmpty() || bounds <= length) @@ -2538,7 +2328,7 @@ int QStaticListViewBase::perItemScrollingPageSteps(int length, int bounds, bool return qMax(pageSteps, 1); } -int QStaticListViewBase::perItemScrollToValue(int index, int scrollValue, int viewportSize, +int QListModeViewBase::perItemScrollToValue(int index, int scrollValue, int viewportSize, QAbstractItemView::ScrollHint hint, Qt::Orientation orientation, bool wrap, int itemExtent) const { @@ -2598,7 +2388,7 @@ int QStaticListViewBase::perItemScrollToValue(int index, int scrollValue, int vi return scrollValue; } -void QStaticListViewBase::clear() +void QListModeViewBase::clear() { flowPositions.clear(); segmentPositions.clear(); @@ -2610,10 +2400,175 @@ void QStaticListViewBase::clear() } /* - * Dynamic ListView Implementation + * IconMode ListView Implementation */ -void QDynamicListViewBase::dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight) +void QIconModeViewBase::setPositionForIndex(const QPoint &position, const QModelIndex &index) +{ + if (index.row() >= items.count()) + return; + const QSize oldContents = contentsSize; + qq->update(index); // update old position + moveItem(index.row(), position); + qq->update(index); // update new position + + if (contentsSize != oldContents) + dd->viewUpdateGeometries(); // update the scroll bars +} + +void QIconModeViewBase::appendHiddenRow(int row) +{ + if (row >= 0 && row < items.count()) //remove item + tree.removeLeaf(items.at(row).rect(), row); + QCommonListViewBase::appendHiddenRow(row); +} + +void QIconModeViewBase::removeHiddenRow(int row) +{ + QCommonListViewBase::appendHiddenRow(row); + if (row >= 0 && row < items.count()) //insert item + tree.insertLeaf(items.at(row).rect(), row); +} + +#ifndef QT_NO_DRAGANDDROP +void QIconModeViewBase::paintDragDrop(QPainter *painter) +{ + if (!draggedItems.isEmpty() && viewport()->rect().contains(draggedItemsPos)) { + //we need to draw the items that arre dragged + painter->translate(draggedItemsDelta()); + QStyleOptionViewItemV4 option = viewOptions(); + option.state &= ~QStyle::State_MouseOver; + QVector<QModelIndex>::const_iterator it = draggedItems.begin(); + QListViewItem item = indexToListViewItem(*it); + for (; it != draggedItems.end(); ++it) { + item = indexToListViewItem(*it); + option.rect = viewItemRect(item); + delegate(*it)->paint(painter, option, *it); + } + } +} + +bool QIconModeViewBase::filterStartDrag(Qt::DropActions supportedActions) +{ + // This function does the same thing as in QAbstractItemView::startDrag(), + // plus adding viewitems to the draggedItems list. + // We need these items to draw the drag items + QModelIndexList indexes = dd->selectionModel->selectedIndexes(); + if (indexes.count() > 0 ) { + if (viewport()->acceptDrops()) { + QModelIndexList::ConstIterator it = indexes.constBegin(); + for (; it != indexes.constEnd(); ++it) + if (dd->model->flags(*it) & Qt::ItemIsDragEnabled + && (*it).column() == dd->column) + draggedItems.push_back(*it); + } + QDrag *drag = new QDrag(qq); + drag->setMimeData(dd->model->mimeData(indexes)); + Qt::DropAction action = drag->exec(supportedActions, Qt::CopyAction); + draggedItems.clear(); + if (action == Qt::MoveAction) + dd->clearOrRemove(); + } + return true; +} + +bool QIconModeViewBase::filterDropEvent(QDropEvent *e) +{ + if (e->source() != qq) + return false; + + const QSize contents = contentsSize; + QPoint offset(horizontalOffset(), verticalOffset()); + QPoint end = e->pos() + offset; + QPoint start = dd->pressedPosition; + QPoint delta = (dd->movement == QListView::Snap ? snapToGrid(end) - snapToGrid(start) : end - start); + QList<QModelIndex> indexes = dd->selectionModel->selectedIndexes(); + for (int i = 0; i < indexes.count(); ++i) { + QModelIndex index = indexes.at(i); + QRect rect = dd->rectForIndex(index); + viewport()->update(mapToViewport(rect)); + QPoint dest = rect.topLeft() + delta; + if (qq->isRightToLeft()) + dest.setX(dd->flipX(dest.x()) - rect.width()); + moveItem(index.row(), dest); + qq->update(index); + } + dd->stopAutoScroll(); + draggedItems.clear(); + dd->emitIndexesMoved(indexes); + e->accept(); // we have handled the event + // if the size has not grown, we need to check if it has shrinked + if (contentsSize != contents) { + if ((contentsSize.width() <= contents.width() + || contentsSize.height() <= contents.height())) { + updateContentsSize(); + } + dd->viewUpdateGeometries(); + } + return true; +} + +bool QIconModeViewBase::filterDragLeaveEvent(QDragLeaveEvent *e) +{ + viewport()->update(draggedItemsRect()); // erase the area + draggedItemsPos = QPoint(-1, -1); // don't draw the dragged items + return QCommonListViewBase::filterDragLeaveEvent(e); +} + +bool QIconModeViewBase::filterDragMoveEvent(QDragMoveEvent *e) +{ + if (e->source() != qq || !dd->canDecode(e)) + return false; + + // ignore by default + e->ignore(); + // get old dragged items rect + QRect itemsRect = this->itemsRect(draggedItems); + viewport()->update(itemsRect.translated(draggedItemsDelta())); + // update position + draggedItemsPos = e->pos(); + // get new items rect + viewport()->update(itemsRect.translated(draggedItemsDelta())); + // set the item under the cursor to current + QModelIndex index; + if (movement() == QListView::Snap) { + QRect rect(snapToGrid(e->pos() + offset()), gridSize()); + const QVector<QModelIndex> intersectVector = intersectingSet(rect); + index = intersectVector.count() > 0 ? intersectVector.last() : QModelIndex(); + } else { + index = qq->indexAt(e->pos()); + } + // check if we allow drops here + if (draggedItems.contains(index)) + e->accept(); // allow changing item position + else if (dd->model->flags(index) & Qt::ItemIsDropEnabled) + e->accept(); // allow dropping on dropenabled items + else if (!index.isValid()) + e->accept(); // allow dropping in empty areas + + // the event was treated. do autoscrolling + if (dd->shouldAutoScroll(e->pos())) + dd->startAutoScroll(); + return true; +} +#endif // QT_NO_DRAGANDDROP + +void QIconModeViewBase::setRowCount(int rowCount) +{ + tree.create(qMax(rowCount - hiddenCount(), 0)); +} + +void QIconModeViewBase::scrollContentsBy(int dx, int dy, bool scrollElasticBand) +{ + if (scrollElasticBand) + dd->scrollElasticBandBy(isRightToLeft() ? -dx : dx, dy); + + QCommonListViewBase::scrollContentsBy(dx, dy, scrollElasticBand); + if (!draggedItems.isEmpty()) + viewport()->update(draggedItemsRect().translated(dx, dy)); +} + +void QIconModeViewBase::dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight) { if (column() >= topLeft.column() && column() <= bottomRight.column()) { QStyleOptionViewItemV4 option = viewOptions(); @@ -2623,23 +2578,29 @@ void QDynamicListViewBase::dataChanged(const QModelIndex &topLeft, const QModelI } } -bool QDynamicListViewBase::doBatchedItemLayout(const QListViewLayoutInfo &info, int max) +bool QIconModeViewBase::doBatchedItemLayout(const QListViewLayoutInfo &info, int max) { if (info.last >= items.count()) { - createItems(info.last + 1); + //first we create the items + QStyleOptionViewItemV4 option = viewOptions(); + for (int row = items.count(); row <= info.last; ++row) { + QSize size = itemSize(option, modelIndex(row)); + QListViewItem item(QRect(0, 0, size.width(), size.height()), row); // default pos + items.append(item); + } doDynamicLayout(info); } return (batchStartRow > max); // done } -QListViewItem QDynamicListViewBase::indexToListViewItem(const QModelIndex &index) const +QListViewItem QIconModeViewBase::indexToListViewItem(const QModelIndex &index) const { if (index.isValid() && index.row() < items.count()) return items.at(index.row()); return QListViewItem(); } -void QDynamicListViewBase::initBspTree(const QSize &contents) +void QIconModeViewBase::initBspTree(const QSize &contents) { // remove all items from the tree int leafCount = tree.leafCount(); @@ -2656,7 +2617,7 @@ void QDynamicListViewBase::initBspTree(const QSize &contents) tree.init(QRect(0, 0, contents.width(), contents.height()), type); } -QPoint QDynamicListViewBase::initDynamicLayout(const QListViewLayoutInfo &info) +QPoint QIconModeViewBase::initDynamicLayout(const QListViewLayoutInfo &info) { int x, y; if (info.first == 0) { @@ -2678,7 +2639,7 @@ QPoint QDynamicListViewBase::initDynamicLayout(const QListViewLayoutInfo &info) /*! \internal */ -void QDynamicListViewBase::doDynamicLayout(const QListViewLayoutInfo &info) +void QIconModeViewBase::doDynamicLayout(const QListViewLayoutInfo &info) { const bool useItemSize = !info.grid.isValid(); const QPoint topLeft = initDynamicLayout(info); @@ -2712,7 +2673,7 @@ void QDynamicListViewBase::doDynamicLayout(const QListViewLayoutInfo &info) if (moved.count() != items.count()) moved.resize(items.count()); - QRect rect(QPoint(0, 0), topLeft); + QRect rect(QPoint(), topLeft); QListViewItem *item = 0; for (int row = info.first; row <= info.last; ++row) { item = &items[row]; @@ -2805,43 +2766,18 @@ void QDynamicListViewBase::doDynamicLayout(const QListViewLayoutInfo &info) viewport()->update(); } -QVector<QModelIndex> QDynamicListViewBase::intersectingDynamicSet(const QRect &area) const +QVector<QModelIndex> QIconModeViewBase::intersectingSet(const QRect &area) const { - QDynamicListViewBase *that = const_cast<QDynamicListViewBase*>(this); + QIconModeViewBase *that = const_cast<QIconModeViewBase*>(this); QBspTree::Data data(static_cast<void*>(that)); QVector<QModelIndex> res; that->interSectingVector = &res; - that->tree.climbTree(area, &QDynamicListViewBase::addLeaf, data); + that->tree.climbTree(area, &QIconModeViewBase::addLeaf, data); that->interSectingVector = 0; return res; } -void QDynamicListViewBase::createItems(int to) -{ - int count = items.count(); - QSize size; - QStyleOptionViewItemV4 option = viewOptions(); - for (int row = count; row < to; ++row) { - size = itemSize(option, modelIndex(row)); - QListViewItem item(QRect(0, 0, size.width(), size.height()), row); // default pos - items.append(item); - } -} - -void QDynamicListViewBase::drawItems(QPainter *painter, const QVector<QModelIndex> &indexes) const -{ - QStyleOptionViewItemV4 option = viewOptions(); - option.state &= ~QStyle::State_MouseOver; - QVector<QModelIndex>::const_iterator it = indexes.begin(); - QListViewItem item = indexToListViewItem(*it); - for (; it != indexes.end(); ++it) { - item = indexToListViewItem(*it); - option.rect = viewItemRect(item); - delegate(*it)->paint(painter, option, *it); - } -} - -QRect QDynamicListViewBase::itemsRect(const QVector<QModelIndex> &indexes) const +QRect QIconModeViewBase::itemsRect(const QVector<QModelIndex> &indexes) const { QVector<QModelIndex>::const_iterator it = indexes.begin(); QListViewItem item = indexToListViewItem(*it); @@ -2853,7 +2789,7 @@ QRect QDynamicListViewBase::itemsRect(const QVector<QModelIndex> &indexes) const return rect; } -int QDynamicListViewBase::itemIndex(const QListViewItem &item) const +int QIconModeViewBase::itemIndex(const QListViewItem &item) const { if (!item.isValid()) return -1; @@ -2889,11 +2825,11 @@ int QDynamicListViewBase::itemIndex(const QListViewItem &item) const return -1; } -void QDynamicListViewBase::addLeaf(QVector<int> &leaf, const QRect &area, +void QIconModeViewBase::addLeaf(QVector<int> &leaf, const QRect &area, uint visited, QBspTree::Data data) { QListViewItem *vi; - QDynamicListViewBase *_this = static_cast<QDynamicListViewBase *>(data.ptr); + QIconModeViewBase *_this = static_cast<QIconModeViewBase *>(data.ptr); for (int i = 0; i < leaf.count(); ++i) { int idx = leaf.at(i); if (idx < 0 || idx >= _this->items.count()) @@ -2909,19 +2845,7 @@ void QDynamicListViewBase::addLeaf(QVector<int> &leaf, const QRect &area, } } -void QDynamicListViewBase::insertItem(int index) -{ - if (index >= 0 && index < items.count()) - tree.insertLeaf(items.at(index).rect(), index); -} - -void QDynamicListViewBase::removeItem(int index) -{ - if (index >= 0 && index < items.count()) - tree.removeLeaf(items.at(index).rect(), index); -} - -void QDynamicListViewBase::moveItem(int index, const QPoint &dest) +void QIconModeViewBase::moveItem(int index, const QPoint &dest) { // does not impact on the bintree itself or the contents rect QListViewItem *item = &items[index]; @@ -2941,14 +2865,14 @@ void QDynamicListViewBase::moveItem(int index, const QPoint &dest) moved.setBit(index, true); } -QPoint QDynamicListViewBase::snapToGrid(const QPoint &pos) const +QPoint QIconModeViewBase::snapToGrid(const QPoint &pos) const { int x = pos.x() - (pos.x() % gridSize().width()); int y = pos.y() - (pos.y() % gridSize().height()); return QPoint(x, y); } -QPoint QDynamicListViewBase::draggedItemsDelta() const +QPoint QIconModeViewBase::draggedItemsDelta() const { if (movement() == QListView::Snap) { QPoint snapdelta = QPoint((offset().x() % gridSize().width()), @@ -2958,7 +2882,7 @@ QPoint QDynamicListViewBase::draggedItemsDelta() const return draggedItemsPos - pressedPosition(); } -QRect QDynamicListViewBase::draggedItemsRect() const +QRect QIconModeViewBase::draggedItemsRect() const { QRect rect = itemsRect(draggedItems); rect.translate(draggedItemsDelta()); @@ -2977,7 +2901,7 @@ void QListViewPrivate::scrollElasticBandBy(int dx, int dy) elasticBand.moveTop(elasticBand.top() - dy); } -void QDynamicListViewBase::clear() +void QIconModeViewBase::clear() { tree.destroy(); items.clear(); @@ -2986,7 +2910,7 @@ void QDynamicListViewBase::clear() batchSavedDeltaSeg = 0; } -void QDynamicListViewBase::updateContentsSize() +void QIconModeViewBase::updateContentsSize() { QRect bounding; for (int i = 0; i < items.count(); ++i) @@ -3039,7 +2963,7 @@ int QListView::visualIndex(const QModelIndex &index) const Q_D(const QListView); d->executePostedLayout(); QListViewItem itm = d->indexToListViewItem(index); - return d->itemIndex(itm); + return d->commonListView->itemIndex(itm); } QT_END_NAMESPACE diff --git a/src/gui/itemviews/qlistview_p.h b/src/gui/itemviews/qlistview_p.h index 6c8d324..db9eb59 100644 --- a/src/gui/itemviews/qlistview_p.h +++ b/src/gui/itemviews/qlistview_p.h @@ -67,8 +67,8 @@ QT_BEGIN_NAMESPACE class QListViewItem { friend class QListViewPrivate; - friend class QStaticListViewBase; - friend class QDynamicListViewBase; + friend class QListModeViewBase; + friend class QIconModeViewBase; public: inline QListViewItem() : x(-1), y(-1), w(0), h(0), indexHint(-1), visited(0xffff) {} @@ -120,8 +120,42 @@ class QListViewPrivate; class QCommonListViewBase { public: - inline QCommonListViewBase(QListView *q, QListViewPrivate *d) : dd(d), qq(q) {} - + inline QCommonListViewBase(QListView *q, QListViewPrivate *d) : dd(d), qq(q), batchStartRow(0), batchSavedDeltaSeg(0) {} + virtual ~QCommonListViewBase() {} + + //common interface + virtual int itemIndex(const QListViewItem &item) const = 0; + virtual QListViewItem indexToListViewItem(const QModelIndex &index) const = 0; + virtual bool doBatchedItemLayout(const QListViewLayoutInfo &info, int max) = 0; + virtual void clear() = 0; + virtual void setRowCount(int) = 0; + virtual QVector<QModelIndex> intersectingSet(const QRect &area) const = 0; + + virtual int horizontalScrollToValue(int index, QListView::ScrollHint hint, + bool leftOf, bool rightOf, const QRect &area, const QRect &rect) const; + virtual int verticalScrollToValue(int index, QListView::ScrollHint hint, + bool above, bool below, const QRect &area, const QRect &rect) const; + virtual void scrollContentsBy(int dx, int dy, bool scrollElasticBand); + virtual QRect mapToViewport(const QRect &rect) const {return rect;} + virtual int horizontalOffset() const; + virtual int verticalOffset() const { return verticalScrollBar()->value(); } + virtual void updateHorizontalScrollBar(const QSize &step); + virtual void updateVerticalScrollBar(const QSize &step); + virtual void dataChanged(const QModelIndex &, const QModelIndex &) { } + virtual void appendHiddenRow(int row); + virtual void removeHiddenRow(int row); + virtual void setPositionForIndex(const QPoint &, const QModelIndex &) { } + +#ifndef QT_NO_DRAGANDDROP + virtual void paintDragDrop(QPainter *painter) = 0; + virtual bool filterDragMoveEvent(QDragMoveEvent *) { return false; } + virtual bool filterDragLeaveEvent(QDragLeaveEvent *) { return false; } + virtual bool filterDropEvent(QDropEvent *) { return false; } + virtual bool filterStartDrag(Qt::DropActions) { return false; } +#endif + + + //other inline members inline int spacing() const; inline bool isWrapping() const; inline QSize gridSize() const; @@ -133,8 +167,8 @@ public: inline bool uniformItemSizes() const; inline int column() const; - inline int verticalScrollBarValue() const; - inline int horizontalScrollBarValue() const; + inline QScrollBar *verticalScrollBar() const; + inline QScrollBar *horizontalScrollBar() const; inline QListView::ScrollMode verticalScrollMode() const; inline QListView::ScrollMode horizontalScrollMode() const; @@ -157,110 +191,107 @@ public: QListViewPrivate *dd; QListView *qq; + QSize contentsSize; + int batchStartRow; + int batchSavedDeltaSeg; }; -// ### rename to QListModeViewBase -class QStaticListViewBase : public QCommonListViewBase +class QListModeViewBase : public QCommonListViewBase { - friend class QListViewPrivate; public: - QStaticListViewBase(QListView *q, QListViewPrivate *d) : QCommonListViewBase(q, d), - batchStartRow(0), batchSavedDeltaSeg(0), batchSavedPosition(0) {} + QListModeViewBase(QListView *q, QListViewPrivate *d) : QCommonListViewBase(q, d) {} QVector<int> flowPositions; QVector<int> segmentPositions; QVector<int> segmentStartRows; QVector<int> segmentExtents; - QSize contentsSize; - // used when laying out in batches - int batchStartRow; - int batchSavedDeltaSeg; int batchSavedPosition; + //reimplementations + int itemIndex(const QListViewItem &item) const { return item.indexHint; } + QListViewItem indexToListViewItem(const QModelIndex &index) const; bool doBatchedItemLayout(const QListViewLayoutInfo &info, int max); + void clear(); + void setRowCount(int rowCount) { flowPositions.resize(rowCount); } + QVector<QModelIndex> intersectingSet(const QRect &area) const; + + int horizontalScrollToValue(int index, QListView::ScrollHint hint, + bool leftOf, bool rightOf,const QRect &area, const QRect &rect) const; + int verticalScrollToValue(int index, QListView::ScrollHint hint, + bool above, bool below, const QRect &area, const QRect &rect) const; + void scrollContentsBy(int dx, int dy, bool scrollElasticBand); + QRect mapToViewport(const QRect &rect) const; + int horizontalOffset() const; + int verticalOffset() const; + void updateHorizontalScrollBar(const QSize &step); + void updateVerticalScrollBar(const QSize &step); + +#ifndef QT_NO_DRAGANDDROP + void paintDragDrop(QPainter *painter); +#endif +private: QPoint initStaticLayout(const QListViewLayoutInfo &info); void doStaticLayout(const QListViewLayoutInfo &info); - QVector<QModelIndex> intersectingStaticSet(const QRect &area) const; - - int itemIndex(const QListViewItem &item) const; - - int perItemScrollingPageSteps(int length, int bounds, bool wrap) const; - int perItemScrollToValue(int index, int value, int height, QAbstractItemView::ScrollHint hint, Qt::Orientation orientation, bool wrap, int extent) const; - - QRect mapToViewport(const QRect &rect) const; - - QListViewItem indexToListViewItem(const QModelIndex &index) const; - - void scrollContentsBy(int &dx, int &dy); - - int verticalPerItemValue(int itemIndex, int verticalValue, int areaHeight, - bool above, bool below, bool wrap, QListView::ScrollHint hint, int itemHeight) const; - int horizontalPerItemValue(int itemIndex, int horizontalValue, int areaWidth, - bool leftOf, bool rightOf, bool wrap, QListView::ScrollHint hint, int itemWidth) const; - - void clear(); + int perItemScrollingPageSteps(int length, int bounds, bool wrap) const; }; -// ### rename to QIconModeViewBase -class QDynamicListViewBase : public QCommonListViewBase +class QIconModeViewBase : public QCommonListViewBase { - friend class QListViewPrivate; public: - QDynamicListViewBase(QListView *q, QListViewPrivate *d) : QCommonListViewBase(q, d), - batchStartRow(0), batchSavedDeltaSeg(0), interSectingVector(0) {} + QIconModeViewBase(QListView *q, QListViewPrivate *d) : QCommonListViewBase(q, d), interSectingVector(0) {} QBspTree tree; QVector<QListViewItem> items; QBitArray moved; - QSize contentsSize; - QVector<QModelIndex> draggedItems; // indices to the tree.itemVector mutable QPoint draggedItemsPos; // used when laying out in batches - int batchStartRow; - int batchSavedDeltaSeg; - QVector<QModelIndex> *interSectingVector; //used from within intersectingDynamicSet + QVector<QModelIndex> *interSectingVector; //used from within intersectingSet - void dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight); + //reimplementations + int itemIndex(const QListViewItem &item) const; + QListViewItem indexToListViewItem(const QModelIndex &index) const; bool doBatchedItemLayout(const QListViewLayoutInfo &info, int max); + void clear(); + void setRowCount(int rowCount); + QVector<QModelIndex> intersectingSet(const QRect &area) const; + + void scrollContentsBy(int dx, int dy, bool scrollElasticBand); + void dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight); + void appendHiddenRow(int row); + void removeHiddenRow(int row); + void setPositionForIndex(const QPoint &position, const QModelIndex &index); + +#ifndef QT_NO_DRAGANDDROP + void paintDragDrop(QPainter *painter); + bool filterDragMoveEvent(QDragMoveEvent *); + bool filterDragLeaveEvent(QDragLeaveEvent *); + bool filterDropEvent(QDropEvent *e); + bool filterStartDrag(Qt::DropActions); +#endif +private: void initBspTree(const QSize &contents); QPoint initDynamicLayout(const QListViewLayoutInfo &info); void doDynamicLayout(const QListViewLayoutInfo &info); - QVector<QModelIndex> intersectingDynamicSet(const QRect &area) const; - static void addLeaf(QVector<int> &leaf, const QRect &area, uint visited, QBspTree::Data data); - - void insertItem(int index); - void removeItem(int index); - void moveItem(int index, const QPoint &dest); - - int itemIndex(const QListViewItem &item) const; - - void createItems(int to); - void drawItems(QPainter *painter, const QVector<QModelIndex> &indexes) const; QRect itemsRect(const QVector<QModelIndex> &indexes) const; - - QPoint draggedItemsDelta() const; QRect draggedItemsRect() const; - QPoint snapToGrid(const QPoint &pos) const; - - void scrollElasticBandBy(int dx, int dy); - - QListViewItem indexToListViewItem(const QModelIndex &index) const; - - void clear(); void updateContentsSize(); + QPoint draggedItemsDelta() const; + void drawItems(QPainter *painter, const QVector<QModelIndex> &indexes) const; + void moveItem(int index, const QPoint &dest); + }; class QListViewPrivate: public QAbstractItemViewPrivate @@ -278,23 +309,13 @@ public: inline QVector<QModelIndex> intersectingSet(const QRect &area, bool doLayout = true) const { if (doLayout) executePostedLayout(); QRect a = (q_func()->isRightToLeft() ? flipX(area.normalized()) : area.normalized()); - return (viewMode == QListView::ListMode) ? staticListView->intersectingStaticSet(a) - : dynamicListView->intersectingDynamicSet(a); + return commonListView->intersectingSet(a); } - // ### FIXME: - inline void resetBatchStartRow() - { if (viewMode == QListView::ListMode) staticListView->batchStartRow = 0; - else dynamicListView->batchStartRow = 0; } - inline int batchStartRow() const - { return (viewMode == QListView::ListMode - ? staticListView->batchStartRow : dynamicListView->batchStartRow); } - inline QSize contentsSize() const - { return (viewMode == QListView::ListMode - ? staticListView->contentsSize : dynamicListView->contentsSize); } - inline void setContentsSize(int w, int h) - { if (viewMode == QListView::ListMode) staticListView->contentsSize = QSize(w, h); - else dynamicListView->contentsSize = QSize(w, h); } + inline void resetBatchStartRow() { commonListView->batchStartRow = 0; } + inline int batchStartRow() const { return commonListView->batchStartRow; } + inline QSize contentsSize() const { return commonListView->contentsSize; } + inline void setContentsSize(int w, int h) { commonListView->contentsSize = QSize(w, h); } inline int flipX(int x) const { return qMax(viewport->width(), contentsSize().width()) - x; } @@ -305,12 +326,22 @@ public: inline QRect viewItemRect(const QListViewItem &item) const { if (q_func()->isRightToLeft()) return flipX(item.rect()); return item.rect(); } - int itemIndex(const QListViewItem &item) const; QListViewItem indexToListViewItem(const QModelIndex &index) const; inline QModelIndex listViewItemToIndex(const QListViewItem &item) const - { return model->index(itemIndex(item), column, root); } + { return model->index(commonListView->itemIndex(item), column, root); } + + QRect rectForIndex(const QModelIndex &index) const + { + if (!isIndexValid(index) || index.parent() != root || index.column() != column || isHidden(index.row())) + return QRect(); + executePostedLayout(); + return viewItemRect(indexToListViewItem(index)); + } + + void viewUpdateGeometries() { q_func()->updateGeometries(); } - QRect mapToViewport(const QRect &rect, bool greedy = false) const; + + QRect mapToViewport(const QRect &rect, bool extend = true) const; QModelIndex closestIndex(const QRect &target, const QVector<QModelIndex> &candidates) const; QSize itemSize(const QStyleOptionViewItem &option, const QModelIndex &index) const; @@ -351,10 +382,10 @@ public: QItemViewPaintPairs draggablePaintPairs(const QModelIndexList &indexes, QRect *r) const; - union { - QDynamicListViewBase *dynamicListView; - QStaticListViewBase *staticListView; - }; + void emitIndexesMoved(const QModelIndexList &indexes) { emit q_func()->indexesMoved(indexes); } + + + QCommonListViewBase *commonListView; // ### FIXME: see if we can move the members into the dynamic/static classes @@ -412,8 +443,8 @@ inline QPoint QCommonListViewBase::pressedPosition() const { return dd->pressedP inline bool QCommonListViewBase::uniformItemSizes() const { return dd->uniformItemSizes; } inline int QCommonListViewBase::column() const { return dd->column; } -inline int QCommonListViewBase::verticalScrollBarValue() const { return qq->verticalScrollBar()->value(); } -inline int QCommonListViewBase::horizontalScrollBarValue() const { return qq->horizontalScrollBar()->value(); } +inline QScrollBar *QCommonListViewBase::verticalScrollBar() const { return qq->verticalScrollBar(); } +inline QScrollBar *QCommonListViewBase::horizontalScrollBar() const { return qq->horizontalScrollBar(); } inline QListView::ScrollMode QCommonListViewBase::verticalScrollMode() const { return qq->verticalScrollMode(); } inline QListView::ScrollMode QCommonListViewBase::horizontalScrollMode() const { return qq->horizontalScrollMode(); } diff --git a/src/gui/kernel/qapplication.cpp b/src/gui/kernel/qapplication.cpp index 38592e0..8e9c3a1 100644 --- a/src/gui/kernel/qapplication.cpp +++ b/src/gui/kernel/qapplication.cpp @@ -104,6 +104,10 @@ extern bool qt_wince_is_pocket_pc(); //qguifunctions_wince.cpp #include "qdatetime.h" +#ifdef QT_MAC_USE_COCOA +#include <private/qt_cocoa_helpers_mac_p.h> +#endif + //#define ALIEN_DEBUG static void initResources() @@ -3497,6 +3501,15 @@ void QApplication::changeOverrideCursor(const QCursor &cursor) if (qApp->d_func()->cursor_list.isEmpty()) return; qApp->d_func()->cursor_list.removeFirst(); +#ifdef QT_MAC_USE_COCOA + // We use native NSCursor stacks in Cocoa. The currentCursor is the + // top of this stack. So to avoid flickering of cursor, we have to + // change the cusor instead of pop-ing the existing OverrideCursor + // and pushing the new one. + qApp->d_func()->cursor_list.prepend(cursor); + qt_cocoaChangeOverrideCursor(cursor); + return; +#endif setOverrideCursor(cursor); } #endif diff --git a/src/gui/kernel/qapplication_x11.cpp b/src/gui/kernel/qapplication_x11.cpp index d942519..0d07a02 100644 --- a/src/gui/kernel/qapplication_x11.cpp +++ b/src/gui/kernel/qapplication_x11.cpp @@ -4572,6 +4572,7 @@ bool QETWidget::translateXinputEvent(const XEvent *ev, QTabletDeviceData *tablet int deviceType = QTabletEvent::NoDevice; int pointerType = QTabletEvent::UnknownPointer; XEvent mouseMotionEvent; + XEvent dummy; const XDeviceMotionEvent *motion = 0; XDeviceButtonEvent *button = 0; const XProximityNotifyEvent *proximity = 0; @@ -4589,7 +4590,6 @@ bool QETWidget::translateXinputEvent(const XEvent *ev, QTabletDeviceData *tablet // Do event compression. Skip over tablet+mouse move events if there are newer ones. qt_tablet_motion_data tabletMotionData; tabletMotionData.tabletMotionType = tablet->xinput_motion; - XEvent dummy; while (true) { // Find first mouse event since we expect them in pairs inside Qt tabletMotionData.error =false; diff --git a/src/gui/kernel/qclipboard_s60.cpp b/src/gui/kernel/qclipboard_s60.cpp index 942e4d1..26ea844 100644 --- a/src/gui/kernel/qclipboard_s60.cpp +++ b/src/gui/kernel/qclipboard_s60.cpp @@ -49,6 +49,7 @@ #include "qbuffer.h" #include "qwidget.h" #include "qevent.h" +#include "private/qapplication_p.h" #include <QtDebug> // Symbian's clipboard @@ -78,28 +79,19 @@ public: bool connected() { return connection; } void clear(); - RFs fsSession(); - private: QMimeData* src; - RFs iFs; bool connection; }; QClipboardData::QClipboardData():src(0),connection(true) { clear(); - if (KErrNone != iFs.Connect()) - { - qWarning("QClipboardData::fileserver connnect failed"); - connection = false; - } } QClipboardData::~QClipboardData() { - iFs.Close(); connection = false; delete src; } @@ -110,10 +102,6 @@ void QClipboardData::clear() delete src; src = newSrc; } -RFs QClipboardData::fsSession() -{ - return iFs; -} static QClipboardData *internalCbData = 0; @@ -206,7 +194,7 @@ const QMimeData* QClipboard::mimeData(Mode mode) const if (d) { TRAPD(err,{ - RFs fs = d->fsSession(); + RFs& fs = QCoreApplicationPrivate::fsSession(); CClipboard* cb = CClipboard::NewForReadingLC(fs); Q_ASSERT(cb); RStoreReadStream stream; @@ -232,7 +220,7 @@ void QClipboard::setMimeData(QMimeData* src, Mode mode) if (d) { TRAPD(err,{ - RFs fs = d->fsSession(); + RFs& fs = QCoreApplicationPrivate::fsSession(); CClipboard* cb = CClipboard::NewForWritingLC(fs); RStoreWriteStream stream; TStreamId stid = stream.CreateLC(cb->Store()); diff --git a/src/gui/kernel/qcocoaapplicationdelegate_mac.mm b/src/gui/kernel/qcocoaapplicationdelegate_mac.mm index 572df70..172d07b 100644 --- a/src/gui/kernel/qcocoaapplicationdelegate_mac.mm +++ b/src/gui/kernel/qcocoaapplicationdelegate_mac.mm @@ -197,11 +197,19 @@ static void cleanupCocoaApplicationDelegate() } } - // Prevent Cocoa from terminating the application, since this simply - // exits the program whithout allowing QApplication::exec() to return. - // The call to QApplication::quit() above will instead quit the - // application from the Qt side. - return NSTerminateCancel; + if (qtPrivate->threadData->eventLoops.size() == 0) { + // INVARIANT: No event loop is executing. This probably + // means that Qt is used as a plugin, or as a part of a native + // Cocoa application. In any case it should be fine to + // terminate now: + return NSTerminateNow; + } else { + // Prevent Cocoa from terminating the application, since this simply + // exits the program whithout allowing QApplication::exec() to return. + // The call to QApplication::quit() above will instead quit the + // application from the Qt side. + return NSTerminateCancel; + } } - (void)applicationDidFinishLaunching:(NSNotification *)aNotification diff --git a/src/gui/kernel/qcocoaview_mac.mm b/src/gui/kernel/qcocoaview_mac.mm index 7ac0d89..5a0209d 100644 --- a/src/gui/kernel/qcocoaview_mac.mm +++ b/src/gui/kernel/qcocoaview_mac.mm @@ -667,7 +667,15 @@ extern "C" { - (void)mouseMoved:(NSEvent *)theEvent { - qt_mac_handleMouseEvent(self, theEvent, QEvent::MouseMove, Qt::NoButton); + // We always enable mouse tracking for all QCocoaView-s. In cases where we have + // child views, we will receive mouseMoved for both parent & the child (if + // mouse is over the child). We need to ignore the parent mouseMoved in such + // cases. + NSPoint windowPoint = [theEvent locationInWindow]; + NSView *candidateView = [[[self window] contentView] hitTest:windowPoint]; + if (candidateView && candidateView == self) { + qt_mac_handleMouseEvent(self, theEvent, QEvent::MouseMove, Qt::NoButton); + } } - (void)mouseDown:(NSEvent *)theEvent diff --git a/src/gui/kernel/qdnd_x11.cpp b/src/gui/kernel/qdnd_x11.cpp index a4042c1..8f92fea 100644 --- a/src/gui/kernel/qdnd_x11.cpp +++ b/src/gui/kernel/qdnd_x11.cpp @@ -1073,12 +1073,14 @@ void qt_xdnd_send_leave() if (!qt_xdnd_current_target) return; + QDragManager *manager = QDragManager::self(); + XClientMessageEvent leave; leave.type = ClientMessage; leave.window = qt_xdnd_current_target; leave.format = 32; leave.message_type = ATOM(XdndLeave); - leave.data.l[0] = qt_xdnd_dragsource_xid; + leave.data.l[0] = manager->dragPrivate()->source->effectiveWinId(); leave.data.l[1] = 0; // flags leave.data.l[2] = 0; // x, y leave.data.l[3] = 0; // w, h @@ -1094,8 +1096,8 @@ void qt_xdnd_send_leave() else XSendEvent(X11->display, qt_xdnd_current_proxy_target, False, NoEventMask, (XEvent*)&leave); + // reset the drag manager state - QDragManager *manager = QDragManager::self(); manager->willDrop = false; if (global_accepted_action != Qt::IgnoreAction) manager->emitActionChanged(Qt::IgnoreAction); diff --git a/src/gui/kernel/qevent.cpp b/src/gui/kernel/qevent.cpp index 1cc168f..76d52c7 100644 --- a/src/gui/kernel/qevent.cpp +++ b/src/gui/kernel/qevent.cpp @@ -3549,38 +3549,108 @@ QMenubarUpdatedEvent::QMenubarUpdatedEvent(QMenuBar * const menuBar) \since 4.6 \ingroup events - Touch events occur when pressing, releasing, or moving one or more - touch points on a touch device (such as a touch-screen or - track-pad). To receive touch events, widgets have to have the - Qt::WA_AcceptTouchEvents attribute set and graphics items need to have - the \l{QGraphicsItem::setAcceptTouchEvents()}{acceptTouchEvents} - attribute set to true. - - All touch events are of type QEvent::TouchBegin, - QEvent::TouchUpdate, or QEvent::TouchEnd. The touchPoints() - function returns a list of all touch points contained in the event. - Information about each touch point can be retrieved using the - QTouchEvent::TouchPoint class. The Qt::TouchPointState enum - describes the different states that a touch point may have. - - Similar to QMouseEvent, Qt automatically grabs each touch point on - the first press inside a widget; the widget will receive all - updates for the touch point until it is released. Note that it is - possible for a widget to receive events for multiple touch points, - and that multiple widgets may be receiving touch events at the same - time. - - A touch event contains a special accept flag that indicates - whether the receiver wants the event. By default, the event is - accepted. You should call ignore() if the touch event is not handled by - your widget. A QEvent::TouchBegin event is propagated up the parent widget - chain until a widget accepts it with accept(), or an event filter - consumes it. If the QEvent::TouchBegin event is neither accepted nor consumed, - then mouse events are simulated from the state of the first touch - point. - - Reimplement QWidget::event() for widgets and QGraphicsItem::sceneEvent() - for items in a graphics view to receive touch events. + \section1 Enabling Touch Events + + Touch events occur when pressing, releasing, or moving one or more touch points on a touch + device (such as a touch-screen or track-pad). To receive touch events, widgets have to have the + Qt::WA_AcceptTouchEvents attribute set and graphics items need to have the + \l{QGraphicsItem::setAcceptTouchEvents()}{acceptTouchEvents} attribute set to true. + + Note: when using QAbstractScrollArea based widgets, you should enabled the + Qt::WA_AcceptTouchEvents attribute on the scroll area's + \l{QAbstractScrollArea::viewport()}{viewport}. + + \section1 Event Delivery and Propagation + + All touch events are of type QEvent::TouchBegin, QEvent::TouchUpdate, or QEvent::TouchEnd. + Reimplement QWidget::event() or QAbstractScrollArea::viewportEvent() for widgets and + QGraphicsItem::sceneEvent() for items in a graphics view to receive touch events. By default, + QWidget::event() translates the first non-primary touch point in a QTouchEvent into a + QMouseEvent. This makes it possible to enable touch events on existing widgets that do not + normally handle QTouchEvent. See below for information on some special considerations needed + when doing this. + + QEvent::TouchBegin is the first touch event sent to a widget. The QEvent::TouchBegin event + contains a special accept flag that indicates whether the receiver wants the event. By default, + the event is accepted. You should call ignore() if the touch event is not handled by your + widget. The QEvent::TouchBegin event is propagated up the parent widget chain until a widget + accepts it with accept(), or an event filter consumes it. For QGraphicsItems, the + QEvent::TouchBegin event is propagated to items under the mouse (similar to mouse event + propagation for QGraphicsItems). + + The QEvent::TouchUpdate and QEvent::TouchEnd events are sent to the widget or item that + accepted the QEvent::TouchBegin event. If the QEvent::TouchBegin event is not accepted and not + filtered by an event filter, then no further touch events are sent until the next + QEvent::TouchBegin. + + The touchPoints() function returns a list of all touch points contained in the event. + Information about each touch point can be retrieved using the QTouchEvent::TouchPoint class. + The Qt::TouchPointState enum describes the different states that a touch point may have. + + Similar to QMouseEvent, Qt automatically grabs each touch point on the first press inside a + widget; the widget will receive all updates for the touch point until it is released. Note that + it is possible for a widget to receive events for multiple touch points, and that multiple + widgets may be receiving touch events at the same time. + + \section1 Touch Point Grouping + + As mentioned above, it is possible that several widgets can be receiving QTouchEvents at the + same time. However, Qt makes sure to never send duplicate QEvent::TouchBegin events to the same + widget, which could theoretically happen during propagation if, for example, the user touched 2 + separate widgets in a QGroupBox and both widgets ignored the QEvent::TouchBegin event. + + To avoid this, Qt will group new touch points together using the following rules: + + \list + + \i When the first touch point is detected, the destination widget is determined firstly by the + location on screen first and secondly by the propagation rules. + + \i When additional touch points are detected, Qt first looks to see if there are any active + touch points on any ancestor or descendent of the widget under the new touch point. If there + are, the new touch point is grouped with the first, and the new touch point will be sent in a + single QTouchEvent to the widget that handled the first touch point. (The widget under the new + touch point will not receive an event). + + \endlist + + This makes it possible for sibling widgets to handle touch events independently while making + sure that the sequence of QTouchEvents is always correct. + + \section1 Mouse Events and the Primary Touch Point + + QTouchEvent delivery is independent from that of QMouseEvent. On some windowing systems, mouse + events are also sent for the \l{QTouchEvent::TouchPoint::isPrimary()}{primary touch point}. + This means it is possible for your widget to receive both QTouchEvent and QMouseEvent for the + same user interaction point. You can use the QTouchEvent::TouchPoint::isPrimary() function to + identify the primary touch point. + + Note that on some systems, it is possible to receive touch events without a primary touch + point. All this means is that there will be no mouse event generated for the touch points in + the QTouchEvent. + + \section1 Caveats + + \list + + \i As mentioned above, enabling touch events means multiple widgets can be receiving touch + events simultaneously. Combined with the default QWidget::event() handling for QTouchEvents, + this gives you great flexibility in designing multi-touch user interfaces. Be aware of the + implications. For example, is is possible that the user is moving a QSlider with one finger and + pressing a QPushButton with another. The signals are emitted from these widgets will be + interleaved. + + \i Recursion into the event loop using one of the exec() methods (e.g. QDialog::exec() or + QMenu::exec()) in a QTouchEvent event handler is not supported. Since there are multiple event + recipients, unexpected recursion may cause problems, including but not limited to lost events + and unexpected infinite recursion. + + \i QTouchEvents are not affected by a \l{QWidget::grabMouse()}{mouse grab} or an + \l{QApplication::activePopupWidget()}{active popup widget}. The behavior of QTouchEvents is + undefined when opening a popup or grabbing the mouse while there are multiple active touch + points. + + \endlist \sa QTouchEvent::TouchPoint, Qt::TouchPointState, Qt::WA_AcceptTouchEvents, QGraphicsItem::acceptTouchEvents() @@ -3659,6 +3729,11 @@ QTouchEvent::~QTouchEvent() Returns the list of touch points contained in the touch event. */ +/*! \fn QTouchEvent::DeviceType QTouchEvent::deviceType() const + + Returns the touch device Type, which is of type \l {QTouchEvent::DeviceType} {DeviceType}. +*/ + /*! \fn void QTouchEvent::setWidget(QWidget *widget) \internal @@ -3680,6 +3755,14 @@ QTouchEvent::~QTouchEvent() Sets the list of touch points for this event. */ +/*! \fn void QTouchEvent::setDeviceType(DeviceType deviceType) + + \internal + + Sets the device type to \a deviceType, which is of type \l {QTouchEvent::DeviceType} + {DeviceType}. +*/ + /*! \class QTouchEvent::TouchPoint \brief The QTouchEvent::TouchPoint class provides information about a touch point in a QTouchEvent. \since 4.6 @@ -4091,15 +4174,4 @@ QTouchEvent::TouchPoint &QTouchEvent::TouchPoint::operator=(const QTouchEvent::T return *this; } -/*! \fn QTouchEvent::DeviceType QTouchEvent::deviceType() const - Returns the touch device Type, which is of type - \l {QTouchEvent::DeviceType} {DeviceType}. - */ - -/*! \fn void QTouchEvent::setDeviceType(DeviceType deviceType) - Sets the device type to \a deviceType, which is of type - \l {QTouchEvent::DeviceType} {DeviceType}. - */ - - QT_END_NAMESPACE diff --git a/src/gui/kernel/qshortcutmap.cpp b/src/gui/kernel/qshortcutmap.cpp index 59b9a2d..585a7e1 100644 --- a/src/gui/kernel/qshortcutmap.cpp +++ b/src/gui/kernel/qshortcutmap.cpp @@ -99,11 +99,11 @@ struct QShortcutEntry QObject *owner; }; -#ifndef QT_NO_DEBUG_STREAM +#if 0 //ndef QT_NO_DEBUG_STREAM /*! \internal QDebug operator<< for easy debug output of the shortcut entries. */ -QDebug &operator<<(QDebug &dbg, const QShortcutEntry *se) { +static QDebug &operator<<(QDebug &dbg, const QShortcutEntry *se) { if (!se) return dbg << "QShortcutEntry(0x0)"; dbg.nospace() diff --git a/src/gui/kernel/qstandardgestures.cpp b/src/gui/kernel/qstandardgestures.cpp index 47f3146..10689ba 100644 --- a/src/gui/kernel/qstandardgestures.cpp +++ b/src/gui/kernel/qstandardgestures.cpp @@ -172,8 +172,6 @@ bool QPanGesture::eventFilter(QObject *receiver, QEvent *event) bool QPanGesture::filterEvent(QEvent *event) { Q_D(QPanGesture); - if (!event->spontaneous()) - return false; const QTouchEvent *ev = static_cast<const QTouchEvent*>(event); if (event->type() == QEvent::TouchBegin) { QTouchEvent::TouchPoint p = ev->touchPoints().at(0); @@ -329,10 +327,11 @@ bool QPinchGesture::event(QEvent *event) bool QPinchGesture::eventFilter(QObject *receiver, QEvent *event) { -#ifdef Q_WS_WIN +#if defined(Q_WS_WIN) || defined(Q_WS_MAC) Q_D(QPinchGesture); if (receiver->isWidgetType() && event->type() == QEvent::NativeGesture) { QNativeGestureEvent *ev = static_cast<QNativeGestureEvent*>(event); +#if defined(Q_WS_WIN) QApplicationPrivate *qAppPriv = QApplicationPrivate::instance(); QApplicationPrivate::WidgetStandardGesturesMap::iterator it; it = qAppPriv->widgetGestures.find(static_cast<QWidget*>(receiver)); @@ -340,7 +339,9 @@ bool QPinchGesture::eventFilter(QObject *receiver, QEvent *event) return false; if (this != it.value().pinch) return false; +#endif Qt::GestureState nextState = Qt::NoGesture; + switch(ev->gestureType) { case QNativeGestureEvent::GestureBegin: // next we might receive the first gesture update event, so we @@ -349,15 +350,22 @@ bool QPinchGesture::eventFilter(QObject *receiver, QEvent *event) d->scaleFactor = d->lastScaleFactor = 1; d->rotationAngle = d->lastRotationAngle = 0; d->startCenterPoint = d->centerPoint = d->lastCenterPoint = QPoint(); +#if defined(Q_WS_WIN) d->initialDistance = 0; +#endif return false; case QNativeGestureEvent::Rotate: d->lastRotationAngle = d->rotationAngle; +#if defined(Q_WS_WIN) d->rotationAngle = -1 * GID_ROTATE_ANGLE_FROM_ARGUMENT(ev->argument); +#elif defined(Q_WS_MAC) + d->rotationAngle = ev->percentage; +#endif nextState = Qt::GestureUpdated; event->accept(); break; case QNativeGestureEvent::Zoom: +#if defined(Q_WS_WIN) if (d->initialDistance != 0) { d->lastScaleFactor = d->scaleFactor; int distance = int(qint64(ev->argument)); @@ -365,6 +373,10 @@ bool QPinchGesture::eventFilter(QObject *receiver, QEvent *event) } else { d->initialDistance = int(qint64(ev->argument)); } +#elif defined(Q_WS_MAC) + d->lastScaleFactor = d->scaleFactor; + d->scaleFactor = ev->percentage; +#endif nextState = Qt::GestureUpdated; event->accept(); break; diff --git a/src/gui/kernel/qt_cocoa_helpers_mac.mm b/src/gui/kernel/qt_cocoa_helpers_mac.mm index 7596802..a9b8970 100644 --- a/src/gui/kernel/qt_cocoa_helpers_mac.mm +++ b/src/gui/kernel/qt_cocoa_helpers_mac.mm @@ -1238,4 +1238,12 @@ void qt_mac_menu_collapseSeparators(void */*NSMenu **/ theMenu, bool collapse) } } +#ifdef QT_MAC_USE_COCOA +void qt_cocoaChangeOverrideCursor(const QCursor &cursor) +{ + QMacCocoaAutoReleasePool pool; + [static_cast<NSCursor *>(qt_mac_nsCursorForQCursor(cursor)) set]; +} +#endif + QT_END_NAMESPACE diff --git a/src/gui/kernel/qt_cocoa_helpers_mac_p.h b/src/gui/kernel/qt_cocoa_helpers_mac_p.h index 24d7096..932abef 100644 --- a/src/gui/kernel/qt_cocoa_helpers_mac_p.h +++ b/src/gui/kernel/qt_cocoa_helpers_mac_p.h @@ -133,6 +133,7 @@ bool qt_mac_checkForNativeSizeGrip(const QWidget *widget); void qt_dispatchTabletProximityEvent(void * /*NSEvent * */ tabletEvent); #ifdef QT_MAC_USE_COCOA bool qt_dispatchKeyEventWithCocoa(void * /*NSEvent * */ keyEvent, QWidget *widgetToGetEvent); +void qt_cocoaChangeOverrideCursor(const QCursor &cursor); #endif void qt_mac_menu_collapseSeparators(void * /*NSMenu */ menu, bool collapse); bool qt_dispatchKeyEvent(void * /*NSEvent * */ keyEvent, QWidget *widgetToGetEvent); diff --git a/src/gui/kernel/qt_mac.cpp b/src/gui/kernel/qt_mac.cpp index 642aa5c..bef3449 100644 --- a/src/gui/kernel/qt_mac.cpp +++ b/src/gui/kernel/qt_mac.cpp @@ -1,11 +1,11 @@ /**************************************************************************** - ** - ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). ** Contact: Nokia Corporation (qt-info@nokia.com) - ** - ** This file is part of the QtGui module of the Qt Toolkit. - ** - ** $QT_BEGIN_LICENSE:LGPL$ +** +** 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 @@ -36,11 +36,8 @@ ** 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$ - ** - ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE - ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. - ** - ****************************************************************************/ +** +****************************************************************************/ #include <private/qt_mac_p.h> #include <private/qpixmap_mac_p.h> diff --git a/src/gui/kernel/qwidget_mac.mm b/src/gui/kernel/qwidget_mac.mm index 3dd2e65..4bd7222 100644 --- a/src/gui/kernel/qwidget_mac.mm +++ b/src/gui/kernel/qwidget_mac.mm @@ -1055,7 +1055,7 @@ OSStatus QWidgetPrivate::qt_window_event(EventHandlerCallRef er, EventRef event, handled_event = false; break; } - qNGEvent.gestureType = QNativeGestureEvent::Zoom; + qNGEvent.gestureType = QNativeGestureEvent::Rotate; qNGEvent.percentage = float(amount); break; } case kEventGestureSwipe: { diff --git a/src/gui/kernel/qwidget_qws.cpp b/src/gui/kernel/qwidget_qws.cpp index 66c047a..09bffe6 100644 --- a/src/gui/kernel/qwidget_qws.cpp +++ b/src/gui/kernel/qwidget_qws.cpp @@ -581,6 +581,15 @@ void QWidgetPrivate::show_sys() if (q->isWindow()) { + + if (!q->testAttribute(Qt::WA_ShowWithoutActivating) + && q->windowType() != Qt::Popup + && q->windowType() != Qt::Tool + && q->windowType() != Qt::ToolTip) { + QWidget::qwsDisplay()->requestFocus(data.winid,true); + } + + if (QWindowSurface *surface = q->windowSurface()) { const QRect frameRect = q->frameGeometry(); if (surface->geometry() != frameRect) @@ -597,12 +606,6 @@ void QWidgetPrivate::show_sys() #endif data.fstrut_dirty = true; invalidateBuffer(r); - if (!q->testAttribute(Qt::WA_ShowWithoutActivating) - && q->windowType() != Qt::Popup - && q->windowType() != Qt::Tool - && q->windowType() != Qt::ToolTip) { - QWidget::qwsDisplay()->requestFocus(data.winid,true); - } bool staysontop = (q->windowFlags() & Qt::WindowStaysOnTopHint) || q->windowType() == Qt::Popup; diff --git a/src/gui/kernel/qwidget_win.cpp b/src/gui/kernel/qwidget_win.cpp index c74368c..3d618fe 100644 --- a/src/gui/kernel/qwidget_win.cpp +++ b/src/gui/kernel/qwidget_win.cpp @@ -2064,8 +2064,6 @@ void QWidgetPrivate::winSetupGestures() Q_Q(QWidget); if (!q) return; - q->setAttribute(Qt::WA_DontCreateNativeAncestors); - q->setAttribute(Qt::WA_NativeWindow); if (!q->isVisible()) return; QApplicationPrivate *qAppPriv = QApplicationPrivate::instance(); diff --git a/src/gui/kernel/qwidget_x11.cpp b/src/gui/kernel/qwidget_x11.cpp index 8baebe8..5e3897b 100644 --- a/src/gui/kernel/qwidget_x11.cpp +++ b/src/gui/kernel/qwidget_x11.cpp @@ -1280,7 +1280,7 @@ void QWidgetPrivate::updateSystemBackground() else if (brush.style() == Qt::TexturePattern) { extern QPixmap qt_toX11Pixmap(const QPixmap &pixmap); // qpixmap_x11.cpp XSetWindowBackgroundPixmap(X11->display, q->internalWinId(), - static_cast<QX11PixmapData*>(qt_toX11Pixmap(brush.texture()).data)->x11ConvertToDefaultDepth()); + static_cast<QX11PixmapData*>(qt_toX11Pixmap(brush.texture()).data.data())->x11ConvertToDefaultDepth()); } else XSetWindowBackground(X11->display, q->internalWinId(), QColormap::instance(xinfo.screen()).pixel(brush.color())); @@ -1426,7 +1426,7 @@ void QWidgetPrivate::setWindowIcon_sys(bool forceReset) // violates the ICCCM), since this works on all DEs known to Qt if (!forceReset || !topData->iconPixmap) topData->iconPixmap = new QPixmap(qt_toX11Pixmap(icon.pixmap(QSize(64,64)))); - pixmap_handle = static_cast<QX11PixmapData*>(topData->iconPixmap->data)->x11ConvertToDefaultDepth(); + pixmap_handle = static_cast<QX11PixmapData*>(topData->iconPixmap->data.data())->x11ConvertToDefaultDepth(); } } } diff --git a/src/gui/math3d/qmatrix4x4.cpp b/src/gui/math3d/qmatrix4x4.cpp index 8fc439b..36ffcbe 100644 --- a/src/gui/math3d/qmatrix4x4.cpp +++ b/src/gui/math3d/qmatrix4x4.cpp @@ -41,6 +41,7 @@ #include "qmatrix4x4.h" #include <QtCore/qmath.h> +#include <QtCore/qvariant.h> #include <QtGui/qmatrix.h> #include <QtGui/qtransform.h> @@ -1438,18 +1439,59 @@ QMatrix QMatrix4x4::toAffine() const qreal(m[3][0]), qreal(m[3][1])); } +static const qreal inv_dist_to_plane = 1. / 1024.; + /*! Returns the conventional Qt 2D transformation matrix that - corresponds to this matrix. It is assumed that this matrix - only contains 2D transformation elements. + corresponds to this matrix. + + If \a distanceToPlane is non-zero, it indicates a projection + factor to use to adjust for the z co-ordinate. The default + value of 1024 corresponds to the projection factor used + by QTransform::rotate() for the x and y axes. + + If \a distToPlane is zero, then the returned QTransform + is formed by simply dropping the third row and third column + of the QMatrix4x4. This is suitable for implementing + orthographic projections where the z co-ordinate should + be dropped rather than projected. \sa toAffine() */ -QTransform QMatrix4x4::toTransform() const +QTransform QMatrix4x4::toTransform(qreal distanceToPlane) const { - return QTransform(qreal(m[0][0]), qreal(m[0][1]), qreal(m[0][3]), - qreal(m[1][0]), qreal(m[1][1]), qreal(m[1][3]), - qreal(m[3][0]), qreal(m[3][1]), qreal(m[3][3])); + if (distanceToPlane == 1024.0f) { + // Optimize the common case with constants. + return QTransform(qreal(m[0][0]), qreal(m[0][1]), + qreal(m[0][3]) - qreal(m[0][2]) * + inv_dist_to_plane, + qreal(m[1][0]), qreal(m[1][1]), + qreal(m[1][3]) - qreal(m[1][2]) * + inv_dist_to_plane, + qreal(m[3][0]), qreal(m[3][1]), + qreal(m[3][3]) - qreal(m[3][2]) * + inv_dist_to_plane); + } else if (distanceToPlane != 0.0f) { + // The following projection matrix is pre-multiplied with "matrix": + // | 1 0 0 0 | + // | 0 1 0 0 | + // | 0 0 1 0 | + // | 0 0 d 1 | + // where d = -1 / distanceToPlane. After projection, row 3 and + // column 3 are dropped to form the final QTransform. + qreal d = 1.0f / distanceToPlane; + return QTransform(qreal(m[0][0]), qreal(m[0][1]), + qreal(m[0][3]) - qreal(m[0][2]) * d, + qreal(m[1][0]), qreal(m[1][1]), + qreal(m[1][3]) - qreal(m[1][2]) * d, + qreal(m[3][0]), qreal(m[3][1]), + qreal(m[3][3]) - qreal(m[3][2]) * d); + } else { + // Orthographic projection: drop row 3 and column 3. + return QTransform(qreal(m[0][0]), qreal(m[0][1]), qreal(m[0][3]), + qreal(m[1][0]), qreal(m[1][1]), qreal(m[1][3]), + qreal(m[3][0]), qreal(m[3][1]), qreal(m[3][3])); + } } /*! @@ -1776,6 +1818,14 @@ void QMatrix4x4::inferSpecialType() flagBits = Scale; } +/*! + Returns the matrix as a QVariant. +*/ +QMatrix4x4::operator QVariant() const +{ + return QVariant(QVariant::Matrix4x4, this); +} + #ifndef QT_NO_DEBUG_STREAM QDebug operator<<(QDebug dbg, const QMatrix4x4 &m) diff --git a/src/gui/math3d/qmatrix4x4.h b/src/gui/math3d/qmatrix4x4.h index b02608d..10e628b 100644 --- a/src/gui/math3d/qmatrix4x4.h +++ b/src/gui/math3d/qmatrix4x4.h @@ -58,6 +58,7 @@ QT_MODULE(Gui) class QMatrix; class QTransform; +class QVariant; class Q_GUI_EXPORT QMatrix4x4 { @@ -158,7 +159,7 @@ public: void toValueArray(qreal *values) const; QMatrix toAffine() const; - QTransform toTransform() const; + QTransform toTransform(qreal distanceToPlane = 1024.0f) const; QPoint map(const QPoint& point) const; QPointF map(const QPointF& point) const; @@ -182,6 +183,8 @@ public: void inferSpecialType(); + operator QVariant() const; + #ifndef QT_NO_DEBUG_STREAM friend Q_GUI_EXPORT QDebug operator<<(QDebug dbg, const QMatrix4x4 &m); #endif diff --git a/src/gui/math3d/qquaternion.cpp b/src/gui/math3d/qquaternion.cpp index b2c598f..f279388 100644 --- a/src/gui/math3d/qquaternion.cpp +++ b/src/gui/math3d/qquaternion.cpp @@ -41,6 +41,7 @@ #include "qquaternion.h" #include <QtCore/qmath.h> +#include <QtCore/qvariant.h> #include <QtCore/qdebug.h> QT_BEGIN_NAMESPACE @@ -560,6 +561,14 @@ QQuaternion QQuaternion::nlerp return (q1 * (1.0f - t) + q2b * t).normalized(); } +/*! + Returns the quaternion as a QVariant. +*/ +QQuaternion::operator QVariant() const +{ + return QVariant(QVariant::Quaternion, this); +} + #ifndef QT_NO_DEBUG_STREAM QDebug operator<<(QDebug dbg, const QQuaternion &q) diff --git a/src/gui/math3d/qquaternion.h b/src/gui/math3d/qquaternion.h index a2fb29f..af60b61 100644 --- a/src/gui/math3d/qquaternion.h +++ b/src/gui/math3d/qquaternion.h @@ -54,6 +54,7 @@ QT_MODULE(Gui) #ifndef QT_NO_QUATERNION class QMatrix4x4; +class QVariant; class Q_GUI_EXPORT QQuaternion { @@ -118,6 +119,8 @@ public: QVector4D toVector4D() const; #endif + operator QVariant() const; + #ifndef QT_NO_VECTOR3D static QQuaternion fromAxisAndAngle(const QVector3D& axis, qreal angle); #endif diff --git a/src/gui/math3d/qvector2d.cpp b/src/gui/math3d/qvector2d.cpp index 1662020..35c86a1 100644 --- a/src/gui/math3d/qvector2d.cpp +++ b/src/gui/math3d/qvector2d.cpp @@ -43,6 +43,7 @@ #include "qvector3d.h" #include "qvector4d.h" #include <QtCore/qdebug.h> +#include <QtCore/qvariant.h> #include <QtCore/qmath.h> QT_BEGIN_NAMESPACE @@ -402,6 +403,14 @@ QVector4D QVector2D::toVector4D() const \sa toPoint(), toVector3D() */ +/*! + Returns the 2D vector as a QVariant. +*/ +QVector2D::operator QVariant() const +{ + return QVariant(QVariant::Vector2D, this); +} + #ifndef QT_NO_DEBUG_STREAM QDebug operator<<(QDebug dbg, const QVector2D &vector) diff --git a/src/gui/math3d/qvector2d.h b/src/gui/math3d/qvector2d.h index 93052f6..d9727e2 100644 --- a/src/gui/math3d/qvector2d.h +++ b/src/gui/math3d/qvector2d.h @@ -53,6 +53,7 @@ QT_MODULE(Gui) class QVector3D; class QVector4D; +class QVariant; #ifndef QT_NO_VECTOR2D @@ -114,6 +115,8 @@ public: QPoint toPoint() const; QPointF toPointF() const; + operator QVariant() const; + private: float xp, yp; diff --git a/src/gui/math3d/qvector3d.cpp b/src/gui/math3d/qvector3d.cpp index 0e3f4e1..5c96979 100644 --- a/src/gui/math3d/qvector3d.cpp +++ b/src/gui/math3d/qvector3d.cpp @@ -43,6 +43,7 @@ #include "qvector2d.h" #include "qvector4d.h" #include <QtCore/qmath.h> +#include <QtCore/qvariant.h> #include <QtCore/qdebug.h> QT_BEGIN_NAMESPACE @@ -530,6 +531,14 @@ QVector4D QVector3D::toVector4D() const */ /*! + Returns the 3D vector as a QVariant. +*/ +QVector3D::operator QVariant() const +{ + return QVariant(QVariant::Vector3D, this); +} + +/*! Returns the length of the vector from the origin. \sa lengthSquared(), normalized() diff --git a/src/gui/math3d/qvector3d.h b/src/gui/math3d/qvector3d.h index 36292d2..d1370fb 100644 --- a/src/gui/math3d/qvector3d.h +++ b/src/gui/math3d/qvector3d.h @@ -127,6 +127,8 @@ public: QPoint toPoint() const; QPointF toPointF() const; + operator QVariant() const; + private: float xp, yp, zp; diff --git a/src/gui/math3d/qvector4d.cpp b/src/gui/math3d/qvector4d.cpp index a2efff7..11eb4c4 100644 --- a/src/gui/math3d/qvector4d.cpp +++ b/src/gui/math3d/qvector4d.cpp @@ -43,6 +43,7 @@ #include "qvector3d.h" #include "qvector2d.h" #include <QtCore/qdebug.h> +#include <QtCore/qvariant.h> #include <QtCore/qmath.h> QT_BEGIN_NAMESPACE @@ -500,6 +501,14 @@ QVector3D QVector4D::toVector3DAffine() const \sa toPoint(), toVector2D() */ +/*! + Returns the 4D vector as a QVariant. +*/ +QVector4D::operator QVariant() const +{ + return QVariant(QVariant::Vector4D, this); +} + #ifndef QT_NO_DEBUG_STREAM QDebug operator<<(QDebug dbg, const QVector4D &vector) diff --git a/src/gui/math3d/qvector4d.h b/src/gui/math3d/qvector4d.h index 42db45b..69ccd30 100644 --- a/src/gui/math3d/qvector4d.h +++ b/src/gui/math3d/qvector4d.h @@ -124,6 +124,8 @@ public: QPoint toPoint() const; QPointF toPointF() const; + operator QVariant() const; + private: float xp, yp, zp, wp; diff --git a/src/gui/painting/qblendfunctions.cpp b/src/gui/painting/qblendfunctions.cpp index e447301..cd78de0 100644 --- a/src/gui/painting/qblendfunctions.cpp +++ b/src/gui/painting/qblendfunctions.cpp @@ -374,9 +374,9 @@ template <typename T> void qt_blend_argb24_on_rgb16(uchar *destPixels, int dbpl, const uchar *src = srcPixels + y * sbpl; const uchar *srcEnd = src + srcOffset; while (src < srcEnd) { -#if defined(QT_ARCH_ARM) || defined(QT_ARCH_POWERPC) || defined(QT_ARCH_SH) || (defined(QT_ARCH_WINDOWSCE) && !defined(_X86_)) +#if defined(QT_ARCH_ARM) || defined(QT_ARCH_POWERPC) || defined(QT_ARCH_SH) || defined(QT_ARCH_AVR32) || (defined(QT_ARCH_WINDOWSCE) && !defined(_X86_)) // non-16-bit aligned memory access is not possible on PowerPC, - // ARM <v6 (QT_ARCH_ARMV6) & SH + // ARM <v6 (QT_ARCH_ARMV6) & SH & AVR32 quint16 spix = (quint16(src[2])<<8) + src[1]; #else quint16 spix = *(quint16 *) (src + 1); diff --git a/src/gui/painting/qgrayraster.c b/src/gui/painting/qgrayraster.c index 7c960df..3d1b806 100644 --- a/src/gui/painting/qgrayraster.c +++ b/src/gui/painting/qgrayraster.c @@ -160,12 +160,7 @@ #include <private/qrasterdefs_p.h> #include <private/qgrayraster_p.h> -// Bug in stdlib.h, see more information from fixed_stdlib.h -#if (defined __SYMBIAN32__ && !defined __cplusplus) -#include <fixed_stdlib.h> -#else #include <stdlib.h> -#endif // defined __SYMBIAN32__ && !defined __cplusplus #include <stdio.h> /* This macro is used to indicate that a function parameter is unused. */ diff --git a/src/gui/painting/qpaintdevice_mac.cpp b/src/gui/painting/qpaintdevice_mac.cpp index aa7c2ac..51d52a2 100644 --- a/src/gui/painting/qpaintdevice_mac.cpp +++ b/src/gui/painting/qpaintdevice_mac.cpp @@ -123,7 +123,7 @@ Q_GUI_EXPORT CGContextRef qt_mac_cg_context(const QPaintDevice *pdev) // It would make sense to put this into a mac #ifdef'ed // virtual function in the QPixmapData at some point if (pm->data->classId() == QPixmapData::MacClass) { - const QMacPixmapData *pmData = static_cast<const QMacPixmapData*>(pm->data); + const QMacPixmapData *pmData = static_cast<const QMacPixmapData*>(pm->data.data()); ret = CGBitmapContextCreate(pmData->pixels, pmData->w, pmData->h, 8, pmData->bytesPerRow, colorspace, flags); diff --git a/src/gui/painting/qpaintengine_raster.cpp b/src/gui/painting/qpaintengine_raster.cpp index 8679e15..72bb164 100644 --- a/src/gui/painting/qpaintengine_raster.cpp +++ b/src/gui/painting/qpaintengine_raster.cpp @@ -2368,7 +2368,7 @@ void QRasterPaintEngine::drawPixmap(const QPointF &pos, const QPixmap &pixmap) #endif if (pixmap.data->classId() == QPixmapData::RasterClass) { - const QImage &image = ((QRasterPixmapData *) pixmap.data)->image; + const QImage &image = static_cast<QRasterPixmapData *>(pixmap.data.data())->image; if (image.depth() == 1) { Q_D(QRasterPaintEngine); QRasterPaintEngineState *s = state(); @@ -2406,7 +2406,7 @@ void QRasterPaintEngine::drawPixmap(const QRectF &r, const QPixmap &pixmap, cons #endif if (pixmap.data->classId() == QPixmapData::RasterClass) { - const QImage &image = ((QRasterPixmapData *) pixmap.data)->image; + const QImage &image = static_cast<QRasterPixmapData *>(pixmap.data.data())->image; if (image.depth() == 1) { Q_D(QRasterPaintEngine); QRasterPaintEngineState *s = state(); @@ -2716,7 +2716,7 @@ void QRasterPaintEngine::drawTiledPixmap(const QRectF &r, const QPixmap &pixmap, QImage image; if (pixmap.data->classId() == QPixmapData::RasterClass) { - image = ((QRasterPixmapData *) pixmap.data)->image; + image = static_cast<QRasterPixmapData *>(pixmap.data.data())->image; } else { image = pixmap.toImage(); } diff --git a/src/gui/painting/qpaintengine_x11.cpp b/src/gui/painting/qpaintengine_x11.cpp index b8781b1..b627d36 100644 --- a/src/gui/painting/qpaintengine_x11.cpp +++ b/src/gui/painting/qpaintengine_x11.cpp @@ -482,7 +482,7 @@ bool QX11PaintEngine::begin(QPaintDevice *pdev) d->picture = (::Picture)w->x11PictureHandle(); } else if (pdev->devType() == QInternal::Pixmap) { const QPixmap *pm = static_cast<const QPixmap *>(pdev); - QX11PixmapData *data = static_cast<QX11PixmapData*>(pm->data); + QX11PixmapData *data = static_cast<QX11PixmapData*>(pm->data.data()); if (X11->use_xrender && data->depth() != 32 && data->x11_mask) data->convertToARGB32(); d->picture = (::Picture)static_cast<const QPixmap *>(pdev)->x11PictureHandle(); @@ -1365,7 +1365,7 @@ void QX11PaintEngine::updateBrush(const QBrush &brush, const QPointF &origin) XRenderPictureAttributes attrs; attrs.repeat = true; XRenderChangePicture(d->dpy, d->brush_pm.x11PictureHandle(), CPRepeat, &attrs); - QX11PixmapData *data = static_cast<QX11PixmapData*>(d->brush_pm.data); + QX11PixmapData *data = static_cast<QX11PixmapData*>(d->brush_pm.data.data()); if (data->mask_picture) XRenderChangePicture(d->dpy, data->mask_picture, CPRepeat, &attrs); } @@ -1402,13 +1402,13 @@ void QX11PaintEngine::updateBrush(const QBrush &brush, const QPointF &origin) mask |= GCTile; #ifndef QT_NO_XRENDER if (d->pdev_depth == 32 && d->brush_pm.depth() != 32) { - QX11PixmapData *brushData = static_cast<QX11PixmapData*>(d->brush_pm.data); + QX11PixmapData *brushData = static_cast<QX11PixmapData*>(d->brush_pm.data.data()); brushData->convertToARGB32(); } #endif vals.tile = (d->brush_pm.depth() == d->pdev_depth ? d->brush_pm.handle() - : static_cast<QX11PixmapData*>(d->brush_pm.data)->x11ConvertToDefaultDepth()); + : static_cast<QX11PixmapData*>(d->brush_pm.data.data())->x11ConvertToDefaultDepth()); s = FillTiled; #if !defined(QT_NO_XRENDER) d->current_brush = d->cbrush.texture().x11PictureHandle(); @@ -1915,7 +1915,7 @@ void QX11PaintEngine::drawPixmap(const QRectF &r, const QPixmap &px, const QRect QPixmap::x11SetDefaultScreen(pixmap.x11Info().screen()); #ifndef QT_NO_XRENDER - ::Picture src_pict = static_cast<QX11PixmapData*>(pixmap.data)->picture; + ::Picture src_pict = static_cast<QX11PixmapData*>(pixmap.data.data())->picture; if (src_pict && d->picture) { const int pDepth = pixmap.depth(); if (pDepth == 1 && (d->has_alpha_pen)) { @@ -1934,7 +1934,7 @@ void QX11PaintEngine::drawPixmap(const QRectF &r, const QPixmap &px, const QRect bool mono_dst = d->pdev_depth == 1; bool restore_clip = false; - if (static_cast<QX11PixmapData*>(pixmap.data)->x11_mask) { // pixmap has a mask + if (static_cast<QX11PixmapData*>(pixmap.data.data())->x11_mask) { // pixmap has a mask QBitmap comb(sw, sh); GC cgc = XCreateGC(d->dpy, comb.handle(), 0, 0); XSetForeground(d->dpy, cgc, 0); @@ -1951,7 +1951,7 @@ void QX11PaintEngine::drawPixmap(const QRectF &r, const QPixmap &px, const QRect XSetFillStyle(d->dpy, cgc, FillOpaqueStippled); XSetTSOrigin(d->dpy, cgc, -sx, -sy); XSetStipple(d->dpy, cgc, - static_cast<QX11PixmapData*>(pixmap.data)->x11_mask); + static_cast<QX11PixmapData*>(pixmap.data.data())->x11_mask); XFillRectangle(d->dpy, comb.handle(), cgc, 0, 0, sw, sh); XFreeGC(d->dpy, cgc); @@ -1994,8 +1994,8 @@ void QX11PaintEngine::drawPixmap(const QRectF &r, const QPixmap &px, const QRect if (d->pdev->devType() == QInternal::Pixmap) { const QPixmap *px = static_cast<const QPixmap*>(d->pdev); - Pixmap src_mask = static_cast<QX11PixmapData*>(pixmap.data)->x11_mask; - Pixmap dst_mask = static_cast<QX11PixmapData*>(px->data)->x11_mask; + Pixmap src_mask = static_cast<QX11PixmapData*>(pixmap.data.data())->x11_mask; + Pixmap dst_mask = static_cast<QX11PixmapData*>(px->data.data())->x11_mask; if (dst_mask) { GC cgc = XCreateGC(d->dpy, dst_mask, 0, 0); if (src_mask) { // copy src mask into dst mask @@ -2214,7 +2214,7 @@ void QX11PaintEngine::drawTiledPixmap(const QRectF &r, const QPixmap &pixmap, co #endif } else #endif // !QT_NO_XRENDER - if (pixmap.depth() > 1 && !static_cast<QX11PixmapData*>(pixmap.data)->x11_mask) { + if (pixmap.depth() > 1 && !static_cast<QX11PixmapData*>(pixmap.data.data())->x11_mask) { XSetTile(d->dpy, d->gc, pixmap.handle()); XSetFillStyle(d->dpy, d->gc, FillTiled); XSetTSOrigin(d->dpy, d->gc, x-sx, y-sy); diff --git a/src/gui/painting/qpainter.cpp b/src/gui/painting/qpainter.cpp index 0bca8f7..e1a6e80 100644 --- a/src/gui/painting/qpainter.cpp +++ b/src/gui/painting/qpainter.cpp @@ -2563,6 +2563,8 @@ void QPainter::setClipRect(const QRectF &rect, Qt::ClipOperation op) QVectorPath vp(pts, 4, 0, QVectorPath::RectangleHint); d->state->clipEnabled = true; d->extended->clip(vp, op); + if (op == Qt::ReplaceClip || op == Qt::NoClip) + d->state->clipInfo.clear(); d->state->clipInfo << QPainterClipInfo(rect, op, d->state->matrix); d->state->clipOperation = op; return; @@ -2609,6 +2611,8 @@ void QPainter::setClipRect(const QRect &rect, Qt::ClipOperation op) if (d->extended) { d->state->clipEnabled = true; d->extended->clip(rect, op); + if (op == Qt::ReplaceClip || op == Qt::NoClip) + d->state->clipInfo.clear(); d->state->clipInfo << QPainterClipInfo(rect, op, d->state->matrix); d->state->clipOperation = op; return; @@ -2662,6 +2666,8 @@ void QPainter::setClipRegion(const QRegion &r, Qt::ClipOperation op) if (d->extended) { d->state->clipEnabled = true; d->extended->clip(r, op); + if (op == Qt::NoClip || op == Qt::ReplaceClip) + d->state->clipInfo.clear(); d->state->clipInfo << QPainterClipInfo(r, op, d->state->matrix); d->state->clipOperation = op; return; @@ -3066,6 +3072,8 @@ void QPainter::setClipPath(const QPainterPath &path, Qt::ClipOperation op) if (d->extended) { d->state->clipEnabled = true; d->extended->clip(path, op); + if (op == Qt::NoClip || op == Qt::ReplaceClip) + d->state->clipInfo.clear(); d->state->clipInfo << QPainterClipInfo(path, op, d->state->matrix); d->state->clipOperation = op; return; diff --git a/src/gui/painting/qwindowsurface_x11.cpp b/src/gui/painting/qwindowsurface_x11.cpp index 783d1c5..e878c5c 100644 --- a/src/gui/painting/qwindowsurface_x11.cpp +++ b/src/gui/painting/qwindowsurface_x11.cpp @@ -96,7 +96,7 @@ void QX11WindowSurface::beginPaint(const QRegion &rgn) #ifndef QT_NO_XRENDER if (d_ptr->translucentBackground) { if (d_ptr->device.depth() != 32) - static_cast<QX11PixmapData *>(d_ptr->device.data_ptr())->convertToARGB32(); + static_cast<QX11PixmapData *>(d_ptr->device.data_ptr().data())->convertToARGB32(); ::Picture src = X11->getSolidFill(d_ptr->device.x11Info().screen(), Qt::transparent); ::Picture dst = d_ptr->device.x11PictureHandle(); const QVector<QRect> rects = rgn.rects(); diff --git a/src/gui/styles/gtksymbols.cpp b/src/gui/styles/gtksymbols.cpp index 51f40e3..6a5ea49 100644 --- a/src/gui/styles/gtksymbols.cpp +++ b/src/gui/styles/gtksymbols.cpp @@ -642,7 +642,7 @@ GtkStyle* QGtk::gtkStyle(const QString &path) return 0; } -static void update_toolbar_style(GtkWidget *gtkToolBar, GParamSpec *pspec, gpointer user_data) +static void update_toolbar_style(GtkWidget *gtkToolBar, GParamSpec *, gpointer) { GtkToolbarStyle toolbar_style = GTK_TOOLBAR_ICONS; g_object_get(gtkToolBar, "toolbar-style", &toolbar_style, NULL); diff --git a/src/gui/styles/qs60style_s60.cpp b/src/gui/styles/qs60style_s60.cpp index 85f0ae4..bc218cd 100644 --- a/src/gui/styles/qs60style_s60.cpp +++ b/src/gui/styles/qs60style_s60.cpp @@ -1358,6 +1358,7 @@ QS60Style::QS60Style() { } +#ifdef Q_WS_S60 void QS60StylePrivate::handleDynamicLayoutVariantSwitch() { clearCaches(QS60StylePrivate::CC_LayoutChange); @@ -1379,6 +1380,7 @@ void QS60StylePrivate::handleSkinChange() topLevelWidget->ensurePolished(); } } +#endif // Q_WS_S60 QT_END_NAMESPACE diff --git a/src/gui/text/qfont.cpp b/src/gui/text/qfont.cpp index 96905d0..3443a64 100644 --- a/src/gui/text/qfont.cpp +++ b/src/gui/text/qfont.cpp @@ -217,7 +217,6 @@ QFontPrivate::QFontPrivate() rawMode(false), underline(false), overline(false), strikeOut(false), kerning(true), capital(0), letterSpacingIsAbsolute(false), scFont(0) { - ref = 1; #ifdef Q_WS_X11 if (QX11Info::display()) screen = QX11Info::appScreen(); @@ -237,7 +236,6 @@ QFontPrivate::QFontPrivate(const QFontPrivate &other) letterSpacing(other.letterSpacing), wordSpacing(other.wordSpacing), scFont(other.scFont) { - ref = 1; #ifdef Q_WS_WIN hdc = other.hdc; #endif @@ -721,11 +719,11 @@ QFont::QFont(const QFont &font, QPaintDevice *pd) const int screen = 0; #endif if (font.d->dpi != dpi || font.d->screen != screen ) { - d.reset(new QFontPrivate(*font.d)); + d = new QFontPrivate(*font.d); d->dpi = dpi; d->screen = screen; } else { - d.assign(font.d.data()); + d = font.d.data(); } #ifdef Q_WS_WIN if (pd->devType() == QInternal::Printer && pd->getDC()) @@ -737,9 +735,8 @@ QFont::QFont(const QFont &font, QPaintDevice *pd) \internal */ QFont::QFont(QFontPrivate *data) - : resolve_mask(QFont::AllPropertiesResolved) + : d(data), resolve_mask(QFont::AllPropertiesResolved) { - d.assign(data); } /*! \internal @@ -766,9 +763,8 @@ void QFont::detach() \sa QApplication::setFont(), QApplication::font() */ QFont::QFont() - :resolve_mask(0) + : d(QApplication::font().d.data()), resolve_mask(0) { - d.assign(QApplication::font().d.data()); } /*! @@ -790,10 +786,8 @@ QFont::QFont() setStyleHint() QApplication::font() */ QFont::QFont(const QString &family, int pointSize, int weight, bool italic) + : d(new QFontPrivate()), resolve_mask(QFont::FamilyResolved) { - d.reset(new QFontPrivate()); - resolve_mask = QFont::FamilyResolved; - if (pointSize <= 0) { #ifdef Q_OS_SYMBIAN pointSize = 7; @@ -821,9 +815,8 @@ QFont::QFont(const QString &family, int pointSize, int weight, bool italic) Constructs a font that is a copy of \a font. */ QFont::QFont(const QFont &font) + : d(font.d.data()), resolve_mask(font.resolve_mask) { - d.assign(font.d.data()); - resolve_mask = font.resolve_mask; } /*! @@ -838,7 +831,7 @@ QFont::~QFont() */ QFont &QFont::operator=(const QFont &font) { - d.assign(font.d.data()); + d = font.d.data(); resolve_mask = font.resolve_mask; return *this; } @@ -2197,8 +2190,7 @@ QDataStream &operator<<(QDataStream &s, const QFont &font) */ QDataStream &operator>>(QDataStream &s, QFont &font) { - font.d.assign(0); - font.d.reset(new QFontPrivate); + font.d = new QFontPrivate; font.resolve_mask = QFont::AllPropertiesResolved; quint8 styleHint, styleStrategy = QFont::PreferDefault, charSet, weight, bits; diff --git a/src/gui/text/qfont.h b/src/gui/text/qfont.h index 10ec062..e91e017 100644 --- a/src/gui/text/qfont.h +++ b/src/gui/text/qfont.h @@ -44,7 +44,7 @@ #include <QtGui/qwindowdefs.h> #include <QtCore/qstring.h> -#include <QtCore/qscopedpointer.h> +#include <QtCore/qsharedpointer.h> #if defined(Q_WS_X11) || defined(Q_WS_QWS) typedef struct FT_FaceRec_* FT_Face; @@ -313,7 +313,7 @@ private: friend Q_GUI_EXPORT QDataStream &operator>>(QDataStream &, QFont &); #endif - QScopedSharedPointer<QFontPrivate> d; + QExplicitlySharedDataPointer<QFontPrivate> d; uint resolve_mask; }; diff --git a/src/gui/text/qtextdocument_p.cpp b/src/gui/text/qtextdocument_p.cpp index a4dee6a..dec44ab 100644 --- a/src/gui/text/qtextdocument_p.cpp +++ b/src/gui/text/qtextdocument_p.cpp @@ -63,10 +63,10 @@ QT_BEGIN_NAMESPACE // The VxWorks DIAB compiler crashes when initializing the anonymouse union with { a7 } #if !defined(Q_CC_DIAB) # define QT_INIT_TEXTUNDOCOMMAND(c, a1, a2, a3, a4, a5, a6, a7, a8) \ - QTextUndoCommand c = { a1, a2, a3, a4, a5, a6, { a7 }, a8 } + QTextUndoCommand c = { a1, a2, 0, 0, a3, a4, a5, a6, { a7 }, a8 } #else # define QT_INIT_TEXTUNDOCOMMAND(c, a1, a2, a3, a4, a5, a6, a7, a8) \ - QTextUndoCommand c = { a1, a2, a3, a4, a5, a6 }; c.blockFormat = a7; c.revision = a8 + QTextUndoCommand c = { a1, a2, 0, 0, a3, a4, a5, a6 }; c.blockFormat = a7; c.revision = a8 #endif /* @@ -967,14 +967,18 @@ int QTextDocumentPrivate::undoRedo(bool undo) B->revision = c.revision; } - if (undo) { - if (undoState == 0 || !undoStack[undoState-1].block) - break; - } else { + if (!undo) ++undoState; - if (undoState == undoStack.size() || !undoStack[undoState-1].block) - break; - } + + bool inBlock = ( + undoState > 0 + && undoState < undoStack.size() + && undoStack[undoState].block_part + && undoStack[undoState-1].block_part + && !undoStack[undoState-1].block_end + ); + if (!inBlock) + break; } undoEnabled = true; int editPos = -1; @@ -999,7 +1003,8 @@ void QTextDocumentPrivate::appendUndoItem(QAbstractUndoItem *item) QTextUndoCommand c; c.command = QTextUndoCommand::Custom; - c.block = editBlock != 0; + c.block_part = editBlock != 0; + c.block_end = 0; c.operation = QTextUndoCommand::MoveCursor; c.format = 0; c.strPos = 0; @@ -1020,9 +1025,10 @@ void QTextDocumentPrivate::appendUndoItem(const QTextUndoCommand &c) if (!undoStack.isEmpty() && modified) { QTextUndoCommand &last = undoStack[undoState - 1]; - if ( (last.block && c.block) // part of the same block => can merge - || (!c.block && !last.block // two single undo items => can merge - && (undoState < 2 || !undoStack[undoState-2].block))) { + + if ( (last.block_part && c.block_part && !last.block_end) // part of the same block => can merge + || (!c.block_part && !last.block_part)) { // two single undo items => can merge + if (last.tryMerge(c)) return; } @@ -1034,7 +1040,7 @@ void QTextDocumentPrivate::appendUndoItem(const QTextUndoCommand &c) emitUndoAvailable(true); emitRedoAvailable(false); - if (!c.block) + if (!c.block_part) emit document()->undoCommandAdded(); } @@ -1100,7 +1106,7 @@ void QTextDocumentPrivate::joinPreviousEditBlock() beginEditBlock(); if (undoEnabled && undoState) - undoStack[undoState - 1].block = true; + undoStack[undoState - 1].block_end = false; } void QTextDocumentPrivate::endEditBlock() @@ -1109,10 +1115,10 @@ void QTextDocumentPrivate::endEditBlock() return; if (undoEnabled && undoState > 0) { - const bool wasBlocking = undoStack[undoState - 1].block; - undoStack[undoState - 1].block = false; - if (wasBlocking) + if (undoStack[undoState - 1].block_part) { + undoStack[undoState - 1].block_end = true; emit document()->undoCommandAdded(); + } } finishEdit(); diff --git a/src/gui/text/qtextdocument_p.h b/src/gui/text/qtextdocument_p.h index 55aa17e..363309c 100644 --- a/src/gui/text/qtextdocument_p.h +++ b/src/gui/text/qtextdocument_p.h @@ -139,7 +139,9 @@ public: MoveCursor = 1 }; quint16 command; - quint8 block; ///< All undo commands that have this set to true are combined with the preceding command on undo/redo. + uint block_part : 1; // all commands that are part of an undo block (including the first and the last one) have this set to 1 + uint block_end : 1; // the last command in an undo block has this set to 1. + uint block_padding : 6; // padding since block used to be a quint8 quint8 operation; int format; quint32 strPos; diff --git a/src/gui/widgets/qabstractscrollarea.cpp b/src/gui/widgets/qabstractscrollarea.cpp index f09768a..533a56b 100644 --- a/src/gui/widgets/qabstractscrollarea.cpp +++ b/src/gui/widgets/qabstractscrollarea.cpp @@ -934,6 +934,10 @@ bool QAbstractScrollArea::event(QEvent *e) case QEvent::DragMove: case QEvent::DragLeave: #endif + // ignore touch events in case they have been propagated from the viewport + case QEvent::TouchBegin: + case QEvent::TouchUpdate: + case QEvent::TouchEnd: return false; case QEvent::StyleChange: case QEvent::LayoutDirectionChange: @@ -974,6 +978,9 @@ bool QAbstractScrollArea::viewportEvent(QEvent *e) case QEvent::MouseButtonPress: case QEvent::MouseButtonRelease: case QEvent::MouseButtonDblClick: + case QEvent::TouchBegin: + case QEvent::TouchUpdate: + case QEvent::TouchEnd: case QEvent::MouseMove: case QEvent::ContextMenu: #ifndef QT_NO_WHEELEVENT diff --git a/src/gui/widgets/qdatetimeedit.cpp b/src/gui/widgets/qdatetimeedit.cpp index 133d73c..5a9c7e1 100644 --- a/src/gui/widgets/qdatetimeedit.cpp +++ b/src/gui/widgets/qdatetimeedit.cpp @@ -1,4 +1,4 @@ -/****************************************************************************) +/**************************************************************************** ** ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). ** Contact: Nokia Corporation (qt-info@nokia.com) diff --git a/src/gui/widgets/qmainwindow.cpp b/src/gui/widgets/qmainwindow.cpp index 7db800b..15e6791 100644 --- a/src/gui/widgets/qmainwindow.cpp +++ b/src/gui/widgets/qmainwindow.cpp @@ -1032,6 +1032,8 @@ void QMainWindow::addDockWidget(Qt::DockWidgetArea area, QDockWidget *dockwidget Restores the state of \a dockwidget if it is created after the call to restoreState(). Returns true if the state was restored; otherwise returns false. + + \sa restoreState(), saveState() */ bool QMainWindow::restoreDockWidget(QDockWidget *dockwidget) @@ -1163,6 +1165,11 @@ Qt::DockWidgetArea QMainWindow::dockWidgetArea(QDockWidget *dockwidget) const To restore the saved state, pass the return value and \a version number to restoreState(). + To save the geometry when the window closes, you can + implement a close event like this: + + \snippet doc/src/snippets/code/src_gui_widgets_qmainwindow.cpp 0 + \sa restoreState(), QWidget::saveGeometry(), QWidget::restoreGeometry() */ QByteArray QMainWindow::saveState(int version) const @@ -1182,7 +1189,13 @@ QByteArray QMainWindow::saveState(int version) const unchanged, and this function returns \c false; otherwise, the state is restored, and this function returns \c true. - \sa saveState(), QWidget::saveGeometry(), QWidget::restoreGeometry() + To restore geometry saved using QSettings, you can use code like + this: + + \snippet doc/src/snippets/code/src_gui_widgets_qmainwindow.cpp 1 + + \sa saveState(), QWidget::saveGeometry(), + QWidget::restoreGeometry(), restoreDockWidget() */ bool QMainWindow::restoreState(const QByteArray &state, int version) { diff --git a/src/gui/widgets/qmenu.h b/src/gui/widgets/qmenu.h index c180fea..14ba5f6 100644 --- a/src/gui/widgets/qmenu.h +++ b/src/gui/widgets/qmenu.h @@ -63,7 +63,7 @@ QT_MODULE(Gui) #ifdef Q_WS_S60 IMPORT_C void qt_symbian_show_toplevel(CEikMenuPane* menuPane); IMPORT_C void qt_symbian_show_submenu(CEikMenuPane* menuPane, int id); -#endif +#endif // Q_WS_S60 #ifndef QT_NO_MENU diff --git a/src/gui/widgets/qmenu_symbian.cpp b/src/gui/widgets/qmenu_symbian.cpp index e4079c5..dc92b07 100644 --- a/src/gui/widgets/qmenu_symbian.cpp +++ b/src/gui/widgets/qmenu_symbian.cpp @@ -238,6 +238,7 @@ static void rebuildMenu() } } +#ifdef Q_WS_S60 Q_GUI_EXPORT void qt_symbian_show_toplevel( CEikMenuPane* menuPane) { if (!menuExists()) @@ -255,6 +256,7 @@ Q_GUI_EXPORT void qt_symbian_show_submenu( CEikMenuPane* menuPane, int id) QT_TRAP_THROWING(menuPane->AddMenuItemL(menu->children.at(i)->menuItemData)); } } +#endif // Q_WS_S60 void QMenuBarPrivate::symbianCommands(int command) { |