source: trunk/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp@ 642

Last change on this file since 642 was 561, checked in by Dmitry A. Kuminov, 15 years ago

trunk: Merged in qt 4.6.1 sources.

  • Property svn:eol-style set to native
File size: 107.6 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
4** All rights reserved.
5** Contact: Nokia Corporation ([email protected])
6**
7** This file is part of the QtGui module of the Qt Toolkit.
8**
9** $QT_BEGIN_LICENSE:LGPL$
10** Commercial Usage
11** Licensees holding valid Qt Commercial licenses may use this file in
12** accordance with the Qt Commercial License Agreement provided with the
13** Software or, alternatively, in accordance with the terms contained in
14** a written agreement between you and Nokia.
15**
16** GNU Lesser General Public License Usage
17** Alternatively, this file may be used under the terms of the GNU Lesser
18** General Public License version 2.1 as published by the Free Software
19** Foundation and appearing in the file LICENSE.LGPL included in the
20** packaging of this file. Please review the following information to
21** ensure the GNU Lesser General Public License version 2.1 requirements
22** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
23**
24** In addition, as a special exception, Nokia gives you certain additional
25** rights. These rights are described in the Nokia Qt LGPL Exception
26** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
27**
28** GNU General Public License Usage
29** Alternatively, this file may be used under the terms of the GNU
30** General Public License version 3.0 as published by the Free Software
31** Foundation and appearing in the file LICENSE.GPL included in the
32** packaging of this file. Please review the following information to
33** ensure the GNU General Public License version 3.0 requirements will be
34** met: http://www.gnu.org/copyleft/gpl.html.
35**
36** If you have questions regarding the use of this file, please contact
37** Nokia at [email protected].
38** $QT_END_LICENSE$
39**
40****************************************************************************/
41
42#include <QtGui/qwidget.h>
43#include <QtGui/qapplication.h>
44#include <QtCore/qlinkedlist.h>
45#include <QtCore/qstack.h>
46
47#ifdef QT_DEBUG
48#include <QtCore/qfile.h>
49#endif
50
51#include "qgraphicsanchorlayout_p.h"
52
53#ifndef QT_NO_GRAPHICSVIEW
54QT_BEGIN_NAMESPACE
55
56// To ensure that all variables inside the simplex solver are non-negative,
57// we limit the size of anchors in the interval [-limit, limit]. Then before
58// sending them to the simplex solver we add "limit" as an offset, so that
59// they are actually calculated in the interval [0, 2 * limit]
60// To avoid numerical errors in platforms where we use single precision,
61// we use a tighter limit for the variables range.
62const qreal g_offset = (sizeof(qreal) == sizeof(double)) ? QWIDGETSIZE_MAX : QWIDGETSIZE_MAX / 32;
63
64QGraphicsAnchorPrivate::QGraphicsAnchorPrivate(int version)
65 : QObjectPrivate(version), layoutPrivate(0), data(0),
66 sizePolicy(QSizePolicy::Fixed), preferredSize(0),
67 hasSize(true)
68{
69}
70
71QGraphicsAnchorPrivate::~QGraphicsAnchorPrivate()
72{
73 if (data) {
74 // The QGraphicsAnchor was already deleted at this moment. We must clean
75 // the dangling pointer to avoid double deletion in the AnchorData dtor.
76 data->graphicsAnchor = 0;
77
78 layoutPrivate->removeAnchor(data->from, data->to);
79 }
80}
81
82void QGraphicsAnchorPrivate::setSizePolicy(QSizePolicy::Policy policy)
83{
84 if (sizePolicy != policy) {
85 sizePolicy = policy;
86 layoutPrivate->q_func()->invalidate();
87 }
88}
89
90void QGraphicsAnchorPrivate::setSpacing(qreal value)
91{
92 if (!data) {
93 qWarning("QGraphicsAnchor::setSpacing: The anchor does not exist.");
94 return;
95 }
96
97 if (hasSize && (preferredSize == value))
98 return;
99
100 // The anchor has an user-defined size
101 hasSize = true;
102 preferredSize = value;
103
104 layoutPrivate->q_func()->invalidate();
105}
106
107void QGraphicsAnchorPrivate::unsetSpacing()
108{
109 if (!data) {
110 qWarning("QGraphicsAnchor::setSpacing: The anchor does not exist.");
111 return;
112 }
113
114 // Return to standard direction
115 hasSize = false;
116
117 layoutPrivate->q_func()->invalidate();
118}
119
120qreal QGraphicsAnchorPrivate::spacing() const
121{
122 if (!data) {
123 qWarning("QGraphicsAnchor::setSpacing: The anchor does not exist.");
124 return 0;
125 }
126
127 return preferredSize;
128}
129
130
131static void applySizePolicy(QSizePolicy::Policy policy,
132 qreal minSizeHint, qreal prefSizeHint, qreal maxSizeHint,
133 qreal *minSize, qreal *prefSize,
134 qreal *maxSize)
135{
136 // minSize, prefSize and maxSize are initialized
137 // with item's preferred Size: this is QSizePolicy::Fixed.
138 //
139 // Then we check each flag to find the resultant QSizePolicy,
140 // according to the following table:
141 //
142 // constant value
143 // QSizePolicy::Fixed 0
144 // QSizePolicy::Minimum GrowFlag
145 // QSizePolicy::Maximum ShrinkFlag
146 // QSizePolicy::Preferred GrowFlag | ShrinkFlag
147 // QSizePolicy::Ignored GrowFlag | ShrinkFlag | IgnoreFlag
148
149 if (policy & QSizePolicy::ShrinkFlag)
150 *minSize = minSizeHint;
151 else
152 *minSize = prefSizeHint;
153
154 if (policy & QSizePolicy::GrowFlag)
155 *maxSize = maxSizeHint;
156 else
157 *maxSize = prefSizeHint;
158
159 // Note that these two initializations are affected by the previous flags
160 if (policy & QSizePolicy::IgnoreFlag)
161 *prefSize = *minSize;
162 else
163 *prefSize = prefSizeHint;
164}
165
166AnchorData::~AnchorData()
167{
168 if (graphicsAnchor) {
169 // Remove reference to ourself to avoid double removal in
170 // QGraphicsAnchorPrivate dtor.
171 graphicsAnchor->d_func()->data = 0;
172
173 delete graphicsAnchor;
174 }
175}
176
177
178void AnchorData::refreshSizeHints(const QLayoutStyleInfo *styleInfo)
179{
180 QSizePolicy::Policy policy;
181 qreal minSizeHint;
182 qreal prefSizeHint;
183 qreal maxSizeHint;
184
185 if (item) {
186 // It is an internal anchor, fetch size information from the item
187 if (isLayoutAnchor) {
188 minSize = 0;
189 prefSize = 0;
190 maxSize = QWIDGETSIZE_MAX;
191 if (isCenterAnchor)
192 maxSize /= 2;
193
194 minPrefSize = prefSize;
195 maxPrefSize = maxSize;
196 return;
197 } else {
198 if (orientation == QGraphicsAnchorLayoutPrivate::Horizontal) {
199 policy = item->sizePolicy().horizontalPolicy();
200 minSizeHint = item->effectiveSizeHint(Qt::MinimumSize).width();
201 prefSizeHint = item->effectiveSizeHint(Qt::PreferredSize).width();
202 maxSizeHint = item->effectiveSizeHint(Qt::MaximumSize).width();
203 } else {
204 policy = item->sizePolicy().verticalPolicy();
205 minSizeHint = item->effectiveSizeHint(Qt::MinimumSize).height();
206 prefSizeHint = item->effectiveSizeHint(Qt::PreferredSize).height();
207 maxSizeHint = item->effectiveSizeHint(Qt::MaximumSize).height();
208 }
209
210 if (isCenterAnchor) {
211 minSizeHint /= 2;
212 prefSizeHint /= 2;
213 maxSizeHint /= 2;
214 }
215 }
216 } else {
217 // It is a user-created anchor, fetch size information from the associated QGraphicsAnchor
218 Q_ASSERT(graphicsAnchor);
219 QGraphicsAnchorPrivate *anchorPrivate = graphicsAnchor->d_func();
220
221 // Policy, min and max sizes are straightforward
222 policy = anchorPrivate->sizePolicy;
223 minSizeHint = 0;
224 maxSizeHint = QWIDGETSIZE_MAX;
225
226 // Preferred Size
227 if (anchorPrivate->hasSize) {
228 // Anchor has user-defined size
229 prefSizeHint = anchorPrivate->preferredSize;
230 } else {
231 // Fetch size information from style
232 const Qt::Orientation orient = Qt::Orientation(QGraphicsAnchorLayoutPrivate::edgeOrientation(from->m_edge) + 1);
233 qreal s = styleInfo->defaultSpacing(orient);
234 if (s < 0) {
235 QSizePolicy::ControlType controlTypeFrom = from->m_item->sizePolicy().controlType();
236 QSizePolicy::ControlType controlTypeTo = to->m_item->sizePolicy().controlType();
237 s = styleInfo->perItemSpacing(controlTypeFrom, controlTypeTo, orient);
238
239 // ### Currently we do not support negative anchors inside the graph.
240 // To avoid those being created by a negative style spacing, we must
241 // make this test.
242 if (s < 0)
243 s = 0;
244 }
245 prefSizeHint = s;
246 }
247 }
248
249 // Fill minSize, prefSize and maxSize based on policy and sizeHints
250 applySizePolicy(policy, minSizeHint, prefSizeHint, maxSizeHint,
251 &minSize, &prefSize, &maxSize);
252
253 minPrefSize = prefSize;
254 maxPrefSize = maxSize;
255
256 // Set the anchor effective sizes to preferred.
257 //
258 // Note: The idea here is that all items should remain at their
259 // preferred size unless where that's impossible. In cases where
260 // the item is subject to restrictions (anchored to the layout
261 // edges, for instance), the simplex solver will be run to
262 // recalculate and override the values we set here.
263 sizeAtMinimum = prefSize;
264 sizeAtPreferred = prefSize;
265 sizeAtMaximum = prefSize;
266}
267
268void ParallelAnchorData::updateChildrenSizes()
269{
270 firstEdge->sizeAtMinimum = sizeAtMinimum;
271 firstEdge->sizeAtPreferred = sizeAtPreferred;
272 firstEdge->sizeAtMaximum = sizeAtMaximum;
273
274 if (secondForward()) {
275 secondEdge->sizeAtMinimum = sizeAtMinimum;
276 secondEdge->sizeAtPreferred = sizeAtPreferred;
277 secondEdge->sizeAtMaximum = sizeAtMaximum;
278 } else {
279 secondEdge->sizeAtMinimum = -sizeAtMinimum;
280 secondEdge->sizeAtPreferred = -sizeAtPreferred;
281 secondEdge->sizeAtMaximum = -sizeAtMaximum;
282 }
283
284 firstEdge->updateChildrenSizes();
285 secondEdge->updateChildrenSizes();
286}
287
288/*
289 \internal
290
291 Initialize the parallel anchor size hints using the sizeHint information from
292 its children.
293
294 Note that parallel groups can lead to unfeasibility, so during calculation, we can
295 find out one unfeasibility. Because of that this method return boolean. This can't
296 happen in sequential, so there the method is void.
297 */
298bool ParallelAnchorData::calculateSizeHints()
299{
300 // Normalize second child sizes.
301 // A negative anchor of sizes min, minPref, pref, maxPref and max, is equivalent
302 // to a forward anchor of sizes -max, -maxPref, -pref, -minPref, -min
303 qreal secondMin;
304 qreal secondMinPref;
305 qreal secondPref;
306 qreal secondMaxPref;
307 qreal secondMax;
308
309 if (secondForward()) {
310 secondMin = secondEdge->minSize;
311 secondMinPref = secondEdge->minPrefSize;
312 secondPref = secondEdge->prefSize;
313 secondMaxPref = secondEdge->maxPrefSize;
314 secondMax = secondEdge->maxSize;
315 } else {
316 secondMin = -secondEdge->maxSize;
317 secondMinPref = -secondEdge->maxPrefSize;
318 secondPref = -secondEdge->prefSize;
319 secondMaxPref = -secondEdge->minPrefSize;
320 secondMax = -secondEdge->minSize;
321 }
322
323 minSize = qMax(firstEdge->minSize, secondMin);
324 maxSize = qMin(firstEdge->maxSize, secondMax);
325
326 // This condition means that the maximum size of one anchor being simplified is smaller than
327 // the minimum size of the other anchor. The consequence is that there won't be a valid size
328 // for this parallel setup.
329 if (minSize > maxSize) {
330 return false;
331 }
332
333 // Preferred size calculation
334 // The calculation of preferred size is done as follows:
335 //
336 // 1) Check whether one of the child anchors is the layout structural anchor
337 // If so, we can simply copy the preferred information from the other child,
338 // after bounding it to our minimum and maximum sizes.
339 // If not, then we proceed with the actual calculations.
340 //
341 // 2) The whole algorithm for preferred size calculation is based on the fact
342 // that, if a given anchor cannot remain at its preferred size, it'd rather
343 // grow than shrink.
344 //
345 // What happens though is that while this affirmative is true for simple
346 // anchors, it may not be true for sequential anchors that have one or more
347 // reversed anchors inside it. That happens because when a sequential anchor
348 // grows, any reversed anchors inside it may be required to shrink, something
349 // we try to avoid, as said above.
350 //
351 // To overcome this, besides their actual preferred size "prefSize", each anchor
352 // exports what we call "minPrefSize" and "maxPrefSize". These two values define
353 // a surrounding interval where, if required to move, the anchor would rather
354 // remain inside.
355 //
356 // For standard anchors, this area simply represents the region between
357 // prefSize and maxSize, which makes sense since our first affirmation.
358 // For composed anchors, these values are calculated as to reduce the global
359 // "damage", that is, to reduce the total deviation and the total amount of
360 // anchors that had to shrink.
361
362 if (firstEdge->isLayoutAnchor) {
363 prefSize = qBound(minSize, secondPref, maxSize);
364 minPrefSize = qBound(minSize, secondMinPref, maxSize);
365 maxPrefSize = qBound(minSize, secondMaxPref, maxSize);
366 } else if (secondEdge->isLayoutAnchor) {
367 prefSize = qBound(minSize, firstEdge->prefSize, maxSize);
368 minPrefSize = qBound(minSize, firstEdge->minPrefSize, maxSize);
369 maxPrefSize = qBound(minSize, firstEdge->maxPrefSize, maxSize);
370 } else {
371 // Calculate the intersection between the "preferred" regions of each child
372 const qreal lowerBoundary =
373 qBound(minSize, qMax(firstEdge->minPrefSize, secondMinPref), maxSize);
374 const qreal upperBoundary =
375 qBound(minSize, qMin(firstEdge->maxPrefSize, secondMaxPref), maxSize);
376 const qreal prefMean =
377 qBound(minSize, (firstEdge->prefSize + secondPref) / 2, maxSize);
378
379 if (lowerBoundary < upperBoundary) {
380 // If there is an intersection between the two regions, this intersection
381 // will be used as the preferred region of the parallel anchor itself.
382 // The preferred size will be the bounded average between the two preferred
383 // sizes.
384 prefSize = qBound(lowerBoundary, prefMean, upperBoundary);
385 minPrefSize = lowerBoundary;
386 maxPrefSize = upperBoundary;
387 } else {
388 // If there is no intersection, we have to attribute "damage" to at least
389 // one of the children. The minimum total damage is achieved in points
390 // inside the region that extends from (1) the upper boundary of the lower
391 // region to (2) the lower boundary of the upper region.
392 // Then, we expose this region as _our_ preferred region and once again,
393 // use the bounded average as our preferred size.
394 prefSize = qBound(upperBoundary, prefMean, lowerBoundary);
395 minPrefSize = upperBoundary;
396 maxPrefSize = lowerBoundary;
397 }
398 }
399
400 // See comment in AnchorData::refreshSizeHints() about sizeAt* values
401 sizeAtMinimum = prefSize;
402 sizeAtPreferred = prefSize;
403 sizeAtMaximum = prefSize;
404
405 return true;
406}
407
408/*!
409 \internal
410 returns the factor in the interval [-1, 1].
411 -1 is at Minimum
412 0 is at Preferred
413 1 is at Maximum
414*/
415static QPair<QGraphicsAnchorLayoutPrivate::Interval, qreal> getFactor(qreal value, qreal min,
416 qreal minPref, qreal pref,
417 qreal maxPref, qreal max)
418{
419 QGraphicsAnchorLayoutPrivate::Interval interval;
420 qreal lower;
421 qreal upper;
422
423 if (value < minPref) {
424 interval = QGraphicsAnchorLayoutPrivate::MinimumToMinPreferred;
425 lower = min;
426 upper = minPref;
427 } else if (value < pref) {
428 interval = QGraphicsAnchorLayoutPrivate::MinPreferredToPreferred;
429 lower = minPref;
430 upper = pref;
431 } else if (value < maxPref) {
432 interval = QGraphicsAnchorLayoutPrivate::PreferredToMaxPreferred;
433 lower = pref;
434 upper = maxPref;
435 } else {
436 interval = QGraphicsAnchorLayoutPrivate::MaxPreferredToMaximum;
437 lower = maxPref;
438 upper = max;
439 }
440
441 qreal progress;
442 if (upper == lower) {
443 progress = 0;
444 } else {
445 progress = (value - lower) / (upper - lower);
446 }
447
448 return qMakePair(interval, progress);
449}
450
451static qreal interpolate(const QPair<QGraphicsAnchorLayoutPrivate::Interval, qreal> &factor,
452 qreal min, qreal minPref, qreal pref, qreal maxPref, qreal max)
453{
454 qreal lower;
455 qreal upper;
456
457 switch (factor.first) {
458 case QGraphicsAnchorLayoutPrivate::MinimumToMinPreferred:
459 lower = min;
460 upper = minPref;
461 break;
462 case QGraphicsAnchorLayoutPrivate::MinPreferredToPreferred:
463 lower = minPref;
464 upper = pref;
465 break;
466 case QGraphicsAnchorLayoutPrivate::PreferredToMaxPreferred:
467 lower = pref;
468 upper = maxPref;
469 break;
470 case QGraphicsAnchorLayoutPrivate::MaxPreferredToMaximum:
471 lower = maxPref;
472 upper = max;
473 break;
474 }
475
476 return lower + factor.second * (upper - lower);
477}
478
479void SequentialAnchorData::updateChildrenSizes()
480{
481 // Band here refers if the value is in the Minimum To Preferred
482 // band (the lower band) or the Preferred To Maximum (the upper band).
483
484 const QPair<QGraphicsAnchorLayoutPrivate::Interval, qreal> minFactor =
485 getFactor(sizeAtMinimum, minSize, minPrefSize, prefSize, maxPrefSize, maxSize);
486 const QPair<QGraphicsAnchorLayoutPrivate::Interval, qreal> prefFactor =
487 getFactor(sizeAtPreferred, minSize, minPrefSize, prefSize, maxPrefSize, maxSize);
488 const QPair<QGraphicsAnchorLayoutPrivate::Interval, qreal> maxFactor =
489 getFactor(sizeAtMaximum, minSize, minPrefSize, prefSize, maxPrefSize, maxSize);
490
491 // XXX This is not safe if Vertex simplification takes place after the sequential
492 // anchor is created. In that case, "prev" will be a group-vertex, different from
493 // "from" or "to", that _contains_ one of them.
494 AnchorVertex *prev = from;
495
496 for (int i = 0; i < m_edges.count(); ++i) {
497 AnchorData *e = m_edges.at(i);
498
499 const bool edgeIsForward = (e->from == prev);
500 if (edgeIsForward) {
501 e->sizeAtMinimum = interpolate(minFactor, e->minSize, e->minPrefSize,
502 e->prefSize, e->maxPrefSize, e->maxSize);
503 e->sizeAtPreferred = interpolate(prefFactor, e->minSize, e->minPrefSize,
504 e->prefSize, e->maxPrefSize, e->maxSize);
505 e->sizeAtMaximum = interpolate(maxFactor, e->minSize, e->minPrefSize,
506 e->prefSize, e->maxPrefSize, e->maxSize);
507 prev = e->to;
508 } else {
509 Q_ASSERT(prev == e->to);
510 e->sizeAtMinimum = interpolate(minFactor, e->maxSize, e->maxPrefSize,
511 e->prefSize, e->minPrefSize, e->minSize);
512 e->sizeAtPreferred = interpolate(prefFactor, e->maxSize, e->maxPrefSize,
513 e->prefSize, e->minPrefSize, e->minSize);
514 e->sizeAtMaximum = interpolate(maxFactor, e->maxSize, e->maxPrefSize,
515 e->prefSize, e->minPrefSize, e->minSize);
516 prev = e->from;
517 }
518
519 e->updateChildrenSizes();
520 }
521}
522
523void SequentialAnchorData::calculateSizeHints()
524{
525 minSize = 0;
526 prefSize = 0;
527 maxSize = 0;
528 minPrefSize = 0;
529 maxPrefSize = 0;
530
531 AnchorVertex *prev = from;
532
533 for (int i = 0; i < m_edges.count(); ++i) {
534 AnchorData *edge = m_edges.at(i);
535
536 const bool edgeIsForward = (edge->from == prev);
537 if (edgeIsForward) {
538 minSize += edge->minSize;
539 prefSize += edge->prefSize;
540 maxSize += edge->maxSize;
541 minPrefSize += edge->minPrefSize;
542 maxPrefSize += edge->maxPrefSize;
543 prev = edge->to;
544 } else {
545 Q_ASSERT(prev == edge->to);
546 minSize -= edge->maxSize;
547 prefSize -= edge->prefSize;
548 maxSize -= edge->minSize;
549 minPrefSize -= edge->maxPrefSize;
550 maxPrefSize -= edge->minPrefSize;
551 prev = edge->from;
552 }
553 }
554
555 // See comment in AnchorData::refreshSizeHints() about sizeAt* values
556 sizeAtMinimum = prefSize;
557 sizeAtPreferred = prefSize;
558 sizeAtMaximum = prefSize;
559}
560
561#ifdef QT_DEBUG
562void AnchorData::dump(int indent) {
563 if (type == Parallel) {
564 qDebug("%*s type: parallel:", indent, "");
565 ParallelAnchorData *p = static_cast<ParallelAnchorData *>(this);
566 p->firstEdge->dump(indent+2);
567 p->secondEdge->dump(indent+2);
568 } else if (type == Sequential) {
569 SequentialAnchorData *s = static_cast<SequentialAnchorData *>(this);
570 int kids = s->m_edges.count();
571 qDebug("%*s type: sequential(%d):", indent, "", kids);
572 for (int i = 0; i < kids; ++i) {
573 s->m_edges.at(i)->dump(indent+2);
574 }
575 } else {
576 qDebug("%*s type: Normal:", indent, "");
577 }
578}
579
580#endif
581
582QSimplexConstraint *GraphPath::constraint(const GraphPath &path) const
583{
584 // Calculate
585 QSet<AnchorData *> cPositives;
586 QSet<AnchorData *> cNegatives;
587 QSet<AnchorData *> intersection;
588
589 cPositives = positives + path.negatives;
590 cNegatives = negatives + path.positives;
591
592 intersection = cPositives & cNegatives;
593
594 cPositives -= intersection;
595 cNegatives -= intersection;
596
597 // Fill
598 QSimplexConstraint *c = new QSimplexConstraint;
599 QSet<AnchorData *>::iterator i;
600 for (i = cPositives.begin(); i != cPositives.end(); ++i)
601 c->variables.insert(*i, 1.0);
602
603 for (i = cNegatives.begin(); i != cNegatives.end(); ++i)
604 c->variables.insert(*i, -1.0);
605
606 return c;
607}
608
609#ifdef QT_DEBUG
610QString GraphPath::toString() const
611{
612 QString string(QLatin1String("Path: "));
613 foreach(AnchorData *edge, positives)
614 string += QString::fromAscii(" (+++) %1").arg(edge->toString());
615
616 foreach(AnchorData *edge, negatives)
617 string += QString::fromAscii(" (---) %1").arg(edge->toString());
618
619 return string;
620}
621#endif
622
623QGraphicsAnchorLayoutPrivate::QGraphicsAnchorLayoutPrivate()
624 : calculateGraphCacheDirty(true), styleInfoDirty(true)
625{
626 for (int i = 0; i < NOrientations; ++i) {
627 for (int j = 0; j < 3; ++j) {
628 sizeHints[i][j] = -1;
629 }
630 interpolationProgress[i] = -1;
631
632 spacings[i] = -1;
633 graphHasConflicts[i] = false;
634
635 layoutFirstVertex[i] = 0;
636 layoutCentralVertex[i] = 0;
637 layoutLastVertex[i] = 0;
638 }
639}
640
641Qt::AnchorPoint QGraphicsAnchorLayoutPrivate::oppositeEdge(Qt::AnchorPoint edge)
642{
643 switch (edge) {
644 case Qt::AnchorLeft:
645 edge = Qt::AnchorRight;
646 break;
647 case Qt::AnchorRight:
648 edge = Qt::AnchorLeft;
649 break;
650 case Qt::AnchorTop:
651 edge = Qt::AnchorBottom;
652 break;
653 case Qt::AnchorBottom:
654 edge = Qt::AnchorTop;
655 break;
656 default:
657 break;
658 }
659 return edge;
660}
661
662
663/*!
664 * \internal
665 *
666 * helper function in order to avoid overflowing anchor sizes
667 * the returned size will never be larger than FLT_MAX
668 *
669 */
670inline static qreal checkAdd(qreal a, qreal b)
671{
672 if (FLT_MAX - b < a)
673 return FLT_MAX;
674 return a + b;
675}
676
677/*!
678 \internal
679
680 Adds \a newAnchor to the graph.
681
682 Returns the newAnchor itself if it could be added without further changes to the graph. If a
683 new parallel anchor had to be created, then returns the new parallel anchor. If a parallel anchor
684 had to be created and it results in an unfeasible setup, \a feasible is set to false, otherwise
685 true.
686
687 Note that in the case a new parallel anchor is created, it might also take over some constraints
688 from its children anchors.
689*/
690AnchorData *QGraphicsAnchorLayoutPrivate::addAnchorMaybeParallel(AnchorData *newAnchor, bool *feasible)
691{
692 Orientation orientation = Orientation(newAnchor->orientation);
693 Graph<AnchorVertex, AnchorData> &g = graph[orientation];
694 *feasible = true;
695
696 // If already exists one anchor where newAnchor is supposed to be, we create a parallel
697 // anchor.
698 if (AnchorData *oldAnchor = g.takeEdge(newAnchor->from, newAnchor->to)) {
699 ParallelAnchorData *parallel = new ParallelAnchorData(oldAnchor, newAnchor);
700
701 // The parallel anchor will "replace" its children anchors in
702 // every center constraint that they appear.
703
704 // ### If the dependent (center) anchors had reference(s) to their constraints, we
705 // could avoid traversing all the itemCenterConstraints.
706 QList<QSimplexConstraint *> &constraints = itemCenterConstraints[orientation];
707
708 AnchorData *children[2] = { oldAnchor, newAnchor };
709 QList<QSimplexConstraint *> *childrenConstraints[2] = { &parallel->m_firstConstraints,
710 &parallel->m_secondConstraints };
711
712 for (int i = 0; i < 2; ++i) {
713 AnchorData *child = children[i];
714 QList<QSimplexConstraint *> *childConstraints = childrenConstraints[i];
715
716 // We need to fix the second child constraints if the parallel group will have the
717 // opposite direction of the second child anchor. For the point of view of external
718 // entities, this anchor was reversed. So if at some point we say that the parallel
719 // has a value of 20, this mean that the second child (when reversed) will be
720 // assigned -20.
721 const bool needsReverse = i == 1 && !parallel->secondForward();
722
723 if (!child->isCenterAnchor)
724 continue;
725
726 parallel->isCenterAnchor = true;
727
728 for (int j = 0; j < constraints.count(); ++j) {
729 QSimplexConstraint *c = constraints[j];
730 if (c->variables.contains(child)) {
731 childConstraints->append(c);
732 qreal v = c->variables.take(child);
733 if (needsReverse)
734 v *= -1;
735 c->variables.insert(parallel, v);
736 }
737 }
738 }
739
740 // At this point we can identify that the parallel anchor is not feasible, e.g. one
741 // anchor minimum size is bigger than the other anchor maximum size.
742 *feasible = parallel->calculateSizeHints();
743 newAnchor = parallel;
744 }
745
746 g.createEdge(newAnchor->from, newAnchor->to, newAnchor);
747 return newAnchor;
748}
749
750/*!
751 \internal
752
753 Takes the sequence of vertices described by (\a before, \a vertices, \a after) and removes
754 all anchors connected to the vertices in \a vertices, returning one simplified anchor between
755 \a before and \a after.
756
757 Note that this function doesn't add the created anchor to the graph. This should be done by
758 the caller.
759*/
760static AnchorData *createSequence(Graph<AnchorVertex, AnchorData> *graph,
761 AnchorVertex *before,
762 const QVector<AnchorVertex*> &vertices,
763 AnchorVertex *after)
764{
765#if defined(QT_DEBUG) && 0
766 QString strVertices;
767 for (int i = 0; i < vertices.count(); ++i) {
768 strVertices += QString::fromAscii("%1 - ").arg(vertices.at(i)->toString());
769 }
770 QString strPath = QString::fromAscii("%1 - %2%3").arg(before->toString(), strVertices, after->toString());
771 qDebug("simplifying [%s] to [%s - %s]", qPrintable(strPath), qPrintable(before->toString()), qPrintable(after->toString()));
772#endif
773
774 AnchorVertex *prev = before;
775 QVector<AnchorData *> edges;
776
777 // Take from the graph, the edges that will be simplificated
778 for (int i = 0; i < vertices.count(); ++i) {
779 AnchorVertex *next = vertices.at(i);
780 AnchorData *ad = graph->takeEdge(prev, next);
781 Q_ASSERT(ad);
782 edges.append(ad);
783 prev = next;
784 }
785
786 // Take the last edge (not covered in the loop above)
787 AnchorData *ad = graph->takeEdge(vertices.last(), after);
788 Q_ASSERT(ad);
789 edges.append(ad);
790
791 // Create sequence
792 SequentialAnchorData *sequence = new SequentialAnchorData(vertices, edges);
793 sequence->from = before;
794 sequence->to = after;
795
796 sequence->calculateSizeHints();
797
798 return sequence;
799}
800
801/*!
802 \internal
803
804 The purpose of this function is to simplify the graph.
805 Simplification serves two purposes:
806 1. Reduce the number of edges in the graph, (thus the number of variables to the equation
807 solver is reduced, and the solver performs better).
808 2. Be able to do distribution of sequences of edges more intelligently (esp. with sequential
809 anchors)
810
811 It is essential that it must be possible to restore simplified anchors back to their "original"
812 form. This is done by restoreSimplifiedAnchor().
813
814 There are two types of simplification that can be done:
815 1. Sequential simplification
816 Sequential simplification means that all sequences of anchors will be merged into one single
817 anchor. Only anhcors that points in the same direction will be merged.
818 2. Parallel simplification
819 If a simplified sequential anchor is about to be inserted between two vertices in the graph
820 and there already exist an anchor between those two vertices, a parallel anchor will be
821 created that serves as a placeholder for the sequential anchor and the anchor that was
822 already between the two vertices.
823
824 The process of simplification can be described as:
825
826 1. Simplify all sequences of anchors into one anchor.
827 If no further simplification was done, go to (3)
828 - If there already exist an anchor where the sequential anchor is supposed to be inserted,
829 take that anchor out of the graph
830 - Then create a parallel anchor that holds the sequential anchor and the anchor just taken
831 out of the graph.
832 2. Go to (1)
833 3. Done
834
835 When creating the parallel anchors, the algorithm might identify unfeasible situations. In this
836 case the simplification process stops and returns false. Otherwise returns true.
837*/
838bool QGraphicsAnchorLayoutPrivate::simplifyGraph(Orientation orientation)
839{
840 if (items.isEmpty())
841 return true;
842
843#if defined(QT_DEBUG) && 0
844 qDebug("Simplifying Graph for %s",
845 orientation == Horizontal ? "Horizontal" : "Vertical");
846
847 static int count = 0;
848 if (orientation == Horizontal) {
849 count++;
850 dumpGraph(QString::fromAscii("%1-full").arg(count));
851 }
852#endif
853
854 // Vertex simplification
855 if (!simplifyVertices(orientation)) {
856 restoreVertices(orientation);
857 return false;
858 }
859
860 // Anchor simplification
861 bool dirty;
862 bool feasible = true;
863 do {
864 dirty = simplifyGraphIteration(orientation, &feasible);
865 } while (dirty && feasible);
866
867 // Note that if we are not feasible, we fallback and make sure that the graph is fully restored
868 if (!feasible) {
869 restoreSimplifiedGraph(orientation);
870 restoreVertices(orientation);
871 return false;
872 }
873
874#if defined(QT_DEBUG) && 0
875 dumpGraph(QString::fromAscii("%1-simplified-%2").arg(count).arg(
876 QString::fromAscii(orientation == Horizontal ? "Horizontal" : "Vertical")));
877#endif
878
879 return true;
880}
881
882static AnchorVertex *replaceVertex_helper(AnchorData *data, AnchorVertex *oldV, AnchorVertex *newV)
883{
884 AnchorVertex *other;
885 if (data->from == oldV) {
886 data->from = newV;
887 other = data->to;
888 } else {
889 data->to = newV;
890 other = data->from;
891 }
892 return other;
893}
894
895bool QGraphicsAnchorLayoutPrivate::replaceVertex(Orientation orientation, AnchorVertex *oldV,
896 AnchorVertex *newV, const QList<AnchorData *> &edges)
897{
898 Graph<AnchorVertex, AnchorData> &g = graph[orientation];
899 bool feasible = true;
900
901 for (int i = 0; i < edges.count(); ++i) {
902 AnchorData *ad = edges[i];
903 AnchorVertex *otherV = replaceVertex_helper(ad, oldV, newV);
904
905#if defined(QT_DEBUG)
906 ad->name = QString::fromAscii("%1 --to--> %2").arg(ad->from->toString()).arg(ad->to->toString());
907#endif
908
909 bool newFeasible;
910 AnchorData *newAnchor = addAnchorMaybeParallel(ad, &newFeasible);
911 feasible &= newFeasible;
912
913 if (newAnchor != ad) {
914 // A parallel was created, we mark that in the list of anchors created by vertex
915 // simplification. This is needed because we want to restore them in a separate step
916 // from the restoration of anchor simplification.
917 anchorsFromSimplifiedVertices[orientation].append(newAnchor);
918 }
919
920 g.takeEdge(oldV, otherV);
921 }
922
923 return feasible;
924}
925
926/*!
927 \internal
928*/
929bool QGraphicsAnchorLayoutPrivate::simplifyVertices(Orientation orientation)
930{
931 Q_Q(QGraphicsAnchorLayout);
932 Graph<AnchorVertex, AnchorData> &g = graph[orientation];
933
934 // We'll walk through vertices
935 QStack<AnchorVertex *> stack;
936 stack.push(layoutFirstVertex[orientation]);
937 QSet<AnchorVertex *> visited;
938
939 while (!stack.isEmpty()) {
940 AnchorVertex *v = stack.pop();
941 visited.insert(v);
942
943 // Each adjacent of 'v' is a possible vertex to be merged. So we traverse all of
944 // them. Since once a merge is made, we might add new adjacents, and we don't want to
945 // pass two times through one adjacent. The 'index' is used to track our position.
946 QList<AnchorVertex *> adjacents = g.adjacentVertices(v);
947 int index = 0;
948
949 while (index < adjacents.count()) {
950 AnchorVertex *next = adjacents.at(index);
951 index++;
952
953 AnchorData *data = g.edgeData(v, next);
954 const bool bothLayoutVertices = v->m_item == q && next->m_item == q;
955 const bool zeroSized = !data->minSize && !data->maxSize;
956
957 if (!bothLayoutVertices && zeroSized) {
958
959 // Create a new vertex pair, note that we keep a list of those vertices so we can
960 // easily process them when restoring the graph.
961 AnchorVertexPair *newV = new AnchorVertexPair(v, next, data);
962 simplifiedVertices[orientation].append(newV);
963
964 // Collect the anchors of both vertices, the new vertex pair will take their place
965 // in those anchors
966 const QList<AnchorVertex *> &vAdjacents = g.adjacentVertices(v);
967 const QList<AnchorVertex *> &nextAdjacents = g.adjacentVertices(next);
968
969 for (int i = 0; i < vAdjacents.count(); ++i) {
970 AnchorVertex *adjacent = vAdjacents.at(i);
971 if (adjacent != next) {
972 AnchorData *ad = g.edgeData(v, adjacent);
973 newV->m_firstAnchors.append(ad);
974 }
975 }
976
977 for (int i = 0; i < nextAdjacents.count(); ++i) {
978 AnchorVertex *adjacent = nextAdjacents.at(i);
979 if (adjacent != v) {
980 AnchorData *ad = g.edgeData(next, adjacent);
981 newV->m_secondAnchors.append(ad);
982
983 // We'll also add new vertices to the adjacent list of the new 'v', to be
984 // created as a vertex pair and replace the current one.
985 if (!adjacents.contains(adjacent))
986 adjacents.append(adjacent);
987 }
988 }
989
990 // ### merge this loop into the ones that calculated m_firstAnchors/m_secondAnchors?
991 // Make newV take the place of v and next
992 bool feasible = replaceVertex(orientation, v, newV, newV->m_firstAnchors);
993 feasible &= replaceVertex(orientation, next, newV, newV->m_secondAnchors);
994
995 // Update the layout vertex information if one of the vertices is a layout vertex.
996 AnchorVertex *layoutVertex = 0;
997 if (v->m_item == q)
998 layoutVertex = v;
999 else if (next->m_item == q)
1000 layoutVertex = next;
1001
1002 if (layoutVertex) {
1003 // Layout vertices always have m_item == q...
1004 newV->m_item = q;
1005 changeLayoutVertex(orientation, layoutVertex, newV);
1006 }
1007
1008 g.takeEdge(v, next);
1009
1010 // If a non-feasibility is found, we leave early and cancel the simplification
1011 if (!feasible)
1012 return false;
1013
1014 v = newV;
1015 visited.insert(newV);
1016
1017 } else if (!visited.contains(next) && !stack.contains(next)) {
1018 // If the adjacent is not fit for merge and it wasn't visited by the outermost
1019 // loop, we add it to the stack.
1020 stack.push(next);
1021 }
1022 }
1023 }
1024
1025 return true;
1026}
1027
1028/*!
1029 \internal
1030
1031 One iteration of the simplification algorithm. Returns true if another iteration is needed.
1032
1033 The algorithm walks the graph in depth-first order, and only collects vertices that has two
1034 edges connected to it. If the vertex does not have two edges or if it is a layout edge, it
1035 will take all the previously collected vertices and try to create a simplified sequential
1036 anchor representing all the previously collected vertices. Once the simplified anchor is
1037 inserted, the collected list is cleared in order to find the next sequence to simplify.
1038
1039 Note that there are some catches to this that are not covered by the above explanation, see
1040 the function comments for more details.
1041*/
1042bool QGraphicsAnchorLayoutPrivate::simplifyGraphIteration(QGraphicsAnchorLayoutPrivate::Orientation orientation,
1043 bool *feasible)
1044{
1045 Q_Q(QGraphicsAnchorLayout);
1046 Graph<AnchorVertex, AnchorData> &g = graph[orientation];
1047
1048 QSet<AnchorVertex *> visited;
1049 QStack<QPair<AnchorVertex *, AnchorVertex *> > stack;
1050 stack.push(qMakePair(static_cast<AnchorVertex *>(0), layoutFirstVertex[orientation]));
1051 QVector<AnchorVertex*> candidates;
1052
1053 // Walk depth-first, in the stack we store start of the candidate sequence (beforeSequence)
1054 // and the vertex to be visited.
1055 while (!stack.isEmpty()) {
1056 QPair<AnchorVertex *, AnchorVertex *> pair = stack.pop();
1057 AnchorVertex *beforeSequence = pair.first;
1058 AnchorVertex *v = pair.second;
1059
1060 // The basic idea is to determine whether we found an end of sequence,
1061 // if that's the case, we stop adding vertices to the candidate list
1062 // and do a simplification step.
1063 //
1064 // A vertex can trigger an end of sequence if
1065 // (a) it is a layout vertex, we don't simplify away the layout vertices;
1066 // (b) it does not have exactly 2 adjacents;
1067 // (c) its next adjacent is already visited (a cycle in the graph).
1068 // (d) the next anchor is a center anchor.
1069
1070 const QList<AnchorVertex *> &adjacents = g.adjacentVertices(v);
1071 const bool isLayoutVertex = v->m_item == q;
1072 AnchorVertex *afterSequence = v;
1073 bool endOfSequence = false;
1074
1075 //
1076 // Identify the end cases.
1077 //
1078
1079 // Identifies cases (a) and (b)
1080 endOfSequence = isLayoutVertex || adjacents.count() != 2;
1081
1082 if (!endOfSequence) {
1083 // This is a tricky part. We peek at the next vertex to find out whether
1084 //
1085 // - we already visited the next vertex (c);
1086 // - the next anchor is a center (d).
1087 //
1088 // Those are needed to identify the remaining end of sequence cases. Note that unlike
1089 // (a) and (b), we preempt the end of sequence by looking into the next vertex.
1090
1091 // Peek at the next vertex
1092 AnchorVertex *after;
1093 if (candidates.isEmpty())
1094 after = (beforeSequence == adjacents.last() ? adjacents.first() : adjacents.last());
1095 else
1096 after = (candidates.last() == adjacents.last() ? adjacents.first() : adjacents.last());
1097
1098 // ### At this point we assumed that candidates will not contain 'after', this may not hold
1099 // when simplifying FLOATing anchors.
1100 Q_ASSERT(!candidates.contains(after));
1101
1102 const AnchorData *data = g.edgeData(v, after);
1103 Q_ASSERT(data);
1104 const bool cycleFound = visited.contains(after);
1105
1106 // Now cases (c) and (d)...
1107 endOfSequence = cycleFound || data->isCenterAnchor;
1108
1109 if (!endOfSequence) {
1110 // If it's not an end of sequence, then the vertex didn't trigger neither of the
1111 // previously three cases, so it can be added to the candidates list.
1112 candidates.append(v);
1113 } else if (cycleFound && (beforeSequence != after)) {
1114 afterSequence = after;
1115 candidates.append(v);
1116 }
1117 }
1118
1119 //
1120 // Add next non-visited vertices to the stack.
1121 //
1122 for (int i = 0; i < adjacents.count(); ++i) {
1123 AnchorVertex *next = adjacents.at(i);
1124 if (visited.contains(next))
1125 continue;
1126
1127 // If current vertex is an end of sequence, and it'll reset the candidates list. So
1128 // the next vertices will build candidates lists with the current vertex as 'before'
1129 // vertex. If it's not an end of sequence, we keep the original 'before' vertex,
1130 // since we are keeping the candidates list.
1131 if (endOfSequence)
1132 stack.push(qMakePair(v, next));
1133 else
1134 stack.push(qMakePair(beforeSequence, next));
1135 }
1136
1137 visited.insert(v);
1138
1139 if (!endOfSequence || candidates.isEmpty())
1140 continue;
1141
1142 //
1143 // Create a sequence for (beforeSequence, candidates, afterSequence).
1144 //
1145
1146 // One restriction we have is to not simplify half of an anchor and let the other half
1147 // unsimplified. So we remove center edges before and after the sequence.
1148 const AnchorData *firstAnchor = g.edgeData(beforeSequence, candidates.first());
1149 if (firstAnchor->isCenterAnchor) {
1150 beforeSequence = candidates.first();
1151 candidates.remove(0);
1152
1153 // If there's not candidates to be simplified, leave.
1154 if (candidates.isEmpty())
1155 continue;
1156 }
1157
1158 const AnchorData *lastAnchor = g.edgeData(candidates.last(), afterSequence);
1159 if (lastAnchor->isCenterAnchor) {
1160 afterSequence = candidates.last();
1161 candidates.remove(candidates.count() - 1);
1162
1163 if (candidates.isEmpty())
1164 continue;
1165 }
1166
1167 //
1168 // Add the sequence to the graph.
1169 //
1170
1171 AnchorData *sequence = createSequence(&g, beforeSequence, candidates, afterSequence);
1172
1173 // If 'beforeSequence' and 'afterSequence' already had an anchor between them, we'll
1174 // create a parallel anchor between the new sequence and the old anchor.
1175 bool newFeasible;
1176 AnchorData *newAnchor = addAnchorMaybeParallel(sequence, &newFeasible);
1177
1178 if (!newFeasible) {
1179 *feasible = false;
1180 return false;
1181 }
1182
1183 // When a new parallel anchor is create in the graph, we finish the iteration and return
1184 // true to indicate a new iteration is needed. This happens because a parallel anchor
1185 // changes the number of adjacents one vertex has, possibly opening up oportunities for
1186 // building candidate lists (when adjacents == 2).
1187 if (newAnchor != sequence)
1188 return true;
1189
1190 // If there was no parallel simplification, we'll keep walking the graph. So we clear the
1191 // candidates list to start again.
1192 candidates.clear();
1193 }
1194
1195 return false;
1196}
1197
1198void QGraphicsAnchorLayoutPrivate::restoreSimplifiedAnchor(AnchorData *edge)
1199{
1200#if 0
1201 static const char *anchortypes[] = {"Normal",
1202 "Sequential",
1203 "Parallel"};
1204 qDebug("Restoring %s edge.", anchortypes[int(edge->type)]);
1205#endif
1206
1207 Graph<AnchorVertex, AnchorData> &g = graph[edge->orientation];
1208
1209 if (edge->type == AnchorData::Normal) {
1210 g.createEdge(edge->from, edge->to, edge);
1211
1212 } else if (edge->type == AnchorData::Sequential) {
1213 SequentialAnchorData *sequence = static_cast<SequentialAnchorData *>(edge);
1214
1215 for (int i = 0; i < sequence->m_edges.count(); ++i) {
1216 AnchorData *data = sequence->m_edges.at(i);
1217 restoreSimplifiedAnchor(data);
1218 }
1219
1220 delete sequence;
1221
1222 } else if (edge->type == AnchorData::Parallel) {
1223
1224 // Skip parallel anchors that were created by vertex simplification, they will be processed
1225 // later, when restoring vertex simplification.
1226 // ### we could improve this check bit having a bit inside 'edge'
1227 if (anchorsFromSimplifiedVertices[edge->orientation].contains(edge))
1228 return;
1229
1230 ParallelAnchorData* parallel = static_cast<ParallelAnchorData*>(edge);
1231 restoreSimplifiedConstraints(parallel);
1232
1233 // ### Because of the way parallel anchors are created in the anchor simplification
1234 // algorithm, we know that one of these will be a sequence, so it'll be safe if the other
1235 // anchor create an edge between the same vertices as the parallel.
1236 Q_ASSERT(parallel->firstEdge->type == AnchorData::Sequential
1237 || parallel->secondEdge->type == AnchorData::Sequential);
1238 restoreSimplifiedAnchor(parallel->firstEdge);
1239 restoreSimplifiedAnchor(parallel->secondEdge);
1240
1241 delete parallel;
1242 }
1243}
1244
1245void QGraphicsAnchorLayoutPrivate::restoreSimplifiedConstraints(ParallelAnchorData *parallel)
1246{
1247 if (!parallel->isCenterAnchor)
1248 return;
1249
1250 for (int i = 0; i < parallel->m_firstConstraints.count(); ++i) {
1251 QSimplexConstraint *c = parallel->m_firstConstraints.at(i);
1252 qreal v = c->variables[parallel];
1253 c->variables.remove(parallel);
1254 c->variables.insert(parallel->firstEdge, v);
1255 }
1256
1257 // When restoring, we might have to revert constraints back. See comments on
1258 // addAnchorMaybeParallel().
1259 const bool needsReverse = !parallel->secondForward();
1260
1261 for (int i = 0; i < parallel->m_secondConstraints.count(); ++i) {
1262 QSimplexConstraint *c = parallel->m_secondConstraints.at(i);
1263 qreal v = c->variables[parallel];
1264 if (needsReverse)
1265 v *= -1;
1266 c->variables.remove(parallel);
1267 c->variables.insert(parallel->secondEdge, v);
1268 }
1269}
1270
1271void QGraphicsAnchorLayoutPrivate::restoreSimplifiedGraph(Orientation orientation)
1272{
1273#if 0
1274 qDebug("Restoring Simplified Graph for %s",
1275 orientation == Horizontal ? "Horizontal" : "Vertical");
1276#endif
1277
1278 // Restore anchor simplification
1279 Graph<AnchorVertex, AnchorData> &g = graph[orientation];
1280 QList<QPair<AnchorVertex*, AnchorVertex*> > connections = g.connections();
1281 for (int i = 0; i < connections.count(); ++i) {
1282 AnchorVertex *v1 = connections.at(i).first;
1283 AnchorVertex *v2 = connections.at(i).second;
1284 AnchorData *edge = g.edgeData(v1, v2);
1285
1286 // We restore only sequential anchors and parallels that were not created by
1287 // vertex simplification.
1288 if (edge->type == AnchorData::Sequential
1289 || (edge->type == AnchorData::Parallel &&
1290 !anchorsFromSimplifiedVertices[orientation].contains(edge))) {
1291
1292 g.takeEdge(v1, v2);
1293 restoreSimplifiedAnchor(edge);
1294 }
1295 }
1296
1297 restoreVertices(orientation);
1298}
1299
1300void QGraphicsAnchorLayoutPrivate::restoreVertices(Orientation orientation)
1301{
1302 Q_Q(QGraphicsAnchorLayout);
1303
1304 Graph<AnchorVertex, AnchorData> &g = graph[orientation];
1305 QList<AnchorVertexPair *> &toRestore = simplifiedVertices[orientation];
1306
1307 // Since we keep a list of parallel anchors and vertices that were created during vertex
1308 // simplification, we can now iterate on those lists instead of traversing the graph
1309 // recursively.
1310
1311 // First, restore the constraints changed when we created parallel anchors. Note that this
1312 // works at this point because the constraints doesn't depend on vertex information and at
1313 // this point it's always safe to identify whether the second child is forward or backwards.
1314 // In the next step, we'll change the anchors vertices so that would not be possible anymore.
1315 QList<AnchorData *> &parallelAnchors = anchorsFromSimplifiedVertices[orientation];
1316
1317 for (int i = parallelAnchors.count() - 1; i >= 0; --i) {
1318 ParallelAnchorData *parallel = static_cast<ParallelAnchorData *>(parallelAnchors.at(i));
1319 restoreSimplifiedConstraints(parallel);
1320 }
1321
1322 // Then, we will restore the vertices in the inverse order of creation, this way we ensure that
1323 // the vertex being restored was not wrapped by another simplification.
1324 for (int i = toRestore.count() - 1; i >= 0; --i) {
1325 AnchorVertexPair *pair = toRestore.at(i);
1326 QList<AnchorVertex *> adjacents = g.adjacentVertices(pair);
1327
1328 // Restore the removed edge, this will also restore both vertices 'first' and 'second' to
1329 // the graph structure.
1330 AnchorVertex *first = pair->m_first;
1331 AnchorVertex *second = pair->m_second;
1332 g.createEdge(first, second, pair->m_removedAnchor);
1333
1334 // Restore the anchors for the first child vertex
1335 for (int j = 0; j < pair->m_firstAnchors.count(); ++j) {
1336 AnchorData *ad = pair->m_firstAnchors.at(j);
1337 Q_ASSERT(ad->from == pair || ad->to == pair);
1338
1339 replaceVertex_helper(ad, pair, first);
1340 g.createEdge(ad->from, ad->to, ad);
1341 }
1342
1343 // Restore the anchors for the second child vertex
1344 for (int j = 0; j < pair->m_secondAnchors.count(); ++j) {
1345 AnchorData *ad = pair->m_secondAnchors.at(j);
1346 Q_ASSERT(ad->from == pair || ad->to == pair);
1347
1348 replaceVertex_helper(ad, pair, second);
1349 g.createEdge(ad->from, ad->to, ad);
1350 }
1351
1352 for (int j = 0; j < adjacents.count(); ++j) {
1353 g.takeEdge(pair, adjacents.at(j));
1354 }
1355
1356 // The pair simplified a layout vertex, so place back the correct vertex in the variable
1357 // that track layout vertices
1358 if (pair->m_item == q) {
1359 AnchorVertex *layoutVertex = first->m_item == q ? first : second;
1360 Q_ASSERT(layoutVertex->m_item == q);
1361 changeLayoutVertex(orientation, pair, layoutVertex);
1362 }
1363
1364 delete pair;
1365 }
1366 qDeleteAll(parallelAnchors);
1367 parallelAnchors.clear();
1368 toRestore.clear();
1369}
1370
1371QGraphicsAnchorLayoutPrivate::Orientation
1372QGraphicsAnchorLayoutPrivate::edgeOrientation(Qt::AnchorPoint edge)
1373{
1374 return edge > Qt::AnchorRight ? Vertical : Horizontal;
1375}
1376
1377/*!
1378 \internal
1379
1380 Create internal anchors to connect the layout edges (Left to Right and
1381 Top to Bottom).
1382
1383 These anchors doesn't have size restrictions, that will be enforced by
1384 other anchors and items in the layout.
1385*/
1386void QGraphicsAnchorLayoutPrivate::createLayoutEdges()
1387{
1388 Q_Q(QGraphicsAnchorLayout);
1389 QGraphicsLayoutItem *layout = q;
1390
1391 // Horizontal
1392 AnchorData *data = new AnchorData;
1393 addAnchor_helper(layout, Qt::AnchorLeft, layout,
1394 Qt::AnchorRight, data);
1395 data->maxSize = QWIDGETSIZE_MAX;
1396
1397 // Save a reference to layout vertices
1398 layoutFirstVertex[Horizontal] = internalVertex(layout, Qt::AnchorLeft);
1399 layoutCentralVertex[Horizontal] = 0;
1400 layoutLastVertex[Horizontal] = internalVertex(layout, Qt::AnchorRight);
1401
1402 // Vertical
1403 data = new AnchorData;
1404 addAnchor_helper(layout, Qt::AnchorTop, layout,
1405 Qt::AnchorBottom, data);
1406 data->maxSize = QWIDGETSIZE_MAX;
1407
1408 // Save a reference to layout vertices
1409 layoutFirstVertex[Vertical] = internalVertex(layout, Qt::AnchorTop);
1410 layoutCentralVertex[Vertical] = 0;
1411 layoutLastVertex[Vertical] = internalVertex(layout, Qt::AnchorBottom);
1412}
1413
1414void QGraphicsAnchorLayoutPrivate::deleteLayoutEdges()
1415{
1416 Q_Q(QGraphicsAnchorLayout);
1417
1418 Q_ASSERT(!internalVertex(q, Qt::AnchorHorizontalCenter));
1419 Q_ASSERT(!internalVertex(q, Qt::AnchorVerticalCenter));
1420
1421 removeAnchor_helper(internalVertex(q, Qt::AnchorLeft),
1422 internalVertex(q, Qt::AnchorRight));
1423 removeAnchor_helper(internalVertex(q, Qt::AnchorTop),
1424 internalVertex(q, Qt::AnchorBottom));
1425}
1426
1427void QGraphicsAnchorLayoutPrivate::createItemEdges(QGraphicsLayoutItem *item)
1428{
1429 items.append(item);
1430
1431 // Create horizontal and vertical internal anchors for the item and
1432 // refresh its size hint / policy values.
1433 AnchorData *data = new AnchorData;
1434 addAnchor_helper(item, Qt::AnchorLeft, item, Qt::AnchorRight, data);
1435 data->refreshSizeHints();
1436
1437 data = new AnchorData;
1438 addAnchor_helper(item, Qt::AnchorTop, item, Qt::AnchorBottom, data);
1439 data->refreshSizeHints();
1440}
1441
1442/*!
1443 \internal
1444
1445 By default, each item in the layout is represented internally as
1446 a single anchor in each direction. For instance, from Left to Right.
1447
1448 However, to support anchorage of items to the center of items, we
1449 must split this internal anchor into two half-anchors. From Left
1450 to Center and then from Center to Right, with the restriction that
1451 these anchors must have the same time at all times.
1452*/
1453void QGraphicsAnchorLayoutPrivate::createCenterAnchors(
1454 QGraphicsLayoutItem *item, Qt::AnchorPoint centerEdge)
1455{
1456 Q_Q(QGraphicsAnchorLayout);
1457
1458 Orientation orientation;
1459 switch (centerEdge) {
1460 case Qt::AnchorHorizontalCenter:
1461 orientation = Horizontal;
1462 break;
1463 case Qt::AnchorVerticalCenter:
1464 orientation = Vertical;
1465 break;
1466 default:
1467 // Don't create center edges unless needed
1468 return;
1469 }
1470
1471 // Check if vertex already exists
1472 if (internalVertex(item, centerEdge))
1473 return;
1474
1475 // Orientation code
1476 Qt::AnchorPoint firstEdge;
1477 Qt::AnchorPoint lastEdge;
1478
1479 if (orientation == Horizontal) {
1480 firstEdge = Qt::AnchorLeft;
1481 lastEdge = Qt::AnchorRight;
1482 } else {
1483 firstEdge = Qt::AnchorTop;
1484 lastEdge = Qt::AnchorBottom;
1485 }
1486
1487 AnchorVertex *first = internalVertex(item, firstEdge);
1488 AnchorVertex *last = internalVertex(item, lastEdge);
1489 Q_ASSERT(first && last);
1490
1491 // Create new anchors
1492 QSimplexConstraint *c = new QSimplexConstraint;
1493
1494 AnchorData *data = new AnchorData;
1495 c->variables.insert(data, 1.0);
1496 addAnchor_helper(item, firstEdge, item, centerEdge, data);
1497 data->isCenterAnchor = true;
1498 data->dependency = AnchorData::Master;
1499 data->refreshSizeHints();
1500
1501 data = new AnchorData;
1502 c->variables.insert(data, -1.0);
1503 addAnchor_helper(item, centerEdge, item, lastEdge, data);
1504 data->isCenterAnchor = true;
1505 data->dependency = AnchorData::Slave;
1506 data->refreshSizeHints();
1507
1508 itemCenterConstraints[orientation].append(c);
1509
1510 // Remove old one
1511 removeAnchor_helper(first, last);
1512
1513 if (item == q) {
1514 layoutCentralVertex[orientation] = internalVertex(q, centerEdge);
1515 }
1516}
1517
1518void QGraphicsAnchorLayoutPrivate::removeCenterAnchors(
1519 QGraphicsLayoutItem *item, Qt::AnchorPoint centerEdge,
1520 bool substitute)
1521{
1522 Q_Q(QGraphicsAnchorLayout);
1523
1524 Orientation orientation;
1525 switch (centerEdge) {
1526 case Qt::AnchorHorizontalCenter:
1527 orientation = Horizontal;
1528 break;
1529 case Qt::AnchorVerticalCenter:
1530 orientation = Vertical;
1531 break;
1532 default:
1533 // Don't remove edges that not the center ones
1534 return;
1535 }
1536
1537 // Orientation code
1538 Qt::AnchorPoint firstEdge;
1539 Qt::AnchorPoint lastEdge;
1540
1541 if (orientation == Horizontal) {
1542 firstEdge = Qt::AnchorLeft;
1543 lastEdge = Qt::AnchorRight;
1544 } else {
1545 firstEdge = Qt::AnchorTop;
1546 lastEdge = Qt::AnchorBottom;
1547 }
1548
1549 AnchorVertex *center = internalVertex(item, centerEdge);
1550 if (!center)
1551 return;
1552 AnchorVertex *first = internalVertex(item, firstEdge);
1553
1554 Q_ASSERT(first);
1555 Q_ASSERT(center);
1556
1557 Graph<AnchorVertex, AnchorData> &g = graph[orientation];
1558
1559
1560 AnchorData *oldData = g.edgeData(first, center);
1561 // Remove center constraint
1562 for (int i = itemCenterConstraints[orientation].count() - 1; i >= 0; --i) {
1563 if (itemCenterConstraints[orientation].at(i)->variables.contains(oldData)) {
1564 delete itemCenterConstraints[orientation].takeAt(i);
1565 break;
1566 }
1567 }
1568
1569 if (substitute) {
1570 // Create the new anchor that should substitute the left-center-right anchors.
1571 AnchorData *data = new AnchorData;
1572 addAnchor_helper(item, firstEdge, item, lastEdge, data);
1573 data->refreshSizeHints();
1574
1575 // Remove old anchors
1576 removeAnchor_helper(first, center);
1577 removeAnchor_helper(center, internalVertex(item, lastEdge));
1578
1579 } else {
1580 // this is only called from removeAnchors()
1581 // first, remove all non-internal anchors
1582 QList<AnchorVertex*> adjacents = g.adjacentVertices(center);
1583 for (int i = 0; i < adjacents.count(); ++i) {
1584 AnchorVertex *v = adjacents.at(i);
1585 if (v->m_item != item) {
1586 removeAnchor_helper(center, internalVertex(v->m_item, v->m_edge));
1587 }
1588 }
1589 // when all non-internal anchors is removed it will automatically merge the
1590 // center anchor into a left-right (or top-bottom) anchor. We must also delete that.
1591 // by this time, the center vertex is deleted and merged into a non-centered internal anchor
1592 removeAnchor_helper(first, internalVertex(item, lastEdge));
1593 }
1594
1595 if (item == q) {
1596 layoutCentralVertex[orientation] = 0;
1597 }
1598}
1599
1600
1601void QGraphicsAnchorLayoutPrivate::removeCenterConstraints(QGraphicsLayoutItem *item,
1602 Orientation orientation)
1603{
1604 // Remove the item center constraints associated to this item
1605 // ### This is a temporary solution. We should probably use a better
1606 // data structure to hold items and/or their associated constraints
1607 // so that we can remove those easily
1608
1609 AnchorVertex *first = internalVertex(item, orientation == Horizontal ?
1610 Qt::AnchorLeft :
1611 Qt::AnchorTop);
1612 AnchorVertex *center = internalVertex(item, orientation == Horizontal ?
1613 Qt::AnchorHorizontalCenter :
1614 Qt::AnchorVerticalCenter);
1615
1616 // Skip if no center constraints exist
1617 if (!center)
1618 return;
1619
1620 Q_ASSERT(first);
1621 AnchorData *internalAnchor = graph[orientation].edgeData(first, center);
1622
1623 // Look for our anchor in all item center constraints, then remove it
1624 for (int i = 0; i < itemCenterConstraints[orientation].size(); ++i) {
1625 if (itemCenterConstraints[orientation].at(i)->variables.contains(internalAnchor)) {
1626 delete itemCenterConstraints[orientation].takeAt(i);
1627 break;
1628 }
1629 }
1630}
1631
1632/*!
1633 * \internal
1634 * Implements the high level "addAnchor" feature. Called by the public API
1635 * addAnchor method.
1636 *
1637 * The optional \a spacing argument defines the size of the anchor. If not provided,
1638 * the anchor size is either 0 or not-set, depending on type of anchor created (see
1639 * matrix below).
1640 *
1641 * All anchors that remain with size not-set will assume the standard spacing,
1642 * set either by the layout style or through the "setSpacing" layout API.
1643 */
1644QGraphicsAnchor *QGraphicsAnchorLayoutPrivate::addAnchor(QGraphicsLayoutItem *firstItem,
1645 Qt::AnchorPoint firstEdge,
1646 QGraphicsLayoutItem *secondItem,
1647 Qt::AnchorPoint secondEdge,
1648 qreal *spacing)
1649{
1650 Q_Q(QGraphicsAnchorLayout);
1651 if ((firstItem == 0) || (secondItem == 0)) {
1652 qWarning("QGraphicsAnchorLayout::addAnchor(): "
1653 "Cannot anchor NULL items");
1654 return 0;
1655 }
1656
1657 if (firstItem == secondItem) {
1658 qWarning("QGraphicsAnchorLayout::addAnchor(): "
1659 "Cannot anchor the item to itself");
1660 return 0;
1661 }
1662
1663 if (edgeOrientation(secondEdge) != edgeOrientation(firstEdge)) {
1664 qWarning("QGraphicsAnchorLayout::addAnchor(): "
1665 "Cannot anchor edges of different orientations");
1666 return 0;
1667 }
1668
1669 const QGraphicsLayoutItem *parentWidget = q->parentLayoutItem();
1670 if (firstItem == parentWidget || secondItem == parentWidget) {
1671 qWarning("QGraphicsAnchorLayout::addAnchor(): "
1672 "You cannot add the parent of the layout to the layout.");
1673 return 0;
1674 }
1675
1676 // In QGraphicsAnchorLayout, items are represented in its internal
1677 // graph as four anchors that connect:
1678 // - Left -> HCenter
1679 // - HCenter-> Right
1680 // - Top -> VCenter
1681 // - VCenter -> Bottom
1682
1683 // Ensure that the internal anchors have been created for both items.
1684 if (firstItem != q && !items.contains(firstItem)) {
1685 createItemEdges(firstItem);
1686 addChildLayoutItem(firstItem);
1687 }
1688 if (secondItem != q && !items.contains(secondItem)) {
1689 createItemEdges(secondItem);
1690 addChildLayoutItem(secondItem);
1691 }
1692
1693 // Create center edges if needed
1694 createCenterAnchors(firstItem, firstEdge);
1695 createCenterAnchors(secondItem, secondEdge);
1696
1697 // Use heuristics to find out what the user meant with this anchor.
1698 correctEdgeDirection(firstItem, firstEdge, secondItem, secondEdge);
1699
1700 AnchorData *data = new AnchorData;
1701 QGraphicsAnchor *graphicsAnchor = acquireGraphicsAnchor(data);
1702
1703 addAnchor_helper(firstItem, firstEdge, secondItem, secondEdge, data);
1704
1705 if (spacing) {
1706 graphicsAnchor->setSpacing(*spacing);
1707 } else {
1708 // If firstItem or secondItem is the layout itself, the spacing will default to 0.
1709 // Otherwise, the following matrix is used (questionmark means that the spacing
1710 // is queried from the style):
1711 // from
1712 // to Left HCenter Right
1713 // Left 0 0 ?
1714 // HCenter 0 0 0
1715 // Right ? 0 0
1716 if (firstItem == q
1717 || secondItem == q
1718 || pickEdge(firstEdge, Horizontal) == Qt::AnchorHorizontalCenter
1719 || oppositeEdge(firstEdge) != secondEdge) {
1720 graphicsAnchor->setSpacing(0);
1721 } else {
1722 graphicsAnchor->unsetSpacing();
1723 }
1724 }
1725
1726 return graphicsAnchor;
1727}
1728
1729/*
1730 \internal
1731
1732 This method adds an AnchorData to the internal graph. It is responsible for doing
1733 the boilerplate part of such task.
1734
1735 If another AnchorData exists between the mentioned vertices, it is deleted and
1736 the new one is inserted.
1737*/
1738void QGraphicsAnchorLayoutPrivate::addAnchor_helper(QGraphicsLayoutItem *firstItem,
1739 Qt::AnchorPoint firstEdge,
1740 QGraphicsLayoutItem *secondItem,
1741 Qt::AnchorPoint secondEdge,
1742 AnchorData *data)
1743{
1744 Q_Q(QGraphicsAnchorLayout);
1745
1746 const Orientation orientation = edgeOrientation(firstEdge);
1747
1748 // Create or increase the reference count for the related vertices.
1749 AnchorVertex *v1 = addInternalVertex(firstItem, firstEdge);
1750 AnchorVertex *v2 = addInternalVertex(secondItem, secondEdge);
1751
1752 // Remove previous anchor
1753 if (graph[orientation].edgeData(v1, v2)) {
1754 removeAnchor_helper(v1, v2);
1755 }
1756
1757 // If its an internal anchor, set the associated item
1758 if (firstItem == secondItem)
1759 data->item = firstItem;
1760
1761 data->orientation = orientation;
1762
1763 // Create a bi-directional edge in the sense it can be transversed both
1764 // from v1 or v2. "data" however is shared between the two references
1765 // so we still know that the anchor direction is from 1 to 2.
1766 data->from = v1;
1767 data->to = v2;
1768#ifdef QT_DEBUG
1769 data->name = QString::fromAscii("%1 --to--> %2").arg(v1->toString()).arg(v2->toString());
1770#endif
1771 // ### bit to track internal anchors, since inside AnchorData methods
1772 // we don't have access to the 'q' pointer.
1773 data->isLayoutAnchor = (data->item == q);
1774
1775 graph[orientation].createEdge(v1, v2, data);
1776}
1777
1778QGraphicsAnchor *QGraphicsAnchorLayoutPrivate::getAnchor(QGraphicsLayoutItem *firstItem,
1779 Qt::AnchorPoint firstEdge,
1780 QGraphicsLayoutItem *secondItem,
1781 Qt::AnchorPoint secondEdge)
1782{
1783 // Do not expose internal anchors
1784 if (firstItem == secondItem)
1785 return 0;
1786
1787 const Orientation orientation = edgeOrientation(firstEdge);
1788 AnchorVertex *v1 = internalVertex(firstItem, firstEdge);
1789 AnchorVertex *v2 = internalVertex(secondItem, secondEdge);
1790
1791 QGraphicsAnchor *graphicsAnchor = 0;
1792
1793 AnchorData *data = graph[orientation].edgeData(v1, v2);
1794 if (data) {
1795 // We could use "acquireGraphicsAnchor" here, but to avoid a regression where
1796 // an internal anchor was wrongly exposed, I want to ensure no new
1797 // QGraphicsAnchor instances are created by this call.
1798 // This assumption must hold because anchors are either user-created (and already
1799 // have their public object created), or they are internal (and must not reach
1800 // this point).
1801 Q_ASSERT(data->graphicsAnchor);
1802 graphicsAnchor = data->graphicsAnchor;
1803 }
1804 return graphicsAnchor;
1805}
1806
1807/*!
1808 * \internal
1809 *
1810 * Implements the high level "removeAnchor" feature. Called by
1811 * the QAnchorData destructor.
1812 */
1813void QGraphicsAnchorLayoutPrivate::removeAnchor(AnchorVertex *firstVertex,
1814 AnchorVertex *secondVertex)
1815{
1816 Q_Q(QGraphicsAnchorLayout);
1817
1818 // Save references to items while it's safe to assume the vertices exist
1819 QGraphicsLayoutItem *firstItem = firstVertex->m_item;
1820 QGraphicsLayoutItem *secondItem = secondVertex->m_item;
1821
1822 // Delete the anchor (may trigger deletion of center vertices)
1823 removeAnchor_helper(firstVertex, secondVertex);
1824
1825 // Ensure no dangling pointer is left behind
1826 firstVertex = secondVertex = 0;
1827
1828 // Checking if the item stays in the layout or not
1829 bool keepFirstItem = false;
1830 bool keepSecondItem = false;
1831
1832 QPair<AnchorVertex *, int> v;
1833 int refcount = -1;
1834
1835 if (firstItem != q) {
1836 for (int i = Qt::AnchorLeft; i <= Qt::AnchorBottom; ++i) {
1837 v = m_vertexList.value(qMakePair(firstItem, static_cast<Qt::AnchorPoint>(i)));
1838 if (v.first) {
1839 if (i == Qt::AnchorHorizontalCenter || i == Qt::AnchorVerticalCenter)
1840 refcount = 2;
1841 else
1842 refcount = 1;
1843
1844 if (v.second > refcount) {
1845 keepFirstItem = true;
1846 break;
1847 }
1848 }
1849 }
1850 } else
1851 keepFirstItem = true;
1852
1853 if (secondItem != q) {
1854 for (int i = Qt::AnchorLeft; i <= Qt::AnchorBottom; ++i) {
1855 v = m_vertexList.value(qMakePair(secondItem, static_cast<Qt::AnchorPoint>(i)));
1856 if (v.first) {
1857 if (i == Qt::AnchorHorizontalCenter || i == Qt::AnchorVerticalCenter)
1858 refcount = 2;
1859 else
1860 refcount = 1;
1861
1862 if (v.second > refcount) {
1863 keepSecondItem = true;
1864 break;
1865 }
1866 }
1867 }
1868 } else
1869 keepSecondItem = true;
1870
1871 if (!keepFirstItem)
1872 q->removeAt(items.indexOf(firstItem));
1873
1874 if (!keepSecondItem)
1875 q->removeAt(items.indexOf(secondItem));
1876
1877 // Removing anchors invalidates the layout
1878 q->invalidate();
1879}
1880
1881/*
1882 \internal
1883
1884 Implements the low level "removeAnchor" feature. Called by
1885 private methods.
1886*/
1887void QGraphicsAnchorLayoutPrivate::removeAnchor_helper(AnchorVertex *v1, AnchorVertex *v2)
1888{
1889 Q_ASSERT(v1 && v2);
1890
1891 // Remove edge from graph
1892 const Orientation o = edgeOrientation(v1->m_edge);
1893 graph[o].removeEdge(v1, v2);
1894
1895 // Decrease vertices reference count (may trigger a deletion)
1896 removeInternalVertex(v1->m_item, v1->m_edge);
1897 removeInternalVertex(v2->m_item, v2->m_edge);
1898}
1899
1900AnchorVertex *QGraphicsAnchorLayoutPrivate::addInternalVertex(QGraphicsLayoutItem *item,
1901 Qt::AnchorPoint edge)
1902{
1903 QPair<QGraphicsLayoutItem *, Qt::AnchorPoint> pair(item, edge);
1904 QPair<AnchorVertex *, int> v = m_vertexList.value(pair);
1905
1906 if (!v.first) {
1907 Q_ASSERT(v.second == 0);
1908 v.first = new AnchorVertex(item, edge);
1909 }
1910 v.second++;
1911 m_vertexList.insert(pair, v);
1912 return v.first;
1913}
1914
1915/**
1916 * \internal
1917 *
1918 * returns the AnchorVertex that was dereferenced, also when it was removed.
1919 * returns 0 if it did not exist.
1920 */
1921void QGraphicsAnchorLayoutPrivate::removeInternalVertex(QGraphicsLayoutItem *item,
1922 Qt::AnchorPoint edge)
1923{
1924 QPair<QGraphicsLayoutItem *, Qt::AnchorPoint> pair(item, edge);
1925 QPair<AnchorVertex *, int> v = m_vertexList.value(pair);
1926
1927 if (!v.first) {
1928 qWarning("This item with this edge is not in the graph");
1929 return;
1930 }
1931
1932 v.second--;
1933 if (v.second == 0) {
1934 // Remove reference and delete vertex
1935 m_vertexList.remove(pair);
1936 delete v.first;
1937 } else {
1938 // Update reference count
1939 m_vertexList.insert(pair, v);
1940
1941 if ((v.second == 2) &&
1942 ((edge == Qt::AnchorHorizontalCenter) ||
1943 (edge == Qt::AnchorVerticalCenter))) {
1944 removeCenterAnchors(item, edge, true);
1945 }
1946 }
1947}
1948
1949void QGraphicsAnchorLayoutPrivate::removeVertex(QGraphicsLayoutItem *item, Qt::AnchorPoint edge)
1950{
1951 if (AnchorVertex *v = internalVertex(item, edge)) {
1952 Graph<AnchorVertex, AnchorData> &g = graph[edgeOrientation(edge)];
1953 const QList<AnchorVertex *> allVertices = graph[edgeOrientation(edge)].adjacentVertices(v);
1954 AnchorVertex *v2;
1955 foreach (v2, allVertices) {
1956 g.removeEdge(v, v2);
1957 removeInternalVertex(item, edge);
1958 removeInternalVertex(v2->m_item, v2->m_edge);
1959 }
1960 }
1961}
1962
1963void QGraphicsAnchorLayoutPrivate::removeAnchors(QGraphicsLayoutItem *item)
1964{
1965 // remove the center anchor first!!
1966 removeCenterAnchors(item, Qt::AnchorHorizontalCenter, false);
1967 removeVertex(item, Qt::AnchorLeft);
1968 removeVertex(item, Qt::AnchorRight);
1969
1970 removeCenterAnchors(item, Qt::AnchorVerticalCenter, false);
1971 removeVertex(item, Qt::AnchorTop);
1972 removeVertex(item, Qt::AnchorBottom);
1973}
1974
1975/*!
1976 \internal
1977
1978 Use heuristics to determine the correct orientation of a given anchor.
1979
1980 After API discussions, we decided we would like expressions like
1981 anchor(A, Left, B, Right) to mean the same as anchor(B, Right, A, Left).
1982 The problem with this is that anchors could become ambiguous, for
1983 instance, what does the anchor A, B of size X mean?
1984
1985 "pos(B) = pos(A) + X" or "pos(A) = pos(B) + X" ?
1986
1987 To keep the API user friendly and at the same time, keep our algorithm
1988 deterministic, we use an heuristic to determine a direction for each
1989 added anchor and then keep it. The heuristic is based on the fact
1990 that people usually avoid overlapping items, therefore:
1991
1992 "A, RIGHT to B, LEFT" means that B is to the LEFT of A.
1993 "B, LEFT to A, RIGHT" is corrected to the above anchor.
1994
1995 Special correction is also applied when one of the items is the
1996 layout. We handle Layout Left as if it was another items's Right
1997 and Layout Right as another item's Left.
1998*/
1999void QGraphicsAnchorLayoutPrivate::correctEdgeDirection(QGraphicsLayoutItem *&firstItem,
2000 Qt::AnchorPoint &firstEdge,
2001 QGraphicsLayoutItem *&secondItem,
2002 Qt::AnchorPoint &secondEdge)
2003{
2004 Q_Q(QGraphicsAnchorLayout);
2005
2006 if ((firstItem != q) && (secondItem != q)) {
2007 // If connection is between widgets (not the layout itself)
2008 // Ensure that "right-edges" sit to the left of "left-edges".
2009 if (firstEdge < secondEdge) {
2010 qSwap(firstItem, secondItem);
2011 qSwap(firstEdge, secondEdge);
2012 }
2013 } else if (firstItem == q) {
2014 // If connection involves the right or bottom of a layout, ensure
2015 // the layout is the second item.
2016 if ((firstEdge == Qt::AnchorRight) || (firstEdge == Qt::AnchorBottom)) {
2017 qSwap(firstItem, secondItem);
2018 qSwap(firstEdge, secondEdge);
2019 }
2020 } else if ((secondEdge != Qt::AnchorRight) && (secondEdge != Qt::AnchorBottom)) {
2021 // If connection involves the left, center or top of layout, ensure
2022 // the layout is the first item.
2023 qSwap(firstItem, secondItem);
2024 qSwap(firstEdge, secondEdge);
2025 }
2026}
2027
2028QLayoutStyleInfo &QGraphicsAnchorLayoutPrivate::styleInfo() const
2029{
2030 if (styleInfoDirty) {
2031 Q_Q(const QGraphicsAnchorLayout);
2032 //### Fix this if QGV ever gets support for Metal style or different Aqua sizes.
2033 QWidget *wid = 0;
2034
2035 QGraphicsLayoutItem *parent = q->parentLayoutItem();
2036 while (parent && parent->isLayout()) {
2037 parent = parent->parentLayoutItem();
2038 }
2039 QGraphicsWidget *w = 0;
2040 if (parent) {
2041 QGraphicsItem *parentItem = parent->graphicsItem();
2042 if (parentItem && parentItem->isWidget())
2043 w = static_cast<QGraphicsWidget*>(parentItem);
2044 }
2045
2046 QStyle *style = w ? w->style() : QApplication::style();
2047 cachedStyleInfo = QLayoutStyleInfo(style, wid);
2048 cachedStyleInfo.setDefaultSpacing(Qt::Horizontal, spacings[0]);
2049 cachedStyleInfo.setDefaultSpacing(Qt::Vertical, spacings[1]);
2050
2051 styleInfoDirty = false;
2052 }
2053 return cachedStyleInfo;
2054}
2055
2056/*!
2057 \internal
2058
2059 Called on activation. Uses Linear Programming to define minimum, preferred
2060 and maximum sizes for the layout. Also calculates the sizes that each item
2061 should assume when the layout is in one of such situations.
2062*/
2063void QGraphicsAnchorLayoutPrivate::calculateGraphs()
2064{
2065 if (!calculateGraphCacheDirty)
2066 return;
2067 calculateGraphs(Horizontal);
2068 calculateGraphs(Vertical);
2069 calculateGraphCacheDirty = false;
2070}
2071
2072// ### Maybe getGraphParts could return the variables when traversing, at least
2073// for trunk...
2074QList<AnchorData *> getVariables(QList<QSimplexConstraint *> constraints)
2075{
2076 QSet<AnchorData *> variableSet;
2077 for (int i = 0; i < constraints.count(); ++i) {
2078 const QSimplexConstraint *c = constraints.at(i);
2079 foreach (QSimplexVariable *var, c->variables.keys()) {
2080 variableSet += static_cast<AnchorData *>(var);
2081 }
2082 }
2083 return variableSet.toList();
2084}
2085
2086/*!
2087 \internal
2088
2089 Calculate graphs is the method that puts together all the helper routines
2090 so that the AnchorLayout can calculate the sizes of each item.
2091
2092 In a nutshell it should do:
2093
2094 1) Refresh anchor nominal sizes, that is, the size that each anchor would
2095 have if no other restrictions applied. This is done by quering the
2096 layout style and the sizeHints of the items belonging to the layout.
2097
2098 2) Simplify the graph by grouping together parallel and sequential anchors
2099 into "group anchors". These have equivalent minimum, preferred and maximum
2100 sizeHints as the anchors they replace.
2101
2102 3) Check if we got to a trivial case. In some cases, the whole graph can be
2103 simplified into a single anchor. If so, use this information. If not,
2104 then call the Simplex solver to calculate the anchors sizes.
2105
2106 4) Once the root anchors had its sizes calculated, propagate that to the
2107 anchors they represent.
2108*/
2109void QGraphicsAnchorLayoutPrivate::calculateGraphs(
2110 QGraphicsAnchorLayoutPrivate::Orientation orientation)
2111{
2112#if defined(QT_DEBUG) || defined(Q_AUTOTEST_EXPORT)
2113 lastCalculationUsedSimplex[orientation] = false;
2114#endif
2115
2116 static bool simplificationEnabled = qgetenv("QT_ANCHORLAYOUT_NO_SIMPLIFICATION").isEmpty();
2117
2118 // Reset the nominal sizes of each anchor based on the current item sizes
2119 refreshAllSizeHints(orientation);
2120
2121 // Simplify the graph
2122 if (simplificationEnabled && !simplifyGraph(orientation)) {
2123 qWarning("QGraphicsAnchorLayout: anchor setup is not feasible.");
2124 graphHasConflicts[orientation] = true;
2125 return;
2126 }
2127
2128 // Traverse all graph edges and store the possible paths to each vertex
2129 findPaths(orientation);
2130
2131 // From the paths calculated above, extract the constraints that the current
2132 // anchor setup impose, to our Linear Programming problem.
2133 constraintsFromPaths(orientation);
2134
2135 // Split the constraints and anchors into groups that should be fed to the
2136 // simplex solver independently. Currently we find two groups:
2137 //
2138 // 1) The "trunk", that is, the set of anchors (items) that are connected
2139 // to the two opposite sides of our layout, and thus need to stretch in
2140 // order to fit in the current layout size.
2141 //
2142 // 2) The floating or semi-floating anchors (items) that are those which
2143 // are connected to only one (or none) of the layout sides, thus are not
2144 // influenced by the layout size.
2145 QList<QList<QSimplexConstraint *> > parts = getGraphParts(orientation);
2146
2147 // Now run the simplex solver to calculate Minimum, Preferred and Maximum sizes
2148 // of the "trunk" set of constraints and variables.
2149 // ### does trunk always exist? empty = trunk is the layout left->center->right
2150 QList<QSimplexConstraint *> trunkConstraints = parts.at(0);
2151 QList<AnchorData *> trunkVariables = getVariables(trunkConstraints);
2152
2153 // For minimum and maximum, use the path between the two layout sides as the
2154 // objective function.
2155 AnchorVertex *v = layoutLastVertex[orientation];
2156 GraphPath trunkPath = graphPaths[orientation].value(v);
2157
2158 bool feasible = calculateTrunk(orientation, trunkPath, trunkConstraints, trunkVariables);
2159
2160 // For the other parts that not the trunk, solve only for the preferred size
2161 // that is the size they will remain at, since they are not stretched by the
2162 // layout.
2163
2164 // Skipping the first (trunk)
2165 for (int i = 1; i < parts.count(); ++i) {
2166 if (!feasible)
2167 break;
2168
2169 QList<QSimplexConstraint *> partConstraints = parts.at(i);
2170 QList<AnchorData *> partVariables = getVariables(partConstraints);
2171 Q_ASSERT(!partVariables.isEmpty());
2172 feasible &= calculateNonTrunk(partConstraints, partVariables);
2173 }
2174
2175 // Propagate the new sizes down the simplified graph, ie. tell the
2176 // group anchors to set their children anchors sizes.
2177 updateAnchorSizes(orientation);
2178
2179 graphHasConflicts[orientation] = !feasible;
2180
2181 // Clean up our data structures. They are not needed anymore since
2182 // distribution uses just interpolation.
2183 qDeleteAll(constraints[orientation]);
2184 constraints[orientation].clear();
2185 graphPaths[orientation].clear(); // ###
2186
2187 if (simplificationEnabled)
2188 restoreSimplifiedGraph(orientation);
2189}
2190
2191/*!
2192 \internal
2193
2194 Shift all the constraints by a certain amount. This allows us to deal with negative values in
2195 the linear program if they are bounded by a certain limit. Functions should be careful to
2196 call it again with a negative amount, to shift the constraints back.
2197*/
2198static void shiftConstraints(const QList<QSimplexConstraint *> &constraints, qreal amount)
2199{
2200 for (int i = 0; i < constraints.count(); ++i) {
2201 QSimplexConstraint *c = constraints.at(i);
2202 qreal multiplier = 0;
2203 foreach (qreal v, c->variables.values()) {
2204 multiplier += v;
2205 }
2206 c->constant += multiplier * amount;
2207 }
2208}
2209
2210/*!
2211 \internal
2212
2213 Calculate the sizes for all anchors which are part of the trunk. This works
2214 on top of a (possibly) simplified graph.
2215*/
2216bool QGraphicsAnchorLayoutPrivate::calculateTrunk(Orientation orientation, const GraphPath &path,
2217 const QList<QSimplexConstraint *> &constraints,
2218 const QList<AnchorData *> &variables)
2219{
2220 bool feasible = true;
2221 bool needsSimplex = !constraints.isEmpty();
2222
2223#if 0
2224 qDebug("Simplex %s for trunk of %s", needsSimplex ? "used" : "NOT used",
2225 orientation == Horizontal ? "Horizontal" : "Vertical");
2226#endif
2227
2228 if (needsSimplex) {
2229
2230 QList<QSimplexConstraint *> sizeHintConstraints = constraintsFromSizeHints(variables);
2231 QList<QSimplexConstraint *> allConstraints = constraints + sizeHintConstraints;
2232
2233 shiftConstraints(allConstraints, g_offset);
2234
2235 // Solve min and max size hints
2236 qreal min, max;
2237 feasible = solveMinMax(allConstraints, path, &min, &max);
2238
2239 if (feasible) {
2240 solvePreferred(constraints, variables);
2241
2242 // Calculate and set the preferred size for the layout,
2243 // from the edge sizes that were calculated above.
2244 qreal pref(0.0);
2245 foreach (const AnchorData *ad, path.positives) {
2246 pref += ad->sizeAtPreferred;
2247 }
2248 foreach (const AnchorData *ad, path.negatives) {
2249 pref -= ad->sizeAtPreferred;
2250 }
2251
2252 sizeHints[orientation][Qt::MinimumSize] = min;
2253 sizeHints[orientation][Qt::PreferredSize] = pref;
2254 sizeHints[orientation][Qt::MaximumSize] = max;
2255 }
2256
2257 qDeleteAll(sizeHintConstraints);
2258 shiftConstraints(constraints, -g_offset);
2259
2260 } else {
2261 // No Simplex is necessary because the path was simplified all the way to a single
2262 // anchor.
2263 Q_ASSERT(path.positives.count() == 1);
2264 Q_ASSERT(path.negatives.count() == 0);
2265
2266 AnchorData *ad = path.positives.toList()[0];
2267 ad->sizeAtMinimum = ad->minSize;
2268 ad->sizeAtPreferred = ad->prefSize;
2269 ad->sizeAtMaximum = ad->maxSize;
2270
2271 sizeHints[orientation][Qt::MinimumSize] = ad->sizeAtMinimum;
2272 sizeHints[orientation][Qt::PreferredSize] = ad->sizeAtPreferred;
2273 sizeHints[orientation][Qt::MaximumSize] = ad->sizeAtMaximum;
2274 }
2275
2276#if defined(QT_DEBUG) || defined(Q_AUTOTEST_EXPORT)
2277 lastCalculationUsedSimplex[orientation] = needsSimplex;
2278#endif
2279
2280 return feasible;
2281}
2282
2283/*!
2284 \internal
2285*/
2286bool QGraphicsAnchorLayoutPrivate::calculateNonTrunk(const QList<QSimplexConstraint *> &constraints,
2287 const QList<AnchorData *> &variables)
2288{
2289 shiftConstraints(constraints, g_offset);
2290 bool feasible = solvePreferred(constraints, variables);
2291
2292 if (feasible) {
2293 // Propagate size at preferred to other sizes. Semi-floats always will be
2294 // in their sizeAtPreferred.
2295 for (int j = 0; j < variables.count(); ++j) {
2296 AnchorData *ad = variables.at(j);
2297 Q_ASSERT(ad);
2298 ad->sizeAtMinimum = ad->sizeAtPreferred;
2299 ad->sizeAtMaximum = ad->sizeAtPreferred;
2300 }
2301 }
2302
2303 shiftConstraints(constraints, -g_offset);
2304 return feasible;
2305}
2306
2307/*!
2308 \internal
2309
2310 Traverse the graph refreshing the size hints. Edges will query their associated
2311 item or graphicsAnchor for their size hints.
2312*/
2313void QGraphicsAnchorLayoutPrivate::refreshAllSizeHints(Orientation orientation)
2314{
2315 Graph<AnchorVertex, AnchorData> &g = graph[orientation];
2316 QList<QPair<AnchorVertex *, AnchorVertex *> > vertices = g.connections();
2317
2318 QLayoutStyleInfo styleInf = styleInfo();
2319 for (int i = 0; i < vertices.count(); ++i) {
2320 AnchorData *data = g.edgeData(vertices.at(i).first, vertices.at(i).second);
2321 data->refreshSizeHints(&styleInf);
2322 }
2323}
2324
2325/*!
2326 \internal
2327
2328 This method walks the graph using a breadth-first search to find paths
2329 between the root vertex and each vertex on the graph. The edges
2330 directions in each path are considered and they are stored as a
2331 positive edge (left-to-right) or negative edge (right-to-left).
2332
2333 The list of paths is used later to generate a list of constraints.
2334 */
2335void QGraphicsAnchorLayoutPrivate::findPaths(Orientation orientation)
2336{
2337 QQueue<QPair<AnchorVertex *, AnchorVertex *> > queue;
2338
2339 QSet<AnchorData *> visited;
2340
2341 AnchorVertex *root = layoutFirstVertex[orientation];
2342
2343 graphPaths[orientation].insert(root, GraphPath());
2344
2345 foreach (AnchorVertex *v, graph[orientation].adjacentVertices(root)) {
2346 queue.enqueue(qMakePair(root, v));
2347 }
2348
2349 while(!queue.isEmpty()) {
2350 QPair<AnchorVertex *, AnchorVertex *> pair = queue.dequeue();
2351 AnchorData *edge = graph[orientation].edgeData(pair.first, pair.second);
2352
2353 if (visited.contains(edge))
2354 continue;
2355
2356 visited.insert(edge);
2357 GraphPath current = graphPaths[orientation].value(pair.first);
2358
2359 if (edge->from == pair.first)
2360 current.positives.insert(edge);
2361 else
2362 current.negatives.insert(edge);
2363
2364 graphPaths[orientation].insert(pair.second, current);
2365
2366 foreach (AnchorVertex *v,
2367 graph[orientation].adjacentVertices(pair.second)) {
2368 queue.enqueue(qMakePair(pair.second, v));
2369 }
2370 }
2371
2372 // We will walk through every reachable items (non-float) store them in a temporary set.
2373 // We them create a set of all items and subtract the non-floating items from the set in
2374 // order to get the floating items. The floating items is then stored in m_floatItems
2375 identifyFloatItems(visited, orientation);
2376}
2377
2378/*!
2379 \internal
2380
2381 Each vertex on the graph that has more than one path to it
2382 represents a contra int to the sizes of the items in these paths.
2383
2384 This method walks the list of paths to each vertex, generate
2385 the constraints and store them in a list so they can be used later
2386 by the Simplex solver.
2387*/
2388void QGraphicsAnchorLayoutPrivate::constraintsFromPaths(Orientation orientation)
2389{
2390 foreach (AnchorVertex *vertex, graphPaths[orientation].uniqueKeys())
2391 {
2392 int valueCount = graphPaths[orientation].count(vertex);
2393 if (valueCount == 1)
2394 continue;
2395
2396 QList<GraphPath> pathsToVertex = graphPaths[orientation].values(vertex);
2397 for (int i = 1; i < valueCount; ++i) {
2398 constraints[orientation] += \
2399 pathsToVertex[0].constraint(pathsToVertex.at(i));
2400 }
2401 }
2402}
2403
2404/*!
2405 \internal
2406*/
2407void QGraphicsAnchorLayoutPrivate::updateAnchorSizes(Orientation orientation)
2408{
2409 Graph<AnchorVertex, AnchorData> &g = graph[orientation];
2410 const QList<QPair<AnchorVertex *, AnchorVertex *> > &vertices = g.connections();
2411
2412 for (int i = 0; i < vertices.count(); ++i) {
2413 AnchorData *ad = g.edgeData(vertices.at(i).first, vertices.at(i).second);
2414 ad->updateChildrenSizes();
2415 }
2416}
2417
2418/*!
2419 \internal
2420
2421 Create LP constraints for each anchor based on its minimum and maximum
2422 sizes, as specified in its size hints
2423*/
2424QList<QSimplexConstraint *> QGraphicsAnchorLayoutPrivate::constraintsFromSizeHints(
2425 const QList<AnchorData *> &anchors)
2426{
2427 if (anchors.isEmpty())
2428 return QList<QSimplexConstraint *>();
2429
2430 // Look for the layout edge. That can be either the first half in case the
2431 // layout is split in two, or the whole layout anchor.
2432 Orientation orient = Orientation(anchors.first()->orientation);
2433 AnchorData *layoutEdge = 0;
2434 if (layoutCentralVertex[orient]) {
2435 layoutEdge = graph[orient].edgeData(layoutFirstVertex[orient], layoutCentralVertex[orient]);
2436 } else {
2437 layoutEdge = graph[orient].edgeData(layoutFirstVertex[orient], layoutLastVertex[orient]);
2438 }
2439
2440 // If maxSize is less then "infinite", that means there are other anchors
2441 // grouped together with this one. We can't ignore its maximum value so we
2442 // set back the variable to NULL to prevent the continue condition from being
2443 // satisfied in the loop below.
2444 const qreal expectedMax = layoutCentralVertex[orient] ? QWIDGETSIZE_MAX / 2 : QWIDGETSIZE_MAX;
2445 qreal actualMax;
2446 if (layoutEdge->from == layoutFirstVertex[orient]) {
2447 actualMax = layoutEdge->maxSize;
2448 } else {
2449 actualMax = -layoutEdge->minSize;
2450 }
2451 if (actualMax != expectedMax) {
2452 layoutEdge = 0;
2453 }
2454
2455 // For each variable, create constraints based on size hints
2456 QList<QSimplexConstraint *> anchorConstraints;
2457 bool unboundedProblem = true;
2458 for (int i = 0; i < anchors.size(); ++i) {
2459 AnchorData *ad = anchors.at(i);
2460
2461 // Anchors that have their size directly linked to another one don't need constraints
2462 // For exammple, the second half of an item has exactly the same size as the first half
2463 // thus constraining the latter is enough.
2464 if (ad->dependency == AnchorData::Slave)
2465 continue;
2466
2467 // To use negative variables inside simplex, we shift them so the minimum negative value is
2468 // mapped to zero before solving. To make sure that it works, we need to guarantee that the
2469 // variables are all inside a certain boundary.
2470 qreal boundedMin = qBound(-g_offset, ad->minSize, g_offset);
2471 qreal boundedMax = qBound(-g_offset, ad->maxSize, g_offset);
2472
2473 if ((boundedMin == boundedMax) || qFuzzyCompare(boundedMin, boundedMax)) {
2474 QSimplexConstraint *c = new QSimplexConstraint;
2475 c->variables.insert(ad, 1.0);
2476 c->constant = boundedMin;
2477 c->ratio = QSimplexConstraint::Equal;
2478 anchorConstraints += c;
2479 unboundedProblem = false;
2480 } else {
2481 QSimplexConstraint *c = new QSimplexConstraint;
2482 c->variables.insert(ad, 1.0);
2483 c->constant = boundedMin;
2484 c->ratio = QSimplexConstraint::MoreOrEqual;
2485 anchorConstraints += c;
2486
2487 // We avoid adding restrictions to the layout internal anchors. That's
2488 // to prevent unnecessary fair distribution from happening due to this
2489 // artificial restriction.
2490 if (ad == layoutEdge)
2491 continue;
2492
2493 c = new QSimplexConstraint;
2494 c->variables.insert(ad, 1.0);
2495 c->constant = boundedMax;
2496 c->ratio = QSimplexConstraint::LessOrEqual;
2497 anchorConstraints += c;
2498 unboundedProblem = false;
2499 }
2500 }
2501
2502 // If no upper boundary restriction was added, add one to avoid unbounded problem
2503 if (unboundedProblem) {
2504 QSimplexConstraint *c = new QSimplexConstraint;
2505 c->variables.insert(layoutEdge, 1.0);
2506 // The maximum size that the layout can take
2507 c->constant = g_offset;
2508 c->ratio = QSimplexConstraint::LessOrEqual;
2509 anchorConstraints += c;
2510 }
2511
2512 return anchorConstraints;
2513}
2514
2515/*!
2516 \internal
2517*/
2518QList< QList<QSimplexConstraint *> >
2519QGraphicsAnchorLayoutPrivate::getGraphParts(Orientation orientation)
2520{
2521 Q_ASSERT(layoutFirstVertex[orientation] && layoutLastVertex[orientation]);
2522
2523 AnchorData *edgeL1 = 0;
2524 AnchorData *edgeL2 = 0;
2525
2526 // The layout may have a single anchor between Left and Right or two half anchors
2527 // passing through the center
2528 if (layoutCentralVertex[orientation]) {
2529 edgeL1 = graph[orientation].edgeData(layoutFirstVertex[orientation], layoutCentralVertex[orientation]);
2530 edgeL2 = graph[orientation].edgeData(layoutCentralVertex[orientation], layoutLastVertex[orientation]);
2531 } else {
2532 edgeL1 = graph[orientation].edgeData(layoutFirstVertex[orientation], layoutLastVertex[orientation]);
2533 }
2534
2535 QLinkedList<QSimplexConstraint *> remainingConstraints;
2536 for (int i = 0; i < constraints[orientation].count(); ++i) {
2537 remainingConstraints += constraints[orientation].at(i);
2538 }
2539 for (int i = 0; i < itemCenterConstraints[orientation].count(); ++i) {
2540 remainingConstraints += itemCenterConstraints[orientation].at(i);
2541 }
2542
2543 QList<QSimplexConstraint *> trunkConstraints;
2544 QSet<QSimplexVariable *> trunkVariables;
2545
2546 trunkVariables += edgeL1;
2547 if (edgeL2)
2548 trunkVariables += edgeL2;
2549
2550 bool dirty;
2551 do {
2552 dirty = false;
2553
2554 QLinkedList<QSimplexConstraint *>::iterator it = remainingConstraints.begin();
2555 while (it != remainingConstraints.end()) {
2556 QSimplexConstraint *c = *it;
2557 bool match = false;
2558
2559 // Check if this constraint have some overlap with current
2560 // trunk variables...
2561 foreach (QSimplexVariable *ad, trunkVariables) {
2562 if (c->variables.contains(ad)) {
2563 match = true;
2564 break;
2565 }
2566 }
2567
2568 // If so, we add it to trunk, and erase it from the
2569 // remaining constraints.
2570 if (match) {
2571 trunkConstraints += c;
2572 trunkVariables += QSet<QSimplexVariable *>::fromList(c->variables.keys());
2573 it = remainingConstraints.erase(it);
2574 dirty = true;
2575 } else {
2576 // Note that we don't erase the constraint if it's not
2577 // a match, since in a next iteration of a do-while we
2578 // can pass on it again and it will be a match.
2579 //
2580 // For example: if trunk share a variable with
2581 // remainingConstraints[1] and it shares with
2582 // remainingConstraints[0], we need a second iteration
2583 // of the do-while loop to match both.
2584 ++it;
2585 }
2586 }
2587 } while (dirty);
2588
2589 QList< QList<QSimplexConstraint *> > result;
2590 result += trunkConstraints;
2591
2592 if (!remainingConstraints.isEmpty()) {
2593 QList<QSimplexConstraint *> nonTrunkConstraints;
2594 QLinkedList<QSimplexConstraint *>::iterator it = remainingConstraints.begin();
2595 while (it != remainingConstraints.end()) {
2596 nonTrunkConstraints += *it;
2597 ++it;
2598 }
2599 result += nonTrunkConstraints;
2600 }
2601
2602 return result;
2603}
2604
2605/*!
2606 \internal
2607
2608 Use all visited Anchors on findPaths() so we can identify non-float Items.
2609*/
2610void QGraphicsAnchorLayoutPrivate::identifyFloatItems(const QSet<AnchorData *> &visited, Orientation orientation)
2611{
2612 QSet<QGraphicsLayoutItem *> nonFloating;
2613
2614 foreach (const AnchorData *ad, visited)
2615 identifyNonFloatItems_helper(ad, &nonFloating);
2616
2617 QSet<QGraphicsLayoutItem *> allItems;
2618 foreach (QGraphicsLayoutItem *item, items)
2619 allItems.insert(item);
2620 m_floatItems[orientation] = allItems - nonFloating;
2621}
2622
2623
2624/*!
2625 \internal
2626
2627 Given an anchor, if it is an internal anchor and Normal we must mark it's item as non-float.
2628 If the anchor is Sequential or Parallel, we must iterate on its children recursively until we reach
2629 internal anchors (items).
2630*/
2631void QGraphicsAnchorLayoutPrivate::identifyNonFloatItems_helper(const AnchorData *ad, QSet<QGraphicsLayoutItem *> *nonFloatingItemsIdentifiedSoFar)
2632{
2633 Q_Q(QGraphicsAnchorLayout);
2634
2635 switch(ad->type) {
2636 case AnchorData::Normal:
2637 if (ad->item && ad->item != q)
2638 nonFloatingItemsIdentifiedSoFar->insert(ad->item);
2639 break;
2640 case AnchorData::Sequential:
2641 foreach (const AnchorData *d, static_cast<const SequentialAnchorData *>(ad)->m_edges)
2642 identifyNonFloatItems_helper(d, nonFloatingItemsIdentifiedSoFar);
2643 break;
2644 case AnchorData::Parallel:
2645 identifyNonFloatItems_helper(static_cast<const ParallelAnchorData *>(ad)->firstEdge, nonFloatingItemsIdentifiedSoFar);
2646 identifyNonFloatItems_helper(static_cast<const ParallelAnchorData *>(ad)->secondEdge, nonFloatingItemsIdentifiedSoFar);
2647 break;
2648 }
2649}
2650
2651/*!
2652 \internal
2653
2654 Use the current vertices distance to calculate and set the geometry of
2655 each item.
2656*/
2657void QGraphicsAnchorLayoutPrivate::setItemsGeometries(const QRectF &geom)
2658{
2659 Q_Q(QGraphicsAnchorLayout);
2660 AnchorVertex *firstH, *secondH, *firstV, *secondV;
2661
2662 qreal top;
2663 qreal left;
2664 qreal right;
2665
2666 q->getContentsMargins(&left, &top, &right, 0);
2667 const Qt::LayoutDirection visualDir = visualDirection();
2668 if (visualDir == Qt::RightToLeft)
2669 qSwap(left, right);
2670
2671 left += geom.left();
2672 top += geom.top();
2673 right = geom.right() - right;
2674
2675 foreach (QGraphicsLayoutItem *item, items) {
2676 QRectF newGeom;
2677 QSizeF itemPreferredSize = item->effectiveSizeHint(Qt::PreferredSize);
2678 if (m_floatItems[Horizontal].contains(item)) {
2679 newGeom.setLeft(0);
2680 newGeom.setRight(itemPreferredSize.width());
2681 } else {
2682 firstH = internalVertex(item, Qt::AnchorLeft);
2683 secondH = internalVertex(item, Qt::AnchorRight);
2684
2685 if (visualDir == Qt::LeftToRight) {
2686 newGeom.setLeft(left + firstH->distance);
2687 newGeom.setRight(left + secondH->distance);
2688 } else {
2689 newGeom.setLeft(right - secondH->distance);
2690 newGeom.setRight(right - firstH->distance);
2691 }
2692 }
2693
2694 if (m_floatItems[Vertical].contains(item)) {
2695 newGeom.setTop(0);
2696 newGeom.setBottom(itemPreferredSize.height());
2697 } else {
2698 firstV = internalVertex(item, Qt::AnchorTop);
2699 secondV = internalVertex(item, Qt::AnchorBottom);
2700
2701 newGeom.setTop(top + firstV->distance);
2702 newGeom.setBottom(top + secondV->distance);
2703 }
2704
2705 item->setGeometry(newGeom);
2706 }
2707}
2708
2709/*!
2710 \internal
2711
2712 Calculate the position of each vertex based on the paths to each of
2713 them as well as the current edges sizes.
2714*/
2715void QGraphicsAnchorLayoutPrivate::calculateVertexPositions(
2716 QGraphicsAnchorLayoutPrivate::Orientation orientation)
2717{
2718 QQueue<QPair<AnchorVertex *, AnchorVertex *> > queue;
2719 QSet<AnchorVertex *> visited;
2720
2721 // Get root vertex
2722 AnchorVertex *root = layoutFirstVertex[orientation];
2723
2724 root->distance = 0;
2725 visited.insert(root);
2726
2727 // Add initial edges to the queue
2728 foreach (AnchorVertex *v, graph[orientation].adjacentVertices(root)) {
2729 queue.enqueue(qMakePair(root, v));
2730 }
2731
2732 // Do initial calculation required by "interpolateEdge()"
2733 setupEdgesInterpolation(orientation);
2734
2735 // Traverse the graph and calculate vertex positions
2736 while (!queue.isEmpty()) {
2737 QPair<AnchorVertex *, AnchorVertex *> pair = queue.dequeue();
2738 AnchorData *edge = graph[orientation].edgeData(pair.first, pair.second);
2739
2740 if (visited.contains(pair.second))
2741 continue;
2742
2743 visited.insert(pair.second);
2744 interpolateEdge(pair.first, edge);
2745
2746 QList<AnchorVertex *> adjacents = graph[orientation].adjacentVertices(pair.second);
2747 for (int i = 0; i < adjacents.count(); ++i) {
2748 if (!visited.contains(adjacents.at(i)))
2749 queue.enqueue(qMakePair(pair.second, adjacents.at(i)));
2750 }
2751 }
2752}
2753
2754/*!
2755 \internal
2756
2757 Calculate interpolation parameters based on current Layout Size.
2758 Must be called once before calling "interpolateEdgeSize()" for
2759 the edges.
2760*/
2761void QGraphicsAnchorLayoutPrivate::setupEdgesInterpolation(
2762 Orientation orientation)
2763{
2764 Q_Q(QGraphicsAnchorLayout);
2765
2766 qreal current;
2767 current = (orientation == Horizontal) ? q->contentsRect().width() : q->contentsRect().height();
2768
2769 QPair<Interval, qreal> result;
2770 result = getFactor(current,
2771 sizeHints[orientation][Qt::MinimumSize],
2772 sizeHints[orientation][Qt::PreferredSize],
2773 sizeHints[orientation][Qt::PreferredSize],
2774 sizeHints[orientation][Qt::PreferredSize],
2775 sizeHints[orientation][Qt::MaximumSize]);
2776
2777 interpolationInterval[orientation] = result.first;
2778 interpolationProgress[orientation] = result.second;
2779}
2780
2781/*!
2782 \internal
2783
2784 Calculate the current Edge size based on the current Layout size and the
2785 size the edge is supposed to have when the layout is at its:
2786
2787 - minimum size,
2788 - preferred size,
2789 - maximum size.
2790
2791 These three key values are calculated in advance using linear
2792 programming (more expensive) or the simplification algorithm, then
2793 subsequential resizes of the parent layout require a simple
2794 interpolation.
2795*/
2796void QGraphicsAnchorLayoutPrivate::interpolateEdge(AnchorVertex *base, AnchorData *edge)
2797{
2798 const Orientation orientation = Orientation(edge->orientation);
2799 const QPair<Interval, qreal> factor(interpolationInterval[orientation],
2800 interpolationProgress[orientation]);
2801
2802 qreal edgeDistance = interpolate(factor, edge->sizeAtMinimum, edge->sizeAtPreferred,
2803 edge->sizeAtPreferred, edge->sizeAtPreferred,
2804 edge->sizeAtMaximum);
2805
2806 Q_ASSERT(edge->from == base || edge->to == base);
2807
2808 // Calculate the distance for the vertex opposite to the base
2809 if (edge->from == base) {
2810 edge->to->distance = base->distance + edgeDistance;
2811 } else {
2812 edge->from->distance = base->distance - edgeDistance;
2813 }
2814}
2815
2816bool QGraphicsAnchorLayoutPrivate::solveMinMax(const QList<QSimplexConstraint *> &constraints,
2817 GraphPath path, qreal *min, qreal *max)
2818{
2819 QSimplex simplex;
2820 bool feasible = simplex.setConstraints(constraints);
2821 if (feasible) {
2822 // Obtain the objective constraint
2823 QSimplexConstraint objective;
2824 QSet<AnchorData *>::const_iterator iter;
2825 for (iter = path.positives.constBegin(); iter != path.positives.constEnd(); ++iter)
2826 objective.variables.insert(*iter, 1.0);
2827
2828 for (iter = path.negatives.constBegin(); iter != path.negatives.constEnd(); ++iter)
2829 objective.variables.insert(*iter, -1.0);
2830
2831 const qreal objectiveOffset = (path.positives.count() - path.negatives.count()) * g_offset;
2832 simplex.setObjective(&objective);
2833
2834 // Calculate minimum values
2835 *min = simplex.solveMin() - objectiveOffset;
2836
2837 // Save sizeAtMinimum results
2838 QList<AnchorData *> variables = getVariables(constraints);
2839 for (int i = 0; i < variables.size(); ++i) {
2840 AnchorData *ad = static_cast<AnchorData *>(variables.at(i));
2841 ad->sizeAtMinimum = ad->result - g_offset;
2842 }
2843
2844 // Calculate maximum values
2845 *max = simplex.solveMax() - objectiveOffset;
2846
2847 // Save sizeAtMaximum results
2848 for (int i = 0; i < variables.size(); ++i) {
2849 AnchorData *ad = static_cast<AnchorData *>(variables.at(i));
2850 ad->sizeAtMaximum = ad->result - g_offset;
2851 }
2852 }
2853 return feasible;
2854}
2855
2856enum slackType { Grower = -1, Shrinker = 1 };
2857static QPair<QSimplexVariable *, QSimplexConstraint *> createSlack(QSimplexConstraint *sizeConstraint,
2858 qreal interval, slackType type)
2859{
2860 QSimplexVariable *slack = new QSimplexVariable;
2861 sizeConstraint->variables.insert(slack, type);
2862
2863 QSimplexConstraint *limit = new QSimplexConstraint;
2864 limit->variables.insert(slack, 1.0);
2865 limit->ratio = QSimplexConstraint::LessOrEqual;
2866 limit->constant = interval;
2867
2868 return qMakePair(slack, limit);
2869}
2870
2871bool QGraphicsAnchorLayoutPrivate::solvePreferred(const QList<QSimplexConstraint *> &constraints,
2872 const QList<AnchorData *> &variables)
2873{
2874 QList<QSimplexConstraint *> preferredConstraints;
2875 QList<QSimplexVariable *> preferredVariables;
2876 QSimplexConstraint objective;
2877
2878 // Fill the objective coefficients for this variable. In the
2879 // end the objective function will be
2880 //
2881 // z = n * (A_shrinker_hard + A_grower_hard + B_shrinker_hard + B_grower_hard + ...) +
2882 // (A_shrinker_soft + A_grower_soft + B_shrinker_soft + B_grower_soft + ...)
2883 //
2884 // where n is the number of variables that have
2885 // slacks. Note that here we use the number of variables
2886 // as coefficient, this is to mark the "shrinker slack
2887 // variable" less likely to get value than the "grower
2888 // slack variable".
2889
2890 // This will fill the values for the structural constraints
2891 // and we now fill the values for the slack constraints (one per variable),
2892 // which have this form (the constant A_pref was set when creating the slacks):
2893 //
2894 // A + A_shrinker_hard + A_shrinker_soft - A_grower_hard - A_grower_soft = A_pref
2895 //
2896 for (int i = 0; i < variables.size(); ++i) {
2897 AnchorData *ad = variables.at(i);
2898
2899 // The layout original structure anchors are not relevant in preferred size calculation
2900 if (ad->isLayoutAnchor)
2901 continue;
2902
2903 // By default, all variables are equal to their preferred size. If they have room to
2904 // grow or shrink, such flexibility will be added by the additional variables below.
2905 QSimplexConstraint *sizeConstraint = new QSimplexConstraint;
2906 preferredConstraints += sizeConstraint;
2907 sizeConstraint->variables.insert(ad, 1.0);
2908 sizeConstraint->constant = ad->prefSize + g_offset;
2909
2910 // Can easily shrink
2911 QPair<QSimplexVariable *, QSimplexConstraint *> slack;
2912 const qreal softShrinkInterval = ad->prefSize - ad->minPrefSize;
2913 if (softShrinkInterval) {
2914 slack = createSlack(sizeConstraint, softShrinkInterval, Shrinker);
2915 preferredVariables += slack.first;
2916 preferredConstraints += slack.second;
2917
2918 // Add to objective with ratio == 1 (soft)
2919 objective.variables.insert(slack.first, 1.0);
2920 }
2921
2922 // Can easily grow
2923 const qreal softGrowInterval = ad->maxPrefSize - ad->prefSize;
2924 if (softGrowInterval) {
2925 slack = createSlack(sizeConstraint, softGrowInterval, Grower);
2926 preferredVariables += slack.first;
2927 preferredConstraints += slack.second;
2928
2929 // Add to objective with ratio == 1 (soft)
2930 objective.variables.insert(slack.first, 1.0);
2931 }
2932
2933 // Can shrink if really necessary
2934 const qreal hardShrinkInterval = ad->minPrefSize - ad->minSize;
2935 if (hardShrinkInterval) {
2936 slack = createSlack(sizeConstraint, hardShrinkInterval, Shrinker);
2937 preferredVariables += slack.first;
2938 preferredConstraints += slack.second;
2939
2940 // Add to objective with ratio == N (hard)
2941 objective.variables.insert(slack.first, variables.size());
2942 }
2943
2944 // Can grow if really necessary
2945 const qreal hardGrowInterval = ad->maxSize - ad->maxPrefSize;
2946 if (hardGrowInterval) {
2947 slack = createSlack(sizeConstraint, hardGrowInterval, Grower);
2948 preferredVariables += slack.first;
2949 preferredConstraints += slack.second;
2950
2951 // Add to objective with ratio == N (hard)
2952 objective.variables.insert(slack.first, variables.size());
2953 }
2954 }
2955
2956 QSimplex *simplex = new QSimplex;
2957 bool feasible = simplex->setConstraints(constraints + preferredConstraints);
2958 if (feasible) {
2959 simplex->setObjective(&objective);
2960
2961 // Calculate minimum values
2962 simplex->solveMin();
2963
2964 // Save sizeAtPreferred results
2965 for (int i = 0; i < variables.size(); ++i) {
2966 AnchorData *ad = variables.at(i);
2967 ad->sizeAtPreferred = ad->result - g_offset;
2968 }
2969
2970 // Make sure we delete the simplex solver -before- we delete the
2971 // constraints used by it.
2972 delete simplex;
2973 }
2974 // Delete constraints and variables we created.
2975 qDeleteAll(preferredConstraints);
2976 qDeleteAll(preferredVariables);
2977
2978 return feasible;
2979}
2980
2981/*!
2982 \internal
2983 Returns true if there are no arrangement that satisfies all constraints.
2984 Otherwise returns false.
2985
2986 \sa addAnchor()
2987*/
2988bool QGraphicsAnchorLayoutPrivate::hasConflicts() const
2989{
2990 QGraphicsAnchorLayoutPrivate *that = const_cast<QGraphicsAnchorLayoutPrivate*>(this);
2991 that->calculateGraphs();
2992
2993 bool floatConflict = !m_floatItems[0].isEmpty() || !m_floatItems[1].isEmpty();
2994
2995 return graphHasConflicts[0] || graphHasConflicts[1] || floatConflict;
2996}
2997
2998#ifdef QT_DEBUG
2999void QGraphicsAnchorLayoutPrivate::dumpGraph(const QString &name)
3000{
3001 QFile file(QString::fromAscii("anchorlayout.%1.dot").arg(name));
3002 if (!file.open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate))
3003 qWarning("Could not write to %s", file.fileName().toLocal8Bit().constData());
3004
3005 QString str = QString::fromAscii("digraph anchorlayout {\nnode [shape=\"rect\"]\n%1}");
3006 QString dotContents = graph[0].serializeToDot();
3007 dotContents += graph[1].serializeToDot();
3008 file.write(str.arg(dotContents).toLocal8Bit());
3009
3010 file.close();
3011}
3012#endif
3013
3014QT_END_NAMESPACE
3015#endif //QT_NO_GRAPHICSVIEW
Note: See TracBrowser for help on using the repository browser.