summaryrefslogtreecommitdiffstats
path: root/src/Authoring/Qt3DStudio/Application/DataInputListDlg.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/Authoring/Qt3DStudio/Application/DataInputListDlg.cpp')
-rw-r--r--src/Authoring/Qt3DStudio/Application/DataInputListDlg.cpp796
1 files changed, 796 insertions, 0 deletions
diff --git a/src/Authoring/Qt3DStudio/Application/DataInputListDlg.cpp b/src/Authoring/Qt3DStudio/Application/DataInputListDlg.cpp
new file mode 100644
index 00000000..2f8bc29b
--- /dev/null
+++ b/src/Authoring/Qt3DStudio/Application/DataInputListDlg.cpp
@@ -0,0 +1,796 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** 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.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "DataInputListDlg.h"
+#include "ui_DataInputListDlg.h"
+#include "DataInputDlg.h"
+#include "StudioPreferences.h"
+#include "StudioApp.h"
+#include "Dialogs.h"
+#include "Core.h"
+#include "Doc.h"
+#include "Qt3DSDMStudioSystem.h"
+#include "ClientDataModelBridge.h"
+#include "DataModelObjectReferenceHelper.h"
+
+#include <QtWidgets/qpushbutton.h>
+#include <QtGui/qstandarditemmodel.h>
+#include <QtGui/qevent.h>
+#include <QtWidgets/qaction.h>
+#include <algorithm>
+#include <QtCore/qtimer.h>
+
+const int columnCount = 3;
+
+CDataInputListDlg::CDataInputListDlg(QMap<QString, CDataInputDialogItem *> *datainputs,
+ bool goToAdd, QWidget *parent, EDataType defaultType,
+ const QVector<EDataType> &acceptedTypes)
+ : QDialog(parent)
+ , m_ui(new Ui::DataInputListDlg)
+ , m_actualDataInputs(datainputs)
+ , m_currentDataInputIndex(-1)
+ , m_tableContents(new QStandardItemModel(0, columnCount, this))
+ , m_infoContents(new QStandardItemModel(0, columnCount, this))
+ , m_goToAdd(goToAdd)
+ , m_sortColumn(-1)
+ , m_defaultType(defaultType)
+ , m_acceptedTypes(acceptedTypes)
+{
+ m_ui->setupUi(this);
+
+ // Create icon buttons. Give them object and accessible names so their style can be modified
+ // via stylesheet.
+ QPushButton *addButton = new QPushButton(this);
+ addButton->setIcon(QIcon(":/images/add.png"));
+ addButton->setAccessibleName(QStringLiteral("DataInputListButton"));
+ addButton->setObjectName(QStringLiteral("DataInputListButton"));
+ QPushButton *removeButton = new QPushButton(this);
+ removeButton->setIcon(QIcon(":/images/Action-Trash-Disabled.png"));
+ removeButton->setAccessibleName(QStringLiteral("DataInputListButton"));
+ removeButton->setObjectName(QStringLiteral("DataInputListButton"));
+
+ m_ui->buttonBoxAddEditRemove->addButton(addButton, QDialogButtonBox::ActionRole);
+ m_ui->buttonBoxAddEditRemove->addButton(removeButton, QDialogButtonBox::ActionRole);
+ QList<QAbstractButton *>buttons = m_ui->buttonBoxAddEditRemove->buttons();
+ connect(buttons.at(0), &QAbstractButton::clicked, this, &CDataInputListDlg::onAddDataInput);
+ connect(buttons.at(1), &QAbstractButton::clicked, this, &CDataInputListDlg::onRemoveDataInput);
+
+ buttons[0]->setToolTip(tr("Add New Data Input..."));
+ buttons[0]->setText(tr("Add Data Input"));
+ buttons[1]->setToolTip(tr("Remove Data Input"));
+ buttons[1]->setText(tr("Remove existing Data Input"));
+
+ m_ui->typeFilterCombo->addItems({tr("[All types]"), tr("Boolean"),
+ tr("Float"), tr("Ranged Number"), tr("String"), tr("Variant"),
+ tr("Vector2"), tr("Vector3"), tr("Vector4")});
+ m_ui->typeFilterCombo->setToolTip(tr("Filter the list by Data Input type"));
+
+ m_ui->searchField->setToolTip(tr("Search for Data Input"));
+
+ connect(m_ui->typeFilterCombo, QOverload<int>::of(&QComboBox::currentIndexChanged),
+ this, &CDataInputListDlg::onFilterTypeChanged);
+ connect(m_ui->searchField, &QLineEdit::textChanged, this,
+ &CDataInputListDlg::onSearchTextChanged);
+
+ setTabOrder(m_ui->searchField, addButton);
+ setTabOrder(addButton, removeButton);
+
+ initDialog();
+
+ window()->setFixedSize(size());
+}
+
+CDataInputListDlg::~CDataInputListDlg()
+{
+ delete m_ui;
+}
+
+void CDataInputListDlg::initDialog()
+{
+ // Copy given list to our internal one. We want to commit to the changes only after "Ok"
+ // has been pressed.
+ const auto keys = m_actualDataInputs->keys();
+ for (auto name : keys)
+ m_dataInputs.insert(name, m_actualDataInputs->value(name));
+
+ // Check available list. If there are none, disable "Remove" and "Edit" buttons
+ updateButtons();
+
+ // Update table contents
+ updateContents();
+ updateInfo();
+
+ // Make the expression column wider than name and type
+ m_ui->tableView->horizontalHeader()->setStretchLastSection(true);
+ m_ui->tableView->horizontalHeader()->setMinimumSectionSize(125);
+ m_ui->tableView->setFocusPolicy(Qt::NoFocus);
+
+ // Align columns left and prevent selecting the whole column
+ m_ui->tableView->horizontalHeader()->setDefaultAlignment(Qt::AlignLeft);
+
+ m_replaceSelectedAction = new QAction(QObject::tr("Replace selected"), m_ui->elementInfo);
+ m_replaceAllAction = new QAction(QObject::tr("Replace all"), m_ui->elementInfo);
+ m_replaceSelectedAction->setDisabled(true);
+ m_replaceAllAction->setDisabled(true);
+
+ m_ui->elementInfo->addAction(m_replaceSelectedAction);
+ m_ui->elementInfo->addAction(m_replaceAllAction);
+ m_ui->elementInfo->setContextMenuPolicy(Qt::ActionsContextMenu);
+ m_ui->elementInfo->setFocusPolicy(Qt::NoFocus);
+ m_ui->elementInfo->resizeColumnsToContents();
+ m_ui->elementInfo->horizontalHeader()->setStretchLastSection(true);
+ m_ui->elementInfo->horizontalHeader()->setMinimumSectionSize(140);
+ m_ui->elementInfo->setModel(m_infoContents);
+ m_ui->elementInfo->horizontalHeader()->setDefaultAlignment(Qt::AlignLeft);
+
+ connect(m_replaceSelectedAction, &QAction::triggered, this,
+ &CDataInputListDlg::onReplaceSelected);
+ connect(m_replaceAllAction, &QAction::triggered, this, &CDataInputListDlg::onReplaceAll);
+
+ connect(m_ui->elementInfo->selectionModel(), &QItemSelectionModel::selectionChanged,
+ this, &CDataInputListDlg::onElementSelectionChanged);
+
+ connect(m_ui->tableView->selectionModel(), &QItemSelectionModel::selectionChanged,
+ this, &CDataInputListDlg::onSelectionChanged);
+ connect(m_ui->tableView, &QTableView::activated, this, &CDataInputListDlg::onActivated);
+ connect(m_ui->tableView->horizontalHeader(), &QHeaderView::sortIndicatorChanged,
+ this, &CDataInputListDlg::onSortOrderChanged);
+
+ // Directly show data input modification dialog
+ if (m_goToAdd)
+ QTimer::singleShot(0, this, &CDataInputListDlg::onAddDataInput);
+}
+
+void CDataInputListDlg::updateButtons()
+{
+ if (m_ui->tableView->selectionModel()
+ && m_ui->tableView->selectionModel()->selectedIndexes().size() > 0) {
+ m_ui->buttonBoxAddEditRemove->buttons()[1]->setEnabled(true);
+ m_ui->buttonBoxAddEditRemove->buttons()[1]->setIcon(
+ QIcon(":/images/Action-Trash-Normal.png"));
+ } else {
+ m_ui->buttonBoxAddEditRemove->buttons()[1]->setEnabled(false);
+ m_ui->buttonBoxAddEditRemove->buttons()[1]->setIcon(
+ QIcon(":/images/Action-Trash-Disabled.png"));
+ }
+}
+
+void CDataInputListDlg::updateContents()
+{
+ m_tableContents->clear();
+
+ QStringList labels;
+ labels << tr("Name") << tr("Input Type") << tr("Expression");
+ m_tableContents->setHorizontalHeaderLabels(labels);
+
+ QList<QStandardItem *> dataInput;
+
+ for (auto &it : qAsConst(m_dataInputs)) {
+ dataInput.clear();
+
+ EDataType dataInputType = (EDataType)it->type;
+
+ if ((dataInputType == m_typeFilter || m_typeFilter == -1)
+ && it->name.contains(m_searchString)){
+
+ dataInput.append(new QStandardItem(it->name));
+
+ if (dataInputType == DataTypeRangedNumber
+ && (m_typeFilter == (int)DataTypeRangedNumber || m_typeFilter == -1)) {
+ dataInput.append(new QStandardItem(tr("Ranged Number")));
+ QString expression = QStringLiteral("[ ")
+ + QString::number(it->minValue)
+ + QStringLiteral(" ... ")
+ + QString::number(it->maxValue)
+ + QStringLiteral(" ]");
+ dataInput.append(new QStandardItem(expression));
+ } else if (dataInputType == DataTypeString
+ && (m_typeFilter == (int)DataTypeString || m_typeFilter == -1)) {
+ dataInput.append(new QStandardItem(tr("String")));
+ } else if (dataInputType == DataTypeFloat
+ && (m_typeFilter == (int)DataTypeFloat || m_typeFilter == -1)) {
+ dataInput.append(new QStandardItem(tr("Float")));
+#ifdef DATAINPUT_EVALUATOR_ENABLED
+ } else if (dataInputType == DataTypeEvaluator
+ && (m_typeFilter == (int)DataTypeEvaluator || m_typeFilter == -1)) {
+ dataInput.append(new QStandardItem(tr("Evaluator")));
+ dataInput.append(new QStandardItem(m_dataInputs.at(i)->valueString));
+#endif
+ } else if (dataInputType == DataTypeBoolean
+ && (m_typeFilter == (int)DataTypeBoolean || m_typeFilter == -1)) {
+ dataInput.append(new QStandardItem(tr("Boolean")));
+ } else if (dataInputType == DataTypeVector4
+ && (m_typeFilter == (int)DataTypeVector4 || m_typeFilter == -1)) {
+ dataInput.append(new QStandardItem(tr("Vector4")));
+ } else if (dataInputType == DataTypeVector3
+ && (m_typeFilter == (int)DataTypeVector3 || m_typeFilter == -1)) {
+ dataInput.append(new QStandardItem(tr("Vector3")));
+ } else if (dataInputType == DataTypeVector2
+ && (m_typeFilter == (int)DataTypeVector2 || m_typeFilter == -1)) {
+ dataInput.append(new QStandardItem(tr("Vector2")));
+ } else if (dataInputType == DataTypeVariant
+ && (m_typeFilter == (int)DataTypeVariant || m_typeFilter == -1)) {
+ dataInput.append(new QStandardItem(tr("Variant")));
+ }
+
+ // highlight datainputs that are in use
+ if (it->ctrldElems.size() || it->externalPresBoundTypes.size())
+ dataInput.first()->setForeground(QBrush(CStudioPreferences::dataInputColor()));
+
+ // warn if any datainputs have mismatching datatype with an icon after datatype
+ // indicator
+ static QString warning(tr("Data Input type is not matching with one "
+ "or several bound properties"));
+ for (const auto &ctrlElem : qAsConst(it->ctrldElems)) {
+ if (!CDataInputDlg::getAcceptedTypes(ctrlElem.dataType.first)
+ .contains(dataInputType)) {
+ dataInput[1]->setIcon(QIcon(":/images/warning.png"));
+ dataInput[1]->setToolTip(warning);
+ }
+ }
+
+ for (const auto &extBoundType : qAsConst(it->externalPresBoundTypes)) {
+ if (!CDataInputDlg::getAcceptedTypes(extBoundType.first).contains(dataInputType)) {
+ dataInput[1]->setIcon(QIcon(":/images/warning.png"));
+ dataInput[1]->setToolTip(warning);
+ }
+ }
+
+ m_tableContents->appendRow(dataInput);
+ }
+ }
+
+ m_ui->tableView->setModel(m_tableContents);
+
+ if (m_sortColumn >= 0)
+ m_ui->tableView->sortByColumn(m_sortColumn, m_sortOrder);
+}
+
+void CDataInputListDlg::updateInfo()
+{
+ auto doc = g_StudioApp.GetCore()->GetDoc();
+ auto refHelper = doc->GetDataModelObjectReferenceHelper();
+
+ m_infoContents->clear();
+
+ QStringList labels;
+ labels << tr("Object Name") << tr("Location") << tr("Properties");
+ m_infoContents->setHorizontalHeaderLabels(labels);
+
+ // Only show controlled instances if we have a single datainput selected.
+ if (m_ui->tableView->selectionModel()->selectedRows(0).size() == 1) {
+ for (auto allCtrldElemsIt = m_dataInputs[m_currentDataInputName]->ctrldElems.begin();
+ allCtrldElemsIt != m_dataInputs[m_currentDataInputName]->ctrldElems.end();) {
+ bool typeNotMatching = false;
+ QStandardItem *item = new QStandardItem(
+ g_StudioApp.GetCore()->GetDoc()->GetStudioSystem()
+ ->GetClientDataModelBridge()->GetName(
+ allCtrldElemsIt->instHandle.GetHandleValue()).toQString());
+ // Store actual handle value to Qt::Userdata+1
+ item->setData(allCtrldElemsIt->instHandle.GetHandleValue());
+ auto path = refHelper->GetObjectReferenceString(
+ doc->GetSceneInstance(), CRelativePathTools::EPATHTYPE_GUID,
+ allCtrldElemsIt->instHandle).toQString();
+ // One element can have several properties controlled by this datainput,
+ // do not show element several times. Show the number of properties after
+ // the elementpath and the list of property names in a separate column.
+ if (m_infoContents->findItems(path, Qt::MatchContains, 1).isEmpty()) {
+ item->setToolTip(path);
+ item->setEditable(false);
+
+ QString propNames;
+ int count = 0;
+ CDataInputDialogItem *di = m_dataInputs[m_currentDataInputName];
+
+ QVector<CDataInputDialogItem::ControlledItem> thisInstCtrldItems;
+ di->getInstCtrldItems(
+ allCtrldElemsIt->instHandle.GetHandleValue(), thisInstCtrldItems);
+ for (auto thisInstCtrldItemsIt : qAsConst(thisInstCtrldItems)) {
+ if (propNames.size() != 0)
+ propNames.append(QLatin1String(", "));
+
+ if (thisInstCtrldItemsIt.propHandle.Valid()) {
+ propNames.append(
+ QString::fromStdWString(
+ g_StudioApp.GetCore()->GetDoc()->GetStudioSystem()->
+ GetPropertySystem()->GetFormalName(
+ thisInstCtrldItemsIt.instHandle,
+ thisInstCtrldItemsIt.propHandle).wide_str()));
+ } else {
+ propNames.append(QObject::tr("timeline/slide"));
+ }
+
+ count++;
+
+ // Check if there is a non-matching datatype binding with one or several
+ // properties for this element.
+ if (!CDataInputDlg::getAcceptedTypes(allCtrldElemsIt->dataType.first).contains(
+ (EDataType)(m_dataInputs[m_currentDataInputName]->type))) {
+ typeNotMatching = true;
+ }
+ // Advance main iterator so that after the inner loop we end up
+ // at the start of next instance's batch of controlleditems.
+ allCtrldElemsIt++;
+ }
+
+ QStandardItem *item2 = new QStandardItem(path + QStringLiteral("(")
+ + QString::number(count)
+ + QStringLiteral(")"));
+ item2->setToolTip(path);
+ item2->setEditable(false);
+ QStandardItem *item3 = new QStandardItem(propNames);
+ item3->setToolTip(propNames);
+ item3->setEditable(false);
+
+ // Highlight the entire property name item if a non-match was found.
+ if (typeNotMatching) {
+ item3->setForeground(
+ QBrush(CStudioPreferences::invalidDataInputIndicatorColor()));
+ static QString warning(tr("\n\nData Input type is not matching with one or "
+ "several bound properties"));
+ item3->setToolTip(propNames + warning);
+ }
+ m_infoContents->appendRow(QList<QStandardItem *>({item, item2, item3}));
+ }
+ }
+ // Show this datainput uses in subpresentations but leave property name list
+ // empty because we do not have that info.
+ const auto uniqueKeys
+ = m_dataInputs[m_currentDataInputName]->externalPresBoundTypes.uniqueKeys();
+ for (auto &k : uniqueKeys) {
+ m_infoContents->appendRow(QList<QStandardItem *>(
+ {new QStandardItem(QObject::tr("<another presentation>")),
+ new QStandardItem(k), new QStandardItem()}));
+ }
+ }
+
+ m_ui->elementInfo->setModel(m_infoContents);
+}
+bool CDataInputListDlg::event(QEvent *event)
+{
+ if (event->type() == QEvent::ShortcutOverride) {
+ QKeyEvent *ke = static_cast<QKeyEvent *>(event);
+ if (m_currentDataInputIndex >= 0 && (ke->key() == Qt::Key_Delete)) {
+ onRemoveDataInput();
+ event->accept();
+ return true;
+ } else {
+ return QDialog::event(event);
+ }
+ } else {
+ return QDialog::event(event);
+ }
+}
+
+void CDataInputListDlg::keyPressEvent(QKeyEvent *event)
+{
+ if ((event->key() == Qt::Key_Enter || event->key() == Qt::Key_Return)) {
+ // Eat enter if we have selections
+ const QModelIndexList indexes = m_ui->tableView->selectionModel()->selectedIndexes();
+ if (indexes.size() > 0)
+ event->accept();
+ else
+ QDialog::keyPressEvent(event);
+ } else {
+ QDialog::keyPressEvent(event);
+ }
+}
+
+void CDataInputListDlg::accept()
+{
+ m_actualDataInputs->clear();
+
+ const auto keys = m_dataInputs.keys();
+ for (auto name : keys)
+ m_actualDataInputs->insert(name, m_dataInputs.value(name));
+
+ // Only show automatic binding removal choice if user has deleted
+ // in-use datainput during this activation of the dialog, to avoid constant
+ // nagging at closing of this dialog in case we have invalid datainputs.
+ if (m_deletedDiInUse)
+ QTimer::singleShot(0, &g_StudioApp, &CStudioApp::checkDeletedDatainputs);
+
+ if (g_StudioApp.GetCore()->GetDoc()->isTransactionOpened()) {
+ g_StudioApp.GetCore()->GetDoc()->closeTransaction();
+ // If a datainput has been edited (and datainput controller names
+ // updated in element "controlledproperty" properties), we need to make all changes
+ // non-undoable. This is because datainput definitions in UIA file (and in global
+ // datainput map) are not participating in the command stack. Changes there cannot be
+ // reversed, leading to datainput map and element properties being out of sync after Undo.
+ if (m_diHasBeenEdited)
+ g_StudioApp.GetCore()->GetCmdStack()->RemoveLastUndo();
+ }
+
+ QDialog::accept();
+}
+
+void CDataInputListDlg::reject()
+{
+ // If the user cancels, also roll back any possible changes to data input bindings.
+ if (g_StudioApp.GetCore()->GetDoc()->isTransactionOpened())
+ g_StudioApp.GetCore()->GetDoc()->rollbackTransaction();
+
+ QDialog::reject();
+}
+
+void CDataInputListDlg::onAddDataInput()
+{
+ // Create a new data input dialog item and give it to dialog
+ CDataInputDialogItem *dataInput = new CDataInputDialogItem();
+ dataInput->type = m_defaultType;
+ CDataInputDlg datainputdialog(&dataInput, m_tableContents, this, m_acceptedTypes);
+ datainputdialog.setWindowTitle("Add Data Input");
+ if (datainputdialog.exec() == QDialog::Accepted) {
+ m_dataInputs.insert(dataInput->name, dataInput);
+ m_mostRecentlyAdded = dataInput->name;
+ } else {
+ m_mostRecentlyAdded.clear();
+ }
+
+ updateButtons();
+ updateContents();
+
+ // If we went straight to adding a new datainput, close
+ // dialog automatically
+ if (m_goToAdd)
+ accept();
+
+ // Otherwise find the new position of added DI and select it.
+ auto idxList = m_ui->tableView->selectionModel()->model()->match(
+ m_ui->tableView->selectionModel()->model()->index(
+ 0,0), Qt::EditRole, m_mostRecentlyAdded);
+
+ if (!idxList.empty())
+ m_ui->tableView->selectRow(idxList.first().row());
+}
+
+void CDataInputListDlg::onRemoveDataInput()
+{
+ QVector<int> removedRows;
+ bool anyDiInUse = false;
+
+ if (m_ui->tableView->selectionModel()) {
+ const QModelIndexList indexes = m_ui->tableView->selectionModel()->selectedIndexes();
+ for (const auto &index : indexes) {
+ if (!removedRows.contains(index.row())) {
+ removedRows.append(index.row());
+ QString diName = m_tableContents
+ ->itemFromIndex(index)->data(Qt::EditRole).toString();
+ if (m_dataInputs[diName]->ctrldElems.size()
+ || m_dataInputs[diName]->externalPresBoundTypes.size() ) {
+ anyDiInUse = true;
+ }
+ }
+ }
+ }
+
+
+ QString title(QObject::tr("Warning"));
+ QString text;
+
+ if (anyDiInUse)
+ text.append(QObject::tr("One or more datainputs are currently in use. "));
+ text.append(QObject::tr("This operation cannot be undone.\n\nAre you sure?"));
+
+ auto ret = g_StudioApp.GetDialogs()->DisplayMessageBox(title, text,
+ Qt3DSMessageBox::ICON_WARNING, true,
+ this);
+ if (ret != Qt3DSMessageBox::MSGBX_OK)
+ return;
+
+ m_deletedDiInUse = anyDiInUse;
+
+ if (removedRows.size() > 0) {
+ std::sort(removedRows.begin(), removedRows.end());
+ for (int i = removedRows.size() - 1; i >= 0; --i) {
+ m_dataInputs.remove(
+ m_tableContents->item(removedRows[i])->data(Qt::EditRole).toString());
+ }
+ m_ui->tableView->clearSelection();
+ m_currentDataInputIndex = -1;
+
+ updateButtons();
+ updateContents();
+ }
+}
+
+void CDataInputListDlg::onEditDataInput()
+{
+ if (m_currentDataInputIndex >= 0) {
+ CDataInputDialogItem *di = m_dataInputs.value(m_currentDataInputName);
+
+ // Only show types that are ok for _all_ currently controlled properties.
+ // If datainput is not controlling any elements, all types are ok.
+
+ // Datainput binding types
+ QVector<EDataType> allowedTypes;
+ bool strictFound = false;
+ // Datatype and strictness requirement
+ QVector<QPair<qt3dsdm::DataModelDataType::Value, bool>> types;
+ di->getBoundTypes(types);
+ if (types.size()) {
+ for (auto type : qAsConst(types)) {
+ // If we hit strict type requirement for a certain bound datatype, set allowed types
+ // to only this data type (and Variant) and exit after appending it to
+ // allowedTypes vector.
+ // We should never have strict requirement for two different datatypes, obviously.
+ if (type.second)
+ allowedTypes.clear();
+
+ auto acceptedTypes = CDataInputDlg::getAcceptedTypes(type.first, type.second);
+
+ for (auto t : qAsConst(acceptedTypes)) {
+ if (!allowedTypes.contains(t))
+ allowedTypes.append(t);
+ }
+ // if we just hit a strict type requirement we are finished
+ if (type.second) {
+ strictFound = true;
+ break;
+ }
+ }
+ }
+
+ // Datainput bindings for all other presentations, unless we already have a
+ // strict type requirement
+ if (di->externalPresBoundTypes.size() && !strictFound) {
+ for (auto type : qAsConst(di->externalPresBoundTypes)) {
+ if (type.second)
+ allowedTypes.clear();
+
+ const auto acceptedTypes = CDataInputDlg::getAcceptedTypes(type.first, type.second);
+
+ for (auto t : acceptedTypes) {
+ if (!allowedTypes.contains(t))
+ allowedTypes.append(t);
+ }
+ // if we just hit a strict type requirement we are finished
+ if (type.second)
+ break;
+ }
+ }
+
+ // no bindings in this or other presentations, all datatypes are ok
+ if (!allowedTypes.size())
+ allowedTypes = allDataTypes;
+
+ CDataInputDlg datainputdialog(&di, m_tableContents, this, allowedTypes);
+ datainputdialog.exec();
+
+ // if we are renaming a datainput, remove the old key - value and
+ // add it again as new entry with new name
+ if (m_currentDataInputName != di->name) {
+ m_ui->elementInfo->selectAll();
+ const QModelIndexList indexes = m_ui->elementInfo->selectionModel()->selectedRows();
+ QList<qt3dsdm::Qt3DSDMInstanceHandle> elementHandles;
+ for (auto it : indexes) {
+ elementHandles.append(
+ m_ui->elementInfo->model()->data(it, Qt::UserRole + 1).toInt());
+ }
+ // Opens up a transaction if one is not existing; we will close it at
+ // dialog exit to batch ok/cancel all changes done in this dialog.
+ g_StudioApp.GetCore()->GetDoc()->ReplaceDatainput(m_currentDataInputName,
+ di->name, elementHandles);
+ m_dataInputs.remove(m_currentDataInputName);
+ m_currentDataInputName = di->name;
+ m_diHasBeenEdited = true;
+ }
+ // Insert replaces the previous key - value pair if existing.
+ m_dataInputs.insert(m_currentDataInputName, di);
+
+ updateButtons();
+ updateContents();
+
+ // Find the new position of renamed DI and select it.
+ auto idxList = m_ui->tableView->selectionModel()->model()->match(
+ m_ui->tableView->selectionModel()->model()->index(
+ 0,0), Qt::EditRole, m_currentDataInputName);
+
+ if (!idxList.empty())
+ m_ui->tableView->selectRow(idxList.first().row());
+ }
+}
+
+void CDataInputListDlg::onActivated(const QModelIndex &index)
+{
+ const QModelIndexList indexes = m_ui->tableView->selectionModel()->selectedRows(0);
+ m_currentDataInputIndex = indexes.size() ? index.row() : -1;
+ if (m_currentDataInputIndex >= 0) {
+ m_currentDataInputName
+ = m_tableContents->itemFromIndex(indexes.at(0))->data(Qt::EditRole).toString();
+ }
+ onEditDataInput();
+}
+
+void CDataInputListDlg::onSelectionChanged()
+{
+ const QModelIndexList indexes = m_ui->tableView->selectionModel()->selectedRows(0);
+ m_currentDataInputIndex = indexes.size() ? indexes.at(0).row() : -1;
+ if (m_currentDataInputIndex >= 0) {
+ m_currentDataInputName
+ = m_tableContents->itemFromIndex(indexes.at(0))->data(Qt::EditRole).toString();
+ }
+
+ updateButtons();
+ updateInfo();
+ onElementSelectionChanged();
+}
+
+void CDataInputListDlg::onSortOrderChanged(int column, Qt::SortOrder order)
+{
+ m_sortColumn = column;
+ m_sortOrder = order;
+}
+
+void CDataInputListDlg::onReplaceSelected()
+{
+ if (!m_dataInputChooserView) {
+ m_dataInputChooserView = new DataInputSelectView(m_acceptedTypes, this);
+ // Do not show "Add new" and "None" choices.
+ m_dataInputChooserView->getModel()->showFixedItems(false);
+ } else {
+ disconnect(m_dataInputChooserView, nullptr, nullptr, nullptr);
+ }
+
+ connect(m_dataInputChooserView, &DataInputSelectView::dataInputChanged, this,
+ [this](int handle, int instance, const QString &controllerName) {
+ Q_UNUSED(handle)
+ Q_UNUSED(instance)
+
+ const QModelIndexList indexes = m_ui->elementInfo->selectionModel()->selectedRows();
+
+ replaceDatainputs(indexes, controllerName);
+ refreshDIs();
+ });
+
+ QVector<QPair<qt3dsdm::DataModelDataType::Value, bool>> selBoundTypes;
+
+ const auto selRows = m_ui->elementInfo->selectionModel()->selectedRows(0);
+ for (auto it : selRows)
+ selBoundTypes.append(m_dataInputs[m_currentDataInputName]
+ ->ctrldElems[it.row()].dataType);
+
+ setUniqueAcceptedDITypes(selBoundTypes);
+
+ CDialogs::showWidgetBrowser(this, m_dataInputChooserView, geometry().center(),
+ CDialogs::WidgetBrowserAlign::Center);
+}
+
+void CDataInputListDlg::onReplaceAll()
+{
+ if (!m_dataInputChooserView) {
+ m_dataInputChooserView = new DataInputSelectView(m_acceptedTypes, this);
+ // Do not show "Add new" and "None" choices.
+ m_dataInputChooserView->getModel()->showFixedItems(false);
+ } else {
+ disconnect(m_dataInputChooserView, nullptr, nullptr, nullptr);
+ }
+
+ connect(m_dataInputChooserView, &DataInputSelectView::dataInputChanged, this,
+ [this](int handle, int instance, const QString &controllerName) {
+ Q_UNUSED(handle)
+ Q_UNUSED(instance)
+
+ m_ui->elementInfo->selectAll();
+ const QModelIndexList indexes = m_ui->elementInfo->selectionModel()->selectedRows();
+
+ replaceDatainputs(indexes, controllerName);
+ refreshDIs();
+ });
+ QVector<QPair<qt3dsdm::DataModelDataType::Value, bool>> types;
+ m_dataInputs[m_currentDataInputName]->getBoundTypes(types);
+ setUniqueAcceptedDITypes(types);
+
+ CDialogs::showWidgetBrowser(this, m_dataInputChooserView, geometry().center(),
+ CDialogs::WidgetBrowserAlign::Center);
+}
+
+void CDataInputListDlg::onElementSelectionChanged()
+{
+ bool disable = true;
+ QModelIndexList selected = m_ui->elementInfo->selectionModel()->selectedRows(2);
+ // We only can change bindings in the currently open project. Disable replace
+ // actions if we have selected a row denoting control in a subpresentation
+ // (fastest way to check is to see if property list is empty).
+ if (selected.isEmpty())
+ disable = true;
+ else if (m_ui->elementInfo->model()->data(selected.at(0)).isNull())
+ disable = true;
+ else
+ disable = false;
+
+ m_replaceSelectedAction->setDisabled(disable);
+ m_replaceAllAction->setDisabled(disable);
+}
+
+void CDataInputListDlg::onFilterTypeChanged(int index)
+{
+ m_typeFilter = index - 1;
+ updateContents();
+}
+
+void CDataInputListDlg::onSearchTextChanged()
+{
+ m_searchString = m_ui->searchField->text();
+ updateContents();
+}
+
+void CDataInputListDlg::refreshDIs()
+{
+ updateContents();
+ updateInfo();
+}
+
+void CDataInputListDlg::setUniqueAcceptedDITypes(
+ const QVector<QPair<qt3dsdm::DataModelDataType::Value, bool>> &boundTypes)
+{
+ QVector<EDataType> okDiTypes(allDataTypes);
+
+ for (auto it : qAsConst(allDataTypes)) {
+ for (auto it2 : boundTypes) {
+ if (!CDataInputDlg::isEquivalentDataType(it, it2.first, it2.second)) {
+ auto idx = okDiTypes.indexOf(it);
+ if (idx != -1)
+ okDiTypes.remove(idx);
+ }
+ }
+
+ QVector<QPair<QString, int>> dataInputList;
+ for (auto &it : qAsConst(m_dataInputs))
+ dataInputList.append({it->name, it->type});
+
+ m_dataInputChooserView->setMatchingTypes(okDiTypes);
+ m_dataInputChooserView->setData(dataInputList, m_currentDataInputName);
+ }
+}
+
+void CDataInputListDlg::replaceDatainputs(const QModelIndexList &selectedBindings,
+ const QString &newDIName)
+{
+ QList<qt3dsdm::Qt3DSDMInstanceHandle> elementHandles;
+ for (auto it : selectedBindings)
+ elementHandles.append(m_ui->elementInfo->model()->data(it, Qt::UserRole + 1).toInt());
+
+ // Update bindings for the internal list held by this dialog.
+ for (auto it : qAsConst(elementHandles)) {
+ CDataInputDialogItem *oldDI = *m_dataInputs.find(m_currentDataInputName);
+ CDataInputDialogItem *newDI = *m_dataInputs.find(newDIName);
+
+ QVector<CDataInputDialogItem::ControlledItem> ctrlItems;
+ oldDI->getInstCtrldItems(it.GetHandleValue(), ctrlItems);
+
+ for (auto &it2 : qAsConst(ctrlItems)) {
+ oldDI->ctrldElems.removeAll(it2);
+ newDI->ctrldElems.append(it2);
+ }
+ }
+ // Make direct changes to object properties. Transaction that is opened will be
+ // closed when the dialog ultimately exits.
+ g_StudioApp.GetCore()->GetDoc()->ReplaceDatainput(m_currentDataInputName,
+ newDIName, elementHandles);
+}