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 "propertyeditor.h"
30
31#include "qttreepropertybrowser.h"
32#include "qtbuttonpropertybrowser.h"
33#include "qtvariantproperty.h"
34#include "designerpropertymanager.h"
35#include "qdesigner_propertysheet_p.h"
36#include "formwindowbase_p.h"
37
38#include "newdynamicpropertydialog.h"
39#include "dynamicpropertysheet.h"
40#include "shared_enums_p.h"
41
42// sdk
43#include <QtDesigner/abstractformeditor.h>
44#include <QtDesigner/abstractformwindowmanager.h>
45#include <QtDesigner/qextensionmanager.h>
46#include <QtDesigner/propertysheet.h>
47#include <QtDesigner/abstractwidgetdatabase.h>
48#include <QtDesigner/abstractsettings.h>
49// shared
50#include <qdesigner_utils_p.h>
51#include <qdesigner_propertycommand_p.h>
52#include <metadatabase_p.h>
53#include <iconloader_p.h>
54#include <widgetfactory_p.h>
55
56#include <QtWidgets/qaction.h>
57#include <QtWidgets/qlineedit.h>
58#include <QtWidgets/qmenu.h>
59#include <QtWidgets/qapplication.h>
60#include <QtWidgets/qboxlayout.h>
61#include <QtWidgets/qscrollarea.h>
62#include <QtWidgets/qstackedwidget.h>
63#include <QtWidgets/qtoolbar.h>
64#include <QtWidgets/qtoolbutton.h>
65#include <QtWidgets/qactiongroup.h>
66#include <QtWidgets/qlabel.h>
67#include <QtGui/qpainter.h>
68
69#include <QtCore/qdebug.h>
70#include <QtCore/qtextstream.h>
71
72static const char *SettingsGroupC = "PropertyEditor";
73static const char *ViewKeyC = "View";
74static const char *ColorKeyC = "Colored";
75static const char *SortedKeyC = "Sorted";
76static const char *ExpansionKeyC = "ExpandedItems";
77static const char *SplitterPositionKeyC = "SplitterPosition";
78
79enum SettingsView { TreeView, ButtonView };
80
81QT_BEGIN_NAMESPACE
82
83// ---------------------------------------------------------------------------------
84
85namespace qdesigner_internal {
86
87// ----------- ElidingLabel
88// QLabel does not support text eliding so we need a helper class
89
90class ElidingLabel : public QWidget
91{
92public:
93 explicit ElidingLabel(const QString &text = QString(),
94 QWidget *parent = nullptr) : QWidget(parent), m_text(text)
95 { setContentsMargins(left: 3, top: 2, right: 3, bottom: 2); }
96
97 void setText(const QString &text) {
98 m_text = text;
99 updateGeometry();
100 }
101 void setElidemode(Qt::TextElideMode mode) {
102 m_mode = mode;
103 updateGeometry();
104 }
105
106protected:
107 QSize sizeHint() const override;
108 void paintEvent(QPaintEvent *e) override;
109
110private:
111 QString m_text;
112 Qt::TextElideMode m_mode = Qt::ElideRight;
113};
114
115QSize ElidingLabel::sizeHint() const
116{
117 QSize size = fontMetrics().boundingRect(text: m_text).size();
118 size += QSize(contentsMargins().left() + contentsMargins().right(),
119 contentsMargins().top() + contentsMargins().bottom());
120 return size;
121}
122
123void ElidingLabel::paintEvent(QPaintEvent *) {
124 QPainter painter(this);
125 painter.setPen(QColor(0, 0, 0, 60));
126 painter.setBrush(QColor(255, 255, 255, 40));
127 painter.drawRect(r: rect().adjusted(xp1: 0, yp1: 0, xp2: -1, yp2: -1));
128 painter.setPen(palette().windowText().color());
129 painter.drawText(r: contentsRect(), flags: Qt::AlignLeft,
130 text: fontMetrics().elidedText(text: m_text, mode: Qt::ElideRight, width: width(), flags: 0));
131}
132
133
134// ----------- PropertyEditor::Strings
135
136PropertyEditor::Strings::Strings() :
137 m_fontProperty(QStringLiteral("font")),
138 m_qLayoutWidget(QStringLiteral("QLayoutWidget")),
139 m_designerPrefix(QStringLiteral("QDesigner")),
140 m_layout(QStringLiteral("Layout")),
141 m_validationModeAttribute(QStringLiteral("validationMode")),
142 m_fontAttribute(QStringLiteral("font")),
143 m_superPaletteAttribute(QStringLiteral("superPalette")),
144 m_enumNamesAttribute(QStringLiteral("enumNames")),
145 m_resettableAttribute(QStringLiteral("resettable")),
146 m_flagsAttribute(QStringLiteral("flags"))
147{
148 m_alignmentProperties.insert(QStringLiteral("alignment"));
149 m_alignmentProperties.insert(QStringLiteral("layoutLabelAlignment")); // QFormLayout
150 m_alignmentProperties.insert(QStringLiteral("layoutFormAlignment"));
151}
152
153// ----------- PropertyEditor
154
155QDesignerMetaDataBaseItemInterface* PropertyEditor::metaDataBaseItem() const
156{
157 QObject *o = object();
158 if (!o)
159 return nullptr;
160 QDesignerMetaDataBaseInterface *db = core()->metaDataBase();
161 if (!db)
162 return nullptr;
163 return db->item(object: o);
164}
165
166void PropertyEditor::setupStringProperty(QtVariantProperty *property, bool isMainContainer)
167{
168 const StringPropertyParameters params = textPropertyValidationMode(core: core(), object: m_object, propertyName: property->propertyName(), isMainContainer);
169 // Does a meta DB entry exist - add comment
170 const bool hasComment = params.second;
171 property->setAttribute(attribute: m_strings.m_validationModeAttribute, value: params.first);
172 // assuming comment cannot appear or disappear for the same property in different object instance
173 if (!hasComment)
174 qDeleteAll(c: property->subProperties());
175}
176
177void PropertyEditor::setupPaletteProperty(QtVariantProperty *property)
178{
179 QPalette superPalette = QPalette();
180 QWidget *currentWidget = qobject_cast<QWidget *>(o: m_object);
181 if (currentWidget) {
182 if (currentWidget->isWindow())
183 superPalette = QApplication::palette(currentWidget);
184 else {
185 if (currentWidget->parentWidget())
186 superPalette = currentWidget->parentWidget()->palette();
187 }
188 }
189 m_updatingBrowser = true;
190 property->setAttribute(attribute: m_strings.m_superPaletteAttribute, value: superPalette);
191 m_updatingBrowser = false;
192}
193
194static inline QToolButton *createDropDownButton(QAction *defaultAction, QWidget *parent = nullptr)
195{
196 QToolButton *rc = new QToolButton(parent);
197 rc->setDefaultAction(defaultAction);
198 rc->setPopupMode(QToolButton::InstantPopup);
199 return rc;
200}
201
202PropertyEditor::PropertyEditor(QDesignerFormEditorInterface *core, QWidget *parent, Qt::WindowFlags flags) :
203 QDesignerPropertyEditor(parent, flags),
204 m_core(core),
205 m_propertyManager(new DesignerPropertyManager(m_core, this)),
206 m_stackedWidget(new QStackedWidget),
207 m_filterWidget(new QLineEdit),
208 m_addDynamicAction(new QAction(createIconSet(QStringLiteral("plus.png")), tr(s: "Add Dynamic Property..."), this)),
209 m_removeDynamicAction(new QAction(createIconSet(QStringLiteral("minus.png")), tr(s: "Remove Dynamic Property"), this)),
210 m_sortingAction(new QAction(createIconSet(QStringLiteral("sort.png")), tr(s: "Sorting"), this)),
211 m_coloringAction(new QAction(createIconSet(QStringLiteral("color.png")), tr(s: "Color Groups"), this)),
212 m_treeAction(new QAction(tr(s: "Tree View"), this)),
213 m_buttonAction(new QAction(tr(s: "Drop Down Button View"), this)),
214 m_classLabel(new ElidingLabel)
215{
216 QVector<QColor> colors;
217 colors.reserve(asize: 6);
218 colors.push_back(t: QColor(255, 230, 191));
219 colors.push_back(t: QColor(255, 255, 191));
220 colors.push_back(t: QColor(191, 255, 191));
221 colors.push_back(t: QColor(199, 255, 255));
222 colors.push_back(t: QColor(234, 191, 255));
223 colors.push_back(t: QColor(255, 191, 239));
224 m_colors.reserve(asize: colors.count());
225 const int darknessFactor = 250;
226 for (int i = 0; i < colors.count(); i++) {
227 const QColor &c = colors.at(i);
228 m_colors.push_back(t: qMakePair(x: c, y: c.darker(f: darknessFactor)));
229 }
230 QColor dynamicColor(191, 207, 255);
231 QColor layoutColor(255, 191, 191);
232 m_dynamicColor = qMakePair(x: dynamicColor, y: dynamicColor.darker(f: darknessFactor));
233 m_layoutColor = qMakePair(x: layoutColor, y: layoutColor.darker(f: darknessFactor));
234
235 updateForegroundBrightness();
236
237 QActionGroup *actionGroup = new QActionGroup(this);
238
239 m_treeAction->setCheckable(true);
240 m_treeAction->setIcon(createIconSet(QStringLiteral("widgets/listview.png")));
241 m_buttonAction->setCheckable(true);
242 m_buttonAction->setIcon(createIconSet(QStringLiteral("dropdownbutton.png")));
243
244 actionGroup->addAction(a: m_treeAction);
245 actionGroup->addAction(a: m_buttonAction);
246 connect(sender: actionGroup, signal: &QActionGroup::triggered,
247 receiver: this, slot: &PropertyEditor::slotViewTriggered);
248
249 // Add actions
250 QActionGroup *addDynamicActionGroup = new QActionGroup(this);
251 connect(sender: addDynamicActionGroup, signal: &QActionGroup::triggered,
252 receiver: this, slot: &PropertyEditor::slotAddDynamicProperty);
253
254 QMenu *addDynamicActionMenu = new QMenu(this);
255 m_addDynamicAction->setMenu(addDynamicActionMenu);
256 m_addDynamicAction->setEnabled(false);
257 QAction *addDynamicAction = addDynamicActionGroup->addAction(text: tr(s: "String..."));
258 addDynamicAction->setData(static_cast<int>(QVariant::String));
259 addDynamicActionMenu->addAction(action: addDynamicAction);
260 addDynamicAction = addDynamicActionGroup->addAction(text: tr(s: "Bool..."));
261 addDynamicAction->setData(static_cast<int>(QVariant::Bool));
262 addDynamicActionMenu->addAction(action: addDynamicAction);
263 addDynamicActionMenu->addSeparator();
264 addDynamicAction = addDynamicActionGroup->addAction(text: tr(s: "Other..."));
265 addDynamicAction->setData(static_cast<int>(QVariant::Invalid));
266 addDynamicActionMenu->addAction(action: addDynamicAction);
267 // remove
268 m_removeDynamicAction->setEnabled(false);
269 connect(sender: m_removeDynamicAction, signal: &QAction::triggered, receiver: this, slot: &PropertyEditor::slotRemoveDynamicProperty);
270 // Configure
271 QAction *configureAction = new QAction(tr(s: "Configure Property Editor"), this);
272 configureAction->setIcon(createIconSet(QStringLiteral("configure.png")));
273 QMenu *configureMenu = new QMenu(this);
274 configureAction->setMenu(configureMenu);
275
276 m_sortingAction->setCheckable(true);
277 connect(sender: m_sortingAction, signal: &QAction::toggled, receiver: this, slot: &PropertyEditor::slotSorting);
278
279 m_coloringAction->setCheckable(true);
280 connect(sender: m_coloringAction, signal: &QAction::toggled, receiver: this, slot: &PropertyEditor::slotColoring);
281
282 configureMenu->addAction(action: m_sortingAction);
283 configureMenu->addAction(action: m_coloringAction);
284 configureMenu->addSeparator();
285 configureMenu->addAction(action: m_treeAction);
286 configureMenu->addAction(action: m_buttonAction);
287 // Assemble toolbar
288 QToolBar *toolBar = new QToolBar;
289 toolBar->addWidget(widget: m_filterWidget);
290 toolBar->addWidget(widget: createDropDownButton(defaultAction: m_addDynamicAction));
291 toolBar->addAction(action: m_removeDynamicAction);
292 toolBar->addWidget(widget: createDropDownButton(defaultAction: configureAction));
293 // Views
294 QScrollArea *buttonScroll = new QScrollArea(m_stackedWidget);
295 m_buttonBrowser = new QtButtonPropertyBrowser(buttonScroll);
296 buttonScroll->setWidgetResizable(true);
297 buttonScroll->setWidget(m_buttonBrowser);
298 m_buttonIndex = m_stackedWidget->addWidget(w: buttonScroll);
299 connect(sender: m_buttonBrowser, signal: &QtAbstractPropertyBrowser::currentItemChanged,
300 receiver: this, slot: &PropertyEditor::slotCurrentItemChanged);
301
302 m_treeBrowser = new QtTreePropertyBrowser(m_stackedWidget);
303 m_treeBrowser->setRootIsDecorated(false);
304 m_treeBrowser->setPropertiesWithoutValueMarked(true);
305 m_treeBrowser->setResizeMode(QtTreePropertyBrowser::Interactive);
306 m_treeIndex = m_stackedWidget->addWidget(w: m_treeBrowser);
307 connect(sender: m_treeBrowser, signal: &QtAbstractPropertyBrowser::currentItemChanged,
308 receiver: this, slot: &PropertyEditor::slotCurrentItemChanged);
309 m_filterWidget->setPlaceholderText(tr(s: "Filter"));
310 m_filterWidget->setClearButtonEnabled(true);
311 connect(sender: m_filterWidget, signal: &QLineEdit::textChanged, receiver: this, slot: &PropertyEditor::setFilter);
312
313 QVBoxLayout *layout = new QVBoxLayout(this);
314 layout->addWidget(toolBar);
315 layout->addWidget(m_classLabel);
316 layout->addSpacerItem(spacerItem: new QSpacerItem(0,1));
317 layout->addWidget(m_stackedWidget);
318 layout->setContentsMargins(QMargins());
319 layout->setSpacing(0);
320
321 m_treeFactory = new DesignerEditorFactory(m_core, this);
322 m_treeFactory->setSpacing(0);
323 m_groupFactory = new DesignerEditorFactory(m_core, this);
324 QtVariantPropertyManager *variantManager = m_propertyManager;
325 m_buttonBrowser->setFactoryForManager(manager: variantManager, factory: m_groupFactory);
326 m_treeBrowser->setFactoryForManager(manager: variantManager, factory: m_treeFactory);
327
328 m_stackedWidget->setCurrentIndex(m_treeIndex);
329 m_currentBrowser = m_treeBrowser;
330 m_treeAction->setChecked(true);
331
332 connect(sender: m_groupFactory, signal: &DesignerEditorFactory::resetProperty,
333 receiver: this, slot: &PropertyEditor::slotResetProperty);
334 connect(sender: m_treeFactory, signal: &DesignerEditorFactory::resetProperty,
335 receiver: this, slot: &PropertyEditor::slotResetProperty);
336 connect(sender: m_propertyManager, signal: &DesignerPropertyManager::valueChanged,
337 receiver: this, slot: &PropertyEditor::slotValueChanged);
338
339 // retrieve initial settings
340 QDesignerSettingsInterface *settings = m_core->settingsManager();
341 settings->beginGroup(prefix: QLatin1String(SettingsGroupC));
342 const SettingsView view = settings->value(key: QLatin1String(ViewKeyC), defaultValue: TreeView).toInt() == TreeView ? TreeView : ButtonView;
343 // Coloring not available unless treeview and not sorted
344 m_sorting = settings->value(key: QLatin1String(SortedKeyC), defaultValue: false).toBool();
345 m_coloring = settings->value(key: QLatin1String(ColorKeyC), defaultValue: true).toBool();
346 const QVariantMap expansionState = settings->value(key: QLatin1String(ExpansionKeyC), defaultValue: QVariantMap()).toMap();
347 const int splitterPosition = settings->value(key: QLatin1String(SplitterPositionKeyC), defaultValue: 150).toInt();
348 settings->endGroup();
349 // Apply settings
350 m_sortingAction->setChecked(m_sorting);
351 m_coloringAction->setChecked(m_coloring);
352 m_treeBrowser->setSplitterPosition(splitterPosition);
353 switch (view) {
354 case TreeView:
355 m_currentBrowser = m_treeBrowser;
356 m_stackedWidget->setCurrentIndex(m_treeIndex);
357 m_treeAction->setChecked(true);
358 break;
359 case ButtonView:
360 m_currentBrowser = m_buttonBrowser;
361 m_stackedWidget->setCurrentIndex(m_buttonIndex);
362 m_buttonAction->setChecked(true);
363 break;
364 }
365 // Restore expansionState from QVariant map
366 if (!expansionState.isEmpty()) {
367 const QVariantMap::const_iterator cend = expansionState.constEnd();
368 for (QVariantMap::const_iterator it = expansionState.constBegin(); it != cend; ++it)
369 m_expansionState.insert(akey: it.key(), avalue: it.value().toBool());
370 }
371 updateActionsState();
372}
373
374PropertyEditor::~PropertyEditor()
375{
376 storeExpansionState();
377 saveSettings();
378}
379
380void PropertyEditor::saveSettings() const
381{
382 QDesignerSettingsInterface *settings = m_core->settingsManager();
383 settings->beginGroup(prefix: QLatin1String(SettingsGroupC));
384 settings->setValue(key: QLatin1String(ViewKeyC), value: QVariant(m_treeAction->isChecked() ? TreeView : ButtonView));
385 settings->setValue(key: QLatin1String(ColorKeyC), value: QVariant(m_coloring));
386 settings->setValue(key: QLatin1String(SortedKeyC), value: QVariant(m_sorting));
387 // Save last expansionState as QVariant map
388 QVariantMap expansionState;
389 if (!m_expansionState.isEmpty()) {
390 const QMap<QString, bool>::const_iterator cend = m_expansionState.constEnd();
391 for (QMap<QString, bool>::const_iterator it = m_expansionState.constBegin(); it != cend; ++it)
392 expansionState.insert(akey: it.key(), avalue: QVariant(it.value()));
393 }
394 settings->setValue(key: QLatin1String(ExpansionKeyC), value: expansionState);
395 settings->setValue(key: QLatin1String(SplitterPositionKeyC), value: m_treeBrowser->splitterPosition());
396 settings->endGroup();
397}
398
399void PropertyEditor::setExpanded(QtBrowserItem *item, bool expanded)
400{
401 if (m_buttonBrowser == m_currentBrowser)
402 m_buttonBrowser->setExpanded(item, expanded);
403 else if (m_treeBrowser == m_currentBrowser)
404 m_treeBrowser->setExpanded(item, expanded);
405}
406
407bool PropertyEditor::isExpanded(QtBrowserItem *item) const
408{
409 if (m_buttonBrowser == m_currentBrowser)
410 return m_buttonBrowser->isExpanded(item);
411 if (m_treeBrowser == m_currentBrowser)
412 return m_treeBrowser->isExpanded(item);
413 return false;
414}
415
416void PropertyEditor::setItemVisible(QtBrowserItem *item, bool visible)
417{
418 if (m_currentBrowser == m_treeBrowser) {
419 m_treeBrowser->setItemVisible(item, visible);
420 } else {
421 qWarning(msg: "** WARNING %s is not implemented for this browser.", Q_FUNC_INFO);
422 }
423}
424
425bool PropertyEditor::isItemVisible(QtBrowserItem *item) const
426{
427 return m_currentBrowser == m_treeBrowser ? m_treeBrowser->isItemVisible(item) : true;
428}
429
430/* Default handling of items not found in the map:
431 * - Top-level items (classes) are assumed to be expanded
432 * - Anything below (properties) is assumed to be collapsed
433 * That is, the map is required, the state cannot be stored in a set */
434
435void PropertyEditor::storePropertiesExpansionState(const QList<QtBrowserItem *> &items)
436{
437 const QChar bar = QLatin1Char('|');
438 for (QtBrowserItem *propertyItem : items) {
439 if (!propertyItem->children().isEmpty()) {
440 QtProperty *property = propertyItem->property();
441 const QString propertyName = property->propertyName();
442 const QMap<QtProperty *, QString>::const_iterator itGroup = m_propertyToGroup.constFind(akey: property);
443 if (itGroup != m_propertyToGroup.constEnd()) {
444 QString key = itGroup.value();
445 key += bar;
446 key += propertyName;
447 m_expansionState[key] = isExpanded(item: propertyItem);
448 }
449 }
450 }
451}
452
453void PropertyEditor::storeExpansionState()
454{
455 const auto items = m_currentBrowser->topLevelItems();
456 if (m_sorting) {
457 storePropertiesExpansionState(items);
458 } else {
459 for (QtBrowserItem *item : items) {
460 const QString groupName = item->property()->propertyName();
461 auto propertyItems = item->children();
462 if (!propertyItems.isEmpty())
463 m_expansionState[groupName] = isExpanded(item);
464
465 // properties stuff here
466 storePropertiesExpansionState(items: propertyItems);
467 }
468 }
469}
470
471void PropertyEditor::collapseAll()
472{
473 const auto items = m_currentBrowser->topLevelItems();
474 for (QtBrowserItem *group : items)
475 setExpanded(item: group, expanded: false);
476}
477
478void PropertyEditor::applyPropertiesExpansionState(const QList<QtBrowserItem *> &items)
479{
480 const QChar bar = QLatin1Char('|');
481 for (QtBrowserItem *propertyItem : items) {
482 const QMap<QString, bool>::const_iterator excend = m_expansionState.constEnd();
483 QtProperty *property = propertyItem->property();
484 const QString propertyName = property->propertyName();
485 const QMap<QtProperty *, QString>::const_iterator itGroup = m_propertyToGroup.constFind(akey: property);
486 if (itGroup != m_propertyToGroup.constEnd()) {
487 QString key = itGroup.value();
488 key += bar;
489 key += propertyName;
490 const QMap<QString, bool>::const_iterator pit = m_expansionState.constFind(akey: key);
491 if (pit != excend)
492 setExpanded(item: propertyItem, expanded: pit.value());
493 else
494 setExpanded(item: propertyItem, expanded: false);
495 }
496 }
497}
498
499void PropertyEditor::applyExpansionState()
500{
501 const auto items = m_currentBrowser->topLevelItems();
502 if (m_sorting) {
503 applyPropertiesExpansionState(items);
504 } else {
505 const QMap<QString, bool>::const_iterator excend = m_expansionState.constEnd();
506 for (QtBrowserItem *item : items) {
507 const QString groupName = item->property()->propertyName();
508 const QMap<QString, bool>::const_iterator git = m_expansionState.constFind(akey: groupName);
509 if (git != excend)
510 setExpanded(item, expanded: git.value());
511 else
512 setExpanded(item, expanded: true);
513 // properties stuff here
514 applyPropertiesExpansionState(items: item->children());
515 }
516 }
517}
518
519int PropertyEditor::applyPropertiesFilter(const QList<QtBrowserItem *> &items)
520{
521 int showCount = 0;
522 const bool matchAll = m_filterPattern.isEmpty();
523 for (QtBrowserItem *propertyItem : items) {
524 QtProperty *property = propertyItem->property();
525 const QString propertyName = property->propertyName();
526 const bool showProperty = matchAll || propertyName.contains(s: m_filterPattern, cs: Qt::CaseInsensitive);
527 setItemVisible(item: propertyItem, visible: showProperty);
528 if (showProperty)
529 showCount++;
530 }
531 return showCount;
532}
533
534void PropertyEditor::applyFilter()
535{
536 const auto items = m_currentBrowser->topLevelItems();
537 if (m_sorting) {
538 applyPropertiesFilter(items);
539 } else {
540 for (QtBrowserItem *item : items)
541 setItemVisible(item, visible: applyPropertiesFilter(items: item->children()));
542 }
543}
544
545void PropertyEditor::clearView()
546{
547 m_currentBrowser->clear();
548}
549
550bool PropertyEditor::event(QEvent *event)
551{
552 if (event->type() == QEvent::PaletteChange)
553 updateForegroundBrightness();
554
555 return QDesignerPropertyEditor::event(event);
556}
557
558void PropertyEditor::updateForegroundBrightness()
559{
560 QColor c = palette().color(cr: QPalette::Text);
561 bool newBrightness = qRound(d: 0.3 * c.redF() + 0.59 * c.greenF() + 0.11 * c.blueF());
562
563 if (m_brightness == newBrightness)
564 return;
565
566 m_brightness = newBrightness;
567
568 updateColors();
569}
570
571QColor PropertyEditor::propertyColor(QtProperty *property) const
572{
573 if (!m_coloring)
574 return QColor();
575
576 QtProperty *groupProperty = property;
577
578 QMap<QtProperty *, QString>::ConstIterator itProp = m_propertyToGroup.constFind(akey: property);
579 if (itProp != m_propertyToGroup.constEnd())
580 groupProperty = m_nameToGroup.value(akey: itProp.value());
581
582 const int groupIdx = m_groups.indexOf(t: groupProperty);
583 QPair<QColor, QColor> pair;
584 if (groupIdx != -1) {
585 if (groupProperty == m_dynamicGroup)
586 pair = m_dynamicColor;
587 else if (isLayoutGroup(group: groupProperty))
588 pair = m_layoutColor;
589 else
590 pair = m_colors[groupIdx % m_colors.count()];
591 }
592 if (!m_brightness)
593 return pair.first;
594 return pair.second;
595}
596
597void PropertyEditor::fillView()
598{
599 if (m_sorting) {
600 for (auto itProperty = m_nameToProperty.cbegin(), end = m_nameToProperty.cend(); itProperty != end; ++itProperty)
601 m_currentBrowser->addProperty(property: itProperty.value());
602 } else {
603 for (QtProperty *group : qAsConst(t&: m_groups)) {
604 QtBrowserItem *item = m_currentBrowser->addProperty(property: group);
605 if (m_currentBrowser == m_treeBrowser)
606 m_treeBrowser->setBackgroundColor(item, color: propertyColor(property: group));
607 group->setModified(m_currentBrowser == m_treeBrowser);
608 }
609 }
610}
611
612bool PropertyEditor::isLayoutGroup(QtProperty *group) const
613{
614 return group->propertyName() == m_strings.m_layout;
615}
616
617void PropertyEditor::updateActionsState()
618{
619 m_coloringAction->setEnabled(m_treeAction->isChecked() && !m_sortingAction->isChecked());
620}
621
622void PropertyEditor::slotViewTriggered(QAction *action)
623{
624 storeExpansionState();
625 collapseAll();
626 {
627 UpdateBlocker ub(this);
628 clearView();
629 int idx = 0;
630 if (action == m_treeAction) {
631 m_currentBrowser = m_treeBrowser;
632 idx = m_treeIndex;
633 } else if (action == m_buttonAction) {
634 m_currentBrowser = m_buttonBrowser;
635 idx = m_buttonIndex;
636 }
637 fillView();
638 m_stackedWidget->setCurrentIndex(idx);
639 applyExpansionState();
640 applyFilter();
641 }
642 updateActionsState();
643}
644
645void PropertyEditor::slotSorting(bool sort)
646{
647 if (sort == m_sorting)
648 return;
649
650 storeExpansionState();
651 m_sorting = sort;
652 collapseAll();
653 {
654 UpdateBlocker ub(this);
655 clearView();
656 m_treeBrowser->setRootIsDecorated(sort);
657 fillView();
658 applyExpansionState();
659 applyFilter();
660 }
661 updateActionsState();
662}
663
664void PropertyEditor::updateColors()
665{
666 if (m_treeBrowser && m_currentBrowser == m_treeBrowser) {
667 const auto items = m_treeBrowser->topLevelItems();
668 for (QtBrowserItem *item : items)
669 m_treeBrowser->setBackgroundColor(item, color: propertyColor(property: item->property()));
670 }
671}
672
673void PropertyEditor::slotColoring(bool coloring)
674{
675 if (coloring == m_coloring)
676 return;
677
678 m_coloring = coloring;
679
680 updateColors();
681}
682
683void PropertyEditor::slotAddDynamicProperty(QAction *action)
684{
685 if (!m_propertySheet)
686 return;
687
688 const QDesignerDynamicPropertySheetExtension *dynamicSheet =
689 qt_extension<QDesignerDynamicPropertySheetExtension*>(manager: m_core->extensionManager(), object: m_object);
690
691 if (!dynamicSheet)
692 return;
693
694 QString newName;
695 QVariant newValue;
696 { // Make sure the dialog is closed before the signal is emitted.
697 const QVariant::Type type = static_cast<QVariant::Type>(action->data().toInt());
698 NewDynamicPropertyDialog dlg(core()->dialogGui(), m_currentBrowser);
699 if (type != QVariant::Invalid)
700 dlg.setPropertyType(type);
701
702 QStringList reservedNames;
703 const int propertyCount = m_propertySheet->count();
704 for (int i = 0; i < propertyCount; i++) {
705 if (!dynamicSheet->isDynamicProperty(index: i) || m_propertySheet->isVisible(index: i))
706 reservedNames.append(t: m_propertySheet->propertyName(index: i));
707 }
708 dlg.setReservedNames(reservedNames);
709 if (dlg.exec() == QDialog::Rejected)
710 return;
711 newName = dlg.propertyName();
712 newValue = dlg.propertyValue();
713 }
714 m_recentlyAddedDynamicProperty = newName;
715 emit addDynamicProperty(name: newName, value: newValue);
716}
717
718QDesignerFormEditorInterface *PropertyEditor::core() const
719{
720 return m_core;
721}
722
723bool PropertyEditor::isReadOnly() const
724{
725 return false;
726}
727
728void PropertyEditor::setReadOnly(bool /*readOnly*/)
729{
730 qDebug() << "PropertyEditor::setReadOnly() request";
731}
732
733void PropertyEditor::setPropertyValue(const QString &name, const QVariant &value, bool changed)
734{
735 const QMap<QString, QtVariantProperty*>::const_iterator it = m_nameToProperty.constFind(akey: name);
736 if (it == m_nameToProperty.constEnd())
737 return;
738 QtVariantProperty *property = it.value();
739 updateBrowserValue(property, value);
740 property->setModified(changed);
741}
742
743/* Quick update that assumes the actual count of properties has not changed
744 * N/A when for example executing a layout command and margin properties appear. */
745void PropertyEditor::updatePropertySheet()
746{
747 if (!m_propertySheet)
748 return;
749
750 updateToolBarLabel();
751
752 const int propertyCount = m_propertySheet->count();
753 const QMap<QString, QtVariantProperty*>::const_iterator npcend = m_nameToProperty.constEnd();
754 for (int i = 0; i < propertyCount; ++i) {
755 const QString propertyName = m_propertySheet->propertyName(index: i);
756 QMap<QString, QtVariantProperty*>::const_iterator it = m_nameToProperty.constFind(akey: propertyName);
757 if (it != npcend)
758 updateBrowserValue(property: it.value(), value: m_propertySheet->property(index: i));
759 }
760}
761
762static inline QLayout *layoutOfQLayoutWidget(QObject *o)
763{
764 if (o->isWidgetType() && !qstrcmp(str1: o->metaObject()->className(), str2: "QLayoutWidget"))
765 return static_cast<QWidget*>(o)->layout();
766 return nullptr;
767}
768
769void PropertyEditor::updateToolBarLabel()
770{
771 QString objectName;
772 QString className;
773 if (m_object) {
774 if (QLayout *l = layoutOfQLayoutWidget(o: m_object))
775 objectName = l->objectName();
776 else
777 objectName = m_object->objectName();
778 className = realClassName(object: m_object);
779 }
780
781 m_classLabel->setVisible(!objectName.isEmpty() || !className.isEmpty());
782 m_classLabel->setSizePolicy(hor: QSizePolicy::Expanding, ver: QSizePolicy::Fixed);
783
784 QString classLabelText;
785 if (!objectName.isEmpty())
786 classLabelText += objectName + QStringLiteral(" : ");
787 classLabelText += className;
788
789 m_classLabel->setText(classLabelText);
790 m_classLabel->setToolTip(tr(s: "Object: %1\nClass: %2")
791 .arg(args&: objectName, args&: className));
792}
793
794void PropertyEditor::updateBrowserValue(QtVariantProperty *property, const QVariant &value)
795{
796 QVariant v = value;
797 const int type = property->propertyType();
798 if (type == QtVariantPropertyManager::enumTypeId()) {
799 const PropertySheetEnumValue e = qvariant_cast<PropertySheetEnumValue>(v);
800 v = e.metaEnum.keys().indexOf(t: e.metaEnum.valueToKey(value: e.value));
801 } else if (type == DesignerPropertyManager::designerFlagTypeId()) {
802 const PropertySheetFlagValue f = qvariant_cast<PropertySheetFlagValue>(v);
803 v = QVariant(f.value);
804 } else if (type == DesignerPropertyManager::designerAlignmentTypeId()) {
805 const PropertySheetFlagValue f = qvariant_cast<PropertySheetFlagValue>(v);
806 v = QVariant(f.value);
807 }
808 QDesignerPropertySheet *sheet = qobject_cast<QDesignerPropertySheet*>(object: m_core->extensionManager()->extension(object: m_object, Q_TYPEID(QDesignerPropertySheetExtension)));
809 int index = -1;
810 if (sheet)
811 index = sheet->indexOf(name: property->propertyName());
812 if (sheet && m_propertyToGroup.contains(akey: property)) { // don't do it for comments since property sheet doesn't keep them
813 property->setEnabled(sheet->isEnabled(index));
814 }
815
816 // Rich text string property with comment: Store/Update the font the rich text editor dialog starts out with
817 if (type == QVariant::String && !property->subProperties().isEmpty()) {
818 const int fontIndex = m_propertySheet->indexOf(name: m_strings.m_fontProperty);
819 if (fontIndex != -1)
820 property->setAttribute(attribute: m_strings.m_fontAttribute, value: m_propertySheet->property(index: fontIndex));
821 }
822
823 m_updatingBrowser = true;
824 property->setValue(v);
825 if (sheet && sheet->isResourceProperty(index))
826 property->setAttribute(QStringLiteral("defaultResource"), value: sheet->defaultResourceProperty(index));
827 m_updatingBrowser = false;
828}
829
830int PropertyEditor::toBrowserType(const QVariant &value, const QString &propertyName) const
831{
832 if (value.canConvert<PropertySheetFlagValue>()) {
833 if (m_strings.m_alignmentProperties.contains(value: propertyName))
834 return DesignerPropertyManager::designerAlignmentTypeId();
835 return DesignerPropertyManager::designerFlagTypeId();
836 }
837 if (value.canConvert<PropertySheetEnumValue>())
838 return DesignerPropertyManager::enumTypeId();
839
840 return value.userType();
841}
842
843QString PropertyEditor::realClassName(QObject *object) const
844{
845 if (!object)
846 return QString();
847
848 QString className = QLatin1String(object->metaObject()->className());
849 const QDesignerWidgetDataBaseInterface *db = core()->widgetDataBase();
850 if (QDesignerWidgetDataBaseItemInterface *widgetItem = db->item(index: db->indexOfObject(object, resolveName: true))) {
851 className = widgetItem->name();
852
853 if (object->isWidgetType() && className == m_strings.m_qLayoutWidget
854 && static_cast<QWidget*>(object)->layout()) {
855 className = QLatin1String(static_cast<QWidget*>(object)->layout()->metaObject()->className());
856 }
857 }
858
859 if (className.startsWith(s: m_strings.m_designerPrefix))
860 className.remove(i: 1, len: m_strings.m_designerPrefix.size() - 1);
861
862 return className;
863}
864
865static const char *typeName(int type)
866{
867 if (type == qMetaTypeId<PropertySheetStringValue>())
868 type = QVariant::String;
869 if (type < int(QVariant::UserType))
870 return QVariant::typeToName(typeId: static_cast<QVariant::Type>(type));
871 if (type == qMetaTypeId<PropertySheetIconValue>())
872 return "QIcon";
873 if (type == qMetaTypeId<PropertySheetPixmapValue>())
874 return "QPixmap";
875 if (type == qMetaTypeId<PropertySheetKeySequenceValue>())
876 return "QKeySequence";
877 if (type == qMetaTypeId<PropertySheetFlagValue>())
878 return "QFlags";
879 if (type == qMetaTypeId<PropertySheetEnumValue>())
880 return "enum";
881 if (type == QVariant::Invalid)
882 return "invalid";
883 if (type == QVariant::UserType)
884 return "user type";
885 return nullptr;
886}
887
888static QString msgUnsupportedType(const QString &propertyName, int type)
889{
890 QString rc;
891 QTextStream str(&rc);
892 const char *typeS = typeName(type);
893 str << "The property \"" << propertyName << "\" of type ("
894 << (typeS ? typeS : "unknown") << ") is not supported yet!";
895 return rc;
896}
897
898void PropertyEditor::setObject(QObject *object)
899{
900 QDesignerFormWindowInterface *oldFormWindow = QDesignerFormWindowInterface::findFormWindow(obj: m_object);
901 // In the first setObject() call following the addition of a dynamic property, focus and edit it.
902 const bool editNewDynamicProperty = object != nullptr && m_object == object && !m_recentlyAddedDynamicProperty.isEmpty();
903 m_object = object;
904 m_propertyManager->setObject(object);
905 QDesignerFormWindowInterface *formWindow = QDesignerFormWindowInterface::findFormWindow(obj: m_object);
906 // QTBUG-68507: Form window can be null for objects in Morph Undo macros with buddies
907 if (object != nullptr && formWindow == nullptr) {
908 formWindow = m_core->formWindowManager()->activeFormWindow();
909 if (formWindow == nullptr) {
910 qWarning(msg: "PropertyEditor::setObject(): Unable to find form window for \"%s\".",
911 qPrintable(object->objectName()));
912 return;
913 }
914 }
915 FormWindowBase *fwb = qobject_cast<FormWindowBase *>(object: formWindow);
916 const bool idIdBasedTranslation = fwb && fwb->useIdBasedTranslations();
917 const bool idIdBasedTranslationUnchanged = (idIdBasedTranslation == DesignerPropertyManager::useIdBasedTranslations());
918 DesignerPropertyManager::setUseIdBasedTranslations(idIdBasedTranslation);
919 m_treeFactory->setFormWindowBase(fwb);
920 m_groupFactory->setFormWindowBase(fwb);
921
922 storeExpansionState();
923
924 UpdateBlocker ub(this);
925
926 updateToolBarLabel();
927
928 QMap<QString, QtVariantProperty *> toRemove = m_nameToProperty;
929
930 const QDesignerDynamicPropertySheetExtension *dynamicSheet =
931 qt_extension<QDesignerDynamicPropertySheetExtension*>(manager: m_core->extensionManager(), object: m_object);
932 const QDesignerPropertySheet *sheet = qobject_cast<QDesignerPropertySheet*>(object: m_core->extensionManager()->extension(object: m_object, Q_TYPEID(QDesignerPropertySheetExtension)));
933
934 // Optimizization: Instead of rebuilding the complete list every time, compile a list of properties to remove,
935 // remove them, traverse the sheet, in case property exists just set a value, otherwise - create it.
936 QExtensionManager *m = m_core->extensionManager();
937
938 m_propertySheet = qobject_cast<QDesignerPropertySheetExtension*>(object: m->extension(object, Q_TYPEID(QDesignerPropertySheetExtension)));
939 if (m_propertySheet) {
940 const int stringTypeId = qMetaTypeId<PropertySheetStringValue>();
941 const int propertyCount = m_propertySheet->count();
942 for (int i = 0; i < propertyCount; ++i) {
943 if (!m_propertySheet->isVisible(index: i))
944 continue;
945
946 const QString propertyName = m_propertySheet->propertyName(index: i);
947 if (m_propertySheet->indexOf(name: propertyName) != i)
948 continue;
949 const QString groupName = m_propertySheet->propertyGroup(index: i);
950 const QMap<QString, QtVariantProperty *>::const_iterator rit = toRemove.constFind(akey: propertyName);
951 if (rit != toRemove.constEnd()) {
952 QtVariantProperty *property = rit.value();
953 const int propertyType = property->propertyType();
954 // Also remove string properties in case a change in translation mode
955 // occurred since different sub-properties are used (disambiguation/id).
956 if (m_propertyToGroup.value(akey: property) == groupName
957 && (idIdBasedTranslationUnchanged || propertyType != stringTypeId)
958 && toBrowserType(value: m_propertySheet->property(index: i), propertyName) == propertyType) {
959 toRemove.remove(akey: propertyName);
960 }
961 }
962 }
963 }
964
965 for (auto itRemove = toRemove.cbegin(), end = toRemove.cend(); itRemove != end; ++itRemove) {
966 QtVariantProperty *property = itRemove.value();
967 m_nameToProperty.remove(akey: itRemove.key());
968 m_propertyToGroup.remove(akey: property);
969 delete property;
970 }
971
972 if (oldFormWindow != formWindow)
973 reloadResourceProperties();
974
975 bool isMainContainer = false;
976 if (QWidget *widget = qobject_cast<QWidget*>(o: object)) {
977 if (QDesignerFormWindowInterface *fw = QDesignerFormWindowInterface::findFormWindow(w: widget)) {
978 isMainContainer = (fw->mainContainer() == widget);
979 }
980 }
981 m_groups.clear();
982
983 if (m_propertySheet) {
984 const QString className = WidgetFactory::classNameOf(core: formWindow->core(), o: m_object);
985 const QDesignerCustomWidgetData customData = formWindow->core()->pluginManager()->customWidgetData(className);
986
987 QtProperty *lastProperty = nullptr;
988 QtProperty *lastGroup = nullptr;
989 const int propertyCount = m_propertySheet->count();
990 for (int i = 0; i < propertyCount; ++i) {
991 if (!m_propertySheet->isVisible(index: i))
992 continue;
993
994 const QString propertyName = m_propertySheet->propertyName(index: i);
995 if (m_propertySheet->indexOf(name: propertyName) != i)
996 continue;
997 const QVariant value = m_propertySheet->property(index: i);
998
999 const int type = toBrowserType(value, propertyName);
1000
1001 QtVariantProperty *property = m_nameToProperty.value(akey: propertyName, adefaultValue: 0);
1002 bool newProperty = property == nullptr;
1003 if (newProperty) {
1004 property = m_propertyManager->addProperty(propertyType: type, name: propertyName);
1005 if (property) {
1006 newProperty = true;
1007 if (type == DesignerPropertyManager::enumTypeId()) {
1008 const PropertySheetEnumValue e = qvariant_cast<PropertySheetEnumValue>(v: value);
1009 m_updatingBrowser = true;
1010 property->setAttribute(attribute: m_strings.m_enumNamesAttribute, value: e.metaEnum.keys());
1011 m_updatingBrowser = false;
1012 } else if (type == DesignerPropertyManager::designerFlagTypeId()) {
1013 const PropertySheetFlagValue f = qvariant_cast<PropertySheetFlagValue>(v: value);
1014 QList<QPair<QString, uint> > flags;
1015 for (const QString &name : f.metaFlags.keys()) {
1016 const uint val = f.metaFlags.keyToValue(key: name);
1017 flags.append(t: qMakePair(x: name, y: val));
1018 }
1019 m_updatingBrowser = true;
1020 QVariant v;
1021 v.setValue(flags);
1022 property->setAttribute(attribute: m_strings.m_flagsAttribute, value: v);
1023 m_updatingBrowser = false;
1024 }
1025 }
1026 }
1027
1028 if (property != nullptr) {
1029 const bool dynamicProperty = (dynamicSheet && dynamicSheet->isDynamicProperty(index: i))
1030 || (sheet && sheet->isDefaultDynamicProperty(index: i));
1031 QString descriptionToolTip;
1032 if (!dynamicProperty && !customData.isNull())
1033 descriptionToolTip = customData.propertyToolTip(name: propertyName);
1034 if (descriptionToolTip.isEmpty()) {
1035 if (const char *typeS = typeName(type)) {
1036 descriptionToolTip = propertyName + QLatin1String(" (")
1037 + QLatin1String(typeS) + QLatin1Char(')');
1038 }
1039 }
1040 if (!descriptionToolTip.isEmpty())
1041 property->setDescriptionToolTip(descriptionToolTip);
1042 switch (type) {
1043 case QVariant::Palette:
1044 setupPaletteProperty(property);
1045 break;
1046 case QVariant::KeySequence:
1047 //addCommentProperty(property, propertyName);
1048 break;
1049 default:
1050 break;
1051 }
1052 if (type == QVariant::String || type == qMetaTypeId<PropertySheetStringValue>())
1053 setupStringProperty(property, isMainContainer);
1054 property->setAttribute(attribute: m_strings.m_resettableAttribute, value: m_propertySheet->hasReset(index: i));
1055
1056 const QString groupName = m_propertySheet->propertyGroup(index: i);
1057 QtVariantProperty *groupProperty = nullptr;
1058
1059 if (newProperty) {
1060 QMap<QString, QtVariantProperty*>::const_iterator itPrev(m_nameToProperty.insert(akey: propertyName, avalue: property));
1061 m_propertyToGroup[property] = groupName;
1062 if (m_sorting) {
1063 QtProperty *previous = nullptr;
1064 if (itPrev != m_nameToProperty.constBegin())
1065 previous = (--itPrev).value();
1066 m_currentBrowser->insertProperty(property, afterProperty: previous);
1067 }
1068 }
1069 const QMap<QString, QtVariantProperty*>::const_iterator gnit = m_nameToGroup.constFind(akey: groupName);
1070 if (gnit != m_nameToGroup.constEnd()) {
1071 groupProperty = gnit.value();
1072 } else {
1073 groupProperty = m_propertyManager->addProperty(propertyType: QtVariantPropertyManager::groupTypeId(), name: groupName);
1074 QtBrowserItem *item = nullptr;
1075 if (!m_sorting)
1076 item = m_currentBrowser->insertProperty(property: groupProperty, afterProperty: lastGroup);
1077 m_nameToGroup[groupName] = groupProperty;
1078 m_groups.append(t: groupProperty);
1079 if (dynamicProperty)
1080 m_dynamicGroup = groupProperty;
1081 if (m_currentBrowser == m_treeBrowser && item) {
1082 m_treeBrowser->setBackgroundColor(item, color: propertyColor(property: groupProperty));
1083 groupProperty->setModified(true);
1084 }
1085 }
1086 /* Group changed or new group. Append to last subproperty of
1087 * that group. Note that there are cases in which a derived
1088 * property sheet appends fake properties for the class
1089 * which will appear after the layout group properties
1090 * (QWizardPage). To make them appear at the end of the
1091 * actual class group, goto last element. */
1092 if (lastGroup != groupProperty) {
1093 lastGroup = groupProperty;
1094 lastProperty = nullptr; // Append at end
1095 const auto subProperties = lastGroup->subProperties();
1096 if (!subProperties.isEmpty())
1097 lastProperty = subProperties.constLast();
1098 lastGroup = groupProperty;
1099 }
1100 if (!m_groups.contains(t: groupProperty))
1101 m_groups.append(t: groupProperty);
1102 if (newProperty)
1103 groupProperty->insertSubProperty(property, afterProperty: lastProperty);
1104
1105 lastProperty = property;
1106
1107 updateBrowserValue(property, value);
1108
1109 property->setModified(m_propertySheet->isChanged(index: i));
1110 if (propertyName == QStringLiteral("geometry") && type == QVariant::Rect) {
1111 const auto &subProperties = property->subProperties();
1112 for (QtProperty *subProperty : subProperties) {
1113 const QString subPropertyName = subProperty->propertyName();
1114 if (subPropertyName == QStringLiteral("X") || subPropertyName == QStringLiteral("Y"))
1115 subProperty->setEnabled(!isMainContainer);
1116 }
1117 }
1118 } else {
1119 qWarning(msg: "%s", qPrintable(msgUnsupportedType(propertyName, type)));
1120 }
1121 }
1122 }
1123 QMap<QString, QtVariantProperty *> groups = m_nameToGroup;
1124 for (auto itGroup = groups.cbegin(), end = groups.cend(); itGroup != end; ++itGroup) {
1125 QtVariantProperty *groupProperty = itGroup.value();
1126 if (groupProperty->subProperties().isEmpty()) {
1127 if (groupProperty == m_dynamicGroup)
1128 m_dynamicGroup = nullptr;
1129 delete groupProperty;
1130 m_nameToGroup.remove(akey: itGroup.key());
1131 }
1132 }
1133 const bool addEnabled = dynamicSheet ? dynamicSheet->dynamicPropertiesAllowed() : false;
1134 m_addDynamicAction->setEnabled(addEnabled);
1135 m_removeDynamicAction->setEnabled(false);
1136 applyExpansionState();
1137 applyFilter();
1138 // In the first setObject() call following the addition of a dynamic property, focus and edit it.
1139 if (editNewDynamicProperty) {
1140 // Have QApplication process the events related to completely closing the modal 'add' dialog,
1141 // otherwise, we cannot focus the property editor in docked mode.
1142 QApplication::processEvents(flags: QEventLoop::ExcludeUserInputEvents);
1143 editProperty(name: m_recentlyAddedDynamicProperty);
1144 }
1145 m_recentlyAddedDynamicProperty.clear();
1146 m_filterWidget->setEnabled(object);
1147}
1148
1149void PropertyEditor::reloadResourceProperties()
1150{
1151 m_updatingBrowser = true;
1152 m_propertyManager->reloadResourceProperties();
1153 m_updatingBrowser = false;
1154}
1155
1156QtBrowserItem *PropertyEditor::nonFakePropertyBrowserItem(QtBrowserItem *item) const
1157{
1158 // Top-level properties are QObject/QWidget groups, etc. Find first item property below
1159 // which should be nonfake
1160 const auto topLevelItems = m_currentBrowser->topLevelItems();
1161 do {
1162 if (topLevelItems.contains(t: item->parent()))
1163 return item;
1164 item = item->parent();
1165 } while (item);
1166 return nullptr;
1167}
1168
1169QString PropertyEditor::currentPropertyName() const
1170{
1171 if (QtBrowserItem *browserItem = m_currentBrowser->currentItem())
1172 if (QtBrowserItem *topLevelItem = nonFakePropertyBrowserItem(item: browserItem)) {
1173 return topLevelItem->property()->propertyName();
1174 }
1175 return QString();
1176}
1177
1178void PropertyEditor::slotResetProperty(QtProperty *property)
1179{
1180 QDesignerFormWindowInterface *form = m_core->formWindowManager()->activeFormWindow();
1181 if (!form)
1182 return;
1183
1184 if (m_propertyManager->resetFontSubProperty(property))
1185 return;
1186
1187 if (m_propertyManager->resetIconSubProperty(subProperty: property))
1188 return;
1189
1190 if (m_propertyManager->resetTextAlignmentProperty(property))
1191 return;
1192
1193 if (!m_propertyToGroup.contains(akey: property))
1194 return;
1195
1196 emit resetProperty(name: property->propertyName());
1197}
1198
1199void PropertyEditor::slotValueChanged(QtProperty *property, const QVariant &value, bool enableSubPropertyHandling)
1200{
1201 if (m_updatingBrowser)
1202 return;
1203
1204 if (!m_propertySheet)
1205 return;
1206
1207 QtVariantProperty *varProp = m_propertyManager->variantProperty(property);
1208
1209 if (!varProp)
1210 return;
1211
1212 if (!m_propertyToGroup.contains(akey: property))
1213 return;
1214
1215 if (varProp->propertyType() == QtVariantPropertyManager::enumTypeId()) {
1216 PropertySheetEnumValue e = qvariant_cast<PropertySheetEnumValue>(v: m_propertySheet->property(index: m_propertySheet->indexOf(name: property->propertyName())));
1217 const int val = value.toInt();
1218 const QString valName = varProp->attributeValue(attribute: m_strings.m_enumNamesAttribute).toStringList().at(i: val);
1219 bool ok = false;
1220 e.value = e.metaEnum.parseEnum(s: valName, ok: &ok);
1221 Q_ASSERT(ok);
1222 QVariant v;
1223 v.setValue(e);
1224 emitPropertyValueChanged(name: property->propertyName(), value: v, enableSubPropertyHandling: true);
1225 return;
1226 }
1227
1228 emitPropertyValueChanged(name: property->propertyName(), value, enableSubPropertyHandling);
1229}
1230
1231bool PropertyEditor::isDynamicProperty(const QtBrowserItem* item) const
1232{
1233 if (!item)
1234 return false;
1235
1236 const QDesignerDynamicPropertySheetExtension *dynamicSheet =
1237 qt_extension<QDesignerDynamicPropertySheetExtension*>(manager: m_core->extensionManager(), object: m_object);
1238
1239 if (!dynamicSheet)
1240 return false;
1241
1242 return m_propertyToGroup.contains(akey: item->property())
1243 && dynamicSheet->isDynamicProperty(index: m_propertySheet->indexOf(name: item->property()->propertyName()));
1244}
1245
1246void PropertyEditor::editProperty(const QString &name)
1247{
1248 // find the browser item belonging to the property, make it current and edit it
1249 QtBrowserItem *browserItem = nullptr;
1250 if (QtVariantProperty *property = m_nameToProperty.value(akey: name, adefaultValue: 0)) {
1251 const auto items = m_currentBrowser->items(property);
1252 if (items.size() == 1)
1253 browserItem = items.constFirst();
1254 }
1255 if (browserItem == nullptr)
1256 return;
1257 m_currentBrowser->setFocus(Qt::OtherFocusReason);
1258 if (m_currentBrowser == m_treeBrowser) { // edit is currently only supported in tree view
1259 m_treeBrowser->editItem(item: browserItem);
1260 } else {
1261 m_currentBrowser->setCurrentItem(browserItem);
1262 }
1263}
1264
1265void PropertyEditor::slotCurrentItemChanged(QtBrowserItem *item)
1266{
1267 m_removeDynamicAction->setEnabled(isDynamicProperty(item));
1268
1269}
1270
1271void PropertyEditor::slotRemoveDynamicProperty()
1272{
1273 if (QtBrowserItem* item = m_currentBrowser->currentItem())
1274 if (isDynamicProperty(item))
1275 emit removeDynamicProperty(name: item->property()->propertyName());
1276}
1277
1278void PropertyEditor::setFilter(const QString &pattern)
1279{
1280 m_filterPattern = pattern;
1281 applyFilter();
1282}
1283}
1284
1285QT_END_NAMESPACE
1286

source code of qttools/src/designer/src/components/propertyeditor/propertyeditor.cpp