1 /****************************************************************************
2 **
3 ** Copyright (C) 2015 The Qt Company Ltd.
4 ** Contact: http://www.qt.io/licensing/
5 **
6 ** This file is part of the QtGui module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see http://www.qt.io/terms-conditions. For further
15 ** information use the contact form at http://www.qt.io/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 or version 3 as published by the Free
20 ** Software Foundation and appearing in the file LICENSE.LGPLv21 and
21 ** LICENSE.LGPLv3 included in the packaging of this file. Please review the
22 ** following information to ensure the GNU Lesser General Public License
23 ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
24 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25 **
26 ** As a special exception, The Qt Company gives you certain additional
27 ** rights. These rights are described in The Qt Company LGPL Exception
28 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29 **
30 ** GNU General Public License Usage
31 ** Alternatively, this file may be used under the terms of the GNU
32 ** General Public License version 3.0 as published by the Free Software
33 ** Foundation and appearing in the file LICENSE.GPL included in the
34 ** packaging of this file.  Please review the following information to
35 ** ensure the GNU General Public License version 3.0 requirements will be
36 ** met: http://www.gnu.org/copyleft/gpl.html.
37 **
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 
52 QT_BEGIN_NAMESPACE
53 
54 namespace {
55 // Fixed column matrix, stores items as [i11, i12, i21, i22...],
56 // with FORTRAN-style index operator(r, c).
57 template <class T, int NumColumns>
58 class FixedColumnMatrix {
59 public:
60     typedef QVector<T> Storage;
61 
FixedColumnMatrix()62     FixedColumnMatrix() { }
63 
clear()64     void clear() { m_storage.clear(); }
65 
operator ()(int r,int c) const66     const T &operator()(int r, int c) const { return m_storage[r * NumColumns + c]; }
operator ()(int r,int c)67     T &operator()(int r, int c) { return m_storage[r * NumColumns + c]; }
68 
rowCount() const69     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 ;
count(const T & value) const75     int count(const T &value) const { return m_storage.count(value);  }
76 
77     // Hmmpf.. Some things are faster that way.
storage() const78     const Storage &storage() const { return m_storage; }
79 
80     static void storageIndexToPosition(int idx, int *rowPtr, int *colPtr);
81 
82 private:
83     Storage m_storage;
84 };
85 
86 template <class T, int NumColumns>
addRow(const T & value)87 void FixedColumnMatrix<T, NumColumns>::addRow(const T &value)
88 {
89     for (int i = 0; i < NumColumns; ++i)
90         m_storage.append(value);
91 }
92 
93 template <class T, int NumColumns>
insertRow(int r,const T & value)94 void 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 
101 template <class T, int NumColumns>
removeRow(int r)102 void FixedColumnMatrix<T, NumColumns>::removeRow(int r)
103 {
104     m_storage.remove(r * NumColumns, NumColumns);
105 }
106 
107 template <class T, int NumColumns>
find(const T & value,int * rowPtr,int * colPtr) const108 bool 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 
117 template <class T, int NumColumns>
storageIndexToPosition(int idx,int * rowPtr,int * colPtr)118 void 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
127 const uint DefaultFieldGrowthPolicy = 255;
128 const uint DefaultRowWrapPolicy = 255;
129 
130 enum { ColumnCount = 2 };
131 
132 // -- our data structure for our items
133 // This owns the QLayoutItem
134 struct QFormLayoutItem
135 {
QFormLayoutItemQFormLayoutItem136     QFormLayoutItem(QLayoutItem* i) : item(i), fullRow(false), isHfw(false) { }
~QFormLayoutItemQFormLayoutItem137     ~QFormLayoutItem() { delete item; }
138 
139     // Wrappers
widgetQFormLayoutItem140     QWidget *widget() const { return item->widget(); }
layoutQFormLayoutItem141     QLayout *layout() const { return item->layout(); }
142 
hasHeightForWidthQFormLayoutItem143     bool hasHeightForWidth() const { return item->hasHeightForWidth(); }
heightForWidthQFormLayoutItem144     int heightForWidth(int width) const { return item->heightForWidth(width); }
minimumHeightForWidthQFormLayoutItem145     int minimumHeightForWidth(int width) const { return item->minimumHeightForWidth(width); }
expandingDirectionsQFormLayoutItem146     Qt::Orientations expandingDirections() const { return item->expandingDirections(); }
controlTypesQFormLayoutItem147     QSizePolicy::ControlTypes controlTypes() const { return item->controlTypes(); }
vStretchQFormLayoutItem148     int vStretch() const { return widget() ? widget()->sizePolicy().verticalStretch() : 0; }
149 
setGeometryQFormLayoutItem150     void setGeometry(const QRect& r) { item->setGeometry(r); }
geometryQFormLayoutItem151     QRect geometry() const { return item->geometry(); }
152 
153     // For use with FixedColumnMatrix
operator ==QFormLayoutItem154     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 
178 class QFormLayoutPrivate : public QLayoutPrivate
179 {
180     Q_DECLARE_PUBLIC(QFormLayout)
181 
182 public:
183     typedef FixedColumnMatrix<QFormLayoutItem *, ColumnCount> ItemMatrix;
184 
185     QFormLayoutPrivate();
~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 
haveHfwCached(int width) const203     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 
QFormLayoutPrivate()251 QFormLayoutPrivate::QFormLayoutPrivate()
252     : fieldGrowthPolicy(DefaultFieldGrowthPolicy),
253       rowWrapPolicy(DefaultRowWrapPolicy), has_hfw(false), dirty(true), sizesDirty(true),
254       expandVertical(0), expandHorizontal(0), labelAlignment(0), formAlignment(0),
255       layoutWidth(-1), 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 
fixedAlignment(Qt::Alignment alignment,Qt::LayoutDirection layoutDirection)260 static 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 
storageIndexFromLayoutItem(const QFormLayoutPrivate::ItemMatrix & m,QFormLayoutItem * item)272 static 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 
updateFormLayoutItem(QFormLayoutItem * item,int userVSpacing,QFormLayout::FieldGrowthPolicy fieldGrowthPolicy,bool fullRow)282 static 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 */
updateSizes()305 void 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 
372             // See if we need to calculate default spacings
373             if ((userHSpacing < 0 || userVSpacing < 0) && style) {
374                 QSizePolicy::ControlTypes lbltypes =
375                     QSizePolicy::ControlTypes(label ? label->controlTypes() : QSizePolicy::DefaultType);
376                 QSizePolicy::ControlTypes fldtypes =
377                     QSizePolicy::ControlTypes(field ? field->controlTypes() : QSizePolicy::DefaultType);
378 
379                 // VSpacing
380                 if (userVSpacing < 0) {
381                     if (wrapAllRows) {
382                         // label spacing is to a previous item
383                         QFormLayoutItem *lbltop = prevFld ? prevFld : prevLbl;
384                         // field spacing is to the label (or a previous item)
385                         QFormLayoutItem *fldtop = label ? label : lbltop;
386                         QSizePolicy::ControlTypes lbltoptypes =
387                             QSizePolicy::ControlTypes(lbltop ? lbltop->controlTypes() : QSizePolicy::DefaultType);
388                         QSizePolicy::ControlTypes fldtoptypes =
389                             QSizePolicy::ControlTypes(fldtop ? fldtop->controlTypes() : QSizePolicy::DefaultType);
390                         if (label && lbltop)
391                             label->vSpace = style->combinedLayoutSpacing(lbltoptypes, lbltypes, Qt::Vertical, 0, parent);
392                         if (field && fldtop)
393                             field->vSpace = style->combinedLayoutSpacing(fldtoptypes, fldtypes, Qt::Vertical, 0, parent);
394                     } else {
395                         // Side by side..  we have to also consider the spacings to empty cells, which can strangely be more than
396                         // non empty cells..
397                         QFormLayoutItem *lbltop = prevLbl ? prevLbl : prevFld;
398                         QFormLayoutItem *fldtop = prevFld;
399                         QSizePolicy::ControlTypes lbltoptypes =
400                             QSizePolicy::ControlTypes(lbltop ? lbltop->controlTypes() : QSizePolicy::DefaultType);
401                         QSizePolicy::ControlTypes fldtoptypes =
402                             QSizePolicy::ControlTypes(fldtop ? fldtop->controlTypes() : QSizePolicy::DefaultType);
403 
404                         // To be compatible to QGridLayout, we have to compare solitary labels & fields with both predecessors
405                         if (label) {
406                             if (!field) {
407                                 int lblspacing = style->combinedLayoutSpacing(lbltoptypes, lbltypes, Qt::Vertical, 0, parent);
408                                 int fldspacing = style->combinedLayoutSpacing(fldtoptypes, lbltypes, Qt::Vertical, 0, parent);
409                                 label->vSpace = qMax(lblspacing, fldspacing);
410                             } else
411                                 label->vSpace = style->combinedLayoutSpacing(lbltoptypes, lbltypes, Qt::Vertical, 0, parent);
412                         }
413 
414                         if (field) {
415                             // check spacing against both the previous label and field
416                             if (!label) {
417                                 int lblspacing = style->combinedLayoutSpacing(lbltoptypes, fldtypes, Qt::Vertical, 0, parent);
418                                 int fldspacing = style->combinedLayoutSpacing(fldtoptypes, fldtypes, Qt::Vertical, 0, parent);
419                                 field->vSpace = qMax(lblspacing, fldspacing);
420                             } else
421                                 field->vSpace = style->combinedLayoutSpacing(fldtoptypes, fldtypes, Qt::Vertical, 0, parent);
422                         }
423                     }
424                 }
425 
426                 // HSpacing
427                 // hard-coded the left and right control types so that all the rows have the same
428                 // inter-column spacing (otherwise the right column isn't always left aligned)
429                 if (userHSpacing < 0 && !wrapAllRows && (label || !field->fullRow) && field)
430                     field->sbsHSpace = style->combinedLayoutSpacing(QSizePolicy::Label, QSizePolicy::LineEdit, Qt::Horizontal, 0, parent);
431             }
432 
433             // Now update our min/sizehint widths
434             // We choose to put the spacing in the field side in sbs, so
435             // the right edge of the labels will align, but fields may
436             // be a little ragged.. since different controls may have
437             // different appearances, a slight raggedness in the left
438             // edges of fields can be tolerated.
439             // (Note - field->sbsHSpace is 0 for WrapAllRows mode)
440             if (label) {
441                 maxMinLblWidth = qMax(maxMinLblWidth, label->minSize.width());
442                 maxShLblWidth = qMax(maxShLblWidth, label->sizeHint.width());
443                 if (field) {
444                     maxMinFldWidth = qMax(maxMinFldWidth, field->minSize.width() + field->sbsHSpace);
445                     maxShFldWidth = qMax(maxShFldWidth, field->sizeHint.width() + field->sbsHSpace);
446                 }
447             } else if (field) {
448                 maxMinIfldWidth = qMax(maxMinIfldWidth, field->minSize.width());
449                 maxShIfldWidth = qMax(maxShIfldWidth, field->sizeHint.width());
450             }
451 
452             prevLbl = label;
453             prevFld = field;
454         }
455 
456         // Now, finally update the min/sizeHint widths
457         if (wrapAllRows) {
458             sh_width = qMax(maxShLblWidth, qMax(maxShIfldWidth, maxShFldWidth));
459             min_width = qMax(maxMinLblWidth, qMax(maxMinIfldWidth, maxMinFldWidth));
460             // in two line, we don't care as much about the threshold width
461             thresh_width = 0;
462         } else if (dontWrapRows) {
463             // This is just the max widths glommed together
464             sh_width = qMax(maxShLblWidth + maxShFldWidth, maxShIfldWidth);
465             min_width = qMax(maxMinLblWidth + maxMinFldWidth, maxMinIfldWidth);
466             thresh_width = QWIDGETSIZE_MAX;
467         } else {
468             // This is just the max widths glommed together
469             sh_width = qMax(maxShLblWidth + maxShFldWidth, maxShIfldWidth);
470             // min width needs to be the min when everything is wrapped,
471             // otherwise we'll never get set with a width that causes wrapping
472             min_width = qMax(maxMinLblWidth, qMax(maxMinIfldWidth, maxMinFldWidth));
473             // We split a pair at label sh + field min (### for now..)
474             thresh_width = maxShLblWidth + maxMinFldWidth;
475         }
476 
477         // Update the expansions
478         expandVertical = expandV;
479         expandHorizontal = expandH;
480     }
481     sizesDirty = false;
482 }
483 
recalcHFW(int w)484 void QFormLayoutPrivate::recalcHFW(int w)
485 {
486     setupHfwLayoutData();
487 
488     int h = 0;
489     int mh = 0;
490 
491     for (int r = 0; r < vLayoutCount; ++r) {
492         int spacing = hfwLayouts.at(r).spacing;
493         h += hfwLayouts.at(r).sizeHint + spacing;
494         mh += hfwLayouts.at(r).minimumSize + spacing;
495     }
496 
497     if (sh_width > 0 && sh_width == w) {
498         hfw_sh_height = qMin(QLAYOUTSIZE_MAX, h);
499         hfw_sh_minheight = qMin(QLAYOUTSIZE_MAX, mh);
500     } else {
501         hfw_width = w;
502         hfw_height = qMin(QLAYOUTSIZE_MAX, h);
503         hfw_minheight = qMin(QLAYOUTSIZE_MAX, mh);
504     }
505 }
506 
setupHfwLayoutData()507 void QFormLayoutPrivate::setupHfwLayoutData()
508 {
509     // setupVerticalLayoutData must be called before this
510     // setupHorizontalLayoutData must also be called before this
511     // copies non hfw data into hfw
512     // then updates size and min
513 
514 
515     // Note: QGridLayout doesn't call minimumHeightForWidth,
516     // but instead uses heightForWidth for both min and sizeHint.
517     // For the common case where minimumHeightForWidth just calls
518     // heightForWidth, we do the calculation twice, which can be
519     // very expensive for word wrapped QLabels/QTextEdits, for example.
520     // So we just use heightForWidth as well.
521     int i;
522     int rr = m_matrix.rowCount();
523 
524     hfwLayouts.clear();
525     hfwLayouts.resize(vLayoutCount);
526     for (i = 0; i < vLayoutCount; ++i)
527         hfwLayouts[i] = vLayouts.at(i);
528 
529     for (i = 0; i < rr; ++i) {
530         QFormLayoutItem *label = m_matrix(i, 0);
531         QFormLayoutItem *field = m_matrix(i, 1);
532 
533         if (label) {
534             if (label->isHfw) {
535                 // We don't check sideBySide here, since a label is only
536                 // ever side by side with its field
537                 int hfw = label->heightForWidth(label->layoutWidth);
538                 hfwLayouts[label->vLayoutIndex].minimumSize = hfw;
539                 hfwLayouts[label->vLayoutIndex].sizeHint = hfw;
540             } else {
541                 // Reset these here, so the field can do a qMax below (the previous value may have
542                 // been the fields non-hfw values, which are often larger than hfw)
543                 hfwLayouts[label->vLayoutIndex].sizeHint = label->sizeHint.height();
544                 hfwLayouts[label->vLayoutIndex].minimumSize = label->minSize.height();
545             }
546         }
547 
548         if (field) {
549             int hfw = field->isHfw ? field->heightForWidth(field->layoutWidth) : 0;
550             int h = field->isHfw ? hfw : field->sizeHint.height();
551             int mh = field->isHfw ? hfw : field->minSize.height();
552 
553             if (field->sideBySide) {
554                 int oh = hfwLayouts.at(field->vLayoutIndex).sizeHint;
555                 int omh = hfwLayouts.at(field->vLayoutIndex).minimumSize;
556 
557                 hfwLayouts[field->vLayoutIndex].sizeHint = qMax(h, oh);
558                 hfwLayouts[field->vLayoutIndex].minimumSize = qMax(mh, omh);
559             } else {
560                 hfwLayouts[field->vLayoutIndex].sizeHint = h;
561                 hfwLayouts[field->vLayoutIndex].minimumSize = mh;
562             }
563         }
564     }
565 }
566 
567 /*
568   Given up to four items involved in a vertical spacing calculation
569   (two rows * two columns), return the max vertical spacing for the
570   row containing item1 (which may also include item2)
571   We assume parent and item1 are not null.
572 
573   If a particular row is split, then the spacings for that row and
574   the following row are affected, and this function should be
575   called with recalculate = true for both rows (note: only rows with both
576   a label and a field can be split).
577 
578   In particular:
579 
580   1) the split label's row vspace needs to be changed to qMax(label/prevLabel, label/prevField)
581     [call with item1 = label, item2 = null, prevItem1 & prevItem2 as before]
582   2) the split field's row vspace needs to be changed to the label/field spacing
583     [call with item1 = field, item2 = null, prevItem1 = label, prevItem2 = null]
584 
585  [if the next row has one item, 'item']
586   3a) the following row's vspace needs to be changed to item/field spacing (would
587       previously been the qMax(item/label, item/field) spacings)
588     [call with item1 = item, item2 = null, prevItem1 = field, prevItem2 = null]
589 
590   [if the next row has two items, 'label2' and 'field2']
591   3b) the following row's vspace needs to be changed to be qMax(field/label2, field/field2) spacing
592     [call with item1 = label2, item2 = field2, prevItem1 = field, prevItem2 = null]
593 
594   In the (common) non split case, we can just use the precalculated vspace (possibly qMaxed between
595   label and field).
596 
597   If recalculate is true, we expect:
598   -  parent != null
599   -  item1 != null
600   -  item2 can be null
601   -  prevItem1 can be null
602   -  if item2 is not null, prevItem2 will be null (e.g. steps 1 or 3 above)
603   -  if prevItem1 is null, prevItem2 will be null
604 */
spacingHelper(QWidget * parent,QStyle * style,int userVSpacing,bool recalculate,QFormLayoutItem * item1,QFormLayoutItem * item2,QFormLayoutItem * prevItem1,QFormLayoutItem * prevItem2)605 static inline int spacingHelper(QWidget* parent, QStyle *style, int userVSpacing, bool recalculate, QFormLayoutItem* item1, QFormLayoutItem* item2, QFormLayoutItem* prevItem1, QFormLayoutItem *prevItem2)
606 {
607     int spacing = userVSpacing;
608     if (spacing < 0) {
609         if (!recalculate) {
610             if (item1)
611                 spacing = item1->vSpace;
612             if (item2)
613                 spacing = qMax(spacing, item2->vSpace);
614         } else {
615             if (style && prevItem1) {
616                 QSizePolicy::ControlTypes itemtypes =
617                     QSizePolicy::ControlTypes(item1 ? item1->controlTypes() : QSizePolicy::DefaultType);
618                 int spacing2 = 0;
619 
620                 spacing = style->combinedLayoutSpacing(itemtypes, prevItem1->controlTypes(), Qt::Vertical, 0, parent);
621 
622                 // At most of one of item2 and prevItem2 will be nonnull
623                 if (item2)
624                     spacing2 = style->combinedLayoutSpacing(item2->controlTypes(), prevItem1->controlTypes(), Qt::Vertical, 0, parent);
625                 else if (prevItem2)
626                     spacing2 = style->combinedLayoutSpacing(itemtypes, prevItem2->controlTypes(), Qt::Vertical, 0, parent);
627 
628                 spacing = qMax(spacing, spacing2);
629             }
630         }
631     } else {
632         if (prevItem1) {
633             QWidget *wid = prevItem1->item->widget();
634             if (wid)
635                 spacing = qMax(spacing, prevItem1->geometry().top() - wid->geometry().top() );
636         }
637         if (prevItem2) {
638             QWidget *wid = prevItem2->item->widget();
639             if (wid)
640                 spacing = qMax(spacing, prevItem2->geometry().top() - wid->geometry().top() );
641         }
642     }
643     return spacing;
644 }
645 
initLayoutStruct(QLayoutStruct & sl,QFormLayoutItem * item)646 static inline void initLayoutStruct(QLayoutStruct& sl, QFormLayoutItem* item)
647 {
648     sl.init(item->vStretch(), item->minSize.height());
649     sl.sizeHint = item->sizeHint.height();
650     sl.maximumSize = item->maxSize.height();
651     sl.expansive = (item->expandingDirections() & Qt::Vertical);
652     sl.empty = false;
653 }
654 
setupVerticalLayoutData(int width)655 void QFormLayoutPrivate::setupVerticalLayoutData(int width)
656 {
657     Q_Q(QFormLayout);
658 
659     // Early out if we have no changes that would cause a change in vertical layout
660     if ((width == layoutWidth || (width >= thresh_width && layoutWidth >= thresh_width)) && !dirty && !sizesDirty)
661         return;
662 
663     layoutWidth = width;
664 
665     int rr = m_matrix.rowCount();
666     int vidx = 1;
667     QFormLayout::RowWrapPolicy rowWrapPolicy = q->rowWrapPolicy();
668     bool wrapAllRows = (rowWrapPolicy == QFormLayout::WrapAllRows);
669     bool addTopBottomStretch = true;
670 
671     vLayouts.clear();
672     vLayouts.resize((2 * rr) + 2); // a max, some may be unused
673 
674     QStyle *style = 0;
675 
676     int userVSpacing = q->verticalSpacing();
677 
678     if (userVSpacing < 0) {
679         if (QWidget *widget = q->parentWidget())
680             style = widget->style();
681     }
682 
683     // make sure our sizes are up to date
684     updateSizes();
685 
686     // Grab the widest label width here
687     // This might be different from the value computed during
688     // sizeHint/minSize, since we don't count label/field pairs that
689     // are split.
690     maxLabelWidth = 0;
691     if (!wrapAllRows) {
692         for (int i = 0; i < rr; ++i) {
693             const QFormLayoutItem *label = m_matrix(i, 0);
694             const QFormLayoutItem *field = m_matrix(i, 1);
695             if (label && (label->sizeHint.width() + (field ? field->minSize.width() : 0) <= width))
696                 maxLabelWidth = qMax(maxLabelWidth, label->sizeHint.width());
697         }
698     } else {
699         maxLabelWidth = width;
700     }
701 
702     QFormLayoutItem *prevItem1 = 0;
703     QFormLayoutItem *prevItem2 = 0;
704     bool prevRowSplit = false;
705 
706     for (int i = 0; i < rr; ++i) {
707         QFormLayoutItem *label =  m_matrix(i, 0);
708         QFormLayoutItem *field = m_matrix(i, 1);
709 
710         // Totally ignore empty rows...
711         if (!label && !field)
712             continue;
713 
714         QSize min1;
715         QSize min2;
716         QSize sh1;
717         QSize sh2;
718         if (label) {
719             min1 = label->minSize;
720             sh1 = label->sizeHint;
721         }
722         if (field) {
723             min2 = field->minSize;
724             sh2 = field->sizeHint;
725         }
726 
727         // In separate lines, we make a vLayout for everything that isn't null
728         // in side by side, we only separate label/field if we're going to wrap it
729         bool splitSideBySide = (rowWrapPolicy == QFormLayout::WrapLongRows)
730                                 && ((maxLabelWidth < sh1.width()) || (width < (maxLabelWidth + min2.width())));
731 
732         if (wrapAllRows || splitSideBySide) {
733             if (label) {
734                 initLayoutStruct(vLayouts[vidx], label);
735 
736                 if (vidx > 1)
737                     vLayouts[vidx - 1].spacing = spacingHelper(q->parentWidget(), style, userVSpacing, splitSideBySide || prevRowSplit, label, 0, prevItem1, prevItem2);
738 
739                 label->vLayoutIndex = vidx;
740                 label->sideBySide = false;
741 
742                 prevItem1 = label;
743                 prevItem2 = 0;
744 
745                 if (vLayouts[vidx].stretch > 0)
746                     addTopBottomStretch = false;
747 
748                 ++vidx;
749             }
750 
751             if (field) {
752                 initLayoutStruct(vLayouts[vidx], field);
753 
754                 if (vidx > 1)
755                     vLayouts[vidx - 1].spacing = spacingHelper(q->parentWidget(), style, userVSpacing, splitSideBySide || prevRowSplit, field, 0, prevItem1, prevItem2);
756 
757                 field->vLayoutIndex = vidx;
758                 field->sideBySide = false;
759 
760                 prevItem1 = field;
761                 prevItem2 = 0;
762 
763                 if (vLayouts[vidx].stretch > 0)
764                     addTopBottomStretch = false;
765 
766                 ++vidx;
767             }
768 
769             prevRowSplit = splitSideBySide;
770         } else {
771             // we're in side by side mode, and we have enough space to do that
772             QSize max1(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX);
773             QSize max2(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX);
774 
775             int stretch1 = 0;
776             int stretch2 = 0;
777             bool expanding = false;
778 
779             if (label) {
780                 max1 = label->maxSize;
781                 if (label->expandingDirections() & Qt::Vertical)
782                     expanding = true;
783 
784                 label->sideBySide = (field != 0);
785                 label->vLayoutIndex = vidx;
786                 stretch1 = label->vStretch();
787             }
788 
789             if (field) {
790                 max2 = field->maxSize;
791                 if (field->expandingDirections() & Qt::Vertical)
792                     expanding = true;
793 
794                 field->sideBySide = (label || !field->fullRow);
795                 field->vLayoutIndex = vidx;
796                 stretch2 = field->vStretch();
797             }
798 
799             vLayouts[vidx].init(qMax(stretch1, stretch2), qMax(min1.height(), min2.height()));
800             vLayouts[vidx].sizeHint = qMax(sh1.height(), sh2.height());
801             vLayouts[vidx].maximumSize = qMin(max1.height(), max2.height());
802             vLayouts[vidx].expansive = expanding || (vLayouts[vidx].stretch > 0);
803             vLayouts[vidx].empty = false;
804 
805             if (vLayouts[vidx].stretch > 0)
806                 addTopBottomStretch = false;
807 
808             if (vidx > 1)
809                 vLayouts[vidx - 1].spacing = spacingHelper(q->parentWidget(), style, userVSpacing, prevRowSplit, label, field, prevItem1, prevItem2);
810 
811             if (label) {
812                 prevItem1 = label;
813                 prevItem2 = field;
814             } else {
815                 prevItem1 = field;
816                 prevItem2 = 0;
817             }
818 
819             prevRowSplit = false;
820             ++vidx;
821         }
822     }
823 
824     if (addTopBottomStretch) {
825         Qt::Alignment formAlignment = q->formAlignment();
826 
827         if (!(formAlignment & Qt::AlignBottom)) {
828             // AlignTop (default if unspecified) or AlignVCenter: We add a stretch at the bottom
829             vLayouts[vidx].init(1, 0);
830             vLayouts[vidx].expansive = true;
831             ++vidx;
832         }
833 
834         if (formAlignment & (Qt::AlignVCenter | Qt::AlignBottom)) {
835             // AlignVCenter or AlignBottom: We add a stretch at the top
836             vLayouts[0].init(1, 0);
837             vLayouts[0].expansive = true;
838         } else {
839             vLayouts[0].init(0, 0);
840         }
841     } else {
842         vLayouts[0].init(0, 0);
843     }
844 
845     vLayoutCount = vidx;
846     dirty = false;
847 }
848 
setupHorizontalLayoutData(int width)849 void QFormLayoutPrivate::setupHorizontalLayoutData(int width)
850 {
851     Q_Q(QFormLayout);
852 
853     // requires setupVerticalLayoutData to be called first
854 
855     int fieldMaxWidth = 0;
856 
857     int rr = m_matrix.rowCount();
858     bool wrapAllRows = (q->rowWrapPolicy() == QFormLayout::WrapAllRows);
859 
860     for (int i = 0; i < rr; ++i) {
861         QFormLayoutItem *label = m_matrix(i, 0);
862         QFormLayoutItem *field = m_matrix(i, 1);
863 
864         // Totally ignore empty rows...
865         if (!label && !field)
866             continue;
867 
868         if (label) {
869             // if there is a field, and we're side by side, we use maxLabelWidth
870             // otherwise we just use the sizehint
871             label->layoutWidth = (field && label->sideBySide) ? maxLabelWidth : label->sizeHint.width();
872             label->layoutPos = 0;
873         }
874 
875         if (field) {
876             // This is the default amount allotted to fields in sbs
877             int fldwidth = width - maxLabelWidth - field->sbsHSpace;
878 
879             // If we've split a row, we still decide to align
880             // the field with all the other field if it will fit
881             // Fields in sbs mode get the remnants of the maxLabelWidth
882             if (!field->sideBySide) {
883                 if (wrapAllRows || (!label && field->fullRow) || field->sizeHint.width() > fldwidth) {
884                     field->layoutWidth = width;
885                     field->layoutPos = 0;
886                 } else {
887                     field->layoutWidth = fldwidth;
888                     field->layoutPos = width - fldwidth;
889                 }
890             } else {
891                 // We're sbs, so we should have a label
892                 field->layoutWidth = fldwidth;
893                 field->layoutPos = width - fldwidth;
894             }
895 
896             fieldMaxWidth = qMax(fieldMaxWidth, field->maxSize.width());
897         }
898     }
899 
900     formMaxWidth = maxLabelWidth + fieldMaxWidth;
901 }
902 
calcSizeHints()903 void QFormLayoutPrivate::calcSizeHints()
904 {
905     Q_Q(QFormLayout);
906 
907     int leftMargin, topMargin, rightMargin, bottomMargin;
908     q->getContentsMargins(&leftMargin, &topMargin, &rightMargin, &bottomMargin);
909 
910     updateSizes();
911     setupVerticalLayoutData(QLAYOUTSIZE_MAX);
912     // Don't need to call setupHorizontal here
913 
914     int h = topMargin + bottomMargin;
915     int mh = topMargin + bottomMargin;
916 
917     // The following are set in updateSizes
918     int w = sh_width + leftMargin + rightMargin;
919     int mw = min_width + leftMargin + rightMargin;
920 
921     for (int i = 0; i < vLayoutCount; ++i) {
922         int spacing = vLayouts.at(i).spacing;
923         h += vLayouts.at(i).sizeHint + spacing;
924         mh += vLayouts.at(i).minimumSize + spacing;
925     }
926 
927     minSize.rwidth() = qMin(mw, QLAYOUTSIZE_MAX);
928     minSize.rheight() = qMin(mh, QLAYOUTSIZE_MAX);
929     prefSize.rwidth() = qMin(w, QLAYOUTSIZE_MAX);
930     prefSize.rheight() = qMin(h, QLAYOUTSIZE_MAX);
931 }
932 
insertRow(int row)933 int QFormLayoutPrivate::insertRow(int row)
934 {
935     int rowCnt = m_matrix.rowCount();
936     if (uint(row) > uint(rowCnt))
937         row = rowCnt;
938 
939     insertRows(row, 1);
940     return row;
941 }
942 
insertRows(int row,int count)943 void QFormLayoutPrivate::insertRows(int row, int count)
944 {
945     while (count > 0) {
946         m_matrix.insertRow(row, 0);
947         --count;
948     }
949 }
950 
setItem(int row,QFormLayout::ItemRole role,QLayoutItem * item)951 void QFormLayoutPrivate::setItem(int row, QFormLayout::ItemRole role, QLayoutItem *item)
952 {
953     const bool fullRow = role == QFormLayout::SpanningRole;
954     const int column =  role == QFormLayout::SpanningRole ? 1 : static_cast<int>(role);
955     if (uint(row) >= uint(m_matrix.rowCount()) || uint(column) > 1U) {
956         qWarning("QFormLayoutPrivate::setItem: Invalid cell (%d, %d)", row, column);
957         return;
958     }
959 
960     if (!item)
961         return;
962 
963     if (m_matrix(row, column)) {
964         qWarning("QFormLayoutPrivate::setItem: Cell (%d, %d) already occupied", row, column);
965         return;
966     }
967 
968     QFormLayoutItem *i = new QFormLayoutItem(item);
969     i->fullRow = fullRow;
970     m_matrix(row, column) = i;
971 
972     m_things.append(i);
973 }
974 
setLayout(int row,QFormLayout::ItemRole role,QLayout * layout)975 void QFormLayoutPrivate::setLayout(int row, QFormLayout::ItemRole role, QLayout *layout)
976 {
977     if (layout) {
978         Q_Q(QFormLayout);
979         if (q->adoptLayout(layout))
980             setItem(row, role, layout);
981     }
982 }
983 
setWidget(int row,QFormLayout::ItemRole role,QWidget * widget)984 void QFormLayoutPrivate::setWidget(int row, QFormLayout::ItemRole role, QWidget *widget)
985 {
986     if (widget) {
987         Q_Q(QFormLayout);
988         q->addChildWidget(widget);
989         setItem(row, role, QLayoutPrivate::createWidgetItem(q, widget));
990     }
991 }
992 
getStyle() const993 QStyle* QFormLayoutPrivate::getStyle() const
994 {
995     Q_Q(const QFormLayout);
996 
997     // ### cache
998     if (QWidget *parentWidget = q->parentWidget())
999         return parentWidget->style();
1000     else
1001         return QApplication::style();
1002 }
1003 
1004 /*!
1005     \class QFormLayout
1006     \since 4.4
1007     \brief The QFormLayout class manages forms of input widgets and their associated labels.
1008 
1009     \ingroup geomanagement
1010 
1011 
1012     QFormLayout is a convenience layout class that lays out its
1013     children in a two-column form. The left column consists of labels
1014     and the right column consists of "field" widgets (line editors,
1015     spin boxes, etc.).
1016 
1017     Traditionally, such two-column form layouts were achieved using
1018     QGridLayout. QFormLayout is a higher-level alternative that
1019     provides the following advantages:
1020 
1021     \list
1022     \o \bold{Adherence to the different platform's look and feel guidelines.}
1023 
1024         For example, the
1025         \l{Mac OS X Aqua} and KDE guidelines specify that the
1026         labels should be right-aligned, whereas Windows and GNOME
1027         applications normally use left-alignment.
1028 
1029     \o \bold{Support for wrapping long rows.}
1030 
1031        For devices with small displays, QFormLayout can be set to
1032        \l{WrapLongRows}{wrap long rows}, or even to
1033        \l{WrapAllRows}{wrap all rows}.
1034 
1035     \o \bold{Convenient API for creating label--field pairs.}
1036 
1037        The addRow() overload that takes a QString and a QWidget *
1038        creates a QLabel behind the scenes and automatically set up
1039        its buddy. We can then write code like this:
1040 
1041     \snippet doc/src/snippets/code/src_gui_kernel_qformlayout.cpp 0
1042 
1043        Compare this with the following code, written using QGridLayout:
1044 
1045     \snippet doc/src/snippets/code/src_gui_kernel_qformlayout.cpp 1
1046     \endlist
1047 
1048     The table below shows the default appearance in different styles.
1049 
1050     \table
1051     \header
1052         \o QCommonStyle derived styles (except QPlastiqueStyle)
1053         \o QMacStyle
1054         \o QPlastiqueStyle
1055         \o Qt Extended styles
1056     \row
1057         \o \inlineimage qformlayout-win.png
1058         \o \inlineimage qformlayout-mac.png
1059         \o \inlineimage qformlayout-kde.png
1060         \o \inlineimage qformlayout-qpe.png
1061     \row
1062         \o Traditional style used for Windows, GNOME, and earlier
1063            versions of KDE. Labels are left aligned, and expanding
1064            fields grow to fill the available space. (This normally
1065            corresponds to what we would get using a two-column
1066            QGridLayout.)
1067         \o Style based on the
1068            \l{Mac OS X Aqua} guidelines. Labels are right-aligned,
1069            the fields don't grow beyond their size hint, and the
1070            form is horizontally centered.
1071         \o Recommended style for
1072            \l{KDE applications}. Similar to MacStyle, except that the form
1073            is left-aligned and all fields grow to fill the available
1074            space.
1075         \o Default style for Qt Extended styles. Labels are right-aligned,
1076            expanding fields grow to fill the available space, and row
1077            wrapping is enabled for long lines.
1078     \endtable
1079 
1080     The form styles can be also be overridden individually by calling
1081     setLabelAlignment(), setFormAlignment(), setFieldGrowthPolicy(),
1082     and setRowWrapPolicy().  For example, to simulate the form layout
1083     appearance of QMacStyle on all platforms, but with left-aligned
1084     labels, you could write:
1085 
1086     \snippet doc/src/snippets/code/src_gui_kernel_qformlayout.cpp 2
1087 
1088     \sa QGridLayout, QBoxLayout, QStackedLayout
1089 */
1090 
1091 
1092 /*!
1093     \enum QFormLayout::FieldGrowthPolicy
1094 
1095     This enum specifies the different policies that can be used to
1096     control the way in which the form's fields grow.
1097 
1098     \value FieldsStayAtSizeHint
1099            The fields never grow beyond their
1100            \l{QWidgetItem::sizeHint()}{effective size hint}. This is
1101            the default for QMacStyle.
1102 
1103     \value ExpandingFieldsGrow
1104            Fields with an horizontal \l{QSizePolicy}{size policy} of
1105            \l{QSizePolicy::}{Expanding} or
1106            \l{QSizePolicy::}{MinimumExpanding} will grow to fill the
1107            available space. The other fields will not grow beyond
1108            their effective size hint. This is the default policy for
1109            Plastique.
1110 
1111     \value AllNonFixedFieldsGrow
1112            All fields with a size policy that allows them to grow
1113            will grow to fill the available space. This is the default
1114            policy for most styles.
1115 
1116     \sa fieldGrowthPolicy
1117 */
1118 
1119 /*!
1120     \enum QFormLayout::RowWrapPolicy
1121 
1122     This enum specifies the different policies that can be used to
1123     control the way in which the form's rows wrap.
1124 
1125     \value DontWrapRows
1126            Fields are always laid out next to their label.  This is
1127            the default policy for all styles except Qt Extended styles
1128            and QS60Style.
1129 
1130     \value WrapLongRows
1131            Labels are given enough horizontal space to fit the widest label,
1132            and the rest of the space is given to the fields. If the minimum
1133            size of a field pair is wider than the available space, the field
1134            is wrapped to the next line.  This is the default policy for
1135            Qt Extended styles and and QS60Style.
1136 
1137     \value WrapAllRows
1138            Fields are always laid out below their label.
1139 
1140     \sa rowWrapPolicy
1141 */
1142 
1143 /*!
1144     \enum QFormLayout::ItemRole
1145 
1146     This enum specifies the types of widgets (or other layout items)
1147     that may appear in a row.
1148 
1149     \value LabelRole A label widget.
1150     \value FieldRole A field widget.
1151     \value SpanningRole A widget that spans label and field columns.
1152 
1153     \sa itemAt(), getItemPosition()
1154 */
1155 
1156 /*!
1157     Constructs a new form layout with the given \a parent widget.
1158 
1159     \sa QWidget::setLayout()
1160 */
QFormLayout(QWidget * parent)1161 QFormLayout::QFormLayout(QWidget *parent)
1162     : QLayout(*new QFormLayoutPrivate, 0, parent)
1163 {
1164 }
1165 
1166 /*!
1167     Destroys the form layout.
1168 */
~QFormLayout()1169 QFormLayout::~QFormLayout()
1170 {
1171     Q_D(QFormLayout);
1172 
1173     /*
1174         The clearing and destruction order here is important. We start by clearing
1175         m_things so that QLayout and the rest of the world know that we don't babysit
1176         the layout items anymore and don't care if they are destroyed.
1177     */
1178     d->m_things.clear();
1179     qDeleteAll(d->m_matrix.storage());
1180     d->m_matrix.clear();
1181 }
1182 
1183 /*!
1184     Adds a new row to the bottom of this form layout, with the given
1185     \a label and \a field.
1186 
1187     \sa insertRow()
1188 */
addRow(QWidget * label,QWidget * field)1189 void QFormLayout::addRow(QWidget *label, QWidget *field)
1190 {
1191     insertRow(-1, label, field);
1192 }
1193 
1194 /*!
1195     \overload
1196 */
addRow(QWidget * label,QLayout * field)1197 void QFormLayout::addRow(QWidget *label, QLayout *field)
1198 {
1199     insertRow(-1, label, field);
1200 }
1201 
1202 /*!
1203     \overload
1204 
1205     This overload automatically creates a QLabel behind the scenes
1206     with \a labelText as its text. The \a field is set as the new
1207     QLabel's \l{QLabel::setBuddy()}{buddy}.
1208 */
addRow(const QString & labelText,QWidget * field)1209 void QFormLayout::addRow(const QString &labelText, QWidget *field)
1210 {
1211     insertRow(-1, labelText, field);
1212 }
1213 
1214 /*!
1215     \overload
1216 
1217     This overload automatically creates a QLabel behind the scenes
1218     with \a labelText as its text.
1219 */
addRow(const QString & labelText,QLayout * field)1220 void QFormLayout::addRow(const QString &labelText, QLayout *field)
1221 {
1222     insertRow(-1, labelText, field);
1223 }
1224 
1225 /*!
1226     \overload
1227 
1228     Adds the specified \a widget at the end of this form layout. The
1229     \a widget spans both columns.
1230 */
addRow(QWidget * widget)1231 void QFormLayout::addRow(QWidget *widget)
1232 {
1233     insertRow(-1, widget);
1234 }
1235 
1236 /*!
1237     \overload
1238 
1239     Adds the specified \a layout at the end of this form layout. The
1240     \a layout spans both columns.
1241 */
addRow(QLayout * layout)1242 void QFormLayout::addRow(QLayout *layout)
1243 {
1244     insertRow(-1, layout);
1245 }
1246 
1247 /*!
1248     Inserts a new row at position \a row in this form layout, with
1249     the given \a label and \a field. If \a row is out of bounds, the
1250     new row is added at the end.
1251 
1252     \sa addRow()
1253 */
insertRow(int row,QWidget * label,QWidget * field)1254 void QFormLayout::insertRow(int row, QWidget *label, QWidget *field)
1255 {
1256     Q_D(QFormLayout);
1257 
1258     row = d->insertRow(row);
1259     if (label)
1260         d->setWidget(row, LabelRole, label);
1261     if (field)
1262         d->setWidget(row, FieldRole, field);
1263     invalidate();
1264 }
1265 
1266 /*!
1267     \overload
1268 */
insertRow(int row,QWidget * label,QLayout * field)1269 void QFormLayout::insertRow(int row, QWidget *label, QLayout *field)
1270 {
1271     Q_D(QFormLayout);
1272 
1273     row = d->insertRow(row);
1274     if (label)
1275         d->setWidget(row, LabelRole, label);
1276     if (field)
1277         d->setLayout(row, FieldRole, field);
1278     invalidate();
1279 }
1280 
1281 /*!
1282     \overload
1283 
1284     This overload automatically creates a QLabel behind the scenes
1285     with \a labelText as its text. The \a field is set as the new
1286     QLabel's \l{QLabel::setBuddy()}{buddy}.
1287 */
insertRow(int row,const QString & labelText,QWidget * field)1288 void QFormLayout::insertRow(int row, const QString &labelText, QWidget *field)
1289 {
1290     QLabel *label = 0;
1291     if (!labelText.isEmpty()) {
1292         label = new QLabel(labelText);
1293 #ifndef QT_NO_SHORTCUT
1294         label->setBuddy(field);
1295 #endif
1296     }
1297     insertRow(row, label, field);
1298 }
1299 
1300 /*!
1301     \overload
1302 
1303     This overload automatically creates a QLabel behind the scenes
1304     with \a labelText as its text.
1305 */
insertRow(int row,const QString & labelText,QLayout * field)1306 void QFormLayout::insertRow(int row, const QString &labelText, QLayout *field)
1307 {
1308     insertRow(row, labelText.isEmpty() ? 0 : new QLabel(labelText), field);
1309 }
1310 
1311 /*!
1312     \overload
1313 
1314     Inserts the specified \a widget at position \a row in this form
1315     layout. The \a widget spans both columns. If \a row is out of
1316     bounds, the widget is added at the end.
1317 */
insertRow(int row,QWidget * widget)1318 void QFormLayout::insertRow(int row, QWidget *widget)
1319 {
1320     Q_D(QFormLayout);
1321 
1322     if (!widget) {
1323         qWarning("QFormLayout: Cannot add null field to %s", qPrintable(objectName()));
1324         return;
1325     }
1326 
1327     row = d->insertRow(row);
1328     d->setWidget(row, SpanningRole, widget);
1329     invalidate();
1330 }
1331 
1332 /*!
1333     \overload
1334 
1335     Inserts the specified \a layout at position \a row in this form
1336     layout. The \a layout spans both columns. If \a row is out of
1337     bounds, the widget is added at the end.
1338 */
insertRow(int row,QLayout * layout)1339 void QFormLayout::insertRow(int row, QLayout *layout)
1340 {
1341     Q_D(QFormLayout);
1342 
1343     if (!layout) {
1344         qWarning("QFormLayout: Cannot add null field to %s", qPrintable(objectName()));
1345         return;
1346     }
1347 
1348     row = d->insertRow(row);
1349     d->setLayout(row, SpanningRole, layout);
1350     invalidate();
1351 }
1352 
1353 /*!
1354     \reimp
1355 */
addItem(QLayoutItem * item)1356 void QFormLayout::addItem(QLayoutItem *item)
1357 {
1358     Q_D(QFormLayout);
1359 
1360     int row = d->insertRow(d->m_matrix.rowCount());
1361     d->setItem(row, FieldRole, item);
1362     invalidate();
1363 }
1364 
1365 /*!
1366     \reimp
1367 */
count() const1368 int QFormLayout::count() const
1369 {
1370     Q_D(const QFormLayout);
1371     return d->m_things.count();
1372 }
1373 
1374 /*!
1375     \reimp
1376 */
itemAt(int index) const1377 QLayoutItem *QFormLayout::itemAt(int index) const
1378 {
1379     Q_D(const QFormLayout);
1380     if (QFormLayoutItem *formItem = d->m_things.value(index))
1381         return formItem->item;
1382     return 0;
1383 }
1384 
1385 /*!
1386     \reimp
1387 */
takeAt(int index)1388 QLayoutItem *QFormLayout::takeAt(int index)
1389 {
1390     Q_D(QFormLayout);
1391 
1392     const int storageIndex = storageIndexFromLayoutItem(d->m_matrix, d->m_things.value(index));
1393     if (storageIndex == -1) {
1394         qWarning("QFormLayout::takeAt: Invalid index %d", index);
1395         return 0;
1396     }
1397 
1398     int row, col;
1399     QFormLayoutPrivate::ItemMatrix::storageIndexToPosition(storageIndex, &row, &col);
1400     Q_ASSERT(d->m_matrix(row, col));
1401 
1402     QFormLayoutItem *item = d->m_matrix(row, col);
1403     Q_ASSERT(item);
1404     d->m_things.removeAt(index);
1405     d->m_matrix(row, col) = 0;
1406 
1407     invalidate();
1408 
1409     // grab ownership back from the QFormLayoutItem
1410     QLayoutItem *i = item->item;
1411     item->item = 0;
1412     delete item;
1413 
1414     if (QLayout *l = i->layout()) {
1415         // sanity check in case the user passed something weird to QObject::setParent()
1416         if (l->parent() == this)
1417             l->setParent(0);
1418     }
1419 
1420     return i;
1421 }
1422 
1423 /*!
1424     \reimp
1425 */
expandingDirections() const1426 Qt::Orientations QFormLayout::expandingDirections() const
1427 {
1428     Q_D(const QFormLayout);
1429     QFormLayoutPrivate *e = const_cast<QFormLayoutPrivate *>(d);
1430     e->updateSizes();
1431 
1432     Qt::Orientations o = 0;
1433     if (e->expandHorizontal)
1434         o = Qt::Horizontal;
1435     if (e->expandVertical)
1436         o |= Qt::Vertical;
1437     return o;
1438 }
1439 
1440 /*!
1441     \reimp
1442 */
hasHeightForWidth() const1443 bool QFormLayout::hasHeightForWidth() const
1444 {
1445     Q_D(const QFormLayout);
1446     QFormLayoutPrivate *e = const_cast<QFormLayoutPrivate *>(d);
1447     e->updateSizes();
1448     return (d->has_hfw || rowWrapPolicy() == WrapLongRows);
1449 }
1450 
1451 /*!
1452     \reimp
1453 */
heightForWidth(int width) const1454 int QFormLayout::heightForWidth(int width) const
1455 {
1456     Q_D(const QFormLayout);
1457     if (!hasHeightForWidth())
1458         return -1;
1459 
1460     int leftMargin, topMargin, rightMargin, bottomMargin;
1461     getContentsMargins(&leftMargin, &topMargin, &rightMargin, &bottomMargin);
1462 
1463     int targetWidth = width - leftMargin - rightMargin;
1464 
1465     if (!d->haveHfwCached(targetWidth)) {
1466         QFormLayoutPrivate *dat = const_cast<QFormLayoutPrivate *>(d);
1467         dat->setupVerticalLayoutData(targetWidth);
1468         dat->setupHorizontalLayoutData(targetWidth);
1469         dat->recalcHFW(targetWidth);
1470     }
1471     if (targetWidth == d->sh_width)
1472         return d->hfw_sh_height + topMargin + bottomMargin;
1473     else
1474         return d->hfw_height + topMargin + bottomMargin;
1475 }
1476 
1477 /*!
1478     \reimp
1479 */
setGeometry(const QRect & rect)1480 void QFormLayout::setGeometry(const QRect &rect)
1481 {
1482     Q_D(QFormLayout);
1483     if (d->dirty || rect != geometry()) {
1484         QRect cr = rect;
1485         int leftMargin, topMargin, rightMargin, bottomMargin;
1486         getContentsMargins(&leftMargin, &topMargin, &rightMargin, &bottomMargin);
1487         cr.adjust(+leftMargin, +topMargin, -rightMargin, -bottomMargin);
1488 
1489         bool hfw = hasHeightForWidth();
1490         d->setupVerticalLayoutData(cr.width());
1491         d->setupHorizontalLayoutData(cr.width());
1492         if (hfw && (!d->haveHfwCached(cr.width()) || d->hfwLayouts.size() != d->vLayoutCount))
1493             d->recalcHFW(cr.width());
1494         if (hfw) {
1495             qGeomCalc(d->hfwLayouts, 0, d->vLayoutCount, cr.y(), cr.height());
1496             d->arrangeWidgets(d->hfwLayouts, cr);
1497         } else {
1498             qGeomCalc(d->vLayouts, 0, d->vLayoutCount, cr.y(), cr.height());
1499             d->arrangeWidgets(d->vLayouts, cr);
1500         }
1501         QLayout::setGeometry(rect);
1502     }
1503 }
1504 
1505 /*!
1506     \reimp
1507 */
sizeHint() const1508 QSize QFormLayout::sizeHint() const
1509 {
1510     Q_D(const QFormLayout);
1511     if (!d->prefSize.isValid()) {
1512         QFormLayoutPrivate *dat = const_cast<QFormLayoutPrivate *>(d);
1513         dat->calcSizeHints();
1514     }
1515     return d->prefSize;
1516 }
1517 
1518 /*!
1519     \reimp
1520 */
minimumSize() const1521 QSize QFormLayout::minimumSize() const
1522 {
1523     // ### fix minimumSize if hfw
1524     Q_D(const QFormLayout);
1525     if (!d->minSize.isValid()) {
1526         QFormLayoutPrivate *dat = const_cast<QFormLayoutPrivate *>(d);
1527         dat->calcSizeHints();
1528     }
1529     return d->minSize;
1530 }
1531 
1532 /*!
1533     \reimp
1534 */
invalidate()1535 void QFormLayout::invalidate()
1536 {
1537     Q_D(QFormLayout);
1538     d->dirty = true;
1539     d->sizesDirty = true;
1540     d->minSize = QSize();
1541     d->prefSize = QSize();
1542     d->formMaxWidth = -1;
1543     d->hfw_width = -1;
1544     d->sh_width = -1;
1545     d->layoutWidth = -1;
1546     d->hfw_sh_height = -1;
1547     QLayout::invalidate();
1548 }
1549 
1550 /*!
1551     Returns the number of rows in the form.
1552 
1553     \sa QLayout::count()
1554 */
rowCount() const1555 int QFormLayout::rowCount() const
1556 {
1557     Q_D(const QFormLayout);
1558     return d->m_matrix.rowCount();
1559 }
1560 
1561 /*!
1562     Returns the layout item in the given \a row with the specified \a
1563     role (column). Returns 0 if there is no such item.
1564 
1565     \sa QLayout::itemAt(), setItem()
1566 */
itemAt(int row,ItemRole role) const1567 QLayoutItem *QFormLayout::itemAt(int row, ItemRole role) const
1568 {
1569     Q_D(const QFormLayout);
1570     if (uint(row) >= uint(d->m_matrix.rowCount()))
1571         return 0;
1572     switch (role) {
1573     case SpanningRole:
1574         if (QFormLayoutItem *item = d->m_matrix(row, 1))
1575             if (item->fullRow)
1576                 return item->item;
1577         break;
1578     case LabelRole:
1579     case FieldRole:
1580         if (QFormLayoutItem *item = d->m_matrix(row, (role == LabelRole) ? 0 : 1))
1581             return item->item;
1582         break;
1583     }
1584     return 0;
1585 }
1586 
1587 /*!
1588     Retrieves the row and role (column) of the item at the specified
1589     \a index. If \a index is out of bounds, *\a rowPtr is set to -1;
1590     otherwise the row is stored in *\a rowPtr and the role is stored
1591     in *\a rolePtr.
1592 
1593     \sa itemAt(), count(), getLayoutPosition(), getWidgetPosition()
1594 */
getItemPosition(int index,int * rowPtr,ItemRole * rolePtr) const1595 void QFormLayout::getItemPosition(int index, int *rowPtr, ItemRole *rolePtr) const
1596 {
1597     Q_D(const QFormLayout);
1598     int col = -1;
1599     int row = -1;
1600 
1601     const int storageIndex = storageIndexFromLayoutItem(d->m_matrix, d->m_things.value(index));
1602     if (storageIndex != -1)
1603         QFormLayoutPrivate::ItemMatrix::storageIndexToPosition(storageIndex, &row, &col);
1604 
1605     if (rowPtr)
1606         *rowPtr = row;
1607     if (rolePtr && col != -1) {
1608         const bool spanning = col == 1 && d->m_matrix(row, col)->fullRow;
1609         if (spanning) {
1610             *rolePtr = SpanningRole;
1611         } else {
1612             *rolePtr = ItemRole(col);
1613         }
1614     }
1615 }
1616 
1617 /*!
1618     Retrieves the row and role (column) of the specified child \a
1619     layout. If \a layout is not in the form layout, *\a rowPtr is set
1620     to -1; otherwise the row is stored in *\a rowPtr and the role is stored
1621     in *\a rolePtr.
1622 */
getLayoutPosition(QLayout * layout,int * rowPtr,ItemRole * rolePtr) const1623 void QFormLayout::getLayoutPosition(QLayout *layout, int *rowPtr, ItemRole *rolePtr) const
1624 {
1625     int n = count();
1626     int index = 0;
1627     while (index < n) {
1628         if (itemAt(index) == layout)
1629             break;
1630         ++index;
1631     }
1632     getItemPosition(index, rowPtr, rolePtr);
1633 }
1634 
1635 /*!
1636     Retrieves the row and role (column) of the specified \a widget in
1637     the layout. If \a widget is not in the layout, *\a rowPtr is set
1638     to -1; otherwise the row is stored in *\a rowPtr and the role is stored
1639     in *\a rolePtr.
1640 
1641     \sa getItemPosition(), itemAt()
1642 */
getWidgetPosition(QWidget * widget,int * rowPtr,ItemRole * rolePtr) const1643 void QFormLayout::getWidgetPosition(QWidget *widget, int *rowPtr, ItemRole *rolePtr) const
1644 {
1645     getItemPosition(indexOf(widget), rowPtr, rolePtr);
1646 }
1647 
1648 // ### eliminate labelForField()
1649 
1650 /*!
1651     Returns the label associated with the given \a field.
1652 
1653     \sa itemAt()
1654 */
labelForField(QWidget * field) const1655 QWidget *QFormLayout::labelForField(QWidget *field) const
1656 {
1657     Q_D(const QFormLayout);
1658 
1659     int row;
1660     ItemRole role;
1661 
1662     getWidgetPosition(field, &row, &role);
1663 
1664     if (row != -1 && role == FieldRole) {
1665         if (QFormLayoutItem *label = d->m_matrix(row, LabelRole))
1666             return label->widget();
1667     }
1668     return 0;
1669 }
1670 
1671 /*!
1672     \overload
1673 */
labelForField(QLayout * field) const1674 QWidget *QFormLayout::labelForField(QLayout *field) const
1675 {
1676     Q_D(const QFormLayout);
1677 
1678     int row;
1679     ItemRole role;
1680 
1681     getLayoutPosition(field, &row, &role);
1682 
1683     if (row != -1 && role == FieldRole) {
1684         if (QFormLayoutItem *label = d->m_matrix(row, LabelRole))
1685             return label->widget();
1686     }
1687     return 0;
1688 }
1689 
1690 /*!
1691     \property QFormLayout::fieldGrowthPolicy
1692     \brief the way in which the form's fields grow
1693 
1694     The default value depends on the widget or application style. For
1695     QMacStyle, the default is FieldsStayAtSizeHint; for QCommonStyle
1696     derived styles (like Plastique and Windows), the default
1697     is ExpandingFieldsGrow; for Qt Extended styles, the default is
1698     AllNonFixedFieldsGrow.
1699 
1700     If none of the fields can grow and the form is resized, extra
1701     space is distributed according to the current
1702     \l{formAlignment}{form alignment}.
1703 
1704     \sa formAlignment, rowWrapPolicy
1705 */
1706 
setFieldGrowthPolicy(FieldGrowthPolicy policy)1707 void QFormLayout::setFieldGrowthPolicy(FieldGrowthPolicy policy)
1708 {
1709     Q_D(QFormLayout);
1710     if (FieldGrowthPolicy(d->fieldGrowthPolicy) != policy) {
1711         d->fieldGrowthPolicy = policy;
1712         invalidate();
1713     }
1714 }
1715 
fieldGrowthPolicy() const1716 QFormLayout::FieldGrowthPolicy QFormLayout::fieldGrowthPolicy() const
1717 {
1718     Q_D(const QFormLayout);
1719     if (d->fieldGrowthPolicy == DefaultFieldGrowthPolicy) {
1720         return QFormLayout::FieldGrowthPolicy(d->getStyle()->styleHint(QStyle::SH_FormLayoutFieldGrowthPolicy));
1721     } else {
1722         return QFormLayout::FieldGrowthPolicy(d->fieldGrowthPolicy);
1723     }
1724 }
1725 
1726 /*!
1727     \property QFormLayout::rowWrapPolicy
1728     \brief the way in which the form's rows wrap
1729 
1730     The default value depends on the widget or application style. For
1731     Qt Extended styles and QS60Style, the default is WrapLongRows;
1732     for the other styles, the default is DontWrapRows.
1733 
1734     If you want to display each label above its associated field
1735     (instead of next to it), set this property to WrapAllRows.
1736 
1737     \sa fieldGrowthPolicy
1738 */
1739 
setRowWrapPolicy(RowWrapPolicy policy)1740 void QFormLayout::setRowWrapPolicy(RowWrapPolicy policy)
1741 {
1742     Q_D(QFormLayout);
1743     if (RowWrapPolicy(d->rowWrapPolicy) != policy) {
1744         d->rowWrapPolicy = policy;
1745         invalidate();
1746     }
1747 }
1748 
rowWrapPolicy() const1749 QFormLayout::RowWrapPolicy QFormLayout::rowWrapPolicy() const
1750 {
1751     Q_D(const QFormLayout);
1752     if (d->rowWrapPolicy == DefaultRowWrapPolicy) {
1753         return QFormLayout::RowWrapPolicy(d->getStyle()->styleHint(QStyle::SH_FormLayoutWrapPolicy));
1754     } else {
1755         return QFormLayout::RowWrapPolicy(d->rowWrapPolicy);
1756     }
1757 }
1758 
1759 /*!
1760     \property QFormLayout::labelAlignment
1761     \brief the horizontal alignment of the labels
1762 
1763     The default value depends on the widget or application style. For
1764     QCommonStyle derived styles, except for QPlastiqueStyle, the
1765     default is Qt::AlignLeft; for the other styles, the default is
1766     Qt::AlignRight.
1767 
1768     \sa formAlignment
1769 */
1770 
setLabelAlignment(Qt::Alignment alignment)1771 void QFormLayout::setLabelAlignment(Qt::Alignment alignment)
1772 {
1773     Q_D(QFormLayout);
1774     if (d->labelAlignment != alignment) {
1775         d->labelAlignment = alignment;
1776         invalidate();
1777     }
1778 }
1779 
labelAlignment() const1780 Qt::Alignment QFormLayout::labelAlignment() const
1781 {
1782     Q_D(const QFormLayout);
1783     if (!d->labelAlignment) {
1784         return Qt::Alignment(d->getStyle()->styleHint(QStyle::SH_FormLayoutLabelAlignment));
1785     } else {
1786         return d->labelAlignment;
1787     }
1788 }
1789 
1790 /*!
1791     \property QFormLayout::formAlignment
1792     \brief the alignment of the form layout's contents within the layout's geometry
1793 
1794     The default value depends on the widget or application style. For
1795     QMacStyle, the default is Qt::AlignHCenter | Qt::AlignTop; for the
1796     other styles, the default is Qt::AlignLeft | Qt::AlignTop.
1797 
1798     \sa labelAlignment, rowWrapPolicy
1799 */
1800 
setFormAlignment(Qt::Alignment alignment)1801 void QFormLayout::setFormAlignment(Qt::Alignment alignment)
1802 {
1803     Q_D(QFormLayout);
1804     if (d->formAlignment != alignment) {
1805         d->formAlignment = alignment;
1806         invalidate();
1807     }
1808 }
1809 
formAlignment() const1810 Qt::Alignment QFormLayout::formAlignment() const
1811 {
1812     Q_D(const QFormLayout);
1813     if (!d->formAlignment) {
1814         return Qt::Alignment(d->getStyle()->styleHint(QStyle::SH_FormLayoutFormAlignment));
1815     } else {
1816         return d->formAlignment;
1817     }
1818 }
1819 
1820 /*!
1821     \property QFormLayout::horizontalSpacing
1822     \brief the spacing between widgets that are laid out side by side
1823 
1824     By default, if no value is explicitly set, the layout's horizontal
1825     spacing is inherited from the parent layout, or from the style settings
1826     for the parent widget.
1827 
1828     \sa verticalSpacing, QStyle::pixelMetric(), {QStyle::}{PM_LayoutHorizontalSpacing}
1829 */
setHorizontalSpacing(int spacing)1830 void QFormLayout::setHorizontalSpacing(int spacing)
1831 {
1832     Q_D(QFormLayout);
1833     if (spacing != d->hSpacing) {
1834         d->hSpacing = spacing;
1835         invalidate();
1836     }
1837 }
1838 
horizontalSpacing() const1839 int QFormLayout::horizontalSpacing() const
1840 {
1841     Q_D(const QFormLayout);
1842     if (d->hSpacing >= 0) {
1843         return d->hSpacing;
1844     } else {
1845         return qSmartSpacing(this, QStyle::PM_LayoutHorizontalSpacing);
1846     }
1847 }
1848 
1849 /*!
1850     \property QFormLayout::verticalSpacing
1851     \brief the spacing between widgets that are laid out vertically
1852 
1853     By default, if no value is explicitly set, the layout's vertical spacing is
1854     inherited from the parent layout, or from the style settings for the parent
1855     widget.
1856 
1857     \sa horizontalSpacing, QStyle::pixelMetric(), {QStyle::}{PM_LayoutHorizontalSpacing}
1858 */
setVerticalSpacing(int spacing)1859 void QFormLayout::setVerticalSpacing(int spacing)
1860 {
1861     Q_D(QFormLayout);
1862     if (spacing != d->vSpacing) {
1863         d->vSpacing = spacing;
1864         invalidate();
1865     }
1866 }
1867 
verticalSpacing() const1868 int QFormLayout::verticalSpacing() const
1869 {
1870     Q_D(const QFormLayout);
1871     if (d->vSpacing >= 0) {
1872         return d->vSpacing;
1873     } else {
1874         return qSmartSpacing(this, QStyle::PM_LayoutVerticalSpacing);
1875     }
1876 }
1877 
1878 /*!
1879     This function sets both the vertical and horizontal spacing to
1880     \a spacing.
1881 
1882     \sa setVerticalSpacing(), setHorizontalSpacing()
1883 */
setSpacing(int spacing)1884 void QFormLayout::setSpacing(int spacing)
1885 {
1886     Q_D(QFormLayout);
1887     d->vSpacing = d->hSpacing = spacing;
1888     invalidate();
1889 }
1890 
1891 /*!
1892     If the vertical spacing is equal to the horizontal spacing,
1893     this function returns that value; otherwise it returns -1.
1894 
1895     \sa setSpacing(), verticalSpacing(), horizontalSpacing()
1896 */
spacing() const1897 int QFormLayout::spacing() const
1898 {
1899     int hSpacing = horizontalSpacing();
1900     if (hSpacing == verticalSpacing()) {
1901         return hSpacing;
1902     } else {
1903         return -1;
1904     }
1905 }
1906 
arrangeWidgets(const QVector<QLayoutStruct> & layouts,QRect & rect)1907 void QFormLayoutPrivate::arrangeWidgets(const QVector<QLayoutStruct>& layouts, QRect &rect)
1908 {
1909     Q_Q(QFormLayout);
1910 
1911     int i;
1912     const int rr = m_matrix.rowCount();
1913     QWidget *w = q->parentWidget();
1914     Qt::LayoutDirection layoutDirection = w ? w->layoutDirection() : QApplication::layoutDirection();
1915 
1916     Qt::Alignment formAlignment = fixedAlignment(q->formAlignment(), layoutDirection);
1917     int leftOffset = 0;
1918     int delta = rect.width() - formMaxWidth;
1919     if (formAlignment & (Qt::AlignHCenter | Qt::AlignRight) && delta > 0) {
1920         leftOffset = delta;
1921         if (formAlignment & Qt::AlignHCenter)
1922             leftOffset >>= 1;
1923     }
1924 
1925     for (i = 0; i < rr; ++i) {
1926         QFormLayoutItem *label = m_matrix(i, 0);
1927         QFormLayoutItem *field = m_matrix(i, 1);
1928 
1929         if (label) {
1930             int height = layouts.at(label->vLayoutIndex).size;
1931             if ((label->expandingDirections() & Qt::Vertical) == 0) {
1932                 /*
1933                     If the field on the right-hand side is tall,
1934                     we want the label to be top-aligned, but not too
1935                     much. So we introduce a 7 / 4 factor so that it
1936                     gets some extra pixels at the top.
1937                 */
1938                 height = qMin(height,
1939                               qMin(label->sizeHint.height() * 7 / 4,
1940                                    label->maxSize.height()));
1941             }
1942 
1943             QSize sz(qMin(label->layoutWidth, label->sizeHint.width()), height);
1944             int x = leftOffset + rect.x() + label->layoutPos;
1945             if (fixedAlignment(q->labelAlignment(), layoutDirection) & Qt::AlignRight)
1946                 x += label->layoutWidth - sz.width();
1947             QPoint p(x, layouts.at(label->vLayoutIndex).pos);
1948             // ### expansion & sizepolicy stuff
1949 
1950             label->setGeometry(QStyle::visualRect(layoutDirection, rect, QRect(p, sz)));
1951         }
1952 
1953         if (field) {
1954             QSize sz(field->layoutWidth, layouts.at(field->vLayoutIndex).size);
1955             QPoint p(field->layoutPos + leftOffset + rect.x(), layouts.at(field->vLayoutIndex).pos);
1956 /*
1957             if ((field->widget() && field->widget()->sizePolicy().horizontalPolicy() & (QSizePolicy::GrowFlag | QSizePolicy::ExpandFlag | QSizePolicy::IgnoreFlag))
1958                 || (field->layout() && sz.width() < field->maxSize.width())) {
1959                 sz.rwidth() = field->layoutWidth;
1960             }
1961 */
1962             if (field->maxSize.isValid())
1963                 sz = sz.boundedTo(field->maxSize);
1964 
1965             field->setGeometry(QStyle::visualRect(layoutDirection, rect, QRect(p, sz)));
1966         }
1967     }
1968 }
1969 
1970 /*!
1971     Sets the widget in the given \a row for the given \a role to \a widget, extending the
1972     layout with empty rows if necessary.
1973 
1974     If the cell is already occupied, the \a widget is not inserted and an error message is
1975     sent to the console.
1976 
1977     \bold{Note:} For most applications, addRow() or insertRow() should be used instead of setWidget().
1978 
1979     \sa setLayout()
1980 */
setWidget(int row,ItemRole role,QWidget * widget)1981 void QFormLayout::setWidget(int row, ItemRole role, QWidget *widget)
1982 {
1983     Q_D(QFormLayout);
1984     int rowCnt = rowCount();
1985     if (row >= rowCnt)
1986         d->insertRows(rowCnt, row - rowCnt + 1);
1987     d->setWidget(row, role, widget);
1988 }
1989 
1990 /*!
1991     Sets the sub-layout in the given \a row for the given \a role to \a layout, extending the
1992     form layout with empty rows if necessary.
1993 
1994     If the cell is already occupied, the \a layout is not inserted and an error message is
1995     sent to the console.
1996 
1997     \bold{Note:} For most applications, addRow() or insertRow() should be used instead of setLayout().
1998 
1999     \sa setWidget()
2000 */
setLayout(int row,ItemRole role,QLayout * layout)2001 void QFormLayout::setLayout(int row, ItemRole role, QLayout *layout)
2002 {
2003     Q_D(QFormLayout);
2004     int rowCnt = rowCount();
2005     if (row >= rowCnt)
2006         d->insertRows(rowCnt, row - rowCnt + 1);
2007     d->setLayout(row, role, layout);
2008 }
2009 
2010 /*!
2011     Sets the item in the given \a row for the given \a role to \a item, extending the
2012     layout with empty rows if necessary.
2013 
2014     If the cell is already occupied, the \a item is not inserted and an error message is
2015     sent to the console.
2016     The \a item spans both columns.
2017 
2018     \warning Do not use this function to add child layouts or child
2019     widget items. Use setLayout() or setWidget() instead.
2020 
2021     \sa setLayout()
2022 */
setItem(int row,ItemRole role,QLayoutItem * item)2023 void QFormLayout::setItem(int row, ItemRole role, QLayoutItem *item)
2024 {
2025     Q_D(QFormLayout);
2026     int rowCnt = rowCount();
2027     if (row >= rowCnt)
2028         d->insertRows(rowCnt, row - rowCnt + 1);
2029     d->setItem(row, role, item);
2030 }
2031 
2032 /*!
2033      \internal
2034  */
2035 
resetFieldGrowthPolicy()2036 void QFormLayout::resetFieldGrowthPolicy()
2037 {
2038     Q_D(QFormLayout);
2039     d->fieldGrowthPolicy = DefaultFieldGrowthPolicy;
2040 }
2041 
2042 /*!
2043      \internal
2044  */
2045 
resetRowWrapPolicy()2046 void QFormLayout::resetRowWrapPolicy()
2047 {
2048     Q_D(QFormLayout);
2049     d->rowWrapPolicy = DefaultRowWrapPolicy;
2050 }
2051 
2052 /*!
2053      \internal
2054  */
2055 
resetFormAlignment()2056 void QFormLayout::resetFormAlignment()
2057 {
2058     Q_D(QFormLayout);
2059     d->formAlignment = 0;
2060 }
2061 
2062 /*!
2063      \internal
2064  */
2065 
resetLabelAlignment()2066 void QFormLayout::resetLabelAlignment()
2067 {
2068     Q_D(QFormLayout);
2069     d->labelAlignment = 0;
2070 }
2071 
2072 #if 0
2073 void QFormLayout::dump() const
2074 {
2075     Q_D(const QFormLayout);
2076     for (int i = 0; i < rowCount(); ++i) {
2077         for (int j = 0; j < 2; ++j) {
2078             qDebug("m_matrix(%d, %d) = %p", i, j, d->m_matrix(i, j));
2079         }
2080     }
2081     for (int i = 0; i < d->m_things.count(); ++i)
2082         qDebug("m_things[%d] = %p", i, d->m_things.at(i));
2083 }
2084 #endif
2085 
2086 QT_END_NAMESPACE
2087