forked from qt-creator/qt-creator
QmlDesigner: Import a model to the default JSON model group
Task-number: QDS-11312 Change-Id: Ib97273a15db4c7fb46ed01debf99602b71ec7630 Reviewed-by: Mahmoud Badri <mahmoud.badri@qt.io>
This commit is contained in:
@@ -24,15 +24,8 @@ Item {
|
|||||||
warningDialog.open()
|
warningDialog.open()
|
||||||
}
|
}
|
||||||
|
|
||||||
JsonImport {
|
ImportDialog {
|
||||||
id: jsonImporter
|
id: importDialog
|
||||||
|
|
||||||
backendValue: root.rootView
|
|
||||||
anchors.centerIn: parent
|
|
||||||
}
|
|
||||||
|
|
||||||
CsvImport {
|
|
||||||
id: csvImporter
|
|
||||||
|
|
||||||
backendValue: root.rootView
|
backendValue: root.rootView
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
@@ -82,26 +75,14 @@ Item {
|
|||||||
leftPadding: 15
|
leftPadding: 15
|
||||||
}
|
}
|
||||||
|
|
||||||
IconTextButton {
|
HelperWidgets.IconButton {
|
||||||
Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
|
Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
|
||||||
|
|
||||||
icon: StudioTheme.Constants.import_medium
|
icon: StudioTheme.Constants.import_medium
|
||||||
text: qsTr("JSON")
|
tooltip: qsTr("Import a model")
|
||||||
tooltip: qsTr("Import JSON")
|
|
||||||
radius: StudioTheme.Values.smallRadius
|
radius: StudioTheme.Values.smallRadius
|
||||||
|
|
||||||
onClicked: jsonImporter.open()
|
onClicked: importDialog.open()
|
||||||
}
|
|
||||||
|
|
||||||
IconTextButton {
|
|
||||||
Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
|
|
||||||
|
|
||||||
icon: StudioTheme.Constants.import_medium
|
|
||||||
text: qsTr("CSV")
|
|
||||||
tooltip: qsTr("Import CSV")
|
|
||||||
radius: StudioTheme.Values.smallRadius
|
|
||||||
|
|
||||||
onClicked: csvImporter.open()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -13,7 +13,7 @@ import StudioTheme as StudioTheme
|
|||||||
StudioControls.Dialog {
|
StudioControls.Dialog {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
title: qsTr("Import A CSV File")
|
title: qsTr("Import a model")
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
closePolicy: Popup.CloseOnEscape
|
closePolicy: Popup.CloseOnEscape
|
||||||
modal: true
|
modal: true
|
||||||
@@ -23,8 +23,8 @@ StudioControls.Dialog {
|
|||||||
property bool fileExists: false
|
property bool fileExists: false
|
||||||
|
|
||||||
onOpened: {
|
onOpened: {
|
||||||
collectionName.text = "Collection_"
|
collectionName.text = "Model"
|
||||||
fileName.text = qsTr("New CSV File")
|
fileName.text = qsTr("Model path")
|
||||||
fileName.selectAll()
|
fileName.selectAll()
|
||||||
fileName.forceActiveFocus()
|
fileName.forceActiveFocus()
|
||||||
}
|
}
|
||||||
@@ -40,6 +40,14 @@ StudioControls.Dialog {
|
|||||||
|
|
||||||
PlatformWidgets.FileDialog {
|
PlatformWidgets.FileDialog {
|
||||||
id: fileDialog
|
id: fileDialog
|
||||||
|
nameFilters : ["All Model Files (*.json *.csv)",
|
||||||
|
"JSON Files (*.json)",
|
||||||
|
"Comma-Separated Values (*.csv)"]
|
||||||
|
|
||||||
|
title: qsTr("Select a model file")
|
||||||
|
fileMode: PlatformWidgets.FileDialog.OpenFile
|
||||||
|
acceptLabel: qsTr("Open")
|
||||||
|
|
||||||
onAccepted: fileName.text = fileDialog.file
|
onAccepted: fileName.text = fileDialog.file
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -61,7 +69,7 @@ StudioControls.Dialog {
|
|||||||
spacing: 2
|
spacing: 2
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
text: qsTr("File name: ")
|
text: qsTr("File name")
|
||||||
color: StudioTheme.Values.themeTextColor
|
color: StudioTheme.Values.themeTextColor
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -80,11 +88,11 @@ StudioControls.Dialog {
|
|||||||
translationIndicator.visible: false
|
translationIndicator.visible: false
|
||||||
validator: fileNameValidator
|
validator: fileNameValidator
|
||||||
|
|
||||||
Keys.onEnterPressed: btnCreate.onClicked()
|
Keys.onEnterPressed: btnImport.onClicked()
|
||||||
Keys.onReturnPressed: btnCreate.onClicked()
|
Keys.onReturnPressed: btnImport.onClicked()
|
||||||
Keys.onEscapePressed: root.reject()
|
Keys.onEscapePressed: root.reject()
|
||||||
|
|
||||||
onTextChanged: root.fileExists = root.backendValue.isCsvFile(fileName.text)
|
onTextChanged: root.fileExists = root.backendValue.isValidUrlToImport(fileName.text)
|
||||||
}
|
}
|
||||||
|
|
||||||
HelperWidgets.Button {
|
HelperWidgets.Button {
|
||||||
@@ -100,7 +108,7 @@ StudioControls.Dialog {
|
|||||||
Spacer {}
|
Spacer {}
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
text: qsTr("The model name: ")
|
text: qsTr("The model name")
|
||||||
color: StudioTheme.Values.themeTextColor
|
color: StudioTheme.Values.themeTextColor
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -115,8 +123,8 @@ StudioControls.Dialog {
|
|||||||
regularExpression: /^\w+$/
|
regularExpression: /^\w+$/
|
||||||
}
|
}
|
||||||
|
|
||||||
Keys.onEnterPressed: btnCreate.onClicked()
|
Keys.onEnterPressed: btnImport.onClicked()
|
||||||
Keys.onReturnPressed: btnCreate.onClicked()
|
Keys.onReturnPressed: btnImport.onClicked()
|
||||||
Keys.onEscapePressed: root.reject()
|
Keys.onEscapePressed: root.reject()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -179,15 +187,17 @@ StudioControls.Dialog {
|
|||||||
Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
|
Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
|
||||||
|
|
||||||
HelperWidgets.Button {
|
HelperWidgets.Button {
|
||||||
id: btnCreate
|
id: btnImport
|
||||||
|
|
||||||
text: qsTr("Import")
|
text: qsTr("Import")
|
||||||
enabled: root.fileExists && collectionName.text !== ""
|
enabled: root.fileExists && collectionName.text !== ""
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
let csvLoaded = root.backendValue.loadCsvFile(fileName.text, collectionName.text)
|
let collectionImported = root.backendValue.importCollectionToDataStore(
|
||||||
|
collectionName.text,
|
||||||
|
fileName.text)
|
||||||
|
|
||||||
if (csvLoaded)
|
if (collectionImported)
|
||||||
root.accept()
|
root.accept()
|
||||||
else
|
else
|
||||||
creationFailedDialog.open()
|
creationFailedDialog.open()
|
@@ -1,145 +0,0 @@
|
|||||||
// Copyright (C) 2023 The Qt Company Ltd.
|
|
||||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
|
||||||
|
|
||||||
import QtQuick
|
|
||||||
import QtQuick.Controls
|
|
||||||
import QtQuick.Layouts
|
|
||||||
import QtQuickDesignerTheme 1.0
|
|
||||||
import Qt.labs.platform as PlatformWidgets
|
|
||||||
import HelperWidgets 2.0 as HelperWidgets
|
|
||||||
import StudioControls 1.0 as StudioControls
|
|
||||||
import StudioTheme as StudioTheme
|
|
||||||
|
|
||||||
StudioControls.Dialog {
|
|
||||||
id: root
|
|
||||||
|
|
||||||
title: qsTr("Import Models")
|
|
||||||
anchors.centerIn: parent
|
|
||||||
closePolicy: Popup.CloseOnEscape
|
|
||||||
modal: true
|
|
||||||
|
|
||||||
required property var backendValue
|
|
||||||
property bool fileExists: false
|
|
||||||
|
|
||||||
onOpened: {
|
|
||||||
fileName.text = qsTr("New JSON File")
|
|
||||||
fileName.selectAll()
|
|
||||||
fileName.forceActiveFocus()
|
|
||||||
}
|
|
||||||
|
|
||||||
onRejected: {
|
|
||||||
fileName.text = ""
|
|
||||||
}
|
|
||||||
|
|
||||||
RegularExpressionValidator {
|
|
||||||
id: fileNameValidator
|
|
||||||
regularExpression: /^(\w[^*><?|]*)[^/\\:*><?|]$/
|
|
||||||
}
|
|
||||||
|
|
||||||
PlatformWidgets.FileDialog {
|
|
||||||
id: fileDialog
|
|
||||||
onAccepted: fileName.text = fileDialog.file
|
|
||||||
}
|
|
||||||
|
|
||||||
Message {
|
|
||||||
id: creationFailedDialog
|
|
||||||
|
|
||||||
title: qsTr("Could not load the file")
|
|
||||||
message: qsTr("An error occurred while trying to load the file.")
|
|
||||||
|
|
||||||
onClosed: root.reject()
|
|
||||||
}
|
|
||||||
|
|
||||||
contentItem: ColumnLayout {
|
|
||||||
spacing: 2
|
|
||||||
|
|
||||||
Text {
|
|
||||||
text: qsTr("File name: ")
|
|
||||||
color: StudioTheme.Values.themeTextColor
|
|
||||||
}
|
|
||||||
|
|
||||||
RowLayout {
|
|
||||||
spacing: StudioTheme.Values.sectionRowSpacing
|
|
||||||
|
|
||||||
Layout.fillWidth: true
|
|
||||||
|
|
||||||
StudioControls.TextField {
|
|
||||||
id: fileName
|
|
||||||
|
|
||||||
Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter
|
|
||||||
Layout.fillWidth: true
|
|
||||||
|
|
||||||
actionIndicator.visible: false
|
|
||||||
translationIndicator.visible: false
|
|
||||||
validator: fileNameValidator
|
|
||||||
|
|
||||||
Keys.onEnterPressed: btnCreate.onClicked()
|
|
||||||
Keys.onReturnPressed: btnCreate.onClicked()
|
|
||||||
Keys.onEscapePressed: root.reject()
|
|
||||||
|
|
||||||
onTextChanged: root.fileExists = root.backendValue.isJsonFile(fileName.text)
|
|
||||||
}
|
|
||||||
|
|
||||||
HelperWidgets.Button {
|
|
||||||
id: fileDialogButton
|
|
||||||
|
|
||||||
Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
|
|
||||||
|
|
||||||
text: qsTr("Open")
|
|
||||||
onClicked: fileDialog.open()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Item { // spacer
|
|
||||||
implicitWidth: 1
|
|
||||||
implicitHeight: StudioTheme.Values.controlLabelGap
|
|
||||||
}
|
|
||||||
|
|
||||||
Label {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
|
|
||||||
padding: 5
|
|
||||||
text: qsTr("File name cannot be empty.")
|
|
||||||
wrapMode: Label.WordWrap
|
|
||||||
color: StudioTheme.Values.themeTextColor
|
|
||||||
visible: fileName.text === ""
|
|
||||||
|
|
||||||
background: Rectangle {
|
|
||||||
color: "transparent"
|
|
||||||
border.width: StudioTheme.Values.border
|
|
||||||
border.color: StudioTheme.Values.themeWarning
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Item { // spacer
|
|
||||||
implicitWidth: 1
|
|
||||||
implicitHeight: StudioTheme.Values.sectionColumnSpacing
|
|
||||||
}
|
|
||||||
|
|
||||||
RowLayout {
|
|
||||||
spacing: StudioTheme.Values.sectionRowSpacing
|
|
||||||
|
|
||||||
Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
|
|
||||||
|
|
||||||
HelperWidgets.Button {
|
|
||||||
id: btnCreate
|
|
||||||
|
|
||||||
text: qsTr("Import")
|
|
||||||
enabled: root.fileExists
|
|
||||||
onClicked: {
|
|
||||||
let jsonLoaded = root.backendValue.loadJsonFile(fileName.text)
|
|
||||||
|
|
||||||
if (jsonLoaded)
|
|
||||||
root.accept()
|
|
||||||
else
|
|
||||||
creationFailedDialog.open()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
HelperWidgets.Button {
|
|
||||||
text: qsTr("Cancel")
|
|
||||||
onClicked: root.reject()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@@ -839,6 +839,7 @@ extend_qtc_plugin(QmlDesigner
|
|||||||
collectiondetailssortfiltermodel.cpp collectiondetailssortfiltermodel.h
|
collectiondetailssortfiltermodel.cpp collectiondetailssortfiltermodel.h
|
||||||
collectioneditorconstants.h
|
collectioneditorconstants.h
|
||||||
collectioneditorutils.cpp collectioneditorutils.h
|
collectioneditorutils.cpp collectioneditorutils.h
|
||||||
|
collectionimporttools.cpp collectionimporttools.h
|
||||||
collectionlistmodel.cpp collectionlistmodel.h
|
collectionlistmodel.cpp collectionlistmodel.h
|
||||||
collectionsourcemodel.cpp collectionsourcemodel.h
|
collectionsourcemodel.cpp collectionsourcemodel.h
|
||||||
collectionview.cpp collectionview.h
|
collectionview.cpp collectionview.h
|
||||||
|
@@ -72,6 +72,18 @@ bool variantIslessThan(const QVariant &a, const QVariant &b, CollectionDetails::
|
|||||||
return std::visit(LessThanVisitor{}, valueToVariant(a, type), valueToVariant(b, type));
|
return std::visit(LessThanVisitor{}, valueToVariant(a, type), valueToVariant(b, type));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SourceFormat getSourceCollectionFormat(const ModelNode &node)
|
||||||
|
{
|
||||||
|
using namespace QmlDesigner;
|
||||||
|
if (node.type() == CollectionEditor::JSONCOLLECTIONMODEL_TYPENAME)
|
||||||
|
return CollectionEditor::SourceFormat::Json;
|
||||||
|
|
||||||
|
if (node.type() == CollectionEditor::CSVCOLLECTIONMODEL_TYPENAME)
|
||||||
|
return CollectionEditor::SourceFormat::Csv;
|
||||||
|
|
||||||
|
return CollectionEditor::SourceFormat::Unknown;
|
||||||
|
}
|
||||||
|
|
||||||
QString getSourceCollectionType(const ModelNode &node)
|
QString getSourceCollectionType(const ModelNode &node)
|
||||||
{
|
{
|
||||||
using namespace QmlDesigner;
|
using namespace QmlDesigner;
|
||||||
|
@@ -4,6 +4,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "collectiondetails.h"
|
#include "collectiondetails.h"
|
||||||
|
#include "collectioneditorconstants.h"
|
||||||
|
|
||||||
QT_BEGIN_NAMESPACE
|
QT_BEGIN_NAMESPACE
|
||||||
class QJsonArray;
|
class QJsonArray;
|
||||||
@@ -13,6 +14,8 @@ namespace QmlDesigner::CollectionEditor {
|
|||||||
|
|
||||||
bool variantIslessThan(const QVariant &a, const QVariant &b, CollectionDetails::DataType type);
|
bool variantIslessThan(const QVariant &a, const QVariant &b, CollectionDetails::DataType type);
|
||||||
|
|
||||||
|
SourceFormat getSourceCollectionFormat(const QmlDesigner::ModelNode &node);
|
||||||
|
|
||||||
QString getSourceCollectionType(const QmlDesigner::ModelNode &node);
|
QString getSourceCollectionType(const QmlDesigner::ModelNode &node);
|
||||||
|
|
||||||
QString getSourceCollectionPath(const QmlDesigner::ModelNode &node);
|
QString getSourceCollectionPath(const QmlDesigner::ModelNode &node);
|
||||||
|
@@ -0,0 +1,105 @@
|
|||||||
|
// 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 "collectionimporttools.h"
|
||||||
|
|
||||||
|
#include <QFile>
|
||||||
|
#include <QJsonArray>
|
||||||
|
#include <QJsonDocument>
|
||||||
|
#include <QJsonObject>
|
||||||
|
#include <QJsonParseError>
|
||||||
|
#include <QJsonValue>
|
||||||
|
#include <QUrl>
|
||||||
|
|
||||||
|
namespace QmlDesigner::CollectionEditor::ImportTools {
|
||||||
|
|
||||||
|
QJsonArray loadAsSingleJsonCollection(const QUrl &url)
|
||||||
|
{
|
||||||
|
QFile file(url.isLocalFile() ? url.toLocalFile() : url.toString());
|
||||||
|
QJsonArray collection;
|
||||||
|
QByteArray jsonData;
|
||||||
|
if (file.open(QFile::ReadOnly))
|
||||||
|
jsonData = file.readAll();
|
||||||
|
|
||||||
|
file.close();
|
||||||
|
if (jsonData.isEmpty())
|
||||||
|
return {};
|
||||||
|
|
||||||
|
QJsonParseError parseError;
|
||||||
|
QJsonDocument document = QJsonDocument::fromJson(jsonData, &parseError);
|
||||||
|
if (parseError.error != QJsonParseError::NoError)
|
||||||
|
return {};
|
||||||
|
|
||||||
|
auto refineJsonArray = [](const QJsonArray &array) -> QJsonArray {
|
||||||
|
QJsonArray resultArray;
|
||||||
|
for (const QJsonValue &collectionData : array) {
|
||||||
|
if (!collectionData.isObject())
|
||||||
|
resultArray.push_back(collectionData);
|
||||||
|
}
|
||||||
|
return resultArray;
|
||||||
|
};
|
||||||
|
|
||||||
|
if (document.isArray()) {
|
||||||
|
collection = refineJsonArray(document.array());
|
||||||
|
} else if (document.isObject()) {
|
||||||
|
QJsonObject documentObject = document.object();
|
||||||
|
const QStringList mainKeys = documentObject.keys();
|
||||||
|
|
||||||
|
bool arrayFound = false;
|
||||||
|
for (const QString &key : mainKeys) {
|
||||||
|
const QJsonValue &value = documentObject.value(key);
|
||||||
|
if (value.isArray()) {
|
||||||
|
arrayFound = true;
|
||||||
|
collection = refineJsonArray(value.toArray());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!arrayFound) {
|
||||||
|
QJsonObject singleObject;
|
||||||
|
for (const QString &key : mainKeys) {
|
||||||
|
const QJsonValue value = documentObject.value(key);
|
||||||
|
|
||||||
|
if (!value.isObject())
|
||||||
|
singleObject.insert(key, value);
|
||||||
|
}
|
||||||
|
collection.push_back(singleObject);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return collection;
|
||||||
|
}
|
||||||
|
|
||||||
|
QJsonArray loadAsCsvCollection(const QUrl &url)
|
||||||
|
{
|
||||||
|
QFile sourceFile(url.isLocalFile() ? url.toLocalFile() : url.toString());
|
||||||
|
QStringList headers;
|
||||||
|
QJsonArray elements;
|
||||||
|
|
||||||
|
if (sourceFile.open(QFile::ReadOnly)) {
|
||||||
|
QTextStream stream(&sourceFile);
|
||||||
|
|
||||||
|
if (!stream.atEnd())
|
||||||
|
headers = stream.readLine().split(',');
|
||||||
|
|
||||||
|
for (QString &header : headers)
|
||||||
|
header = header.trimmed();
|
||||||
|
|
||||||
|
if (!headers.isEmpty()) {
|
||||||
|
while (!stream.atEnd()) {
|
||||||
|
const QStringList recordDataList = stream.readLine().split(',');
|
||||||
|
int column = -1;
|
||||||
|
QJsonObject recordData;
|
||||||
|
for (const QString &cellData : recordDataList) {
|
||||||
|
if (++column == headers.size())
|
||||||
|
break;
|
||||||
|
recordData.insert(headers.at(column), cellData);
|
||||||
|
}
|
||||||
|
elements.append(recordData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return elements;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace QmlDesigner::CollectionEditor::ImportTools
|
@@ -0,0 +1,16 @@
|
|||||||
|
// Copyright (C) 2023 The Qt Company Ltd.
|
||||||
|
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
QT_BEGIN_NAMESPACE
|
||||||
|
class QJsonArray;
|
||||||
|
class QUrl;
|
||||||
|
QT_END_NAMESPACE
|
||||||
|
|
||||||
|
namespace QmlDesigner::CollectionEditor::ImportTools {
|
||||||
|
|
||||||
|
QJsonArray loadAsSingleJsonCollection(const QUrl &url);
|
||||||
|
QJsonArray loadAsCsvCollection(const QUrl &url);
|
||||||
|
|
||||||
|
} // namespace QmlDesigner::CollectionEditor::ImportTools
|
@@ -268,6 +268,7 @@ bool CollectionSourceModel::collectionExists(const ModelNode &node, const QStrin
|
|||||||
|
|
||||||
bool CollectionSourceModel::addCollectionToSource(const ModelNode &node,
|
bool CollectionSourceModel::addCollectionToSource(const ModelNode &node,
|
||||||
const QString &collectionName,
|
const QString &collectionName,
|
||||||
|
const QJsonArray &newCollectionData,
|
||||||
QString *errorString)
|
QString *errorString)
|
||||||
{
|
{
|
||||||
auto returnError = [errorString](const QString &msg) -> bool {
|
auto returnError = [errorString](const QString &msg) -> bool {
|
||||||
@@ -284,7 +285,7 @@ bool CollectionSourceModel::addCollectionToSource(const ModelNode &node,
|
|||||||
return returnError(tr("Node should be a JSON model."));
|
return returnError(tr("Node should be a JSON model."));
|
||||||
|
|
||||||
if (collectionExists(node, collectionName))
|
if (collectionExists(node, collectionName))
|
||||||
return returnError(tr("Model does not exist."));
|
return returnError(tr("A model with the identical name already exists."));
|
||||||
|
|
||||||
QString sourceFileAddress = CollectionEditor::getSourceCollectionPath(node);
|
QString sourceFileAddress = CollectionEditor::getSourceCollectionPath(node);
|
||||||
|
|
||||||
@@ -305,7 +306,7 @@ bool CollectionSourceModel::addCollectionToSource(const ModelNode &node,
|
|||||||
|
|
||||||
if (document.isObject()) {
|
if (document.isObject()) {
|
||||||
QJsonObject sourceObject = document.object();
|
QJsonObject sourceObject = document.object();
|
||||||
sourceObject.insert(collectionName, CollectionEditor::defaultCollectionArray());
|
sourceObject.insert(collectionName, newCollectionData);
|
||||||
document.setObject(sourceObject);
|
document.setObject(sourceObject);
|
||||||
if (!jsonFile.resize(0))
|
if (!jsonFile.resize(0))
|
||||||
return returnError(tr("Can't clean \"%1\".").arg(sourceFileInfo.absoluteFilePath()));
|
return returnError(tr("Can't clean \"%1\".").arg(sourceFileInfo.absoluteFilePath()));
|
||||||
|
@@ -54,6 +54,7 @@ public:
|
|||||||
bool collectionExists(const ModelNode &node, const QString &collectionName) const;
|
bool collectionExists(const ModelNode &node, const QString &collectionName) const;
|
||||||
bool addCollectionToSource(const ModelNode &node,
|
bool addCollectionToSource(const ModelNode &node,
|
||||||
const QString &collectionName,
|
const QString &collectionName,
|
||||||
|
const QJsonArray &newCollectionData,
|
||||||
QString *errorString = nullptr);
|
QString *errorString = nullptr);
|
||||||
|
|
||||||
ModelNode sourceNodeAt(int idx);
|
ModelNode sourceNodeAt(int idx);
|
||||||
|
@@ -23,6 +23,7 @@
|
|||||||
#include <utils/qtcassert.h>
|
#include <utils/qtcassert.h>
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
inline bool isStudioCollectionModel(const QmlDesigner::ModelNode &node)
|
inline bool isStudioCollectionModel(const QmlDesigner::ModelNode &node)
|
||||||
{
|
{
|
||||||
using namespace QmlDesigner::CollectionEditor;
|
using namespace QmlDesigner::CollectionEditor;
|
||||||
|
@@ -6,6 +6,7 @@
|
|||||||
#include "collectiondetailsmodel.h"
|
#include "collectiondetailsmodel.h"
|
||||||
#include "collectiondetailssortfiltermodel.h"
|
#include "collectiondetailssortfiltermodel.h"
|
||||||
#include "collectioneditorutils.h"
|
#include "collectioneditorutils.h"
|
||||||
|
#include "collectionimporttools.h"
|
||||||
#include "collectionsourcemodel.h"
|
#include "collectionsourcemodel.h"
|
||||||
#include "collectionview.h"
|
#include "collectionview.h"
|
||||||
#include "qmldesignerconstants.h"
|
#include "qmldesignerconstants.h"
|
||||||
@@ -188,6 +189,20 @@ bool CollectionWidget::isCsvFile(const QUrl &url) const
|
|||||||
return file.exists() && file.fileName().endsWith(".csv");
|
return file.exists() && file.fileName().endsWith(".csv");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool CollectionWidget::isValidUrlToImport(const QUrl &url) const
|
||||||
|
{
|
||||||
|
using Utils::FilePath;
|
||||||
|
FilePath fileInfo = FilePath::fromUserInput(url.isLocalFile() ? url.toLocalFile()
|
||||||
|
: url.toString());
|
||||||
|
if (fileInfo.suffix() == "json")
|
||||||
|
return isJsonFile(url);
|
||||||
|
|
||||||
|
if (fileInfo.suffix() == "csv")
|
||||||
|
return isCsvFile(url);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
bool CollectionWidget::addCollection(const QString &collectionName,
|
bool CollectionWidget::addCollection(const QString &collectionName,
|
||||||
const QString &collectionType,
|
const QString &collectionType,
|
||||||
const QUrl &sourceUrl,
|
const QUrl &sourceUrl,
|
||||||
@@ -243,7 +258,10 @@ bool CollectionWidget::addCollection(const QString &collectionName,
|
|||||||
}
|
}
|
||||||
} else if (collectionType == "json") {
|
} else if (collectionType == "json") {
|
||||||
QString errorMsg;
|
QString errorMsg;
|
||||||
bool added = m_sourceModel->addCollectionToSource(node, collectionName, &errorMsg);
|
bool added = m_sourceModel->addCollectionToSource(node,
|
||||||
|
collectionName,
|
||||||
|
CollectionEditor::defaultCollectionArray(),
|
||||||
|
&errorMsg);
|
||||||
if (!added)
|
if (!added)
|
||||||
warn(tr("Can not add a model to the JSON file"), errorMsg);
|
warn(tr("Can not add a model to the JSON file"), errorMsg);
|
||||||
return added;
|
return added;
|
||||||
@@ -252,6 +270,50 @@ bool CollectionWidget::addCollection(const QString &collectionName,
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool CollectionWidget::importToJson(const QVariant &sourceNode,
|
||||||
|
const QString &collectionName,
|
||||||
|
const QUrl &url)
|
||||||
|
{
|
||||||
|
using CollectionEditor::SourceFormat;
|
||||||
|
using Utils::FilePath;
|
||||||
|
const ModelNode node = sourceNode.value<ModelNode>();
|
||||||
|
const SourceFormat nodeFormat = CollectionEditor::getSourceCollectionFormat(node);
|
||||||
|
QTC_ASSERT(node.isValid() && nodeFormat == SourceFormat::Json, return false);
|
||||||
|
|
||||||
|
FilePath fileInfo = FilePath::fromUserInput(url.isLocalFile() ? url.toLocalFile()
|
||||||
|
: url.toString());
|
||||||
|
bool added = false;
|
||||||
|
QString errorMsg;
|
||||||
|
QJsonArray loadedCollection;
|
||||||
|
|
||||||
|
if (fileInfo.suffix() == "json")
|
||||||
|
loadedCollection = CollectionEditor::ImportTools::loadAsSingleJsonCollection(url);
|
||||||
|
else if (fileInfo.suffix() == "csv")
|
||||||
|
loadedCollection = CollectionEditor::ImportTools::loadAsCsvCollection(url);
|
||||||
|
|
||||||
|
if (!loadedCollection.isEmpty()) {
|
||||||
|
const QString newCollectionName = generateUniqueCollectionName(node, collectionName);
|
||||||
|
added = m_sourceModel->addCollectionToSource(node, newCollectionName, loadedCollection, &errorMsg);
|
||||||
|
} else {
|
||||||
|
errorMsg = tr("The imported model is empty or is not supported.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!added)
|
||||||
|
warn(tr("Can not add a model to the JSON file"), errorMsg);
|
||||||
|
return added;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CollectionWidget::importCollectionToDataStore(const QString &collectionName, const QUrl &url)
|
||||||
|
{
|
||||||
|
using Utils::FilePath;
|
||||||
|
const ModelNode node = dataStoreNode();
|
||||||
|
if (node.isValid())
|
||||||
|
return importToJson(QVariant::fromValue(node), collectionName, url);
|
||||||
|
|
||||||
|
warn(tr("Can not import to the main model"), tr("The data store is not available."));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
void CollectionWidget::assignSourceNodeToSelectedItem(const QVariant &sourceNode)
|
void CollectionWidget::assignSourceNodeToSelectedItem(const QVariant &sourceNode)
|
||||||
{
|
{
|
||||||
ModelNode sourceModel = sourceNode.value<ModelNode>();
|
ModelNode sourceModel = sourceNode.value<ModelNode>();
|
||||||
@@ -268,6 +330,16 @@ void CollectionWidget::assignSourceNodeToSelectedItem(const QVariant &sourceNode
|
|||||||
CollectionEditor::assignCollectionSourceToNode(m_view, targetNode, sourceModel);
|
CollectionEditor::assignCollectionSourceToNode(m_view, targetNode, sourceModel);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ModelNode CollectionWidget::dataStoreNode() const
|
||||||
|
{
|
||||||
|
for (int i = 0; i < m_sourceModel->rowCount(); ++i) {
|
||||||
|
const ModelNode node = m_sourceModel->sourceNodeAt(i);
|
||||||
|
if (CollectionEditor::getSourceCollectionFormat(node) == CollectionEditor::SourceFormat::Json)
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
void CollectionWidget::warn(const QString &title, const QString &body)
|
void CollectionWidget::warn(const QString &title, const QString &body)
|
||||||
{
|
{
|
||||||
QMetaObject::invokeMethod(m_quickWidget->rootObject(),
|
QMetaObject::invokeMethod(m_quickWidget->rootObject(),
|
||||||
@@ -285,4 +357,20 @@ void CollectionWidget::setTargetNodeSelected(bool selected)
|
|||||||
emit targetNodeSelectedChanged(m_targetNodeSelected);
|
emit targetNodeSelectedChanged(m_targetNodeSelected);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString CollectionWidget::generateUniqueCollectionName(const ModelNode &node, const QString &name)
|
||||||
|
{
|
||||||
|
if (!m_sourceModel->collectionExists(node, name))
|
||||||
|
return name;
|
||||||
|
|
||||||
|
static QRegularExpression reg("^(?<mainName>[\\w\\d\\.\\_\\-]+)\\_(?<number>\\d+)$");
|
||||||
|
QRegularExpressionMatch match = reg.match(name);
|
||||||
|
if (match.hasMatch()) {
|
||||||
|
int nextNumber = match.captured("number").toInt() + 1;
|
||||||
|
return generateUniqueCollectionName(
|
||||||
|
node, QString("%1_%2").arg(match.captured("mainName")).arg(nextNumber));
|
||||||
|
} else {
|
||||||
|
return generateUniqueCollectionName(node, QString("%1_1").arg(name));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace QmlDesigner
|
} // namespace QmlDesigner
|
||||||
|
@@ -38,13 +38,22 @@ public:
|
|||||||
Q_INVOKABLE bool loadCsvFile(const QUrl &url, const QString &collectionName = {});
|
Q_INVOKABLE bool loadCsvFile(const QUrl &url, const QString &collectionName = {});
|
||||||
Q_INVOKABLE bool isJsonFile(const QUrl &url) const;
|
Q_INVOKABLE bool isJsonFile(const QUrl &url) const;
|
||||||
Q_INVOKABLE bool isCsvFile(const QUrl &url) const;
|
Q_INVOKABLE bool isCsvFile(const QUrl &url) const;
|
||||||
|
Q_INVOKABLE bool isValidUrlToImport(const QUrl &url) const;
|
||||||
Q_INVOKABLE bool addCollection(const QString &collectionName,
|
Q_INVOKABLE bool addCollection(const QString &collectionName,
|
||||||
const QString &collectionType,
|
const QString &collectionType,
|
||||||
const QUrl &sourceUrl,
|
const QUrl &sourceUrl,
|
||||||
const QVariant &sourceNode);
|
const QVariant &sourceNode);
|
||||||
|
|
||||||
|
Q_INVOKABLE bool importToJson(const QVariant &sourceNode,
|
||||||
|
const QString &collectionName,
|
||||||
|
const QUrl &url);
|
||||||
|
|
||||||
|
Q_INVOKABLE bool importCollectionToDataStore(const QString &collectionName, const QUrl &url);
|
||||||
|
|
||||||
Q_INVOKABLE void assignSourceNodeToSelectedItem(const QVariant &sourceNode);
|
Q_INVOKABLE void assignSourceNodeToSelectedItem(const QVariant &sourceNode);
|
||||||
|
|
||||||
|
Q_INVOKABLE ModelNode dataStoreNode() const;
|
||||||
|
|
||||||
void warn(const QString &title, const QString &body);
|
void warn(const QString &title, const QString &body);
|
||||||
void setTargetNodeSelected(bool selected);
|
void setTargetNodeSelected(bool selected);
|
||||||
|
|
||||||
@@ -52,6 +61,8 @@ signals:
|
|||||||
void targetNodeSelectedChanged(bool);
|
void targetNodeSelectedChanged(bool);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
QString generateUniqueCollectionName(const ModelNode &node, const QString &name);
|
||||||
|
|
||||||
QPointer<CollectionView> m_view;
|
QPointer<CollectionView> m_view;
|
||||||
QPointer<CollectionSourceModel> m_sourceModel;
|
QPointer<CollectionSourceModel> m_sourceModel;
|
||||||
QPointer<CollectionDetailsModel> m_collectionDetailsModel;
|
QPointer<CollectionDetailsModel> m_collectionDetailsModel;
|
||||||
|
Reference in New Issue
Block a user