source: trunk/src/gui/kernel/qformlayout.cpp@ 503

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

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

File size: 65.0 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 "qapplication.h"
43#include "qdebug.h"
44#include "qformlayout.h"
45#include "qlabel.h"
46#include "qlayout_p.h"
47#include "qlayoutengine_p.h"
48#include "qrect.h"
49#include "qvector.h"
50#include "qwidget.h"
51
52QT_BEGIN_NAMESPACE
53
54namespace {
55// Fixed column matrix, stores items as [i11, i12, i21, i22...],
56// with FORTRAN-style index operator(r, c).
57template <class T, int NumColumns>
58class FixedColumnMatrix {
59public:
60 typedef QVector<T> Storage;
61
62 FixedColumnMatrix() { }
63
64 void clear() { m_storage.clear(); }
65
66 const T &operator()(int r, int c) const { return m_storage[r * NumColumns + c]; }
67 T &operator()(int r, int c) { return m_storage[r * NumColumns + c]; }
68
69 int rowCount() const { return m_storage.size() / NumColumns; }
70 void addRow(const T &value);
71 void insertRow(int r, const T &value);
72 void removeRow(int r);
73
74 bool find(const T &value, int *rowPtr, int *colPtr) const ;
75 int count(const T &value) const { return m_storage.count(value); }
76
77 // Hmmpf.. Some things are faster that way.
78 const Storage &storage() const { return m_storage; }
79
80 static void storageIndexToPosition(int idx, int *rowPtr, int *colPtr);
81
82private:
83 Storage m_storage;
84};
85
86template <class T, int NumColumns>
87void FixedColumnMatrix<T, NumColumns>::addRow(const T &value)
88{
89 for (int i = 0; i < NumColumns; ++i)
90 m_storage.append(value);
91}
92
93template <class T, int NumColumns>
94void FixedColumnMatrix<T, NumColumns>::insertRow(int r, const T &value)
95{
96 Q_TYPENAME Storage::iterator it = m_storage.begin();
97 it += r * NumColumns;
98 m_storage.insert(it, NumColumns, value);
99}
100
101template <class T, int NumColumns>
102void FixedColumnMatrix<T, NumColumns>::removeRow(int r)
103{
104 m_storage.remove(r * NumColumns, NumColumns);
105}
106
107template <class T, int NumColumns>
108bool FixedColumnMatrix<T, NumColumns>::find(const T &value, int *rowPtr, int *colPtr) const
109{
110 const int idx = m_storage.indexOf(value);
111 if (idx == -1)
112 return false;
113 storageIndexToPosition(idx, rowPtr, colPtr);
114 return true;
115}
116
117template <class T, int NumColumns>
118void FixedColumnMatrix<T, NumColumns>::storageIndexToPosition(int idx, int *rowPtr, int *colPtr)
119{
120 *rowPtr = idx / NumColumns;
121 *colPtr = idx % NumColumns;
122}
123} // namespace
124
125// special values for unset fields; must not clash with values of FieldGrowthPolicy or
126// RowWrapPolicy
127const uint DefaultFieldGrowthPolicy = 255;
128const uint DefaultRowWrapPolicy = 255;
129
130enum { ColumnCount = 2 };
131
132// -- our data structure for our items
133// This owns the QLayoutItem
134struct QFormLayoutItem
135{
136 QFormLayoutItem(QLayoutItem* i) : item(i), fullRow(false), isHfw(false) { }
137 ~QFormLayoutItem() { delete item; }
138
139 // Wrappers
140 QWidget *widget() const { return item->widget(); }
141 QLayout *layout() const { return item->layout(); }
142
143 bool hasHeightForWidth() const { return item->hasHeightForWidth(); }
144 int heightForWidth(int width) const { return item->heightForWidth(width); }
145 int minimumHeightForWidth(int width) const { return item->minimumHeightForWidth(width); }
146 Qt::Orientations expandingDirections() const { return item->expandingDirections(); }
147 QSizePolicy::ControlTypes controlTypes() const { return item->controlTypes(); }
148 int vStretch() const { return widget() ? widget()->sizePolicy().verticalStretch() : 0; }
149
150 void setGeometry(const QRect& r) { item->setGeometry(r); }
151 QRect geometry() const { return item->geometry(); }
152
153 // For use with FixedColumnMatrix
154 bool operator==(const QFormLayoutItem& other) { return item == other.item; }
155
156 QLayoutItem *item;
157 bool fullRow;
158
159 // set by updateSizes
160 bool isHfw;
161 QSize minSize;
162 QSize sizeHint;
163 QSize maxSize;
164
165 // also set by updateSizes
166 int sbsHSpace; // only used for side by side, for the field item only (not label)
167 int vSpace; // This is the spacing to the item in the row above
168
169 // set by setupVerticalLayoutData
170 bool sideBySide;
171 int vLayoutIndex;
172
173 // set by setupHorizontalLayoutData
174 int layoutPos;
175 int layoutWidth;
176};
177
178class QFormLayoutPrivate : public QLayoutPrivate
179{
180 Q_DECLARE_PUBLIC(QFormLayout)
181
182public:
183 typedef FixedColumnMatrix<QFormLayoutItem *, ColumnCount> ItemMatrix;
184
185 QFormLayoutPrivate();
186 ~QFormLayoutPrivate() { }
187
188 int insertRow(int row);
189 void insertRows(int row, int count);
190 void setItem(int row, QFormLayout::ItemRole role, QLayoutItem *item);
191 void setLayout(int row, QFormLayout::ItemRole role, QLayout *layout);
192 void setWidget(int row, QFormLayout::ItemRole role, QWidget *widget);
193
194 void arrangeWidgets(const QVector<QLayoutStruct>& layouts, QRect &rect);
195
196 void updateSizes();
197
198 void setupVerticalLayoutData(int width);
199 void setupHorizontalLayoutData(int width);
200
201 QStyle* getStyle() const;
202
203 inline bool haveHfwCached(int width) const
204 {
205 return (hfw_width == width) || (width == sh_width && hfw_sh_height >= 0);
206 }
207
208 void recalcHFW(int w);
209 void setupHfwLayoutData();
210
211 uint fieldGrowthPolicy : 8;
212 uint rowWrapPolicy : 8;
213 uint has_hfw : 2;
214 uint dirty : 2; // have we laid out yet?
215 uint sizesDirty : 2; // have we (not) gathered layout item sizes?
216 uint expandVertical : 1; // Do we expand vertically?
217 uint expandHorizontal : 1; // Do we expand horizonally?
218 Qt::Alignment labelAlignment;
219 Qt::Alignment formAlignment;
220
221 ItemMatrix m_matrix;
222 QList<QFormLayoutItem *> m_things;
223
224 int layoutWidth; // the last width that we called setupVerticalLayoutData on (for vLayouts)
225
226 int hfw_width; // the last width we calculated HFW for
227 int hfw_height; // what that height was
228 int hfw_minheight; // what that minheight was
229
230 int hfw_sh_height; // the hfw for sh_width
231 int hfw_sh_minheight; // the minhfw for sh_width
232
233 int min_width; // the width that gets turned into minSize (from updateSizes)
234 int sh_width; // the width that gets turned into prefSize (from updateSizes)
235 int thresh_width; // the width that we start splitting label/field pairs at (from updateSizes)
236 QSize minSize;
237 QSize prefSize;
238 int formMaxWidth;
239 void calcSizeHints();
240
241 QVector<QLayoutStruct> vLayouts; // set by setupVerticalLayoutData;
242 int vLayoutCount; // Number of rows we calculated in setupVerticalLayoutData
243 int maxLabelWidth; // the label width we calculated in setupVerticalLayoutData
244
245 QVector<QLayoutStruct> hfwLayouts;
246
247 int hSpacing;
248 int vSpacing;
249};
250
251QFormLayoutPrivate::QFormLayoutPrivate()
252 : fieldGrowthPolicy(DefaultFieldGrowthPolicy),
253 rowWrapPolicy(DefaultRowWrapPolicy), has_hfw(false), dirty(true), sizesDirty(true),
254 expandVertical(0), expandHorizontal(0), labelAlignment(0), formAlignment(0),
255 hfw_width(-1), hfw_sh_height(-1), min_width(-1),
256 sh_width(-1), thresh_width(QLAYOUTSIZE_MAX), hSpacing(-1), vSpacing(-1)
257{
258}
259
260static Qt::Alignment fixedAlignment(Qt::Alignment alignment, Qt::LayoutDirection layoutDirection)
261{
262 if (layoutDirection == Qt::RightToLeft && alignment & Qt::AlignAbsolute) {
263 // swap left and right, and eliminate absolute flag
264 return Qt::Alignment((alignment & ~(Qt::AlignLeft | Qt::AlignRight | Qt::AlignAbsolute))
265 | ((alignment & Qt::AlignRight) ? Qt::AlignLeft : 0)
266 | ((alignment & Qt::AlignLeft) ? Qt::AlignRight : 0));
267 } else {
268 return alignment & ~Qt::AlignAbsolute;
269 }
270}
271
272static int storageIndexFromLayoutItem(const QFormLayoutPrivate::ItemMatrix &m,
273 QFormLayoutItem *item)
274{
275 if (item) {
276 return m.storage().indexOf(item);
277 } else {
278 return -1;
279 }
280}
281
282static void updateFormLayoutItem(QFormLayoutItem *item, int userVSpacing,
283 QFormLayout::FieldGrowthPolicy fieldGrowthPolicy,
284 bool fullRow)
285{
286 item->minSize = item->item->minimumSize();
287 item->sizeHint = item->item->sizeHint();
288 item->maxSize = item->item->maximumSize();
289
290 if (!fullRow && (fieldGrowthPolicy == QFormLayout::FieldsStayAtSizeHint
291 || (fieldGrowthPolicy == QFormLayout::ExpandingFieldsGrow
292 && !(item->item->expandingDirections() & Qt::Horizontal))))
293 item->maxSize.setWidth(item->sizeHint.width());
294
295 item->isHfw = item->item->hasHeightForWidth();
296 item->vSpace = userVSpacing;
297}
298
299/*
300 Iterate over all the controls and gather their size information
301 (min, sizeHint and max). Also work out what the spacing between
302 pairs of controls should be, and figure out the min and sizeHint
303 widths.
304*/
305void QFormLayoutPrivate::updateSizes()
306{
307 Q_Q(QFormLayout);
308
309 if (sizesDirty) {
310 QFormLayout::RowWrapPolicy wrapPolicy = q->rowWrapPolicy();
311 bool wrapAllRows = (wrapPolicy == QFormLayout::WrapAllRows);
312 bool dontWrapRows = (wrapPolicy == QFormLayout::DontWrapRows);
313 int rr = m_matrix.rowCount();
314
315 has_hfw = false;
316
317 // If any control can expand, so can this layout
318 // Wrapping doesn't affect expansion, though, just the minsize
319 bool expandH = false;
320 bool expandV = false;
321
322 QFormLayoutItem *prevLbl = 0;
323 QFormLayoutItem *prevFld = 0;
324
325 QWidget *parent = q->parentWidget();
326 QStyle *style = parent ? parent->style() : 0;
327
328 int userVSpacing = q->verticalSpacing();
329 int userHSpacing = wrapAllRows ? 0 : q->horizontalSpacing();
330
331 int maxMinLblWidth = 0;
332 int maxMinFldWidth = 0; // field with label
333 int maxMinIfldWidth = 0; // independent field
334
335 int maxShLblWidth = 0;
336 int maxShFldWidth = 0;
337 int maxShIfldWidth = 0;
338
339 for (int i = 0; i < rr; ++i) {
340 QFormLayoutItem *label = m_matrix(i, 0);
341 QFormLayoutItem *field = m_matrix(i, 1);
342
343 // Skip empty rows
344 if (!label && !field)
345 continue;
346
347 if (label) {
348 updateFormLayoutItem(label, userVSpacing, q->fieldGrowthPolicy(), false);
349 if (label->isHfw)
350 has_hfw = true;
351 Qt::Orientations o = label->expandingDirections();
352
353 if (o & Qt::Vertical)
354 expandV = true;
355 if (o & Qt::Horizontal)
356 expandH = true;
357 }
358 if (field) {
359 updateFormLayoutItem(field, userVSpacing, q->fieldGrowthPolicy(), !label && field->fullRow);
360 field->sbsHSpace = (!label && field->fullRow) ? 0 : userHSpacing;
361 if (field->isHfw)
362 has_hfw = true;
363
364 Qt::Orientations o = field->expandingDirections();
365
366 if (o & Qt::Vertical)
367 expandV = true;
368 if (o & Qt::Horizontal)
369 expandH = true;
370 }
371