diff options
Diffstat (limited to 'src/designer/src/components/propertyeditor/propertyeditor.cpp')
-rw-r--r-- | src/designer/src/components/propertyeditor/propertyeditor.cpp | 1294 |
1 files changed, 1294 insertions, 0 deletions
diff --git a/src/designer/src/components/propertyeditor/propertyeditor.cpp b/src/designer/src/components/propertyeditor/propertyeditor.cpp new file mode 100644 index 000000000..9f298d0a4 --- /dev/null +++ b/src/designer/src/components/propertyeditor/propertyeditor.cpp @@ -0,0 +1,1294 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "propertyeditor.h" + +#include "qttreepropertybrowser.h" +#include "qtbuttonpropertybrowser.h" +#include "qtvariantproperty.h" +#include "designerpropertymanager.h" +#include "qdesigner_propertysheet_p.h" +#include "formwindowbase_p.h" +#include "filterwidget_p.h" // For FilterWidget + +#include "newdynamicpropertydialog.h" +#include "dynamicpropertysheet.h" +#include "shared_enums_p.h" + +// sdk +#include <QtDesigner/QDesignerFormEditorInterface> +#include <QtDesigner/QDesignerFormWindowManagerInterface> +#include <QtDesigner/QExtensionManager> +#include <QtDesigner/QDesignerPropertySheetExtension> +#include <QtDesigner/QDesignerWidgetDataBaseInterface> +#include <QtDesigner/private/abstractsettings_p.h> +// shared +#include <qdesigner_utils_p.h> +#include <qdesigner_propertycommand_p.h> +#include <metadatabase_p.h> +#include <iconloader_p.h> +#ifdef Q_OS_WIN +# include <widgetfactory_p.h> +#endif +#include <QtGui/QAction> +#include <QtGui/QLineEdit> +#include <QtGui/QMenu> +#include <QtGui/QApplication> +#include <QtGui/QVBoxLayout> +#include <QtGui/QScrollArea> +#include <QtGui/QStackedWidget> +#include <QtGui/QToolBar> +#include <QtGui/QToolButton> +#include <QtGui/QActionGroup> +#include <QtGui/QLabel> +#include <QtGui/QPainter> + +#include <QtCore/QDebug> +#include <QtCore/QTextStream> + +static const char *SettingsGroupC = "PropertyEditor"; +#if QT_VERSION >= 0x040500 +static const char *ViewKeyC = "View"; +#endif +static const char *ColorKeyC = "Colored"; +static const char *SortedKeyC = "Sorted"; +static const char *ExpansionKeyC = "ExpandedItems"; +static const char *SplitterPositionKeyC = "SplitterPosition"; + +enum SettingsView { TreeView, ButtonView }; + +QT_BEGIN_NAMESPACE + +// --------------------------------------------------------------------------------- + +namespace qdesigner_internal { + +// ----------- ElidingLabel +// QLabel does not support text eliding so we need a helper class + +class ElidingLabel : public QWidget +{ +public: + ElidingLabel(const QString &text = QString(), QWidget *parent = 0) + : QWidget(parent), + m_text(text), + m_mode(Qt::ElideRight) { + setContentsMargins(3, 2, 3, 2); + } + QSize sizeHint() const; + void paintEvent(QPaintEvent *e); + void setText(const QString &text) { + m_text = text; + updateGeometry(); + } + void setElidemode(Qt::TextElideMode mode) { + m_mode = mode; + updateGeometry(); + } +private: + QString m_text; + Qt::TextElideMode m_mode; +}; + +QSize ElidingLabel::sizeHint() const +{ + QSize size = fontMetrics().boundingRect(m_text).size(); + size += QSize(contentsMargins().left() + contentsMargins().right(), + contentsMargins().top() + contentsMargins().bottom()); + return size; +} + +void ElidingLabel::paintEvent(QPaintEvent *) { + QPainter painter(this); + painter.setPen(QColor(0, 0, 0, 60)); + painter.setBrush(QColor(255, 255, 255, 40)); + painter.drawRect(rect().adjusted(0, 0, -1, -1)); + painter.setPen(palette().windowText().color()); + painter.drawText(contentsRect(), Qt::AlignLeft, + fontMetrics().elidedText(m_text, Qt::ElideRight, width(), 0)); +} + + +// ----------- PropertyEditor::Strings + +PropertyEditor::Strings::Strings() : + m_fontProperty(QLatin1String("font")), + m_qLayoutWidget(QLatin1String("QLayoutWidget")), + m_designerPrefix(QLatin1String("QDesigner")), + m_layout(QLatin1String("Layout")), + m_validationModeAttribute(QLatin1String("validationMode")), + m_fontAttribute(QLatin1String("font")), + m_superPaletteAttribute(QLatin1String("superPalette")), + m_enumNamesAttribute(QLatin1String("enumNames")), + m_resettableAttribute(QLatin1String("resettable")), + m_flagsAttribute(QLatin1String("flags")) +{ + m_alignmentProperties.insert(QLatin1String("alignment")); + m_alignmentProperties.insert(QLatin1String("layoutLabelAlignment")); // QFormLayout + m_alignmentProperties.insert(QLatin1String("layoutFormAlignment")); +} + +// ----------- PropertyEditor + +QDesignerMetaDataBaseItemInterface* PropertyEditor::metaDataBaseItem() const +{ + QObject *o = object(); + if (!o) + return 0; + QDesignerMetaDataBaseInterface *db = core()->metaDataBase(); + if (!db) + return 0; + return db->item(o); +} + +void PropertyEditor::setupStringProperty(QtVariantProperty *property, bool isMainContainer) +{ + const StringPropertyParameters params = textPropertyValidationMode(core(), m_object, property->propertyName(), isMainContainer); + // Does a meta DB entry exist - add comment + const bool hasComment = params.second; + property->setAttribute(m_strings.m_validationModeAttribute, params.first); + // assuming comment cannot appear or disappear for the same property in different object instance + if (!hasComment) + qDeleteAll(property->subProperties()); +} + +void PropertyEditor::setupPaletteProperty(QtVariantProperty *property) +{ + QPalette value = qvariant_cast<QPalette>(property->value()); + QPalette superPalette = QPalette(); + QWidget *currentWidget = qobject_cast<QWidget *>(m_object); + if (currentWidget) { + if (currentWidget->isWindow()) + superPalette = QApplication::palette(currentWidget); + else { + if (currentWidget->parentWidget()) + superPalette = currentWidget->parentWidget()->palette(); + } + } + m_updatingBrowser = true; + property->setAttribute(m_strings.m_superPaletteAttribute, superPalette); + m_updatingBrowser = false; +} + +static inline QToolButton *createDropDownButton(QAction *defaultAction, QWidget *parent = 0) +{ + QToolButton *rc = new QToolButton(parent); + rc->setDefaultAction(defaultAction); + rc->setPopupMode(QToolButton::InstantPopup); + return rc; +} + +PropertyEditor::PropertyEditor(QDesignerFormEditorInterface *core, QWidget *parent, Qt::WindowFlags flags) : + QDesignerPropertyEditor(parent, flags), + m_core(core), + m_propertySheet(0), + m_currentBrowser(0), + m_treeBrowser(0), + m_propertyManager(new DesignerPropertyManager(m_core, this)), + m_dynamicGroup(0), + m_updatingBrowser(false), + m_stackedWidget(new QStackedWidget), + m_filterWidget(new FilterWidget(0, FilterWidget::LayoutAlignNone)), + m_buttonIndex(-1), + m_treeIndex(-1), + m_addDynamicAction(new QAction(createIconSet(QLatin1String("plus.png")), tr("Add Dynamic Property..."), this)), + m_removeDynamicAction(new QAction(createIconSet(QLatin1String("minus.png")), tr("Remove Dynamic Property"), this)), + m_sortingAction(new QAction(createIconSet(QLatin1String("sort.png")), tr("Sorting"), this)), + m_coloringAction(new QAction(createIconSet(QLatin1String("color.png")), tr("Color Groups"), this)), + m_treeAction(new QAction(tr("Tree View"), this)), + m_buttonAction(new QAction(tr("Drop Down Button View"), this)), + m_classLabel(new ElidingLabel), + m_sorting(false), + m_coloring(false), + m_brightness(false) +{ + QVector<QColor> colors; + colors.reserve(6); + colors.push_back(QColor(255, 230, 191)); + colors.push_back(QColor(255, 255, 191)); + colors.push_back(QColor(191, 255, 191)); + colors.push_back(QColor(199, 255, 255)); + colors.push_back(QColor(234, 191, 255)); + colors.push_back(QColor(255, 191, 239)); + m_colors.reserve(colors.count()); + const int darknessFactor = 250; + for (int i = 0; i < colors.count(); i++) { + QColor c = colors.at(i); + m_colors.push_back(qMakePair(c, c.darker(darknessFactor))); + } + QColor dynamicColor(191, 207, 255); + QColor layoutColor(255, 191, 191); + m_dynamicColor = qMakePair(dynamicColor, dynamicColor.darker(darknessFactor)); + m_layoutColor = qMakePair(layoutColor, layoutColor.darker(darknessFactor)); + + updateForegroundBrightness(); + + QActionGroup *actionGroup = new QActionGroup(this); + + m_treeAction->setCheckable(true); + m_treeAction->setIcon(createIconSet(QLatin1String("widgets/listview.png"))); + m_buttonAction->setCheckable(true); + m_buttonAction->setIcon(createIconSet(QLatin1String("dropdownbutton.png"))); + + actionGroup->addAction(m_treeAction); + actionGroup->addAction(m_buttonAction); + connect(actionGroup, SIGNAL(triggered(QAction*)), this, SLOT(slotViewTriggered(QAction*))); + + // Add actions + QActionGroup *addDynamicActionGroup = new QActionGroup(this); + connect(addDynamicActionGroup, SIGNAL(triggered(QAction*)), this, SLOT(slotAddDynamicProperty(QAction*))); + + QMenu *addDynamicActionMenu = new QMenu(this); + m_addDynamicAction->setMenu(addDynamicActionMenu); + m_addDynamicAction->setEnabled(false); + QAction *addDynamicAction = addDynamicActionGroup->addAction(tr("String...")); + addDynamicAction->setData(static_cast<int>(QVariant::String)); + addDynamicActionMenu->addAction(addDynamicAction); + addDynamicAction = addDynamicActionGroup->addAction(tr("Bool...")); + addDynamicAction->setData(static_cast<int>(QVariant::Bool)); + addDynamicActionMenu->addAction(addDynamicAction); + addDynamicActionMenu->addSeparator(); + addDynamicAction = addDynamicActionGroup->addAction(tr("Other...")); + addDynamicAction->setData(static_cast<int>(QVariant::Invalid)); + addDynamicActionMenu->addAction(addDynamicAction); + // remove + m_removeDynamicAction->setEnabled(false); + connect(m_removeDynamicAction, SIGNAL(triggered()), this, SLOT(slotRemoveDynamicProperty())); + // Configure + QAction *configureAction = new QAction(tr("Configure Property Editor"), this); + configureAction->setIcon(createIconSet(QLatin1String("configure.png"))); + QMenu *configureMenu = new QMenu(this); + configureAction->setMenu(configureMenu); + + m_sortingAction->setCheckable(true); + connect(m_sortingAction, SIGNAL(toggled(bool)), this, SLOT(slotSorting(bool))); + + m_coloringAction->setCheckable(true); + connect(m_coloringAction, SIGNAL(toggled(bool)), this, SLOT(slotColoring(bool))); + + configureMenu->addAction(m_sortingAction); + configureMenu->addAction(m_coloringAction); +#if QT_VERSION >= 0x04FF00 + configureMenu->addSeparator(); + configureMenu->addAction(m_treeAction); + configureMenu->addAction(m_buttonAction); +#endif + // Assemble toolbar + QToolBar *toolBar = new QToolBar; + toolBar->addWidget(m_filterWidget); + toolBar->addWidget(createDropDownButton(m_addDynamicAction)); + toolBar->addAction(m_removeDynamicAction); + toolBar->addWidget(createDropDownButton(configureAction)); + // Views + QScrollArea *buttonScroll = new QScrollArea(m_stackedWidget); + m_buttonBrowser = new QtButtonPropertyBrowser(buttonScroll); + buttonScroll->setWidgetResizable(true); + buttonScroll->setWidget(m_buttonBrowser); + m_buttonIndex = m_stackedWidget->addWidget(buttonScroll); + connect(m_buttonBrowser, SIGNAL(currentItemChanged(QtBrowserItem*)), this, SLOT(slotCurrentItemChanged(QtBrowserItem*))); + + m_treeBrowser = new QtTreePropertyBrowser(m_stackedWidget); + m_treeBrowser->setRootIsDecorated(false); + m_treeBrowser->setPropertiesWithoutValueMarked(true); + m_treeBrowser->setResizeMode(QtTreePropertyBrowser::Interactive); + m_treeIndex = m_stackedWidget->addWidget(m_treeBrowser); + connect(m_treeBrowser, SIGNAL(currentItemChanged(QtBrowserItem*)), this, SLOT(slotCurrentItemChanged(QtBrowserItem*))); + connect(m_filterWidget, SIGNAL(filterChanged(QString)), this, SLOT(setFilter(QString))); + + QVBoxLayout *layout = new QVBoxLayout(this); + layout->addWidget(toolBar); + layout->addWidget(m_classLabel); + layout->addSpacerItem(new QSpacerItem(0,1)); + layout->addWidget(m_stackedWidget); + layout->setMargin(0); + layout->setSpacing(0); + + m_treeFactory = new DesignerEditorFactory(m_core, this); + m_treeFactory->setSpacing(0); + m_groupFactory = new DesignerEditorFactory(m_core, this); + QtVariantPropertyManager *variantManager = m_propertyManager; + m_buttonBrowser->setFactoryForManager(variantManager, m_groupFactory); + m_treeBrowser->setFactoryForManager(variantManager, m_treeFactory); + + m_stackedWidget->setCurrentIndex(m_treeIndex); + m_currentBrowser = m_treeBrowser; + m_treeAction->setChecked(true); + + connect(m_groupFactory, SIGNAL(resetProperty(QtProperty*)), this, SLOT(slotResetProperty(QtProperty*))); + connect(m_treeFactory, SIGNAL(resetProperty(QtProperty*)), this, SLOT(slotResetProperty(QtProperty*))); + connect(variantManager, SIGNAL(valueChanged(QtProperty*,QVariant,bool)), this, SLOT(slotValueChanged(QtProperty*,QVariant,bool))); + + // retrieve initial settings + QDesignerSettingsInterface *settings = m_core->settingsManager(); + settings->beginGroup(QLatin1String(SettingsGroupC)); +#if QT_VERSION >= 0x040500 + const SettingsView view = settings->value(QLatin1String(ViewKeyC), TreeView).toInt() == TreeView ? TreeView : ButtonView; +#endif + // Coloring not available unless treeview and not sorted + m_sorting = settings->value(QLatin1String(SortedKeyC), false).toBool(); + m_coloring = settings->value(QLatin1String(ColorKeyC), true).toBool(); + const QVariantMap expansionState = settings->value(QLatin1String(ExpansionKeyC), QVariantMap()).toMap(); + const int splitterPosition = settings->value(QLatin1String(SplitterPositionKeyC), 150).toInt(); + settings->endGroup(); + // Apply settings + m_sortingAction->setChecked(m_sorting); + m_coloringAction->setChecked(m_coloring); + m_treeBrowser->setSplitterPosition(splitterPosition); +#if QT_VERSION >= 0x040500 + switch (view) { + case TreeView: + m_currentBrowser = m_treeBrowser; + m_stackedWidget->setCurrentIndex(m_treeIndex); + m_treeAction->setChecked(true); + break; + case ButtonView: + m_currentBrowser = m_buttonBrowser; + m_stackedWidget->setCurrentIndex(m_buttonIndex); + m_buttonAction->setChecked(true); + break; + } +#endif + // Restore expansionState from QVariant map + if (!expansionState.empty()) { + const QVariantMap::const_iterator cend = expansionState.constEnd(); + for (QVariantMap::const_iterator it = expansionState.constBegin(); it != cend; ++it) + m_expansionState.insert(it.key(), it.value().toBool()); + } + updateActionsState(); +} + +PropertyEditor::~PropertyEditor() +{ + storeExpansionState(); + saveSettings(); +} + +void PropertyEditor::saveSettings() const +{ + QDesignerSettingsInterface *settings = m_core->settingsManager(); + settings->beginGroup(QLatin1String(SettingsGroupC)); +#if QT_VERSION >= 0x040500 + settings->setValue(QLatin1String(ViewKeyC), QVariant(m_treeAction->isChecked() ? TreeView : ButtonView)); +#endif + settings->setValue(QLatin1String(ColorKeyC), QVariant(m_coloring)); + settings->setValue(QLatin1String(SortedKeyC), QVariant(m_sorting)); + // Save last expansionState as QVariant map + QVariantMap expansionState; + if (!m_expansionState.empty()) { + const QMap<QString, bool>::const_iterator cend = m_expansionState.constEnd(); + for (QMap<QString, bool>::const_iterator it = m_expansionState.constBegin(); it != cend; ++it) + expansionState.insert(it.key(), QVariant(it.value())); + } + settings->setValue(QLatin1String(ExpansionKeyC), expansionState); + settings->setValue(QLatin1String(SplitterPositionKeyC), m_treeBrowser->splitterPosition()); + settings->endGroup(); +} + +void PropertyEditor::setExpanded(QtBrowserItem *item, bool expanded) +{ + if (m_buttonBrowser == m_currentBrowser) + m_buttonBrowser->setExpanded(item, expanded); + else if (m_treeBrowser == m_currentBrowser) + m_treeBrowser->setExpanded(item, expanded); +} + +bool PropertyEditor::isExpanded(QtBrowserItem *item) const +{ + if (m_buttonBrowser == m_currentBrowser) + return m_buttonBrowser->isExpanded(item); + else if (m_treeBrowser == m_currentBrowser) + return m_treeBrowser->isExpanded(item); + return false; +} + +void PropertyEditor::setItemVisible(QtBrowserItem *item, bool visible) +{ + if (m_currentBrowser == m_treeBrowser) { + m_treeBrowser->setItemVisible(item, visible); + } else { + qWarning("** WARNING %s is not implemented for this browser.", Q_FUNC_INFO); + } +} + +bool PropertyEditor::isItemVisible(QtBrowserItem *item) const +{ + return m_currentBrowser == m_treeBrowser ? m_treeBrowser->isItemVisible(item) : true; +} + +/* Default handling of items not found in the map: + * - Top-level items (classes) are assumed to be expanded + * - Anything below (properties) is assumed to be collapsed + * That is, the map is required, the state cannot be stored in a set */ + +void PropertyEditor::storePropertiesExpansionState(const QList<QtBrowserItem *> &items) +{ + const QChar bar = QLatin1Char('|'); + QListIterator<QtBrowserItem *> itProperty(items); + while (itProperty.hasNext()) { + QtBrowserItem *propertyItem = itProperty.next(); + if (!propertyItem->children().empty()) { + QtProperty *property = propertyItem->property(); + const QString propertyName = property->propertyName(); + const QMap<QtProperty *, QString>::const_iterator itGroup = m_propertyToGroup.constFind(property); + if (itGroup != m_propertyToGroup.constEnd()) { + QString key = itGroup.value(); + key += bar; + key += propertyName; + m_expansionState[key] = isExpanded(propertyItem); + } + } + } +} + +void PropertyEditor::storeExpansionState() +{ + const QList<QtBrowserItem *> items = m_currentBrowser->topLevelItems(); + if (m_sorting) { + storePropertiesExpansionState(items); + } else { + QListIterator<QtBrowserItem *> itGroup(items); + while (itGroup.hasNext()) { + QtBrowserItem *item = itGroup.next(); + const QString groupName = item->property()->propertyName(); + QList<QtBrowserItem *> propertyItems = item->children(); + if (!propertyItems.empty()) + m_expansionState[groupName] = isExpanded(item); + + // properties stuff here + storePropertiesExpansionState(propertyItems); + } + } +} + +void PropertyEditor::collapseAll() +{ + QList<QtBrowserItem *> items = m_currentBrowser->topLevelItems(); + QListIterator<QtBrowserItem *> itGroup(items); + while (itGroup.hasNext()) + setExpanded(itGroup.next(), false); +} + +void PropertyEditor::applyPropertiesExpansionState(const QList<QtBrowserItem *> &items) +{ + const QChar bar = QLatin1Char('|'); + QListIterator<QtBrowserItem *> itProperty(items); + while (itProperty.hasNext()) { + const QMap<QString, bool>::const_iterator excend = m_expansionState.constEnd(); + QtBrowserItem *propertyItem = itProperty.next(); + QtProperty *property = propertyItem->property(); + const QString propertyName = property->propertyName(); + const QMap<QtProperty *, QString>::const_iterator itGroup = m_propertyToGroup.constFind(property); + if (itGroup != m_propertyToGroup.constEnd()) { + QString key = itGroup.value(); + key += bar; + key += propertyName; + const QMap<QString, bool>::const_iterator pit = m_expansionState.constFind(key); + if (pit != excend) + setExpanded(propertyItem, pit.value()); + else + setExpanded(propertyItem, false); + } + } +} + +void PropertyEditor::applyExpansionState() +{ + const QList<QtBrowserItem *> items = m_currentBrowser->topLevelItems(); + if (m_sorting) { + applyPropertiesExpansionState(items); + } else { + QListIterator<QtBrowserItem *> itTopLevel(items); + const QMap<QString, bool>::const_iterator excend = m_expansionState.constEnd(); + while (itTopLevel.hasNext()) { + QtBrowserItem *item = itTopLevel.next(); + const QString groupName = item->property()->propertyName(); + const QMap<QString, bool>::const_iterator git = m_expansionState.constFind(groupName); + if (git != excend) + setExpanded(item, git.value()); + else + setExpanded(item, true); + // properties stuff here + applyPropertiesExpansionState(item->children()); + } + } +} + +int PropertyEditor::applyPropertiesFilter(const QList<QtBrowserItem *> &items) +{ + int showCount = 0; + const bool matchAll = m_filterPattern.isEmpty(); + QListIterator<QtBrowserItem *> itProperty(items); + while (itProperty.hasNext()) { + QtBrowserItem *propertyItem = itProperty.next(); + QtProperty *property = propertyItem->property(); + const QString propertyName = property->propertyName(); + const bool showProperty = matchAll || propertyName.contains(m_filterPattern, Qt::CaseInsensitive); + setItemVisible(propertyItem, showProperty); + if (showProperty) + showCount++; + } + return showCount; +} + +void PropertyEditor::applyFilter() +{ + const QList<QtBrowserItem *> items = m_currentBrowser->topLevelItems(); + if (m_sorting) { + applyPropertiesFilter(items); + } else { + QListIterator<QtBrowserItem *> itTopLevel(items); + while (itTopLevel.hasNext()) { + QtBrowserItem *item = itTopLevel.next(); + setItemVisible(item, applyPropertiesFilter(item->children())); + } + } +} + +void PropertyEditor::clearView() +{ + m_currentBrowser->clear(); +} + +bool PropertyEditor::event(QEvent *event) +{ + if (event->type() == QEvent::PaletteChange) + updateForegroundBrightness(); + + return QDesignerPropertyEditor::event(event); +} + +void PropertyEditor::updateForegroundBrightness() +{ + QColor c = palette().color(QPalette::Text); + bool newBrightness = qRound(0.3 * c.redF() + 0.59 * c.greenF() + 0.11 * c.blueF()); + + if (m_brightness == newBrightness) + return; + + m_brightness = newBrightness; + + updateColors(); +} + +QColor PropertyEditor::propertyColor(QtProperty *property) const +{ + if (!m_coloring) + return QColor(); + + QtProperty *groupProperty = property; + + QMap<QtProperty *, QString>::ConstIterator itProp = m_propertyToGroup.constFind(property); + if (itProp != m_propertyToGroup.constEnd()) + groupProperty = m_nameToGroup.value(itProp.value()); + + const int groupIdx = m_groups.indexOf(groupProperty); + QPair<QColor, QColor> pair; + if (groupIdx != -1) { + if (groupProperty == m_dynamicGroup) + pair = m_dynamicColor; + else if (isLayoutGroup(groupProperty)) + pair = m_layoutColor; + else + pair = m_colors[groupIdx % m_colors.count()]; + } + if (!m_brightness) + return pair.first; + return pair.second; +} + +void PropertyEditor::fillView() +{ + if (m_sorting) { + QMapIterator<QString, QtVariantProperty *> itProperty(m_nameToProperty); + while (itProperty.hasNext()) { + QtVariantProperty *property = itProperty.next().value(); + m_currentBrowser->addProperty(property); + } + } else { + QListIterator<QtProperty *> itGroup(m_groups); + while (itGroup.hasNext()) { + QtProperty *group = itGroup.next(); + QtBrowserItem *item = m_currentBrowser->addProperty(group); + if (m_currentBrowser == m_treeBrowser) + m_treeBrowser->setBackgroundColor(item, propertyColor(group)); + group->setModified(m_currentBrowser == m_treeBrowser); + } + } +} + +bool PropertyEditor::isLayoutGroup(QtProperty *group) const +{ + return group->propertyName() == m_strings.m_layout; +} + +void PropertyEditor::updateActionsState() +{ + m_coloringAction->setEnabled(m_treeAction->isChecked() && !m_sortingAction->isChecked()); +} + +void PropertyEditor::slotViewTriggered(QAction *action) +{ + storeExpansionState(); + collapseAll(); + { + UpdateBlocker ub(this); + clearView(); + int idx = 0; + if (action == m_treeAction) { + m_currentBrowser = m_treeBrowser; + idx = m_treeIndex; + } else if (action == m_buttonAction) { + m_currentBrowser = m_buttonBrowser; + idx = m_buttonIndex; + } + fillView(); + m_stackedWidget->setCurrentIndex(idx); + applyExpansionState(); + applyFilter(); + } + updateActionsState(); +} + +void PropertyEditor::slotSorting(bool sort) +{ + if (sort == m_sorting) + return; + + storeExpansionState(); + m_sorting = sort; + collapseAll(); + { + UpdateBlocker ub(this); + clearView(); + m_treeBrowser->setRootIsDecorated(sort); + fillView(); + applyExpansionState(); + applyFilter(); + } + updateActionsState(); +} + +void PropertyEditor::updateColors() +{ + if (m_treeBrowser && m_currentBrowser == m_treeBrowser) { + QList<QtBrowserItem *> items = m_treeBrowser->topLevelItems(); + QListIterator<QtBrowserItem *> itItem(items); + while (itItem.hasNext()) { + QtBrowserItem *item = itItem.next(); + m_treeBrowser->setBackgroundColor(item, propertyColor(item->property())); + } + } +} + +void PropertyEditor::slotColoring(bool coloring) +{ + if (coloring == m_coloring) + return; + + m_coloring = coloring; + + updateColors(); +} + +void PropertyEditor::slotAddDynamicProperty(QAction *action) +{ + if (!m_propertySheet) + return; + + const QDesignerDynamicPropertySheetExtension *dynamicSheet = + qt_extension<QDesignerDynamicPropertySheetExtension*>(m_core->extensionManager(), m_object); + + if (!dynamicSheet) + return; + + QString newName; + QVariant newValue; + { // Make sure the dialog is closed before the signal is emitted. + const QVariant::Type type = static_cast<QVariant::Type>(action->data().toInt()); + NewDynamicPropertyDialog dlg(core()->dialogGui(), m_currentBrowser); + if (type != QVariant::Invalid) + dlg.setPropertyType(type); + + QStringList reservedNames; + const int propertyCount = m_propertySheet->count(); + for (int i = 0; i < propertyCount; i++) { + if (!dynamicSheet->isDynamicProperty(i) || m_propertySheet->isVisible(i)) + reservedNames.append(m_propertySheet->propertyName(i)); + } + dlg.setReservedNames(reservedNames); + if (dlg.exec() == QDialog::Rejected) + return; + newName = dlg.propertyName(); + newValue = dlg.propertyValue(); + } + m_recentlyAddedDynamicProperty = newName; + emit addDynamicProperty(newName, newValue); +} + +QDesignerFormEditorInterface *PropertyEditor::core() const +{ + return m_core; +} + +bool PropertyEditor::isReadOnly() const +{ + return false; +} + +void PropertyEditor::setReadOnly(bool /*readOnly*/) +{ + qDebug() << "PropertyEditor::setReadOnly() request"; +} + +void PropertyEditor::setPropertyValue(const QString &name, const QVariant &value, bool changed) +{ + const QMap<QString, QtVariantProperty*>::const_iterator it = m_nameToProperty.constFind(name); + if (it == m_nameToProperty.constEnd()) + return; + QtVariantProperty *property = it.value(); + updateBrowserValue(property, value); + property->setModified(changed); +} + +/* Quick update that assumes the actual count of properties has not changed + * N/A when for example executing a layout command and margin properties appear. */ +void PropertyEditor::updatePropertySheet() +{ + if (!m_propertySheet) + return; + + updateToolBarLabel(); + + const int propertyCount = m_propertySheet->count(); + const QMap<QString, QtVariantProperty*>::const_iterator npcend = m_nameToProperty.constEnd(); + for (int i = 0; i < propertyCount; ++i) { + const QString propertyName = m_propertySheet->propertyName(i); + QMap<QString, QtVariantProperty*>::const_iterator it = m_nameToProperty.constFind(propertyName); + if (it != npcend) + updateBrowserValue(it.value(), m_propertySheet->property(i)); + } +} + +static inline QLayout *layoutOfQLayoutWidget(QObject *o) +{ + if (o->isWidgetType() && !qstrcmp(o->metaObject()->className(), "QLayoutWidget")) + return static_cast<QWidget*>(o)->layout(); + return 0; +} + +void PropertyEditor::updateToolBarLabel() +{ + QString objectName; + QString className; + if (m_object) { + if (QLayout *l = layoutOfQLayoutWidget(m_object)) + objectName = l->objectName(); + else + objectName = m_object->objectName(); + className = realClassName(m_object); + } + + m_classLabel->setVisible(!objectName.isEmpty() || !className.isEmpty()); + m_classLabel->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); + + QString classLabelText; + if (!objectName.isEmpty()) + classLabelText += objectName + QLatin1String(" : "); + classLabelText += className; + + m_classLabel->setText(classLabelText); + m_classLabel->setToolTip(tr("Object: %1\nClass: %2").arg(objectName).arg(className)); +} + +void PropertyEditor::updateBrowserValue(QtVariantProperty *property, const QVariant &value) +{ + QVariant v = value; + const int type = property->propertyType(); + if (type == QtVariantPropertyManager::enumTypeId()) { + const PropertySheetEnumValue e = qvariant_cast<PropertySheetEnumValue>(v); + v = e.metaEnum.keys().indexOf(e.metaEnum.valueToKey(e.value)); + } else if (type == DesignerPropertyManager::designerFlagTypeId()) { + const PropertySheetFlagValue f = qvariant_cast<PropertySheetFlagValue>(v); + v = QVariant(f.value); + } else if (type == DesignerPropertyManager::designerAlignmentTypeId()) { + const PropertySheetFlagValue f = qvariant_cast<PropertySheetFlagValue>(v); + v = QVariant(f.value); + } + QDesignerPropertySheet *sheet = qobject_cast<QDesignerPropertySheet*>(m_core->extensionManager()->extension(m_object, Q_TYPEID(QDesignerPropertySheetExtension))); + int index = -1; + if (sheet) + index = sheet->indexOf(property->propertyName()); + if (sheet && m_propertyToGroup.contains(property)) { // don't do it for comments since property sheet doesn't keep them + property->setEnabled(sheet->isEnabled(index)); + } + + // Rich text string property with comment: Store/Update the font the rich text editor dialog starts out with + if (type == QVariant::String && !property->subProperties().empty()) { + const int fontIndex = m_propertySheet->indexOf(m_strings.m_fontProperty); + if (fontIndex != -1) + property->setAttribute(m_strings.m_fontAttribute, m_propertySheet->property(fontIndex)); + } + + m_updatingBrowser = true; + property->setValue(v); + if (sheet && sheet->isResourceProperty(index)) + property->setAttribute(QLatin1String("defaultResource"), sheet->defaultResourceProperty(index)); + m_updatingBrowser = false; +} + +int PropertyEditor::toBrowserType(const QVariant &value, const QString &propertyName) const +{ + if (value.canConvert<PropertySheetFlagValue>()) { + if (m_strings.m_alignmentProperties.contains(propertyName)) + return DesignerPropertyManager::designerAlignmentTypeId(); + return DesignerPropertyManager::designerFlagTypeId(); + } + if (value.canConvert<PropertySheetEnumValue>()) + return DesignerPropertyManager::enumTypeId(); + + return value.userType(); +} + +QString PropertyEditor::realClassName(QObject *object) const +{ + if (!object) + return QString(); + + QString className = QLatin1String(object->metaObject()->className()); + const QDesignerWidgetDataBaseInterface *db = core()->widgetDataBase(); + if (QDesignerWidgetDataBaseItemInterface *widgetItem = db->item(db->indexOfObject(object, true))) { + className = widgetItem->name(); + + if (object->isWidgetType() && className == m_strings.m_qLayoutWidget + && static_cast<QWidget*>(object)->layout()) { + className = QLatin1String(static_cast<QWidget*>(object)->layout()->metaObject()->className()); + } + } + + if (className.startsWith(m_strings.m_designerPrefix)) + className.remove(1, m_strings.m_designerPrefix.size() - 1); + + return className; +} + +static QString msgUnsupportedType(const QString &propertyName, unsigned type) +{ + QString rc; + QTextStream str(&rc); + str << "The property \"" << propertyName << "\" of type " << type; + if (type == QVariant::Invalid) { + str << " (invalid) "; + } else { + if (type < QVariant::UserType) { + if (const char *typeName = QVariant::typeToName(static_cast<QVariant::Type>(type))) + str << " (" << typeName << ") "; + } else { + str << " (user type) "; + } + } + str << " is not supported yet!"; + return rc; +} + +void PropertyEditor::setObject(QObject *object) +{ + QDesignerFormWindowInterface *oldFormWindow = QDesignerFormWindowInterface::findFormWindow(m_object); + // In the first setObject() call following the addition of a dynamic property, focus and edit it. + const bool editNewDynamicProperty = object != 0 && m_object == object && !m_recentlyAddedDynamicProperty.isEmpty(); + m_object = object; + m_propertyManager->setObject(object); + QDesignerFormWindowInterface *formWindow = QDesignerFormWindowInterface::findFormWindow(m_object); + FormWindowBase *fwb = qobject_cast<FormWindowBase *>(formWindow); + m_treeFactory->setFormWindowBase(fwb); + m_groupFactory->setFormWindowBase(fwb); + + storeExpansionState(); + + UpdateBlocker ub(this); + + updateToolBarLabel(); + + QMap<QString, QtVariantProperty *> toRemove = m_nameToProperty; + + const QDesignerDynamicPropertySheetExtension *dynamicSheet = + qt_extension<QDesignerDynamicPropertySheetExtension*>(m_core->extensionManager(), m_object); + const QDesignerPropertySheet *sheet = qobject_cast<QDesignerPropertySheet*>(m_core->extensionManager()->extension(m_object, Q_TYPEID(QDesignerPropertySheetExtension))); + + // Optimizization: Instead of rebuilding the complete list every time, compile a list of properties to remove, + // remove them, traverse the sheet, in case property exists just set a value, otherwise - create it. + QExtensionManager *m = m_core->extensionManager(); + + m_propertySheet = qobject_cast<QDesignerPropertySheetExtension*>(m->extension(object, Q_TYPEID(QDesignerPropertySheetExtension))); + if (m_propertySheet) { + const int propertyCount = m_propertySheet->count(); + for (int i = 0; i < propertyCount; ++i) { + if (!m_propertySheet->isVisible(i)) + continue; + + const QString propertyName = m_propertySheet->propertyName(i); + if (m_propertySheet->indexOf(propertyName) != i) + continue; + const QString groupName = m_propertySheet->propertyGroup(i); + const QMap<QString, QtVariantProperty *>::const_iterator rit = toRemove.constFind(propertyName); + if (rit != toRemove.constEnd()) { + QtVariantProperty *property = rit.value(); + if (m_propertyToGroup.value(property) == groupName && toBrowserType(m_propertySheet->property(i), propertyName) == property->propertyType()) + toRemove.remove(propertyName); + } + } + } + + QMapIterator<QString, QtVariantProperty *> itRemove(toRemove); + while (itRemove.hasNext()) { + itRemove.next(); + + QtVariantProperty *property = itRemove.value(); + m_nameToProperty.remove(itRemove.key()); + m_propertyToGroup.remove(property); + delete property; + } + + if (oldFormWindow != formWindow) + reloadResourceProperties(); + + bool isMainContainer = false; + if (QWidget *widget = qobject_cast<QWidget*>(object)) { + if (QDesignerFormWindowInterface *fw = QDesignerFormWindowInterface::findFormWindow(widget)) { + isMainContainer = (fw->mainContainer() == widget); + } + } + m_groups.clear(); + + if (m_propertySheet) { + QtProperty *lastProperty = 0; + QtProperty *lastGroup = 0; + const int propertyCount = m_propertySheet->count(); + for (int i = 0; i < propertyCount; ++i) { + if (!m_propertySheet->isVisible(i)) + continue; + + const QString propertyName = m_propertySheet->propertyName(i); + if (m_propertySheet->indexOf(propertyName) != i) + continue; + const QVariant value = m_propertySheet->property(i); + + const int type = toBrowserType(value, propertyName); + + QtVariantProperty *property = m_nameToProperty.value(propertyName, 0); + bool newProperty = property == 0; + if (newProperty) { + property = m_propertyManager->addProperty(type, propertyName); + if (property) { + newProperty = true; + if (type == DesignerPropertyManager::enumTypeId()) { + const PropertySheetEnumValue e = qvariant_cast<PropertySheetEnumValue>(value); + QStringList names; + QStringListIterator it(e.metaEnum.keys()); + while (it.hasNext()) + names.append(it.next()); + m_updatingBrowser = true; + property->setAttribute(m_strings.m_enumNamesAttribute, names); + m_updatingBrowser = false; + } else if (type == DesignerPropertyManager::designerFlagTypeId()) { + const PropertySheetFlagValue f = qvariant_cast<PropertySheetFlagValue>(value); + QList<QPair<QString, uint> > flags; + QStringListIterator it(f.metaFlags.keys()); + while (it.hasNext()) { + const QString name = it.next(); + const uint val = f.metaFlags.keyToValue(name); + flags.append(qMakePair(name, val)); + } + m_updatingBrowser = true; + QVariant v; + v.setValue(flags); + property->setAttribute(m_strings.m_flagsAttribute, v); + m_updatingBrowser = false; + } + } + } + + if (property != 0) { + const bool dynamicProperty = (dynamicSheet && dynamicSheet->isDynamicProperty(i)) + || (sheet && sheet->isDefaultDynamicProperty(i)); + switch (type) { + case QVariant::Palette: + setupPaletteProperty(property); + break; + case QVariant::KeySequence: + //addCommentProperty(property, propertyName); + break; + default: + break; + } + if (type == QVariant::String || type == qMetaTypeId<PropertySheetStringValue>()) + setupStringProperty(property, isMainContainer); + property->setAttribute(m_strings.m_resettableAttribute, m_propertySheet->hasReset(i)); + + const QString groupName = m_propertySheet->propertyGroup(i); + QtVariantProperty *groupProperty = 0; + + if (newProperty) { + QMap<QString, QtVariantProperty*>::const_iterator itPrev = m_nameToProperty.insert(propertyName, property); + m_propertyToGroup[property] = groupName; + if (m_sorting) { + QtProperty *previous = 0; + if (itPrev != m_nameToProperty.constBegin()) + previous = (--itPrev).value(); + m_currentBrowser->insertProperty(property, previous); + } + } + const QMap<QString, QtVariantProperty*>::const_iterator gnit = m_nameToGroup.constFind(groupName); + if (gnit != m_nameToGroup.constEnd()) { + groupProperty = gnit.value(); + } else { + groupProperty = m_propertyManager->addProperty(QtVariantPropertyManager::groupTypeId(), groupName); + QtBrowserItem *item = 0; + if (!m_sorting) + item = m_currentBrowser->insertProperty(groupProperty, lastGroup); + m_nameToGroup[groupName] = groupProperty; + m_groups.append(groupProperty); + if (dynamicProperty) + m_dynamicGroup = groupProperty; + if (m_currentBrowser == m_treeBrowser && item) { + m_treeBrowser->setBackgroundColor(item, propertyColor(groupProperty)); + groupProperty->setModified(true); + } + } + /* Group changed or new group. Append to last subproperty of + * that group. Note that there are cases in which a derived + * property sheet appends fake properties for the class + * which will appear after the layout group properties + * (QWizardPage). To make them appear at the end of the + * actual class group, goto last element. */ + if (lastGroup != groupProperty) { + lastGroup = groupProperty; + lastProperty = 0; // Append at end + const QList<QtProperty*> subProperties = lastGroup->subProperties(); + if (!subProperties.empty()) + lastProperty = subProperties.back(); + lastGroup = groupProperty; + } + if (!m_groups.contains(groupProperty)) + m_groups.append(groupProperty); + if (newProperty) + groupProperty->insertSubProperty(property, lastProperty); + + lastProperty = property; + + updateBrowserValue(property, value); + + property->setModified(m_propertySheet->isChanged(i)); + if (propertyName == QLatin1String("geometry") && type == QVariant::Rect) { + QList<QtProperty *> subProperties = property->subProperties(); + foreach (QtProperty *subProperty, subProperties) { + const QString subPropertyName = subProperty->propertyName(); + if (subPropertyName == QLatin1String("X") || subPropertyName == QLatin1String("Y")) + subProperty->setEnabled(!isMainContainer); + } + } + } else { + qWarning("%s", qPrintable(msgUnsupportedType(propertyName, type))); + } + } + } + QMap<QString, QtVariantProperty *> groups = m_nameToGroup; + QMapIterator<QString, QtVariantProperty *> itGroup(groups); + while (itGroup.hasNext()) { + QtVariantProperty *groupProperty = itGroup.next().value(); + if (groupProperty->subProperties().empty()) { + if (groupProperty == m_dynamicGroup) + m_dynamicGroup = 0; + delete groupProperty; + m_nameToGroup.remove(itGroup.key()); + } + } + const bool addEnabled = dynamicSheet ? dynamicSheet->dynamicPropertiesAllowed() : false; + m_addDynamicAction->setEnabled(addEnabled); + m_removeDynamicAction->setEnabled(false); + applyExpansionState(); + applyFilter(); + // In the first setObject() call following the addition of a dynamic property, focus and edit it. + if (editNewDynamicProperty) { + // Have QApplication process the events related to completely closing the modal 'add' dialog, + // otherwise, we cannot focus the property editor in docked mode. + QApplication::processEvents(QEventLoop::ExcludeUserInputEvents); + editProperty(m_recentlyAddedDynamicProperty); + } + m_recentlyAddedDynamicProperty.clear(); + m_filterWidget->setEnabled(object); +} + +void PropertyEditor::reloadResourceProperties() +{ + m_updatingBrowser = true; + m_propertyManager->reloadResourceProperties(); + m_updatingBrowser = false; +} + +QtBrowserItem *PropertyEditor::nonFakePropertyBrowserItem(QtBrowserItem *item) const +{ + // Top-level properties are QObject/QWidget groups, etc. Find first item property below + // which should be nonfake + const QList<QtBrowserItem *> topLevelItems = m_currentBrowser->topLevelItems(); + do { + if (topLevelItems.contains(item->parent())) + return item; + item = item->parent(); + } while (item); + return 0; +} + +QString PropertyEditor::currentPropertyName() const +{ + if (QtBrowserItem *browserItem = m_currentBrowser->currentItem()) + if (QtBrowserItem *topLevelItem = nonFakePropertyBrowserItem(browserItem)) { + return topLevelItem->property()->propertyName(); + } + return QString(); +} + +void PropertyEditor::slotResetProperty(QtProperty *property) +{ + QDesignerFormWindowInterface *form = m_core->formWindowManager()->activeFormWindow(); + if (!form) + return; + + if (m_propertyManager->resetFontSubProperty(property)) + return; + + if (m_propertyManager->resetIconSubProperty(property)) + return; + + if (!m_propertyToGroup.contains(property)) + return; + + emit resetProperty(property->propertyName()); +} + +void PropertyEditor::slotValueChanged(QtProperty *property, const QVariant &value, bool enableSubPropertyHandling) +{ + if (m_updatingBrowser) + return; + + if (!m_propertySheet) + return; + + QtVariantProperty *varProp = m_propertyManager->variantProperty(property); + + if (!varProp) + return; + + if (!m_propertyToGroup.contains(property)) + return; + + if (varProp->propertyType() == QtVariantPropertyManager::enumTypeId()) { + PropertySheetEnumValue e = qvariant_cast<PropertySheetEnumValue>(m_propertySheet->property(m_propertySheet->indexOf(property->propertyName()))); + const int val = value.toInt(); + const QString valName = varProp->attributeValue(m_strings.m_enumNamesAttribute).toStringList().at(val); + bool ok = false; + e.value = e.metaEnum.parseEnum(valName, &ok); + Q_ASSERT(ok); + QVariant v; + v.setValue(e); + emitPropertyValueChanged(property->propertyName(), v, true); + return; + } + + emitPropertyValueChanged(property->propertyName(), value, enableSubPropertyHandling); +} + +bool PropertyEditor::isDynamicProperty(const QtBrowserItem* item) const +{ + if (!item) + return false; + + const QDesignerDynamicPropertySheetExtension *dynamicSheet = + qt_extension<QDesignerDynamicPropertySheetExtension*>(m_core->extensionManager(), m_object); + + if (!dynamicSheet) + return false; + + if (m_propertyToGroup.contains(item->property()) + && dynamicSheet->isDynamicProperty(m_propertySheet->indexOf(item->property()->propertyName()))) + return true; + return false; +} + +void PropertyEditor::editProperty(const QString &name) +{ + // find the browser item belonging to the property, make it current and edit it + QtBrowserItem *browserItem = 0; + if (QtVariantProperty *property = m_nameToProperty.value(name, 0)) { + const QList<QtBrowserItem *> items = m_currentBrowser->items(property); + if (items.size() == 1) + browserItem = items.front(); + } + if (browserItem == 0) + return; + m_currentBrowser->setFocus(Qt::OtherFocusReason); + if (m_currentBrowser == m_treeBrowser) { // edit is currently only supported in tree view + m_treeBrowser->editItem(browserItem); + } else { + m_currentBrowser->setCurrentItem(browserItem); + } +} + +void PropertyEditor::slotCurrentItemChanged(QtBrowserItem *item) +{ + m_removeDynamicAction->setEnabled(isDynamicProperty(item)); + +} + +void PropertyEditor::slotRemoveDynamicProperty() +{ + if (QtBrowserItem* item = m_currentBrowser->currentItem()) + if (isDynamicProperty(item)) + emit removeDynamicProperty(item->property()->propertyName()); +} + +void PropertyEditor::setFilter(const QString &pattern) +{ + m_filterPattern = pattern; + applyFilter(); +} +} + +QT_END_NAMESPACE |