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

Last change on this file since 122 was 2, checked in by Dmitry A. Kuminov, 16 years ago

Initially imported qt-all-opensource-src-4.5.1 from Trolltech.

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