Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qdesigner_propertycommand.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
3// Qt-Security score:significant reason:default
4
11
12#include <QtDesigner/abstractformeditor.h>
13#include <QtDesigner/abstractintegration.h>
14#include <QtDesigner/abstractformwindow.h>
15#include <QtDesigner/abstractformwindowcursor.h>
16#include <QtDesigner/dynamicpropertysheet.h>
17#include <QtDesigner/propertysheet.h>
18#include <QtDesigner/abstractpropertyeditor.h>
19#include <QtDesigner/abstractobjectinspector.h>
20#include <QtDesigner/abstractintegration.h>
21#include <QtDesigner/abstractwidgetdatabase.h>
22#include <QtDesigner/qextensionmanager.h>
23
24#include <QtWidgets/qwidget.h>
25#include <QtWidgets/qapplication.h>
26#include <QtWidgets/qdialog.h>
27#include <QtWidgets/qpushbutton.h>
28#include <QtWidgets/qlayout.h>
29
30#include <QtGui/qaction.h>
31
32#include <QtCore/qdebug.h>
33#include <QtCore/qsize.h>
34#include <QtCore/qtextstream.h>
35
36QT_BEGIN_NAMESPACE
37
38using namespace Qt::StringLiterals;
39
40namespace {
41enum { debugPropertyCommands = 0 };
42
43const unsigned QFontFamiliesResolved = (QFont::FamilyResolved | QFont::FamiliesResolved);
44
45// Debug resolve mask of font
46QString fontMask(unsigned m)
47{
48 QString rc;
49 if (m & QFontFamiliesResolved)
50 rc += "Family"_L1;
51 if (m & QFont::SizeResolved)
52 rc += "Size "_L1;
53 if (m & QFont::WeightResolved)
54 rc += "Bold "_L1;
55 if (m & QFont::StyleResolved)
56 rc += "Style "_L1;
57 if (m & QFont::UnderlineResolved)
58 rc += "Underline "_L1;
59 if (m & QFont::StrikeOutResolved)
60 rc += "StrikeOut "_L1;
61 if (m & QFont::KerningResolved)
62 rc += "Kerning "_L1;
63 if (m & QFont::StyleStrategyResolved)
64 rc += "StyleStrategy"_L1;
65 return rc;
66}
67
68// Debug font
69QString fontString(const QFont &f)
70{
71 QString rc; {
72 QTextStream str(&rc);
73 str << "QFont(\"" << f.family() << ',' << f.pointSize();
74 if (f.bold())
75 str << ',' << "bold";
76 if (f.italic())
77 str << ',' << "italic";
78 if (f.underline())
79 str << ',' << "underline";
80 if (f.strikeOut())
81 str << ',' << "strikeOut";
82 if (f.kerning())
83 str << ',' << "kerning";
84 str << ',' << f.styleStrategy() << " resolve: "
85 << fontMask(f.resolveMask()) << ')';
86 }
87 return rc;
88}
89QSize checkSize(const QSize &size)
90{
91 return size.boundedTo(QSize(0xFFFFFF, 0xFFFFFF));
92}
93
94QSize diffSize(QDesignerFormWindowInterface *fw)
95{
96 const QWidget *container = fw->core()->integration()->containerWindow(fw);
97 if (!container)
98 return QSize();
99
100 const QSize diff = container->size() - fw->size(); // decoration offset of container window
101 return diff;
102}
103
104void checkSizes(QDesignerFormWindowInterface *fw, const QSize &size, QSize *formSize, QSize *containerSize)
105{
106 const QWidget *container = fw->core()->integration()->containerWindow(fw);
107 if (!container)
108 return;
109
110 const QSize diff = diffSize(fw); // decoration offset of container window
111
112 QSize newFormSize = checkSize(size).expandedTo(fw->mainContainer()->minimumSizeHint()); // don't try to resize to smaller size than minimumSizeHint
113 QSize newContainerSize = newFormSize + diff;
114
115 newContainerSize = newContainerSize.expandedTo(container->minimumSizeHint());
116 newContainerSize = newContainerSize.expandedTo(container->minimumSize());
117
118 newFormSize = newContainerSize - diff;
119
120 newContainerSize = checkSize(newContainerSize);
121
122 if (formSize)
123 *formSize = newFormSize;
124 if (containerSize)
125 *containerSize = newContainerSize;
126}
127
128/* SubProperties: When applying a changed property to a multiselection, it sometimes makes
129 * sense to apply only parts (subproperties) of the property.
130 * For example, if someone changes the x-value of a geometry in the property editor
131 * and applies it to a multi-selection, y should not be applied as this would cause all
132 * the widgets to overlap.
133 * The following routines can be used to find out the changed subproperties of a property,
134 * which are represented as a mask, and to apply them while leaving the others intact. */
135
136enum RectSubPropertyMask { SubPropertyX=1, SubPropertyY = 2, SubPropertyWidth = 4, SubPropertyHeight = 8 };
137enum SizePolicySubPropertyMask { SubPropertyHSizePolicy = 1, SubPropertyHStretch = 2, SubPropertyVSizePolicy = 4, SubPropertyVStretch = 8 };
138enum AlignmentSubPropertyMask { SubPropertyHorizontalAlignment = 1, SubPropertyVerticalAlignment = 2 };
139enum StringSubPropertyMask { SubPropertyStringValue = 1, SubPropertyStringComment = 2,
140 SubPropertyStringTranslatable = 4, SubPropertyStringDisambiguation = 8,
141 SubPropertyStringId = 16 };
142enum StringListSubPropertyMask { SubPropertyStringListValue = 1, SubPropertyStringListComment = 2,
143 SubPropertyStringListTranslatable = 4, SubPropertyStringListDisambiguation = 8,
144 SubPropertyStringListId = 16 };
145enum KeySequenceSubPropertyMask { SubPropertyKeySequenceValue = 1, SubPropertyKeySequenceComment = 2,
146 SubPropertyKeySequenceTranslatable = 4, SubPropertyKeySequenceDisambiguation = 8,
147 SubPropertyKeySequenceId = 16 };
148
149enum CommonSubPropertyMask : quint64 { SubPropertyAll = quint64(-1) };
150
151// Set the mask flag in mask if the properties do not match.
152#define COMPARE_SUBPROPERTY(object1, object2, getter, mask, maskFlag)
153 if (object1.getter() != object2.getter()) (mask) |= (maskFlag);
154
155// find changed subproperties of a rectangle
156quint64 compareSubProperties(const QRect & r1, const QRect & r2)
157{
158 quint64 rc = 0;
159 COMPARE_SUBPROPERTY(r1, r2, x, rc, SubPropertyX)
160 COMPARE_SUBPROPERTY(r1, r2, y, rc, SubPropertyY)
161 COMPARE_SUBPROPERTY(r1, r2, width, rc, SubPropertyWidth)
162 COMPARE_SUBPROPERTY(r1, r2, height, rc, SubPropertyHeight)
163 return rc;
164}
165
166// find changed subproperties of a QSize
167quint64 compareSubProperties(const QSize & r1, const QSize & r2)
168{
169 quint64 rc = 0;
170 COMPARE_SUBPROPERTY(r1, r2, width, rc, SubPropertyWidth)
171 COMPARE_SUBPROPERTY(r1, r2, height, rc, SubPropertyHeight)
172 return rc;
173}
174// find changed subproperties of a QSizePolicy
175quint64 compareSubProperties(const QSizePolicy & sp1, const QSizePolicy & sp2)
176{
177 quint64 rc = 0;
178 COMPARE_SUBPROPERTY(sp1, sp2, horizontalPolicy, rc, SubPropertyHSizePolicy)
179 COMPARE_SUBPROPERTY(sp1, sp2, horizontalStretch, rc, SubPropertyHStretch)
180 COMPARE_SUBPROPERTY(sp1, sp2, verticalPolicy, rc, SubPropertyVSizePolicy)
181 COMPARE_SUBPROPERTY(sp1, sp2, verticalStretch, rc, SubPropertyVStretch)
182 return rc;
183}
184// find changed subproperties of qdesigner_internal::PropertySheetStringValue
185quint64 compareSubProperties(const qdesigner_internal::PropertySheetStringValue & str1, const qdesigner_internal::PropertySheetStringValue & str2)
186{
187 quint64 rc = 0;
188 COMPARE_SUBPROPERTY(str1, str2, value, rc, SubPropertyStringValue)
189 COMPARE_SUBPROPERTY(str1, str2, comment, rc, SubPropertyStringComment)
190 COMPARE_SUBPROPERTY(str1, str2, translatable, rc, SubPropertyStringTranslatable)
191 COMPARE_SUBPROPERTY(str1, str2, disambiguation, rc, SubPropertyStringDisambiguation)
192 COMPARE_SUBPROPERTY(str1, str2, id, rc, SubPropertyStringId)
193 return rc;
194}
195// find changed subproperties of qdesigner_internal::PropertySheetStringListValue
196quint64 compareSubProperties(const qdesigner_internal::PropertySheetStringListValue & str1, const qdesigner_internal::PropertySheetStringListValue & str2)
197{
198 quint64 rc = 0;
199 COMPARE_SUBPROPERTY(str1, str2, value, rc, SubPropertyStringListValue)
200 COMPARE_SUBPROPERTY(str1, str2, comment, rc, SubPropertyStringListComment)
201 COMPARE_SUBPROPERTY(str1, str2, translatable, rc, SubPropertyStringListTranslatable)
202 COMPARE_SUBPROPERTY(str1, str2, disambiguation, rc, SubPropertyStringListDisambiguation)
203 COMPARE_SUBPROPERTY(str1, str2, id, rc, SubPropertyStringListId)
204 return rc;
205}
206// find changed subproperties of qdesigner_internal::PropertySheetKeySequenceValue
207quint64 compareSubProperties(const qdesigner_internal::PropertySheetKeySequenceValue & str1, const qdesigner_internal::PropertySheetKeySequenceValue & str2)
208{
209 quint64 rc = 0;
210 COMPARE_SUBPROPERTY(str1, str2, value, rc, SubPropertyKeySequenceValue)
211 COMPARE_SUBPROPERTY(str1, str2, comment, rc, SubPropertyKeySequenceComment)
212 COMPARE_SUBPROPERTY(str1, str2, translatable, rc, SubPropertyKeySequenceTranslatable)
213 COMPARE_SUBPROPERTY(str1, str2, disambiguation, rc, SubPropertyKeySequenceDisambiguation)
214 COMPARE_SUBPROPERTY(str1, str2, id, rc, SubPropertyKeySequenceId)
215 return rc;
216}
217
218// Compare font-subproperties taking the [undocumented]
219// resolve flag into account
220template <class Property>
221void compareFontSubProperty(const QFont & f1,
222 const QFont & f2,
223 Property (QFont::*getter) () const,
224 quint64 maskBit,
225 quint64 &mask)
226{
227 const bool f1Changed = f1.resolveMask() & maskBit;
228 const bool f2Changed = f2.resolveMask() & maskBit;
229 // Role has been set/reset in editor
230 if (f1Changed != f2Changed) {
231 mask |= maskBit;
232 } else {
233 // Was modified in both palettes: Compare values.
234 if (f1Changed && f2Changed && (f1.*getter)() != (f2.*getter)())
235 mask |= maskBit;
236 }
237}
238// find changed subproperties of a QFont
239quint64 compareSubProperties(const QFont & f1, const QFont & f2)
240{
241 quint64 rc = 0;
242 compareFontSubProperty(f1, f2, &QFont::family, QFontFamiliesResolved, rc);
243 compareFontSubProperty(f1, f2, &QFont::pointSize, QFont::SizeResolved, rc);
244 compareFontSubProperty(f1, f2, &QFont::weight, QFont::WeightResolved, rc);
245 compareFontSubProperty(f1, f2, &QFont::italic, QFont::StyleResolved, rc);
246 compareFontSubProperty(f1, f2, &QFont::underline, QFont::UnderlineResolved, rc);
247 compareFontSubProperty(f1, f2, &QFont::strikeOut, QFont::StrikeOutResolved, rc);
248 compareFontSubProperty(f1, f2, &QFont::kerning, QFont::KerningResolved, rc);
249 compareFontSubProperty(f1, f2, &QFont::styleStrategy, QFont::StyleStrategyResolved, rc);
250 compareFontSubProperty(f1, f2, &QFont::hintingPreference, QFont::HintingPreferenceResolved, rc);
251 if (debugPropertyCommands)
252 qDebug() << "compareSubProperties " << fontString(f1) << fontString(f2) << "\n\treturns " << fontMask(rc);
253 return rc;
254}
255
256// find changed subproperties of a QPalette taking the [undocumented] resolve flags into account
257quint64 compareSubProperties(const QPalette & p1, const QPalette & p2)
258{
259 quint64 rc = 0;
260 // generate a mask for each role
261 const auto p1Changed = p1.resolveMask();
262 const auto p2Changed = p2.resolveMask();
263
264 for (int r = 0; r < static_cast<int>(QPalette::NColorRoles); ++r) {
265 for (int g = 0; g < static_cast<int>(QPalette::NColorGroups); ++g) {
266 const auto role = static_cast<QPalette::ColorRole>(r);
267 const auto group = static_cast<QPalette::ColorGroup>(g);
268 const auto maskBit = qdesigner_internal::paletteResolveMask(group, role);
269 const bool p1RoleChanged = p1Changed & maskBit;
270 const bool p2RoleChanged = p2Changed & maskBit;
271 if (p1RoleChanged != p2RoleChanged // Role has been set/reset in editor
272 // Was modified in both palettes: Compare values.
273 || (p1RoleChanged && p2RoleChanged
274 && p1.brush(group, role).color() != p2.brush(group, role).color())) {
275 rc |= maskBit;
276 }
277 }
278 }
279
280 return rc;
281}
282
283// find changed subproperties of a QAlignment which is a flag combination of vertical and horizontal
284
285quint64 compareSubProperties(Qt::Alignment a1, Qt::Alignment a2)
286{
287 quint64 rc = 0;
288 if ((a1 & Qt::AlignHorizontal_Mask) != (a2 & Qt::AlignHorizontal_Mask))
289 rc |= SubPropertyHorizontalAlignment;
290 if ((a1 & Qt::AlignVertical_Mask) != (a2 & Qt::AlignVertical_Mask))
291 rc |= SubPropertyVerticalAlignment;
292 return rc;
293}
294
295Qt::Alignment variantToAlignment(const QVariant & q)
296{
297 return Qt::Alignment(qdesigner_internal::Utils::valueOf(q));
298}
299// find changed subproperties of a variant
300quint64 compareSubProperties(const QVariant & q1, const QVariant & q2, qdesigner_internal::SpecialProperty specialProperty)
301{
302 // Do not clobber new value in the comparison function in
303 // case someone sets a QString on a PropertySheetStringValue.
304 const int t1 = q1.metaType().id();
305 const int t2 = q2.metaType().id();
306 if (t1 != t2)
307 return SubPropertyAll;
308 switch (t1) {
309 case QMetaType::QRect:
310 return compareSubProperties(q1.toRect(), q2.toRect());
311 case QMetaType::QSize:
312 return compareSubProperties(q1.toSize(), q2.toSize());
313 case QMetaType::QSizePolicy:
314 return compareSubProperties(qvariant_cast<QSizePolicy>(q1), qvariant_cast<QSizePolicy>(q2));
315 case QMetaType::QFont:
316 return compareSubProperties(qvariant_cast<QFont>(q1), qvariant_cast<QFont>(q2));
317 case QMetaType::QPalette:
318 return compareSubProperties(qvariant_cast<QPalette>(q1), qvariant_cast<QPalette>(q2));
319 default:
320 if (q1.userType() == qMetaTypeId<qdesigner_internal::PropertySheetIconValue>())
321 return qvariant_cast<qdesigner_internal::PropertySheetIconValue>(q1).compare(qvariant_cast<qdesigner_internal::PropertySheetIconValue>(q2));
322 else if (q1.userType() == qMetaTypeId<qdesigner_internal::PropertySheetStringValue>())
323 return compareSubProperties(qvariant_cast<qdesigner_internal::PropertySheetStringValue>(q1), qvariant_cast<qdesigner_internal::PropertySheetStringValue>(q2));
324 else if (q1.userType() == qMetaTypeId<qdesigner_internal::PropertySheetStringListValue>())
325 return compareSubProperties(qvariant_cast<qdesigner_internal::PropertySheetStringListValue>(q1), qvariant_cast<qdesigner_internal::PropertySheetStringListValue>(q2));
326 else if (q1.userType() == qMetaTypeId<qdesigner_internal::PropertySheetKeySequenceValue>())
327 return compareSubProperties(qvariant_cast<qdesigner_internal::PropertySheetKeySequenceValue>(q1), qvariant_cast<qdesigner_internal::PropertySheetKeySequenceValue>(q2));
328 // Enumerations, flags
329 switch (specialProperty) {
330 case qdesigner_internal::SP_Alignment:
331 return compareSubProperties(variantToAlignment(q1), variantToAlignment(q2));
332 default:
333 break;
334 }
335 break;
336 }
337 return SubPropertyAll;
338}
339
340// Apply the sub property if mask flag is set in mask
341#define SET_SUBPROPERTY(rc, newValue, getter, setter, mask, maskFlag)
342 if ((mask) & (maskFlag)) rc.setter((newValue).getter());
343
344// apply changed subproperties to a rectangle
345QRect applyRectSubProperty(const QRect &oldValue, const QRect &newValue, unsigned mask)
346{
347 QRect rc = oldValue;
348 SET_SUBPROPERTY(rc, newValue, x, moveLeft, mask, SubPropertyX)
349 SET_SUBPROPERTY(rc, newValue, y, moveTop, mask, SubPropertyY)
350 SET_SUBPROPERTY(rc, newValue, width, setWidth, mask, SubPropertyWidth)
351 SET_SUBPROPERTY(rc, newValue, height, setHeight, mask, SubPropertyHeight)
352 return rc;
353}
354
355
356// apply changed subproperties to a rectangle QSize
357QSize applySizeSubProperty(const QSize &oldValue, const QSize &newValue, unsigned mask)
358{
359 QSize rc = oldValue;
360 SET_SUBPROPERTY(rc, newValue, width, setWidth, mask, SubPropertyWidth)
361 SET_SUBPROPERTY(rc, newValue, height, setHeight, mask, SubPropertyHeight)
362 return rc;
363}
364
365
366// apply changed subproperties to a SizePolicy
367QSizePolicy applySizePolicySubProperty(const QSizePolicy &oldValue, const QSizePolicy &newValue, unsigned mask)
368{
369 QSizePolicy rc = oldValue;
370 SET_SUBPROPERTY(rc, newValue, horizontalPolicy, setHorizontalPolicy, mask, SubPropertyHSizePolicy)
371 SET_SUBPROPERTY(rc, newValue, horizontalStretch, setHorizontalStretch, mask, SubPropertyHStretch)
372 SET_SUBPROPERTY(rc, newValue, verticalPolicy, setVerticalPolicy, mask, SubPropertyVSizePolicy)
373 SET_SUBPROPERTY(rc, newValue, verticalStretch, setVerticalStretch, mask, SubPropertyVStretch)
374 return rc;
375}
376
377// apply changed subproperties to a qdesigner_internal::PropertySheetStringValue
378qdesigner_internal::PropertySheetStringValue applyStringSubProperty(const qdesigner_internal::PropertySheetStringValue &oldValue,
379 const qdesigner_internal::PropertySheetStringValue &newValue, unsigned mask)
380{
381 qdesigner_internal::PropertySheetStringValue rc = oldValue;
382 SET_SUBPROPERTY(rc, newValue, value, setValue, mask, SubPropertyStringValue)
383 SET_SUBPROPERTY(rc, newValue, comment, setComment, mask, SubPropertyStringComment)
384 SET_SUBPROPERTY(rc, newValue, translatable, setTranslatable, mask, SubPropertyStringTranslatable)
385 SET_SUBPROPERTY(rc, newValue, disambiguation, setDisambiguation, mask, SubPropertyStringDisambiguation)
386 SET_SUBPROPERTY(rc, newValue, id, setId, mask, SubPropertyStringId)
387 return rc;
388}
389
390// apply changed subproperties to a qdesigner_internal::PropertySheetStringListValue
391qdesigner_internal::PropertySheetStringListValue applyStringListSubProperty(const qdesigner_internal::PropertySheetStringListValue &oldValue,
392 const qdesigner_internal::PropertySheetStringListValue &newValue, unsigned mask)
393{
394 qdesigner_internal::PropertySheetStringListValue rc = oldValue;
395 SET_SUBPROPERTY(rc, newValue, value, setValue, mask, SubPropertyStringListValue)
396 SET_SUBPROPERTY(rc, newValue, comment, setComment, mask, SubPropertyStringListComment)
397 SET_SUBPROPERTY(rc, newValue, translatable, setTranslatable, mask, SubPropertyStringListTranslatable)
398 SET_SUBPROPERTY(rc, newValue, disambiguation, setDisambiguation, mask, SubPropertyStringListDisambiguation)
399 SET_SUBPROPERTY(rc, newValue, id, setId, mask, SubPropertyStringListId)
400 return rc;
401}
402
403// apply changed subproperties to a qdesigner_internal::PropertySheetKeySequenceValue
404qdesigner_internal::PropertySheetKeySequenceValue applyKeySequenceSubProperty(const qdesigner_internal::PropertySheetKeySequenceValue &oldValue,
405 const qdesigner_internal::PropertySheetKeySequenceValue &newValue, unsigned mask)
406{
407 qdesigner_internal::PropertySheetKeySequenceValue rc = oldValue;
408 SET_SUBPROPERTY(rc, newValue, value, setValue, mask, SubPropertyKeySequenceValue)
409 SET_SUBPROPERTY(rc, newValue, comment, setComment, mask, SubPropertyKeySequenceComment)
410 SET_SUBPROPERTY(rc, newValue, translatable, setTranslatable, mask, SubPropertyKeySequenceTranslatable)
411 SET_SUBPROPERTY(rc, newValue, disambiguation, setDisambiguation, mask, SubPropertyKeySequenceDisambiguation)
412 SET_SUBPROPERTY(rc, newValue, id, setId, mask, SubPropertyKeySequenceId)
413 return rc;
414}
415
416// Apply the font-subproperties keeping the [undocumented]
417// resolve flag in sync (note that PropertySetterType might be something like const T&).
418template <class PropertyReturnType, class PropertySetterType>
419inline void setFontSubProperty(unsigned mask,
420 const QFont &newValue,
421 unsigned maskBit,
422 PropertyReturnType (QFont::*getter) () const,
423 void (QFont::*setter) (PropertySetterType),
424 QFont &value)
425{
426 if (mask & maskBit) {
427 (value.*setter)((newValue.*getter)());
428 // Set the resolve bit from NewValue in return value
429 uint r = value.resolveMask();
430 const bool origFlag = newValue.resolveMask() & maskBit;
431 if (origFlag)
432 r |= maskBit;
433 else
434 r &= ~maskBit;
435 value.setResolveMask(r);
436 if (debugPropertyCommands)
437 qDebug() << "setFontSubProperty " << fontMask(maskBit) << " resolve=" << origFlag;
438 }
439}
440// apply changed subproperties to a QFont
441QFont applyFontSubProperty(const QFont &oldValue, const QFont &newValue, unsigned mask)
442{
443 QFont rc = oldValue;
444 setFontSubProperty(mask, newValue, QFontFamiliesResolved, &QFont::family, &QFont::setFamily, rc);
445 setFontSubProperty(mask, newValue, QFont::SizeResolved, &QFont::pointSize, &QFont::setPointSize, rc);
446 setFontSubProperty(mask, newValue, QFont::WeightResolved, &QFont::weight, &QFont::setWeight, rc);
447 setFontSubProperty(mask, newValue, QFont::StyleResolved, &QFont::italic, &QFont::setItalic, rc);
448 setFontSubProperty(mask, newValue, QFont::UnderlineResolved, &QFont::underline, &QFont::setUnderline, rc);
449 setFontSubProperty(mask, newValue, QFont::StrikeOutResolved, &QFont::strikeOut, &QFont::setStrikeOut, rc);
450 setFontSubProperty(mask, newValue, QFont::KerningResolved, &QFont::kerning, &QFont::setKerning, rc);
451 setFontSubProperty(mask, newValue, QFont::StyleStrategyResolved, &QFont::styleStrategy, &QFont::setStyleStrategy, rc);
452 setFontSubProperty(mask, newValue, QFont::HintingPreferenceResolved, &QFont::hintingPreference, &QFont::setHintingPreference, rc);
453 if (debugPropertyCommands)
454 qDebug() << "applyFontSubProperty old " << fontMask(oldValue.resolveMask()) << " new " << fontMask(newValue.resolveMask()) << " return: " << fontMask(rc.resolveMask());
455 return rc;
456}
457
458// apply changed subproperties to a QPalette
459QPalette applyPaletteSubProperty(const QPalette &oldValue, const QPalette &newValue,
460 quint64 mask)
461{
462 QPalette rc = oldValue;
463 // apply a mask for each role/group
464 for (int r = 0; r < static_cast<int>(QPalette::NColorRoles); ++r) {
465 for (int g = 0; g < static_cast<int>(QPalette::NColorGroups); ++g) {
466 const auto role = static_cast<QPalette::ColorRole>(r);
467 const auto group = static_cast<QPalette::ColorGroup>(g);
468 const auto maskBit = qdesigner_internal::paletteResolveMask(group, role);
469 if (mask & maskBit) {
470 rc.setColor(group, role, newValue.color(group, role));
471 // Set the resolve bit from NewValue in return value
472 auto resolveMask = rc.resolveMask();
473 const bool origFlag = newValue.resolveMask() & maskBit;
474 if (origFlag)
475 resolveMask |= maskBit;
476 else
477 resolveMask &= ~maskBit;
478 rc.setResolveMask(resolveMask);
479 }
480 }
481 }
482 return rc;
483}
484
485// apply changed subproperties to a QAlignment which is a flag combination of vertical and horizontal
486Qt::Alignment applyAlignmentSubProperty(Qt::Alignment oldValue, Qt::Alignment newValue, unsigned mask)
487{
488 // easy: both changed.
489 if (mask == (SubPropertyHorizontalAlignment|SubPropertyVerticalAlignment))
490 return newValue;
491 // Change subprop
492 const Qt::Alignment changeMask = (mask & SubPropertyHorizontalAlignment) ? Qt::AlignHorizontal_Mask : Qt::AlignVertical_Mask;
493 const Qt::Alignment takeOverMask = (mask & SubPropertyHorizontalAlignment) ? Qt::AlignVertical_Mask : Qt::AlignHorizontal_Mask;
494 return (oldValue & takeOverMask) | (newValue & changeMask);
495}
496
497}
498
499namespace qdesigner_internal {
500
501// apply changed subproperties to a variant
502PropertyHelper::Value applySubProperty(const QVariant &oldValue, const QVariant &newValue,
503 qdesigner_internal::SpecialProperty specialProperty,
504 quint64 mask, bool changed)
505{
506 if (mask == SubPropertyAll)
507 return PropertyHelper::Value(newValue, changed);
508
509 switch (oldValue.metaType().id()) {
510 case QMetaType::QRect:
511 return PropertyHelper::Value(applyRectSubProperty(oldValue.toRect(), newValue.toRect(), mask), changed);
512 case QMetaType::QSize:
513 return PropertyHelper::Value(applySizeSubProperty(oldValue.toSize(), newValue.toSize(), mask), changed);
514 case QMetaType::QSizePolicy:
515 return PropertyHelper::Value(QVariant::fromValue(applySizePolicySubProperty(qvariant_cast<QSizePolicy>(oldValue), qvariant_cast<QSizePolicy>(newValue), mask)), changed);
516 case QMetaType::QFont: {
517 // Changed flag in case of font and palette depends on resolve mask only, not on the passed "changed" value.
518
519 // The first case: the user changed bold subproperty and then pressed reset button for this subproperty (not for
520 // the whole font property). We instantiate SetPropertyCommand passing changed=true. But in this case no
521 // subproperty is changed and the whole property should be marked an unchanged.
522
523 // The second case: there are 2 pushbuttons, for 1st the user set bold and italic subproperties,
524 // for the 2nd he set bold only. He does multiselection so that the current widget is the 2nd one.
525 // He press reset next to bold subproperty. In result the 2nd widget should have the whole
526 // font property marked as unchanged and the 1st widget should have the font property
527 // marked as changed and only italic subproperty should be marked as changed (the bold should be reset).
528
529 // The third case: there are 2 pushbuttons, for 1st the user set bold and italic subproperties,
530 // for the 2nd he set bold only. He does multiselection so that the current widget is the 2nd one.
531 // He press reset button for the whole font property. In result whole font properties for both
532 // widgets should be marked as unchanged.
533 QFont font = applyFontSubProperty(qvariant_cast<QFont>(oldValue), qvariant_cast<QFont>(newValue), mask);
534 return PropertyHelper::Value(QVariant::fromValue(font), font.resolveMask());
535 }
536 case QMetaType::QPalette: {
537 QPalette palette = applyPaletteSubProperty(qvariant_cast<QPalette>(oldValue), qvariant_cast<QPalette>(newValue), mask);
538 return PropertyHelper::Value(QVariant::fromValue(palette), palette.resolveMask());
539 }
540 default:
541 if (oldValue.userType() == qMetaTypeId<qdesigner_internal::PropertySheetIconValue>()) {
542 PropertySheetIconValue icon = qvariant_cast<qdesigner_internal::PropertySheetIconValue>(oldValue);
543 icon.assign(qvariant_cast<qdesigner_internal::PropertySheetIconValue>(newValue), mask);
544 return PropertyHelper::Value(QVariant::fromValue(icon), icon.mask());
545 } else if (oldValue.userType() == qMetaTypeId<qdesigner_internal::PropertySheetStringValue>()) {
546 qdesigner_internal::PropertySheetStringValue str = applyStringSubProperty(
547 qvariant_cast<qdesigner_internal::PropertySheetStringValue>(oldValue),
548 qvariant_cast<qdesigner_internal::PropertySheetStringValue>(newValue), mask);
549 return PropertyHelper::Value(QVariant::fromValue(str), changed);
550 } else if (oldValue.userType() == qMetaTypeId<qdesigner_internal::PropertySheetStringListValue>()) {
551 qdesigner_internal::PropertySheetStringListValue str = applyStringListSubProperty(
552 qvariant_cast<qdesigner_internal::PropertySheetStringListValue>(oldValue),
553 qvariant_cast<qdesigner_internal::PropertySheetStringListValue>(newValue), mask);
554 return PropertyHelper::Value(QVariant::fromValue(str), changed);
555 } else if (oldValue.userType() == qMetaTypeId<qdesigner_internal::PropertySheetKeySequenceValue>()) {
556 qdesigner_internal::PropertySheetKeySequenceValue key = applyKeySequenceSubProperty(
557 qvariant_cast<qdesigner_internal::PropertySheetKeySequenceValue>(oldValue),
558 qvariant_cast<qdesigner_internal::PropertySheetKeySequenceValue>(newValue), mask);
559 return PropertyHelper::Value(QVariant::fromValue(key), changed);
560 }
561 // Enumerations, flags
562 switch (specialProperty) {
564 qdesigner_internal::PropertySheetFlagValue f = qvariant_cast<qdesigner_internal::PropertySheetFlagValue>(oldValue);
565 f.value = applyAlignmentSubProperty(variantToAlignment(oldValue), variantToAlignment(newValue), mask);
566 QVariant v;
567 v.setValue(f);
568 return PropertyHelper::Value(v, changed);
569 }
570 default:
571 break;
572 }
573 break;
574 }
575 return PropertyHelper::Value(newValue, changed);
576
577}
578// figure out special property
579enum SpecialProperty getSpecialProperty(const QString& propertyName)
580{
581 if (propertyName == "objectName"_L1)
582 return SP_ObjectName;
583 if (propertyName == "layoutName"_L1)
584 return SP_LayoutName;
585 if (propertyName == "spacerName"_L1)
586 return SP_SpacerName;
587 if (propertyName == "icon"_L1)
588 return SP_Icon;
589 if (propertyName == "currentTabName"_L1)
590 return SP_CurrentTabName;
591 if (propertyName == "currentItemName"_L1)
592 return SP_CurrentItemName;
593 if (propertyName == "currentPageName"_L1)
594 return SP_CurrentPageName;
595 if (propertyName == "geometry"_L1)
596 return SP_Geometry;
597 if (propertyName == "windowTitle"_L1)
598 return SP_WindowTitle;
599 if (propertyName == "minimumSize"_L1)
600 return SP_MinimumSize;
601 if (propertyName == "maximumSize"_L1)
602 return SP_MaximumSize;
603 if (propertyName == "alignment"_L1)
604 return SP_Alignment;
605 if (propertyName == "autoDefault"_L1)
606 return SP_AutoDefault;
607 if (propertyName == "shortcut"_L1)
608 return SP_Shortcut;
609 if (propertyName == "orientation"_L1)
610 return SP_Orientation;
611 return SP_None;
612}
613
614
642
647
648// Set widget value, apply corrections and checks in case of main window.
651{
652
653 bool isMainContainer = false;
655 if (cursor->isWidgetSelected(w)) {
657 isMainContainer = true;
658 }
659 }
660 }
661 if (!isMainContainer)
662 return;
663
665 if (!container)
666 return;
667
668
669 switch (specialProperty) {
670 case SP_MinimumSize: {
671 const QSize size = checkSize(value.toSize());
673 }
674
675 break;
676 case SP_MaximumSize: {
677 QSize fs, cs;
678 checkSizes(fw, value.toSize(), &fs, &cs);
682
683 }
684 break;
685 case SP_Geometry: {
686 QRect r = value.toRect();
687 QSize fs, cs;
688 checkSizes(fw, r.size(), &fs, &cs);
690 r.setSize(fs);
692 }
693 break;
694 default:
695 break;
696 }
697}
698
699unsigned PropertyHelper::updateMask() const
700{
701 unsigned rc = 0;
702 switch (m_specialProperty) {
703 case SP_ObjectName:
704 case SP_LayoutName:
705 case SP_SpacerName:
711 break;
712 case SP_Icon:
715 break;
716 case SP_Orientation: // for updating splitter icon
718 break;
719 default:
720 break;
721
722 }
723 return rc;
724}
725
726
728{
729 return m_object == other.m_object && m_index == other.m_index;
730}
731
733{
734 a->setData(QVariant(true)); // this triggers signal "changed" in QAction
735 a->setData(QVariant(false));
736}
737
738// Update the object to reflect the changes
740{
742 qDebug() << "PropertyHelper::updateObject(" << m_object->objectName() << ") " << oldValue << " -> " << newValue;
743 }
744 switch (m_objectType) {
745 case OT_Widget: {
746 switch (m_specialProperty) {
747 case SP_ObjectName: {
751 }
752 break;
753 default:
754 break;
755 }
756 } break;
758 case OT_FreeAction:
759 // SP_Shortcut is a fake property, so, QAction::changed does not trigger.
762 break;
763 default:
764 break;
765 }
766
767 switch (m_specialProperty) {
768 case SP_ObjectName:
769 case SP_LayoutName:
770 case SP_SpacerName:
775 }
776 break;
777 default:
778 break;
779 }
780}
781
783{
784 switch (m_specialProperty) {
785 case SP_SpacerName:
786 if (object->isWidgetType()) {
787 if (Spacer *sp = qobject_cast<Spacer *>(object)) {
789 return;
790 }
791 }
793 break;
794 case SP_LayoutName: // Layout name is invoked on the parent widget.
795 if (object->isWidgetType()) {
796 const QWidget * w = qobject_cast<const QWidget *>(object);
797 if (QLayout *wlayout = w->layout()) {
799 return;
800 }
801 }
803 break;
804 case SP_ObjectName:
806 break;
807 default:
808 break;
809 }
810}
811
823
824// Apply the value and update. Returns corrected value
826{
828 qDebug() << "PropertyHelper::applyValue(" << m_object << ") " << oldValue << " -> " << newValue.first << " changed=" << newValue.second;
829 }
830
831 if (m_objectType == OT_Widget) {
833 }
834
837
838 switch (m_specialProperty) {
839 case SP_LayoutName:
840 case SP_ObjectName:
841 case SP_SpacerName:
844 break;
845 default:
846 break;
847 }
848
850 return newValue;
851}
852
857
858// find the default value in widget DB in case PropertySheet::reset fails
860{
862 // AutoDefault defaults to true on dialogs
863 const bool isDialog = qobject_cast<const QDialog *>(fw->mainContainer());
864 return QVariant(isDialog);
865 }
866
868 if (item_idx == -1)
869 return m_oldValue.first; // We simply don't know the value in this case
870
875
877 return QColor();
878
879 return m_oldValue.first; // Again, we just don't know
880}
881
915
916// ---- PropertyListCommand::PropertyDescription(
917
918
928
933
939
940
941// ---- PropertyListCommand
947
952
957
958// add an object
960{
963
964 const int index = sheet->indexOf(propertyName);
965 if (index == -1)
966 return false;
967
968 if (!sheet->isEnabled(index))
969 return false;
970
972
974 // first entry
976 } else {
977 // checks: mismatch or only one object in case of name
980 return false;
981 }
982
985 return true;
986}
987
994
995// Init from a list and make sure referenceObject is added first to obtain the right property group
997{
999
1000 // Ensure the referenceObject (property editor) is first, so the right property group is chosen.
1001 if (referenceObject) {
1003 return false;
1004 }
1005 for (QObject *o : list) {
1006 if (o != referenceObject)
1008 }
1009
1010 return !m_propertyHelperList.empty();
1011}
1012
1013
1019
1025
1031// ----- SetValueFunction: Set a new value when applied to a PropertyHelper.
1033public:
1034 SetValueFunction(QDesignerFormWindowInterface *formWindow, const PropertyHelper::Value &newValue,
1035 quint64 subPropertyMask);
1036
1038private:
1039 QDesignerFormWindowInterface *m_formWindow;
1041 const quint64 m_subPropertyMask;
1042};
1043
1044
1045SetValueFunction::SetValueFunction(QDesignerFormWindowInterface *formWindow,
1046 const PropertyHelper::Value &newValue,
1047 quint64 subPropertyMask) :
1048 m_formWindow(formWindow),
1051{
1052}
1053
1054PropertyHelper::Value SetValueFunction::operator()(PropertyHelper &ph) {
1055 return ph.setValue(m_formWindow, m_newValue.first, m_newValue.second, m_subPropertyMask);
1056}
1057
1058// ----- UndoSetValueFunction: Restore old value when applied to a PropertyHelper.
1060public:
1061 UndoSetValueFunction(QDesignerFormWindowInterface *formWindow) : m_formWindow(formWindow) {}
1063private:
1065};
1066
1067// ----- RestoreDefaultFunction: Restore default value when applied to a PropertyHelper.
1075
1076// ----- changePropertyList: Iterates over a sequence of PropertyHelpers and
1077// applies a function to them.
1078// The function returns the corrected value which is then set in the property editor.
1079// Returns a combination of update flags.
1080template <class PropertyListIterator, class Function>
1081 unsigned changePropertyList(QDesignerFormEditorInterface *core,
1082 const QString &propertyName,
1083 PropertyListIterator begin,
1084 PropertyListIterator end,
1085 Function function)
1086{
1087 unsigned updateMask = 0;
1088 QDesignerPropertyEditorInterface *propertyEditor = core->propertyEditor();
1089 bool updatedPropertyEditor = false;
1090
1091 for (auto it = begin; it != end; ++it) {
1092 PropertyHelper *ph = it->get();
1093 if (QObject* object = ph->object()) { // Might have been deleted in the meantime
1094 const PropertyHelper::Value newValue = function( *ph );
1095 updateMask |= ph->updateMask();
1096 // Update property editor if it is the current object
1097 if (!updatedPropertyEditor && propertyEditor && object == propertyEditor->object()) {
1098 propertyEditor->setPropertyValue(propertyName, newValue.first, newValue.second);
1099 updatedPropertyEditor = true;
1100 }
1101 }
1102 }
1103 if (!updatedPropertyEditor) updateMask |= PropertyHelper::UpdatePropertyEditor;
1104 return updateMask;
1105}
1106
1107
1108// set a new value, return update mask
1119
1120// restore old value, return update mask
1122{
1124 qDebug() << "PropertyListCommand::restoreOldValue()";
1125
1129}
1130// set default value, return update mask
1132{
1134 qDebug() << "PropertyListCommand::restoreDefaultValue()";
1135
1139}
1140
1141// update
1143{
1145 qDebug() << "PropertyListCommand::update(" << updateMask << ')';
1146
1150 }
1151
1153 // this is needed when f.ex. undo, changes parent's palette, but
1154 // the child is the active widget,
1155 // TODO: current object?
1158 }
1159 }
1160}
1161
1169
1170// check if lists are aequivalent for command merging (same widgets and props)
1172{
1174 return false;
1175 for (size_t i = 0; i < m_propertyHelperList.size(); ++i) {
1177 return false;
1178 }
1179 return true;
1180}
1181
1182// ---- SetPropertyCommand ----
1189
1191{
1193
1195
1197 if (!add(object, apropertyName))
1198 return false;
1199
1201 return true;
1202}
1203
1206{
1208 return false;
1209
1211
1213 qDebug() << "SetPropertyCommand::init()" << propertyHelperList().size() << '/' << list.size() << " reference " << referenceObject;
1214
1216
1219 return true;
1220}
1221
1223{
1224 // figure out the mask of changed sub properties when comparing newValue to the current value of the reference object.
1225 if (!referenceObject)
1226 return SubPropertyAll;
1227
1229 Q_ASSERT(sheet);
1230
1231 const int index = sheet->indexOf(propertyName());
1232 if (index == -1 || !sheet->isVisible(index))
1233 return SubPropertyAll;
1234
1236}
1237
1239{
1240 if (propertyHelperList().size() == 1) {
1241 setText(QApplication::translate("Command", "Changed '%1' of '%2'")
1243 } else {
1244 int count = static_cast<int>(propertyHelperList().size());
1245 setText(QCoreApplication::translate("Command", "Changed '%1' of %n objects", "", count).arg(propertyName()));
1246 }
1247}
1248
1256
1257
1259{
1260 return 1976;
1261}
1262
1267
1269{
1270 if (id() != other->id() || !formWindow()->isDirty())
1271 return false;
1272
1273 // Merging: When for example when the user types ahead in an inplace-editor,
1274 // it makes sense to merge all the generated commands containing the one-character changes.
1275 // In the case of subproperties, if the user changes the font size from 10 to 30 via 20
1276 // and then changes to bold, it makes sense to merge the font size commands only.
1277 // This is why the m_subPropertyMask is checked.
1278
1279 const SetPropertyCommand *cmd = static_cast<const SetPropertyCommand*>(other);
1283 return false;
1284
1286 if (!newValue.isValid())
1287 return false;
1291 qDebug() << "SetPropertyCommand::mergeWith() succeeded " << propertyName();
1292
1293 return true;
1294}
1295
1296// ---- ResetPropertyCommand ----
1301
1303{
1305
1307 if (!add(object, apropertyName))
1308 return false;
1309
1311 return true;
1312}
1313
1315{
1316 QObjectList modifiedList = list; // filter out modified properties
1317 for (auto it = modifiedList.begin(); it != modifiedList.end() ; ) {
1319 Q_ASSERT(sheet);
1320 const int index = sheet->indexOf(apropertyName);
1321 if (index == -1 || !sheet->isChanged(index))
1323 else
1324 ++it;
1325 }
1327 referenceObject = nullptr;
1329 return false;
1330
1332 qDebug() << "ResetPropertyCommand::init()" << propertyHelperList().size() << '/' << list.size();
1333
1335 return true;
1336}
1337
1339{
1340 if (propertyHelperList().size() == 1) {
1341 setText(QCoreApplication::translate("Command", "Reset '%1' of '%2'")
1343 } else {
1344 int count = static_cast<int>(propertyHelperList().size());
1345 setText(QCoreApplication::translate("Command", "Reset '%1' of %n objects", "", count).arg(propertyName()));
1346 }
1347}
1348
1356
1362
1397
1410
1424
1426{
1427 if (m_selection.size() == 1) {
1428 setText(QApplication::translate("Command", "Add dynamic property '%1' to '%2'")
1430 } else {
1431 int count = m_selection.size();
1432 setText(QCoreApplication::translate("Command", "Add dynamic property '%1' to %n objects", "", count)
1434 }
1435}
1436
1437
1443
1445 const QString &propertyName)
1446{
1449
1455
1457
1460 return false;
1461
1464
1465 for (QObject *obj : selection) {
1467 continue;
1468
1475 }
1476
1478 return true;
1479}
1480
1495
1511
1513{
1514 if (m_objectToValueAndChanged.size() == 1) {
1515 setText(QApplication::translate("Command",
1516 "Remove dynamic property '%1' from '%2'")
1518 } else {
1520 setText(QApplication::translate("Command",
1521 "Remove dynamic property '%1' from %n objects", "", count)
1523 }
1524}
1525
1526
1527} // namespace qdesigner_internal
1528
1529QT_END_NAMESPACE
RestoreDefaultFunction(QDesignerFormWindowInterface *formWindow)
SetValueFunction(QDesignerFormWindowInterface *formWindow, const PropertyHelper::Value &newValue, quint64 subPropertyMask)
UndoSetValueFunction(QDesignerFormWindowInterface *formWindow)
Auxiliary methods to store/retrieve settings.
PropertyHelper::Value applySubProperty(const QVariant &oldValue, const QVariant &newValue, qdesigner_internal::SpecialProperty specialProperty, quint64 mask, bool changed)
enum SpecialProperty getSpecialProperty(const QString &propertyName)
unsigned changePropertyList(QDesignerFormEditorInterface *core, const QString &propertyName, PropertyListIterator begin, PropertyListIterator end, Function function)
#define SET_SUBPROPERTY(rc, newValue, getter, setter, mask, maskFlag)
#define COMPARE_SUBPROPERTY(object1, object2, getter, mask, maskFlag)