2023-08-25 11:28:29 +03:00
|
|
|
// Copyright (C) 2023 The Qt Company Ltd.
|
|
|
|
|
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
|
|
|
|
|
|
|
|
|
#include "collectionview.h"
|
2023-09-22 10:50:06 +03:00
|
|
|
|
2023-10-12 16:34:06 +03:00
|
|
|
#include "collectiondetailsmodel.h"
|
2023-09-22 10:50:06 +03:00
|
|
|
#include "collectioneditorconstants.h"
|
2023-11-01 17:54:12 +02:00
|
|
|
#include "collectioneditorutils.h"
|
2023-09-26 17:08:20 +03:00
|
|
|
#include "collectionsourcemodel.h"
|
2023-08-25 11:28:29 +03:00
|
|
|
#include "collectionwidget.h"
|
2023-11-23 12:43:29 +02:00
|
|
|
#include "datastoremodelnode.h"
|
2023-08-25 11:28:29 +03:00
|
|
|
#include "designmodecontext.h"
|
2023-09-22 10:50:06 +03:00
|
|
|
#include "nodeabstractproperty.h"
|
2023-08-25 11:28:29 +03:00
|
|
|
#include "nodemetainfo.h"
|
|
|
|
|
#include "qmldesignerplugin.h"
|
|
|
|
|
#include "variantproperty.h"
|
|
|
|
|
|
2023-11-23 12:43:29 +02:00
|
|
|
#include <projectexplorer/project.h>
|
|
|
|
|
#include <projectexplorer/projectexplorer.h>
|
|
|
|
|
#include <projectexplorer/projectmanager.h>
|
|
|
|
|
|
2023-08-25 11:28:29 +03:00
|
|
|
#include <QJsonArray>
|
|
|
|
|
#include <QJsonDocument>
|
|
|
|
|
#include <QJsonObject>
|
|
|
|
|
|
|
|
|
|
#include <coreplugin/icore.h>
|
|
|
|
|
#include <utils/algorithm.h>
|
|
|
|
|
#include <utils/qtcassert.h>
|
|
|
|
|
|
|
|
|
|
namespace {
|
2023-11-18 01:23:24 +02:00
|
|
|
|
2023-09-22 10:50:06 +03:00
|
|
|
inline bool isStudioCollectionModel(const QmlDesigner::ModelNode &node)
|
2023-08-25 11:28:29 +03:00
|
|
|
{
|
2023-09-22 10:50:06 +03:00
|
|
|
using namespace QmlDesigner::CollectionEditor;
|
|
|
|
|
return node.metaInfo().typeName() == JSONCOLLECTIONMODEL_TYPENAME
|
|
|
|
|
|| node.metaInfo().typeName() == CSVCOLLECTIONMODEL_TYPENAME;
|
2023-08-25 11:28:29 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} // namespace
|
|
|
|
|
|
|
|
|
|
namespace QmlDesigner {
|
|
|
|
|
|
|
|
|
|
CollectionView::CollectionView(ExternalDependenciesInterface &externalDependencies)
|
|
|
|
|
: AbstractView(externalDependencies)
|
2023-11-23 12:43:29 +02:00
|
|
|
, m_dataStore(std::make_unique<DataStoreModelNode>())
|
|
|
|
|
|
|
|
|
|
{
|
|
|
|
|
connect(ProjectExplorer::ProjectManager::instance(),
|
|
|
|
|
&ProjectExplorer::ProjectManager::startupProjectChanged,
|
|
|
|
|
this,
|
|
|
|
|
&CollectionView::resetDataStoreNode);
|
|
|
|
|
resetDataStoreNode();
|
|
|
|
|
}
|
2023-08-25 11:28:29 +03:00
|
|
|
|
|
|
|
|
bool CollectionView::hasWidget() const
|
|
|
|
|
{
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QmlDesigner::WidgetInfo CollectionView::widgetInfo()
|
|
|
|
|
{
|
|
|
|
|
if (m_widget.isNull()) {
|
|
|
|
|
m_widget = new CollectionWidget(this);
|
2023-11-29 17:30:31 +02:00
|
|
|
m_widget->setMinimumSize(m_widget->minimumSizeHint());
|
2023-08-25 11:28:29 +03:00
|
|
|
|
|
|
|
|
auto collectionEditorContext = new Internal::CollectionEditorContext(m_widget.data());
|
|
|
|
|
Core::ICore::addContextObject(collectionEditorContext);
|
2023-09-26 17:08:20 +03:00
|
|
|
CollectionSourceModel *sourceModel = m_widget->sourceModel().data();
|
2023-09-08 15:14:42 +03:00
|
|
|
|
2023-09-22 10:50:06 +03:00
|
|
|
connect(sourceModel,
|
|
|
|
|
&CollectionSourceModel::collectionSelected,
|
|
|
|
|
this,
|
2023-12-01 12:42:58 +02:00
|
|
|
[this](const QString &collection) {
|
|
|
|
|
m_widget->collectionDetailsModel()->loadCollection(dataStoreNode(), collection);
|
2023-09-22 10:50:06 +03:00
|
|
|
});
|
2023-11-23 12:43:29 +02:00
|
|
|
|
|
|
|
|
connect(sourceModel, &CollectionSourceModel::isEmptyChanged, this, [this](bool isEmpty) {
|
|
|
|
|
if (isEmpty)
|
|
|
|
|
m_widget->collectionDetailsModel()->loadCollection({}, {});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
connect(sourceModel,
|
2023-12-01 12:42:58 +02:00
|
|
|
&CollectionSourceModel::collectionNamesInitialized,
|
2023-11-23 12:43:29 +02:00
|
|
|
this,
|
2023-12-01 12:42:58 +02:00
|
|
|
[this](const QStringList &collectionNames) {
|
|
|
|
|
m_dataStore->setCollectionNames(collectionNames);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
connect(sourceModel,
|
|
|
|
|
&CollectionSourceModel::collectionRenamed,
|
|
|
|
|
this,
|
|
|
|
|
[this](const QString &oldName, const QString &newName) {
|
|
|
|
|
m_dataStore->renameCollection(oldName, newName);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
connect(sourceModel,
|
|
|
|
|
&CollectionSourceModel::collectionRemoved,
|
|
|
|
|
this,
|
|
|
|
|
[this](const QString &collectionName) {
|
|
|
|
|
m_dataStore->removeCollection(collectionName);
|
2023-11-23 12:43:29 +02:00
|
|
|
});
|
2023-08-25 11:28:29 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return createWidgetInfo(m_widget.data(),
|
|
|
|
|
"CollectionEditor",
|
|
|
|
|
WidgetInfo::LeftPane,
|
|
|
|
|
0,
|
2023-11-08 16:56:14 +02:00
|
|
|
tr("Model Editor"),
|
|
|
|
|
tr("Model Editor view"));
|
2023-08-25 11:28:29 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CollectionView::modelAttached(Model *model)
|
|
|
|
|
{
|
|
|
|
|
AbstractView::modelAttached(model);
|
2023-11-23 12:43:29 +02:00
|
|
|
resetDataStoreNode();
|
2023-08-25 11:28:29 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CollectionView::nodeReparented(const ModelNode &node,
|
2023-09-22 10:50:06 +03:00
|
|
|
[[maybe_unused]] const NodeAbstractProperty &newPropertyParent,
|
|
|
|
|
[[maybe_unused]] const NodeAbstractProperty &oldPropertyParent,
|
2023-08-25 11:28:29 +03:00
|
|
|
[[maybe_unused]] PropertyChangeFlags propertyChange)
|
|
|
|
|
{
|
2023-09-22 10:50:06 +03:00
|
|
|
if (!isStudioCollectionModel(node))
|
2023-08-25 11:28:29 +03:00
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
refreshModel();
|
|
|
|
|
|
2023-09-22 10:50:06 +03:00
|
|
|
m_widget->sourceModel()->selectSource(node);
|
2023-08-25 11:28:29 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CollectionView::nodeAboutToBeRemoved(const ModelNode &removedNode)
|
|
|
|
|
{
|
2023-11-08 16:56:14 +02:00
|
|
|
// removing the model lib node
|
2023-09-22 10:50:06 +03:00
|
|
|
if (isStudioCollectionModel(removedNode))
|
|
|
|
|
m_widget->sourceModel()->removeSource(removedNode);
|
2023-08-25 11:28:29 +03:00
|
|
|
}
|
|
|
|
|
|
2023-09-22 10:50:06 +03:00
|
|
|
void CollectionView::nodeRemoved(const ModelNode &removedNode,
|
|
|
|
|
[[maybe_unused]] const NodeAbstractProperty &parentProperty,
|
2023-08-25 11:28:29 +03:00
|
|
|
[[maybe_unused]] PropertyChangeFlags propertyChange)
|
|
|
|
|
{
|
2023-09-22 10:50:06 +03:00
|
|
|
if (isStudioCollectionModel(removedNode))
|
|
|
|
|
m_widget->sourceModel()->updateSelectedSource(true);
|
2023-08-25 11:28:29 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CollectionView::variantPropertiesChanged(const QList<VariantProperty> &propertyList,
|
|
|
|
|
[[maybe_unused]] PropertyChangeFlags propertyChange)
|
|
|
|
|
{
|
|
|
|
|
for (const VariantProperty &property : propertyList) {
|
|
|
|
|
ModelNode node(property.parentModelNode());
|
2023-09-22 10:50:06 +03:00
|
|
|
if (isStudioCollectionModel(node)) {
|
2023-08-25 11:28:29 +03:00
|
|
|
if (property.name() == "objectName")
|
2023-09-26 17:08:20 +03:00
|
|
|
m_widget->sourceModel()->updateNodeName(node);
|
2023-09-22 10:50:06 +03:00
|
|
|
else if (property.name() == CollectionEditor::SOURCEFILE_PROPERTY)
|
|
|
|
|
m_widget->sourceModel()->updateNodeSource(node);
|
2023-08-25 11:28:29 +03:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CollectionView::selectedNodesChanged(const QList<ModelNode> &selectedNodeList,
|
|
|
|
|
[[maybe_unused]] const QList<ModelNode> &lastSelectedNodeList)
|
|
|
|
|
{
|
2023-11-01 17:54:12 +02:00
|
|
|
QList<ModelNode> selectedCollectionNodes = Utils::filtered(selectedNodeList,
|
2023-09-22 10:50:06 +03:00
|
|
|
&isStudioCollectionModel);
|
2023-08-25 11:28:29 +03:00
|
|
|
|
2023-11-01 17:54:12 +02:00
|
|
|
bool singleNonCollectionNodeSelected = selectedNodeList.size() == 1
|
|
|
|
|
&& selectedCollectionNodes.isEmpty();
|
|
|
|
|
|
|
|
|
|
bool singleSelectedHasModelProperty = false;
|
|
|
|
|
if (singleNonCollectionNodeSelected) {
|
|
|
|
|
const ModelNode selectedNode = selectedNodeList.first();
|
|
|
|
|
singleSelectedHasModelProperty = CollectionEditor::canAcceptCollectionAsModel(selectedNode);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
m_widget->setTargetNodeSelected(singleSelectedHasModelProperty);
|
|
|
|
|
|
2023-11-08 16:56:14 +02:00
|
|
|
// More than one model is selected. So ignore them
|
2023-11-01 17:54:12 +02:00
|
|
|
if (selectedCollectionNodes.size() > 1)
|
2023-08-25 11:28:29 +03:00
|
|
|
return;
|
|
|
|
|
|
2023-11-08 16:56:14 +02:00
|
|
|
if (selectedCollectionNodes.size() == 1) { // If exactly one model is selected
|
2023-11-01 17:54:12 +02:00
|
|
|
m_widget->sourceModel()->selectSource(selectedCollectionNodes.first());
|
2023-08-25 11:28:29 +03:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-09-22 10:50:06 +03:00
|
|
|
void CollectionView::addResource(const QUrl &url, const QString &name, const QString &type)
|
|
|
|
|
{
|
|
|
|
|
executeInTransaction(Q_FUNC_INFO, [this, &url, &name, &type]() {
|
|
|
|
|
ensureStudioModelImport();
|
2023-11-13 18:28:10 +02:00
|
|
|
QString sourceAddress;
|
|
|
|
|
if (url.isLocalFile()) {
|
|
|
|
|
Utils::FilePath fp = QmlDesignerPlugin::instance()->currentDesignDocument()->fileName().parentDir();
|
|
|
|
|
sourceAddress = Utils::FilePath::calcRelativePath(url.toLocalFile(),
|
|
|
|
|
fp.absoluteFilePath().toString());
|
|
|
|
|
} else {
|
|
|
|
|
sourceAddress = url.toString();
|
|
|
|
|
}
|
|
|
|
|
|
2023-09-22 10:50:06 +03:00
|
|
|
const NodeMetaInfo resourceMetaInfo = type.compare("json", Qt::CaseInsensitive) == 0
|
|
|
|
|
? jsonCollectionMetaInfo()
|
|
|
|
|
: csvCollectionMetaInfo();
|
|
|
|
|
ModelNode resourceNode = createModelNode(resourceMetaInfo.typeName(),
|
|
|
|
|
resourceMetaInfo.majorVersion(),
|
|
|
|
|
resourceMetaInfo.minorVersion());
|
|
|
|
|
VariantProperty sourceProperty = resourceNode.variantProperty(
|
|
|
|
|
CollectionEditor::SOURCEFILE_PROPERTY);
|
|
|
|
|
VariantProperty nameProperty = resourceNode.variantProperty("objectName");
|
|
|
|
|
sourceProperty.setValue(sourceAddress);
|
|
|
|
|
nameProperty.setValue(name);
|
2023-11-08 16:56:14 +02:00
|
|
|
resourceNode.setIdWithoutRefactoring(model()->generateIdFromName(name, "model"));
|
2023-09-22 10:50:06 +03:00
|
|
|
rootModelNode().defaultNodeAbstractProperty().reparentHere(resourceNode);
|
2023-08-25 11:28:29 +03:00
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
2023-12-01 12:42:58 +02:00
|
|
|
void CollectionView::assignCollectionToSelectedNode(const QString &collectionName)
|
|
|
|
|
{
|
|
|
|
|
QTC_ASSERT(dataStoreNode() && hasSingleSelectedModelNode(), return);
|
|
|
|
|
m_dataStore->assignCollectionToNode(this, singleSelectedModelNode(), collectionName);
|
|
|
|
|
}
|
|
|
|
|
|
2023-10-18 11:41:18 +03:00
|
|
|
void CollectionView::registerDeclarativeType()
|
|
|
|
|
{
|
|
|
|
|
CollectionDetails::registerDeclarativeType();
|
2023-10-27 15:21:50 +03:00
|
|
|
CollectionJsonSourceFilterModel::registerDeclarativeType();
|
2023-10-18 11:41:18 +03:00
|
|
|
}
|
|
|
|
|
|
2023-11-23 12:43:29 +02:00
|
|
|
void CollectionView::resetDataStoreNode()
|
|
|
|
|
{
|
|
|
|
|
m_dataStore->reloadModel();
|
|
|
|
|
refreshModel();
|
|
|
|
|
}
|
|
|
|
|
|
2023-11-27 23:50:53 +02:00
|
|
|
ModelNode CollectionView::dataStoreNode() const
|
|
|
|
|
{
|
|
|
|
|
return m_dataStore->modelNode();
|
|
|
|
|
}
|
|
|
|
|
|
2023-08-25 11:28:29 +03:00
|
|
|
void CollectionView::refreshModel()
|
|
|
|
|
{
|
|
|
|
|
if (!model())
|
|
|
|
|
return;
|
|
|
|
|
|
2023-11-08 16:56:14 +02:00
|
|
|
// Load Model Groups
|
2023-11-23 12:43:29 +02:00
|
|
|
ModelNodes collectionSourceNodes;
|
|
|
|
|
|
|
|
|
|
if (ModelNode dataStore = m_dataStore->modelNode())
|
|
|
|
|
collectionSourceNodes << dataStore;
|
|
|
|
|
|
2023-10-06 12:39:33 +03:00
|
|
|
m_widget->sourceModel()->setSources(collectionSourceNodes);
|
2023-08-25 11:28:29 +03:00
|
|
|
}
|
|
|
|
|
|
2023-09-22 10:50:06 +03:00
|
|
|
NodeMetaInfo CollectionView::jsonCollectionMetaInfo() const
|
2023-08-25 11:28:29 +03:00
|
|
|
{
|
2023-09-22 10:50:06 +03:00
|
|
|
return model()->metaInfo(CollectionEditor::JSONCOLLECTIONMODEL_TYPENAME);
|
2023-08-25 11:28:29 +03:00
|
|
|
}
|
|
|
|
|
|
2023-09-22 10:50:06 +03:00
|
|
|
NodeMetaInfo CollectionView::csvCollectionMetaInfo() const
|
2023-08-25 11:28:29 +03:00
|
|
|
{
|
2023-09-22 10:50:06 +03:00
|
|
|
return model()->metaInfo(CollectionEditor::CSVCOLLECTIONMODEL_TYPENAME);
|
2023-08-25 11:28:29 +03:00
|
|
|
}
|
|
|
|
|
|
2023-09-22 10:50:06 +03:00
|
|
|
void CollectionView::ensureStudioModelImport()
|
2023-08-25 11:28:29 +03:00
|
|
|
{
|
|
|
|
|
executeInTransaction(__FUNCTION__, [&] {
|
2023-09-22 10:50:06 +03:00
|
|
|
Import import = Import::createLibraryImport(CollectionEditor::COLLECTIONMODEL_IMPORT);
|
|
|
|
|
try {
|
|
|
|
|
if (!model()->hasImport(import, true, true))
|
|
|
|
|
model()->changeImports({import}, {});
|
|
|
|
|
} catch (const Exception &) {
|
|
|
|
|
QTC_ASSERT(false, return);
|
|
|
|
|
}
|
2023-08-25 11:28:29 +03:00
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} // namespace QmlDesigner
|