diff options
author | Daniel Molkentin <daniel.molkentin@nokia.com> | 2010-11-11 16:49:17 +0100 |
---|---|---|
committer | Eike Ziller <eike.ziller@nokia.com> | 2011-06-29 00:31:47 +0200 |
commit | 497dd323ea550cfc9987b3ee049700986b5d8859 (patch) | |
tree | d602e654adec95d78d4f5e7ce237169641296eb5 /src/tools/examplesscanner | |
parent | b23aa108900e32b514e1939494c0d54fa9e75e32 (diff) | |
download | qt-creator-497dd323ea550cfc9987b3ee049700986b5d8859.tar.gz |
Introduce QML-based welcome screen using desktop components
Implements new XML-based format for examples, demos & tutorials
Done-with: Primrose Mbanefo <ext-primrose.mbanefo@nokia.com>
Change-Id: I42c0afdb419cffe5637cd4f298e828d09e0fb15a
Reviewed-on: http://codereview.qt.nokia.com/840
Reviewed-by: Eike Ziller <eike.ziller@nokia.com>
Diffstat (limited to 'src/tools/examplesscanner')
-rw-r--r-- | src/tools/examplesscanner/examplesscanner.pro | 9 | ||||
-rw-r--r-- | src/tools/examplesscanner/helpextractor.cpp | 372 | ||||
-rw-r--r-- | src/tools/examplesscanner/helpextractor.h | 41 | ||||
-rw-r--r-- | src/tools/examplesscanner/main.cpp | 9 |
4 files changed, 431 insertions, 0 deletions
diff --git a/src/tools/examplesscanner/examplesscanner.pro b/src/tools/examplesscanner/examplesscanner.pro new file mode 100644 index 0000000000..531a4aabfb --- /dev/null +++ b/src/tools/examplesscanner/examplesscanner.pro @@ -0,0 +1,9 @@ +QT += declarative xml network +CONFIG += help + +SOURCES += \ + main.cpp \ + helpextractor.cpp + +HEADERS += \ + helpextractor.h diff --git a/src/tools/examplesscanner/helpextractor.cpp b/src/tools/examplesscanner/helpextractor.cpp new file mode 100644 index 0000000000..7fad855197 --- /dev/null +++ b/src/tools/examplesscanner/helpextractor.cpp @@ -0,0 +1,372 @@ +#include "helpextractor.h" + +#include <QtGui> +#include <QDebug> + +HelpExtractor::HelpExtractor() +{ + initHelpEngine(); +} + +void HelpExtractor::initHelpEngine() +{ + helpRootUrl = QString("qthelp://com.trolltech.qt/qdoc/"); + // .arg(QT_VERSION >> 16).arg((QT_VERSION >> 8) & 0xFF) + // .arg(QT_VERSION & 0xFF); + + // Store help collection file in cache dir of assistant + QString cacheDir = QDesktopServices::storageLocation(QDesktopServices::DataLocation) + + QLatin1String("/Trolltech/Assistant/"); + QString helpDataFile = QString(QLatin1String("qtdemo_%1.qhc")).arg(QLatin1String(QT_VERSION_STR)); + + QDir dir; + if (!dir.exists(cacheDir)) + dir.mkpath(cacheDir); + + // Create help engine (and new + // helpDataFile if it does not exist): + helpEngine = new QHelpEngineCore(cacheDir + helpDataFile); + helpEngine->setupData(); + + QString qtDocRoot = QLibraryInfo::location(QLibraryInfo::DocumentationPath) + QLatin1String("/qch"); + qtDocRoot = QDir(qtDocRoot).absolutePath(); + + QStringList qchFiles; + qchFiles << QLatin1String("/qt.qch") + << QLatin1String("/designer.qch") + << QLatin1String("/linguist.qch"); + + QString oldDir = helpEngine->customValue(QLatin1String("docDir"), QString()).toString(); + if (oldDir != qtDocRoot) { + foreach (const QString &qchFile, qchFiles) + helpEngine->unregisterDocumentation(QHelpEngineCore::namespaceName(qtDocRoot + qchFile)); + } + + // If the data that the engine will work + // on is not yet registered, do it now: + foreach (const QString &qchFile, qchFiles) + helpEngine->registerDocumentation(qtDocRoot + qchFile); + + helpEngine->setCustomValue(QLatin1String("docDir"), qtDocRoot); +} + +void HelpExtractor::readXmlDocument() +{ + contentsDoc = new QDomDocument(); + QString errorStr; + int errorLine; + int errorColumn; + + QString qtDemoPath = QLibraryInfo::location(QLibraryInfo::DemosPath) + QLatin1String("/qtdemo"); + QFile file(qtDemoPath + "/xml/examples.xml"); + bool statusOK = contentsDoc->setContent(&file, true, &errorStr, &errorLine, &errorColumn); + if (!statusOK) { + qDebug() << QString::fromLatin1("DOM Parser: Could not read or find the contents document. Error at line %1, column %2:\n%3") + .arg(errorLine).arg(errorColumn).arg(errorStr); + exit(-1); + } + //convertToSql(contentsDoc->documentElement()); + convertToAggregatableXml(contentsDoc->documentElement()); +} + + +void HelpExtractor::convertToAggregatableXml(const QDomElement &documentElement) +{ + QDomDocument outDocument; + QDomElement root = outDocument.createElement("instructionals"); + QDomElement demos = outDocument.createElement("demos"); + QDomElement examples = outDocument.createElement("examples"); + QDomElement tutorials = outDocument.createElement("tutorials"); + root.setAttribute("module", "Qt"); + root.appendChild(demos); + root.appendChild(examples); + root.appendChild(tutorials); + outDocument.appendChild(root); + + QDomNode currentNode = documentElement.firstChild(); + QDomElement step, steps, instructional; + int id = 0; + while (!currentNode.isNull()){ + //qDebug() << '\t' << label; + QDomNode sub = currentNode.firstChild(); + while (!sub.isNull()) { + QDomElement element = sub.toElement(); + readInfoAboutExample(element); + QString exampleName = element.attribute("name"); + StringHash exampleInfo = info[exampleName]; + + // type = category - last char + QString category = exampleInfo["category"]; + QString categoryName = sub.parentNode().toElement().attribute("name"); + QString type = category; + type.chop(1); + + QString dirName = exampleInfo["dirname"]; + + if (category != "tutorial" || (category == "tutorial" && category != lastCategory)) { + instructional = outDocument.createElement(type); + if (category == "tutorial" && category != lastCategory) + instructional.setAttribute("name", categoryName); + else + instructional.setAttribute("name", exampleName); + } + + QString projectPath = dirName + '/' + exampleInfo["filename"] + '/' + exampleInfo["filename"]; + + bool qml = (exampleInfo["qml"] == "true"); + if (qml) + projectPath += ".qmlproject"; + else + projectPath += ".pro"; + + + if (category == "tutorials") + { + if (category != lastCategory) { + steps = outDocument.createElement("steps"); + instructional.appendChild(steps); + } + + step = outDocument.createElement("step"); + step.setAttribute("projectPath", projectPath); + step.setAttribute("imageUrl", getImageUrl(exampleName)); + step.setAttribute("docUrl", resolveDocUrl(exampleName)); + steps.appendChild(step); + } + + instructional.setAttribute("projectPath", projectPath); + instructional.setAttribute("imageUrl", getImageUrl(exampleName)); + instructional.setAttribute("docUrl", resolveDocUrl(exampleName)); + instructional.setAttribute("difficulty", "?"); + QDomElement description = outDocument.createElement("description"); + QString descriptionText = loadDescription(exampleName); + description.appendChild(outDocument.createCDATASection(descriptionText)); + QDomElement tags = outDocument.createElement("tags"); + // TODO + QStringList tagList; + tagList << type << exampleInfo["filename"].split('/'); + if (dirName != ".") + tagList << dirName.split('/'); + if (qml) + tagList << "qml" << "qt quick"; + QRegExp ttText("<tt>(.*)</tt>"); + ttText.setMinimal(true); // non-greedy + int index = 0; + QStringList keywords; + while ((index = ttText.indexIn(descriptionText, index)) != -1) { + keywords << descriptionText.mid(index+4, ttText.matchedLength()-9); + index = index + ttText.matchedLength(); + } + + // Blacklist Checking... + QStringList blackList; + blackList << "license" << "trafikanten"; + foreach (const QString& keyword, keywords) + if (!keyword.isEmpty()) { + bool skip = false; + foreach (const QString& blackListItem, blackList) + if (keyword.contains(blackListItem, Qt::CaseInsensitive)) { + skip = true; + break; + } + if (!skip) + tagList << keyword.simplified().toLower(); + } + + tags.appendChild(outDocument.createTextNode(tagList.join(","))); + instructional.appendChild(description); + instructional.appendChild(tags); + + if (category != "tutorials") { + if (category == "demos") + demos.appendChild(instructional); + else + examples.appendChild(instructional); + } else if (lastCategory != "tutorials") + tutorials.appendChild(instructional); + + id++; + sub = sub.nextSibling(); + lastCategory = category; + + } + currentNode = currentNode.nextSibling(); + } + + + QFile outFile("../../../share/qtcreator/welcomescreen/examples_fallback.xml"); + + QByteArray xml = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"; + xml += outDocument.toByteArray(); + if (!outFile.open(QIODevice::WriteOnly)) + return; + outFile.write(xml); + //qDebug() << xml; + outFile.close(); + +} + +QString HelpExtractor::loadDescription(const QString &name) +{ + QByteArray ba = getHtml(name); + QString errorMsg; + int errorLine, errorColumn; + + QDomDocument exampleDoc; + if (ba.isEmpty()) { + qDebug() << "No documentation found for" << name << "Is the documentation built?"; + } else if (!exampleDoc.setContent(ba, false, &errorMsg, &errorLine, &errorColumn)) { + qDebug() << "Error loading documentation for " << name << ": " << errorMsg << errorLine << errorColumn; + } + + QDomNodeList paragraphs = exampleDoc.elementsByTagName("p"); + if (paragraphs.length() < 1) + qDebug() << "- ExampleContent::loadDescription(): Could not load description:" + << info[name]["docfile"]; + QString description = QLatin1String(""); + for (int p = 0; p < int(paragraphs.length()); ++p) { + description = extractTextFromParagraph(paragraphs.item(p)); + if (isSummaryOf(description, name)) { + break; + } + } + return description; +} + +void HelpExtractor::readInfoAboutExample(const QDomElement &example) +{ + QString name = example.attribute("name"); + if (info.contains(name)) + qWarning() << "__WARNING: HelpExtractor::readInfoAboutExample: Demo/example with name" + << name << "appears twice in the xml-file!__"; + + info[name]["filename"] = example.attribute("filename"); + QString dirName = example.parentNode().toElement().attribute("dirname"); + info[name]["dirname"] = dirName; + QString category; + if (dirName.startsWith("tutorials")) + category = "tutorials"; + else + category = example.parentNode().toElement().tagName(); + + if (category == "category") + category = "examples"; + + info[name]["category"] = category; + info[name]["changedirectory"] = example.attribute("changedirectory"); + info[name]["image"] = example.attribute("image"); + info[name]["qml"] = example.attribute("qml"); +} + +QString HelpExtractor::resolveDocUrl(const QString &name) +{ + QString dirName = info[name]["dirname"]; + QString category = info[name]["category"]; + QString fileName = info[name]["filename"]; + + if (category == "demos") + return helpRootUrl + "demos-" + fileName.replace("/", "-") + ".html"; + else + return helpRootUrl + dirName.replace("/", "-") + "-" + fileName + ".html"; +} + + +QString HelpExtractor::resolveImageUrl(const QString &name) +{ + return helpRootUrl + "images/" + name; +} + +QByteArray HelpExtractor::getResource(const QString &name) +{ + return helpEngine->fileData(name); +} + +QByteArray HelpExtractor::getHtml(const QString &name) +{ + return getResource(resolveDocUrl(name)); +} + +QByteArray HelpExtractor::getImage(const QString &name) +{ + QString imageName = this->info[name]["image"]; + QString category = this->info[name]["category"]; + QString fileName = this->info[name]["filename"]; + bool qml = (this->info[name]["qml"] == QLatin1String("true")); + if (qml) + fileName = QLatin1String("qml-") + fileName.split('/').last(); + + if (imageName.isEmpty()){ + if (category == "demos") + imageName = fileName + "-demo.png"; + else + imageName = fileName + "-example.png"; + if ((getResource(resolveImageUrl(imageName))).isEmpty()) + imageName = fileName + ".png"; + if ((getResource(resolveImageUrl(imageName))).isEmpty()) + imageName = fileName + "example.png"; + } + return getResource(resolveImageUrl(imageName)); +} + +QString HelpExtractor::getImageUrl(const QString &name) +{ + QString imageName = this->info[name]["image"]; + QString category = this->info[name]["category"]; + QString fileName = this->info[name]["filename"]; + bool qml = (this->info[name]["qml"] == QLatin1String("true")); + if (qml) + fileName = QLatin1String("qml-") + fileName.split('/').last(); + + if (imageName.isEmpty()){ + if (category == "demos") + imageName = fileName + "-demo.png"; + else + imageName = fileName + "-example.png"; + if ((getResource(resolveImageUrl(imageName))).isEmpty()) + imageName = fileName + ".png"; + if ((getResource(resolveImageUrl(imageName))).isEmpty()) + imageName = fileName + "example.png"; + if ((getResource(resolveImageUrl(imageName))).isEmpty()) + return ""; + } + return resolveImageUrl(imageName); +} + +QString HelpExtractor::extractTextFromParagraph(const QDomNode &parentNode) +{ + QString description; + QDomNode node = parentNode.firstChild(); + + while (!node.isNull()) { + QString beginTag; + QString endTag; + if (node.isText()) + description += node.nodeValue(); + else if (node.hasChildNodes()) { + if (node.nodeName() == "b") { + beginTag = "<b>"; + endTag = "</b>"; + } else if (node.nodeName() == "a") { + beginTag = "<tt>"; + endTag = "</tt>"; + } else if (node.nodeName() == "i") { + beginTag = "<i>"; + endTag = "</i>"; + } else if (node.nodeName() == "tt") { + beginTag = "<tt>"; + endTag = "</tt>"; + } + description += beginTag + extractTextFromParagraph(node) + endTag; + } + node = node.nextSibling(); + } + + return description; +} + +bool HelpExtractor::isSummaryOf(const QString &text, const QString &example) +{ + return (!text.contains("[") && + text.indexOf(QRegExp(QString("(In )?((The|This) )?(%1 )?.*(tutorial|example|demo|application)").arg(example), + Qt::CaseInsensitive)) != -1); +} diff --git a/src/tools/examplesscanner/helpextractor.h b/src/tools/examplesscanner/helpextractor.h new file mode 100644 index 0000000000..4210a6635d --- /dev/null +++ b/src/tools/examplesscanner/helpextractor.h @@ -0,0 +1,41 @@ +#ifndef HELPEXTRACTOR_H +#define HELPEXTRACTOR_H + +#include <QtCore> +#include <QtXml> +#include <QtHelp> + +typedef QHash<QString, QString> StringHash; +typedef QHash<QString, StringHash> HashHash; + +class HelpExtractor +{ +public: + HelpExtractor(); + void readXmlDocument(); + +private: + void initHelpEngine(); + void convertToAggregatableXml(const QDomElement &documentElement); + QByteArray getResource(const QString &name); + QByteArray getHtml(const QString &name); + QByteArray getImage(const QString &name); + QString getImageUrl(const QString &name); + QString resolveDocUrl(const QString &name); + QString resolveImageUrl(const QString &name); + QString loadDescription(const QString &name); + void readInfoAboutExample(const QDomElement &example); + QString extractTextFromParagraph(const QDomNode &parentNode); + bool isSummaryOf(const QString &text, const QString &example); + + HashHash info; + QHelpEngineCore *helpEngine; + QDomDocument *contentsDoc; + QString helpRootUrl; + QDir docDir; + QDir imgDir; + QString lastCategory; + +}; + +#endif // HELPEXTRACTOR_H diff --git a/src/tools/examplesscanner/main.cpp b/src/tools/examplesscanner/main.cpp new file mode 100644 index 0000000000..5c47a9788b --- /dev/null +++ b/src/tools/examplesscanner/main.cpp @@ -0,0 +1,9 @@ +#include <QtCore> + +#include "helpextractor.h" + +int main(int argc, char *argv[]) +{ + HelpExtractor extractor; + extractor.readXmlDocument(); +} |