summaryrefslogtreecommitdiffstats
path: root/src/tools/qdoc/helpprojectwriter.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/tools/qdoc/helpprojectwriter.cpp')
-rw-r--r--src/tools/qdoc/helpprojectwriter.cpp858
1 files changed, 0 insertions, 858 deletions
diff --git a/src/tools/qdoc/helpprojectwriter.cpp b/src/tools/qdoc/helpprojectwriter.cpp
deleted file mode 100644
index 8161913f1f..0000000000
--- a/src/tools/qdoc/helpprojectwriter.cpp
+++ /dev/null
@@ -1,858 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2015 The Qt Company Ltd.
-** Contact: http://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL21$
-** 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 http://www.qt.io/terms-conditions. For further
-** information use the contact form at http://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 2.1 or version 3 as published by the Free
-** Software Foundation and appearing in the file LICENSE.LGPLv21 and
-** LICENSE.LGPLv3 included in the packaging of this file. Please review the
-** following information to ensure the GNU Lesser General Public License
-** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
-** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
-**
-** As a special exception, The Qt Company gives you certain additional
-** rights. These rights are described in The Qt Company LGPL Exception
-** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include <qcryptographichash.h>
-#include <qdebug.h>
-#include <qhash.h>
-#include <qmap.h>
-
-#include "atom.h"
-#include "helpprojectwriter.h"
-#include "htmlgenerator.h"
-#include "config.h"
-#include "node.h"
-#include "qdocdatabase.h"
-#include <qdebug.h>
-
-QT_BEGIN_NAMESPACE
-
-HelpProjectWriter::HelpProjectWriter(const Config &config,
- const QString &defaultFileName,
- Generator* g)
-{
- reset(config, defaultFileName, g);
-}
-
-void HelpProjectWriter::reset(const Config &config,
- const QString &defaultFileName,
- Generator* g)
-{
- projects.clear();
- 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.
- */
- qdb_ = QDocDatabase::qdocDB();
-
- // The output directory should already have been checked by the calling
- // generator.
- outputDir = config.getOutputDir();
-
- QStringList names = config.getStringList(CONFIG_QHP + Config::dot + "projects");
-
- foreach (const QString &projectName, names) {
- HelpProject project;
- project.name = projectName;
-
- QString prefix = CONFIG_QHP + Config::dot + projectName + Config::dot;
- project.helpNamespace = config.getString(prefix + "namespace");
- project.virtualFolder = config.getString(prefix + "virtualFolder");
- project.fileName = config.getString(prefix + "file");
- if (project.fileName.isEmpty())
- project.fileName = defaultFileName;
- project.extraFiles = config.getStringSet(prefix + "extraFiles");
- project.extraFiles += config.getStringSet(CONFIG_QHP + Config::dot + "extraFiles");
- project.indexTitle = config.getString(prefix + "indexTitle");
- project.indexRoot = config.getString(prefix + "indexRoot");
- project.filterAttributes = config.getStringList(prefix + "filterAttributes").toSet();
- project.includeIndexNodes = config.getBool(prefix + "includeIndexNodes");
- QSet<QString> customFilterNames = config.subVars(prefix + "customFilters");
- foreach (const QString &filterName, customFilterNames) {
- QString name = config.getString(prefix + "customFilters" + Config::dot + filterName + Config::dot + "name");
- QSet<QString> filters = config.getStringList(prefix + "customFilters" + Config::dot + filterName + Config::dot + "filterAttributes").toSet();
- project.customFilters[name] = filters;
- }
- //customFilters = config.defs.
-
- foreach (QString name, config.getStringSet(prefix + "excluded"))
- project.excluded.insert(name.replace(QLatin1Char('\\'), QLatin1Char('/')));
-
- foreach (const QString &name, config.getStringList(prefix + "subprojects")) {
- SubProject subproject;
- QString subprefix = prefix + "subprojects" + Config::dot + name + Config::dot;
- subproject.title = config.getString(subprefix + "title");
- subproject.indexTitle = config.getString(subprefix + "indexTitle");
- subproject.sortPages = config.getBool(subprefix + "sortPages");
- subproject.type = config.getString(subprefix + "type");
- readSelectors(subproject, config.getStringList(subprefix + "selectors"));
- project.subprojects.append(subproject);
- }
-
- if (project.subprojects.isEmpty()) {
- SubProject subproject;
- readSelectors(subproject, config.getStringList(prefix + "selectors"));
- project.subprojects.insert(0, subproject);
- }
-
- 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["doc"] = Node::Document;
- typeHash["fake"] = Node::Document; // Legacy alias for 'doc'
- typeHash["enum"] = Node::Enum;
- typeHash["typedef"] = Node::Typedef;
- typeHash["function"] = Node::Function;
- typeHash["property"] = Node::Property;
- typeHash["variable"] = Node::Variable;
- typeHash["group"] = Node::Group;
- typeHash["module"] = Node::Module;
- typeHash["qmlmodule"] = Node::QmlModule;
- typeHash["qmlproperty"] = Node::QmlProperty;
- typeHash["qmlsignal"] = Node::QmlSignal;
- typeHash["qmlsignalhandler"] = Node::QmlSignalHandler;
- typeHash["qmlmethod"] = Node::QmlMethod;
- typeHash["qmlpropertygroup"] = Node::QmlPropertyGroup;
- typeHash["qmlclass"] = Node::QmlType; // Legacy alias for 'qmltype'
- typeHash["qmltype"] = Node::QmlType;
- typeHash["qmlbasictype"] = Node::QmlBasicType;
-
- QHash<QString, Node::DocSubtype> docSubtypeHash;
- docSubtypeHash["example"] = Node::Example;
- docSubtypeHash["headerfile"] = Node::HeaderFile;
- docSubtypeHash["file"] = Node::File;
- docSubtypeHash["page"] = Node::Page;
- docSubtypeHash["externalpage"] = Node::ExternalPage;
-
- QSet<Node::DocSubtype> allSubTypes = QSet<Node::DocSubtype>::fromList(docSubtypeHash.values());
-
- foreach (const QString &selector, selectors) {
- QStringList pieces = selector.split(QLatin1Char(':'));
- if (pieces.size() == 1) {
- QString lower = selector.toLower();
- if (typeHash.contains(lower))
- subproject.selectors[typeHash[lower]] = allSubTypes;
- } else if (pieces.size() >= 2) {
- QString docType = pieces[0].toLower();
- pieces = pieces[1].split(QLatin1Char(','));
- if (typeHash.contains(docType)) {
- QSet<Node::DocSubtype> docSubtypes;
- for (int i = 0; i < pieces.size(); ++i) {
- QString piece = pieces[i].toLower();
- if (typeHash[docType] == Node::Group) {
- subproject.groups << piece;
- continue;
- }
- if (docSubtypeHash.contains(piece))
- docSubtypes.insert(docSubtypeHash[piece]);
- }
- subproject.selectors[typeHash[docType]] = docSubtypes;
- }
- }
- }
-}
-
-void HelpProjectWriter::addExtraFile(const QString &file)
-{
- for (int i = 0; i < projects.size(); ++i)
- projects[i].extraFiles.insert(file);
-}
-
-void HelpProjectWriter::addExtraFiles(const QSet<QString> &files)
-{
- for (int i = 0; i < projects.size(); ++i)
- projects[i].extraFiles.unite(files);
-}
-
-/*
- Returns a list of strings describing the keyword details for a given node.
-
- The first string is the human-readable name to be shown in Assistant.
- The second string is a unique identifier.
- The third string is the location of the documentation for the keyword.
-*/
-QStringList HelpProjectWriter::keywordDetails(const Node *node) const
-{
- QStringList details;
-
- if (node->parent() && !node->parent()->name().isEmpty()) {
- // "name"
- if (node->type() == Node::Enum || node->type() == Node::Typedef)
- details << node->parent()->name()+"::"+node->name();
- else
- details << node->name();
- // "id"
- details << node->parent()->name()+"::"+node->name();
- }
- else if (node->isQmlType() || node->isQmlBasicType()) {
- details << node->name();
- details << "QML." + node->name();
- }
- else if (node->isJsType() || node->isJsBasicType()) {
- details << node->name();
- details << "JS." + node->name();
- }
- else if (node->isDocumentNode()) {
- const DocumentNode *fake = static_cast<const DocumentNode *>(node);
- details << fake->fullTitle();
- details << fake->fullTitle();
- }
- else {
- details << node->name();
- details << node->name();
- }
- details << gen_->fullDocumentLocation(node, false);
- return details;
-}
-
-bool HelpProjectWriter::generateSection(HelpProject &project,
- QXmlStreamWriter & /* writer */,
- const Node *node)
-{
- if (!node->url().isEmpty() && !(project.includeIndexNodes && !node->url().startsWith("http")))
- return false;
-
- if (node->access() == Node::Private || node->status() == Node::Internal)
- return false;
-
- if (node->name().isEmpty())
- return true;
-
- QString docPath = node->doc().location().filePath();
- if (!docPath.isEmpty() && project.excluded.contains(docPath))
- return false;
-
- QString objName = node->isDocumentNode() ? 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.subprojects.length(); i++) {
- SubProject subproject = project.subprojects[i];
- // No selectors: accept all nodes.
- if (subproject.selectors.isEmpty()) {
- project.subprojects[i].nodes[objName] = node;
- }
- else if (subproject.selectors.contains(node->type())) {
- // Add all group members for 'group:name' selector
- if (node->isGroup()) {
- if (project.subprojects[i].groups.contains(node->name())) {
- const CollectionNode* cn = static_cast<const CollectionNode*>(node);
- foreach (const Node* m, cn->members()) {
- QString memberName = m->isDocumentNode()
- ? m->fullTitle() : m->fullDocumentName();
- project.subprojects[i].nodes[memberName] = m;
- }
- }
- }
- // Accept only the node types in the selectors hash.
- else if (node->type() != Node::Document)
- project.subprojects[i].nodes[objName] = node;
- else {
- // Accept only doc nodes with subtypes contained in the selector's
- // mask.
- const DocumentNode *docNode = static_cast<const DocumentNode *>(node);
- if (subproject.selectors[node->type()].contains(docNode->docSubtype()) &&
- docNode->docSubtype() != Node::ExternalPage &&
- !docNode->fullTitle().isEmpty()) {
-
- project.subprojects[i].nodes[objName] = node;
- }
- }
- }
- }
-
- switch (node->type()) {
-
- case Node::Class:
- project.keywords.append(keywordDetails(node));
- break;
- case Node::QmlType:
- case Node::QmlBasicType:
- if (node->doc().hasKeywords()) {
- foreach (const Atom* keyword, node->doc().keywords()) {
- if (!keyword->string().isEmpty()) {
- QStringList details;
- details << keyword->string()
- << keyword->string()
- << gen_->fullDocumentLocation(node, false);
- project.keywords.append(details);
- }
- else
- node->doc().location().warning(tr("Bad keyword in %1").arg(gen_->fullDocumentLocation(node, false)));
- }
- }
- project.keywords.append(keywordDetails(node));
- break;
-
- case Node::Namespace:
- project.keywords.append(keywordDetails(node));
- break;
-
- case Node::Enum:
- project.keywords.append(keywordDetails(node));
- {
- const EnumNode *enumNode = static_cast<const EnumNode*>(node);
- foreach (const EnumItem &item, enumNode->items()) {
- QStringList details;
-
- if (enumNode->itemAccess(item.name()) == Node::Private)
- continue;
-
- if (!node->parent()->name().isEmpty()) {
- details << node->parent()->name()+"::"+item.name(); // "name"
- details << node->parent()->name()+"::"+item.name(); // "id"
- } else {
- details << item.name(); // "name"
- details << item.name(); // "id"
- }
- details << gen_->fullDocumentLocation(node, false);
- project.keywords.append(details);
- }
- }
- break;
-
- case Node::Group:
- case Node::Module:
- case Node::QmlModule:
- {
- const CollectionNode* cn = static_cast<const CollectionNode*>(node);
- if (!cn->fullTitle().isEmpty()) {
- if (cn->doc().hasKeywords()) {
- foreach (const Atom* keyword, cn->doc().keywords()) {
- if (!keyword->string().isEmpty()) {
- QStringList details;
- details << keyword->string()
- << keyword->string()
- << gen_->fullDocumentLocation(node, false);
- project.keywords.append(details);
- }
- else
- cn->doc().location().warning(
- tr("Bad keyword in %1").arg(gen_->fullDocumentLocation(node, false))
- );
- }
- }
- project.keywords.append(keywordDetails(node));
- }
- }
- break;
-
- case Node::Property:
- case Node::QmlProperty:
- case Node::QmlSignal:
- case Node::QmlSignalHandler:
- case Node::QmlMethod:
- project.keywords.append(keywordDetails(node));
- break;
-
- case Node::Function:
- {
- const FunctionNode *funcNode = static_cast<const FunctionNode *>(node);
-
- // Only insert keywords for non-constructors. Constructors are covered
- // by the classes themselves.
-
- if (funcNode->metaness() != FunctionNode::Ctor)
- project.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->relates()) {
- project.memberStatus[node->relates()].insert(node->status());
- } else if (node->parent())
- project.memberStatus[node->parent()].insert(node->status());
- }
- break;
-
- case Node::Typedef:
- {
- const TypedefNode *typedefNode = static_cast<const TypedefNode *>(node);
- QStringList 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[2] = gen_->fullDocumentLocation(enumNode, false);
-
- project.keywords.append(typedefDetails);
- }
- break;
-
- case Node::Variable:
- {
- project.keywords.append(keywordDetails(node));
- }
- break;
-
- // Document nodes (such as manual pages) contain subtypes, titles and other
- // attributes.
- case Node::Document: {
- const DocumentNode *docNode = static_cast<const DocumentNode*>(node);
- if (docNode->docSubtype() != Node::ExternalPage &&
- docNode->docSubtype() != Node::Image &&
- !docNode->fullTitle().isEmpty()) {
-
- if (docNode->docSubtype() != Node::File) {
- if (docNode->doc().hasKeywords()) {
- foreach (const Atom *keyword, docNode->doc().keywords()) {
- if (!keyword->string().isEmpty()) {
- QStringList details;
- details << keyword->string()
- << keyword->string()
- << gen_->fullDocumentLocation(node, false);
- project.keywords.append(details);
- } else
- docNode->doc().location().warning(
- tr("Bad keyword in %1").arg(gen_->fullDocumentLocation(node, false))
- );
- }
- }
- project.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.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. Or DITA map nodes.
- */
- if (node->isIndexNode() || node->docSubtype() == Node::DitaMap)
- return;
- if (!generateSection(project, writer, node))
- return;
-
- if (node->isAggregate()) {
- const Aggregate *inner = static_cast<const Aggregate *>(node);
-
- // Ensure that we don't visit nodes more than once.
- QMap<QString, const Node*> childMap;
- foreach (const Node *childNode, inner->childNodes()) {
- if (childNode->isIndexNode())
- continue;
-
- if (childNode->access() == Node::Private)
- continue;
-
- if (childNode->type() == Node::Document) {
- childMap[static_cast<const DocumentNode *>(childNode)->fullTitle()] = childNode;
- }
- else if (childNode->isQmlPropertyGroup() || childNode->isJsPropertyGroup()) {
- /*
- Don't visit QML/JS property group nodes,
- but visit their children, which are all
- QML/JS property nodes.
-
- This is probably not correct anymore,
- because The Qml/Js Property Group is
- an actual documented thing.
- */
- const Aggregate* inner = static_cast<const Aggregate*>(childNode);
- foreach (const Node* n, inner->childNodes()) {
- if (n->access() == Node::Private)
- continue;
- childMap[n->fullDocumentName()] = n;
- }
- }
- else {
- // Store member status of children
- project.memberStatus[node].insert(childNode->status());
- if (childNode->relates()) {
- project.memberStatus[childNode->relates()].insert(childNode->status());
- }
-
- if (childNode->type() == Node::Function) {
- const FunctionNode *funcNode = static_cast<const FunctionNode *>(childNode);
- if (funcNode->isOverload())
- continue;
- }
- childMap[childNode->fullDocumentName()] = childNode;
- }
- }
- foreach (const Node *child, childMap)
- generateSections(project, writer, child);
- }
-}
-
-void HelpProjectWriter::generate()
-{
- for (int i = 0; i < projects.size(); ++i)
- generateProject(projects[i]);
-}
-
-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)
-{
- QString href = gen_->fullDocumentLocation(node, false);
- href = href.left(href.size()-5);
- if (href.isEmpty())
- return;
-
- bool derivedClass = false;
- if (node->type() == Node::Class)
- 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->isHeaderFile() &&
- (derivedClass || node->isQmlType() || node->isJsType() ||
- !project.memberStatus[node].isEmpty())) {
- QString membersPath = href + QStringLiteral("-members.html");
- writeSection(writer, membersPath, tr("List of all members"));
- }
- if (project.memberStatus[node].contains(Node::Compat)) {
- QString compatPath = href + QStringLiteral("-compat.html");
- writeSection(writer, compatPath, tr("Compatibility members"));
- }
- if (project.memberStatus[node].contains(Node::Obsolete)) {
- QString obsoletePath = href + QStringLiteral("-obsolete.html");
- writeSection(writer, obsoletePath, tr("Obsolete members"));
- }
-}
-
-void HelpProjectWriter::writeNode(HelpProject &project, QXmlStreamWriter &writer,
- const Node *node)
-{
- QString href = gen_->fullDocumentLocation(node, false);
- QString objName = node->name();
-
- switch (node->type()) {
-
- case Node::Class:
- writer.writeStartElement("section");
- writer.writeAttribute("ref", href);
- if (node->parent() && !node->parent()->name().isEmpty())
- writer.writeAttribute("title", tr("%1::%2 Class Reference").arg(node->parent()->name()).arg(objName));
- else
- writer.writeAttribute("title", tr("%1 Class Reference").arg(objName));
-
- addMembers(project, writer, node);
- writer.writeEndElement(); // section
- break;
-
- case Node::Namespace:
- writeSection(writer, href, objName);
- break;
-
- case Node::QmlType:
- writer.writeStartElement("section");
- writer.writeAttribute("ref", href);
- writer.writeAttribute("title", tr("%1 Type Reference").arg(node->fullTitle()));
- addMembers(project, writer, node);
- writer.writeEndElement(); // section
- break;
-
- case Node::Document: {
- // Document nodes (such as manual pages) contain subtypes, titles and other
- // attributes.
- const DocumentNode *docNode = static_cast<const DocumentNode*>(node);
-
- writer.writeStartElement("section");
- writer.writeAttribute("ref", href);
- writer.writeAttribute("title", docNode->fullTitle());
-
- if (docNode->docSubtype() == Node::HeaderFile)
- addMembers(project, writer, node);
-
- writer.writeEndElement(); // section
- }
- break;
- case Node::Group:
- case Node::Module:
- case Node::QmlModule:
- {
- const CollectionNode* cn = static_cast<const CollectionNode*>(node);
- writer.writeStartElement("section");
- writer.writeAttribute("ref", href);
- writer.writeAttribute("title", cn->fullTitle());
- writer.writeEndElement(); // section
- }
- break;
- default:
- ;
- }
-}
-
-void HelpProjectWriter::generateProject(HelpProject &project)
-{
- const Node *rootNode;
-
- // Restrict searching only to the local (primary) tree
- QVector<Tree*> searchOrder = qdb_->searchOrder();
- qdb_->setLocalSearch();
-
- if (!project.indexRoot.isEmpty())
- rootNode = qdb_->findDocumentNodeByTitle(project.indexRoot);
- else
- rootNode = qdb_->primaryTreeRoot();
-
- if (!rootNode)
- return;
-
- project.files.clear();
- project.keywords.clear();
-
- QFile file(outputDir + QDir::separator() + project.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.helpNamespace);
- writer.writeTextElement("virtualFolder", project.virtualFolder);
-
- // Write customFilter elements.
- QHash<QString, QSet<QString> >::ConstIterator it;
- for (it = project.customFilters.constBegin(); it != project.customFilters.constEnd(); ++it) {
- writer.writeStartElement("customFilter");
- writer.writeAttribute("name", it.key());
- foreach (const QString &filter, it.value())
- writer.writeTextElement("filterAttribute", filter);
- writer.writeEndElement(); // customFilter
- }
-
- // Start the filterSection.
- writer.writeStartElement("filterSection");
-
- // Write filterAttribute elements.
- foreach (const QString &filterName, project.filterAttributes)
- writer.writeTextElement("filterAttribute", filterName);
-
- writer.writeStartElement("toc");
- writer.writeStartElement("section");
- const Node* node = qdb_->findDocumentNodeByTitle(project.indexTitle);
- if (node == 0)
- node = qdb_->findNodeByNameAndType(QStringList("index.html"), Node::Document);
- QString indexPath;
- if (node)
- indexPath = gen_->fullDocumentLocation(node, false);
- else
- indexPath = "index.html";
- writer.writeAttribute("ref", indexPath);
- writer.writeAttribute("title", project.indexTitle);
-
- generateSections(project, writer, rootNode);
-
- for (int i = 0; i < project.subprojects.length(); i++) {
- SubProject subproject = project.subprojects[i];
-
- if (subproject.type == QLatin1String("manual")) {
-
- const Node *indexPage = qdb_->findNodeForTarget(subproject.indexTitle, 0);
- 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 = qdb_->findNodeForTarget(atom->string(), 0);
- writer.writeStartElement("section");
- QString indexPath = gen_->fullDocumentLocation(page, false);
- writer.writeAttribute("ref", indexPath);
- writer.writeAttribute("title", atom->string());
-
- sectionStack.top() += 1;
- }
- break;
- default:
- ;
- }
-
- if (atom == indexBody.lastAtom())
- break;
- atom = atom->next();
- }
- } else
- rootNode->doc().location().warning(
- tr("Failed to find index: %1").arg(subproject.indexTitle)
- );
-
- } else {
-
- writer.writeStartElement("section");
- QString indexPath = gen_->fullDocumentLocation(qdb_->findNodeForTarget(subproject.indexTitle, 0),
- false);
- writer.writeAttribute("ref", indexPath);
- writer.writeAttribute("title", subproject.title);
-
- if (subproject.sortPages) {
- QStringList titles = subproject.nodes.keys();
- titles.sort();
- foreach (const QString &title, titles) {
- writeNode(project, writer, subproject.nodes[title]);
- }
- } else {
- // Find a contents node and navigate from there, using the NextLink values.
- QSet<QString> visited;
- bool contentsFound = false;
- foreach (const Node *node, subproject.nodes) {
- QString nextTitle = node->links().value(Node::NextLink).first;
- if (!nextTitle.isEmpty() &&
- node->links().value(Node::ContentsLink).first.isEmpty()) {
-
- const Node *nextPage = qdb_->findNodeForTarget(nextTitle, 0);
-
- // 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 = qdb_->findNodeForTarget(nextTitle, 0);
- visited.insert(nextTitle);
- }
- break;
- }
- }
- // No contents/nextpage links found, write all nodes unsorted
- if (!contentsFound) {
- foreach (const Node *node, subproject.nodes)
- writeNode(project, writer, node);
- }
- }
-
- writer.writeEndElement(); // section
- }
- }
-
- // Restore original search order
- qdb_->setSearchOrder(searchOrder);
-
- writer.writeEndElement(); // section
- writer.writeEndElement(); // toc
-
- writer.writeStartElement("keywords");
- foreach (const QStringList &details, project.keywords) {
- writer.writeStartElement("keyword");
- writer.writeAttribute("name", details[0]);
- writer.writeAttribute("id", details[1]);
- writer.writeAttribute("ref", details[2]);
- 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>::fromList(gen_->outputFileNames());
- files.unite(project.files);
- files.unite(project.extraFiles);
- foreach (const QString &usedFile, files) {
- 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