From ac17e0e2ad47f16c80eb233725c4a603f6053acb Mon Sep 17 00:00:00 2001 From: Eike Ziller Date: Wed, 22 Feb 2023 10:17:19 +0100 Subject: [PATCH] Convert Examples model to FilePath Change-Id: I56219d2f9516662b32d45fd9b2108a0ad34113cc Reviewed-by: David Schulz (cherry picked from commit 255afd45bf614f20b5c0ccee092dcf28c50a1827) Reviewed-by: hjk Reviewed-by: Christian Stenger --- src/plugins/qtsupport/exampleslistmodel.cpp | 15 ++- src/plugins/qtsupport/examplesparser.cpp | 96 +++++++++---------- src/plugins/qtsupport/examplesparser.h | 15 +-- .../qtsupport/gettingstartedwelcomepage.cpp | 73 +++++++------- .../qtsupport/gettingstartedwelcomepage.h | 9 +- 5 files changed, 101 insertions(+), 107 deletions(-) diff --git a/src/plugins/qtsupport/exampleslistmodel.cpp b/src/plugins/qtsupport/exampleslistmodel.cpp index 700248e1f97..40196934d46 100644 --- a/src/plugins/qtsupport/exampleslistmodel.cpp +++ b/src/plugins/qtsupport/exampleslistmodel.cpp @@ -298,14 +298,15 @@ static bool isValidExampleOrDemo(ExampleItem *item) doesn't have any namespace */ QString reason; bool ok = true; - if (!item->hasSourceCode || !QFileInfo::exists(item->projectPath)) { + if (!item->hasSourceCode || !item->projectPath.exists()) { ok = false; - reason = QString::fromLatin1("projectPath \"%1\" empty or does not exist").arg(item->projectPath); + reason = QString::fromLatin1("projectPath \"%1\" empty or does not exist") + .arg(item->projectPath.toUserOutput()); } else if (item->imageUrl.startsWith(invalidPrefix) || !QUrl(item->imageUrl).isValid()) { ok = false; reason = QString::fromLatin1("imageUrl \"%1\" not valid").arg(item->imageUrl); } else if (!item->docUrl.isEmpty() - && (item->docUrl.startsWith(invalidPrefix) || !QUrl(item->docUrl).isValid())) { + && (item->docUrl.startsWith(invalidPrefix) || !QUrl(item->docUrl).isValid())) { ok = false; reason = QString::fromLatin1("docUrl \"%1\" non-empty but not valid").arg(item->docUrl); } @@ -331,13 +332,17 @@ void ExamplesViewController::updateExamples() QList items; for (const QString &exampleSource : sources) { + const auto manifest = FilePath::fromUserInput(exampleSource); if (debugExamples()) { qWarning() << QString::fromLatin1("Reading file \"%1\"...") - .arg(QFileInfo(exampleSource).absoluteFilePath()); + .arg(manifest.absoluteFilePath().toUserOutput()); } const expected_str> result - = parseExamples(exampleSource, examplesInstallPath, demosInstallPath, m_isExamples); + = parseExamples(manifest, + FilePath::fromUserInput(examplesInstallPath), + FilePath::fromUserInput(demosInstallPath), + m_isExamples); if (!result) { if (debugExamples()) { qWarning() << "ERROR: Could not read examples from" << exampleSource << ":" diff --git a/src/plugins/qtsupport/examplesparser.cpp b/src/plugins/qtsupport/examplesparser.cpp index 2bd50bf8102..1d7211b6367 100644 --- a/src/plugins/qtsupport/examplesparser.cpp +++ b/src/plugins/qtsupport/examplesparser.cpp @@ -4,11 +4,9 @@ #include "examplesparser.h" #include +#include #include -#include -#include -#include #include #include @@ -16,16 +14,15 @@ using namespace Utils; namespace QtSupport::Internal { -static QString relativeOrInstallPath(const QString &path, - const QString &manifestPath, - const QString &installPath) +static FilePath relativeOrInstallPath(const FilePath &path, + const FilePath &manifestPath, + const FilePath &installPath) { - const QChar slash = QLatin1Char('/'); - const QString relativeResolvedPath = manifestPath + slash + path; - const QString installResolvedPath = installPath + slash + path; - if (QFile::exists(relativeResolvedPath)) + const FilePath relativeResolvedPath = manifestPath.resolvePath(path); + const FilePath installResolvedPath = installPath.resolvePath(path); + if (relativeResolvedPath.exists()) return relativeResolvedPath; - if (QFile::exists(installResolvedPath)) + if (installResolvedPath.exists()) return installResolvedPath; // doesn't exist, just return relative return relativeResolvedPath; @@ -47,12 +44,11 @@ static QStringList trimStringList(const QStringList &stringlist) } static QList parseExamples(QXmlStreamReader *reader, - const QString &projectsOffset, - const QString &examplesInstallPath) + const FilePath &projectsOffset, + const FilePath &examplesInstallPath) { QList result; std::unique_ptr item; - const QChar slash = QLatin1Char('/'); while (!reader->atEnd()) { switch (reader->readNext()) { case QXmlStreamReader::StartElement: @@ -61,7 +57,8 @@ static QList parseExamples(QXmlStreamReader *reader, item->type = Example; QXmlStreamAttributes attributes = reader->attributes(); item->name = attributes.value(QLatin1String("name")).toString(); - item->projectPath = attributes.value(QLatin1String("projectPath")).toString(); + item->projectPath = FilePath::fromUserInput( + attributes.value(QLatin1String("projectPath")).toString()); item->hasSourceCode = !item->projectPath.isEmpty(); item->projectPath = relativeOrInstallPath(item->projectPath, projectsOffset, @@ -75,9 +72,9 @@ static QList parseExamples(QXmlStreamReader *reader, } else if (reader->name() == QLatin1String("fileToOpen")) { const QString mainFileAttribute = reader->attributes().value(QLatin1String("mainFile")).toString(); - const QString filePath - = relativeOrInstallPath(reader->readElementText( - QXmlStreamReader::ErrorOnUnexpectedElement), + const FilePath filePath + = relativeOrInstallPath(FilePath::fromUserInput(reader->readElementText( + QXmlStreamReader::ErrorOnUnexpectedElement)), projectsOffset, examplesInstallPath); item->filesToOpen.append(filePath); @@ -88,8 +85,8 @@ static QList parseExamples(QXmlStreamReader *reader, reader->readElementText(QXmlStreamReader::ErrorOnUnexpectedElement)); } else if (reader->name() == QLatin1String("dependency")) { item->dependencies.append( - projectsOffset + slash - + reader->readElementText(QXmlStreamReader::ErrorOnUnexpectedElement)); + projectsOffset + / reader->readElementText(QXmlStreamReader::ErrorOnUnexpectedElement)); } else if (reader->name() == QLatin1String("tags")) { item->tags = trimStringList( reader->readElementText(QXmlStreamReader::ErrorOnUnexpectedElement) @@ -115,12 +112,11 @@ static QList parseExamples(QXmlStreamReader *reader, } static QList parseDemos(QXmlStreamReader *reader, - const QString &projectsOffset, - const QString &demosInstallPath) + const FilePath &projectsOffset, + const FilePath &demosInstallPath) { QList result; std::unique_ptr item; - const QChar slash = QLatin1Char('/'); while (!reader->atEnd()) { switch (reader->readNext()) { case QXmlStreamReader::StartElement: @@ -129,7 +125,8 @@ static QList parseDemos(QXmlStreamReader *reader, item->type = Demo; QXmlStreamAttributes attributes = reader->attributes(); item->name = attributes.value(QLatin1String("name")).toString(); - item->projectPath = attributes.value(QLatin1String("projectPath")).toString(); + item->projectPath = FilePath::fromUserInput( + attributes.value(QLatin1String("projectPath")).toString()); item->hasSourceCode = !item->projectPath.isEmpty(); item->projectPath = relativeOrInstallPath(item->projectPath, projectsOffset, @@ -141,8 +138,8 @@ static QList parseDemos(QXmlStreamReader *reader, == QLatin1String("true"); } else if (reader->name() == QLatin1String("fileToOpen")) { item->filesToOpen.append( - relativeOrInstallPath(reader->readElementText( - QXmlStreamReader::ErrorOnUnexpectedElement), + relativeOrInstallPath(FilePath::fromUserInput(reader->readElementText( + QXmlStreamReader::ErrorOnUnexpectedElement)), projectsOffset, demosInstallPath)); } else if (reader->name() == QLatin1String("description")) { @@ -150,8 +147,8 @@ static QList parseDemos(QXmlStreamReader *reader, reader->readElementText(QXmlStreamReader::ErrorOnUnexpectedElement)); } else if (reader->name() == QLatin1String("dependency")) { item->dependencies.append( - projectsOffset + slash - + reader->readElementText(QXmlStreamReader::ErrorOnUnexpectedElement)); + projectsOffset + / reader->readElementText(QXmlStreamReader::ErrorOnUnexpectedElement)); } else if (reader->name() == QLatin1String("tags")) { item->tags = reader->readElementText(QXmlStreamReader::ErrorOnUnexpectedElement) .split(QLatin1Char(',')); @@ -171,11 +168,10 @@ static QList parseDemos(QXmlStreamReader *reader, return result; } -static QList parseTutorials(QXmlStreamReader *reader, const QString &projectsOffset) +static QList parseTutorials(QXmlStreamReader *reader, const FilePath &projectsOffset) { QList result; std::unique_ptr item = std::make_unique(); - const QChar slash = QLatin1Char('/'); while (!reader->atEnd()) { switch (reader->readNext()) { case QXmlStreamReader::StartElement: @@ -184,10 +180,9 @@ static QList parseTutorials(QXmlStreamReader *reader, const QStri item->type = Tutorial; QXmlStreamAttributes attributes = reader->attributes(); item->name = attributes.value(QLatin1String("name")).toString(); - item->projectPath = attributes.value(QLatin1String("projectPath")).toString(); + item->projectPath = projectsOffset + / attributes.value(QLatin1String("projectPath")).toString(); item->hasSourceCode = !item->projectPath.isEmpty(); - item->projectPath.prepend(slash); - item->projectPath.prepend(projectsOffset); item->imageUrl = Utils::StyleHelper::dpiSpecificImageFile( attributes.value(QLatin1String("imageUrl")).toString()); QPixmapCache::remove(item->imageUrl); @@ -198,15 +193,15 @@ static QList parseTutorials(QXmlStreamReader *reader, const QStri item->videoLength = attributes.value(QLatin1String("videoLength")).toString(); } else if (reader->name() == QLatin1String("fileToOpen")) { item->filesToOpen.append( - projectsOffset + slash - + reader->readElementText(QXmlStreamReader::ErrorOnUnexpectedElement)); + projectsOffset + / reader->readElementText(QXmlStreamReader::ErrorOnUnexpectedElement)); } else if (reader->name() == QLatin1String("description")) { item->description = fixStringForTags( reader->readElementText(QXmlStreamReader::ErrorOnUnexpectedElement)); } else if (reader->name() == QLatin1String("dependency")) { item->dependencies.append( - projectsOffset + slash - + reader->readElementText(QXmlStreamReader::ErrorOnUnexpectedElement)); + projectsOffset + / reader->readElementText(QXmlStreamReader::ErrorOnUnexpectedElement)); } else if (reader->name() == QLatin1String("tags")) { item->tags = reader->readElementText(QXmlStreamReader::ErrorOnUnexpectedElement) .split(QLatin1Char(',')); @@ -225,31 +220,28 @@ static QList parseTutorials(QXmlStreamReader *reader, const QStri return result; } -expected_str> parseExamples(const QString &manifest, - const QString &examplesInstallPath, - const QString &demosInstallPath, +expected_str> parseExamples(const FilePath &manifest, + const FilePath &examplesInstallPath, + const FilePath &demosInstallPath, const bool examples) { - QFile exampleFile(manifest); - if (!exampleFile.open(QIODevice::ReadOnly)) - return make_unexpected(QString("Could not open file \"%1\"").arg(manifest)); + const expected_str contents = manifest.fileContents(); + if (!contents) + return make_unexpected(contents.error()); - QFileInfo fi(manifest); - QString offsetPath = fi.path(); - QDir examplesDir(offsetPath); - QDir demosDir(offsetPath); + const FilePath path = manifest.parentDir(); QList items; - QXmlStreamReader reader(&exampleFile); + QXmlStreamReader reader(*contents); while (!reader.atEnd()) { switch (reader.readNext()) { case QXmlStreamReader::StartElement: if (examples && reader.name() == QLatin1String("examples")) - items += parseExamples(&reader, examplesDir.path(), examplesInstallPath); + items += parseExamples(&reader, path, examplesInstallPath); else if (examples && reader.name() == QLatin1String("demos")) - items += parseDemos(&reader, demosDir.path(), demosInstallPath); + items += parseDemos(&reader, path, demosInstallPath); else if (!examples && reader.name() == QLatin1String("tutorials")) - items += parseTutorials(&reader, examplesDir.path()); + items += parseTutorials(&reader, path); break; default: // nothing break; @@ -259,7 +251,7 @@ expected_str> parseExamples(const QString &manifest, if (reader.hasError()) { qDeleteAll(items); return make_unexpected(QString("Could not parse file \"%1\" as XML document: %2:%3: %4") - .arg(manifest) + .arg(manifest.toUserOutput()) .arg(reader.lineNumber()) .arg(reader.columnNumber()) .arg(reader.errorString())); diff --git a/src/plugins/qtsupport/examplesparser.h b/src/plugins/qtsupport/examplesparser.h index d08a8da11bc..494a42b1473 100644 --- a/src/plugins/qtsupport/examplesparser.h +++ b/src/plugins/qtsupport/examplesparser.h @@ -5,6 +5,7 @@ #include #include +#include namespace QtSupport::Internal { @@ -13,11 +14,11 @@ enum InstructionalType { Example = 0, Demo, Tutorial }; class ExampleItem : public Core::ListItem { public: - QString projectPath; + Utils::FilePath projectPath; QString docUrl; - QStringList filesToOpen; - QString mainFile; /* file to be visible after opening filesToOpen */ - QStringList dependencies; + Utils::FilePaths filesToOpen; + Utils::FilePath mainFile; /* file to be visible after opening filesToOpen */ + Utils::FilePaths dependencies; InstructionalType type; int difficulty = 0; bool hasSourceCode = false; @@ -28,9 +29,9 @@ public: QStringList platforms; }; -Utils::expected_str> parseExamples(const QString &manifest, - const QString &examplesInstallPath, - const QString &demosInstallPath, +Utils::expected_str> parseExamples(const Utils::FilePath &manifest, + const Utils::FilePath &examplesInstallPath, + const Utils::FilePath &demosInstallPath, bool examples); } // namespace QtSupport::Internal diff --git a/src/plugins/qtsupport/gettingstartedwelcomepage.cpp b/src/plugins/qtsupport/gettingstartedwelcomepage.cpp index fea56880356..bf19233c211 100644 --- a/src/plugins/qtsupport/gettingstartedwelcomepage.cpp +++ b/src/plugins/qtsupport/gettingstartedwelcomepage.cpp @@ -69,16 +69,18 @@ Id ExamplesWelcomePage::id() const return m_showExamples ? "Examples" : "Tutorials"; } -QString ExamplesWelcomePage::copyToAlternativeLocation(const QFileInfo& proFileInfo, QStringList &filesToOpen, const QStringList& dependencies) +FilePath ExamplesWelcomePage::copyToAlternativeLocation(const FilePath &proFile, + FilePaths &filesToOpen, + const FilePaths &dependencies) { - const QString projectDir = proFileInfo.canonicalPath(); + const FilePath projectDir = proFile.canonicalPath().parentDir(); QDialog d(ICore::dialogParent()); auto lay = new QGridLayout(&d); auto descrLbl = new QLabel; d.setWindowTitle(Tr::tr("Copy Project to writable Location?")); descrLbl->setTextFormat(Qt::RichText); descrLbl->setWordWrap(false); - const QString nativeProjectDir = QDir::toNativeSeparators(projectDir); + const QString nativeProjectDir = projectDir.toUserOutput(); descrLbl->setText(QString::fromLatin1("
%1
").arg(nativeProjectDir)); descrLbl->setMinimumWidth(descrLbl->sizeHint().width()); descrLbl->setWordWrap(true); @@ -95,9 +97,10 @@ QString ExamplesWelcomePage::copyToAlternativeLocation(const QFileInfo& proFileI txt->setBuddy(chooser); chooser->setExpectedKind(PathChooser::ExistingDirectory); chooser->setHistoryCompleter(QLatin1String("Qt.WritableExamplesDir.History")); - const QString defaultRootDirectory = DocumentManager::projectsDirectory().toString(); + const FilePath defaultRootDirectory = DocumentManager::projectsDirectory(); QtcSettings *settings = ICore::settings(); - chooser->setFilePath(FilePath::fromSettings(settings->value(C_FALLBACK_ROOT, defaultRootDirectory))); + chooser->setFilePath( + FilePath::fromSettings(settings->value(C_FALLBACK_ROOT, defaultRootDirectory.toVariant()))); lay->addWidget(txt, 1, 0); lay->addWidget(chooser, 1, 1); enum { Copy = QDialog::Accepted + 1, Keep = QDialog::Accepted + 2 }; @@ -111,35 +114,32 @@ QString ExamplesWelcomePage::copyToAlternativeLocation(const QFileInfo& proFileI connect(chooser, &PathChooser::validChanged, copyBtn, &QWidget::setEnabled); int code = d.exec(); if (code == Copy) { - QString exampleDirName = proFileInfo.dir().dirName(); - QString destBaseDir = chooser->filePath().toString(); + const QString exampleDirName = projectDir.fileName(); + const FilePath destBaseDir = chooser->filePath(); settings->setValueWithDefault(C_FALLBACK_ROOT, destBaseDir, defaultRootDirectory); - QDir toDirWithExamplesDir(destBaseDir); - if (toDirWithExamplesDir.cd(exampleDirName)) { - toDirWithExamplesDir.cdUp(); // step out, just to not be in the way + const FilePath targetDir = destBaseDir / exampleDirName; + if (targetDir.exists()) { QMessageBox::warning(ICore::dialogParent(), Tr::tr("Cannot Use Location"), Tr::tr("The specified location already exists. " "Please specify a valid location."), QMessageBox::Ok, QMessageBox::NoButton); - return QString(); + return {}; } else { - QString targetDir = destBaseDir + QLatin1Char('/') + exampleDirName; - - expected_str result - = FilePath::fromString(projectDir).copyRecursively(FilePath::fromString(targetDir)); + expected_str result = projectDir.copyRecursively(targetDir); if (result) { // set vars to new location - const QStringList::Iterator end = filesToOpen.end(); - for (QStringList::Iterator it = filesToOpen.begin(); it != end; ++it) - it->replace(projectDir, targetDir); + const FilePaths::Iterator end = filesToOpen.end(); + for (FilePaths::Iterator it = filesToOpen.begin(); it != end; ++it) { + const FilePath relativePath = it->relativeChildPath(projectDir); + *it = targetDir.resolvePath(relativePath); + } - for (const QString &dependency : dependencies) { - const FilePath targetFile = FilePath::fromString(targetDir) - .pathAppended(QDir(dependency).dirName()); - result = FilePath::fromString(dependency).copyRecursively(targetFile); + for (const FilePath &dependency : dependencies) { + const FilePath targetFile = targetDir.pathAppended(dependency.fileName()); + result = dependency.copyRecursively(targetFile); if (!result) { QMessageBox::warning(ICore::dialogParent(), Tr::tr("Cannot Copy Project"), @@ -148,7 +148,7 @@ QString ExamplesWelcomePage::copyToAlternativeLocation(const QFileInfo& proFileI } } - return targetDir + QLatin1Char('/') + proFileInfo.fileName(); + return targetDir / proFile.fileName(); } else { QMessageBox::warning(ICore::dialogParent(), Tr::tr("Cannot Copy Project"), @@ -157,46 +157,43 @@ QString ExamplesWelcomePage::copyToAlternativeLocation(const QFileInfo& proFileI } } if (code == Keep) - return proFileInfo.absoluteFilePath(); - return QString(); + return proFile.absoluteFilePath(); + return {}; } void ExamplesWelcomePage::openProject(const ExampleItem *item) { using namespace ProjectExplorer; - QString proFile = item->projectPath; + FilePath proFile = item->projectPath; if (proFile.isEmpty()) return; - QStringList filesToOpen = item->filesToOpen; + FilePaths filesToOpen = item->filesToOpen; if (!item->mainFile.isEmpty()) { // ensure that the main file is opened on top (i.e. opened last) filesToOpen.removeAll(item->mainFile); filesToOpen.append(item->mainFile); } - QFileInfo proFileInfo(proFile); - if (!proFileInfo.exists()) + if (!proFile.exists()) return; // If the Qt is a distro Qt on Linux, it will not be writable, hence compilation will fail // Same if it is installed in non-writable location for other reasons - const bool needsCopy = withNtfsPermissions([proFileInfo] { - QFileInfo pathInfo(proFileInfo.path()); - return !proFileInfo.isWritable() - || !pathInfo.isWritable() /* path of .pro file */ - || !QFileInfo(pathInfo.path()).isWritable() /* shadow build directory */; + const bool needsCopy = withNtfsPermissions([proFile] { + return !proFile.isWritableFile() + || !proFile.parentDir().isWritableDir() /* path of project file */ + || !proFile.parentDir().parentDir().isWritableDir() /* shadow build directory */; }); if (needsCopy) - proFile = copyToAlternativeLocation(proFileInfo, filesToOpen, item->dependencies); + proFile = copyToAlternativeLocation(proFile, filesToOpen, item->dependencies); // don't try to load help and files if loading the help request is being cancelled if (proFile.isEmpty()) return; - ProjectExplorerPlugin::OpenProjectResult result = - ProjectExplorerPlugin::openProject(FilePath::fromString(proFile)); + ProjectExplorerPlugin::OpenProjectResult result = ProjectExplorerPlugin::openProject(proFile); if (result) { - ICore::openFiles(FileUtils::toFilePathList(filesToOpen)); + ICore::openFiles(filesToOpen); ModeManager::activateMode(Core::Constants::MODE_EDIT); QUrl docUrl = QUrl::fromUserInput(item->docUrl); if (docUrl.isValid()) diff --git a/src/plugins/qtsupport/gettingstartedwelcomepage.h b/src/plugins/qtsupport/gettingstartedwelcomepage.h index 2781666a923..aa07f669167 100644 --- a/src/plugins/qtsupport/gettingstartedwelcomepage.h +++ b/src/plugins/qtsupport/gettingstartedwelcomepage.h @@ -4,10 +4,7 @@ #pragma once #include - -QT_BEGIN_NAMESPACE -class QFileInfo; -QT_END_NAMESPACE +#include namespace QtSupport { namespace Internal { @@ -29,7 +26,9 @@ public: static void openProject(const ExampleItem *item); private: - static QString copyToAlternativeLocation(const QFileInfo &fileInfo, QStringList &filesToOpen, const QStringList &dependencies); + static Utils::FilePath copyToAlternativeLocation(const Utils::FilePath &fileInfo, + Utils::FilePaths &filesToOpen, + const Utils::FilePaths &dependencies); const bool m_showExamples; };