forked from qt-creator/qt-creator
Move examples manifest parser in separate function
and file. To make it auto-testable. Change-Id: I19d263bf080a0089eb9a4ec0f379c52446771c0a Reviewed-by: David Schulz <david.schulz@qt.io>
This commit is contained in:
@@ -8,6 +8,8 @@ add_qtc_plugin(QtSupport
|
|||||||
codegensettings.cpp codegensettings.h
|
codegensettings.cpp codegensettings.h
|
||||||
codegensettingspage.cpp codegensettingspage.h
|
codegensettingspage.cpp codegensettingspage.h
|
||||||
exampleslistmodel.cpp exampleslistmodel.h
|
exampleslistmodel.cpp exampleslistmodel.h
|
||||||
|
examplesparser.cpp
|
||||||
|
examplesparser.h
|
||||||
externaleditors.cpp externaleditors.h
|
externaleditors.cpp externaleditors.h
|
||||||
gettingstartedwelcomepage.cpp gettingstartedwelcomepage.h
|
gettingstartedwelcomepage.cpp gettingstartedwelcomepage.h
|
||||||
profilereader.cpp profilereader.h
|
profilereader.cpp profilereader.h
|
||||||
|
@@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
#include "exampleslistmodel.h"
|
#include "exampleslistmodel.h"
|
||||||
|
|
||||||
|
#include "examplesparser.h"
|
||||||
#include "qtsupporttr.h"
|
#include "qtsupporttr.h"
|
||||||
|
|
||||||
#include <QBuffer>
|
#include <QBuffer>
|
||||||
@@ -288,38 +289,11 @@ ExamplesViewController::ExamplesViewController(ExampleSetModel *exampleSetModel,
|
|||||||
updateExamples();
|
updateExamples();
|
||||||
}
|
}
|
||||||
|
|
||||||
static QString fixStringForTags(const QString &string)
|
|
||||||
{
|
|
||||||
QString returnString = string;
|
|
||||||
returnString.remove(QLatin1String("<i>"));
|
|
||||||
returnString.remove(QLatin1String("</i>"));
|
|
||||||
returnString.remove(QLatin1String("<tt>"));
|
|
||||||
returnString.remove(QLatin1String("</tt>"));
|
|
||||||
return returnString;
|
|
||||||
}
|
|
||||||
|
|
||||||
static QStringList trimStringList(const QStringList &stringlist)
|
|
||||||
{
|
|
||||||
return Utils::transform(stringlist, [](const QString &str) { return str.trimmed(); });
|
|
||||||
}
|
|
||||||
|
|
||||||
static QString relativeOrInstallPath(const QString &path, const QString &manifestPath,
|
|
||||||
const QString &installPath)
|
|
||||||
{
|
|
||||||
const QChar slash = QLatin1Char('/');
|
|
||||||
const QString relativeResolvedPath = manifestPath + slash + path;
|
|
||||||
const QString installResolvedPath = installPath + slash + path;
|
|
||||||
if (QFile::exists(relativeResolvedPath))
|
|
||||||
return relativeResolvedPath;
|
|
||||||
if (QFile::exists(installResolvedPath))
|
|
||||||
return installResolvedPath;
|
|
||||||
// doesn't exist, just return relative
|
|
||||||
return relativeResolvedPath;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool isValidExampleOrDemo(ExampleItem *item)
|
static bool isValidExampleOrDemo(ExampleItem *item)
|
||||||
{
|
{
|
||||||
QTC_ASSERT(item, return false);
|
QTC_ASSERT(item, return false);
|
||||||
|
if (item->type == Tutorial)
|
||||||
|
return true;
|
||||||
static QString invalidPrefix = QLatin1String("qthelp:////"); /* means that the qthelp url
|
static QString invalidPrefix = QLatin1String("qthelp:////"); /* means that the qthelp url
|
||||||
doesn't have any namespace */
|
doesn't have any namespace */
|
||||||
QString reason;
|
QString reason;
|
||||||
@@ -345,158 +319,6 @@ static bool isValidExampleOrDemo(ExampleItem *item)
|
|||||||
return ok || debugExamples();
|
return ok || debugExamples();
|
||||||
}
|
}
|
||||||
|
|
||||||
static QList<ExampleItem *> parseExamples(QXmlStreamReader *reader,
|
|
||||||
const QString &projectsOffset,
|
|
||||||
const QString &examplesInstallPath)
|
|
||||||
{
|
|
||||||
QList<ExampleItem *> result;
|
|
||||||
std::unique_ptr<ExampleItem> item;
|
|
||||||
const QChar slash = QLatin1Char('/');
|
|
||||||
while (!reader->atEnd()) {
|
|
||||||
switch (reader->readNext()) {
|
|
||||||
case QXmlStreamReader::StartElement:
|
|
||||||
if (reader->name() == QLatin1String("example")) {
|
|
||||||
item = std::make_unique<ExampleItem>();
|
|
||||||
item->type = Example;
|
|
||||||
QXmlStreamAttributes attributes = reader->attributes();
|
|
||||||
item->name = attributes.value(QLatin1String("name")).toString();
|
|
||||||
item->projectPath = attributes.value(QLatin1String("projectPath")).toString();
|
|
||||||
item->hasSourceCode = !item->projectPath.isEmpty();
|
|
||||||
item->projectPath = relativeOrInstallPath(item->projectPath, projectsOffset, examplesInstallPath);
|
|
||||||
item->imageUrl = attributes.value(QLatin1String("imageUrl")).toString();
|
|
||||||
QPixmapCache::remove(item->imageUrl);
|
|
||||||
item->docUrl = attributes.value(QLatin1String("docUrl")).toString();
|
|
||||||
item->isHighlighted = attributes.value(QLatin1String("isHighlighted")).toString() == QLatin1String("true");
|
|
||||||
|
|
||||||
} else if (reader->name() == QLatin1String("fileToOpen")) {
|
|
||||||
const QString mainFileAttribute = reader->attributes().value(
|
|
||||||
QLatin1String("mainFile")).toString();
|
|
||||||
const QString filePath = relativeOrInstallPath(
|
|
||||||
reader->readElementText(QXmlStreamReader::ErrorOnUnexpectedElement),
|
|
||||||
projectsOffset, examplesInstallPath);
|
|
||||||
item->filesToOpen.append(filePath);
|
|
||||||
if (mainFileAttribute.compare(QLatin1String("true"), Qt::CaseInsensitive) == 0)
|
|
||||||
item->mainFile = filePath;
|
|
||||||
} 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));
|
|
||||||
} else if (reader->name() == QLatin1String("tags")) {
|
|
||||||
item->tags = trimStringList(reader->readElementText(QXmlStreamReader::ErrorOnUnexpectedElement).split(QLatin1Char(','), Qt::SkipEmptyParts));
|
|
||||||
} else if (reader->name() == QLatin1String("platforms")) {
|
|
||||||
item->platforms = trimStringList(reader->readElementText(QXmlStreamReader::ErrorOnUnexpectedElement).split(QLatin1Char(','), Qt::SkipEmptyParts));
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case QXmlStreamReader::EndElement:
|
|
||||||
if (reader->name() == QLatin1String("example")) {
|
|
||||||
if (isValidExampleOrDemo(item.get()))
|
|
||||||
result.push_back(item.release());
|
|
||||||
} else if (reader->name() == QLatin1String("examples")) {
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default: // nothing
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
static QList<ExampleItem *> parseDemos(QXmlStreamReader *reader,
|
|
||||||
const QString &projectsOffset,
|
|
||||||
const QString &demosInstallPath)
|
|
||||||
{
|
|
||||||
QList<ExampleItem *> result;
|
|
||||||
std::unique_ptr<ExampleItem> item;
|
|
||||||
const QChar slash = QLatin1Char('/');
|
|
||||||
while (!reader->atEnd()) {
|
|
||||||
switch (reader->readNext()) {
|
|
||||||
case QXmlStreamReader::StartElement:
|
|
||||||
if (reader->name() == QLatin1String("demo")) {
|
|
||||||
item = std::make_unique<ExampleItem>();
|
|
||||||
item->type = Demo;
|
|
||||||
QXmlStreamAttributes attributes = reader->attributes();
|
|
||||||
item->name = attributes.value(QLatin1String("name")).toString();
|
|
||||||
item->projectPath = attributes.value(QLatin1String("projectPath")).toString();
|
|
||||||
item->hasSourceCode = !item->projectPath.isEmpty();
|
|
||||||
item->projectPath = relativeOrInstallPath(item->projectPath, projectsOffset, demosInstallPath);
|
|
||||||
item->imageUrl = attributes.value(QLatin1String("imageUrl")).toString();
|
|
||||||
QPixmapCache::remove(item->imageUrl);
|
|
||||||
item->docUrl = attributes.value(QLatin1String("docUrl")).toString();
|
|
||||||
item->isHighlighted = attributes.value(QLatin1String("isHighlighted")).toString() == QLatin1String("true");
|
|
||||||
} else if (reader->name() == QLatin1String("fileToOpen")) {
|
|
||||||
item->filesToOpen.append(relativeOrInstallPath(reader->readElementText(QXmlStreamReader::ErrorOnUnexpectedElement),
|
|
||||||
projectsOffset, demosInstallPath));
|
|
||||||
} 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));
|
|
||||||
} else if (reader->name() == QLatin1String("tags")) {
|
|
||||||
item->tags = reader->readElementText(QXmlStreamReader::ErrorOnUnexpectedElement).split(QLatin1Char(','));
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case QXmlStreamReader::EndElement:
|
|
||||||
if (reader->name() == QLatin1String("demo")) {
|
|
||||||
if (isValidExampleOrDemo(item.get()))
|
|
||||||
result.push_back(item.release());
|
|
||||||
} else if (reader->name() == QLatin1String("demos")) {
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default: // nothing
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
static QList<ExampleItem *> parseTutorials(QXmlStreamReader *reader, const QString &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:
|
|
||||||
if (reader->name() == QLatin1String("tutorial")) {
|
|
||||||
item = std::make_unique<ExampleItem>();
|
|
||||||
item->type = Tutorial;
|
|
||||||
QXmlStreamAttributes attributes = reader->attributes();
|
|
||||||
item->name = attributes.value(QLatin1String("name")).toString();
|
|
||||||
item->projectPath = 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);
|
|
||||||
item->docUrl = attributes.value(QLatin1String("docUrl")).toString();
|
|
||||||
item->isVideo = attributes.value(QLatin1String("isVideo")).toString() == QLatin1String("true");
|
|
||||||
item->videoUrl = attributes.value(QLatin1String("videoUrl")).toString();
|
|
||||||
item->videoLength = attributes.value(QLatin1String("videoLength")).toString();
|
|
||||||
} else if (reader->name() == QLatin1String("fileToOpen")) {
|
|
||||||
item->filesToOpen.append(projectsOffset + slash + 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));
|
|
||||||
} else if (reader->name() == QLatin1String("tags")) {
|
|
||||||
item->tags = reader->readElementText(QXmlStreamReader::ErrorOnUnexpectedElement).split(QLatin1Char(','));
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case QXmlStreamReader::EndElement:
|
|
||||||
if (reader->name() == QLatin1String("tutorial"))
|
|
||||||
result.push_back(item.release());
|
|
||||||
else if (reader->name() == QLatin1String("tutorials"))
|
|
||||||
return result;
|
|
||||||
break;
|
|
||||||
default: // nothing
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ExamplesViewController::updateExamples()
|
void ExamplesViewController::updateExamples()
|
||||||
{
|
{
|
||||||
QString examplesInstallPath;
|
QString examplesInstallPath;
|
||||||
@@ -509,41 +331,23 @@ void ExamplesViewController::updateExamples()
|
|||||||
|
|
||||||
QList<ExampleItem *> items;
|
QList<ExampleItem *> items;
|
||||||
for (const QString &exampleSource : sources) {
|
for (const QString &exampleSource : sources) {
|
||||||
QFile exampleFile(exampleSource);
|
if (debugExamples()) {
|
||||||
if (!exampleFile.open(QIODevice::ReadOnly)) {
|
qWarning() << QString::fromLatin1("Reading file \"%1\"...")
|
||||||
if (debugExamples())
|
.arg(QFileInfo(exampleSource).absoluteFilePath());
|
||||||
qWarning() << "ERROR: Could not open file" << exampleSource;
|
}
|
||||||
|
|
||||||
|
const expected_str<QList<ExampleItem *>> result
|
||||||
|
= parseExamples(exampleSource, examplesInstallPath, demosInstallPath, m_isExamples);
|
||||||
|
if (!result) {
|
||||||
|
if (debugExamples()) {
|
||||||
|
qWarning() << "ERROR: Could not read examples from" << exampleSource << ":"
|
||||||
|
<< result.error();
|
||||||
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
items += filtered(*result, isValidExampleOrDemo);
|
||||||
QFileInfo fi(exampleSource);
|
|
||||||
QString offsetPath = fi.path();
|
|
||||||
QDir examplesDir(offsetPath);
|
|
||||||
QDir demosDir(offsetPath);
|
|
||||||
|
|
||||||
if (debugExamples())
|
|
||||||
qWarning() << QString::fromLatin1("Reading file \"%1\"...").arg(fi.absoluteFilePath());
|
|
||||||
QXmlStreamReader reader(&exampleFile);
|
|
||||||
while (!reader.atEnd())
|
|
||||||
switch (reader.readNext()) {
|
|
||||||
case QXmlStreamReader::StartElement:
|
|
||||||
if (m_isExamples && reader.name() == QLatin1String("examples"))
|
|
||||||
items += parseExamples(&reader, examplesDir.path(), examplesInstallPath);
|
|
||||||
else if (m_isExamples && reader.name() == QLatin1String("demos"))
|
|
||||||
items += parseDemos(&reader, demosDir.path(), demosInstallPath);
|
|
||||||
else if (!m_isExamples && reader.name() == QLatin1String("tutorials"))
|
|
||||||
items += parseTutorials(&reader, examplesDir.path());
|
|
||||||
break;
|
|
||||||
default: // nothing
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (reader.hasError() && debugExamples()) {
|
|
||||||
qWarning().noquote().nospace() << "ERROR: Could not parse file as XML document ("
|
|
||||||
<< exampleSource << "):" << reader.lineNumber() << ':' << reader.columnNumber()
|
|
||||||
<< ": " << reader.errorString();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_isExamples) {
|
if (m_isExamples) {
|
||||||
if (m_exampleSetModel->selectedQtSupports(Android::Constants::ANDROID_DEVICE_TYPE)) {
|
if (m_exampleSetModel->selectedQtSupports(Android::Constants::ANDROID_DEVICE_TYPE)) {
|
||||||
items = Utils::filtered(items, [](ExampleItem *item) {
|
items = Utils::filtered(items, [](ExampleItem *item) {
|
||||||
|
@@ -77,29 +77,6 @@ private:
|
|||||||
bool m_initalized = false;
|
bool m_initalized = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
enum InstructionalType
|
|
||||||
{
|
|
||||||
Example = 0, Demo, Tutorial
|
|
||||||
};
|
|
||||||
|
|
||||||
class ExampleItem : public Core::ListItem
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
QString projectPath;
|
|
||||||
QString docUrl;
|
|
||||||
QStringList filesToOpen;
|
|
||||||
QString mainFile; /* file to be visible after opening filesToOpen */
|
|
||||||
QStringList dependencies;
|
|
||||||
InstructionalType type;
|
|
||||||
int difficulty = 0;
|
|
||||||
bool hasSourceCode = false;
|
|
||||||
bool isVideo = false;
|
|
||||||
bool isHighlighted = false;
|
|
||||||
QString videoUrl;
|
|
||||||
QString videoLength;
|
|
||||||
QStringList platforms;
|
|
||||||
};
|
|
||||||
|
|
||||||
class ExamplesViewController : public QObject
|
class ExamplesViewController : public QObject
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
@@ -119,5 +96,3 @@ private:
|
|||||||
|
|
||||||
} // namespace Internal
|
} // namespace Internal
|
||||||
} // namespace QtSupport
|
} // namespace QtSupport
|
||||||
|
|
||||||
Q_DECLARE_METATYPE(QtSupport::Internal::ExampleItem *)
|
|
||||||
|
270
src/plugins/qtsupport/examplesparser.cpp
Normal file
270
src/plugins/qtsupport/examplesparser.cpp
Normal file
@@ -0,0 +1,270 @@
|
|||||||
|
// 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 "examplesparser.h"
|
||||||
|
|
||||||
|
#include <utils/algorithm.h>
|
||||||
|
#include <utils/stylehelper.h>
|
||||||
|
|
||||||
|
#include <QDir>
|
||||||
|
#include <QFile>
|
||||||
|
#include <QFileInfo>
|
||||||
|
#include <QPixmapCache>
|
||||||
|
#include <QXmlStreamReader>
|
||||||
|
|
||||||
|
using namespace Utils;
|
||||||
|
|
||||||
|
namespace QtSupport::Internal {
|
||||||
|
|
||||||
|
static QString relativeOrInstallPath(const QString &path,
|
||||||
|
const QString &manifestPath,
|
||||||
|
const QString &installPath)
|
||||||
|
{
|
||||||
|
const QChar slash = QLatin1Char('/');
|
||||||
|
const QString relativeResolvedPath = manifestPath + slash + path;
|
||||||
|
const QString installResolvedPath = installPath + slash + path;
|
||||||
|
if (QFile::exists(relativeResolvedPath))
|
||||||
|
return relativeResolvedPath;
|
||||||
|
if (QFile::exists(installResolvedPath))
|
||||||
|
return installResolvedPath;
|
||||||
|
// doesn't exist, just return relative
|
||||||
|
return relativeResolvedPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
static QString fixStringForTags(const QString &string)
|
||||||
|
{
|
||||||
|
QString returnString = string;
|
||||||
|
returnString.remove(QLatin1String("<i>"));
|
||||||
|
returnString.remove(QLatin1String("</i>"));
|
||||||
|
returnString.remove(QLatin1String("<tt>"));
|
||||||
|
returnString.remove(QLatin1String("</tt>"));
|
||||||
|
return returnString;
|
||||||
|
}
|
||||||
|
|
||||||
|
static QStringList trimStringList(const QStringList &stringlist)
|
||||||
|
{
|
||||||
|
return Utils::transform(stringlist, [](const QString &str) { return str.trimmed(); });
|
||||||
|
}
|
||||||
|
|
||||||
|
static QList<ExampleItem *> parseExamples(QXmlStreamReader *reader,
|
||||||
|
const QString &projectsOffset,
|
||||||
|
const QString &examplesInstallPath)
|
||||||
|
{
|
||||||
|
QList<ExampleItem *> result;
|
||||||
|
std::unique_ptr<ExampleItem> item;
|
||||||
|
const QChar slash = QLatin1Char('/');
|
||||||
|
while (!reader->atEnd()) {
|
||||||
|
switch (reader->readNext()) {
|
||||||
|
case QXmlStreamReader::StartElement:
|
||||||
|
if (reader->name() == QLatin1String("example")) {
|
||||||
|
item = std::make_unique<ExampleItem>();
|
||||||
|
item->type = Example;
|
||||||
|
QXmlStreamAttributes attributes = reader->attributes();
|
||||||
|
item->name = attributes.value(QLatin1String("name")).toString();
|
||||||
|
item->projectPath = attributes.value(QLatin1String("projectPath")).toString();
|
||||||
|
item->hasSourceCode = !item->projectPath.isEmpty();
|
||||||
|
item->projectPath = relativeOrInstallPath(item->projectPath,
|
||||||
|
projectsOffset,
|
||||||
|
examplesInstallPath);
|
||||||
|
item->imageUrl = attributes.value(QLatin1String("imageUrl")).toString();
|
||||||
|
QPixmapCache::remove(item->imageUrl);
|
||||||
|
item->docUrl = attributes.value(QLatin1String("docUrl")).toString();
|
||||||
|
item->isHighlighted = attributes.value(QLatin1String("isHighlighted")).toString()
|
||||||
|
== QLatin1String("true");
|
||||||
|
|
||||||
|
} else if (reader->name() == QLatin1String("fileToOpen")) {
|
||||||
|
const QString mainFileAttribute
|
||||||
|
= reader->attributes().value(QLatin1String("mainFile")).toString();
|
||||||
|
const QString filePath
|
||||||
|
= relativeOrInstallPath(reader->readElementText(
|
||||||
|
QXmlStreamReader::ErrorOnUnexpectedElement),
|
||||||
|
projectsOffset,
|
||||||
|
examplesInstallPath);
|
||||||
|
item->filesToOpen.append(filePath);
|
||||||
|
if (mainFileAttribute.compare(QLatin1String("true"), Qt::CaseInsensitive) == 0)
|
||||||
|
item->mainFile = filePath;
|
||||||
|
} 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));
|
||||||
|
} else if (reader->name() == QLatin1String("tags")) {
|
||||||
|
item->tags = trimStringList(
|
||||||
|
reader->readElementText(QXmlStreamReader::ErrorOnUnexpectedElement)
|
||||||
|
.split(QLatin1Char(','), Qt::SkipEmptyParts));
|
||||||
|
} else if (reader->name() == QLatin1String("platforms")) {
|
||||||
|
item->platforms = trimStringList(
|
||||||
|
reader->readElementText(QXmlStreamReader::ErrorOnUnexpectedElement)
|
||||||
|
.split(QLatin1Char(','), Qt::SkipEmptyParts));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case QXmlStreamReader::EndElement:
|
||||||
|
if (reader->name() == QLatin1String("example")) {
|
||||||
|
result.push_back(item.release());
|
||||||
|
} else if (reader->name() == QLatin1String("examples")) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default: // nothing
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static QList<ExampleItem *> parseDemos(QXmlStreamReader *reader,
|
||||||
|
const QString &projectsOffset,
|
||||||
|
const QString &demosInstallPath)
|
||||||
|
{
|
||||||
|
QList<ExampleItem *> result;
|
||||||
|
std::unique_ptr<ExampleItem> item;
|
||||||
|
const QChar slash = QLatin1Char('/');
|
||||||
|
while (!reader->atEnd()) {
|
||||||
|
switch (reader->readNext()) {
|
||||||
|
case QXmlStreamReader::StartElement:
|
||||||
|
if (reader->name() == QLatin1String("demo")) {
|
||||||
|
item = std::make_unique<ExampleItem>();
|
||||||
|
item->type = Demo;
|
||||||
|
QXmlStreamAttributes attributes = reader->attributes();
|
||||||
|
item->name = attributes.value(QLatin1String("name")).toString();
|
||||||
|
item->projectPath = attributes.value(QLatin1String("projectPath")).toString();
|
||||||
|
item->hasSourceCode = !item->projectPath.isEmpty();
|
||||||
|
item->projectPath = relativeOrInstallPath(item->projectPath,
|
||||||
|
projectsOffset,
|
||||||
|
demosInstallPath);
|
||||||
|
item->imageUrl = attributes.value(QLatin1String("imageUrl")).toString();
|
||||||
|
QPixmapCache::remove(item->imageUrl);
|
||||||
|
item->docUrl = attributes.value(QLatin1String("docUrl")).toString();
|
||||||
|
item->isHighlighted = attributes.value(QLatin1String("isHighlighted")).toString()
|
||||||
|
== QLatin1String("true");
|
||||||
|
} else if (reader->name() == QLatin1String("fileToOpen")) {
|
||||||
|
item->filesToOpen.append(
|
||||||
|
relativeOrInstallPath(reader->readElementText(
|
||||||
|
QXmlStreamReader::ErrorOnUnexpectedElement),
|
||||||
|
projectsOffset,
|
||||||
|
demosInstallPath));
|
||||||
|
} 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));
|
||||||
|
} else if (reader->name() == QLatin1String("tags")) {
|
||||||
|
item->tags = reader->readElementText(QXmlStreamReader::ErrorOnUnexpectedElement)
|
||||||
|
.split(QLatin1Char(','));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case QXmlStreamReader::EndElement:
|
||||||
|
if (reader->name() == QLatin1String("demo")) {
|
||||||
|
result.push_back(item.release());
|
||||||
|
} else if (reader->name() == QLatin1String("demos")) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default: // nothing
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static QList<ExampleItem *> parseTutorials(QXmlStreamReader *reader, const QString &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:
|
||||||
|
if (reader->name() == QLatin1String("tutorial")) {
|
||||||
|
item = std::make_unique<ExampleItem>();
|
||||||
|
item->type = Tutorial;
|
||||||
|
QXmlStreamAttributes attributes = reader->attributes();
|
||||||
|
item->name = attributes.value(QLatin1String("name")).toString();
|
||||||
|
item->projectPath = 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);
|
||||||
|
item->docUrl = attributes.value(QLatin1String("docUrl")).toString();
|
||||||
|
item->isVideo = attributes.value(QLatin1String("isVideo")).toString()
|
||||||
|
== QLatin1String("true");
|
||||||
|
item->videoUrl = attributes.value(QLatin1String("videoUrl")).toString();
|
||||||
|
item->videoLength = attributes.value(QLatin1String("videoLength")).toString();
|
||||||
|
} else if (reader->name() == QLatin1String("fileToOpen")) {
|
||||||
|
item->filesToOpen.append(
|
||||||
|
projectsOffset + slash
|
||||||
|
+ 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));
|
||||||
|
} else if (reader->name() == QLatin1String("tags")) {
|
||||||
|
item->tags = reader->readElementText(QXmlStreamReader::ErrorOnUnexpectedElement)
|
||||||
|
.split(QLatin1Char(','));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case QXmlStreamReader::EndElement:
|
||||||
|
if (reader->name() == QLatin1String("tutorial"))
|
||||||
|
result.push_back(item.release());
|
||||||
|
else if (reader->name() == QLatin1String("tutorials"))
|
||||||
|
return result;
|
||||||
|
break;
|
||||||
|
default: // nothing
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
expected_str<QList<ExampleItem *>> parseExamples(const QString &manifest,
|
||||||
|
const QString &examplesInstallPath,
|
||||||
|
const QString &demosInstallPath,
|
||||||
|
const bool examples)
|
||||||
|
{
|
||||||
|
QFile exampleFile(manifest);
|
||||||
|
if (!exampleFile.open(QIODevice::ReadOnly))
|
||||||
|
return make_unexpected(QString("Could not open file \"%1\"").arg(manifest));
|
||||||
|
|
||||||
|
QFileInfo fi(manifest);
|
||||||
|
QString offsetPath = fi.path();
|
||||||
|
QDir examplesDir(offsetPath);
|
||||||
|
QDir demosDir(offsetPath);
|
||||||
|
|
||||||
|
QList<ExampleItem *> items;
|
||||||
|
QXmlStreamReader reader(&exampleFile);
|
||||||
|
while (!reader.atEnd()) {
|
||||||
|
switch (reader.readNext()) {
|
||||||
|
case QXmlStreamReader::StartElement:
|
||||||
|
if (examples && reader.name() == QLatin1String("examples"))
|
||||||
|
items += parseExamples(&reader, examplesDir.path(), examplesInstallPath);
|
||||||
|
else if (examples && reader.name() == QLatin1String("demos"))
|
||||||
|
items += parseDemos(&reader, demosDir.path(), demosInstallPath);
|
||||||
|
else if (!examples && reader.name() == QLatin1String("tutorials"))
|
||||||
|
items += parseTutorials(&reader, examplesDir.path());
|
||||||
|
break;
|
||||||
|
default: // nothing
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (reader.hasError()) {
|
||||||
|
qDeleteAll(items);
|
||||||
|
return make_unexpected(QString("Could not parse file \"%1\" as XML document: %2:%3: %4")
|
||||||
|
.arg(manifest)
|
||||||
|
.arg(reader.lineNumber())
|
||||||
|
.arg(reader.columnNumber())
|
||||||
|
.arg(reader.errorString()));
|
||||||
|
}
|
||||||
|
return items;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace QtSupport::Internal
|
38
src/plugins/qtsupport/examplesparser.h
Normal file
38
src/plugins/qtsupport/examplesparser.h
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
// 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
|
||||||
|
|
||||||
|
#include <coreplugin/welcomepagehelper.h>
|
||||||
|
#include <utils/expected.h>
|
||||||
|
|
||||||
|
namespace QtSupport::Internal {
|
||||||
|
|
||||||
|
enum InstructionalType { Example = 0, Demo, Tutorial };
|
||||||
|
|
||||||
|
class ExampleItem : public Core::ListItem
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
QString projectPath;
|
||||||
|
QString docUrl;
|
||||||
|
QStringList filesToOpen;
|
||||||
|
QString mainFile; /* file to be visible after opening filesToOpen */
|
||||||
|
QStringList dependencies;
|
||||||
|
InstructionalType type;
|
||||||
|
int difficulty = 0;
|
||||||
|
bool hasSourceCode = false;
|
||||||
|
bool isVideo = false;
|
||||||
|
bool isHighlighted = false;
|
||||||
|
QString videoUrl;
|
||||||
|
QString videoLength;
|
||||||
|
QStringList platforms;
|
||||||
|
};
|
||||||
|
|
||||||
|
Utils::expected_str<QList<ExampleItem *>> parseExamples(const QString &manifest,
|
||||||
|
const QString &examplesInstallPath,
|
||||||
|
const QString &demosInstallPath,
|
||||||
|
bool examples);
|
||||||
|
|
||||||
|
} // namespace QtSupport::Internal
|
||||||
|
|
||||||
|
Q_DECLARE_METATYPE(QtSupport::Internal::ExampleItem *)
|
@@ -4,6 +4,7 @@
|
|||||||
#include "gettingstartedwelcomepage.h"
|
#include "gettingstartedwelcomepage.h"
|
||||||
|
|
||||||
#include "exampleslistmodel.h"
|
#include "exampleslistmodel.h"
|
||||||
|
#include "examplesparser.h"
|
||||||
#include "qtsupporttr.h"
|
#include "qtsupporttr.h"
|
||||||
|
|
||||||
#include <coreplugin/coreconstants.h>
|
#include <coreplugin/coreconstants.h>
|
||||||
|
@@ -77,6 +77,8 @@ Project {
|
|||||||
"qtsupport.qrc",
|
"qtsupport.qrc",
|
||||||
"exampleslistmodel.cpp",
|
"exampleslistmodel.cpp",
|
||||||
"exampleslistmodel.h",
|
"exampleslistmodel.h",
|
||||||
|
"examplesparser.cpp",
|
||||||
|
"examplesparser.h",
|
||||||
"profilereader.cpp",
|
"profilereader.cpp",
|
||||||
"profilereader.h",
|
"profilereader.h",
|
||||||
"qscxmlcgenerator.cpp",
|
"qscxmlcgenerator.cpp",
|
||||||
|
Reference in New Issue
Block a user