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

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

trunk: Merged in qt 4.6.2 sources.

File size: 55.0 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
4** All rights reserved.
5** Contact: Nokia Corporation (qt-info@nokia.com)
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 qt-info@nokia.com.
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()
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);
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 stretches[start + k] = qMax(stretches[start + k], stretch);
198 }
199 }
200 multiCellMap.clear();
201}
202
203void QGridLayoutRowData::calculateGeometries(int start, int end, qreal targetSize, qreal *positions,
204 qreal *sizes, qreal *descents,
205 const QGridLayoutBox &totalBox)
206{
207 Q_ASSERT(end > start);
208
209 targetSize = qBound(totalBox.q_minimumSize, targetSize, totalBox.q_maximumSize);
210
211 int n = end - start;
212 QVarLengthArray<qreal> newSizes(n);
213 QVarLengthArray<qreal> factors(n);
214 qreal sumFactors = 0.0;
215 int sumStretches = 0;
216 qreal sumAvailable;
217
218 for (int i = 0; i < n; ++i) {
219 if (stretches[start + i] > 0)
220 sumStretches += stretches[start + i];
221 }
222
223 if (targetSize < totalBox.q_preferredSize) {
224 stealBox(start, end, MinimumSize, positions, sizes);
225
226 sumAvailable = targetSize - totalBox.q_minimumSize;
227 if (sumAvailable > 0.0) {
228 qreal sumDesired = totalBox.q_preferredSize - totalBox.q_minimumSize;
229
230 for (int i = 0; i < n; ++i) {
231 if (ignore.testBit(start + i)) {
232 factors[i] = 0.0;
233 continue;
234 }
235
236 const QGridLayoutBox &box = boxes.at(start + i);
237 qreal desired = box.q_preferredSize - box.q_minimumSize;
238 factors[i] = growthFactorBelowPreferredSize(desired, sumAvailable, sumDesired);
239 sumFactors += factors[i];
240 }
241
242 for (int i = 0; i < n; ++i) {
243 Q_ASSERT(sumFactors > 0.0);
244 qreal delta = sumAvailable * factors[i] / sumFactors;
245 newSizes[i] = sizes[i] + delta;
246 }
247 }
248 } else {
249 stealBox(start, end, PreferredSize, positions, sizes);
250
251 sumAvailable = targetSize - totalBox.q_preferredSize;
252 if (sumAvailable > 0.0) {
253 bool somethingHasAMaximumSize = false;
254
255 qreal sumPreferredSizes = 0.0;
256 for (int i = 0; i < n; ++i)
257 sumPreferredSizes += sizes[i];
258
259 for (int i = 0; i < n; ++i) {
260 if (ignore.testBit(start + i)) {
261 newSizes[i] = 0.0;
262 factors[i] = 0.0;
263 continue;
264 }
265
266 const QGridLayoutBox &box = boxes.at(start + i);
267 qreal desired = box.q_maximumSize - box.q_preferredSize;
268 if (desired == 0.0) {
269 newSizes[i] = sizes[i];
270 factors[i] = 0.0;
271 } else {
272 Q_ASSERT(desired > 0.0);
273
274 int stretch = stretches[start + i];
275 if (sumStretches == 0) {
276 if (hasIgnoreFlag) {
277 factors[i] = (stretch < 0) ? 1.0 : 0.0;
278 } else {
279 factors[i] = (stretch < 0) ? sizes[i] : 0.0;
280 }
281 } else if (stretch == sumStretches) {
282 factors[i] = 1.0;
283 } else if (stretch <= 0) {
284 factors[i] = 0.0;
285 } else {
286 qreal ultimatePreferredSize;
287 qreal ultimateSumPreferredSizes;
288 qreal x = ((stretch * sumPreferredSizes)
289 - (sumStretches * box.q_preferredSize))
290 / (sumStretches - stretch);
291 if (x >= 0.0) {
292 ultimatePreferredSize = box.q_preferredSize + x;
293 ultimateSumPreferredSizes = sumPreferredSizes + x;
294 } else {
295 ultimatePreferredSize = box.q_preferredSize;
296 ultimateSumPreferredSizes = (sumStretches * box.q_preferredSize)
297 / stretch;
298 }
299
300 /*
301 We multiply these by 1.5 to give some space for a smooth transition
302 (at the expense of the stretch factors, which are not fully respected
303 during the transition).
304 */
305 ultimatePreferredSize = ultimatePreferredSize * 3 / 2;
306 ultimateSumPreferredSizes = ultimateSumPreferredSizes * 3 / 2;
307
308 qreal ultimateFactor = (stretch * ultimateSumPreferredSizes
309 / sumStretches)
310 - (box.q_preferredSize);
311 qreal transitionalFactor = sumAvailable
312 * (ultimatePreferredSize - box.q_preferredSize)
313 / (ultimateSumPreferredSizes
314 - sumPreferredSizes);
315
316 qreal alpha = qMin(sumAvailable,
317 ultimateSumPreferredSizes - sumPreferredSizes);
318 qreal beta = ultimateSumPreferredSizes - sumPreferredSizes;
319
320 factors[i] = ((alpha * ultimateFactor)
321 + ((beta - alpha) * transitionalFactor)) / beta;
322 }
323 sumFactors += factors[i];
324 if (desired < sumAvailable)
325 somethingHasAMaximumSize = true;
326
327 newSizes[i] = -1.0;
328 }
329 }
330
331 bool keepGoing = somethingHasAMaximumSize;
332 while (keepGoing) {
333 keepGoing = false;
334
335 for (int i = 0; i < n; ++i) {
336 if (newSizes[i] >= 0.0)
337 continue;
338
339 const QGridLayoutBox &box = boxes.at(start + i);
340 qreal avail = sumAvailable * factors[i] / sumFactors;
341 if (sizes[i] + avail >= box.q_maximumSize) {
342 newSizes[i] = box.q_maximumSize;
343 sumAvailable -= box.q_maximumSize - sizes[i];
344 sumFactors -= factors[i];
345 keepGoing = (sumAvailable > 0.0);
346 if (!keepGoing)
347 break;
348 }
349 }
350 }
351
352 for (int i = 0; i < n; ++i) {
353 if (newSizes[i] < 0.0) {
354 qreal delta = (sumFactors == 0.0) ? 0.0
355 : sumAvailable * factors[i] / sumFactors;
356 newSizes[i] = sizes[i] + delta;
357 }
358 }
359 }
360 }
361
362 if (sumAvailable > 0) {
363 qreal offset = 0;
364 for (int i = 0; i < n; ++i) {
365 qreal delta = newSizes[i] - sizes[i];
366 positions[i] += offset;
367 sizes[i] += delta;
368 offset += delta;
369 }
370
371#if 0 // some "pixel allocation"
372 int surplus = targetSize - (positions[n - 1] + sizes[n - 1]);
373 Q_ASSERT(surplus >= 0 && surplus <= n);
374
375 int prevSurplus = -1;
376 while (surplus > 0 && surplus != prevSurplus) {
377 prevSurplus = surplus;
378
379 int offset = 0;
380 for (int i = 0; i < n; ++i) {
381 const QGridLayoutBox &box = boxes.at(start + i);
382 int delta = (!ignore.testBit(start + i) && surplus > 0
383 && factors[i] > 0 && sizes[i] < box.q_maximumSize)
384 ? 1 : 0;
385
386 positions[i] += offset;
387 sizes[i] += delta;
388 offset += delta;
389 surplus -= delta;
390 }
391 }
392 Q_ASSERT(surplus == 0);
393#endif
394 }
395
396 if (descents) {
397 for (int i = 0; i < n; ++i) {
398 if (ignore.testBit(start + i))
399 continue;
400 const QGridLayoutBox &box = boxes.at(start + i);
401 descents[i] = fixedDescent(box.q_minimumDescent, box.q_minimumAscent, sizes[i]);
402 }
403 }
404}
405
406QGridLayoutBox QGridLayoutRowData::totalBox(int start, int end) const
407{
408 QGridLayoutBox result;
409 if (start < end) {
410 result.q_maximumSize = 0.0;
411 qreal nextSpacing = 0.0;
412 for (int i = start; i < end; ++i) {
413 result.add(boxes.at(i), stretches.at(i), nextSpacing);
414 nextSpacing = spacings.at(i);
415 }
416 }
417 return result;
418}
419
420void QGridLayoutRowData::stealBox(int start, int end, int which, qreal *positions, qreal *sizes)
421{
422 qreal offset = 0.0;
423 qreal nextSpacing = 0.0;
424
425 for (int i = start; i < end; ++i) {
426 qreal avail = 0.0;
427
428 if (!ignore.testBit(i)) {
429 const QGridLayoutBox &box = boxes.at(i);
430 avail = box.q_sizes(which);
431 offset += nextSpacing;
432 nextSpacing = spacings.at(i);
433 }
434
435 *positions++ = offset;
436 *sizes++ = avail;
437 offset += avail;
438 }
439}
440
441#ifdef QT_DEBUG
442void QGridLayoutRowData::dump(int indent) const
443{
444 qDebug("%*sData", indent, "");
445
446 for (int i = 0; i < ignore.count(); ++i) {
447 qDebug("%*s Row %d (stretch %d, spacing %g)", indent, "", i, stretches.at(i),
448 spacings.at(i));
449 if (ignore.testBit(i))
450 qDebug("%*s Ignored", indent, "");
451 boxes.at(i).dump(indent + 2);
452 }
453
454 MultiCellMap::const_iterator it = multiCellMap.constBegin();
455 while (it != multiCellMap.constEnd()) {
456 qDebug("%*s Multi-cell entry <%d, %d> (stretch %d)", indent, "", it.key().first,
457 it.key().second, it.value().q_stretch);
458 it.value().q_box.dump(indent + 2);
459 }
460}
461#endif
462
463QGridLayoutItem::QGridLayoutItem(QGridLayoutEngine *engine, QGraphicsLayoutItem *layoutItem,
464 int row, int column, int rowSpan, int columnSpan,
465 Qt::Alignment alignment, int itemAtIndex)
466 : q_engine(engine), q_layoutItem(layoutItem), q_alignment(alignment)
467{
468 q_firstRows[Hor] = column;
469 q_firstRows[Ver] = row;
470 q_rowSpans[Hor] = columnSpan;
471 q_rowSpans[Ver] = rowSpan;
472 q_stretches[Hor] = -1;
473 q_stretches[Ver] = -1;
474
475 q_engine->insertItem(this, itemAtIndex);
476}
477
478int QGridLayoutItem::firstRow(Qt::Orientation orientation) const
479{
480 return q_firstRows[orientation == Qt::Vertical];
481}
482
483int QGridLayoutItem::firstColumn(Qt::Orientation orientation) const
484{
485 return q_firstRows[orientation == Qt::Horizontal];
486}
487
488int QGridLayoutItem::lastRow(Qt::Orientation orientation) const
489{
490 return firstRow(orientation) + rowSpan(orientation) - 1;
491}
492
493int QGridLayoutItem::lastColumn(Qt::Orientation orientation) const
494{
495 return firstColumn(orientation) + columnSpan(orientation) - 1;
496}
497
498int QGridLayoutItem::rowSpan(Qt::Orientation orientation) const
499{
500 return q_rowSpans[orientation == Qt::Vertical];
501}
502
503int QGridLayoutItem::columnSpan(Qt::Orientation orientation) const
504{
505 return q_rowSpans[orientation == Qt::Horizontal];
506}
507
508void QGridLayoutItem::setFirstRow(int row, Qt::Orientation orientation)
509{
510 q_firstRows[orientation == Qt::Vertical] = row;
511}
512
513void QGridLayoutItem::setRowSpan(int rowSpan, Qt::Orientation orientation)
514{
515 q_rowSpans[orientation == Qt::Vertical] = rowSpan;
516}
517
518int QGridLayoutItem::stretchFactor(Qt::Orientation orientation) const
519{
520 int stretch = q_stretches[orientation == Qt::Vertical];
521 if (stretch >= 0)
522 return stretch;
523
524 QSizePolicy::Policy policy = sizePolicy(orientation);
525
526 if (policy & QSizePolicy::ExpandFlag) {
527 return 1;
528 } else if (policy & QSizePolicy::GrowFlag) {
529 return -1; // because we max it up
530 } else {
531 return 0;
532 }
533}
534
535void QGridLayoutItem::setStretchFactor(int stretch, Qt::Orientation orientation)
536{
537 Q_ASSERT(stretch >= 0); // ### deal with too big stretches
538 q_stretches[orientation == Qt::Vertical] = stretch;
539}
540
541QSizePolicy::Policy QGridLayoutItem::sizePolicy(Qt::Orientation orientation) const
542{
543 QSizePolicy sizePolicy(q_layoutItem->sizePolicy());
544 return (orientation == Qt::Horizontal) ? sizePolicy.horizontalPolicy()
545 : sizePolicy.verticalPolicy();
546}
547
548QSizePolicy::ControlTypes QGridLayoutItem::controlTypes(LayoutSide /* side */) const
549{
550 return q_layoutItem->sizePolicy().controlType();
551}
552
553QSizeF QGridLayoutItem::sizeHint(Qt::SizeHint which, const QSizeF &constraint) const
554{
555 return q_layoutItem->effectiveSizeHint(which, constraint);
556}
557
558QGridLayoutBox QGridLayoutItem::box(Qt::Orientation orientation, qreal constraint) const
559{
560 QGridLayoutBox result;
561 QSizePolicy::Policy policy = sizePolicy(orientation);
562
563 if (orientation == Qt::Horizontal) {
564 QSizeF constraintSize(-1.0, constraint);
565
566 result.q_preferredSize = sizeHint(Qt::PreferredSize, constraintSize).width();
567
568 if (policy & QSizePolicy::ShrinkFlag) {
569 result.q_minimumSize = sizeHint(Qt::MinimumSize, constraintSize).width();
570 } else {
571 result.q_minimumSize = result.q_preferredSize;
572 }
573
574 if (policy & (QSizePolicy::GrowFlag | QSizePolicy::ExpandFlag)) {
575 result.q_maximumSize = sizeHint(Qt::MaximumSize, constraintSize).width();
576 } else {
577 result.q_maximumSize = result.q_preferredSize;
578 }
579 } else {
580 QSizeF constraintSize(constraint, -1.0);
581
582 result.q_preferredSize = sizeHint(Qt::PreferredSize, constraintSize).height();
583
584 if (policy & QSizePolicy::ShrinkFlag) {
585 result.q_minimumSize = sizeHint(Qt::MinimumSize, constraintSize).height();
586 } else {
587 result.q_minimumSize = result.q_preferredSize;
588 }
589
590 if (policy & (QSizePolicy::GrowFlag | QSizePolicy::ExpandFlag)) {
591 result.q_maximumSize = sizeHint(Qt::MaximumSize, constraintSize).height();
592 } else {
593 result.q_maximumSize = result.q_preferredSize;
594 }
595
596 result.q_minimumDescent = sizeHint(Qt::MinimumDescent, constraintSize).height();
597 if (result.q_minimumDescent >= 0.0)
598 result.q_minimumAscent = result.q_minimumSize - result.q_minimumDescent;
599 }
600 if (policy & QSizePolicy::IgnoreFlag)
601 result.q_preferredSize = result.q_minimumSize;
602
603 return result;
604}
605
606QRectF QGridLayoutItem::geometryWithin(qreal x, qreal y, qreal width, qreal height,
607 qreal rowDescent) const
608{
609 rowDescent = -1.0; // ### This disables the descent
610
611 QGridLayoutBox vBox = box(Qt::Vertical);
612 if (vBox.q_minimumDescent < 0.0 || rowDescent < 0.0) {
613 qreal cellWidth = width;
614 qreal cellHeight = height;
615
616 QSizeF size = effectiveMaxSize().boundedTo(QSizeF(cellWidth, cellHeight));
617 width = size.width();
618 height = size.height();
619
620 Qt::Alignment align = q_engine->effectiveAlignment(this);
621 switch (align & Qt::AlignHorizontal_Mask) {
622 case Qt::AlignHCenter:
623 x += (cellWidth - width)/2;
624 break;
625 case Qt::AlignRight:
626 x += cellWidth - width;
627 break;
628 default:
629 break;
630 }
631 switch (align & Qt::AlignVertical_Mask) {
632 case Qt::AlignVCenter:
633 y += (cellHeight - height)/2;
634 break;
635 case Qt::AlignBottom:
636 y += cellHeight - height;
637 break;
638 default:
639 break;
640 }
641 return QRectF(x, y, width, height);
642 } else {
643 qreal descent = vBox.q_minimumDescent;
644 qreal ascent = vBox.q_minimumSize - descent;
645 return QRectF(x, y + height - rowDescent - ascent, width, ascent + descent);
646 }
647}
648
649void QGridLayoutItem::setGeometry(const QRectF &rect)
650{
651 q_layoutItem->setGeometry(rect);
652}
653
654void QGridLayoutItem::transpose()
655{
656 qSwap(q_firstRows[Hor], q_firstRows[Ver]);
657 qSwap(q_rowSpans[Hor], q_rowSpans[Ver]);
658 qSwap(q_stretches[Hor], q_stretches[Ver]);
659}
660
661void QGridLayoutItem::insertOrRemoveRows(int row, int delta, Qt::Orientation orientation)
662{
663 int oldFirstRow = firstRow(orientation);
664 if (oldFirstRow >= row) {
665 setFirstRow(oldFirstRow + delta, orientation);
666 } else if (lastRow(orientation) >= row) {
667 setRowSpan(rowSpan(orientation) + delta, orientation);
668 }
669}
670/*!
671 \internal
672 returns the effective maximumSize, will take the sizepolicy into
673 consideration. (i.e. if sizepolicy does not have QSizePolicy::Grow, then
674 maxSizeHint will be the preferredSize)
675 Note that effectiveSizeHint does not take sizePolicy into consideration,
676 (since it only evaluates the hints, as the name implies)
677*/
678QSizeF QGridLayoutItem::effectiveMaxSize() const
679{
680 QSizeF size;
681 bool vGrow = (sizePolicy(Qt::Vertical) & QSizePolicy::GrowFlag) == QSizePolicy::GrowFlag;
682 bool hGrow = (sizePolicy(Qt::Horizontal) & QSizePolicy::GrowFlag) == QSizePolicy::GrowFlag;
683 if (!vGrow || !hGrow) {
684 QSizeF pref = layoutItem()->effectiveSizeHint(Qt::PreferredSize);
685 if (!vGrow)
686 size.setHeight(pref.height());
687 if (!hGrow)
688 size.setWidth(pref.width());
689 }
690
691 if (!size.isValid()) {
692 QSizeF maxSize = layoutItem()->effectiveSizeHint(Qt::MaximumSize);
693 if (size.width() == -1)
694 size.setWidth(maxSize.width());
695 if (size.height() == -1)
696 size.setHeight(maxSize.height());
697 }
698 return size;
699}
700
701#ifdef QT_DEBUG
702void QGridLayoutItem::dump(int indent) const
703{
704 qDebug("%*s%p (%d, %d) %d x %d", indent, "", q_layoutItem, firstRow(), firstColumn(),
705 rowSpan(), columnSpan());
706
707 if (q_stretches[Hor] >= 0)
708 qDebug("%*s Horizontal stretch: %d", indent, "", q_stretches[Hor]);
709 if (q_stretches[Ver] >= 0)
710 qDebug("%*s Vertical stretch: %d", indent, "", q_stretches[Ver]);
711 if (q_alignment != 0)
712 qDebug("%*s Alignment: %x", indent, "", uint(q_alignment));
713 qDebug("%*s Horizontal size policy: %x Vertical size policy: %x",
714 indent, "", sizePolicy(Qt::Horizontal), sizePolicy(Qt::Vertical));
715}
716#endif
717
718void QGridLayoutRowInfo::insertOrRemoveRows(int row, int delta)
719{
720 count += delta;
721
722 insertOrRemoveItems(stretches, row, delta);
723 insertOrRemoveItems(spacings, row, delta);
724 insertOrRemoveItems(alignments, row, delta);
725 insertOrRemoveItems(boxes, row, delta);
726}
727
728#ifdef QT_DEBUG
729void QGridLayoutRowInfo::dump(int indent) const
730{
731 qDebug("%*sInfo (count: %d)", indent, "", count);
732 for (int i = 0; i < count; ++i) {
733 QString message;
734
735 if (stretches.value(i).value() >= 0)
736 message += QString::fromAscii(" stretch %1").arg(stretches.value(i).value());
737 if (spacings.value(i).value() >= 0.0)
738 message += QString::fromAscii(" spacing %1").arg(spacings.value(i).value());
739 if (alignments.value(i) != 0)
740 message += QString::fromAscii(" alignment %1").arg(int(alignments.value(i)), 16);
741
742 if (!message.isEmpty() || boxes.value(i) != QGridLayoutBox()) {
743 qDebug("%*s Row %d:%s", indent, "", i, qPrintable(message));
744 if (boxes.value(i) != QGridLayoutBox())
745 boxes.value(i).dump(indent + 1);
746 }
747 }
748}
749#endif
750
751QGridLayoutEngine::QGridLayoutEngine()
752{
753 m_visualDirection = Qt::LeftToRight;
754 invalidate();
755}
756
757int QGridLayoutEngine::rowCount(Qt::Orientation orientation) const
758{
759 return q_infos[orientation == Qt::Vertical].count;
760}
761
762int QGridLayoutEngine::columnCount(Qt::Orientation orientation) const
763{
764 return q_infos[orientation == Qt::Horizontal].count;
765}
766
767int QGridLayoutEngine::itemCount() const
768{
769 return q_items.count();
770}
771
772QGridLayoutItem *QGridLayoutEngine::itemAt(int index) const
773{
774 Q_ASSERT(index >= 0 && index < itemCount());
775 return q_items.at(index);
776}
777
778int QGridLayoutEngine::effectiveFirstRow(Qt::Orientation orientation) const
779{
780 ensureEffectiveFirstAndLastRows();
781 return q_cachedEffectiveFirstRows[orientation == Qt::Vertical];
782}
783
784int QGridLayoutEngine::effectiveLastRow(Qt::Orientation orientation) const
785{
786 ensureEffectiveFirstAndLastRows();
787 return q_cachedEffectiveLastRows[orientation == Qt::Vertical];
788}
789
790void QGridLayoutEngine::setSpacing(qreal spacing, Qt::Orientations orientations)
791{
792 Q_ASSERT(spacing >= 0.0);
793 if (orientations & Qt::Horizontal)
794 q_defaultSpacings[Hor].setUserValue(spacing);
795 if (orientations & Qt::Vertical)
796 q_defaultSpacings[Ver].setUserValue(spacing);
797
798 invalidate();
799}
800
801qreal QGridLayoutEngine::spacing(const QLayoutStyleInfo &styleInfo, Qt::Orientation orientation) const
802{
803 if (q_defaultSpacings[orientation == Qt::Vertical].isDefault()) {
804 QStyle *style = styleInfo.style();
805 QStyleOption option;
806 option.initFrom(styleInfo.widget());
807 qreal defaultSpacing = (qreal)style->pixelMetric(orientation == Qt::Vertical ? QStyle::PM_LayoutVerticalSpacing
808 : QStyle::PM_LayoutHorizontalSpacing, &option, styleInfo.widget());
809 q_defaultSpacings[orientation == Qt::Vertical].setCachedValue(defaultSpacing);
810 }
811 return q_defaultSpacings[orientation == Qt::Vertical].value();
812}
813
814void QGridLayoutEngine::setRowSpacing(int row, qreal spacing, Qt::Orientation orientation)
815{
816 Q_ASSERT(row >= 0);
817
818 QGridLayoutRowInfo &rowInfo = q_infos[orientation == Qt::Vertical];
819 if (row >= rowInfo.spacings.count())
820 rowInfo.spacings.resize(row + 1);
821 if (spacing >= 0)
822 rowInfo.spacings[row].setUserValue(spacing);
823 else
824 rowInfo.spacings[row] = QLayoutParameter<qreal>();
825 invalidate();
826}
827
828qreal QGridLayoutEngine::rowSpacing(int row, Qt::Orientation orientation) const
829{
830 QLayoutParameter<qreal> spacing = q_infos[orientation == Qt::Vertical].spacings.value(row);
831 if (!spacing.isDefault())
832 return spacing.value();
833 return q_defaultSpacings[orientation == Qt::Vertical].value();
834}
835
836void QGridLayoutEngine::setRowStretchFactor(int row, int stretch, Qt::Orientation orientation)
837{
838 Q_ASSERT(row >= 0);
839 Q_ASSERT(stretch >= 0);
840
841 maybeExpandGrid(row, -1, orientation);
842
843 QGridLayoutRowInfo &rowInfo = q_infos[orientation == Qt::Vertical];
844 if (row >= rowInfo.stretches.count())
845 rowInfo.stretches.resize(row + 1);
846 rowInfo.stretches[row].setUserValue(stretch);
847}
848
849int QGridLayoutEngine::rowStretchFactor(int row, Qt::Orientation orientation) const
850{
851 QStretchParameter stretch = q_infos[orientation == Qt::Vertical].stretches.value(row);
852 if (!stretch.isDefault())
853 return stretch.value();
854 return 0;
855}
856
857void QGridLayoutEngine::setStretchFactor(QGraphicsLayoutItem *layoutItem, int stretch,
858 Qt::Orientation orientation)
859{
860 Q_ASSERT(stretch >= 0);
861
862 if (QGridLayoutItem *item = findLayoutItem(layoutItem))
863 item->setStretchFactor(stretch, orientation);
864}
865
866int QGridLayoutEngine::stretchFactor(QGraphicsLayoutItem *layoutItem, Qt::Orientation orientation) const
867{
868 if (QGridLayoutItem *item = findLayoutItem(layoutItem))
869 return item->stretchFactor(orientation);
870 return 0;
871}
872
873void QGridLayoutEngine::setRowSizeHint(Qt::SizeHint which, int row, qreal size,
874 Qt::Orientation orientation)
875{
876 Q_ASSERT(row >= 0);
877 Q_ASSERT(size >= 0.0);
878
879 maybeExpandGrid(row, -1, orientation);
880
881 QGridLayoutRowInfo &rowInfo = q_infos[orientation == Qt::Vertical];
882 if (row >= rowInfo.boxes.count())
883 rowInfo.boxes.resize(row + 1);
884 rowInfo.boxes[row].q_sizes(which) = size;
885}
886
887qreal QGridLayoutEngine::rowSizeHint(Qt::SizeHint which, int row, Qt::Orientation orientation) const
888{
889 return q_infos[orientation == Qt::Vertical].boxes.value(row).q_sizes(which);
890}
891
892void QGridLayoutEngine::setRowAlignment(int row, Qt::Alignment alignment,
893 Qt::Orientation orientation)
894{
895 Q_ASSERT(row >= 0);
896
897 maybeExpandGrid(row, -1, orientation);
898
899 QGridLayoutRowInfo &rowInfo = q_infos[orientation == Qt::Vertical];
900 if (row >= rowInfo.alignments.count())
901 rowInfo.alignments.resize(row + 1);
902 rowInfo.alignments[row] = alignment;
903}
904
905Qt::Alignment QGridLayoutEngine::rowAlignment(int row, Qt::Orientation orientation) const
906{
907 Q_ASSERT(row >= 0);
908 return q_infos[orientation == Qt::Vertical].alignments.value(row);
909}
910
911void QGridLayoutEngine::setAlignment(QGraphicsLayoutItem *layoutItem, Qt::Alignment alignment)
912{
913 if (QGridLayoutItem *item = findLayoutItem(layoutItem))
914 item->setAlignment(alignment);
915 invalidate();
916}
917
918Qt::Alignment QGridLayoutEngine::alignment(QGraphicsLayoutItem *layoutItem) const
919{
920 if (QGridLayoutItem *item = findLayoutItem(layoutItem))
921 return item->alignment();
922 return 0;
923}
924
925Qt::Alignment QGridLayoutEngine::effectiveAlignment(const QGridLayoutItem *layoutItem) const
926{
927 Qt::Alignment align = layoutItem->alignment();
928 if (!(align & Qt::AlignVertical_Mask)) {
929 // no vertical alignment, respect the row alignment
930 int y = layoutItem->firstRow();
931 align |= (rowAlignment(y, Qt::Vertical) & Qt::AlignVertical_Mask);
932 }
933 if (!(align & Qt::AlignHorizontal_Mask)) {
934 // no horizontal alignment, respect the column alignment
935 int x = layoutItem->firstColumn();
936 align |= (rowAlignment(x, Qt::Horizontal) & Qt::AlignHorizontal_Mask);
937 }
938 return align;
939}
940
941/*!
942 \internal
943 The \a index is only used by QGraphicsLinearLayout to ensure that itemAt() reflects the order
944 of visual arrangement. Strictly speaking it does not have to, but most people expect it to.
945 (And if it didn't we would have to add itemArrangedAt(int index) or something..)
946 */
947void QGridLayoutEngine::insertItem(QGridLayoutItem *item, int index)
948{
949 maybeExpandGrid(item->lastRow(), item->lastColumn());
950
951 if (index == -1)
952 q_items.append(item);
953 else
954 q_items.insert(index, item);
955
956 for (int i = item->firstRow(); i <= item->lastRow(); ++i) {
957 for (int j = item->firstColumn(); j <= item->lastColumn(); ++j) {
958 if (itemAt(i, j))
959 qWarning("QGridLayoutEngine::addItem: Cell (%d, %d) already taken", i, j);
960 setItemAt(i, j, item);
961 }
962 }
963}
964
965void QGridLayoutEngine::addItem(QGridLayoutItem *item)
966{
967 insertItem(item, -1);
968}
969
970void QGridLayoutEngine::removeItem(QGridLayoutItem *item)
971{
972 Q_ASSERT(q_items.contains(item));
973
974 invalidate();
975
976 for (int i = item->firstRow(); i <= item->lastRow(); ++i) {
977 for (int j = item->firstColumn(); j <= item->lastColumn(); ++j) {
978 if (itemAt(i, j) == item)
979 setItemAt(i, j, 0);
980 }
981 }
982
983 q_items.removeAll(item);
984}
985
986QGridLayoutItem *QGridLayoutEngine::findLayoutItem(QGraphicsLayoutItem *layoutItem) const
987{
988 for (int i = q_items.count() - 1; i >= 0; --i) {
989 QGridLayoutItem *item = q_items.at(i);
990 if (item->layoutItem() == layoutItem)
991 return item;
992 }
993 return 0;
994}
995
996QGridLayoutItem *QGridLayoutEngine::itemAt(int row, int column, Qt::Orientation orientation) const
997{
998 if (orientation == Qt::Horizontal)
999 qSwap(row, column);
1000 if (uint(row) >= uint(rowCount()) || uint(column) >= uint(columnCount()))
1001 return 0;
1002 return q_grid.at((row * internalGridColumnCount()) + column);
1003}
1004
1005void QGridLayoutEngine::invalidate()
1006{
1007 q_cachedEffectiveFirstRows[Hor] = -1;
1008 q_cachedEffectiveFirstRows[Ver] = -1;
1009 q_cachedEffectiveLastRows[Hor] = -1;
1010 q_cachedEffectiveLastRows[Ver] = -1;
1011 q_cachedDataForStyleInfo.invalidate();
1012 q_cachedSize = QSizeF();
1013}
1014
1015static void visualRect(QRectF *geom, Qt::LayoutDirection dir, const QRectF &contentsRect)
1016{
1017 if (dir == Qt::RightToLeft)
1018 geom->moveRight(contentsRect.right() - (geom->left() - contentsRect.left()));
1019}
1020
1021void QGridLayoutEngine::setGeometries(const QLayoutStyleInfo &styleInfo,
1022 const QRectF &contentsGeometry)
1023{
1024 if (rowCount() < 1 || columnCount() < 1)
1025 return;
1026
1027 ensureGeometries(styleInfo, contentsGeometry.size());
1028
1029 for (int i = q_items.count() - 1; i >= 0; --i) {
1030 QGridLayoutItem *item = q_items.at(i);
1031
1032 qreal x = q_xx[item->firstColumn()];
1033 qreal y = q_yy[item->firstRow()];
1034 qreal width = q_widths[item->lastColumn()];
1035 qreal height = q_heights[item->lastRow()];
1036
1037 if (item->columnSpan() != 1)
1038 width += q_xx[item->lastColumn()] - x;
1039 if (item->rowSpan() != 1)
1040 height += q_yy[item->lastRow()] - y;
1041
1042 QRectF geom = item->geometryWithin(contentsGeometry.x() + x, contentsGeometry.y() + y,
1043 width, height, q_descents[item->lastRow()]);
1044 visualRect(&geom, visualDirection(), contentsGeometry);
1045 item->setGeometry(geom);
1046 }
1047}
1048
1049// ### candidate for deletion
1050QRectF QGridLayoutEngine::cellRect(const QLayoutStyleInfo &styleInfo,
1051 const QRectF &contentsGeometry, int row, int column, int rowSpan,
1052 int columnSpan) const
1053{
1054 if (uint(row) >= uint(rowCount()) || uint(column) >= uint(columnCount())
1055 || rowSpan < 1 || columnSpan < 1)
1056 return QRectF();
1057
1058 ensureGeometries(styleInfo, contentsGeometry.size());
1059
1060 int lastColumn = qMax(column + columnSpan, columnCount()) - 1;
1061 int lastRow = qMax(row + rowSpan, rowCount()) - 1;
1062
1063 qreal x = q_xx[column];
1064 qreal y = q_yy[row];
1065 qreal width = q_widths[lastColumn];
1066 qreal height = q_heights[lastRow];
1067
1068 if (columnSpan != 1)
1069 width += q_xx[lastColumn] - x;
1070 if (rowSpan != 1)
1071 height += q_yy[lastRow] - y;
1072
1073 return QRectF(contentsGeometry.x() + x, contentsGeometry.y() + y, width, height);
1074}
1075
1076QSizeF QGridLayoutEngine::sizeHint(const QLayoutStyleInfo &styleInfo, Qt::SizeHint which,
1077 const QSizeF & /* constraint */) const
1078{
1079 ensureColumnAndRowData(styleInfo);
1080
1081 switch (which) {
1082 case Qt::MinimumSize:
1083 return QSizeF(q_totalBoxes[Hor].q_minimumSize, q_totalBoxes[Ver].q_minimumSize);
1084 case Qt::PreferredSize:
1085 return QSizeF(q_totalBoxes[Hor].q_preferredSize, q_totalBoxes[Ver].q_preferredSize);
1086 case Qt::MaximumSize:
1087 return QSizeF(q_totalBoxes[Hor].q_maximumSize, q_totalBoxes[Ver].q_maximumSize);
1088 case Qt::MinimumDescent:
1089 return QSizeF(-1.0, q_totalBoxes[Hor].q_minimumDescent); // ### doesn't work
1090 default:
1091 break;
1092 }
1093 return QSizeF();
1094}
1095
1096QSizePolicy::ControlTypes QGridLayoutEngine::controlTypes(LayoutSide side) const
1097{
1098 Qt::Orientation orientation = (side == Top || side == Bottom) ? Qt::Vertical : Qt::Horizontal;
1099 int row = (side == Top || side == Left) ? effectiveFirstRow(orientation)
1100 : effectiveLastRow(orientation);
1101 QSizePolicy::ControlTypes result = 0;
1102
1103 for (int column = columnCount(orientation) - 1; column >= 0; --column) {
1104 if (QGridLayoutItem *item = itemAt(row, column, orientation))
1105 result |= item->controlTypes(side);
1106 }
1107 return result;
1108}
1109
1110void QGridLayoutEngine::transpose()
1111{
1112 invalidate();
1113
1114 for (int i = q_items.count() - 1; i >= 0; --i)
1115 q_items.at(i)->transpose();
1116
1117 qSwap(q_defaultSpacings[Hor], q_defaultSpacings[Ver]);
1118 qSwap(q_infos[Hor], q_infos[Ver]);
1119
1120 regenerateGrid();
1121}
1122
1123void QGridLayoutEngine::setVisualDirection(Qt::LayoutDirection direction)
1124{
1125 m_visualDirection = direction;
1126}
1127
1128Qt::LayoutDirection QGridLayoutEngine::visualDirection() const
1129{
1130 return m_visualDirection;
1131}
1132
1133#ifdef QT_DEBUG
1134void QGridLayoutEngine::dump(int indent) const
1135{
1136 qDebug("%*sEngine", indent, "");
1137
1138 qDebug("%*s Items (%d)", indent, "", q_items.count());
1139 int i;
1140 for (i = 0; i < q_items.count(); ++i)
1141 q_items.at(i)->dump(indent + 2);
1142
1143 qDebug("%*s Grid (%d x %d)", indent, "", internalGridRowCount(),
1144 internalGridColumnCount());
1145 for (int row = 0; row < internalGridRowCount(); ++row) {
1146 QString message = QLatin1String("[ ");
1147 for (int column = 0; column < internalGridColumnCount(); ++column) {
1148 message += QString::number(q_items.indexOf(itemAt(row, column))).rightJustified(3);
1149 message += QLatin1Char(' ');
1150 }
1151 message += QLatin1Char(']');
1152 qDebug("%*s %s", indent, "", qPrintable(message));
1153 }
1154
1155 if (q_defaultSpacings[Hor].value() >= 0.0 || q_defaultSpacings[Ver].value() >= 0.0)
1156 qDebug("%*s Default spacings: %g %g", indent, "", q_defaultSpacings[Hor].value(),
1157 q_defaultSpacings[Ver].value());
1158
1159 qDebug("%*s Column and row info", indent, "");
1160 q_infos[Hor].dump(indent + 2);
1161 q_infos[Ver].dump(indent + 2);
1162
1163 qDebug("%*s Column and row data", indent, "");
1164 q_columnData.dump(indent + 2);
1165 q_rowData.dump(indent + 2);
1166
1167 qDebug("%*s Geometries output", indent, "");
1168 QVector<qreal> *cellPos = &q_yy;
1169 for (int pass = 0; pass < 2; ++pass) {
1170 QString message;
1171 for (i = 0; i < cellPos->count(); ++i) {
1172 message += QLatin1String((message.isEmpty() ? "[" : ", "));
1173 message += QString::number(cellPos->at(i));
1174 }
1175 message += QLatin1Char(']');
1176 qDebug("%*s %s %s", indent, "", (pass == 0 ? "rows:" : "columns:"), qPrintable(message));
1177 cellPos = &q_xx;
1178 }
1179}
1180#endif
1181
1182void QGridLayoutEngine::maybeExpandGrid(int row, int column, Qt::Orientation orientation)
1183{
1184 invalidate(); // ### move out of here?
1185
1186 if (orientation == Qt::Horizontal)
1187 qSwap(row, column);
1188
1189 if (row < rowCount() && column < columnCount())
1190 return;
1191
1192 int oldGridRowCount = internalGridRowCount();
1193 int oldGridColumnCount = internalGridColumnCount();
1194
1195 q_infos[Ver].count = qMax(row + 1, rowCount());
1196 q_infos[Hor].count = qMax(column + 1, columnCount());
1197
1198 int newGridRowCount = internalGridRowCount();
1199 int newGridColumnCount = internalGridColumnCount();
1200
1201 int newGridSize = newGridRowCount * newGridColumnCount;
1202 if (newGridSize != q_grid.count()) {
1203 q_grid.resize(newGridSize);
1204
1205 if (newGridColumnCount != oldGridColumnCount) {
1206 for (int i = oldGridRowCount - 1; i >= 1; --i) {
1207 for (int j = oldGridColumnCount - 1; j >= 0; --j) {
1208 int oldIndex = (i * oldGridColumnCount) + j;
1209 int newIndex = (i * newGridColumnCount) + j;
1210
1211 Q_ASSERT(newIndex > oldIndex);
1212 q_grid[newIndex] = q_grid[oldIndex];
1213 q_grid[oldIndex] = 0;
1214 }
1215 }
1216 }
1217 }
1218}
1219
1220void QGridLayoutEngine::regenerateGrid()
1221{
1222 q_grid.fill(0);
1223
1224 for (int i = q_items.count() - 1; i >= 0; --i) {
1225 QGridLayoutItem *item = q_items.at(i);
1226
1227 for (int j = item->firstRow(); j <= item->lastRow(); ++j) {
1228 for (int k = item->firstColumn(); k <= item->lastColumn(); ++k) {
1229 setItemAt(j, k, item);
1230 }
1231 }
1232 }
1233}
1234
1235void QGridLayoutEngine::setItemAt(int row, int column, QGridLayoutItem *item)
1236{
1237 Q_ASSERT(row >= 0 && row < rowCount());
1238 Q_ASSERT(column >= 0 && column < columnCount());
1239 q_grid[(row * internalGridColumnCount()) + column] = item;
1240}
1241
1242void QGridLayoutEngine::insertOrRemoveRows(int row, int delta, Qt::Orientation orientation)
1243{
1244 int oldRowCount = rowCount(orientation);
1245 Q_ASSERT(uint(row) <= uint(oldRowCount));
1246
1247 invalidate();
1248
1249 // appending rows (or columns) is easy
1250 if (row == oldRowCount && delta > 0) {
1251 maybeExpandGrid(oldRowCount + delta - 1, -1, orientation);
1252 return;
1253 }
1254
1255 q_infos[orientation == Qt::Vertical].insertOrRemoveRows(row, delta);
1256
1257 for (int i = q_items.count() - 1; i >= 0; --i)
1258 q_items.at(i)->insertOrRemoveRows(row, delta, orientation);
1259
1260 q_grid.resize(internalGridRowCount() * internalGridColumnCount());
1261 regenerateGrid();
1262}
1263
1264void QGridLayoutEngine::fillRowData(QGridLayoutRowData *rowData, const QLayoutStyleInfo &styleInfo,
1265 Qt::Orientation orientation) const
1266{
1267 const int ButtonMask = QSizePolicy::ButtonBox | QSizePolicy::PushButton;
1268 const QGridLayoutRowInfo &rowInfo = q_infos[orientation == Qt::Vertical];
1269 const QGridLayoutRowInfo &columnInfo = q_infos[orientation == Qt::Horizontal];
1270 LayoutSide top = (orientation == Qt::Vertical) ? Top : Left;
1271 LayoutSide bottom = (orientation == Qt::Vertical) ? Bottom : Right;
1272
1273 QStyle *style = styleInfo.style();
1274 QStyleOption option;
1275 option.initFrom(styleInfo.widget());
1276
1277 const QLayoutParameter<qreal> &defaultSpacing = q_defaultSpacings[orientation == Qt::Vertical];
1278 qreal innerSpacing = 0.0;
1279 if (style)
1280 innerSpacing = (qreal)style->pixelMetric(orientation == Qt::Vertical ? QStyle::PM_LayoutVerticalSpacing
1281 : QStyle::PM_LayoutHorizontalSpacing,
1282 &option, styleInfo.widget());
1283 if (innerSpacing >= 0.0)
1284 defaultSpacing.setCachedValue(innerSpacing);
1285
1286 for (int row = 0; row < rowInfo.count; ++row) {
1287 bool rowIsEmpty = true;
1288 bool rowIsIdenticalToPrevious = (row > 0);
1289
1290 for (int column = 0; column < columnInfo.count; ++column) {
1291 QGridLayoutItem *item = itemAt(row, column, orientation);
1292
1293 if (rowIsIdenticalToPrevious && item != itemAt(row - 1, column, orientation))
1294 rowIsIdenticalToPrevious = false;
1295
1296 if (item)
1297 rowIsEmpty = false;
1298 }
1299
1300 if ((rowIsEmpty || rowIsIdenticalToPrevious)
1301 && rowInfo.spacings.value(row).isDefault()
1302 && rowInfo.stretches.value(row).isDefault()
1303 && rowInfo.boxes.value(row) == QGridLayoutBox())
1304 rowData->ignore.setBit(row, true);
1305
1306 if (rowInfo.spacings.value(row).isUser()) {
1307 rowData->spacings[row] = rowInfo.spacings.at(row).value();
1308 } else if (!defaultSpacing.isDefault()) {
1309 rowData->spacings[row] = defaultSpacing.value();
1310 }
1311
1312 rowData->stretches[row] = rowInfo.stretches.value(row).value();
1313 }
1314
1315 struct RowAdHocData {
1316 int q_row;
1317 unsigned int q_hasButtons : 8;
1318 unsigned int q_hasNonButtons : 8;
1319
1320 inline RowAdHocData() : q_row(-1), q_hasButtons(false), q_hasNonButtons(false) {}
1321 inline void init(int row) {
1322 this->q_row = row;
1323 q_hasButtons = false;
1324 q_hasNonButtons = false;
1325 }
1326 inline bool hasOnlyButtons() const { return q_hasButtons && !q_hasNonButtons; }
1327 inline bool hasOnlyNonButtons() const { return q_hasNonButtons && !q_hasButtons; }
1328 };
1329 RowAdHocData lastRowAdHocData;
1330 RowAdHocData nextToLastRowAdHocData;
1331 RowAdHocData nextToNextToLastRowAdHocData;
1332
1333 rowData->hasIgnoreFlag = false;
1334 for (int row = 0; row < rowInfo.count; ++row) {
1335 if (rowData->ignore.testBit(row))
1336 continue;
1337
1338 QGridLayoutBox &rowBox = rowData->boxes[row];
1339 if (option.state & QStyle::State_Window) {
1340 nextToNextToLastRowAdHocData = nextToLastRowAdHocData;
1341 nextToLastRowAdHocData = lastRowAdHocData;
1342 lastRowAdHocData.init(row);
1343 }
1344
1345 bool userRowStretch = rowInfo.stretches.value(row).isUser();
1346 int &rowStretch = rowData->stretches[row];
1347
1348 bool hasIgnoreFlag = true;
1349 for (int column = 0; column < columnInfo.count; ++column) {
1350 QGridLayoutItem *item = itemAt(row, column, orientation);
1351 if (item) {
1352 int itemRow = item->firstRow(orientation);
1353 int itemColumn = item->firstColumn(orientation);
1354
1355 if (itemRow == row && itemColumn == column) {
1356 int itemStretch = item->stretchFactor(orientation);
1357 if (!(item->sizePolicy(orientation) & QSizePolicy::IgnoreFlag))
1358 hasIgnoreFlag = false;
1359 int itemRowSpan = item->rowSpan(orientation);
1360
1361 int effectiveRowSpan = 1;
1362 for (int i = 1; i < itemRowSpan; ++i) {
1363 if (!rowData->ignore.testBit(i))
1364 ++effectiveRowSpan;
1365 }
1366
1367 QGridLayoutBox *box;
1368 if (effectiveRowSpan == 1) {
1369 box = &rowBox;
1370 if (!userRowStretch)
1371 rowStretch = qMax(rowStretch, itemStretch);
1372 } else {
1373 QGridLayoutMultiCellData &multiCell =
1374 rowData->multiCellMap[qMakePair(row, effectiveRowSpan)];
1375 box = &multiCell.q_box;
1376 multiCell.q_stretch = itemStretch;
1377 }
1378 box->combine(item->box(orientation));
1379
1380 if (effectiveRowSpan == 1) {
1381 QSizePolicy::ControlTypes controls = item->controlTypes(top);
1382 if (controls & ButtonMask)
1383 lastRowAdHocData.q_hasButtons = true;
1384 if (controls & ~ButtonMask)
1385 lastRowAdHocData.q_hasNonButtons = true;
1386 }
1387 }
1388 }
1389 }
1390 if (row < rowInfo.boxes.count()) {
1391 QGridLayoutBox rowBoxInfo = rowInfo.boxes.at(row);
1392 rowBoxInfo.normalize();
1393 rowBox.q_minimumSize = qMax(rowBox.q_minimumSize, rowBoxInfo.q_minimumSize);
1394 rowBox.q_maximumSize = qMax(rowBox.q_minimumSize,
1395 (rowBoxInfo.q_maximumSize != FLT_MAX ?
1396 rowBoxInfo.q_maximumSize : rowBox.q_maximumSize));
1397 rowBox.q_preferredSize = qBound(rowBox.q_minimumSize,
1398 qMax(rowBox.q_preferredSize, rowBoxInfo.q_preferredSize),
1399 rowBox.q_maximumSize);
1400 }
1401 if (hasIgnoreFlag)
1402 rowData->hasIgnoreFlag = true;
1403 }
1404
1405 /*
1406 Heuristic: Detect button boxes that don't use QSizePolicy::ButtonBox.
1407 This is somewhat ad hoc but it usually does the trick.
1408 */
1409 bool lastRowIsButtonBox = (lastRowAdHocData.hasOnlyButtons()
1410 && nextToLastRowAdHocData.hasOnlyNonButtons());
1411 bool lastTwoRowsIsButtonBox = (lastRowAdHocData.hasOnlyButtons()
1412 && nextToLastRowAdHocData.hasOnlyButtons()
1413 && nextToNextToLastRowAdHocData.hasOnlyNonButtons()
1414 && orientation == Qt::Vertical);
1415
1416 if (defaultSpacing.isDefault()) {
1417 int prevRow = -1;
1418 for (int row = 0; row < rowInfo.count; ++row) {
1419 if (rowData->ignore.testBit(row))
1420 continue;
1421
1422 if (prevRow != -1 && !rowInfo.spacings.value(prevRow).isUser()) {
1423 qreal &rowSpacing = rowData->spacings[prevRow];
1424 for (int column = 0; column < columnInfo.count; ++column) {
1425 QGridLayoutItem *item1 = itemAt(prevRow, column, orientation);
1426 QGridLayoutItem *item2 = itemAt(row, column, orientation);
1427
1428 if (item1 && item2 && item1 != item2) {
1429 QSizePolicy::ControlTypes controls1 = item1->controlTypes(bottom);
1430 QSizePolicy::ControlTypes controls2 = item2->controlTypes(top);
1431
1432 if (controls2 & QSizePolicy::PushButton) {
1433 if ((row == nextToLastRowAdHocData.q_row && lastTwoRowsIsButtonBox)
1434 || (row == lastRowAdHocData.q_row && lastRowIsButtonBox)) {
1435 controls2 &= ~QSizePolicy::PushButton;
1436 controls2 |= QSizePolicy::ButtonBox;
1437 }
1438 }
1439
1440 qreal spacing = style->combinedLayoutSpacing(controls1, controls2,
1441 orientation, &option,
1442 styleInfo.widget());
1443 if (orientation == Qt::Horizontal) {
1444 qreal width1 = rowData->boxes.at(prevRow).q_minimumSize;
1445 qreal width2 = rowData->boxes.at(row).q_minimumSize;
1446 QRectF rect1 = item1->geometryWithin(0.0, 0.0, width1, FLT_MAX, -1.0);
1447 QRectF rect2 = item2->geometryWithin(0.0, 0.0, width2, FLT_MAX, -1.0);
1448 spacing -= (width1 - (rect1.x() + rect1.width())) + rect2.x();
1449 } else {
1450 const QGridLayoutBox &box1 = rowData->boxes.at(prevRow);
1451 const QGridLayoutBox &box2 = rowData->boxes.at(row);
1452 qreal height1 = box1.q_minimumSize;
1453 qreal height2 = box2.q_minimumSize;
1454 qreal rowDescent1 = fixedDescent(box1.q_minimumDescent,
1455 box1.q_minimumAscent, height1);
1456 qreal rowDescent2 = fixedDescent(box2.q_minimumDescent,
1457 box2.q_minimumAscent, height2);
1458 QRectF rect1 = item1->geometryWithin(0.0, 0.0, FLT_MAX, height1,
1459 rowDescent1);
1460 QRectF rect2 = item2->geometryWithin(0.0, 0.0, FLT_MAX, height2,
1461 rowDescent2);
1462 spacing -= (height1 - (rect1.y() + rect1.height())) + rect2.y();
1463 }
1464 rowSpacing = qMax(spacing, rowSpacing);
1465 }
1466 }
1467 }
1468 prevRow = row;
1469 }
1470 } else if (lastRowIsButtonBox || lastTwoRowsIsButtonBox) {
1471 /*
1472 Even for styles that define a uniform spacing, we cheat a
1473 bit and use the window margin as the spacing. This
1474 significantly improves the look of dialogs.
1475 */
1476 int prevRow = lastRowIsButtonBox ? nextToLastRowAdHocData.q_row
1477 : nextToNextToLastRowAdHocData.q_row;
1478 if (!defaultSpacing.isUser() && !rowInfo.spacings.value(prevRow).isUser()) {
1479 qreal windowMargin = style->pixelMetric(orientation == Qt::Vertical
1480 ? QStyle::PM_LayoutBottomMargin
1481 : QStyle::PM_LayoutRightMargin,
1482 &option, styleInfo.widget());
1483
1484 qreal &rowSpacing = rowData->spacings[prevRow];
1485 rowSpacing = qMax(windowMargin, rowSpacing);
1486 }
1487 }
1488}
1489
1490void QGridLayoutEngine::ensureEffectiveFirstAndLastRows() const
1491{
1492 if (q_cachedEffectiveFirstRows[Hor] == -1 && !q_items.isEmpty()) {
1493 int rowCount = this->rowCount();
1494 int columnCount = this->columnCount();
1495
1496 q_cachedEffectiveFirstRows[Ver] = rowCount;
1497 q_cachedEffectiveFirstRows[Hor] = columnCount;
1498 q_cachedEffectiveLastRows[Ver] = -1;
1499 q_cachedEffectiveLastRows[Hor] = -1;
1500
1501 for (int i = q_items.count() - 1; i >= 0; --i) {
1502 const QGridLayoutItem *item = q_items.at(i);
1503
1504 for (int j = 0; j < NOrientations; ++j) {
1505 Qt::Orientation orientation = (j == Hor) ? Qt::Horizontal : Qt::Vertical;
1506 if (item->firstRow(orientation) < q_cachedEffectiveFirstRows[j])
1507 q_cachedEffectiveFirstRows[j] = item->firstRow(orientation);
1508 if (item->lastRow(orientation) > q_cachedEffectiveLastRows[j])
1509 q_cachedEffectiveLastRows[j] = item->lastRow(orientation);
1510 }
1511 }
1512 }
1513}
1514
1515void QGridLayoutEngine::ensureColumnAndRowData(const QLayoutStyleInfo &styleInfo) const
1516{
1517 if (q_cachedDataForStyleInfo == styleInfo)
1518 return;
1519
1520 q_columnData.reset(columnCount());
1521 q_rowData.reset(rowCount());
1522
1523 fillRowData(&q_columnData, styleInfo, Qt::Horizontal);
1524 fillRowData(&q_rowData, styleInfo, Qt::Vertical);
1525
1526 q_columnData.distributeMultiCells();
1527 q_rowData.distributeMultiCells();
1528
1529 q_totalBoxes[Hor] = q_columnData.totalBox(0, columnCount());
1530 q_totalBoxes[Ver] = q_rowData.totalBox(0, rowCount());
1531
1532 q_cachedDataForStyleInfo = styleInfo;
1533}
1534
1535void QGridLayoutEngine::ensureGeometries(const QLayoutStyleInfo &styleInfo,
1536 const QSizeF &size) const
1537{
1538 ensureColumnAndRowData(styleInfo);
1539 if (q_cachedSize == size)
1540 return;
1541
1542 q_xx.resize(columnCount());
1543 q_yy.resize(rowCount());
1544 q_widths.resize(columnCount());
1545 q_heights.resize(rowCount());
1546 q_descents.resize(rowCount());
1547 q_columnData.calculateGeometries(0, columnCount(), size.width(), q_xx.data(), q_widths.data(),
1548 0, q_totalBoxes[Hor]);
1549 q_rowData.calculateGeometries(0, rowCount(), size.height(), q_yy.data(), q_heights.data(),
1550 q_descents.data(), q_totalBoxes[Ver]);
1551
1552 q_cachedSize = size;
1553}
1554
1555QT_END_NAMESPACE
1556
1557#endif //QT_NO_GRAPHICSVIEW
Note: See TracBrowser for help on using the repository browser.