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