source: trunk/tools/designer/src/lib/shared/qlayout_widget.cpp@ 944

Last change on this file since 944 was 846, checked in by Dmitry A. Kuminov, 15 years ago

trunk: Merged in qt 4.7.2 sources from branches/vendor/nokia/qt.

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