1 /****************************************************************************
2 **
3 ** Copyright (C) 2015 The Qt Company Ltd.
4 ** Contact: http://www.qt.io/licensing/
5 **
6 ** This file is part of the Qt Designer of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see http://www.qt.io/terms-conditions. For further
15 ** information use the contact form at http://www.qt.io/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 or version 3 as published by the Free
20 ** Software Foundation and appearing in the file LICENSE.LGPLv21 and
21 ** LICENSE.LGPLv3 included in the packaging of this file. Please review the
22 ** following information to ensure the GNU Lesser General Public License
23 ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
24 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25 **
26 ** As a special exception, The Qt Company gives you certain additional
27 ** rights. These rights are described in The Qt Company LGPL Exception
28 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29 **
30 ** GNU General Public License Usage
31 ** Alternatively, this file may be used under the terms of the GNU
32 ** General Public License version 3.0 as published by the Free Software
33 ** Foundation and appearing in the file LICENSE.GPL included in the
34 ** packaging of this file.  Please review the following information to
35 ** ensure the GNU General Public License version 3.0 requirements will be
36 ** met: http://www.gnu.org/copyleft/gpl.html.
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41 
42 #include "qdesigner_propertycommand_p.h"
43 #include "qdesigner_utils_p.h"
44 #include "dynamicpropertysheet.h"
45 #include "qdesigner_propertyeditor_p.h"
46 #include "qdesigner_integration_p.h"
47 #include "spacer_widget_p.h"
48 #include "qdesigner_propertysheet_p.h"
49 
50 #include <QtDesigner/QDesignerFormEditorInterface>
51 #include <QtDesigner/QDesignerFormWindowInterface>
52 #include <QtDesigner/QDesignerFormWindowCursorInterface>
53 #include <QtDesigner/QDesignerDynamicPropertySheetExtension>
54 #include <QtDesigner/QDesignerPropertySheetExtension>
55 #include <QtDesigner/QDesignerPropertyEditorInterface>
56 #include <QtDesigner/QDesignerObjectInspectorInterface>
57 #include <QtDesigner/QDesignerIntegrationInterface>
58 #include <QtDesigner/QDesignerWidgetDataBaseInterface>
59 #include <QtDesigner/QExtensionManager>
60 
61 #include <QtCore/QSize>
62 #include <QtCore/QTextStream>
63 #include <QtGui/QWidget>
64 #include <QtGui/QApplication>
65 #include <QtGui/QAction>
66 #include <QtGui/QDialog>
67 #include <QtGui/QPushButton>
68 #include <QtGui/QLayout>
69 #include <qdebug.h>
70 
71 QT_BEGIN_NAMESPACE
72 
73 namespace  {
74 enum { debugPropertyCommands = 0 };
75 
76 // Debug resolve mask of font
fontMask(unsigned m)77 QString fontMask(unsigned m)
78 {
79     QString rc;
80     if (m & QFont::FamilyResolved)
81         rc += QLatin1String("Family");
82     if (m & QFont::SizeResolved)
83         rc += QLatin1String("Size ");
84     if (m & QFont::WeightResolved)
85         rc += QLatin1String("Bold ");
86     if (m & QFont::StyleResolved)
87         rc += QLatin1String("Style ");
88     if (m & QFont::UnderlineResolved)
89         rc += QLatin1String("Underline ");
90     if (m & QFont::StrikeOutResolved)
91         rc += QLatin1String("StrikeOut ");
92     if (m & QFont::KerningResolved)
93         rc += QLatin1String("Kerning ");
94     if (m & QFont::StyleStrategyResolved)
95         rc += QLatin1String("StyleStrategy");
96     return rc;
97 }
98 
99 // Debug font
fontString(const QFont & f)100 QString fontString(const QFont &f)
101 {
102     QString rc; {
103         const QChar comma = QLatin1Char(',');
104         QTextStream str(&rc);
105         str << QLatin1String("QFont(\"") <<  f.family() << comma <<
106             f.pointSize();
107         if (f.bold())
108             str << comma <<  QLatin1String("bold");
109         if (f.italic())
110             str << comma <<  QLatin1String("italic");
111         if (f.underline())
112             str << comma <<  QLatin1String("underline");
113         if (f.strikeOut())
114             str << comma <<  QLatin1String("strikeOut");
115         if (f.kerning())
116             str << comma << QLatin1String("kerning");
117         str <<  comma << f.styleStrategy() << QLatin1String(" resolve: ")
118             << fontMask(f.resolve()) << QLatin1Char(')');
119     }
120     return rc;
121 }
checkSize(const QSize & size)122 QSize checkSize(const QSize &size)
123 {
124     return size.boundedTo(QSize(0xFFFFFF, 0xFFFFFF));
125 }
126 
diffSize(QDesignerFormWindowInterface * fw)127 QSize diffSize(QDesignerFormWindowInterface *fw)
128 {
129     const QWidget *container = fw->core()->integration()->containerWindow(fw);
130     if (!container)
131         return QSize();
132 
133     const QSize diff = container->size() - fw->size(); // decoration offset of container window
134     return diff;
135 }
136 
checkSizes(QDesignerFormWindowInterface * fw,const QSize & size,QSize * formSize,QSize * containerSize)137 void checkSizes(QDesignerFormWindowInterface *fw, const QSize &size, QSize *formSize, QSize *containerSize)
138 {
139     const QWidget *container = fw->core()->integration()->containerWindow(fw);
140     if (!container)
141         return;
142 
143     const  QSize diff = diffSize(fw); // decoration offset of container window
144 
145     QSize newFormSize = checkSize(size).expandedTo(fw->mainContainer()->minimumSizeHint()); // don't try to resize to smaller size than minimumSizeHint
146     QSize newContainerSize = newFormSize + diff;
147 
148     newContainerSize = newContainerSize.expandedTo(container->minimumSizeHint());
149     newContainerSize = newContainerSize.expandedTo(container->minimumSize());
150 
151     newFormSize = newContainerSize - diff;
152 
153     newContainerSize = checkSize(newContainerSize);
154 
155     if (formSize)
156         *formSize = newFormSize;
157     if (containerSize)
158         *containerSize = newContainerSize;
159 }
160 
161 /* SubProperties: When applying a changed property to a multiselection, it sometimes makes
162  * sense to apply only parts (subproperties) of the property.
163  * For example, if someone changes the x-value of a geometry in the property editor
164  * and applies it to a multi-selection, y should not be applied as this would cause all
165  * the widgets to overlap.
166  * The following routines can be used to find out the changed subproperties of a property,
167  * which are represented as a mask, and to apply them while leaving the others intact. */
168 
169 enum RectSubPropertyMask {  SubPropertyX=1, SubPropertyY = 2, SubPropertyWidth = 4, SubPropertyHeight = 8 };
170 enum SizePolicySubPropertyMask { SubPropertyHSizePolicy = 1, SubPropertyHStretch = 2, SubPropertyVSizePolicy = 4, SubPropertyVStretch = 8 };
171 enum AlignmentSubPropertyMask { SubPropertyHorizontalAlignment = 1, SubPropertyVerticalAlignment = 2 };
172 enum StringSubPropertyMask { SubPropertyStringValue = 1, SubPropertyStringComment = 2, SubPropertyStringTranslatable = 4, SubPropertyStringDisambiguation = 8 };
173 enum KeySequenceSubPropertyMask { SubPropertyKeySequenceValue = 1, SubPropertyKeySequenceComment = 2, SubPropertyKeySequenceTranslatable = 4, SubPropertyKeySequenceDisambiguation = 8 };
174 
175 enum CommonSubPropertyMask { SubPropertyAll = 0xFFFFFFFF };
176 
177 // Set the mask flag in mask if the properties do not match.
178 #define COMPARE_SUBPROPERTY(object1, object2, getter, mask, maskFlag) if (object1.getter() != object2.getter()) mask |= maskFlag;
179 
180 // find changed subproperties of a rectangle
compareSubProperties(const QRect & r1,const QRect & r2)181 unsigned compareSubProperties(const QRect & r1, const QRect & r2)
182 {
183     unsigned rc = 0;
184     COMPARE_SUBPROPERTY(r1, r2, x, rc, SubPropertyX)
185     COMPARE_SUBPROPERTY(r1, r2, y, rc, SubPropertyY)
186     COMPARE_SUBPROPERTY(r1, r2, width, rc, SubPropertyWidth)
187     COMPARE_SUBPROPERTY(r1, r2, height, rc, SubPropertyHeight)
188     return rc;
189 }
190 
191 // find changed subproperties of a QSize
compareSubProperties(const QSize & r1,const QSize & r2)192 unsigned compareSubProperties(const QSize & r1, const QSize & r2)
193 {
194     unsigned rc = 0;
195     COMPARE_SUBPROPERTY(r1, r2, width,  rc, SubPropertyWidth)
196     COMPARE_SUBPROPERTY(r1, r2, height, rc, SubPropertyHeight)
197     return rc;
198 }
199 // find changed subproperties of a QSizePolicy
compareSubProperties(const QSizePolicy & sp1,const QSizePolicy & sp2)200 unsigned compareSubProperties(const QSizePolicy & sp1, const QSizePolicy & sp2)
201 {
202     unsigned rc = 0;
203     COMPARE_SUBPROPERTY(sp1, sp2, horizontalPolicy,  rc, SubPropertyHSizePolicy)
204     COMPARE_SUBPROPERTY(sp1, sp2, horizontalStretch, rc, SubPropertyHStretch)
205     COMPARE_SUBPROPERTY(sp1, sp2, verticalPolicy,    rc, SubPropertyVSizePolicy)
206     COMPARE_SUBPROPERTY(sp1, sp2, verticalStretch,   rc, SubPropertyVStretch)
207     return rc;
208 }
209 // find changed subproperties of qdesigner_internal::PropertySheetStringValue
compareSubProperties(const qdesigner_internal::PropertySheetStringValue & str1,const qdesigner_internal::PropertySheetStringValue & str2)210 unsigned compareSubProperties(const qdesigner_internal::PropertySheetStringValue & str1, const qdesigner_internal::PropertySheetStringValue & str2)
211 {
212     unsigned rc = 0;
213     COMPARE_SUBPROPERTY(str1, str2, value,          rc, SubPropertyStringValue)
214     COMPARE_SUBPROPERTY(str1, str2, comment,        rc, SubPropertyStringComment)
215     COMPARE_SUBPROPERTY(str1, str2, translatable,   rc, SubPropertyStringTranslatable)
216     COMPARE_SUBPROPERTY(str1, str2, disambiguation, rc, SubPropertyStringDisambiguation)
217     return rc;
218 }
219 // find changed subproperties of qdesigner_internal::PropertySheetKeySequenceValue
compareSubProperties(const qdesigner_internal::PropertySheetKeySequenceValue & str1,const qdesigner_internal::PropertySheetKeySequenceValue & str2)220 unsigned compareSubProperties(const qdesigner_internal::PropertySheetKeySequenceValue & str1, const qdesigner_internal::PropertySheetKeySequenceValue & str2)
221 {
222     unsigned rc = 0;
223     COMPARE_SUBPROPERTY(str1, str2, value,          rc, SubPropertyKeySequenceValue)
224     COMPARE_SUBPROPERTY(str1, str2, comment,        rc, SubPropertyKeySequenceComment)
225     COMPARE_SUBPROPERTY(str1, str2, translatable,   rc, SubPropertyKeySequenceTranslatable)
226     COMPARE_SUBPROPERTY(str1, str2, disambiguation, rc, SubPropertyKeySequenceDisambiguation)
227     return rc;
228 }
229 
230 // Compare font-subproperties taking the [undocumented]
231 // resolve flag into account
232 template <class Property>
compareFontSubProperty(const QFont & f1,const QFont & f2,Property (QFont::* getter)()const,unsigned maskBit,unsigned & mask)233 void compareFontSubProperty(const QFont & f1,
234                             const QFont & f2,
235                             Property (QFont::*getter) () const,
236                             unsigned maskBit,
237                             unsigned &mask)
238 {
239     const bool f1Changed = f1.resolve() & maskBit;
240     const bool f2Changed = f2.resolve() & maskBit;
241     // Role has been set/reset in editor
242     if (f1Changed != f2Changed) {
243         mask |= maskBit;
244     } else {
245         // Was modified in both palettes: Compare values.
246         if (f1Changed && f2Changed && (f1.*getter)() != (f2.*getter)())
247             mask |= maskBit;
248     }
249 }
250 // find changed subproperties of a QFont
compareSubProperties(const QFont & f1,const QFont & f2)251 unsigned compareSubProperties(const QFont & f1, const QFont & f2)
252 {
253     unsigned rc = 0;
254     compareFontSubProperty(f1, f2, &QFont::family,        QFont::FamilyResolved, rc);
255     compareFontSubProperty(f1, f2, &QFont::pointSize,     QFont::SizeResolved, rc);
256     compareFontSubProperty(f1, f2, &QFont::bold,          QFont::WeightResolved, rc);
257     compareFontSubProperty(f1, f2, &QFont::italic,        QFont::StyleResolved, rc);
258     compareFontSubProperty(f1, f2, &QFont::underline,     QFont::UnderlineResolved, rc);
259     compareFontSubProperty(f1, f2, &QFont::strikeOut,     QFont::StrikeOutResolved, rc);
260     compareFontSubProperty(f1, f2, &QFont::kerning,       QFont::KerningResolved, rc);
261     compareFontSubProperty(f1, f2, &QFont::styleStrategy, QFont::StyleStrategyResolved, rc);
262     if (debugPropertyCommands)
263         qDebug() << "compareSubProperties " <<  fontString(f1) << fontString(f2) << "\n\treturns " << fontMask(rc);
264     return rc;
265 }
266 
267 // Compare colors of a role
roleColorChanged(const QPalette & p1,const QPalette & p2,QPalette::ColorRole role)268 bool roleColorChanged(const QPalette & p1, const QPalette & p2, QPalette::ColorRole role)
269 {
270     for (int group = QPalette::Active; group < QPalette::NColorGroups;  group++) {
271         const QPalette::ColorGroup pgroup = static_cast<QPalette::ColorGroup>(group);
272         if (p1.color(pgroup, role) != p2.color(pgroup, role))
273             return true;
274     }
275     return false;
276 }
277 // find changed subproperties of a QPalette taking the [undocumented] resolve flags into account
compareSubProperties(const QPalette & p1,const QPalette & p2)278 unsigned compareSubProperties(const QPalette & p1, const QPalette & p2)
279 {
280     unsigned rc = 0;
281     unsigned maskBit = 1u;
282     // generate a mask for each role
283     const unsigned p1Changed = p1.resolve();
284     const unsigned p2Changed = p2.resolve();
285     for (int role = QPalette::WindowText;  role < QPalette::NColorRoles; role++, maskBit <<= 1u) {
286         const bool p1RoleChanged = p1Changed & maskBit;
287         const bool p2RoleChanged = p2Changed & maskBit;
288         // Role has been set/reset in editor
289         if (p1RoleChanged != p2RoleChanged) {
290             rc |= maskBit;
291         } else {
292             // Was modified in both palettes: Compare values.
293             if (p1RoleChanged && p2RoleChanged && roleColorChanged(p1, p2, static_cast<QPalette::ColorRole>(role)))
294                 rc |= maskBit;
295         }
296     }
297     return rc;
298 }
299 
300 // find changed subproperties of a QAlignment which is a flag combination of vertical and horizontal
301 
compareSubProperties(Qt::Alignment a1,Qt::Alignment a2)302 unsigned compareSubProperties(Qt::Alignment a1, Qt::Alignment a2)
303 {
304     unsigned rc = 0;
305     if ((a1 & Qt::AlignHorizontal_Mask) != (a2 & Qt::AlignHorizontal_Mask))
306         rc |= SubPropertyHorizontalAlignment;
307     if ((a1 & Qt::AlignVertical_Mask) != (a2 & Qt::AlignVertical_Mask))
308         rc |= SubPropertyVerticalAlignment;
309     return rc;
310 }
311 
variantToAlignment(const QVariant & q)312 Qt::Alignment variantToAlignment(const QVariant & q)
313 {
314     return Qt::Alignment(qdesigner_internal::Utils::valueOf(q));
315 }
316 // find changed subproperties of a variant
compareSubProperties(const QVariant & q1,const QVariant & q2,qdesigner_internal::SpecialProperty specialProperty)317 unsigned compareSubProperties(const QVariant & q1, const QVariant & q2, qdesigner_internal::SpecialProperty specialProperty)
318 {
319     // Do not clobber new value in the comparison function in
320     // case someone sets a QString on a PropertySheetStringValue.
321     if (q1.type() != q2.type())
322         return SubPropertyAll;
323     switch (q1.type()) {
324     case QVariant::Rect:
325         return compareSubProperties(q1.toRect(), q2.toRect());
326     case QVariant::Size:
327         return compareSubProperties(q1.toSize(), q2.toSize());
328     case QVariant::SizePolicy:
329         return compareSubProperties(qvariant_cast<QSizePolicy>(q1), qvariant_cast<QSizePolicy>(q2));
330     case QVariant::Font:
331         return compareSubProperties(qvariant_cast<QFont>(q1), qvariant_cast<QFont>(q2));
332     case QVariant::Palette:
333         return compareSubProperties(qvariant_cast<QPalette>(q1), qvariant_cast<QPalette>(q2));
334     default:
335         if (q1.userType() == qMetaTypeId<qdesigner_internal::PropertySheetIconValue>())
336             return qvariant_cast<qdesigner_internal::PropertySheetIconValue>(q1).compare(qvariant_cast<qdesigner_internal::PropertySheetIconValue>(q2));
337         else if (q1.userType() == qMetaTypeId<qdesigner_internal::PropertySheetStringValue>())
338             return compareSubProperties(qvariant_cast<qdesigner_internal::PropertySheetStringValue>(q1), qvariant_cast<qdesigner_internal::PropertySheetStringValue>(q2));
339         else if (q1.userType() == qMetaTypeId<qdesigner_internal::PropertySheetKeySequenceValue>())
340             return compareSubProperties(qvariant_cast<qdesigner_internal::PropertySheetKeySequenceValue>(q1), qvariant_cast<qdesigner_internal::PropertySheetKeySequenceValue>(q2));
341         // Enumerations, flags
342         switch (specialProperty) {
343         case qdesigner_internal::SP_Alignment:
344             return compareSubProperties(variantToAlignment(q1), variantToAlignment(q2));
345         default:
346         break;
347         }
348         break;
349     }
350     return SubPropertyAll;
351 }
352 
353 // Apply  the sub property if mask flag is set in mask
354 #define SET_SUBPROPERTY(rc, newValue, getter, setter, mask, maskFlag) if (mask & maskFlag) rc.setter(newValue.getter());
355 
356 // apply changed subproperties to a rectangle
applyRectSubProperty(const QRect & oldValue,const QRect & newValue,unsigned mask)357 QRect applyRectSubProperty(const QRect &oldValue, const QRect &newValue, unsigned mask)
358 {
359     QRect rc = oldValue;
360     SET_SUBPROPERTY(rc, newValue, x,      moveLeft,  mask, SubPropertyX)
361     SET_SUBPROPERTY(rc, newValue, y,      moveTop,   mask, SubPropertyY)
362     SET_SUBPROPERTY(rc, newValue, width,  setWidth,  mask, SubPropertyWidth)
363     SET_SUBPROPERTY(rc, newValue, height, setHeight, mask, SubPropertyHeight)
364     return rc;
365 }
366 
367 
368 // apply changed subproperties to a rectangle QSize
applySizeSubProperty(const QSize & oldValue,const QSize & newValue,unsigned mask)369 QSize applySizeSubProperty(const QSize &oldValue, const QSize &newValue, unsigned mask)
370 {
371     QSize rc = oldValue;
372     SET_SUBPROPERTY(rc, newValue, width,  setWidth,  mask, SubPropertyWidth)
373     SET_SUBPROPERTY(rc, newValue, height, setHeight, mask, SubPropertyHeight)
374     return rc;
375 }
376 
377 
378 // apply changed subproperties to a SizePolicy
applySizePolicySubProperty(const QSizePolicy & oldValue,const QSizePolicy & newValue,unsigned mask)379 QSizePolicy applySizePolicySubProperty(const QSizePolicy &oldValue, const QSizePolicy &newValue, unsigned mask)
380 {
381     QSizePolicy rc = oldValue;
382     SET_SUBPROPERTY(rc, newValue, horizontalPolicy,  setHorizontalPolicy,  mask, SubPropertyHSizePolicy)
383     SET_SUBPROPERTY(rc, newValue, horizontalStretch, setHorizontalStretch, mask, SubPropertyHStretch)
384     SET_SUBPROPERTY(rc, newValue, verticalPolicy,    setVerticalPolicy,    mask, SubPropertyVSizePolicy)
385     SET_SUBPROPERTY(rc, newValue, verticalStretch,   setVerticalStretch,   mask, SubPropertyVStretch)
386     return rc;
387 }
388 
389 // apply changed subproperties to a qdesigner_internal::PropertySheetStringValue
applyStringSubProperty(const qdesigner_internal::PropertySheetStringValue & oldValue,const qdesigner_internal::PropertySheetStringValue & newValue,unsigned mask)390 qdesigner_internal::PropertySheetStringValue applyStringSubProperty(const qdesigner_internal::PropertySheetStringValue &oldValue,
391             const qdesigner_internal::PropertySheetStringValue &newValue, unsigned mask)
392 {
393     qdesigner_internal::PropertySheetStringValue rc = oldValue;
394     SET_SUBPROPERTY(rc, newValue, value, setValue, mask, SubPropertyStringValue)
395     SET_SUBPROPERTY(rc, newValue, comment, setComment, mask, SubPropertyStringComment)
396     SET_SUBPROPERTY(rc, newValue, translatable, setTranslatable, mask, SubPropertyStringTranslatable)
397     SET_SUBPROPERTY(rc, newValue, disambiguation, setDisambiguation, mask, SubPropertyStringDisambiguation)
398     return rc;
399 }
400 
401 // apply changed subproperties to a qdesigner_internal::PropertySheetKeySequenceValue
applyKeySequenceSubProperty(const qdesigner_internal::PropertySheetKeySequenceValue & oldValue,const qdesigner_internal::PropertySheetKeySequenceValue & newValue,unsigned mask)402 qdesigner_internal::PropertySheetKeySequenceValue applyKeySequenceSubProperty(const qdesigner_internal::PropertySheetKeySequenceValue &oldValue,
403             const qdesigner_internal::PropertySheetKeySequenceValue &newValue, unsigned mask)
404 {
405     qdesigner_internal::PropertySheetKeySequenceValue rc = oldValue;
406     SET_SUBPROPERTY(rc, newValue, value, setValue, mask, SubPropertyKeySequenceValue)
407     SET_SUBPROPERTY(rc, newValue, comment, setComment, mask, SubPropertyKeySequenceComment)
408     SET_SUBPROPERTY(rc, newValue, translatable, setTranslatable, mask, SubPropertyKeySequenceTranslatable)
409     SET_SUBPROPERTY(rc, newValue, disambiguation, setDisambiguation, mask, SubPropertyKeySequenceDisambiguation)
410     return rc;
411 }
412 
413 // Apply the font-subproperties keeping the [undocumented]
414 // resolve flag in sync (note that PropertySetterType might be something like const T&).
415 template <class PropertyReturnType, class PropertySetterType>
setFontSubProperty(unsigned mask,const QFont & newValue,unsigned maskBit,PropertyReturnType (QFont::* getter)()const,void (QFont::* setter)(PropertySetterType),QFont & value)416 inline void setFontSubProperty(unsigned mask,
417                                const QFont &newValue,
418                                unsigned maskBit,
419                                PropertyReturnType (QFont::*getter) () const,
420                                void (QFont::*setter) (PropertySetterType),
421                                QFont &value)
422 {
423     if (mask & maskBit) {
424         (value.*setter)((newValue.*getter)());
425         // Set the resolve bit from NewValue in return value
426         uint r = value.resolve();
427         const bool origFlag = newValue.resolve() & maskBit;
428         if (origFlag)
429             r |= maskBit;
430         else
431             r &= ~maskBit;
432         value.resolve(r);
433         if (debugPropertyCommands)
434             qDebug() << "setFontSubProperty " <<  fontMask(maskBit) << " resolve=" << origFlag;
435     }
436 }
437 // apply changed subproperties to a QFont
applyFontSubProperty(const QFont & oldValue,const QFont & newValue,unsigned mask)438 QFont applyFontSubProperty(const QFont &oldValue, const QFont &newValue, unsigned mask)
439 {
440     QFont  rc = oldValue;
441     setFontSubProperty(mask, newValue, QFont::FamilyResolved,        &QFont::family,        &QFont::setFamily, rc);
442     setFontSubProperty(mask, newValue, QFont::SizeResolved,          &QFont::pointSize,     &QFont::setPointSize, rc);
443     setFontSubProperty(mask, newValue, QFont::WeightResolved,        &QFont::bold,          &QFont::setBold, rc);
444     setFontSubProperty(mask, newValue, QFont::StyleResolved,         &QFont::italic,        &QFont::setItalic, rc);
445     setFontSubProperty(mask, newValue, QFont::UnderlineResolved,     &QFont::underline,     &QFont::setUnderline, rc);
446     setFontSubProperty(mask, newValue, QFont::StrikeOutResolved,     &QFont::strikeOut,     &QFont::setStrikeOut, rc);
447     setFontSubProperty(mask, newValue, QFont::KerningResolved,       &QFont::kerning,       &QFont::setKerning, rc);
448     setFontSubProperty(mask, newValue, QFont::StyleStrategyResolved, &QFont::styleStrategy, &QFont::setStyleStrategy, rc);
449     if (debugPropertyCommands)
450         qDebug() << "applyFontSubProperty old " <<  fontMask(oldValue.resolve()) << " new " << fontMask(newValue.resolve()) << " return: " << fontMask(rc.resolve());
451     return rc;
452 }
453 
454 // apply changed subproperties to a QPalette
applyPaletteSubProperty(const QPalette & oldValue,const QPalette & newValue,unsigned mask)455 QPalette applyPaletteSubProperty(const QPalette &oldValue, const QPalette &newValue, unsigned mask)
456 {
457     QPalette rc = oldValue;
458     // apply a mask for each role
459     unsigned maskBit = 1u;
460     for (int role = QPalette::WindowText;  role < QPalette::NColorRoles; role++, maskBit <<= 1u) {
461         if (mask & maskBit) {
462             for (int group = QPalette::Active; group < QPalette::NColorGroups;  group++) {
463                 const QPalette::ColorGroup pgroup = static_cast<QPalette::ColorGroup>(group);
464                 const QPalette::ColorRole prole =  static_cast<QPalette::ColorRole>(role);
465                 rc.setColor(pgroup, prole, newValue.color(pgroup, prole));
466             }
467             // Set the resolve bit from NewValue in return value
468             uint r = rc.resolve();
469             const bool origFlag = newValue.resolve() & maskBit;
470             if (origFlag)
471                 r |= maskBit;
472             else
473                 r &= ~maskBit;
474             rc.resolve(r);
475         }
476     }
477     return rc;
478 }
479 
480 // apply changed subproperties to  a QAlignment which is a flag combination of vertical and horizontal
applyAlignmentSubProperty(Qt::Alignment oldValue,Qt::Alignment newValue,unsigned mask)481 Qt::Alignment applyAlignmentSubProperty(Qt::Alignment oldValue, Qt::Alignment newValue, unsigned mask)
482 {
483     // easy: both changed.
484     if (mask == (SubPropertyHorizontalAlignment|SubPropertyVerticalAlignment))
485         return newValue;
486     // Change subprop
487     const Qt::Alignment changeMask   = (mask & SubPropertyHorizontalAlignment) ? Qt::AlignHorizontal_Mask : Qt::AlignVertical_Mask;
488     const Qt::Alignment takeOverMask = (mask & SubPropertyHorizontalAlignment) ? Qt::AlignVertical_Mask   : Qt::AlignHorizontal_Mask;
489     return (oldValue & takeOverMask) | (newValue & changeMask);
490 }
491 
492 }
493 
494 namespace qdesigner_internal {
495 
496 // apply changed subproperties to a variant
applySubProperty(const QVariant & oldValue,const QVariant & newValue,qdesigner_internal::SpecialProperty specialProperty,unsigned mask,bool changed)497 PropertyHelper::Value applySubProperty(const QVariant &oldValue, const QVariant &newValue, qdesigner_internal::SpecialProperty specialProperty, unsigned mask, bool changed)
498 {
499     if (mask == SubPropertyAll)
500         return PropertyHelper::Value(newValue, changed);
501 
502     switch (oldValue.type()) {
503     case QVariant::Rect:
504         return PropertyHelper::Value(applyRectSubProperty(oldValue.toRect(), newValue.toRect(), mask), changed);
505     case QVariant::Size:
506         return PropertyHelper::Value(applySizeSubProperty(oldValue.toSize(), newValue.toSize(), mask), changed);
507     case QVariant::SizePolicy:
508         return PropertyHelper::Value(QVariant::fromValue(applySizePolicySubProperty(qvariant_cast<QSizePolicy>(oldValue), qvariant_cast<QSizePolicy>(newValue), mask)), changed);
509     case QVariant::Font: {
510         // Changed flag in case of font and palette depends on resolve mask only, not on the passed "changed" value.
511 
512         // The first case: the user changed bold subproperty and then pressed reset button for this subproperty (not for
513         // the whole font property). We instantiate SetPropertyCommand passing changed=true. But in this case no
514         // subproperty is changed and the whole property should be marked an unchanged.
515 
516         // The second case: there are 2 pushbuttons, for 1st the user set bold and italic subproperties,
517         // for the 2nd he set bold only. He does multiselection so that the current widget is the 2nd one.
518         // He press reset next to bold subproperty. In result the 2nd widget should have the whole
519         // font property marked as unchanged and the 1st widget should have the font property
520         // marked as changed and only italic subproperty should be marked as changed (the bold should be reset).
521 
522         // The third case: there are 2 pushbuttons, for 1st the user set bold and italic subproperties,
523         // for the 2nd he set bold only. He does multiselection so that the current widget is the 2nd one.
524         // He press reset button for the whole font property. In result whole font properties for both
525         // widgets should be marked as unchanged.
526         QFont font = applyFontSubProperty(qvariant_cast<QFont>(oldValue), qvariant_cast<QFont>(newValue), mask);
527         return PropertyHelper::Value(QVariant::fromValue(font), font.resolve());
528         }
529     case QVariant::Palette: {
530         QPalette palette = applyPaletteSubProperty(qvariant_cast<QPalette>(oldValue), qvariant_cast<QPalette>(newValue), mask);
531         return PropertyHelper::Value(QVariant::fromValue(palette), palette.resolve());
532         }
533     default:
534         if (oldValue.userType() == qMetaTypeId<qdesigner_internal::PropertySheetIconValue>()) {
535             PropertySheetIconValue icon = qvariant_cast<qdesigner_internal::PropertySheetIconValue>(oldValue);
536             icon.assign(qvariant_cast<qdesigner_internal::PropertySheetIconValue>(newValue), mask);
537             return PropertyHelper::Value(QVariant::fromValue(icon), icon.mask());
538         } else if (oldValue.userType() == qMetaTypeId<qdesigner_internal::PropertySheetStringValue>()) {
539             qdesigner_internal::PropertySheetStringValue str = applyStringSubProperty(
540                         qvariant_cast<qdesigner_internal::PropertySheetStringValue>(oldValue),
541                         qvariant_cast<qdesigner_internal::PropertySheetStringValue>(newValue), mask);
542             return PropertyHelper::Value(QVariant::fromValue(str), changed);
543         } else if (oldValue.userType() == qMetaTypeId<qdesigner_internal::PropertySheetKeySequenceValue>()) {
544             qdesigner_internal::PropertySheetKeySequenceValue key = applyKeySequenceSubProperty(
545                         qvariant_cast<qdesigner_internal::PropertySheetKeySequenceValue>(oldValue),
546                         qvariant_cast<qdesigner_internal::PropertySheetKeySequenceValue>(newValue), mask);
547             return PropertyHelper::Value(QVariant::fromValue(key), changed);
548         }
549         // Enumerations, flags
550         switch (specialProperty) {
551         case qdesigner_internal::SP_Alignment: {
552             qdesigner_internal::PropertySheetFlagValue f = qvariant_cast<qdesigner_internal::PropertySheetFlagValue>(oldValue);
553             f.value = applyAlignmentSubProperty(variantToAlignment(oldValue), variantToAlignment(newValue), mask);
554             QVariant v;
555             v.setValue(f);
556             return PropertyHelper::Value(v, changed);
557                                                }
558         default:
559         break;
560         }
561         break;
562     }
563     return PropertyHelper::Value(newValue, changed);
564 
565 }
566 // figure out special property
getSpecialProperty(const QString & propertyName)567 enum SpecialProperty getSpecialProperty(const QString& propertyName)
568 {
569     if (propertyName == QLatin1String("objectName"))
570         return SP_ObjectName;
571     if (propertyName == QLatin1String("layoutName"))
572         return SP_LayoutName;
573     if (propertyName == QLatin1String("spacerName"))
574         return SP_SpacerName;
575     if (propertyName == QLatin1String("icon"))
576         return SP_Icon;
577     if (propertyName == QLatin1String("currentTabName"))
578         return SP_CurrentTabName;
579     if (propertyName == QLatin1String("currentItemName"))
580         return SP_CurrentItemName;
581     if (propertyName == QLatin1String("currentPageName"))
582         return SP_CurrentPageName;
583     if (propertyName == QLatin1String("geometry"))
584         return SP_Geometry;
585     if (propertyName == QLatin1String("windowTitle"))
586         return SP_WindowTitle;
587     if (propertyName == QLatin1String("minimumSize"))
588         return SP_MinimumSize;
589     if (propertyName == QLatin1String("maximumSize"))
590         return SP_MaximumSize;
591     if (propertyName == QLatin1String("alignment"))
592         return SP_Alignment;
593     if (propertyName == QLatin1String("autoDefault"))
594         return SP_AutoDefault;
595     if (propertyName == QLatin1String("shortcut"))
596         return SP_Shortcut;
597     if (propertyName == QLatin1String("orientation"))
598         return SP_Orientation;
599     return SP_None;
600 }
601 
602 
PropertyHelper(QObject * object,SpecialProperty specialProperty,QDesignerPropertySheetExtension * sheet,int index)603 PropertyHelper::PropertyHelper(QObject* object,
604                                SpecialProperty specialProperty,
605                                QDesignerPropertySheetExtension *sheet,
606                                int index) :
607     m_specialProperty(specialProperty),
608     m_object(object),
609     m_objectType(OT_Object),
610     m_propertySheet(sheet),  m_index(index),
611     m_oldValue(m_propertySheet->property(m_index), m_propertySheet->isChanged(m_index))
612 {
613     if (object->isWidgetType()) {
614         m_parentWidget = (qobject_cast<QWidget*>(object))->parentWidget();
615         m_objectType = OT_Widget;
616     } else {
617         if (const QAction *action = qobject_cast<const QAction *>(m_object))
618             m_objectType = action->associatedWidgets().empty() ? OT_FreeAction : OT_AssociatedAction;
619     }
620 
621     if(debugPropertyCommands)
622         qDebug() << "PropertyHelper on " << m_object->objectName() << " index= " << m_index << " type = " << m_objectType;
623 }
624 
integration(QDesignerFormWindowInterface * fw) const625 QDesignerIntegration *PropertyHelper::integration(QDesignerFormWindowInterface *fw) const
626 {
627     return qobject_cast<QDesignerIntegration *>(fw->core()->integration());
628 }
629 
630 // Set widget value, apply corrections and checks in case of main window.
checkApplyWidgetValue(QDesignerFormWindowInterface * fw,QWidget * w,SpecialProperty specialProperty,QVariant & value)631 void PropertyHelper::checkApplyWidgetValue(QDesignerFormWindowInterface *fw, QWidget* w,
632                                       SpecialProperty specialProperty, QVariant &value)
633 {
634 
635     bool isMainContainer = false;
636     if (QDesignerFormWindowCursorInterface *cursor = fw->cursor()) {
637         if (cursor->isWidgetSelected(w)) {
638             if (cursor->isWidgetSelected(fw->mainContainer())) {
639                 isMainContainer = true;
640             }
641         }
642     }
643     if (!isMainContainer)
644         return;
645 
646     QWidget *container = fw->core()->integration()->containerWindow(fw);
647     if (!container)
648         return;
649 
650 
651     switch (specialProperty) {
652     case SP_MinimumSize: {
653         const QSize size = checkSize(value.toSize());
654         value.setValue(size);
655     }
656 
657         break;
658     case SP_MaximumSize: {
659         QSize fs, cs;
660         checkSizes(fw, value.toSize(), &fs, &cs);
661         container->setMaximumSize(cs);
662         fw->mainContainer()->setMaximumSize(fs);
663         value.setValue(fs);
664 
665     }
666         break;
667     case SP_Geometry: {
668         QRect r = value.toRect();
669         QSize fs, cs;
670         checkSizes(fw, r.size(), &fs, &cs);
671         container->resize(cs);
672         r.setSize(fs);
673         value.setValue(r);
674     }
675         break;
676     default:
677         break;
678     }
679 }
680 
updateMask() const681 unsigned PropertyHelper::updateMask() const
682 {
683     unsigned rc = 0;
684     switch (m_specialProperty) {
685     case SP_ObjectName:
686     case SP_LayoutName:
687     case SP_SpacerName:
688     case SP_CurrentTabName:
689     case SP_CurrentItemName:
690     case SP_CurrentPageName:
691         if (m_objectType != OT_FreeAction)
692             rc |= UpdateObjectInspector;
693         break;
694     case SP_Icon:
695         if (m_objectType == OT_AssociatedAction)
696             rc |= UpdateObjectInspector;
697         break;
698     case SP_Orientation: // for updating splitter icon
699         rc |= UpdateObjectInspector;
700         break;
701     default:
702         break;
703 
704     }
705     return rc;
706 }
707 
708 
canMerge(const PropertyHelper & other) const709 bool PropertyHelper::canMerge(const PropertyHelper &other) const
710 {
711     return m_object == other.m_object &&  m_index == other.m_index;
712 }
713 
triggerActionChanged(QAction * a)714 void PropertyHelper::triggerActionChanged(QAction *a)
715 {
716     a->setData(QVariant(true)); // this triggers signal "changed" in QAction
717     a->setData(QVariant(false));
718 }
719 
720 // Update the object to reflect the changes
updateObject(QDesignerFormWindowInterface * fw,const QVariant & oldValue,const QVariant & newValue)721 void PropertyHelper::updateObject(QDesignerFormWindowInterface *fw, const QVariant &oldValue, const QVariant &newValue)
722 {
723     if(debugPropertyCommands){
724          qDebug() << "PropertyHelper::updateObject(" << m_object->objectName() << ") " << oldValue << " -> " << newValue;
725     }
726     switch (m_objectType) {
727     case OT_Widget: {
728         switch (m_specialProperty) {
729         case SP_ObjectName: {
730             const QString oldName = qvariant_cast<PropertySheetStringValue>(oldValue).value();
731             const QString newName = qvariant_cast<PropertySheetStringValue>(newValue).value();
732             QDesignerFormWindowCommand::updateBuddies(fw, oldName, newName);
733         }
734             break;
735         default:
736             break;
737         }
738     } break;
739     case OT_AssociatedAction:
740     case OT_FreeAction:
741         // SP_Shortcut is a fake property, so, QAction::changed does not trigger.
742         if (m_specialProperty == SP_ObjectName || m_specialProperty == SP_Shortcut)
743             triggerActionChanged(qobject_cast<QAction *>(m_object));
744         break;
745     default:
746         break;
747     }
748 
749     switch (m_specialProperty) {
750     case SP_ObjectName:
751     case SP_LayoutName:
752     case SP_SpacerName:
753         if (QDesignerIntegration *integr = integration(fw)) {
754             const QString oldName = qvariant_cast<PropertySheetStringValue>(oldValue).value();
755             const QString newName = qvariant_cast<PropertySheetStringValue>(newValue).value();
756             integr->emitObjectNameChanged(fw, m_object, newName, oldName);
757         }
758         break;
759     default:
760         break;
761     }
762 }
763 
ensureUniqueObjectName(QDesignerFormWindowInterface * fw,QObject * object) const764 void PropertyHelper::ensureUniqueObjectName(QDesignerFormWindowInterface *fw, QObject *object) const
765 {
766     switch (m_specialProperty) {
767     case SP_SpacerName:
768         if (object->isWidgetType()) {
769             if (Spacer *sp = qobject_cast<Spacer *>(object)) {
770                 fw->ensureUniqueObjectName(sp);
771                 return;
772             }
773         }
774         fw->ensureUniqueObjectName(object);
775         break;
776     case SP_LayoutName: // Layout name is invoked on the parent widget.
777         if (object->isWidgetType()) {
778             const QWidget * w = qobject_cast<const QWidget *>(object);
779             if (QLayout *wlayout = w->layout()) {
780                 fw->ensureUniqueObjectName(wlayout);
781                 return;
782             }
783         }
784         fw->ensureUniqueObjectName(object);
785         break;
786     case SP_ObjectName:
787         fw->ensureUniqueObjectName(object);
788         break;
789     default:
790         break;
791     }
792 }
793 
setValue(QDesignerFormWindowInterface * fw,const QVariant & value,bool changed,unsigned subPropertyMask)794 PropertyHelper::Value PropertyHelper::setValue(QDesignerFormWindowInterface *fw, const QVariant &value, bool changed, unsigned subPropertyMask)
795 {
796     // Set new whole value
797     if (subPropertyMask == SubPropertyAll)
798         return  applyValue(fw, m_oldValue.first, Value(value, changed));
799 
800     // apply subproperties
801     const PropertyHelper::Value maskedNewValue = applySubProperty(m_oldValue.first, value, m_specialProperty, subPropertyMask, changed);
802     return applyValue(fw, m_oldValue.first, maskedNewValue);
803 }
804 
805 // Apply the value and update. Returns corrected value
applyValue(QDesignerFormWindowInterface * fw,const QVariant & oldValue,Value newValue)806 PropertyHelper::Value PropertyHelper::applyValue(QDesignerFormWindowInterface *fw, const QVariant &oldValue, Value newValue)
807 {
808      if(debugPropertyCommands){
809          qDebug() << "PropertyHelper::applyValue(" << m_object << ") " << oldValue << " -> " << newValue.first << " changed=" << newValue.second;
810      }
811 
812     if (m_objectType ==  OT_Widget) {
813         checkApplyWidgetValue(fw, qobject_cast<QWidget *>(m_object), m_specialProperty, newValue.first);
814     }
815 
816     m_propertySheet->setProperty(m_index, newValue.first);
817     m_propertySheet->setChanged(m_index, newValue.second);
818 
819     switch (m_specialProperty) {
820     case SP_LayoutName:
821     case SP_ObjectName:
822     case SP_SpacerName:
823         ensureUniqueObjectName(fw, m_object);
824         newValue.first = m_propertySheet->property(m_index);
825         break;
826     default:
827         break;
828     }
829 
830     updateObject(fw, oldValue, newValue.first);
831     return newValue;
832 }
833 
restoreOldValue(QDesignerFormWindowInterface * fw)834 PropertyHelper::Value PropertyHelper::restoreOldValue(QDesignerFormWindowInterface *fw)
835 {
836     return applyValue(fw, m_propertySheet->property(m_index), m_oldValue);
837 }
838 
839 // find the default value in widget DB in case PropertySheet::reset fails
findDefaultValue(QDesignerFormWindowInterface * fw) const840 QVariant PropertyHelper::findDefaultValue(QDesignerFormWindowInterface *fw) const
841 {
842     if (m_specialProperty == SP_AutoDefault && qobject_cast<const QPushButton*>(m_object)) {
843         // AutoDefault defaults to true on dialogs
844         const bool isDialog = qobject_cast<const QDialog *>(fw->mainContainer());
845         return QVariant(isDialog);
846     }
847 
848     const int item_idx = fw->core()->widgetDataBase()->indexOfObject(m_object);
849     if (item_idx == -1)
850         return  m_oldValue.first; // We simply don't know the value in this case
851 
852     const QDesignerWidgetDataBaseItemInterface *item = fw->core()->widgetDataBase()->item(item_idx);
853     const QList<QVariant> default_prop_values = item->defaultPropertyValues();
854     if (m_index < default_prop_values.size())
855         return default_prop_values.at(m_index);
856 
857     if (m_oldValue.first.type() == QVariant::Color)
858         return QColor();
859 
860     return m_oldValue.first; // Again, we just don't know
861 }
862 
restoreDefaultValue(QDesignerFormWindowInterface * fw)863 PropertyHelper::Value PropertyHelper::restoreDefaultValue(QDesignerFormWindowInterface *fw)
864 {
865 
866     Value defaultValue = qMakePair(QVariant(), false);
867     const QVariant currentValue = m_propertySheet->property(m_index);
868     // try to reset sheet, else try to find default
869     if (m_propertySheet->reset(m_index)) {
870         defaultValue.first = m_propertySheet->property(m_index);
871     } else {
872         defaultValue.first = findDefaultValue(fw);
873         m_propertySheet->setProperty(m_index, defaultValue.first);
874     }
875 
876     m_propertySheet->setChanged(m_index, defaultValue.second);
877 
878     if (m_objectType == OT_Widget) {
879         checkApplyWidgetValue(fw, qobject_cast<QWidget *>(m_object), m_specialProperty, defaultValue.first);
880     }
881 
882     switch (m_specialProperty) {
883     case SP_LayoutName:
884     case SP_ObjectName:
885     case SP_SpacerName:
886         ensureUniqueObjectName(fw, m_object);
887         defaultValue.first = m_propertySheet->property(m_index);
888         break;
889     default:
890         break;
891     }
892 
893     updateObject(fw, currentValue, defaultValue.first);
894     return defaultValue;
895 }
896 
897 // ---- PropertyListCommand::PropertyDescription(
898 
899 
PropertyDescription(const QString & propertyName,QDesignerPropertySheetExtension * propertySheet,int index)900 PropertyListCommand::PropertyDescription::PropertyDescription(const QString &propertyName,
901                                                               QDesignerPropertySheetExtension *propertySheet,
902                                                               int index) :
903     m_propertyName(propertyName),
904     m_propertyGroup(propertySheet->propertyGroup(index)),
905     m_propertyType(propertySheet->property(index).type()),
906     m_specialProperty(getSpecialProperty(propertyName))
907 {
908 }
909 
PropertyDescription()910 PropertyListCommand::PropertyDescription::PropertyDescription() :
911     m_propertyType(QVariant::Invalid),
912     m_specialProperty(SP_None)
913 {
914 }
915 
debug() const916 void PropertyListCommand::PropertyDescription::debug() const
917 {
918     qDebug() << m_propertyName << m_propertyGroup << m_propertyType << m_specialProperty;
919 }
920 
equals(const PropertyDescription & p) const921 bool PropertyListCommand::PropertyDescription::equals(const PropertyDescription &p) const
922 {
923     return m_propertyType == p.m_propertyType && m_specialProperty == p.m_specialProperty &&
924            m_propertyName == p.m_propertyName && m_propertyGroup   == p.m_propertyGroup;
925 }
926 
927 
928 // ---- PropertyListCommand
PropertyListCommand(QDesignerFormWindowInterface * formWindow,QUndoCommand * parent)929 PropertyListCommand::PropertyListCommand(QDesignerFormWindowInterface *formWindow,
930                                          QUndoCommand *parent) :
931     QDesignerFormWindowCommand(QString(), formWindow, parent)
932 {
933 }
934 
propertyName() const935 const QString PropertyListCommand::propertyName() const
936 {
937     return m_propertyDescription.m_propertyName;
938 }
939 
specialProperty() const940 SpecialProperty PropertyListCommand::specialProperty() const
941 {
942     return m_propertyDescription.m_specialProperty;
943 }
944 
945 // add an object
add(QObject * object,const QString & propertyName)946 bool PropertyListCommand::add(QObject *object, const QString &propertyName)
947 {
948     QDesignerPropertySheetExtension* sheet = propertySheet(object);
949     Q_ASSERT(sheet);
950 
951     const int index = sheet->indexOf(propertyName);
952     if (index == -1)
953         return false;
954 
955     if (QDesignerPropertySheet *exSheet = qobject_cast<QDesignerPropertySheet*>(core()->extensionManager()->extension(object, Q_TYPEID(QDesignerPropertySheetExtension))))
956         if (!exSheet->isEnabled(index))
957             return false;
958 
959     const PropertyDescription description(propertyName, sheet, index);
960 
961     if (m_propertyHelperList.empty()) {
962         // first entry
963         m_propertyDescription = description;
964     } else {
965         // checks: mismatch or only one object in case of name
966         const bool match = m_propertyDescription.equals(description);
967         if (!match || m_propertyDescription.m_specialProperty == SP_ObjectName)
968             return false;
969     }
970 
971     const PropertyHelperPtr ph(createPropertyHelper(object, m_propertyDescription.m_specialProperty, sheet, index));
972     m_propertyHelperList.push_back(ph);
973     return true;
974 }
975 
createPropertyHelper(QObject * object,SpecialProperty sp,QDesignerPropertySheetExtension * sheet,int sheetIndex) const976 PropertyHelper *PropertyListCommand::createPropertyHelper(QObject *object, SpecialProperty sp,
977                                                           QDesignerPropertySheetExtension *sheet, int sheetIndex) const
978 {
979     return new PropertyHelper(object, sp, sheet, sheetIndex);
980 }
981 
982 // Init from a list and make sure referenceObject is added first to obtain the right property group
initList(const ObjectList & list,const QString & apropertyName,QObject * referenceObject)983 bool PropertyListCommand::initList(const ObjectList &list, const QString &apropertyName, QObject *referenceObject)
984 {
985     propertyHelperList().clear();
986 
987     // Ensure the referenceObject (property editor) is first, so the right property group is chosen.
988     if (referenceObject) {
989         if (!add(referenceObject, apropertyName))
990             return false;
991     }
992     foreach (QObject *o, list) {
993         if (o != referenceObject)
994             add(o, apropertyName);
995     }
996 
997     return !propertyHelperList().empty();
998 }
999 
1000 
object(int index) const1001 QObject* PropertyListCommand::object(int index) const
1002 {
1003     Q_ASSERT(index < m_propertyHelperList.size());
1004     return m_propertyHelperList.at(index)->object();
1005 }
1006 
oldValue(int index) const1007 QVariant PropertyListCommand::oldValue(int index) const
1008 {
1009     Q_ASSERT(index < m_propertyHelperList.size());
1010     return m_propertyHelperList.at(index)->oldValue();
1011 }
1012 
setOldValue(const QVariant & oldValue,int index)1013 void PropertyListCommand::setOldValue(const QVariant &oldValue, int index)
1014 {
1015     Q_ASSERT(index < m_propertyHelperList.size());
1016     m_propertyHelperList.at(index)->setOldValue(oldValue);
1017 }
1018 // ----- SetValueFunction: Set a new value when applied to a PropertyHelper.
1019 class SetValueFunction {
1020 public:
1021     SetValueFunction(QDesignerFormWindowInterface *formWindow, const PropertyHelper::Value &newValue, unsigned subPropertyMask);
1022 
1023     PropertyHelper::Value operator()(PropertyHelper&);
1024 private:
1025     QDesignerFormWindowInterface *m_formWindow;
1026     const PropertyHelper::Value m_newValue;
1027     const unsigned m_subPropertyMask;
1028 };
1029 
1030 
SetValueFunction(QDesignerFormWindowInterface * formWindow,const PropertyHelper::Value & newValue,unsigned subPropertyMask)1031 SetValueFunction::SetValueFunction(QDesignerFormWindowInterface *formWindow, const PropertyHelper::Value &newValue, unsigned subPropertyMask) :
1032     m_formWindow(formWindow),
1033     m_newValue(newValue),
1034     m_subPropertyMask(subPropertyMask)
1035 {
1036 }
1037 
operator ()(PropertyHelper & ph)1038 PropertyHelper::Value SetValueFunction::operator()(PropertyHelper &ph) {
1039         return ph.setValue(m_formWindow, m_newValue.first, m_newValue.second, m_subPropertyMask);
1040 }
1041 
1042 // ----- UndoSetValueFunction: Restore old value when applied to a PropertyHelper.
1043 class UndoSetValueFunction {
1044 public:
UndoSetValueFunction(QDesignerFormWindowInterface * formWindow)1045     UndoSetValueFunction(QDesignerFormWindowInterface *formWindow) : m_formWindow(formWindow) {}
operator ()(PropertyHelper & ph)1046     PropertyHelper::Value operator()(PropertyHelper& ph) { return ph.restoreOldValue(m_formWindow); }
1047 private:
1048     QDesignerFormWindowInterface *m_formWindow;
1049 };
1050 
1051 // ----- RestoreDefaultFunction: Restore default value when applied to a PropertyHelper.
1052 class RestoreDefaultFunction {
1053 public:
RestoreDefaultFunction(QDesignerFormWindowInterface * formWindow)1054     RestoreDefaultFunction(QDesignerFormWindowInterface *formWindow) : m_formWindow(formWindow) {}
operator ()(PropertyHelper & ph)1055     PropertyHelper::Value operator()(PropertyHelper& ph) { return ph.restoreDefaultValue(m_formWindow); }
1056 private:
1057     QDesignerFormWindowInterface *m_formWindow;
1058 };
1059 
1060 // ----- changePropertyList: Iterates over a sequence of PropertyHelpers and
1061 // applies a function to them.
1062 // The function returns the  corrected value which is then set in  the property editor.
1063 // Returns a combination of update flags.
1064 template <class PropertyListIterator, class Function>
changePropertyList(QDesignerFormEditorInterface * core,const QString & propertyName,PropertyListIterator begin,PropertyListIterator end,Function function)1065         unsigned changePropertyList(QDesignerFormEditorInterface *core,
1066                                     const QString &propertyName,
1067                                     PropertyListIterator begin,
1068                                     PropertyListIterator end,
1069                                     Function function)
1070 {
1071     unsigned updateMask = 0;
1072     QDesignerPropertyEditorInterface *propertyEditor = core->propertyEditor();
1073     bool updatedPropertyEditor = false;
1074 
1075     for (PropertyListIterator it = begin; it != end; ++it) {
1076         PropertyHelper *ph = it->data();
1077         if (QObject* object = ph->object()) { // Might have been deleted in the meantime
1078             const PropertyHelper::Value newValue = function( *ph );
1079             updateMask |= ph->updateMask();
1080             // Update property editor if it is the current object
1081             if (!updatedPropertyEditor && propertyEditor && object == propertyEditor->object()) {
1082                 propertyEditor->setPropertyValue(propertyName, newValue.first,  newValue.second);
1083                 updatedPropertyEditor = true;
1084             }
1085         }
1086     }
1087     if (!updatedPropertyEditor) updateMask |=  PropertyHelper::UpdatePropertyEditor;
1088     return updateMask;
1089 }
1090 
1091 
1092 // set a new value, return update mask
setValue(QVariant value,bool changed,unsigned subPropertyMask)1093 unsigned PropertyListCommand::setValue(QVariant value, bool changed, unsigned subPropertyMask)
1094 {
1095     if(debugPropertyCommands)
1096         qDebug() << "PropertyListCommand::setValue(" << value
1097                  << changed << subPropertyMask << ')';
1098     return changePropertyList(formWindow()->core(),
1099                               m_propertyDescription.m_propertyName,
1100                               m_propertyHelperList.begin(), m_propertyHelperList.end(),
1101                               SetValueFunction(formWindow(), PropertyHelper::Value(value, changed), subPropertyMask));
1102 }
1103 
1104 // restore old value,  return update mask
restoreOldValue()1105 unsigned PropertyListCommand::restoreOldValue()
1106 {
1107     if(debugPropertyCommands)
1108         qDebug() << "PropertyListCommand::restoreOldValue()";
1109 
1110     return changePropertyList(formWindow()->core(),
1111                               m_propertyDescription.m_propertyName, m_propertyHelperList.begin(), m_propertyHelperList.end(),
1112                               UndoSetValueFunction(formWindow()));
1113 }
1114 // set default value,  return update mask
restoreDefaultValue()1115 unsigned PropertyListCommand::restoreDefaultValue()
1116 {
1117     if(debugPropertyCommands)
1118         qDebug() << "PropertyListCommand::restoreDefaultValue()";
1119 
1120     return changePropertyList(formWindow()->core(),
1121                               m_propertyDescription.m_propertyName, m_propertyHelperList.begin(), m_propertyHelperList.end(),
1122                               RestoreDefaultFunction(formWindow()));
1123 }
1124 
1125 // update
update(unsigned updateMask)1126 void PropertyListCommand::update(unsigned updateMask)
1127 {
1128     if(debugPropertyCommands)
1129         qDebug() << "PropertyListCommand::update(" << updateMask << ')';
1130 
1131     if (updateMask & PropertyHelper::UpdateObjectInspector) {
1132         if (QDesignerObjectInspectorInterface *oi = formWindow()->core()->objectInspector())
1133             oi->setFormWindow(formWindow());
1134     }
1135 
1136     if (updateMask & PropertyHelper::UpdatePropertyEditor) {
1137         // this is needed when f.ex. undo, changes parent's palette, but
1138         // the child is the active widget,
1139         // TODO: current object?
1140         if (QDesignerPropertyEditorInterface *propertyEditor = formWindow()->core()->propertyEditor()) {
1141             propertyEditor->setObject(propertyEditor->object());
1142         }
1143     }
1144 }
1145 
undo()1146 void PropertyListCommand::undo()
1147 {
1148     update(restoreOldValue());
1149     QDesignerPropertyEditor *designerPropertyEditor = qobject_cast<QDesignerPropertyEditor *>(core()->propertyEditor());
1150     if (designerPropertyEditor)
1151         designerPropertyEditor->updatePropertySheet();
1152 }
1153 
1154 // check if lists are aequivalent for command merging (same widgets and props)
canMergeLists(const PropertyHelperList & other) const1155 bool PropertyListCommand::canMergeLists(const PropertyHelperList& other) const
1156 {
1157     if (m_propertyHelperList.size() !=  other.size())
1158         return false;
1159     for (int i = 0; i < m_propertyHelperList.size(); i++) {
1160         if (!m_propertyHelperList.at(i)->canMerge(*other.at(i)))
1161             return false;
1162     }
1163     return true;
1164 }
1165 
1166 // ---- SetPropertyCommand ----
SetPropertyCommand(QDesignerFormWindowInterface * formWindow,QUndoCommand * parent)1167 SetPropertyCommand::SetPropertyCommand(QDesignerFormWindowInterface *formWindow,
1168                                        QUndoCommand *parent)
1169     :  PropertyListCommand(formWindow, parent),
1170        m_subPropertyMask(SubPropertyAll)
1171 {
1172 }
1173 
init(QObject * object,const QString & apropertyName,const QVariant & newValue)1174 bool SetPropertyCommand::init(QObject *object, const QString &apropertyName, const QVariant &newValue)
1175 {
1176     Q_ASSERT(object);
1177 
1178     m_newValue = newValue;
1179 
1180     propertyHelperList().clear();
1181     if (!add(object, apropertyName))
1182         return false;
1183 
1184     setDescription();
1185     return true;
1186 }
1187 
init(const ObjectList & list,const QString & apropertyName,const QVariant & newValue,QObject * referenceObject,bool enableSubPropertyHandling)1188 bool SetPropertyCommand::init(const ObjectList &list, const QString &apropertyName, const QVariant &newValue,
1189                               QObject *referenceObject, bool enableSubPropertyHandling)
1190 {
1191     if (!initList(list, apropertyName, referenceObject))
1192         return false;
1193 
1194     m_newValue = newValue;
1195 
1196     if(debugPropertyCommands)
1197         qDebug() << "SetPropertyCommand::init()" << propertyHelperList().size() << '/' << list.size() << " reference " << referenceObject;
1198 
1199     setDescription();
1200 
1201     if (enableSubPropertyHandling)
1202         m_subPropertyMask = subPropertyMask(newValue, referenceObject);
1203     return true;
1204 }
1205 
subPropertyMask(const QVariant & newValue,QObject * referenceObject)1206 unsigned SetPropertyCommand::subPropertyMask(const QVariant &newValue, QObject *referenceObject)
1207 {
1208     // figure out the mask of changed sub properties when comparing newValue to the current value of the reference object.
1209     if (!referenceObject)
1210         return SubPropertyAll;
1211 
1212     QDesignerPropertySheetExtension* sheet = propertySheet(referenceObject);
1213     Q_ASSERT(sheet);
1214 
1215     const int index = sheet->indexOf(propertyName());
1216     if (index == -1 || !sheet->isVisible(index))
1217         return SubPropertyAll;
1218 
1219     return compareSubProperties(sheet->property(index), newValue, specialProperty());
1220 }
1221 
setDescription()1222 void SetPropertyCommand::setDescription()
1223 {
1224     if (propertyHelperList().size() == 1) {
1225         setText(QApplication::translate("Command", "Changed '%1' of '%2'").arg(propertyName()).arg(propertyHelperList().at(0)->object()->objectName()));
1226     } else {
1227         int count = propertyHelperList().size();
1228         setText(QApplication::translate("Command", "Changed '%1' of %n objects", "", QCoreApplication::UnicodeUTF8, count).arg(propertyName()));
1229     }
1230 }
1231 
redo()1232 void SetPropertyCommand::redo()
1233 {
1234     update(setValue(m_newValue, true, m_subPropertyMask));
1235     QDesignerPropertyEditor *designerPropertyEditor = qobject_cast<QDesignerPropertyEditor *>(core()->propertyEditor());
1236     if (designerPropertyEditor)
1237         designerPropertyEditor->updatePropertySheet();
1238 }
1239 
1240 
id() const1241 int SetPropertyCommand::id() const
1242 {
1243     return 1976;
1244 }
1245 
mergeValue(const QVariant & newValue)1246 QVariant SetPropertyCommand::mergeValue(const QVariant &newValue)
1247 {
1248     return newValue;
1249 }
1250 
mergeWith(const QUndoCommand * other)1251 bool SetPropertyCommand::mergeWith(const QUndoCommand *other)
1252 {
1253     if (id() != other->id() || !formWindow()->isDirty())
1254         return false;
1255 
1256     // Merging: When  for example when the user types ahead in an inplace-editor,
1257     // it makes sense to merge all the generated commands containing the one-character changes.
1258     // In the case of subproperties, if the user changes the font size from 10 to 30 via 20
1259     // and then changes to bold, it makes sense to merge the font size commands only.
1260     // This is why the m_subPropertyMask is checked.
1261 
1262     const SetPropertyCommand *cmd = static_cast<const SetPropertyCommand*>(other);
1263     if (!propertyDescription().equals(cmd->propertyDescription()) ||
1264         m_subPropertyMask  != cmd->m_subPropertyMask ||
1265         !canMergeLists(cmd->propertyHelperList()))
1266         return false;
1267 
1268     const QVariant newValue = mergeValue(cmd->newValue());
1269     if (!newValue.isValid())
1270         return false;
1271     m_newValue = newValue;
1272     m_subPropertyMask |= cmd->m_subPropertyMask;
1273     if(debugPropertyCommands)
1274         qDebug() << "SetPropertyCommand::mergeWith() succeeded " << propertyName();
1275 
1276     return true;
1277 }
1278 
1279 // ---- ResetPropertyCommand ----
ResetPropertyCommand(QDesignerFormWindowInterface * formWindow)1280 ResetPropertyCommand::ResetPropertyCommand(QDesignerFormWindowInterface *formWindow)
1281     : PropertyListCommand(formWindow)
1282 {
1283 }
1284 
init(QObject * object,const QString & apropertyName)1285 bool ResetPropertyCommand::init(QObject *object, const QString &apropertyName)
1286 {
1287     Q_ASSERT(object);
1288 
1289     propertyHelperList().clear();
1290     if (!add(object, apropertyName))
1291         return false;
1292 
1293     setDescription();
1294     return true;
1295 }
1296 
init(const ObjectList & list,const QString & apropertyName,QObject * referenceObject)1297 bool ResetPropertyCommand::init(const ObjectList &list, const QString &apropertyName, QObject *referenceObject)
1298 {
1299     if (!initList(list, apropertyName, referenceObject))
1300         return false;
1301 
1302     if(debugPropertyCommands)
1303         qDebug() << "ResetPropertyCommand::init()" << propertyHelperList().size() << '/' << list.size();
1304 
1305     setDescription();
1306     return true;
1307 }
1308 
setDescription()1309 void ResetPropertyCommand::setDescription()
1310 {
1311     if (propertyHelperList().size() == 1) {
1312         setText(QApplication::translate("Command", "Reset '%1' of '%2'").arg(propertyName()).arg(propertyHelperList().at(0)->object()->objectName()));
1313     } else {
1314         int count = propertyHelperList().size();
1315         setText(QApplication::translate("Command", "Reset '%1' of %n objects", "", QCoreApplication::UnicodeUTF8, count).arg(propertyName()));
1316     }
1317 }
1318 
redo()1319 void ResetPropertyCommand::redo()
1320 {
1321     update(restoreDefaultValue());
1322     QDesignerPropertyEditor *designerPropertyEditor = qobject_cast<QDesignerPropertyEditor *>(core()->propertyEditor());
1323     if (designerPropertyEditor)
1324         designerPropertyEditor->updatePropertySheet();
1325 }
1326 
AddDynamicPropertyCommand(QDesignerFormWindowInterface * formWindow)1327 AddDynamicPropertyCommand::AddDynamicPropertyCommand(QDesignerFormWindowInterface *formWindow)
1328     : QDesignerFormWindowCommand(QString(), formWindow)
1329 {
1330 
1331 }
1332 
init(const QList<QObject * > & selection,QObject * current,const QString & propertyName,const QVariant & value)1333 bool AddDynamicPropertyCommand::init(const QList<QObject *> &selection, QObject *current,
1334             const QString &propertyName, const QVariant &value)
1335 {
1336     Q_ASSERT(current);
1337     m_propertyName = propertyName;
1338 
1339     QDesignerFormEditorInterface *core = formWindow()->core();
1340     QDesignerDynamicPropertySheetExtension *dynamicSheet = qt_extension<QDesignerDynamicPropertySheetExtension*>(core->extensionManager(), current);
1341     Q_ASSERT(dynamicSheet);
1342 
1343     m_selection.clear();
1344 
1345     if (!value.isValid())
1346         return false;
1347 
1348     if (!dynamicSheet->canAddDynamicProperty(m_propertyName))
1349         return false;
1350 
1351     m_selection.append(current);
1352 
1353     m_value = value;
1354 
1355     QListIterator<QObject *> it(selection);
1356     while (it.hasNext()) {
1357         QObject *obj = it.next();
1358         if (m_selection.contains(obj))
1359             continue;
1360         dynamicSheet = qt_extension<QDesignerDynamicPropertySheetExtension*>(core->extensionManager(), obj);
1361         Q_ASSERT(dynamicSheet);
1362         if (dynamicSheet->canAddDynamicProperty(m_propertyName))
1363             m_selection.append(obj);
1364     }
1365 
1366     setDescription();
1367     return true;
1368 }
1369 
redo()1370 void AddDynamicPropertyCommand::redo()
1371 {
1372     QDesignerFormEditorInterface *core = formWindow()->core();
1373     QListIterator<QObject *> it(m_selection);
1374     while (it.hasNext()) {
1375         QObject *obj = it.next();
1376         QDesignerDynamicPropertySheetExtension *dynamicSheet = qt_extension<QDesignerDynamicPropertySheetExtension*>(core->extensionManager(), obj);
1377         dynamicSheet->addDynamicProperty(m_propertyName, m_value);
1378         if (QDesignerPropertyEditorInterface *propertyEditor = formWindow()->core()->propertyEditor()) {
1379             if (propertyEditor->object() == obj)
1380                 propertyEditor->setObject(obj);
1381         }
1382     }
1383 }
1384 
undo()1385 void AddDynamicPropertyCommand::undo()
1386 {
1387     QDesignerFormEditorInterface *core = formWindow()->core();
1388     QListIterator<QObject *> it(m_selection);
1389     while (it.hasNext()) {
1390         QObject *obj = it.next();
1391         QDesignerPropertySheetExtension *sheet = qt_extension<QDesignerPropertySheetExtension*>(core->extensionManager(), obj);
1392         QDesignerDynamicPropertySheetExtension *dynamicSheet = qt_extension<QDesignerDynamicPropertySheetExtension*>(core->extensionManager(), obj);
1393         dynamicSheet->removeDynamicProperty(sheet->indexOf(m_propertyName));
1394         if (QDesignerPropertyEditorInterface *propertyEditor = formWindow()->core()->propertyEditor()) {
1395             if (propertyEditor->object() == obj)
1396                 propertyEditor->setObject(obj);
1397         }
1398     }
1399 }
1400 
setDescription()1401 void AddDynamicPropertyCommand::setDescription()
1402 {
1403     if (m_selection.size() == 1) {
1404         setText(QApplication::translate("Command", "Add dynamic property '%1' to '%2'").arg(m_propertyName).arg(m_selection.first()->objectName()));
1405     } else {
1406         int count = m_selection.size();
1407         setText(QApplication::translate("Command", "Add dynamic property '%1' to %n objects", "", QCoreApplication::UnicodeUTF8, count).arg(m_propertyName));
1408     }
1409 }
1410 
1411 
RemoveDynamicPropertyCommand(QDesignerFormWindowInterface * formWindow)1412 RemoveDynamicPropertyCommand::RemoveDynamicPropertyCommand(QDesignerFormWindowInterface *formWindow)
1413     : QDesignerFormWindowCommand(QString(), formWindow)
1414 {
1415 
1416 }
1417 
init(const QList<QObject * > & selection,QObject * current,const QString & propertyName)1418 bool RemoveDynamicPropertyCommand::init(const QList<QObject *> &selection, QObject *current,
1419             const QString &propertyName)
1420 {
1421     Q_ASSERT(current);
1422     m_propertyName = propertyName;
1423 
1424     QDesignerFormEditorInterface *core = formWindow()->core();
1425     QDesignerPropertySheetExtension *propertySheet = qt_extension<QDesignerPropertySheetExtension*>(core->extensionManager(), current);
1426     Q_ASSERT(propertySheet);
1427     QDesignerDynamicPropertySheetExtension *dynamicSheet = qt_extension<QDesignerDynamicPropertySheetExtension*>(core->extensionManager(), current);
1428     Q_ASSERT(dynamicSheet);
1429 
1430     m_objectToValueAndChanged.clear();
1431 
1432     const int index = propertySheet->indexOf(m_propertyName);
1433     if (!dynamicSheet->isDynamicProperty(index))
1434         return false;
1435 
1436     m_objectToValueAndChanged[current] = qMakePair(propertySheet->property(index), propertySheet->isChanged(index));
1437 
1438     QListIterator<QObject *> it(selection);
1439     while (it.hasNext()) {
1440         QObject *obj = it.next();
1441         if (m_objectToValueAndChanged.contains(obj))
1442             continue;
1443 
1444         propertySheet = qt_extension<QDesignerPropertySheetExtension*>(core->extensionManager(), obj);
1445         dynamicSheet = qt_extension<QDesignerDynamicPropertySheetExtension*>(core->extensionManager(), obj);
1446         const int idx = propertySheet->indexOf(m_propertyName);
1447         if (dynamicSheet->isDynamicProperty(idx))
1448             m_objectToValueAndChanged[obj] = qMakePair(propertySheet->property(idx), propertySheet->isChanged(idx));
1449     }
1450 
1451     setDescription();
1452     return true;
1453 }
1454 
redo()1455 void RemoveDynamicPropertyCommand::redo()
1456 {
1457     QDesignerFormEditorInterface *core = formWindow()->core();
1458     QMap<QObject *, QPair<QVariant, bool> >::ConstIterator it = m_objectToValueAndChanged.constBegin();
1459     while (it != m_objectToValueAndChanged.constEnd()) {
1460         QObject *obj = it.key();
1461         QDesignerDynamicPropertySheetExtension *dynamicSheet = qt_extension<QDesignerDynamicPropertySheetExtension*>(core->extensionManager(), obj);
1462         QDesignerPropertySheetExtension *sheet = qt_extension<QDesignerPropertySheetExtension*>(core->extensionManager(), obj);
1463         dynamicSheet->removeDynamicProperty(sheet->indexOf(m_propertyName));
1464         if (QDesignerPropertyEditorInterface *propertyEditor = formWindow()->core()->propertyEditor()) {
1465             if (propertyEditor->object() == obj)
1466                 propertyEditor->setObject(obj);
1467         }
1468         ++it;
1469     }
1470 }
1471 
undo()1472 void RemoveDynamicPropertyCommand::undo()
1473 {
1474     QDesignerFormEditorInterface *core = formWindow()->core();
1475     QMap<QObject *, QPair<QVariant, bool> >::ConstIterator it = m_objectToValueAndChanged.constBegin();
1476     while (it != m_objectToValueAndChanged.constEnd()) {
1477         QObject *obj = it.key();
1478         QDesignerPropertySheetExtension *propertySheet = qt_extension<QDesignerPropertySheetExtension*>(core->extensionManager(), obj);
1479         QDesignerDynamicPropertySheetExtension *dynamicSheet = qt_extension<QDesignerDynamicPropertySheetExtension*>(core->extensionManager(), obj);
1480         const int index = dynamicSheet->addDynamicProperty(m_propertyName, it.value().first);
1481         propertySheet->setChanged(index, it.value().second);
1482         if (QDesignerPropertyEditorInterface *propertyEditor = formWindow()->core()->propertyEditor()) {
1483             if (propertyEditor->object() == obj)
1484                 propertyEditor->setObject(obj);
1485         }
1486         ++it;
1487     }
1488 }
1489 
setDescription()1490 void RemoveDynamicPropertyCommand::setDescription()
1491 {
1492     if (m_objectToValueAndChanged.size() == 1) {
1493         setText(QApplication::translate("Command", "Remove dynamic property '%1' from '%2'").arg(m_propertyName).arg(m_objectToValueAndChanged.constBegin().key()->objectName()));
1494     } else {
1495         int count = m_objectToValueAndChanged.size();
1496         setText(QApplication::translate("Command", "Remove dynamic property '%1' from %n objects", "", QCoreApplication::UnicodeUTF8, count).arg(m_propertyName));
1497     }
1498 }
1499 
1500 
1501 } // namespace qdesigner_internal
1502 
1503 QT_END_NAMESPACE
1504