diff options
author | Alexis Menard <alexis.menard@nokia.com> | 2009-04-17 14:06:06 (GMT) |
---|---|---|
committer | Alexis Menard <alexis.menard@nokia.com> | 2009-04-17 14:06:06 (GMT) |
commit | f15b8a83e2e51955776a3f07cb85ebfc342dd8ef (patch) | |
tree | c5dc684986051654898db11ce73e03b9fec8db99 /tools/designer/src/lib/shared/qdesigner_propertycommand.cpp | |
download | Qt-f15b8a83e2e51955776a3f07cb85ebfc342dd8ef.zip Qt-f15b8a83e2e51955776a3f07cb85ebfc342dd8ef.tar.gz Qt-f15b8a83e2e51955776a3f07cb85ebfc342dd8ef.tar.bz2 |
Initial import of statemachine branch from the old kinetic repository
Diffstat (limited to 'tools/designer/src/lib/shared/qdesigner_propertycommand.cpp')
-rw-r--r-- | tools/designer/src/lib/shared/qdesigner_propertycommand.cpp | 1479 |
1 files changed, 1479 insertions, 0 deletions
diff --git a/tools/designer/src/lib/shared/qdesigner_propertycommand.cpp b/tools/designer/src/lib/shared/qdesigner_propertycommand.cpp new file mode 100644 index 0000000..ff3b50b --- /dev/null +++ b/tools/designer/src/lib/shared/qdesigner_propertycommand.cpp @@ -0,0 +1,1479 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, 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.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdesigner_propertycommand_p.h" +#include "qdesigner_utils_p.h" +#include "dynamicpropertysheet.h" +#include "qdesigner_propertyeditor_p.h" +#include "qdesigner_integration_p.h" +#include "spacer_widget_p.h" +#include "qdesigner_propertysheet_p.h" + +#include <QtDesigner/QDesignerFormEditorInterface> +#include <QtDesigner/QDesignerFormWindowInterface> +#include <QtDesigner/QDesignerFormWindowCursorInterface> +#include <QtDesigner/QDesignerDynamicPropertySheetExtension> +#include <QtDesigner/QDesignerPropertySheetExtension> +#include <QtDesigner/QDesignerPropertyEditorInterface> +#include <QtDesigner/QDesignerObjectInspectorInterface> +#include <QtDesigner/QDesignerIntegrationInterface> +#include <QtDesigner/QDesignerWidgetDataBaseInterface> +#include <QtDesigner/QExtensionManager> + +#include <QtCore/QSize> +#include <QtCore/QTextStream> +#include <QtGui/QWidget> +#include <QtGui/QApplication> +#include <QtGui/QAction> +#include <QtGui/QDialog> +#include <QtGui/QPushButton> +#include <QtGui/QLayout> +#include <qdebug.h> + +QT_BEGIN_NAMESPACE + +namespace { +enum { debugPropertyCommands = 0 }; + +// Debug resolve mask of font +QString fontMask(unsigned m) +{ + QString rc; + if (m & QFont::FamilyResolved) + rc += QLatin1String("Family"); + if (m & QFont::SizeResolved) + rc += QLatin1String("Size "); + if (m & QFont::WeightResolved) + rc += QLatin1String("Bold "); + if (m & QFont::StyleResolved) + rc += QLatin1String("Style "); + if (m & QFont::UnderlineResolved) + rc += QLatin1String("Underline "); + if (m & QFont::StrikeOutResolved) + rc += QLatin1String("StrikeOut "); + if (m & QFont::KerningResolved) + rc += QLatin1String("Kerning "); + if (m & QFont::StyleStrategyResolved) + rc += QLatin1String("StyleStrategy"); + return rc; +} + +// Debug font +QString fontString(const QFont &f) +{ + QString rc; { + const QChar comma = QLatin1Char(','); + QTextStream str(&rc); + str << QLatin1String("QFont(\"") << f.family() << comma << + f.pointSize(); + if (f.bold()) + str << comma << QLatin1String("bold"); + if (f.italic()) + str << comma << QLatin1String("italic"); + if (f.underline()) + str << comma << QLatin1String("underline"); + if (f.strikeOut()) + str << comma << QLatin1String("strikeOut"); + if (f.kerning()) + str << comma << QLatin1String("kerning"); + str << comma << f.styleStrategy() << QLatin1String(" resolve: ") + << fontMask(f.resolve()) << QLatin1Char(')'); + } + return rc; +} +QSize checkSize(const QSize &size) +{ + return size.boundedTo(QSize(0xFFFFFF, 0xFFFFFF)); +} + +QSize diffSize(QDesignerFormWindowInterface *fw) +{ + const QWidget *container = fw->core()->integration()->containerWindow(fw); + if (!container) + return QSize(); + + const QSize diff = container->size() - fw->size(); // decoration offset of container window + return diff; +} + +void checkSizes(QDesignerFormWindowInterface *fw, const QSize &size, QSize *formSize, QSize *containerSize) +{ + const QWidget *container = fw->core()->integration()->containerWindow(fw); + if (!container) + return; + + const QSize diff = diffSize(fw); // decoration offset of container window + + QSize newFormSize = checkSize(size).expandedTo(fw->mainContainer()->minimumSizeHint()); // don't try to resize to smaller size than minimumSizeHint + QSize newContainerSize = newFormSize + diff; + + newContainerSize = newContainerSize.expandedTo(container->minimumSizeHint()); + newContainerSize = newContainerSize.expandedTo(container->minimumSize()); + + newFormSize = newContainerSize - diff; + + newContainerSize = checkSize(newContainerSize); + + if (formSize) + *formSize = newFormSize; + if (containerSize) + *containerSize = newContainerSize; +} + +/* SubProperties: When applying a changed property to a multiselection, it sometimes makes + * sense to apply only parts (subproperties) of the property. + * For example, if someone changes the x-value of a geometry in the property editor + * and applies it to a multi-selection, y should not be applied as this would cause all + * the widgets to overlap. + * The following routines can be used to find out the changed subproperties of a property, + * which are represented as a mask, and to apply them while leaving the others intact. */ + +enum RectSubPropertyMask { SubPropertyX=1, SubPropertyY = 2, SubPropertyWidth = 4, SubPropertyHeight = 8 }; +enum SizePolicySubPropertyMask { SubPropertyHSizePolicy = 1, SubPropertyHStretch = 2, SubPropertyVSizePolicy = 4, SubPropertyVStretch = 8 }; +enum AlignmentSubPropertyMask { SubPropertyHorizontalAlignment = 1, SubPropertyVerticalAlignment = 2 }; +enum StringSubPropertyMask { SubPropertyStringValue = 1, SubPropertyStringComment = 2, SubPropertyStringTranslatable = 4, SubPropertyStringDisambiguation = 8 }; +enum KeySequenceSubPropertyMask { SubPropertyKeySequenceValue = 1, SubPropertyKeySequenceComment = 2, SubPropertyKeySequenceTranslatable = 4, SubPropertyKeySequenceDisambiguation = 8 }; + +enum CommonSubPropertyMask { SubPropertyAll = 0xFFFFFFFF }; + +// Set the mask flag in mask if the properties do not match. +#define COMPARE_SUBPROPERTY(object1, object2, getter, mask, maskFlag) if (object1.getter() != object2.getter()) mask |= maskFlag; + +// find changed subproperties of a rectangle +unsigned compareSubProperties(const QRect & r1, const QRect & r2) +{ + unsigned rc = 0; + COMPARE_SUBPROPERTY(r1, r2, x, rc, SubPropertyX) + COMPARE_SUBPROPERTY(r1, r2, y, rc, SubPropertyY) + COMPARE_SUBPROPERTY(r1, r2, width, rc, SubPropertyWidth) + COMPARE_SUBPROPERTY(r1, r2, height, rc, SubPropertyHeight) + return rc; +} + +// find changed subproperties of a QSize +unsigned compareSubProperties(const QSize & r1, const QSize & r2) +{ + unsigned rc = 0; + COMPARE_SUBPROPERTY(r1, r2, width, rc, SubPropertyWidth) + COMPARE_SUBPROPERTY(r1, r2, height, rc, SubPropertyHeight) + return rc; +} +// find changed subproperties of a QSizePolicy +unsigned compareSubProperties(const QSizePolicy & sp1, const QSizePolicy & sp2) +{ + unsigned rc = 0; + COMPARE_SUBPROPERTY(sp1, sp2, horizontalPolicy, rc, SubPropertyHSizePolicy) + COMPARE_SUBPROPERTY(sp1, sp2, horizontalStretch, rc, SubPropertyHStretch) + COMPARE_SUBPROPERTY(sp1, sp2, verticalPolicy, rc, SubPropertyVSizePolicy) + COMPARE_SUBPROPERTY(sp1, sp2, verticalStretch, rc, SubPropertyVStretch) + return rc; +} +// find changed subproperties of qdesigner_internal::PropertySheetStringValue +unsigned compareSubProperties(const qdesigner_internal::PropertySheetStringValue & str1, const qdesigner_internal::PropertySheetStringValue & str2) +{ + unsigned rc = 0; + COMPARE_SUBPROPERTY(str1, str2, value, rc, SubPropertyStringValue) + COMPARE_SUBPROPERTY(str1, str2, comment, rc, SubPropertyStringComment) + COMPARE_SUBPROPERTY(str1, str2, translatable, rc, SubPropertyStringTranslatable) + COMPARE_SUBPROPERTY(str1, str2, disambiguation, rc, SubPropertyStringDisambiguation) + return rc; +} +// find changed subproperties of qdesigner_internal::PropertySheetKeySequenceValue +unsigned compareSubProperties(const qdesigner_internal::PropertySheetKeySequenceValue & str1, const qdesigner_internal::PropertySheetKeySequenceValue & str2) +{ + unsigned rc = 0; + COMPARE_SUBPROPERTY(str1, str2, value, rc, SubPropertyKeySequenceValue) + COMPARE_SUBPROPERTY(str1, str2, comment, rc, SubPropertyKeySequenceComment) + COMPARE_SUBPROPERTY(str1, str2, translatable, rc, SubPropertyKeySequenceTranslatable) + COMPARE_SUBPROPERTY(str1, str2, disambiguation, rc, SubPropertyKeySequenceDisambiguation) + return rc; +} + +// Compare font-subproperties taking the [undocumented] +// resolve flag into account +template <class Property> +void compareFontSubProperty(const QFont & f1, + const QFont & f2, + Property (QFont::*getter) () const, + unsigned maskBit, + unsigned &mask) +{ + const bool f1Changed = f1.resolve() & maskBit; + const bool f2Changed = f2.resolve() & maskBit; + // Role has been set/reset in editor + if (f1Changed != f2Changed) { + mask |= maskBit; + } else { + // Was modified in both palettes: Compare values. + if (f1Changed && f2Changed && (f1.*getter)() != (f2.*getter)()) + mask |= maskBit; + } +} +// find changed subproperties of a QFont +unsigned compareSubProperties(const QFont & f1, const QFont & f2) +{ + unsigned rc = 0; + compareFontSubProperty(f1, f2, &QFont::family, QFont::FamilyResolved, rc); + compareFontSubProperty(f1, f2, &QFont::pointSize, QFont::SizeResolved, rc); + compareFontSubProperty(f1, f2, &QFont::bold, QFont::WeightResolved, rc); + compareFontSubProperty(f1, f2, &QFont::italic, QFont::StyleResolved, rc); + compareFontSubProperty(f1, f2, &QFont::underline, QFont::UnderlineResolved, rc); + compareFontSubProperty(f1, f2, &QFont::strikeOut, QFont::StrikeOutResolved, rc); + compareFontSubProperty(f1, f2, &QFont::kerning, QFont::KerningResolved, rc); + compareFontSubProperty(f1, f2, &QFont::styleStrategy, QFont::StyleStrategyResolved, rc); + if (debugPropertyCommands) + qDebug() << "compareSubProperties " << fontString(f1) << fontString(f2) << "\n\treturns " << fontMask(rc); + return rc; +} + +// Compare colors of a role +bool roleColorChanged(const QPalette & p1, const QPalette & p2, QPalette::ColorRole role) +{ + for (int group = QPalette::Active; group < QPalette::NColorGroups; group++) { + const QPalette::ColorGroup pgroup = static_cast<QPalette::ColorGroup>(group); + if (p1.color(pgroup, role) != p2.color(pgroup, role)) + return true; + } + return false; +} +// find changed subproperties of a QPalette taking the [undocumented] resolve flags into account +unsigned compareSubProperties(const QPalette & p1, const QPalette & p2) +{ + unsigned rc = 0; + unsigned maskBit = 1u; + // generate a mask for each role + const unsigned p1Changed = p1.resolve(); + const unsigned p2Changed = p2.resolve(); + for (int role = QPalette::WindowText; role < QPalette::NColorRoles; role++, maskBit <<= 1u) { + const bool p1RoleChanged = p1Changed & maskBit; + const bool p2RoleChanged = p2Changed & maskBit; + // Role has been set/reset in editor + if (p1RoleChanged != p2RoleChanged) { + rc |= maskBit; + } else { + // Was modified in both palettes: Compare values. + if (p1RoleChanged && p2RoleChanged && roleColorChanged(p1, p2, static_cast<QPalette::ColorRole>(role))) + rc |= maskBit; + } + } + return rc; +} + +// find changed subproperties of a QAlignment which is a flag combination of vertical and horizontal + +unsigned compareSubProperties(Qt::Alignment a1, Qt::Alignment a2) +{ + unsigned rc = 0; + if ((a1 & Qt::AlignHorizontal_Mask) != (a2 & Qt::AlignHorizontal_Mask)) + rc |= SubPropertyHorizontalAlignment; + if ((a1 & Qt::AlignVertical_Mask) != (a2 & Qt::AlignVertical_Mask)) + rc |= SubPropertyVerticalAlignment; + return rc; +} + +Qt::Alignment variantToAlignment(const QVariant & q) +{ + return Qt::Alignment(qdesigner_internal::Utils::valueOf(q)); +} +// find changed subproperties of a variant +unsigned compareSubProperties(const QVariant & q1, const QVariant & q2, qdesigner_internal::SpecialProperty specialProperty) +{ + switch (q1.type()) { + case QVariant::Rect: + return compareSubProperties(q1.toRect(), q2.toRect()); + case QVariant::Size: + return compareSubProperties(q1.toSize(), q2.toSize()); + case QVariant::SizePolicy: + return compareSubProperties(qvariant_cast<QSizePolicy>(q1), qvariant_cast<QSizePolicy>(q2)); + case QVariant::Font: + return compareSubProperties(qvariant_cast<QFont>(q1), qvariant_cast<QFont>(q2)); + case QVariant::Palette: + return compareSubProperties(qvariant_cast<QPalette>(q1), qvariant_cast<QPalette>(q2)); + default: + if (q1.userType() == qMetaTypeId<qdesigner_internal::PropertySheetIconValue>()) + return qvariant_cast<qdesigner_internal::PropertySheetIconValue>(q1).compare(qvariant_cast<qdesigner_internal::PropertySheetIconValue>(q2)); + else if (q1.userType() == qMetaTypeId<qdesigner_internal::PropertySheetStringValue>()) + return compareSubProperties(qvariant_cast<qdesigner_internal::PropertySheetStringValue>(q1), qvariant_cast<qdesigner_internal::PropertySheetStringValue>(q2)); + else if (q1.userType() == qMetaTypeId<qdesigner_internal::PropertySheetKeySequenceValue>()) + return compareSubProperties(qvariant_cast<qdesigner_internal::PropertySheetKeySequenceValue>(q1), qvariant_cast<qdesigner_internal::PropertySheetKeySequenceValue>(q2)); + // Enumerations, flags + switch (specialProperty) { + case qdesigner_internal::SP_Alignment: + return compareSubProperties(variantToAlignment(q1), variantToAlignment(q2)); + default: + break; + } + break; + } + return SubPropertyAll; +} + +// Apply the sub property if mask flag is set in mask +#define SET_SUBPROPERTY(rc, newValue, getter, setter, mask, maskFlag) if (mask & maskFlag) rc.setter(newValue.getter()); + +// apply changed subproperties to a rectangle +QRect applyRectSubProperty(const QRect &oldValue, const QRect &newValue, unsigned mask) +{ + QRect rc = oldValue; + SET_SUBPROPERTY(rc, newValue, x, moveLeft, mask, SubPropertyX) + SET_SUBPROPERTY(rc, newValue, y, moveTop, mask, SubPropertyY) + SET_SUBPROPERTY(rc, newValue, width, setWidth, mask, SubPropertyWidth) + SET_SUBPROPERTY(rc, newValue, height, setHeight, mask, SubPropertyHeight) + return rc; +} + + +// apply changed subproperties to a rectangle QSize +QSize applySizeSubProperty(const QSize &oldValue, const QSize &newValue, unsigned mask) +{ + QSize rc = oldValue; + SET_SUBPROPERTY(rc, newValue, width, setWidth, mask, SubPropertyWidth) + SET_SUBPROPERTY(rc, newValue, height, setHeight, mask, SubPropertyHeight) + return rc; +} + + +// apply changed subproperties to a SizePolicy +QSizePolicy applySizePolicySubProperty(const QSizePolicy &oldValue, const QSizePolicy &newValue, unsigned mask) +{ + QSizePolicy rc = oldValue; + SET_SUBPROPERTY(rc, newValue, horizontalPolicy, setHorizontalPolicy, mask, SubPropertyHSizePolicy) + SET_SUBPROPERTY(rc, newValue, horizontalStretch, setHorizontalStretch, mask, SubPropertyHStretch) + SET_SUBPROPERTY(rc, newValue, verticalPolicy, setVerticalPolicy, mask, SubPropertyVSizePolicy) + SET_SUBPROPERTY(rc, newValue, verticalStretch, setVerticalStretch, mask, SubPropertyVStretch) + return rc; +} + +// apply changed subproperties to a qdesigner_internal::PropertySheetStringValue +qdesigner_internal::PropertySheetStringValue applyStringSubProperty(const qdesigner_internal::PropertySheetStringValue &oldValue, + const qdesigner_internal::PropertySheetStringValue &newValue, unsigned mask) +{ + qdesigner_internal::PropertySheetStringValue rc = oldValue; + SET_SUBPROPERTY(rc, newValue, value, setValue, mask, SubPropertyStringValue) + SET_SUBPROPERTY(rc, newValue, comment, setComment, mask, SubPropertyStringComment) + SET_SUBPROPERTY(rc, newValue, translatable, setTranslatable, mask, SubPropertyStringTranslatable) + SET_SUBPROPERTY(rc, newValue, disambiguation, setDisambiguation, mask, SubPropertyStringDisambiguation) + return rc; +} + +// apply changed subproperties to a qdesigner_internal::PropertySheetKeySequenceValue +qdesigner_internal::PropertySheetKeySequenceValue applyKeySequenceSubProperty(const qdesigner_internal::PropertySheetKeySequenceValue &oldValue, + const qdesigner_internal::PropertySheetKeySequenceValue &newValue, unsigned mask) +{ + qdesigner_internal::PropertySheetKeySequenceValue rc = oldValue; + SET_SUBPROPERTY(rc, newValue, value, setValue, mask, SubPropertyKeySequenceValue) + SET_SUBPROPERTY(rc, newValue, comment, setComment, mask, SubPropertyKeySequenceComment) + SET_SUBPROPERTY(rc, newValue, translatable, setTranslatable, mask, SubPropertyKeySequenceTranslatable) + SET_SUBPROPERTY(rc, newValue, disambiguation, setDisambiguation, mask, SubPropertyKeySequenceDisambiguation) + return rc; +} + +// Apply the font-subproperties keeping the [undocumented] +// resolve flag in sync (note that PropertySetterType might be something like const T&). +template <class PropertyReturnType, class PropertySetterType> +inline void setFontSubProperty(unsigned mask, + const QFont &newValue, + unsigned maskBit, + PropertyReturnType (QFont::*getter) () const, + void (QFont::*setter) (PropertySetterType), + QFont &value) +{ + if (mask & maskBit) { + (value.*setter)((newValue.*getter)()); + // Set the resolve bit from NewValue in return value + uint r = value.resolve(); + const bool origFlag = newValue.resolve() & maskBit; + if (origFlag) + r |= maskBit; + else + r &= ~maskBit; + value.resolve(r); + if (debugPropertyCommands) + qDebug() << "setFontSubProperty " << fontMask(maskBit) << " resolve=" << origFlag; + } +} +// apply changed subproperties to a QFont +QFont applyFontSubProperty(const QFont &oldValue, const QFont &newValue, unsigned mask) +{ + QFont rc = oldValue; + setFontSubProperty(mask, newValue, QFont::FamilyResolved, &QFont::family, &QFont::setFamily, rc); + setFontSubProperty(mask, newValue, QFont::SizeResolved, &QFont::pointSize, &QFont::setPointSize, rc); + setFontSubProperty(mask, newValue, QFont::WeightResolved, &QFont::bold, &QFont::setBold, rc); + setFontSubProperty(mask, newValue, QFont::StyleResolved, &QFont::italic, &QFont::setItalic, rc); + setFontSubProperty(mask, newValue, QFont::UnderlineResolved, &QFont::underline, &QFont::setUnderline, rc); + setFontSubProperty(mask, newValue, QFont::StrikeOutResolved, &QFont::strikeOut, &QFont::setStrikeOut, rc); + setFontSubProperty(mask, newValue, QFont::KerningResolved, &QFont::kerning, &QFont::setKerning, rc); + setFontSubProperty(mask, newValue, QFont::StyleStrategyResolved, &QFont::styleStrategy, &QFont::setStyleStrategy, rc); + if (debugPropertyCommands) + qDebug() << "applyFontSubProperty old " << fontMask(oldValue.resolve()) << " new " << fontMask(newValue.resolve()) << " return: " << fontMask(rc.resolve()); + return rc; +} + +// apply changed subproperties to a QPalette +QPalette applyPaletteSubProperty(const QPalette &oldValue, const QPalette &newValue, unsigned mask) +{ + QPalette rc = oldValue; + // apply a mask for each role + unsigned maskBit = 1u; + for (int role = QPalette::WindowText; role < QPalette::NColorRoles; role++, maskBit <<= 1u) { + if (mask & maskBit) { + for (int group = QPalette::Active; group < QPalette::NColorGroups; group++) { + const QPalette::ColorGroup pgroup = static_cast<QPalette::ColorGroup>(group); + const QPalette::ColorRole prole = static_cast<QPalette::ColorRole>(role); + rc.setColor(pgroup, prole, newValue.color(pgroup, prole)); + } + // Set the resolve bit from NewValue in return value + uint r = rc.resolve(); + const bool origFlag = newValue.resolve() & maskBit; + if (origFlag) + r |= maskBit; + else + r &= ~maskBit; + rc.resolve(r); + } + } + return rc; +} + +// apply changed subproperties to a QAlignment which is a flag combination of vertical and horizontal +Qt::Alignment applyAlignmentSubProperty(Qt::Alignment oldValue, Qt::Alignment newValue, unsigned mask) +{ + // easy: both changed. + if (mask == (SubPropertyHorizontalAlignment|SubPropertyVerticalAlignment)) + return newValue; + // Change subprop + const Qt::Alignment changeMask = (mask & SubPropertyHorizontalAlignment) ? Qt::AlignHorizontal_Mask : Qt::AlignVertical_Mask; + const Qt::Alignment takeOverMask = (mask & SubPropertyHorizontalAlignment) ? Qt::AlignVertical_Mask : Qt::AlignHorizontal_Mask; + return (oldValue & takeOverMask) | (newValue & changeMask); +} + +} + +namespace qdesigner_internal { + +// apply changed subproperties to a variant +PropertyHelper::Value applySubProperty(const QVariant &oldValue, const QVariant &newValue, qdesigner_internal::SpecialProperty specialProperty, unsigned mask, bool changed) +{ + if (mask == SubPropertyAll) + return PropertyHelper::Value(newValue, changed); + + switch (oldValue.type()) { + case QVariant::Rect: + return PropertyHelper::Value(applyRectSubProperty(oldValue.toRect(), newValue.toRect(), mask), changed); + case QVariant::Size: + return PropertyHelper::Value(applySizeSubProperty(oldValue.toSize(), newValue.toSize(), mask), changed); + case QVariant::SizePolicy: + return PropertyHelper::Value(qVariantFromValue(applySizePolicySubProperty(qvariant_cast<QSizePolicy>(oldValue), qvariant_cast<QSizePolicy>(newValue), mask)), changed); + case QVariant::Font: { + // Changed flag in case of font and palette depends on resolve mask only, not on the passed "changed" value. + + // The first case: the user changed bold subproperty and then pressed reset button for this subproperty (not for + // the whole font property). We instantiate SetPropertyCommand passing changed=true. But in this case no + // subproperty is changed and the whole property should be marked an unchanged. + + // The second case: there are 2 pushbuttons, for 1st the user set bold and italic subproperties, + // for the 2nd he set bold only. He does multiselection so that the current widget is the 2nd one. + // He press reset next to bold subproperty. In result the 2nd widget should have the whole + // font property marked as unchanged and the 1st widget should have the font property + // marked as changed and only italic subproperty should be marked as changed (the bold should be reset). + + // The third case: there are 2 pushbuttons, for 1st the user set bold and italic subproperties, + // for the 2nd he set bold only. He does multiselection so that the current widget is the 2nd one. + // He press reset button for the whole font property. In result whole font properties for both + // widgets should be marked as unchanged. + QFont font = applyFontSubProperty(qvariant_cast<QFont>(oldValue), qvariant_cast<QFont>(newValue), mask); + return PropertyHelper::Value(qVariantFromValue(font), font.resolve()); + } + case QVariant::Palette: { + QPalette palette = applyPaletteSubProperty(qvariant_cast<QPalette>(oldValue), qvariant_cast<QPalette>(newValue), mask); + return PropertyHelper::Value(qVariantFromValue(palette), palette.resolve()); + } + default: + if (oldValue.userType() == qMetaTypeId<qdesigner_internal::PropertySheetIconValue>()) { + PropertySheetIconValue icon = qvariant_cast<qdesigner_internal::PropertySheetIconValue>(oldValue); + icon.assign(qvariant_cast<qdesigner_internal::PropertySheetIconValue>(newValue), mask); + return PropertyHelper::Value(qVariantFromValue(icon), icon.mask()); + } else if (oldValue.userType() == qMetaTypeId<qdesigner_internal::PropertySheetStringValue>()) { + qdesigner_internal::PropertySheetStringValue str = applyStringSubProperty( + qvariant_cast<qdesigner_internal::PropertySheetStringValue>(oldValue), + qvariant_cast<qdesigner_internal::PropertySheetStringValue>(newValue), mask); + return PropertyHelper::Value(qVariantFromValue(str), changed); + } else if (oldValue.userType() == qMetaTypeId<qdesigner_internal::PropertySheetKeySequenceValue>()) { + qdesigner_internal::PropertySheetKeySequenceValue key = applyKeySequenceSubProperty( + qvariant_cast<qdesigner_internal::PropertySheetKeySequenceValue>(oldValue), + qvariant_cast<qdesigner_internal::PropertySheetKeySequenceValue>(newValue), mask); + return PropertyHelper::Value(qVariantFromValue(key), changed); + } + // Enumerations, flags + switch (specialProperty) { + case qdesigner_internal::SP_Alignment: { + qdesigner_internal::PropertySheetFlagValue f = qvariant_cast<qdesigner_internal::PropertySheetFlagValue>(oldValue); + f.value = applyAlignmentSubProperty(variantToAlignment(oldValue), variantToAlignment(newValue), mask); + QVariant v; + qVariantSetValue(v, f); + return PropertyHelper::Value(v, changed); + } + default: + break; + } + break; + } + return PropertyHelper::Value(newValue, changed); + +} +// figure out special property +enum SpecialProperty getSpecialProperty(const QString& propertyName) +{ + if (propertyName == QLatin1String("objectName")) + return SP_ObjectName; + if (propertyName == QLatin1String("layoutName")) + return SP_LayoutName; + if (propertyName == QLatin1String("spacerName")) + return SP_SpacerName; + if (propertyName == QLatin1String("icon")) + return SP_Icon; + if (propertyName == QLatin1String("currentTabName")) + return SP_CurrentTabName; + if (propertyName == QLatin1String("currentItemName")) + return SP_CurrentItemName; + if (propertyName == QLatin1String("currentPageName")) + return SP_CurrentPageName; + if (propertyName == QLatin1String("geometry")) + return SP_Geometry; + if (propertyName == QLatin1String("windowTitle")) + return SP_WindowTitle; + if (propertyName == QLatin1String("minimumSize")) + return SP_MinimumSize; + if (propertyName == QLatin1String("maximumSize")) + return SP_MaximumSize; + if (propertyName == QLatin1String("alignment")) + return SP_Alignment; + if (propertyName == QLatin1String("autoDefault")) + return SP_AutoDefault; + if (propertyName == QLatin1String("shortcut")) + return SP_Shortcut; + if (propertyName == QLatin1String("orientation")) + return SP_Orientation; + return SP_None; +} + + +PropertyHelper::PropertyHelper(QObject* object, + SpecialProperty specialProperty, + QDesignerPropertySheetExtension *sheet, + int index) : + m_specialProperty(specialProperty), + m_object(object), + m_objectType(OT_Object), + m_propertySheet(sheet), m_index(index), + m_oldValue(m_propertySheet->property(m_index), m_propertySheet->isChanged(m_index)) +{ + if (object->isWidgetType()) { + m_parentWidget = (qobject_cast<QWidget*>(object))->parentWidget(); + m_objectType = OT_Widget; + } else { + if (const QAction *action = qobject_cast<const QAction *>(m_object)) + m_objectType = action->associatedWidgets().empty() ? OT_FreeAction : OT_AssociatedAction; + } + + if(debugPropertyCommands) + qDebug() << "PropertyHelper on " << m_object->objectName() << " index= " << m_index << " type = " << m_objectType; +} + +QDesignerIntegration *PropertyHelper::integration(QDesignerFormWindowInterface *fw) const +{ + return qobject_cast<QDesignerIntegration *>(fw->core()->integration()); +} + +// Set widget value, apply corrections and checks in case of main window. +void PropertyHelper::checkApplyWidgetValue(QDesignerFormWindowInterface *fw, QWidget* w, + SpecialProperty specialProperty, QVariant &value) +{ + + bool isMainContainer = false; + if (QDesignerFormWindowCursorInterface *cursor = fw->cursor()) { + if (cursor->isWidgetSelected(w)) { + if (cursor->isWidgetSelected(fw->mainContainer())) { + isMainContainer = true; + } + } + } + if (!isMainContainer) + return; + + QWidget *container = fw->core()->integration()->containerWindow(fw); + if (!container) + return; + + + switch (specialProperty) { + case SP_MinimumSize: { + const QSize size = checkSize(value.toSize()); + qVariantSetValue(value, size); + } + + break; + case SP_MaximumSize: { + QSize fs, cs; + checkSizes(fw, value.toSize(), &fs, &cs); + container->setMaximumSize(cs); + fw->mainContainer()->setMaximumSize(fs); + qVariantSetValue(value, fs); + + } + break; + case SP_Geometry: { + QRect r = value.toRect(); + QSize fs, cs; + checkSizes(fw, r.size(), &fs, &cs); + container->resize(cs); + r.setSize(fs); + qVariantSetValue(value, r); + } + break; + default: + break; + } +} + +unsigned PropertyHelper::updateMask() const +{ + unsigned rc = 0; + switch (m_specialProperty) { + case SP_ObjectName: + case SP_LayoutName: + case SP_SpacerName: + case SP_CurrentTabName: + case SP_CurrentItemName: + case SP_CurrentPageName: + if (m_objectType != OT_FreeAction) + rc |= UpdateObjectInspector; + break; + case SP_Icon: + if (m_objectType == OT_AssociatedAction) + rc |= UpdateObjectInspector; + break; + case SP_Orientation: // for updating splitter icon + rc |= UpdateObjectInspector; + break; + default: + break; + + } + return rc; +} + + +bool PropertyHelper::canMerge(const PropertyHelper &other) const +{ + return m_object == other.m_object && m_index == other.m_index; +} + +void PropertyHelper::triggerActionChanged(QAction *a) +{ + a->setData(QVariant(true)); // this triggers signal "changed" in QAction + a->setData(QVariant(false)); +} + +// Update the object to reflect the changes +void PropertyHelper::updateObject(QDesignerFormWindowInterface *fw, const QVariant &oldValue, const QVariant &newValue) +{ + if(debugPropertyCommands){ + qDebug() << "PropertyHelper::updateObject(" << m_object->objectName() << ") " << oldValue << " -> " << newValue; + } + switch (m_objectType) { + case OT_Widget: { + switch (m_specialProperty) { + case SP_ObjectName: { + const QString oldName = qVariantValue<PropertySheetStringValue>(oldValue).value(); + const QString newName = qVariantValue<PropertySheetStringValue>(newValue).value(); + QDesignerFormWindowCommand::updateBuddies(fw, oldName, newName); + } + break; + default: + break; + } + } break; + case OT_AssociatedAction: + case OT_FreeAction: + // SP_Shortcut is a fake property, so, QAction::changed does not trigger. + if (m_specialProperty == SP_ObjectName || m_specialProperty == SP_Shortcut) + triggerActionChanged(qobject_cast<QAction *>(m_object)); + break; + default: + break; + } + + switch (m_specialProperty) { + case SP_ObjectName: + case SP_LayoutName: + case SP_SpacerName: + if (QDesignerIntegration *integr = integration(fw)) { + const QString oldName = qVariantValue<PropertySheetStringValue>(oldValue).value(); + const QString newName = qVariantValue<PropertySheetStringValue>(newValue).value(); + integr->emitObjectNameChanged(fw, m_object, newName, oldName); + } + break; + default: + break; + } +} + +void PropertyHelper::ensureUniqueObjectName(QDesignerFormWindowInterface *fw, QObject *object) const +{ + switch (m_specialProperty) { + case SP_SpacerName: + if (object->isWidgetType()) { + if (Spacer *sp = qobject_cast<Spacer *>(object)) { + fw->ensureUniqueObjectName(sp); + return; + } + } + fw->ensureUniqueObjectName(object); + break; + case SP_LayoutName: // Layout name is invoked on the parent widget. + if (object->isWidgetType()) { + const QWidget * w = qobject_cast<const QWidget *>(object); + if (QLayout *wlayout = w->layout()) { + fw->ensureUniqueObjectName(wlayout); + return; + } + } + fw->ensureUniqueObjectName(object); + break; + case SP_ObjectName: + fw->ensureUniqueObjectName(object); + break; + default: + break; + } +} + +PropertyHelper::Value PropertyHelper::setValue(QDesignerFormWindowInterface *fw, const QVariant &value, bool changed, unsigned subPropertyMask) +{ + // Set new whole value + if (subPropertyMask == SubPropertyAll) + return applyValue(fw, m_oldValue.first, Value(value, changed)); + + // apply subproperties + const PropertyHelper::Value maskedNewValue = applySubProperty(m_oldValue.first, value, m_specialProperty, subPropertyMask, changed); + return applyValue(fw, m_oldValue.first, maskedNewValue); +} + +// Apply the value and update. Returns corrected value +PropertyHelper::Value PropertyHelper::applyValue(QDesignerFormWindowInterface *fw, const QVariant &oldValue, Value newValue) +{ + if(debugPropertyCommands){ + qDebug() << "PropertyHelper::applyValue(" << m_object << ") " << oldValue << " -> " << newValue.first << " changed=" << newValue.second; + } + + if (m_objectType == OT_Widget) { + checkApplyWidgetValue(fw, qobject_cast<QWidget *>(m_object), m_specialProperty, newValue.first); + } + + m_propertySheet->setProperty(m_index, newValue.first); + m_propertySheet->setChanged(m_index, newValue.second); + + switch (m_specialProperty) { + case SP_LayoutName: + case SP_ObjectName: + case SP_SpacerName: + ensureUniqueObjectName(fw, m_object); + newValue.first = m_propertySheet->property(m_index); + break; + default: + break; + } + + updateObject(fw, oldValue, newValue.first); + return newValue; +} + +PropertyHelper::Value PropertyHelper::restoreOldValue(QDesignerFormWindowInterface *fw) +{ + return applyValue(fw, m_propertySheet->property(m_index), m_oldValue); +} + +// find the default value in widget DB in case PropertySheet::reset fails +QVariant PropertyHelper::findDefaultValue(QDesignerFormWindowInterface *fw) const +{ + if (m_specialProperty == SP_AutoDefault && qobject_cast<const QPushButton*>(m_object)) { + // AutoDefault defaults to true on dialogs + const bool isDialog = qobject_cast<const QDialog *>(fw->mainContainer()); + return QVariant(isDialog); + } + + const int item_idx = fw->core()->widgetDataBase()->indexOfObject(m_object); + if (item_idx == -1) + return m_oldValue.first; // We simply don't know the value in this case + + const QDesignerWidgetDataBaseItemInterface *item = fw->core()->widgetDataBase()->item(item_idx); + const QList<QVariant> default_prop_values = item->defaultPropertyValues(); + if (m_index < default_prop_values.size()) + return default_prop_values.at(m_index); + + if (m_oldValue.first.type() == QVariant::Color) + return QColor(); + + return m_oldValue.first; // Again, we just don't know +} + +PropertyHelper::Value PropertyHelper::restoreDefaultValue(QDesignerFormWindowInterface *fw) +{ + + Value defaultValue = qMakePair(QVariant(), false); + const QVariant currentValue = m_propertySheet->property(m_index); + // try to reset sheet, else try to find default + if (m_propertySheet->reset(m_index)) { + defaultValue.first = m_propertySheet->property(m_index); + } else { + defaultValue.first = findDefaultValue(fw); + m_propertySheet->setProperty(m_index, defaultValue.first); + } + + m_propertySheet->setChanged(m_index, defaultValue.second); + + if (m_objectType == OT_Widget) { + checkApplyWidgetValue(fw, qobject_cast<QWidget *>(m_object), m_specialProperty, defaultValue.first); + } + + switch (m_specialProperty) { + case SP_LayoutName: + case SP_ObjectName: + case SP_SpacerName: + ensureUniqueObjectName(fw, m_object); + defaultValue.first = m_propertySheet->property(m_index); + break; + default: + break; + } + + updateObject(fw, currentValue, defaultValue.first); + return defaultValue; +} + +// ---- PropertyListCommand::PropertyDescription( + + +PropertyListCommand::PropertyDescription::PropertyDescription(const QString &propertyName, + QDesignerPropertySheetExtension *propertySheet, + int index) : + m_propertyName(propertyName), + m_propertyGroup(propertySheet->propertyGroup(index)), + m_propertyType(propertySheet->property(index).type()), + m_specialProperty(getSpecialProperty(propertyName)) +{ +} + +PropertyListCommand::PropertyDescription::PropertyDescription() : + m_propertyType(QVariant::Invalid), + m_specialProperty(SP_None) +{ +} + +void PropertyListCommand::PropertyDescription::debug() const +{ + qDebug() << m_propertyName << m_propertyGroup << m_propertyType << m_specialProperty; +} + +bool PropertyListCommand::PropertyDescription::equals(const PropertyDescription &p) const +{ + return m_propertyType == p.m_propertyType && m_specialProperty == p.m_specialProperty && + m_propertyName == p.m_propertyName && m_propertyGroup == p.m_propertyGroup; +} + + +// ---- PropertyListCommand +PropertyListCommand::PropertyListCommand(QDesignerFormWindowInterface *formWindow) : + QDesignerFormWindowCommand(QString(), formWindow) +{ +} + +const QString PropertyListCommand::propertyName() const +{ + return m_propertyDescription.m_propertyName; +} + +SpecialProperty PropertyListCommand::specialProperty() const +{ + return m_propertyDescription.m_specialProperty; +} + +// add an object +bool PropertyListCommand::add(QObject *object, const QString &propertyName) +{ + QDesignerPropertySheetExtension* sheet = propertySheet(object); + Q_ASSERT(sheet); + + const int index = sheet->indexOf(propertyName); + if (index == -1) + return false; + + if (QDesignerPropertySheet *exSheet = qobject_cast<QDesignerPropertySheet*>(core()->extensionManager()->extension(object, Q_TYPEID(QDesignerPropertySheetExtension)))) + if (!exSheet->isEnabled(index)) + return false; + + const PropertyDescription description(propertyName, sheet, index); + + if (m_propertyHelperList.empty()) { + // first entry + m_propertyDescription = description; + } else { + // checks: mismatch or only one object in case of name + const bool match = m_propertyDescription.equals(description); + if (!match || m_propertyDescription.m_specialProperty == SP_ObjectName) + return false; + } + m_propertyHelperList.push_back(PropertyHelper(object, m_propertyDescription.m_specialProperty, sheet, index)); + return true; +} + + +// Init from a list and make sure referenceObject is added first to obtain the right property group +bool PropertyListCommand::initList(const ObjectList &list, const QString &apropertyName, QObject *referenceObject) +{ + propertyHelperList().clear(); + + // Ensure the referenceObject (property editor) is first, so the right property group is chosen. + if (referenceObject) { + if (!add(referenceObject, apropertyName)) + return false; + } + foreach (QObject *o, list) { + if (o != referenceObject) + add(o, apropertyName); + } + + return !propertyHelperList().empty(); +} + + +QObject* PropertyListCommand::object(int index) const +{ + Q_ASSERT(index < m_propertyHelperList.size()); + return m_propertyHelperList[index].object(); +} + +QVariant PropertyListCommand::oldValue(int index) const +{ + Q_ASSERT(index < m_propertyHelperList.size()); + return m_propertyHelperList[index].oldValue(); +} + +void PropertyListCommand::setOldValue(const QVariant &oldValue, int index) +{ + Q_ASSERT(index < m_propertyHelperList.size()); + m_propertyHelperList[index].setOldValue(oldValue); +} +// ----- SetValueFunction: Set a new value when applied to a PropertyHelper. +class SetValueFunction { +public: + SetValueFunction(QDesignerFormWindowInterface *formWindow, const PropertyHelper::Value &newValue, unsigned subPropertyMask); + + PropertyHelper::Value operator()(PropertyHelper&); +private: + QDesignerFormWindowInterface *m_formWindow; + const PropertyHelper::Value m_newValue; + const unsigned m_subPropertyMask; +}; + + +SetValueFunction::SetValueFunction(QDesignerFormWindowInterface *formWindow, const PropertyHelper::Value &newValue, unsigned subPropertyMask) : + m_formWindow(formWindow), + m_newValue(newValue), + m_subPropertyMask(subPropertyMask) +{ +} + +PropertyHelper::Value SetValueFunction::operator()(PropertyHelper &ph) { + return ph.setValue(m_formWindow, m_newValue.first, m_newValue.second, m_subPropertyMask); +} + +// ----- UndoSetValueFunction: Restore old value when applied to a PropertyHelper. +class UndoSetValueFunction { +public: + UndoSetValueFunction(QDesignerFormWindowInterface *formWindow) : m_formWindow(formWindow) {} + PropertyHelper::Value operator()(PropertyHelper& ph) { return ph.restoreOldValue(m_formWindow); } +private: + QDesignerFormWindowInterface *m_formWindow; +}; + +// ----- RestoreDefaultFunction: Restore default value when applied to a PropertyHelper. +class RestoreDefaultFunction { +public: + RestoreDefaultFunction(QDesignerFormWindowInterface *formWindow) : m_formWindow(formWindow) {} + PropertyHelper::Value operator()(PropertyHelper& ph) { return ph.restoreDefaultValue(m_formWindow); } +private: + QDesignerFormWindowInterface *m_formWindow; +}; + +// ----- changePropertyList: Iterates over a sequence of PropertyHelpers and +// applies a function to them. +// The function returns the corrected value which is then set in the property editor. +// Returns a combination of update flags. +template <class PropertyListIterator, class Function> + unsigned changePropertyList(QDesignerFormEditorInterface *core, + const QString &propertyName, + PropertyListIterator begin, + PropertyListIterator end, + Function function) +{ + unsigned updateMask = 0; + QDesignerPropertyEditorInterface *propertyEditor = core->propertyEditor(); + bool updatedPropertyEditor = false; + + for (PropertyListIterator it = begin; it != end; ++it) { + if (QObject* object = it->object()) { // Might have been deleted in the meantime + const PropertyHelper::Value newValue = function(*it); + updateMask |= it->updateMask(); + // Update property editor if it is the current object + if (!updatedPropertyEditor && propertyEditor && object == propertyEditor->object()) { + propertyEditor->setPropertyValue(propertyName, newValue.first, newValue.second); + updatedPropertyEditor = true; + } + } + } + if (!updatedPropertyEditor) updateMask |= PropertyHelper::UpdatePropertyEditor; + return updateMask; +} + + +// set a new value, return update mask +unsigned PropertyListCommand::setValue(QVariant value, bool changed, unsigned subPropertyMask) +{ + if(debugPropertyCommands) + qDebug() << "PropertyListCommand::setValue(" << value << changed << subPropertyMask << ')'; + return changePropertyList(formWindow()->core(), + m_propertyDescription.m_propertyName, m_propertyHelperList.begin(), m_propertyHelperList.end(), + SetValueFunction(formWindow(), PropertyHelper::Value(value, changed), subPropertyMask)); +} + +// restore old value, return update mask +unsigned PropertyListCommand::restoreOldValue() +{ + if(debugPropertyCommands) + qDebug() << "PropertyListCommand::restoreOldValue()"; + + return changePropertyList(formWindow()->core(), + m_propertyDescription.m_propertyName, m_propertyHelperList.begin(), m_propertyHelperList.end(), + UndoSetValueFunction(formWindow())); +} +// set default value, return update mask +unsigned PropertyListCommand::restoreDefaultValue() +{ + if(debugPropertyCommands) + qDebug() << "PropertyListCommand::restoreDefaultValue()"; + + return changePropertyList(formWindow()->core(), + m_propertyDescription.m_propertyName, m_propertyHelperList.begin(), m_propertyHelperList.end(), + RestoreDefaultFunction(formWindow())); +} + +// update +void PropertyListCommand::update(unsigned updateMask) +{ + if(debugPropertyCommands) + qDebug() << "PropertyListCommand::update(" << updateMask << ')'; + + if (updateMask & PropertyHelper::UpdateObjectInspector) { + if (QDesignerObjectInspectorInterface *oi = formWindow()->core()->objectInspector()) + oi->setFormWindow(formWindow()); + } + + if (updateMask & PropertyHelper::UpdatePropertyEditor) { + // this is needed when f.ex. undo, changes parent's palette, but + // the child is the active widget, + // TODO: current object? + if (QDesignerPropertyEditorInterface *propertyEditor = formWindow()->core()->propertyEditor()) { + propertyEditor->setObject(propertyEditor->object()); + } + } +} + +void PropertyListCommand::undo() +{ + update(restoreOldValue()); + QDesignerPropertyEditor *designerPropertyEditor = qobject_cast<QDesignerPropertyEditor *>(core()->propertyEditor()); + if (designerPropertyEditor) + designerPropertyEditor->updatePropertySheet(); +} + +// check if lists are aequivalent for command merging (same widgets and props) +bool PropertyListCommand::canMergeLists(const PropertyHelperList& other) const +{ + if (m_propertyHelperList.size() != other.size()) + return false; + for (int i = 0; i < m_propertyHelperList.size(); i++) { + if (!m_propertyHelperList[i].canMerge(other[i])) + return false; + } + return true; +} + +// ---- SetPropertyCommand ---- +SetPropertyCommand::SetPropertyCommand(QDesignerFormWindowInterface *formWindow) + : PropertyListCommand(formWindow), + m_subPropertyMask(SubPropertyAll) +{ +} + +bool SetPropertyCommand::init(QObject *object, const QString &apropertyName, const QVariant &newValue) +{ + Q_ASSERT(object); + + m_newValue = newValue; + + propertyHelperList().clear(); + if (!add(object, apropertyName)) + return false; + + setDescription(); + return true; +} + +bool SetPropertyCommand::init(const ObjectList &list, const QString &apropertyName, const QVariant &newValue, + QObject *referenceObject, bool enableSubPropertyHandling) +{ + if (!initList(list, apropertyName, referenceObject)) + return false; + + m_newValue = newValue; + + if(debugPropertyCommands) + qDebug() << "SetPropertyCommand::init()" << propertyHelperList().size() << '/' << list.size() << " reference " << referenceObject; + + setDescription(); + + if (enableSubPropertyHandling) + m_subPropertyMask = subPropertyMask(newValue, referenceObject); + return true; +} + +unsigned SetPropertyCommand::subPropertyMask(const QVariant &newValue, QObject *referenceObject) +{ + // figure out the mask of changed sub properties when comparing newValue to the current value of the reference object. + if (!referenceObject) + return SubPropertyAll; + + QDesignerPropertySheetExtension* sheet = propertySheet(referenceObject); + Q_ASSERT(sheet); + + const int index = sheet->indexOf(propertyName()); + if (index == -1 || !sheet->isVisible(index)) + return SubPropertyAll; + + return compareSubProperties(sheet->property(index), newValue, specialProperty()); +} + +void SetPropertyCommand::setDescription() +{ + if (propertyHelperList().size() == 1) { + setText(QApplication::translate("Command", "Changed '%1' of '%2'").arg(propertyName()).arg(propertyHelperList()[0].object()->objectName())); + } else { + int count = propertyHelperList().size(); + setText(QApplication::translate("Command", "Changed '%1' of %n objects", "", QCoreApplication::UnicodeUTF8, count).arg(propertyName())); + } +} + +void SetPropertyCommand::redo() +{ + update(setValue(m_newValue, true, m_subPropertyMask)); + QDesignerPropertyEditor *designerPropertyEditor = qobject_cast<QDesignerPropertyEditor *>(core()->propertyEditor()); + if (designerPropertyEditor) + designerPropertyEditor->updatePropertySheet(); +} + + +int SetPropertyCommand::id() const +{ + return 1976; +} + +bool SetPropertyCommand::mergeWith(const QUndoCommand *other) +{ + if (id() != other->id() || !formWindow()->isDirty()) + return false; + + // Merging: When for example when the user types ahead in an inplace-editor, + // it makes sense to merge all the generated commands containing the one-character changes. + // In the case of subproperties, if the user changes the font size from 10 to 30 via 20 + // and then changes to bold, it makes sense to merge the font size commands only. + // This is why the m_subPropertyMask is checked. + + const SetPropertyCommand *cmd = static_cast<const SetPropertyCommand*>(other); + if (!propertyDescription().equals(cmd->propertyDescription()) || + m_subPropertyMask != cmd->m_subPropertyMask || + !canMergeLists(cmd->propertyHelperList())) + return false; + + m_newValue = cmd->newValue(); + m_subPropertyMask |= cmd->m_subPropertyMask; + if(debugPropertyCommands) + qDebug() << "SetPropertyCommand::mergeWith() succeeded " << propertyName(); + + return true; +} + +// ---- ResetPropertyCommand ---- +ResetPropertyCommand::ResetPropertyCommand(QDesignerFormWindowInterface *formWindow) + : PropertyListCommand(formWindow) +{ +} + +bool ResetPropertyCommand::init(QObject *object, const QString &apropertyName) +{ + Q_ASSERT(object); + + propertyHelperList().clear(); + if (!add(object, apropertyName)) + return false; + + setDescription(); + return true; +} + +bool ResetPropertyCommand::init(const ObjectList &list, const QString &apropertyName, QObject *referenceObject) +{ + if (!initList(list, apropertyName, referenceObject)) + return false; + + if(debugPropertyCommands) + qDebug() << "ResetPropertyCommand::init()" << propertyHelperList().size() << '/' << list.size(); + + setDescription(); + return true; +} + +void ResetPropertyCommand::setDescription() +{ + if (propertyHelperList().size() == 1) { + setText(QApplication::translate("Command", "Reset '%1' of '%2'").arg(propertyName()).arg(propertyHelperList()[0].object()->objectName())); + } else { + int count = propertyHelperList().size(); + setText(QApplication::translate("Command", "Reset '%1' of %n objects", "", QCoreApplication::UnicodeUTF8, count).arg(propertyName())); + } +} + +void ResetPropertyCommand::redo() +{ + update(restoreDefaultValue()); + QDesignerPropertyEditor *designerPropertyEditor = qobject_cast<QDesignerPropertyEditor *>(core()->propertyEditor()); + if (designerPropertyEditor) + designerPropertyEditor->updatePropertySheet(); +} + +AddDynamicPropertyCommand::AddDynamicPropertyCommand(QDesignerFormWindowInterface *formWindow) + : QDesignerFormWindowCommand(QString(), formWindow) +{ + +} + +bool AddDynamicPropertyCommand::init(const QList<QObject *> &selection, QObject *current, + const QString &propertyName, const QVariant &value) +{ + Q_ASSERT(current); + m_propertyName = propertyName; + + QDesignerFormEditorInterface *core = formWindow()->core(); + QDesignerDynamicPropertySheetExtension *dynamicSheet = qt_extension<QDesignerDynamicPropertySheetExtension*>(core->extensionManager(), current); + Q_ASSERT(dynamicSheet); + + m_selection.clear(); + + if (!value.isValid()) + return false; + + if (!dynamicSheet->canAddDynamicProperty(m_propertyName)) + return false; + + m_selection.append(current); + + m_value = value; + + QListIterator<QObject *> it(selection); + while (it.hasNext()) { + QObject *obj = it.next(); + if (m_selection.contains(obj)) + continue; + dynamicSheet = qt_extension<QDesignerDynamicPropertySheetExtension*>(core->extensionManager(), obj); + Q_ASSERT(dynamicSheet); + if (dynamicSheet->canAddDynamicProperty(m_propertyName)) + m_selection.append(obj); + } + + setDescription(); + return true; +} + +void AddDynamicPropertyCommand::redo() +{ + QDesignerFormEditorInterface *core = formWindow()->core(); + QListIterator<QObject *> it(m_selection); + while (it.hasNext()) { + QObject *obj = it.next(); + QDesignerDynamicPropertySheetExtension *dynamicSheet = qt_extension<QDesignerDynamicPropertySheetExtension*>(core->extensionManager(), obj); + dynamicSheet->addDynamicProperty(m_propertyName, m_value); + if (QDesignerPropertyEditorInterface *propertyEditor = formWindow()->core()->propertyEditor()) { + if (propertyEditor->object() == obj) + propertyEditor->setObject(obj); + } + } +} + +void AddDynamicPropertyCommand::undo() +{ + QDesignerFormEditorInterface *core = formWindow()->core(); + QListIterator<QObject *> it(m_selection); + while (it.hasNext()) { + QObject *obj = it.next(); + QDesignerPropertySheetExtension *sheet = qt_extension<QDesignerPropertySheetExtension*>(core->extensionManager(), obj); + QDesignerDynamicPropertySheetExtension *dynamicSheet = qt_extension<QDesignerDynamicPropertySheetExtension*>(core->extensionManager(), obj); + dynamicSheet->removeDynamicProperty(sheet->indexOf(m_propertyName)); + if (QDesignerPropertyEditorInterface *propertyEditor = formWindow()->core()->propertyEditor()) { + if (propertyEditor->object() == obj) + propertyEditor->setObject(obj); + } + } +} + +void AddDynamicPropertyCommand::setDescription() +{ + if (m_selection.size() == 1) { + setText(QApplication::translate("Command", "Add dynamic property '%1' to '%2'").arg(m_propertyName).arg(m_selection.first()->objectName())); + } else { + int count = m_selection.size(); + setText(QApplication::translate("Command", "Add dynamic property '%1' to %n objects", "", QCoreApplication::UnicodeUTF8, count).arg(m_propertyName)); + } +} + + +RemoveDynamicPropertyCommand::RemoveDynamicPropertyCommand(QDesignerFormWindowInterface *formWindow) + : QDesignerFormWindowCommand(QString(), formWindow) +{ + +} + +bool RemoveDynamicPropertyCommand::init(const QList<QObject *> &selection, QObject *current, + const QString &propertyName) +{ + Q_ASSERT(current); + m_propertyName = propertyName; + + QDesignerFormEditorInterface *core = formWindow()->core(); + QDesignerPropertySheetExtension *propertySheet = qt_extension<QDesignerPropertySheetExtension*>(core->extensionManager(), current); + Q_ASSERT(propertySheet); + QDesignerDynamicPropertySheetExtension *dynamicSheet = qt_extension<QDesignerDynamicPropertySheetExtension*>(core->extensionManager(), current); + Q_ASSERT(dynamicSheet); + + m_objectToValueAndChanged.clear(); + + const int index = propertySheet->indexOf(m_propertyName); + if (!dynamicSheet->isDynamicProperty(index)) + return false; + + m_objectToValueAndChanged[current] = qMakePair(propertySheet->property(index), propertySheet->isChanged(index)); + + QListIterator<QObject *> it(selection); + while (it.hasNext()) { + QObject *obj = it.next(); + if (m_objectToValueAndChanged.contains(obj)) + continue; + + propertySheet = qt_extension<QDesignerPropertySheetExtension*>(core->extensionManager(), obj); + dynamicSheet = qt_extension<QDesignerDynamicPropertySheetExtension*>(core->extensionManager(), obj); + const int idx = propertySheet->indexOf(m_propertyName); + if (dynamicSheet->isDynamicProperty(idx)) + m_objectToValueAndChanged[obj] = qMakePair(propertySheet->property(idx), propertySheet->isChanged(idx)); + } + + setDescription(); + return true; +} + +void RemoveDynamicPropertyCommand::redo() +{ + QDesignerFormEditorInterface *core = formWindow()->core(); + QMap<QObject *, QPair<QVariant, bool> >::ConstIterator it = m_objectToValueAndChanged.constBegin(); + while (it != m_objectToValueAndChanged.constEnd()) { + QObject *obj = it.key(); + QDesignerDynamicPropertySheetExtension *dynamicSheet = qt_extension<QDesignerDynamicPropertySheetExtension*>(core->extensionManager(), obj); + QDesignerPropertySheetExtension *sheet = qt_extension<QDesignerPropertySheetExtension*>(core->extensionManager(), obj); + dynamicSheet->removeDynamicProperty(sheet->indexOf(m_propertyName)); + if (QDesignerPropertyEditorInterface *propertyEditor = formWindow()->core()->propertyEditor()) { + if (propertyEditor->object() == obj) + propertyEditor->setObject(obj); + } + ++it; + } +} + +void RemoveDynamicPropertyCommand::undo() +{ + QDesignerFormEditorInterface *core = formWindow()->core(); + QMap<QObject *, QPair<QVariant, bool> >::ConstIterator it = m_objectToValueAndChanged.constBegin(); + while (it != m_objectToValueAndChanged.constEnd()) { + QObject *obj = it.key(); + QDesignerPropertySheetExtension *propertySheet = qt_extension<QDesignerPropertySheetExtension*>(core->extensionManager(), obj); + QDesignerDynamicPropertySheetExtension *dynamicSheet = qt_extension<QDesignerDynamicPropertySheetExtension*>(core->extensionManager(), obj); + const int index = dynamicSheet->addDynamicProperty(m_propertyName, it.value().first); + propertySheet->setChanged(index, it.value().second); + if (QDesignerPropertyEditorInterface *propertyEditor = formWindow()->core()->propertyEditor()) { + if (propertyEditor->object() == obj) + propertyEditor->setObject(obj); + } + ++it; + } +} + +void RemoveDynamicPropertyCommand::setDescription() +{ + if (m_objectToValueAndChanged.size() == 1) { + setText(QApplication::translate("Command", "Remove dynamic property '%1' from '%2'").arg(m_propertyName).arg(m_objectToValueAndChanged.constBegin().key()->objectName())); + } else { + int count = m_objectToValueAndChanged.size(); + setText(QApplication::translate("Command", "Remove dynamic property '%1' from %n objects", "", QCoreApplication::UnicodeUTF8, count).arg(m_propertyName)); + } +} + + +} // namespace qdesigner_internal + +QT_END_NAMESPACE |