source: trunk/src/gui/graphicsview/qgridlayoutengine.cpp

Last change on this file was 846, checked in by Dmitry A. Kuminov, 14 years ago

trunk: Merged in qt 4.7.2 sources from branches/vendor/nokia/qt.

File size: 64.2 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4** All rights reserved.
5** Contact: Nokia Corporation ([email protected])
6**
7** This file is part of the QtGui module of the Qt Toolkit.
8**
9** $QT_BEGIN_LICENSE:LGPL$
10** Commercial Usage
11** Licensees holding valid Qt Commercial licenses may use this file in
12** accordance with the Qt Commercial License Agreement provided with the
13** Software or, alternatively, in accordance with the terms contained in
14** a written agreement between you and Nokia.
15**
16** GNU Lesser General Public License Usage
17** Alternatively, this file may be used under the terms of the GNU Lesser
18** General Public License version 2.1 as published by the Free Software
19** Foundation and appearing in the file LICENSE.LGPL included in the
20** packaging of this file. Please review the following information to
21** ensure the GNU Lesser General Public License version 2.1 requirements
22** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
23**
24** In addition, as a special exception, Nokia gives you certain additional
25** rights. These rights are described in the Nokia Qt LGPL Exception
26** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
27**
28** GNU General Public License Usage
29** Alternatively, this file may be used under the terms of the GNU
30** General Public License version 3.0 as published by the Free Software
31** Foundation and appearing in the file LICENSE.GPL included in the
32** packaging of this file. Please review the following information to
33** ensure the GNU General Public License version 3.0 requirements will be
34** met: http://www.gnu.org/copyleft/gpl.html.
35**
36** If you have questions regarding the use of this file, please contact
37** Nokia at [email protected].
38** $QT_END_LICENSE$
39**
40****************************************************************************/
41
42#include "qglobal.h"
43
44#ifndef QT_NO_GRAPHICSVIEW
45
46#include <math.h>
47
48#include "qgraphicslayoutitem.h"
49#include "qgridlayoutengine_p.h"
50#include "qstyleoption.h"
51#include "qvarlengtharray.h"
52
53#include <QtDebug>
54#include <QtCore/qmath.h>
55
56QT_BEGIN_NAMESPACE
57
58template <typename T>
59static void insertOrRemoveItems(QVector<T> &items, int index, int delta)
60{
61 int count = items.count();
62 if (index < count) {
63 if (delta > 0) {
64 items.insert(index, delta, T());
65 } else if (delta < 0) {
66 items.remove(index, qMin(-delta, count - index));
67 }
68 }
69}
70
71static qreal growthFactorBelowPreferredSize(qreal desired, qreal sumAvailable, qreal sumDesired)
72{
73 Q_ASSERT(sumDesired != 0.0);
74 return desired * qPow(sumAvailable / sumDesired, desired / sumDesired);
75}
76
77static qreal fixedDescent(qreal descent, qreal ascent, qreal targetSize)
78{
79 if (descent < 0.0)
80 return -1.0;
81
82 Q_ASSERT(descent >= 0.0);
83 Q_ASSERT(ascent >= 0.0);
84 Q_ASSERT(targetSize >= ascent + descent);
85
86 qreal extra = targetSize - (ascent + descent);
87 return descent + (extra / 2.0);
88}
89
90static qreal compare(const QGridLayoutBox &box1, const QGridLayoutBox &box2, int which)
91{
92 qreal size1 = box1.q_sizes(which);
93 qreal size2 = box2.q_sizes(which);
94
95 if (which == MaximumSize) {
96 return size2 - size1;
97 } else {
98 return size1 - size2;
99 }
100}
101
102void QGridLayoutBox::add(const QGridLayoutBox &other, int stretch, qreal spacing)
103{
104 Q_ASSERT(q_minimumDescent < 0.0);
105
106 q_minimumSize += other.q_minimumSize + spacing;
107 q_preferredSize += other.q_preferredSize + spacing;
108 q_maximumSize += ((stretch == 0) ? other.q_preferredSize : other.q_maximumSize) + spacing;
109}
110
111void QGridLayoutBox::combine(const QGridLayoutBox &other)
112{
113 q_minimumDescent = qMax(q_minimumDescent, other.q_minimumDescent);
114 q_minimumAscent = qMax(q_minimumAscent, other.q_minimumAscent);
115
116 q_minimumSize = qMax(q_minimumAscent + q_minimumDescent,
117 qMax(q_minimumSize, other.q_minimumSize));
118 qreal maxMax;
119 if (q_maximumSize == FLT_MAX && other.q_maximumSize != FLT_MAX)
120 maxMax = other.q_maximumSize;
121 else if (other.q_maximumSize == FLT_MAX && q_maximumSize != FLT_MAX)
122 maxMax = q_maximumSize;
123 else
124 maxMax = qMax(q_maximumSize, other.q_maximumSize);
125
126 q_maximumSize = qMax(q_minimumSize, maxMax);
127 q_preferredSize = qBound(q_minimumSize, qMax(q_preferredSize, other.q_preferredSize),
128 q_maximumSize);
129}
130
131void QGridLayoutBox::normalize()
132{
133 q_maximumSize = qMax(qreal(0.0), q_maximumSize);
134 q_minimumSize = qBound(qreal(0.0), q_minimumSize, q_maximumSize);
135 q_preferredSize = qBound(q_minimumSize, q_preferredSize, q_maximumSize);
136 q_minimumDescent = qMin(q_minimumDescent, q_minimumSize);
137
138 Q_ASSERT((q_minimumDescent < 0.0) == (q_minimumAscent < 0.0));
139}
140
141#ifdef QT_DEBUG
142void QGridLayoutBox::dump(int indent) const
143{
144 qDebug("%*sBox (%g <= %g <= %g [%g/%g])", indent, "", q_minimumSize, q_preferredSize,
145 q_maximumSize, q_minimumAscent, q_minimumDescent);
146}
147#endif
148
149bool operator==(const QGridLayoutBox &box1, const QGridLayoutBox &box2)
150{
151 for (int i = 0; i < NSizes; ++i) {
152 if (box1.q_sizes(i) != box2.q_sizes(i))
153 return false;
154 }
155 return box1.q_minimumDescent == box2.q_minimumDescent
156 && box1.q_minimumAscent == box2.q_minimumAscent;
157}
158
159void QGridLayoutRowData::reset(int count)
160{
161 ignore.fill(false, count);
162 boxes.fill(QGridLayoutBox(), count);
163 multiCellMap.clear();
164 stretches.fill(0, count);
165 spacings.fill(0.0, count);
166 hasIgnoreFlag = false;
167}
168
169void QGridLayoutRowData::distributeMultiCells(const QGridLayoutRowInfo &rowInfo)
170{
171 MultiCellMap::const_iterator i = multiCellMap.constBegin();
172 for (; i != multiCellMap.constEnd(); ++i) {
173 int start = i.key().first;
174 int span = i.key().second;
175 int end = start + span;
176 const QGridLayoutBox &box = i.value().q_box;
177 int stretch = i.value().q_stretch;
178
179 QGridLayoutBox totalBox = this->totalBox(start, end);
180 QVarLengthArray<QGridLayoutBox> extras(span);
181 QVarLengthArray<qreal> dummy(span);
182 QVarLengthArray<qreal> newSizes(span);
183
184 for (int j = 0; j < NSizes; ++j) {
185 qreal extra = compare(box, totalBox, j);
186 if (extra > 0.0) {
187 calculateGeometries(start, end, box.q_sizes(j), dummy.data(), newSizes.data(),
188 0, totalBox, rowInfo);
189
190 for (int k = 0; k < span; ++k)
191 extras[k].q_sizes(j) = newSizes[k];
192 }
193 }
194
195 for (int k = 0; k < span; ++k) {
196 boxes[start + k].combine(extras[k]);
197 if (stretch != 0)
198 stretches[start + k] = qMax(stretches[start + k], stretch);
199 }
200 }
201 multiCellMap.clear();
202}
203
204void QGridLayoutRowData::calculateGeometries(int start, int end, qreal targetSize, qreal *positions,
205 qreal *sizes, qreal *descents,
206 const QGridLayoutBox &totalBox,
207 const QGridLayoutRowInfo &rowInfo)
208{
209 Q_ASSERT(end > start);
210
211 targetSize = qMax(totalBox.q_minimumSize, targetSize);
212
213 int n = end - start;
214 QVarLengthArray<qreal> newSizes(n);
215 QVarLengthArray<qreal> factors(n);
216 qreal sumFactors = 0.0;
217 int sumStretches = 0;
218 qreal sumAvailable;
219
220 for (int i = 0; i < n; ++i) {
221 if (stretches[start + i] > 0)
222 sumStretches += stretches[start + i];
223 }
224
225 if (targetSize < totalBox.q_preferredSize) {
226 stealBox(start, end, MinimumSize, positions, sizes);
227
228 sumAvailable = targetSize - totalBox.q_minimumSize;
229 if (sumAvailable > 0.0) {
230 qreal sumDesired = totalBox.q_preferredSize - totalBox.q_minimumSize;
231
232 for (int i = 0; i < n; ++i) {
233 if (ignore.testBit(start + i)) {
234 factors[i] = 0.0;
235 continue;
236 }
237
238 const QGridLayoutBox &box = boxes.at(start + i);
239 qreal desired = box.q_preferredSize - box.q_minimumSize;
240 factors[i] = growthFactorBelowPreferredSize(desired, sumAvailable, sumDesired);
241 sumFactors += factors[i];
242 }
243
244 for (int i = 0; i < n; ++i) {
245 Q_ASSERT(sumFactors > 0.0);
246 qreal delta = sumAvailable * factors[i] / sumFactors;
247 newSizes[i] = sizes[i] + delta;
248 }
249 }
250 } else {
251 bool isLargerThanMaximum = (targetSize > totalBox.q_maximumSize);
252 if (isLargerThanMaximum) {
253 stealBox(start, end, MaximumSize, positions, sizes);
254 sumAvailable = targetSize - totalBox.q_maximumSize;
255 } else {
256 stealBox(start, end, PreferredSize, positions, sizes);
257 sumAvailable = targetSize - totalBox.q_preferredSize;
258 }
259
260 if (sumAvailable > 0.0) {
261 qreal sumCurrentAvailable = sumAvailable;
262 bool somethingHasAMaximumSize = false;
263
264 qreal sumSizes = 0.0;
265 for (int i = 0; i < n; ++i)
266 sumSizes += sizes[i];
267
268 for (int i = 0; i < n; ++i) {
269 if (ignore.testBit(start + i)) {
270 newSizes[i] = 0.0;
271 factors[i] = 0.0;
272 continue;
273 }
274
275 const QGridLayoutBox &box = boxes.at(start + i);
276 qreal boxSize;
277
278 qreal desired;
279 if (isLargerThanMaximum) {
280 boxSize = box.q_maximumSize;
281 desired = rowInfo.boxes.value(start + i).q_maximumSize - boxSize;
282 } else {
283 boxSize = box.q_preferredSize;
284 desired = box.q_maximumSize - boxSize;
285 }
286 if (desired == 0.0) {
287 newSizes[i] = sizes[i];
288 factors[i] = 0.0;
289 } else {
290 Q_ASSERT(desired > 0.0);
291
292 int stretch = stretches[start + i];
293 if (sumStretches == 0) {
294 if (hasIgnoreFlag) {
295 factors[i] = (stretch < 0) ? 1.0 : 0.0;
296 } else {
297 factors[i] = (stretch < 0) ? sizes[i] : 0.0;
298 }
299 } else if (stretch == sumStretches) {
300 factors[i] = 1.0;
301 } else if (stretch <= 0) {
302 factors[i] = 0.0;
303 } else {
304 qreal ultimateSize;
305 qreal ultimateSumSizes;
306 qreal x = ((stretch * sumSizes)
307 - (sumStretches * boxSize))
308 / (sumStretches - stretch);
309 if (x >= 0.0) {
310 ultimateSize = boxSize + x;
311 ultimateSumSizes = sumSizes + x;
312 } else {
313 ultimateSize = boxSize;
314 ultimateSumSizes = (sumStretches * boxSize)
315 / stretch;
316 }
317
318 /*
319 We multiply these by 1.5 to give some space for a smooth transition
320 (at the expense of the stretch factors, which are not fully respected
321 during the transition).
322 */
323 ultimateSize = ultimateSize * 3 / 2;
324 ultimateSumSizes = ultimateSumSizes * 3 / 2;
325
326 qreal beta = ultimateSumSizes - sumSizes;
327 if (!beta) {
328 factors[i] = 1;
329 } else {
330 qreal alpha = qMin(sumCurrentAvailable, beta);
331 qreal ultimateFactor = (stretch * ultimateSumSizes / sumStretches)
332 - (boxSize);
333 qreal transitionalFactor = sumCurrentAvailable * (ultimateSize - boxSize) / beta;
334
335 factors[i] = ((alpha * ultimateFactor)
336 + ((beta - alpha) * transitionalFactor)) / beta;
337 }
338
339 }
340 sumFactors += factors[i];
341 if (desired < sumCurrentAvailable)
342 somethingHasAMaximumSize = true;
343
344 newSizes[i] = -1.0;
345 }
346 }
347
348 bool keepGoing = somethingHasAMaximumSize;
349 while (keepGoing) {
350 keepGoing = false;
351
352 for (int i = 0; i < n; ++i) {
353 if (newSizes[i] >= 0.0)
354 continue;
355
356 qreal maxBoxSize;
357 if (isLargerThanMaximum)
358 maxBoxSize = rowInfo.boxes.value(start + i).q_maximumSize;
359 else
360 maxBoxSize = boxes.at(start + i).q_maximumSize;
361
362 qreal avail = sumCurrentAvailable * factors[i] / sumFactors;
363 if (sizes[i] + avail >= maxBoxSize) {
364 newSizes[i] = maxBoxSize;
365 sumCurrentAvailable -= maxBoxSize - sizes[i];
366 sumFactors -= factors[i];
367 keepGoing = (sumCurrentAvailable > 0.0);
368 if (!keepGoing)
369 break;
370 }
371 }
372 }
373
374 for (int i = 0; i < n; ++i) {
375 if (newSizes[i] < 0.0) {
376 qreal delta = (sumFactors == 0.0) ? 0.0
377 : sumCurrentAvailable * factors[i] / sumFactors;
378 newSizes[i] = sizes[i] + delta;
379 }
380 }
381 }
382 }
383
384 if (sumAvailable > 0) {
385 qreal offset = 0;
386 for (int i = 0; i < n; ++i) {
387 qreal delta = newSizes[i] - sizes[i];
388 positions[i] += offset;
389 sizes[i] += delta;
390 offset += delta;
391 }
392
393#if 0 // some "pixel allocation"
394 int surplus = targetSize - (positions[n - 1] + sizes[n - 1]);
395 Q_ASSERT(surplus >= 0 && surplus <= n);
396
397 int prevSurplus = -1;
398 while (surplus > 0 && surplus != prevSurplus) {
399 prevSurplus = surplus;
400
401 int offset = 0;
402 for (int i = 0; i < n; ++i) {
403 const QGridLayoutBox &box = boxes.at(start + i);
404 int delta = (!ignore.testBit(start + i) && surplus > 0
405 && factors[i] > 0 && sizes[i] < box.q_maximumSize)
406 ? 1 : 0;
407
408 positions[i] += offset;
409 sizes[i] += delta;
410 offset += delta;
411 surplus -= delta;
412 }
413 }
414 Q_ASSERT(surplus == 0);
415#endif
416 }
417
418 if (descents) {
419 for (int i = 0; i < n; ++i) {
420 if (ignore.testBit(start + i))
421 continue;
422 const QGridLayoutBox &box = boxes.at(start + i);
423 descents[i] = fixedDescent(box.q_minimumDescent, box.q_minimumAscent, sizes[i]);
424 }
425 }
426}
427
428QGridLayoutBox QGridLayoutRowData::totalBox(int start, int end) const
429{
430 QGridLayoutBox result;
431 if (start < end) {
432 result.q_maximumSize = 0.0;
433 qreal nextSpacing = 0.0;
434 for (int i = start; i < end; ++i) {
435 result.add(boxes.at(i), stretches.at(i), nextSpacing);
436 nextSpacing = spacings.at(i);
437 }
438 }
439 return result;
440}
441
442void QGridLayoutRowData::stealBox(int start, int end, int which, qreal *positions, qreal *sizes)
443{
444 qreal offset = 0.0;
445 qreal nextSpacing = 0.0;
446
447 for (int i = start; i < end; ++i) {
448 qreal avail = 0.0;
449
450 if (!ignore.testBit(i)) {
451 const QGridLayoutBox &box = boxes.at(i);
452 avail = box.q_sizes(which);
453 offset += nextSpacing;
454 nextSpacing = spacings.at(i);
455 }
456
457 *positions++ = offset;
458 *sizes++ = avail;
459 offset += avail;
460 }
461}
462
463#ifdef QT_DEBUG
464void QGridLayoutRowData::dump(int indent) const
465{
466 qDebug("%*sData", indent, "");
467
468 for (int i = 0; i < ignore.count(); ++i) {
469 qDebug("%*s Row %d (stretch %d, spacing %g)", indent, "", i, stretches.at(i),
470 spacings.at(i));
471 if (ignore.testBit(i))
472 qDebug("%*s Ignored", indent, "");
473 boxes.at(i).dump(indent + 2);
474 }
475
476 MultiCellMap::const_iterator it = multiCellMap.constBegin();
477 while (it != multiCellMap.constEnd()) {
478 qDebug("%*s Multi-cell entry <%d, %d> (stretch %d)", indent, "", it.key().first,
479 it.key().second, it.value().q_stretch);
480 it.value().q_box.dump(indent + 2);
481 }
482}
483#endif
484
485QGridLayoutItem::QGridLayoutItem(QGridLayoutEngine *engine, QGraphicsLayoutItem *layoutItem,
486 int row, int column, int rowSpan, int columnSpan,
487 Qt::Alignment alignment, int itemAtIndex)
488 : q_engine(engine), q_layoutItem(layoutItem), q_alignment(alignment)
489{
490 q_firstRows[Hor] = column;
491 q_firstRows[Ver] = row;
492 q_rowSpans[Hor] = columnSpan;
493 q_rowSpans[Ver] = rowSpan;
494 q_stretches[Hor] = -1;
495 q_stretches[Ver] = -1;
496
497 q_engine->insertItem(this, itemAtIndex);
498}
499
500int QGridLayoutItem::firstRow(Qt::Orientation orientation) const
501{
502 return q_firstRows[orientation == Qt::Vertical];
503}
504
505int QGridLayoutItem::firstColumn(Qt::Orientation orientation) const
506{
507 return q_firstRows[orientation == Qt::Horizontal];
508}
509
510int QGridLayoutItem::lastRow(Qt::Orientation orientation) const
511{
512 return firstRow(orientation) + rowSpan(orientation) - 1;
513}
514
515int QGridLayoutItem::lastColumn(Qt::Orientation orientation) const
516{
517 return firstColumn(orientation) + columnSpan(orientation) - 1;
518}
519
520int QGridLayoutItem::rowSpan(Qt::Orientation orientation) const
521{
522 return q_rowSpans[orientation == Qt::Vertical];
523}
524
525int QGridLayoutItem::columnSpan(Qt::Orientation orientation) const
526{
527 return q_rowSpans[orientation == Qt::Horizontal];
528}
529
530void QGridLayoutItem::setFirstRow(int row, Qt::Orientation orientation)
531{
532 q_firstRows[orientation == Qt::Vertical] = row;
533}
534
535void QGridLayoutItem::setRowSpan(int rowSpan, Qt::Orientation orientation)
536{
537 q_rowSpans[orientation == Qt::Vertical] = rowSpan;
538}
539
540int QGridLayoutItem::stretchFactor(Qt::Orientation orientation) const
541{
542 int stretch = q_stretches[orientation == Qt::Vertical];
543 if (stretch >= 0)
544 return stretch;
545
546 QSizePolicy::Policy policy = sizePolicy(orientation);
547
548 if (policy & QSizePolicy::ExpandFlag) {
549 return 1;
550 } else if (policy & QSizePolicy::GrowFlag) {
551 return -1; // because we max it up
552 } else {
553 return 0;
554 }
555}
556
557void QGridLayoutItem::setStretchFactor(int stretch, Qt::Orientation orientation)
558{
559 Q_ASSERT(stretch >= 0); // ### deal with too big stretches
560 q_stretches[orientation == Qt::Vertical] = stretch;
561}
562
563QSizePolicy::Policy QGridLayoutItem::sizePolicy(Qt::Orientation orientation) const
564{
565 QSizePolicy sizePolicy(q_layoutItem->sizePolicy());
566 return (orientation == Qt::Horizontal) ? sizePolicy.horizontalPolicy()
567 : sizePolicy.verticalPolicy();
568}
569
570/*
571 returns true if the size policy returns true for either hasHeightForWidth()
572 or hasWidthForHeight()
573 */
574bool QGridLayoutItem::hasDynamicConstraint() const
575{
576 return QGraphicsLayoutItemPrivate::get(q_layoutItem)->hasHeightForWidth()
577 || QGraphicsLayoutItemPrivate::get(q_layoutItem)->hasWidthForHeight();
578}
579
580Qt::Orientation QGridLayoutItem::dynamicConstraintOrientation() const
581{
582 if (QGraphicsLayoutItemPrivate::get(q_layoutItem)->hasHeightForWidth())
583 return Qt::Vertical;
584 else //if (QGraphicsLayoutItemPrivate::get(q_layoutItem)->hasWidthForHeight())
585 return Qt::Horizontal;
586}
587
588QSizePolicy::ControlTypes QGridLayoutItem::controlTypes(LayoutSide /* side */) const
589{
590 return q_layoutItem->sizePolicy().controlType();
591}
592
593QSizeF QGridLayoutItem::sizeHint(Qt::SizeHint which, const QSizeF &constraint) const
594{
595 return q_layoutItem->effectiveSizeHint(which, constraint);
596}
597
598QGridLayoutBox QGridLayoutItem::box(Qt::Orientation orientation, qreal constraint) const
599{
600 QGridLayoutBox result;
601 QSizePolicy::Policy policy = sizePolicy(orientation);
602
603 if (orientation == Qt::Horizontal) {
604 QSizeF constraintSize(-1.0, constraint);
605
606 result.q_preferredSize = sizeHint(Qt::PreferredSize, constraintSize).width();
607
608 if (policy & QSizePolicy::ShrinkFlag) {
609 result.q_minimumSize = sizeHint(Qt::MinimumSize, constraintSize).width();
610 } else {
611 result.q_minimumSize = result.q_preferredSize;
612 }
613
614 if (policy & (QSizePolicy::GrowFlag | QSizePolicy::ExpandFlag)) {
615 result.q_maximumSize = sizeHint(Qt::MaximumSize, constraintSize).width();
616 } else {
617 result.q_maximumSize = result.q_preferredSize;
618 }
619 } else {
620 QSizeF constraintSize(constraint, -1.0);
621
622 result.q_preferredSize = sizeHint(Qt::PreferredSize, constraintSize).height();
623
624 if (policy & QSizePolicy::ShrinkFlag) {
625 result.q_minimumSize = sizeHint(Qt::MinimumSize, constraintSize).height();
626 } else {
627 result.q_minimumSize = result.q_preferredSize;
628 }
629
630 if (policy & (QSizePolicy::GrowFlag | QSizePolicy::ExpandFlag)) {
631 result.q_maximumSize = sizeHint(Qt::MaximumSize, constraintSize).height();
632 } else {
633 result.q_maximumSize = result.q_preferredSize;
634 }
635
636 result.q_minimumDescent = sizeHint(Qt::MinimumDescent, constraintSize).height();
637 if (result.q_minimumDescent >= 0.0)
638 result.q_minimumAscent = result.q_minimumSize - result.q_minimumDescent;
639 }
640 if (policy & QSizePolicy::IgnoreFlag)
641 result.q_preferredSize = result.q_minimumSize;
642
643 return result;
644}
645
646QRectF QGridLayoutItem::geometryWithin(qreal x, qreal y, qreal width, qreal height,
647 qreal rowDescent) const
648{
649 rowDescent = -1.0; // ### This disables the descent
650
651 QGridLayoutBox vBox = box(Qt::Vertical);
652 if (vBox.q_minimumDescent < 0.0 || rowDescent < 0.0) {
653 qreal cellWidth = width;
654 qreal cellHeight = height;
655
656
657 QSizeF size = effectiveMaxSize(QSizeF(-1,-1));
658 if (hasDynamicConstraint()) {
659 if (dynamicConstraintOrientation() == Qt::Vertical) {
660 if (size.width() > cellWidth)
661 size = effectiveMaxSize(QSizeF(cellWidth, -1));
662 } else if (size.height() > cellHeight) {
663 size = effectiveMaxSize(QSizeF(-1, cellHeight));
664 }
665 }
666 size = size.boundedTo(QSizeF(cellWidth, cellHeight));
667 width = size.width();
668 height = size.height();
669
670 Qt::Alignment align = q_engine->effectiveAlignment(this);
671 switch (align & Qt::AlignHorizontal_Mask) {
672 case Qt::AlignHCenter:
673 x += (cellWidth - width)/2;
674 break;
675 case Qt::AlignRight:
676 x += cellWidth - width;
677 break;
678 default:
679 break;
680 }
681 switch (align & Qt::AlignVertical_Mask) {
682 case Qt::AlignVCenter:
683 y += (cellHeight - height)/2;
684 break;
685 case Qt::AlignBottom:
686 y += cellHeight - height;
687 break;
688 default:
689 break;
690 }
691 return QRectF(x, y, width, height);
692 } else {
693 qreal descent = vBox.q_minimumDescent;
694 qreal ascent = vBox.q_minimumSize - descent;
695 return QRectF(x, y + height - rowDescent - ascent, width, ascent + descent);
696 }
697}
698
699void QGridLayoutItem::setGeometry(const QRectF &rect)
700{
701 q_layoutItem->setGeometry(rect);
702}
703
704void QGridLayoutItem::transpose()
705{
706 qSwap(q_firstRows[Hor], q_firstRows[Ver]);
707 qSwap(q_rowSpans[Hor], q_rowSpans[Ver]);
708 qSwap(q_stretches[Hor], q_stretches[Ver]);
709}
710
711void QGridLayoutItem::insertOrRemoveRows(int row, int delta, Qt::Orientation orientation)
712{
713 int oldFirstRow = firstRow(orientation);
714 if (oldFirstRow >= row) {
715 setFirstRow(oldFirstRow + delta, orientation);
716 } else if (lastRow(orientation) >= row) {
717 setRowSpan(rowSpan(orientation) + delta, orientation);
718 }
719}
720/*!
721 \internal
722 returns the effective maximumSize, will take the sizepolicy into
723 consideration. (i.e. if sizepolicy does not have QSizePolicy::Grow, then
724 maxSizeHint will be the preferredSize)
725 Note that effectiveSizeHint does not take sizePolicy into consideration,
726 (since it only evaluates the hints, as the name implies)
727*/
728QSizeF QGridLayoutItem::effectiveMaxSize(const QSizeF &constraint) const
729{
730 QSizeF size = constraint;
731 bool vGrow = (sizePolicy(Qt::Vertical) & QSizePolicy::GrowFlag) == QSizePolicy::GrowFlag;
732 bool hGrow = (sizePolicy(Qt::Horizontal) & QSizePolicy::GrowFlag) == QSizePolicy::GrowFlag;
733 if (!vGrow || !hGrow) {
734 QSizeF pref = layoutItem()->effectiveSizeHint(Qt::PreferredSize, constraint);
735 if (!vGrow)
736 size.setHeight(pref.height());
737 if (!hGrow)
738 size.setWidth(pref.width());
739 }
740
741 if (!size.isValid()) {
742 QSizeF maxSize = layoutItem()->effectiveSizeHint(Qt::MaximumSize, size);
743 if (size.width() == -1)
744 size.setWidth(maxSize.width());
745 if (size.height() == -1)
746 size.setHeight(maxSize.height());
747 }
748 return size;
749}
750
751#ifdef QT_DEBUG
752void QGridLayoutItem::dump(int indent) const
753{
754 qDebug("%*s%p (%d, %d) %d x %d", indent, "", q_layoutItem, firstRow(), firstColumn(),
755 rowSpan(), columnSpan());
756
757 if (q_stretches[Hor] >= 0)
758 qDebug("%*s Horizontal stretch: %d", indent, "", q_stretches[Hor]);
759 if (q_stretches[Ver] >= 0)
760 qDebug("%*s Vertical stretch: %d", indent, "", q_stretches[Ver]);
761 if (q_alignment != 0)
762 qDebug("%*s Alignment: %x", indent, "", uint(q_alignment));
763 qDebug("%*s Horizontal size policy: %x Vertical size policy: %x",
764 indent, "", sizePolicy(Qt::Horizontal), sizePolicy(Qt::Vertical));
765}
766#endif
767
768void QGridLayoutRowInfo::insertOrRemoveRows(int row, int delta)
769{
770 count += delta;
771
772 insertOrRemoveItems(stretches, row, delta);
773 insertOrRemoveItems(spacings, row, delta);
774 insertOrRemoveItems(alignments, row, delta);
775 insertOrRemoveItems(boxes, row, delta);
776}
777
778#ifdef QT_DEBUG
779void QGridLayoutRowInfo::dump(int indent) const
780{
781 qDebug("%*sInfo (count: %d)", indent, "", count);
782 for (int i = 0; i < count; ++i) {
783 QString message;
784
785 if (stretches.value(i).value() >= 0)
786 message += QString::fromAscii(" stretch %1").arg(stretches.value(i).value());
787 if (spacings.value(i).value() >= 0.0)
788 message += QString::fromAscii(" spacing %1").arg(spacings.value(i).value());
789 if (alignments.value(i) != 0)
790 message += QString::fromAscii(" alignment %1").arg(int(alignments.value(i)), 16);
791
792 if (!message.isEmpty() || boxes.value(i) != QGridLayoutBox()) {
793 qDebug("%*s Row %d:%s", indent, "", i, qPrintable(message));
794 if (boxes.value(i) != QGridLayoutBox())
795 boxes.value(i).dump(indent + 1);
796 }
797 }
798}
799#endif
800
801QGridLayoutEngine::QGridLayoutEngine()
802{
803 m_visualDirection = Qt::LeftToRight;
804 invalidate();
805}
806
807int QGridLayoutEngine::rowCount(Qt::Orientation orientation) const
808{
809 return q_infos[orientation == Qt::Vertical].count;
810}
811
812int QGridLayoutEngine::columnCount(Qt::Orientation orientation) const
813{
814 return q_infos[orientation == Qt::Horizontal].count;
815}
816
817int QGridLayoutEngine::itemCount() const
818{
819 return q_items.count();
820}
821
822QGridLayoutItem *QGridLayoutEngine::itemAt(int index) const
823{
824 Q_ASSERT(index >= 0 && index < itemCount());
825 return q_items.at(index);
826}
827
828int QGridLayoutEngine::effectiveFirstRow(Qt::Orientation orientation) const
829{
830 ensureEffectiveFirstAndLastRows();
831 return q_cachedEffectiveFirstRows[orientation == Qt::Vertical];
832}
833
834int QGridLayoutEngine::effectiveLastRow(Qt::Orientation orientation) const
835{
836 ensureEffectiveFirstAndLastRows();
837 return q_cachedEffectiveLastRows[orientation == Qt::Vertical];
838}
839
840void QGridLayoutEngine::setSpacing(qreal spacing, Qt::Orientations orientations)
841{
842 Q_ASSERT(spacing >= 0.0);
843 if (orientations & Qt::Horizontal)
844 q_defaultSpacings[Hor].setUserValue(spacing);
845 if (orientations & Qt::Vertical)
846 q_defaultSpacings[Ver].setUserValue(spacing);
847
848 invalidate();
849}
850
851qreal QGridLayoutEngine::spacing(const QLayoutStyleInfo &styleInfo, Qt::Orientation orientation) const
852{
853 if (q_defaultSpacings[orientation == Qt::Vertical].isDefault()) {
854 QStyle *style = styleInfo.style();
855 QStyleOption option;
856 option.initFrom(styleInfo.widget());
857 qreal defaultSpacing = (qreal)style->pixelMetric(orientation == Qt::Vertical ? QStyle::PM_LayoutVerticalSpacing
858 : QStyle::PM_LayoutHorizontalSpacing, &option, styleInfo.widget());
859 q_defaultSpacings[orientation == Qt::Vertical].setCachedValue(defaultSpacing);
860 }
861 return q_defaultSpacings[orientation == Qt::Vertical].value();
862}
863
864void QGridLayoutEngine::setRowSpacing(int row, qreal spacing, Qt::Orientation orientation)
865{
866 Q_ASSERT(row >= 0);
867
868 QGridLayoutRowInfo &rowInfo = q_infos[orientation == Qt::Vertical];
869 if (row >= rowInfo.spacings.count())
870 rowInfo.spacings.resize(row + 1);
871 if (spacing >= 0)
872 rowInfo.spacings[row].setUserValue(spacing);
873 else
874 rowInfo.spacings[row] = QLayoutParameter<qreal>();
875 invalidate();
876}
877
878qreal QGridLayoutEngine::rowSpacing(int row, Qt::Orientation orientation) const
879{
880 QLayoutParameter<qreal> spacing = q_infos[orientation == Qt::Vertical].spacings.value(row);
881 if (!spacing.isDefault())
882 return spacing.value();
883 return q_defaultSpacings[orientation == Qt::Vertical].value();
884}
885
886void QGridLayoutEngine::setRowStretchFactor(int row, int stretch, Qt::Orientation orientation)
887{
888 Q_ASSERT(row >= 0);
889 Q_ASSERT(stretch >= 0);
890
891 maybeExpandGrid(row, -1, orientation);
892
893 QGridLayoutRowInfo &rowInfo = q_infos[orientation == Qt::Vertical];
894 if (row >= rowInfo.stretches.count())
895 rowInfo.stretches.resize(row + 1);
896 rowInfo.stretches[row].setUserValue(stretch);
897}
898
899int QGridLayoutEngine::rowStretchFactor(int row, Qt::Orientation orientation) const
900{
901 QStretchParameter stretch = q_infos[orientation == Qt::Vertical].stretches.value(row);
902 if (!stretch.isDefault())
903 return stretch.value();
904 return 0;
905}
906
907void QGridLayoutEngine::setStretchFactor(QGraphicsLayoutItem *layoutItem, int stretch,
908 Qt::Orientation orientation)
909{
910 Q_ASSERT(stretch >= 0);
911
912 if (QGridLayoutItem *item = findLayoutItem(layoutItem))
913 item->setStretchFactor(stretch, orientation);
914}
915
916int QGridLayoutEngine::stretchFactor(QGraphicsLayoutItem *layoutItem, Qt::Orientation orientation) const
917{
918 if (QGridLayoutItem *item = findLayoutItem(layoutItem))
919 return item->stretchFactor(orientation);
920 return 0;
921}
922
923void QGridLayoutEngine::setRowSizeHint(Qt::SizeHint which, int row, qreal size,
924 Qt::Orientation orientation)
925{
926 Q_ASSERT(row >= 0);
927 Q_ASSERT(size >= 0.0);
928
929 maybeExpandGrid(row, -1, orientation);
930
931 QGridLayoutRowInfo &rowInfo = q_infos[orientation == Qt::Vertical];
932 if (row >= rowInfo.boxes.count())
933 rowInfo.boxes.resize(row + 1);
934 rowInfo.boxes[row].q_sizes(which) = size;
935}
936
937qreal QGridLayoutEngine::rowSizeHint(Qt::SizeHint which, int row, Qt::Orientation orientation) const
938{
939 return q_infos[orientation == Qt::Vertical].boxes.value(row).q_sizes(which);
940}
941
942void QGridLayoutEngine::setRowAlignment(int row, Qt::Alignment alignment,
943 Qt::Orientation orientation)
944{
945 Q_ASSERT(row >= 0);
946
947 maybeExpandGrid(row, -1, orientation);
948
949 QGridLayoutRowInfo &rowInfo = q_infos[orientation == Qt::Vertical];
950 if (row >= rowInfo.alignments.count())
951 rowInfo.alignments.resize(row + 1);
952 rowInfo.alignments[row] = alignment;
953}
954
955Qt::Alignment QGridLayoutEngine::rowAlignment(int row, Qt::Orientation orientation) const
956{
957 Q_ASSERT(row >= 0);
958 return q_infos[orientation == Qt::Vertical].alignments.value(row);
959}
960
961void QGridLayoutEngine::setAlignment(QGraphicsLayoutItem *layoutItem, Qt::Alignment alignment)
962{
963 if (QGridLayoutItem *item = findLayoutItem(layoutItem))
964 item->setAlignment(alignment);
965 invalidate();
966}
967
968Qt::Alignment QGridLayoutEngine::alignment(QGraphicsLayoutItem *layoutItem) const
969{
970 if (QGridLayoutItem *item = findLayoutItem(layoutItem))
971 return item->alignment();
972 return 0;
973}
974
975Qt::Alignment QGridLayoutEngine::effectiveAlignment(const QGridLayoutItem *layoutItem) const
976{
977 Qt::Alignment align = layoutItem->alignment();
978 if (!(align & Qt::AlignVertical_Mask)) {
979 // no vertical alignment, respect the row alignment
980 int y = layoutItem->firstRow();
981 align |= (rowAlignment(y, Qt::Vertical) & Qt::AlignVertical_Mask);
982 }
983 if (!(align & Qt::AlignHorizontal_Mask)) {
984 // no horizontal alignment, respect the column alignment
985 int x = layoutItem->firstColumn();
986 align |= (rowAlignment(x, Qt::Horizontal) & Qt::AlignHorizontal_Mask);
987 }
988 return align;
989}
990
991/*!
992 \internal
993 The \a index is only used by QGraphicsLinearLayout to ensure that itemAt() reflects the order
994 of visual arrangement. Strictly speaking it does not have to, but most people expect it to.
995 (And if it didn't we would have to add itemArrangedAt(int index) or something..)
996 */
997void QGridLayoutEngine::insertItem(QGridLayoutItem *item, int index)
998{
999 maybeExpandGrid(item->lastRow(), item->lastColumn());
1000
1001 if (index == -1)
1002 q_items.append(item);
1003 else
1004 q_items.insert(index, item);
1005
1006 for (int i = item->firstRow(); i <= item->lastRow(); ++i) {
1007 for (int j = item->firstColumn(); j <= item->lastColumn(); ++j) {
1008 if (itemAt(i, j))
1009 qWarning("QGridLayoutEngine::addItem: Cell (%d, %d) already taken", i, j);
1010 setItemAt(i, j, item);
1011 }
1012 }
1013}
1014
1015void QGridLayoutEngine::addItem(QGridLayoutItem *item)
1016{
1017 insertItem(item, -1);
1018}
1019
1020void QGridLayoutEngine::removeItem(QGridLayoutItem *item)
1021{
1022 Q_ASSERT(q_items.contains(item));
1023
1024 invalidate();
1025
1026 for (int i = item->firstRow(); i <= item->lastRow(); ++i) {
1027 for (int j = item->firstColumn(); j <= item->lastColumn(); ++j) {
1028 if (itemAt(i, j) == item)
1029 setItemAt(i, j, 0);
1030 }
1031 }
1032
1033 q_items.removeAll(item);
1034}
1035
1036QGridLayoutItem *QGridLayoutEngine::findLayoutItem(QGraphicsLayoutItem *layoutItem) const
1037{
1038 for (int i = q_items.count() - 1; i >= 0; --i) {
1039 QGridLayoutItem *item = q_items.at(i);
1040 if (item->layoutItem() == layoutItem)
1041 return item;
1042 }
1043 return 0;
1044}
1045
1046QGridLayoutItem *QGridLayoutEngine::itemAt(int row, int column, Qt::Orientation orientation) const
1047{
1048 if (orientation == Qt::Horizontal)
1049 qSwap(row, column);
1050 if (uint(row) >= uint(rowCount()) || uint(column) >= uint(columnCount()))
1051 return 0;
1052 return q_grid.at((row * internalGridColumnCount()) + column);
1053}
1054
1055void QGridLayoutEngine::invalidate()
1056{
1057 q_cachedEffectiveFirstRows[Hor] = -1;
1058 q_cachedEffectiveFirstRows[Ver] = -1;
1059 q_cachedEffectiveLastRows[Hor] = -1;
1060 q_cachedEffectiveLastRows[Ver] = -1;
1061 q_cachedDataForStyleInfo.invalidate();
1062 q_cachedSize = QSizeF();
1063 q_cachedConstraintOrientation = UnknownConstraint;
1064}
1065
1066static void visualRect(QRectF *geom, Qt::LayoutDirection dir, const QRectF &contentsRect)
1067{
1068 if (dir == Qt::RightToLeft)
1069 geom->moveRight(contentsRect.right() - (geom->left() - contentsRect.left()));
1070}
1071
1072void QGridLayoutEngine::setGeometries(const QLayoutStyleInfo &styleInfo,
1073 const QRectF &contentsGeometry)
1074{
1075 if (rowCount() < 1 || columnCount() < 1)
1076 return;
1077
1078 ensureGeometries(styleInfo, contentsGeometry.size());
1079
1080 for (int i = q_items.count() - 1; i >= 0; --i) {
1081 QGridLayoutItem *item = q_items.at(i);
1082
1083 qreal x = q_xx[item->firstColumn()];
1084 qreal y = q_yy[item->firstRow()];
1085 qreal width = q_widths[item->lastColumn()];
1086 qreal height = q_heights[item->lastRow()];
1087
1088 if (item->columnSpan() != 1)
1089 width += q_xx[item->lastColumn()] - x;
1090 if (item->rowSpan() != 1)
1091 height += q_yy[item->lastRow()] - y;
1092
1093 QRectF geom = item->geometryWithin(contentsGeometry.x() + x, contentsGeometry.y() + y,
1094 width, height, q_descents[item->lastRow()]);
1095 visualRect(&geom, visualDirection(), contentsGeometry);
1096 item->setGeometry(geom);
1097 }
1098}
1099
1100// ### candidate for deletion
1101QRectF QGridLayoutEngine::cellRect(const QLayoutStyleInfo &styleInfo,
1102 const QRectF &contentsGeometry, int row, int column, int rowSpan,
1103 int columnSpan) const
1104{
1105 if (uint(row) >= uint(rowCount()) || uint(column) >= uint(columnCount())
1106 || rowSpan < 1 || columnSpan < 1)
1107 return QRectF();
1108
1109 ensureGeometries(styleInfo, contentsGeometry.size());
1110
1111 int lastColumn = qMax(column + columnSpan, columnCount()) - 1;
1112 int lastRow = qMax(row + rowSpan, rowCount()) - 1;
1113
1114 qreal x = q_xx[column];
1115 qreal y = q_yy[row];
1116 qreal width = q_widths[lastColumn];
1117 qreal height = q_heights[lastRow];
1118
1119 if (columnSpan != 1)
1120 width += q_xx[lastColumn] - x;
1121 if (rowSpan != 1)
1122 height += q_yy[lastRow] - y;
1123
1124 return QRectF(contentsGeometry.x() + x, contentsGeometry.y() + y, width, height);
1125}
1126
1127QSizeF QGridLayoutEngine::sizeHint(const QLayoutStyleInfo &styleInfo, Qt::SizeHint which,
1128 const QSizeF &constraint) const
1129{
1130 QGridLayoutBox sizehint_totalBoxes[NOrientations];
1131
1132 bool sizeHintCalculated = false;
1133
1134 if (hasDynamicConstraint() && rowCount() > 0 && columnCount() > 0) {
1135 if (constraintOrientation() == Qt::Vertical) {
1136 //We have items whose height depends on their width
1137 if (constraint.width() >= 0) {
1138 if (q_cachedDataForStyleInfo != styleInfo)
1139 ensureColumnAndRowData(&q_columnData, &sizehint_totalBoxes[Hor], styleInfo, NULL, NULL, Qt::Horizontal);
1140 else
1141 sizehint_totalBoxes[Hor] = q_totalBoxes[Hor];
1142 QVector<qreal> sizehint_xx;
1143 QVector<qreal> sizehint_widths;
1144
1145 sizehint_xx.resize(columnCount());
1146 sizehint_widths.resize(columnCount());
1147 qreal width = constraint.width();
1148 //Calculate column widths and positions, and put results in q_xx.data() and q_widths.data() so that we can use this information as
1149 //constraints to find the row heights
1150 q_columnData.calculateGeometries(0, columnCount(), width, sizehint_xx.data(), sizehint_widths.data(),
1151 0, sizehint_totalBoxes[Hor], q_infos[Hor]);
1152 ensureColumnAndRowData(&q_rowData, &sizehint_totalBoxes[Ver], styleInfo, sizehint_xx.data(), sizehint_widths.data(), Qt::Vertical);
1153 sizeHintCalculated = true;
1154 }
1155 } else {
1156 if (constraint.height() >= 0) {
1157 //We have items whose width depends on their height
1158 ensureColumnAndRowData(&q_rowData, &sizehint_totalBoxes[Ver], styleInfo, NULL, NULL, Qt::Vertical);
1159 QVector<qreal> sizehint_yy;
1160 QVector<qreal> sizehint_heights;
1161
1162 sizehint_yy.resize(rowCount());
1163 sizehint_heights.resize(rowCount());
1164 qreal height = constraint.height();
1165 //Calculate row heights and positions, and put results in q_yy.data() and q_heights.data() so that we can use this information as
1166 //constraints to find the column widths
1167 q_rowData.calculateGeometries(0, rowCount(), height, sizehint_yy.data(), sizehint_heights.data(),
1168 0, sizehint_totalBoxes[Ver], q_infos[Ver]);
1169 ensureColumnAndRowData(&q_columnData, &sizehint_totalBoxes[Hor], styleInfo, sizehint_yy.data(), sizehint_heights.data(), Qt::Vertical);
1170 sizeHintCalculated = true;
1171 }
1172 }
1173 }
1174
1175 if (!sizeHintCalculated) {
1176 //No items with height for width, so it doesn't matter which order we do these in
1177 if (q_cachedDataForStyleInfo != styleInfo) {
1178 ensureColumnAndRowData(&q_columnData, &sizehint_totalBoxes[Hor], styleInfo, NULL, NULL, Qt::Horizontal);
1179 ensureColumnAndRowData(&q_rowData, &sizehint_totalBoxes[Ver], styleInfo, NULL, NULL, Qt::Vertical);
1180 } else {
1181 sizehint_totalBoxes[Hor] = q_totalBoxes[Hor];
1182 sizehint_totalBoxes[Ver] = q_totalBoxes[Ver];
1183 }
1184 }
1185
1186 switch (which) {
1187 case Qt::MinimumSize:
1188 return QSizeF(sizehint_totalBoxes[Hor].q_minimumSize, sizehint_totalBoxes[Ver].q_minimumSize);
1189 case Qt::PreferredSize:
1190 return QSizeF(sizehint_totalBoxes[Hor].q_preferredSize, sizehint_totalBoxes[Ver].q_preferredSize);
1191 case Qt::MaximumSize:
1192 return QSizeF(sizehint_totalBoxes[Hor].q_maximumSize, sizehint_totalBoxes[Ver].q_maximumSize);
1193 case Qt::MinimumDescent:
1194 return QSizeF(-1.0, sizehint_totalBoxes[Hor].q_minimumDescent); // ### doesn't work
1195 default:
1196 break;
1197 }
1198 return QSizeF();
1199}
1200
1201QSizePolicy::ControlTypes QGridLayoutEngine::controlTypes(LayoutSide side) const
1202{
1203 Qt::Orientation orientation = (side == Top || side == Bottom) ? Qt::Vertical : Qt::Horizontal;
1204 int row = (side == Top || side == Left) ? effectiveFirstRow(orientation)
1205 : effectiveLastRow(orientation);
1206 QSizePolicy::ControlTypes result = 0;
1207
1208 for (int column = columnCount(orientation) - 1; column >= 0; --column) {
1209 if (QGridLayoutItem *item = itemAt(row, column, orientation))
1210 result |= item->controlTypes(side);
1211 }
1212 return result;
1213}
1214
1215void QGridLayoutEngine::transpose()
1216{
1217 invalidate();
1218
1219 for (int i = q_items.count() - 1; i >= 0; --i)
1220 q_items.at(i)->transpose();
1221
1222 qSwap(q_defaultSpacings[Hor], q_defaultSpacings[Ver]);
1223 qSwap(q_infos[Hor], q_infos[Ver]);
1224
1225 regenerateGrid();
1226}
1227
1228void QGridLayoutEngine::setVisualDirection(Qt::LayoutDirection direction)
1229{
1230 m_visualDirection = direction;
1231}
1232
1233Qt::LayoutDirection QGridLayoutEngine::visualDirection() const
1234{
1235 return m_visualDirection;
1236}
1237
1238#ifdef QT_DEBUG
1239void QGridLayoutEngine::dump(int indent) const
1240{
1241 qDebug("%*sEngine", indent, "");
1242
1243 qDebug("%*s Items (%d)", indent, "", q_items.count());
1244 int i;
1245 for (i = 0; i < q_items.count(); ++i)
1246 q_items.at(i)->dump(indent + 2);
1247
1248 qDebug("%*s Grid (%d x %d)", indent, "", internalGridRowCount(),
1249 internalGridColumnCount());
1250 for (int row = 0; row < internalGridRowCount(); ++row) {
1251 QString message = QLatin1String("[ ");
1252 for (int column = 0; column < internalGridColumnCount(); ++column) {
1253 message += QString::number(q_items.indexOf(itemAt(row, column))).rightJustified(3);
1254 message += QLatin1Char(' ');
1255 }
1256 message += QLatin1Char(']');
1257 qDebug("%*s %s", indent, "", qPrintable(message));
1258 }
1259
1260 if (q_defaultSpacings[Hor].value() >= 0.0 || q_defaultSpacings[Ver].value() >= 0.0)
1261 qDebug("%*s Default spacings: %g %g", indent, "", q_defaultSpacings[Hor].value(),
1262 q_defaultSpacings[Ver].value());
1263
1264 qDebug("%*s Column and row info", indent, "");
1265 q_infos[Hor].dump(indent + 2);
1266 q_infos[Ver].dump(indent + 2);
1267
1268 qDebug("%*s Column and row data", indent, "");
1269 q_columnData.dump(indent + 2);
1270 q_rowData.dump(indent + 2);
1271
1272 qDebug("%*s Geometries output", indent, "");
1273 QVector<qreal> *cellPos = &q_yy;
1274 for (int pass = 0; pass < 2; ++pass) {
1275 QString message;
1276 for (i = 0; i < cellPos->count(); ++i) {
1277 message += QLatin1String((message.isEmpty() ? "[" : ", "));
1278 message += QString::number(cellPos->at(i));
1279 }
1280 message += QLatin1Char(']');
1281 qDebug("%*s %s %s", indent, "", (pass == 0 ? "rows:" : "columns:"), qPrintable(message));
1282 cellPos = &q_xx;
1283 }
1284}
1285#endif
1286
1287void QGridLayoutEngine::maybeExpandGrid(int row, int column, Qt::Orientation orientation)
1288{
1289 invalidate(); // ### move out of here?
1290
1291 if (orientation == Qt::Horizontal)
1292 qSwap(row, column);
1293
1294 if (row < rowCount() && column < columnCount())
1295 return;
1296
1297 int oldGridRowCount = internalGridRowCount();
1298 int oldGridColumnCount = internalGridColumnCount();
1299
1300 q_infos[Ver].count = qMax(row + 1, rowCount());
1301 q_infos[Hor].count = qMax(column + 1, columnCount());
1302
1303 int newGridRowCount = internalGridRowCount();
1304 int newGridColumnCount = internalGridColumnCount();
1305
1306 int newGridSize = newGridRowCount * newGridColumnCount;
1307 if (newGridSize != q_grid.count()) {
1308 q_grid.resize(newGridSize);
1309
1310 if (newGridColumnCount != oldGridColumnCount) {
1311 for (int i = oldGridRowCount - 1; i >= 1; --i) {
1312 for (int j = oldGridColumnCount - 1; j >= 0; --j) {
1313 int oldIndex = (i * oldGridColumnCount) + j;
1314 int newIndex = (i * newGridColumnCount) + j;
1315
1316 Q_ASSERT(newIndex > oldIndex);
1317 q_grid[newIndex] = q_grid[oldIndex];
1318 q_grid[oldIndex] = 0;
1319 }
1320 }
1321 }
1322 }
1323}
1324
1325void QGridLayoutEngine::regenerateGrid()
1326{
1327 q_grid.fill(0);
1328
1329 for (int i = q_items.count() - 1; i >= 0; --i) {
1330 QGridLayoutItem *item = q_items.at(i);
1331
1332 for (int j = item->firstRow(); j <= item->lastRow(); ++j) {
1333 for (int k = item->firstColumn(); k <= item->lastColumn(); ++k) {
1334 setItemAt(j, k, item);
1335 }
1336 }
1337 }
1338}
1339
1340void QGridLayoutEngine::setItemAt(int row, int column, QGridLayoutItem *item)
1341{
1342 Q_ASSERT(row >= 0 && row < rowCount());
1343 Q_ASSERT(column >= 0 && column < columnCount());
1344 q_grid[(row * internalGridColumnCount()) + column] = item;
1345}
1346
1347void QGridLayoutEngine::insertOrRemoveRows(int row, int delta, Qt::Orientation orientation)
1348{
1349 int oldRowCount = rowCount(orientation);
1350 Q_ASSERT(uint(row) <= uint(oldRowCount));
1351
1352 invalidate();
1353
1354 // appending rows (or columns) is easy
1355 if (row == oldRowCount && delta > 0) {
1356 maybeExpandGrid(oldRowCount + delta - 1, -1, orientation);
1357 return;
1358 }
1359
1360 q_infos[orientation == Qt::Vertical].insertOrRemoveRows(row, delta);
1361
1362 for (int i = q_items.count() - 1; i >= 0; --i)
1363 q_items.at(i)->insertOrRemoveRows(row, delta, orientation);
1364
1365 q_grid.resize(internalGridRowCount() * internalGridColumnCount());
1366 regenerateGrid();
1367}
1368
1369void QGridLayoutEngine::fillRowData(QGridLayoutRowData *rowData, const QLayoutStyleInfo &styleInfo,
1370 qreal *colPositions, qreal *colSizes,
1371 Qt::Orientation orientation) const
1372{
1373 const int ButtonMask = QSizePolicy::ButtonBox | QSizePolicy::PushButton;
1374 const QGridLayoutRowInfo &rowInfo = q_infos[orientation == Qt::Vertical];
1375 const QGridLayoutRowInfo &columnInfo = q_infos[orientation == Qt::Horizontal];
1376 LayoutSide top = (orientation == Qt::Vertical) ? Top : Left;
1377 LayoutSide bottom = (orientation == Qt::Vertical) ? Bottom : Right;
1378
1379 QStyle *style = styleInfo.style();
1380 QStyleOption option;
1381 option.initFrom(styleInfo.widget());
1382
1383 const QLayoutParameter<qreal> &defaultSpacing = q_defaultSpacings[orientation == Qt::Vertical];
1384 qreal innerSpacing = 0.0;
1385 if (style)
1386 innerSpacing = (qreal)style->pixelMetric(orientation == Qt::Vertical ? QStyle::PM_LayoutVerticalSpacing
1387 : QStyle::PM_LayoutHorizontalSpacing,
1388 &option, styleInfo.widget());
1389 if (innerSpacing >= 0.0)
1390 defaultSpacing.setCachedValue(innerSpacing);
1391
1392 for (int row = 0; row < rowInfo.count; ++row) {
1393 bool rowIsEmpty = true;
1394 bool rowIsIdenticalToPrevious = (row > 0);
1395
1396 for (int column = 0; column < columnInfo.count; ++column) {
1397 QGridLayoutItem *item = itemAt(row, column, orientation);
1398
1399 if (rowIsIdenticalToPrevious && item != itemAt(row - 1, column, orientation))
1400 rowIsIdenticalToPrevious = false;
1401
1402 if (item)
1403 rowIsEmpty = false;
1404 }
1405
1406 if ((rowIsEmpty || rowIsIdenticalToPrevious)
1407 && rowInfo.spacings.value(row).isDefault()
1408 && rowInfo.stretches.value(row).isDefault()
1409 && rowInfo.boxes.value(row) == QGridLayoutBox())
1410 rowData->ignore.setBit(row, true);
1411
1412 if (rowInfo.spacings.value(row).isUser()) {
1413 rowData->spacings[row] = rowInfo.spacings.at(row).value();
1414 } else if (!defaultSpacing.isDefault()) {
1415 rowData->spacings[row] = defaultSpacing.value();
1416 }
1417
1418 rowData->stretches[row] = rowInfo.stretches.value(row).value();
1419 }
1420
1421 struct RowAdHocData {
1422 int q_row;
1423 unsigned int q_hasButtons : 8;
1424 unsigned int q_hasNonButtons : 8;
1425
1426 inline RowAdHocData() : q_row(-1), q_hasButtons(false), q_hasNonButtons(false) {}
1427 inline void init(int row) {
1428 this->q_row = row;
1429 q_hasButtons = false;
1430 q_hasNonButtons = false;
1431 }
1432 inline bool hasOnlyButtons() const { return q_hasButtons && !q_hasNonButtons; }
1433 inline bool hasOnlyNonButtons() const { return q_hasNonButtons && !q_hasButtons; }
1434 };
1435 RowAdHocData lastRowAdHocData;
1436 RowAdHocData nextToLastRowAdHocData;
1437 RowAdHocData nextToNextToLastRowAdHocData;
1438
1439 rowData->hasIgnoreFlag = false;
1440 for (int row = 0; row < rowInfo.count; ++row) {
1441 if (rowData->ignore.testBit(row))
1442 continue;
1443
1444 QGridLayoutBox &rowBox = rowData->boxes[row];
1445 if (option.state & QStyle::State_Window) {
1446 nextToNextToLastRowAdHocData = nextToLastRowAdHocData;
1447 nextToLastRowAdHocData = lastRowAdHocData;
1448 lastRowAdHocData.init(row);
1449 }
1450
1451 bool userRowStretch = rowInfo.stretches.value(row).isUser();
1452 int &rowStretch = rowData->stretches[row];
1453
1454 bool hasIgnoreFlag = true;
1455 for (int column = 0; column < columnInfo.count; ++column) {
1456 QGridLayoutItem *item = itemAt(row, column, orientation);
1457 if (item) {
1458 int itemRow = item->firstRow(orientation);
1459 int itemColumn = item->firstColumn(orientation);
1460
1461 if (itemRow == row && itemColumn == column) {
1462 int itemStretch = item->stretchFactor(orientation);
1463 if (!(item->sizePolicy(orientation) & QSizePolicy::IgnoreFlag))
1464 hasIgnoreFlag = false;
1465 int itemRowSpan = item->rowSpan(orientation);
1466
1467 int effectiveRowSpan = 1;
1468 for (int i = 1; i < itemRowSpan; ++i) {
1469 if (!rowData->ignore.testBit(i))
1470 ++effectiveRowSpan;
1471 }
1472
1473 QGridLayoutBox *box;
1474 if (effectiveRowSpan == 1) {
1475 box = &rowBox;
1476 if (!userRowStretch && itemStretch != 0)
1477 rowStretch = qMax(rowStretch, itemStretch);
1478 } else {
1479 QGridLayoutMultiCellData &multiCell =
1480 rowData->multiCellMap[qMakePair(row, effectiveRowSpan)];
1481 box = &multiCell.q_box;
1482 multiCell.q_stretch = itemStretch;
1483 }
1484 // Items with constraints need to be passed the constraint
1485 if (colSizes && colPositions && item->hasDynamicConstraint() && orientation == item->dynamicConstraintOrientation()) {
1486 /* Get the width of the item by summing up the widths of the columns that it spans.
1487 * We need to have already calculated the widths of the columns by calling
1488 * q_columns->calculateGeometries() before hand and passing the value in the colSizes
1489 * and colPositions parameters.
1490 * The variable name is still colSizes even when it actually has the row sizes
1491 */
1492 qreal length = colSizes[item->lastColumn(orientation)];
1493 if (item->columnSpan(orientation) != 1)
1494 length += colPositions[item->lastColumn(orientation)] - colPositions[item->firstColumn(orientation)];
1495 box->combine(item->box(orientation, length));
1496 } else {
1497 box->combine(item->box(orientation));
1498 }
1499
1500 if (effectiveRowSpan == 1) {
1501 QSizePolicy::ControlTypes controls = item->controlTypes(top);
1502 if (controls & ButtonMask)
1503 lastRowAdHocData.q_hasButtons = true;
1504 if (controls & ~ButtonMask)
1505 lastRowAdHocData.q_hasNonButtons = true;
1506 }
1507 }
1508 }
1509 }
1510 if (row < rowInfo.boxes.count()) {
1511 QGridLayoutBox rowBoxInfo = rowInfo.boxes.at(row);
1512 rowBoxInfo.normalize();
1513 rowBox.q_minimumSize = qMax(rowBox.q_minimumSize, rowBoxInfo.q_minimumSize);
1514 rowBox.q_maximumSize = qMax(rowBox.q_minimumSize,
1515 (rowBoxInfo.q_maximumSize != FLT_MAX ?
1516 rowBoxInfo.q_maximumSize : rowBox.q_maximumSize));
1517 rowBox.q_preferredSize = qBound(rowBox.q_minimumSize,
1518 qMax(rowBox.q_preferredSize, rowBoxInfo.q_preferredSize),
1519 rowBox.q_maximumSize);
1520 }
1521 if (hasIgnoreFlag)
1522 rowData->hasIgnoreFlag = true;
1523 }
1524
1525 /*
1526 Heuristic: Detect button boxes that don't use QSizePolicy::ButtonBox.
1527 This is somewhat ad hoc but it usually does the trick.
1528 */
1529 bool lastRowIsButtonBox = (lastRowAdHocData.hasOnlyButtons()
1530 && nextToLastRowAdHocData.hasOnlyNonButtons());
1531 bool lastTwoRowsIsButtonBox = (lastRowAdHocData.hasOnlyButtons()
1532 && nextToLastRowAdHocData.hasOnlyButtons()
1533 && nextToNextToLastRowAdHocData.hasOnlyNonButtons()
1534 && orientation == Qt::Vertical);
1535
1536 if (defaultSpacing.isDefault()) {
1537 int prevRow = -1;
1538 for (int row = 0; row < rowInfo.count; ++row) {
1539 if (rowData->ignore.testBit(row))
1540 continue;
1541
1542 if (prevRow != -1 && !rowInfo.spacings.value(prevRow).isUser()) {
1543 qreal &rowSpacing = rowData->spacings[prevRow];
1544 for (int column = 0; column < columnInfo.count; ++column) {
1545 QGridLayoutItem *item1 = itemAt(prevRow, column, orientation);
1546 QGridLayoutItem *item2 = itemAt(row, column, orientation);
1547
1548 if (item1 && item2 && item1 != item2) {
1549 QSizePolicy::ControlTypes controls1 = item1->controlTypes(bottom);
1550 QSizePolicy::ControlTypes controls2 = item2->controlTypes(top);
1551
1552 if (controls2 & QSizePolicy::PushButton) {
1553 if ((row == nextToLastRowAdHocData.q_row && lastTwoRowsIsButtonBox)
1554 || (row == lastRowAdHocData.q_row && lastRowIsButtonBox)) {
1555 controls2 &= ~QSizePolicy::PushButton;
1556 controls2 |= QSizePolicy::ButtonBox;
1557 }
1558 }
1559
1560 qreal spacing = style->combinedLayoutSpacing(controls1, controls2,
1561 orientation, &option,
1562 styleInfo.widget());
1563 if (orientation == Qt::Horizontal) {
1564 qreal width1 = rowData->boxes.at(prevRow).q_minimumSize;
1565 qreal width2 = rowData->boxes.at(row).q_minimumSize;
1566 QRectF rect1 = item1->geometryWithin(0.0, 0.0, width1, FLT_MAX, -1.0);
1567 QRectF rect2 = item2->geometryWithin(0.0, 0.0, width2, FLT_MAX, -1.0);
1568 spacing -= (width1 - (rect1.x() + rect1.width())) + rect2.x();
1569 } else {
1570 const QGridLayoutBox &box1 = rowData->boxes.at(prevRow);
1571 const QGridLayoutBox &box2 = rowData->boxes.at(row);
1572 qreal height1 = box1.q_minimumSize;
1573 qreal height2 = box2.q_minimumSize;
1574 qreal rowDescent1 = fixedDescent(box1.q_minimumDescent,
1575 box1.q_minimumAscent, height1);
1576 qreal rowDescent2 = fixedDescent(box2.q_minimumDescent,
1577 box2.q_minimumAscent, height2);
1578 QRectF rect1 = item1->geometryWithin(0.0, 0.0, FLT_MAX, height1,
1579 rowDescent1);
1580 QRectF rect2 = item2->geometryWithin(0.0, 0.0, FLT_MAX, height2,
1581 rowDescent2);
1582 spacing -= (height1 - (rect1.y() + rect1.height())) + rect2.y();
1583 }
1584 rowSpacing = qMax(spacing, rowSpacing);
1585 }
1586 }
1587 }
1588 prevRow = row;
1589 }
1590 } else if (lastRowIsButtonBox || lastTwoRowsIsButtonBox) {
1591 /*
1592 Even for styles that define a uniform spacing, we cheat a
1593 bit and use the window margin as the spacing. This
1594 significantly improves the look of dialogs.
1595 */
1596 int prevRow = lastRowIsButtonBox ? nextToLastRowAdHocData.q_row
1597 : nextToNextToLastRowAdHocData.q_row;
1598 if (!defaultSpacing.isUser() && !rowInfo.spacings.value(prevRow).isUser()) {
1599 qreal windowMargin = style->pixelMetric(orientation == Qt::Vertical
1600 ? QStyle::PM_LayoutBottomMargin
1601 : QStyle::PM_LayoutRightMargin,
1602 &option, styleInfo.widget());
1603
1604 qreal &rowSpacing = rowData->spacings[prevRow];
1605 rowSpacing = qMax(windowMargin, rowSpacing);
1606 }
1607 }
1608}
1609
1610void QGridLayoutEngine::ensureEffectiveFirstAndLastRows() const
1611{
1612 if (q_cachedEffectiveFirstRows[Hor] == -1 && !q_items.isEmpty()) {
1613 int rowCount = this->rowCount();
1614 int columnCount = this->columnCount();
1615
1616 q_cachedEffectiveFirstRows[Ver] = rowCount;
1617 q_cachedEffectiveFirstRows[Hor] = columnCount;
1618 q_cachedEffectiveLastRows[Ver] = -1;
1619 q_cachedEffectiveLastRows[Hor] = -1;
1620
1621 for (int i = q_items.count() - 1; i >= 0; --i) {
1622 const QGridLayoutItem *item = q_items.at(i);
1623
1624 for (int j = 0; j < NOrientations; ++j) {
1625 Qt::Orientation orientation = (j == Hor) ? Qt::Horizontal : Qt::Vertical;
1626 if (item->firstRow(orientation) < q_cachedEffectiveFirstRows[j])
1627 q_cachedEffectiveFirstRows[j] = item->firstRow(orientation);
1628 if (item->lastRow(orientation) > q_cachedEffectiveLastRows[j])
1629 q_cachedEffectiveLastRows[j] = item->lastRow(orientation);
1630 }
1631 }
1632 }
1633}
1634
1635void QGridLayoutEngine::ensureColumnAndRowData(QGridLayoutRowData *rowData, QGridLayoutBox *totalBox,
1636 const QLayoutStyleInfo &styleInfo,
1637 qreal *colPositions, qreal *colSizes,
1638 Qt::Orientation orientation) const
1639{
1640 rowData->reset(rowCount(orientation));
1641 fillRowData(rowData, styleInfo, colPositions, colSizes, orientation);
1642 const QGridLayoutRowInfo &rowInfo = q_infos[orientation == Qt::Vertical];
1643 rowData->distributeMultiCells(rowInfo);
1644 *totalBox = rowData->totalBox(0, rowCount(orientation));
1645 //We have items whose width depends on their height
1646}
1647
1648/**
1649 returns false if the layout has contradicting constraints (i.e. some items with a horizontal
1650 constraint and other items with a vertical constraint)
1651 */
1652bool QGridLayoutEngine::ensureDynamicConstraint() const
1653{
1654 if (q_cachedConstraintOrientation == UnknownConstraint) {
1655 for (int i = q_items.count() - 1; i >= 0; --i) {
1656 QGridLayoutItem *item = q_items.at(i);
1657 if (item->hasDynamicConstraint()) {
1658 Qt::Orientation itemConstraintOrientation = item->dynamicConstraintOrientation();
1659 if (q_cachedConstraintOrientation == UnknownConstraint) {
1660 q_cachedConstraintOrientation = itemConstraintOrientation;
1661 } else if (q_cachedConstraintOrientation != itemConstraintOrientation) {
1662 q_cachedConstraintOrientation = UnfeasibleConstraint;
1663 qWarning("QGridLayoutEngine: Unfeasible, cannot mix horizontal and"
1664 " vertical constraint in the same layout");
1665 return false;
1666 }
1667 }
1668 }
1669 if (q_cachedConstraintOrientation == UnknownConstraint)
1670 q_cachedConstraintOrientation = NoConstraint;
1671 }
1672 return true;
1673}
1674
1675bool QGridLayoutEngine::hasDynamicConstraint() const
1676{
1677 if (!ensureDynamicConstraint())
1678 return false;
1679 return q_cachedConstraintOrientation != NoConstraint;
1680}
1681
1682/*
1683 * return value is only valid if hasConstraint() returns true
1684 */
1685Qt::Orientation QGridLayoutEngine::constraintOrientation() const
1686{
1687 (void)ensureDynamicConstraint();
1688 return (Qt::Orientation)q_cachedConstraintOrientation;
1689}
1690
1691void QGridLayoutEngine::ensureGeometries(const QLayoutStyleInfo &styleInfo,
1692 const QSizeF &size) const
1693{
1694 if (q_cachedDataForStyleInfo == styleInfo && q_cachedSize == size)
1695 return;
1696
1697 q_cachedDataForStyleInfo = styleInfo;
1698 q_cachedSize = size;
1699
1700 q_xx.resize(columnCount());
1701 q_widths.resize(columnCount());
1702 q_yy.resize(rowCount());
1703 q_heights.resize(rowCount());
1704 q_descents.resize(rowCount());
1705
1706 if (constraintOrientation() != Qt::Horizontal) {
1707 //We might have items whose width depends on their height
1708 ensureColumnAndRowData(&q_columnData, &q_totalBoxes[Hor], styleInfo, NULL, NULL, Qt::Horizontal);
1709 //Calculate column widths and positions, and put results in q_xx.data() and q_widths.data() so that we can use this information as
1710 //constraints to find the row heights
1711 q_columnData.calculateGeometries(0, columnCount(), size.width(), q_xx.data(), q_widths.data(),
1712 0, q_totalBoxes[Hor], q_infos[Hor] );
1713 ensureColumnAndRowData(&q_rowData, &q_totalBoxes[Ver], styleInfo, q_xx.data(), q_widths.data(), Qt::Vertical);
1714 //Calculate row heights and positions, and put results in q_yy.data() and q_heights.data()
1715 q_rowData.calculateGeometries(0, rowCount(), size.height(), q_yy.data(), q_heights.data(),
1716 q_descents.data(), q_totalBoxes[Ver], q_infos[Ver]);
1717 } else {
1718 //We have items whose height depends on their width
1719 ensureColumnAndRowData(&q_rowData, &q_totalBoxes[Ver], styleInfo, NULL, NULL, Qt::Vertical);
1720 //Calculate row heights and positions, and put results in q_yy.data() and q_heights.data() so that we can use this information as
1721 //constraints to find the column widths
1722 q_rowData.calculateGeometries(0, rowCount(), size.height(), q_yy.data(), q_heights.data(),
1723 q_descents.data(), q_totalBoxes[Ver], q_infos[Ver]);
1724 ensureColumnAndRowData(&q_columnData, &q_totalBoxes[Hor], styleInfo, q_yy.data(), q_heights.data(), Qt::Horizontal);
1725 //Calculate row heights and positions, and put results in q_yy.data() and q_heights.data()
1726 q_columnData.calculateGeometries(0, columnCount(), size.width(), q_xx.data(), q_widths.data(),
1727 0, q_totalBoxes[Hor], q_infos[Hor]);
1728 }
1729}
1730
1731QT_END_NAMESPACE
1732
1733#endif //QT_NO_GRAPHICSVIEW
Note: See TracBrowser for help on using the repository browser.