qgraphicsanchorlayout_p.cpp

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

Generated by Squish Coco Non-Commercial 4.3.0-BETA-master-30-08-2018-4cb69e9