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