/**************************************************************************** | |
** | |
** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). | |
** All rights reserved. | |
** Contact: Nokia Corporation (qt-info@nokia.com) | |
** | |
** This file is part of the Qt Designer of the Qt Toolkit. | |
** | |
** $QT_BEGIN_LICENSE:LGPL$ | |
** GNU Lesser General Public License Usage | |
** This file may be used under the terms of the GNU Lesser General Public | |
** License version 2.1 as published by the Free Software Foundation and | |
** appearing in the file LICENSE.LGPL included in the packaging of this | |
** file. Please review the following information to ensure the GNU Lesser | |
** General Public License version 2.1 requirements will be met: | |
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. | |
** | |
** In addition, as a special exception, Nokia gives you certain additional | |
** rights. These rights are described in the Nokia Qt LGPL Exception | |
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. | |
** | |
** GNU General Public License Usage | |
** Alternatively, this file may be used under the terms of the GNU General | |
** Public License version 3.0 as published by the Free Software Foundation | |
** and appearing in the file LICENSE.GPL included in the packaging of this | |
** file. Please review the following information to ensure the GNU General | |
** Public License version 3.0 requirements will be met: | |
** http://www.gnu.org/copyleft/gpl.html. | |
** | |
** Other Usage | |
** Alternatively, this file may be used in accordance with the terms and | |
** conditions contained in a signed written agreement between you and Nokia. | |
** | |
** | |
** | |
** | |
** | |
** $QT_END_LICENSE$ | |
** | |
****************************************************************************/ | |
#include "qlayout_widget_p.h" | |
#include "qdesigner_utils_p.h" | |
#include "layout_p.h" | |
#include "layoutinfo_p.h" | |
#include "invisible_widget_p.h" | |
#include "qdesigner_widgetitem_p.h" | |
#include <QtDesigner/QDesignerFormWindowInterface> | |
#include <QtDesigner/QExtensionManager> | |
#include <QtDesigner/QDesignerFormEditorInterface> | |
#include <QtDesigner/QDesignerPropertySheetExtension> | |
#include <QtDesigner/QDesignerWidgetFactoryInterface> | |
#include <QtGui/QPainter> | |
#include <QtGui/QHBoxLayout> | |
#include <QtGui/QVBoxLayout> | |
#include <QtGui/QGridLayout> | |
#include <QtGui/QFormLayout> | |
#include <QtGui/qevent.h> | |
#include <QtCore/qdebug.h> | |
#include <QtCore/QtAlgorithms> | |
#include <QtCore/QMap> | |
#include <QtCore/QStack> | |
#include <QtCore/QPair> | |
#include <QtCore/QSet> | |
enum { ShiftValue = 1 }; | |
enum { debugLayout = 0 }; | |
enum { FormLayoutColumns = 2 }; | |
enum { indicatorSize = 2 }; | |
// Grid/form Helpers: get info (overloads to make templates work) | |
namespace { // Do not use static, will break HP-UX due to templates | |
QT_USE_NAMESPACE | |
// overloads to make templates over QGridLayout/QFormLayout work | |
inline int gridRowCount(const QGridLayout *gridLayout) | |
{ | |
return gridLayout->rowCount(); | |
} | |
inline int gridColumnCount(const QGridLayout *gridLayout) | |
{ | |
return gridLayout->columnCount(); | |
} | |
// QGridLayout/QFormLayout Helpers: get item position (overloads to make templates work) | |
inline void getGridItemPosition(QGridLayout *gridLayout, int index, | |
int *row, int *column, int *rowspan, int *colspan) | |
{ | |
gridLayout->getItemPosition(index, row, column, rowspan, colspan); | |
} | |
QRect gridItemInfo(QGridLayout *grid, int index) | |
{ | |
int row, column, rowSpan, columnSpan; | |
// getItemPosition is not const, grmbl.. | |
grid->getItemPosition(index, &row, &column, &rowSpan, &columnSpan); | |
return QRect(column, row, columnSpan, rowSpan); | |
} | |
inline int gridRowCount(const QFormLayout *formLayout) { return formLayout->rowCount(); } | |
inline int gridColumnCount(const QFormLayout *) { return FormLayoutColumns; } | |
inline void getGridItemPosition(QFormLayout *formLayout, int index, int *row, int *column, int *rowspan, int *colspan) | |
{ | |
qdesigner_internal::getFormLayoutItemPosition(formLayout, index, row, column, rowspan, colspan); | |
} | |
QRect gridItemInfo(const QFormLayout *form, int index) | |
{ | |
int row; | |
int column; | |
int colspan; | |
qdesigner_internal::getFormLayoutItemPosition(form, index, &row, &column, 0, &colspan); | |
return QRect(column, row, colspan, 1); | |
} | |
} // namespace anonymous | |
QT_BEGIN_NAMESPACE | |
static const char *objectNameC = "objectName"; | |
static const char *sizeConstraintC = "sizeConstraint"; | |
/* A padding spacer element that is used to represent an empty form layout cell. It should grow with its cell. | |
* Should not be used on a grid as it causes resizing inconsistencies */ | |
namespace qdesigner_internal { | |
class PaddingSpacerItem : public QSpacerItem { | |
public: | |
PaddingSpacerItem() : QSpacerItem(0, 0, QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding) {} | |
virtual Qt::Orientations expandingDirections () const { return Qt::Vertical | Qt::Horizontal; } | |
}; | |
} | |
static inline QSpacerItem *createGridSpacer() | |
{ | |
return new QSpacerItem(0, 0); | |
} | |
static inline QSpacerItem *createFormSpacer() | |
{ | |
return new qdesigner_internal::PaddingSpacerItem; | |
} | |
// QGridLayout/QFormLayout Helpers: Debug items of GridLikeLayout | |
template <class GridLikeLayout> | |
static QDebug debugGridLikeLayout(QDebug str, const GridLikeLayout &gl) | |
{ | |
const int count = gl.count(); | |
str << "Grid: " << gl.objectName() << gridRowCount(&gl) << " rows x " << gridColumnCount(&gl) | |
<< " cols " << count << " items\n"; | |
for (int i = 0; i < count; i++) { | |
QLayoutItem *item = gl.itemAt(i); | |
str << "Item " << i << item << item->widget() << gridItemInfo(const_cast<GridLikeLayout *>(&gl), i) << " empty=" << qdesigner_internal::LayoutInfo::isEmptyItem(item) << "\n"; | |
} | |
return str; | |
} | |
static inline QDebug operator<<(QDebug str, const QGridLayout &gl) { return debugGridLikeLayout(str, gl); } | |
static inline QDebug operator<<(QDebug str, const QFormLayout &fl) { return debugGridLikeLayout(str, fl); } | |
static inline bool isEmptyFormLayoutRow(const QFormLayout *fl, int row) | |
{ | |
// Spanning can never be empty | |
if (fl->itemAt(row, QFormLayout::SpanningRole)) | |
return false; | |
return qdesigner_internal::LayoutInfo::isEmptyItem(fl->itemAt(row, QFormLayout::LabelRole)) && qdesigner_internal::LayoutInfo::isEmptyItem(fl->itemAt(row, QFormLayout::FieldRole)); | |
} | |
static inline bool canSimplifyFormLayout(const QFormLayout *formLayout, const QRect &restrictionArea) | |
{ | |
if (restrictionArea.x() >= FormLayoutColumns) | |
return false; | |
// Try to find empty rows | |
const int bottomCheckRow = qMin(formLayout->rowCount(), restrictionArea.top() + restrictionArea.height()); | |
for (int r = restrictionArea.y(); r < bottomCheckRow; r++) | |
if (isEmptyFormLayoutRow(formLayout, r)) | |
return true; | |
return false; | |
} | |
// recreate a managed layout (which does not automagically remove | |
// empty rows/columns like grid or form layout) in case it needs to shrink | |
static QLayout *recreateManagedLayout(const QDesignerFormEditorInterface *core, QWidget *w, QLayout *lt) | |
{ | |
const qdesigner_internal::LayoutInfo::Type t = qdesigner_internal::LayoutInfo::layoutType(core, lt); | |
qdesigner_internal::LayoutProperties properties; | |
const int mask = properties.fromPropertySheet(core, lt, qdesigner_internal::LayoutProperties::AllProperties); | |
qdesigner_internal::LayoutInfo::deleteLayout(core, w); | |
QLayout *rc = core->widgetFactory()->createLayout(w, 0, t); | |
properties.toPropertySheet(core, rc, mask, true); | |
return rc; | |
} | |
// QGridLayout/QFormLayout Helpers: find an item on a form/grid. Return index | |
template <class GridLikeLayout> | |
int findGridItemAt(GridLikeLayout *gridLayout, int at_row, int at_column) | |
{ | |
Q_ASSERT(gridLayout); | |
const int count = gridLayout->count(); | |
for (int index = 0; index < count; index++) { | |
int row, column, rowspan, colspan; | |
getGridItemPosition(gridLayout, index, &row, &column, &rowspan, &colspan); | |
if (at_row >= row && at_row < (row + rowspan) | |
&& at_column >= column && at_column < (column + colspan)) { | |
return index; | |
} | |
} | |
return -1; | |
} | |
// QGridLayout/QFormLayout Helpers: remove dummy spacers on form/grid | |
template <class GridLikeLayout> | |
static bool removeEmptyCellsOnGrid(GridLikeLayout *grid, const QRect &area) | |
{ | |
// check if there are any items in the way. Should be only spacers | |
// Unique out items that span rows/columns. | |
QVector<int> indexesToBeRemoved; | |
indexesToBeRemoved.reserve(grid->count()); | |
const int rightColumn = area.x() + area.width(); | |
const int bottomRow = area.y() + area.height(); | |
for (int c = area.x(); c < rightColumn; c++) | |
for (int r = area.y(); r < bottomRow; r++) { | |
const int index = findGridItemAt(grid, r ,c); | |
if (index != -1) | |
if (QLayoutItem *item = grid->itemAt(index)) { | |
if (qdesigner_internal::LayoutInfo::isEmptyItem(item)) { | |
if (indexesToBeRemoved.indexOf(index) == -1) | |
indexesToBeRemoved.push_back(index); | |
} else { | |
return false; | |
} | |
} | |
} | |
// remove, starting from last | |
if (!indexesToBeRemoved.empty()) { | |
qStableSort(indexesToBeRemoved.begin(), indexesToBeRemoved.end()); | |
for (int i = indexesToBeRemoved.size() - 1; i >= 0; i--) | |
delete grid->takeAt(indexesToBeRemoved[i]); | |
} | |
return true; | |
} | |
namespace qdesigner_internal { | |
// --------- LayoutProperties | |
LayoutProperties::LayoutProperties() | |
{ | |
clear(); | |
} | |
void LayoutProperties::clear() | |
{ | |
qFill(m_margins, m_margins + MarginCount, 0); | |
qFill(m_marginsChanged, m_marginsChanged + MarginCount, false); | |
qFill(m_spacings, m_spacings + SpacingsCount, 0); | |
qFill(m_spacingsChanged, m_spacingsChanged + SpacingsCount, false); | |
m_objectName = QVariant(); | |
m_objectNameChanged = false; | |
m_sizeConstraint = QVariant(QLayout::SetDefaultConstraint); | |
m_sizeConstraintChanged = false; | |
m_fieldGrowthPolicyChanged = m_rowWrapPolicyChanged = m_labelAlignmentChanged = m_formAlignmentChanged = false; | |
m_fieldGrowthPolicy = m_rowWrapPolicy = m_formAlignment = QVariant(); | |
m_boxStretchChanged = m_gridRowStretchChanged = m_gridColumnStretchChanged = m_gridRowMinimumHeightChanged = false; | |
m_boxStretch = m_gridRowStretch = m_gridColumnStretch = m_gridRowMinimumHeight = QVariant(); | |
} | |
int LayoutProperties::visibleProperties(const QLayout *layout) | |
{ | |
// Grid like layout have 2 spacings. | |
const bool isFormLayout = qobject_cast<const QFormLayout*>(layout); | |
const bool isGridLike = qobject_cast<const QGridLayout*>(layout) || isFormLayout; | |
int rc = ObjectNameProperty|LeftMarginProperty|TopMarginProperty|RightMarginProperty|BottomMarginProperty| | |
SizeConstraintProperty; | |
rc |= isGridLike ? (HorizSpacingProperty|VertSpacingProperty) : SpacingProperty; | |
if (isFormLayout) { | |
rc |= FieldGrowthPolicyProperty|RowWrapPolicyProperty|LabelAlignmentProperty|FormAlignmentProperty; | |
} else { | |
if (isGridLike) { | |
rc |= GridRowStretchProperty|GridColumnStretchProperty|GridRowMinimumHeightProperty|GridColumnMinimumWidthProperty; | |
} else { | |
rc |= BoxStretchProperty; | |
} | |
} | |
return rc; | |
} | |
static const char *marginPropertyNamesC[] = {"leftMargin", "topMargin", "rightMargin", "bottomMargin"}; | |
static const char *spacingPropertyNamesC[] = {"spacing", "horizontalSpacing", "verticalSpacing" }; | |
static const char *fieldGrowthPolicyPropertyC = "fieldGrowthPolicy"; | |
static const char *rowWrapPolicyPropertyC = "rowWrapPolicy"; | |
static const char *labelAlignmentPropertyC = "labelAlignment"; | |
static const char *formAlignmentPropertyC = "formAlignment"; | |
static const char *boxStretchPropertyC = "stretch"; | |
static const char *gridRowStretchPropertyC = "rowStretch"; | |
static const char *gridColumnStretchPropertyC = "columnStretch"; | |
static const char *gridRowMinimumHeightPropertyC = "rowMinimumHeight"; | |
static const char *gridColumnMinimumWidthPropertyC = "columnMinimumWidth"; | |
static bool intValueFromSheet(const QDesignerPropertySheetExtension *sheet, const QString &name, int *value, bool *changed) | |
{ | |
const int sheetIndex = sheet->indexOf(name); | |
if (sheetIndex == -1) | |
return false; | |
*value = sheet->property(sheetIndex).toInt(); | |
*changed = sheet->isChanged(sheetIndex); | |
return true; | |
} | |
static void variantPropertyFromSheet(int mask, int flag, const QDesignerPropertySheetExtension *sheet, const QString &name, | |
QVariant *value, bool *changed, int *returnMask) | |
{ | |
if (mask & flag) { | |
const int sIndex = sheet->indexOf(name); | |
if (sIndex != -1) { | |
*value = sheet->property(sIndex); | |
*changed = sheet->isChanged(sIndex); | |
*returnMask |= flag; | |
} | |
} | |
} | |
int LayoutProperties::fromPropertySheet(const QDesignerFormEditorInterface *core, QLayout *l, int mask) | |
{ | |
int rc = 0; | |
const QDesignerPropertySheetExtension *sheet = qt_extension<QDesignerPropertySheetExtension*>(core->extensionManager(), l); | |
Q_ASSERT(sheet); | |
// name | |
if (mask & ObjectNameProperty) { | |
const int nameIndex = sheet->indexOf(QLatin1String(objectNameC)); | |
Q_ASSERT(nameIndex != -1); | |
m_objectName = sheet->property(nameIndex); | |
m_objectNameChanged = sheet->isChanged(nameIndex); | |
rc |= ObjectNameProperty; | |
} | |
// -- Margins | |
const int marginFlags[MarginCount] = { LeftMarginProperty, TopMarginProperty, RightMarginProperty, BottomMarginProperty}; | |
for (int i = 0; i < MarginCount; i++) | |
if (mask & marginFlags[i]) | |
if (intValueFromSheet(sheet, QLatin1String(marginPropertyNamesC[i]), m_margins + i, m_marginsChanged + i)) | |
rc |= marginFlags[i]; | |
const int spacingFlags[] = { SpacingProperty, HorizSpacingProperty, VertSpacingProperty}; | |
for (int i = 0; i < SpacingsCount; i++) | |
if (mask & spacingFlags[i]) | |
if (intValueFromSheet(sheet, QLatin1String(spacingPropertyNamesC[i]), m_spacings + i, m_spacingsChanged + i)) | |
rc |= spacingFlags[i]; | |
// sizeConstraint, flags | |
variantPropertyFromSheet(mask, SizeConstraintProperty, sheet, QLatin1String(sizeConstraintC), &m_sizeConstraint, &m_sizeConstraintChanged, &rc); | |
variantPropertyFromSheet(mask, FieldGrowthPolicyProperty, sheet, QLatin1String(fieldGrowthPolicyPropertyC), &m_fieldGrowthPolicy, &m_fieldGrowthPolicyChanged, &rc); | |
variantPropertyFromSheet(mask, RowWrapPolicyProperty, sheet, QLatin1String(rowWrapPolicyPropertyC), &m_rowWrapPolicy, &m_rowWrapPolicyChanged, &rc); | |
variantPropertyFromSheet(mask, LabelAlignmentProperty, sheet, QLatin1String(labelAlignmentPropertyC), &m_labelAlignment, &m_labelAlignmentChanged, &rc); | |
variantPropertyFromSheet(mask, FormAlignmentProperty, sheet, QLatin1String(formAlignmentPropertyC), &m_formAlignment, &m_formAlignmentChanged, &rc); | |
variantPropertyFromSheet(mask, BoxStretchProperty, sheet, QLatin1String(boxStretchPropertyC), &m_boxStretch, & m_boxStretchChanged, &rc); | |
variantPropertyFromSheet(mask, GridRowStretchProperty, sheet, QLatin1String(gridRowStretchPropertyC), &m_gridRowStretch, &m_gridRowStretchChanged, &rc); | |
variantPropertyFromSheet(mask, GridColumnStretchProperty, sheet, QLatin1String(gridColumnStretchPropertyC), &m_gridColumnStretch, &m_gridColumnStretchChanged, &rc); | |
variantPropertyFromSheet(mask, GridRowMinimumHeightProperty, sheet, QLatin1String(gridRowMinimumHeightPropertyC), &m_gridRowMinimumHeight, &m_gridRowMinimumHeightChanged, &rc); | |
variantPropertyFromSheet(mask, GridColumnMinimumWidthProperty, sheet, QLatin1String(gridColumnMinimumWidthPropertyC), &m_gridColumnMinimumWidth, &m_gridColumnMinimumWidthChanged, &rc); | |
return rc; | |
} | |
static bool intValueToSheet(QDesignerPropertySheetExtension *sheet, const QString &name, int value, bool changed, bool applyChanged) | |
{ | |
const int sheetIndex = sheet->indexOf(name); | |
if (sheetIndex == -1) { | |
qWarning() << " LayoutProperties: Attempt to set property " << name << " that does not exist for the layout."; | |
return false; | |
} | |
sheet->setProperty(sheetIndex, QVariant(value)); | |
if (applyChanged) | |
sheet->setChanged(sheetIndex, changed); | |
return true; | |
} | |
static void variantPropertyToSheet(int mask, int flag, bool applyChanged, QDesignerPropertySheetExtension *sheet, const QString &name, | |
const QVariant &value, bool changed, int *returnMask) | |
{ | |
if (mask & flag) { | |
const int sIndex = sheet->indexOf(name); | |
if (sIndex != -1) { | |
sheet->setProperty(sIndex, value); | |
if (applyChanged) | |
sheet->setChanged(sIndex, changed); | |
*returnMask |= flag; | |
} | |
} | |
} | |
int LayoutProperties::toPropertySheet(const QDesignerFormEditorInterface *core, QLayout *l, int mask, bool applyChanged) const | |
{ | |
int rc = 0; | |
QDesignerPropertySheetExtension *sheet = qt_extension<QDesignerPropertySheetExtension*>(core->extensionManager(), l); | |
Q_ASSERT(sheet); | |
// name | |
if (mask & ObjectNameProperty) { | |
const int nameIndex = sheet->indexOf(QLatin1String(objectNameC)); | |
Q_ASSERT(nameIndex != -1); | |
sheet->setProperty(nameIndex, m_objectName); | |
if (applyChanged) | |
sheet->setChanged(nameIndex, m_objectNameChanged); | |
rc |= ObjectNameProperty; | |
} | |
// margins | |
const int marginFlags[MarginCount] = { LeftMarginProperty, TopMarginProperty, RightMarginProperty, BottomMarginProperty}; | |
for (int i = 0; i < MarginCount; i++) | |
if (mask & marginFlags[i]) | |
if (intValueToSheet(sheet, QLatin1String(marginPropertyNamesC[i]), m_margins[i], m_marginsChanged[i], applyChanged)) | |
rc |= marginFlags[i]; | |
const int spacingFlags[] = { SpacingProperty, HorizSpacingProperty, VertSpacingProperty}; | |
for (int i = 0; i < SpacingsCount; i++) | |
if (mask & spacingFlags[i]) | |
if (intValueToSheet(sheet, QLatin1String(spacingPropertyNamesC[i]), m_spacings[i], m_spacingsChanged[i], applyChanged)) | |
rc |= spacingFlags[i]; | |
// sizeConstraint | |
variantPropertyToSheet(mask, SizeConstraintProperty, applyChanged, sheet, QLatin1String(sizeConstraintC), m_sizeConstraint, m_sizeConstraintChanged, &rc); | |
variantPropertyToSheet(mask, FieldGrowthPolicyProperty, applyChanged, sheet, QLatin1String(fieldGrowthPolicyPropertyC), m_fieldGrowthPolicy, &m_fieldGrowthPolicyChanged, &rc); | |
variantPropertyToSheet(mask, RowWrapPolicyProperty, applyChanged, sheet, QLatin1String(rowWrapPolicyPropertyC), m_rowWrapPolicy, m_rowWrapPolicyChanged, &rc); | |
variantPropertyToSheet(mask, LabelAlignmentProperty, applyChanged, sheet, QLatin1String(labelAlignmentPropertyC), m_labelAlignment, m_labelAlignmentChanged, &rc); | |
variantPropertyToSheet(mask, FormAlignmentProperty, applyChanged, sheet, QLatin1String(formAlignmentPropertyC), m_formAlignment, m_formAlignmentChanged, &rc); | |
variantPropertyToSheet(mask, BoxStretchProperty, applyChanged, sheet, QLatin1String(boxStretchPropertyC), m_boxStretch, m_boxStretchChanged, &rc); | |
variantPropertyToSheet(mask, GridRowStretchProperty, applyChanged, sheet, QLatin1String(gridRowStretchPropertyC), m_gridRowStretch, m_gridRowStretchChanged, &rc); | |
variantPropertyToSheet(mask, GridColumnStretchProperty, applyChanged, sheet, QLatin1String(gridColumnStretchPropertyC), m_gridColumnStretch, m_gridColumnStretchChanged, &rc); | |
variantPropertyToSheet(mask, GridRowMinimumHeightProperty, applyChanged, sheet, QLatin1String(gridRowMinimumHeightPropertyC), m_gridRowMinimumHeight, m_gridRowMinimumHeightChanged, &rc); | |
variantPropertyToSheet(mask, GridColumnMinimumWidthProperty, applyChanged, sheet, QLatin1String(gridColumnMinimumWidthPropertyC), m_gridColumnMinimumWidth, m_gridColumnMinimumWidthChanged, &rc); | |
return rc; | |
} | |
// ---------------- LayoutHelper | |
LayoutHelper::LayoutHelper() | |
{ | |
} | |
LayoutHelper::~LayoutHelper() | |
{ | |
} | |
int LayoutHelper::indexOf(const QLayout *lt, const QWidget *widget) | |
{ | |
if (!lt) | |
return -1; | |
const int itemCount = lt->count(); | |
for (int i = 0; i < itemCount; i++) | |
if (lt->itemAt(i)->widget() == widget) | |
return i; | |
return -1; | |
} | |
QRect LayoutHelper::itemInfo(QLayout *lt, const QWidget *widget) const | |
{ | |
const int index = indexOf(lt, widget); | |
if (index == -1) { | |
qWarning() << "LayoutHelper::itemInfo: " << widget << " not in layout " << lt; | |
return QRect(0, 0, 1, 1); | |
} | |
return itemInfo(lt, index); | |
} | |
// ---------------- BoxLayoutHelper | |
class BoxLayoutHelper : public LayoutHelper { | |
public: | |
BoxLayoutHelper(const Qt::Orientation orientation) : m_orientation(orientation) {} | |
virtual QRect itemInfo(QLayout *lt, int index) const; | |
virtual void insertWidget(QLayout *lt, const QRect &info, QWidget *w); | |
virtual void removeWidget(QLayout *lt, QWidget *widget); | |
virtual void replaceWidget(QLayout *lt, QWidget *before, QWidget *after); | |
virtual void pushState(const QDesignerFormEditorInterface *, const QWidget *); | |
virtual void popState(const QDesignerFormEditorInterface *, QWidget *); | |
virtual bool canSimplify(const QDesignerFormEditorInterface *, const QWidget *, const QRect &) const { return false; } | |
virtual void simplify(const QDesignerFormEditorInterface *, QWidget *, const QRect &) {} | |
// Helper for restoring layout states | |
typedef QVector <QLayoutItem *> LayoutItemVector; | |
static LayoutItemVector disassembleLayout(QLayout *lt); | |
static QLayoutItem *findItemOfWidget(const LayoutItemVector &lv, QWidget *w); | |
private: | |
typedef QVector<QWidget *> BoxLayoutState; | |
static BoxLayoutState state(const QBoxLayout*lt); | |
QStack<BoxLayoutState> m_states; | |
const Qt::Orientation m_orientation; | |
}; | |
QRect BoxLayoutHelper::itemInfo(QLayout * /*lt*/, int index) const | |
{ | |
return m_orientation == Qt::Horizontal ? QRect(index, 0, 1, 1) : QRect(0, index, 1, 1); | |
} | |
void BoxLayoutHelper::insertWidget(QLayout *lt, const QRect &info, QWidget *w) | |
{ | |
QDesignerWidgetItemInstaller wii; // Make sure we use QDesignerWidgetItem. | |
QBoxLayout *boxLayout = qobject_cast<QBoxLayout *>(lt); | |
Q_ASSERT(boxLayout); | |
boxLayout->insertWidget(m_orientation == Qt::Horizontal ? info.x() : info.y(), w); | |
} | |
void BoxLayoutHelper::removeWidget(QLayout *lt, QWidget *widget) | |
{ | |
QBoxLayout *boxLayout = qobject_cast<QBoxLayout *>(lt); | |
Q_ASSERT(boxLayout); | |
boxLayout->removeWidget(widget); | |
} | |
void BoxLayoutHelper::replaceWidget(QLayout *lt, QWidget *before, QWidget *after) | |
{ | |
bool ok = false; | |
QDesignerWidgetItemInstaller wii; // Make sure we use QDesignerWidgetItem. | |
if (QBoxLayout *boxLayout = qobject_cast<QBoxLayout *>(lt)) { | |
const int index = boxLayout->indexOf(before); | |
if (index != -1) { | |
const bool visible = before->isVisible(); | |
delete boxLayout->takeAt(index); | |
if (visible) | |
before->hide(); | |
before->setParent(0); | |
boxLayout->insertWidget(index, after); | |
ok = true; | |
} | |
} | |
if (!ok) | |
qWarning() << "BoxLayoutHelper::replaceWidget : Unable to replace " << before << " by " << after << " in " << lt; | |
} | |
BoxLayoutHelper::BoxLayoutState BoxLayoutHelper::state(const QBoxLayout*lt) | |
{ | |
BoxLayoutState rc; | |
if (const int count = lt->count()) { | |
rc.reserve(count); | |
for (int i = 0; i < count; i++) | |
if (QWidget *w = lt->itemAt(i)->widget()) | |
rc.push_back(w); | |
} | |
return rc; | |
} | |
void BoxLayoutHelper::pushState(const QDesignerFormEditorInterface *core, const QWidget *w) | |
{ | |
const QBoxLayout *boxLayout = qobject_cast<const QBoxLayout *>(LayoutInfo::managedLayout(core, w)); | |
Q_ASSERT(boxLayout); | |
m_states.push(state(boxLayout)); | |
} | |
QLayoutItem *BoxLayoutHelper::findItemOfWidget(const LayoutItemVector &lv, QWidget *w) | |
{ | |
const LayoutItemVector::const_iterator cend = lv.constEnd(); | |
for (LayoutItemVector::const_iterator it = lv.constBegin(); it != cend; ++it) | |
if ( (*it)->widget() == w) | |
return *it; | |
return 0; | |
} | |
BoxLayoutHelper::LayoutItemVector BoxLayoutHelper::disassembleLayout(QLayout *lt) | |
{ | |
// Take items | |
const int count = lt->count(); | |
if (count == 0) | |
return LayoutItemVector(); | |
LayoutItemVector rc; | |
rc.reserve(count); | |
for (int i = count - 1; i >= 0; i--) | |
rc.push_back(lt->takeAt(i)); | |
return rc; | |
} | |
void BoxLayoutHelper::popState(const QDesignerFormEditorInterface *core, QWidget *w) | |
{ | |
QBoxLayout *boxLayout = qobject_cast<QBoxLayout *>(LayoutInfo::managedLayout(core, w)); | |
Q_ASSERT(boxLayout); | |
const BoxLayoutState savedState = m_states.pop(); | |
const BoxLayoutState currentState = state(boxLayout); | |
// Check for equality/empty. Note that this will currently | |
// always trigger as box layouts do not have a state apart from | |
// the order and there is no layout order editor yet. | |
if (savedState == state(boxLayout)) | |
return; | |
const int count = savedState.size(); | |
Q_ASSERT(count == currentState.size()); | |
// Take items and reassemble in saved order | |
const LayoutItemVector items = disassembleLayout(boxLayout); | |
for (int i = 0; i < count; i++) { | |
QLayoutItem *item = findItemOfWidget(items, savedState[i]); | |
Q_ASSERT(item); | |
boxLayout->addItem(item); | |
} | |
} | |
// Grid Layout state. Datatypically store the state of a GridLayout as a map of | |
// widgets to QRect(columns, rows) and size. Used to store the state for undo operations | |
// that do not change the widgets within the layout; also provides some manipulation | |
// functions and ability to apply the state to a layout provided its widgets haven't changed. | |
struct GridLayoutState { | |
GridLayoutState(); | |
void fromLayout(QGridLayout *l); | |
void applyToLayout(const QDesignerFormEditorInterface *core, QWidget *w) const; | |
void insertRow(int row); | |
void insertColumn(int column); | |
bool simplify(const QRect &r, bool testOnly); | |
void removeFreeRow(int row); | |
void removeFreeColumn(int column); | |
// State of a cell in one dimension | |
enum DimensionCellState { | |
Free, | |
Spanned, // Item spans it | |
Occupied // Item bordering on it | |
}; | |
// Horiontal, Vertical pair of state | |
typedef QPair<DimensionCellState, DimensionCellState> CellState; | |
typedef QVector<CellState> CellStates; | |
// Figure out states of a cell and return as a flat vector of | |
// [column1, column2,...] (address as row * columnCount + col) | |
static CellStates cellStates(const QList<QRect> &rects, int numRows, int numColumns); | |
typedef QMap<QWidget *, QRect> WidgetItemMap; | |
WidgetItemMap widgetItemMap; | |
int rowCount; | |
int colCount; | |
}; | |
static inline bool needsSpacerItem(const GridLayoutState::CellState &cs) { | |
return cs.first == GridLayoutState::Free && cs.second == GridLayoutState::Free; | |
} | |
static inline QDebug operator<<(QDebug str, const GridLayoutState &gs) | |
{ | |
str << "GridLayoutState: " << gs.rowCount << " rows x " << gs.colCount | |
<< " cols " << gs.widgetItemMap.size() << " items\n"; | |
const GridLayoutState::WidgetItemMap::const_iterator wcend = gs.widgetItemMap.constEnd(); | |
for (GridLayoutState::WidgetItemMap::const_iterator it = gs.widgetItemMap.constBegin(); it != wcend; ++it) | |
str << "Item " << it.key() << it.value() << '\n'; | |
return str; | |
} | |
GridLayoutState::GridLayoutState() : | |
rowCount(0), | |
colCount(0) | |
{ | |
} | |
GridLayoutState::CellStates GridLayoutState::cellStates(const QList<QRect> &rects, int numRows, int numColumns) | |
{ | |
CellStates rc = CellStates(numRows * numColumns, CellState(Free, Free)); | |
const QList<QRect>::const_iterator rcend = rects.constEnd(); | |
for (QList<QRect>::const_iterator it = rects.constBegin(); it != rcend; ++it) { | |
const int leftColumn = it->x(); | |
const int topRow = it->y(); | |
const int rightColumn = leftColumn + it->width() - 1; | |
const int bottomRow = topRow + it->height() - 1; | |
for (int r = topRow; r <= bottomRow; r++) | |
for (int c = leftColumn; c <= rightColumn; c++) { | |
const int flatIndex = r * numColumns + c; | |
// Bordering horizontally? | |
DimensionCellState &horizState = rc[flatIndex].first; | |
if (c == leftColumn || c == rightColumn) { | |
horizState = Occupied; | |
} else { | |
if (horizState < Spanned) | |
horizState = Spanned; | |
} | |
// Bordering vertically? | |
DimensionCellState &vertState = rc[flatIndex].second; | |
if (r == topRow || r == bottomRow) { | |
vertState = Occupied; | |
} else { | |
if (vertState < Spanned) | |
vertState = Spanned; | |
} | |
} | |
} | |
if (debugLayout) { | |
qDebug() << "GridLayoutState::cellStates: " << numRows << " x " << numColumns; | |
for (int r = 0; r < numRows; r++) | |
for (int c = 0; c < numColumns; c++) | |
qDebug() << " Row: " << r << " column: " << c << rc[r * numColumns + c]; | |
} | |
return rc; | |
} | |
void GridLayoutState::fromLayout(QGridLayout *l) | |
{ | |
rowCount = l->rowCount(); | |
colCount = l->columnCount(); | |
const int count = l->count(); | |
for (int i = 0; i < count; i++) { | |
QLayoutItem *item = l->itemAt(i); | |
if (!LayoutInfo::isEmptyItem(item)) | |
widgetItemMap.insert(item->widget(), gridItemInfo(l, i)); | |
} | |
} | |
void GridLayoutState::applyToLayout(const QDesignerFormEditorInterface *core, QWidget *w) const | |
{ | |
typedef QMap<QLayoutItem *, QRect> LayoutItemRectMap; | |
QGridLayout *grid = qobject_cast<QGridLayout *>(LayoutInfo::managedLayout(core, w)); | |
Q_ASSERT(grid); | |
if (debugLayout) | |
qDebug() << ">GridLayoutState::applyToLayout" << *this << *grid; | |
const bool shrink = grid->rowCount() > rowCount || grid->columnCount() > colCount; | |
// Build a map of existing items to rectangles via widget map, delete spacers | |
LayoutItemRectMap itemMap; | |
while (grid->count()) { | |
QLayoutItem *item = grid->takeAt(0); | |
if (!LayoutInfo::isEmptyItem(item)) { | |
QWidget *itemWidget = item->widget(); | |
const WidgetItemMap::const_iterator it = widgetItemMap.constFind(itemWidget); | |
if (it == widgetItemMap.constEnd()) | |
qFatal("GridLayoutState::applyToLayout: Attempt to apply to a layout that has a widget '%s'/'%s' added after saving the state.", | |
itemWidget->metaObject()->className(), itemWidget->objectName().toUtf8().constData()); | |
itemMap.insert(item, it.value()); | |
} else { | |
delete item; | |
} | |
} | |
Q_ASSERT(itemMap.size() == widgetItemMap.size()); | |
// recreate if shrink | |
if (shrink) | |
grid = static_cast<QGridLayout*>(recreateManagedLayout(core, w, grid)); | |
// Add widgets items | |
const LayoutItemRectMap::const_iterator icend = itemMap.constEnd(); | |
for (LayoutItemRectMap::const_iterator it = itemMap.constBegin(); it != icend; ++it) { | |
const QRect info = it.value(); | |
grid->addItem(it.key(), info.y(), info.x(), info.height(), info.width()); | |
} | |
// create spacers | |
const CellStates cs = cellStates(itemMap.values(), rowCount, colCount); | |
for (int r = 0; r < rowCount; r++) | |
for (int c = 0; c < colCount; c++) | |
if (needsSpacerItem(cs[r * colCount + c])) | |
grid->addItem(createGridSpacer(), r, c); | |
grid->activate(); | |
if (debugLayout) | |
qDebug() << "<GridLayoutState::applyToLayout" << *grid; | |
} | |
void GridLayoutState::insertRow(int row) | |
{ | |
rowCount++; | |
const WidgetItemMap::iterator iend = widgetItemMap.end(); | |
for (WidgetItemMap::iterator it = widgetItemMap.begin(); it != iend; ++it) { | |
const int topRow = it.value().y(); | |
if (topRow >= row) { | |
it.value().translate(0, 1); | |
} else { //Over it: Does it span it -> widen? | |
const int rowSpan = it.value().height(); | |
if (rowSpan > 1 && topRow + rowSpan > row) | |
it.value().setHeight(rowSpan + 1); | |
} | |
} | |
} | |
void GridLayoutState::insertColumn(int column) | |
{ | |
colCount++; | |
const WidgetItemMap::iterator iend = widgetItemMap.end(); | |
for (WidgetItemMap::iterator it = widgetItemMap.begin(); it != iend; ++it) { | |
const int leftColumn = it.value().x(); | |
if (leftColumn >= column) { | |
it.value().translate(1, 0); | |
} else { // Left of it: Does it span it -> widen? | |
const int colSpan = it.value().width(); | |
if (colSpan > 1 && leftColumn + colSpan > column) | |
it.value().setWidth(colSpan + 1); | |
} | |
} | |
} | |
// Simplify: Remove empty columns/rows and such ones that are only spanned (shrink | |
// spanning items). | |
// 'AB.C.' 'ABC' | |
// 'DDDD.' ==> 'DDD' | |
// 'EF.G.' 'EFG' | |
bool GridLayoutState::simplify(const QRect &r, bool testOnly) | |
{ | |
// figure out free rows/columns. | |
QVector<bool> occupiedRows(rowCount, false); | |
QVector<bool> occupiedColumns(colCount, false); | |
// Mark everything outside restriction rectangle as occupied | |
const int restrictionLeftColumn = r.x(); | |
const int restrictionRightColumn = restrictionLeftColumn + r.width(); | |
const int restrictionTopRow = r.y(); | |
const int restrictionBottomRow = restrictionTopRow + r.height(); | |
if (restrictionLeftColumn > 0 || restrictionRightColumn < colCount || | |
restrictionTopRow > 0 || restrictionBottomRow < rowCount) { | |
for (int r = 0; r < rowCount; r++) | |
if (r < restrictionTopRow || r >= restrictionBottomRow) | |
occupiedRows[r] = true; | |
for (int c = 0; c < colCount; c++) | |
if (c < restrictionLeftColumn || c >= restrictionRightColumn) | |
occupiedColumns[c] = true; | |
} | |
// figure out free fields and tick off occupied rows and columns | |
const CellStates cs = cellStates(widgetItemMap.values(), rowCount, colCount); | |
for (int r = 0; r < rowCount; r++) | |
for (int c = 0; c < colCount; c++) { | |
const CellState &state = cs[r * colCount + c]; | |
if (state.first == Occupied) | |
occupiedColumns[c] = true; | |
if (state.second == Occupied) | |
occupiedRows[r] = true; | |
} | |
// Any free rows/columns? | |
if (occupiedRows.indexOf(false) == -1 && occupiedColumns.indexOf(false) == -1) | |
return false; | |
if (testOnly) | |
return true; | |
// remove rows | |
for (int r = rowCount - 1; r >= 0; r--) | |
if (!occupiedRows[r]) | |
removeFreeRow(r); | |
// remove columns | |
for (int c = colCount - 1; c >= 0; c--) | |
if (!occupiedColumns[c]) | |
removeFreeColumn(c); | |
return true; | |
} | |
void GridLayoutState::removeFreeRow(int removeRow) | |
{ | |
const WidgetItemMap::iterator iend = widgetItemMap.end(); | |
for (WidgetItemMap::iterator it = widgetItemMap.begin(); it != iend; ++it) { | |
const int r = it.value().y(); | |
Q_ASSERT(r != removeRow); // Free rows only | |
if (r < removeRow) { // Does the item span it? - shrink it | |
const int rowSpan = it.value().height(); | |
if (rowSpan > 1) { | |
const int bottomRow = r + rowSpan; | |
if (bottomRow > removeRow) | |
it.value().setHeight(rowSpan - 1); | |
} | |
} else | |
if (r > removeRow) // Item below it? - move. | |
it.value().translate(0, -1); | |
} | |
rowCount--; | |
} | |
void GridLayoutState::removeFreeColumn(int removeColumn) | |
{ | |
const WidgetItemMap::iterator iend = widgetItemMap.end(); | |
for (WidgetItemMap::iterator it = widgetItemMap.begin(); it != iend; ++it) { | |
const int c = it.value().x(); | |
Q_ASSERT(c != removeColumn); // Free columns only | |
if (c < removeColumn) { // Does the item span it? - shrink it | |
const int colSpan = it.value().width(); | |
if (colSpan > 1) { | |
const int rightColumn = c + colSpan; | |
if (rightColumn > removeColumn) | |
it.value().setWidth(colSpan - 1); | |
} | |
} else | |
if (c > removeColumn) // Item to the right of it? - move. | |
it.value().translate(-1, 0); | |
} | |
colCount--; | |
} | |
// ---------------- GridLayoutHelper | |
class GridLayoutHelper : public LayoutHelper { | |
public: | |
GridLayoutHelper() {} | |
virtual QRect itemInfo(QLayout *lt, int index) const; | |
virtual void insertWidget(QLayout *lt, const QRect &info, QWidget *w); | |
virtual void removeWidget(QLayout *lt, QWidget *widget); | |
virtual void replaceWidget(QLayout *lt, QWidget *before, QWidget *after); | |
virtual void pushState(const QDesignerFormEditorInterface *core, const QWidget *widgetWithManagedLayout); | |
virtual void popState(const QDesignerFormEditorInterface *core, QWidget *widgetWithManagedLayout); | |
virtual bool canSimplify(const QDesignerFormEditorInterface *core, const QWidget *widgetWithManagedLayout, const QRect &restrictionArea) const; | |
virtual void simplify(const QDesignerFormEditorInterface *core, QWidget *widgetWithManagedLayout, const QRect &restrictionArea); | |
static void insertRow(QGridLayout *grid, int row); | |
private: | |
QStack<GridLayoutState> m_states; | |
}; | |
void GridLayoutHelper::insertRow(QGridLayout *grid, int row) | |
{ | |
GridLayoutState state; | |
state.fromLayout(grid); | |
state.insertRow(row); | |
QDesignerFormWindowInterface *fw = QDesignerFormWindowInterface::findFormWindow(grid); | |
state.applyToLayout(fw->core(), grid->parentWidget()); | |
} | |
QRect GridLayoutHelper::itemInfo(QLayout * lt, int index) const | |
{ | |
QGridLayout *grid = qobject_cast<QGridLayout *>(lt); | |
Q_ASSERT(grid); | |
return gridItemInfo(grid, index); | |
} | |
void GridLayoutHelper::insertWidget(QLayout *lt, const QRect &info, QWidget *w) | |
{ | |
QDesignerWidgetItemInstaller wii; // Make sure we use QDesignerWidgetItem. | |
QGridLayout *gridLayout = qobject_cast<QGridLayout *>(lt); | |
Q_ASSERT(gridLayout); | |
// check if there are any items. Should be only spacers, else something is wrong | |
const int row = info.y(); | |
int column = info.x(); | |
int colSpan = info.width(); | |
int rowSpan = info.height(); | |
// If not empty: A multiselection was dropped on an empty item, insert row | |
// and spread items along new row | |
if (!removeEmptyCellsOnGrid(gridLayout, info)) { | |
int freeColumn = -1; | |
colSpan = rowSpan = 1; | |
// First look to the right for a free column | |
const int columnCount = gridLayout->columnCount(); | |
for (int c = column; c < columnCount; c++) { | |
const int idx = findGridItemAt(gridLayout, row, c); | |
if (idx != -1 && LayoutInfo::isEmptyItem(gridLayout->itemAt(idx))) { | |
freeColumn = c; | |
break; | |
} | |
} | |
if (freeColumn != -1) { | |
removeEmptyCellsOnGrid(gridLayout, QRect(freeColumn, row, 1, 1)); | |
column = freeColumn; | |
} else { | |
GridLayoutHelper::insertRow(gridLayout, row); | |
column = 0; | |
} | |
} | |
gridLayout->addWidget(w, row , column, rowSpan, colSpan); | |
} | |
void GridLayoutHelper::removeWidget(QLayout *lt, QWidget *widget) | |
{ | |
QGridLayout *gridLayout = qobject_cast<QGridLayout *>(lt); | |
Q_ASSERT(gridLayout); | |
const int index = gridLayout->indexOf(widget); | |
if (index == -1) { | |
qWarning() << "GridLayoutHelper::removeWidget : Attempt to remove " << widget << " which is not in the layout."; | |
return; | |
} | |
// delete old item and pad with by spacer items | |
int row, column, rowspan, colspan; | |
gridLayout->getItemPosition(index, &row, &column, &rowspan, &colspan); | |
delete gridLayout->takeAt(index); | |
const int rightColumn = column + colspan; | |
const int bottomRow = row + rowspan; | |
for (int c = column; c < rightColumn; c++) | |
for (int r = row; r < bottomRow; r++) | |
gridLayout->addItem(createGridSpacer(), r, c); | |
} | |
void GridLayoutHelper::replaceWidget(QLayout *lt, QWidget *before, QWidget *after) | |
{ | |
bool ok = false; | |
QDesignerWidgetItemInstaller wii; // Make sure we use QDesignerWidgetItem. | |
if (QGridLayout *gridLayout = qobject_cast<QGridLayout *>(lt)) { | |
const int index = gridLayout->indexOf(before); | |
if (index != -1) { | |
int row, column, rowSpan, columnSpan; | |
gridLayout->getItemPosition (index, &row, &column, &rowSpan, &columnSpan); | |
const bool visible = before->isVisible(); | |
delete gridLayout->takeAt(index); | |
if (visible) | |
before->hide(); | |
before->setParent(0); | |
gridLayout->addWidget(after, row, column, rowSpan, columnSpan); | |
ok = true; | |
} | |
} | |
if (!ok) | |
qWarning() << "GridLayoutHelper::replaceWidget : Unable to replace " << before << " by " << after << " in " << lt; | |
} | |
void GridLayoutHelper::pushState(const QDesignerFormEditorInterface *core, const QWidget *widgetWithManagedLayout) | |
{ | |
QGridLayout *gridLayout = qobject_cast<QGridLayout *>(LayoutInfo::managedLayout(core, widgetWithManagedLayout)); | |
Q_ASSERT(gridLayout); | |
GridLayoutState gs; | |
gs.fromLayout(gridLayout); | |
m_states.push(gs); | |
} | |
void GridLayoutHelper::popState(const QDesignerFormEditorInterface *core, QWidget *widgetWithManagedLayout) | |
{ | |
Q_ASSERT(!m_states.empty()); | |
const GridLayoutState state = m_states.pop(); | |
state.applyToLayout(core, widgetWithManagedLayout); | |
} | |
bool GridLayoutHelper::canSimplify(const QDesignerFormEditorInterface *core, const QWidget *widgetWithManagedLayout, const QRect &restrictionArea) const | |
{ | |
QGridLayout *gridLayout = qobject_cast<QGridLayout *>(LayoutInfo::managedLayout(core, widgetWithManagedLayout)); | |
Q_ASSERT(gridLayout); | |
GridLayoutState gs; | |
gs.fromLayout(gridLayout); | |
return gs.simplify(restrictionArea, true); | |
} | |
void GridLayoutHelper::simplify(const QDesignerFormEditorInterface *core, QWidget *widgetWithManagedLayout, const QRect &restrictionArea) | |
{ | |
QGridLayout *gridLayout = qobject_cast<QGridLayout *>(LayoutInfo::managedLayout(core, widgetWithManagedLayout)); | |
Q_ASSERT(gridLayout); | |
if (debugLayout) | |
qDebug() << ">GridLayoutHelper::simplify" << *gridLayout; | |
GridLayoutState gs; | |
gs.fromLayout(gridLayout); | |
if (gs.simplify(restrictionArea, false)) | |
gs.applyToLayout(core, widgetWithManagedLayout); | |
if (debugLayout) | |
qDebug() << "<GridLayoutHelper::simplify" << *gridLayout; | |
} | |
// ---------------- FormLayoutHelper | |
class FormLayoutHelper : public LayoutHelper { | |
public: | |
typedef QPair<QWidget *, QWidget *> WidgetPair; | |
typedef QVector<WidgetPair> FormLayoutState; | |
FormLayoutHelper() {} | |
virtual QRect itemInfo(QLayout *lt, int index) const; | |
virtual void insertWidget(QLayout *lt, const QRect &info, QWidget *w); | |
virtual void removeWidget(QLayout *lt, QWidget *widget); | |
virtual void replaceWidget(QLayout *lt, QWidget *before, QWidget *after); | |
virtual void pushState(const QDesignerFormEditorInterface *core, const QWidget *widgetWithManagedLayout); | |
virtual void popState(const QDesignerFormEditorInterface *core, QWidget *widgetWithManagedLayout); | |
virtual bool canSimplify(const QDesignerFormEditorInterface *core, const QWidget *, const QRect &) const; | |
virtual void simplify(const QDesignerFormEditorInterface *, QWidget *, const QRect &); | |
private: | |
static FormLayoutState state(const QFormLayout *lt); | |
QStack<FormLayoutState> m_states; | |
}; | |
QRect FormLayoutHelper::itemInfo(QLayout * lt, int index) const | |
{ | |
QFormLayout *form = qobject_cast<QFormLayout *>(lt); | |
Q_ASSERT(form); | |
int row, column, colspan; | |
getFormLayoutItemPosition(form, index, &row, &column, 0, &colspan); | |
return QRect(column, row, colspan, 1); | |
} | |
void FormLayoutHelper::insertWidget(QLayout *lt, const QRect &info, QWidget *w) | |
{ | |
if (debugLayout) | |
qDebug() << "FormLayoutHelper::insertWidget:" << w << info; | |
QDesignerWidgetItemInstaller wii; // Make sure we use QDesignerWidgetItem. | |
QFormLayout *formLayout = qobject_cast<QFormLayout *>(lt); | |
Q_ASSERT(formLayout); | |
// check if there are any nonspacer items? (Drop on 3rd column or drop of a multiselection | |
// on an empty item. As the Form layout does not have insert semantics; we need to manually insert a row | |
const bool insert = !removeEmptyCellsOnGrid(formLayout, info); | |
formLayoutAddWidget(formLayout, w, info, insert); | |
QLayoutSupport::createEmptyCells(formLayout); | |
} | |
void FormLayoutHelper::removeWidget(QLayout *lt, QWidget *widget) | |
{ | |
QFormLayout *formLayout = qobject_cast<QFormLayout *>(lt); | |
Q_ASSERT(formLayout); | |
const int index = formLayout->indexOf(widget); | |
if (index == -1) { | |
qWarning() << "FormLayoutHelper::removeWidget : Attempt to remove " << widget << " which is not in the layout."; | |
return; | |
} | |
// delete old item and pad with by spacer items | |
int row, column, colspan; | |
getFormLayoutItemPosition(formLayout, index, &row, &column, 0, &colspan); | |
if (debugLayout) | |
qDebug() << "FormLayoutHelper::removeWidget: #" << index << widget << " at " << row << column << colspan; | |
delete formLayout->takeAt(index); | |
if (colspan > 1 || column == 0) | |
formLayout->setItem(row, QFormLayout::LabelRole, createFormSpacer()); | |
if (colspan > 1 || column == 1) | |
formLayout->setItem(row, QFormLayout::FieldRole, createFormSpacer()); | |
} | |
void FormLayoutHelper::replaceWidget(QLayout *lt, QWidget *before, QWidget *after) | |
{ | |
bool ok = false; | |
QDesignerWidgetItemInstaller wii; // Make sure we use QDesignerWidgetItem. | |
if (QFormLayout *formLayout = qobject_cast<QFormLayout *>(lt)) { | |
const int index = formLayout->indexOf(before); | |
if (index != -1) { | |
int row; | |
QFormLayout::ItemRole role; | |
formLayout->getItemPosition (index, &row, &role); | |
const bool visible = before->isVisible(); | |
delete formLayout->takeAt(index); | |
if (visible) | |
before->hide(); | |
before->setParent(0); | |
formLayout->setWidget(row, role, after); | |
ok = true; | |
} | |
} | |
if (!ok) | |
qWarning() << "FormLayoutHelper::replaceWidget : Unable to replace " << before << " by " << after << " in " << lt; | |
} | |
FormLayoutHelper::FormLayoutState FormLayoutHelper::state(const QFormLayout *lt) | |
{ | |
const int rowCount = lt->rowCount(); | |
if (rowCount == 0) | |
return FormLayoutState(); | |
FormLayoutState rc(rowCount, WidgetPair(0, 0)); | |
const int count = lt->count(); | |
int row, column, colspan; | |
for (int i = 0; i < count; i++) { | |
QLayoutItem *item = lt->itemAt(i); | |
if (!LayoutInfo::isEmptyItem(item)) { | |
QWidget *w = item->widget(); | |
Q_ASSERT(w); | |
getFormLayoutItemPosition(lt, i, &row, &column, 0, &colspan); | |
if (colspan > 1 || column == 0) | |
rc[row].first = w; | |
if (colspan > 1 || column == 1) | |
rc[row].second = w; | |
} | |
} | |
if (debugLayout) { | |
qDebug() << "FormLayoutHelper::state: " << rowCount; | |
for (int r = 0; r < rowCount; r++) | |
qDebug() << " Row: " << r << rc[r].first << rc[r].second; | |
} | |
return rc; | |
} | |
void FormLayoutHelper::pushState(const QDesignerFormEditorInterface *core, const QWidget *widgetWithManagedLayout) | |
{ | |
QFormLayout *formLayout = qobject_cast<QFormLayout *>(LayoutInfo::managedLayout(core, widgetWithManagedLayout)); | |
Q_ASSERT(formLayout); | |
m_states.push(state(formLayout)); | |
} | |
void FormLayoutHelper::popState(const QDesignerFormEditorInterface *core, QWidget *widgetWithManagedLayout) | |
{ | |
QFormLayout *formLayout = qobject_cast<QFormLayout *>(LayoutInfo::managedLayout(core, widgetWithManagedLayout)); | |
Q_ASSERT(!m_states.empty() && formLayout); | |
const FormLayoutState storedState = m_states.pop(); | |
const FormLayoutState currentState = state(formLayout); | |
if (currentState == storedState) | |
return; | |
const int rowCount = storedState.size(); | |
// clear out, shrink if required, but maintain items via map, pad spacers | |
const BoxLayoutHelper::LayoutItemVector items = BoxLayoutHelper::disassembleLayout(formLayout); | |
if (rowCount < formLayout->rowCount()) | |
formLayout = static_cast<QFormLayout*>(recreateManagedLayout(core, widgetWithManagedLayout, formLayout )); | |
for (int r = 0; r < rowCount; r++) { | |
QWidget *widgets[FormLayoutColumns] = { storedState[r].first, storedState[r].second }; | |
const bool spanning = widgets[0] != 0 && widgets[0] == widgets[1]; | |
if (spanning) { | |
formLayout->setWidget(r, QFormLayout::SpanningRole, widgets[0]); | |
} else { | |
for (int c = 0; c < FormLayoutColumns; c++) { | |
const QFormLayout::ItemRole role = c == 0 ? QFormLayout::LabelRole : QFormLayout::FieldRole; | |
if (widgets[c] && BoxLayoutHelper::findItemOfWidget(items, widgets[c])) { | |
formLayout->setWidget(r, role, widgets[c]); | |
} else { | |
formLayout->setItem(r, role, createFormSpacer()); | |
} | |
} | |
} | |
} | |
} | |
bool FormLayoutHelper::canSimplify(const QDesignerFormEditorInterface *core, const QWidget *widgetWithManagedLayout, const QRect &restrictionArea) const | |
{ | |
const QFormLayout *formLayout = qobject_cast<QFormLayout *>(LayoutInfo::managedLayout(core, widgetWithManagedLayout)); | |
Q_ASSERT(formLayout); | |
return canSimplifyFormLayout(formLayout, restrictionArea); | |
} | |
void FormLayoutHelper::simplify(const QDesignerFormEditorInterface *core, QWidget *widgetWithManagedLayout, const QRect &restrictionArea) | |
{ | |
typedef QPair<QLayoutItem*, QLayoutItem*> LayoutItemPair; | |
typedef QVector<LayoutItemPair> LayoutItemPairs; | |
QFormLayout *formLayout = qobject_cast<QFormLayout *>(LayoutInfo::managedLayout(core, widgetWithManagedLayout)); | |
Q_ASSERT(formLayout); | |
if (debugLayout) | |
qDebug() << "FormLayoutHelper::simplify"; | |
// Transform into vector of item pairs | |
const int rowCount = formLayout->rowCount(); | |
LayoutItemPairs pairs(rowCount, LayoutItemPair(0, 0)); | |
for (int i = formLayout->count() - 1; i >= 0; i--) { | |
int row, col,colspan; | |
getFormLayoutItemPosition(formLayout, i, &row, &col, 0, &colspan); | |
if (colspan > 1) { | |
pairs[row].first = pairs[row].second = formLayout->takeAt(i); | |
} else { | |
if (col == 0) | |
pairs[row].first = formLayout->takeAt(i); | |
else | |
pairs[row].second = formLayout->takeAt(i); | |
} | |
} | |
// Weed out empty ones | |
const int bottomCheckRow = qMin(rowCount, restrictionArea.y() + restrictionArea.height()); | |
for (int r = bottomCheckRow - 1; r >= restrictionArea.y(); r--) | |
if (LayoutInfo::isEmptyItem(pairs[r].first) && LayoutInfo::isEmptyItem(pairs[r].second)) { | |
delete pairs[r].first; | |
delete pairs[r].second; | |
pairs.remove(r); | |
} | |
const int simpleRowCount = pairs.size(); | |
if (simpleRowCount < rowCount) | |
formLayout = static_cast<QFormLayout *>(recreateManagedLayout(core, widgetWithManagedLayout, formLayout)); | |
// repopulate | |
for (int r = 0; r < simpleRowCount; r++) { | |
const bool spanning = pairs[r].first == pairs[r].second; | |
if (spanning) { | |
formLayout->setItem(r, QFormLayout::SpanningRole, pairs[r].first); | |
} else { | |
formLayout->setItem(r, QFormLayout::LabelRole, pairs[r].first); | |
formLayout->setItem(r, QFormLayout::FieldRole, pairs[r].second); | |
} | |
} | |
} | |
LayoutHelper *LayoutHelper::createLayoutHelper(int type) | |
{ | |
LayoutHelper *rc = 0; | |
switch (type) { | |
case LayoutInfo::HBox: | |
rc = new BoxLayoutHelper(Qt::Horizontal); | |
break; | |
case LayoutInfo::VBox: | |
rc = new BoxLayoutHelper(Qt::Vertical); | |
break; | |
case LayoutInfo::Grid: | |
rc = new GridLayoutHelper; | |
break; | |
case LayoutInfo::Form: | |
return new FormLayoutHelper; | |
default: | |
break; | |
} | |
Q_ASSERT(rc); | |
return rc; | |
} | |
// ---- QLayoutSupport (LayoutDecorationExtension) | |
QLayoutSupport::QLayoutSupport(QDesignerFormWindowInterface *formWindow, QWidget *widget, LayoutHelper *helper, QObject *parent) : | |
QObject(parent), | |
m_formWindow(formWindow), | |
m_helper(helper), | |
m_widget(widget), | |
m_currentIndex(-1), | |
m_currentInsertMode(QDesignerLayoutDecorationExtension::InsertWidgetMode) | |
{ | |
} | |
QLayout * QLayoutSupport::layout() const | |
{ | |
return LayoutInfo::managedLayout(m_formWindow->core(), m_widget); | |
} | |
void QLayoutSupport::hideIndicator(Indicator i) | |
{ | |
if (m_indicators[i]) | |
m_indicators[i]->hide(); | |
} | |
void QLayoutSupport::showIndicator(Indicator i, const QRect &geometry, const QPalette &p) | |
{ | |
if (!m_indicators[i]) | |
m_indicators[i] = new qdesigner_internal::InvisibleWidget(m_widget); | |
QWidget *indicator = m_indicators[i]; | |
indicator->setAutoFillBackground(true); | |
indicator->setPalette(p); | |
indicator->setGeometry(geometry); | |
indicator->show(); | |
indicator->raise(); | |
} | |
QLayoutSupport::~QLayoutSupport() | |
{ | |
delete m_helper; | |
for (int i = 0; i < NumIndicators; i++) | |
if (m_indicators[i]) | |
m_indicators[i]->deleteLater(); | |
} | |
QGridLayout * QLayoutSupport::gridLayout() const | |
{ | |
return qobject_cast<QGridLayout*>(LayoutInfo::managedLayout(m_formWindow->core(), m_widget)); | |
} | |
QRect QLayoutSupport::itemInfo(int index) const | |
{ | |
return m_helper->itemInfo(LayoutInfo::managedLayout(m_formWindow->core(), m_widget), index); | |
} | |
void QLayoutSupport::setInsertMode(InsertMode im) | |
{ | |
m_currentInsertMode = im; | |
} | |
void QLayoutSupport::setCurrentCell(const QPair<int, int> &cell) | |
{ | |
m_currentCell = cell; | |
} | |
void QLayoutSupport::adjustIndicator(const QPoint &pos, int index) | |
{ | |
if (index == -1) { // first item goes anywhere | |
hideIndicator(LeftIndicator); | |
hideIndicator(TopIndicator); | |
hideIndicator(RightIndicator); | |
hideIndicator(BottomIndicator); | |
return; | |
} | |
m_currentIndex = index; | |
m_currentInsertMode = QDesignerLayoutDecorationExtension::InsertWidgetMode; | |
QLayoutItem *item = layout()->itemAt(index); | |
const QRect g = extendedGeometry(index); | |
// ### cleanup | |
if (LayoutInfo::isEmptyItem(item)) { | |
// Empty grid/form cell. Draw a rectangle | |
QPalette redPalette; | |
redPalette.setColor(QPalette::Window, Qt::red); | |
showIndicator(LeftIndicator, QRect(g.x(), g.y(), indicatorSize, g.height()), redPalette); | |
showIndicator(TopIndicator, QRect(g.x(), g.y(), g.width(), indicatorSize), redPalette); | |
showIndicator(RightIndicator, QRect(g.right(), g.y(), indicatorSize, g.height()), redPalette); | |
showIndicator(BottomIndicator, QRect(g.x(), g.bottom(), g.width(), indicatorSize), redPalette); | |
setCurrentCellFromIndicatorOnEmptyCell(m_currentIndex); | |
} else { | |
// Append/Insert. Draw a bar left/right or above/below | |
QPalette bluePalette; | |
bluePalette.setColor(QPalette::Window, Qt::blue); | |
hideIndicator(LeftIndicator); | |
hideIndicator(TopIndicator); | |
const int fromRight = g.right() - pos.x(); | |
const int fromBottom = g.bottom() - pos.y(); | |
const int fromLeft = pos.x() - g.x(); | |
const int fromTop = pos.y() - g.y(); | |
const int fromLeftRight = qMin(fromRight, fromLeft ); | |
const int fromBottomTop = qMin(fromBottom, fromTop); | |
const Qt::Orientation indicatorOrientation = fromLeftRight < fromBottomTop ? Qt::Vertical : Qt::Horizontal; | |
if (supportsIndicatorOrientation(indicatorOrientation)) { | |
const QRect r(layout()->geometry().topLeft(), layout()->parentWidget()->size()); | |
switch (indicatorOrientation) { | |
case Qt::Vertical: { | |
hideIndicator(BottomIndicator); | |
const bool closeToLeft = fromLeftRight == fromLeft; | |
showIndicator(RightIndicator, QRect(closeToLeft ? g.x() : g.right() + 1 - indicatorSize, 0, indicatorSize, r.height()), bluePalette); | |
const int incr = closeToLeft ? 0 : +1; | |
setCurrentCellFromIndicator(indicatorOrientation, m_currentIndex, incr); | |
} | |
break; | |
case Qt::Horizontal: { | |
hideIndicator(RightIndicator); | |
const bool closeToTop = fromBottomTop == fromTop; | |
showIndicator(BottomIndicator, QRect(r.x(), closeToTop ? g.y() : g.bottom() + 1 - indicatorSize, r.width(), indicatorSize), bluePalette); | |
const int incr = closeToTop ? 0 : +1; | |
setCurrentCellFromIndicator(indicatorOrientation, m_currentIndex, incr); | |
} | |
break; | |
} | |
} else { | |
hideIndicator(RightIndicator); | |
hideIndicator(BottomIndicator); | |
} // can handle indicatorOrientation | |
} | |
} | |
int QLayoutSupport::indexOf(QLayoutItem *i) const | |
{ | |
const QLayout *lt = layout(); | |
if (!lt) | |
return -1; | |
int index = 0; | |
while (QLayoutItem *item = lt->itemAt(index)) { | |
if (item == i) | |
return index; | |
++index; | |
} | |
return -1; | |
} | |
int QLayoutSupport::indexOf(QWidget *widget) const | |
{ | |
const QLayout *lt = layout(); | |
if (!lt) | |
return -1; | |
int index = 0; | |
while (QLayoutItem *item = lt->itemAt(index)) { | |
if (item->widget() == widget) | |
return index; | |
++index; | |
} | |
return -1; | |
} | |
QList<QWidget*> QLayoutSupport::widgets(QLayout *layout) const | |
{ | |
if (!layout) | |
return QList<QWidget*>(); | |
QList<QWidget*> lst; | |
int index = 0; | |
while (QLayoutItem *item = layout->itemAt(index)) { | |
++index; | |
QWidget *widget = item->widget(); | |
if (widget && formWindow()->isManaged(widget)) | |
lst.append(widget); | |
} | |
return lst; | |
} | |
int QLayoutSupport::findItemAt(QGridLayout *gridLayout, int at_row, int at_column) | |
{ | |
return findGridItemAt(gridLayout, at_row, at_column); | |
} | |
// Quick check whether simplify should be enabled for grids. May return false positives. | |
// Note: Calculating the occupied area does not work as spanning items may also be simplified. | |
bool QLayoutSupport::canSimplifyQuickCheck(const QGridLayout *gl) | |
{ | |
if (!gl) | |
return false; | |
const int colCount = gl->columnCount(); | |
const int rowCount = gl->rowCount(); | |
if (colCount < 2 || rowCount < 2) | |
return false; | |
// try to find a spacer. | |
const int count = gl->count(); | |
for (int index = 0; index < count; index++) | |
if (LayoutInfo::isEmptyItem(gl->itemAt(index))) | |
return true; | |
return false; | |
} | |
bool QLayoutSupport::canSimplifyQuickCheck(const QFormLayout *fl) | |
{ | |
return canSimplifyFormLayout(fl, QRect(QPoint(0, 0), QSize(32767, 32767))); | |
} | |
// remove dummy spacers | |
bool QLayoutSupport::removeEmptyCells(QGridLayout *grid, const QRect &area) | |
{ | |
return removeEmptyCellsOnGrid(grid, area); | |
} | |
void QLayoutSupport::createEmptyCells(QGridLayout *gridLayout) | |
{ | |
Q_ASSERT(gridLayout); | |
GridLayoutState gs; | |
gs.fromLayout(gridLayout); | |
const GridLayoutState::CellStates cs = GridLayoutState::cellStates(gs.widgetItemMap.values(), gs.rowCount, gs.colCount); | |
for (int c = 0; c < gs.colCount; c++) | |
for (int r = 0; r < gs.rowCount; r++) | |
if (needsSpacerItem(cs[r * gs.colCount + c])) { | |
const int existingItemIndex = findItemAt(gridLayout, r, c); | |
if (existingItemIndex == -1) | |
gridLayout->addItem(createGridSpacer(), r, c); | |
} | |
} | |
bool QLayoutSupport::removeEmptyCells(QFormLayout *formLayout, const QRect &area) | |
{ | |
return removeEmptyCellsOnGrid(formLayout, area); | |
} | |
void QLayoutSupport::createEmptyCells(QFormLayout *formLayout) | |
{ | |
// No spanning items here.. | |
if (const int rowCount = formLayout->rowCount()) | |
for (int c = 0; c < FormLayoutColumns; c++) | |
for (int r = 0; r < rowCount; r++) | |
if (findGridItemAt(formLayout, r, c) == -1) | |
formLayout->setItem(r, c == 0 ? QFormLayout::LabelRole : QFormLayout::FieldRole, createFormSpacer()); | |
} | |
int QLayoutSupport::findItemAt(const QPoint &pos) const | |
{ | |
if (!layout()) | |
return -1; | |
const QLayout *lt = layout(); | |
const int count = lt->count(); | |
if (count == 0) | |
return -1; | |
int best = -1; | |
int bestIndex = -1; | |
for (int index = 0; index < count; index++) { | |
QLayoutItem *item = lt->itemAt(index); | |
bool visible = true; | |
// When dragging widgets within layout, the source widget is invisible and must not be hit | |
if (const QWidget *w = item->widget()) | |
visible = w->isVisible(); | |
if (visible) { | |
const QRect g = item->geometry(); | |
const int dist = (g.center() - pos).manhattanLength(); | |
if (best == -1 || dist < best) { | |
best = dist; | |
bestIndex = index; | |
} | |
} | |
} | |
return bestIndex; | |
} | |
// ------------ QBoxLayoutSupport (LayoutDecorationExtension) | |
namespace { | |
class QBoxLayoutSupport: public QLayoutSupport | |
{ | |
public: | |
QBoxLayoutSupport(QDesignerFormWindowInterface *formWindow, QWidget *widget, Qt::Orientation orientation, QObject *parent = 0); | |
virtual void insertWidget(QWidget *widget, const QPair<int, int> &cell); | |
virtual void removeWidget(QWidget *widget); | |
virtual void simplify() {} | |
virtual void insertRow(int /*row*/) {} | |
virtual void insertColumn(int /*column*/) {} | |
virtual int findItemAt(int /*at_row*/, int /*at_column*/) const { return -1; } | |
private: | |
virtual void setCurrentCellFromIndicatorOnEmptyCell(int index); | |
virtual void setCurrentCellFromIndicator(Qt::Orientation indicatorOrientation, int index, int increment); | |
virtual bool supportsIndicatorOrientation(Qt::Orientation indicatorOrientation) const; | |
virtual QRect extendedGeometry(int index) const; | |
const Qt::Orientation m_orientation; | |
}; | |
void QBoxLayoutSupport::removeWidget(QWidget *widget) | |
{ | |
QLayout *lt = layout(); | |
const int index = lt->indexOf(widget); | |
// Adjust the current cell in case a widget was dragged within the same layout to a position | |
// of higher index, which happens as follows: | |
// Drag start: The widget is hidden | |
// Drop: Current cell is stored, widget is removed and re-added, causing an index offset that needs to be compensated | |
QPair<int, int> currCell = currentCell(); | |
switch (m_orientation) { | |
case Qt::Horizontal: | |
if (currCell.second > 0 && index < currCell.second ) { | |
currCell.second--; | |
setCurrentCell(currCell); | |
} | |
break; | |
case Qt::Vertical: | |
if (currCell.first > 0 && index < currCell.first) { | |
currCell.first--; | |
setCurrentCell(currCell); | |
} | |
break; | |
} | |
helper()->removeWidget(lt, widget); | |
} | |
QBoxLayoutSupport::QBoxLayoutSupport(QDesignerFormWindowInterface *formWindow, QWidget *widget, Qt::Orientation orientation, QObject *parent) : | |
QLayoutSupport(formWindow, widget, new BoxLayoutHelper(orientation), parent), | |
m_orientation(orientation) | |
{ | |
} | |
void QBoxLayoutSupport::setCurrentCellFromIndicatorOnEmptyCell(int index) | |
{ | |
qDebug() << "QBoxLayoutSupport::setCurrentCellFromIndicatorOnEmptyCell(): Warning: found a fake spacer inside a vbox layout at " << index; | |
setCurrentCell(qMakePair(0, 0)); | |
} | |
void QBoxLayoutSupport::insertWidget(QWidget *widget, const QPair<int, int> &cell) | |
{ | |
switch (m_orientation) { | |
case Qt::Horizontal: | |
helper()->insertWidget(layout(), QRect(cell.second, 0, 1, 1), widget); | |
break; | |
case Qt::Vertical: | |
helper()->insertWidget(layout(), QRect(0, cell.first, 1, 1), widget); | |
break; | |
} | |
} | |
void QBoxLayoutSupport::setCurrentCellFromIndicator(Qt::Orientation indicatorOrientation, int index, int increment) | |
{ | |
if (m_orientation == Qt::Horizontal && indicatorOrientation == Qt::Vertical) { | |
setCurrentCell(qMakePair(0, index + increment)); | |
} else if (m_orientation == Qt::Vertical && indicatorOrientation == Qt::Horizontal) { | |
setCurrentCell(qMakePair(index + increment, 0)); | |
} | |
} | |
bool QBoxLayoutSupport::supportsIndicatorOrientation(Qt::Orientation indicatorOrientation) const | |
{ | |
return m_orientation != indicatorOrientation; | |
} | |
QRect QBoxLayoutSupport::extendedGeometry(int index) const | |
{ | |
QLayoutItem *item = layout()->itemAt(index); | |
// start off with item geometry | |
QRect g = item->geometry(); | |
const QRect info = itemInfo(index); | |
// On left border: extend to widget border | |
if (info.x() == 0) { | |
QPoint topLeft = g.topLeft(); | |
topLeft.rx() = layout()->geometry().left(); | |
g.setTopLeft(topLeft); | |
} | |
// On top border: extend to widget border | |
if (info.y() == 0) { | |
QPoint topLeft = g.topLeft(); | |
topLeft.ry() = layout()->geometry().top(); | |
g.setTopLeft(topLeft); | |
} | |
// is this the last item? | |
const QBoxLayout *box = static_cast<const QBoxLayout*>(layout()); | |
if (index < box->count() -1) | |
return g; // Nope. | |
// extend to widget border | |
QPoint bottomRight = g.bottomRight(); | |
switch (m_orientation) { | |
case Qt::Vertical: | |
bottomRight.ry() = layout()->geometry().bottom(); | |
break; | |
case Qt::Horizontal: | |
bottomRight.rx() = layout()->geometry().right(); | |
break; | |
} | |
g.setBottomRight(bottomRight); | |
return g; | |
} | |
// -------------- Base class for QGridLayout-like support classes (LayoutDecorationExtension) | |
template <class GridLikeLayout> | |
class GridLikeLayoutSupportBase: public QLayoutSupport | |
{ | |
public: | |
GridLikeLayoutSupportBase(QDesignerFormWindowInterface *formWindow, QWidget *widget, LayoutHelper *helper, QObject *parent = 0) : | |
QLayoutSupport(formWindow, widget, helper, parent) {} | |
void insertWidget(QWidget *widget, const QPair<int, int> &cell); | |
virtual void removeWidget(QWidget *widget) { helper()->removeWidget(layout(), widget); } | |
virtual int findItemAt(int row, int column) const; | |
protected: | |
GridLikeLayout *gridLikeLayout() const { | |
return qobject_cast<GridLikeLayout*>(LayoutInfo::managedLayout(formWindow()->core(), widget())); | |
} | |
private: | |
virtual void setCurrentCellFromIndicatorOnEmptyCell(int index); | |
virtual void setCurrentCellFromIndicator(Qt::Orientation indicatorOrientation, int index, int increment); | |
virtual bool supportsIndicatorOrientation(Qt::Orientation) const { return true; } | |
virtual QRect extendedGeometry(int index) const; | |
// Overwrite to check the insertion position (if there are limits) | |
virtual void checkCellForInsertion(int * /*row*/, int * /*col*/) const {} | |
}; | |
template <class GridLikeLayout> | |
void GridLikeLayoutSupportBase<GridLikeLayout>::setCurrentCellFromIndicatorOnEmptyCell(int index) | |
{ | |
GridLikeLayout *grid = gridLikeLayout(); | |
Q_ASSERT(grid); | |
setInsertMode(InsertWidgetMode); | |
int row, column, rowspan, colspan; | |
getGridItemPosition(grid, index, &row, &column, &rowspan, &colspan); | |
setCurrentCell(qMakePair(row, column)); | |
} | |
template <class GridLikeLayout> | |
void GridLikeLayoutSupportBase<GridLikeLayout>::setCurrentCellFromIndicator(Qt::Orientation indicatorOrientation, int index, int increment) { | |
const QRect info = itemInfo(index); | |
switch (indicatorOrientation) { | |
case Qt::Vertical: { | |
setInsertMode(InsertColumnMode); | |
int row = info.top(); | |
int column = increment ? info.right() + 1 : info.left(); | |
checkCellForInsertion(&row, &column); | |
setCurrentCell(qMakePair(row , column)); | |
} | |
break; | |
case Qt::Horizontal: { | |
setInsertMode(InsertRowMode); | |
int row = increment ? info.bottom() + 1 : info.top(); | |
int column = info.left(); | |
checkCellForInsertion(&row, &column); | |
setCurrentCell(qMakePair(row, column)); | |
} | |
break; | |
} | |
} | |
template <class GridLikeLayout> | |
void GridLikeLayoutSupportBase<GridLikeLayout>::insertWidget(QWidget *widget, const QPair<int, int> &cell) | |
{ | |
helper()->insertWidget(layout(), QRect(cell.second, cell.first, 1, 1), widget); | |
} | |
template <class GridLikeLayout> | |
int GridLikeLayoutSupportBase<GridLikeLayout>::findItemAt(int at_row, int at_column) const | |
{ | |
GridLikeLayout *grid = gridLikeLayout(); | |
Q_ASSERT(grid); | |
return findGridItemAt(grid, at_row, at_column); | |
} | |
template <class GridLikeLayout> | |
QRect GridLikeLayoutSupportBase<GridLikeLayout>::extendedGeometry(int index) const | |
{ | |
QLayoutItem *item = layout()->itemAt(index); | |
// start off with item geometry | |
QRect g = item->geometry(); | |
const QRect info = itemInfo(index); | |
// On left border: extend to widget border | |
if (info.x() == 0) { | |
QPoint topLeft = g.topLeft(); | |
topLeft.rx() = layout()->geometry().left(); | |
g.setTopLeft(topLeft); | |
} | |
// On top border: extend to widget border | |
if (info.y() == 0) { | |
QPoint topLeft = g.topLeft(); | |
topLeft.ry() = layout()->geometry().top(); | |
g.setTopLeft(topLeft); | |
} | |
const GridLikeLayout *grid = gridLikeLayout(); | |
Q_ASSERT(grid); | |
// extend to widget border | |
QPoint bottomRight = g.bottomRight(); | |
if (gridRowCount(grid) == info.y()) | |
bottomRight.ry() = layout()->geometry().bottom(); | |
if (gridColumnCount(grid) == info.x()) | |
bottomRight.rx() = layout()->geometry().right(); | |
g.setBottomRight(bottomRight); | |
return g; | |
} | |
// -------------- QGridLayoutSupport (LayoutDecorationExtension) | |
class QGridLayoutSupport: public GridLikeLayoutSupportBase<QGridLayout> | |
{ | |
public: | |
QGridLayoutSupport(QDesignerFormWindowInterface *formWindow, QWidget *widget, QObject *parent = 0); | |
virtual void simplify(); | |
virtual void insertRow(int row); | |
virtual void insertColumn(int column); | |
private: | |
}; | |
QGridLayoutSupport::QGridLayoutSupport(QDesignerFormWindowInterface *formWindow, QWidget *widget, QObject *parent) : | |
GridLikeLayoutSupportBase<QGridLayout>(formWindow, widget, new GridLayoutHelper, parent) | |
{ | |
} | |
void QGridLayoutSupport::insertRow(int row) | |
{ | |
QGridLayout *grid = gridLayout(); | |
Q_ASSERT(grid); | |
GridLayoutHelper::insertRow(grid, row); | |
} | |
void QGridLayoutSupport::insertColumn(int column) | |
{ | |
QGridLayout *grid = gridLayout(); | |
Q_ASSERT(grid); | |
GridLayoutState state; | |
state.fromLayout(grid); | |
state.insertColumn(column); | |
state.applyToLayout(formWindow()->core(), widget()); | |
} | |
void QGridLayoutSupport::simplify() | |
{ | |
QGridLayout *grid = gridLayout(); | |
Q_ASSERT(grid); | |
GridLayoutState state; | |
state.fromLayout(grid); | |
const QRect fullArea = QRect(0, 0, state.colCount, state.rowCount); | |
if (state.simplify(fullArea, false)) | |
state.applyToLayout(formWindow()->core(), widget()); | |
} | |
// -------------- QFormLayoutSupport (LayoutDecorationExtension) | |
class QFormLayoutSupport: public GridLikeLayoutSupportBase<QFormLayout> | |
{ | |
public: | |
QFormLayoutSupport(QDesignerFormWindowInterface *formWindow, QWidget *widget, QObject *parent = 0); | |
virtual void simplify() {} | |
virtual void insertRow(int /*row*/) {} | |
virtual void insertColumn(int /*column*/) {} | |
private: | |
virtual void checkCellForInsertion(int * row, int *col) const; | |
}; | |
QFormLayoutSupport::QFormLayoutSupport(QDesignerFormWindowInterface *formWindow, QWidget *widget, QObject *parent) : | |
GridLikeLayoutSupportBase<QFormLayout>(formWindow, widget, new FormLayoutHelper, parent) | |
{ | |
} | |
void QFormLayoutSupport::checkCellForInsertion(int *row, int *col) const | |
{ | |
if (*col >= FormLayoutColumns) { // Clamp to 2 columns | |
*col = 1; | |
(*row)++; | |
} | |
} | |
} // anonymous namespace | |
QLayoutSupport *QLayoutSupport::createLayoutSupport(QDesignerFormWindowInterface *formWindow, QWidget *widget, QObject *parent) | |
{ | |
const QLayout *layout = LayoutInfo::managedLayout(formWindow->core(), widget); | |
Q_ASSERT(layout); | |
QLayoutSupport *rc = 0; | |
switch (LayoutInfo::layoutType(formWindow->core(), layout)) { | |
case LayoutInfo::HBox: | |
rc = new QBoxLayoutSupport(formWindow, widget, Qt::Horizontal, parent); | |
break; | |
case LayoutInfo::VBox: | |
rc = new QBoxLayoutSupport(formWindow, widget, Qt::Vertical, parent); | |
break; | |
case LayoutInfo::Grid: | |
rc = new QGridLayoutSupport(formWindow, widget, parent); | |
break; | |
case LayoutInfo::Form: | |
rc = new QFormLayoutSupport(formWindow, widget, parent); | |
break; | |
default: | |
break; | |
} | |
Q_ASSERT(rc); | |
return rc; | |
} | |
} // namespace qdesigner_internal | |
// -------------- QLayoutWidget | |
QLayoutWidget::QLayoutWidget(QDesignerFormWindowInterface *formWindow, QWidget *parent) | |
: QWidget(parent), m_formWindow(formWindow), | |
m_leftMargin(0), m_topMargin(0), m_rightMargin(0), m_bottomMargin(0) | |
{ | |
} | |
void QLayoutWidget::paintEvent(QPaintEvent*) | |
{ | |
if (m_formWindow->currentTool() != 0) | |
return; | |
// only draw red borders if we're editting widgets | |
QPainter p(this); | |
QMap<int, QMap<int, bool> > excludedRowsForColumn; | |
QMap<int, QMap<int, bool> > excludedColumnsForRow; | |
QLayout *lt = layout(); | |
QGridLayout *grid = qobject_cast<QGridLayout *>(lt); | |
if (lt) { | |
if (const int count = lt->count()) { | |
p.setPen(QPen(QColor(255, 0, 0, 35), 1)); | |
for (int i = 0; i < count; i++) { | |
QLayoutItem *item = lt->itemAt(i); | |
if (grid) { | |
int row, column, rowSpan, columnSpan; | |
grid->getItemPosition(i, &row, &column, &rowSpan, &columnSpan); | |
QMap<int, bool> rows; | |
QMap<int, bool> columns; | |
for (int i = rowSpan; i > 1; i--) | |
rows[row + i - 2] = true; | |
for (int i = columnSpan; i > 1; i--) | |
columns[column + i - 2] = true; | |
while (rowSpan > 0) { | |
excludedColumnsForRow[row + rowSpan - 1].unite(columns); | |
rowSpan--; | |
} | |
while (columnSpan > 0) { | |
excludedRowsForColumn[column + columnSpan - 1].unite(rows); | |
columnSpan--; | |
} | |
} | |
if (item->spacerItem()) { | |
const QRect geometry = item->geometry(); | |
if (!geometry.isNull()) | |
p.drawRect(geometry.adjusted(1, 1, -2, -2)); | |
} | |
} | |
} | |
} | |
if (grid) { | |
p.setPen(QPen(QColor(0, 0x80, 0, 0x80), 1)); | |
const int rowCount = grid->rowCount(); | |
const int columnCount = grid->columnCount(); | |
for (int i = 0; i < rowCount; i++) { | |
for (int j = 0; j < columnCount; j++) { | |
const QRect cellRect = grid->cellRect(i, j); | |
if (j < columnCount - 1 && excludedColumnsForRow.value(i).value(j, false) == false) { | |
const double y0 = (i == 0) | |
? 0 : (grid->cellRect(i - 1, j).bottom() + cellRect.top()) / 2.0; | |
const double y1 = (i == rowCount - 1) | |
? height() - 1 : (cellRect.bottom() + grid->cellRect(i + 1, j).top()) / 2.0; | |
const double x = (cellRect.right() + grid->cellRect(i, j + 1).left()) / 2.0; | |
p.drawLine(QPointF(x, y0), QPointF(x, y1)); | |
} | |
if (i < rowCount - 1 && excludedRowsForColumn.value(j).value(i, false) == false) { | |
const double x0 = (j == 0) | |
? 0 : (grid->cellRect(i, j - 1).right() + cellRect.left()) / 2.0; | |
const double x1 = (j == columnCount - 1) | |
? width() - 1 : (cellRect.right() + grid->cellRect(i, j + 1).left()) / 2.0; | |
const double y = (cellRect.bottom() + grid->cellRect(i + 1, j).top()) / 2.0; | |
p.drawLine(QPointF(x0, y), QPointF(x1, y)); | |
} | |
} | |
} | |
} | |
p.setPen(QPen(QColor(255, 0, 0, 128), 1)); | |
p.drawRect(0, 0, width() - 1, height() - 1); | |
} | |
bool QLayoutWidget::event(QEvent *e) | |
{ | |
switch (e->type()) { | |
case QEvent::LayoutRequest: { | |
(void) QWidget::event(e); | |
// Magic: We are layouted, but the parent is not.. | |
if (layout() && qdesigner_internal::LayoutInfo::layoutType(formWindow()->core(), parentWidget()) == qdesigner_internal::LayoutInfo::NoLayout) { | |
resize(layout()->totalMinimumSize().expandedTo(size())); | |
} | |
update(); | |
return true; | |
} | |
default: | |
break; | |
} | |
return QWidget::event(e); | |
} | |
int QLayoutWidget::layoutLeftMargin() const | |
{ | |
if (m_leftMargin < 0 && layout()) { | |
int margin; | |
layout()->getContentsMargins(&margin, 0, 0, 0); | |
return margin; | |
} | |
return m_leftMargin; | |
} | |
void QLayoutWidget::setLayoutLeftMargin(int layoutMargin) | |
{ | |
m_leftMargin = layoutMargin; | |
if (layout()) { | |
int newMargin = m_leftMargin; | |
if (newMargin >= 0 && newMargin < ShiftValue) | |
newMargin = ShiftValue; | |
int left, top, right, bottom; | |
layout()->getContentsMargins(&left, &top, &right, &bottom); | |
layout()->setContentsMargins(newMargin, top, right, bottom); | |
} | |
} | |
int QLayoutWidget::layoutTopMargin() const | |
{ | |
if (m_topMargin < 0 && layout()) { | |
int margin; | |
layout()->getContentsMargins(0, &margin, 0, 0); | |
return margin; | |
} | |
return m_topMargin; | |
} | |
void QLayoutWidget::setLayoutTopMargin(int layoutMargin) | |
{ | |
m_topMargin = layoutMargin; | |
if (layout()) { | |
int newMargin = m_topMargin; | |
if (newMargin >= 0 && newMargin < ShiftValue) | |
newMargin = ShiftValue; | |
int left, top, right, bottom; | |
layout()->getContentsMargins(&left, &top, &right, &bottom); | |
layout()->setContentsMargins(left, newMargin, right, bottom); | |
} | |
} | |
int QLayoutWidget::layoutRightMargin() const | |
{ | |
if (m_rightMargin < 0 && layout()) { | |
int margin; | |
layout()->getContentsMargins(0, 0, &margin, 0); | |
return margin; | |
} | |
return m_rightMargin; | |
} | |
void QLayoutWidget::setLayoutRightMargin(int layoutMargin) | |
{ | |
m_rightMargin = layoutMargin; | |
if (layout()) { | |
int newMargin = m_rightMargin; | |
if (newMargin >= 0 && newMargin < ShiftValue) | |
newMargin = ShiftValue; | |
int left, top, right, bottom; | |
layout()->getContentsMargins(&left, &top, &right, &bottom); | |
layout()->setContentsMargins(left, top, newMargin, bottom); | |
} | |
} | |
int QLayoutWidget::layoutBottomMargin() const | |
{ | |
if (m_bottomMargin < 0 && layout()) { | |
int margin; | |
layout()->getContentsMargins(0, 0, 0, &margin); | |
return margin; | |
} | |
return m_bottomMargin; | |
} | |
void QLayoutWidget::setLayoutBottomMargin(int layoutMargin) | |
{ | |
m_bottomMargin = layoutMargin; | |
if (layout()) { | |
int newMargin = m_bottomMargin; | |
if (newMargin >= 0 && newMargin < ShiftValue) | |
newMargin = ShiftValue; | |
int left, top, right, bottom; | |
layout()->getContentsMargins(&left, &top, &right, &bottom); | |
layout()->setContentsMargins(left, top, right, newMargin); | |
} | |
} | |
QT_END_NAMESPACE |