summaryrefslogtreecommitdiffstats
path: root/src/qdoc/helpprojectwriter.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/qdoc/helpprojectwriter.cpp')
-rw-r--r--src/qdoc/helpprojectwriter.cpp815
1 files changed, 0 insertions, 815 deletions
diff --git a/src/qdoc/helpprojectwriter.cpp b/src/qdoc/helpprojectwriter.cpp
deleted file mode 100644
index 447cb8264..000000000
--- a/src/qdoc/helpprojectwriter.cpp
+++ /dev/null
@@ -1,815 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include "helpprojectwriter.h"
-
-#include "access.h"
-#include "aggregate.h"
-#include "atom.h"
-#include "classnode.h"
-#include "collectionnode.h"
-#include "config.h"
-#include "enumnode.h"
-#include "functionnode.h"
-#include "htmlgenerator.h"
-#include "node.h"
-#include "qdocdatabase.h"
-#include "typedefnode.h"
-
-#include <QtCore/qcryptographichash.h>
-#include <QtCore/qdebug.h>
-#include <QtCore/qhash.h>
-
-QT_BEGIN_NAMESPACE
-
-HelpProjectWriter::HelpProjectWriter(const QString &defaultFileName, Generator *g)
-{
- reset(defaultFileName, g);
-}
-
-void HelpProjectWriter::reset(const QString &defaultFileName, Generator *g)
-{
- m_projects.clear();
- m_gen = g;
- /*
- Get the pointer to the singleton for the qdoc database and
- store it locally. This replaces all the local accesses to
- the node tree, which are now private.
- */
- m_qdb = QDocDatabase::qdocDB();
-
- // The output directory should already have been checked by the calling
- // generator.
- Config &config = Config::instance();
- m_outputDir = config.getOutputDir();
-
- const QStringList names = config.getStringList(CONFIG_QHP + Config::dot + "projects");
-
- for (const auto &projectName : names) {
- HelpProject project;
- project.m_name = projectName;
-
- QString prefix = CONFIG_QHP + Config::dot + projectName + Config::dot;
- project.m_helpNamespace = config.getString(prefix + "namespace");
- project.m_virtualFolder = config.getString(prefix + "virtualFolder");
- project.m_version = config.getString(CONFIG_VERSION);
- project.m_fileName = config.getString(prefix + "file");
- if (project.m_fileName.isEmpty())
- project.m_fileName = defaultFileName;
- project.m_extraFiles = config.getStringSet(prefix + "extraFiles");
- project.m_extraFiles += config.getStringSet(CONFIG_QHP + Config::dot + "extraFiles");
- project.m_indexTitle = config.getString(prefix + "indexTitle");
- project.m_indexRoot = config.getString(prefix + "indexRoot");
- const auto &filterAttributes = config.getStringList(prefix + "filterAttributes");
- project.m_filterAttributes =
- QSet<QString>(filterAttributes.cbegin(), filterAttributes.cend());
- project.m_includeIndexNodes = config.getBool(prefix + "includeIndexNodes");
- const QSet<QString> customFilterNames = config.subVars(prefix + "customFilters");
- for (const auto &filterName : customFilterNames) {
- QString name = config.getString(prefix + "customFilters" + Config::dot + filterName
- + Config::dot + "name");
- const auto &filters =
- config.getStringList(prefix + "customFilters" + Config::dot + filterName
- + Config::dot + "filterAttributes");
- project.m_customFilters[name] = QSet<QString>(filters.cbegin(), filters.cend());
- }
-
- const auto excludedPrefixes = config.getStringSet(prefix + "excluded");
- for (auto name : excludedPrefixes)
- project.m_excluded.insert(name.replace(QLatin1Char('\\'), QLatin1Char('/')));
-
- const auto subprojectPrefixes = config.getStringList(prefix + "subprojects");
- for (const auto &name : subprojectPrefixes) {
- SubProject subproject;
- QString subprefix = prefix + "subprojects" + Config::dot + name + Config::dot;
- subproject.m_title = config.getString(subprefix + "title");
- if (subproject.m_title.isEmpty())
- continue;
- subproject.m_indexTitle = config.getString(subprefix + "indexTitle");
- subproject.m_sortPages = config.getBool(subprefix + "sortPages");
- subproject.m_type = config.getString(subprefix + "type");
- readSelectors(subproject, config.getStringList(subprefix + "selectors"));
- project.m_subprojects.append(subproject);
- }
-
- if (project.m_subprojects.isEmpty()) {
- SubProject subproject;
- readSelectors(subproject, config.getStringList(prefix + "selectors"));
- project.m_subprojects.insert(0, subproject);
- }
-
- m_projects.append(project);
- }
-}
-
-void HelpProjectWriter::readSelectors(SubProject &subproject, const QStringList &selectors)
-{
- QHash<QString, Node::NodeType> typeHash;
- typeHash["namespace"] = Node::Namespace;
- typeHash["class"] = Node::Class;
- typeHash["struct"] = Node::Struct;
- typeHash["union"] = Node::Union;
- typeHash["header"] = Node::HeaderFile;
- typeHash["headerfile"] = Node::HeaderFile;
- typeHash["doc"] = Node::Page; // Unused (supported but ignored as a prefix)
- typeHash["fake"] = Node::Page; // Unused (supported but ignored as a prefix)
- typeHash["page"] = Node::Page;
- typeHash["enum"] = Node::Enum;
- typeHash["example"] = Node::Example;
- typeHash["externalpage"] = Node::ExternalPage;
- typeHash["typedef"] = Node::Typedef;
- typeHash["typealias"] = Node::TypeAlias;
- typeHash["function"] = Node::Function;
- typeHash["property"] = Node::Property;
- typeHash["variable"] = Node::Variable;
- typeHash["group"] = Node::Group;
- typeHash["module"] = Node::Module;
- typeHash["jsmodule"] = Node::JsModule;
- typeHash["qmlmodule"] = Node::QmlModule;
- typeHash["qmlproperty"] = Node::JsProperty;
- typeHash["jsproperty"] = Node::QmlProperty;
- typeHash["qmlclass"] = Node::QmlType; // Legacy alias for 'qmltype'
- typeHash["qmltype"] = Node::QmlType;
- typeHash["qmlbasictype"] = Node::QmlBasicType;
-
- for (const QString &selector : selectors) {
- QStringList pieces = selector.split(QLatin1Char(':'));
- // Remove doc: or fake: prefix
- if (pieces.size() > 1 && typeHash.value(pieces[0].toLower()) == Node::Page)
- pieces.takeFirst();
-
- QString typeName = pieces.takeFirst().toLower();
- if (!typeHash.contains(typeName))
- continue;
-
- subproject.m_selectors << typeHash.value(typeName);
- if (!pieces.isEmpty()) {
- pieces = pieces[0].split(QLatin1Char(','));
- for (const auto &piece : qAsConst(pieces)) {
- if (typeHash[typeName] == Node::Group
- || typeHash[typeName] == Node::Module
- || typeHash[typeName] == Node::QmlModule
- || typeHash[typeName] == Node::JsModule) {
- subproject.m_groups << piece.toLower();
- }
- }
- }
- }
-}
-
-void HelpProjectWriter::addExtraFile(const QString &file)
-{
- for (HelpProject &project : m_projects)
- project.m_extraFiles.insert(file);
-}
-
-Keyword HelpProjectWriter::keywordDetails(const Node *node) const
-{
- QString ref = m_gen->fullDocumentLocation(node, false);
-
- if (node->parent() && !node->parent()->name().isEmpty()) {
- QString name = (node->isEnumType() || node->isTypedef())
- ? node->parent()->name()+"::"+node->name()
- : node->name();
- QString id = (!node->isRelatedNonmember())
- ? node->parent()->name()+"::"+node->name()
- : node->name();
- return Keyword(name, id, ref);
- } else if (node->isQmlType() || node->isQmlBasicType()) {
- const QString &name = node->name();
- QString moduleName = node->logicalModuleName();
- QStringList ids("QML." + name);
- if (!moduleName.isEmpty()) {
- QString majorVersion = node->logicalModule()
- ? node->logicalModule()->logicalModuleVersion().split('.')[0]
- : QString();
- ids << "QML." + moduleName + majorVersion + "." + name;
- }
- return Keyword(name, ids, ref);
- } else if (node->isJsType() || node->isJsBasicType()) {
- return Keyword(node->name(), "JS." + node->name(), ref);
- } else if (node->isTextPageNode()) {
- const auto *pageNode = static_cast<const PageNode *>(node);
- return Keyword(pageNode->fullTitle(), pageNode->fullTitle(), ref);
- } else {
- return Keyword(node->name(), node->name(), ref);
- }
-}
-
-bool HelpProjectWriter::generateSection(HelpProject &project, QXmlStreamWriter & /* writer */,
- const Node *node)
-{
- if (!node->url().isEmpty() && !(project.m_includeIndexNodes && !node->url().startsWith("http")))
- return false;
-
- if (node->isPrivate() || node->isInternal() || node->isDontDocument())
- return false;
-
- if (node->name().isEmpty())
- return true;
-
- QString docPath = node->doc().location().filePath();
- if (!docPath.isEmpty() && project.m_excluded.contains(docPath))
- return false;
-
- QString objName = node->isTextPageNode() ? node->fullTitle() : node->fullDocumentName();
- // Only add nodes to the set for each subproject if they match a selector.
- // Those that match will be listed in the table of contents.
-
- for (int i = 0; i < project.m_subprojects.length(); i++) {
- SubProject subproject = project.m_subprojects[i];
- // No selectors: accept all nodes.
- if (subproject.m_selectors.isEmpty()) {
- project.m_subprojects[i].m_nodes[objName] = node;
- } else if (subproject.m_selectors.contains(node->nodeType())) {
- // Add all group members for '[group|module|qmlmodule]:name' selector
- if (node->isCollectionNode()) {
- if (project.m_subprojects[i].m_groups.contains(node->name().toLower())) {
- const auto *cn = static_cast<const CollectionNode *>(node);
- const auto members = cn->members();
- for (const Node *m : members) {
- if (!m->isInAPI())
- continue;
- QString memberName =
- m->isTextPageNode() ? m->fullTitle() : m->fullDocumentName();
- project.m_subprojects[i].m_nodes[memberName] = m;
- }
- continue;
- } else if (!project.m_subprojects[i].m_groups.isEmpty()) {
- continue; // Node does not represent specified group(s)
- }
- } else if (node->isTextPageNode()) {
- if (node->isExternalPage() || node->fullTitle().isEmpty())
- continue;
- }
- project.m_subprojects[i].m_nodes[objName] = node;
- }
- }
-
- switch (node->nodeType()) {
-
- case Node::Class:
- case Node::Struct:
- case Node::Union:
- project.m_keywords.append(keywordDetails(node));
- break;
- case Node::QmlType:
- case Node::QmlBasicType:
- case Node::JsType:
- case Node::JsBasicType:
- if (node->doc().hasKeywords()) {
- const auto keywords = node->doc().keywords();
- for (const Atom *keyword : keywords) {
- if (!keyword->string().isEmpty()) {
- project.m_keywords.append(Keyword(keyword->string(), keyword->string(),
- m_gen->fullDocumentLocation(node, false)));
- }
- else
- node->doc().location().warning(
- QStringLiteral("Bad keyword in %1")
- .arg(m_gen->fullDocumentLocation(node, false)));
- }
- }
- project.m_keywords.append(keywordDetails(node));
- break;
-
- case Node::Namespace:
- project.m_keywords.append(keywordDetails(node));
- break;
-
- case Node::Enum:
- project.m_keywords.append(keywordDetails(node));
- {
- const auto *enumNode = static_cast<const EnumNode *>(node);
- const auto items = enumNode->items();
- for (const auto &item : items) {
- if (enumNode->itemAccess(item.name()) == Access::Private)
- continue;
-
- QString name;
- QString id;
- if (!node->parent()->name().isEmpty()) {
- name = id = node->parent()->name() + "::" + item.name();
- } else {
- name = id = item.name();
- }
- QString ref = m_gen->fullDocumentLocation(node, false);
- project.m_keywords.append(Keyword(name, id, ref));
- }
- }
- break;
-
- case Node::Group:
- case Node::Module:
- case Node::QmlModule:
- case Node::JsModule: {
- const auto *cn = static_cast<const CollectionNode *>(node);
- if (!cn->fullTitle().isEmpty()) {
- if (cn->doc().hasKeywords()) {
- const auto keywords = cn->doc().keywords();
- for (const Atom *keyword : keywords) {
- if (!keyword->string().isEmpty()) {
- project.m_keywords.append(
- Keyword(keyword->string(), keyword->string(),
- m_gen->fullDocumentLocation(node, false)));
- } else
- cn->doc().location().warning(
- QStringLiteral("Bad keyword in %1")
- .arg(m_gen->fullDocumentLocation(node, false)));
- }
- }
- project.m_keywords.append(keywordDetails(node));
- }
- } break;
-
- case Node::Property:
- case Node::QmlProperty:
- case Node::JsProperty:
- project.m_keywords.append(keywordDetails(node));
- break;
-
- case Node::Function: {
- const auto *funcNode = static_cast<const FunctionNode *>(node);
-
- /*
- QML and JS methods, signals, and signal handlers used to be node types,
- but now they are Function nodes with a Metaness value that specifies
- what kind of function they are, QmlSignal, JsSignal, QmlMethod, etc. It
- suffices at this point to test whether the node is of the QML or JS Genus,
- because we already know it is NodeType::Function.
- */
- if (funcNode->isQmlNode() || funcNode->isJsNode()) {
- project.m_keywords.append(keywordDetails(node));
- break;
- }
- // Only insert keywords for non-constructors. Constructors are covered
- // by the classes themselves.
-
- if (!funcNode->isSomeCtor())
- project.m_keywords.append(keywordDetails(node));
-
- // Insert member status flags into the entries for the parent
- // node of the function, or the node it is related to.
- // Since parent nodes should have already been inserted into
- // the set of files, we only need to ensure that related nodes
- // are inserted.
-
- if (node->parent())
- project.m_memberStatus[node->parent()].insert(node->status());
- } break;
- case Node::TypeAlias:
- case Node::Typedef: {
- const auto *typedefNode = static_cast<const TypedefNode *>(node);
- Keyword typedefDetails = keywordDetails(node);
- const EnumNode *enumNode = typedefNode->associatedEnum();
- // Use the location of any associated enum node in preference
- // to that of the typedef.
- if (enumNode)
- typedefDetails.m_ref = m_gen->fullDocumentLocation(enumNode, false);
-
- project.m_keywords.append(typedefDetails);
- } break;
-
- case Node::Variable: {
- project.m_keywords.append(keywordDetails(node));
- } break;
-
- // Page nodes (such as manual pages) contain subtypes, titles and other
- // attributes.
- case Node::Page: {
- const auto *pn = static_cast<const PageNode *>(node);
- if (!pn->fullTitle().isEmpty()) {
- if (pn->doc().hasKeywords()) {
- const auto keywords = pn->doc().keywords();
- for (const Atom *keyword : keywords) {
- if (!keyword->string().isEmpty()) {
- project.m_keywords.append(
- Keyword(keyword->string(), keyword->string(),
- m_gen->fullDocumentLocation(node, false)));
- } else {
- QString loc = m_gen->fullDocumentLocation(node, false);
- pn->doc().location().warning(QStringLiteral("Bad keyword in %1").arg(loc));
- }
- }
- }
- project.m_keywords.append(keywordDetails(node));
- }
- break;
- }
- default:;
- }
-
- // Add all images referenced in the page to the set of files to include.
- const Atom *atom = node->doc().body().firstAtom();
- while (atom) {
- if (atom->type() == Atom::Image || atom->type() == Atom::InlineImage) {
- // Images are all placed within a single directory regardless of
- // whether the source images are in a nested directory structure.
- QStringList pieces = atom->string().split(QLatin1Char('/'));
- project.m_files.insert("images/" + pieces.last());
- }
- atom = atom->next();
- }
-
- return true;
-}
-
-void HelpProjectWriter::generateSections(HelpProject &project, QXmlStreamWriter &writer,
- const Node *node)
-{
- /*
- Don't include index nodes in the help file.
- */
- if (node->isIndexNode())
- return;
- if (!generateSection(project, writer, node))
- return;
-
- if (node->isAggregate()) {
- const auto *aggregate = static_cast<const Aggregate *>(node);
-
- // Ensure that we don't visit nodes more than once.
- QSet<const Node *> childSet;
- const NodeList &children = aggregate->childNodes();
- for (const auto *child : children) {
- // Skip related non-members adopted by some other aggregate
- if (child->parent() != aggregate)
- continue;
- if (child->isIndexNode() || child->isPrivate())
- continue;
- if (child->isTextPageNode()) {
- childSet << child;
- } else {
- // Store member status of children
- project.m_memberStatus[node].insert(child->status());
- if (child->isFunction() && static_cast<const FunctionNode *>(child)->isOverload())
- continue;
- childSet << child;
- }
- }
- for (const auto *child : qAsConst(childSet))
- generateSections(project, writer, child);
- }
-}
-
-void HelpProjectWriter::generate()
-{
- for (HelpProject &project : m_projects)
- generateProject(project);
-}
-
-void HelpProjectWriter::writeHashFile(QFile &file)
-{
- QCryptographicHash hash(QCryptographicHash::Sha1);
- hash.addData(&file);
-
- QFile hashFile(file.fileName() + ".sha1");
- if (!hashFile.open(QFile::WriteOnly | QFile::Text))
- return;
-
- hashFile.write(hash.result().toHex());
- hashFile.close();
-}
-
-void HelpProjectWriter::writeSection(QXmlStreamWriter &writer, const QString &path,
- const QString &value)
-{
- writer.writeStartElement(QStringLiteral("section"));
- writer.writeAttribute(QStringLiteral("ref"), path);
- writer.writeAttribute(QStringLiteral("title"), value);
- writer.writeEndElement(); // section
-}
-
-/*!
- Write subsections for all members, compatibility members and obsolete members.
- */
-void HelpProjectWriter::addMembers(HelpProject &project, QXmlStreamWriter &writer, const Node *node)
-{
- if (node->isQmlBasicType() || node->isJsBasicType())
- return;
-
- QString href = m_gen->fullDocumentLocation(node, false);
- href = href.left(href.size() - 5);
- if (href.isEmpty())
- return;
-
- bool derivedClass = false;
- if (node->isClassNode())
- derivedClass = !(static_cast<const ClassNode *>(node)->baseClasses().isEmpty());
-
- // Do not generate a 'List of all members' for namespaces or header files,
- // but always generate it for derived classes and QML classes
- if (!node->isNamespace() && !node->isHeader()
- && (derivedClass || node->isQmlType() || node->isJsType()
- || !project.m_memberStatus[node].isEmpty())) {
- QString membersPath = href + QStringLiteral("-members.html");
- writeSection(writer, membersPath, QStringLiteral("List of all members"));
- }
- if (project.m_memberStatus[node].contains(Node::Deprecated)) {
- QString obsoletePath = href + QStringLiteral("-obsolete.html");
- writeSection(writer, obsoletePath, QStringLiteral("Obsolete members"));
- }
-}
-
-void HelpProjectWriter::writeNode(HelpProject &project, QXmlStreamWriter &writer, const Node *node)
-{
- QString href = m_gen->fullDocumentLocation(node, false);
- QString objName = node->name();
-
- switch (node->nodeType()) {
-
- case Node::Class:
- case Node::Struct:
- case Node::Union:
- case Node::QmlType:
- case Node::JsType:
- case Node::QmlBasicType:
- case Node::JsBasicType: {
- QString typeStr = m_gen->typeString(node);
- if (!typeStr.isEmpty())
- typeStr[0] = typeStr[0].toTitleCase();
- writer.writeStartElement("section");
- writer.writeAttribute("ref", href);
- if (node->parent() && !node->parent()->name().isEmpty())
- writer.writeAttribute("title",
- QStringLiteral("%1::%2 %3 Reference")
- .arg(node->parent()->name(), objName, typeStr));
- else
- writer.writeAttribute("title", QStringLiteral("%1 %2 Reference").arg(objName, typeStr));
-
- addMembers(project, writer, node);
- writer.writeEndElement(); // section
- } break;
-
- case Node::Namespace:
- writeSection(writer, href, objName);
- break;
-
- case Node::Example:
- case Node::HeaderFile:
- case Node::Page:
- case Node::Group:
- case Node::Module:
- case Node::JsModule:
- case Node::QmlModule: {
- writer.writeStartElement("section");
- writer.writeAttribute("ref", href);
- writer.writeAttribute("title", node->fullTitle());
- if (node->nodeType() == Node::HeaderFile)
- addMembers(project, writer, node);
- writer.writeEndElement(); // section
- } break;
- default:;
- }
-}
-
-void HelpProjectWriter::generateProject(HelpProject &project)
-{
- const Node *rootNode;
-
- // Restrict searching only to the local (primary) tree
- QList<Tree *> searchOrder = m_qdb->searchOrder();
- m_qdb->setLocalSearch();
-
- if (!project.m_indexRoot.isEmpty())
- rootNode = m_qdb->findPageNodeByTitle(project.m_indexRoot);
- else
- rootNode = m_qdb->primaryTreeRoot();
-
- if (rootNode == nullptr)
- return;
-
- project.m_files.clear();
- project.m_keywords.clear();
-
- QFile file(m_outputDir + QDir::separator() + project.m_fileName);
- if (!file.open(QFile::WriteOnly | QFile::Text))
- return;
-
- QXmlStreamWriter writer(&file);
- writer.setAutoFormatting(true);
- writer.writeStartDocument();
- writer.writeStartElement("QtHelpProject");
- writer.writeAttribute("version", "1.0");
-
- // Write metaData, virtualFolder and namespace elements.
- writer.writeTextElement("namespace", project.m_helpNamespace);
- writer.writeTextElement("virtualFolder", project.m_virtualFolder);
- writer.writeStartElement("metaData");
- writer.writeAttribute("name", "version");
- writer.writeAttribute("value", project.m_version);
- writer.writeEndElement();
-
- // Write customFilter elements.
- for (auto it = project.m_customFilters.constBegin(); it != project.m_customFilters.constEnd();
- ++it) {
- writer.writeStartElement("customFilter");
- writer.writeAttribute("name", it.key());
- QStringList sortedAttributes = it.value().values();
- sortedAttributes.sort();
- for (const auto &filter : qAsConst(sortedAttributes))
- writer.writeTextElement("filterAttribute", filter);
- writer.writeEndElement(); // customFilter
- }
-
- // Start the filterSection.
- writer.writeStartElement("filterSection");
-
- // Write filterAttribute elements.
- QStringList sortedFilterAttributes = project.m_filterAttributes.values();
- sortedFilterAttributes.sort();
- for (const auto &filterName : qAsConst(sortedFilterAttributes))
- writer.writeTextElement("filterAttribute", filterName);
-
- writer.writeStartElement("toc");
- writer.writeStartElement("section");
- const Node *node = m_qdb->findPageNodeByTitle(project.m_indexTitle);
- if (!node)
- node = m_qdb->findNodeByNameAndType(QStringList(project.m_indexTitle), &Node::isPageNode);
- if (!node)
- node = m_qdb->findNodeByNameAndType(QStringList("index.html"), &Node::isPageNode);
- QString indexPath;
- if (node)
- indexPath = m_gen->fullDocumentLocation(node, false);
- else
- indexPath = "index.html";
- writer.writeAttribute("ref", indexPath);
- writer.writeAttribute("title", project.m_indexTitle);
-
- generateSections(project, writer, rootNode);
-
- for (int i = 0; i < project.m_subprojects.length(); i++) {
- SubProject subproject = project.m_subprojects[i];
-
- if (subproject.m_type == QLatin1String("manual")) {
-
- const Node *indexPage = m_qdb->findNodeForTarget(subproject.m_indexTitle, nullptr);
- if (indexPage) {
- Text indexBody = indexPage->doc().body();
- const Atom *atom = indexBody.firstAtom();
- QStack<int> sectionStack;
- bool inItem = false;
-
- while (atom) {
- switch (atom->type()) {
- case Atom::ListLeft:
- sectionStack.push(0);
- break;
- case Atom::ListRight:
- if (sectionStack.pop() > 0)
- writer.writeEndElement(); // section
- break;
- case Atom::ListItemLeft:
- inItem = true;
- break;
- case Atom::ListItemRight:
- inItem = false;
- break;
- case Atom::Link:
- if (inItem) {
- if (sectionStack.top() > 0)
- writer.writeEndElement(); // section
-
- const Node *page = m_qdb->findNodeForTarget(atom->string(), nullptr);
- writer.writeStartElement("section");
- QString indexPath = m_gen->fullDocumentLocation(page, false);
- writer.writeAttribute("ref", indexPath);
- writer.writeAttribute("title", atom->linkText());
-
- sectionStack.top() += 1;
- }
- break;
- default:;
- }
-
- if (atom == indexBody.lastAtom())
- break;
- atom = atom->next();
- }
- } else
- rootNode->doc().location().warning(
- QStringLiteral("Failed to find index: %1").arg(subproject.m_indexTitle));
-
- } else {
-
- writer.writeStartElement("section");
- QString indexPath = m_gen->fullDocumentLocation(
- m_qdb->findNodeForTarget(subproject.m_indexTitle, nullptr), false);
- writer.writeAttribute("ref", indexPath);
- writer.writeAttribute("title", subproject.m_title);
-
- if (subproject.m_sortPages) {
- QStringList titles = subproject.m_nodes.keys();
- titles.sort();
- for (const auto &title : qAsConst(titles)) {
- writeNode(project, writer, subproject.m_nodes[title]);
- }
- } else {
- // Find a contents node and navigate from there, using the NextLink values.
- QSet<QString> visited;
- bool contentsFound = false;
- for (const auto *node : qAsConst(subproject.m_nodes)) {
- QString nextTitle = node->links().value(Node::NextLink).first;
- if (!nextTitle.isEmpty()
- && node->links().value(Node::ContentsLink).first.isEmpty()) {
-
- const Node *nextPage = m_qdb->findNodeForTarget(nextTitle, nullptr);
-
- // Write the contents node.
- writeNode(project, writer, node);
- contentsFound = true;
-
- while (nextPage) {
- writeNode(project, writer, nextPage);
- nextTitle = nextPage->links().value(Node::NextLink).first;
- if (nextTitle.isEmpty() || visited.contains(nextTitle))
- break;
- nextPage = m_qdb->findNodeForTarget(nextTitle, nullptr);
- visited.insert(nextTitle);
- }
- break;
- }
- }
- // No contents/nextpage links found, write all nodes unsorted
- if (!contentsFound) {
- QList<const Node *> subnodes = subproject.m_nodes.values();
-
- std::sort(subnodes.begin(), subnodes.end(), Node::nodeNameLessThan);
-
- for (const auto *node : qAsConst(subnodes))
- writeNode(project, writer, node);
- }
- }
-
- writer.writeEndElement(); // section
- }
- }
-
- // Restore original search order
- m_qdb->setSearchOrder(searchOrder);
-
- writer.writeEndElement(); // section
- writer.writeEndElement(); // toc
-
- writer.writeStartElement("keywords");
- std::sort(project.m_keywords.begin(), project.m_keywords.end());
- for (const auto &k : qAsConst(project.m_keywords)) {
- for (const auto &id : qAsConst(k.m_ids)) {
- writer.writeStartElement("keyword");
- writer.writeAttribute("name", k.m_name);
- writer.writeAttribute("id", id);
- writer.writeAttribute("ref", k.m_ref);
- writer.writeEndElement(); //keyword
- }
- }
- writer.writeEndElement(); // keywords
-
- writer.writeStartElement("files");
-
- // The list of files to write is the union of generated files and
- // other files (images and extras) included in the project
- QSet<QString> files =
- QSet<QString>(m_gen->outputFileNames().cbegin(), m_gen->outputFileNames().cend());
- files.unite(project.m_files);
- files.unite(project.m_extraFiles);
- QStringList sortedFiles = files.values();
- sortedFiles.sort();
- for (const auto &usedFile : qAsConst(sortedFiles)) {
- if (!usedFile.isEmpty())
- writer.writeTextElement("file", usedFile);
- }
- writer.writeEndElement(); // files
-
- writer.writeEndElement(); // filterSection
- writer.writeEndElement(); // QtHelpProject
- writer.writeEndDocument();
- writeHashFile(file);
- file.close();
-}
-
-QT_END_NAMESPACE