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