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