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
58QT_BEGIN_NAMESPACE
59
60namespace {
61enum { debugPropertyCommands = 0 };
62
63// Debug resolve mask of font
64QString 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
87QString 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(m: f.resolve()) << QLatin1Char(')');
106 }
107 return rc;
108}
109QSize checkSize(const QSize &size)
110{
111 return size.boundedTo(otherSize: QSize(0xFFFFFF, 0xFFFFFF));
112}
113
114QSize diffSize(QDesignerFormWindowInterface *fw)
115{
116 const QWidget *container = fw->core()->integration()->containerWindow(widget: 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
124void checkSizes(QDesignerFormWindowInterface *fw, const QSize &size, QSize *formSize, QSize *containerSize)
125{
126 const QWidget *container = fw->core()->integration()->containerWindow(widget: 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(otherSize: fw->mainContainer()->minimumSizeHint()); // don't try to resize to smaller size than minimumSizeHint
133 QSize newContainerSize = newFormSize + diff;
134
135 newContainerSize = newContainerSize.expandedTo(otherSize: container->minimumSizeHint());
136 newContainerSize = newContainerSize.expandedTo(otherSize: container->minimumSize());
137
138 newFormSize = newContainerSize - diff;
139
140 newContainerSize = checkSize(size: 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
156enum RectSubPropertyMask { SubPropertyX=1, SubPropertyY = 2, SubPropertyWidth = 4, SubPropertyHeight = 8 };
157enum SizePolicySubPropertyMask { SubPropertyHSizePolicy = 1, SubPropertyHStretch = 2, SubPropertyVSizePolicy = 4, SubPropertyVStretch = 8 };
158enum AlignmentSubPropertyMask { SubPropertyHorizontalAlignment = 1, SubPropertyVerticalAlignment = 2 };
159enum StringSubPropertyMask { SubPropertyStringValue = 1, SubPropertyStringComment = 2,
160 SubPropertyStringTranslatable = 4, SubPropertyStringDisambiguation = 8,
161 SubPropertyStringId = 16 };
162enum StringListSubPropertyMask { SubPropertyStringListValue = 1, SubPropertyStringListComment = 2,
163 SubPropertyStringListTranslatable = 4, SubPropertyStringListDisambiguation = 8,
164 SubPropertyStringListId = 16 };
165enum KeySequenceSubPropertyMask { SubPropertyKeySequenceValue = 1, SubPropertyKeySequenceComment = 2,
166 SubPropertyKeySequenceTranslatable = 4, SubPropertyKeySequenceDisambiguation = 8,
167 SubPropertyKeySequenceId = 16 };
168
169enum 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
176unsigned 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
187unsigned 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
195unsigned 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
205unsigned 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
216unsigned 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
227unsigned 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
240template <class Property>
241void 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
259unsigned compareSubProperties(const QFont & f1, const QFont & f2)
260{
261 unsigned rc = 0;
262 compareFontSubProperty(f1, f2, getter: &QFont::family, maskBit: QFont::FamilyResolved, mask&: rc);
263 compareFontSubProperty(f1, f2, getter: &QFont::pointSize, maskBit: QFont::SizeResolved, mask&: rc);
264 compareFontSubProperty(f1, f2, getter: &QFont::bold, maskBit: QFont::WeightResolved, mask&: rc);
265 compareFontSubProperty(f1, f2, getter: &QFont::italic, maskBit: QFont::StyleResolved, mask&: rc);
266 compareFontSubProperty(f1, f2, getter: &QFont::underline, maskBit: QFont::UnderlineResolved, mask&: rc);
267 compareFontSubProperty(f1, f2, getter: &QFont::strikeOut, maskBit: QFont::StrikeOutResolved, mask&: rc);
268 compareFontSubProperty(f1, f2, getter: &QFont::kerning, maskBit: QFont::KerningResolved, mask&: rc);
269 compareFontSubProperty(f1, f2, getter: &QFont::styleStrategy, maskBit: QFont::StyleStrategyResolved, mask&: rc);
270 if (debugPropertyCommands)
271 qDebug() << "compareSubProperties " << fontString(f: f1) << fontString(f: f2) << "\n\treturns " << fontMask(m: rc);
272 return rc;
273}
274
275// Compare colors of a role
276bool 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(cg: pgroup, cr: role) != p2.color(cg: pgroup, cr: role))
281 return true;
282 }
283 return false;
284}
285// find changed subproperties of a QPalette taking the [undocumented] resolve flags into account
286unsigned 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, role: 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
310unsigned 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
320Qt::Alignment variantToAlignment(const QVariant & q)
321{
322 return Qt::Alignment(qdesigner_internal::Utils::valueOf(value: q));
323}
324// find changed subproperties of a variant
325unsigned 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(r1: q1.toRect(), r2: q2.toRect());
334 case QVariant::Size:
335 return compareSubProperties(r1: q1.toSize(), r2: q2.toSize());
336 case QVariant::SizePolicy:
337 return compareSubProperties(sp1: qvariant_cast<QSizePolicy>(v: q1), sp2: qvariant_cast<QSizePolicy>(v: q2));
338 case QVariant::Font:
339 return compareSubProperties(f1: qvariant_cast<QFont>(v: q1), f2: qvariant_cast<QFont>(v: q2));
340 case QVariant::Palette:
341 return compareSubProperties(p1: qvariant_cast<QPalette>(v: q1), p2: qvariant_cast<QPalette>(v: q2));
342 default:
343 if (q1.userType() == qMetaTypeId<qdesigner_internal::PropertySheetIconValue>())
344 return qvariant_cast<qdesigner_internal::PropertySheetIconValue>(v: q1).compare(other: qvariant_cast<qdesigner_internal::PropertySheetIconValue>(v: q2));
345 else if (q1.userType() == qMetaTypeId<qdesigner_internal::PropertySheetStringValue>())
346 return compareSubProperties(str1: qvariant_cast<qdesigner_internal::PropertySheetStringValue>(v: q1), str2: qvariant_cast<qdesigner_internal::PropertySheetStringValue>(v: q2));
347 else if (q1.userType() == qMetaTypeId<qdesigner_internal::PropertySheetStringListValue>())
348 return compareSubProperties(str1: qvariant_cast<qdesigner_internal::PropertySheetStringListValue>(v: q1), str2: qvariant_cast<qdesigner_internal::PropertySheetStringListValue>(v: q2));
349 else if (q1.userType() == qMetaTypeId<qdesigner_internal::PropertySheetKeySequenceValue>())
350 return compareSubProperties(str1: qvariant_cast<qdesigner_internal::PropertySheetKeySequenceValue>(v: q1), str2: qvariant_cast<qdesigner_internal::PropertySheetKeySequenceValue>(v: q2));
351 // Enumerations, flags
352 switch (specialProperty) {
353 case qdesigner_internal::SP_Alignment:
354 return compareSubProperties(a1: variantToAlignment(q: q1), a2: variantToAlignment(q: 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
368QRect 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
380QSize 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
390QSizePolicy 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
401qdesigner_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
414qdesigner_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
427qdesigner_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&).
441template <class PropertyReturnType, class PropertySetterType>
442inline 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(mask: r);
459 if (debugPropertyCommands)
460 qDebug() << "setFontSubProperty " << fontMask(m: maskBit) << " resolve=" << origFlag;
461 }
462}
463// apply changed subproperties to a QFont
464QFont applyFontSubProperty(const QFont &oldValue, const QFont &newValue, unsigned mask)
465{
466 QFont rc = oldValue;
467 setFontSubProperty(mask, newValue, maskBit: QFont::FamilyResolved, getter: &QFont::family, setter: &QFont::setFamily, value&: rc);
468 setFontSubProperty(mask, newValue, maskBit: QFont::SizeResolved, getter: &QFont::pointSize, setter: &QFont::setPointSize, value&: rc);
469 setFontSubProperty(mask, newValue, maskBit: QFont::WeightResolved, getter: &QFont::bold, setter: &QFont::setBold, value&: rc);
470 setFontSubProperty(mask, newValue, maskBit: QFont::StyleResolved, getter: &QFont::italic, setter: &QFont::setItalic, value&: rc);
471 setFontSubProperty(mask, newValue, maskBit: QFont::UnderlineResolved, getter: &QFont::underline, setter: &QFont::setUnderline, value&: rc);
472 setFontSubProperty(mask, newValue, maskBit: QFont::StrikeOutResolved, getter: &QFont::strikeOut, setter: &QFont::setStrikeOut, value&: rc);
473 setFontSubProperty(mask, newValue, maskBit: QFont::KerningResolved, getter: &QFont::kerning, setter: &QFont::setKerning, value&: rc);
474 setFontSubProperty(mask, newValue, maskBit: QFont::StyleStrategyResolved, getter: &QFont::styleStrategy, setter: &QFont::setStyleStrategy, value&: rc);
475 if (debugPropertyCommands)
476 qDebug() << "applyFontSubProperty old " << fontMask(m: oldValue.resolve()) << " new " << fontMask(m: newValue.resolve()) << " return: " << fontMask(m: rc.resolve());
477 return rc;
478}
479
480// apply changed subproperties to a QPalette
481QPalette 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(acg: pgroup, acr: prole, acolor: newValue.color(cg: pgroup, cr: 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(mask: r);
501 }
502 }
503 return rc;
504}
505
506// apply changed subproperties to a QAlignment which is a flag combination of vertical and horizontal
507Qt::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
520namespace qdesigner_internal {
521
522// apply changed subproperties to a variant
523PropertyHelper::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: oldValue.toRect(), newValue: newValue.toRect(), mask), changed);
531 case QVariant::Size:
532 return PropertyHelper::Value(applySizeSubProperty(oldValue: oldValue.toSize(), newValue: newValue.toSize(), mask), changed);
533 case QVariant::SizePolicy:
534 return PropertyHelper::Value(QVariant::fromValue(value: applySizePolicySubProperty(oldValue: qvariant_cast<QSizePolicy>(v: oldValue), newValue: qvariant_cast<QSizePolicy>(v: 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(oldValue: qvariant_cast<QFont>(v: oldValue), newValue: qvariant_cast<QFont>(v: newValue), mask);
553 return PropertyHelper::Value(QVariant::fromValue(value: font), font.resolve());
554 }
555 case QVariant::Palette: {
556 QPalette palette = applyPaletteSubProperty(oldValue: qvariant_cast<QPalette>(v: oldValue), newValue: qvariant_cast<QPalette>(v: newValue), mask);
557 return PropertyHelper::Value(QVariant::fromValue(value: palette), palette.resolve());
558 }
559 default:
560 if (oldValue.userType() == qMetaTypeId<qdesigner_internal::PropertySheetIconValue>()) {
561 PropertySheetIconValue icon = qvariant_cast<qdesigner_internal::PropertySheetIconValue>(v: oldValue);
562 icon.assign(other: qvariant_cast<qdesigner_internal::PropertySheetIconValue>(v: newValue), mask);
563 return PropertyHelper::Value(QVariant::fromValue(value: icon), icon.mask());
564 } else if (oldValue.userType() == qMetaTypeId<qdesigner_internal::PropertySheetStringValue>()) {
565 qdesigner_internal::PropertySheetStringValue str = applyStringSubProperty(
566 oldValue: qvariant_cast<qdesigner_internal::PropertySheetStringValue>(v: oldValue),
567 newValue: qvariant_cast<qdesigner_internal::PropertySheetStringValue>(v: newValue), mask);
568 return PropertyHelper::Value(QVariant::fromValue(value: str), changed);
569 } else if (oldValue.userType() == qMetaTypeId<qdesigner_internal::PropertySheetStringListValue>()) {
570 qdesigner_internal::PropertySheetStringListValue str = applyStringListSubProperty(
571 oldValue: qvariant_cast<qdesigner_internal::PropertySheetStringListValue>(v: oldValue),
572 newValue: qvariant_cast<qdesigner_internal::PropertySheetStringListValue>(v: newValue), mask);
573 return PropertyHelper::Value(QVariant::fromValue(value: str), changed);
574 } else if (oldValue.userType() == qMetaTypeId<qdesigner_internal::PropertySheetKeySequenceValue>()) {
575 qdesigner_internal::PropertySheetKeySequenceValue key = applyKeySequenceSubProperty(
576 oldValue: qvariant_cast<qdesigner_internal::PropertySheetKeySequenceValue>(v: oldValue),
577 newValue: qvariant_cast<qdesigner_internal::PropertySheetKeySequenceValue>(v: newValue), mask);
578 return PropertyHelper::Value(QVariant::fromValue(value: 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>(v: oldValue);
584 f.value = applyAlignmentSubProperty(oldValue: variantToAlignment(q: oldValue), newValue: variantToAlignment(q: 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
598enum 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
634PropertyHelper::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(index: m_index), m_propertySheet->isChanged(index: m_index))
643{
644 if (object->isWidgetType()) {
645 m_parentWidget = (qobject_cast<QWidget*>(o: object))->parentWidget();
646 m_objectType = OT_Widget;
647 } else {
648 if (const QAction *action = qobject_cast<const QAction *>(object: 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
656QDesignerIntegration *PropertyHelper::integration(QDesignerFormWindowInterface *fw) const
657{
658 return qobject_cast<QDesignerIntegration *>(object: fw->core()->integration());
659}
660
661// Set widget value, apply corrections and checks in case of main window.
662void 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(widget: w)) {
669 if (cursor->isWidgetSelected(widget: fw->mainContainer())) {
670 isMainContainer = true;
671 }
672 }
673 }
674 if (!isMainContainer)
675 return;
676
677 QWidget *container = fw->core()->integration()->containerWindow(widget: fw);
678 if (!container)
679 return;
680
681
682 switch (specialProperty) {
683 case SP_MinimumSize: {
684 const QSize size = checkSize(size: value.toSize());
685 value.setValue(size);
686 }
687
688 break;
689 case SP_MaximumSize: {
690 QSize fs, cs;
691 checkSizes(fw, size: value.toSize(), formSize: &fs, containerSize: &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, size: r.size(), formSize: &fs, containerSize: &cs);
702 container->resize(cs);
703 r.setSize(fs);
704 value.setValue(r);
705 }
706 break;
707 default:
708 break;
709 }
710}
711
712unsigned 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
740bool PropertyHelper::canMerge(const PropertyHelper &other) const
741{
742 return m_object == other.m_object && m_index == other.m_index;
743}
744
745void 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
752void 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>(v: oldValue).value();
762 const QString newName = qvariant_cast<PropertySheetStringValue>(v: newValue).value();
763 QDesignerFormWindowCommand::updateBuddies(form: fw, old_name: oldName, new_name: 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(a: qobject_cast<QAction *>(object: 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>(v: oldValue).value();
786 const QString newName = qvariant_cast<PropertySheetStringValue>(v: newValue).value();
787 integr->emitObjectNameChanged(formWindow: fw, object: m_object, newName, oldName);
788 }
789 break;
790 default:
791 break;
792 }
793}
794
795void 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(object: 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(object: 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
825PropertyHelper::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, oldValue: m_oldValue.first, newValue: Value(value, changed));
830
831 // apply subproperties
832 const PropertyHelper::Value maskedNewValue = applySubProperty(oldValue: m_oldValue.first, newValue: value, specialProperty: m_specialProperty, mask: subPropertyMask, changed);
833 return applyValue(fw, oldValue: m_oldValue.first, newValue: maskedNewValue);
834}
835
836// Apply the value and update. Returns corrected value
837PropertyHelper::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, w: qobject_cast<QWidget *>(o: m_object), specialProperty: m_specialProperty, value&: newValue.first);
845 }
846
847 m_propertySheet->setProperty(index: m_index, value: newValue.first);
848 m_propertySheet->setChanged(index: m_index, changed: newValue.second);
849
850 switch (m_specialProperty) {
851 case SP_LayoutName:
852 case SP_ObjectName:
853 case SP_SpacerName:
854 ensureUniqueObjectName(fw, object: m_object);
855 newValue.first = m_propertySheet->property(index: m_index);
856 break;
857 default:
858 break;
859 }
860
861 updateObject(fw, oldValue, newValue: newValue.first);
862 return newValue;
863}
864
865PropertyHelper::Value PropertyHelper::restoreOldValue(QDesignerFormWindowInterface *fw)
866{
867 return applyValue(fw, oldValue: m_propertySheet->property(index: m_index), newValue: m_oldValue);
868}
869
870// find the default value in widget DB in case PropertySheet::reset fails
871QVariant PropertyHelper::findDefaultValue(QDesignerFormWindowInterface *fw) const
872{
873 if (m_specialProperty == SP_AutoDefault && qobject_cast<const QPushButton*>(object: m_object)) {
874 // AutoDefault defaults to true on dialogs
875 const bool isDialog = qobject_cast<const QDialog *>(object: fw->mainContainer());
876 return QVariant(isDialog);
877 }
878
879 const int item_idx = fw->core()->widgetDataBase()->indexOfObject(object: 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(index: item_idx);
884 const auto default_prop_values = item->defaultPropertyValues();
885 if (m_index < default_prop_values.size())
886 return default_prop_values.at(i: 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
894PropertyHelper::Value PropertyHelper::restoreDefaultValue(QDesignerFormWindowInterface *fw)
895{
896
897 Value defaultValue = qMakePair(x: QVariant(), y: false);
898 const QVariant currentValue = m_propertySheet->property(index: m_index);
899 // try to reset sheet, else try to find default
900 if (m_propertySheet->reset(index: m_index)) {
901 defaultValue.first = m_propertySheet->property(index: m_index);
902 } else {
903 defaultValue.first = findDefaultValue(fw);
904 m_propertySheet->setProperty(index: m_index, value: defaultValue.first);
905 }
906
907 m_propertySheet->setChanged(index: m_index, changed: defaultValue.second);
908
909 if (m_objectType == OT_Widget) {
910 checkApplyWidgetValue(fw, w: qobject_cast<QWidget *>(o: m_object), specialProperty: m_specialProperty, value&: defaultValue.first);
911 }
912
913 switch (m_specialProperty) {
914 case SP_LayoutName:
915 case SP_ObjectName:
916 case SP_SpacerName:
917 ensureUniqueObjectName(fw, object: m_object);
918 defaultValue.first = m_propertySheet->property(index: m_index);
919 break;
920 default:
921 break;
922 }
923
924 updateObject(fw, oldValue: currentValue, newValue: defaultValue.first);
925 return defaultValue;
926}
927
928// ---- PropertyListCommand::PropertyDescription(
929
930
931PropertyListCommand::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
941void PropertyListCommand::PropertyDescription::debug() const
942{
943 qDebug() << m_propertyName << m_propertyGroup << m_propertyType << m_specialProperty;
944}
945
946bool 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
954PropertyListCommand::PropertyListCommand(QDesignerFormWindowInterface *formWindow,
955 QUndoCommand *parent) :
956 QDesignerFormWindowCommand(QString(), formWindow, parent)
957{
958}
959
960const QString PropertyListCommand::propertyName() const
961{
962 return m_propertyDescription.m_propertyName;
963}
964
965SpecialProperty PropertyListCommand::specialProperty() const
966{
967 return m_propertyDescription.m_specialProperty;
968}
969
970// add an object
971bool PropertyListCommand::add(QObject *object, const QString &propertyName)
972{
973 QDesignerPropertySheetExtension* sheet = propertySheet(object);
974 Q_ASSERT(sheet);
975
976 const int index = sheet->indexOf(name: 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(p: description);
991 if (!match || m_propertyDescription.m_specialProperty == SP_ObjectName)
992 return false;
993 }
994
995 const PropertyHelperPtr ph(createPropertyHelper(o: object, sp: m_propertyDescription.m_specialProperty, sheet, sheetIndex: index));
996 m_propertyHelperList.push_back(t: ph);
997 return true;
998}
999
1000PropertyHelper *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
1007bool 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(object: referenceObject, propertyName: apropertyName))
1014 return false;
1015 }
1016 for (QObject *o : list) {
1017 if (o != referenceObject)
1018 add(object: o, propertyName: apropertyName);
1019 }
1020
1021 return !propertyHelperList().isEmpty();
1022}
1023
1024
1025QObject* PropertyListCommand::object(int index) const
1026{
1027 Q_ASSERT(index < m_propertyHelperList.size());
1028 return m_propertyHelperList.at(i: index)->object();
1029}
1030
1031QVariant PropertyListCommand::oldValue(int index) const
1032{
1033 Q_ASSERT(index < m_propertyHelperList.size());
1034 return m_propertyHelperList.at(i: index)->oldValue();
1035}
1036
1037void PropertyListCommand::setOldValue(const QVariant &oldValue, int index)
1038{
1039 Q_ASSERT(index < m_propertyHelperList.size());
1040 m_propertyHelperList.at(i: index)->setOldValue(oldValue);
1041}
1042// ----- SetValueFunction: Set a new value when applied to a PropertyHelper.
1043class SetValueFunction {
1044public:
1045 SetValueFunction(QDesignerFormWindowInterface *formWindow, const PropertyHelper::Value &newValue, unsigned subPropertyMask);
1046
1047 PropertyHelper::Value operator()(PropertyHelper&);
1048private:
1049 QDesignerFormWindowInterface *m_formWindow;
1050 const PropertyHelper::Value m_newValue;
1051 const unsigned m_subPropertyMask;
1052};
1053
1054
1055SetValueFunction::SetValueFunction(QDesignerFormWindowInterface *formWindow, const PropertyHelper::Value &newValue, unsigned subPropertyMask) :
1056 m_formWindow(formWindow),
1057 m_newValue(newValue),
1058 m_subPropertyMask(subPropertyMask)
1059{
1060}
1061
1062PropertyHelper::Value SetValueFunction::operator()(PropertyHelper &ph) {
1063 return ph.setValue(fw: m_formWindow, value: m_newValue.first, changed: m_newValue.second, subPropertyMask: m_subPropertyMask);
1064}
1065
1066// ----- UndoSetValueFunction: Restore old value when applied to a PropertyHelper.
1067class UndoSetValueFunction {
1068public:
1069 UndoSetValueFunction(QDesignerFormWindowInterface *formWindow) : m_formWindow(formWindow) {}
1070 PropertyHelper::Value operator()(PropertyHelper& ph) { return ph.restoreOldValue(fw: m_formWindow); }
1071private:
1072 QDesignerFormWindowInterface *m_formWindow;
1073};
1074
1075// ----- RestoreDefaultFunction: Restore default value when applied to a PropertyHelper.
1076class RestoreDefaultFunction {
1077public:
1078 RestoreDefaultFunction(QDesignerFormWindowInterface *formWindow) : m_formWindow(formWindow) {}
1079 PropertyHelper::Value operator()(PropertyHelper& ph) { return ph.restoreDefaultValue(fw: m_formWindow); }
1080private:
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.
1088template <class PropertyListIterator, class 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(name: propertyName, value: newValue.first, changed: 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
1117unsigned PropertyListCommand::setValue(const QVariant &value, bool changed, unsigned subPropertyMask)
1118{
1119 if(debugPropertyCommands)
1120 qDebug() << "PropertyListCommand::setValue(" << value
1121 << changed << subPropertyMask << ')';
1122 return changePropertyList(core: formWindow()->core(),
1123 propertyName: m_propertyDescription.m_propertyName,
1124 begin: m_propertyHelperList.begin(), end: m_propertyHelperList.end(),
1125 function: SetValueFunction(formWindow(), PropertyHelper::Value(value, changed), subPropertyMask));
1126}
1127
1128// restore old value, return update mask
1129unsigned PropertyListCommand::restoreOldValue()
1130{
1131 if(debugPropertyCommands)
1132 qDebug() << "PropertyListCommand::restoreOldValue()";
1133
1134 return changePropertyList(core: formWindow()->core(),
1135 propertyName: m_propertyDescription.m_propertyName, begin: m_propertyHelperList.begin(), end: m_propertyHelperList.end(),
1136 function: UndoSetValueFunction(formWindow()));
1137}
1138// set default value, return update mask
1139unsigned PropertyListCommand::restoreDefaultValue()
1140{
1141 if(debugPropertyCommands)
1142 qDebug() << "PropertyListCommand::restoreDefaultValue()";
1143
1144 return changePropertyList(core: formWindow()->core(),
1145 propertyName: m_propertyDescription.m_propertyName, begin: m_propertyHelperList.begin(), end: m_propertyHelperList.end(),
1146 function: RestoreDefaultFunction(formWindow()));
1147}
1148
1149// update
1150void 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
1170void PropertyListCommand::undo()
1171{
1172 update(updateMask: restoreOldValue());
1173 QDesignerPropertyEditor *designerPropertyEditor = qobject_cast<QDesignerPropertyEditor *>(object: core()->propertyEditor());
1174 if (designerPropertyEditor)
1175 designerPropertyEditor->updatePropertySheet();
1176}
1177
1178// check if lists are aequivalent for command merging (same widgets and props)
1179bool 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: *other.at(i)))
1185 return false;
1186 }
1187 return true;
1188}
1189
1190// ---- SetPropertyCommand ----
1191SetPropertyCommand::SetPropertyCommand(QDesignerFormWindowInterface *formWindow,
1192 QUndoCommand *parent)
1193 : PropertyListCommand(formWindow, parent),
1194 m_subPropertyMask(SubPropertyAll)
1195{
1196}
1197
1198bool 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, propertyName: apropertyName))
1206 return false;
1207
1208 setDescription();
1209 return true;
1210}
1211
1212bool 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
1230unsigned 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(object: referenceObject);
1237 Q_ASSERT(sheet);
1238
1239 const int index = sheet->indexOf(name: propertyName());
1240 if (index == -1 || !sheet->isVisible(index))
1241 return SubPropertyAll;
1242
1243 return compareSubProperties(q1: sheet->property(index), q2: newValue, specialProperty: specialProperty());
1244}
1245
1246void SetPropertyCommand::setDescription()
1247{
1248 if (propertyHelperList().size() == 1) {
1249 setText(QApplication::translate(context: "Command", key: "Changed '%1' of '%2'")
1250 .arg(args: propertyName(), args: propertyHelperList().at(i: 0)->object()->objectName()));
1251 } else {
1252 int count = propertyHelperList().size();
1253 setText(QCoreApplication::translate(context: "Command", key: "Changed '%1' of %n objects", disambiguation: "", n: count).arg(a: propertyName()));
1254 }
1255}
1256
1257void SetPropertyCommand::redo()
1258{
1259 update(updateMask: setValue(value: m_newValue, changed: true, subPropertyMask: m_subPropertyMask));
1260 QDesignerPropertyEditor *designerPropertyEditor = qobject_cast<QDesignerPropertyEditor *>(object: core()->propertyEditor());
1261 if (designerPropertyEditor)
1262 designerPropertyEditor->updatePropertySheet();
1263}
1264
1265
1266int SetPropertyCommand::id() const
1267{
1268 return 1976;
1269}
1270
1271QVariant SetPropertyCommand::mergeValue(const QVariant &newValue)
1272{
1273 return newValue;
1274}
1275
1276bool 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(p: cmd->propertyDescription()) ||
1289 m_subPropertyMask != cmd->m_subPropertyMask ||
1290 !canMergeLists(other: cmd->propertyHelperList()))
1291 return false;
1292
1293 const QVariant newValue = mergeValue(newValue: 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 ----
1305ResetPropertyCommand::ResetPropertyCommand(QDesignerFormWindowInterface *formWindow)
1306 : PropertyListCommand(formWindow)
1307{
1308}
1309
1310bool ResetPropertyCommand::init(QObject *object, const QString &apropertyName)
1311{
1312 Q_ASSERT(object);
1313
1314 propertyHelperList().clear();
1315 if (!add(object, propertyName: apropertyName))
1316 return false;
1317
1318 setDescription();
1319 return true;
1320}
1321
1322bool 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(object: *it);
1327 Q_ASSERT(sheet);
1328 const int index = sheet->indexOf(name: apropertyName);
1329 if (index == -1 || !sheet->isChanged(index))
1330 it = modifiedList.erase(it);
1331 else
1332 ++it;
1333 }
1334 if (!modifiedList.contains(t: referenceObject))
1335 referenceObject = nullptr;
1336 if (modifiedList.isEmpty() || !initList(list: 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
1346void ResetPropertyCommand::setDescription()
1347{
1348 if (propertyHelperList().size() == 1) {
1349 setText(QCoreApplication::translate(context: "Command", key: "Reset '%1' of '%2'")
1350 .arg(args: propertyName(), args: propertyHelperList().at(i: 0)->object()->objectName()));
1351 } else {
1352 int count = propertyHelperList().size();
1353 setText(QCoreApplication::translate(context: "Command", key: "Reset '%1' of %n objects", disambiguation: "", n: count).arg(a: propertyName()));
1354 }
1355}
1356
1357void ResetPropertyCommand::redo()
1358{
1359 update(updateMask: restoreDefaultValue());
1360 QDesignerPropertyEditor *designerPropertyEditor = qobject_cast<QDesignerPropertyEditor *>(object: core()->propertyEditor());
1361 if (designerPropertyEditor)
1362 designerPropertyEditor->updatePropertySheet();
1363}
1364
1365AddDynamicPropertyCommand::AddDynamicPropertyCommand(QDesignerFormWindowInterface *formWindow)
1366 : QDesignerFormWindowCommand(QString(), formWindow)
1367{
1368
1369}
1370
1371bool 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*>(manager: core->extensionManager(), object: current);
1379 Q_ASSERT(dynamicSheet);
1380
1381 m_selection.clear();
1382
1383 if (!value.isValid())
1384 return false;
1385
1386 if (!dynamicSheet->canAddDynamicProperty(propertyName: m_propertyName))
1387 return false;
1388
1389 m_selection.append(t: current);
1390
1391 m_value = value;
1392
1393 for (QObject *obj : selection) {
1394 if (m_selection.contains(t: obj))
1395 continue;
1396 dynamicSheet = qt_extension<QDesignerDynamicPropertySheetExtension*>(manager: core->extensionManager(), object: obj);
1397 Q_ASSERT(dynamicSheet);
1398 if (dynamicSheet->canAddDynamicProperty(propertyName: m_propertyName))
1399 m_selection.append(t: obj);
1400 }
1401
1402 setDescription();
1403 return true;
1404}
1405
1406void AddDynamicPropertyCommand::redo()
1407{
1408 QDesignerFormEditorInterface *core = formWindow()->core();
1409 for (QObject *obj : qAsConst(t&: m_selection)) {
1410 QDesignerDynamicPropertySheetExtension *dynamicSheet = qt_extension<QDesignerDynamicPropertySheetExtension*>(manager: core->extensionManager(), object: obj);
1411 dynamicSheet->addDynamicProperty(propertyName: m_propertyName, value: m_value);
1412 if (QDesignerPropertyEditorInterface *propertyEditor = formWindow()->core()->propertyEditor()) {
1413 if (propertyEditor->object() == obj)
1414 propertyEditor->setObject(obj);
1415 }
1416 }
1417}
1418
1419void AddDynamicPropertyCommand::undo()
1420{
1421 QDesignerFormEditorInterface *core = formWindow()->core();
1422 for (QObject *obj : qAsConst(t&: m_selection)) {
1423 QDesignerPropertySheetExtension *sheet = qt_extension<QDesignerPropertySheetExtension*>(manager: core->extensionManager(), object: obj);
1424 QDesignerDynamicPropertySheetExtension *dynamicSheet = qt_extension<QDesignerDynamicPropertySheetExtension*>(manager: core->extensionManager(), object: obj);
1425 dynamicSheet->removeDynamicProperty(index: sheet->indexOf(name: m_propertyName));
1426 if (QDesignerPropertyEditorInterface *propertyEditor = formWindow()->core()->propertyEditor()) {
1427 if (propertyEditor->object() == obj)
1428 propertyEditor->setObject(obj);
1429 }
1430 }
1431}
1432
1433void AddDynamicPropertyCommand::setDescription()
1434{
1435 if (m_selection.size() == 1) {
1436 setText(QApplication::translate(context: "Command", key: "Add dynamic property '%1' to '%2'")
1437 .arg(args&: m_propertyName, args: m_selection.first()->objectName()));
1438 } else {
1439 int count = m_selection.size();
1440 setText(QCoreApplication::translate(context: "Command", key: "Add dynamic property '%1' to %n objects", disambiguation: "", n: count)
1441 .arg(a: m_propertyName));
1442 }
1443}
1444
1445
1446RemoveDynamicPropertyCommand::RemoveDynamicPropertyCommand(QDesignerFormWindowInterface *formWindow)
1447 : QDesignerFormWindowCommand(QString(), formWindow)
1448{
1449
1450}
1451
1452bool 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*>(manager: core->extensionManager(), object: current);
1460 Q_ASSERT(propertySheet);
1461 QDesignerDynamicPropertySheetExtension *dynamicSheet = qt_extension<QDesignerDynamicPropertySheetExtension*>(manager: core->extensionManager(), object: current);
1462 Q_ASSERT(dynamicSheet);
1463
1464 m_objectToValueAndChanged.clear();
1465
1466 const int index = propertySheet->indexOf(name: m_propertyName);
1467 if (!dynamicSheet->isDynamicProperty(index))
1468 return false;
1469
1470 m_objectToValueAndChanged[current] = qMakePair(x: propertySheet->property(index), y: propertySheet->isChanged(index));
1471
1472 for (QObject *obj : selection) {
1473 if (m_objectToValueAndChanged.contains(akey: obj))
1474 continue;
1475
1476 propertySheet = qt_extension<QDesignerPropertySheetExtension*>(manager: core->extensionManager(), object: obj);
1477 dynamicSheet = qt_extension<QDesignerDynamicPropertySheetExtension*>(manager: core->extensionManager(), object: obj);
1478 const int idx = propertySheet->indexOf(name: m_propertyName);
1479 if (dynamicSheet->isDynamicProperty(index: idx))
1480 m_objectToValueAndChanged[obj] = qMakePair(x: propertySheet->property(index: idx), y: propertySheet->isChanged(index: idx));
1481 }
1482
1483 setDescription();
1484 return true;
1485}
1486
1487void 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*>(manager: core->extensionManager(), object: obj);
1494 QDesignerPropertySheetExtension *sheet = qt_extension<QDesignerPropertySheetExtension*>(manager: core->extensionManager(), object: obj);
1495 dynamicSheet->removeDynamicProperty(index: sheet->indexOf(name: m_propertyName));
1496 if (QDesignerPropertyEditorInterface *propertyEditor = formWindow()->core()->propertyEditor()) {
1497 if (propertyEditor->object() == obj)
1498 propertyEditor->setObject(obj);
1499 }
1500 ++it;
1501 }
1502}
1503
1504void 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*>(manager: core->extensionManager(), object: obj);
1511 QDesignerDynamicPropertySheetExtension *dynamicSheet = qt_extension<QDesignerDynamicPropertySheetExtension*>(manager: core->extensionManager(), object: obj);
1512 const int index = dynamicSheet->addDynamicProperty(propertyName: m_propertyName, value: it.value().first);
1513 propertySheet->setChanged(index, changed: 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
1522void RemoveDynamicPropertyCommand::setDescription()
1523{
1524 if (m_objectToValueAndChanged.size() == 1) {
1525 setText(QApplication::translate(context: "Command",
1526 key: "Remove dynamic property '%1' from '%2'")
1527 .arg(args&: m_propertyName, args: m_objectToValueAndChanged.constBegin().key()->objectName()));
1528 } else {
1529 int count = m_objectToValueAndChanged.size();
1530 setText(QApplication::translate(context: "Command",
1531 key: "Remove dynamic property '%1' from %n objects", disambiguation: "", n: count)
1532 .arg(a: m_propertyName));
1533 }
1534}
1535
1536
1537} // namespace qdesigner_internal
1538
1539QT_END_NAMESPACE
1540

source code of qttools/src/designer/src/lib/shared/qdesigner_propertycommand.cpp