diff options
author | David Boddie <dboddie@trolltech.com> | 2009-12-14 18:55:43 (GMT) |
---|---|---|
committer | David Boddie <dboddie@trolltech.com> | 2009-12-14 18:55:43 (GMT) |
commit | a068f6dd6115b2f950892e5228efe10b7de57a00 (patch) | |
tree | c55c44b2e1b452838a7443d85ca2463ca1ec15bd /src/gui | |
parent | ad4ca2152c161f7023e5febcf9e90c0652b8e57d (diff) | |
parent | adba8e01f0069d240bf0b97175f54e38271acd19 (diff) | |
download | Qt-a068f6dd6115b2f950892e5228efe10b7de57a00.zip Qt-a068f6dd6115b2f950892e5228efe10b7de57a00.tar.gz Qt-a068f6dd6115b2f950892e5228efe10b7de57a00.tar.bz2 |
Merge branch '4.6' of git@scm.dev.nokia.troll.no:qt/oslo-staging-1 into 4.6
Diffstat (limited to 'src/gui')
32 files changed, 687 insertions, 331 deletions
diff --git a/src/gui/egl/qeglproperties.cpp b/src/gui/egl/qeglproperties.cpp index 2d37edb..4d4410a 100644 --- a/src/gui/egl/qeglproperties.cpp +++ b/src/gui/egl/qeglproperties.cpp @@ -229,6 +229,15 @@ void QEglProperties::setRenderableType(QEgl::API api) // reductions in complexity are possible. bool QEglProperties::reduceConfiguration() { + // EGL chooses configs with the highest color depth over + // those with smaller (but faster) lower color depths. One + // way around this is to set EGL_BUFFER_SIZE to 16, which + // trumps the others. Of course, there may not be a 16-bit + // config avaliable, so it's the first restraint we remove. + if (value(EGL_BUFFER_SIZE) == 16) { + removeValue(EGL_BUFFER_SIZE); + return true; + } if (removeValue(EGL_SAMPLE_BUFFERS)) { removeValue(EGL_SAMPLES); return true; diff --git a/src/gui/graphicsview/qgraph_p.h b/src/gui/graphicsview/qgraph_p.h index 0a2bf27..076b8fa 100644 --- a/src/gui/graphicsview/qgraph_p.h +++ b/src/gui/graphicsview/qgraph_p.h @@ -236,11 +236,13 @@ public: 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") + edges += QString::fromAscii("\"%1\"->\"%2\" [label=\"[%3,%4,%5,%6,%7]\" color=\"#000000\"] \n") .arg(v->toString()) .arg(v1->toString()) .arg(data->minSize) + .arg(data->minPrefSize) .arg(data->prefSize) + .arg(data->maxPrefSize) .arg(data->maxSize) ; } diff --git a/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp b/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp index a6f5992..03ed63d 100644 --- a/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp +++ b/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp @@ -49,20 +49,34 @@ #endif #include "qgraphicsanchorlayout_p.h" + #ifndef QT_NO_GRAPHICSVIEW QT_BEGIN_NAMESPACE +// To ensure that all variables inside the simplex solver are non-negative, +// we limit the size of anchors in the interval [-limit, limit]. Then before +// sending them to the simplex solver we add "limit" as an offset, so that +// they are actually calculated in the interval [0, 2 * limit] +// To avoid numerical errors in platforms where we use single precision, +// we use a tighter limit for the variables range. +const qreal g_offset = (sizeof(qreal) == sizeof(double)) ? QWIDGETSIZE_MAX : QWIDGETSIZE_MAX / 32; QGraphicsAnchorPrivate::QGraphicsAnchorPrivate(int version) : QObjectPrivate(version), layoutPrivate(0), data(0), sizePolicy(QSizePolicy::Fixed), preferredSize(0), - hasSize(true), reversed(false) + hasSize(true) { } QGraphicsAnchorPrivate::~QGraphicsAnchorPrivate() { - layoutPrivate->removeAnchor(data->from, data->to); + if (data) { + // The QGraphicsAnchor was already deleted at this moment. We must clean + // the dangling pointer to avoid double deletion in the AnchorData dtor. + data->graphicsAnchor = 0; + + layoutPrivate->removeAnchor(data->from, data->to); + } } void QGraphicsAnchorPrivate::setSizePolicy(QSizePolicy::Policy policy) @@ -80,27 +94,12 @@ void QGraphicsAnchorPrivate::setSpacing(qreal value) return; } - const qreal rawValue = reversed ? -preferredSize : preferredSize; - if (hasSize && (rawValue == value)) + if (hasSize && (preferredSize == value)) return; // The anchor has an user-defined size hasSize = true; - - // The simplex solver cannot handle negative sizes. To workaround that, - // if value is less than zero, we reverse the anchor and set the absolute - // value; - if (value >= 0) { - preferredSize = value; - if (reversed) - qSwap(data->from, data->to); - reversed = false; - } else { - preferredSize = -value; - if (!reversed) - qSwap(data->from, data->to); - reversed = true; - } + preferredSize = value; layoutPrivate->q_func()->invalidate(); } @@ -114,9 +113,6 @@ void QGraphicsAnchorPrivate::unsetSpacing() // Return to standard direction hasSize = false; - if (reversed) - qSwap(data->from, data->to); - reversed = false; layoutPrivate->q_func()->invalidate(); } @@ -128,14 +124,14 @@ qreal QGraphicsAnchorPrivate::spacing() const return 0; } - return reversed ? -preferredSize : preferredSize; + return preferredSize; } -static void internalSizeHints(QSizePolicy::Policy policy, - qreal minSizeHint, qreal prefSizeHint, qreal maxSizeHint, - qreal *minSize, qreal *prefSize, - qreal *maxSize) +static void applySizePolicy(QSizePolicy::Policy policy, + qreal minSizeHint, qreal prefSizeHint, qreal maxSizeHint, + qreal *minSize, qreal *prefSize, + qreal *maxSize) { // minSize, prefSize and maxSize are initialized // with item's preferred Size: this is QSizePolicy::Fixed. @@ -167,6 +163,18 @@ static void internalSizeHints(QSizePolicy::Policy policy, *prefSize = prefSizeHint; } +AnchorData::~AnchorData() +{ + if (graphicsAnchor) { + // Remove reference to ourself to avoid double removal in + // QGraphicsAnchorPrivate dtor. + graphicsAnchor->d_func()->data = 0; + + delete graphicsAnchor; + } +} + + void AnchorData::refreshSizeHints(const QLayoutStyleInfo *styleInfo) { QSizePolicy::Policy policy; @@ -182,6 +190,9 @@ void AnchorData::refreshSizeHints(const QLayoutStyleInfo *styleInfo) maxSize = QWIDGETSIZE_MAX; if (isCenterAnchor) maxSize /= 2; + + minPrefSize = prefSize; + maxPrefSize = maxSize; return; } else { if (orientation == QGraphicsAnchorLayoutPrivate::Horizontal) { @@ -206,14 +217,18 @@ void AnchorData::refreshSizeHints(const QLayoutStyleInfo *styleInfo) // It is a user-created anchor, fetch size information from the associated QGraphicsAnchor Q_ASSERT(graphicsAnchor); QGraphicsAnchorPrivate *anchorPrivate = graphicsAnchor->d_func(); + + // Policy, min and max sizes are straightforward policy = anchorPrivate->sizePolicy; minSizeHint = 0; + maxSizeHint = QWIDGETSIZE_MAX; + + // Preferred Size if (anchorPrivate->hasSize) { - // One can only configure the preferred size of a normal anchor. Their minimum and - // maximum "size hints" are always 0 and QWIDGETSIZE_MAX, correspondingly. However, - // their effective size hints might be narrowed down due to their size policies. + // Anchor has user-defined size prefSizeHint = anchorPrivate->preferredSize; } else { + // Fetch size information from style const Qt::Orientation orient = Qt::Orientation(QGraphicsAnchorLayoutPrivate::edgeOrientation(from->m_edge) + 1); qreal s = styleInfo->defaultSpacing(orient); if (s < 0) { @@ -229,10 +244,14 @@ void AnchorData::refreshSizeHints(const QLayoutStyleInfo *styleInfo) } prefSizeHint = s; } - maxSizeHint = QWIDGETSIZE_MAX; } - internalSizeHints(policy, minSizeHint, prefSizeHint, maxSizeHint, - &minSize, &prefSize, &maxSize); + + // Fill minSize, prefSize and maxSize based on policy and sizeHints + applySizePolicy(policy, minSizeHint, prefSizeHint, maxSizeHint, + &minSize, &prefSize, &maxSize); + + minPrefSize = prefSize; + maxPrefSize = maxSize; // Set the anchor effective sizes to preferred. // @@ -252,13 +271,7 @@ void ParallelAnchorData::updateChildrenSizes() firstEdge->sizeAtPreferred = sizeAtPreferred; firstEdge->sizeAtMaximum = sizeAtMaximum; - // We have the convention that the first children will define the direction of the - // pararell group. So we can check whether the second edge is "forward" in relation - // to the group if it have the same direction as the first edge. Note that we don't - // use 'this->from' because it might be changed by vertex simplification. - const bool secondForward = (firstEdge->from == secondEdge->from); - - if (secondForward) { + if (secondForward()) { secondEdge->sizeAtMinimum = sizeAtMinimum; secondEdge->sizeAtPreferred = sizeAtPreferred; secondEdge->sizeAtMaximum = sizeAtMaximum; @@ -272,21 +285,40 @@ void ParallelAnchorData::updateChildrenSizes() secondEdge->updateChildrenSizes(); } -bool ParallelAnchorData::calculateSizeHints() -{ - // Note that parallel groups can lead to unfeasibility, so during calculation, we can - // find out one unfeasibility. Because of that this method return boolean. This can't - // happen in sequential, so there the method is void. +/* + \internal - // Account for parallel anchors where the second edge is backwards. - // We rely on the fact that a forward anchor of sizes min, pref, max is equivalent - // to a backwards anchor of size (-max, -pref, -min) + Initialize the parallel anchor size hints using the sizeHint information from + its children. - // Also see comments in updateChildrenSizes(). - const bool secondForward = (firstEdge->from == secondEdge->from); - const qreal secondMin = secondForward ? secondEdge->minSize : -secondEdge->maxSize; - const qreal secondPref = secondForward ? secondEdge->prefSize : -secondEdge->prefSize; - const qreal secondMax = secondForward ? secondEdge->maxSize : -secondEdge->minSize; + Note that parallel groups can lead to unfeasibility, so during calculation, we can + find out one unfeasibility. Because of that this method return boolean. This can't + happen in sequential, so there the method is void. + */ +bool ParallelAnchorData::calculateSizeHints() +{ + // Normalize second child sizes. + // A negative anchor of sizes min, minPref, pref, maxPref and max, is equivalent + // to a forward anchor of sizes -max, -maxPref, -pref, -minPref, -min + qreal secondMin; + qreal secondMinPref; + qreal secondPref; + qreal secondMaxPref; + qreal secondMax; + + if (secondForward()) { + secondMin = secondEdge->minSize; + secondMinPref = secondEdge->minPrefSize; + secondPref = secondEdge->prefSize; + secondMaxPref = secondEdge->maxPrefSize; + secondMax = secondEdge->maxSize; + } else { + secondMin = -secondEdge->maxSize; + secondMinPref = -secondEdge->maxPrefSize; + secondPref = -secondEdge->prefSize; + secondMaxPref = -secondEdge->minPrefSize; + secondMax = -secondEdge->minSize; + } minSize = qMax(firstEdge->minSize, secondMin); maxSize = qMin(firstEdge->maxSize, secondMax); @@ -298,23 +330,72 @@ bool ParallelAnchorData::calculateSizeHints() return false; } - // The equivalent preferred Size of a parallel anchor is calculated as to - // reduce the deviation from the original preferred sizes _and_ to avoid shrinking - // items below their preferred sizes, unless strictly needed. - - // ### This logic only holds if all anchors in the layout are "well-behaved" in the - // following terms: + // Preferred size calculation + // The calculation of preferred size is done as follows: + // + // 1) Check whether one of the child anchors is the layout structural anchor + // If so, we can simply copy the preferred information from the other child, + // after bounding it to our minimum and maximum sizes. + // If not, then we proceed with the actual calculations. // - // - There are no negative-sized anchors - // - All sequential anchors are composed of children in the same direction as the - // sequential anchor itself + // 2) The whole algorithm for preferred size calculation is based on the fact + // that, if a given anchor cannot remain at its preferred size, it'd rather + // grow than shrink. // - // With these assumptions we can grow a child knowing that no hidden items will - // have to shrink as the result of that. - // If any of these does not hold, we have a situation where the ParallelAnchor - // does not have enough information to calculate its equivalent prefSize. - prefSize = qMax(firstEdge->prefSize, secondPref); - prefSize = qMin(prefSize, maxSize); + // What happens though is that while this affirmative is true for simple + // anchors, it may not be true for sequential anchors that have one or more + // reversed anchors inside it. That happens because when a sequential anchor + // grows, any reversed anchors inside it may be required to shrink, something + // we try to avoid, as said above. + // + // To overcome this, besides their actual preferred size "prefSize", each anchor + // exports what we call "minPrefSize" and "maxPrefSize". These two values define + // a surrounding interval where, if required to move, the anchor would rather + // remain inside. + // + // For standard anchors, this area simply represents the region between + // prefSize and maxSize, which makes sense since our first affirmation. + // For composed anchors, these values are calculated as to reduce the global + // "damage", that is, to reduce the total deviation and the total amount of + // anchors that had to shrink. + + if (firstEdge->isLayoutAnchor) { + prefSize = qBound(minSize, secondPref, maxSize); + minPrefSize = qBound(minSize, secondMinPref, maxSize); + maxPrefSize = qBound(minSize, secondMaxPref, maxSize); + } else if (secondEdge->isLayoutAnchor) { + prefSize = qBound(minSize, firstEdge->prefSize, maxSize); + minPrefSize = qBound(minSize, firstEdge->minPrefSize, maxSize); + maxPrefSize = qBound(minSize, firstEdge->maxPrefSize, maxSize); + } else { + // Calculate the intersection between the "preferred" regions of each child + const qreal lowerBoundary = + qBound(minSize, qMax(firstEdge->minPrefSize, secondMinPref), maxSize); + const qreal upperBoundary = + qBound(minSize, qMin(firstEdge->maxPrefSize, secondMaxPref), maxSize); + const qreal prefMean = + qBound(minSize, (firstEdge->prefSize + secondPref) / 2, maxSize); + + if (lowerBoundary < upperBoundary) { + // If there is an intersection between the two regions, this intersection + // will be used as the preferred region of the parallel anchor itself. + // The preferred size will be the bounded average between the two preferred + // sizes. + prefSize = qBound(lowerBoundary, prefMean, upperBoundary); + minPrefSize = lowerBoundary; + maxPrefSize = upperBoundary; + } else { + // If there is no intersection, we have to attribute "damage" to at least + // one of the children. The minimum total damage is achieved in points + // inside the region that extends from (1) the upper boundary of the lower + // region to (2) the lower boundary of the upper region. + // Then, we expose this region as _our_ preferred region and once again, + // use the bounded average as our preferred size. + prefSize = qBound(upperBoundary, prefMean, lowerBoundary); + minPrefSize = upperBoundary; + maxPrefSize = lowerBoundary; + } + } // See comment in AnchorData::refreshSizeHints() about sizeAt* values sizeAtMinimum = prefSize; @@ -332,19 +413,28 @@ bool ParallelAnchorData::calculateSizeHints() 1 is at Maximum */ static QPair<QGraphicsAnchorLayoutPrivate::Interval, qreal> getFactor(qreal value, qreal min, - qreal pref, qreal max) + qreal minPref, qreal pref, + qreal maxPref, qreal max) { QGraphicsAnchorLayoutPrivate::Interval interval; qreal lower; qreal upper; - if (value < pref) { - interval = QGraphicsAnchorLayoutPrivate::MinToPreferred; + if (value < minPref) { + interval = QGraphicsAnchorLayoutPrivate::MinimumToMinPreferred; lower = min; + upper = minPref; + } else if (value < pref) { + interval = QGraphicsAnchorLayoutPrivate::MinPreferredToPreferred; + lower = minPref; upper = pref; - } else { - interval = QGraphicsAnchorLayoutPrivate::PreferredToMax; + } else if (value < maxPref) { + interval = QGraphicsAnchorLayoutPrivate::PreferredToMaxPreferred; lower = pref; + upper = maxPref; + } else { + interval = QGraphicsAnchorLayoutPrivate::MaxPreferredToMaximum; + lower = maxPref; upper = max; } @@ -359,19 +449,26 @@ static QPair<QGraphicsAnchorLayoutPrivate::Interval, qreal> getFactor(qreal valu } static qreal interpolate(const QPair<QGraphicsAnchorLayoutPrivate::Interval, qreal> &factor, - qreal min, qreal pref, - qreal max) + qreal min, qreal minPref, qreal pref, qreal maxPref, qreal max) { qreal lower; qreal upper; switch (factor.first) { - case QGraphicsAnchorLayoutPrivate::MinToPreferred: + case QGraphicsAnchorLayoutPrivate::MinimumToMinPreferred: lower = min; + upper = minPref; + break; + case QGraphicsAnchorLayoutPrivate::MinPreferredToPreferred: + lower = minPref; upper = pref; break; - case QGraphicsAnchorLayoutPrivate::PreferredToMax: + case QGraphicsAnchorLayoutPrivate::PreferredToMaxPreferred: lower = pref; + upper = maxPref; + break; + case QGraphicsAnchorLayoutPrivate::MaxPreferredToMaximum: + lower = maxPref; upper = max; break; } @@ -381,34 +478,43 @@ static qreal interpolate(const QPair<QGraphicsAnchorLayoutPrivate::Interval, qre 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 || qAbs(sizeAtMinimum - minSize) < 0.00000001); - Q_ASSERT(sizeAtPreferred > minSize || qAbs(sizeAtPreferred - minSize) < 0.00000001); - Q_ASSERT(sizeAtMaximum > minSize || qAbs(sizeAtMaximum - minSize) < 0.00000001); - - // These may be false if this anchor was in parallel with the layout stucture - // Q_ASSERT(sizeAtMinimum < maxSize || qAbs(sizeAtMinimum - maxSize) < 0.00000001); - // Q_ASSERT(sizeAtPreferred < maxSize || qAbs(sizeAtPreferred - maxSize) < 0.00000001); - // Q_ASSERT(sizeAtMaximum < maxSize || qAbs(sizeAtMaximum - maxSize) < 0.00000001); - // Band here refers if the value is in the Minimum To Preferred // band (the lower band) or the Preferred To Maximum (the upper band). const QPair<QGraphicsAnchorLayoutPrivate::Interval, qreal> minFactor = - getFactor(sizeAtMinimum, minSize, prefSize, maxSize); + getFactor(sizeAtMinimum, minSize, minPrefSize, prefSize, maxPrefSize, maxSize); const QPair<QGraphicsAnchorLayoutPrivate::Interval, qreal> prefFactor = - getFactor(sizeAtPreferred, minSize, prefSize, maxSize); + getFactor(sizeAtPreferred, minSize, minPrefSize, prefSize, maxPrefSize, maxSize); const QPair<QGraphicsAnchorLayoutPrivate::Interval, qreal> maxFactor = - getFactor(sizeAtMaximum, minSize, prefSize, maxSize); + getFactor(sizeAtMaximum, minSize, minPrefSize, prefSize, maxPrefSize, maxSize); + + // XXX This is not safe if Vertex simplification takes place after the sequential + // anchor is created. In that case, "prev" will be a group-vertex, different from + // "from" or "to", that _contains_ one of them. + AnchorVertex *prev = from; for (int i = 0; i < m_edges.count(); ++i) { AnchorData *e = m_edges.at(i); - e->sizeAtMinimum = interpolate(minFactor, e->minSize, e->prefSize, e->maxSize); - e->sizeAtPreferred = interpolate(prefFactor, e->minSize, e->prefSize, e->maxSize); - e->sizeAtMaximum = interpolate(maxFactor, e->minSize, e->prefSize, e->maxSize); + const bool edgeIsForward = (e->from == prev); + if (edgeIsForward) { + e->sizeAtMinimum = interpolate(minFactor, e->minSize, e->minPrefSize, + e->prefSize, e->maxPrefSize, e->maxSize); + e->sizeAtPreferred = interpolate(prefFactor, e->minSize, e->minPrefSize, + e->prefSize, e->maxPrefSize, e->maxSize); + e->sizeAtMaximum = interpolate(maxFactor, e->minSize, e->minPrefSize, + e->prefSize, e->maxPrefSize, e->maxSize); + prev = e->to; + } else { + Q_ASSERT(prev == e->to); + e->sizeAtMinimum = interpolate(minFactor, e->maxSize, e->maxPrefSize, + e->prefSize, e->minPrefSize, e->minSize); + e->sizeAtPreferred = interpolate(prefFactor, e->maxSize, e->maxPrefSize, + e->prefSize, e->minPrefSize, e->minSize); + e->sizeAtMaximum = interpolate(maxFactor, e->maxSize, e->maxPrefSize, + e->prefSize, e->minPrefSize, e->minSize); + prev = e->from; + } e->updateChildrenSizes(); } @@ -419,12 +525,31 @@ void SequentialAnchorData::calculateSizeHints() minSize = 0; prefSize = 0; maxSize = 0; + minPrefSize = 0; + maxPrefSize = 0; + + AnchorVertex *prev = from; for (int i = 0; i < m_edges.count(); ++i) { AnchorData *edge = m_edges.at(i); - minSize += edge->minSize; - prefSize += edge->prefSize; - maxSize += edge->maxSize; + + const bool edgeIsForward = (edge->from == prev); + if (edgeIsForward) { + minSize += edge->minSize; + prefSize += edge->prefSize; + maxSize += edge->maxSize; + minPrefSize += edge->minPrefSize; + maxPrefSize += edge->maxPrefSize; + prev = edge->to; + } else { + Q_ASSERT(prev == edge->to); + minSize -= edge->maxSize; + prefSize -= edge->prefSize; + maxSize -= edge->minSize; + minPrefSize -= edge->maxPrefSize; + maxPrefSize -= edge->minPrefSize; + prev = edge->from; + } } // See comment in AnchorData::refreshSizeHints() about sizeAt* values @@ -588,16 +713,25 @@ AnchorData *QGraphicsAnchorLayoutPrivate::addAnchorMaybeParallel(AnchorData *new AnchorData *child = children[i]; QList<QSimplexConstraint *> *childConstraints = childrenConstraints[i]; + // We need to fix the second child constraints if the parallel group will have the + // opposite direction of the second child anchor. For the point of view of external + // entities, this anchor was reversed. So if at some point we say that the parallel + // has a value of 20, this mean that the second child (when reversed) will be + // assigned -20. + const bool needsReverse = i == 1 && !parallel->secondForward(); + if (!child->isCenterAnchor) continue; parallel->isCenterAnchor = true; - for (int i = 0; i < constraints.count(); ++i) { - QSimplexConstraint *c = constraints[i]; + for (int j = 0; j < constraints.count(); ++j) { + QSimplexConstraint *c = constraints[j]; if (c->variables.contains(child)) { childConstraints->append(c); qreal v = c->variables.take(child); + if (needsReverse) + v *= -1; c->variables.insert(parallel, v); } } @@ -628,24 +762,10 @@ static AnchorData *createSequence(Graph<AnchorVertex, AnchorData> *graph, const QVector<AnchorVertex*> &vertices, AnchorVertex *after) { - AnchorData *data = graph->edgeData(before, vertices.first()); - Q_ASSERT(data); - - const bool forward = (before == data->from); - QVector<AnchorVertex *> orderedVertices; - - if (forward) { - orderedVertices = vertices; - } else { - qSwap(before, after); - for (int i = vertices.count() - 1; i >= 0; --i) - orderedVertices.append(vertices.at(i)); - } - #if defined(QT_DEBUG) && 0 QString strVertices; - for (int i = 0; i < orderedVertices.count(); ++i) { - strVertices += QString::fromAscii("%1 - ").arg(orderedVertices.at(i)->toString()); + for (int i = 0; i < vertices.count(); ++i) { + strVertices += QString::fromAscii("%1 - ").arg(vertices.at(i)->toString()); } QString strPath = QString::fromAscii("%1 - %2%3").arg(before->toString(), strVertices, after->toString()); qDebug("simplifying [%s] to [%s - %s]", qPrintable(strPath), qPrintable(before->toString()), qPrintable(after->toString())); @@ -654,15 +774,22 @@ static AnchorData *createSequence(Graph<AnchorVertex, AnchorData> *graph, AnchorVertex *prev = before; QVector<AnchorData *> edges; - for (int i = 0; i <= orderedVertices.count(); ++i) { - AnchorVertex *next = (i < orderedVertices.count()) ? orderedVertices.at(i) : after; + // Take from the graph, the edges that will be simplificated + for (int i = 0; i < vertices.count(); ++i) { + AnchorVertex *next = vertices.at(i); AnchorData *ad = graph->takeEdge(prev, next); Q_ASSERT(ad); edges.append(ad); prev = next; } - SequentialAnchorData *sequence = new SequentialAnchorData(orderedVertices, edges); + // Take the last edge (not covered in the loop above) + AnchorData *ad = graph->takeEdge(vertices.last(), after); + Q_ASSERT(ad); + edges.append(ad); + + // Create sequence + SequentialAnchorData *sequence = new SequentialAnchorData(vertices, edges); sequence->from = before; sequence->to = after; @@ -922,7 +1049,6 @@ bool QGraphicsAnchorLayoutPrivate::simplifyGraphIteration(QGraphicsAnchorLayoutP QStack<QPair<AnchorVertex *, AnchorVertex *> > stack; stack.push(qMakePair(static_cast<AnchorVertex *>(0), layoutFirstVertex[orientation])); QVector<AnchorVertex*> candidates; - bool candidatesForward = true; // Walk depth-first, in the stack we store start of the candidate sequence (beforeSequence) // and the vertex to be visited. @@ -938,9 +1064,8 @@ bool QGraphicsAnchorLayoutPrivate::simplifyGraphIteration(QGraphicsAnchorLayoutP // A vertex can trigger an end of sequence if // (a) it is a layout vertex, we don't simplify away the layout vertices; // (b) it does not have exactly 2 adjacents; - // (c) it will change the direction of the sequence; - // (d) its next adjacent is already visited (a cycle in the graph); - // (e) the next anchor is a center anchor. + // (c) its next adjacent is already visited (a cycle in the graph). + // (d) the next anchor is a center anchor. const QList<AnchorVertex *> &adjacents = g.adjacentVertices(v); const bool isLayoutVertex = v->m_item == q; @@ -955,19 +1080,10 @@ bool QGraphicsAnchorLayoutPrivate::simplifyGraphIteration(QGraphicsAnchorLayoutP endOfSequence = isLayoutVertex || adjacents.count() != 2; if (!endOfSequence) { - // If this is the first vertice, determine what is the direction to use for this - // sequence. - if (candidates.isEmpty()) { - const AnchorData *data = g.edgeData(beforeSequence, v); - Q_ASSERT(data); - candidatesForward = (beforeSequence == data->from); - } - // This is a tricky part. We peek at the next vertex to find out whether // - // - the edge from this vertex to the next vertex has the same direction; - // - we already visited the next vertex; - // - the next anchor is a center. + // - we already visited the next vertex (c); + // - the next anchor is a center (d). // // Those are needed to identify the remaining end of sequence cases. Note that unlike // (a) and (b), we preempt the end of sequence by looking into the next vertex. @@ -985,22 +1101,17 @@ bool QGraphicsAnchorLayoutPrivate::simplifyGraphIteration(QGraphicsAnchorLayoutP const AnchorData *data = g.edgeData(v, after); Q_ASSERT(data); - const bool willChangeDirection = (candidatesForward != (v == data->from)); const bool cycleFound = visited.contains(after); - // Now cases (c), (d) and (e)... - endOfSequence = willChangeDirection || cycleFound || data->isCenterAnchor; + // Now cases (c) and (d)... + endOfSequence = cycleFound || data->isCenterAnchor; - if (endOfSequence) { - if (!willChangeDirection) { - // If the direction will not change, we can add the current vertex to the - // candidates list and we know that 'after' can be used as afterSequence. - candidates.append(v); - afterSequence = after; - } - } else { + if (!endOfSequence) { // If it's not an end of sequence, then the vertex didn't trigger neither of the - // previously four cases, so it can be added to the candidates list. + // previously three cases, so it can be added to the candidates list. + candidates.append(v); + } else if (cycleFound && (beforeSequence != after)) { + afterSequence = after; candidates.append(v); } } @@ -1143,9 +1254,15 @@ void QGraphicsAnchorLayoutPrivate::restoreSimplifiedConstraints(ParallelAnchorDa c->variables.insert(parallel->firstEdge, v); } + // When restoring, we might have to revert constraints back. See comments on + // addAnchorMaybeParallel(). + const bool needsReverse = !parallel->secondForward(); + for (int i = 0; i < parallel->m_secondConstraints.count(); ++i) { QSimplexConstraint *c = parallel->m_secondConstraints.at(i); qreal v = c->variables[parallel]; + if (needsReverse) + v *= -1; c->variables.remove(parallel); c->variables.insert(parallel->secondEdge, v); } @@ -1187,7 +1304,22 @@ void QGraphicsAnchorLayoutPrivate::restoreVertices(Orientation orientation) Graph<AnchorVertex, AnchorData> &g = graph[orientation]; QList<AnchorVertexPair *> &toRestore = simplifiedVertices[orientation]; - // We will restore the vertices in the inverse order of creation, this way we ensure that + // Since we keep a list of parallel anchors and vertices that were created during vertex + // simplification, we can now iterate on those lists instead of traversing the graph + // recursively. + + // First, restore the constraints changed when we created parallel anchors. Note that this + // works at this point because the constraints doesn't depend on vertex information and at + // this point it's always safe to identify whether the second child is forward or backwards. + // In the next step, we'll change the anchors vertices so that would not be possible anymore. + QList<AnchorData *> ¶llelAnchors = anchorsFromSimplifiedVertices[orientation]; + + for (int i = parallelAnchors.count() - 1; i >= 0; --i) { + ParallelAnchorData *parallel = static_cast<ParallelAnchorData *>(parallelAnchors.at(i)); + restoreSimplifiedConstraints(parallel); + } + + // Then, we will restore the vertices in the inverse order of creation, this way we ensure that // the vertex being restored was not wrapped by another simplification. for (int i = toRestore.count() - 1; i >= 0; --i) { AnchorVertexPair *pair = toRestore.at(i); @@ -1231,20 +1363,9 @@ void QGraphicsAnchorLayoutPrivate::restoreVertices(Orientation orientation) delete pair; } - toRestore.clear(); - - // The restoration process for vertex simplification also restored the effect of the - // parallel anchors created during vertex simplification, so we just need to restore - // the constraints in case of parallels that contain center anchors. For the same - // reason as above, order matters here. - QList<AnchorData *> ¶llelAnchors = anchorsFromSimplifiedVertices[orientation]; - - for (int i = parallelAnchors.count() - 1; i >= 0; --i) { - ParallelAnchorData *parallel = static_cast<ParallelAnchorData *>(parallelAnchors.at(i)); - restoreSimplifiedConstraints(parallel); - delete parallel; - } + qDeleteAll(parallelAnchors); parallelAnchors.clear(); + toRestore.clear(); } QGraphicsAnchorLayoutPrivate::Orientation @@ -1652,6 +1773,10 @@ QGraphicsAnchor *QGraphicsAnchorLayoutPrivate::getAnchor(QGraphicsLayoutItem *fi QGraphicsLayoutItem *secondItem, Qt::AnchorPoint secondEdge) { + // Do not expose internal anchors + if (firstItem == secondItem) + return 0; + const Orientation orientation = edgeOrientation(firstEdge); AnchorVertex *v1 = internalVertex(firstItem, firstEdge); AnchorVertex *v2 = internalVertex(secondItem, secondEdge); @@ -1659,8 +1784,16 @@ QGraphicsAnchor *QGraphicsAnchorLayoutPrivate::getAnchor(QGraphicsLayoutItem *fi QGraphicsAnchor *graphicsAnchor = 0; AnchorData *data = graph[orientation].edgeData(v1, v2); - if (data) - graphicsAnchor = acquireGraphicsAnchor(data); + if (data) { + // We could use "acquireGraphicsAnchor" here, but to avoid a regression where + // an internal anchor was wrongly exposed, I want to ensure no new + // QGraphicsAnchor instances are created by this call. + // This assumption must hold because anchors are either user-created (and already + // have their public object created), or they are internal (and must not reach + // this point). + Q_ASSERT(data->graphicsAnchor); + graphicsAnchor = data->graphicsAnchor; + } return graphicsAnchor; } @@ -1675,12 +1808,16 @@ void QGraphicsAnchorLayoutPrivate::removeAnchor(AnchorVertex *firstVertex, { Q_Q(QGraphicsAnchorLayout); - // Actually delete the anchor - removeAnchor_helper(firstVertex, secondVertex); - + // Save references to items while it's safe to assume the vertices exist QGraphicsLayoutItem *firstItem = firstVertex->m_item; QGraphicsLayoutItem *secondItem = secondVertex->m_item; + // Delete the anchor (may trigger deletion of center vertices) + removeAnchor_helper(firstVertex, secondVertex); + + // Ensure no dangling pointer is left behind + firstVertex = secondVertex = 0; + // Checking if the item stays in the layout or not bool keepFirstItem = false; bool keepSecondItem = false; @@ -2047,6 +2184,25 @@ void QGraphicsAnchorLayoutPrivate::calculateGraphs( /*! \internal + Shift all the constraints by a certain amount. This allows us to deal with negative values in + the linear program if they are bounded by a certain limit. Functions should be careful to + call it again with a negative amount, to shift the constraints back. +*/ +static void shiftConstraints(const QList<QSimplexConstraint *> &constraints, qreal amount) +{ + for (int i = 0; i < constraints.count(); ++i) { + QSimplexConstraint *c = constraints.at(i); + qreal multiplier = 0; + foreach (qreal v, c->variables.values()) { + multiplier += v; + } + c->constant += multiplier * amount; + } +} + +/*! + \internal + Calculate the sizes for all anchors which are part of the trunk. This works on top of a (possibly) simplified graph. */ @@ -2067,12 +2223,14 @@ bool QGraphicsAnchorLayoutPrivate::calculateTrunk(Orientation orientation, const QList<QSimplexConstraint *> sizeHintConstraints = constraintsFromSizeHints(variables); QList<QSimplexConstraint *> allConstraints = constraints + sizeHintConstraints; + shiftConstraints(allConstraints, g_offset); + // Solve min and max size hints qreal min, max; feasible = solveMinMax(allConstraints, path, &min, &max); if (feasible) { - solvePreferred(allConstraints, variables); + solvePreferred(constraints, variables); // Calculate and set the preferred size for the layout, // from the edge sizes that were calculated above. @@ -2090,6 +2248,7 @@ bool QGraphicsAnchorLayoutPrivate::calculateTrunk(Orientation orientation, const } qDeleteAll(sizeHintConstraints); + shiftConstraints(constraints, -g_offset); } else { // No Simplex is necessary because the path was simplified all the way to a single @@ -2120,8 +2279,8 @@ bool QGraphicsAnchorLayoutPrivate::calculateTrunk(Orientation orientation, const bool QGraphicsAnchorLayoutPrivate::calculateNonTrunk(const QList<QSimplexConstraint *> &constraints, const QList<AnchorData *> &variables) { - QList<QSimplexConstraint *> sizeHintConstraints = constraintsFromSizeHints(variables); - bool feasible = solvePreferred(constraints + sizeHintConstraints, variables); + shiftConstraints(constraints, g_offset); + bool feasible = solvePreferred(constraints, variables); if (feasible) { // Propagate size at preferred to other sizes. Semi-floats always will be @@ -2134,7 +2293,7 @@ bool QGraphicsAnchorLayoutPrivate::calculateNonTrunk(const QList<QSimplexConstra } } - qDeleteAll(sizeHintConstraints); + shiftConstraints(constraints, -g_offset); return feasible; } @@ -2298,17 +2457,23 @@ QList<QSimplexConstraint *> QGraphicsAnchorLayoutPrivate::constraintsFromSizeHin if (ad->dependency == AnchorData::Slave) continue; - if ((ad->minSize == ad->maxSize) || qFuzzyCompare(ad->minSize, ad->maxSize)) { + // To use negative variables inside simplex, we shift them so the minimum negative value is + // mapped to zero before solving. To make sure that it works, we need to guarantee that the + // variables are all inside a certain boundary. + qreal boundedMin = qBound(-g_offset, ad->minSize, g_offset); + qreal boundedMax = qBound(-g_offset, ad->maxSize, g_offset); + + if ((boundedMin == boundedMax) || qFuzzyCompare(boundedMin, boundedMax)) { QSimplexConstraint *c = new QSimplexConstraint; c->variables.insert(ad, 1.0); - c->constant = ad->minSize; + c->constant = boundedMin; c->ratio = QSimplexConstraint::Equal; anchorConstraints += c; unboundedProblem = false; } else { QSimplexConstraint *c = new QSimplexConstraint; c->variables.insert(ad, 1.0); - c->constant = ad->minSize; + c->constant = boundedMin; c->ratio = QSimplexConstraint::MoreOrEqual; anchorConstraints += c; @@ -2320,7 +2485,7 @@ QList<QSimplexConstraint *> QGraphicsAnchorLayoutPrivate::constraintsFromSizeHin c = new QSimplexConstraint; c->variables.insert(ad, 1.0); - c->constant = ad->maxSize; + c->constant = boundedMax; c->ratio = QSimplexConstraint::LessOrEqual; anchorConstraints += c; unboundedProblem = false; @@ -2331,7 +2496,8 @@ QList<QSimplexConstraint *> QGraphicsAnchorLayoutPrivate::constraintsFromSizeHin if (unboundedProblem) { QSimplexConstraint *c = new QSimplexConstraint; c->variables.insert(layoutEdge, 1.0); - c->constant = QWIDGETSIZE_MAX; + // The maximum size that the layout can take + c->constant = g_offset; c->ratio = QSimplexConstraint::LessOrEqual; anchorConstraints += c; } @@ -2597,6 +2763,8 @@ void QGraphicsAnchorLayoutPrivate::setupEdgesInterpolation( result = getFactor(current, sizeHints[orientation][Qt::MinimumSize], sizeHints[orientation][Qt::PreferredSize], + sizeHints[orientation][Qt::PreferredSize], + sizeHints[orientation][Qt::PreferredSize], sizeHints[orientation][Qt::MaximumSize]); interpolationInterval[orientation] = result.first; @@ -2625,6 +2793,7 @@ void QGraphicsAnchorLayoutPrivate::interpolateEdge(AnchorVertex *base, AnchorDat interpolationProgress[orientation]); qreal edgeDistance = interpolate(factor, edge->sizeAtMinimum, edge->sizeAtPreferred, + edge->sizeAtPreferred, edge->sizeAtPreferred, edge->sizeAtMaximum); Q_ASSERT(edge->from == base || edge->to == base); @@ -2652,34 +2821,46 @@ bool QGraphicsAnchorLayoutPrivate::solveMinMax(const QList<QSimplexConstraint *> for (iter = path.negatives.constBegin(); iter != path.negatives.constEnd(); ++iter) objective.variables.insert(*iter, -1.0); + const qreal objectiveOffset = (path.positives.count() - path.negatives.count()) * g_offset; simplex.setObjective(&objective); // Calculate minimum values - *min = simplex.solveMin(); + *min = simplex.solveMin() - objectiveOffset; // Save sizeAtMinimum results QList<AnchorData *> variables = getVariables(constraints); for (int i = 0; i < variables.size(); ++i) { AnchorData *ad = static_cast<AnchorData *>(variables.at(i)); - ad->sizeAtMinimum = ad->result; - Q_ASSERT(ad->sizeAtMinimum >= ad->minSize || - qAbs(ad->sizeAtMinimum - ad->minSize) < 0.00000001); + ad->sizeAtMinimum = ad->result - g_offset; } // Calculate maximum values - *max = simplex.solveMax(); + *max = simplex.solveMax() - objectiveOffset; // Save sizeAtMaximum results for (int i = 0; i < variables.size(); ++i) { AnchorData *ad = static_cast<AnchorData *>(variables.at(i)); - ad->sizeAtMaximum = ad->result; - // Q_ASSERT(ad->sizeAtMaximum <= ad->maxSize || - // qAbs(ad->sizeAtMaximum - ad->maxSize) < 0.00000001); + ad->sizeAtMaximum = ad->result - g_offset; } } return feasible; } +enum slackType { Grower = -1, Shrinker = 1 }; +static QPair<QSimplexVariable *, QSimplexConstraint *> createSlack(QSimplexConstraint *sizeConstraint, + qreal interval, slackType type) +{ + QSimplexVariable *slack = new QSimplexVariable; + sizeConstraint->variables.insert(slack, type); + + QSimplexConstraint *limit = new QSimplexConstraint; + limit->variables.insert(slack, 1.0); + limit->ratio = QSimplexConstraint::LessOrEqual; + limit->constant = interval; + + return qMakePair(slack, limit); +} + bool QGraphicsAnchorLayoutPrivate::solvePreferred(const QList<QSimplexConstraint *> &constraints, const QList<AnchorData *> &variables) { @@ -2690,7 +2871,8 @@ bool QGraphicsAnchorLayoutPrivate::solvePreferred(const QList<QSimplexConstraint // 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 + ...) + // z = n * (A_shrinker_hard + A_grower_hard + B_shrinker_hard + B_grower_hard + ...) + + // (A_shrinker_soft + A_grower_soft + B_shrinker_soft + B_grower_soft + ...) // // where n is the number of variables that have // slacks. Note that here we use the number of variables @@ -2702,7 +2884,7 @@ bool QGraphicsAnchorLayoutPrivate::solvePreferred(const QList<QSimplexConstraint // 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 + // A + A_shrinker_hard + A_shrinker_soft - A_grower_hard - A_grower_soft = A_pref // for (int i = 0; i < variables.size(); ++i) { AnchorData *ad = variables.at(i); @@ -2711,22 +2893,58 @@ bool QGraphicsAnchorLayoutPrivate::solvePreferred(const QList<QSimplexConstraint if (ad->isLayoutAnchor) 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; + // By default, all variables are equal to their preferred size. If they have room to + // grow or shrink, such flexibility will be added by the additional variables below. + QSimplexConstraint *sizeConstraint = new QSimplexConstraint; + preferredConstraints += sizeConstraint; + sizeConstraint->variables.insert(ad, 1.0); + sizeConstraint->constant = ad->prefSize + g_offset; + + // Can easily shrink + QPair<QSimplexVariable *, QSimplexConstraint *> slack; + const qreal softShrinkInterval = ad->prefSize - ad->minPrefSize; + if (softShrinkInterval) { + slack = createSlack(sizeConstraint, softShrinkInterval, Shrinker); + preferredVariables += slack.first; + preferredConstraints += slack.second; + + // Add to objective with ratio == 1 (soft) + objective.variables.insert(slack.first, 1.0); + } - preferredConstraints += c; - preferredVariables += grower; - preferredVariables += shrinker; + // Can easily grow + const qreal softGrowInterval = ad->maxPrefSize - ad->prefSize; + if (softGrowInterval) { + slack = createSlack(sizeConstraint, softGrowInterval, Grower); + preferredVariables += slack.first; + preferredConstraints += slack.second; - objective.variables.insert(grower, 1.0); - objective.variables.insert(shrinker, variables.size()); - } + // Add to objective with ratio == 1 (soft) + objective.variables.insert(slack.first, 1.0); + } + + // Can shrink if really necessary + const qreal hardShrinkInterval = ad->minPrefSize - ad->minSize; + if (hardShrinkInterval) { + slack = createSlack(sizeConstraint, hardShrinkInterval, Shrinker); + preferredVariables += slack.first; + preferredConstraints += slack.second; + + // Add to objective with ratio == N (hard) + objective.variables.insert(slack.first, variables.size()); + } + // Can grow if really necessary + const qreal hardGrowInterval = ad->maxSize - ad->maxPrefSize; + if (hardGrowInterval) { + slack = createSlack(sizeConstraint, hardGrowInterval, Grower); + preferredVariables += slack.first; + preferredConstraints += slack.second; + + // Add to objective with ratio == N (hard) + objective.variables.insert(slack.first, variables.size()); + } + } QSimplex *simplex = new QSimplex; bool feasible = simplex->setConstraints(constraints + preferredConstraints); @@ -2739,7 +2957,7 @@ bool QGraphicsAnchorLayoutPrivate::solvePreferred(const QList<QSimplexConstraint // Save sizeAtPreferred results for (int i = 0; i < variables.size(); ++i) { AnchorData *ad = variables.at(i); - ad->sizeAtPreferred = ad->result; + ad->sizeAtPreferred = ad->result - g_offset; } // Make sure we delete the simplex solver -before- we delete the diff --git a/src/gui/graphicsview/qgraphicsanchorlayout_p.h b/src/gui/graphicsview/qgraphicsanchorlayout_p.h index 8529e2e..3be9d41 100644 --- a/src/gui/graphicsview/qgraphicsanchorlayout_p.h +++ b/src/gui/graphicsview/qgraphicsanchorlayout_p.h @@ -123,17 +123,17 @@ struct AnchorData : public QSimplexVariable { AnchorData() : QSimplexVariable(), from(0), to(0), minSize(0), prefSize(0), maxSize(0), + minPrefSize(0), maxPrefSize(0), sizeAtMinimum(0), sizeAtPreferred(0), sizeAtMaximum(0), item(0), graphicsAnchor(0), type(Normal), isLayoutAnchor(false), isCenterAnchor(false), orientation(0), dependency(Independent) {} + virtual ~AnchorData(); virtual void updateChildrenSizes() {} void refreshSizeHints(const QLayoutStyleInfo *styleInfo = 0); - virtual ~AnchorData() {} - #ifdef QT_DEBUG void dump(int indent = 2); inline QString toString() const; @@ -154,6 +154,9 @@ struct AnchorData : public QSimplexVariable { qreal prefSize; qreal maxSize; + qreal minPrefSize; + qreal maxPrefSize; + // Calculated sizes // These attributes define which sizes should that anchor be in when the // layout is at its minimum, preferred or maximum sizes. Values are @@ -213,7 +216,8 @@ struct ParallelAnchorData : public AnchorData Q_ASSERT(((first->from == second->from) && (first->to == second->to)) || ((first->from == second->to) && (first->to == second->from))); - // We arbitrarily choose the direction of the first child as "our" direction + // Our convention will be that the parallel group anchor will have the same + // direction as the first anchor. from = first->from; to = first->to; #ifdef QT_DEBUG @@ -224,6 +228,13 @@ struct ParallelAnchorData : public AnchorData virtual void updateChildrenSizes(); bool calculateSizeHints(); + bool secondForward() const { + // We have the convention that the first children will define the direction of the + // pararell group. Note that we can't rely on 'this->from' or 'this->to' because they + // might be changed by vertex simplification. + return firstEdge->from == secondEdge->from; + } + AnchorData* firstEdge; AnchorData* secondEdge; @@ -343,7 +354,6 @@ public: qreal preferredSize; uint hasSize : 1; // if false, get size from style. - uint reversed : 1; // if true, the anchor was inverted to keep its value positive }; @@ -365,8 +375,10 @@ public: // // Interval represents which interpolation interval are we operating in. enum Interval { - MinToPreferred = 0, - PreferredToMax + MinimumToMinPreferred = 0, + MinPreferredToPreferred, + PreferredToMaxPreferred, + MaxPreferredToMaximum }; // Several structures internal to the layout are duplicated to handle diff --git a/src/gui/graphicsview/qgraphicsitem.cpp b/src/gui/graphicsview/qgraphicsitem.cpp index bc47872..90cc132 100644 --- a/src/gui/graphicsview/qgraphicsitem.cpp +++ b/src/gui/graphicsview/qgraphicsitem.cpp @@ -1341,8 +1341,8 @@ QGraphicsItem::~QGraphicsItem() } if (!d_ptr->children.isEmpty()) { - QList<QGraphicsItem *> oldChildren = d_ptr->children; - qDeleteAll(oldChildren); + while (!d_ptr->children.isEmpty()) + delete d_ptr->children.first(); Q_ASSERT(d_ptr->children.isEmpty()); } @@ -2554,6 +2554,8 @@ QGraphicsEffect *QGraphicsItem::graphicsEffect() const If \a effect is the installed on a different item, setGraphicsEffect() will remove the effect from the item and install it on this item. + QGraphicsItem takes ownership of \a effect. + \note This function will apply the effect on itself and all its children. \since 4.6 @@ -2563,32 +2565,19 @@ void QGraphicsItem::setGraphicsEffect(QGraphicsEffect *effect) if (d_ptr->graphicsEffect == effect) return; - if (d_ptr->graphicsEffect && effect) { + if (d_ptr->graphicsEffect) { delete d_ptr->graphicsEffect; d_ptr->graphicsEffect = 0; } - if (!effect) { - // Unset current effect. - QGraphicsEffectPrivate *oldEffectPrivate = d_ptr->graphicsEffect->d_func(); - d_ptr->graphicsEffect = 0; - if (oldEffectPrivate) { - oldEffectPrivate->setGraphicsEffectSource(0); // deletes the current source. - if (d_ptr->scene) { // Update the views directly. - d_ptr->scene->d_func()->markDirty(this, QRectF(), /*invalidateChildren=*/false, - /*force=*/false, /*ignoreOpacity=*/false, - /*removeItemFromScene=*/true); - } - } - } else { + if (effect) { // Set new effect. QGraphicsEffectSourcePrivate *sourced = new QGraphicsItemEffectSourcePrivate(this); QGraphicsEffectSource *source = new QGraphicsEffectSource(*sourced); d_ptr->graphicsEffect = effect; effect->d_func()->setGraphicsEffectSource(source); + prepareGeometryChange(); } - - prepareGeometryChange(); } #endif //QT_NO_GRAPHICSEFFECT @@ -7305,13 +7294,6 @@ void QGraphicsObject::ungrabGesture(Qt::GestureType gesture) */ /*! - \property QGraphicsObject::id - \brief the id of of the item - - \sa QObject::objectName(), QObject::setObjectName() -*/ - -/*! \property QGraphicsObject::opacity \brief the opacity of the item diff --git a/src/gui/graphicsview/qgraphicsitem.h b/src/gui/graphicsview/qgraphicsitem.h index 8bbe9f1..8818a0b 100644 --- a/src/gui/graphicsview/qgraphicsitem.h +++ b/src/gui/graphicsview/qgraphicsitem.h @@ -537,7 +537,6 @@ class Q_GUI_EXPORT QGraphicsObject : public QObject, public QGraphicsItem { Q_OBJECT Q_PROPERTY(QGraphicsObject * parent READ parentObject WRITE setParentItem NOTIFY parentChanged DESIGNABLE false) - Q_PROPERTY(QString id READ objectName WRITE setObjectName) Q_PROPERTY(qreal opacity READ opacity WRITE setOpacity NOTIFY opacityChanged FINAL) Q_PROPERTY(bool enabled READ isEnabled WRITE setEnabled NOTIFY enabledChanged) Q_PROPERTY(bool visible READ isVisible WRITE setVisible NOTIFY visibleChanged FINAL) diff --git a/src/gui/graphicsview/qgraphicsitem_p.h b/src/gui/graphicsview/qgraphicsitem_p.h index d6ffb1a..8f9fe54 100644 --- a/src/gui/graphicsview/qgraphicsitem_p.h +++ b/src/gui/graphicsview/qgraphicsitem_p.h @@ -562,7 +562,10 @@ public: {} inline void detach() - { item->setGraphicsEffect(0); } + { + item->d_ptr->graphicsEffect = 0; + item->prepareGeometryChange(); + } inline const QGraphicsItem *graphicsItem() const { return item; } diff --git a/src/gui/graphicsview/qgraphicsscene.cpp b/src/gui/graphicsview/qgraphicsscene.cpp index 8777cdc..2a53456 100644 --- a/src/gui/graphicsview/qgraphicsscene.cpp +++ b/src/gui/graphicsview/qgraphicsscene.cpp @@ -613,6 +613,19 @@ void QGraphicsScenePrivate::removeItemHelper(QGraphicsItem *item) if (item == lastActivePanel) lastActivePanel = 0; + // Cancel active touches + { + QMap<int, QGraphicsItem *>::iterator it = itemForTouchPointId.begin(); + while (it != itemForTouchPointId.end()) { + if (it.value() == item) { + sceneCurrentTouchPoints.remove(it.key()); + it = itemForTouchPointId.erase(it); + } else { + ++it; + } + } + } + // Disable selectionChanged() for individual items ++selectionChanging; int oldSelectedItemsSize = selectedItems.size(); @@ -4233,7 +4246,6 @@ static void _q_paintIntoCache(QPixmap *pix, QGraphicsItem *item, const QRegion & if (!subPix.isNull()) { // Blit the subpixmap into the main pixmap. pixmapPainter.begin(pix); - pixmapPainter.setCompositionMode(QPainter::CompositionMode_Source); pixmapPainter.setClipRegion(pixmapExposed); pixmapPainter.drawPixmap(br.topLeft(), subPix); pixmapPainter.end(); @@ -5680,17 +5692,22 @@ bool QGraphicsScenePrivate::sendTouchBeginEvent(QGraphicsItem *origin, QTouchEve touchEvent->setAccepted(acceptTouchEvents); res = acceptTouchEvents && sendEvent(item, touchEvent); eventAccepted = touchEvent->isAccepted(); - item->d_ptr->acceptedTouchBeginEvent = (res && eventAccepted); + if (itemForTouchPointId.value(touchEvent->touchPoints().first().id()) == 0) { + // item was deleted + item = 0; + } else { + item->d_ptr->acceptedTouchBeginEvent = (res && eventAccepted); + } touchEvent->spont = false; if (res && eventAccepted) { // the first item to accept the TouchBegin gets an implicit grab. for (int i = 0; i < touchEvent->touchPoints().count(); ++i) { const QTouchEvent::TouchPoint &touchPoint = touchEvent->touchPoints().at(i); - itemForTouchPointId[touchPoint.id()] = item; + itemForTouchPointId[touchPoint.id()] = item; // can be zero } break; } - if (item->isPanel()) + if (item && item->isPanel()) break; } diff --git a/src/gui/graphicsview/qsimplex_p.h b/src/gui/graphicsview/qsimplex_p.h index a5816d1..2004471 100644 --- a/src/gui/graphicsview/qsimplex_p.h +++ b/src/gui/graphicsview/qsimplex_p.h @@ -107,7 +107,7 @@ struct QSimplexConstraint Q_ASSERT(constant > 0 || qFuzzyCompare(1, 1 + constant)); - if ((leftHandSide == constant) || qAbs(leftHandSide - constant) < 0.00000001) + if ((leftHandSide == constant) || qAbs(leftHandSide - constant) < 0.0000001) return true; switch (ratio) { diff --git a/src/gui/image/qimagepixmapcleanuphooks.cpp b/src/gui/image/qimagepixmapcleanuphooks.cpp index e411cd1..2aa61b8 100644 --- a/src/gui/image/qimagepixmapcleanuphooks.cpp +++ b/src/gui/image/qimagepixmapcleanuphooks.cpp @@ -106,8 +106,8 @@ void QImagePixmapCleanupHooks::executePixmapModificationHooks(QPixmap* pm) void QImagePixmapCleanupHooks::executePixmapDestructionHooks(QPixmap* pm) { QImagePixmapCleanupHooks *h = qt_image_and_pixmap_cleanup_hooks(); - for (int i = 0; i < h->pixmapModificationHooks.count(); ++i) - qt_image_and_pixmap_cleanup_hooks()->pixmapDestructionHooks[i](pm); + for (int i = 0; i < h->pixmapDestructionHooks.count(); ++i) + h->pixmapDestructionHooks[i](pm); if (qt_pixmap_cleanup_hook_64) qt_pixmap_cleanup_hook_64(pm->cacheKey()); diff --git a/src/gui/inputmethod/qcoefepinputcontext_s60.cpp b/src/gui/inputmethod/qcoefepinputcontext_s60.cpp index bdff5e7..a295d66 100644 --- a/src/gui/inputmethod/qcoefepinputcontext_s60.cpp +++ b/src/gui/inputmethod/qcoefepinputcontext_s60.cpp @@ -437,6 +437,10 @@ void QCoeFepInputContext::applyHints(Qt::InputMethodHints hints) void QCoeFepInputContext::applyFormat(QList<QInputMethodEvent::Attribute> *attributes) { TCharFormat cFormat; + QColor styleTextColor = QApplication::palette("QLineEdit").text().color(); + TLogicalRgb tontColor(TRgb(styleTextColor.red(), styleTextColor.green(), styleTextColor.blue(), styleTextColor.alpha())); + cFormat.iFontPresentation.iTextColor = tontColor; + TInt numChars = 0; TInt charPos = 0; int oldSize = attributes->size(); diff --git a/src/gui/itemviews/qlistwidget.cpp b/src/gui/itemviews/qlistwidget.cpp index 5dd1d76..929d688 100644 --- a/src/gui/itemviews/qlistwidget.cpp +++ b/src/gui/itemviews/qlistwidget.cpp @@ -176,7 +176,8 @@ void QListModel::move(int srcRow, int dstRow) || dstRow < 0 || dstRow >= items.count()) return; - beginMoveRows(QModelIndex(), srcRow, srcRow, QModelIndex(), dstRow); + if (!beginMoveRows(QModelIndex(), srcRow, srcRow, QModelIndex(), dstRow)) + return; if (srcRow < dstRow) --dstRow; items.move(srcRow, dstRow); diff --git a/src/gui/kernel/qapplication.cpp b/src/gui/kernel/qapplication.cpp index 9f4cd0c..8c63968 100644 --- a/src/gui/kernel/qapplication.cpp +++ b/src/gui/kernel/qapplication.cpp @@ -4092,9 +4092,15 @@ bool QApplication::notify(QObject *receiver, QEvent *e) bool acceptTouchEvents = widget->testAttribute(Qt::WA_AcceptTouchEvents); touchEvent->setWidget(widget); touchEvent->setAccepted(acceptTouchEvents); + QWeakPointer<QWidget> p = widget; res = acceptTouchEvents && d->notify_helper(widget, touchEvent); eventAccepted = touchEvent->isAccepted(); - widget->setAttribute(Qt::WA_WState_AcceptedTouchBeginEvent, res && eventAccepted); + if (p.isNull()) { + // widget was deleted + widget = 0; + } else { + widget->setAttribute(Qt::WA_WState_AcceptedTouchBeginEvent, res && eventAccepted); + } touchEvent->spont = false; if (res && eventAccepted) { // the first widget to accept the TouchBegin gets an implicit grab. @@ -4103,7 +4109,7 @@ bool QApplication::notify(QObject *receiver, QEvent *e) d->widgetForTouchPointId[touchPoint.id()] = widget; } break; - } else if (widget->isWindow() || widget->testAttribute(Qt::WA_NoMousePropagation)) { + } else if (p.isNull() || widget->isWindow() || widget->testAttribute(Qt::WA_NoMousePropagation)) { break; } QPoint offset = widget->pos(); diff --git a/src/gui/kernel/qsoftkeymanager.cpp b/src/gui/kernel/qsoftkeymanager.cpp index 1acc9b3..0e98f39 100644 --- a/src/gui/kernel/qsoftkeymanager.cpp +++ b/src/gui/kernel/qsoftkeymanager.cpp @@ -246,15 +246,22 @@ void QSoftKeyManagerPrivate::updateSoftKeys_sys(const QList<QAction*> &softkeys) break; } - int command = (softKeyAction->objectName().contains("_q_menuSoftKeyAction")) + int command = (softKeyAction->objectName().contains(QLatin1String("_q_menuSoftKeyAction"))) ? EAknSoftkeyOptions : s60CommandStart + index; + // _q_menuSoftKeyAction action is set to "invisible" and all invisible actions are by default + // disabled. However we never want to dim options softkey, even it is set to "invisible" + bool dimmed = (command == EAknSoftkeyOptions) ? false : !softKeyAction->isEnabled(); + if (position != -1) { const int underlineShortCut = QApplication::style()->styleHint(QStyle::SH_UnderlineShortcut); QString iconText = softKeyAction->iconText(); TPtrC text = qt_QString2TPtrC( underlineShortCut ? softKeyAction->text() : iconText); - QT_TRAP_THROWING(nativeContainer->SetCommandL(position, command, text)); + QT_TRAP_THROWING( + nativeContainer->SetCommandL(position, command, text); + nativeContainer->DimCommand(command, dimmed); + ); } } diff --git a/src/gui/kernel/qwidget.cpp b/src/gui/kernel/qwidget.cpp index 06d52ae..e551a1d 100644 --- a/src/gui/kernel/qwidget.cpp +++ b/src/gui/kernel/qwidget.cpp @@ -5046,6 +5046,8 @@ QGraphicsEffect *QWidget::graphicsEffect() const If \a effect is the installed on a different widget, setGraphicsEffect() will remove the effect from the widget and install it on this widget. + QWidget takes ownership of \a effect. + \note This function will apply the effect on itself and all its children. \since 4.6 @@ -5059,28 +5061,22 @@ void QWidget::setGraphicsEffect(QGraphicsEffect *effect) if (d->graphicsEffect == effect) return; - if (d->graphicsEffect && effect) { + if (d->graphicsEffect) { + d->invalidateBuffer(rect()); delete d->graphicsEffect; d->graphicsEffect = 0; } - if (!effect) { - // Unset current effect. - QGraphicsEffectPrivate *oldEffectPrivate = d->graphicsEffect->d_func(); - d->graphicsEffect = 0; - if (oldEffectPrivate) { - oldEffectPrivate->setGraphicsEffectSource(0); // deletes the current source. - } - } else { + if (effect) { // Set new effect. QGraphicsEffectSourcePrivate *sourced = new QWidgetEffectSourcePrivate(this); QGraphicsEffectSource *source = new QGraphicsEffectSource(*sourced); d->graphicsEffect = effect; effect->d_func()->setGraphicsEffectSource(source); + update(); } d->updateIsOpaque(); - update(); } #endif //QT_NO_GRAPHICSEFFECT diff --git a/src/gui/kernel/qwidget.h b/src/gui/kernel/qwidget.h index 5ba1d23..0824db3 100644 --- a/src/gui/kernel/qwidget.h +++ b/src/gui/kernel/qwidget.h @@ -744,6 +744,7 @@ private: friend struct QWidgetExceptionCleaner; friend class QGestureManager; friend class QWinNativePanGestureRecognizer; + friend class QWidgetEffectSourcePrivate; #ifdef Q_WS_MAC friend class QCoreGraphicsPaintEnginePrivate; diff --git a/src/gui/kernel/qwidget_p.h b/src/gui/kernel/qwidget_p.h index 04cf4bb..5d73951 100644 --- a/src/gui/kernel/qwidget_p.h +++ b/src/gui/kernel/qwidget_p.h @@ -823,7 +823,7 @@ public: {} inline void detach() - { m_widget->setGraphicsEffect(0); } + { m_widget->d_func()->graphicsEffect = 0; } inline const QGraphicsItem *graphicsItem() const { return 0; } diff --git a/src/gui/painting/qpainter.cpp b/src/gui/painting/qpainter.cpp index 30f8c9e..66bf4f7 100644 --- a/src/gui/painting/qpainter.cpp +++ b/src/gui/painting/qpainter.cpp @@ -7630,7 +7630,7 @@ start_lengthVariant: // in the paint engines when drawing on floating point offsets const qreal scale = painter->transform().m22(); if (scale != 0) - yoff = qRound(yoff * scale) / scale; + yoff = -qRound(-yoff * scale) / scale; } } } diff --git a/src/gui/styles/qgtkstyle.cpp b/src/gui/styles/qgtkstyle.cpp index afa3325..e10bb41 100644 --- a/src/gui/styles/qgtkstyle.cpp +++ b/src/gui/styles/qgtkstyle.cpp @@ -3375,12 +3375,28 @@ QIcon QGtkStyle::standardIconImplementation(StandardPixmap standardIcon, /*! \reimp */ QRect QGtkStyle::subElementRect(SubElement element, const QStyleOption *option, const QWidget *widget) const { + Q_D(const QGtkStyle); + QRect r = QCleanlooksStyle::subElementRect(element, option, widget); switch (element) { case SE_ProgressBarLabel: case SE_ProgressBarContents: case SE_ProgressBarGroove: return option->rect; + case SE_PushButtonContents: + if (!d->gtk_check_version(2, 10, 0)) { + GtkWidget *gtkButton = d->gtkWidget(QLS("GtkButton")); + GtkBorder *border = 0; + d->gtk_widget_style_get(gtkButton, "inner-border", &border, NULL); + if (border) { + r = option->rect.adjusted(border->left, border->top, -border->right, -border->bottom); + d->gtk_border_free(border); + } else { + r = option->rect.adjusted(1, 1, -1, -1); + } + r = visualRect(option->direction, option->rect, r); + } + break; default: break; } diff --git a/src/gui/styles/qgtkstyle_p.cpp b/src/gui/styles/qgtkstyle_p.cpp index 22dfc62..a644a5b 100644 --- a/src/gui/styles/qgtkstyle_p.cpp +++ b/src/gui/styles/qgtkstyle_p.cpp @@ -158,6 +158,7 @@ Ptr_gtk_window_get_type QGtkStylePrivate::gtk_window_get_type = 0; Ptr_gtk_widget_get_type QGtkStylePrivate::gtk_widget_get_type = 0; Ptr_gtk_rc_get_style_by_paths QGtkStylePrivate::gtk_rc_get_style_by_paths = 0; Ptr_gtk_check_version QGtkStylePrivate::gtk_check_version = 0; +Ptr_gtk_border_free QGtkStylePrivate::gtk_border_free = 0; Ptr_pango_font_description_get_size QGtkStylePrivate::pango_font_description_get_size = 0; Ptr_pango_font_description_get_weight QGtkStylePrivate::pango_font_description_get_weight = 0; @@ -416,6 +417,7 @@ void QGtkStylePrivate::resolveGtk() const gtk_widget_get_type =(Ptr_gtk_widget_get_type)libgtk.resolve("gtk_widget_get_type"); gtk_rc_get_style_by_paths =(Ptr_gtk_rc_get_style_by_paths)libgtk.resolve("gtk_rc_get_style_by_paths"); gtk_check_version =(Ptr_gtk_check_version)libgtk.resolve("gtk_check_version"); + gtk_border_free =(Ptr_gtk_border_free)libgtk.resolve("gtk_border_free"); pango_font_description_get_size = (Ptr_pango_font_description_get_size)libgtk.resolve("pango_font_description_get_size"); pango_font_description_get_weight = (Ptr_pango_font_description_get_weight)libgtk.resolve("pango_font_description_get_weight"); pango_font_description_get_family = (Ptr_pango_font_description_get_family)libgtk.resolve("pango_font_description_get_family"); diff --git a/src/gui/styles/qgtkstyle_p.h b/src/gui/styles/qgtkstyle_p.h index f6ab8a3..c27308f 100644 --- a/src/gui/styles/qgtkstyle_p.h +++ b/src/gui/styles/qgtkstyle_p.h @@ -176,6 +176,7 @@ typedef GtkWidget* (*Ptr_gtk_file_chooser_dialog_new)(const gchar *title, typedef void (*Ptr_gtk_file_chooser_set_current_name) (GtkFileChooser *, const gchar *); typedef gboolean (*Ptr_gtk_file_chooser_set_filename) (GtkFileChooser *chooser, const gchar *name); typedef gint (*Ptr_gtk_dialog_run) (GtkDialog*); +typedef void (*Ptr_gtk_border_free)(GtkBorder *); typedef guchar* (*Ptr_gdk_pixbuf_get_pixels) (const GdkPixbuf *pixbuf); typedef int (*Ptr_gdk_pixbuf_get_width) (const GdkPixbuf *pixbuf); @@ -371,6 +372,7 @@ public: static Ptr_gtk_widget_get_type gtk_widget_get_type; static Ptr_gtk_rc_get_style_by_paths gtk_rc_get_style_by_paths; static Ptr_gtk_check_version gtk_check_version; + static Ptr_gtk_border_free gtk_border_free; static Ptr_pango_font_description_get_size pango_font_description_get_size; static Ptr_pango_font_description_get_weight pango_font_description_get_weight; diff --git a/src/gui/styles/qs60style.cpp b/src/gui/styles/qs60style.cpp index dca78ca..be4f15a 100644 --- a/src/gui/styles/qs60style.cpp +++ b/src/gui/styles/qs60style.cpp @@ -454,9 +454,6 @@ void QS60StylePrivate::setThemePalette(QApplication *app) const Q_UNUSED(app) QPalette widgetPalette = QPalette(Qt::white); setThemePalette(&widgetPalette); - QApplication::setPalette(widgetPalette); //calling QApplication::setPalette clears palette hash - setThemePaletteHash(&widgetPalette); - storeThemePalette(&widgetPalette); } QPalette* QS60StylePrivate::themePalette() @@ -470,8 +467,6 @@ void QS60StylePrivate::setBackgroundTexture(QApplication *app) const QPalette applicationPalette = QApplication::palette(); applicationPalette.setBrush(QPalette::Window, backgroundTexture()); setThemePalette(&applicationPalette); - QApplication::setPalette(applicationPalette); - setThemePaletteHash(&applicationPalette); } void QS60StylePrivate::deleteBackground() @@ -659,7 +654,7 @@ void QS60StylePrivate::setThemePalette(QPalette *palette) const palette->setColor(QPalette::WindowText, s60Color(QS60StyleEnums::CL_QsnTextColors, 6, 0)); palette->setColor(QPalette::ButtonText, - s60Color(QS60StyleEnums::CL_QsnTextColors, 6, 0)); + s60Color(QS60StyleEnums::CL_QsnTextColors, 20, 0)); palette->setColor(QPalette::Text, s60Color(QS60StyleEnums::CL_QsnTextColors, 6, 0)); palette->setColor(QPalette::ToolTipText, @@ -687,6 +682,10 @@ void QS60StylePrivate::setThemePalette(QPalette *palette) const palette->setColor(QPalette::Midlight, palette->color(QPalette::Button).lighter(125)); palette->setColor(QPalette::Mid, palette->color(QPalette::Button).darker(150)); palette->setColor(QPalette::Shadow, Qt::black); + + QApplication::setPalette(*palette); //calling QApplication::setPalette clears palette hash + setThemePaletteHash(palette); + storeThemePalette(palette); } void QS60StylePrivate::deleteThemePalette() @@ -754,13 +753,15 @@ void QS60StylePrivate::setThemePaletteHash(QPalette *palette) const QApplication::setPalette(widgetPalette, "QTableView"); widgetPalette = *palette; + widgetPalette.setColor(QPalette::Text, + s60Color(QS60StyleEnums::CL_QsnTextColors, 27, 0)); widgetPalette.setColor(QPalette::HighlightedText, s60Color(QS60StyleEnums::CL_QsnTextColors, 24, 0)); QApplication::setPalette(widgetPalette, "QLineEdit"); widgetPalette = *palette; widgetPalette.setColor(QPalette::Text, - s60Color(QS60StyleEnums::CL_QsnTextColors, 34, 0)); + s60Color(QS60StyleEnums::CL_QsnTextColors, 27, 0)); widgetPalette.setColor(QPalette::HighlightedText, s60Color(QS60StyleEnums::CL_QsnTextColors, 24, 0)); QApplication::setPalette(widgetPalette, "QTextEdit"); @@ -2344,10 +2345,10 @@ int QS60Style::styleHint(StyleHint sh, const QStyleOption *opt, const QWidget *w int retValue = -1; switch (sh) { case SH_Table_GridLineColor: - retValue = QS60StylePrivate::s60Color(QS60StyleEnums::CL_QsnLineColors,2,0).rgb(); + retValue = int(QS60StylePrivate::s60Color(QS60StyleEnums::CL_QsnLineColors,2,0).rgba()); break; case SH_GroupBox_TextLabelColor: - retValue = QS60StylePrivate::s60Color(QS60StyleEnums::CL_QsnTextColors,6,0).rgb(); + retValue = int(QS60StylePrivate::s60Color(QS60StyleEnums::CL_QsnTextColors,6,0).rgba()); break; case SH_ScrollBar_ScrollWhenPointerLeavesControl: retValue = true; @@ -2403,10 +2404,9 @@ int QS60Style::styleHint(StyleHint sh, const QStyleOption *opt, const QWidget *w retValue = QFormLayout::WrapLongRows; break; default: + retValue = QCommonStyle::styleHint(sh, opt, widget, hret); break; } - if (retValue == -1) - retValue = QCommonStyle::styleHint(sh, opt, widget, hret); return retValue; } diff --git a/src/gui/text/qtextdocument_p.cpp b/src/gui/text/qtextdocument_p.cpp index 18e1ffc..b015198 100644 --- a/src/gui/text/qtextdocument_p.cpp +++ b/src/gui/text/qtextdocument_p.cpp @@ -870,6 +870,7 @@ int QTextDocumentPrivate::undoRedo(bool undo) undoEnabled = false; beginEditBlock(); + int editPos = -1; while (1) { if (undo) --undoState; @@ -881,11 +882,13 @@ int QTextDocumentPrivate::undoRedo(bool undo) remove(c.pos, c.length, (QTextUndoCommand::Operation)c.operation); PMDEBUG(" erase: from %d, length %d", c.pos, c.length); c.command = QTextUndoCommand::Removed; + editPos = c.pos; break; case QTextUndoCommand::Removed: PMDEBUG(" insert: format %d (from %d, length %d, strpos=%d)", c.format, c.pos, c.length, c.strPos); insert_string(c.pos, c.strPos, c.length, c.format, (QTextUndoCommand::Operation)c.operation); c.command = QTextUndoCommand::Inserted; + editPos = c.pos + c.length; break; case QTextUndoCommand::BlockInserted: case QTextUndoCommand::BlockAdded: @@ -895,6 +898,7 @@ int QTextDocumentPrivate::undoRedo(bool undo) c.command = QTextUndoCommand::BlockRemoved; else c.command = QTextUndoCommand::BlockDeleted; + editPos = c.pos; break; case QTextUndoCommand::BlockRemoved: case QTextUndoCommand::BlockDeleted: @@ -905,6 +909,7 @@ int QTextDocumentPrivate::undoRedo(bool undo) c.command = QTextUndoCommand::BlockInserted; else c.command = QTextUndoCommand::BlockAdded; + editPos = c.pos + 1; break; case QTextUndoCommand::CharFormatChanged: { resetBlockRevision = -1; // ## TODO @@ -915,6 +920,7 @@ int QTextDocumentPrivate::undoRedo(bool undo) int oldFormat = it.value()->format; setCharFormat(c.pos, c.length, formats.charFormat(c.format)); c.format = oldFormat; + editPos = c.pos + c.length; break; } case QTextUndoCommand::BlockFormatChanged: { @@ -937,6 +943,7 @@ int QTextDocumentPrivate::undoRedo(bool undo) group->blockFormatChanged(it); } documentChange(it.position(), it.length()); + editPos = -1; break; } case QTextUndoCommand::GroupFormatChange: { @@ -946,6 +953,7 @@ int QTextDocumentPrivate::undoRedo(bool undo) int oldFormat = formats.objectFormatIndex(c.objectIndex); changeObjectFormat(object, c.format); c.format = oldFormat; + editPos = -1; break; } case QTextUndoCommand::Custom: @@ -954,6 +962,7 @@ int QTextDocumentPrivate::undoRedo(bool undo) c.custom->undo(); else c.custom->redo(); + editPos = -1; break; default: Q_ASSERT(false); @@ -979,8 +988,7 @@ int QTextDocumentPrivate::undoRedo(bool undo) break; } undoEnabled = true; - int editPos = -1; - if (docChangeFrom >= 0) { + if (editPos < 0 && docChangeFrom >= 0) { editPos = qMin(docChangeFrom + docChangeLength, length() - 1); } endEditBlock(); diff --git a/src/gui/util/qdesktopservices_s60.cpp b/src/gui/util/qdesktopservices_s60.cpp index 1890d56..c6932de 100644 --- a/src/gui/util/qdesktopservices_s60.cpp +++ b/src/gui/util/qdesktopservices_s60.cpp @@ -41,7 +41,7 @@ // This flag changes the implementation to use S60 CDcoumentHandler // instead of apparch when opening the files -#undef USE_DOCUMENTHANDLER +#define USE_DOCUMENTHANDLER #include <qcoreapplication.h> #include <qdir.h> @@ -58,12 +58,14 @@ #include <rsendasmessage.h> // RSendAsMessage #ifdef Q_WS_S60 -# include <pathinfo.h> // PathInfo +# include <pathinfo.h> // PathInfo # ifdef USE_DOCUMENTHANDLER -# include <documenthandler.h> // CDocumentHandler +# include <documenthandler.h> // CDocumentHandler +# include <aknserverapp.h> # endif -#elif defined(USE_DOCUMENTHANDLER) -# error CDocumentHandler requires support for S60 +#else +# warning CDocumentHandler requires support for S60 +# undef USE_DOCUMENTHANDLER // Fallback to RApaLsSession based implementation #endif QT_BEGIN_NAMESPACE @@ -95,6 +97,42 @@ private: R* mPtr; }; +#ifdef USE_DOCUMENTHANDLER +class QS60DocumentHandler : public MAknServerAppExitObserver +{ +public: + QS60DocumentHandler() :docHandler(0) {} + + ~QS60DocumentHandler() { + delete docHandler; + } + + CDocumentHandler& documentHandler() { + // In case user calls openUrl twice subsequently, before the first embedded app is closed + // we use the same CDocumentHandler instance. Using same instance makes sure the first + // launched embedded app is closed and latter one gets embedded to our app. + // Using different instance would help only theoretically since user cannot interact with + // several embedded apps at the same time. + if(!docHandler) { + QT_TRAP_THROWING(docHandler = CDocumentHandler::NewL()); + docHandler->SetExitObserver(this); + } + return *docHandler; + } + +private: // From MAknServerAppExitObserver + void HandleServerAppExit(TInt /*aReason*/) { + delete docHandler; + docHandler = 0; + } + +private: + CDocumentHandler* docHandler; +}; +Q_GLOBAL_STATIC(QS60DocumentHandler, qt_s60_documenthandler); +#endif + + static void handleMailtoSchemeLX(const QUrl &url) { // this function has many intermingled leaves and throws. Qt and Symbian objects do not have @@ -264,21 +302,9 @@ static void openDocumentL(const TDesC& aUrl) CleanupStack::PopAndDestroy(); // appArcSession #else // This is an alternative way to launch app associated to MIME type - // CDocumentHandler would support opening apps in embedded mode, - // but our Qt application window group seems to always get switched on top of embedded one - // -> Cannot use menus etc of embedded app -> used - - CDocumentHandler* docHandler = CDocumentHandler::NewLC(); + // CDocumentHandler also supports opening apps in embedded mode. TDataType temp; - //Standalone file opening fails for some file-types at least in S60 3.1 emulator - //For example .txt file fails with KErrAlreadyInUse and music files with KERN-EXEC 0 - //Workaround is to use OpenFileEmbeddedL - //docHandler->OpenFileL(aUrl, temp); - - // Opening file with CDocumentHandler will leave if file does not exist - // Leave is trapped in openDocument and false returned to user. - docHandler->OpenFileEmbeddedL(aUrl, temp); - CleanupStack::PopAndDestroy(docHandler); + qt_s60_documenthandler()->documentHandler().OpenFileEmbeddedL(aUrl, temp); #endif } @@ -349,7 +375,7 @@ QString QDesktopServices::storageLocation(StandardLocation type) case DesktopLocation: qWarning("No desktop concept in Symbian OS"); // But lets still use some feasible default - path.Append(writableDataRoot()); + path.Append(writableDataRoot()); break; case DocumentsLocation: path.Append(writableDataRoot()); @@ -380,7 +406,7 @@ QString QDesktopServices::storageLocation(StandardLocation type) #endif break; case TempLocation: - return QDir::tempPath(); + return QDir::tempPath(); break; case HomeLocation: path.Append(writableDataRoot()); @@ -394,10 +420,10 @@ QString QDesktopServices::storageLocation(StandardLocation type) CEikonEnv::Static()->FsSession().PrivatePath(path); path.Insert(0, writableExeDrive().Name()); path.Append(KCacheSubDir); - break; + break; default: // Lets use feasible default - path.Append(writableDataRoot()); + path.Append(writableDataRoot()); break; } diff --git a/src/gui/widgets/qdialogbuttonbox.cpp b/src/gui/widgets/qdialogbuttonbox.cpp index 0e859f1..f2ef941 100644 --- a/src/gui/widgets/qdialogbuttonbox.cpp +++ b/src/gui/widgets/qdialogbuttonbox.cpp @@ -46,6 +46,7 @@ #include <QtGui/qdialog.h> #include <QtGui/qapplication.h> #include <QtGui/private/qwidget_p.h> +#include <QtGui/qaction.h> #include "qdialogbuttonbox.h" @@ -259,6 +260,31 @@ static const int layouts[2][5][14] = } }; +class QDialogButtonEnabledProxy : public QObject +{ +public: + QDialogButtonEnabledProxy(QObject *parent, QWidget *src, QAction *trg) : QObject(parent), source(src), target(trg) + { + source->installEventFilter(this); + target->setEnabled(source->isEnabled()); + } + ~QDialogButtonEnabledProxy() + { + source->removeEventFilter(this); + } + bool eventFilter(QObject *object, QEvent *event) + { + if (object == source && event->type() == QEvent::EnabledChange) { + target->setEnabled(source->isEnabled()); + } + return false; + }; +private: + QWidget *source; + QAction *target; +}; + + class QDialogButtonBoxPrivate : public QWidgetPrivate { Q_DECLARE_PUBLIC(QDialogButtonBox) @@ -548,7 +574,9 @@ void QDialogButtonBoxPrivate::addButton(QAbstractButton *button, QDialogButtonBo QObject::connect(button, SIGNAL(destroyed()), q, SLOT(_q_handleButtonDestroyed())); buttonLists[role].append(button); #ifdef QT_SOFTKEYS_ENABLED - softKeyActions.insert(button, createSoftKey(button, role)); + QAction *action = createSoftKey(button, role); + softKeyActions.insert(button, action); + new QDialogButtonEnabledProxy(action, button, action); #endif if (doLayout) layoutButtons(); diff --git a/src/gui/widgets/qmenu.cpp b/src/gui/widgets/qmenu.cpp index 2e27226..ec9683d 100644 --- a/src/gui/widgets/qmenu.cpp +++ b/src/gui/widgets/qmenu.cpp @@ -65,6 +65,8 @@ #include "qmenubar_p.h" #include "qwidgetaction.h" #include "qtoolbutton.h" +#include "qpushbutton.h" +#include <private/qpushbutton_p.h> #include <private/qaction_p.h> #include <private/qsoftkeymanager_p.h> #ifdef QT3_SUPPORT @@ -417,12 +419,7 @@ void QMenuPrivate::hideUpToMenuBar() hideMenu(m, fadeMenus); if (!fadeMenus) // Mac doesn't clear the action until after hidden. m->d_func()->setCurrentAction(0); - } else { -#ifndef QT_NO_TOOLBUTTON - if (qobject_cast<QToolButton*>(caused) == 0) -#endif - qWarning("QMenu: Internal error"); - caused = 0; + } else { caused = 0; } } #if defined(Q_WS_MAC) @@ -1825,8 +1822,15 @@ void QMenu::popup(const QPoint &p, QAction *atAction) ensurePolished(); // Get the right font emit aboutToShow(); + const bool actionListChanged = d->itemsDirty; d->updateActionRects(); - QPoint pos = p; + QPoint pos; + QPushButton *causedButton = qobject_cast<QPushButton*>(d->causedPopup.widget); + if (actionListChanged && causedButton) + pos = QPushButtonPrivate::get(causedButton)->adjustedMenuPosition(); + else + pos = p; + QSize size = sizeHint(); QRect screen; #ifndef QT_NO_GRAPHICSVIEW @@ -2302,22 +2306,9 @@ void QMenu::mouseReleaseEvent(QMouseEvent *e) if (action->menu()) action->menu()->d_func()->setFirstActionActive(); else { -#if defined(Q_WS_WIN) && !defined(QT_NO_MENUBAR) +#if defined(Q_WS_WIN) //On Windows only context menus can be activated with the right button - bool isContextMenu = true; - const QWidget *cause = d->causedPopup.widget; - while (cause) { - //if the popup was caused by either QMenuBar or a QToolButton, it is not a context menu - if (qobject_cast<const QMenuBar *>(cause) || qobject_cast<const QToolButton *>(cause)) { - isContextMenu = false; - break; - } else if (const QMenu *menu = qobject_cast<const QMenu *>(cause)) { - cause = menu->d_func()->causedPopup.widget; - } else { - break; - } - } - if (e->button() == Qt::LeftButton || isContextMenu) + if (e->button() == Qt::LeftButton || d->topCausedWidget() == 0) #endif d->activateAction(action, QAction::Trigger); } diff --git a/src/gui/widgets/qmenu_p.h b/src/gui/widgets/qmenu_p.h index 5757885..93017f5 100644 --- a/src/gui/widgets/qmenu_p.h +++ b/src/gui/widgets/qmenu_p.h @@ -182,6 +182,7 @@ public: } void init(); + static QMenuPrivate *get(QMenu *m) { return m->d_func(); } int scrollerHeight() const; //item calculations diff --git a/src/gui/widgets/qpushbutton.cpp b/src/gui/widgets/qpushbutton.cpp index eb34336..849bc43 100644 --- a/src/gui/widgets/qpushbutton.cpp +++ b/src/gui/widgets/qpushbutton.cpp @@ -63,6 +63,7 @@ #include "qaccessible.h" #endif +#include "private/qmenu_p.h" #include "private/qpushbutton_p.h" QT_BEGIN_NAMESPACE @@ -575,12 +576,33 @@ void QPushButtonPrivate::_q_popupPressed() return; menu->setNoReplayFor(q); + + QPoint menuPos = adjustedMenuPosition(); + + QPointer<QPushButton> guard(q); + QMenuPrivate::get(menu)->causedPopup.widget = guard; + + //Because of a delay in menu effects, we must keep track of the + //menu visibility to avoid flicker on button release + menuOpen = true; + menu->exec(menuPos); + if (guard) { + menuOpen = false; + q->setDown(false); + } +} + +QPoint QPushButtonPrivate::adjustedMenuPosition() +{ + Q_Q(QPushButton); + bool horizontal = true; #if !defined(QT_NO_TOOLBAR) QToolBar *tb = qobject_cast<QToolBar*>(parent); if (tb && tb->orientation() == Qt::Vertical) horizontal = false; #endif + QWidgetItem item(q); QRect rect = item.geometry(); rect.setRect(rect.x() - q->x(), rect.y() - q->y(), rect.width(), rect.height()); @@ -603,17 +625,10 @@ void QPushButtonPrivate::_q_popupPressed() else x -= menuSize.width(); } - QPointer<QPushButton> guard(q); - //Because of a delay in menu effects, we must keep track of the - //menu visibility to avoid flicker on button release - menuOpen = true; - menu->exec(QPoint(x, y)); - if (guard) { - menuOpen = false; - q->setDown(false); - } + return QPoint(x,y); } + #endif // QT_NO_MENU void QPushButtonPrivate::resetLayoutItemMargins() diff --git a/src/gui/widgets/qpushbutton_p.h b/src/gui/widgets/qpushbutton_p.h index f448027..2510e05 100644 --- a/src/gui/widgets/qpushbutton_p.h +++ b/src/gui/widgets/qpushbutton_p.h @@ -68,6 +68,10 @@ public: defaultButton(false), flat(false), menuOpen(false), lastAutoDefault(false) {} inline void init() { resetLayoutItemMargins(); } + static QPushButtonPrivate* get(QPushButton *b) { return b->d_func(); } +#ifndef QT_NO_MENU + QPoint adjustedMenuPosition(); +#endif void resetLayoutItemMargins(); void _q_popupPressed(); QDialog *dialogParent() const; diff --git a/src/gui/widgets/qtoolbar.cpp b/src/gui/widgets/qtoolbar.cpp index 58a3d28..c0ca015 100644 --- a/src/gui/widgets/qtoolbar.cpp +++ b/src/gui/widgets/qtoolbar.cpp @@ -396,10 +396,10 @@ bool QToolBarPrivate::mouseMoveEvent(QMouseEvent *event) void QToolBarPrivate::unplug(const QRect &_r) { Q_Q(QToolBar); - layout->setExpanded(false); QRect r = _r; r.moveTopLeft(q->mapToGlobal(QPoint(0, 0))); setWindowState(true, true, r); + layout->setExpanded(false); } void QToolBarPrivate::plug(const QRect &r) diff --git a/src/gui/widgets/qtoolbararealayout.cpp b/src/gui/widgets/qtoolbararealayout.cpp index c329305..b83026b 100644 --- a/src/gui/widgets/qtoolbararealayout.cpp +++ b/src/gui/widgets/qtoolbararealayout.cpp @@ -598,16 +598,21 @@ int QToolBarAreaLayoutInfo::distance(const QPoint &pos) const { switch (dockPos) { case QInternal::LeftDock: - return pos.x() - rect.right(); + if (pos.y() < rect.bottom()) + return pos.x() - rect.right(); case QInternal::RightDock: - return rect.left() - pos.x(); + if (pos.y() < rect.bottom()) + return rect.left() - pos.x(); case QInternal::TopDock: - return pos.y() - rect.bottom(); + if (pos.x() < rect.right()) + return pos.y() - rect.bottom(); case QInternal::BottomDock: - return rect.top() - pos.y(); + if (pos.x() < rect.right()) + return rect.top() - pos.y(); default: - return -1; + break; } + return -1; } /****************************************************************************** diff --git a/src/gui/widgets/qtoolbarlayout.cpp b/src/gui/widgets/qtoolbarlayout.cpp index 0afe5d8..93429e4 100644 --- a/src/gui/widgets/qtoolbarlayout.cpp +++ b/src/gui/widgets/qtoolbarlayout.cpp @@ -654,6 +654,7 @@ void QToolBarLayout::setExpanded(bool exp) if (!tb) return; if (QMainWindow *win = qobject_cast<QMainWindow*>(tb->parentWidget())) { + animating = !tb->isWindow() && win->isAnimated(); QMainWindowLayout *layout = qobject_cast<QMainWindowLayout*>(win->layout()); if (expanded) { tb->raise(); @@ -664,7 +665,7 @@ void QToolBarLayout::setExpanded(bool exp) layoutActions(rect.size()); } } - layout->layoutState.toolBarAreaLayout.apply(win->isAnimated()); + layout->layoutState.toolBarAreaLayout.apply(animating); } } |