1 /****************************************************************************
2 **
3 ** Copyright (C) 2016 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of the Qt Designer of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:GPL-EXCEPT$
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 https://www.qt.io/terms-conditions. For further
15 ** information use the contact form at https://www.qt.io/contact-us.
16 **
17 ** GNU General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU
19 ** General Public License version 3 as published by the Free Software
20 ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
21 ** included in the packaging of this file. Please review the following
22 ** information to ensure the GNU General Public License requirements will
23 ** be met: https://www.gnu.org/licenses/gpl-3.0.html.
24 **
25 ** $QT_END_LICENSE$
26 **
27 ****************************************************************************/
28 
29 #include "qlayout_widget_p.h"
30 #include "qdesigner_utils_p.h"
31 #include "layout_p.h"
32 #include "layoutinfo_p.h"
33 #include "invisible_widget_p.h"
34 #include "qdesigner_widgetitem_p.h"
35 
36 #include <QtDesigner/abstractformwindow.h>
37 #include <QtDesigner/qextensionmanager.h>
38 #include <QtDesigner/abstractformeditor.h>
39 #include <QtDesigner/propertysheet.h>
40 #include <QtDesigner/abstractwidgetfactory.h>
41 
42 #include <QtGui/qpainter.h>
43 #include <QtWidgets/qboxlayout.h>
44 #include <QtWidgets/qgridlayout.h>
45 #include <QtWidgets/qformlayout.h>
46 #include <QtWidgets/qapplication.h>
47 #include <QtGui/qevent.h>
48 
49 #include <QtCore/qdebug.h>
50 #include <QtCore/qalgorithms.h>
51 #include <QtCore/qmap.h>
52 #include <QtCore/qstack.h>
53 #include <QtCore/qpair.h>
54 #include <QtCore/qset.h>
55 
56 #include <algorithm>
57 
58 enum { ShiftValue = 1 };
59 enum { debugLayout = 0 };
60 enum { FormLayoutColumns = 2 };
61 enum { indicatorSize = 2 };
62 // Grid/form Helpers: get info (overloads to make templates work)
63 
64 namespace { // Do not use static, will break HP-UX due to templates
65 
66 QT_USE_NAMESPACE
67 
68 // overloads to make templates over QGridLayout/QFormLayout work
gridRowCount(const QGridLayout * gridLayout)69 inline int gridRowCount(const QGridLayout *gridLayout)
70 {
71     return  gridLayout->rowCount();
72 }
73 
gridColumnCount(const QGridLayout * gridLayout)74 inline int gridColumnCount(const QGridLayout *gridLayout)
75 {
76     return  gridLayout->columnCount();
77 }
78 
79 // QGridLayout/QFormLayout Helpers: get item position (overloads to make templates work)
getGridItemPosition(QGridLayout * gridLayout,int index,int * row,int * column,int * rowspan,int * colspan)80 inline void getGridItemPosition(QGridLayout *gridLayout, int index,
81     int *row, int *column, int *rowspan, int *colspan)
82 {
83     gridLayout->getItemPosition(index, row, column, rowspan, colspan);
84 }
85 
gridItemInfo(QGridLayout * grid,int index)86 QRect gridItemInfo(QGridLayout *grid, int index)
87 {
88     int row, column, rowSpan, columnSpan;
89     // getItemPosition is not const, grmbl..
90     grid->getItemPosition(index, &row, &column, &rowSpan, &columnSpan);
91     return QRect(column, row, columnSpan, rowSpan);
92 }
93 
gridRowCount(const QFormLayout * formLayout)94 inline int gridRowCount(const QFormLayout *formLayout)    { return  formLayout->rowCount(); }
gridColumnCount(const QFormLayout *)95 inline int gridColumnCount(const QFormLayout *) { return FormLayoutColumns; }
96 
getGridItemPosition(QFormLayout * formLayout,int index,int * row,int * column,int * rowspan,int * colspan)97 inline void getGridItemPosition(QFormLayout *formLayout, int index, int *row, int *column, int *rowspan, int *colspan)
98 {
99     qdesigner_internal::getFormLayoutItemPosition(formLayout, index, row, column, rowspan, colspan);
100 }
101 } // namespace anonymous
102 
103 QT_BEGIN_NAMESPACE
104 
105 static const char *objectNameC = "objectName";
106 static const char *sizeConstraintC = "sizeConstraint";
107 
108 /* A padding spacer element that is used to represent an empty form layout cell. It should grow with its cell.
109  * Should not be used on a grid as it causes resizing inconsistencies */
110 namespace qdesigner_internal {
111     class PaddingSpacerItem : public QSpacerItem {
112     public:
PaddingSpacerItem()113         PaddingSpacerItem() : QSpacerItem(0, 0, QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding) {}
114 
expandingDirections() const115         Qt::Orientations expandingDirections () const override
116         { return Qt::Vertical | Qt::Horizontal; }
117     };
118 }
119 
createGridSpacer()120 static inline QSpacerItem *createGridSpacer()
121 {
122     return new QSpacerItem(0, 0);
123 }
124 
createFormSpacer()125 static inline QSpacerItem *createFormSpacer()
126 {
127     return new qdesigner_internal::PaddingSpacerItem;
128 }
129 
130 // QGridLayout/QFormLayout Helpers: Debug items of GridLikeLayout
131 template <class GridLikeLayout>
debugGridLikeLayout(QDebug str,const GridLikeLayout & gl)132 static QDebug debugGridLikeLayout(QDebug str, const GridLikeLayout &gl)
133 {
134     const int count = gl.count();
135     str << "Grid: " << gl.objectName() <<   gridRowCount(&gl) << " rows x " <<  gridColumnCount(&gl)
136         << " cols " << count << " items\n";
137     for (int i = 0; i < count; i++) {
138         QLayoutItem *item = gl.itemAt(i);
139         str << "Item " << i << item << item->widget() << gridItemInfo(const_cast<GridLikeLayout *>(&gl), i) << " empty=" << qdesigner_internal::LayoutInfo::isEmptyItem(item) << "\n";
140     }
141     return str;
142 }
143 
operator <<(QDebug str,const QGridLayout & gl)144 static inline QDebug operator<<(QDebug str, const QGridLayout &gl) { return debugGridLikeLayout(str, gl); }
145 
isEmptyFormLayoutRow(const QFormLayout * fl,int row)146 static inline bool isEmptyFormLayoutRow(const QFormLayout *fl, int row)
147 {
148     // Spanning can never be empty
149     if (fl->itemAt(row, QFormLayout::SpanningRole))
150         return false;
151     return qdesigner_internal::LayoutInfo::isEmptyItem(fl->itemAt(row, QFormLayout::LabelRole)) && qdesigner_internal::LayoutInfo::isEmptyItem(fl->itemAt(row, QFormLayout::FieldRole));
152 }
153 
canSimplifyFormLayout(const QFormLayout * formLayout,const QRect & restrictionArea)154 static inline bool canSimplifyFormLayout(const QFormLayout *formLayout, const QRect &restrictionArea)
155 {
156     if (restrictionArea.x() >= FormLayoutColumns)
157         return false;
158     // Try to find empty rows
159     const int bottomCheckRow = qMin(formLayout->rowCount(), restrictionArea.top() + restrictionArea.height());
160     for (int r = restrictionArea.y(); r < bottomCheckRow; r++)
161         if (isEmptyFormLayoutRow(formLayout, r))
162             return true;
163     return false;
164 }
165 
166 // recreate a managed layout (which does not automagically remove
167 // empty rows/columns like grid or form layout) in case it needs to shrink
168 
recreateManagedLayout(const QDesignerFormEditorInterface * core,QWidget * w,QLayout * lt)169 static QLayout *recreateManagedLayout(const QDesignerFormEditorInterface *core, QWidget *w, QLayout *lt)
170 {
171     const qdesigner_internal::LayoutInfo::Type t = qdesigner_internal::LayoutInfo::layoutType(core, lt);
172     qdesigner_internal::LayoutProperties properties;
173     const int mask = properties.fromPropertySheet(core, lt, qdesigner_internal::LayoutProperties::AllProperties);
174     qdesigner_internal::LayoutInfo::deleteLayout(core, w);
175     QLayout *rc = core->widgetFactory()->createLayout(w, nullptr, t);
176     properties.toPropertySheet(core, rc, mask, true);
177     return rc;
178 }
179 
180 // QGridLayout/QFormLayout Helpers: find an item on a form/grid. Return index
181 template <class GridLikeLayout>
findGridItemAt(GridLikeLayout * gridLayout,int at_row,int at_column)182 int findGridItemAt(GridLikeLayout *gridLayout, int at_row, int at_column)
183 {
184     Q_ASSERT(gridLayout);
185     const int count = gridLayout->count();
186     for (int index = 0; index <  count; index++) {
187         int row, column, rowspan, colspan;
188         getGridItemPosition(gridLayout, index, &row, &column, &rowspan, &colspan);
189         if (at_row >= row && at_row < (row + rowspan)
190             && at_column >= column && at_column < (column + colspan)) {
191             return index;
192         }
193     }
194     return -1;
195 }
196 // QGridLayout/QFormLayout  Helpers: remove dummy spacers on form/grid
197 template <class GridLikeLayout>
removeEmptyCellsOnGrid(GridLikeLayout * grid,const QRect & area)198 static bool removeEmptyCellsOnGrid(GridLikeLayout *grid, const QRect &area)
199 {
200     // check if there are any items in the way. Should be only spacers
201     // Unique out items that span rows/columns.
202     QVector<int> indexesToBeRemoved;
203     indexesToBeRemoved.reserve(grid->count());
204     const int rightColumn = area.x() + area.width();
205     const int bottomRow = area.y() + area.height();
206     for (int c = area.x(); c < rightColumn; c++)
207         for (int r = area.y(); r < bottomRow; r++) {
208             const int index = findGridItemAt(grid, r ,c);
209             if (index != -1)
210                 if (QLayoutItem *item = grid->itemAt(index)) {
211                     if (qdesigner_internal::LayoutInfo::isEmptyItem(item)) {
212                         if (indexesToBeRemoved.indexOf(index) == -1)
213                             indexesToBeRemoved.push_back(index);
214                     } else {
215                         return false;
216                     }
217                 }
218         }
219     // remove, starting from last
220     if (!indexesToBeRemoved.isEmpty()) {
221         std::stable_sort(indexesToBeRemoved.begin(), indexesToBeRemoved.end());
222         for (int i = indexesToBeRemoved.size() - 1; i >= 0; i--)
223             delete grid->takeAt(indexesToBeRemoved[i]);
224     }
225     return true;
226 }
227 
228 namespace qdesigner_internal {
229 // --------- LayoutProperties
230 
LayoutProperties()231 LayoutProperties::LayoutProperties()
232 {
233     clear();
234 }
235 
clear()236 void LayoutProperties::clear()
237 {
238     std::fill(m_margins, m_margins + MarginCount, 0);
239     std::fill(m_marginsChanged, m_marginsChanged + MarginCount, false);
240     std::fill(m_spacings, m_spacings + SpacingsCount, 0);
241     std::fill(m_spacingsChanged, m_spacingsChanged + SpacingsCount, false);
242 
243     m_objectName = QVariant();
244     m_objectNameChanged = false;
245     m_sizeConstraint = QVariant(QLayout::SetDefaultConstraint);
246     m_sizeConstraintChanged = false;
247 
248     m_fieldGrowthPolicyChanged = m_rowWrapPolicyChanged =  m_labelAlignmentChanged = m_formAlignmentChanged = false;
249     m_fieldGrowthPolicy =  m_rowWrapPolicy =  m_formAlignment = QVariant();
250 
251     m_boxStretchChanged = m_gridRowStretchChanged = m_gridColumnStretchChanged = m_gridRowMinimumHeightChanged = false;
252     m_boxStretch = m_gridRowStretch =  m_gridColumnStretch =  m_gridRowMinimumHeight = QVariant();
253 }
254 
visibleProperties(const QLayout * layout)255 int LayoutProperties::visibleProperties(const  QLayout *layout)
256 {
257     // Grid like layout have 2 spacings.
258     const bool isFormLayout = qobject_cast<const QFormLayout*>(layout);
259     const bool isGridLike = qobject_cast<const QGridLayout*>(layout) || isFormLayout;
260     int rc = ObjectNameProperty|LeftMarginProperty|TopMarginProperty|RightMarginProperty|BottomMarginProperty|
261              SizeConstraintProperty;
262 
263     rc |= isGridLike ? (HorizSpacingProperty|VertSpacingProperty) : SpacingProperty;
264     if (isFormLayout) {
265         rc |= FieldGrowthPolicyProperty|RowWrapPolicyProperty|LabelAlignmentProperty|FormAlignmentProperty;
266     } else {
267         if (isGridLike) {
268             rc |=  GridRowStretchProperty|GridColumnStretchProperty|GridRowMinimumHeightProperty|GridColumnMinimumWidthProperty;
269         } else {
270             rc |=  BoxStretchProperty;
271         }
272     }
273     return rc;
274 }
275 
276 static const char *marginPropertyNamesC[] = {"leftMargin", "topMargin", "rightMargin", "bottomMargin"};
277 static const char *spacingPropertyNamesC[] = {"spacing", "horizontalSpacing", "verticalSpacing" };
278 static const char *fieldGrowthPolicyPropertyC = "fieldGrowthPolicy";
279 static const char *rowWrapPolicyPropertyC = "rowWrapPolicy";
280 static const char *labelAlignmentPropertyC = "labelAlignment";
281 static const char *formAlignmentPropertyC = "formAlignment";
282 static const char *boxStretchPropertyC = "stretch";
283 static const char *gridRowStretchPropertyC = "rowStretch";
284 static const char *gridColumnStretchPropertyC = "columnStretch";
285 static const char *gridRowMinimumHeightPropertyC = "rowMinimumHeight";
286 static const char *gridColumnMinimumWidthPropertyC = "columnMinimumWidth";
287 
intValueFromSheet(const QDesignerPropertySheetExtension * sheet,const QString & name,int * value,bool * changed)288 static bool intValueFromSheet(const QDesignerPropertySheetExtension *sheet, const QString &name, int *value, bool *changed)
289 {
290     const int sheetIndex = sheet->indexOf(name);
291     if (sheetIndex == -1)
292         return false;
293     *value = sheet->property(sheetIndex).toInt();
294     *changed = sheet->isChanged(sheetIndex);
295     return true;
296 }
297 
variantPropertyFromSheet(int mask,int flag,const QDesignerPropertySheetExtension * sheet,const QString & name,QVariant * value,bool * changed,int * returnMask)298 static void variantPropertyFromSheet(int mask, int flag, const QDesignerPropertySheetExtension *sheet, const QString &name,
299                                      QVariant *value, bool *changed, int *returnMask)
300 {
301     if (mask & flag) {
302         const int sIndex = sheet->indexOf(name);
303         if (sIndex != -1) {
304             *value = sheet->property(sIndex);
305             *changed = sheet->isChanged(sIndex);
306             *returnMask |= flag;
307         }
308     }
309 }
310 
fromPropertySheet(const QDesignerFormEditorInterface * core,QLayout * l,int mask)311 int LayoutProperties::fromPropertySheet(const QDesignerFormEditorInterface *core, QLayout *l, int mask)
312 {
313     int rc = 0;
314     const QDesignerPropertySheetExtension *sheet = qt_extension<QDesignerPropertySheetExtension*>(core->extensionManager(), l);
315     Q_ASSERT(sheet);
316     // name
317     if (mask & ObjectNameProperty) {
318         const int nameIndex = sheet->indexOf(QLatin1String(objectNameC));
319         Q_ASSERT(nameIndex != -1);
320         m_objectName = sheet->property(nameIndex);
321         m_objectNameChanged =  sheet->isChanged(nameIndex);
322         rc |= ObjectNameProperty;
323     }
324     // -- Margins
325     const int marginFlags[MarginCount] = { LeftMarginProperty, TopMarginProperty, RightMarginProperty, BottomMarginProperty};
326     for (int i = 0; i < MarginCount; i++)
327         if (mask & marginFlags[i])
328             if (intValueFromSheet(sheet, QLatin1String(marginPropertyNamesC[i]), m_margins + i, m_marginsChanged + i))
329                 rc |= marginFlags[i];
330 
331     const int spacingFlags[] = { SpacingProperty, HorizSpacingProperty, VertSpacingProperty};
332     for (int i = 0; i < SpacingsCount; i++)
333         if (mask & spacingFlags[i])
334             if (intValueFromSheet(sheet, QLatin1String(spacingPropertyNamesC[i]), m_spacings + i, m_spacingsChanged + i))
335                 rc |= spacingFlags[i];
336     // sizeConstraint, flags
337     variantPropertyFromSheet(mask, SizeConstraintProperty, sheet, QLatin1String(sizeConstraintC), &m_sizeConstraint, &m_sizeConstraintChanged, &rc);
338     variantPropertyFromSheet(mask, FieldGrowthPolicyProperty, sheet, QLatin1String(fieldGrowthPolicyPropertyC), &m_fieldGrowthPolicy, &m_fieldGrowthPolicyChanged, &rc);
339     variantPropertyFromSheet(mask, RowWrapPolicyProperty, sheet, QLatin1String(rowWrapPolicyPropertyC), &m_rowWrapPolicy, &m_rowWrapPolicyChanged, &rc);
340     variantPropertyFromSheet(mask, LabelAlignmentProperty, sheet, QLatin1String(labelAlignmentPropertyC), &m_labelAlignment, &m_labelAlignmentChanged, &rc);
341     variantPropertyFromSheet(mask, FormAlignmentProperty, sheet, QLatin1String(formAlignmentPropertyC), &m_formAlignment, &m_formAlignmentChanged, &rc);
342     variantPropertyFromSheet(mask, BoxStretchProperty, sheet, QLatin1String(boxStretchPropertyC), &m_boxStretch, & m_boxStretchChanged, &rc);
343     variantPropertyFromSheet(mask, GridRowStretchProperty, sheet, QLatin1String(gridRowStretchPropertyC), &m_gridRowStretch, &m_gridRowStretchChanged, &rc);
344     variantPropertyFromSheet(mask, GridColumnStretchProperty, sheet, QLatin1String(gridColumnStretchPropertyC), &m_gridColumnStretch, &m_gridColumnStretchChanged, &rc);
345     variantPropertyFromSheet(mask, GridRowMinimumHeightProperty, sheet, QLatin1String(gridRowMinimumHeightPropertyC), &m_gridRowMinimumHeight, &m_gridRowMinimumHeightChanged, &rc);
346     variantPropertyFromSheet(mask, GridColumnMinimumWidthProperty, sheet, QLatin1String(gridColumnMinimumWidthPropertyC), &m_gridColumnMinimumWidth, &m_gridColumnMinimumWidthChanged, &rc);
347     return rc;
348 }
349 
intValueToSheet(QDesignerPropertySheetExtension * sheet,const QString & name,int value,bool changed,bool applyChanged)350 static bool intValueToSheet(QDesignerPropertySheetExtension *sheet, const QString &name, int value, bool changed, bool applyChanged)
351 
352 {
353 
354     const int sheetIndex = sheet->indexOf(name);
355     if (sheetIndex == -1) {
356         qWarning() << " LayoutProperties: Attempt to set property " << name << " that does not exist for the layout.";
357         return false;
358     }
359     sheet->setProperty(sheetIndex, QVariant(value));
360     if (applyChanged)
361         sheet->setChanged(sheetIndex, changed);
362     return true;
363 }
364 
variantPropertyToSheet(int mask,int flag,bool applyChanged,QDesignerPropertySheetExtension * sheet,const QString & name,const QVariant & value,bool changed,int * returnMask)365 static void variantPropertyToSheet(int mask, int flag, bool applyChanged, QDesignerPropertySheetExtension *sheet, const QString &name,
366                                    const QVariant &value, bool changed, int *returnMask)
367 {
368     if (mask & flag) {
369         const int sIndex = sheet->indexOf(name);
370         if (sIndex != -1) {
371             sheet->setProperty(sIndex, value);
372             if (applyChanged)
373                 sheet->setChanged(sIndex, changed);
374             *returnMask |= flag;
375         }
376     }
377 }
378 
toPropertySheet(const QDesignerFormEditorInterface * core,QLayout * l,int mask,bool applyChanged) const379 int LayoutProperties::toPropertySheet(const QDesignerFormEditorInterface *core, QLayout *l, int mask, bool applyChanged) const
380 {
381     int rc = 0;
382     QDesignerPropertySheetExtension *sheet = qt_extension<QDesignerPropertySheetExtension*>(core->extensionManager(), l);
383     Q_ASSERT(sheet);
384     // name
385     if (mask & ObjectNameProperty) {
386         const int nameIndex = sheet->indexOf(QLatin1String(objectNameC));
387         Q_ASSERT(nameIndex != -1);
388         sheet->setProperty(nameIndex, m_objectName);
389         if (applyChanged)
390            sheet->setChanged(nameIndex, m_objectNameChanged);
391         rc |= ObjectNameProperty;
392     }
393     // margins
394     const int marginFlags[MarginCount] = { LeftMarginProperty, TopMarginProperty, RightMarginProperty, BottomMarginProperty};
395     for (int i = 0; i < MarginCount; i++)
396         if (mask & marginFlags[i])
397             if (intValueToSheet(sheet, QLatin1String(marginPropertyNamesC[i]), m_margins[i], m_marginsChanged[i], applyChanged))
398                 rc |= marginFlags[i];
399 
400     const int spacingFlags[] = { SpacingProperty, HorizSpacingProperty, VertSpacingProperty};
401     for (int i = 0; i < SpacingsCount; i++)
402         if (mask & spacingFlags[i])
403             if (intValueToSheet(sheet, QLatin1String(spacingPropertyNamesC[i]), m_spacings[i], m_spacingsChanged[i], applyChanged))
404                 rc |= spacingFlags[i];
405     // sizeConstraint
406     variantPropertyToSheet(mask, SizeConstraintProperty, applyChanged, sheet, QLatin1String(sizeConstraintC), m_sizeConstraint, m_sizeConstraintChanged, &rc);
407     variantPropertyToSheet(mask, FieldGrowthPolicyProperty, applyChanged, sheet, QLatin1String(fieldGrowthPolicyPropertyC), m_fieldGrowthPolicy, m_fieldGrowthPolicyChanged, &rc);
408     variantPropertyToSheet(mask, RowWrapPolicyProperty, applyChanged, sheet, QLatin1String(rowWrapPolicyPropertyC), m_rowWrapPolicy, m_rowWrapPolicyChanged, &rc);
409     variantPropertyToSheet(mask, LabelAlignmentProperty, applyChanged, sheet, QLatin1String(labelAlignmentPropertyC), m_labelAlignment, m_labelAlignmentChanged, &rc);
410     variantPropertyToSheet(mask, FormAlignmentProperty, applyChanged, sheet, QLatin1String(formAlignmentPropertyC), m_formAlignment, m_formAlignmentChanged, &rc);
411     variantPropertyToSheet(mask, BoxStretchProperty, applyChanged, sheet, QLatin1String(boxStretchPropertyC), m_boxStretch, m_boxStretchChanged, &rc);
412     variantPropertyToSheet(mask, GridRowStretchProperty, applyChanged, sheet, QLatin1String(gridRowStretchPropertyC), m_gridRowStretch, m_gridRowStretchChanged, &rc);
413     variantPropertyToSheet(mask, GridColumnStretchProperty, applyChanged, sheet, QLatin1String(gridColumnStretchPropertyC), m_gridColumnStretch, m_gridColumnStretchChanged, &rc);
414     variantPropertyToSheet(mask, GridRowMinimumHeightProperty, applyChanged, sheet, QLatin1String(gridRowMinimumHeightPropertyC), m_gridRowMinimumHeight, m_gridRowMinimumHeightChanged, &rc);
415     variantPropertyToSheet(mask, GridColumnMinimumWidthProperty, applyChanged, sheet, QLatin1String(gridColumnMinimumWidthPropertyC), m_gridColumnMinimumWidth, m_gridColumnMinimumWidthChanged, &rc);
416     return rc;
417 }
418 
419 // ---------------- LayoutHelper
420 LayoutHelper::LayoutHelper() = default;
421 
422 LayoutHelper::~LayoutHelper() = default;
423 
indexOf(const QLayout * lt,const QWidget * widget)424 int LayoutHelper::indexOf(const QLayout *lt, const QWidget *widget)
425 {
426     if (!lt)
427         return -1;
428 
429     const int itemCount = lt->count();
430     for (int i = 0; i < itemCount; i++)
431         if (lt->itemAt(i)->widget() == widget)
432             return i;
433     return -1;
434 }
435 
itemInfo(QLayout * lt,const QWidget * widget) const436 QRect LayoutHelper::itemInfo(QLayout *lt, const QWidget *widget) const
437 {
438     const int index = indexOf(lt, widget);
439     if (index == -1) {
440         qWarning() << "LayoutHelper::itemInfo: " << widget << " not in layout " << lt;
441         return QRect(0, 0, 1, 1);
442     }
443     return itemInfo(lt, index);
444 }
445 
446     // ---------------- BoxLayoutHelper
447     class BoxLayoutHelper : public  LayoutHelper {
448     public:
BoxLayoutHelper(const Qt::Orientation orientation)449         BoxLayoutHelper(const Qt::Orientation orientation) : m_orientation(orientation) {}
450 
451         QRect itemInfo(QLayout *lt, int index) const override;
452         void insertWidget(QLayout *lt, const QRect &info, QWidget *w) override;
453         void removeWidget(QLayout *lt, QWidget *widget) override;
454         void replaceWidget(QLayout *lt, QWidget *before, QWidget *after) override;
455 
456         void pushState(const QDesignerFormEditorInterface *, const QWidget *) override;
457         void popState(const QDesignerFormEditorInterface *, QWidget *) override;
458 
canSimplify(const QDesignerFormEditorInterface *,const QWidget *,const QRect &) const459         bool canSimplify(const QDesignerFormEditorInterface *, const QWidget *, const QRect &) const override { return  false; }
simplify(const QDesignerFormEditorInterface *,QWidget *,const QRect &)460         void simplify(const QDesignerFormEditorInterface *, QWidget *, const QRect &) override {}
461 
462         // Helper for restoring layout states
463         using LayoutItemVector = QVector<QLayoutItem *>;
464         static LayoutItemVector disassembleLayout(QLayout *lt);
465         static QLayoutItem *findItemOfWidget(const LayoutItemVector &lv, QWidget *w);
466 
467     private:
468         using BoxLayoutState = QVector<QWidget *>;
469 
470         static BoxLayoutState state(const QBoxLayout*lt);
471 
472         QStack<BoxLayoutState> m_states;
473         const Qt::Orientation m_orientation;
474     };
475 
itemInfo(QLayout *,int index) const476     QRect BoxLayoutHelper::itemInfo(QLayout * /*lt*/, int index) const
477     {
478         return m_orientation == Qt::Horizontal ?  QRect(index, 0, 1, 1) : QRect(0, index, 1, 1);
479     }
480 
insertWidget(QLayout * lt,const QRect & info,QWidget * w)481     void BoxLayoutHelper::insertWidget(QLayout *lt, const QRect &info, QWidget *w)
482     {
483         QDesignerWidgetItemInstaller wii; // Make sure we use QDesignerWidgetItem.
484         QBoxLayout *boxLayout = qobject_cast<QBoxLayout *>(lt);
485         Q_ASSERT(boxLayout);
486         boxLayout->insertWidget(m_orientation == Qt::Horizontal ? info.x() : info.y(), w);
487     }
488 
removeWidget(QLayout * lt,QWidget * widget)489     void BoxLayoutHelper::removeWidget(QLayout *lt, QWidget *widget)
490     {
491         QBoxLayout *boxLayout = qobject_cast<QBoxLayout *>(lt);
492         Q_ASSERT(boxLayout);
493         boxLayout->removeWidget(widget);
494     }
495 
replaceWidget(QLayout * lt,QWidget * before,QWidget * after)496     void BoxLayoutHelper::replaceWidget(QLayout *lt, QWidget *before, QWidget *after)
497     {
498         bool ok = false;
499         QDesignerWidgetItemInstaller wii; // Make sure we use QDesignerWidgetItem.
500         if (QBoxLayout *boxLayout = qobject_cast<QBoxLayout *>(lt)) {
501             const int index = boxLayout->indexOf(before);
502             if (index != -1) {
503                 const bool visible = before->isVisible();
504                 delete boxLayout->takeAt(index);
505                 if (visible)
506                     before->hide();
507                 before->setParent(nullptr);
508                 boxLayout->insertWidget(index, after);
509                 ok = true;
510             }
511         }
512         if (!ok)
513             qWarning() << "BoxLayoutHelper::replaceWidget : Unable to replace " << before << " by " << after << " in " << lt;
514     }
515 
state(const QBoxLayout * lt)516     BoxLayoutHelper::BoxLayoutState BoxLayoutHelper::state(const QBoxLayout*lt)
517     {
518         BoxLayoutState rc;
519         if (const int count = lt->count()) {
520             rc.reserve(count);
521             for (int i = 0; i < count; i++)
522                 if (QWidget *w = lt->itemAt(i)->widget())
523                     rc.push_back(w);
524         }
525         return rc;
526     }
527 
pushState(const QDesignerFormEditorInterface * core,const QWidget * w)528     void BoxLayoutHelper::pushState(const QDesignerFormEditorInterface *core, const QWidget *w)
529     {
530         const QBoxLayout *boxLayout = qobject_cast<const QBoxLayout *>(LayoutInfo::managedLayout(core, w));
531         Q_ASSERT(boxLayout);
532         m_states.push(state(boxLayout));
533     }
534 
findItemOfWidget(const LayoutItemVector & lv,QWidget * w)535     QLayoutItem *BoxLayoutHelper::findItemOfWidget(const LayoutItemVector &lv, QWidget *w)
536     {
537         const LayoutItemVector::const_iterator cend = lv.constEnd();
538         for (LayoutItemVector::const_iterator it = lv.constBegin(); it != cend; ++it)
539             if ( (*it)->widget() == w)
540                 return *it;
541 
542         return nullptr;
543     }
544 
disassembleLayout(QLayout * lt)545     BoxLayoutHelper::LayoutItemVector BoxLayoutHelper::disassembleLayout(QLayout *lt)
546     {
547         // Take items
548         const int count = lt->count();
549         if (count == 0)
550             return LayoutItemVector();
551         LayoutItemVector rc;
552         rc.reserve(count);
553         for (int i = count - 1; i >= 0; i--)
554             rc.push_back(lt->takeAt(i));
555         return rc;
556     }
557 
popState(const QDesignerFormEditorInterface * core,QWidget * w)558     void BoxLayoutHelper::popState(const QDesignerFormEditorInterface *core, QWidget *w)
559     {
560         QBoxLayout *boxLayout = qobject_cast<QBoxLayout *>(LayoutInfo::managedLayout(core, w));
561         Q_ASSERT(boxLayout);
562         const BoxLayoutState savedState = m_states.pop();
563         const BoxLayoutState currentState = state(boxLayout);
564         // Check for equality/empty. Note that this will currently
565         // always trigger as box layouts do not have a state apart from
566         // the order and there is no layout order editor yet.
567         if (savedState == state(boxLayout))
568             return;
569 
570         const int count = savedState.size();
571         Q_ASSERT(count == currentState.size());
572         // Take items and reassemble in saved order
573         const LayoutItemVector items = disassembleLayout(boxLayout);
574         for (int i = 0; i < count; i++) {
575             QLayoutItem *item = findItemOfWidget(items, savedState[i]);
576             Q_ASSERT(item);
577             boxLayout->addItem(item);
578         }
579     }
580 
581     // Grid Layout state. Datatype storing the state of a GridLayout as a map of
582     // widgets to QRect(columns, rows) and size. Used to store the state for undo operations
583     // that do not change the widgets within the layout; also provides some manipulation
584     // functions and ability to apply the state to a layout provided its widgets haven't changed.
585     struct GridLayoutState {
586         GridLayoutState() = default;
587 
588         void fromLayout(QGridLayout *l);
589         void applyToLayout(const QDesignerFormEditorInterface *core, QWidget *w) const;
590 
591         void insertRow(int row);
592         void insertColumn(int column);
593 
594         bool simplify(const QRect &r, bool testOnly);
595         void removeFreeRow(int row);
596         void removeFreeColumn(int column);
597 
598 
599         // State of a cell in one dimension
600         enum DimensionCellState {
601             Free,
602             Spanned,  // Item spans it
603             Occupied  // Item bordering on it
604         };
605         // Horiontal, Vertical pair of state
606         typedef QPair<DimensionCellState, DimensionCellState> CellState;
607         using CellStates = QVector<CellState>;
608 
609         // Figure out states of a cell and return as a flat vector of
610         // [column1, column2,...] (address as  row * columnCount + col)
611         static CellStates cellStates(const QList<QRect> &rects, int numRows, int numColumns);
612 
613         typedef QMap<QWidget *, QRect> WidgetItemMap;
614         typedef QMap<QWidget *, Qt::Alignment> WidgetAlignmentMap;
615 
616         WidgetItemMap widgetItemMap;
617         WidgetAlignmentMap widgetAlignmentMap;
618 
619         int rowCount = 0;
620         int colCount = 0;
621     };
622 
needsSpacerItem(const GridLayoutState::CellState & cs)623     static inline bool needsSpacerItem(const GridLayoutState::CellState &cs) {
624         return cs.first == GridLayoutState::Free && cs.second == GridLayoutState::Free;
625     }
626 
operator <<(QDebug str,const GridLayoutState & gs)627     static inline QDebug operator<<(QDebug str, const GridLayoutState &gs)
628     {
629         str << "GridLayoutState: " <<  gs.rowCount << " rows x " <<  gs.colCount
630             << " cols " << gs.widgetItemMap.size() << " items\n";
631 
632         const GridLayoutState::WidgetItemMap::const_iterator wcend = gs.widgetItemMap.constEnd();
633         for (GridLayoutState::WidgetItemMap::const_iterator it = gs.widgetItemMap.constBegin(); it != wcend; ++it)
634             str << "Item " << it.key() << it.value() << '\n';
635         return str;
636     }
637 
cellStates(const QList<QRect> & rects,int numRows,int numColumns)638     GridLayoutState::CellStates GridLayoutState::cellStates(const QList<QRect> &rects, int numRows, int numColumns)
639     {
640         CellStates rc = CellStates(numRows * numColumns, CellState(Free, Free));
641         for (const auto &rect : rects) {
642             const int leftColumn = rect.x();
643             const int topRow = rect.y();
644             const int rightColumn = leftColumn + rect.width() - 1;
645             const int bottomRow = topRow + rect.height() - 1;
646             for (int r = topRow; r <= bottomRow; r++)
647                 for (int c = leftColumn; c <= rightColumn; c++) {
648                     const int flatIndex = r * numColumns + c;
649                     // Bordering horizontally?
650                     DimensionCellState &horizState = rc[flatIndex].first;
651                     if (c == leftColumn || c == rightColumn) {
652                         horizState = Occupied;
653                     } else {
654                         if (horizState < Spanned)
655                             horizState = Spanned;
656                     }
657                     // Bordering vertically?
658                     DimensionCellState &vertState = rc[flatIndex].second;
659                     if (r == topRow || r == bottomRow) {
660                         vertState = Occupied;
661                     } else {
662                         if (vertState < Spanned)
663                             vertState = Spanned;
664                     }
665                 }
666         }
667         if (debugLayout) {
668             qDebug() << "GridLayoutState::cellStates: " << numRows << " x " << numColumns;
669             for (int r = 0; r < numRows; r++)
670                 for (int c = 0; c < numColumns; c++)
671                     qDebug() << "  Row: " << r << " column: " << c <<  rc[r * numColumns + c];
672         }
673         return rc;
674     }
675 
fromLayout(QGridLayout * l)676     void GridLayoutState::fromLayout(QGridLayout *l)
677     {
678         rowCount = l->rowCount();
679         colCount = l->columnCount();
680         const int count = l->count();
681         for (int i = 0; i < count; i++) {
682             QLayoutItem *item = l->itemAt(i);
683             if (!LayoutInfo::isEmptyItem(item)) {
684                 widgetItemMap.insert(item->widget(), gridItemInfo(l, i));
685                 if (item->alignment())
686                     widgetAlignmentMap.insert(item->widget(), item->alignment());
687             }
688         }
689     }
690 
applyToLayout(const QDesignerFormEditorInterface * core,QWidget * w) const691     void GridLayoutState::applyToLayout(const QDesignerFormEditorInterface *core, QWidget *w) const
692     {
693         using LayoutItemRectMap =QHash<QLayoutItem *, QRect>;
694         QGridLayout *grid = qobject_cast<QGridLayout *>(LayoutInfo::managedLayout(core, w));
695         Q_ASSERT(grid);
696         if (debugLayout)
697             qDebug() << ">GridLayoutState::applyToLayout" <<  *this << *grid;
698         const bool shrink = grid->rowCount() > rowCount || grid->columnCount() > colCount;
699         // Build a map of existing items to rectangles via widget map, delete spacers
700         LayoutItemRectMap itemMap;
701         while (grid->count()) {
702             QLayoutItem *item = grid->takeAt(0);
703             if (!LayoutInfo::isEmptyItem(item)) {
704                 QWidget *itemWidget = item->widget();
705                 const WidgetItemMap::const_iterator it = widgetItemMap.constFind(itemWidget);
706                 if (it == widgetItemMap.constEnd())
707                     qFatal("GridLayoutState::applyToLayout: Attempt to apply to a layout that has a widget '%s'/'%s' added after saving the state.",
708                            itemWidget->metaObject()->className(), itemWidget->objectName().toUtf8().constData());
709                 itemMap.insert(item, it.value());
710             } else {
711                 delete item;
712             }
713         }
714         Q_ASSERT(itemMap.size() == widgetItemMap.size());
715         // recreate if shrink
716         if (shrink)
717             grid = static_cast<QGridLayout*>(recreateManagedLayout(core, w, grid));
718 
719         // Add widgets items
720         const LayoutItemRectMap::const_iterator icend = itemMap.constEnd();
721         for (LayoutItemRectMap::const_iterator it = itemMap.constBegin(); it != icend; ++it) {
722             const QRect info = it.value();
723             const Qt::Alignment alignment = widgetAlignmentMap.value(it.key()->widget(), {});
724             grid->addItem(it.key(), info.y(), info.x(), info.height(), info.width(), alignment);
725         }
726         // create spacers
727         const CellStates cs = cellStates(itemMap.values(), rowCount, colCount);
728         for (int r = 0; r < rowCount; r++)
729             for (int c = 0; c < colCount; c++)
730                 if (needsSpacerItem(cs[r * colCount  + c]))
731                     grid->addItem(createGridSpacer(), r, c);
732         grid->activate();
733         if (debugLayout)
734             qDebug() << "<GridLayoutState::applyToLayout" <<  *grid;
735     }
736 
insertRow(int row)737     void GridLayoutState::insertRow(int row)
738     {
739         rowCount++;
740         for (auto it = widgetItemMap.begin(), iend = widgetItemMap.end(); it != iend; ++it) {
741             const int topRow = it.value().y();
742             if (topRow >= row) {
743                 it.value().translate(0, 1);
744             } else {  //Over  it: Does it span it -> widen?
745                 const int rowSpan = it.value().height();
746                 if (rowSpan > 1 && topRow + rowSpan > row)
747                     it.value().setHeight(rowSpan + 1);
748             }
749         }
750     }
751 
insertColumn(int column)752     void GridLayoutState::insertColumn(int column)
753     {
754         colCount++;
755         for (auto it = widgetItemMap.begin(), iend = widgetItemMap.end(); it != iend; ++it) {
756             const int leftColumn = it.value().x();
757             if (leftColumn >= column) {
758                 it.value().translate(1, 0);
759             } else { // Left of it: Does it span it -> widen?
760                 const int colSpan = it.value().width();
761                 if (colSpan  > 1 &&  leftColumn + colSpan > column)
762                     it.value().setWidth(colSpan + 1);
763             }
764         }
765     }
766 
767     // Simplify: Remove empty columns/rows and such ones that are only spanned (shrink
768     // spanning items).
769     // 'AB.C.'           'ABC'
770     // 'DDDD.'     ==>   'DDD'
771     // 'EF.G.'           'EFG'
simplify(const QRect & r,bool testOnly)772     bool GridLayoutState::simplify(const QRect &r, bool testOnly)
773     {
774         // figure out free rows/columns.
775         QVector<bool> occupiedRows(rowCount, false);
776         QVector<bool> occupiedColumns(colCount, false);
777         // Mark everything outside restriction rectangle as occupied
778         const int restrictionLeftColumn = r.x();
779         const int restrictionRightColumn = restrictionLeftColumn + r.width();
780         const int restrictionTopRow = r.y();
781         const int restrictionBottomRow = restrictionTopRow + r.height();
782         if (restrictionLeftColumn > 0 || restrictionRightColumn < colCount ||
783             restrictionTopRow     > 0 || restrictionBottomRow   < rowCount) {
784             for (int r = 0; r <  rowCount; r++)
785                 if (r < restrictionTopRow || r >= restrictionBottomRow)
786                     occupiedRows[r] = true;
787             for (int c = 0; c < colCount; c++)
788                 if (c < restrictionLeftColumn ||  c >= restrictionRightColumn)
789                     occupiedColumns[c] = true;
790         }
791         // figure out free fields and tick off occupied rows and columns
792         const CellStates cs = cellStates(widgetItemMap.values(), rowCount, colCount);
793         for (int r = 0; r < rowCount; r++)
794             for (int c = 0; c < colCount; c++) {
795                 const CellState &state = cs[r * colCount  + c];
796                 if (state.first == Occupied)
797                     occupiedColumns[c] = true;
798                 if (state.second == Occupied)
799                     occupiedRows[r] = true;
800             }
801         // Any free rows/columns?
802         if (occupiedRows.indexOf(false) ==  -1 && occupiedColumns.indexOf(false) == -1)
803             return false;
804         if (testOnly)
805             return true;
806         // remove rows
807         for (int r = rowCount - 1; r >= 0; r--)
808             if (!occupiedRows[r])
809                 removeFreeRow(r);
810         // remove columns
811         for (int c = colCount - 1; c >= 0; c--)
812             if (!occupiedColumns[c])
813                 removeFreeColumn(c);
814         return true;
815     }
816 
removeFreeRow(int removeRow)817     void GridLayoutState::removeFreeRow(int removeRow)
818     {
819         for (auto it = widgetItemMap.begin(), iend = widgetItemMap.end(); it != iend; ++it) {
820             const int r = it.value().y();
821             Q_ASSERT(r != removeRow); // Free rows only
822             if (r < removeRow) { // Does the item span it? - shrink it
823                 const int rowSpan = it.value().height();
824                 if (rowSpan > 1) {
825                     const int bottomRow = r + rowSpan;
826                     if (bottomRow > removeRow)
827                         it.value().setHeight(rowSpan - 1);
828                 }
829             } else
830                 if (r > removeRow) // Item below it? - move.
831                     it.value().translate(0, -1);
832         }
833         rowCount--;
834     }
835 
removeFreeColumn(int removeColumn)836     void GridLayoutState::removeFreeColumn(int removeColumn)
837     {
838         for (auto it = widgetItemMap.begin(), iend = widgetItemMap.end(); it != iend; ++it) {
839             const int c = it.value().x();
840             Q_ASSERT(c != removeColumn); // Free columns only
841             if (c < removeColumn) { // Does the item span it? - shrink it
842                 const int colSpan = it.value().width();
843                 if (colSpan > 1) {
844                     const int rightColumn = c + colSpan;
845                     if (rightColumn > removeColumn)
846                         it.value().setWidth(colSpan - 1);
847                 }
848             } else
849                 if (c > removeColumn) // Item to the right of it?  - move.
850                     it.value().translate(-1, 0);
851         }
852         colCount--;
853     }
854 
855     // ---------------- GridLayoutHelper
856     class GridLayoutHelper : public  LayoutHelper {
857     public:
858         GridLayoutHelper() = default;
859 
860         QRect itemInfo(QLayout *lt, int index) const override;
861         void insertWidget(QLayout *lt, const QRect &info, QWidget *w) override;
862         void removeWidget(QLayout *lt, QWidget *widget) override;
863         void replaceWidget(QLayout *lt, QWidget *before, QWidget *after) override;
864 
865         void pushState(const QDesignerFormEditorInterface *core, const QWidget *widgetWithManagedLayout) override;
866         void popState(const QDesignerFormEditorInterface *core, QWidget *widgetWithManagedLayout) override;
867 
868         bool canSimplify(const QDesignerFormEditorInterface *core, const QWidget *widgetWithManagedLayout, const QRect &restrictionArea) const override;
869         void simplify(const QDesignerFormEditorInterface *core, QWidget *widgetWithManagedLayout, const QRect &restrictionArea) override;
870 
871         static void insertRow(QGridLayout *grid, int row);
872 
873     private:
874         QStack<GridLayoutState> m_states;
875     };
876 
insertRow(QGridLayout * grid,int row)877     void GridLayoutHelper::insertRow(QGridLayout *grid, int row)
878     {
879         GridLayoutState state;
880         state.fromLayout(grid);
881         state.insertRow(row);
882         QDesignerFormWindowInterface *fw = QDesignerFormWindowInterface::findFormWindow(grid);
883         state.applyToLayout(fw->core(), grid->parentWidget());
884     }
885 
itemInfo(QLayout * lt,int index) const886     QRect GridLayoutHelper::itemInfo(QLayout * lt, int index) const
887     {
888         QGridLayout *grid = qobject_cast<QGridLayout *>(lt);
889         Q_ASSERT(grid);
890         return gridItemInfo(grid, index);
891     }
892 
insertWidget(QLayout * lt,const QRect & info,QWidget * w)893     void GridLayoutHelper::insertWidget(QLayout *lt, const QRect &info, QWidget *w)
894     {
895         QDesignerWidgetItemInstaller wii; // Make sure we use QDesignerWidgetItem.
896         QGridLayout *gridLayout = qobject_cast<QGridLayout *>(lt);
897         Q_ASSERT(gridLayout);
898         // check if there are any items. Should be only spacers, else something is wrong
899         const int row = info.y();
900         int column = info.x();
901         int colSpan = info.width();
902         int rowSpan = info.height();
903         // If not empty: A multiselection was dropped on an empty item, insert row
904         // and spread items along new row
905         if (!removeEmptyCellsOnGrid(gridLayout, info)) {
906             int freeColumn = -1;
907             colSpan = rowSpan = 1;
908             // First look to the right for a free column
909             const int columnCount = gridLayout->columnCount();
910             for (int c = column; c <  columnCount; c++) {
911                 const int idx = findGridItemAt(gridLayout, row, c);
912                 if (idx != -1 && LayoutInfo::isEmptyItem(gridLayout->itemAt(idx))) {
913                     freeColumn = c;
914                     break;
915                 }
916             }
917             if (freeColumn != -1) {
918                 removeEmptyCellsOnGrid(gridLayout, QRect(freeColumn, row, 1, 1));
919                 column = freeColumn;
920             } else {
921                 GridLayoutHelper::insertRow(gridLayout, row);
922                 column = 0;
923             }
924         }
925         gridLayout->addWidget(w, row , column, rowSpan, colSpan);
926     }
927 
removeWidget(QLayout * lt,QWidget * widget)928     void GridLayoutHelper::removeWidget(QLayout *lt, QWidget *widget)
929     {
930         QGridLayout *gridLayout = qobject_cast<QGridLayout *>(lt);
931         Q_ASSERT(gridLayout);
932         const int index = gridLayout->indexOf(widget);
933         if (index == -1) {
934             qWarning() << "GridLayoutHelper::removeWidget : Attempt to remove " << widget <<  " which is not in the layout.";
935             return;
936         }
937         // delete old item and pad with  by spacer items
938         int row, column, rowspan, colspan;
939         gridLayout->getItemPosition(index, &row, &column, &rowspan, &colspan);
940         delete gridLayout->takeAt(index);
941         const int rightColumn = column + colspan;
942         const int bottomRow = row +  rowspan;
943         for (int c = column; c < rightColumn; c++)
944             for (int r = row; r < bottomRow; r++)
945                 gridLayout->addItem(createGridSpacer(), r, c);
946     }
947 
replaceWidget(QLayout * lt,QWidget * before,QWidget * after)948     void GridLayoutHelper::replaceWidget(QLayout *lt, QWidget *before, QWidget *after)
949     {
950         bool ok = false;
951         QDesignerWidgetItemInstaller wii; // Make sure we use QDesignerWidgetItem.
952         if (QGridLayout *gridLayout = qobject_cast<QGridLayout *>(lt)) {
953             const int index = gridLayout->indexOf(before);
954             if (index != -1) {
955                 int row, column, rowSpan, columnSpan;
956                 gridLayout->getItemPosition (index,  &row, &column, &rowSpan, &columnSpan);
957                 const bool visible = before->isVisible();
958                 delete gridLayout->takeAt(index);
959                 if (visible)
960                     before->hide();
961                 before->setParent(nullptr);
962                 gridLayout->addWidget(after, row, column, rowSpan, columnSpan);
963                 ok = true;
964             }
965         }
966         if (!ok)
967             qWarning() << "GridLayoutHelper::replaceWidget : Unable to replace " << before << " by " << after << " in " << lt;
968     }
969 
pushState(const QDesignerFormEditorInterface * core,const QWidget * widgetWithManagedLayout)970     void GridLayoutHelper::pushState(const QDesignerFormEditorInterface *core, const QWidget *widgetWithManagedLayout)
971     {
972         QGridLayout *gridLayout = qobject_cast<QGridLayout *>(LayoutInfo::managedLayout(core, widgetWithManagedLayout));
973         Q_ASSERT(gridLayout);
974         GridLayoutState gs;
975         gs.fromLayout(gridLayout);
976         m_states.push(gs);
977     }
978 
popState(const QDesignerFormEditorInterface * core,QWidget * widgetWithManagedLayout)979     void GridLayoutHelper::popState(const QDesignerFormEditorInterface *core, QWidget *widgetWithManagedLayout)
980     {
981         Q_ASSERT(!m_states.isEmpty());
982         const GridLayoutState state = m_states.pop();
983         state.applyToLayout(core, widgetWithManagedLayout);
984     }
985 
canSimplify(const QDesignerFormEditorInterface * core,const QWidget * widgetWithManagedLayout,const QRect & restrictionArea) const986     bool GridLayoutHelper::canSimplify(const QDesignerFormEditorInterface *core, const QWidget *widgetWithManagedLayout, const QRect &restrictionArea) const
987     {
988         QGridLayout *gridLayout = qobject_cast<QGridLayout *>(LayoutInfo::managedLayout(core, widgetWithManagedLayout));
989         Q_ASSERT(gridLayout);
990         GridLayoutState gs;
991         gs.fromLayout(gridLayout);
992         return gs.simplify(restrictionArea, true);
993     }
994 
simplify(const QDesignerFormEditorInterface * core,QWidget * widgetWithManagedLayout,const QRect & restrictionArea)995     void GridLayoutHelper::simplify(const QDesignerFormEditorInterface *core, QWidget *widgetWithManagedLayout, const QRect &restrictionArea)
996     {
997         QGridLayout *gridLayout = qobject_cast<QGridLayout *>(LayoutInfo::managedLayout(core, widgetWithManagedLayout));
998         Q_ASSERT(gridLayout);
999         if (debugLayout)
1000             qDebug() << ">GridLayoutHelper::simplify" <<  *gridLayout;
1001         GridLayoutState gs;
1002         gs.fromLayout(gridLayout);
1003         if (gs.simplify(restrictionArea, false))
1004             gs.applyToLayout(core, widgetWithManagedLayout);
1005         if (debugLayout)
1006             qDebug() << "<GridLayoutHelper::simplify" <<  *gridLayout;
1007    }
1008 
1009     // ---------------- FormLayoutHelper
1010     class FormLayoutHelper : public  LayoutHelper {
1011     public:
1012         typedef QPair<QWidget *, QWidget *> WidgetPair;
1013         using FormLayoutState = QVector<WidgetPair>;
1014 
1015         FormLayoutHelper() = default;
1016 
1017         QRect itemInfo(QLayout *lt, int index) const override;
1018         void insertWidget(QLayout *lt, const QRect &info, QWidget *w) override;
1019         void removeWidget(QLayout *lt, QWidget *widget) override;
1020         void replaceWidget(QLayout *lt, QWidget *before, QWidget *after) override;
1021 
1022         void pushState(const QDesignerFormEditorInterface *core, const QWidget *widgetWithManagedLayout) override;
1023         void popState(const QDesignerFormEditorInterface *core, QWidget *widgetWithManagedLayout) override;
1024 
1025         bool canSimplify(const QDesignerFormEditorInterface *core, const QWidget *, const QRect &) const override;
1026         void simplify(const QDesignerFormEditorInterface *, QWidget *, const QRect &) override;
1027 
1028     private:
1029         static FormLayoutState state(const QFormLayout *lt);
1030 
1031         QStack<FormLayoutState> m_states;
1032     };
1033 
itemInfo(QLayout * lt,int index) const1034     QRect FormLayoutHelper::itemInfo(QLayout * lt, int index) const
1035     {
1036         QFormLayout *form = qobject_cast<QFormLayout *>(lt);
1037         Q_ASSERT(form);
1038         int row, column, colspan;
1039         getFormLayoutItemPosition(form, index, &row, &column, nullptr, &colspan);
1040         return QRect(column, row, colspan, 1);
1041     }
1042 
insertWidget(QLayout * lt,const QRect & info,QWidget * w)1043     void FormLayoutHelper::insertWidget(QLayout *lt, const QRect &info, QWidget *w)
1044     {
1045         if (debugLayout)
1046             qDebug() << "FormLayoutHelper::insertWidget:" << w << info;
1047         QDesignerWidgetItemInstaller wii; // Make sure we use QDesignerWidgetItem.
1048         QFormLayout *formLayout = qobject_cast<QFormLayout *>(lt);
1049         Q_ASSERT(formLayout);
1050         // check if there are any nonspacer items? (Drop on 3rd column or drop of a multiselection
1051         // on an empty item. As the Form layout does not have insert semantics; we need to manually insert a row
1052         const bool insert = !removeEmptyCellsOnGrid(formLayout, info);
1053         formLayoutAddWidget(formLayout, w, info, insert);
1054         QLayoutSupport::createEmptyCells(formLayout);
1055     }
1056 
removeWidget(QLayout * lt,QWidget * widget)1057     void FormLayoutHelper::removeWidget(QLayout *lt, QWidget *widget)
1058     {
1059         QFormLayout *formLayout = qobject_cast<QFormLayout *>(lt);
1060         Q_ASSERT(formLayout);
1061         const int index = formLayout->indexOf(widget);
1062         if (index == -1) {
1063             qWarning() << "FormLayoutHelper::removeWidget : Attempt to remove " << widget <<  " which is not in the layout.";
1064             return;
1065         }
1066         // delete old item and pad with  by spacer items
1067         int row, column, colspan;
1068         getFormLayoutItemPosition(formLayout, index, &row, &column, nullptr, &colspan);
1069         if (debugLayout)
1070             qDebug() << "FormLayoutHelper::removeWidget: #" << index << widget << " at " << row << column <<  colspan;
1071         delete formLayout->takeAt(index);
1072         if (colspan > 1 || column == 0)
1073             formLayout->setItem(row, QFormLayout::LabelRole, createFormSpacer());
1074         if (colspan > 1 || column == 1)
1075             formLayout->setItem(row, QFormLayout::FieldRole, createFormSpacer());
1076     }
1077 
replaceWidget(QLayout * lt,QWidget * before,QWidget * after)1078     void FormLayoutHelper::replaceWidget(QLayout *lt, QWidget *before, QWidget *after)
1079     {
1080         bool ok = false;
1081         QDesignerWidgetItemInstaller wii; // Make sure we use QDesignerWidgetItem.
1082         if (QFormLayout *formLayout = qobject_cast<QFormLayout *>(lt)) {
1083             const int index = formLayout->indexOf(before);
1084             if (index != -1) {
1085                 int row;
1086                 QFormLayout::ItemRole role;
1087                 formLayout->getItemPosition (index, &row, &role);
1088                 const bool visible = before->isVisible();
1089                 delete formLayout->takeAt(index);
1090                 if (visible)
1091                     before->hide();
1092                 before->setParent(nullptr);
1093                 formLayout->setWidget(row, role, after);
1094                 ok = true;
1095             }
1096         }
1097         if (!ok)
1098             qWarning() << "FormLayoutHelper::replaceWidget : Unable to replace " << before << " by " << after << " in " << lt;
1099     }
1100 
state(const QFormLayout * lt)1101     FormLayoutHelper::FormLayoutState FormLayoutHelper::state(const QFormLayout *lt)
1102     {
1103         const int rowCount = lt->rowCount();
1104         if (rowCount == 0)
1105             return FormLayoutState();
1106         FormLayoutState rc(rowCount, WidgetPair(0, 0));
1107         const int count = lt->count();
1108         int row, column, colspan;
1109         for (int i = 0; i < count; i++) {
1110             QLayoutItem *item = lt->itemAt(i);
1111             if (!LayoutInfo::isEmptyItem(item)) {
1112                 QWidget *w = item->widget();
1113                 Q_ASSERT(w);
1114                 getFormLayoutItemPosition(lt, i, &row, &column, nullptr, &colspan);
1115                 if (colspan > 1 || column == 0)
1116                     rc[row].first = w;
1117                 if (colspan > 1 || column == 1)
1118                     rc[row].second = w;
1119             }
1120         }
1121         if (debugLayout) {
1122             qDebug() << "FormLayoutHelper::state: " << rowCount;
1123             for (int r = 0; r < rowCount; r++)
1124                 qDebug() << "  Row: " << r << rc[r].first << rc[r].second;
1125         }
1126         return rc;
1127     }
1128 
pushState(const QDesignerFormEditorInterface * core,const QWidget * widgetWithManagedLayout)1129     void FormLayoutHelper::pushState(const QDesignerFormEditorInterface *core, const QWidget *widgetWithManagedLayout)
1130     {
1131         QFormLayout *formLayout = qobject_cast<QFormLayout *>(LayoutInfo::managedLayout(core, widgetWithManagedLayout));
1132         Q_ASSERT(formLayout);
1133         m_states.push(state(formLayout));
1134     }
1135 
popState(const QDesignerFormEditorInterface * core,QWidget * widgetWithManagedLayout)1136     void FormLayoutHelper::popState(const QDesignerFormEditorInterface *core, QWidget *widgetWithManagedLayout)
1137     {
1138         QFormLayout *formLayout = qobject_cast<QFormLayout *>(LayoutInfo::managedLayout(core, widgetWithManagedLayout));
1139         Q_ASSERT(!m_states.isEmpty() && formLayout);
1140 
1141         const FormLayoutState storedState = m_states.pop();
1142         const FormLayoutState currentState =  state(formLayout);
1143         if (currentState ==  storedState)
1144             return;
1145         const int rowCount = storedState.size();
1146         // clear out, shrink if required, but maintain items via map, pad spacers
1147         const BoxLayoutHelper::LayoutItemVector items = BoxLayoutHelper::disassembleLayout(formLayout);
1148         if (rowCount < formLayout->rowCount())
1149             formLayout = static_cast<QFormLayout*>(recreateManagedLayout(core, widgetWithManagedLayout, formLayout ));
1150         for (int r = 0; r < rowCount; r++) {
1151             QWidget *widgets[FormLayoutColumns] = { storedState[r].first, storedState[r].second };
1152             const bool spanning = widgets[0] != nullptr && widgets[0] == widgets[1];
1153             if (spanning) {
1154                 formLayout->setWidget(r, QFormLayout::SpanningRole, widgets[0]);
1155             } else {
1156                 for (int c = 0; c < FormLayoutColumns; c++) {
1157                     const QFormLayout::ItemRole role = c == 0 ? QFormLayout::LabelRole : QFormLayout::FieldRole;
1158                     if (widgets[c] && BoxLayoutHelper::findItemOfWidget(items, widgets[c])) {
1159                         formLayout->setWidget(r, role, widgets[c]);
1160                     } else {
1161                         formLayout->setItem(r, role, createFormSpacer());
1162                     }
1163                 }
1164             }
1165         }
1166     }
1167 
canSimplify(const QDesignerFormEditorInterface * core,const QWidget * widgetWithManagedLayout,const QRect & restrictionArea) const1168     bool FormLayoutHelper::canSimplify(const QDesignerFormEditorInterface *core, const QWidget *widgetWithManagedLayout, const QRect &restrictionArea) const
1169     {
1170         const QFormLayout *formLayout = qobject_cast<QFormLayout *>(LayoutInfo::managedLayout(core, widgetWithManagedLayout));
1171         Q_ASSERT(formLayout);
1172         return canSimplifyFormLayout(formLayout, restrictionArea);
1173     }
1174 
simplify(const QDesignerFormEditorInterface * core,QWidget * widgetWithManagedLayout,const QRect & restrictionArea)1175     void FormLayoutHelper::simplify(const QDesignerFormEditorInterface *core, QWidget *widgetWithManagedLayout, const QRect &restrictionArea)
1176     {
1177         using LayoutItemPair = QPair<QLayoutItem*, QLayoutItem*>;
1178         using LayoutItemPairs = QVector<LayoutItemPair>;
1179 
1180         QFormLayout *formLayout = qobject_cast<QFormLayout *>(LayoutInfo::managedLayout(core, widgetWithManagedLayout));
1181         Q_ASSERT(formLayout);
1182         if (debugLayout)
1183             qDebug() << "FormLayoutHelper::simplify";
1184         // Transform into vector of item pairs
1185         const int rowCount = formLayout->rowCount();
1186         LayoutItemPairs pairs(rowCount, LayoutItemPair(0, 0));
1187         for (int i =  formLayout->count() - 1; i >= 0; i--) {
1188             int row, col,colspan;
1189             getFormLayoutItemPosition(formLayout, i, &row, &col, nullptr, &colspan);
1190             if (colspan > 1) {
1191                  pairs[row].first = pairs[row].second = formLayout->takeAt(i);
1192             } else {
1193                 if (col == 0)
1194                     pairs[row].first = formLayout->takeAt(i);
1195                 else
1196                     pairs[row].second = formLayout->takeAt(i);
1197             }
1198         }
1199         // Weed out empty ones
1200         const int bottomCheckRow = qMin(rowCount, restrictionArea.y() + restrictionArea.height());
1201         for (int r = bottomCheckRow - 1; r >= restrictionArea.y(); r--)
1202             if (LayoutInfo::isEmptyItem(pairs[r].first) && LayoutInfo::isEmptyItem(pairs[r].second)) {
1203                 delete pairs[r].first;
1204                 delete pairs[r].second;
1205                 pairs.remove(r);
1206             }
1207         const int simpleRowCount = pairs.size();
1208         if (simpleRowCount < rowCount)
1209             formLayout = static_cast<QFormLayout *>(recreateManagedLayout(core, widgetWithManagedLayout, formLayout));
1210         // repopulate
1211         for (int r = 0; r < simpleRowCount; r++) {
1212             const bool spanning = pairs[r].first == pairs[r].second;
1213             if (spanning) {
1214                 formLayout->setItem(r, QFormLayout::SpanningRole, pairs[r].first);
1215             } else {
1216                 formLayout->setItem(r, QFormLayout::LabelRole, pairs[r].first);
1217                 formLayout->setItem(r, QFormLayout::FieldRole, pairs[r].second);
1218             }
1219         }
1220     }
1221 
createLayoutHelper(int type)1222 LayoutHelper *LayoutHelper::createLayoutHelper(int type)
1223 {
1224     LayoutHelper *rc = nullptr;
1225     switch (type) {
1226     case LayoutInfo::HBox:
1227         rc = new BoxLayoutHelper(Qt::Horizontal);
1228         break;
1229     case LayoutInfo::VBox:
1230         rc = new BoxLayoutHelper(Qt::Vertical);
1231         break;
1232     case LayoutInfo::Grid:
1233         rc = new GridLayoutHelper;
1234         break;
1235     case LayoutInfo::Form:
1236         return new FormLayoutHelper;
1237      default:
1238         break;
1239     }
1240     Q_ASSERT(rc);
1241     return rc;
1242 }
1243 
1244 // ---- QLayoutSupport (LayoutDecorationExtension)
QLayoutSupport(QDesignerFormWindowInterface * formWindow,QWidget * widget,LayoutHelper * helper,QObject * parent)1245 QLayoutSupport::QLayoutSupport(QDesignerFormWindowInterface *formWindow, QWidget *widget, LayoutHelper *helper, QObject *parent)  :
1246       QObject(parent),
1247       m_formWindow(formWindow),
1248       m_helper(helper),
1249       m_widget(widget),
1250       m_currentIndex(-1),
1251       m_currentInsertMode(QDesignerLayoutDecorationExtension::InsertWidgetMode)
1252 {
1253 }
1254 
layout() const1255 QLayout * QLayoutSupport::layout() const
1256 {
1257     return LayoutInfo::managedLayout(m_formWindow->core(), m_widget);
1258 }
1259 
hideIndicator(Indicator i)1260 void QLayoutSupport::hideIndicator(Indicator i)
1261 {
1262     if (m_indicators[i])
1263         m_indicators[i]->hide();
1264 }
1265 
showIndicator(Indicator i,const QRect & geometry,const QPalette & p)1266 void QLayoutSupport::showIndicator(Indicator i, const QRect &geometry, const QPalette &p)
1267 {
1268     if (!m_indicators[i])
1269         m_indicators[i] = new qdesigner_internal::InvisibleWidget(m_widget);
1270     QWidget *indicator = m_indicators[i];
1271     indicator->setAutoFillBackground(true);
1272     indicator->setPalette(p);
1273     indicator->setGeometry(geometry);
1274     indicator->show();
1275     indicator->raise();
1276 }
1277 
~QLayoutSupport()1278 QLayoutSupport::~QLayoutSupport()
1279 {
1280     delete m_helper;
1281     for (const QPointer<QWidget> &w : m_indicators) {
1282         if (!w.isNull())
1283             w->deleteLater();
1284     }
1285 }
1286 
gridLayout() const1287 QGridLayout * QLayoutSupport::gridLayout() const
1288 {
1289     return qobject_cast<QGridLayout*>(LayoutInfo::managedLayout(m_formWindow->core(), m_widget));
1290 }
1291 
itemInfo(int index) const1292 QRect QLayoutSupport::itemInfo(int index) const
1293 {
1294     return m_helper->itemInfo(LayoutInfo::managedLayout(m_formWindow->core(), m_widget), index);
1295 }
1296 
setInsertMode(InsertMode im)1297 void QLayoutSupport::setInsertMode(InsertMode im)
1298 {
1299     m_currentInsertMode = im;
1300 }
1301 
setCurrentCell(const QPair<int,int> & cell)1302 void QLayoutSupport::setCurrentCell(const QPair<int, int> &cell)
1303 {
1304     m_currentCell = cell;
1305 }
1306 
adjustIndicator(const QPoint & pos,int index)1307 void QLayoutSupport::adjustIndicator(const QPoint &pos, int index)
1308 {
1309     if (index == -1) { // first item goes anywhere
1310         hideIndicator(LeftIndicator);
1311         hideIndicator(TopIndicator);
1312         hideIndicator(RightIndicator);
1313         hideIndicator(BottomIndicator);
1314         return;
1315     }
1316     m_currentIndex = index;
1317     m_currentInsertMode = QDesignerLayoutDecorationExtension::InsertWidgetMode;
1318 
1319     QLayoutItem *item = layout()->itemAt(index);
1320     const QRect g = extendedGeometry(index);
1321     // ### cleanup
1322     if (LayoutInfo::isEmptyItem(item)) {
1323         // Empty grid/form cell. Draw a rectangle
1324         QPalette redPalette;
1325         redPalette.setColor(QPalette::Window, Qt::red);
1326 
1327         showIndicator(LeftIndicator,   QRect(g.x(),     g.y(),      indicatorSize, g.height()), redPalette);
1328         showIndicator(TopIndicator,    QRect(g.x(),     g.y(),      g.width(),     indicatorSize), redPalette);
1329         showIndicator(RightIndicator,  QRect(g.right(), g.y(),      indicatorSize, g.height()), redPalette);
1330         showIndicator(BottomIndicator, QRect(g.x(),     g.bottom(), g.width(),     indicatorSize), redPalette);
1331         setCurrentCellFromIndicatorOnEmptyCell(m_currentIndex);
1332     } else {
1333         // Append/Insert. Draw a bar left/right or above/below
1334         QPalette bluePalette;
1335         bluePalette.setColor(QPalette::Window, Qt::blue);
1336         hideIndicator(LeftIndicator);
1337         hideIndicator(TopIndicator);
1338 
1339         const int fromRight = g.right() - pos.x();
1340         const int fromBottom = g.bottom() - pos.y();
1341 
1342         const int fromLeft = pos.x() - g.x();
1343         const int fromTop = pos.y() - g.y();
1344 
1345         const int fromLeftRight = qMin(fromRight, fromLeft );
1346         const int fromBottomTop = qMin(fromBottom, fromTop);
1347 
1348         const Qt::Orientation indicatorOrientation =  fromLeftRight < fromBottomTop ? Qt::Vertical :  Qt::Horizontal;
1349 
1350         if (supportsIndicatorOrientation(indicatorOrientation)) {
1351             const QRect r(layout()->geometry().topLeft(), layout()->parentWidget()->size());
1352             switch (indicatorOrientation) {
1353             case  Qt::Vertical: {
1354                 hideIndicator(BottomIndicator);
1355                 const bool closeToLeft = fromLeftRight == fromLeft;
1356                 showIndicator(RightIndicator, QRect(closeToLeft ? g.x() : g.right() + 1 - indicatorSize, 0, indicatorSize, r.height()), bluePalette);
1357 
1358                 const QWidget *parent = layout()->parentWidget();
1359                 const bool leftToRight = Qt::LeftToRight == (parent ? parent->layoutDirection() : QApplication::layoutDirection());
1360                 const int incr = leftToRight == closeToLeft ? 0 : +1;
1361                 setCurrentCellFromIndicator(indicatorOrientation, m_currentIndex, incr);
1362             }
1363             break;
1364             case  Qt::Horizontal: {
1365                 hideIndicator(RightIndicator);
1366                 const bool closeToTop = fromBottomTop == fromTop;
1367                 showIndicator(BottomIndicator, QRect(r.x(), closeToTop ? g.y() : g.bottom() + 1 - indicatorSize, r.width(), indicatorSize), bluePalette);
1368 
1369                 const int incr = closeToTop ? 0 : +1;
1370                 setCurrentCellFromIndicator(indicatorOrientation, m_currentIndex, incr);
1371             }
1372                 break;
1373             }
1374         } else {
1375             hideIndicator(RightIndicator);
1376             hideIndicator(BottomIndicator);
1377         } // can handle indicatorOrientation
1378     }
1379 }
1380 
indexOf(QLayoutItem * i) const1381 int QLayoutSupport::indexOf(QLayoutItem *i) const
1382 {
1383     const QLayout *lt = layout();
1384     if (!lt)
1385         return -1;
1386 
1387     int index = 0;
1388 
1389     while (QLayoutItem *item = lt->itemAt(index)) {
1390         if (item == i)
1391             return index;
1392 
1393         ++index;
1394     }
1395 
1396     return -1;
1397 }
1398 
indexOf(QWidget * widget) const1399 int QLayoutSupport::indexOf(QWidget *widget) const
1400 {
1401     const QLayout *lt = layout();
1402     if (!lt)
1403         return -1;
1404 
1405     int index = 0;
1406     while (QLayoutItem *item = lt->itemAt(index)) {
1407         if (item->widget() == widget)
1408             return index;
1409 
1410         ++index;
1411     }
1412 
1413     return -1;
1414 }
1415 
widgets(QLayout * layout) const1416 QWidgetList QLayoutSupport::widgets(QLayout *layout) const
1417 {
1418     if (!layout)
1419         return QWidgetList();
1420 
1421     QWidgetList lst;
1422     int index = 0;
1423     while (QLayoutItem *item = layout->itemAt(index)) {
1424         ++index;
1425 
1426         QWidget *widget = item->widget();
1427         if (widget && formWindow()->isManaged(widget))
1428             lst.append(widget);
1429     }
1430 
1431     return lst;
1432 }
1433 
findItemAt(QGridLayout * gridLayout,int at_row,int at_column)1434 int QLayoutSupport::findItemAt(QGridLayout *gridLayout, int at_row, int at_column)
1435 {
1436     return findGridItemAt(gridLayout, at_row, at_column);
1437 }
1438 
1439 // Quick check whether simplify should be enabled for grids. May return false positives.
1440 // Note: Calculating the occupied area does not work as spanning items may also be simplified.
1441 
canSimplifyQuickCheck(const QGridLayout * gl)1442 bool QLayoutSupport::canSimplifyQuickCheck(const QGridLayout *gl)
1443 {
1444     if (!gl)
1445         return false;
1446     const int colCount = gl->columnCount();
1447     const int rowCount = gl->rowCount();
1448     if (colCount < 2 || rowCount < 2)
1449         return false;
1450     // try to find a spacer.
1451     const int count = gl->count();
1452     for (int index = 0; index < count; index++)
1453         if (LayoutInfo::isEmptyItem(gl->itemAt(index)))
1454             return true;
1455     return false;
1456 }
1457 
canSimplifyQuickCheck(const QFormLayout * fl)1458 bool QLayoutSupport::canSimplifyQuickCheck(const QFormLayout *fl)
1459 {
1460     return canSimplifyFormLayout(fl, QRect(QPoint(0, 0), QSize(32767, 32767)));
1461 }
1462 
1463 // remove dummy spacers
removeEmptyCells(QGridLayout * grid,const QRect & area)1464 bool QLayoutSupport::removeEmptyCells(QGridLayout *grid, const QRect &area)
1465 {
1466     return removeEmptyCellsOnGrid(grid, area);
1467 }
1468 
createEmptyCells(QGridLayout * gridLayout)1469 void QLayoutSupport::createEmptyCells(QGridLayout *gridLayout)
1470 {
1471     Q_ASSERT(gridLayout);
1472     GridLayoutState gs;
1473     gs.fromLayout(gridLayout);
1474 
1475     const GridLayoutState::CellStates cs = GridLayoutState::cellStates(gs.widgetItemMap.values(), gs.rowCount, gs.colCount);
1476     for (int c = 0; c < gs.colCount; c++)
1477         for (int r = 0; r < gs.rowCount; r++)
1478             if (needsSpacerItem(cs[r * gs.colCount + c])) {
1479                 const int existingItemIndex = findItemAt(gridLayout, r, c);
1480                 if (existingItemIndex == -1)
1481                     gridLayout->addItem(createGridSpacer(), r, c);
1482             }
1483 }
1484 
removeEmptyCells(QFormLayout * formLayout,const QRect & area)1485 bool QLayoutSupport::removeEmptyCells(QFormLayout *formLayout, const QRect &area)
1486 {
1487     return removeEmptyCellsOnGrid(formLayout, area);
1488 }
1489 
createEmptyCells(QFormLayout * formLayout)1490 void QLayoutSupport::createEmptyCells(QFormLayout *formLayout)
1491 {
1492     // No spanning items here..
1493     if (const int rowCount = formLayout->rowCount())
1494         for (int c = 0; c < FormLayoutColumns; c++)
1495             for (int r = 0; r < rowCount; r++)
1496                 if (findGridItemAt(formLayout, r, c) == -1)
1497                     formLayout->setItem(r, c == 0 ? QFormLayout::LabelRole : QFormLayout::FieldRole, createFormSpacer());
1498 }
1499 
findItemAt(const QPoint & pos) const1500 int QLayoutSupport::findItemAt(const QPoint &pos) const
1501 {
1502     if (!layout())
1503         return -1;
1504 
1505     const QLayout *lt = layout();
1506     const int count = lt->count();
1507 
1508     if (count == 0)
1509         return -1;
1510 
1511     int best = -1;
1512     int bestIndex = -1;
1513 
1514     for (int index = 0;  index < count;  index++) {
1515         QLayoutItem *item = lt->itemAt(index);
1516         bool visible = true;
1517         // When dragging widgets within layout, the source widget is invisible and must not be hit
1518         if (const QWidget *w = item->widget())
1519             visible = w->isVisible();
1520         if (visible) {
1521             const QRect g = item->geometry();
1522 
1523             const int dist = (g.center() - pos).manhattanLength();
1524             if (best == -1 || dist < best) {
1525                 best = dist;
1526                 bestIndex = index;
1527             }
1528         }
1529     }
1530     return bestIndex;
1531 }
1532 
1533 // ------------ QBoxLayoutSupport (LayoutDecorationExtension)
1534 namespace {
1535 class QBoxLayoutSupport: public QLayoutSupport
1536 {
1537 public:
1538     QBoxLayoutSupport(QDesignerFormWindowInterface *formWindow, QWidget *widget, Qt::Orientation orientation, QObject *parent = nullptr);
1539 
1540     void insertWidget(QWidget *widget, const QPair<int, int> &cell) override;
1541     void removeWidget(QWidget *widget) override;
simplify()1542     void simplify() override {}
insertRow(int)1543     void insertRow(int /*row*/) override {}
insertColumn(int)1544     void insertColumn(int /*column*/) override {}
1545 
findItemAt(int,int) const1546     int findItemAt(int /*at_row*/, int /*at_column*/) const override {    return -1; }
1547     using QLayoutSupport::findItemAt;
1548 
1549 private:
1550     void setCurrentCellFromIndicatorOnEmptyCell(int index) override;
1551     void setCurrentCellFromIndicator(Qt::Orientation indicatorOrientation, int index, int increment) override;
1552     bool supportsIndicatorOrientation(Qt::Orientation indicatorOrientation) const override;
1553     QRect extendedGeometry(int index) const override;
1554 
1555     const Qt::Orientation m_orientation;
1556 };
1557 
removeWidget(QWidget * widget)1558 void QBoxLayoutSupport::removeWidget(QWidget *widget)
1559 {
1560     QLayout *lt = layout();
1561     const int index = lt->indexOf(widget);
1562     // Adjust the current cell in case a widget was dragged within the same layout to a position
1563     // of higher index, which happens as follows:
1564     // Drag start: The widget is hidden
1565     // Drop: Current cell is stored, widget is removed and re-added, causing an index offset that needs to be compensated
1566     QPair<int, int> currCell = currentCell();
1567     switch (m_orientation) {
1568     case Qt::Horizontal:
1569         if (currCell.second > 0 && index < currCell.second ) {
1570             currCell.second--;
1571             setCurrentCell(currCell);
1572         }
1573         break;
1574     case Qt::Vertical:
1575         if (currCell.first > 0 && index < currCell.first) {
1576             currCell.first--;
1577             setCurrentCell(currCell);
1578         }
1579         break;
1580     }
1581     helper()->removeWidget(lt, widget);
1582 }
1583 
QBoxLayoutSupport(QDesignerFormWindowInterface * formWindow,QWidget * widget,Qt::Orientation orientation,QObject * parent)1584 QBoxLayoutSupport::QBoxLayoutSupport(QDesignerFormWindowInterface *formWindow, QWidget *widget, Qt::Orientation orientation, QObject *parent) :
1585     QLayoutSupport(formWindow, widget, new BoxLayoutHelper(orientation), parent),
1586     m_orientation(orientation)
1587 {
1588 }
1589 
setCurrentCellFromIndicatorOnEmptyCell(int index)1590 void QBoxLayoutSupport::setCurrentCellFromIndicatorOnEmptyCell(int index)
1591 {
1592     qDebug() << "QBoxLayoutSupport::setCurrentCellFromIndicatorOnEmptyCell(): Warning: found a fake spacer inside a vbox layout at " << index;
1593     setCurrentCell(qMakePair(0, 0));
1594 }
1595 
insertWidget(QWidget * widget,const QPair<int,int> & cell)1596 void QBoxLayoutSupport::insertWidget(QWidget *widget, const QPair<int, int> &cell)
1597 {
1598     switch (m_orientation) {
1599     case  Qt::Horizontal:
1600         helper()->insertWidget(layout(), QRect(cell.second, 0, 1, 1), widget);
1601         break;
1602     case  Qt::Vertical:
1603         helper()->insertWidget(layout(), QRect(0, cell.first, 1, 1), widget);
1604         break;
1605     }
1606 }
1607 
setCurrentCellFromIndicator(Qt::Orientation indicatorOrientation,int index,int increment)1608 void QBoxLayoutSupport::setCurrentCellFromIndicator(Qt::Orientation indicatorOrientation, int index, int increment)
1609 {
1610     if (m_orientation == Qt::Horizontal && indicatorOrientation == Qt::Vertical) {
1611         setCurrentCell(qMakePair(0, index + increment));
1612     } else if (m_orientation == Qt::Vertical && indicatorOrientation == Qt::Horizontal) {
1613         setCurrentCell(qMakePair(index + increment, 0));
1614     }
1615 }
1616 
supportsIndicatorOrientation(Qt::Orientation indicatorOrientation) const1617 bool QBoxLayoutSupport::supportsIndicatorOrientation(Qt::Orientation indicatorOrientation) const
1618 {
1619     return m_orientation != indicatorOrientation;
1620 }
1621 
extendedGeometry(int index) const1622 QRect QBoxLayoutSupport::extendedGeometry(int index) const
1623 {
1624     QLayoutItem *item = layout()->itemAt(index);
1625     // start off with item geometry
1626     QRect g = item->geometry();
1627 
1628     const QRect info = itemInfo(index);
1629 
1630     // On left border: extend to widget border
1631     if (info.x() == 0) {
1632         QPoint topLeft = g.topLeft();
1633         topLeft.rx() = layout()->geometry().left();
1634         g.setTopLeft(topLeft);
1635     }
1636 
1637     // On top border: extend to widget border
1638     if (info.y() == 0) {
1639         QPoint topLeft = g.topLeft();
1640         topLeft.ry() = layout()->geometry().top();
1641         g.setTopLeft(topLeft);
1642     }
1643 
1644     // is this the last item?
1645     const QBoxLayout *box = static_cast<const QBoxLayout*>(layout());
1646     if (index < box->count() -1)
1647         return g; // Nope.
1648 
1649     // extend to widget border
1650     QPoint bottomRight = g.bottomRight();
1651     switch (m_orientation) {
1652     case Qt::Vertical:
1653         bottomRight.ry() = layout()->geometry().bottom();
1654         break;
1655     case Qt::Horizontal:
1656         bottomRight.rx() = layout()->geometry().right();
1657         break;
1658     }
1659     g.setBottomRight(bottomRight);
1660     return g;
1661 }
1662 
1663 // --------------  Base class for QGridLayout-like support classes (LayoutDecorationExtension)
1664 template <class GridLikeLayout>
1665 class GridLikeLayoutSupportBase: public QLayoutSupport
1666 {
1667 public:
1668 
GridLikeLayoutSupportBase(QDesignerFormWindowInterface * formWindow,QWidget * widget,LayoutHelper * helper,QObject * parent=nullptr)1669     GridLikeLayoutSupportBase(QDesignerFormWindowInterface *formWindow, QWidget *widget, LayoutHelper *helper, QObject *parent = nullptr) :
1670         QLayoutSupport(formWindow, widget, helper, parent) {}
1671 
1672     void insertWidget(QWidget *widget, const QPair<int, int> &cell) override;
removeWidget(QWidget * widget)1673     void removeWidget(QWidget *widget) override { helper()->removeWidget(layout(), widget); }
1674     int findItemAt(int row, int column) const override;
1675     using QLayoutSupport::findItemAt;
1676 
1677 protected:
gridLikeLayout() const1678     GridLikeLayout *gridLikeLayout() const {
1679         return qobject_cast<GridLikeLayout*>(LayoutInfo::managedLayout(formWindow()->core(), widget()));
1680     }
1681 
1682 private:
1683 
1684     void setCurrentCellFromIndicatorOnEmptyCell(int index) override;
1685     void setCurrentCellFromIndicator(Qt::Orientation indicatorOrientation, int index, int increment) override;
supportsIndicatorOrientation(Qt::Orientation) const1686     bool supportsIndicatorOrientation(Qt::Orientation) const override { return true; }
1687 
1688     QRect extendedGeometry(int index) const override;
1689 
1690     // Overwrite to check the insertion position (if there are limits)
checkCellForInsertion(int *,int *) const1691     virtual void checkCellForInsertion(int * /*row*/, int * /*col*/) const {}
1692 };
1693 
1694 template <class GridLikeLayout>
setCurrentCellFromIndicatorOnEmptyCell(int index)1695 void GridLikeLayoutSupportBase<GridLikeLayout>::setCurrentCellFromIndicatorOnEmptyCell(int index)
1696 {
1697     GridLikeLayout *grid = gridLikeLayout();
1698     Q_ASSERT(grid);
1699 
1700     setInsertMode(InsertWidgetMode);
1701     int row, column, rowspan, colspan;
1702 
1703     getGridItemPosition(grid, index, &row, &column, &rowspan, &colspan);
1704     setCurrentCell(qMakePair(row, column));
1705 }
1706 
1707 template <class GridLikeLayout>
setCurrentCellFromIndicator(Qt::Orientation indicatorOrientation,int index,int increment)1708 void GridLikeLayoutSupportBase<GridLikeLayout>::setCurrentCellFromIndicator(Qt::Orientation indicatorOrientation, int index, int increment) {
1709     const QRect info = itemInfo(index);
1710     switch (indicatorOrientation) {
1711     case Qt::Vertical: {
1712         setInsertMode(InsertColumnMode);
1713         int row = info.top();
1714         int column = increment ? info.right() + 1 : info.left();
1715         checkCellForInsertion(&row, &column);
1716         setCurrentCell(qMakePair(row , column));
1717     }
1718         break;
1719     case Qt::Horizontal: {
1720         setInsertMode(InsertRowMode);
1721         int row = increment ? info.bottom() + 1 : info.top();
1722         int column = info.left();
1723         checkCellForInsertion(&row, &column);
1724         setCurrentCell(qMakePair(row, column));
1725     }
1726         break;
1727     }
1728 }
1729 
1730 template <class GridLikeLayout>
insertWidget(QWidget * widget,const QPair<int,int> & cell)1731 void GridLikeLayoutSupportBase<GridLikeLayout>::insertWidget(QWidget *widget, const QPair<int, int> &cell)
1732 {
1733     helper()->insertWidget(layout(), QRect(cell.second, cell.first, 1, 1), widget);
1734 }
1735 
1736 template <class GridLikeLayout>
findItemAt(int at_row,int at_column) const1737 int GridLikeLayoutSupportBase<GridLikeLayout>::findItemAt(int at_row, int at_column) const
1738 {
1739     GridLikeLayout *grid = gridLikeLayout();
1740     Q_ASSERT(grid);
1741     return findGridItemAt(grid, at_row, at_column);
1742 }
1743 
1744 template <class GridLikeLayout>
extendedGeometry(int index) const1745 QRect GridLikeLayoutSupportBase<GridLikeLayout>::extendedGeometry(int index) const
1746 {
1747     QLayoutItem *item = layout()->itemAt(index);
1748     // start off with item geometry
1749     QRect g = item->geometry();
1750 
1751     const QRect info = itemInfo(index);
1752 
1753     // On left border: extend to widget border
1754     if (info.x() == 0) {
1755         QPoint topLeft = g.topLeft();
1756         topLeft.rx() = layout()->geometry().left();
1757         g.setTopLeft(topLeft);
1758     }
1759 
1760     // On top border: extend to widget border
1761     if (info.y() == 0) {
1762         QPoint topLeft = g.topLeft();
1763         topLeft.ry() = layout()->geometry().top();
1764         g.setTopLeft(topLeft);
1765     }
1766     const GridLikeLayout *grid = gridLikeLayout();
1767     Q_ASSERT(grid);
1768 
1769     // extend to widget border
1770     QPoint bottomRight = g.bottomRight();
1771     if (gridRowCount(grid) == info.y())
1772         bottomRight.ry() = layout()->geometry().bottom();
1773     if (gridColumnCount(grid) == info.x())
1774         bottomRight.rx() = layout()->geometry().right();
1775     g.setBottomRight(bottomRight);
1776     return g;
1777 }
1778 
1779 // --------------  QGridLayoutSupport (LayoutDecorationExtension)
1780 class QGridLayoutSupport: public GridLikeLayoutSupportBase<QGridLayout>
1781 {
1782 public:
1783 
1784     QGridLayoutSupport(QDesignerFormWindowInterface *formWindow, QWidget *widget, QObject *parent = nullptr);
1785 
1786     void simplify() override;
1787     void insertRow(int row) override;
1788     void insertColumn(int column) override;
1789 
1790 private:
1791 };
1792 
QGridLayoutSupport(QDesignerFormWindowInterface * formWindow,QWidget * widget,QObject * parent)1793 QGridLayoutSupport::QGridLayoutSupport(QDesignerFormWindowInterface *formWindow, QWidget *widget, QObject *parent) :
1794     GridLikeLayoutSupportBase<QGridLayout>(formWindow, widget, new GridLayoutHelper, parent)
1795 {
1796 }
1797 
insertRow(int row)1798 void QGridLayoutSupport::insertRow(int row)
1799 {
1800     QGridLayout *grid = gridLayout();
1801     Q_ASSERT(grid);
1802     GridLayoutHelper::insertRow(grid, row);
1803 }
1804 
insertColumn(int column)1805 void QGridLayoutSupport::insertColumn(int column)
1806 {
1807     QGridLayout *grid = gridLayout();
1808     Q_ASSERT(grid);
1809     GridLayoutState state;
1810     state.fromLayout(grid);
1811     state.insertColumn(column);
1812     state.applyToLayout(formWindow()->core(), widget());
1813 }
1814 
simplify()1815 void QGridLayoutSupport::simplify()
1816 {
1817     QGridLayout *grid = gridLayout();
1818     Q_ASSERT(grid);
1819     GridLayoutState state;
1820     state.fromLayout(grid);
1821 
1822     const QRect fullArea = QRect(0, 0, state.colCount, state.rowCount);
1823     if (state.simplify(fullArea, false))
1824         state.applyToLayout(formWindow()->core(), widget());
1825 }
1826 
1827 // --------------  QFormLayoutSupport (LayoutDecorationExtension)
1828 class QFormLayoutSupport: public GridLikeLayoutSupportBase<QFormLayout>
1829 {
1830 public:
1831     QFormLayoutSupport(QDesignerFormWindowInterface *formWindow, QWidget *widget, QObject *parent = nullptr);
1832 
simplify()1833     void simplify() override {}
insertRow(int)1834     void insertRow(int /*row*/) override {}
insertColumn(int)1835     void insertColumn(int /*column*/) override {}
1836 
1837 private:
1838     void checkCellForInsertion(int * row, int *col) const override;
1839 };
1840 
QFormLayoutSupport(QDesignerFormWindowInterface * formWindow,QWidget * widget,QObject * parent)1841 QFormLayoutSupport::QFormLayoutSupport(QDesignerFormWindowInterface *formWindow, QWidget *widget, QObject *parent) :
1842     GridLikeLayoutSupportBase<QFormLayout>(formWindow, widget, new FormLayoutHelper, parent)
1843 {
1844 }
1845 
checkCellForInsertion(int * row,int * col) const1846 void QFormLayoutSupport::checkCellForInsertion(int *row, int *col) const
1847 {
1848     if (*col >= FormLayoutColumns) { // Clamp to 2 columns
1849         *col = 1;
1850         (*row)++;
1851     }
1852 }
1853 } //  anonymous namespace
1854 
createLayoutSupport(QDesignerFormWindowInterface * formWindow,QWidget * widget,QObject * parent)1855 QLayoutSupport *QLayoutSupport::createLayoutSupport(QDesignerFormWindowInterface *formWindow, QWidget *widget, QObject *parent)
1856 {
1857     const QLayout *layout = LayoutInfo::managedLayout(formWindow->core(), widget);
1858     Q_ASSERT(layout);
1859     QLayoutSupport *rc = nullptr;
1860     switch (LayoutInfo::layoutType(formWindow->core(), layout)) {
1861     case LayoutInfo::HBox:
1862         rc = new QBoxLayoutSupport(formWindow, widget, Qt::Horizontal, parent);
1863         break;
1864     case LayoutInfo::VBox:
1865         rc = new QBoxLayoutSupport(formWindow, widget, Qt::Vertical, parent);
1866         break;
1867     case LayoutInfo::Grid:
1868         rc = new QGridLayoutSupport(formWindow, widget, parent);
1869         break;
1870     case LayoutInfo::Form:
1871         rc = new QFormLayoutSupport(formWindow, widget, parent);
1872         break;
1873      default:
1874         break;
1875     }
1876     Q_ASSERT(rc);
1877     return rc;
1878 }
1879 } // namespace qdesigner_internal
1880 
1881 // -------------- QLayoutWidget
QLayoutWidget(QDesignerFormWindowInterface * formWindow,QWidget * parent)1882 QLayoutWidget::QLayoutWidget(QDesignerFormWindowInterface *formWindow, QWidget *parent)
1883     : QWidget(parent), m_formWindow(formWindow),
1884       m_leftMargin(0), m_topMargin(0), m_rightMargin(0), m_bottomMargin(0)
1885 {
1886 }
1887 
paintEvent(QPaintEvent *)1888 void QLayoutWidget::paintEvent(QPaintEvent*)
1889 {
1890     if (m_formWindow->currentTool() != 0)
1891         return;
1892 
1893     // only draw red borders if we're editting widgets
1894 
1895     QPainter p(this);
1896 
1897     QMap<int, QMap<int, bool> > excludedRowsForColumn;
1898     QMap<int, QMap<int, bool> > excludedColumnsForRow;
1899 
1900     QLayout *lt = layout();
1901     QGridLayout *grid = qobject_cast<QGridLayout *>(lt);
1902     if (lt) {
1903         if (const int count = lt->count()) {
1904             p.setPen(QPen(QColor(255, 0, 0, 35), 1));
1905             for (int i = 0; i < count; i++) {
1906                 QLayoutItem *item = lt->itemAt(i);
1907                 if (grid) {
1908                     int row, column, rowSpan, columnSpan;
1909                     grid->getItemPosition(i, &row, &column, &rowSpan, &columnSpan);
1910                     QMap<int, bool> rows;
1911                     QMap<int, bool> columns;
1912                     for (int i = rowSpan; i > 1; i--)
1913                         rows[row + i - 2] = true;
1914                     for (int i = columnSpan; i > 1; i--)
1915                         columns[column + i - 2] = true;
1916 
1917                     while (rowSpan > 0) {
1918                         excludedColumnsForRow[row + rowSpan - 1].insert(columns);
1919                         rowSpan--;
1920                     }
1921                     while (columnSpan > 0) {
1922                         excludedRowsForColumn[column + columnSpan - 1].insert(rows);
1923                         columnSpan--;
1924                     }
1925                 }
1926                 if (item->spacerItem()) {
1927                     const QRect geometry = item->geometry();
1928                     if (!geometry.isNull())
1929                         p.drawRect(geometry.adjusted(1, 1, -2, -2));
1930                 }
1931             }
1932         }
1933     }
1934     if (grid) {
1935         p.setPen(QPen(QColor(0, 0x80, 0, 0x80), 1));
1936         const int rowCount = grid->rowCount();
1937         const int columnCount = grid->columnCount();
1938         for (int i = 0; i < rowCount; i++) {
1939             for (int j = 0; j < columnCount; j++) {
1940                 const QRect cellRect = grid->cellRect(i, j);
1941                 if (j < columnCount - 1 && !excludedColumnsForRow.value(i).value(j, false)) {
1942                     const double y0 = (i == 0)
1943                             ? 0 : (grid->cellRect(i - 1, j).bottom() + cellRect.top()) / 2.0;
1944                     const double y1 = (i == rowCount - 1)
1945                             ? height() - 1 : (cellRect.bottom() + grid->cellRect(i + 1, j).top()) / 2.0;
1946                     const double x = (cellRect.right() + grid->cellRect(i, j + 1).left()) / 2.0;
1947                     p.drawLine(QPointF(x, y0), QPointF(x, y1));
1948                 }
1949                 if (i < rowCount - 1 && !excludedRowsForColumn.value(j).value(i, false)) {
1950                     const double x0 = (j == 0)
1951                             ? 0 : (grid->cellRect(i, j - 1).right() + cellRect.left()) / 2.0;
1952                     const double x1 = (j == columnCount - 1)
1953                             ? width() - 1 : (cellRect.right() + grid->cellRect(i, j + 1).left()) / 2.0;
1954                     const double y = (cellRect.bottom() + grid->cellRect(i + 1, j).top()) / 2.0;
1955                     p.drawLine(QPointF(x0, y), QPointF(x1, y));
1956                 }
1957             }
1958         }
1959     }
1960     p.setPen(QPen(QColor(255, 0, 0, 128), 1));
1961     p.drawRect(0, 0, width() - 1, height() - 1);
1962 }
1963 
event(QEvent * e)1964 bool QLayoutWidget::event(QEvent *e)
1965 {
1966     switch (e->type()) {
1967         case QEvent::LayoutRequest: {
1968             (void) QWidget::event(e);
1969             // Magic: We are layouted, but the parent is not..
1970             if (layout() && qdesigner_internal::LayoutInfo::layoutType(formWindow()->core(), parentWidget()) == qdesigner_internal::LayoutInfo::NoLayout) {
1971                 resize(layout()->totalMinimumSize().expandedTo(size()));
1972             }
1973 
1974             update();
1975 
1976             return true;
1977         }
1978 
1979         default:
1980             break;
1981     }
1982 
1983     return QWidget::event(e);
1984 }
1985 
layoutLeftMargin() const1986 int QLayoutWidget::layoutLeftMargin() const
1987 {
1988     if (m_leftMargin < 0 && layout()) {
1989         int margin;
1990         layout()->getContentsMargins(&margin, nullptr, nullptr, nullptr);
1991         return margin;
1992     }
1993     return m_leftMargin;
1994 }
1995 
setLayoutLeftMargin(int layoutMargin)1996 void QLayoutWidget::setLayoutLeftMargin(int layoutMargin)
1997 {
1998     m_leftMargin = layoutMargin;
1999     if (layout()) {
2000         int newMargin = m_leftMargin;
2001         if (newMargin >= 0 && newMargin < ShiftValue)
2002             newMargin = ShiftValue;
2003         int left, top, right, bottom;
2004         layout()->getContentsMargins(&left, &top, &right, &bottom);
2005         layout()->setContentsMargins(newMargin, top, right, bottom);
2006     }
2007 }
2008 
layoutTopMargin() const2009 int QLayoutWidget::layoutTopMargin() const
2010 {
2011     if (m_topMargin < 0 && layout()) {
2012         int margin;
2013         layout()->getContentsMargins(nullptr, &margin, nullptr, nullptr);
2014         return margin;
2015     }
2016     return m_topMargin;
2017 }
2018 
setLayoutTopMargin(int layoutMargin)2019 void QLayoutWidget::setLayoutTopMargin(int layoutMargin)
2020 {
2021     m_topMargin = layoutMargin;
2022     if (layout()) {
2023         int newMargin = m_topMargin;
2024         if (newMargin >= 0 && newMargin < ShiftValue)
2025             newMargin = ShiftValue;
2026         int left, top, right, bottom;
2027         layout()->getContentsMargins(&left, &top, &right, &bottom);
2028         layout()->setContentsMargins(left, newMargin, right, bottom);
2029     }
2030 }
2031 
layoutRightMargin() const2032 int QLayoutWidget::layoutRightMargin() const
2033 {
2034     if (m_rightMargin < 0 && layout()) {
2035         int margin;
2036         layout()->getContentsMargins(nullptr, nullptr, &margin, nullptr);
2037         return margin;
2038     }
2039     return m_rightMargin;
2040 }
2041 
setLayoutRightMargin(int layoutMargin)2042 void QLayoutWidget::setLayoutRightMargin(int layoutMargin)
2043 {
2044     m_rightMargin = layoutMargin;
2045     if (layout()) {
2046         int newMargin = m_rightMargin;
2047         if (newMargin >= 0 && newMargin < ShiftValue)
2048             newMargin = ShiftValue;
2049         int left, top, right, bottom;
2050         layout()->getContentsMargins(&left, &top, &right, &bottom);
2051         layout()->setContentsMargins(left, top, newMargin, bottom);
2052     }
2053 }
2054 
layoutBottomMargin() const2055 int QLayoutWidget::layoutBottomMargin() const
2056 {
2057     if (m_bottomMargin < 0 && layout()) {
2058         int margin;
2059         layout()->getContentsMargins(nullptr, nullptr, nullptr, &margin);
2060         return margin;
2061     }
2062     return m_bottomMargin;
2063 }
2064 
setLayoutBottomMargin(int layoutMargin)2065 void QLayoutWidget::setLayoutBottomMargin(int layoutMargin)
2066 {
2067     m_bottomMargin = layoutMargin;
2068     if (layout()) {
2069         int newMargin = m_bottomMargin;
2070         if (newMargin >= 0 && newMargin < ShiftValue)
2071             newMargin = ShiftValue;
2072         int left, top, right, bottom;
2073         layout()->getContentsMargins(&left, &top, &right, &bottom);
2074         layout()->setContentsMargins(left, top, right, newMargin);
2075     }
2076 }
2077 
2078 QT_END_NAMESPACE
2079