aboutsummaryrefslogtreecommitdiffstats
path: root/src/plugins/qmldesigner/components/connectioneditor
diff options
context:
space:
mode:
Diffstat (limited to 'src/plugins/qmldesigner/components/connectioneditor')
-rw-r--r--src/plugins/qmldesigner/components/connectioneditor/addnewbackenddialog.cpp114
-rw-r--r--src/plugins/qmldesigner/components/connectioneditor/addnewbackenddialog.h63
-rw-r--r--src/plugins/qmldesigner/components/connectioneditor/addnewbackenddialog.ui135
-rw-r--r--src/plugins/qmldesigner/components/connectioneditor/backendmodel.cpp333
-rw-r--r--src/plugins/qmldesigner/components/connectioneditor/backendmodel.h74
-rw-r--r--src/plugins/qmldesigner/components/connectioneditor/bindingmodel.cpp446
-rw-r--r--src/plugins/qmldesigner/components/connectioneditor/bindingmodel.h93
-rw-r--r--src/plugins/qmldesigner/components/connectioneditor/connectioneditor.pri26
-rw-r--r--src/plugins/qmldesigner/components/connectioneditor/connectioneditor.qrc5
-rw-r--r--src/plugins/qmldesigner/components/connectioneditor/connectionmodel.cpp357
-rw-r--r--src/plugins/qmldesigner/components/connectioneditor/connectionmodel.h89
-rw-r--r--src/plugins/qmldesigner/components/connectioneditor/connectionview.cpp222
-rw-r--r--src/plugins/qmldesigner/components/connectioneditor/connectionview.h100
-rw-r--r--src/plugins/qmldesigner/components/connectioneditor/connectionviewwidget.cpp348
-rw-r--r--src/plugins/qmldesigner/components/connectioneditor/connectionviewwidget.h102
-rw-r--r--src/plugins/qmldesigner/components/connectioneditor/connectionviewwidget.ui273
-rw-r--r--src/plugins/qmldesigner/components/connectioneditor/delegates.cpp361
-rw-r--r--src/plugins/qmldesigner/components/connectioneditor/delegates.h104
-rw-r--r--src/plugins/qmldesigner/components/connectioneditor/dynamicpropertiesmodel.cpp677
-rw-r--r--src/plugins/qmldesigner/components/connectioneditor/dynamicpropertiesmodel.h104
-rw-r--r--src/plugins/qmldesigner/components/connectioneditor/stylesheet.css123
21 files changed, 4149 insertions, 0 deletions
diff --git a/src/plugins/qmldesigner/components/connectioneditor/addnewbackenddialog.cpp b/src/plugins/qmldesigner/components/connectioneditor/addnewbackenddialog.cpp
new file mode 100644
index 0000000000..9de928ae90
--- /dev/null
+++ b/src/plugins/qmldesigner/components/connectioneditor/addnewbackenddialog.cpp
@@ -0,0 +1,114 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#include "addnewbackenddialog.h"
+#include "ui_addnewbackenddialog.h"
+
+#include <QPushButton>
+
+namespace QmlDesigner {
+
+AddNewBackendDialog::AddNewBackendDialog(QWidget *parent) :
+ QDialog(parent),
+ m_ui(new Ui::AddNewBackendDialog)
+{
+ setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
+ m_ui->setupUi(this);
+
+ connect(m_ui->comboBox, &QComboBox::currentTextChanged, this, &AddNewBackendDialog::invalidate);
+
+ connect(m_ui->buttonBox, &QDialogButtonBox::accepted, this, [this]() {
+ m_applied = true;
+ close();
+ });
+
+ connect(m_ui->buttonBox, &QDialogButtonBox::rejected, this, &AddNewBackendDialog::close);
+}
+
+AddNewBackendDialog::~AddNewBackendDialog()
+{
+ delete m_ui;
+}
+
+void AddNewBackendDialog::setupPossibleTypes(const QList<CppTypeData> &types)
+{
+ QSignalBlocker blocker(this);
+ m_typeData = types;
+ for (const CppTypeData &typeData : types)
+ m_ui->comboBox->addItem(typeData.typeName);
+
+ m_ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(m_ui->comboBox->count() > 0);
+ invalidate();
+}
+
+QString AddNewBackendDialog::importString() const
+{
+ if (m_ui->comboBox->currentIndex() < 0)
+ return QString();
+
+ CppTypeData typeData = m_typeData.at(m_ui->comboBox->currentIndex());
+
+ return typeData.importUrl + " " + typeData.versionString;
+}
+
+QString AddNewBackendDialog::type() const
+{
+ if (m_ui->comboBox->currentIndex() < 0)
+ return QString();
+
+ return m_typeData.at(m_ui->comboBox->currentIndex()).typeName;
+}
+
+bool AddNewBackendDialog::applied() const
+{
+ return m_applied;
+}
+
+bool AddNewBackendDialog::localDefinition() const
+{
+ return m_ui->checkBox->isChecked();
+}
+
+bool AddNewBackendDialog::isSingleton() const
+{
+ return m_isSingleton;
+}
+
+void AddNewBackendDialog::invalidate()
+{
+ if (m_ui->comboBox->currentIndex() < 0)
+ return;
+
+ CppTypeData typeData = m_typeData.at(m_ui->comboBox->currentIndex());
+ m_ui->importLabel->setText(importString());
+
+ m_ui->checkBox->setChecked(false);
+ if (typeData.isSingleton)
+ m_ui->checkBox->setEnabled(false);
+
+ m_isSingleton = typeData.isSingleton;
+}
+
+} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/connectioneditor/addnewbackenddialog.h b/src/plugins/qmldesigner/components/connectioneditor/addnewbackenddialog.h
new file mode 100644
index 0000000000..4cfa46d66e
--- /dev/null
+++ b/src/plugins/qmldesigner/components/connectioneditor/addnewbackenddialog.h
@@ -0,0 +1,63 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#pragma once
+
+#include <QDialog>
+
+#include <rewriterview.h>
+
+namespace QmlDesigner {
+
+namespace Ui {
+class AddNewBackendDialog;
+}
+
+class AddNewBackendDialog : public QDialog
+{
+ Q_OBJECT
+
+public:
+ explicit AddNewBackendDialog(QWidget *parent = nullptr);
+ ~AddNewBackendDialog() override;
+ void setupPossibleTypes(const QList<CppTypeData> &types);
+ QString importString() const;
+ QString type() const;
+ bool applied() const;
+ bool localDefinition() const;
+ bool isSingleton() const;
+
+private:
+ void invalidate();
+
+ Ui::AddNewBackendDialog *m_ui;
+ QList<CppTypeData> m_typeData;
+
+ bool m_applied = false;
+ bool m_isSingleton = false;
+};
+
+
+} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/connectioneditor/addnewbackenddialog.ui b/src/plugins/qmldesigner/components/connectioneditor/addnewbackenddialog.ui
new file mode 100644
index 0000000000..1da43f7526
--- /dev/null
+++ b/src/plugins/qmldesigner/components/connectioneditor/addnewbackenddialog.ui
@@ -0,0 +1,135 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>QmlDesigner::AddNewBackendDialog</class>
+ <widget class="QDialog" name="QmlDesigner::AddNewBackendDialog">
+ <property name="windowModality">
+ <enum>Qt::ApplicationModal</enum>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>412</width>
+ <height>202</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>Add New C++ Backend</string>
+ </property>
+ <property name="modal">
+ <bool>true</bool>
+ </property>
+ <layout class="QGridLayout" name="gridLayout_2">
+ <item row="2" column="0">
+ <spacer name="verticalSpacer">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>13</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item row="3" column="1">
+ <widget class="QDialogButtonBox" name="buttonBox">
+ <property name="standardButtons">
+ <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0" colspan="2">
+ <widget class="QFrame" name="frame">
+ <layout class="QGridLayout" name="gridLayout">
+ <item row="0" column="0">
+ <widget class="QLabel" name="label_3">
+ <property name="text">
+ <string>Type</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="QComboBox" name="comboBox"/>
+ </item>
+ <item row="0" column="2">
+ <spacer name="horizontalSpacer">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>169</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item row="0" column="3">
+ <widget class="QLabel" name="label_2">
+ <property name="text">
+ <string>Define object locally</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="4">
+ <widget class="QCheckBox" name="checkBox">
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="3">
+ <widget class="QLabel" name="label">
+ <property name="text">
+ <string>Required import</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="4">
+ <widget class="QLabel" name="importLabel">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Fixed" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>160</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>160</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item row="0" column="0" colspan="2">
+ <widget class="QLabel" name="label_4">
+ <property name="layoutDirection">
+ <enum>Qt::LeftToRight</enum>
+ </property>
+ <property name="text">
+ <string>Choose a type that is registered using qmlRegisterType or qmlRegisterSingletonType. The type will be available as a property in the current .qml file.</string>
+ </property>
+ <property name="wordWrap">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/src/plugins/qmldesigner/components/connectioneditor/backendmodel.cpp b/src/plugins/qmldesigner/components/connectioneditor/backendmodel.cpp
new file mode 100644
index 0000000000..4a004ca575
--- /dev/null
+++ b/src/plugins/qmldesigner/components/connectioneditor/backendmodel.cpp
@@ -0,0 +1,333 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#include <utils/algorithm.h>
+
+#include "backendmodel.h"
+
+#include "bindingproperty.h"
+#include "connectionview.h"
+#include "exception.h"
+#include "nodemetainfo.h"
+#include "nodeproperty.h"
+#include "rewriterview.h"
+#include "rewritertransaction.h"
+
+#include "addnewbackenddialog.h"
+
+#include <coreplugin/icore.h>
+#include <utils/qtcassert.h>
+
+namespace QmlDesigner {
+
+namespace Internal {
+
+BackendModel::BackendModel(ConnectionView *parent) :
+ QStandardItemModel(parent)
+ ,m_connectionView(parent)
+{
+ connect(this, &QStandardItemModel::dataChanged, this, &BackendModel::handleDataChanged);
+}
+
+ConnectionView *QmlDesigner::Internal::BackendModel::connectionView() const
+{
+ return m_connectionView;
+}
+
+void BackendModel::resetModel()
+{
+ if (!m_connectionView->model())
+ return;
+
+ RewriterView *rewriterView = m_connectionView->model()->rewriterView();
+
+ m_lock = true;
+
+ beginResetModel();
+ clear();
+
+ setHorizontalHeaderLabels(QStringList({ tr("Type"), tr("Name"), tr("Singleton"), tr("Local") }));
+
+ ModelNode rootNode = connectionView()->rootModelNode();
+
+ static const PropertyTypeList simpleTypes = {"int", "real", "color", "string"};
+
+ if (rewriterView)
+ for (const CppTypeData &cppTypeData : rewriterView->getCppTypes())
+ if (cppTypeData.isSingleton) {
+ NodeMetaInfo metaInfo = m_connectionView->model()->metaInfo(cppTypeData.typeName.toUtf8());
+ if (metaInfo.isValid() && !metaInfo.isSubclassOf("QtQuick.Item")) {
+ auto type = new QStandardItem(cppTypeData.typeName);
+ type->setData(cppTypeData.typeName, Qt::UserRole + 1);
+ type->setData(true, Qt::UserRole + 2);
+ type->setEditable(false);
+
+ auto name = new QStandardItem(cppTypeData.typeName);
+ name->setEditable(false);
+
+ QStandardItem *singletonItem = new QStandardItem("");
+ singletonItem->setCheckState(Qt::Checked);
+
+ singletonItem->setCheckable(true);
+ singletonItem->setEnabled(false);
+
+ QStandardItem *inlineItem = new QStandardItem("");
+
+ inlineItem->setCheckState(Qt::Unchecked);
+
+ inlineItem->setCheckable(true);
+ inlineItem->setEnabled(false);
+
+ appendRow({ type, name, singletonItem, inlineItem });
+ }
+ }
+
+ if (rootNode.isValid())
+ foreach (const AbstractProperty &property ,rootNode.properties())
+ if (property.isDynamic() && !simpleTypes.contains(property.dynamicTypeName())) {
+
+ NodeMetaInfo metaInfo = m_connectionView->model()->metaInfo(property.dynamicTypeName());
+ if (metaInfo.isValid() && !metaInfo.isSubclassOf("QtQuick.Item")) {
+ QStandardItem *type = new QStandardItem(QString::fromUtf8(property.dynamicTypeName()));
+ type->setEditable(false);
+
+ type->setData(QString::fromUtf8(property.name()), Qt::UserRole + 1);
+ type->setData(false, Qt::UserRole + 2);
+ QStandardItem *name = new QStandardItem(QString::fromUtf8(property.name()));
+
+ QStandardItem *singletonItem = new QStandardItem("");
+ singletonItem->setCheckState(Qt::Unchecked);
+
+ singletonItem->setCheckable(true);
+ singletonItem->setEnabled(false);
+
+ QStandardItem *inlineItem = new QStandardItem("");
+
+ inlineItem->setCheckState(property.isNodeProperty() ? Qt::Checked : Qt::Unchecked);
+
+ inlineItem->setCheckable(true);
+ inlineItem->setEnabled(false);
+
+ appendRow({ type, name, singletonItem, inlineItem });
+ }
+ }
+
+ m_lock = false;
+
+ endResetModel();
+}
+
+QStringList BackendModel::possibleCppTypes() const
+{
+ RewriterView *rewriterView = m_connectionView->model()->rewriterView();
+
+ QStringList list;
+
+ if (rewriterView)
+ foreach (const CppTypeData &cppTypeData, rewriterView->getCppTypes())
+ list.append(cppTypeData.typeName);
+
+ return list;
+}
+
+CppTypeData BackendModel::cppTypeDataForType(const QString &typeName) const
+{
+ RewriterView *rewriterView = m_connectionView->model()->rewriterView();
+
+ if (!rewriterView)
+ return CppTypeData();
+
+ return Utils::findOr(rewriterView->getCppTypes(), CppTypeData(), [&typeName](const CppTypeData &data) {
+ return typeName == data.typeName;
+ });
+}
+
+void BackendModel::deletePropertyByRow(int rowNumber)
+{
+ Model *model = m_connectionView->model();
+ if (!model)
+ return;
+
+ /* singleton case remove the import */
+ if (data(index(rowNumber, 0), Qt::UserRole + 1).toBool()) {
+ const QString typeName = data(index(rowNumber, 0), Qt::UserRole + 1).toString();
+ CppTypeData cppTypeData = cppTypeDataForType(typeName);
+
+ if (cppTypeData.isSingleton) {
+
+ Import import = Import::createLibraryImport(cppTypeData.importUrl, cppTypeData.versionString);
+
+ try {
+ if (model->hasImport(import))
+ model->changeImports({}, {import});
+ } catch (const Exception &e) {
+ e.showException();
+ }
+ }
+ } else {
+ const QString propertyName = data(index(rowNumber, 0), Qt::UserRole + 1).toString();
+
+ ModelNode modelNode = connectionView()->rootModelNode();
+
+ try {
+ modelNode.removeProperty(propertyName.toUtf8());
+ } catch (const Exception &e) {
+ e.showException();
+ }
+ }
+
+ resetModel();
+}
+
+void BackendModel::addNewBackend()
+{
+ Model *model = m_connectionView->model();
+ if (!model)
+ return;
+
+ AddNewBackendDialog dialog(Core::ICore::mainWindow());
+
+ RewriterView *rewriterView = model->rewriterView();
+
+ QStringList availableTypes;
+
+ if (rewriterView)
+ dialog.setupPossibleTypes(Utils::filtered(rewriterView->getCppTypes(), [model](const CppTypeData &cppTypeData) {
+ return !cppTypeData.isSingleton || !model->metaInfo(cppTypeData.typeName.toUtf8()).isValid();
+ /* Only show singletons if the import is missing */
+ }));
+
+ dialog.exec();
+
+ if (dialog.applied()) {
+ QStringList importSplit = dialog.importString().split(" ");
+ if (importSplit.count() != 2) {
+ qWarning() << Q_FUNC_INFO << "invalid import" << importSplit;
+ QTC_ASSERT(false, return);
+ }
+
+ QString typeName = dialog.type();
+
+ Import import = Import::createLibraryImport(importSplit.constFirst(), importSplit.constLast());
+
+ /* We cannot add an import and add a node from that import in a single transaction.
+ * We need the import to have the meta info available.
+ */
+
+ if (!model->hasImport(import))
+ model->changeImports({import}, {});
+
+ QString propertyName = m_connectionView->generateNewId(typeName);
+
+ NodeMetaInfo metaInfo = model->metaInfo(typeName.toUtf8());
+
+ QTC_ASSERT(metaInfo.isValid(), return);
+
+ /* Add a property for non singleton types. For singletons just adding the import is enough. */
+ if (!dialog.isSingleton()) {
+ m_connectionView->executeInTransaction("BackendModel::addNewBackend", [=, &dialog](){
+ int minorVersion = metaInfo.minorVersion();
+ int majorVersion = metaInfo.majorVersion();
+
+ if (dialog.localDefinition()) {
+ ModelNode newNode = m_connectionView->createModelNode(metaInfo.typeName(), majorVersion, minorVersion);
+
+ m_connectionView->rootModelNode().nodeProperty(propertyName.toUtf8()).setDynamicTypeNameAndsetModelNode(
+ typeName.toUtf8(), newNode);
+ } else {
+ m_connectionView->rootModelNode().bindingProperty(
+ propertyName.toUtf8()).setDynamicTypeNameAndExpression(typeName.toUtf8(), "null");
+ }
+ });
+ }
+ }
+ resetModel();
+}
+
+void BackendModel::updatePropertyName(int rowNumber)
+{
+ const PropertyName newName = data(index(rowNumber, 1)).toString().toUtf8();
+ const PropertyName oldName = data(index(rowNumber, 0), Qt::UserRole + 1).toString().toUtf8();
+
+ m_connectionView->executeInTransaction("BackendModel::updatePropertyName", [this, newName, oldName](){
+
+ ModelNode rootModelNode = m_connectionView->rootModelNode();
+ if (rootModelNode.property(oldName).isNodeProperty()) {
+
+ const TypeName typeName = rootModelNode.nodeProperty(oldName).dynamicTypeName();
+ const ModelNode targetModelNode = rootModelNode.nodeProperty(oldName).modelNode();
+ const TypeName fullTypeName = targetModelNode.type();
+ const int majorVersion = targetModelNode.majorVersion();
+ const int minorVersion = targetModelNode.minorVersion();
+
+ rootModelNode.removeProperty(oldName);
+ ModelNode newNode = m_connectionView->createModelNode(fullTypeName, majorVersion, minorVersion);
+ m_connectionView->rootModelNode().nodeProperty(newName).setDynamicTypeNameAndsetModelNode(typeName, newNode);
+
+ } else if (rootModelNode.property(oldName).isBindingProperty()) {
+ const QString expression = rootModelNode.bindingProperty(oldName).expression();
+ const TypeName typeName = rootModelNode.bindingProperty(oldName).dynamicTypeName();
+
+ rootModelNode.removeProperty(oldName);
+ rootModelNode.bindingProperty(newName).setDynamicTypeNameAndExpression(typeName, expression);
+ } else {
+ qWarning() << Q_FUNC_INFO << oldName << newName << "failed...";
+ QTC_ASSERT(false, return);
+ }
+ });
+}
+
+void BackendModel::handleDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight)
+{
+ if (m_lock)
+ return;
+
+ if (topLeft != bottomRight) {
+ qWarning() << "BackendModel::handleDataChanged multi edit?";
+ return;
+ }
+
+ m_lock = true;
+
+ int currentColumn = topLeft.column();
+ int currentRow = topLeft.row();
+
+ switch (currentColumn) {
+ case 0: {
+ //updating user data
+ } break;
+ case 1: {
+ updatePropertyName(currentRow);
+ } break;
+
+ default: qWarning() << "BindingModel::handleDataChanged column" << currentColumn;
+ }
+
+ m_lock = false;
+}
+
+} // namespace Internal
+
+} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/connectioneditor/backendmodel.h b/src/plugins/qmldesigner/components/connectioneditor/backendmodel.h
new file mode 100644
index 0000000000..8abbbe77fa
--- /dev/null
+++ b/src/plugins/qmldesigner/components/connectioneditor/backendmodel.h
@@ -0,0 +1,74 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#pragma once
+#include <QStandardItemModel>
+
+#include "rewriterview.h"
+
+namespace QmlDesigner {
+
+namespace Internal {
+
+class ConnectionView;
+
+class BackendModel : public QStandardItemModel
+{
+ Q_OBJECT
+public:
+ enum ColumnRoles {
+ TypeNameColumn = 0,
+ PropertyNameColumn = 1,
+ IsSingletonColumn = 2,
+ IsLocalColumn = 3,
+ };
+
+ BackendModel(ConnectionView *parent);
+
+ ConnectionView *connectionView() const;
+
+ void resetModel();
+
+ QStringList possibleCppTypes() const;
+ CppTypeData cppTypeDataForType(const QString &typeName) const;
+
+ void deletePropertyByRow(int rowNumber);
+
+ void addNewBackend();
+
+protected:
+ void updatePropertyName(int rowNumber);
+
+private:
+ void handleDataChanged(const QModelIndex &topLeft, const QModelIndex& bottomRight);
+
+private:
+ ConnectionView *m_connectionView;
+ bool m_lock = false;
+};
+
+} // namespace Internal
+
+} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/connectioneditor/bindingmodel.cpp b/src/plugins/qmldesigner/components/connectioneditor/bindingmodel.cpp
new file mode 100644
index 0000000000..2cff12b044
--- /dev/null
+++ b/src/plugins/qmldesigner/components/connectioneditor/bindingmodel.cpp
@@ -0,0 +1,446 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#include "bindingmodel.h"
+
+#include "connectionview.h"
+
+#include <nodemetainfo.h>
+#include <nodeproperty.h>
+#include <bindingproperty.h>
+#include <variantproperty.h>
+#include <rewritingexception.h>
+#include <rewritertransaction.h>
+
+#include <QMessageBox>
+#include <QTimer>
+
+namespace QmlDesigner {
+
+namespace Internal {
+
+BindingModel::BindingModel(ConnectionView *parent)
+ : QStandardItemModel(parent)
+ , m_connectionView(parent)
+{
+ connect(this, &QStandardItemModel::dataChanged, this, &BindingModel::handleDataChanged);
+}
+
+void BindingModel::resetModel()
+{
+ beginResetModel();
+ clear();
+ setHorizontalHeaderLabels(QStringList({ tr("Item"), tr("Property"), tr("Source Item"),
+ tr("Source Property") }));
+
+ foreach (const ModelNode modelNode, m_selectedModelNodes)
+ addModelNode(modelNode);
+
+ endResetModel();
+}
+
+void BindingModel::bindingChanged(const BindingProperty &bindingProperty)
+{
+ m_handleDataChanged = false;
+
+ QList<ModelNode> selectedNodes = connectionView()->selectedModelNodes();
+ if (!selectedNodes.contains(bindingProperty.parentModelNode()))
+ return;
+ if (!m_lock) {
+ int rowNumber = findRowForBinding(bindingProperty);
+
+ if (rowNumber == -1) {
+ addBindingProperty(bindingProperty);
+ } else {
+ updateBindingProperty(rowNumber);
+ }
+ }
+
+ m_handleDataChanged = true;
+}
+
+void BindingModel::bindingRemoved(const BindingProperty &bindingProperty)
+{
+ m_handleDataChanged = false;
+
+ QList<ModelNode> selectedNodes = connectionView()->selectedModelNodes();
+ if (!selectedNodes.contains(bindingProperty.parentModelNode()))
+ return;
+ if (!m_lock) {
+ int rowNumber = findRowForBinding(bindingProperty);
+ removeRow(rowNumber);
+ }
+
+ m_handleDataChanged = true;
+}
+
+void BindingModel::selectionChanged(const QList<ModelNode> &selectedNodes)
+{
+ m_handleDataChanged = false;
+ m_selectedModelNodes = selectedNodes;
+ resetModel();
+ m_handleDataChanged = true;
+}
+
+ConnectionView *BindingModel::connectionView() const
+{
+ return m_connectionView;
+}
+
+BindingProperty BindingModel::bindingPropertyForRow(int rowNumber) const
+{
+
+ const int internalId = data(index(rowNumber, TargetModelNodeRow), Qt::UserRole + 1).toInt();
+ const QString targetPropertyName = data(index(rowNumber, TargetModelNodeRow), Qt::UserRole + 2).toString();
+
+ ModelNode modelNode = connectionView()->modelNodeForInternalId(internalId);
+
+ if (modelNode.isValid())
+ return modelNode.bindingProperty(targetPropertyName.toLatin1());
+
+ return BindingProperty();
+}
+
+QStringList BindingModel::possibleTargetProperties(const BindingProperty &bindingProperty) const
+{
+ const ModelNode modelNode = bindingProperty.parentModelNode();
+
+ if (!modelNode.isValid()) {
+ qWarning() << " BindingModel::possibleTargetPropertiesForRow invalid model node";
+ return QStringList();
+ }
+
+ NodeMetaInfo metaInfo = modelNode.metaInfo();
+
+ if (metaInfo.isValid()) {
+ QStringList possibleProperties;
+ foreach (const PropertyName &propertyName, metaInfo.propertyNames()) {
+ if (metaInfo.propertyIsWritable(propertyName))
+ possibleProperties << QString::fromUtf8(propertyName);
+ }
+
+ return possibleProperties;
+ }
+
+ return QStringList();
+}
+
+QStringList BindingModel::possibleSourceProperties(const BindingProperty &bindingProperty) const
+{
+ const QString expression = bindingProperty.expression();
+ const QStringList stringlist = expression.split(QLatin1String("."));
+
+ TypeName typeName;
+
+ if (bindingProperty.parentModelNode().metaInfo().isValid()) {
+ typeName = bindingProperty.parentModelNode().metaInfo().propertyTypeName(bindingProperty.name());
+ } else {
+ qWarning() << " BindingModel::possibleSourcePropertiesForRow no meta info for target node";
+ }
+
+ const QString &id = stringlist.constFirst();
+
+ ModelNode modelNode = getNodeByIdOrParent(id, bindingProperty.parentModelNode());
+
+ if (!modelNode.isValid()) {
+ qWarning() << " BindingModel::possibleSourcePropertiesForRow invalid model node";
+ return QStringList();
+ }
+
+ NodeMetaInfo metaInfo = modelNode.metaInfo();
+
+ QStringList possibleProperties;
+
+ foreach (VariantProperty variantProperty, modelNode.variantProperties()) {
+ if (variantProperty.isDynamic())
+ possibleProperties << QString::fromUtf8(variantProperty.name());
+ }
+
+ foreach (BindingProperty bindingProperty, modelNode.bindingProperties()) {
+ if (bindingProperty.isDynamic())
+ possibleProperties << QString::fromUtf8((bindingProperty.name()));
+ }
+
+ if (metaInfo.isValid()) {
+ foreach (const PropertyName &propertyName, metaInfo.propertyNames()) {
+ if (metaInfo.propertyTypeName(propertyName) == typeName) //### todo proper check
+ possibleProperties << QString::fromUtf8(propertyName);
+ }
+ } else {
+ qWarning() << " BindingModel::possibleSourcePropertiesForRow no meta info for source node";
+ }
+
+ return possibleProperties;
+}
+
+void BindingModel::deleteBindindByRow(int rowNumber)
+{
+ BindingProperty bindingProperty = bindingPropertyForRow(rowNumber);
+
+ if (bindingProperty.isValid()) {
+ bindingProperty.parentModelNode().removeProperty(bindingProperty.name());
+ }
+
+ resetModel();
+}
+
+static PropertyName unusedProperty(const ModelNode &modelNode)
+{
+ PropertyName propertyName = "none";
+ if (modelNode.metaInfo().isValid()) {
+ foreach (const PropertyName &propertyName, modelNode.metaInfo().propertyNames()) {
+ if (modelNode.metaInfo().propertyIsWritable(propertyName) && !modelNode.hasProperty(propertyName))
+ return propertyName;
+ }
+ }
+
+ return propertyName;
+}
+
+void BindingModel::addBindingForCurrentNode()
+{
+ if (connectionView()->selectedModelNodes().count() == 1) {
+ const ModelNode modelNode = connectionView()->selectedModelNodes().constFirst();
+ if (modelNode.isValid()) {
+ try {
+ modelNode.bindingProperty(unusedProperty(modelNode)).setExpression(QLatin1String("none.none"));
+ } catch (RewritingException &e) {
+ m_exceptionError = e.description();
+ QTimer::singleShot(200, this, &BindingModel::handleException);
+ }
+ }
+ } else {
+ qWarning() << " BindingModel::addBindingForCurrentNode not one node selected";
+ }
+}
+
+void BindingModel::addBindingProperty(const BindingProperty &property)
+{
+ QStandardItem *idItem;
+ QStandardItem *targetPropertyNameItem;
+ QStandardItem *sourceIdItem;
+ QStandardItem *sourcePropertyNameItem;
+
+ QString idLabel = property.parentModelNode().id();
+ if (idLabel.isEmpty())
+ idLabel = property.parentModelNode().simplifiedTypeName();
+ idItem = new QStandardItem(idLabel);
+ updateCustomData(idItem, property);
+ targetPropertyNameItem = new QStandardItem(QString::fromUtf8(property.name()));
+ QList<QStandardItem*> items;
+
+ items.append(idItem);
+ items.append(targetPropertyNameItem);
+
+ QString sourceNodeName;
+ QString sourcePropertyName;
+ getExpressionStrings(property, &sourceNodeName, &sourcePropertyName);
+
+ sourceIdItem = new QStandardItem(sourceNodeName);
+ sourcePropertyNameItem = new QStandardItem(sourcePropertyName);
+
+ items.append(sourceIdItem);
+ items.append(sourcePropertyNameItem);
+ appendRow(items);
+}
+
+void BindingModel::updateBindingProperty(int rowNumber)
+{
+ BindingProperty bindingProperty = bindingPropertyForRow(rowNumber);
+
+ if (bindingProperty.isValid()) {
+ QString targetPropertyName = QString::fromUtf8(bindingProperty.name());
+ updateDisplayRole(rowNumber, TargetPropertyNameRow, targetPropertyName);
+ QString sourceNodeName;
+ QString sourcePropertyName;
+ getExpressionStrings(bindingProperty, &sourceNodeName, &sourcePropertyName);
+ updateDisplayRole(rowNumber, SourceModelNodeRow, sourceNodeName);
+ updateDisplayRole(rowNumber, SourcePropertyNameRow, sourcePropertyName);
+ }
+}
+
+void BindingModel::addModelNode(const ModelNode &modelNode)
+{
+ foreach (const BindingProperty &bindingProperty, modelNode.bindingProperties()) {
+ addBindingProperty(bindingProperty);
+ }
+}
+
+void BindingModel::updateExpression(int row)
+{
+ const QString sourceNode = data(index(row, SourceModelNodeRow)).toString().trimmed();
+ const QString sourceProperty = data(index(row, SourcePropertyNameRow)).toString().trimmed();
+
+ QString expression;
+ if (sourceProperty.isEmpty()) {
+ expression = sourceNode;
+ } else {
+ expression = sourceNode + QLatin1String(".") + sourceProperty;
+ }
+
+ connectionView()->executeInTransaction("BindingModel::updateExpression", [this, row, expression](){
+ BindingProperty bindingProperty = bindingPropertyForRow(row);
+ bindingProperty.setExpression(expression.trimmed());
+ });
+}
+
+void BindingModel::updatePropertyName(int rowNumber)
+{
+ BindingProperty bindingProperty = bindingPropertyForRow(rowNumber);
+
+ const PropertyName newName = data(index(rowNumber, TargetPropertyNameRow)).toString().toUtf8();
+ const QString expression = bindingProperty.expression();
+ const PropertyName dynamicPropertyType = bindingProperty.dynamicTypeName();
+ ModelNode targetNode = bindingProperty.parentModelNode();
+
+ if (!newName.isEmpty()) {
+ RewriterTransaction transaction =
+ connectionView()->beginRewriterTransaction(QByteArrayLiteral("BindingModel::updatePropertyName"));
+ try {
+ if (bindingProperty.isDynamic()) {
+ targetNode.bindingProperty(newName).setDynamicTypeNameAndExpression(dynamicPropertyType, expression);
+ } else {
+ targetNode.bindingProperty(newName).setExpression(expression);
+ }
+ targetNode.removeProperty(bindingProperty.name());
+ transaction.commit(); //committing in the try block
+ } catch (Exception &e) { //better save then sorry
+ m_exceptionError = e.description();
+ QTimer::singleShot(200, this, &BindingModel::handleException);
+ }
+
+ QStandardItem* idItem = item(rowNumber, 0);
+ BindingProperty newBindingProperty = targetNode.bindingProperty(newName);
+ updateCustomData(idItem, newBindingProperty);
+
+ } else {
+ qWarning() << "BindingModel::updatePropertyName invalid property name";
+ }
+}
+
+ModelNode BindingModel::getNodeByIdOrParent(const QString &id, const ModelNode &targetNode) const
+{
+ ModelNode modelNode;
+
+ if (id != QLatin1String("parent")) {
+ modelNode = connectionView()->modelNodeForId(id);
+ } else {
+ if (targetNode.hasParentProperty()) {
+ modelNode = targetNode.parentProperty().parentModelNode();
+ }
+ }
+ return modelNode;
+}
+
+void BindingModel::updateCustomData(QStandardItem *item, const BindingProperty &bindingProperty)
+{
+ item->setData(bindingProperty.parentModelNode().internalId(), Qt::UserRole + 1);
+ item->setData(bindingProperty.name(), Qt::UserRole + 2);
+}
+
+int BindingModel::findRowForBinding(const BindingProperty &bindingProperty)
+{
+ for (int i=0; i < rowCount(); i++) {
+ if (compareBindingProperties(bindingPropertyForRow(i), bindingProperty))
+ return i;
+ }
+ //not found
+ return -1;
+}
+
+bool BindingModel::getExpressionStrings(const BindingProperty &bindingProperty, QString *sourceNode, QString *sourceProperty)
+{
+ //### todo we assume no expressions yet
+
+ const QString expression = bindingProperty.expression();
+
+ if (true) {
+ const QStringList stringList = expression.split(QLatin1String("."));
+
+ *sourceNode = stringList.constFirst();
+
+ QString propertyName;
+
+ for (int i=1; i < stringList.count(); i++) {
+ propertyName += stringList.at(i);
+ if (i != stringList.count() - 1)
+ propertyName += QLatin1String(".");
+ }
+ *sourceProperty = propertyName;
+ }
+ return true;
+}
+
+void BindingModel::updateDisplayRole(int row, int columns, const QString &string)
+{
+ QModelIndex modelIndex = index(row, columns);
+ if (data(modelIndex).toString() != string)
+ setData(modelIndex, string);
+}
+
+void BindingModel::handleDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight)
+{
+ if (!m_handleDataChanged)
+ return;
+
+ if (topLeft != bottomRight) {
+ qWarning() << "BindingModel::handleDataChanged multi edit?";
+ return;
+ }
+
+ m_lock = true;
+
+ int currentColumn = topLeft.column();
+ int currentRow = topLeft.row();
+
+ switch (currentColumn) {
+ case TargetModelNodeRow: {
+ //updating user data
+ } break;
+ case TargetPropertyNameRow: {
+ updatePropertyName(currentRow);
+ } break;
+ case SourceModelNodeRow: {
+ updateExpression(currentRow);
+ } break;
+ case SourcePropertyNameRow: {
+ updateExpression(currentRow);
+ } break;
+
+ default: qWarning() << "BindingModel::handleDataChanged column" << currentColumn;
+ }
+
+ m_lock = false;
+}
+
+void BindingModel::handleException()
+{
+ QMessageBox::warning(nullptr, tr("Error"), m_exceptionError);
+ resetModel();
+}
+
+} // namespace Internal
+
+} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/connectioneditor/bindingmodel.h b/src/plugins/qmldesigner/components/connectioneditor/bindingmodel.h
new file mode 100644
index 0000000000..480ba254ad
--- /dev/null
+++ b/src/plugins/qmldesigner/components/connectioneditor/bindingmodel.h
@@ -0,0 +1,93 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#pragma once
+
+#include <modelnode.h>
+#include <bindingproperty.h>
+#include <variantproperty.h>
+
+#include <QStandardItemModel>
+
+namespace QmlDesigner {
+
+namespace Internal {
+
+class ConnectionView;
+
+class BindingModel : public QStandardItemModel
+{
+ Q_OBJECT
+
+public:
+ enum ColumnRoles {
+ TargetModelNodeRow = 0,
+ TargetPropertyNameRow = 1,
+ SourceModelNodeRow = 2,
+ SourcePropertyNameRow = 3
+ };
+ BindingModel(ConnectionView *parent = nullptr);
+ void bindingChanged(const BindingProperty &bindingProperty);
+ void bindingRemoved(const BindingProperty &bindingProperty);
+ void selectionChanged(const QList<ModelNode> &selectedNodes);
+
+ ConnectionView *connectionView() const;
+ BindingProperty bindingPropertyForRow(int rowNumber) const;
+ QStringList possibleTargetProperties(const BindingProperty &bindingProperty) const;
+ QStringList possibleSourceProperties(const BindingProperty &bindingProperty) const;
+ void deleteBindindByRow(int rowNumber);
+ void addBindingForCurrentNode();
+ void resetModel();
+
+protected:
+ void addBindingProperty(const BindingProperty &property);
+ void updateBindingProperty(int rowNumber);
+ void addModelNode(const ModelNode &modelNode);
+ void updateExpression(int row);
+ void updatePropertyName(int rowNumber);
+ ModelNode getNodeByIdOrParent(const QString &id, const ModelNode &targetNode) const;
+ void updateCustomData(QStandardItem *item, const BindingProperty &bindingProperty);
+ int findRowForBinding(const BindingProperty &bindingProperty);
+
+ bool getExpressionStrings(const BindingProperty &bindingProperty, QString *sourceNode, QString *sourceProperty);
+
+ void updateDisplayRole(int row, int columns, const QString &string);
+
+private:
+ void handleDataChanged(const QModelIndex &topLeft, const QModelIndex& bottomRight);
+ void handleException();
+
+private:
+ QList<ModelNode> m_selectedModelNodes;
+ ConnectionView *m_connectionView;
+ bool m_lock = false;
+ bool m_handleDataChanged = false;
+ QString m_exceptionError;
+
+};
+
+} // namespace Internal
+
+} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/connectioneditor/connectioneditor.pri b/src/plugins/qmldesigner/components/connectioneditor/connectioneditor.pri
new file mode 100644
index 0000000000..5dfa3160ab
--- /dev/null
+++ b/src/plugins/qmldesigner/components/connectioneditor/connectioneditor.pri
@@ -0,0 +1,26 @@
+VPATH += $$PWD
+INCLUDEPATH += $$PWD
+
+HEADERS += delegates.h \
+ connectionview.h \
+ connectionviewwidget.h \
+ connectionmodel.h \
+ bindingmodel.h \
+ dynamicpropertiesmodel.h \
+ backendmodel.h \
+ $$PWD/addnewbackenddialog.h
+
+SOURCES += delegates.cpp \
+ connectionview.cpp \
+ connectionviewwidget.cpp \
+ connectionmodel.cpp \
+ bindingmodel.cpp \
+ dynamicpropertiesmodel.cpp \
+ backendmodel.cpp \
+ $$PWD/addnewbackenddialog.cpp
+
+FORMS += \
+ connectionviewwidget.ui \
+ $$PWD/addnewbackenddialog.ui
+
+RESOURCES += connectioneditor.qrc
diff --git a/src/plugins/qmldesigner/components/connectioneditor/connectioneditor.qrc b/src/plugins/qmldesigner/components/connectioneditor/connectioneditor.qrc
new file mode 100644
index 0000000000..a313b6648d
--- /dev/null
+++ b/src/plugins/qmldesigner/components/connectioneditor/connectioneditor.qrc
@@ -0,0 +1,5 @@
+<RCC>
+ <qresource prefix="/connectionview">
+ <file>stylesheet.css</file>
+ </qresource>
+</RCC>
diff --git a/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.cpp b/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.cpp
new file mode 100644
index 0000000000..80caf51ce0
--- /dev/null
+++ b/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.cpp
@@ -0,0 +1,357 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#include "connectionmodel.h"
+#include "connectionview.h"
+
+#include <bindingproperty.h>
+#include <variantproperty.h>
+#include <signalhandlerproperty.h>
+#include <rewritertransaction.h>
+#include <nodeabstractproperty.h>
+#include <exception.h>
+#include <nodemetainfo.h>
+
+#include <QStandardItemModel>
+#include <QMessageBox>
+#include <QTableView>
+#include <QTimer>
+
+namespace {
+
+QStringList propertyNameListToStringList(const QmlDesigner::PropertyNameList &propertyNameList)
+{
+ QStringList stringList;
+ foreach (QmlDesigner::PropertyName propertyName, propertyNameList) {
+ stringList << QString::fromUtf8(propertyName);
+ }
+ return stringList;
+}
+
+bool isConnection(const QmlDesigner::ModelNode &modelNode)
+{
+ return (modelNode.type() == "Connections"
+ || modelNode.type() == "QtQuick.Connections"
+ || modelNode.type() == "Qt.Connections");
+
+}
+
+} //namespace
+
+namespace QmlDesigner {
+
+namespace Internal {
+
+ConnectionModel::ConnectionModel(ConnectionView *parent)
+ : QStandardItemModel(parent)
+ , m_connectionView(parent)
+{
+ connect(this, &QStandardItemModel::dataChanged, this, &ConnectionModel::handleDataChanged);
+}
+
+void ConnectionModel::resetModel()
+{
+ beginResetModel();
+ clear();
+ setHorizontalHeaderLabels(QStringList({ tr("Target"), tr("Signal Handler"), tr("Action") }));
+
+ if (connectionView()->isAttached()) {
+ foreach (const ModelNode modelNode, connectionView()->allModelNodes())
+ addModelNode(modelNode);
+ }
+
+ const int columnWidthTarget = connectionView()->connectionTableView()->columnWidth(0);
+ connectionView()->connectionTableView()->setColumnWidth(0, columnWidthTarget - 80);
+
+ endResetModel();
+}
+
+SignalHandlerProperty ConnectionModel::signalHandlerPropertyForRow(int rowNumber) const
+{
+ const int internalId = data(index(rowNumber, TargetModelNodeRow), Qt::UserRole + 1).toInt();
+ const QString targetPropertyName = data(index(rowNumber, TargetModelNodeRow), Qt::UserRole + 2).toString();
+
+ ModelNode modelNode = connectionView()->modelNodeForInternalId(internalId);
+
+ if (modelNode.isValid())
+ return modelNode.signalHandlerProperty(targetPropertyName.toUtf8());
+
+ return SignalHandlerProperty();
+}
+
+void ConnectionModel::addModelNode(const ModelNode &modelNode)
+{
+ if (isConnection(modelNode))
+ addConnection(modelNode);
+}
+
+void ConnectionModel::addConnection(const ModelNode &modelNode)
+{
+ foreach (const AbstractProperty &property, modelNode.properties()) {
+ if (property.isSignalHandlerProperty() && property.name() != "target") {
+ addSignalHandler(property.toSignalHandlerProperty());
+ }
+ }
+}
+
+void ConnectionModel::addSignalHandler(const SignalHandlerProperty &signalHandlerProperty)
+{
+ QStandardItem *targetItem;
+ QStandardItem *signalItem;
+ QStandardItem *actionItem;
+
+ QString idLabel;
+
+ ModelNode connectionsModelNode = signalHandlerProperty.parentModelNode();
+
+ if (connectionsModelNode.bindingProperty("target").isValid()) {
+ idLabel =connectionsModelNode.bindingProperty("target").expression();
+ }
+
+ targetItem = new QStandardItem(idLabel);
+ updateCustomData(targetItem, signalHandlerProperty);
+ const QString propertyName = QString::fromUtf8(signalHandlerProperty.name());
+ const QString source = signalHandlerProperty.source();
+
+ signalItem = new QStandardItem(propertyName);
+ QList<QStandardItem*> items;
+
+ items.append(targetItem);
+ items.append(signalItem);
+
+ actionItem = new QStandardItem(source);
+
+ items.append(actionItem);
+
+ appendRow(items);
+}
+
+void ConnectionModel::removeModelNode(const ModelNode &modelNode)
+{
+ if (isConnection(modelNode))
+ removeConnection(modelNode);
+}
+
+void ConnectionModel::removeConnection(const ModelNode & /*modelNode*/)
+{
+ Q_ASSERT_X(false, "not implemented", Q_FUNC_INFO);
+}
+
+void ConnectionModel::updateSource(int row)
+{
+ SignalHandlerProperty signalHandlerProperty = signalHandlerPropertyForRow(row);
+
+ const QString sourceString = data(index(row, SourceRow)).toString();
+
+ RewriterTransaction transaction =
+ connectionView()->beginRewriterTransaction(QByteArrayLiteral("ConnectionModel::updateSource"));
+
+ try {
+ signalHandlerProperty.setSource(sourceString);
+ transaction.commit();
+ }
+ catch (Exception &e) {
+ m_exceptionError = e.description();
+ QTimer::singleShot(200, this, &ConnectionModel::handleException);
+ }
+
+}
+
+void ConnectionModel::updateSignalName(int rowNumber)
+{
+ SignalHandlerProperty signalHandlerProperty = signalHandlerPropertyForRow(rowNumber);
+ ModelNode connectionNode = signalHandlerProperty.parentModelNode();
+
+ const PropertyName newName = data(index(rowNumber, TargetPropertyNameRow)).toString().toUtf8();
+ if (!newName.isEmpty()) {
+ connectionView()->executeInTransaction("ConnectionModel::updateSignalName", [=, &connectionNode](){
+
+ const QString source = signalHandlerProperty.source();
+
+ connectionNode.signalHandlerProperty(newName).setSource(source);
+ connectionNode.removeProperty(signalHandlerProperty.name());
+ });
+
+ QStandardItem* idItem = item(rowNumber, 0);
+ SignalHandlerProperty newSignalHandlerProperty = connectionNode.signalHandlerProperty(newName);
+ updateCustomData(idItem, newSignalHandlerProperty);
+ } else {
+ qWarning() << "BindingModel::updatePropertyName invalid property name";
+ }
+}
+
+void ConnectionModel::updateTargetNode(int rowNumber)
+{
+ SignalHandlerProperty signalHandlerProperty = signalHandlerPropertyForRow(rowNumber);
+ const QString newTarget = data(index(rowNumber, TargetModelNodeRow)).toString();
+ ModelNode connectionNode = signalHandlerProperty.parentModelNode();
+
+ if (!newTarget.isEmpty()) {
+ connectionView()->executeInTransaction("ConnectionModel::updateTargetNode", [= ,&connectionNode](){
+ connectionNode.bindingProperty("target").setExpression(newTarget);
+ });
+
+ QStandardItem* idItem = item(rowNumber, 0);
+ updateCustomData(idItem, signalHandlerProperty);
+
+ } else {
+ qWarning() << "BindingModel::updatePropertyName invalid target id";
+ }
+}
+
+void ConnectionModel::updateCustomData(QStandardItem *item, const SignalHandlerProperty &signalHandlerProperty)
+{
+ item->setData(signalHandlerProperty.parentModelNode().internalId(), Qt::UserRole + 1);
+ item->setData(signalHandlerProperty.name(), Qt::UserRole + 2);
+}
+
+ModelNode ConnectionModel::getTargetNodeForConnection(const ModelNode &connection) const
+{
+ BindingProperty bindingProperty = connection.bindingProperty("target");
+
+ if (bindingProperty.isValid()) {
+ if (bindingProperty.expression() == QLatin1String("parent"))
+ return connection.parentProperty().parentModelNode();
+ return connectionView()->modelNodeForId(bindingProperty.expression());
+ }
+
+ return ModelNode();
+}
+
+void ConnectionModel::addConnection()
+{
+ ModelNode rootModelNode = connectionView()->rootModelNode();
+
+ if (rootModelNode.isValid() && rootModelNode.metaInfo().isValid()) {
+
+ NodeMetaInfo nodeMetaInfo = connectionView()->model()->metaInfo("QtQuick.Connections");
+
+ if (nodeMetaInfo.isValid()) {
+ connectionView()->executeInTransaction("ConnectionModel::addConnection", [=](){
+ ModelNode newNode = connectionView()->createModelNode("QtQuick.Connections",
+ nodeMetaInfo.majorVersion(),
+ nodeMetaInfo.minorVersion());
+
+ rootModelNode.nodeAbstractProperty(rootModelNode.metaInfo().defaultPropertyName()).reparentHere(newNode);
+ newNode.signalHandlerProperty("onClicked").setSource(QLatin1String("print(\"clicked\")"));
+
+ if (connectionView()->selectedModelNodes().count() == 1
+ && !connectionView()->selectedModelNodes().constFirst().id().isEmpty()) {
+ const ModelNode selectedNode = connectionView()->selectedModelNodes().constFirst();
+ newNode.bindingProperty("target").setExpression(selectedNode.id());
+ } else {
+ newNode.bindingProperty("target").setExpression(QLatin1String("parent"));
+ }
+ });
+ }
+ }
+}
+
+void ConnectionModel::bindingPropertyChanged(const BindingProperty &bindingProperty)
+{
+ if (isConnection(bindingProperty.parentModelNode()))
+ resetModel();
+}
+
+void ConnectionModel::variantPropertyChanged(const VariantProperty &variantProperty)
+{
+ if (isConnection(variantProperty.parentModelNode()))
+ resetModel();
+}
+
+void ConnectionModel::deleteConnectionByRow(int currentRow)
+{
+ signalHandlerPropertyForRow(currentRow).parentModelNode().destroy();
+}
+
+void ConnectionModel::handleException()
+{
+ QMessageBox::warning(nullptr, tr("Error"), m_exceptionError);
+ resetModel();
+}
+
+void ConnectionModel::handleDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight)
+{
+ if (topLeft != bottomRight) {
+ qWarning() << "ConnectionModel::handleDataChanged multi edit?";
+ return;
+ }
+
+ m_lock = true;
+
+ int currentColumn = topLeft.column();
+ int currentRow = topLeft.row();
+
+ switch (currentColumn) {
+ case TargetModelNodeRow: {
+ updateTargetNode(currentRow);
+ } break;
+ case TargetPropertyNameRow: {
+ updateSignalName(currentRow);
+ } break;
+ case SourceRow: {
+ updateSource(currentRow);
+ } break;
+
+ default: qWarning() << "ConnectionModel::handleDataChanged column" << currentColumn;
+ }
+
+ m_lock = false;
+}
+
+ConnectionView *ConnectionModel::connectionView() const
+{
+ return m_connectionView;
+}
+
+QStringList ConnectionModel::getSignalsForRow(int row) const
+{
+ QStringList stringList;
+ SignalHandlerProperty signalHandlerProperty = signalHandlerPropertyForRow(row);
+
+ if (signalHandlerProperty.isValid()) {
+ stringList.append(getPossibleSignalsForConnection(signalHandlerProperty.parentModelNode()));
+ }
+
+ return stringList;
+}
+
+QStringList ConnectionModel::getPossibleSignalsForConnection(const ModelNode &connection) const
+{
+ QStringList stringList;
+
+ if (connection.isValid()) {
+ ModelNode targetNode = getTargetNodeForConnection(connection);
+ if (targetNode.isValid() && targetNode.metaInfo().isValid()) {
+ stringList.append(propertyNameListToStringList(targetNode.metaInfo().signalNames()));
+ }
+ }
+
+ return stringList;
+}
+
+} // namespace Internal
+
+} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.h b/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.h
new file mode 100644
index 0000000000..2c66b2ef25
--- /dev/null
+++ b/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.h
@@ -0,0 +1,89 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#pragma once
+
+#include <QStandardItemModel>
+
+namespace QmlDesigner {
+
+class ModelNode;
+class BindingProperty;
+class SignalHandlerProperty;
+class VariantProperty;
+
+namespace Internal {
+
+class ConnectionView;
+
+class ConnectionModel : public QStandardItemModel
+{
+ Q_OBJECT
+public:
+ enum ColumnRoles {
+ TargetModelNodeRow = 0,
+ TargetPropertyNameRow = 1,
+ SourceRow = 2
+ };
+ ConnectionModel(ConnectionView *parent = nullptr);
+ void resetModel();
+ SignalHandlerProperty signalHandlerPropertyForRow(int rowNumber) const;
+ ConnectionView *connectionView() const;
+
+ QStringList getSignalsForRow(int row) const;
+ ModelNode getTargetNodeForConnection(const ModelNode &connection) const;
+
+ void addConnection();
+
+ void bindingPropertyChanged(const BindingProperty &bindingProperty);
+ void variantPropertyChanged(const VariantProperty &variantProperty);
+
+ void deleteConnectionByRow(int currentRow);
+
+protected:
+ void addModelNode(const ModelNode &modelNode);
+ void addConnection(const ModelNode &modelNode);
+ void addSignalHandler(const SignalHandlerProperty &bindingProperty);
+ void removeModelNode(const ModelNode &modelNode);
+ void removeConnection(const ModelNode &modelNode);
+ void updateSource(int row);
+ void updateSignalName(int rowNumber);
+ void updateTargetNode(int rowNumber);
+ void updateCustomData(QStandardItem *item, const SignalHandlerProperty &signalHandlerProperty);
+ QStringList getPossibleSignalsForConnection(const ModelNode &connection) const;
+
+private:
+ void handleDataChanged(const QModelIndex &topLeft, const QModelIndex& bottomRight);
+ void handleException();
+
+private:
+ ConnectionView *m_connectionView;
+ bool m_lock = false;
+ QString m_exceptionError;
+};
+
+} // namespace Internal
+
+} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/connectioneditor/connectionview.cpp b/src/plugins/qmldesigner/components/connectioneditor/connectionview.cpp
new file mode 100644
index 0000000000..9aef01259e
--- /dev/null
+++ b/src/plugins/qmldesigner/components/connectioneditor/connectionview.cpp
@@ -0,0 +1,222 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#include "connectionview.h"
+#include "connectionviewwidget.h"
+
+#include "backendmodel.h"
+#include "bindingmodel.h"
+#include "connectionmodel.h"
+#include "dynamicpropertiesmodel.h"
+
+#include <bindingproperty.h>
+#include <nodeabstractproperty.h>
+#include <variantproperty.h>
+
+namespace QmlDesigner {
+
+namespace Internal {
+
+ConnectionView::ConnectionView(QObject *parent) : AbstractView(parent),
+ m_connectionViewWidget(new ConnectionViewWidget()),
+ m_connectionModel(new ConnectionModel(this)),
+ m_bindingModel(new BindingModel(this)),
+ m_dynamicPropertiesModel(new DynamicPropertiesModel(this)),
+ m_backendModel(new BackendModel(this))
+{
+ connectionViewWidget()->setBindingModel(m_bindingModel);
+ connectionViewWidget()->setConnectionModel(m_connectionModel);
+ connectionViewWidget()->setDynamicPropertiesModel(m_dynamicPropertiesModel);
+ connectionViewWidget()->setBackendModel(m_backendModel);
+}
+
+ConnectionView::~ConnectionView() = default;
+
+void ConnectionView::modelAttached(Model *model)
+{
+ AbstractView::modelAttached(model);
+ bindingModel()->selectionChanged(QList<ModelNode>());
+ dynamicPropertiesModel()->selectionChanged(QList<ModelNode>());
+ connectionModel()->resetModel();
+ connectionViewWidget()->resetItemViews();
+ backendModel()->resetModel();
+}
+
+void ConnectionView::modelAboutToBeDetached(Model *model)
+{
+ AbstractView::modelAboutToBeDetached(model);
+ bindingModel()->selectionChanged(QList<ModelNode>());
+ dynamicPropertiesModel()->selectionChanged(QList<ModelNode>());
+ connectionModel()->resetModel();
+ connectionViewWidget()->resetItemViews();
+}
+
+void ConnectionView::nodeCreated(const ModelNode & /*createdNode*/)
+{
+//bindings
+ connectionModel()->resetModel();
+}
+
+void ConnectionView::nodeRemoved(const ModelNode & /*removedNode*/,
+ const NodeAbstractProperty & /*parentProperty*/,
+ AbstractView::PropertyChangeFlags /*propertyChange*/)
+{
+ connectionModel()->resetModel();
+}
+
+void ConnectionView::nodeReparented(const ModelNode & /*node*/, const NodeAbstractProperty & /*newPropertyParent*/,
+ const NodeAbstractProperty & /*oldPropertyParent*/, AbstractView::PropertyChangeFlags /*propertyChange*/)
+{
+ connectionModel()->resetModel();
+}
+
+void ConnectionView::nodeIdChanged(const ModelNode & /*node*/, const QString & /*newId*/, const QString & /*oldId*/)
+{
+ connectionModel()->resetModel();
+ bindingModel()->resetModel();
+ dynamicPropertiesModel()->resetModel();
+}
+
+void ConnectionView::propertiesAboutToBeRemoved(const QList<AbstractProperty> & propertyList)
+{
+ foreach (const AbstractProperty &property, propertyList) {
+ if (property.isBindingProperty()) {
+ bindingModel()->bindingRemoved(property.toBindingProperty());
+ dynamicPropertiesModel()->bindingRemoved(property.toBindingProperty());
+ } else if (property.isVariantProperty()) {
+ //### dynamicPropertiesModel->bindingRemoved(property.toVariantProperty());
+ }
+ }
+}
+
+void ConnectionView::variantPropertiesChanged(const QList<VariantProperty> &propertyList,
+ AbstractView::PropertyChangeFlags /*propertyChange*/)
+{
+ foreach (const VariantProperty &variantProperty, propertyList) {
+ if (variantProperty.isDynamic())
+ dynamicPropertiesModel()->variantPropertyChanged(variantProperty);
+ if (variantProperty.isDynamic() && variantProperty.parentModelNode().isRootNode())
+ backendModel()->resetModel();
+
+ connectionModel()->variantPropertyChanged(variantProperty);
+ }
+
+}
+
+void ConnectionView::bindingPropertiesChanged(const QList<BindingProperty> &propertyList,
+ AbstractView::PropertyChangeFlags /*propertyChange*/)
+{
+ foreach (const BindingProperty &bindingProperty, propertyList) {
+ bindingModel()->bindingChanged(bindingProperty);
+ if (bindingProperty.isDynamic())
+ dynamicPropertiesModel()->bindingPropertyChanged(bindingProperty);
+ if (bindingProperty.isDynamic() && bindingProperty.parentModelNode().isRootNode())
+ backendModel()->resetModel();
+
+ connectionModel()->bindingPropertyChanged(bindingProperty);
+ }
+}
+
+void ConnectionView::selectedNodesChanged(const QList<ModelNode> & selectedNodeList,
+ const QList<ModelNode> & /*lastSelectedNodeList*/)
+{
+ bindingModel()->selectionChanged(selectedNodeList);
+ dynamicPropertiesModel()->selectionChanged(selectedNodeList);
+ connectionViewWidget()->bindingTableViewSelectionChanged(QModelIndex(), QModelIndex());
+ connectionViewWidget()->dynamicPropertiesTableViewSelectionChanged(QModelIndex(), QModelIndex());
+
+ if (connectionViewWidget()->currentTab() == ConnectionViewWidget::BindingTab
+ || connectionViewWidget()->currentTab() == ConnectionViewWidget::DynamicPropertiesTab)
+ emit connectionViewWidget()->setEnabledAddButton(selectedNodeList.count() == 1);
+}
+
+void ConnectionView::importsChanged(const QList<Import> & /*addedImports*/, const QList<Import> & /*removedImports*/)
+{
+ backendModel()->resetModel();
+}
+
+WidgetInfo ConnectionView::widgetInfo()
+{
+ return createWidgetInfo(m_connectionViewWidget.data(),
+ new WidgetInfo::ToolBarWidgetDefaultFactory<ConnectionViewWidget>(connectionViewWidget()),
+ QLatin1String("ConnectionView"),
+ WidgetInfo::LeftPane,
+ 0,
+ tr("Connection View"));
+}
+
+bool ConnectionView::hasWidget() const
+{
+ return true;
+}
+
+QTableView *ConnectionView::connectionTableView() const
+{
+ return connectionViewWidget()->connectionTableView();
+}
+
+QTableView *ConnectionView::bindingTableView() const
+{
+ return connectionViewWidget()->bindingTableView();
+}
+
+QTableView *ConnectionView::dynamicPropertiesTableView() const
+{
+ return connectionViewWidget()->dynamicPropertiesTableView();
+}
+
+QTableView *ConnectionView::backendView() const
+{
+ return connectionViewWidget()->backendView();
+}
+
+ConnectionViewWidget *ConnectionView::connectionViewWidget() const
+{
+ return m_connectionViewWidget.data();
+}
+
+ConnectionModel *ConnectionView::connectionModel() const
+{
+ return m_connectionModel;
+}
+
+BindingModel *ConnectionView::bindingModel() const
+{
+ return m_bindingModel;
+}
+
+DynamicPropertiesModel *ConnectionView::dynamicPropertiesModel() const
+{
+ return m_dynamicPropertiesModel;
+}
+
+BackendModel *ConnectionView::backendModel() const
+{
+ return m_backendModel;
+}
+
+} // namesapce Internal
+
+} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/connectioneditor/connectionview.h b/src/plugins/qmldesigner/components/connectioneditor/connectionview.h
new file mode 100644
index 0000000000..da7623375a
--- /dev/null
+++ b/src/plugins/qmldesigner/components/connectioneditor/connectionview.h
@@ -0,0 +1,100 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#pragma once
+
+#include <abstractview.h>
+#include <qmlitemnode.h>
+
+#include <QPointer>
+
+QT_BEGIN_NAMESPACE
+class QTableView;
+class QListView;
+QT_END_NAMESPACE
+
+namespace QmlDesigner {
+
+namespace Internal {
+
+class ConnectionViewWidget;
+class BindingModel;
+class ConnectionModel;
+class DynamicPropertiesModel;
+class BackendModel;
+
+class ConnectionView : public AbstractView
+{
+ Q_OBJECT
+
+public:
+ ConnectionView(QObject *parent = nullptr);
+ ~ConnectionView() override;
+
+ // AbstractView
+ void modelAttached(Model *model) override;
+ void modelAboutToBeDetached(Model *model) override;
+
+ void nodeCreated(const ModelNode &createdNode) override;
+ void nodeRemoved(const ModelNode &removedNode, const NodeAbstractProperty &parentProperty, PropertyChangeFlags propertyChange) override;
+ void nodeReparented(const ModelNode &node, const NodeAbstractProperty &newPropertyParent,
+ const NodeAbstractProperty &oldPropertyParent, AbstractView::PropertyChangeFlags propertyChange) override;
+ void nodeIdChanged(const ModelNode& node, const QString& newId, const QString& oldId) override;
+ void propertiesAboutToBeRemoved(const QList<AbstractProperty>& propertyList) override;
+ void variantPropertiesChanged(const QList<VariantProperty>& propertyList, PropertyChangeFlags propertyChange) override;
+ void bindingPropertiesChanged(const QList<BindingProperty>& propertyList, PropertyChangeFlags propertyChange) override;
+
+ void selectedNodesChanged(const QList<ModelNode> &selectedNodeList,
+ const QList<ModelNode> &lastSelectedNodeList) override;
+
+ void importsChanged(const QList<Import> &addedImports, const QList<Import> &removedImports) override;
+
+ WidgetInfo widgetInfo() override;
+ bool hasWidget() const override;
+
+ QTableView *connectionTableView() const;
+ QTableView *bindingTableView() const;
+ QTableView *dynamicPropertiesTableView() const;
+ QTableView *backendView() const;
+
+protected:
+ ConnectionViewWidget *connectionViewWidget() const;
+ ConnectionModel *connectionModel() const;
+ BindingModel *bindingModel() const;
+ DynamicPropertiesModel *dynamicPropertiesModel() const;
+ BackendModel *backendModel() const;
+
+
+private: //variables
+ QPointer<ConnectionViewWidget> m_connectionViewWidget;
+ ConnectionModel *m_connectionModel;
+ BindingModel *m_bindingModel;
+ DynamicPropertiesModel *m_dynamicPropertiesModel;
+ BackendModel *m_backendModel;
+};
+
+} // namespace Internal
+
+} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/connectioneditor/connectionviewwidget.cpp b/src/plugins/qmldesigner/components/connectioneditor/connectionviewwidget.cpp
new file mode 100644
index 0000000000..c9b1277ce4
--- /dev/null
+++ b/src/plugins/qmldesigner/components/connectioneditor/connectionviewwidget.cpp
@@ -0,0 +1,348 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#include "connectionviewwidget.h"
+#include "connectionview.h"
+#include "ui_connectionviewwidget.h"
+
+#include "delegates.h"
+#include "backendmodel.h"
+#include "bindingmodel.h"
+#include "connectionmodel.h"
+#include "dynamicpropertiesmodel.h"
+#include "theme.h"
+
+#include <designersettings.h>
+#include <qmldesignerplugin.h>
+
+#include <coreplugin/coreconstants.h>
+#include <utils/fileutils.h>
+#include <utils/utilsicons.h>
+
+#include <QToolButton>
+#include <QStyleFactory>
+
+namespace QmlDesigner {
+
+namespace Internal {
+
+ConnectionViewWidget::ConnectionViewWidget(QWidget *parent) :
+ QFrame(parent),
+ ui(new Ui::ConnectionViewWidget)
+{
+
+ setWindowTitle(tr("Connections", "Title of connection view"));
+ ui->setupUi(this);
+
+ QStyle *style = QStyleFactory::create("fusion");
+ setStyle(style);
+
+ setStyleSheet(Theme::replaceCssColors(QLatin1String(Utils::FileReader::fetchQrc(QLatin1String(":/connectionview/stylesheet.css")))));
+
+ //ui->tabWidget->tabBar()->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
+
+ ui->tabBar->setUsesScrollButtons(true);
+ ui->tabBar->setElideMode(Qt::ElideRight);
+
+ ui->tabBar->addTab(tr("Connections", "Title of connection view"));
+ ui->tabBar->addTab(tr("Bindings", "Title of connection view"));
+ ui->tabBar->addTab(tr("Properties", "Title of dynamic properties view"));
+
+ auto settings = QmlDesignerPlugin::instance()->settings();
+
+ if (!settings.value(DesignerSettingsKey::STANDALONE_MODE).toBool())
+ ui->tabBar->addTab(tr("Backends", "Title of dynamic properties view"));
+
+ ui->tabBar->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Fixed);
+
+ const QString themedScrollBarCss = Theme::replaceCssColors(
+ QLatin1String(Utils::FileReader::fetchQrc(QLatin1String(":/qmldesigner/scrollbar.css"))));
+
+ ui->connectionView->setStyleSheet(themedScrollBarCss);
+ ui->bindingView->setStyleSheet(themedScrollBarCss);
+ ui->dynamicPropertiesView->setStyleSheet(themedScrollBarCss);
+ ui->backendView->setStyleSheet(themedScrollBarCss);
+
+ connect(ui->tabBar, &QTabBar::currentChanged,
+ ui->stackedWidget, &QStackedWidget::setCurrentIndex);
+
+ connect(ui->tabBar, &QTabBar::currentChanged,
+ this, &ConnectionViewWidget::handleTabChanged);
+
+ ui->stackedWidget->setCurrentIndex(0);
+}
+
+ConnectionViewWidget::~ConnectionViewWidget()
+{
+ delete ui;
+}
+
+void ConnectionViewWidget::setBindingModel(BindingModel *model)
+{
+ ui->bindingView->setModel(model);
+ ui->bindingView->verticalHeader()->hide();
+ ui->bindingView->setSelectionMode(QAbstractItemView::SingleSelection);
+ ui->bindingView->setItemDelegate(new BindingDelegate);
+ connect(ui->bindingView->selectionModel(), &QItemSelectionModel::currentRowChanged,
+ this, &ConnectionViewWidget::bindingTableViewSelectionChanged);
+}
+
+void ConnectionViewWidget::setConnectionModel(ConnectionModel *model)
+{
+ ui->connectionView->setModel(model);
+ ui->connectionView->verticalHeader()->hide();
+ ui->connectionView->horizontalHeader()->setDefaultSectionSize(160);
+ ui->connectionView->setSelectionMode(QAbstractItemView::SingleSelection);
+ ui->connectionView->setItemDelegate(new ConnectionDelegate);
+ connect(ui->connectionView->selectionModel(), &QItemSelectionModel::currentRowChanged,
+ this, &ConnectionViewWidget::connectionTableViewSelectionChanged);
+
+}
+
+void ConnectionViewWidget::setDynamicPropertiesModel(DynamicPropertiesModel *model)
+{
+ ui->dynamicPropertiesView->setModel(model);
+ ui->dynamicPropertiesView->verticalHeader()->hide();
+ ui->dynamicPropertiesView->setSelectionMode(QAbstractItemView::SingleSelection);
+ ui->dynamicPropertiesView->setItemDelegate(new DynamicPropertiesDelegate);
+ connect(ui->dynamicPropertiesView->selectionModel(), &QItemSelectionModel::currentRowChanged,
+ this, &ConnectionViewWidget::dynamicPropertiesTableViewSelectionChanged);
+}
+
+void ConnectionViewWidget::setBackendModel(BackendModel *model)
+{
+ ui->backendView->setModel(model);
+ ui->backendView->verticalHeader()->hide();
+ ui->backendView->setSelectionMode(QAbstractItemView::SingleSelection);
+ ui->backendView->setItemDelegate(new BackendDelegate);
+ model->resetModel();
+ connect(ui->backendView->selectionModel(), &QItemSelectionModel::currentRowChanged,
+ this, &ConnectionViewWidget::backendTableViewSelectionChanged);
+}
+
+QList<QToolButton *> ConnectionViewWidget::createToolBarWidgets()
+{
+ QList<QToolButton *> buttons;
+
+ buttons << new QToolButton();
+ buttons.constLast()->setIcon(Utils::Icons::PLUS_TOOLBAR.icon());
+ buttons.constLast()->setToolTip(tr("Add binding or connection."));
+ connect(buttons.constLast(), &QAbstractButton::clicked, this, &ConnectionViewWidget::addButtonClicked);
+ connect(this, &ConnectionViewWidget::setEnabledAddButton, buttons.constLast(), &QWidget::setEnabled);
+
+ buttons << new QToolButton();
+ buttons.constLast()->setIcon(Utils::Icons::MINUS.icon());
+ buttons.constLast()->setToolTip(tr("Remove selected binding or connection."));
+ buttons.constLast()->setShortcut(QKeySequence(Qt::Key_Delete));
+ connect(buttons.constLast(), &QAbstractButton::clicked, this, &ConnectionViewWidget::removeButtonClicked);
+ connect(this, &ConnectionViewWidget::setEnabledRemoveButton, buttons.constLast(), &QWidget::setEnabled);
+
+ return buttons;
+}
+
+ConnectionViewWidget::TabStatus ConnectionViewWidget::currentTab() const
+{
+ switch (ui->stackedWidget->currentIndex()) {
+ case 0: return ConnectionTab;
+ case 1: return BindingTab;
+ case 2: return DynamicPropertiesTab;
+ case 3: return BackendTab;
+ default: return InvalidTab;
+ }
+}
+
+void ConnectionViewWidget::resetItemViews()
+{
+ if (currentTab() == ConnectionTab) {
+ ui->connectionView->selectionModel()->clear();
+
+ } else if (currentTab() == BindingTab) {
+ ui->bindingView->selectionModel()->clear();
+
+ } else if (currentTab() == DynamicPropertiesTab) {
+ ui->dynamicPropertiesView->selectionModel()->clear();
+ } else if (currentTab() == BackendTab) {
+ ui->backendView->selectionModel()->clear();
+ }
+ invalidateButtonStatus();
+}
+
+void ConnectionViewWidget::invalidateButtonStatus()
+{
+ if (currentTab() == ConnectionTab) {
+ emit setEnabledRemoveButton(ui->connectionView->selectionModel()->hasSelection());
+ emit setEnabledAddButton(true);
+ } else if (currentTab() == BindingTab) {
+ emit setEnabledRemoveButton(ui->bindingView->selectionModel()->hasSelection());
+ auto bindingModel = qobject_cast<BindingModel*>(ui->bindingView->model());
+ emit setEnabledAddButton(bindingModel->connectionView()->model() &&
+ bindingModel->connectionView()->selectedModelNodes().count() == 1);
+
+ } else if (currentTab() == DynamicPropertiesTab) {
+ emit setEnabledRemoveButton(ui->dynamicPropertiesView->selectionModel()->hasSelection());
+ auto dynamicPropertiesModel = qobject_cast<DynamicPropertiesModel*>(ui->dynamicPropertiesView->model());
+ emit setEnabledAddButton(dynamicPropertiesModel->connectionView()->model() &&
+ dynamicPropertiesModel->connectionView()->selectedModelNodes().count() == 1);
+ } else if (currentTab() == BackendTab) {
+ emit setEnabledAddButton(true);
+ emit setEnabledRemoveButton(ui->backendView->selectionModel()->hasSelection());
+ }
+}
+
+QTableView *ConnectionViewWidget::connectionTableView() const
+{
+ return ui->connectionView;
+}
+
+QTableView *ConnectionViewWidget::bindingTableView() const
+{
+ return ui->bindingView;
+}
+
+QTableView *ConnectionViewWidget::dynamicPropertiesTableView() const
+{
+ return ui->dynamicPropertiesView;
+}
+
+QTableView *ConnectionViewWidget::backendView() const
+{
+ return ui->backendView;
+}
+
+void ConnectionViewWidget::handleTabChanged(int)
+{
+ invalidateButtonStatus();
+}
+
+void ConnectionViewWidget::removeButtonClicked()
+{
+ if (currentTab() == ConnectionTab) {
+ if (ui->connectionView->selectionModel()->selectedRows().isEmpty())
+ return;
+ int currentRow = ui->connectionView->selectionModel()->selectedRows().constFirst().row();
+ auto connectionModel = qobject_cast<ConnectionModel*>(ui->connectionView->model());
+ if (connectionModel) {
+ connectionModel->deleteConnectionByRow(currentRow);
+ }
+ } else if (currentTab() == BindingTab) {
+ if (ui->bindingView->selectionModel()->selectedRows().isEmpty())
+ return;
+ int currentRow = ui->bindingView->selectionModel()->selectedRows().constFirst().row();
+ auto bindingModel = qobject_cast<BindingModel*>(ui->bindingView->model());
+ if (bindingModel) {
+ bindingModel->deleteBindindByRow(currentRow);
+ }
+ } else if (currentTab() == DynamicPropertiesTab) {
+ if (ui->dynamicPropertiesView->selectionModel()->selectedRows().isEmpty())
+ return;
+ int currentRow = ui->dynamicPropertiesView->selectionModel()->selectedRows().constFirst().row();
+ auto dynamicPropertiesModel = qobject_cast<DynamicPropertiesModel*>(ui->dynamicPropertiesView->model());
+ if (dynamicPropertiesModel)
+ dynamicPropertiesModel->deleteDynamicPropertyByRow(currentRow);
+ } else if (currentTab() == BackendTab) {
+ int currentRow = ui->backendView->selectionModel()->selectedRows().constFirst().row();
+ auto backendModel = qobject_cast<BackendModel*>(ui->backendView->model());
+ if (backendModel)
+ backendModel->deletePropertyByRow(currentRow);
+ }
+
+ invalidateButtonStatus();
+}
+
+void ConnectionViewWidget::addButtonClicked()
+{
+
+ if (currentTab() == ConnectionTab) {
+ auto connectionModel = qobject_cast<ConnectionModel*>(ui->connectionView->model());
+ if (connectionModel) {
+ connectionModel->addConnection();
+ }
+ } else if (currentTab() == BindingTab) {
+ auto bindingModel = qobject_cast<BindingModel*>(ui->bindingView->model());
+ if (bindingModel) {
+ bindingModel->addBindingForCurrentNode();
+ }
+
+ } else if (currentTab() == DynamicPropertiesTab) {
+ auto dynamicPropertiesModel = qobject_cast<DynamicPropertiesModel*>(ui->dynamicPropertiesView->model());
+ if (dynamicPropertiesModel)
+ dynamicPropertiesModel->addDynamicPropertyForCurrentNode();
+ } else if (currentTab() == BackendTab) {
+ auto backendModel = qobject_cast<BackendModel*>(ui->backendView->model());
+ if (backendModel)
+ backendModel->addNewBackend();
+ }
+
+ invalidateButtonStatus();
+}
+
+void ConnectionViewWidget::bindingTableViewSelectionChanged(const QModelIndex &current, const QModelIndex & /*previous*/)
+{
+ if (currentTab() == BindingTab) {
+ if (current.isValid()) {
+ emit setEnabledRemoveButton(true);
+ } else {
+ emit setEnabledRemoveButton(false);
+ }
+ }
+}
+
+void ConnectionViewWidget::connectionTableViewSelectionChanged(const QModelIndex &current, const QModelIndex & /*previous*/)
+{
+ if (currentTab() == ConnectionTab) {
+ if (current.isValid()) {
+ emit setEnabledRemoveButton(true);
+ } else {
+ emit setEnabledRemoveButton(false);
+ }
+ }
+}
+
+void ConnectionViewWidget::dynamicPropertiesTableViewSelectionChanged(const QModelIndex &current, const QModelIndex & /*previous*/)
+{
+ if (currentTab() == DynamicPropertiesTab) {
+ if (current.isValid()) {
+ emit setEnabledRemoveButton(true);
+ } else {
+ emit setEnabledRemoveButton(false);
+ }
+ }
+}
+
+void ConnectionViewWidget::backendTableViewSelectionChanged(const QModelIndex &current, const QModelIndex & /*revious*/)
+{
+ if (currentTab() == BackendTab) {
+ if (current.isValid()) {
+ emit setEnabledRemoveButton(true);
+ } else {
+ emit setEnabledRemoveButton(false);
+ }
+ }
+
+}
+
+} // namespace Internal
+
+} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/connectioneditor/connectionviewwidget.h b/src/plugins/qmldesigner/components/connectioneditor/connectionviewwidget.h
new file mode 100644
index 0000000000..2bcc3932c1
--- /dev/null
+++ b/src/plugins/qmldesigner/components/connectioneditor/connectionviewwidget.h
@@ -0,0 +1,102 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#pragma once
+
+#include <QFrame>
+#include <QAbstractItemView>
+
+QT_BEGIN_NAMESPACE
+class QToolButton;
+class QTableView;
+class QListView;
+QT_END_NAMESPACE
+
+namespace QmlDesigner {
+
+namespace Ui { class ConnectionViewWidget; }
+
+namespace Internal {
+
+class BindingModel;
+class ConnectionModel;
+class DynamicPropertiesModel;
+class BackendModel;
+
+class ConnectionViewWidget : public QFrame
+{
+ Q_OBJECT
+
+public:
+
+ enum TabStatus {
+ ConnectionTab,
+ BindingTab,
+ DynamicPropertiesTab,
+ BackendTab,
+ InvalidTab
+ };
+
+ explicit ConnectionViewWidget(QWidget *parent = nullptr);
+ ~ConnectionViewWidget() override;
+
+ void setBindingModel(BindingModel *model);
+ void setConnectionModel(ConnectionModel *model);
+ void setDynamicPropertiesModel(DynamicPropertiesModel *model);
+ void setBackendModel(BackendModel *model);
+
+ QList<QToolButton*> createToolBarWidgets();
+
+ TabStatus currentTab() const;
+
+ void resetItemViews();
+ void invalidateButtonStatus();
+
+ QTableView *connectionTableView() const;
+ QTableView *bindingTableView() const;
+ QTableView *dynamicPropertiesTableView() const;
+ QTableView *backendView() const;
+
+ void bindingTableViewSelectionChanged(const QModelIndex &current, const QModelIndex &previous);
+ void connectionTableViewSelectionChanged(const QModelIndex &current, const QModelIndex &previous);
+ void dynamicPropertiesTableViewSelectionChanged(const QModelIndex &current, const QModelIndex &previous);
+ void backendTableViewSelectionChanged(const QModelIndex &current, const QModelIndex &previous);
+
+signals:
+ void setEnabledAddButton(bool enabled);
+ void setEnabledRemoveButton(bool enabled);
+
+private:
+ void handleTabChanged(int i);
+ void removeButtonClicked();
+ void addButtonClicked();
+
+private:
+ Ui::ConnectionViewWidget *ui;
+};
+
+} // namespace Internal
+
+} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/connectioneditor/connectionviewwidget.ui b/src/plugins/qmldesigner/components/connectioneditor/connectionviewwidget.ui
new file mode 100644
index 0000000000..18df078ec6
--- /dev/null
+++ b/src/plugins/qmldesigner/components/connectioneditor/connectionviewwidget.ui
@@ -0,0 +1,273 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>QmlDesigner::ConnectionViewWidget</class>
+ <widget class="QWidget" name="QmlDesigner::ConnectionViewWidget">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>994</width>
+ <height>611</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>Connections</string>
+ </property>
+ <layout class="QGridLayout" name="gridLayout">
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <item row="1" column="0">
+ <widget class="QWidget" name="widgetSpacer" native="true">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>0</width>
+ <height>4</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>16777215</width>
+ <height>4</height>
+ </size>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="0">
+ <widget class="QTabBar" name="tabBar" native="true"/>
+ </item>
+ <item row="2" column="0">
+ <widget class="QStackedWidget" name="stackedWidget">
+ <property name="currentIndex">
+ <number>3</number>
+ </property>
+ <widget class="QWidget" name="connectionViewPage">
+ <layout class="QGridLayout" name="gridLayout_3">
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item row="3" column="0" colspan="5">
+ <widget class="QTableView" name="connectionView">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="alternatingRowColors">
+ <bool>true</bool>
+ </property>
+ <property name="selectionMode">
+ <enum>QAbstractItemView::SingleSelection</enum>
+ </property>
+ <property name="selectionBehavior">
+ <enum>QAbstractItemView::SelectRows</enum>
+ </property>
+ <property name="showGrid">
+ <bool>false</bool>
+ </property>
+ <property name="cornerButtonEnabled">
+ <bool>false</bool>
+ </property>
+ <attribute name="horizontalHeaderHighlightSections">
+ <bool>false</bool>
+ </attribute>
+ <attribute name="horizontalHeaderStretchLastSection">
+ <bool>true</bool>
+ </attribute>
+ <attribute name="verticalHeaderVisible">
+ <bool>false</bool>
+ </attribute>
+ <attribute name="verticalHeaderHighlightSections">
+ <bool>false</bool>
+ </attribute>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <widget class="QWidget" name="bindingViewPage">
+ <layout class="QGridLayout" name="gridLayout_2">
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item row="2" column="0" colspan="3">
+ <widget class="QTableView" name="bindingView">
+ <property name="alternatingRowColors">
+ <bool>true</bool>
+ </property>
+ <property name="selectionMode">
+ <enum>QAbstractItemView::SingleSelection</enum>
+ </property>
+ <property name="selectionBehavior">
+ <enum>QAbstractItemView::SelectRows</enum>
+ </property>
+ <property name="showGrid">
+ <bool>false</bool>
+ </property>
+ <property name="cornerButtonEnabled">
+ <bool>false</bool>
+ </property>
+ <attribute name="horizontalHeaderHighlightSections">
+ <bool>false</bool>
+ </attribute>
+ <attribute name="horizontalHeaderStretchLastSection">
+ <bool>true</bool>
+ </attribute>
+ <attribute name="verticalHeaderVisible">
+ <bool>false</bool>
+ </attribute>
+ <attribute name="verticalHeaderHighlightSections">
+ <bool>false</bool>
+ </attribute>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <widget class="QWidget" name="dynamicPropertiesPage">
+ <layout class="QGridLayout" name="gridLayout_4">
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item row="0" column="0">
+ <widget class="QTableView" name="dynamicPropertiesView">
+ <property name="alternatingRowColors">
+ <bool>true</bool>
+ </property>
+ <property name="selectionMode">
+ <enum>QAbstractItemView::SingleSelection</enum>
+ </property>
+ <property name="selectionBehavior">
+ <enum>QAbstractItemView::SelectRows</enum>
+ </property>
+ <property name="showGrid">
+ <bool>false</bool>
+ </property>
+ <property name="cornerButtonEnabled">
+ <bool>false</bool>
+ </property>
+ <attribute name="horizontalHeaderHighlightSections">
+ <bool>false</bool>
+ </attribute>
+ <attribute name="horizontalHeaderStretchLastSection">
+ <bool>true</bool>
+ </attribute>
+ <attribute name="verticalHeaderVisible">
+ <bool>false</bool>
+ </attribute>
+ <attribute name="verticalHeaderHighlightSections">
+ <bool>false</bool>
+ </attribute>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <widget class="QWidget" name="backendViewPage">
+ <layout class="QGridLayout" name="gridLayout_5">
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item row="0" column="0">
+ <widget class="QTableView" name="backendView">
+ <property name="alternatingRowColors">
+ <bool>true</bool>
+ </property>
+ <property name="selectionMode">
+ <enum>QAbstractItemView::SingleSelection</enum>
+ </property>
+ <property name="selectionBehavior">
+ <enum>QAbstractItemView::SelectRows</enum>
+ </property>
+ <property name="showGrid">
+ <bool>false</bool>
+ </property>
+ <property name="cornerButtonEnabled">
+ <bool>false</bool>
+ </property>
+ <attribute name="horizontalHeaderHighlightSections">
+ <bool>false</bool>
+ </attribute>
+ <attribute name="horizontalHeaderStretchLastSection">
+ <bool>true</bool>
+ </attribute>
+ <attribute name="verticalHeaderVisible">
+ <bool>false</bool>
+ </attribute>
+ <attribute name="verticalHeaderHighlightSections">
+ <bool>false</bool>
+ </attribute>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </widget>
+ </item>
+ </layout>
+ <zorder>stackedWidget</zorder>
+ <zorder>tabBar</zorder>
+ <zorder>widgetSpacer</zorder>
+ </widget>
+ <customwidgets>
+ <customwidget>
+ <class>QTabBar</class>
+ <extends>QWidget</extends>
+ <header location="global">qtabbar.h</header>
+ <container>1</container>
+ </customwidget>
+ </customwidgets>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/src/plugins/qmldesigner/components/connectioneditor/delegates.cpp b/src/plugins/qmldesigner/components/connectioneditor/delegates.cpp
new file mode 100644
index 0000000000..555f448858
--- /dev/null
+++ b/src/plugins/qmldesigner/components/connectioneditor/delegates.cpp
@@ -0,0 +1,361 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#include "delegates.h"
+
+#include "backendmodel.h"
+#include "connectionmodel.h"
+#include "bindingmodel.h"
+#include "dynamicpropertiesmodel.h"
+#include "connectionview.h"
+
+#include <bindingproperty.h>
+
+#include <utils/qtcassert.h>
+
+#include <QStyleFactory>
+#include <QItemEditorFactory>
+#include <QDebug>
+
+namespace QmlDesigner {
+
+namespace Internal {
+
+QStringList prependOnForSignalHandler(const QStringList &signalNames)
+{
+ QStringList signalHandlerNames;
+ foreach (const QString &signalName, signalNames) {
+ QString signalHandlerName = signalName;
+ if (!signalHandlerName.isEmpty()) {
+ QChar firstChar = signalHandlerName.at(0).toUpper();
+ signalHandlerName[0] = firstChar;
+ signalHandlerName.prepend(QLatin1String("on"));
+ signalHandlerNames.append(signalHandlerName);
+ }
+ }
+ return signalHandlerNames;
+}
+
+PropertiesComboBox::PropertiesComboBox(QWidget *parent) : QComboBox(parent)
+{
+ setEditable(true);
+ setValidator(new QRegularExpressionValidator(QRegularExpression(QLatin1String("[a-z|A-Z|0-9|._-]*")), this));
+}
+
+QString PropertiesComboBox::text() const
+{
+ return currentText();
+}
+
+void PropertiesComboBox::setText(const QString &text)
+{
+ setEditText(text);
+}
+
+void PropertiesComboBox::disableValidator()
+{
+ setValidator(nullptr);
+}
+
+ConnectionComboBox::ConnectionComboBox(QWidget *parent) : PropertiesComboBox(parent)
+{
+}
+
+QString ConnectionComboBox::text() const
+{
+ int index = findText(currentText());
+ if (index > -1) {
+ QVariant variantData = itemData(index);
+ if (variantData.isValid())
+ return variantData.toString();
+ }
+
+ return currentText();
+}
+
+ConnectionEditorDelegate::ConnectionEditorDelegate(QWidget *parent)
+ : QStyledItemDelegate(parent)
+{
+}
+
+void ConnectionEditorDelegate::paint(QPainter *painter,
+ const QStyleOptionViewItem &option, const QModelIndex &index) const
+{
+ QStyleOptionViewItem opt = option;
+ opt.state &= ~QStyle::State_HasFocus;
+ QStyledItemDelegate::paint(painter, opt, index);
+}
+
+BindingDelegate::BindingDelegate(QWidget *parent) : ConnectionEditorDelegate(parent)
+{
+ static QItemEditorFactory *factory = nullptr;
+ if (factory == nullptr) {
+ factory = new QItemEditorFactory;
+ QItemEditorCreatorBase *creator
+ = new QItemEditorCreator<PropertiesComboBox>("text");
+ factory->registerEditor(QVariant::String, creator);
+ }
+
+ setItemEditorFactory(factory);
+}
+
+QWidget *BindingDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const
+{
+ QWidget *widget = QStyledItemDelegate::createEditor(parent, option, index);
+
+ const auto model = qobject_cast<const BindingModel*>(index.model());
+ if (!model) {
+ qWarning() << "BindingDelegate::createEditor no model";
+ return widget;
+ }
+ if (!model->connectionView()) {
+ qWarning() << "BindingDelegate::createEditor no connection view";
+ return widget;
+ }
+
+ model->connectionView()->allModelNodes();
+
+ auto bindingComboBox = qobject_cast<PropertiesComboBox*>(widget);
+ if (!bindingComboBox) {
+ qWarning() << "BindingDelegate::createEditor no bindingComboBox";
+ return widget;
+ }
+
+ BindingProperty bindingProperty = model->bindingPropertyForRow(index.row());
+
+ switch (index.column()) {
+ case BindingModel::TargetModelNodeRow:
+ return nullptr; //no editor
+ case BindingModel::TargetPropertyNameRow: {
+ bindingComboBox->addItems(model->possibleTargetProperties(bindingProperty));
+ } break;
+ case BindingModel::SourceModelNodeRow: {
+ foreach (const ModelNode &modelNode, model->connectionView()->allModelNodes()) {
+ if (!modelNode.id().isEmpty()) {
+ bindingComboBox->addItem(modelNode.id());
+ }
+ }
+ if (!bindingProperty.parentModelNode().isRootNode())
+ bindingComboBox->addItem(QLatin1String("parent"));
+ } break;
+ case BindingModel::SourcePropertyNameRow: {
+ bindingComboBox->addItems(model->possibleSourceProperties(bindingProperty));
+ bindingComboBox->disableValidator();
+ } break;
+ default: qWarning() << "BindingDelegate::createEditor column" << index.column();
+ }
+
+ connect(bindingComboBox, QOverload<int>::of(&QComboBox::activated), this, [=]() {
+ auto delegate = const_cast<BindingDelegate*>(this);
+ emit delegate->commitData(bindingComboBox);
+ });
+
+ return widget;
+}
+
+DynamicPropertiesDelegate::DynamicPropertiesDelegate(QWidget *parent) : ConnectionEditorDelegate(parent)
+{
+// static QItemEditorFactory *factory = 0;
+// if (factory == 0) {
+// factory = new QItemEditorFactory;
+// QItemEditorCreatorBase *creator
+// = new QItemEditorCreator<DynamicPropertiesComboBox>("text");
+// factory->registerEditor(QVariant::String, creator);
+// }
+
+// setItemEditorFactory(factory);
+}
+
+QWidget *DynamicPropertiesDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const
+{
+ QWidget *widget = QStyledItemDelegate::createEditor(parent, option, index);
+
+ const auto model = qobject_cast<const DynamicPropertiesModel*>(index.model());
+ if (!model) {
+ qWarning() << "BindingDelegate::createEditor no model";
+ return widget;
+ }
+
+ if (!model->connectionView()) {
+ qWarning() << "BindingDelegate::createEditor no connection view";
+ return widget;
+ }
+ model->connectionView()->allModelNodes();
+
+ switch (index.column()) {
+ case DynamicPropertiesModel::TargetModelNodeRow: {
+ return nullptr; //no editor
+ };
+ case DynamicPropertiesModel::PropertyNameRow: {
+ return QStyledItemDelegate::createEditor(parent, option, index);
+ };
+ case DynamicPropertiesModel::PropertyTypeRow: {
+
+ auto dynamicPropertiesComboBox = new PropertiesComboBox(parent);
+ connect(dynamicPropertiesComboBox, QOverload<int>::of(&QComboBox::activated), this, [=]() {
+ auto delegate = const_cast<DynamicPropertiesDelegate*>(this);
+ emit delegate->commitData(dynamicPropertiesComboBox);
+ });
+
+ dynamicPropertiesComboBox->addItem(QLatin1String("alias"));
+ //dynamicPropertiesComboBox->addItem(QLatin1String("Item"));
+ dynamicPropertiesComboBox->addItem(QLatin1String("real"));
+ dynamicPropertiesComboBox->addItem(QLatin1String("int"));
+ dynamicPropertiesComboBox->addItem(QLatin1String("string"));
+ dynamicPropertiesComboBox->addItem(QLatin1String("bool"));
+ dynamicPropertiesComboBox->addItem(QLatin1String("url"));
+ dynamicPropertiesComboBox->addItem(QLatin1String("color"));
+ dynamicPropertiesComboBox->addItem(QLatin1String("variant"));
+ return dynamicPropertiesComboBox;
+ };
+ case DynamicPropertiesModel::PropertyValueRow: {
+ return QStyledItemDelegate::createEditor(parent, option, index);
+ };
+ default: qWarning() << "BindingDelegate::createEditor column" << index.column();
+ }
+
+ return nullptr;
+}
+
+ConnectionDelegate::ConnectionDelegate(QWidget *parent) : ConnectionEditorDelegate(parent)
+{
+ static QItemEditorFactory *factory = nullptr;
+ if (factory == nullptr) {
+ factory = new QItemEditorFactory;
+ QItemEditorCreatorBase *creator
+ = new QItemEditorCreator<ConnectionComboBox>("text");
+ factory->registerEditor(QVariant::String, creator);
+ }
+
+ setItemEditorFactory(factory);
+}
+
+QWidget *ConnectionDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const
+{
+
+ QWidget *widget = QStyledItemDelegate::createEditor(parent, option, index);
+
+ const auto connectionModel = qobject_cast<const ConnectionModel*>(index.model());
+
+ auto connectionComboBox = qobject_cast<ConnectionComboBox*>(widget);
+
+ if (!connectionModel) {
+ qWarning() << "ConnectionDelegate::createEditor no model";
+ return widget;
+ }
+
+ if (!connectionModel->connectionView()) {
+ qWarning() << "ConnectionDelegate::createEditor no connection view";
+ return widget;
+ }
+
+ if (!connectionComboBox) {
+ qWarning() << "ConnectionDelegate::createEditor no bindingComboBox";
+ return widget;
+ }
+
+ switch (index.column()) {
+ case ConnectionModel::TargetModelNodeRow: {
+ foreach (const ModelNode &modelNode, connectionModel->connectionView()->allModelNodes()) {
+ if (!modelNode.id().isEmpty()) {
+ connectionComboBox->addItem(modelNode.id());
+ }
+ }
+ } break;
+ case ConnectionModel::TargetPropertyNameRow: {
+ connectionComboBox->addItems(prependOnForSignalHandler(connectionModel->getSignalsForRow(index.row())));
+ } break;
+ case ConnectionModel::SourceRow: {
+ ModelNode rootModelNode = connectionModel->connectionView()->rootModelNode();
+ if (QmlItemNode::isValidQmlItemNode(rootModelNode) && !rootModelNode.id().isEmpty()) {
+
+ QString itemText = tr("Change to default state");
+ QString source = QString::fromLatin1("{ %1.state = \"\" }").arg(rootModelNode.id());
+ connectionComboBox->addItem(itemText, source);
+ connectionComboBox->disableValidator();
+
+ foreach (const QmlModelState &state, QmlItemNode(rootModelNode).states().allStates()) {
+ QString itemText = tr("Change state to %1").arg(state.name());
+ QString source = QString::fromLatin1("{ %1.state = \"%2\" }").arg(rootModelNode.id()).arg(state.name());
+ connectionComboBox->addItem(itemText, source);
+ }
+ }
+ connectionComboBox->disableValidator();
+ } break;
+
+ default: qWarning() << "ConnectionDelegate::createEditor column" << index.column();
+ }
+
+ connect(connectionComboBox, QOverload<int>::of(&QComboBox::activated), this, [=]() {
+ auto delegate = const_cast<ConnectionDelegate*>(this);
+ emit delegate->commitData(connectionComboBox);
+ });
+
+ return widget;
+}
+
+BackendDelegate::BackendDelegate(QWidget *parent) : ConnectionEditorDelegate(parent)
+{
+}
+
+QWidget *BackendDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const
+{
+ const auto model = qobject_cast<const BackendModel*>(index.model());
+
+ model->connectionView()->allModelNodes();
+
+ QWidget *widget = QStyledItemDelegate::createEditor(parent, option, index);
+
+ QTC_ASSERT(model, return widget);
+ QTC_ASSERT(model->connectionView(), return widget);
+
+ switch (index.column()) {
+ case BackendModel::TypeNameColumn: {
+ auto backendComboBox = new PropertiesComboBox(parent);
+ backendComboBox->addItems(model->possibleCppTypes());
+ connect(backendComboBox, QOverload<int>::of(&QComboBox::activated), this, [=]() {
+ auto delegate = const_cast<BackendDelegate*>(this);
+ emit delegate->commitData(backendComboBox);
+ });
+ return backendComboBox;
+ };
+ case BackendModel::PropertyNameColumn: {
+ return widget;
+ };
+ case BackendModel::IsSingletonColumn: {
+ return nullptr; //no editor
+ };
+ case BackendModel::IsLocalColumn: {
+ return nullptr; //no editor
+ };
+ default: qWarning() << "BackendDelegate::createEditor column" << index.column();
+ }
+
+ return widget;
+}
+
+} // namesapce Internal
+
+} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/connectioneditor/delegates.h b/src/plugins/qmldesigner/components/connectioneditor/delegates.h
new file mode 100644
index 0000000000..b9792293ac
--- /dev/null
+++ b/src/plugins/qmldesigner/components/connectioneditor/delegates.h
@@ -0,0 +1,104 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#pragma once
+
+#include <QStandardItem>
+#include <QStyledItemDelegate>
+#include <QComboBox>
+
+namespace QmlDesigner {
+
+namespace Internal {
+
+class PropertiesComboBox : public QComboBox
+{
+ Q_OBJECT
+ Q_PROPERTY(QString text READ text WRITE setText USER true)
+public:
+ PropertiesComboBox(QWidget *parent = nullptr);
+
+ virtual QString text() const;
+ void setText(const QString &text);
+ void disableValidator();
+};
+
+class ConnectionComboBox : public PropertiesComboBox
+{
+ Q_OBJECT
+public:
+ ConnectionComboBox(QWidget *parent = nullptr);
+ QString text() const override;
+};
+
+class ConnectionEditorDelegate : public QStyledItemDelegate
+{
+public:
+ ConnectionEditorDelegate(QWidget *parent = nullptr);
+ void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override;
+};
+
+class BindingDelegate : public ConnectionEditorDelegate
+{
+public:
+ BindingDelegate(QWidget *parent = nullptr);
+ QWidget *createEditor(QWidget *parent,
+ const QStyleOptionViewItem &option,
+ const QModelIndex &index) const override;
+};
+
+class DynamicPropertiesDelegate : public ConnectionEditorDelegate
+{
+public:
+ DynamicPropertiesDelegate(QWidget *parent = nullptr);
+ QWidget *createEditor(QWidget *parent,
+ const QStyleOptionViewItem &option,
+ const QModelIndex &index) const override;
+};
+
+
+class ConnectionDelegate : public ConnectionEditorDelegate
+{
+ Q_OBJECT
+public:
+ ConnectionDelegate(QWidget *parent = nullptr);
+ QWidget *createEditor(QWidget *parent,
+ const QStyleOptionViewItem &option,
+ const QModelIndex &index) const override;
+};
+
+class BackendDelegate : public ConnectionEditorDelegate
+{
+ Q_OBJECT
+public:
+ BackendDelegate(QWidget *parent = nullptr);
+ QWidget *createEditor(QWidget *parent,
+ const QStyleOptionViewItem &option,
+ const QModelIndex &index) const override;
+};
+
+} // namespace Internal
+
+} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/connectioneditor/dynamicpropertiesmodel.cpp b/src/plugins/qmldesigner/components/connectioneditor/dynamicpropertiesmodel.cpp
new file mode 100644
index 0000000000..0a08e5c883
--- /dev/null
+++ b/src/plugins/qmldesigner/components/connectioneditor/dynamicpropertiesmodel.cpp
@@ -0,0 +1,677 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#include "dynamicpropertiesmodel.h"
+
+#include "connectionview.h"
+
+#include <nodemetainfo.h>
+#include <nodeproperty.h>
+#include <variantproperty.h>
+#include <bindingproperty.h>
+#include <rewritingexception.h>
+#include <rewritertransaction.h>
+
+#include <utils/fileutils.h>
+
+#include <QMessageBox>
+#include <QTimer>
+#include <QUrl>
+
+namespace {
+
+bool compareVariantProperties(const QmlDesigner::VariantProperty &variantProperty01, const QmlDesigner::VariantProperty &variantProperty02)
+{
+ if (variantProperty01.parentModelNode() != variantProperty02.parentModelNode())
+ return false;
+ if (variantProperty01.name() != variantProperty02.name())
+ return false;
+ return true;
+}
+
+QString idOrTypeNameForNode(const QmlDesigner::ModelNode &modelNode)
+{
+ QString idLabel = modelNode.id();
+ if (idLabel.isEmpty())
+ idLabel = modelNode.simplifiedTypeName();
+
+ return idLabel;
+}
+
+QmlDesigner::PropertyName unusedProperty(const QmlDesigner::ModelNode &modelNode)
+{
+ QmlDesigner::PropertyName propertyName = "property";
+ int i = 0;
+ if (modelNode.metaInfo().isValid()) {
+ while (true) {
+ const QmlDesigner::PropertyName currentPropertyName = propertyName + QString::number(i).toLatin1();
+ if (!modelNode.hasProperty(currentPropertyName) && !modelNode.metaInfo().hasProperty(currentPropertyName))
+ return currentPropertyName;
+ i++;
+ }
+ }
+
+ return propertyName;
+}
+
+QVariant convertVariantForTypeName(const QVariant &variant, const QmlDesigner::TypeName &typeName)
+{
+ QVariant returnValue = variant;
+
+ if (typeName == "int") {
+ bool ok;
+ returnValue = variant.toInt(&ok);
+ if (!ok)
+ returnValue = 0;
+ } else if (typeName == "real") {
+ bool ok;
+ returnValue = variant.toReal(&ok);
+ if (!ok)
+ returnValue = 0.0;
+
+ } else if (typeName == "string") {
+ returnValue = variant.toString();
+
+ } else if (typeName == "bool") {
+ returnValue = variant.toBool();
+ } else if (typeName == "url") {
+ returnValue = variant.toUrl();
+ } else if (typeName == "color") {
+ if (QColor::isValidColor(variant.toString())) {
+ returnValue = variant.toString();
+ } else {
+ returnValue = QColor(Qt::black);
+ }
+ } else if (typeName == "Item") {
+ returnValue = 0;
+ }
+
+ return returnValue;
+}
+
+} //internal namespace
+
+namespace QmlDesigner {
+
+namespace Internal {
+
+DynamicPropertiesModel::DynamicPropertiesModel(ConnectionView *parent)
+ : QStandardItemModel(parent)
+ , m_connectionView(parent)
+{
+ connect(this, &QStandardItemModel::dataChanged, this, &DynamicPropertiesModel::handleDataChanged);
+}
+
+void DynamicPropertiesModel::resetModel()
+{
+ beginResetModel();
+ clear();
+ setHorizontalHeaderLabels(QStringList({ tr("Item"), tr("Property"), tr("Property Type"),
+ tr("Property Value") }));
+
+ foreach (const ModelNode modelNode, m_selectedModelNodes)
+ addModelNode(modelNode);
+
+ endResetModel();
+}
+
+void DynamicPropertiesModel::bindingPropertyChanged(const BindingProperty &bindingProperty)
+{
+ if (!bindingProperty.isDynamic())
+ return;
+
+ m_handleDataChanged = false;
+
+ QList<ModelNode> selectedNodes = connectionView()->selectedModelNodes();
+ if (!selectedNodes.contains(bindingProperty.parentModelNode()))
+ return;
+ if (!m_lock) {
+ int rowNumber = findRowForBindingProperty(bindingProperty);
+
+ if (rowNumber == -1) {
+ addBindingProperty(bindingProperty);
+ } else {
+ updateBindingProperty(rowNumber);
+ }
+ }
+
+ m_handleDataChanged = true;
+}
+
+void DynamicPropertiesModel::variantPropertyChanged(const VariantProperty &variantProperty)
+{
+ if (!variantProperty.isDynamic())
+ return;
+
+ m_handleDataChanged = false;
+
+ QList<ModelNode> selectedNodes = connectionView()->selectedModelNodes();
+ if (!selectedNodes.contains(variantProperty.parentModelNode()))
+ return;
+ if (!m_lock) {
+ int rowNumber = findRowForVariantProperty(variantProperty);
+
+ if (rowNumber == -1) {
+ addVariantProperty(variantProperty);
+ } else {
+ updateVariantProperty(rowNumber);
+ }
+ }
+
+ m_handleDataChanged = true;
+}
+
+void DynamicPropertiesModel::bindingRemoved(const BindingProperty &bindingProperty)
+{
+ m_handleDataChanged = false;
+
+ QList<ModelNode> selectedNodes = connectionView()->selectedModelNodes();
+ if (!selectedNodes.contains(bindingProperty.parentModelNode()))
+ return;
+ if (!m_lock) {
+ int rowNumber = findRowForBindingProperty(bindingProperty);
+ removeRow(rowNumber);
+ }
+
+ m_handleDataChanged = true;
+}
+
+void DynamicPropertiesModel::selectionChanged(const QList<ModelNode> &selectedNodes)
+{
+ m_handleDataChanged = false;
+ m_selectedModelNodes = selectedNodes;
+ resetModel();
+ m_handleDataChanged = true;
+}
+
+ConnectionView *DynamicPropertiesModel::connectionView() const
+{
+ return m_connectionView;
+}
+
+BindingProperty DynamicPropertiesModel::bindingPropertyForRow(int rowNumber) const
+{
+
+ const int internalId = data(index(rowNumber, TargetModelNodeRow), Qt::UserRole + 1).toInt();
+ const QString targetPropertyName = data(index(rowNumber, TargetModelNodeRow), Qt::UserRole + 2).toString();
+
+ ModelNode modelNode = connectionView()->modelNodeForInternalId(internalId);
+
+ if (modelNode.isValid())
+ return modelNode.bindingProperty(targetPropertyName.toUtf8());
+
+ return BindingProperty();
+}
+
+VariantProperty DynamicPropertiesModel::variantPropertyForRow(int rowNumber) const
+{
+ const int internalId = data(index(rowNumber, TargetModelNodeRow), Qt::UserRole + 1).toInt();
+ const QString targetPropertyName = data(index(rowNumber, TargetModelNodeRow), Qt::UserRole + 2).toString();
+
+ ModelNode modelNode = connectionView()->modelNodeForInternalId(internalId);
+
+ if (modelNode.isValid())
+ return modelNode.variantProperty(targetPropertyName.toUtf8());
+
+ return VariantProperty();
+}
+
+QStringList DynamicPropertiesModel::possibleTargetProperties(const BindingProperty &bindingProperty) const
+{
+ const ModelNode modelNode = bindingProperty.parentModelNode();
+
+ if (!modelNode.isValid()) {
+ qWarning() << " BindingModel::possibleTargetPropertiesForRow invalid model node";
+ return QStringList();
+ }
+
+ NodeMetaInfo metaInfo = modelNode.metaInfo();
+
+ if (metaInfo.isValid()) {
+ QStringList possibleProperties;
+ foreach (const PropertyName &propertyName, metaInfo.propertyNames()) {
+ if (metaInfo.propertyIsWritable(propertyName))
+ possibleProperties << QString::fromUtf8(propertyName);
+ }
+
+ return possibleProperties;
+ }
+
+ return QStringList();
+}
+
+void DynamicPropertiesModel::addDynamicPropertyForCurrentNode()
+{
+ if (connectionView()->selectedModelNodes().count() == 1) {
+ const ModelNode modelNode = connectionView()->selectedModelNodes().constFirst();
+ if (modelNode.isValid()) {
+ try {
+ modelNode.variantProperty(unusedProperty(modelNode)).setDynamicTypeNameAndValue("string", QLatin1String("none.none"));
+ } catch (RewritingException &e) {
+ m_exceptionError = e.description();
+ QTimer::singleShot(200, this, &DynamicPropertiesModel::handleException);
+ }
+ }
+ } else {
+ qWarning() << " BindingModel::addBindingForCurrentNode not one node selected";
+ }
+}
+
+QStringList DynamicPropertiesModel::possibleSourceProperties(const BindingProperty &bindingProperty) const
+{
+ const QString expression = bindingProperty.expression();
+ const QStringList stringlist = expression.split(QLatin1String("."));
+
+ PropertyName typeName;
+
+ if (bindingProperty.parentModelNode().metaInfo().isValid()) {
+ typeName = bindingProperty.parentModelNode().metaInfo().propertyTypeName(bindingProperty.name());
+ } else {
+ qWarning() << " BindingModel::possibleSourcePropertiesForRow no meta info for target node";
+ }
+
+ const QString &id = stringlist.constFirst();
+
+ ModelNode modelNode = getNodeByIdOrParent(id, bindingProperty.parentModelNode());
+
+ if (!modelNode.isValid()) {
+ qWarning() << " BindingModel::possibleSourcePropertiesForRow invalid model node";
+ return QStringList();
+ }
+
+ NodeMetaInfo metaInfo = modelNode.metaInfo();
+
+ if (metaInfo.isValid()) {
+ QStringList possibleProperties;
+ foreach (const PropertyName &propertyName, metaInfo.propertyNames()) {
+ if (metaInfo.propertyTypeName(propertyName) == typeName) //### todo proper check
+ possibleProperties << QString::fromUtf8(propertyName);
+ }
+
+ return possibleProperties;
+ } else {
+ qWarning() << " BindingModel::possibleSourcePropertiesForRow no meta info for source node";
+ }
+
+ return QStringList();
+}
+
+void DynamicPropertiesModel::deleteDynamicPropertyByRow(int rowNumber)
+{
+ BindingProperty bindingProperty = bindingPropertyForRow(rowNumber);
+ if (bindingProperty.isValid()) {
+ bindingProperty.parentModelNode().removeProperty(bindingProperty.name());
+ }
+
+ VariantProperty variantProperty = variantPropertyForRow(rowNumber);
+
+ if (variantProperty.isValid()) {
+ variantProperty.parentModelNode().removeProperty(variantProperty.name());
+ }
+
+ resetModel();
+}
+
+void DynamicPropertiesModel::addProperty(const QVariant &propertyValue,
+ const QString &propertyType,
+ const AbstractProperty &abstractProperty)
+{
+ QList<QStandardItem*> items;
+
+ QStandardItem *idItem;
+ QStandardItem *propertyNameItem;
+ QStandardItem *propertyTypeItem;
+ QStandardItem *propertyValueItem;
+
+ idItem = new QStandardItem(idOrTypeNameForNode(abstractProperty.parentModelNode()));
+ updateCustomData(idItem, abstractProperty);
+
+ propertyNameItem = new QStandardItem(QString::fromUtf8(abstractProperty.name()));
+
+ items.append(idItem);
+ items.append(propertyNameItem);
+
+
+ propertyTypeItem = new QStandardItem(propertyType);
+ items.append(propertyTypeItem);
+
+ propertyValueItem = new QStandardItem();
+ propertyValueItem->setData(propertyValue, Qt::DisplayRole);
+ items.append(propertyValueItem);
+
+ appendRow(items);
+}
+
+void DynamicPropertiesModel::addBindingProperty(const BindingProperty &property)
+{
+ QVariant value = property.expression();
+ QString type = QString::fromLatin1(property.dynamicTypeName());
+ addProperty(value, type, property);
+}
+
+void DynamicPropertiesModel::addVariantProperty(const VariantProperty &property)
+{
+ QVariant value = property.value();
+ QString type = QString::fromLatin1(property.dynamicTypeName());
+ addProperty(value, type, property);
+}
+
+void DynamicPropertiesModel::updateBindingProperty(int rowNumber)
+{
+ BindingProperty bindingProperty = bindingPropertyForRow(rowNumber);
+
+ if (bindingProperty.isValid()) {
+ QString propertyName = QString::fromUtf8(bindingProperty.name());
+ updateDisplayRole(rowNumber, PropertyNameRow, propertyName);
+ QString value = bindingProperty.expression();
+ QString type = QString::fromUtf8(bindingProperty.dynamicTypeName());
+ updateDisplayRole(rowNumber, PropertyTypeRow, type);
+ updateDisplayRole(rowNumber, PropertyValueRow, value);
+ }
+}
+
+void DynamicPropertiesModel::updateVariantProperty(int rowNumber)
+{
+ VariantProperty variantProperty = variantPropertyForRow(rowNumber);
+
+ if (variantProperty.isValid()) {
+ QString propertyName = QString::fromUtf8(variantProperty.name());
+ updateDisplayRole(rowNumber, PropertyNameRow, propertyName);
+ QVariant value = variantProperty.value();
+ QString type = QString::fromUtf8(variantProperty.dynamicTypeName());
+ updateDisplayRole(rowNumber, PropertyTypeRow, type);
+
+ updateDisplayRoleFromVariant(rowNumber, PropertyValueRow, value);
+ }
+}
+
+void DynamicPropertiesModel::addModelNode(const ModelNode &modelNode)
+{
+ foreach (const BindingProperty &bindingProperty, modelNode.bindingProperties()) {
+ if (bindingProperty.isDynamic())
+ addBindingProperty(bindingProperty);
+ }
+
+ foreach (const VariantProperty &variantProperty, modelNode.variantProperties()) {
+ if (variantProperty.isDynamic())
+ addVariantProperty(variantProperty);
+ }
+}
+
+void DynamicPropertiesModel::updateValue(int row)
+{
+ BindingProperty bindingProperty = bindingPropertyForRow(row);
+
+ if (bindingProperty.isBindingProperty()) {
+ const QString expression = data(index(row, PropertyValueRow)).toString();
+
+ RewriterTransaction transaction = connectionView()->beginRewriterTransaction(QByteArrayLiteral("DynamicPropertiesModel::updateValue"));
+ try {
+ bindingProperty.setDynamicTypeNameAndExpression(bindingProperty.dynamicTypeName(), expression);
+ transaction.commit(); //committing in the try block
+ } catch (Exception &e) {
+ m_exceptionError = e.description();
+ QTimer::singleShot(200, this, &DynamicPropertiesModel::handleException);
+ }
+ return;
+ }
+
+ VariantProperty variantProperty = variantPropertyForRow(row);
+
+ if (variantProperty.isVariantProperty()) {
+ const QVariant value = data(index(row, PropertyValueRow));
+
+ RewriterTransaction transaction = connectionView()->beginRewriterTransaction(QByteArrayLiteral("DynamicPropertiesModel::updateValue"));
+ try {
+ variantProperty.setDynamicTypeNameAndValue(variantProperty.dynamicTypeName(), value);
+ transaction.commit(); //committing in the try block
+ } catch (Exception &e) {
+ m_exceptionError = e.description();
+ QTimer::singleShot(200, this, &DynamicPropertiesModel::handleException);
+ }
+ }
+}
+
+void DynamicPropertiesModel::updatePropertyName(int rowNumber)
+{
+ const PropertyName newName = data(index(rowNumber, PropertyNameRow)).toString().toUtf8();
+ if (newName.isEmpty()) {
+ qWarning() << "DynamicPropertiesModel::updatePropertyName invalid property name";
+ return;
+ }
+
+ BindingProperty bindingProperty = bindingPropertyForRow(rowNumber);
+
+ ModelNode targetNode = bindingProperty.parentModelNode();
+
+ if (bindingProperty.isBindingProperty()) {
+ connectionView()->executeInTransaction("DynamicPropertiesModel::updatePropertyName", [bindingProperty, newName, &targetNode](){
+ const QString expression = bindingProperty.expression();
+ const PropertyName dynamicPropertyType = bindingProperty.dynamicTypeName();
+
+ targetNode.bindingProperty(newName).setDynamicTypeNameAndExpression(dynamicPropertyType, expression);
+ targetNode.removeProperty(bindingProperty.name());
+ });
+
+ updateCustomData(rowNumber, targetNode.bindingProperty(newName));
+ return;
+ }
+
+ VariantProperty variantProperty = variantPropertyForRow(rowNumber);
+
+ if (variantProperty.isVariantProperty()) {
+ const QVariant value = variantProperty.value();
+ const PropertyName dynamicPropertyType = variantProperty.dynamicTypeName();
+ ModelNode targetNode = variantProperty.parentModelNode();
+
+ connectionView()->executeInTransaction("DynamicPropertiesModel::updatePropertyName", [=](){
+ targetNode.variantProperty(newName).setDynamicTypeNameAndValue(dynamicPropertyType, value);
+ targetNode.removeProperty(variantProperty.name());
+ });
+
+ updateCustomData(rowNumber, targetNode.variantProperty(newName));
+ }
+}
+
+void DynamicPropertiesModel::updatePropertyType(int rowNumber)
+{
+
+ const TypeName newType = data(index(rowNumber, PropertyTypeRow)).toString().toLatin1();
+
+ if (newType.isEmpty()) {
+ qWarning() << "DynamicPropertiesModel::updatePropertyName invalid property type";
+ return;
+ }
+
+ BindingProperty bindingProperty = bindingPropertyForRow(rowNumber);
+
+ if (bindingProperty.isBindingProperty()) {
+ const QString expression = bindingProperty.expression();
+ const PropertyName propertyName = bindingProperty.name();
+ ModelNode targetNode = bindingProperty.parentModelNode();
+
+ connectionView()->executeInTransaction("DynamicPropertiesModel::updatePropertyType", [=](){
+ targetNode.removeProperty(bindingProperty.name());
+ targetNode.bindingProperty(propertyName).setDynamicTypeNameAndExpression(newType, expression);
+ });
+
+ updateCustomData(rowNumber, targetNode.bindingProperty(propertyName));
+ return;
+ }
+
+ VariantProperty variantProperty = variantPropertyForRow(rowNumber);
+
+ if (variantProperty.isVariantProperty()) {
+ const QVariant value = variantProperty.value();
+ ModelNode targetNode = variantProperty.parentModelNode();
+ const PropertyName propertyName = variantProperty.name();
+
+ connectionView()->executeInTransaction("DynamicPropertiesModel::updatePropertyType", [=](){
+ targetNode.removeProperty(variantProperty.name());
+ if (newType == "alias") { //alias properties have to be bindings
+ targetNode.bindingProperty(propertyName).setDynamicTypeNameAndExpression(newType, QLatin1String("none.none"));
+ } else {
+ targetNode.variantProperty(propertyName).setDynamicTypeNameAndValue(newType, convertVariantForTypeName(value, newType));
+ }
+ });
+
+ updateCustomData(rowNumber, targetNode.variantProperty(propertyName));
+
+ if (variantProperty.isVariantProperty()) {
+ updateVariantProperty(rowNumber);
+ } else if (bindingProperty.isBindingProperty()) {
+ updateBindingProperty(rowNumber);
+ }
+ }
+}
+
+ModelNode DynamicPropertiesModel::getNodeByIdOrParent(const QString &id, const ModelNode &targetNode) const
+{
+ ModelNode modelNode;
+
+ if (id != QLatin1String("parent")) {
+ modelNode = connectionView()->modelNodeForId(id);
+ } else {
+ if (targetNode.hasParentProperty()) {
+ modelNode = targetNode.parentProperty().parentModelNode();
+ }
+ }
+ return modelNode;
+}
+
+void DynamicPropertiesModel::updateCustomData(QStandardItem *item, const AbstractProperty &property)
+{
+ item->setData(property.parentModelNode().internalId(), Qt::UserRole + 1);
+ item->setData(property.name(), Qt::UserRole + 2);
+}
+
+void DynamicPropertiesModel::updateCustomData(int row, const AbstractProperty &property)
+{
+ QStandardItem* idItem = item(row, 0);
+ updateCustomData(idItem, property);
+}
+
+int DynamicPropertiesModel::findRowForBindingProperty(const BindingProperty &bindingProperty) const
+{
+ for (int i=0; i < rowCount(); i++) {
+ if (compareBindingProperties(bindingPropertyForRow(i), bindingProperty))
+ return i;
+ }
+ //not found
+ return -1;
+}
+
+int DynamicPropertiesModel::findRowForVariantProperty(const VariantProperty &variantProperty) const
+{
+ for (int i=0; i < rowCount(); i++) {
+ if (compareVariantProperties(variantPropertyForRow(i), variantProperty))
+ return i;
+ }
+ //not found
+ return -1;
+}
+
+bool DynamicPropertiesModel::getExpressionStrings(const BindingProperty &bindingProperty, QString *sourceNode, QString *sourceProperty)
+{
+ //### todo we assume no expressions yet
+
+ const QString expression = bindingProperty.expression();
+
+ if (true) {
+ const QStringList stringList = expression.split(QLatin1String("."));
+
+ *sourceNode = stringList.constFirst();
+
+ QString propertyName;
+
+ for (int i=1; i < stringList.count(); i++) {
+ propertyName += stringList.at(i);
+ if (i != stringList.count() - 1)
+ propertyName += QLatin1String(".");
+ }
+ *sourceProperty = propertyName;
+ }
+ return true;
+}
+
+void DynamicPropertiesModel::updateDisplayRole(int row, int columns, const QString &string)
+{
+ QModelIndex modelIndex = index(row, columns);
+ if (data(modelIndex).toString() != string)
+ setData(modelIndex, string);
+}
+
+void DynamicPropertiesModel::updateDisplayRoleFromVariant(int row, int columns, const QVariant &variant)
+{
+ QModelIndex modelIndex = index(row, columns);
+ if (data(modelIndex) != variant)
+ setData(modelIndex, variant);
+}
+
+
+void DynamicPropertiesModel::handleDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight)
+{
+ if (!m_handleDataChanged)
+ return;
+
+ if (topLeft != bottomRight) {
+ qWarning() << "BindingModel::handleDataChanged multi edit?";
+ return;
+ }
+
+ m_lock = true;
+
+ int currentColumn = topLeft.column();
+ int currentRow = topLeft.row();
+
+ switch (currentColumn) {
+ case TargetModelNodeRow: {
+ //updating user data
+ } break;
+ case PropertyNameRow: {
+ updatePropertyName(currentRow);
+ } break;
+ case PropertyTypeRow: {
+ updatePropertyType(currentRow);
+ } break;
+ case PropertyValueRow: {
+ updateValue(currentRow);
+ } break;
+
+ default: qWarning() << "BindingModel::handleDataChanged column" << currentColumn;
+ }
+
+ m_lock = false;
+}
+
+void DynamicPropertiesModel::handleException()
+{
+ QMessageBox::warning(nullptr, tr("Error"), m_exceptionError);
+ resetModel();
+}
+
+} // namespace Internal
+
+} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/connectioneditor/dynamicpropertiesmodel.h b/src/plugins/qmldesigner/components/connectioneditor/dynamicpropertiesmodel.h
new file mode 100644
index 0000000000..e0c9617fed
--- /dev/null
+++ b/src/plugins/qmldesigner/components/connectioneditor/dynamicpropertiesmodel.h
@@ -0,0 +1,104 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#pragma once
+
+#include <modelnode.h>
+#include <bindingproperty.h>
+#include <variantproperty.h>
+
+#include <QStandardItemModel>
+
+namespace QmlDesigner {
+
+namespace Internal {
+
+class ConnectionView;
+
+class DynamicPropertiesModel : public QStandardItemModel
+{
+ Q_OBJECT
+
+public:
+ enum ColumnRoles {
+ TargetModelNodeRow = 0,
+ PropertyNameRow = 1,
+ PropertyTypeRow = 2,
+ PropertyValueRow = 3
+ };
+ DynamicPropertiesModel(ConnectionView *parent = nullptr);
+ void bindingPropertyChanged(const BindingProperty &bindingProperty);
+ void variantPropertyChanged(const VariantProperty &variantProperty);
+ void bindingRemoved(const BindingProperty &bindingProperty);
+ void selectionChanged(const QList<ModelNode> &selectedNodes);
+
+ ConnectionView *connectionView() const;
+ BindingProperty bindingPropertyForRow(int rowNumber) const;
+ VariantProperty variantPropertyForRow(int rowNumber) const;
+ QStringList possibleTargetProperties(const BindingProperty &bindingProperty) const;
+ QStringList possibleSourceProperties(const BindingProperty &bindingProperty) const;
+ void deleteDynamicPropertyByRow(int rowNumber);
+
+ void updateDisplayRoleFromVariant(int row, int columns, const QVariant &variant);
+ void addDynamicPropertyForCurrentNode();
+ void resetModel();
+
+protected:
+ void addProperty(const QVariant &propertyValue,
+ const QString &propertyType,
+ const AbstractProperty &abstractProperty);
+ void addBindingProperty(const BindingProperty &property);
+ void addVariantProperty(const VariantProperty &property);
+ void updateBindingProperty(int rowNumber);
+ void updateVariantProperty(int rowNumber);
+ void addModelNode(const ModelNode &modelNode);
+ void updateValue(int row);
+ void updatePropertyName(int rowNumber);
+ void updatePropertyType(int rowNumber);
+ ModelNode getNodeByIdOrParent(const QString &id, const ModelNode &targetNode) const;
+ void updateCustomData(QStandardItem *item, const AbstractProperty &property);
+ void updateCustomData(int row, const AbstractProperty &property);
+ int findRowForBindingProperty(const BindingProperty &bindingProperty) const;
+ int findRowForVariantProperty(const VariantProperty &variantProperty) const;
+
+ bool getExpressionStrings(const BindingProperty &bindingProperty, QString *sourceNode, QString *sourceProperty);
+
+ void updateDisplayRole(int row, int columns, const QString &string);
+
+private:
+ void handleDataChanged(const QModelIndex &topLeft, const QModelIndex& bottomRight);
+ void handleException();
+
+private:
+ QList<ModelNode> m_selectedModelNodes;
+ ConnectionView *m_connectionView;
+ bool m_lock = false;
+ bool m_handleDataChanged = false;
+ QString m_exceptionError;
+
+};
+
+} // namespace Internal
+} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/connectioneditor/stylesheet.css b/src/plugins/qmldesigner/components/connectioneditor/stylesheet.css
new file mode 100644
index 0000000000..aeacc63733
--- /dev/null
+++ b/src/plugins/qmldesigner/components/connectioneditor/stylesheet.css
@@ -0,0 +1,123 @@
+QFrame
+{
+ background-color: creatorTheme.QmlDesigner_BackgroundColorDarkAlternate;
+ color: creatorTheme.PanelTextColorLight;
+ font-size: creatorTheme.captionFontPixelSize;
+ border-radius: 0px;
+}
+
+QTableView {
+ color: creatorTheme.PanelTextColorLight;
+ selection-color: creatorTheme.PanelTextColorLight;
+ selection-background-color: creatorTheme.QmlDesigner_HighlightColor;
+
+}
+
+QTabBar QToolButton {
+ background-color: creatorTheme.QmlDesigner_BackgroundColorDarkAlternate;
+ border: 1px solid creatorTheme.QmlDesigner_BackgroundColorDarker;
+ border-radius: 0px;
+}
+
+QTableView::item
+{
+ border: 0px;
+ padding-left: 4px;
+}
+
+QTableView::item:focus
+{
+ border: none;
+ background-color: transparent;
+}
+
+QTableView::item:selected
+{
+ border: none
+}
+
+QHeaderView::section {
+ background-color: #494949;
+ padding: 4px;
+ border: 1px solid black;
+ color: #cacaca;
+ margin: 2px
+}
+
+QTableView {
+ alternate-background-color: #414141;
+}
+
+QWidget#widgetSpacer {
+ background-color: creatorTheme.QmlDesigner_TabLight;
+}
+
+QStackedWidget {
+ border: 0px;
+ background-color: #4f4f4f;
+}
+
+QTabBar::tab:selected {
+ border: none;
+ border-image: none;
+ image: none;
+
+ background-color: creatorTheme.QmlDesigner_TabLight;
+ color: creatorTheme.QmlDesigner_TabDark;
+}
+
+
+QTabBar::tab {
+ width: 92px;
+ height: 22px;
+ margin-top: 0x;
+ margin-bottom: 0px;
+ margin-left: 0px;
+ margin-right: 0px;
+ font: bold;
+ font-size: creatorTheme.captionFontPixelSize;
+ background-color: creatorTheme.QmlDesigner_TabDark;
+ color: creatorTheme.QmlDesigner_TabLight;
+}
+
+QSpinBox
+{
+ font-size: creatorTheme.captionFontPixelSize;
+ color: white;
+ padding-right: 2px;
+ padding-top: 2px;
+ padding-bottom: 2px;
+ padding-left: 12px;
+ border: 2px solid #0F0F0F;
+ border-width: 1;
+ background-color: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1,
+ stop: 0 #2c2c2c, stop: 1 #333333);
+
+ min-height: 22px;
+}
+
+ QDoubleSpinBox
+ {
+ font-size: creatorTheme.captionFontPixelSize;
+ color: white;
+ padding-right: 2px;
+ padding-top: 2px;
+ padding-bottom: 2px;
+ padding-left: 12px;
+ border: 2px solid #0F0F0F;
+ border-width: 1;
+ background-color: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1,
+ stop: 0 #2c2c2c, stop: 1 #333333);
+ min-height: 22px;
+ }
+
+QLineEdit
+{
+ color: white;
+ font-size: creatorTheme.captionFontPixelSize;
+ border: 2px solid #0F0F0F;
+ border-width: 1;
+ min-height: 26px;
+ background-color: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1,
+ stop: 0 #2c2c2c, stop: 1 #333333);
+}