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 
72 static const char *SettingsGroupC = "PropertyEditor";
73 static const char *ViewKeyC = "View";
74 static const char *ColorKeyC = "Colored";
75 static const char *SortedKeyC = "Sorted";
76 static const char *ExpansionKeyC = "ExpandedItems";
77 static const char *SplitterPositionKeyC = "SplitterPosition";
78 
79 enum SettingsView { TreeView, ButtonView };
80 
81 QT_BEGIN_NAMESPACE
82 
83 // ---------------------------------------------------------------------------------
84 
85 namespace qdesigner_internal {
86 
87 // ----------- ElidingLabel
88 // QLabel does not support text eliding so we need a helper class
89 
90 class ElidingLabel : public QWidget
91 {
92 public:
ElidingLabel(const QString & text=QString (),QWidget * parent=nullptr)93     explicit ElidingLabel(const QString &text = QString(),
94                           QWidget *parent = nullptr) : QWidget(parent), m_text(text)
95         { setContentsMargins(3, 2, 3, 2); }
96 
setText(const QString & text)97     void setText(const QString &text) {
98         m_text = text;
99         updateGeometry();
100     }
setElidemode(Qt::TextElideMode mode)101     void setElidemode(Qt::TextElideMode mode) {
102         m_mode = mode;
103         updateGeometry();
104     }
105 
106 protected:
107     QSize sizeHint() const override;
108     void paintEvent(QPaintEvent *e) override;
109 
110 private:
111     QString m_text;
112     Qt::TextElideMode m_mode = Qt::ElideRight;
113 };
114 
sizeHint() const115 QSize ElidingLabel::sizeHint() const
116 {
117     QSize size = fontMetrics().boundingRect(m_text).size();
118     size += QSize(contentsMargins().left() + contentsMargins().right(),
119                   contentsMargins().top() + contentsMargins().bottom());
120     return size;
121 }
122 
paintEvent(QPaintEvent *)123 void 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(rect().adjusted(0, 0, -1, -1));
128     painter.setPen(palette().windowText().color());
129     painter.drawText(contentsRect(), Qt::AlignLeft,
130                      fontMetrics().elidedText(m_text, Qt::ElideRight, width(), 0));
131 }
132 
133 
134 // ----------- PropertyEditor::Strings
135 
Strings()136 PropertyEditor::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 
metaDataBaseItem() const155 QDesignerMetaDataBaseItemInterface* 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(o);
164 }
165 
setupStringProperty(QtVariantProperty * property,bool isMainContainer)166 void PropertyEditor::setupStringProperty(QtVariantProperty *property, bool isMainContainer)
167 {
168     const StringPropertyParameters params = textPropertyValidationMode(core(), m_object, property->propertyName(), isMainContainer);
169     // Does a meta DB entry exist - add comment
170     const bool hasComment = params.second;
171     property->setAttribute(m_strings.m_validationModeAttribute, params.first);
172     // assuming comment cannot appear or disappear for the same property in different object instance
173     if (!hasComment)
174         qDeleteAll(property->subProperties());
175 }
176 
setupPaletteProperty(QtVariantProperty * property)177 void PropertyEditor::setupPaletteProperty(QtVariantProperty *property)
178 {
179     QPalette superPalette = QPalette();
180     QWidget *currentWidget = qobject_cast<QWidget *>(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(m_strings.m_superPaletteAttribute, superPalette);
191     m_updatingBrowser = false;
192 }
193 
createDropDownButton(QAction * defaultAction,QWidget * parent=nullptr)194 static 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 
PropertyEditor(QDesignerFormEditorInterface * core,QWidget * parent,Qt::WindowFlags flags)202 PropertyEditor::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("Add Dynamic Property..."), this)),
209     m_removeDynamicAction(new QAction(createIconSet(QStringLiteral("minus.png")), tr("Remove Dynamic Property"), this)),
210     m_sortingAction(new QAction(createIconSet(QStringLiteral("sort.png")), tr("Sorting"), this)),
211     m_coloringAction(new QAction(createIconSet(QStringLiteral("color.png")), tr("Color Groups"), this)),
212     m_treeAction(new QAction(tr("Tree View"), this)),
213     m_buttonAction(new QAction(tr("Drop Down Button View"), this)),
214     m_classLabel(new ElidingLabel)
215 {
216     QVector<QColor> colors;
217     colors.reserve(6);
218     colors.push_back(QColor(255, 230, 191));
219     colors.push_back(QColor(255, 255, 191));
220     colors.push_back(QColor(191, 255, 191));
221     colors.push_back(QColor(199, 255, 255));
222     colors.push_back(QColor(234, 191, 255));
223     colors.push_back(QColor(255, 191, 239));
224     m_colors.reserve(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(qMakePair(c, c.darker(darknessFactor)));
229     }
230     QColor dynamicColor(191, 207, 255);
231     QColor layoutColor(255, 191, 191);
232     m_dynamicColor = qMakePair(dynamicColor, dynamicColor.darker(darknessFactor));
233     m_layoutColor = qMakePair(layoutColor, layoutColor.darker(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(m_treeAction);
245     actionGroup->addAction(m_buttonAction);
246     connect(actionGroup, &QActionGroup::triggered,
247             this, &PropertyEditor::slotViewTriggered);
248 
249     // Add actions
250     QActionGroup *addDynamicActionGroup = new QActionGroup(this);
251     connect(addDynamicActionGroup, &QActionGroup::triggered,
252             this, &PropertyEditor::slotAddDynamicProperty);
253 
254     QMenu *addDynamicActionMenu = new QMenu(this);
255     m_addDynamicAction->setMenu(addDynamicActionMenu);
256     m_addDynamicAction->setEnabled(false);
257     QAction *addDynamicAction = addDynamicActionGroup->addAction(tr("String..."));
258     addDynamicAction->setData(static_cast<int>(QVariant::String));
259     addDynamicActionMenu->addAction(addDynamicAction);
260     addDynamicAction = addDynamicActionGroup->addAction(tr("Bool..."));
261     addDynamicAction->setData(static_cast<int>(QVariant::Bool));
262     addDynamicActionMenu->addAction(addDynamicAction);
263     addDynamicActionMenu->addSeparator();
264     addDynamicAction = addDynamicActionGroup->addAction(tr("Other..."));
265     addDynamicAction->setData(static_cast<int>(QVariant::Invalid));
266     addDynamicActionMenu->addAction(addDynamicAction);
267     // remove
268     m_removeDynamicAction->setEnabled(false);
269     connect(m_removeDynamicAction, &QAction::triggered, this, &PropertyEditor::slotRemoveDynamicProperty);
270     // Configure
271     QAction *configureAction = new QAction(tr("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(m_sortingAction, &QAction::toggled, this, &PropertyEditor::slotSorting);
278 
279     m_coloringAction->setCheckable(true);
280     connect(m_coloringAction, &QAction::toggled, this, &PropertyEditor::slotColoring);
281 
282     configureMenu->addAction(m_sortingAction);
283     configureMenu->addAction(m_coloringAction);
284     configureMenu->addSeparator();
285     configureMenu->addAction(m_treeAction);
286     configureMenu->addAction(m_buttonAction);
287     // Assemble toolbar
288     QToolBar *toolBar = new QToolBar;
289     toolBar->addWidget(m_filterWidget);
290     toolBar->addWidget(createDropDownButton(m_addDynamicAction));
291     toolBar->addAction(m_removeDynamicAction);
292     toolBar->addWidget(createDropDownButton(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(buttonScroll);
299     connect(m_buttonBrowser, &QtAbstractPropertyBrowser::currentItemChanged,
300             this, &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(m_treeBrowser);
307     connect(m_treeBrowser, &QtAbstractPropertyBrowser::currentItemChanged,
308             this, &PropertyEditor::slotCurrentItemChanged);
309     m_filterWidget->setPlaceholderText(tr("Filter"));
310     m_filterWidget->setClearButtonEnabled(true);
311     connect(m_filterWidget, &QLineEdit::textChanged, this, &PropertyEditor::setFilter);
312 
313     QVBoxLayout *layout = new QVBoxLayout(this);
314     layout->addWidget(toolBar);
315     layout->addWidget(m_classLabel);
316     layout->addSpacerItem(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(variantManager, m_groupFactory);
326     m_treeBrowser->setFactoryForManager(variantManager, m_treeFactory);
327 
328     m_stackedWidget->setCurrentIndex(m_treeIndex);
329     m_currentBrowser = m_treeBrowser;
330     m_treeAction->setChecked(true);
331 
332     connect(m_groupFactory, &DesignerEditorFactory::resetProperty,
333             this, &PropertyEditor::slotResetProperty);
334     connect(m_treeFactory, &DesignerEditorFactory::resetProperty,
335             this, &PropertyEditor::slotResetProperty);
336     connect(m_propertyManager, &DesignerPropertyManager::valueChanged,
337             this, &PropertyEditor::slotValueChanged);
338 
339     // retrieve initial settings
340     QDesignerSettingsInterface *settings = m_core->settingsManager();
341     settings->beginGroup(QLatin1String(SettingsGroupC));
342     const SettingsView view = settings->value(QLatin1String(ViewKeyC), TreeView).toInt() == TreeView ? TreeView :  ButtonView;
343     // Coloring not available unless treeview and not sorted
344     m_sorting = settings->value(QLatin1String(SortedKeyC), false).toBool();
345     m_coloring = settings->value(QLatin1String(ColorKeyC), true).toBool();
346     const QVariantMap expansionState = settings->value(QLatin1String(ExpansionKeyC), QVariantMap()).toMap();
347     const int splitterPosition = settings->value(QLatin1String(SplitterPositionKeyC), 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(it.key(), it.value().toBool());
370     }
371     updateActionsState();
372 }
373 
~PropertyEditor()374 PropertyEditor::~PropertyEditor()
375 {
376     storeExpansionState();
377     saveSettings();
378 }
379 
saveSettings() const380 void PropertyEditor::saveSettings() const
381 {
382     QDesignerSettingsInterface *settings = m_core->settingsManager();
383     settings->beginGroup(QLatin1String(SettingsGroupC));
384     settings->setValue(QLatin1String(ViewKeyC), QVariant(m_treeAction->isChecked() ? TreeView : ButtonView));
385     settings->setValue(QLatin1String(ColorKeyC), QVariant(m_coloring));
386     settings->setValue(QLatin1String(SortedKeyC), 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(it.key(), QVariant(it.value()));
393     }
394     settings->setValue(QLatin1String(ExpansionKeyC), expansionState);
395     settings->setValue(QLatin1String(SplitterPositionKeyC), m_treeBrowser->splitterPosition());
396     settings->endGroup();
397 }
398 
setExpanded(QtBrowserItem * item,bool expanded)399 void 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 
isExpanded(QtBrowserItem * item) const407 bool 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 
setItemVisible(QtBrowserItem * item,bool visible)416 void PropertyEditor::setItemVisible(QtBrowserItem *item, bool visible)
417 {
418     if (m_currentBrowser == m_treeBrowser) {
419         m_treeBrowser->setItemVisible(item, visible);
420     } else {
421         qWarning("** WARNING %s is not implemented for this browser.", Q_FUNC_INFO);
422     }
423 }
424 
isItemVisible(QtBrowserItem * item) const425 bool 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 
storePropertiesExpansionState(const QList<QtBrowserItem * > & items)435 void 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(property);
443             if (itGroup != m_propertyToGroup.constEnd()) {
444                 QString key = itGroup.value();
445                 key += bar;
446                 key += propertyName;
447                 m_expansionState[key] = isExpanded(propertyItem);
448             }
449         }
450     }
451 }
452 
storeExpansionState()453 void 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(propertyItems);
467         }
468     }
469 }
470 
collapseAll()471 void PropertyEditor::collapseAll()
472 {
473     const auto items = m_currentBrowser->topLevelItems();
474     for (QtBrowserItem *group : items)
475         setExpanded(group, false);
476 }
477 
applyPropertiesExpansionState(const QList<QtBrowserItem * > & items)478 void 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(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(key);
491             if (pit != excend)
492                 setExpanded(propertyItem, pit.value());
493             else
494                 setExpanded(propertyItem, false);
495         }
496     }
497 }
498 
applyExpansionState()499 void 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(groupName);
509             if (git != excend)
510                 setExpanded(item, git.value());
511             else
512                 setExpanded(item, true);
513             // properties stuff here
514             applyPropertiesExpansionState(item->children());
515         }
516     }
517 }
518 
applyPropertiesFilter(const QList<QtBrowserItem * > & items)519 int 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(m_filterPattern, Qt::CaseInsensitive);
527         setItemVisible(propertyItem, showProperty);
528         if (showProperty)
529             showCount++;
530     }
531     return showCount;
532 }
533 
applyFilter()534 void 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, applyPropertiesFilter(item->children()));
542     }
543 }
544 
clearView()545 void PropertyEditor::clearView()
546 {
547     m_currentBrowser->clear();
548 }
549 
event(QEvent * event)550 bool PropertyEditor::event(QEvent *event)
551 {
552     if (event->type() == QEvent::PaletteChange)
553         updateForegroundBrightness();
554 
555     return QDesignerPropertyEditor::event(event);
556 }
557 
updateForegroundBrightness()558 void PropertyEditor::updateForegroundBrightness()
559 {
560     QColor c = palette().color(QPalette::Text);
561     bool newBrightness = qRound(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 
propertyColor(QtProperty * property) const571 QColor 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(property);
579     if (itProp != m_propertyToGroup.constEnd())
580         groupProperty = m_nameToGroup.value(itProp.value());
581 
582     const int groupIdx = m_groups.indexOf(groupProperty);
583     QPair<QColor, QColor> pair;
584     if (groupIdx != -1) {
585         if (groupProperty == m_dynamicGroup)
586             pair = m_dynamicColor;
587         else if (isLayoutGroup(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 
fillView()597 void 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(itProperty.value());
602     } else {
603         for (QtProperty *group : qAsConst(m_groups)) {
604             QtBrowserItem *item = m_currentBrowser->addProperty(group);
605             if (m_currentBrowser == m_treeBrowser)
606                 m_treeBrowser->setBackgroundColor(item, propertyColor(group));
607             group->setModified(m_currentBrowser == m_treeBrowser);
608         }
609     }
610 }
611 
isLayoutGroup(QtProperty * group) const612 bool PropertyEditor::isLayoutGroup(QtProperty *group) const
613 {
614    return group->propertyName() == m_strings.m_layout;
615 }
616 
updateActionsState()617 void PropertyEditor::updateActionsState()
618 {
619     m_coloringAction->setEnabled(m_treeAction->isChecked() && !m_sortingAction->isChecked());
620 }
621 
slotViewTriggered(QAction * action)622 void 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 
slotSorting(bool sort)645 void 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 
updateColors()664 void 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, propertyColor(item->property()));
670     }
671 }
672 
slotColoring(bool coloring)673 void PropertyEditor::slotColoring(bool coloring)
674 {
675     if (coloring == m_coloring)
676         return;
677 
678     m_coloring = coloring;
679 
680     updateColors();
681 }
682 
slotAddDynamicProperty(QAction * action)683 void PropertyEditor::slotAddDynamicProperty(QAction *action)
684 {
685     if (!m_propertySheet)
686         return;
687 
688     const QDesignerDynamicPropertySheetExtension *dynamicSheet =
689             qt_extension<QDesignerDynamicPropertySheetExtension*>(m_core->extensionManager(), 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(i) || m_propertySheet->isVisible(i))
706                 reservedNames.append(m_propertySheet->propertyName(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(newName, newValue);
716 }
717 
core() const718 QDesignerFormEditorInterface *PropertyEditor::core() const
719 {
720     return m_core;
721 }
722 
isReadOnly() const723 bool PropertyEditor::isReadOnly() const
724 {
725     return false;
726 }
727 
setReadOnly(bool)728 void PropertyEditor::setReadOnly(bool /*readOnly*/)
729 {
730     qDebug() << "PropertyEditor::setReadOnly() request";
731 }
732 
setPropertyValue(const QString & name,const QVariant & value,bool changed)733 void PropertyEditor::setPropertyValue(const QString &name, const QVariant &value, bool changed)
734 {
735     const QMap<QString, QtVariantProperty*>::const_iterator it = m_nameToProperty.constFind(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. */
updatePropertySheet()745 void 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(i);
756         QMap<QString, QtVariantProperty*>::const_iterator it = m_nameToProperty.constFind(propertyName);
757         if (it != npcend)
758             updateBrowserValue(it.value(), m_propertySheet->property(i));
759     }
760 }
761 
layoutOfQLayoutWidget(QObject * o)762 static inline QLayout *layoutOfQLayoutWidget(QObject *o)
763 {
764     if (o->isWidgetType() && !qstrcmp(o->metaObject()->className(), "QLayoutWidget"))
765         return static_cast<QWidget*>(o)->layout();
766     return nullptr;
767 }
768 
updateToolBarLabel()769 void PropertyEditor::updateToolBarLabel()
770 {
771     QString objectName;
772     QString className;
773     if (m_object) {
774         if (QLayout *l = layoutOfQLayoutWidget(m_object))
775             objectName = l->objectName();
776         else
777             objectName = m_object->objectName();
778         className = realClassName(m_object);
779     }
780 
781     m_classLabel->setVisible(!objectName.isEmpty() || !className.isEmpty());
782     m_classLabel->setSizePolicy(QSizePolicy::Expanding, 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("Object: %1\nClass: %2")
791                              .arg(objectName, className));
792 }
793 
updateBrowserValue(QtVariantProperty * property,const QVariant & value)794 void 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(e.metaEnum.valueToKey(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*>(m_core->extensionManager()->extension(m_object, Q_TYPEID(QDesignerPropertySheetExtension)));
809     int index = -1;
810     if (sheet)
811         index = sheet->indexOf(property->propertyName());
812     if (sheet && m_propertyToGroup.contains(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(m_strings.m_fontProperty);
819         if (fontIndex != -1)
820             property->setAttribute(m_strings.m_fontAttribute, m_propertySheet->property(fontIndex));
821     }
822 
823     m_updatingBrowser = true;
824     property->setValue(v);
825     if (sheet && sheet->isResourceProperty(index))
826         property->setAttribute(QStringLiteral("defaultResource"), sheet->defaultResourceProperty(index));
827     m_updatingBrowser = false;
828 }
829 
toBrowserType(const QVariant & value,const QString & propertyName) const830 int PropertyEditor::toBrowserType(const QVariant &value, const QString &propertyName) const
831 {
832     if (value.canConvert<PropertySheetFlagValue>()) {
833         if (m_strings.m_alignmentProperties.contains(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 
realClassName(QObject * object) const843 QString 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(db->indexOfObject(object, 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(m_strings.m_designerPrefix))
860         className.remove(1, m_strings.m_designerPrefix.size() - 1);
861 
862     return className;
863 }
864 
typeName(int type)865 static 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(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 
msgUnsupportedType(const QString & propertyName,int type)888 static 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 
setObject(QObject * object)898 void PropertyEditor::setObject(QObject *object)
899 {
900     QDesignerFormWindowInterface *oldFormWindow = QDesignerFormWindowInterface::findFormWindow(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(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("PropertyEditor::setObject(): Unable to find form window for \"%s\".",
911                      qPrintable(object->objectName()));
912             return;
913         }
914     }
915     FormWindowBase *fwb = qobject_cast<FormWindowBase *>(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*>(m_core->extensionManager(), m_object);
932     const QDesignerPropertySheet *sheet = qobject_cast<QDesignerPropertySheet*>(m_core->extensionManager()->extension(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*>(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(i))
944                 continue;
945 
946             const QString propertyName = m_propertySheet->propertyName(i);
947             if (m_propertySheet->indexOf(propertyName) != i)
948                 continue;
949             const QString groupName = m_propertySheet->propertyGroup(i);
950             const QMap<QString, QtVariantProperty *>::const_iterator rit = toRemove.constFind(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(property) == groupName
957                     && (idIdBasedTranslationUnchanged || propertyType != stringTypeId)
958                     && toBrowserType(m_propertySheet->property(i), propertyName) == propertyType) {
959                     toRemove.remove(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(itRemove.key());
968         m_propertyToGroup.remove(property);
969         delete property;
970     }
971 
972     if (oldFormWindow != formWindow)
973         reloadResourceProperties();
974 
975     bool isMainContainer = false;
976     if (QWidget *widget = qobject_cast<QWidget*>(object)) {
977         if (QDesignerFormWindowInterface *fw = QDesignerFormWindowInterface::findFormWindow(widget)) {
978             isMainContainer = (fw->mainContainer() == widget);
979         }
980     }
981     m_groups.clear();
982 
983     if (m_propertySheet) {
984         const QString className = WidgetFactory::classNameOf(formWindow->core(), 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(i))
992                 continue;
993 
994             const QString propertyName = m_propertySheet->propertyName(i);
995             if (m_propertySheet->indexOf(propertyName) != i)
996                 continue;
997             const QVariant value = m_propertySheet->property(i);
998 
999             const int type = toBrowserType(value, propertyName);
1000 
1001             QtVariantProperty *property = m_nameToProperty.value(propertyName, 0);
1002             bool newProperty = property == nullptr;
1003             if (newProperty) {
1004                 property = m_propertyManager->addProperty(type, propertyName);
1005                 if (property) {
1006                     newProperty = true;
1007                     if (type == DesignerPropertyManager::enumTypeId()) {
1008                         const PropertySheetEnumValue e = qvariant_cast<PropertySheetEnumValue>(value);
1009                         m_updatingBrowser = true;
1010                         property->setAttribute(m_strings.m_enumNamesAttribute, e.metaEnum.keys());
1011                         m_updatingBrowser = false;
1012                     } else if (type == DesignerPropertyManager::designerFlagTypeId()) {
1013                         const PropertySheetFlagValue f = qvariant_cast<PropertySheetFlagValue>(value);
1014                         QList<QPair<QString, uint> > flags;
1015                         for (const QString &name : f.metaFlags.keys()) {
1016                             const uint val = f.metaFlags.keyToValue(name);
1017                             flags.append(qMakePair(name, val));
1018                         }
1019                         m_updatingBrowser = true;
1020                         QVariant v;
1021                         v.setValue(flags);
1022                         property->setAttribute(m_strings.m_flagsAttribute, v);
1023                         m_updatingBrowser = false;
1024                     }
1025                 }
1026             }
1027 
1028             if (property != nullptr) {
1029                 const bool dynamicProperty = (dynamicSheet && dynamicSheet->isDynamicProperty(i))
1030                             || (sheet && sheet->isDefaultDynamicProperty(i));
1031                 QString descriptionToolTip;
1032                 if (!dynamicProperty && !customData.isNull())
1033                     descriptionToolTip = customData.propertyToolTip(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(m_strings.m_resettableAttribute, m_propertySheet->hasReset(i));
1055 
1056                 const QString groupName = m_propertySheet->propertyGroup(i);
1057                 QtVariantProperty *groupProperty = nullptr;
1058 
1059                 if (newProperty) {
1060                     QMap<QString, QtVariantProperty*>::const_iterator itPrev(m_nameToProperty.insert(propertyName, 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, previous);
1067                     }
1068                 }
1069                 const QMap<QString, QtVariantProperty*>::const_iterator gnit = m_nameToGroup.constFind(groupName);
1070                 if (gnit != m_nameToGroup.constEnd()) {
1071                     groupProperty = gnit.value();
1072                 } else {
1073                     groupProperty = m_propertyManager->addProperty(QtVariantPropertyManager::groupTypeId(), groupName);
1074                     QtBrowserItem *item = nullptr;
1075                     if (!m_sorting)
1076                          item = m_currentBrowser->insertProperty(groupProperty, lastGroup);
1077                     m_nameToGroup[groupName] = groupProperty;
1078                     m_groups.append(groupProperty);
1079                     if (dynamicProperty)
1080                         m_dynamicGroup = groupProperty;
1081                     if (m_currentBrowser == m_treeBrowser && item) {
1082                         m_treeBrowser->setBackgroundColor(item, propertyColor(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(groupProperty))
1101                     m_groups.append(groupProperty);
1102                 if (newProperty)
1103                     groupProperty->insertSubProperty(property, lastProperty);
1104 
1105                 lastProperty = property;
1106 
1107                 updateBrowserValue(property, value);
1108 
1109                 property->setModified(m_propertySheet->isChanged(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("%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(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(QEventLoop::ExcludeUserInputEvents);
1143         editProperty(m_recentlyAddedDynamicProperty);
1144     }
1145     m_recentlyAddedDynamicProperty.clear();
1146     m_filterWidget->setEnabled(object);
1147 }
1148 
reloadResourceProperties()1149 void PropertyEditor::reloadResourceProperties()
1150 {
1151     m_updatingBrowser = true;
1152     m_propertyManager->reloadResourceProperties();
1153     m_updatingBrowser = false;
1154 }
1155 
nonFakePropertyBrowserItem(QtBrowserItem * item) const1156 QtBrowserItem *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(item->parent()))
1163             return item;
1164         item = item->parent();
1165     } while (item);
1166     return nullptr;
1167 }
1168 
currentPropertyName() const1169 QString PropertyEditor::currentPropertyName() const
1170 {
1171     if (QtBrowserItem *browserItem = m_currentBrowser->currentItem())
1172         if (QtBrowserItem *topLevelItem = nonFakePropertyBrowserItem(browserItem)) {
1173             return topLevelItem->property()->propertyName();
1174         }
1175     return QString();
1176 }
1177 
slotResetProperty(QtProperty * property)1178 void 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(property))
1188         return;
1189 
1190     if (!m_propertyToGroup.contains(property))
1191         return;
1192 
1193     emit resetProperty(property->propertyName());
1194 }
1195 
slotValueChanged(QtProperty * property,const QVariant & value,bool enableSubPropertyHandling)1196 void PropertyEditor::slotValueChanged(QtProperty *property, const QVariant &value, bool enableSubPropertyHandling)
1197 {
1198     if (m_updatingBrowser)
1199         return;
1200 
1201     if (!m_propertySheet)
1202         return;
1203 
1204     QtVariantProperty *varProp = m_propertyManager->variantProperty(property);
1205 
1206     if (!varProp)
1207         return;
1208 
1209     if (!m_propertyToGroup.contains(property))
1210         return;
1211 
1212     if (varProp->propertyType() == QtVariantPropertyManager::enumTypeId()) {
1213         PropertySheetEnumValue e = qvariant_cast<PropertySheetEnumValue>(m_propertySheet->property(m_propertySheet->indexOf(property->propertyName())));
1214         const int val = value.toInt();
1215         const QString valName = varProp->attributeValue(m_strings.m_enumNamesAttribute).toStringList().at(val);
1216         bool ok = false;
1217         e.value = e.metaEnum.parseEnum(valName, &ok);
1218         Q_ASSERT(ok);
1219         QVariant v;
1220         v.setValue(e);
1221         emitPropertyValueChanged(property->propertyName(), v, true);
1222         return;
1223     }
1224 
1225     emitPropertyValueChanged(property->propertyName(), value, enableSubPropertyHandling);
1226 }
1227 
isDynamicProperty(const QtBrowserItem * item) const1228 bool PropertyEditor::isDynamicProperty(const QtBrowserItem* item) const
1229 {
1230     if (!item)
1231         return false;
1232 
1233     const QDesignerDynamicPropertySheetExtension *dynamicSheet =
1234             qt_extension<QDesignerDynamicPropertySheetExtension*>(m_core->extensionManager(), m_object);
1235 
1236     if (!dynamicSheet)
1237         return false;
1238 
1239     return m_propertyToGroup.contains(item->property())
1240         && dynamicSheet->isDynamicProperty(m_propertySheet->indexOf(item->property()->propertyName()));
1241 }
1242 
editProperty(const QString & name)1243 void PropertyEditor::editProperty(const QString &name)
1244 {
1245     // find the browser item belonging to the property, make it current and edit it
1246     QtBrowserItem *browserItem = nullptr;
1247     if (QtVariantProperty *property = m_nameToProperty.value(name, 0)) {
1248         const auto items = m_currentBrowser->items(property);
1249         if (items.size() == 1)
1250             browserItem = items.constFirst();
1251     }
1252     if (browserItem == nullptr)
1253         return;
1254     m_currentBrowser->setFocus(Qt::OtherFocusReason);
1255     if (m_currentBrowser == m_treeBrowser) { // edit is currently only supported in tree view
1256         m_treeBrowser->editItem(browserItem);
1257     } else {
1258         m_currentBrowser->setCurrentItem(browserItem);
1259     }
1260 }
1261 
slotCurrentItemChanged(QtBrowserItem * item)1262 void PropertyEditor::slotCurrentItemChanged(QtBrowserItem *item)
1263 {
1264     m_removeDynamicAction->setEnabled(isDynamicProperty(item));
1265 
1266 }
1267 
slotRemoveDynamicProperty()1268 void PropertyEditor::slotRemoveDynamicProperty()
1269 {
1270     if (QtBrowserItem* item = m_currentBrowser->currentItem())
1271         if (isDynamicProperty(item))
1272             emit removeDynamicProperty(item->property()->propertyName());
1273 }
1274 
setFilter(const QString & pattern)1275 void PropertyEditor::setFilter(const QString &pattern)
1276 {
1277     m_filterPattern = pattern;
1278     applyFilter();
1279 }
1280 }
1281 
1282 QT_END_NAMESPACE
1283