QmlDesigner: Use FileSaver instead of file in Model Editor

* Also the warning is removed for internal changes on local files

Fixes: QDS-11903
Change-Id: Ic35966888433a2bd9b1cc10a47bb7ed51280ffb1
Reviewed-by: <github-actions-qt-creator@cristianadam.eu>
Reviewed-by: Qt CI Patch Build Bot <ci_patchbuild_bot@qt.io>
Reviewed-by: Shrief Gabr <shrief.gabr@qt.io>
Reviewed-by: Miikka Heikkinen <miikka.heikkinen@qt.io>
Reviewed-by: Mahmoud Badri <mahmoud.badri@qt.io>
This commit is contained in:
Ali Kianian
2024-02-14 15:39:33 +02:00
parent 4c79e1b8d8
commit efe114872c
7 changed files with 65 additions and 131 deletions

View File

@@ -414,7 +414,6 @@ bool CollectionDetailsModel::saveDataStoreCollections()
const ModelNode node = m_currentCollection.reference().node; const ModelNode node = m_currentCollection.reference().node;
const Utils::FilePath path = CollectionEditorUtils::dataStoreJsonFilePath(); const Utils::FilePath path = CollectionEditorUtils::dataStoreJsonFilePath();
Utils::FileReader fileData; Utils::FileReader fileData;
Utils::FileSaver sourceFile(path);
if (!fileData.fetch(path)) { if (!fileData.fetch(path)) {
qWarning() << Q_FUNC_INFO << "Cannot read the json file:" << fileData.errorString(); qWarning() << Q_FUNC_INFO << "Cannot read the json file:" << fileData.errorString();
@@ -437,10 +436,8 @@ bool CollectionDetailsModel::saveDataStoreCollections()
} }
document.setObject(obj); document.setObject(obj);
bool saved = sourceFile.write(document.toJson());
saved &= sourceFile.finalize();
if (saved) { if (CollectionEditorUtils::writeToJsonDocument(path, document)) {
const CollectionReference currentReference = m_currentCollection.reference(); const CollectionReference currentReference = m_currentCollection.reference();
for (CollectionDetails &collection : collectionsToBeSaved) { for (CollectionDetails &collection : collectionsToBeSaved) {
collection.markSaved(); collection.markSaved();

View File

@@ -7,6 +7,7 @@
#include "nodemetainfo.h" #include "nodemetainfo.h"
#include "propertymetainfo.h" #include "propertymetainfo.h"
#include <coreplugin/documentmanager.h>
#include <coreplugin/icore.h> #include <coreplugin/icore.h>
#include <projectexplorer/project.h> #include <projectexplorer/project.h>
#include <projectexplorer/projectexplorer.h> #include <projectexplorer/projectexplorer.h>
@@ -393,4 +394,16 @@ QStringList dataTypesStringList()
return typesList; return typesList;
} }
bool writeToJsonDocument(const Utils::FilePath &path, const QJsonDocument &document, QString *errorString)
{
Core::FileChangeBlocker fileBlocker(path);
Utils::FileSaver jsonFile(path);
if (jsonFile.write(document.toJson()))
jsonFile.finalize();
if (errorString)
*errorString = jsonFile.errorString();
return !jsonFile.hasError();
}
} // namespace QmlDesigner::CollectionEditorUtils } // namespace QmlDesigner::CollectionEditorUtils

View File

@@ -29,6 +29,10 @@ Utils::FilePath dataStoreJsonFilePath();
Utils::FilePath dataStoreQmlFilePath(); Utils::FilePath dataStoreQmlFilePath();
bool writeToJsonDocument(const Utils::FilePath &path,
const QJsonDocument &document,
QString *errorString = nullptr);
bool isDataStoreNode(const ModelNode &dataStoreNode); bool isDataStoreNode(const ModelNode &dataStoreNode);
bool ensureDataStoreExists(bool &justCreated); bool ensureDataStoreExists(bool &justCreated);

View File

@@ -9,6 +9,7 @@
#include "collectionlistmodel.h" #include "collectionlistmodel.h"
#include "variantproperty.h" #include "variantproperty.h"
#include <utils/fileutils.h>
#include <utils/qtcassert.h> #include <utils/qtcassert.h>
#include <qqml.h> #include <qqml.h>
@@ -27,6 +28,8 @@ QSharedPointer<QmlDesigner::CollectionListModel> loadCollection(
{ {
using namespace QmlDesigner::CollectionEditorConstants; using namespace QmlDesigner::CollectionEditorConstants;
using namespace QmlDesigner::CollectionEditorUtils; using namespace QmlDesigner::CollectionEditorUtils;
using Utils::FilePath;
using Utils::FileReader;
QString sourceFileAddress = getSourceCollectionPath(sourceNode); QString sourceFileAddress = getSourceCollectionPath(sourceNode);
QSharedPointer<QmlDesigner::CollectionListModel> collectionsList; QSharedPointer<QmlDesigner::CollectionListModel> collectionsList;
@@ -40,12 +43,12 @@ QSharedPointer<QmlDesigner::CollectionListModel> loadCollection(
}; };
if (sourceNode.type() == JSONCOLLECTIONMODEL_TYPENAME) { if (sourceNode.type() == JSONCOLLECTIONMODEL_TYPENAME) {
QFile sourceFile(sourceFileAddress); FileReader sourceFile;
if (!sourceFile.open(QFile::ReadOnly)) if (!sourceFile.fetch(FilePath::fromUserInput(sourceFileAddress)))
return {}; return {};
QJsonParseError parseError; QJsonParseError parseError;
QJsonDocument document = QJsonDocument::fromJson(sourceFile.readAll(), &parseError); QJsonDocument document = QJsonDocument::fromJson(sourceFile.data(), &parseError);
if (parseError.error != QJsonParseError::NoError) if (parseError.error != QJsonParseError::NoError)
return {}; return {};
@@ -273,6 +276,8 @@ bool CollectionSourceModel::addCollectionToSource(const ModelNode &node,
const QJsonObject &newCollection, const QJsonObject &newCollection,
QString *errorString) QString *errorString)
{ {
using Utils::FilePath;
using Utils::FileReader;
auto returnError = [errorString](const QString &msg) -> bool { auto returnError = [errorString](const QString &msg) -> bool {
if (errorString) if (errorString)
*errorString = msg; *errorString = msg;
@@ -295,13 +300,15 @@ bool CollectionSourceModel::addCollectionToSource(const ModelNode &node,
if (!sourceFileInfo.isFile()) if (!sourceFileInfo.isFile())
return returnError(tr("Selected node must have a valid source file address")); return returnError(tr("Selected node must have a valid source file address"));
QFile jsonFile(sourceFileAddress); FilePath jsonPath = FilePath::fromUserInput(sourceFileAddress);
if (!jsonFile.open(QFile::ReadWrite)) FileReader jsonFile;
return returnError(tr("Can't read or write \"%1\".\n%2") if (!jsonFile.fetch(jsonPath)) {
.arg(sourceFileInfo.absoluteFilePath(), jsonFile.errorString())); return returnError(
tr("Can't read \"%1\".\n%2").arg(sourceFileInfo.absoluteFilePath(), jsonFile.errorString()));
}
QJsonParseError parseError; QJsonParseError parseError;
QJsonDocument document = QJsonDocument::fromJson(jsonFile.readAll(), &parseError); QJsonDocument document = QJsonDocument::fromJson(jsonFile.data(), &parseError);
if (parseError.error != QJsonParseError::NoError) if (parseError.error != QJsonParseError::NoError)
return returnError(tr("\"%1\" is corrupted.\n%2") return returnError(tr("\"%1\" is corrupted.\n%2")
.arg(sourceFileInfo.absoluteFilePath(), parseError.errorString())); .arg(sourceFileInfo.absoluteFilePath(), parseError.errorString()));
@@ -310,14 +317,8 @@ bool CollectionSourceModel::addCollectionToSource(const ModelNode &node,
QJsonObject sourceObject = document.object(); QJsonObject sourceObject = document.object();
sourceObject.insert(collectionName, newCollection); sourceObject.insert(collectionName, newCollection);
document.setObject(sourceObject); document.setObject(sourceObject);
if (!jsonFile.resize(0))
return returnError(tr("Can't clean \"%1\".").arg(sourceFileInfo.absoluteFilePath()));
QByteArray jsonData = document.toJson(); if (!CollectionEditorUtils::writeToJsonDocument(jsonPath, document))
auto writtenBytes = jsonFile.write(jsonData);
jsonFile.close();
if (writtenBytes != jsonData.size())
return returnError(tr("Can't write to \"%1\".").arg(sourceFileInfo.absoluteFilePath())); return returnError(tr("Can't write to \"%1\".").arg(sourceFileInfo.absoluteFilePath()));
updateCollectionList(index(idx)); updateCollectionList(index(idx));
@@ -417,6 +418,10 @@ void CollectionSourceModel::onSelectedCollectionChanged(CollectionListModel *col
void CollectionSourceModel::onCollectionNameChanged(CollectionListModel *collectionList, void CollectionSourceModel::onCollectionNameChanged(CollectionListModel *collectionList,
const QString &oldName, const QString &newName) const QString &oldName, const QString &newName)
{ {
using Utils::FilePath;
using Utils::FileReader;
using Utils::FileSaver;
auto emitRenameWarning = [this](const QString &msg) -> void { auto emitRenameWarning = [this](const QString &msg) -> void {
emit warning(tr("Rename Model"), msg); emit warning(tr("Rename Model"), msg);
}; };
@@ -446,15 +451,16 @@ void CollectionSourceModel::onCollectionNameChanged(CollectionListModel *collect
return; return;
} }
QFile jsonFile(sourceFileAddress); FilePath jsonPath = FilePath::fromUserInput(sourceFileAddress);
if (!jsonFile.open(QFile::ReadWrite)) { FileReader jsonFile;
emitRenameWarning(tr("Can't read or write \"%1\".\n%2") if (!jsonFile.fetch(jsonPath)) {
.arg(sourceFileInfo.absoluteFilePath(), jsonFile.errorString())); emitRenameWarning(
tr("Can't read \"%1\".\n%2").arg(sourceFileInfo.absoluteFilePath(), jsonFile.errorString()));
return; return;
} }
QJsonParseError parseError; QJsonParseError parseError;
QJsonDocument document = QJsonDocument::fromJson(jsonFile.readAll(), &parseError); QJsonDocument document = QJsonDocument::fromJson(jsonFile.data(), &parseError);
if (parseError.error != QJsonParseError::NoError) { if (parseError.error != QJsonParseError::NoError) {
emitRenameWarning(tr("\"%1\" is corrupted.\n%2") emitRenameWarning(tr("\"%1\" is corrupted.\n%2")
.arg(sourceFileInfo.absoluteFilePath(), parseError.errorString())); .arg(sourceFileInfo.absoluteFilePath(), parseError.errorString()));
@@ -484,16 +490,8 @@ void CollectionSourceModel::onCollectionNameChanged(CollectionListModel *collect
rootObject.remove(oldName); rootObject.remove(oldName);
document.setObject(rootObject); document.setObject(rootObject);
if (!jsonFile.resize(0)) {
emitRenameWarning(tr("Can't clean \"%1\".").arg(sourceFileInfo.absoluteFilePath()));
return;
}
QByteArray jsonData = document.toJson(); if (!CollectionEditorUtils::writeToJsonDocument(jsonPath, document)) {
auto writtenBytes = jsonFile.write(jsonData);
jsonFile.close();
if (writtenBytes != jsonData.size()) {
emitRenameWarning(tr("Can't write to \"%1\".").arg(sourceFileInfo.absoluteFilePath())); emitRenameWarning(tr("Can't write to \"%1\".").arg(sourceFileInfo.absoluteFilePath()));
return; return;
} }
@@ -519,6 +517,8 @@ void CollectionSourceModel::onCollectionNameChanged(CollectionListModel *collect
void CollectionSourceModel::onCollectionsRemoved(CollectionListModel *collectionList, void CollectionSourceModel::onCollectionsRemoved(CollectionListModel *collectionList,
const QStringList &removedCollections) const QStringList &removedCollections)
{ {
using Utils::FilePath;
using Utils::FileReader;
auto emitDeleteWarning = [this](const QString &msg) -> void { auto emitDeleteWarning = [this](const QString &msg) -> void {
emit warning(tr("Delete Model"), msg); emit warning(tr("Delete Model"), msg);
}; };
@@ -547,15 +547,16 @@ void CollectionSourceModel::onCollectionsRemoved(CollectionListModel *collection
return; return;
} }
QFile jsonFile(sourceFileAddress); FilePath jsonPath = FilePath::fromUserInput(sourceFileAddress);
if (!jsonFile.open(QFile::ReadWrite)) { FileReader jsonFile;
if (!jsonFile.fetch(jsonPath)) {
emitDeleteWarning(tr("Can't read or write \"%1\".\n%2") emitDeleteWarning(tr("Can't read or write \"%1\".\n%2")
.arg(sourceFileInfo.absoluteFilePath(), jsonFile.errorString())); .arg(sourceFileInfo.absoluteFilePath(), jsonFile.errorString()));
return; return;
} }
QJsonParseError parseError; QJsonParseError parseError;
QJsonDocument document = QJsonDocument::fromJson(jsonFile.readAll(), &parseError); QJsonDocument document = QJsonDocument::fromJson(jsonFile.data(), &parseError);
if (parseError.error != QJsonParseError::NoError) { if (parseError.error != QJsonParseError::NoError) {
emitDeleteWarning(tr("\"%1\" is corrupted.\n%2") emitDeleteWarning(tr("\"%1\" is corrupted.\n%2")
.arg(sourceFileInfo.absoluteFilePath(), parseError.errorString())); .arg(sourceFileInfo.absoluteFilePath(), parseError.errorString()));
@@ -578,16 +579,8 @@ void CollectionSourceModel::onCollectionsRemoved(CollectionListModel *collection
} }
document.setObject(rootObject); document.setObject(rootObject);
if (!jsonFile.resize(0)) {
emitDeleteWarning(tr("Can't clean \"%1\".").arg(sourceFileInfo.absoluteFilePath()));
return;
}
QByteArray jsonData = document.toJson(); if (!CollectionEditorUtils::writeToJsonDocument(jsonPath, document)) {
auto writtenBytes = jsonFile.write(jsonData);
jsonFile.close();
if (writtenBytes != jsonData.size()) {
emitDeleteWarning(tr("Can't write to \"%1\".").arg(sourceFileInfo.absoluteFilePath())); emitDeleteWarning(tr("Can't write to \"%1\".").arg(sourceFileInfo.absoluteFilePath()));
return; return;
} }

View File

@@ -17,7 +17,6 @@
#include <coreplugin/messagebox.h> #include <coreplugin/messagebox.h>
#include <studioquickwidget.h> #include <studioquickwidget.h>
#include <QFile>
#include <QFileInfo> #include <QFileInfo>
#include <QJsonArray> #include <QJsonArray>
#include <QJsonDocument> #include <QJsonDocument>
@@ -167,14 +166,14 @@ bool CollectionWidget::loadCsvFile(const QUrl &url, const QString &collectionNam
bool CollectionWidget::isJsonFile(const QUrl &url) const bool CollectionWidget::isJsonFile(const QUrl &url) const
{ {
QString filePath = url.isLocalFile() ? url.toLocalFile() : url.toString(); Utils::FilePath filePath = Utils::FilePath::fromUserInput(url.isLocalFile() ? url.toLocalFile()
QFile file(filePath); : url.toString());
Utils::FileReader file;
if (!file.exists() || !file.open(QFile::ReadOnly)) if (!file.fetch(filePath))
return false; return false;
QJsonParseError error; QJsonParseError error;
QJsonDocument::fromJson(file.readAll(), &error); QJsonDocument::fromJson(file.data(), &error);
if (error.error) if (error.error)
return false; return false;
@@ -184,9 +183,8 @@ bool CollectionWidget::isJsonFile(const QUrl &url) const
bool CollectionWidget::isCsvFile(const QUrl &url) const bool CollectionWidget::isCsvFile(const QUrl &url) const
{ {
QString filePath = url.isLocalFile() ? url.toLocalFile() : url.toString(); QString filePath = url.isLocalFile() ? url.toLocalFile() : url.toString();
QFile file(filePath); QFileInfo fileInfo(filePath);
return fileInfo.exists() && !fileInfo.suffix().compare("csv", Qt::CaseInsensitive);
return file.exists() && file.fileName().endsWith(".csv");
} }
bool CollectionWidget::isValidUrlToImport(const QUrl &url) const bool CollectionWidget::isValidUrlToImport(const QUrl &url) const
@@ -203,73 +201,6 @@ bool CollectionWidget::isValidUrlToImport(const QUrl &url) const
return false; return false;
} }
bool CollectionWidget::addCollection(const QString &collectionName,
const QString &collectionType,
const QUrl &sourceUrl,
const QVariant &sourceNode)
{
const ModelNode node = sourceNode.value<ModelNode>();
bool isNewCollection = !node.isValid();
if (isNewCollection) {
QString sourcePath = sourceUrl.isLocalFile() ? sourceUrl.toLocalFile() : sourceUrl.toString();
if (collectionType == "json") {
QJsonObject jsonObject;
jsonObject.insert(collectionName, CollectionEditorUtils::defaultCollection());
QFile sourceFile(sourcePath);
if (!sourceFile.open(QFile::WriteOnly)) {
warn(tr("File error"),
tr("Can not open the file to write.\n") + sourceFile.errorString());
return false;
}
sourceFile.write(QJsonDocument(jsonObject).toJson());
sourceFile.close();
bool loaded = loadJsonFile(sourcePath, collectionName);
if (!loaded)
sourceFile.remove();
return loaded;
} else if (collectionType == "csv") {
QFile sourceFile(sourcePath);
if (!sourceFile.open(QFile::WriteOnly)) {
warn(tr("File error"),
tr("Can not open the file to write.\n") + sourceFile.errorString());
return false;
}
sourceFile.write("Column1\n\n");
sourceFile.close();
bool loaded = loadCsvFile(sourcePath, collectionName);
if (!loaded)
sourceFile.remove();
return loaded;
} else if (collectionType == "existing") {
QFileInfo fileInfo(sourcePath);
if (fileInfo.suffix() == "json")
return loadJsonFile(sourcePath, collectionName);
else if (fileInfo.suffix() == "csv")
return loadCsvFile(sourcePath, collectionName);
}
} else if (collectionType == "json") {
QString errorMsg;
bool added = m_sourceModel->addCollectionToSource(node,
collectionName,
CollectionEditorUtils::defaultCollection(),
&errorMsg);
if (!added)
warn(tr("Can not add a model to the JSON file"), errorMsg);
return added;
}
return false;
}
bool CollectionWidget::importFile(const QString &collectionName, const QUrl &url) bool CollectionWidget::importFile(const QString &collectionName, const QUrl &url)
{ {
using Utils::FilePath; using Utils::FilePath;
@@ -289,15 +220,13 @@ bool CollectionWidget::importFile(const QString &collectionName, const QUrl &url
QByteArray fileContent; QByteArray fileContent;
auto loadUrlContent = [&]() -> bool { auto loadUrlContent = [&]() -> bool {
QFile file(url.isLocalFile() ? url.toLocalFile() : url.toString()); Utils::FileReader file;
if (file.fetch(fileInfo)) {
if (file.open(QFile::ReadOnly)) { fileContent = file.data();
fileContent = file.readAll();
file.close();
return true; return true;
} }
warn(tr("Import from file"), tr("Cannot import from file \"%1\"").arg(file.fileName())); warn(tr("Import from file"), tr("Cannot import from file \"%1\"").arg(fileInfo.fileName()));
return false; return false;
}; };

View File

@@ -39,10 +39,6 @@ public:
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 isValidUrlToImport(const QUrl &url) const;
Q_INVOKABLE bool addCollection(const QString &collectionName,
const QString &collectionType,
const QUrl &sourceUrl,
const QVariant &sourceNode);
Q_INVOKABLE bool importFile(const QString &collectionName, const QUrl &url); Q_INVOKABLE bool importFile(const QString &collectionName, const QUrl &url);
Q_INVOKABLE bool addCollectionToDataStore(const QString &collectionName); Q_INVOKABLE bool addCollectionToDataStore(const QString &collectionName);

View File

@@ -20,6 +20,7 @@
#include <qmljstools/qmljscodestylepreferences.h> #include <qmljstools/qmljscodestylepreferences.h>
#include <qmljstools/qmljstoolssettings.h> #include <qmljstools/qmljstoolssettings.h>
#include <coreplugin/documentmanager.h>
#include <projectexplorer/project.h> #include <projectexplorer/project.h>
#include <projectexplorer/projectexplorer.h> #include <projectexplorer/projectexplorer.h>
#include <projectexplorer/projectmanager.h> #include <projectexplorer/projectmanager.h>
@@ -301,6 +302,7 @@ void DataStoreModelNode::updateSingletonFile()
imports += QStringLiteral("import %1\n").arg(import.toString(true)); imports += QStringLiteral("import %1\n").arg(import.toString(true));
QString content = pragmaSingleTone + imports + getModelQmlText(); QString content = pragmaSingleTone + imports + getModelQmlText();
Core::DocumentManager::expectFileChange(dataStoreQmlFilePath());
FileSaver file(dataStoreQmlFilePath()); FileSaver file(dataStoreQmlFilePath());
file.write(content.toLatin1()); file.write(content.toLatin1());
file.finalize(); file.finalize();