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: |
93 | explicit ElidingLabel(const QString &text = QString(), |
94 | QWidget *parent = nullptr) : QWidget(parent), m_text(text) |
95 | { setContentsMargins(left: 3, top: 2, right: 3, bottom: 2); } |
96 | |
97 | void setText(const QString &text) { |
98 | m_text = text; |
99 | updateGeometry(); |
100 | } |
101 | void setElidemode(Qt::TextElideMode mode) { |
102 | m_mode = mode; |
103 | updateGeometry(); |
104 | } |
105 | |
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 | |
115 | QSize ElidingLabel::sizeHint() const |
116 | { |
117 | QSize size = fontMetrics().boundingRect(text: m_text).size(); |
118 | size += QSize(contentsMargins().left() + contentsMargins().right(), |
119 | contentsMargins().top() + contentsMargins().bottom()); |
120 | return size; |
121 | } |
122 | |
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(r: rect().adjusted(xp1: 0, yp1: 0, xp2: -1, yp2: -1)); |
128 | painter.setPen(palette().windowText().color()); |
129 | painter.drawText(r: contentsRect(), flags: Qt::AlignLeft, |
130 | text: fontMetrics().elidedText(text: m_text, mode: Qt::ElideRight, width: width(), flags: 0)); |
131 | } |
132 | |
133 | |
134 | // ----------- PropertyEditor::Strings |
135 | |
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 | |
155 | 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(object: o); |
164 | } |
165 | |
166 | void PropertyEditor::setupStringProperty(QtVariantProperty *property, bool isMainContainer) |
167 | { |
168 | const StringPropertyParameters params = textPropertyValidationMode(core: core(), object: m_object, propertyName: property->propertyName(), isMainContainer); |
169 | // Does a meta DB entry exist - add comment |
170 | const bool = params.second; |
171 | property->setAttribute(attribute: m_strings.m_validationModeAttribute, value: params.first); |
172 | // assuming comment cannot appear or disappear for the same property in different object instance |
173 | if (!hasComment) |
174 | qDeleteAll(c: property->subProperties()); |
175 | } |
176 | |
177 | void PropertyEditor::setupPaletteProperty(QtVariantProperty *property) |
178 | { |
179 | QPalette superPalette = QPalette(); |
180 | QWidget *currentWidget = qobject_cast<QWidget *>(o: m_object); |
181 | if (currentWidget) { |
182 | if (currentWidget->isWindow()) |
183 | superPalette = QApplication::palette(currentWidget); |
184 | else { |
185 | if (currentWidget->parentWidget()) |
186 | superPalette = currentWidget->parentWidget()->palette(); |
187 | } |
188 | } |
189 | m_updatingBrowser = true; |
190 | property->setAttribute(attribute: m_strings.m_superPaletteAttribute, value: superPalette); |
191 | m_updatingBrowser = false; |
192 | } |
193 | |
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 | |
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(s: "Add Dynamic Property..." ), this)), |
209 | m_removeDynamicAction(new QAction(createIconSet(QStringLiteral("minus.png" )), tr(s: "Remove Dynamic Property" ), this)), |
210 | m_sortingAction(new QAction(createIconSet(QStringLiteral("sort.png" )), tr(s: "Sorting" ), this)), |
211 | m_coloringAction(new QAction(createIconSet(QStringLiteral("color.png" )), tr(s: "Color Groups" ), this)), |
212 | m_treeAction(new QAction(tr(s: "Tree View" ), this)), |
213 | m_buttonAction(new QAction(tr(s: "Drop Down Button View" ), this)), |
214 | m_classLabel(new ElidingLabel) |
215 | { |
216 | QVector<QColor> colors; |
217 | colors.reserve(asize: 6); |
218 | colors.push_back(t: QColor(255, 230, 191)); |
219 | colors.push_back(t: QColor(255, 255, 191)); |
220 | colors.push_back(t: QColor(191, 255, 191)); |
221 | colors.push_back(t: QColor(199, 255, 255)); |
222 | colors.push_back(t: QColor(234, 191, 255)); |
223 | colors.push_back(t: QColor(255, 191, 239)); |
224 | m_colors.reserve(asize: colors.count()); |
225 | const int darknessFactor = 250; |
226 | for (int i = 0; i < colors.count(); i++) { |
227 | const QColor &c = colors.at(i); |
228 | m_colors.push_back(t: qMakePair(x: c, y: c.darker(f: darknessFactor))); |
229 | } |
230 | QColor dynamicColor(191, 207, 255); |
231 | QColor layoutColor(255, 191, 191); |
232 | m_dynamicColor = qMakePair(x: dynamicColor, y: dynamicColor.darker(f: darknessFactor)); |
233 | m_layoutColor = qMakePair(x: layoutColor, y: layoutColor.darker(f: darknessFactor)); |
234 | |
235 | updateForegroundBrightness(); |
236 | |
237 | QActionGroup *actionGroup = new QActionGroup(this); |
238 | |
239 | m_treeAction->setCheckable(true); |
240 | m_treeAction->setIcon(createIconSet(QStringLiteral("widgets/listview.png" ))); |
241 | m_buttonAction->setCheckable(true); |
242 | m_buttonAction->setIcon(createIconSet(QStringLiteral("dropdownbutton.png" ))); |
243 | |
244 | actionGroup->addAction(a: m_treeAction); |
245 | actionGroup->addAction(a: m_buttonAction); |
246 | connect(sender: actionGroup, signal: &QActionGroup::triggered, |
247 | receiver: this, slot: &PropertyEditor::slotViewTriggered); |
248 | |
249 | // Add actions |
250 | QActionGroup *addDynamicActionGroup = new QActionGroup(this); |
251 | connect(sender: addDynamicActionGroup, signal: &QActionGroup::triggered, |
252 | receiver: this, slot: &PropertyEditor::slotAddDynamicProperty); |
253 | |
254 | QMenu * = new QMenu(this); |
255 | m_addDynamicAction->setMenu(addDynamicActionMenu); |
256 | m_addDynamicAction->setEnabled(false); |
257 | QAction *addDynamicAction = addDynamicActionGroup->addAction(text: tr(s: "String..." )); |
258 | addDynamicAction->setData(static_cast<int>(QVariant::String)); |
259 | addDynamicActionMenu->addAction(action: addDynamicAction); |
260 | addDynamicAction = addDynamicActionGroup->addAction(text: tr(s: "Bool..." )); |
261 | addDynamicAction->setData(static_cast<int>(QVariant::Bool)); |
262 | addDynamicActionMenu->addAction(action: addDynamicAction); |
263 | addDynamicActionMenu->addSeparator(); |
264 | addDynamicAction = addDynamicActionGroup->addAction(text: tr(s: "Other..." )); |
265 | addDynamicAction->setData(static_cast<int>(QVariant::Invalid)); |
266 | addDynamicActionMenu->addAction(action: addDynamicAction); |
267 | // remove |
268 | m_removeDynamicAction->setEnabled(false); |
269 | connect(sender: m_removeDynamicAction, signal: &QAction::triggered, receiver: this, slot: &PropertyEditor::slotRemoveDynamicProperty); |
270 | // Configure |
271 | QAction *configureAction = new QAction(tr(s: "Configure Property Editor" ), this); |
272 | configureAction->setIcon(createIconSet(QStringLiteral("configure.png" ))); |
273 | QMenu * = new QMenu(this); |
274 | configureAction->setMenu(configureMenu); |
275 | |
276 | m_sortingAction->setCheckable(true); |
277 | connect(sender: m_sortingAction, signal: &QAction::toggled, receiver: this, slot: &PropertyEditor::slotSorting); |
278 | |
279 | m_coloringAction->setCheckable(true); |
280 | connect(sender: m_coloringAction, signal: &QAction::toggled, receiver: this, slot: &PropertyEditor::slotColoring); |
281 | |
282 | configureMenu->addAction(action: m_sortingAction); |
283 | configureMenu->addAction(action: m_coloringAction); |
284 | configureMenu->addSeparator(); |
285 | configureMenu->addAction(action: m_treeAction); |
286 | configureMenu->addAction(action: m_buttonAction); |
287 | // Assemble toolbar |
288 | QToolBar *toolBar = new QToolBar; |
289 | toolBar->addWidget(widget: m_filterWidget); |
290 | toolBar->addWidget(widget: createDropDownButton(defaultAction: m_addDynamicAction)); |
291 | toolBar->addAction(action: m_removeDynamicAction); |
292 | toolBar->addWidget(widget: createDropDownButton(defaultAction: configureAction)); |
293 | // Views |
294 | QScrollArea *buttonScroll = new QScrollArea(m_stackedWidget); |
295 | m_buttonBrowser = new QtButtonPropertyBrowser(buttonScroll); |
296 | buttonScroll->setWidgetResizable(true); |
297 | buttonScroll->setWidget(m_buttonBrowser); |
298 | m_buttonIndex = m_stackedWidget->addWidget(w: buttonScroll); |
299 | connect(sender: m_buttonBrowser, signal: &QtAbstractPropertyBrowser::currentItemChanged, |
300 | receiver: this, slot: &PropertyEditor::slotCurrentItemChanged); |
301 | |
302 | m_treeBrowser = new QtTreePropertyBrowser(m_stackedWidget); |
303 | m_treeBrowser->setRootIsDecorated(false); |
304 | m_treeBrowser->setPropertiesWithoutValueMarked(true); |
305 | m_treeBrowser->setResizeMode(QtTreePropertyBrowser::Interactive); |
306 | m_treeIndex = m_stackedWidget->addWidget(w: m_treeBrowser); |
307 | connect(sender: m_treeBrowser, signal: &QtAbstractPropertyBrowser::currentItemChanged, |
308 | receiver: this, slot: &PropertyEditor::slotCurrentItemChanged); |
309 | m_filterWidget->setPlaceholderText(tr(s: "Filter" )); |
310 | m_filterWidget->setClearButtonEnabled(true); |
311 | connect(sender: m_filterWidget, signal: &QLineEdit::textChanged, receiver: this, slot: &PropertyEditor::setFilter); |
312 | |
313 | QVBoxLayout *layout = new QVBoxLayout(this); |
314 | layout->addWidget(toolBar); |
315 | layout->addWidget(m_classLabel); |
316 | layout->addSpacerItem(spacerItem: new QSpacerItem(0,1)); |
317 | layout->addWidget(m_stackedWidget); |
318 | layout->setContentsMargins(QMargins()); |
319 | layout->setSpacing(0); |
320 | |
321 | m_treeFactory = new DesignerEditorFactory(m_core, this); |
322 | m_treeFactory->setSpacing(0); |
323 | m_groupFactory = new DesignerEditorFactory(m_core, this); |
324 | QtVariantPropertyManager *variantManager = m_propertyManager; |
325 | m_buttonBrowser->setFactoryForManager(manager: variantManager, factory: m_groupFactory); |
326 | m_treeBrowser->setFactoryForManager(manager: variantManager, factory: m_treeFactory); |
327 | |
328 | m_stackedWidget->setCurrentIndex(m_treeIndex); |
329 | m_currentBrowser = m_treeBrowser; |
330 | m_treeAction->setChecked(true); |
331 | |
332 | connect(sender: m_groupFactory, signal: &DesignerEditorFactory::resetProperty, |
333 | receiver: this, slot: &PropertyEditor::slotResetProperty); |
334 | connect(sender: m_treeFactory, signal: &DesignerEditorFactory::resetProperty, |
335 | receiver: this, slot: &PropertyEditor::slotResetProperty); |
336 | connect(sender: m_propertyManager, signal: &DesignerPropertyManager::valueChanged, |
337 | receiver: this, slot: &PropertyEditor::slotValueChanged); |
338 | |
339 | // retrieve initial settings |
340 | QDesignerSettingsInterface *settings = m_core->settingsManager(); |
341 | settings->beginGroup(prefix: QLatin1String(SettingsGroupC)); |
342 | const SettingsView view = settings->value(key: QLatin1String(ViewKeyC), defaultValue: TreeView).toInt() == TreeView ? TreeView : ButtonView; |
343 | // Coloring not available unless treeview and not sorted |
344 | m_sorting = settings->value(key: QLatin1String(SortedKeyC), defaultValue: false).toBool(); |
345 | m_coloring = settings->value(key: QLatin1String(ColorKeyC), defaultValue: true).toBool(); |
346 | const QVariantMap expansionState = settings->value(key: QLatin1String(ExpansionKeyC), defaultValue: QVariantMap()).toMap(); |
347 | const int splitterPosition = settings->value(key: QLatin1String(SplitterPositionKeyC), defaultValue: 150).toInt(); |
348 | settings->endGroup(); |
349 | // Apply settings |
350 | m_sortingAction->setChecked(m_sorting); |
351 | m_coloringAction->setChecked(m_coloring); |
352 | m_treeBrowser->setSplitterPosition(splitterPosition); |
353 | switch (view) { |
354 | case TreeView: |
355 | m_currentBrowser = m_treeBrowser; |
356 | m_stackedWidget->setCurrentIndex(m_treeIndex); |
357 | m_treeAction->setChecked(true); |
358 | break; |
359 | case ButtonView: |
360 | m_currentBrowser = m_buttonBrowser; |
361 | m_stackedWidget->setCurrentIndex(m_buttonIndex); |
362 | m_buttonAction->setChecked(true); |
363 | break; |
364 | } |
365 | // Restore expansionState from QVariant map |
366 | if (!expansionState.isEmpty()) { |
367 | const QVariantMap::const_iterator cend = expansionState.constEnd(); |
368 | for (QVariantMap::const_iterator it = expansionState.constBegin(); it != cend; ++it) |
369 | m_expansionState.insert(akey: it.key(), avalue: it.value().toBool()); |
370 | } |
371 | updateActionsState(); |
372 | } |
373 | |
374 | PropertyEditor::~PropertyEditor() |
375 | { |
376 | storeExpansionState(); |
377 | saveSettings(); |
378 | } |
379 | |
380 | void PropertyEditor::saveSettings() const |
381 | { |
382 | QDesignerSettingsInterface *settings = m_core->settingsManager(); |
383 | settings->beginGroup(prefix: QLatin1String(SettingsGroupC)); |
384 | settings->setValue(key: QLatin1String(ViewKeyC), value: QVariant(m_treeAction->isChecked() ? TreeView : ButtonView)); |
385 | settings->setValue(key: QLatin1String(ColorKeyC), value: QVariant(m_coloring)); |
386 | settings->setValue(key: QLatin1String(SortedKeyC), value: QVariant(m_sorting)); |
387 | // Save last expansionState as QVariant map |
388 | QVariantMap expansionState; |
389 | if (!m_expansionState.isEmpty()) { |
390 | const QMap<QString, bool>::const_iterator cend = m_expansionState.constEnd(); |
391 | for (QMap<QString, bool>::const_iterator it = m_expansionState.constBegin(); it != cend; ++it) |
392 | expansionState.insert(akey: it.key(), avalue: QVariant(it.value())); |
393 | } |
394 | settings->setValue(key: QLatin1String(ExpansionKeyC), value: expansionState); |
395 | settings->setValue(key: QLatin1String(SplitterPositionKeyC), value: m_treeBrowser->splitterPosition()); |
396 | settings->endGroup(); |
397 | } |
398 | |
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 | |
407 | 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 | |
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(msg: "** WARNING %s is not implemented for this browser." , Q_FUNC_INFO); |
422 | } |
423 | } |
424 | |
425 | 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 | |
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(akey: property); |
443 | if (itGroup != m_propertyToGroup.constEnd()) { |
444 | QString key = itGroup.value(); |
445 | key += bar; |
446 | key += propertyName; |
447 | m_expansionState[key] = isExpanded(item: propertyItem); |
448 | } |
449 | } |
450 | } |
451 | } |
452 | |
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(items: propertyItems); |
467 | } |
468 | } |
469 | } |
470 | |
471 | void PropertyEditor::collapseAll() |
472 | { |
473 | const auto items = m_currentBrowser->topLevelItems(); |
474 | for (QtBrowserItem *group : items) |
475 | setExpanded(item: group, expanded: false); |
476 | } |
477 | |
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(akey: property); |
486 | if (itGroup != m_propertyToGroup.constEnd()) { |
487 | QString key = itGroup.value(); |
488 | key += bar; |
489 | key += propertyName; |
490 | const QMap<QString, bool>::const_iterator pit = m_expansionState.constFind(akey: key); |
491 | if (pit != excend) |
492 | setExpanded(item: propertyItem, expanded: pit.value()); |
493 | else |
494 | setExpanded(item: propertyItem, expanded: false); |
495 | } |
496 | } |
497 | } |
498 | |
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(akey: groupName); |
509 | if (git != excend) |
510 | setExpanded(item, expanded: git.value()); |
511 | else |
512 | setExpanded(item, expanded: true); |
513 | // properties stuff here |
514 | applyPropertiesExpansionState(items: item->children()); |
515 | } |
516 | } |
517 | } |
518 | |
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(s: m_filterPattern, cs: Qt::CaseInsensitive); |
527 | setItemVisible(item: propertyItem, visible: showProperty); |
528 | if (showProperty) |
529 | showCount++; |
530 | } |
531 | return showCount; |
532 | } |
533 | |
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, visible: applyPropertiesFilter(items: item->children())); |
542 | } |
543 | } |
544 | |
545 | void PropertyEditor::clearView() |
546 | { |
547 | m_currentBrowser->clear(); |
548 | } |
549 | |
550 | bool PropertyEditor::event(QEvent *event) |
551 | { |
552 | if (event->type() == QEvent::PaletteChange) |
553 | updateForegroundBrightness(); |
554 | |
555 | return QDesignerPropertyEditor::event(event); |
556 | } |
557 | |
558 | void PropertyEditor::updateForegroundBrightness() |
559 | { |
560 | QColor c = palette().color(cr: QPalette::Text); |
561 | bool newBrightness = qRound(d: 0.3 * c.redF() + 0.59 * c.greenF() + 0.11 * c.blueF()); |
562 | |
563 | if (m_brightness == newBrightness) |
564 | return; |
565 | |
566 | m_brightness = newBrightness; |
567 | |
568 | updateColors(); |
569 | } |
570 | |
571 | 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(akey: property); |
579 | if (itProp != m_propertyToGroup.constEnd()) |
580 | groupProperty = m_nameToGroup.value(akey: itProp.value()); |
581 | |
582 | const int groupIdx = m_groups.indexOf(t: groupProperty); |
583 | QPair<QColor, QColor> pair; |
584 | if (groupIdx != -1) { |
585 | if (groupProperty == m_dynamicGroup) |
586 | pair = m_dynamicColor; |
587 | else if (isLayoutGroup(group: groupProperty)) |
588 | pair = m_layoutColor; |
589 | else |
590 | pair = m_colors[groupIdx % m_colors.count()]; |
591 | } |
592 | if (!m_brightness) |
593 | return pair.first; |
594 | return pair.second; |
595 | } |
596 | |
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(property: itProperty.value()); |
602 | } else { |
603 | for (QtProperty *group : qAsConst(t&: m_groups)) { |
604 | QtBrowserItem *item = m_currentBrowser->addProperty(property: group); |
605 | if (m_currentBrowser == m_treeBrowser) |
606 | m_treeBrowser->setBackgroundColor(item, color: propertyColor(property: group)); |
607 | group->setModified(m_currentBrowser == m_treeBrowser); |
608 | } |
609 | } |
610 | } |
611 | |
612 | bool PropertyEditor::isLayoutGroup(QtProperty *group) const |
613 | { |
614 | return group->propertyName() == m_strings.m_layout; |
615 | } |
616 | |
617 | void PropertyEditor::updateActionsState() |
618 | { |
619 | m_coloringAction->setEnabled(m_treeAction->isChecked() && !m_sortingAction->isChecked()); |
620 | } |
621 | |
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 | |
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 | |
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, color: propertyColor(property: item->property())); |
670 | } |
671 | } |
672 | |
673 | void PropertyEditor::slotColoring(bool coloring) |
674 | { |
675 | if (coloring == m_coloring) |
676 | return; |
677 | |
678 | m_coloring = coloring; |
679 | |
680 | updateColors(); |
681 | } |
682 | |
683 | void PropertyEditor::slotAddDynamicProperty(QAction *action) |
684 | { |
685 | if (!m_propertySheet) |
686 | return; |
687 | |
688 | const QDesignerDynamicPropertySheetExtension *dynamicSheet = |
689 | qt_extension<QDesignerDynamicPropertySheetExtension*>(manager: m_core->extensionManager(), object: m_object); |
690 | |
691 | if (!dynamicSheet) |
692 | return; |
693 | |
694 | QString newName; |
695 | QVariant newValue; |
696 | { // Make sure the dialog is closed before the signal is emitted. |
697 | const QVariant::Type type = static_cast<QVariant::Type>(action->data().toInt()); |
698 | NewDynamicPropertyDialog dlg(core()->dialogGui(), m_currentBrowser); |
699 | if (type != QVariant::Invalid) |
700 | dlg.setPropertyType(type); |
701 | |
702 | QStringList reservedNames; |
703 | const int propertyCount = m_propertySheet->count(); |
704 | for (int i = 0; i < propertyCount; i++) { |
705 | if (!dynamicSheet->isDynamicProperty(index: i) || m_propertySheet->isVisible(index: i)) |
706 | reservedNames.append(t: m_propertySheet->propertyName(index: i)); |
707 | } |
708 | dlg.setReservedNames(reservedNames); |
709 | if (dlg.exec() == QDialog::Rejected) |
710 | return; |
711 | newName = dlg.propertyName(); |
712 | newValue = dlg.propertyValue(); |
713 | } |
714 | m_recentlyAddedDynamicProperty = newName; |
715 | emit addDynamicProperty(name: newName, value: newValue); |
716 | } |
717 | |
718 | QDesignerFormEditorInterface *PropertyEditor::core() const |
719 | { |
720 | return m_core; |
721 | } |
722 | |
723 | bool PropertyEditor::isReadOnly() const |
724 | { |
725 | return false; |
726 | } |
727 | |
728 | void PropertyEditor::setReadOnly(bool /*readOnly*/) |
729 | { |
730 | qDebug() << "PropertyEditor::setReadOnly() request" ; |
731 | } |
732 | |
733 | void PropertyEditor::setPropertyValue(const QString &name, const QVariant &value, bool changed) |
734 | { |
735 | const QMap<QString, QtVariantProperty*>::const_iterator it = m_nameToProperty.constFind(akey: name); |
736 | if (it == m_nameToProperty.constEnd()) |
737 | return; |
738 | QtVariantProperty *property = it.value(); |
739 | updateBrowserValue(property, value); |
740 | property->setModified(changed); |
741 | } |
742 | |
743 | /* Quick update that assumes the actual count of properties has not changed |
744 | * N/A when for example executing a layout command and margin properties appear. */ |
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(index: i); |
756 | QMap<QString, QtVariantProperty*>::const_iterator it = m_nameToProperty.constFind(akey: propertyName); |
757 | if (it != npcend) |
758 | updateBrowserValue(property: it.value(), value: m_propertySheet->property(index: i)); |
759 | } |
760 | } |
761 | |
762 | static inline QLayout *layoutOfQLayoutWidget(QObject *o) |
763 | { |
764 | if (o->isWidgetType() && !qstrcmp(str1: o->metaObject()->className(), str2: "QLayoutWidget" )) |
765 | return static_cast<QWidget*>(o)->layout(); |
766 | return nullptr; |
767 | } |
768 | |
769 | void PropertyEditor::updateToolBarLabel() |
770 | { |
771 | QString objectName; |
772 | QString className; |
773 | if (m_object) { |
774 | if (QLayout *l = layoutOfQLayoutWidget(o: m_object)) |
775 | objectName = l->objectName(); |
776 | else |
777 | objectName = m_object->objectName(); |
778 | className = realClassName(object: m_object); |
779 | } |
780 | |
781 | m_classLabel->setVisible(!objectName.isEmpty() || !className.isEmpty()); |
782 | m_classLabel->setSizePolicy(hor: QSizePolicy::Expanding, ver: QSizePolicy::Fixed); |
783 | |
784 | QString classLabelText; |
785 | if (!objectName.isEmpty()) |
786 | classLabelText += objectName + QStringLiteral(" : " ); |
787 | classLabelText += className; |
788 | |
789 | m_classLabel->setText(classLabelText); |
790 | m_classLabel->setToolTip(tr(s: "Object: %1\nClass: %2" ) |
791 | .arg(args&: objectName, args&: className)); |
792 | } |
793 | |
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(t: e.metaEnum.valueToKey(value: e.value)); |
801 | } else if (type == DesignerPropertyManager::designerFlagTypeId()) { |
802 | const PropertySheetFlagValue f = qvariant_cast<PropertySheetFlagValue>(v); |
803 | v = QVariant(f.value); |
804 | } else if (type == DesignerPropertyManager::designerAlignmentTypeId()) { |
805 | const PropertySheetFlagValue f = qvariant_cast<PropertySheetFlagValue>(v); |
806 | v = QVariant(f.value); |
807 | } |
808 | QDesignerPropertySheet *sheet = qobject_cast<QDesignerPropertySheet*>(object: m_core->extensionManager()->extension(object: m_object, Q_TYPEID(QDesignerPropertySheetExtension))); |
809 | int index = -1; |
810 | if (sheet) |
811 | index = sheet->indexOf(name: property->propertyName()); |
812 | if (sheet && m_propertyToGroup.contains(akey: property)) { // don't do it for comments since property sheet doesn't keep them |
813 | property->setEnabled(sheet->isEnabled(index)); |
814 | } |
815 | |
816 | // Rich text string property with comment: Store/Update the font the rich text editor dialog starts out with |
817 | if (type == QVariant::String && !property->subProperties().isEmpty()) { |
818 | const int fontIndex = m_propertySheet->indexOf(name: m_strings.m_fontProperty); |
819 | if (fontIndex != -1) |
820 | property->setAttribute(attribute: m_strings.m_fontAttribute, value: m_propertySheet->property(index: fontIndex)); |
821 | } |
822 | |
823 | m_updatingBrowser = true; |
824 | property->setValue(v); |
825 | if (sheet && sheet->isResourceProperty(index)) |
826 | property->setAttribute(QStringLiteral("defaultResource" ), value: sheet->defaultResourceProperty(index)); |
827 | m_updatingBrowser = false; |
828 | } |
829 | |
830 | int PropertyEditor::toBrowserType(const QVariant &value, const QString &propertyName) const |
831 | { |
832 | if (value.canConvert<PropertySheetFlagValue>()) { |
833 | if (m_strings.m_alignmentProperties.contains(value: propertyName)) |
834 | return DesignerPropertyManager::designerAlignmentTypeId(); |
835 | return DesignerPropertyManager::designerFlagTypeId(); |
836 | } |
837 | if (value.canConvert<PropertySheetEnumValue>()) |
838 | return DesignerPropertyManager::enumTypeId(); |
839 | |
840 | return value.userType(); |
841 | } |
842 | |
843 | 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(index: db->indexOfObject(object, resolveName: true))) { |
851 | className = widgetItem->name(); |
852 | |
853 | if (object->isWidgetType() && className == m_strings.m_qLayoutWidget |
854 | && static_cast<QWidget*>(object)->layout()) { |
855 | className = QLatin1String(static_cast<QWidget*>(object)->layout()->metaObject()->className()); |
856 | } |
857 | } |
858 | |
859 | if (className.startsWith(s: m_strings.m_designerPrefix)) |
860 | className.remove(i: 1, len: m_strings.m_designerPrefix.size() - 1); |
861 | |
862 | return className; |
863 | } |
864 | |
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(typeId: static_cast<QVariant::Type>(type)); |
871 | if (type == qMetaTypeId<PropertySheetIconValue>()) |
872 | return "QIcon" ; |
873 | if (type == qMetaTypeId<PropertySheetPixmapValue>()) |
874 | return "QPixmap" ; |
875 | if (type == qMetaTypeId<PropertySheetKeySequenceValue>()) |
876 | return "QKeySequence" ; |
877 | if (type == qMetaTypeId<PropertySheetFlagValue>()) |
878 | return "QFlags" ; |
879 | if (type == qMetaTypeId<PropertySheetEnumValue>()) |
880 | return "enum" ; |
881 | if (type == QVariant::Invalid) |
882 | return "invalid" ; |
883 | if (type == QVariant::UserType) |
884 | return "user type" ; |
885 | return nullptr; |
886 | } |
887 | |
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 | |
898 | void PropertyEditor::setObject(QObject *object) |
899 | { |
900 | QDesignerFormWindowInterface *oldFormWindow = QDesignerFormWindowInterface::findFormWindow(obj: m_object); |
901 | // In the first setObject() call following the addition of a dynamic property, focus and edit it. |
902 | const bool editNewDynamicProperty = object != nullptr && m_object == object && !m_recentlyAddedDynamicProperty.isEmpty(); |
903 | m_object = object; |
904 | m_propertyManager->setObject(object); |
905 | QDesignerFormWindowInterface *formWindow = QDesignerFormWindowInterface::findFormWindow(obj: m_object); |
906 | // QTBUG-68507: Form window can be null for objects in Morph Undo macros with buddies |
907 | if (object != nullptr && formWindow == nullptr) { |
908 | formWindow = m_core->formWindowManager()->activeFormWindow(); |
909 | if (formWindow == nullptr) { |
910 | qWarning(msg: "PropertyEditor::setObject(): Unable to find form window for \"%s\"." , |
911 | qPrintable(object->objectName())); |
912 | return; |
913 | } |
914 | } |
915 | FormWindowBase *fwb = qobject_cast<FormWindowBase *>(object: formWindow); |
916 | const bool idIdBasedTranslation = fwb && fwb->useIdBasedTranslations(); |
917 | const bool idIdBasedTranslationUnchanged = (idIdBasedTranslation == DesignerPropertyManager::useIdBasedTranslations()); |
918 | DesignerPropertyManager::setUseIdBasedTranslations(idIdBasedTranslation); |
919 | m_treeFactory->setFormWindowBase(fwb); |
920 | m_groupFactory->setFormWindowBase(fwb); |
921 | |
922 | storeExpansionState(); |
923 | |
924 | UpdateBlocker ub(this); |
925 | |
926 | updateToolBarLabel(); |
927 | |
928 | QMap<QString, QtVariantProperty *> toRemove = m_nameToProperty; |
929 | |
930 | const QDesignerDynamicPropertySheetExtension *dynamicSheet = |
931 | qt_extension<QDesignerDynamicPropertySheetExtension*>(manager: m_core->extensionManager(), object: m_object); |
932 | const QDesignerPropertySheet *sheet = qobject_cast<QDesignerPropertySheet*>(object: m_core->extensionManager()->extension(object: m_object, Q_TYPEID(QDesignerPropertySheetExtension))); |
933 | |
934 | // Optimizization: Instead of rebuilding the complete list every time, compile a list of properties to remove, |
935 | // remove them, traverse the sheet, in case property exists just set a value, otherwise - create it. |
936 | QExtensionManager *m = m_core->extensionManager(); |
937 | |
938 | m_propertySheet = qobject_cast<QDesignerPropertySheetExtension*>(object: m->extension(object, Q_TYPEID(QDesignerPropertySheetExtension))); |
939 | if (m_propertySheet) { |
940 | const int stringTypeId = qMetaTypeId<PropertySheetStringValue>(); |
941 | const int propertyCount = m_propertySheet->count(); |
942 | for (int i = 0; i < propertyCount; ++i) { |
943 | if (!m_propertySheet->isVisible(index: i)) |
944 | continue; |
945 | |
946 | const QString propertyName = m_propertySheet->propertyName(index: i); |
947 | if (m_propertySheet->indexOf(name: propertyName) != i) |
948 | continue; |
949 | const QString groupName = m_propertySheet->propertyGroup(index: i); |
950 | const QMap<QString, QtVariantProperty *>::const_iterator rit = toRemove.constFind(akey: propertyName); |
951 | if (rit != toRemove.constEnd()) { |
952 | QtVariantProperty *property = rit.value(); |
953 | const int propertyType = property->propertyType(); |
954 | // Also remove string properties in case a change in translation mode |
955 | // occurred since different sub-properties are used (disambiguation/id). |
956 | if (m_propertyToGroup.value(akey: property) == groupName |
957 | && (idIdBasedTranslationUnchanged || propertyType != stringTypeId) |
958 | && toBrowserType(value: m_propertySheet->property(index: i), propertyName) == propertyType) { |
959 | toRemove.remove(akey: propertyName); |
960 | } |
961 | } |
962 | } |
963 | } |
964 | |
965 | for (auto itRemove = toRemove.cbegin(), end = toRemove.cend(); itRemove != end; ++itRemove) { |
966 | QtVariantProperty *property = itRemove.value(); |
967 | m_nameToProperty.remove(akey: itRemove.key()); |
968 | m_propertyToGroup.remove(akey: property); |
969 | delete property; |
970 | } |
971 | |
972 | if (oldFormWindow != formWindow) |
973 | reloadResourceProperties(); |
974 | |
975 | bool isMainContainer = false; |
976 | if (QWidget *widget = qobject_cast<QWidget*>(o: object)) { |
977 | if (QDesignerFormWindowInterface *fw = QDesignerFormWindowInterface::findFormWindow(w: widget)) { |
978 | isMainContainer = (fw->mainContainer() == widget); |
979 | } |
980 | } |
981 | m_groups.clear(); |
982 | |
983 | if (m_propertySheet) { |
984 | const QString className = WidgetFactory::classNameOf(core: formWindow->core(), o: m_object); |
985 | const QDesignerCustomWidgetData customData = formWindow->core()->pluginManager()->customWidgetData(className); |
986 | |
987 | QtProperty *lastProperty = nullptr; |
988 | QtProperty *lastGroup = nullptr; |
989 | const int propertyCount = m_propertySheet->count(); |
990 | for (int i = 0; i < propertyCount; ++i) { |
991 | if (!m_propertySheet->isVisible(index: i)) |
992 | continue; |
993 | |
994 | const QString propertyName = m_propertySheet->propertyName(index: i); |
995 | if (m_propertySheet->indexOf(name: propertyName) != i) |
996 | continue; |
997 | const QVariant value = m_propertySheet->property(index: i); |
998 | |
999 | const int type = toBrowserType(value, propertyName); |
1000 | |
1001 | QtVariantProperty *property = m_nameToProperty.value(akey: propertyName, adefaultValue: 0); |
1002 | bool newProperty = property == nullptr; |
1003 | if (newProperty) { |
1004 | property = m_propertyManager->addProperty(propertyType: type, name: propertyName); |
1005 | if (property) { |
1006 | newProperty = true; |
1007 | if (type == DesignerPropertyManager::enumTypeId()) { |
1008 | const PropertySheetEnumValue e = qvariant_cast<PropertySheetEnumValue>(v: value); |
1009 | m_updatingBrowser = true; |
1010 | property->setAttribute(attribute: m_strings.m_enumNamesAttribute, value: e.metaEnum.keys()); |
1011 | m_updatingBrowser = false; |
1012 | } else if (type == DesignerPropertyManager::designerFlagTypeId()) { |
1013 | const PropertySheetFlagValue f = qvariant_cast<PropertySheetFlagValue>(v: value); |
1014 | QList<QPair<QString, uint> > flags; |
1015 | for (const QString &name : f.metaFlags.keys()) { |
1016 | const uint val = f.metaFlags.keyToValue(key: name); |
1017 | flags.append(t: qMakePair(x: name, y: val)); |
1018 | } |
1019 | m_updatingBrowser = true; |
1020 | QVariant v; |
1021 | v.setValue(flags); |
1022 | property->setAttribute(attribute: m_strings.m_flagsAttribute, value: v); |
1023 | m_updatingBrowser = false; |
1024 | } |
1025 | } |
1026 | } |
1027 | |
1028 | if (property != nullptr) { |
1029 | const bool dynamicProperty = (dynamicSheet && dynamicSheet->isDynamicProperty(index: i)) |
1030 | || (sheet && sheet->isDefaultDynamicProperty(index: i)); |
1031 | QString descriptionToolTip; |
1032 | if (!dynamicProperty && !customData.isNull()) |
1033 | descriptionToolTip = customData.propertyToolTip(name: propertyName); |
1034 | if (descriptionToolTip.isEmpty()) { |
1035 | if (const char *typeS = typeName(type)) { |
1036 | descriptionToolTip = propertyName + QLatin1String(" (" ) |
1037 | + QLatin1String(typeS) + QLatin1Char(')'); |
1038 | } |
1039 | } |
1040 | if (!descriptionToolTip.isEmpty()) |
1041 | property->setDescriptionToolTip(descriptionToolTip); |
1042 | switch (type) { |
1043 | case QVariant::Palette: |
1044 | setupPaletteProperty(property); |
1045 | break; |
1046 | case QVariant::KeySequence: |
1047 | //addCommentProperty(property, propertyName); |
1048 | break; |
1049 | default: |
1050 | break; |
1051 | } |
1052 | if (type == QVariant::String || type == qMetaTypeId<PropertySheetStringValue>()) |
1053 | setupStringProperty(property, isMainContainer); |
1054 | property->setAttribute(attribute: m_strings.m_resettableAttribute, value: m_propertySheet->hasReset(index: i)); |
1055 | |
1056 | const QString groupName = m_propertySheet->propertyGroup(index: i); |
1057 | QtVariantProperty *groupProperty = nullptr; |
1058 | |
1059 | if (newProperty) { |
1060 | QMap<QString, QtVariantProperty*>::const_iterator itPrev(m_nameToProperty.insert(akey: propertyName, avalue: property)); |
1061 | m_propertyToGroup[property] = groupName; |
1062 | if (m_sorting) { |
1063 | QtProperty *previous = nullptr; |
1064 | if (itPrev != m_nameToProperty.constBegin()) |
1065 | previous = (--itPrev).value(); |
1066 | m_currentBrowser->insertProperty(property, afterProperty: previous); |
1067 | } |
1068 | } |
1069 | const QMap<QString, QtVariantProperty*>::const_iterator gnit = m_nameToGroup.constFind(akey: groupName); |
1070 | if (gnit != m_nameToGroup.constEnd()) { |
1071 | groupProperty = gnit.value(); |
1072 | } else { |
1073 | groupProperty = m_propertyManager->addProperty(propertyType: QtVariantPropertyManager::groupTypeId(), name: groupName); |
1074 | QtBrowserItem *item = nullptr; |
1075 | if (!m_sorting) |
1076 | item = m_currentBrowser->insertProperty(property: groupProperty, afterProperty: lastGroup); |
1077 | m_nameToGroup[groupName] = groupProperty; |
1078 | m_groups.append(t: groupProperty); |
1079 | if (dynamicProperty) |
1080 | m_dynamicGroup = groupProperty; |
1081 | if (m_currentBrowser == m_treeBrowser && item) { |
1082 | m_treeBrowser->setBackgroundColor(item, color: propertyColor(property: groupProperty)); |
1083 | groupProperty->setModified(true); |
1084 | } |
1085 | } |
1086 | /* Group changed or new group. Append to last subproperty of |
1087 | * that group. Note that there are cases in which a derived |
1088 | * property sheet appends fake properties for the class |
1089 | * which will appear after the layout group properties |
1090 | * (QWizardPage). To make them appear at the end of the |
1091 | * actual class group, goto last element. */ |
1092 | if (lastGroup != groupProperty) { |
1093 | lastGroup = groupProperty; |
1094 | lastProperty = nullptr; // Append at end |
1095 | const auto subProperties = lastGroup->subProperties(); |
1096 | if (!subProperties.isEmpty()) |
1097 | lastProperty = subProperties.constLast(); |
1098 | lastGroup = groupProperty; |
1099 | } |
1100 | if (!m_groups.contains(t: groupProperty)) |
1101 | m_groups.append(t: groupProperty); |
1102 | if (newProperty) |
1103 | groupProperty->insertSubProperty(property, afterProperty: lastProperty); |
1104 | |
1105 | lastProperty = property; |
1106 | |
1107 | updateBrowserValue(property, value); |
1108 | |
1109 | property->setModified(m_propertySheet->isChanged(index: i)); |
1110 | if (propertyName == QStringLiteral("geometry" ) && type == QVariant::Rect) { |
1111 | const auto &subProperties = property->subProperties(); |
1112 | for (QtProperty *subProperty : subProperties) { |
1113 | const QString subPropertyName = subProperty->propertyName(); |
1114 | if (subPropertyName == QStringLiteral("X" ) || subPropertyName == QStringLiteral("Y" )) |
1115 | subProperty->setEnabled(!isMainContainer); |
1116 | } |
1117 | } |
1118 | } else { |
1119 | qWarning(msg: "%s" , qPrintable(msgUnsupportedType(propertyName, type))); |
1120 | } |
1121 | } |
1122 | } |
1123 | QMap<QString, QtVariantProperty *> groups = m_nameToGroup; |
1124 | for (auto itGroup = groups.cbegin(), end = groups.cend(); itGroup != end; ++itGroup) { |
1125 | QtVariantProperty *groupProperty = itGroup.value(); |
1126 | if (groupProperty->subProperties().isEmpty()) { |
1127 | if (groupProperty == m_dynamicGroup) |
1128 | m_dynamicGroup = nullptr; |
1129 | delete groupProperty; |
1130 | m_nameToGroup.remove(akey: itGroup.key()); |
1131 | } |
1132 | } |
1133 | const bool addEnabled = dynamicSheet ? dynamicSheet->dynamicPropertiesAllowed() : false; |
1134 | m_addDynamicAction->setEnabled(addEnabled); |
1135 | m_removeDynamicAction->setEnabled(false); |
1136 | applyExpansionState(); |
1137 | applyFilter(); |
1138 | // In the first setObject() call following the addition of a dynamic property, focus and edit it. |
1139 | if (editNewDynamicProperty) { |
1140 | // Have QApplication process the events related to completely closing the modal 'add' dialog, |
1141 | // otherwise, we cannot focus the property editor in docked mode. |
1142 | QApplication::processEvents(flags: QEventLoop::ExcludeUserInputEvents); |
1143 | editProperty(name: m_recentlyAddedDynamicProperty); |
1144 | } |
1145 | m_recentlyAddedDynamicProperty.clear(); |
1146 | m_filterWidget->setEnabled(object); |
1147 | } |
1148 | |
1149 | void PropertyEditor::reloadResourceProperties() |
1150 | { |
1151 | m_updatingBrowser = true; |
1152 | m_propertyManager->reloadResourceProperties(); |
1153 | m_updatingBrowser = false; |
1154 | } |
1155 | |
1156 | 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(t: item->parent())) |
1163 | return item; |
1164 | item = item->parent(); |
1165 | } while (item); |
1166 | return nullptr; |
1167 | } |
1168 | |
1169 | QString PropertyEditor::currentPropertyName() const |
1170 | { |
1171 | if (QtBrowserItem *browserItem = m_currentBrowser->currentItem()) |
1172 | if (QtBrowserItem *topLevelItem = nonFakePropertyBrowserItem(item: browserItem)) { |
1173 | return topLevelItem->property()->propertyName(); |
1174 | } |
1175 | return QString(); |
1176 | } |
1177 | |
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(subProperty: property)) |
1188 | return; |
1189 | |
1190 | if (m_propertyManager->resetTextAlignmentProperty(property)) |
1191 | return; |
1192 | |
1193 | if (!m_propertyToGroup.contains(akey: property)) |
1194 | return; |
1195 | |
1196 | emit resetProperty(name: property->propertyName()); |
1197 | } |
1198 | |
1199 | void PropertyEditor::slotValueChanged(QtProperty *property, const QVariant &value, bool enableSubPropertyHandling) |
1200 | { |
1201 | if (m_updatingBrowser) |
1202 | return; |
1203 | |
1204 | if (!m_propertySheet) |
1205 | return; |
1206 | |
1207 | QtVariantProperty *varProp = m_propertyManager->variantProperty(property); |
1208 | |
1209 | if (!varProp) |
1210 | return; |
1211 | |
1212 | if (!m_propertyToGroup.contains(akey: property)) |
1213 | return; |
1214 | |
1215 | if (varProp->propertyType() == QtVariantPropertyManager::enumTypeId()) { |
1216 | PropertySheetEnumValue e = qvariant_cast<PropertySheetEnumValue>(v: m_propertySheet->property(index: m_propertySheet->indexOf(name: property->propertyName()))); |
1217 | const int val = value.toInt(); |
1218 | const QString valName = varProp->attributeValue(attribute: m_strings.m_enumNamesAttribute).toStringList().at(i: val); |
1219 | bool ok = false; |
1220 | e.value = e.metaEnum.parseEnum(s: valName, ok: &ok); |
1221 | Q_ASSERT(ok); |
1222 | QVariant v; |
1223 | v.setValue(e); |
1224 | emitPropertyValueChanged(name: property->propertyName(), value: v, enableSubPropertyHandling: true); |
1225 | return; |
1226 | } |
1227 | |
1228 | emitPropertyValueChanged(name: property->propertyName(), value, enableSubPropertyHandling); |
1229 | } |
1230 | |
1231 | bool PropertyEditor::isDynamicProperty(const QtBrowserItem* item) const |
1232 | { |
1233 | if (!item) |
1234 | return false; |
1235 | |
1236 | const QDesignerDynamicPropertySheetExtension *dynamicSheet = |
1237 | qt_extension<QDesignerDynamicPropertySheetExtension*>(manager: m_core->extensionManager(), object: m_object); |
1238 | |
1239 | if (!dynamicSheet) |
1240 | return false; |
1241 | |
1242 | return m_propertyToGroup.contains(akey: item->property()) |
1243 | && dynamicSheet->isDynamicProperty(index: m_propertySheet->indexOf(name: item->property()->propertyName())); |
1244 | } |
1245 | |
1246 | void PropertyEditor::editProperty(const QString &name) |
1247 | { |
1248 | // find the browser item belonging to the property, make it current and edit it |
1249 | QtBrowserItem *browserItem = nullptr; |
1250 | if (QtVariantProperty *property = m_nameToProperty.value(akey: name, adefaultValue: 0)) { |
1251 | const auto items = m_currentBrowser->items(property); |
1252 | if (items.size() == 1) |
1253 | browserItem = items.constFirst(); |
1254 | } |
1255 | if (browserItem == nullptr) |
1256 | return; |
1257 | m_currentBrowser->setFocus(Qt::OtherFocusReason); |
1258 | if (m_currentBrowser == m_treeBrowser) { // edit is currently only supported in tree view |
1259 | m_treeBrowser->editItem(item: browserItem); |
1260 | } else { |
1261 | m_currentBrowser->setCurrentItem(browserItem); |
1262 | } |
1263 | } |
1264 | |
1265 | void PropertyEditor::slotCurrentItemChanged(QtBrowserItem *item) |
1266 | { |
1267 | m_removeDynamicAction->setEnabled(isDynamicProperty(item)); |
1268 | |
1269 | } |
1270 | |
1271 | void PropertyEditor::slotRemoveDynamicProperty() |
1272 | { |
1273 | if (QtBrowserItem* item = m_currentBrowser->currentItem()) |
1274 | if (isDynamicProperty(item)) |
1275 | emit removeDynamicProperty(name: item->property()->propertyName()); |
1276 | } |
1277 | |
1278 | void PropertyEditor::setFilter(const QString &pattern) |
1279 | { |
1280 | m_filterPattern = pattern; |
1281 | applyFilter(); |
1282 | } |
1283 | } |
1284 | |
1285 | QT_END_NAMESPACE |
1286 | |