blob: 4c36d88e07a0f8cc60eb0d045e5057dca2048146 [file] [log] [blame]
/****************************************************************************
**
** 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$
** GNU Lesser General Public License Usage
** 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.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU General
** Public License version 3.0 as published by the Free Software Foundation
** and appearing in the file LICENSE.GPL included in the packaging of this
** file. Please review the following information to ensure the GNU General
** Public License version 3.0 requirements will be met:
** http://www.gnu.org/copyleft/gpl.html.
**
** Other Usage
** Alternatively, this file may be used in accordance with the terms and
** conditions contained in a signed written agreement between you and Nokia.
**
**
**
**
**
** $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";
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();
settings->endGroup();
// Apply settings
m_sortingAction->setChecked(m_sorting);
m_coloringAction->setChecked(m_coloring);
#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->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 (qVariantCanConvert<PropertySheetFlagValue>(value)) {
if (m_strings.m_alignmentProperties.contains(propertyName))
return DesignerPropertyManager::designerAlignmentTypeId();
return DesignerPropertyManager::designerFlagTypeId();
}
if (qVariantCanConvert<PropertySheetEnumValue>(value))
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;
qVariantSetValue(v, 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;
qVariantSetValue(v, 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