Convert Examples model to FilePath

Change-Id: I56219d2f9516662b32d45fd9b2108a0ad34113cc
Reviewed-by: David Schulz <david.schulz@qt.io>
(cherry picked from commit 255afd45bf)
Reviewed-by: hjk <hjk@qt.io>
Reviewed-by: Christian Stenger <christian.stenger@qt.io>
This commit is contained in:
Eike Ziller
2023-02-22 10:17:19 +01:00
parent e6e9405cc9
commit ac17e0e2ad
5 changed files with 101 additions and 107 deletions

View File

@@ -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<ExampleItem *> 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<QList<ExampleItem *>> 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 << ":"

View File

@@ -4,11 +4,9 @@
#include "examplesparser.h"
#include <utils/algorithm.h>
#include <utils/filepath.h>
#include <utils/stylehelper.h>
#include <QDir>
#include <QFile>
#include <QFileInfo>
#include <QPixmapCache>
#include <QXmlStreamReader>
@@ -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<ExampleItem *> parseExamples(QXmlStreamReader *reader,
const QString &projectsOffset,
const QString &examplesInstallPath)
const FilePath &projectsOffset,
const FilePath &examplesInstallPath)
{
QList<ExampleItem *> result;
std::unique_ptr<ExampleItem> item;
const QChar slash = QLatin1Char('/');
while (!reader->atEnd()) {
switch (reader->readNext()) {
case QXmlStreamReader::StartElement:
@@ -61,7 +57,8 @@ static QList<ExampleItem *> 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<ExampleItem *> 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<ExampleItem *> 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<ExampleItem *> parseExamples(QXmlStreamReader *reader,
}
static QList<ExampleItem *> parseDemos(QXmlStreamReader *reader,
const QString &projectsOffset,
const QString &demosInstallPath)
const FilePath &projectsOffset,
const FilePath &demosInstallPath)
{
QList<ExampleItem *> result;
std::unique_ptr<ExampleItem> item;
const QChar slash = QLatin1Char('/');
while (!reader->atEnd()) {
switch (reader->readNext()) {
case QXmlStreamReader::StartElement:
@@ -129,7 +125,8 @@ static QList<ExampleItem *> 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<ExampleItem *> 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<ExampleItem *> 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<ExampleItem *> parseDemos(QXmlStreamReader *reader,
return result;
}
static QList<ExampleItem *> parseTutorials(QXmlStreamReader *reader, const QString &projectsOffset)
static QList<ExampleItem *> parseTutorials(QXmlStreamReader *reader, const FilePath &projectsOffset)
{
QList<ExampleItem *> result;
std::unique_ptr<ExampleItem> item = std::make_unique<ExampleItem>();
const QChar slash = QLatin1Char('/');
while (!reader->atEnd()) {
switch (reader->readNext()) {
case QXmlStreamReader::StartElement:
@@ -184,10 +180,9 @@ static QList<ExampleItem *> 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<ExampleItem *> 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<ExampleItem *> parseTutorials(QXmlStreamReader *reader, const QStri
return result;
}
expected_str<QList<ExampleItem *>> parseExamples(const QString &manifest,
const QString &examplesInstallPath,
const QString &demosInstallPath,
expected_str<QList<ExampleItem *>> 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<QByteArray> 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<ExampleItem *> 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<QList<ExampleItem *>> 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()));

View File

@@ -5,6 +5,7 @@
#include <coreplugin/welcomepagehelper.h>
#include <utils/expected.h>
#include <utils/filepath.h>
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<QList<ExampleItem *>> parseExamples(const QString &manifest,
const QString &examplesInstallPath,
const QString &demosInstallPath,
Utils::expected_str<QList<ExampleItem *>> parseExamples(const Utils::FilePath &manifest,
const Utils::FilePath &examplesInstallPath,
const Utils::FilePath &demosInstallPath,
bool examples);
} // namespace QtSupport::Internal

View File

@@ -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("<blockquote>%1</blockquote>").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<void> result
= FilePath::fromString(projectDir).copyRecursively(FilePath::fromString(targetDir));
expected_str<void> 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<bool>([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<bool>([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())

View File

@@ -4,10 +4,7 @@
#pragma once
#include <coreplugin/iwelcomepage.h>
QT_BEGIN_NAMESPACE
class QFileInfo;
QT_END_NAMESPACE
#include <utils/filepath.h>
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;
};