diff options
Diffstat (limited to 'src/gui/graphicsview')
-rw-r--r-- | src/gui/graphicsview/qgraphicsanchorlayout.cpp | 10 | ||||
-rw-r--r-- | src/gui/graphicsview/qgraphicsanchorlayout_p.cpp | 392 | ||||
-rw-r--r-- | src/gui/graphicsview/qgraphicsanchorlayout_p.h | 53 | ||||
-rw-r--r-- | src/gui/graphicsview/qgraphicsitem.cpp | 45 | ||||
-rw-r--r-- | src/gui/graphicsview/qgraphicsitem_p.h | 2 | ||||
-rw-r--r-- | src/gui/graphicsview/qgraphicslayout_p.h | 51 | ||||
-rw-r--r-- | src/gui/graphicsview/qgraphicsproxywidget.cpp | 2 | ||||
-rw-r--r-- | src/gui/graphicsview/qgraphicsscene.cpp | 18 | ||||
-rw-r--r-- | src/gui/graphicsview/qgraphicstransform.cpp | 3 | ||||
-rw-r--r-- | src/gui/graphicsview/qgraphicstransform.h | 2 | ||||
-rw-r--r-- | src/gui/graphicsview/qgraphicstransform_p.h | 3 | ||||
-rw-r--r-- | src/gui/graphicsview/qgraphicsview.cpp | 8 | ||||
-rw-r--r-- | src/gui/graphicsview/qgraphicswidget.cpp | 2 | ||||
-rw-r--r-- | src/gui/graphicsview/qgridlayoutengine_p.h | 25 | ||||
-rw-r--r-- | src/gui/graphicsview/qsimplex_p.cpp | 111 | ||||
-rw-r--r-- | src/gui/graphicsview/qsimplex_p.h | 38 |
16 files changed, 529 insertions, 236 deletions
diff --git a/src/gui/graphicsview/qgraphicsanchorlayout.cpp b/src/gui/graphicsview/qgraphicsanchorlayout.cpp index e21cd99..56d70e1 100644 --- a/src/gui/graphicsview/qgraphicsanchorlayout.cpp +++ b/src/gui/graphicsview/qgraphicsanchorlayout.cpp @@ -123,7 +123,7 @@ */ #include "qgraphicsanchorlayout_p.h" - +#ifndef QT_NO_GRAPHICSVIEW QT_BEGIN_NAMESPACE QGraphicsAnchor::QGraphicsAnchor(QGraphicsAnchorLayout *parentLayout) @@ -420,7 +420,7 @@ void QGraphicsAnchorLayout::setSpacing(qreal spacing) qreal QGraphicsAnchorLayout::horizontalSpacing() const { Q_D(const QGraphicsAnchorLayout); - return d->effectiveSpacing(QGraphicsAnchorLayoutPrivate::Horizontal); + return d->styleInfo().defaultSpacing(Qt::Horizontal); } /*! @@ -431,7 +431,7 @@ qreal QGraphicsAnchorLayout::horizontalSpacing() const qreal QGraphicsAnchorLayout::verticalSpacing() const { Q_D(const QGraphicsAnchorLayout); - return d->effectiveSpacing(QGraphicsAnchorLayoutPrivate::Vertical); + return d->styleInfo().defaultSpacing(Qt::Vertical); } /*! @@ -501,7 +501,8 @@ void QGraphicsAnchorLayout::invalidate() { Q_D(QGraphicsAnchorLayout); QGraphicsLayout::invalidate(); - d->calculateGraphCacheDirty = 1; + d->calculateGraphCacheDirty = true; + d->styleInfoDirty = true; } /*! @@ -535,3 +536,4 @@ QSizeF QGraphicsAnchorLayout::sizeHint(Qt::SizeHint which, const QSizeF &constra } QT_END_NAMESPACE +#endif //QT_NO_GRAPHICSVIEW diff --git a/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp b/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp index 8c8c303..41aa8aa 100644 --- a/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp +++ b/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp @@ -40,6 +40,7 @@ ****************************************************************************/ #include <QtGui/qwidget.h> +#include <QtGui/qapplication.h> #include <QtCore/qlinkedlist.h> #include <QtCore/qstack.h> @@ -48,7 +49,7 @@ #endif #include "qgraphicsanchorlayout_p.h" - +#ifndef QT_NO_GRAPHICSVIEW QT_BEGIN_NAMESPACE @@ -141,34 +142,25 @@ static void internalSizeHints(QSizePolicy::Policy policy, *expSize = *prefSize; } -void AnchorData::refreshSizeHints(qreal effectiveSpacing) +bool AnchorData::refreshSizeHints(const QLayoutStyleInfo *styleInfo) { - const bool isInternalAnchor = from->m_item == to->m_item; - QSizePolicy::Policy policy; qreal minSizeHint; qreal prefSizeHint; qreal maxSizeHint; - if (isInternalAnchor) { - const QGraphicsAnchorLayoutPrivate::Orientation orient = - QGraphicsAnchorLayoutPrivate::edgeOrientation(from->m_edge); - const Qt::AnchorPoint centerEdge = - QGraphicsAnchorLayoutPrivate::pickEdge(Qt::AnchorHorizontalCenter, orient); - bool hasCenter = (from->m_edge == centerEdge || to->m_edge == centerEdge); - + // It is an internal anchor + if (item) { if (isLayoutAnchor) { minSize = 0; prefSize = 0; expSize = 0; maxSize = QWIDGETSIZE_MAX; - if (hasCenter) + if (isCenterAnchor) maxSize /= 2; - return; + return true; } else { - - QGraphicsLayoutItem *item = from->m_item; - if (orient == QGraphicsAnchorLayoutPrivate::Horizontal) { + if (orientation == QGraphicsAnchorLayoutPrivate::Horizontal) { policy = item->sizePolicy().horizontalPolicy(); minSizeHint = item->effectiveSizeHint(Qt::MinimumSize).width(); prefSizeHint = item->effectiveSizeHint(Qt::PreferredSize).width(); @@ -180,7 +172,7 @@ void AnchorData::refreshSizeHints(qreal effectiveSpacing) maxSizeHint = item->effectiveSizeHint(Qt::MaximumSize).height(); } - if (hasCenter) { + if (isCenterAnchor) { minSizeHint /= 2; prefSizeHint /= 2; maxSizeHint /= 2; @@ -196,7 +188,20 @@ void AnchorData::refreshSizeHints(qreal effectiveSpacing) // their effective size hints might be narrowed down due to their size policies. prefSizeHint = prefSize; } else { - prefSizeHint = effectiveSpacing; + const Qt::Orientation orient = Qt::Orientation(QGraphicsAnchorLayoutPrivate::edgeOrientation(from->m_edge) + 1); + qreal s = styleInfo->defaultSpacing(orient); + if (s < 0) { + QSizePolicy::ControlType controlTypeFrom = from->m_item->sizePolicy().controlType(); + QSizePolicy::ControlType controlTypeTo = to->m_item->sizePolicy().controlType(); + s = styleInfo->perItemSpacing(controlTypeFrom, controlTypeTo, orient); + + // ### Currently we do not support negative anchors inside the graph. + // To avoid those being created by a negative style spacing, we must + // make this test. + if (s < 0) + s = 0; + } + prefSizeHint = s; } maxSizeHint = QWIDGETSIZE_MAX; } @@ -214,6 +219,8 @@ void AnchorData::refreshSizeHints(qreal effectiveSpacing) sizeAtPreferred = prefSize; sizeAtExpanding = prefSize; sizeAtMaximum = prefSize; + + return true; } void ParallelAnchorData::updateChildrenSizes() @@ -227,26 +234,29 @@ void ParallelAnchorData::updateChildrenSizes() secondEdge->updateChildrenSizes(); } -void ParallelAnchorData::refreshSizeHints(qreal effectiveSpacing) +bool ParallelAnchorData::refreshSizeHints(const QLayoutStyleInfo *styleInfo) { - refreshSizeHints_helper(effectiveSpacing); + return refreshSizeHints_helper(styleInfo); } -void ParallelAnchorData::refreshSizeHints_helper(qreal effectiveSpacing, +bool ParallelAnchorData::refreshSizeHints_helper(const QLayoutStyleInfo *styleInfo, bool refreshChildren) { - if (refreshChildren) { - firstEdge->refreshSizeHints(effectiveSpacing); - secondEdge->refreshSizeHints(effectiveSpacing); + if (refreshChildren && (!firstEdge->refreshSizeHints(styleInfo) + || !secondEdge->refreshSizeHints(styleInfo))) { + return false; } - // ### 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); + // This condition means that the maximum size of one anchor being simplified is smaller than + // the minimum size of the other anchor. The consequence is that there won't be a valid size + // for this parallel setup. + if (minSize > maxSize) { + return false; + } + expSize = qMax(firstEdge->expSize, secondEdge->expSize); expSize = qMin(expSize, maxSize); @@ -258,6 +268,8 @@ void ParallelAnchorData::refreshSizeHints_helper(qreal effectiveSpacing, sizeAtPreferred = prefSize; sizeAtExpanding = prefSize; sizeAtMaximum = prefSize; + + return true; } /*! @@ -362,12 +374,12 @@ void SequentialAnchorData::updateChildrenSizes() } } -void SequentialAnchorData::refreshSizeHints(qreal effectiveSpacing) +bool SequentialAnchorData::refreshSizeHints(const QLayoutStyleInfo *styleInfo) { - refreshSizeHints_helper(effectiveSpacing); + return refreshSizeHints_helper(styleInfo); } -void SequentialAnchorData::refreshSizeHints_helper(qreal effectiveSpacing, +bool SequentialAnchorData::refreshSizeHints_helper(const QLayoutStyleInfo *styleInfo, bool refreshChildren) { minSize = 0; @@ -379,8 +391,8 @@ void SequentialAnchorData::refreshSizeHints_helper(qreal effectiveSpacing, AnchorData *edge = m_edges.at(i); // If it's the case refresh children information first - if (refreshChildren) - edge->refreshSizeHints(effectiveSpacing); + if (refreshChildren && !edge->refreshSizeHints(styleInfo)) + return false; minSize += edge->minSize; prefSize += edge->prefSize; @@ -393,6 +405,8 @@ void SequentialAnchorData::refreshSizeHints_helper(qreal effectiveSpacing, sizeAtPreferred = prefSize; sizeAtExpanding = prefSize; sizeAtMaximum = prefSize; + + return true; } #ifdef QT_DEBUG @@ -458,7 +472,7 @@ QString GraphPath::toString() const #endif QGraphicsAnchorLayoutPrivate::QGraphicsAnchorLayoutPrivate() - : calculateGraphCacheDirty(1) + : calculateGraphCacheDirty(true), styleInfoDirty(true) { for (int i = 0; i < NOrientations; ++i) { for (int j = 0; j < 3; ++j) { @@ -510,18 +524,49 @@ inline static qreal checkAdd(qreal a, qreal 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) + \internal + + Adds \a newAnchor to the graph \a g. + + Returns the newAnchor itself if it could be added without further changes to the graph. If a + new parallel anchor had to be created, then returns the new parallel anchor. In case the + addition is unfeasible -- because a parallel setup is not possible, returns 0. +*/ +static AnchorData *addAnchorMaybeParallel(Graph<AnchorVertex, AnchorData> *g, + AnchorData *newAnchor) +{ + bool feasible = true; + + // If already exists one anchor where newAnchor is supposed to be, we create a parallel + // anchor. + if (AnchorData *oldAnchor = g->takeEdge(newAnchor->from, newAnchor->to)) { + ParallelAnchorData *parallel = new ParallelAnchorData(oldAnchor, newAnchor); + + // At this point we can identify that the parallel anchor is not feasible, e.g. one + // anchor minimum size is bigger than the other anchor maximum size. + feasible = parallel->refreshSizeHints_helper(0, false); + newAnchor = parallel; + } + + g->createEdge(newAnchor->from, newAnchor->to, newAnchor); + return feasible ? newAnchor : 0; +} + + +/*! + \internal + + Takes the sequence of vertices described by (\a before, \a vertices, \a after) and removes + all anchors connected to the vertices in \a vertices, returning one simplified anchor between + \a before and \a after. + + Note that this function doesn't add the created anchor to the graph. This should be done by + the caller. +*/ +static AnchorData *createSequence(Graph<AnchorVertex, AnchorData> *graph, + AnchorVertex *before, + const QVector<AnchorVertex*> &vertices, + AnchorVertex *after) { AnchorData *data = graph->edgeData(before, vertices.first()); Q_ASSERT(data); @@ -546,41 +591,24 @@ static bool simplifySequentialChunk(Graph<AnchorVertex, AnchorData> *graph, qDebug("simplifying [%s] to [%s - %s]", qPrintable(strPath), qPrintable(before->toString()), qPrintable(after->toString())); #endif - SequentialAnchorData *sequence = new SequentialAnchorData; AnchorVertex *prev = before; + QVector<AnchorData *> edges; for (int i = 0; i <= orderedVertices.count(); ++i) { AnchorVertex *next = (i < orderedVertices.count()) ? orderedVertices.at(i) : after; AnchorData *ad = graph->takeEdge(prev, next); Q_ASSERT(ad); - sequence->m_edges.append(ad); + edges.append(ad); prev = next; } - sequence->setVertices(orderedVertices); + SequentialAnchorData *sequence = new SequentialAnchorData(orderedVertices, edges); sequence->from = before; sequence->to = after; sequence->refreshSizeHints_helper(0, false); - // 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)) { - ParallelAnchorData *parallel = new ParallelAnchorData(oldAnchor, sequence); - parallel->isLayoutAnchor = (oldAnchor->isLayoutAnchor - || sequence->isLayoutAnchor); - parallel->refreshSizeHints_helper(0, false); - newAnchor = parallel; - } - graph->createEdge(before, after, newAnchor); - - // True if we created a parallel anchor - return newAnchor != sequence; + return sequence; } /*! @@ -617,15 +645,17 @@ static bool simplifySequentialChunk(Graph<AnchorVertex, AnchorData> *graph, 2. Go to (1) 3. Done + When creating the parallel anchors, the algorithm might identify unfeasible situations. In this + case the simplification process stops and returns false. Otherwise returns true. */ -void QGraphicsAnchorLayoutPrivate::simplifyGraph(Orientation orientation) +bool QGraphicsAnchorLayoutPrivate::simplifyGraph(Orientation orientation) { static bool noSimplification = !qgetenv("QT_ANCHORLAYOUT_NO_SIMPLIFICATION").isEmpty(); if (noSimplification || items.isEmpty()) - return; + return true; if (graphSimplified[orientation]) - return; + return true; graphSimplified[orientation] = true; #if 0 @@ -634,12 +664,18 @@ void QGraphicsAnchorLayoutPrivate::simplifyGraph(Orientation orientation) #endif if (!graph[orientation].rootVertex()) - return; + return true; bool dirty; + bool feasible = true; do { - dirty = simplifyGraphIteration(orientation); - } while (dirty); + dirty = simplifyGraphIteration(orientation, &feasible); + } while (dirty && feasible); + + if (!feasible) + graphSimplified[orientation] = false; + + return feasible; } /*! @@ -656,7 +692,8 @@ void QGraphicsAnchorLayoutPrivate::simplifyGraph(Orientation orientation) Note that there are some catches to this that are not covered by the above explanation, see the function comments for more details. */ -bool QGraphicsAnchorLayoutPrivate::simplifyGraphIteration(QGraphicsAnchorLayoutPrivate::Orientation orientation) +bool QGraphicsAnchorLayoutPrivate::simplifyGraphIteration(QGraphicsAnchorLayoutPrivate::Orientation orientation, + bool *feasible) { Q_Q(QGraphicsAnchorLayout); Graph<AnchorVertex, AnchorData> &g = graph[orientation]; @@ -667,8 +704,6 @@ bool QGraphicsAnchorLayoutPrivate::simplifyGraphIteration(QGraphicsAnchorLayoutP QVector<AnchorVertex*> candidates; bool candidatesForward; - const Qt::AnchorPoint centerEdge = pickEdge(Qt::AnchorHorizontalCenter, orientation); - // Walk depth-first, in the stack we store start of the candidate sequence (beforeSequence) // and the vertex to be visited. while (!stack.isEmpty()) { @@ -777,7 +812,8 @@ bool QGraphicsAnchorLayoutPrivate::simplifyGraphIteration(QGraphicsAnchorLayoutP // One restriction we have is to not simplify half of an anchor and let the other half // unsimplified. So we remove center edges before and after the sequence. - if (beforeSequence->m_edge == centerEdge && beforeSequence->m_item == candidates.first()->m_item) { + const AnchorData *firstAnchor = g.edgeData(beforeSequence, candidates.first()); + if (firstAnchor->isCenterAnchor) { beforeSequence = candidates.first(); candidates.remove(0); @@ -786,7 +822,8 @@ bool QGraphicsAnchorLayoutPrivate::simplifyGraphIteration(QGraphicsAnchorLayoutP continue; } - if (afterSequence->m_edge == centerEdge && afterSequence->m_item == candidates.last()->m_item) { + const AnchorData *lastAnchor = g.edgeData(candidates.last(), afterSequence); + if (lastAnchor->isCenterAnchor) { afterSequence = candidates.last(); candidates.remove(candidates.count() - 1); @@ -794,11 +831,26 @@ bool QGraphicsAnchorLayoutPrivate::simplifyGraphIteration(QGraphicsAnchorLayoutP continue; } - // This function will remove the candidates from the graph and create one edge between - // beforeSequence and afterSequence. This function returns true if the sequential - // simplification also caused a parallel simplification to be created. In this case we end - // the iteration and start again (since all the visited state we have may be outdated). - if (simplifySequentialChunk(&g, beforeSequence, candidates, afterSequence)) + // + // Add the sequence to the graph. + // + + AnchorData *sequence = createSequence(&g, beforeSequence, candidates, afterSequence); + + // If 'beforeSequence' and 'afterSequence' already had an anchor between them, we'll + // create a parallel anchor between the new sequence and the old anchor. + AnchorData *newAnchor = addAnchorMaybeParallel(&g, sequence); + + if (!newAnchor) { + *feasible = false; + return false; + } + + // When a new parallel anchor is create in the graph, we finish the iteration and return + // true to indicate a new iteration is needed. This happens because a parallel anchor + // changes the number of adjacents one vertex has, possibly opening up oportunities for + // building candidate lists (when adjacents == 2). + if (newAnchor != sequence) return true; // If there was no parallel simplification, we'll keep walking the graph. So we clear the @@ -1008,11 +1060,13 @@ void QGraphicsAnchorLayoutPrivate::createCenterAnchors( AnchorData *data = new AnchorData; c->variables.insert(data, 1.0); addAnchor_helper(item, firstEdge, item, centerEdge, data); + data->isCenterAnchor = true; data->refreshSizeHints(0); data = new AnchorData; c->variables.insert(data, -1.0); addAnchor_helper(item, centerEdge, item, lastEdge, data); + data->isCenterAnchor = true; data->refreshSizeHints(0); itemCenterConstraints[orientation].append(c); @@ -1234,9 +1288,11 @@ void QGraphicsAnchorLayoutPrivate::addAnchor_helper(QGraphicsLayoutItem *firstIt { Q_Q(QGraphicsAnchorLayout); + const Orientation orientation = edgeOrientation(firstEdge); + // Guarantee that the graph is no simplified when adding this anchor, // anchor manipulation always happen in the full graph - restoreSimplifiedGraph(edgeOrientation(firstEdge)); + restoreSimplifiedGraph(orientation); // Is the Vertex (firstItem, firstEdge) already represented in our // internal structure? @@ -1245,10 +1301,16 @@ void QGraphicsAnchorLayoutPrivate::addAnchor_helper(QGraphicsLayoutItem *firstIt // Remove previous anchor // ### Could we update the existing edgeData rather than creating a new one? - if (graph[edgeOrientation(firstEdge)].edgeData(v1, v2)) { + if (graph[orientation].edgeData(v1, v2)) { removeAnchor_helper(v1, v2); } + // If its an internal anchor, set the associated item + if (firstItem == secondItem) + data->item = firstItem; + + data->orientation = orientation; + // Create a bi-directional edge in the sense it can be transversed both // from v1 or v2. "data" however is shared between the two references // so we still know that the anchor direction is from 1 to 2. @@ -1257,10 +1319,11 @@ void QGraphicsAnchorLayoutPrivate::addAnchor_helper(QGraphicsLayoutItem *firstIt #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); + // ### bit to track internal anchors, since inside AnchorData methods + // we don't have access to the 'q' pointer. + data->isLayoutAnchor = (data->item == q); - graph[edgeOrientation(firstEdge)].createEdge(v1, v2, data); + graph[orientation].createEdge(v1, v2, data); } QGraphicsAnchor *QGraphicsAnchorLayoutPrivate::getAnchor(QGraphicsLayoutItem *firstItem, @@ -1425,7 +1488,7 @@ void QGraphicsAnchorLayoutPrivate::anchorSize(const AnchorData *data, Q_ASSERT(minSize || prefSize || maxSize); Q_ASSERT(data); QGraphicsAnchorLayoutPrivate *that = const_cast<QGraphicsAnchorLayoutPrivate *>(this); - that->restoreSimplifiedGraph(edgeOrientation(data->from->m_edge)); + that->restoreSimplifiedGraph(Orientation(data->orientation)); if (minSize) *minSize = data->minSize; @@ -1565,34 +1628,32 @@ void QGraphicsAnchorLayoutPrivate::correctEdgeDirection(QGraphicsLayoutItem *&fi } } -qreal QGraphicsAnchorLayoutPrivate::effectiveSpacing(Orientation orientation) const +QLayoutStyleInfo &QGraphicsAnchorLayoutPrivate::styleInfo() const { - Q_Q(const QGraphicsAnchorLayout); - qreal s = spacings[orientation]; - if (s < 0) { - // ### make sure behaviour is the same as in QGraphicsGridLayout + if (styleInfoDirty) { + Q_Q(const QGraphicsAnchorLayout); + //### Fix this if QGV ever gets support for Metal style or different Aqua sizes. + QWidget *wid = 0; + QGraphicsLayoutItem *parent = q->parentLayoutItem(); while (parent && parent->isLayout()) { parent = parent->parentLayoutItem(); } + QGraphicsWidget *w = 0; if (parent) { QGraphicsItem *parentItem = parent->graphicsItem(); - if (parentItem && parentItem->isWidget()) { - QGraphicsWidget *w = static_cast<QGraphicsWidget*>(parentItem); - s = w->style()->pixelMetric(orientation == Horizontal - ? QStyle::PM_LayoutHorizontalSpacing - : QStyle::PM_LayoutVerticalSpacing); - } + if (parentItem && parentItem->isWidget()) + w = static_cast<QGraphicsWidget*>(parentItem); } - } - // ### Currently we do not support negative anchors inside the graph. - // To avoid those being created by a negative style spacing, we must - // make this test. - if (s < 0) - s = 0; + QStyle *style = w ? w->style() : QApplication::style(); + cachedStyleInfo = QLayoutStyleInfo(style, wid); + cachedStyleInfo.setDefaultSpacing(Qt::Horizontal, spacings[0]); + cachedStyleInfo.setDefaultSpacing(Qt::Vertical, spacings[1]); - return s; + styleInfoDirty = false; + } + return cachedStyleInfo; } /*! @@ -1620,7 +1681,7 @@ void QGraphicsAnchorLayoutPrivate::calculateGraphs() dumpGraph(QString::fromAscii("%1-after").arg(count)); #endif - calculateGraphCacheDirty = 0; + calculateGraphCacheDirty = false; } // ### Maybe getGraphParts could return the variables when traversing, at least @@ -1638,38 +1699,52 @@ QList<AnchorData *> getVariables(QList<QSimplexConstraint *> constraints) } /*! - \internal + \internal - Calculate graphs is the method that puts together all the helper routines - so that the AnchorLayout can calculate the sizes of each item. + 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: + 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. + 1) Refresh anchor nominal sizes, that is, the size that each anchor would + have if no other restrictions applied. This is done by quering the + layout style and the sizeHints of the items belonging to the layout. - 2) Simplify the graph by grouping together parallel and sequential anchors - into "group anchors". These have equivalent minimum, preferred and maximum - sizeHints as the anchors they replace. + 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. + 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. + 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); +#if defined(QT_DEBUG) || defined(Q_AUTOTEST_EXPORT) + lastCalculationUsedSimplex[orientation] = false; +#endif - // Reset the nominal sizes of each anchor based on the current item sizes - setAnchorSizeHintsFromItems(orientation); + // Reset the nominal sizes of each anchor based on the current item sizes. This function + // works with both simplified and non-simplified graphs, so it'll work when the + // simplification is going to be reused. + if (!refreshAllSizeHints(orientation)) { + qWarning("QGraphicsAnchorLayout: anchor setup is not feasible."); + graphHasConflicts[orientation] = true; + return; + } + + // Simplify the graph + if (!simplifyGraph(orientation)) { + qWarning("QGraphicsAnchorLayout: anchor setup is not feasible."); + graphHasConflicts[orientation] = true; + return; + } // Traverse all graph edges and store the possible paths to each vertex findPaths(orientation); @@ -1837,23 +1912,31 @@ bool QGraphicsAnchorLayoutPrivate::calculateNonTrunk(const QList<QSimplexConstra } /*! - \internal + \internal + + Traverse the graph refreshing the size hints. Complex anchors will call the + refresh method of their children anchors. Simple anchors, if are internal + anchors, will query the associated item for their size hints. - For graph edges ("anchors") that represent items, this method updates their - intrinsic size restrictions, based on the item size hints. + Returns false if some unfeasibility was found in the graph regarding the + complex anchors. */ -void QGraphicsAnchorLayoutPrivate::setAnchorSizeHintsFromItems(Orientation orientation) +bool QGraphicsAnchorLayoutPrivate::refreshAllSizeHints(Orientation orientation) { Graph<AnchorVertex, AnchorData> &g = graph[orientation]; QList<QPair<AnchorVertex *, AnchorVertex *> > vertices = g.connections(); - qreal spacing = effectiveSpacing(orientation); - + QLayoutStyleInfo styleInf = styleInfo(); 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); + + // During the traversal we check the feasibility of the complex anchors. + if (!data->refreshSizeHints(&styleInf)) + return false; } + + return true; } /*! @@ -1960,17 +2043,27 @@ QList<QSimplexConstraint *> QGraphicsAnchorLayoutPrivate::constraintsFromSizeHin { 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; + AnchorData *ad = anchors[i]; + + if ((ad->minSize == ad->maxSize) || qFuzzyCompare(ad->minSize, ad->maxSize)) { + QSimplexConstraint *c = new QSimplexConstraint; + c->variables.insert(ad, 1.0); + c->constant = ad->minSize; + c->ratio = QSimplexConstraint::Equal; + anchorConstraints += c; + } else { + QSimplexConstraint *c = new QSimplexConstraint; + c->variables.insert(ad, 1.0); + c->constant = ad->minSize; + c->ratio = QSimplexConstraint::MoreOrEqual; + anchorConstraints += c; + + c = new QSimplexConstraint; + c->variables.insert(ad, 1.0); + c->constant = ad->maxSize; + c->ratio = QSimplexConstraint::LessOrEqual; + anchorConstraints += c; + } } return anchorConstraints; @@ -2110,8 +2203,8 @@ void QGraphicsAnchorLayoutPrivate::identifyNonFloatItems_helper(const AnchorData switch(ad->type) { case AnchorData::Normal: - if (ad->from->m_item == ad->to->m_item && ad->to->m_item != q) - nonFloatingItemsIdentifiedSoFar->insert(ad->to->m_item); + if (ad->item && ad->item != q) + nonFloatingItemsIdentifiedSoFar->insert(ad->item); break; case AnchorData::Sequential: foreach (const AnchorData *d, static_cast<const SequentialAnchorData *>(ad)->m_edges) @@ -2377,7 +2470,7 @@ bool QGraphicsAnchorLayoutPrivate::solveMinMax(const QList<QSimplexConstraint *> *min = simplex.solveMin(); // Save sizeAtMinimum results - QList<QSimplexVariable *> variables = simplex.constraintsVariables(); + QList<AnchorData *> variables = getVariables(constraints); for (int i = 0; i < variables.size(); ++i) { AnchorData *ad = static_cast<AnchorData *>(variables[i]); Q_ASSERT(ad->result >= ad->minSize || qFuzzyCompare(ad->result, ad->minSize)); @@ -2536,7 +2629,7 @@ void QGraphicsAnchorLayoutPrivate::solveExpanding(const QList<QSimplexConstraint hasExpanding = true; // Lock anchor between boundedExpSize and sizeAtMaximum (ensure 3.a) - if (boundedExpSize == ad->sizeAtMaximum) { + if (boundedExpSize == ad->sizeAtMaximum || qFuzzyCompare(boundedExpSize, ad->sizeAtMaximum)) { // The interval has only one possible value, we can use an "Equal" // constraint and don't need to add this variable to the objective. QSimplexConstraint *itemC = new QSimplexConstraint; @@ -2635,3 +2728,4 @@ void QGraphicsAnchorLayoutPrivate::dumpGraph(const QString &name) #endif QT_END_NAMESPACE +#endif //QT_NO_GRAPHICSVIEW diff --git a/src/gui/graphicsview/qgraphicsanchorlayout_p.h b/src/gui/graphicsview/qgraphicsanchorlayout_p.h index d45c004..7dd0d65 100644 --- a/src/gui/graphicsview/qgraphicsanchorlayout_p.h +++ b/src/gui/graphicsview/qgraphicsanchorlayout_p.h @@ -60,7 +60,7 @@ #include "qgraphicsanchorlayout.h" #include "qgraph_p.h" #include "qsimplex_p.h" - +#ifndef QT_NO_GRAPHICSVIEW QT_BEGIN_NAMESPACE /* @@ -151,15 +151,16 @@ struct AnchorData : public QSimplexVariable { }; AnchorData() - : QSimplexVariable(), from(0), to(0), + : QSimplexVariable(), item(0), from(0), to(0), minSize(0), prefSize(0), expSize(0), maxSize(0), sizeAtMinimum(0), sizeAtPreferred(0), sizeAtExpanding(0), sizeAtMaximum(0), graphicsAnchor(0), skipInPreferred(0), - type(Normal), hasSize(true), isLayoutAnchor(false) {} + type(Normal), hasSize(true), isLayoutAnchor(false), + isCenterAnchor(false), orientation(0) {} virtual void updateChildrenSizes() {} - virtual void refreshSizeHints(qreal effectiveSpacing); + virtual bool refreshSizeHints(const QLayoutStyleInfo *styleInfo); virtual ~AnchorData() {} @@ -180,6 +181,9 @@ struct AnchorData : public QSimplexVariable { hasSize = false; } + // Internal anchors have associated items + QGraphicsLayoutItem *item; + // Anchor is semantically directed AnchorVertex *from; AnchorVertex *to; @@ -205,7 +209,9 @@ struct AnchorData : public QSimplexVariable { 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' + uint isLayoutAnchor : 1; // if this anchor is an internal layout anchor + uint isCenterAnchor : 1; + uint orientation : 1; }; #ifdef QT_DEBUG @@ -217,26 +223,20 @@ inline QString AnchorData::toString() const struct SequentialAnchorData : public AnchorData { - SequentialAnchorData() : AnchorData() + SequentialAnchorData(const QVector<AnchorVertex *> &vertices, const QVector<AnchorData *> &edges) + : AnchorData(), m_children(vertices), m_edges(edges) { type = AnchorData::Sequential; + orientation = m_edges.at(0)->orientation; #ifdef QT_DEBUG - name = QLatin1String("SequentialAnchorData"); + name = QString::fromAscii("%1 -- %2").arg(vertices.first()->toString(), vertices.last()->toString()); #endif } virtual void updateChildrenSizes(); - virtual void refreshSizeHints(qreal effectiveSpacing); + virtual bool refreshSizeHints(const QLayoutStyleInfo *styleInfo); - void refreshSizeHints_helper(qreal effectiveSpacing, bool refreshChildren = true); - - 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 - } + bool refreshSizeHints_helper(const QLayoutStyleInfo *styleInfo, bool refreshChildren = true); QVector<AnchorVertex*> m_children; // list of vertices in the sequence QVector<AnchorData*> m_edges; // keep the list of edges too. @@ -248,6 +248,7 @@ struct ParallelAnchorData : public AnchorData : AnchorData(), firstEdge(first), secondEdge(second) { type = AnchorData::Parallel; + orientation = first->orientation; // ### Those asserts force that both child anchors have the same direction, // but can't we simplify a pair of anchors in opposite directions? @@ -261,9 +262,9 @@ struct ParallelAnchorData : public AnchorData } virtual void updateChildrenSizes(); - virtual void refreshSizeHints(qreal effectiveSpacing); + virtual bool refreshSizeHints(const QLayoutStyleInfo *styleInfo); - void refreshSizeHints_helper(qreal effectiveSpacing, bool refreshChildren = true); + bool refreshSizeHints_helper(const QLayoutStyleInfo *styleInfo, bool refreshChildren = true); AnchorData* firstEdge; AnchorData* secondEdge; @@ -423,13 +424,12 @@ public: 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; + + QLayoutStyleInfo &styleInfo() const; // Activation methods - void simplifyGraph(Orientation orientation); - bool simplifyGraphIteration(Orientation orientation); + bool simplifyGraph(Orientation orientation); + bool simplifyGraphIteration(Orientation orientation, bool *feasible); void restoreSimplifiedGraph(Orientation orientation); void calculateGraphs(); @@ -441,7 +441,7 @@ public: bool calculateNonTrunk(const QList<QSimplexConstraint *> &constraints, const QList<AnchorData *> &variables); - void setAnchorSizeHintsFromItems(Orientation orientation); + bool refreshAllSizeHints(Orientation orientation); void findPaths(Orientation orientation); void constraintsFromPaths(Orientation orientation); void updateAnchorSizes(Orientation orientation); @@ -524,10 +524,13 @@ public: #endif uint calculateGraphCacheDirty : 1; + mutable uint styleInfoDirty : 1; + mutable QLayoutStyleInfo cachedStyleInfo; friend class QGraphicsAnchorPrivate; }; QT_END_NAMESPACE +#endif //QT_NO_GRAPHICSVIEW #endif diff --git a/src/gui/graphicsview/qgraphicsitem.cpp b/src/gui/graphicsview/qgraphicsitem.cpp index 738c6e3..d70b039 100644 --- a/src/gui/graphicsview/qgraphicsitem.cpp +++ b/src/gui/graphicsview/qgraphicsitem.cpp @@ -1039,13 +1039,31 @@ void QGraphicsItemPrivate::setParentItemHelper(QGraphicsItem *newParent) } // Update focus scope item ptr in new scope. - if (newParent) { + QGraphicsItem *newFocusScopeItem = subFocusItem ? subFocusItem : parentFocusScopeItem; + if (newFocusScopeItem && newParent) { + if (subFocusItem) { + // Find the subFocusItem's topmost focus scope. + QGraphicsItem *ancestorScope = 0; + QGraphicsItem *p = subFocusItem->d_ptr->parent; + while (p) { + if (p->flags() & QGraphicsItem::ItemIsFocusScope) + ancestorScope = p; + if (p->isPanel()) + break; + p = p->parentItem(); + } + if (ancestorScope) + newFocusScopeItem = ancestorScope; + } + QGraphicsItem *p = newParent; while (p) { if (p->flags() & QGraphicsItem::ItemIsFocusScope) { - p->d_ptr->focusScopeItem = subFocusItem ? subFocusItem : parentFocusScopeItem; - // ### The below line might not make sense... - if (subFocusItem) + p->d_ptr->focusScopeItem = newFocusScopeItem; + // Ensure the new item is no longer the subFocusItem. The + // only way to set focus on a child of a focus scope is + // by setting focus on the scope itself. + if (subFocusItem && !p->focusItem()) subFocusItem->d_ptr->clearSubFocus(); break; } @@ -1287,6 +1305,8 @@ QGraphicsItem::QGraphicsItem(QGraphicsItemPrivate &dd, QGraphicsItem *parent, */ QGraphicsItem::~QGraphicsItem() { + if (d_ptr->isObject) + QObjectPrivate::get(static_cast<QGraphicsObject *>(this))->wasDeleted = true; d_ptr->inDestructor = 1; d_ptr->removeExtraItemCache(); @@ -2982,8 +3002,11 @@ void QGraphicsItemPrivate::setFocusHelper(Qt::FocusReason focusReason, bool clim while (p) { if (p->flags() & QGraphicsItem::ItemIsFocusScope) { p->d_ptr->focusScopeItem = q_ptr; - if (!q_ptr->isActive() || !p->focusItem()) + if (!p->focusItem()) { + // If you call setFocus on a child of a focus scope that + // doesn't currently have a focus item, then stop. return; + } break; } p = p->d_ptr->parent; @@ -4264,7 +4287,7 @@ void QGraphicsItem::stackBefore(const QGraphicsItem *sibling) // Only move items with the same Z value, and that need moving. int siblingIndex = sibling->d_ptr->siblingIndex; int myIndex = d_ptr->siblingIndex; - if (myIndex >= siblingIndex && d_ptr->z == sibling->d_ptr->z) { + if (myIndex >= siblingIndex) { siblings->move(myIndex, siblingIndex); // Fixup the insertion ordering. for (int i = 0; i < siblings->size(); ++i) { @@ -9674,12 +9697,14 @@ bool QGraphicsTextItem::sceneEvent(QEvent *event) // Reset the focus widget's input context, regardless // of how this item gained or lost focus. if (QWidget *fw = qApp->focusWidget()) { +#ifndef QT_NO_IM if (QInputContext *qic = fw->inputContext()) { if (event->type() == QEvent::FocusIn || event->type() == QEvent::FocusOut) qic->reset(); else qic->update(); } +#endif //QT_NO_IM } break; default: @@ -10788,8 +10813,12 @@ QDebug operator<<(QDebug debug, QGraphicsItem *item) return debug; } - debug << "QGraphicsItem(this =" << ((void*)item) - << ", parent =" << ((void*)item->parentItem()) + if (QGraphicsObject *o = item->toGraphicsObject()) + debug << o->metaObject()->className(); + else + debug << "QGraphicsItem"; + debug << "(this =" << (void*)item + << ", parent =" << (void*)item->parentItem() << ", pos =" << item->pos() << ", z =" << item->zValue() << ", flags = " << item->flags() << ")"; diff --git a/src/gui/graphicsview/qgraphicsitem_p.h b/src/gui/graphicsview/qgraphicsitem_p.h index 183e95b..ca56c18 100644 --- a/src/gui/graphicsview/qgraphicsitem_p.h +++ b/src/gui/graphicsview/qgraphicsitem_p.h @@ -607,7 +607,7 @@ public: return item->type() == QGraphicsPixmapItem::Type && !(item->flags() & QGraphicsItem::ItemIsSelectable) && item->d_ptr->children.size() == 0; - //|| (item->d_ptr->isObject && qobject_cast<QFxImage *>(q_func())); + //|| (item->d_ptr->isObject && qobject_cast<QmlGraphicsImage *>(q_func())); } inline const QStyleOption *styleOption() const diff --git a/src/gui/graphicsview/qgraphicslayout_p.h b/src/gui/graphicsview/qgraphicslayout_p.h index 59c6dba..4aeae39 100644 --- a/src/gui/graphicsview/qgraphicslayout_p.h +++ b/src/gui/graphicsview/qgraphicslayout_p.h @@ -60,6 +60,8 @@ #include "qgraphicslayout.h" #include "qgraphicslayoutitem_p.h" #include <QtGui/qstyle.h> +#include <QtGui/qwidget.h> +#include <QtGui/qstyleoption.h> QT_BEGIN_NAMESPACE @@ -76,6 +78,55 @@ inline bool qt_graphicsLayoutDebug() } #endif + +class QLayoutStyleInfo +{ +public: + inline QLayoutStyleInfo() { invalidate(); } + inline QLayoutStyleInfo(QStyle *style, QWidget *widget) + : m_valid(true), m_style(style), m_widget(widget) + { + Q_ASSERT(style); + if (widget) //### + m_styleOption.initFrom(widget); + m_defaultSpacing[0] = style->pixelMetric(QStyle::PM_LayoutHorizontalSpacing); + m_defaultSpacing[1] = style->pixelMetric(QStyle::PM_LayoutVerticalSpacing); + } + + inline void invalidate() { m_valid = false; m_style = 0; m_widget = 0; } + + inline QStyle *style() const { return m_style; } + inline QWidget *widget() const { return m_widget; } + + inline bool operator==(const QLayoutStyleInfo &other) + { return m_style == other.m_style && m_widget == other.m_widget; } + inline bool operator!=(const QLayoutStyleInfo &other) + { return !(*this == other); } + + inline void setDefaultSpacing(Qt::Orientation o, qreal spacing){ + if (spacing >= 0) + m_defaultSpacing[o - 1] = spacing; + } + + inline qreal defaultSpacing(Qt::Orientation o) const { + return m_defaultSpacing[o - 1]; + } + + inline qreal perItemSpacing(QSizePolicy::ControlType control1, + QSizePolicy::ControlType control2, + Qt::Orientation orientation) const + { + Q_ASSERT(style()); + return style()->layoutSpacing(control1, control2, orientation, &m_styleOption, widget()); + } +private: + bool m_valid; + QStyle *m_style; + QWidget *m_widget; + QStyleOption m_styleOption; + qreal m_defaultSpacing[2]; +}; + class Q_AUTOTEST_EXPORT QGraphicsLayoutPrivate : public QGraphicsLayoutItemPrivate { Q_DECLARE_PUBLIC(QGraphicsLayout) diff --git a/src/gui/graphicsview/qgraphicsproxywidget.cpp b/src/gui/graphicsview/qgraphicsproxywidget.cpp index 64c51ad..e9173a9 100644 --- a/src/gui/graphicsview/qgraphicsproxywidget.cpp +++ b/src/gui/graphicsview/qgraphicsproxywidget.cpp @@ -397,7 +397,7 @@ QWidget *QGraphicsProxyWidgetPrivate::findFocusChild(QWidget *child, bool next) do { if (child->isEnabled() && child->isVisibleTo(widget) - && (child->focusPolicy() & focus_flag == focus_flag) + && ((child->focusPolicy() & focus_flag) == focus_flag) && !(child->d_func()->extra && child->d_func()->extra->focus_proxy)) { return child; } diff --git a/src/gui/graphicsview/qgraphicsscene.cpp b/src/gui/graphicsview/qgraphicsscene.cpp index c459d21..f982f4b 100644 --- a/src/gui/graphicsview/qgraphicsscene.cpp +++ b/src/gui/graphicsview/qgraphicsscene.cpp @@ -525,6 +525,14 @@ void QGraphicsScenePrivate::removeItemHelper(QGraphicsItem *item) item->d_func()->scene = 0; + //We need to remove all children first because they might use their parent + //attributes (e.g. sceneTransform). + if (!item->d_ptr->inDestructor) { + // Remove all children recursively + for (int i = 0; i < item->d_ptr->children.size(); ++i) + q->removeItem(item->d_ptr->children.at(i)); + } + // Unregister focus proxy. item->d_ptr->resetFocusProxy(); @@ -571,12 +579,6 @@ void QGraphicsScenePrivate::removeItemHelper(QGraphicsItem *item) ++iterator; } - if (!item->d_ptr->inDestructor) { - // Remove all children recursively - for (int i = 0; i < item->d_ptr->children.size(); ++i) - q->removeItem(item->d_ptr->children.at(i)); - } - if (item->isPanel() && item->isVisible() && item->panelModality() != QGraphicsItem::NonModal) leaveModal(item); @@ -691,6 +693,7 @@ void QGraphicsScenePrivate::setFocusItemHelper(QGraphicsItem *item, focusItem = 0; sendEvent(lastFocusItem, &event); +#ifndef QT_NO_IM if (lastFocusItem && (lastFocusItem->flags() & QGraphicsItem::ItemAcceptsInputMethod)) { // Reset any visible preedit text @@ -706,6 +709,7 @@ void QGraphicsScenePrivate::setFocusItemHelper(QGraphicsItem *item, views.at(i)->inputContext()->reset(); } } +#endif //QT_NO_IM } if (item) { @@ -4910,7 +4914,7 @@ void QGraphicsScenePrivate::processDirtyItemsRecursive(QGraphicsItem *item, bool continue; } - if (item->d_ptr->paintedViewBoundingRectsNeedRepaint && !paintedViewBoundingRect.isEmpty()) { + if (item->d_ptr->paintedViewBoundingRectsNeedRepaint) { paintedViewBoundingRect.translate(viewPrivate->dirtyScrollOffset); if (!viewPrivate->updateRect(paintedViewBoundingRect)) paintedViewBoundingRect = QRect(-1, -1, -1, -1); // Outside viewport. diff --git a/src/gui/graphicsview/qgraphicstransform.cpp b/src/gui/graphicsview/qgraphicstransform.cpp index e2a3f08..83bc9e1 100644 --- a/src/gui/graphicsview/qgraphicstransform.cpp +++ b/src/gui/graphicsview/qgraphicstransform.cpp @@ -93,8 +93,8 @@ #include <QDebug> #include <QtCore/qmath.h> +#ifndef QT_NO_GRAPHICSVIEW QT_BEGIN_NAMESPACE - void QGraphicsTransformPrivate::setItem(QGraphicsItem *i) { if (item == i) @@ -565,3 +565,4 @@ void QGraphicsRotation::applyTo(QMatrix4x4 *matrix) const #include "moc_qgraphicstransform.cpp" QT_END_NAMESPACE +#endif //QT_NO_GRAPHICSVIEW diff --git a/src/gui/graphicsview/qgraphicstransform.h b/src/gui/graphicsview/qgraphicstransform.h index 58075aa..58e3077 100644 --- a/src/gui/graphicsview/qgraphicstransform.h +++ b/src/gui/graphicsview/qgraphicstransform.h @@ -47,6 +47,7 @@ #include <QtGui/QTransform> #include <QtGui/QMatrix4x4> +#ifndef QT_NO_GRAPHICSVIEW QT_BEGIN_HEADER QT_BEGIN_NAMESPACE @@ -150,5 +151,6 @@ private: QT_END_NAMESPACE QT_END_HEADER +#endif //QT_NO_GRAPHICSVIEW #endif // QFXTRANSFORM_H diff --git a/src/gui/graphicsview/qgraphicstransform_p.h b/src/gui/graphicsview/qgraphicstransform_p.h index 9e708b2..ddf99bb 100644 --- a/src/gui/graphicsview/qgraphicstransform_p.h +++ b/src/gui/graphicsview/qgraphicstransform_p.h @@ -54,7 +54,7 @@ // #include "private/qobject_p.h" - +#ifndef QT_NO_GRAPHICSVIEW QT_BEGIN_NAMESPACE class QGraphicsItem; @@ -73,5 +73,6 @@ public: }; QT_END_NAMESPACE +#endif //QT_NO_GRAPHCISVIEW #endif // QGRAPHICSTRANSFORM_P_H diff --git a/src/gui/graphicsview/qgraphicsview.cpp b/src/gui/graphicsview/qgraphicsview.cpp index 8945f51..c88f678 100644 --- a/src/gui/graphicsview/qgraphicsview.cpp +++ b/src/gui/graphicsview/qgraphicsview.cpp @@ -3325,6 +3325,14 @@ void QGraphicsView::paintEvent(QPaintEvent *event) if (!(d->optimizationFlags & IndirectPainting)) { d->scene->d_func()->drawItems(&painter, viewTransformed ? &viewTransform : 0, &d->exposedRegion, viewport()); + // Make sure the painter's world transform is restored correctly when + // drawing without painter state protection (DontSavePainterState). + // We only change the worldTransform() so there's no need to do a full-blown + // save() and restore(). Also note that we don't have to do this in case of + // IndirectPainting (the else branch), because in that case we always save() + // and restore() in QGraphicsScene::drawItems(). + if (!d->scene->d_func()->painterStateProtection) + painter.setWorldTransform(viewTransform); } else { // Find all exposed items bool allItems = false; diff --git a/src/gui/graphicsview/qgraphicswidget.cpp b/src/gui/graphicsview/qgraphicswidget.cpp index 35a3c13..d70a281 100644 --- a/src/gui/graphicsview/qgraphicswidget.cpp +++ b/src/gui/graphicsview/qgraphicswidget.cpp @@ -1352,6 +1352,8 @@ void QGraphicsWidget::changeEvent(QEvent *event) case QEvent::StyleChange: // ### Don't unset if the margins are explicitly set. unsetWindowFrameMargins(); + if (d->layout) + d->layout->invalidate(); case QEvent::FontChange: update(); updateGeometry(); diff --git a/src/gui/graphicsview/qgridlayoutengine_p.h b/src/gui/graphicsview/qgridlayoutengine_p.h index a42a537..ed335a8 100644 --- a/src/gui/graphicsview/qgridlayoutengine_p.h +++ b/src/gui/graphicsview/qgridlayoutengine_p.h @@ -59,7 +59,7 @@ #include "qmap.h" #include "qpair.h" #include "qvector.h" - +#include "qgraphicslayout_p.h" #include <float.h> QT_BEGIN_NAMESPACE @@ -128,29 +128,6 @@ public: }; -class QLayoutStyleInfo -{ -public: - inline QLayoutStyleInfo() { invalidate(); } - inline QLayoutStyleInfo(QStyle *style, QWidget *widget) - : q_valid(true), q_style(style), q_widget(widget) {} - - inline void invalidate() { q_valid = false; q_style = 0; q_widget = 0; } - - inline QStyle *style() const { return q_style; } - inline QWidget *widget() const { return q_widget; } - - inline bool operator==(const QLayoutStyleInfo &other) - { return q_style == other.q_style && q_widget == other.q_widget; } - inline bool operator!=(const QLayoutStyleInfo &other) - { return !(*this == other); } - -private: - bool q_valid; - QStyle *q_style; - QWidget *q_widget; -}; - class QGridLayoutBox { public: diff --git a/src/gui/graphicsview/qsimplex_p.cpp b/src/gui/graphicsview/qsimplex_p.cpp index b8f8fb4..86b10b4 100644 --- a/src/gui/graphicsview/qsimplex_p.cpp +++ b/src/gui/graphicsview/qsimplex_p.cpp @@ -108,10 +108,8 @@ void QSimplex::clearDataStructures() // 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; + delete constraints[i]; } constraints.clear(); @@ -137,7 +135,23 @@ bool QSimplex::setConstraints(const QList<QSimplexConstraint *> newConstraints) if (newConstraints.isEmpty()) return true; // we are ok with no constraints - constraints = newConstraints; + + // Make deep copy of constraints. We need this copy because we may change + // them in the simplification method. + for (int i = 0; i < newConstraints.size(); ++i) { + QSimplexConstraint *c = new QSimplexConstraint; + c->constant = newConstraints[i]->constant; + c->ratio = newConstraints[i]->ratio; + c->variables = newConstraints[i]->variables; + constraints << c; + } + + // Remove constraints of type Var == K and replace them for their value. + if (!simplifyConstraints(&constraints)) { + qWarning() << "QSimplex: No feasible solution!"; + clearDataStructures(); + return false; + } /////////////////////////////////////// // Prepare variables and constraints // @@ -508,11 +522,21 @@ qreal QSimplex::solver(solverFactor factor) // Remove old objective clearRow(0); - // Set new objective + // Set new objective in the first row of the simplex matrix + qreal resultOffset = 0; QHash<QSimplexVariable *, qreal>::const_iterator iter; for (iter = objective->variables.constBegin(); iter != objective->variables.constEnd(); ++iter) { + + // Check if the variable was removed in the simplification process. + // If so, we save its offset to the objective function and skip adding + // it to the matrix. + if (iter.key()->index == -1) { + resultOffset += iter.value() * iter.key()->result; + continue; + } + setValueAt(0, iter.key()->index, -1 * factor * iter.value()); } @@ -525,7 +549,9 @@ qreal QSimplex::solver(solverFactor factor) } #endif - return factor * valueAt(0, columns - 1); + // Return the value calculated by the simplex plus the value of the + // fixed variables. + return (factor * valueAt(0, columns - 1)) + resultOffset; } /*! @@ -571,4 +597,77 @@ void QSimplex::collectResults() } } +/*! + \internal + + Looks for single-valued variables and remove them from the constraints list. +*/ +bool QSimplex::simplifyConstraints(QList<QSimplexConstraint *> *constraints) +{ + QHash<QSimplexVariable *, qreal> results; // List of single-valued variables + bool modified = true; // Any chance more optimization exists? + + while (modified) { + modified = false; + + // For all constraints + QList<QSimplexConstraint *>::iterator iter = constraints->begin(); + while (iter != constraints->end()) { + QSimplexConstraint *c = *iter; + if ((c->ratio == QSimplexConstraint::Equal) && (c->variables.count() == 1)) { + // Check whether this is a constraint of type Var == K + // If so, save its value to "results". + QSimplexVariable *variable = c->variables.constBegin().key(); + qreal result = c->constant / c->variables.value(variable); + + results.insert(variable, result); + variable->result = result; + variable->index = -1; + modified = true; + + } + + // Replace known values among their variables + QHash<QSimplexVariable *, qreal>::const_iterator r; + for (r = results.constBegin(); r != results.constEnd(); ++r) { + if (c->variables.contains(r.key())) { + c->constant -= r.value() * c->variables.take(r.key()); + modified = true; + } + } + + // Keep it normalized + if (c->constant < 0) + c->invert(); + + if (c->variables.isEmpty()) { + // If constraint became empty due to substitution, delete it. + if (c->isSatisfied() == false) + // We must ensure that the constraint soon to be deleted would not + // make the problem unfeasible if left behind. If that's the case, + // we return false so the simplex solver can properly report that. + return false; + + delete c; + iter = constraints->erase(iter); + } else { + ++iter; + } + } + } + + return true; +} + +void QSimplexConstraint::invert() +{ + constant = -constant; + ratio = Ratio(2 - ratio); + + QHash<QSimplexVariable *, qreal>::iterator iter; + for (iter = variables.begin(); iter != variables.end(); ++iter) { + iter.value() = -iter.value(); + } +} + QT_END_NAMESPACE diff --git a/src/gui/graphicsview/qsimplex_p.h b/src/gui/graphicsview/qsimplex_p.h index 51991a9..084ad7f 100644 --- a/src/gui/graphicsview/qsimplex_p.h +++ b/src/gui/graphicsview/qsimplex_p.h @@ -63,7 +63,7 @@ struct QSimplexVariable QSimplexVariable() : result(0), index(0) {} qreal result; - uint index; + int index; }; @@ -95,7 +95,8 @@ struct QSimplexConstraint QPair<QSimplexVariable *, qreal> helper; QSimplexVariable * artificial; -#ifdef QT_DEBUG + void invert(); + bool isSatisfied() { qreal leftHandSide(0); @@ -106,7 +107,7 @@ struct QSimplexConstraint Q_ASSERT(constant > 0 || qFuzzyCompare(1, 1 + constant)); - if (qFuzzyCompare(1000 + leftHandSide, 1000 + constant)) + if ((leftHandSide == constant) || qFuzzyCompare(1000 + leftHandSide, 1000 + constant)) return true; switch (ratio) { @@ -118,6 +119,30 @@ struct QSimplexConstraint return false; } } + +#ifdef QT_DEBUG + QString toString() { + QString result; + result += QString::fromAscii("-- QSimplexConstraint %1 --").arg(quintptr(this), 0, 16); + + QHash<QSimplexVariable *, qreal>::const_iterator iter; + for (iter = variables.constBegin(); iter != variables.constEnd(); ++iter) { + result += QString::fromAscii(" %1 x %2").arg(iter.value()).arg(quintptr(iter.key()), 0, 16); + } + + switch (ratio) { + case LessOrEqual: + result += QString::fromAscii(" (less) <= %1").arg(constant); + break; + case MoreOrEqual: + result += QString::fromAscii(" (more) >= %1").arg(constant); + break; + default: + result += QString::fromAscii(" (eqal) == %1").arg(constant); + } + + return result; + } #endif }; @@ -129,7 +154,6 @@ public: qreal solveMin(); qreal solveMax(); - QList<QSimplexVariable *> constraintsVariables(); bool setConstraints(const QList<QSimplexConstraint *> constraints); void setObjective(QSimplexConstraint *objective); @@ -145,6 +169,7 @@ private: void combineRows(int toIndex, int fromIndex, qreal factor); // Simplex + bool simplifyConstraints(QList<QSimplexConstraint *> *constraints); int findPivotColumn(); int pivotRowForColumn(int column); void reducedRowEchelon(); @@ -168,11 +193,6 @@ private: qreal *matrix; }; -inline QList<QSimplexVariable *> QSimplex::constraintsVariables() -{ - return variables; -} - inline qreal QSimplex::valueAt(int rowIndex, int columnIndex) { return matrix[rowIndex * columns + columnIndex]; |