diff options
Diffstat (limited to 'src/qdoc/htmlgenerator.cpp')
-rw-r--r-- | src/qdoc/htmlgenerator.cpp | 3640 |
1 files changed, 0 insertions, 3640 deletions
diff --git a/src/qdoc/htmlgenerator.cpp b/src/qdoc/htmlgenerator.cpp deleted file mode 100644 index ccde100f8..000000000 --- a/src/qdoc/htmlgenerator.cpp +++ /dev/null @@ -1,3640 +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 "htmlgenerator.h" - -#include "access.h" -#include "aggregate.h" -#include "classnode.h" -#include "collectionnode.h" -#include "config.h" -#include "codemarker.h" -#include "codeparser.h" -#include "enumnode.h" -#include "functionnode.h" -#include "helpprojectwriter.h" -#include "manifestwriter.h" -#include "node.h" -#include "propertynode.h" -#include "qdocdatabase.h" -#include "qmlpropertynode.h" -#include "sharedcommentnode.h" -#include "tagfilewriter.h" -#include "tree.h" -#include "quoter.h" - -#include <QtCore/qlist.h> -#include <QtCore/qmap.h> -#include <QtCore/quuid.h> -#include <QtCore/qversionnumber.h> -#include <QtCore/qregularexpression.h> - -#include <cctype> - -QT_BEGIN_NAMESPACE - -static bool showBrokenLinks = false; -bool HtmlGenerator::s_inUnorderedList { false }; - -static void addLink(const QString &linkTarget, QStringView nestedStuff, QString *res) -{ - if (!linkTarget.isEmpty()) { - *res += QLatin1String("<a href=\""); - *res += linkTarget; - *res += QLatin1String("\">"); - *res += nestedStuff; - *res += QLatin1String("</a>"); - } else { - *res += nestedStuff; - } -} - -/*! - \internal - Convenience method that starts an unordered list if not in one. - */ -inline void HtmlGenerator::openUnorderedList() -{ - if (!s_inUnorderedList) { - out() << "<ul>\n"; - s_inUnorderedList = true; - } -} - -/*! - \internal - Convenience method that closes an unordered list if in one. - */ -inline void HtmlGenerator::closeUnorderedList() -{ - if (s_inUnorderedList) { - out() << "</ul>\n"; - s_inUnorderedList = false; - } -} - -/*! - Destroys the HTML output generator. Deletes the singleton - instance of HelpProjectWriter and the ManifestWriter instance. - */ -HtmlGenerator::~HtmlGenerator() -{ - if (m_helpProjectWriter) { - delete m_helpProjectWriter; - m_helpProjectWriter = nullptr; - } - - if (m_manifestWriter) { - delete m_manifestWriter; - m_manifestWriter = nullptr; - } -} - -/*! - Initializes the HTML output generator's data structures - from the configuration (Config) singleton. - */ -void HtmlGenerator::initializeGenerator() -{ - static const struct - { - const char *key; - const char *left; - const char *right; - } defaults[] = { { ATOM_FORMATTING_BOLD, "<b>", "</b>" }, - { ATOM_FORMATTING_INDEX, "<!--", "-->" }, - { ATOM_FORMATTING_ITALIC, "<i>", "</i>" }, - { ATOM_FORMATTING_PARAMETER, "<i>", "</i>" }, - { ATOM_FORMATTING_SUBSCRIPT, "<sub>", "</sub>" }, - { ATOM_FORMATTING_SUPERSCRIPT, "<sup>", "</sup>" }, - { ATOM_FORMATTING_TELETYPE, "<code>", - "</code>" }, // <tt> tag is not supported in HTML5 - { ATOM_FORMATTING_UICONTROL, "<b>", "</b>" }, - { ATOM_FORMATTING_UNDERLINE, "<u>", "</u>" }, - { nullptr, nullptr, nullptr } }; - - Generator::initializeGenerator(); - config = &Config::instance(); - setImageFileExtensions(QStringList() << "png" - << "jpg" - << "jpeg" - << "gif"); - - /* - The formatting maps are owned by Generator. They are cleared in - Generator::terminate(). - */ - int i = 0; - while (defaults[i].key) { - formattingLeftMap().insert(QLatin1String(defaults[i].key), QLatin1String(defaults[i].left)); - formattingRightMap().insert(QLatin1String(defaults[i].key), - QLatin1String(defaults[i].right)); - i++; - } - - m_endHeader = config->getString(HtmlGenerator::format() + Config::dot + CONFIG_ENDHEADER); - m_postHeader = - config->getString(HtmlGenerator::format() + Config::dot + HTMLGENERATOR_POSTHEADER); - m_postPostHeader = - config->getString(HtmlGenerator::format() + Config::dot + HTMLGENERATOR_POSTPOSTHEADER); - m_prologue = config->getString(HtmlGenerator::format() + Config::dot + HTMLGENERATOR_PROLOGUE); - - m_footer = config->getString(HtmlGenerator::format() + Config::dot + HTMLGENERATOR_FOOTER); - m_address = config->getString(HtmlGenerator::format() + Config::dot + HTMLGENERATOR_ADDRESS); - m_noNavigationBar = - config->getBool(HtmlGenerator::format() + Config::dot + HTMLGENERATOR_NONAVIGATIONBAR); - m_navigationSeparator = config->getString(HtmlGenerator::format() + Config::dot - + HTMLGENERATOR_NAVIGATIONSEPARATOR); - tocDepth = config->getInt(HtmlGenerator::format() + Config::dot + HTMLGENERATOR_TOCDEPTH); - - m_project = config->getString(CONFIG_PROJECT); - - m_projectDescription = config->getString(CONFIG_DESCRIPTION); - if (m_projectDescription.isEmpty() && !m_project.isEmpty()) - m_projectDescription = m_project + QLatin1String(" Reference Documentation"); - - m_projectUrl = config->getString(CONFIG_URL); - tagFile_ = config->getString(CONFIG_TAGFILE); - - naturalLanguage = config->getString(CONFIG_NATURALLANGUAGE); - if (naturalLanguage.isEmpty()) - naturalLanguage = QLatin1String("en"); - - m_codeIndent = config->getInt(CONFIG_CODEINDENT); // QTBUG-27798 - m_codePrefix = config->getString(CONFIG_CODEPREFIX); - m_codeSuffix = config->getString(CONFIG_CODESUFFIX); - - /* - The help file write should be allocated once and only once - per qdoc execution. - */ - if (m_helpProjectWriter) - m_helpProjectWriter->reset(m_project.toLower() + ".qhp", this); - else - m_helpProjectWriter = new HelpProjectWriter(m_project.toLower() + ".qhp", this); - - if (!m_manifestWriter) - m_manifestWriter = new ManifestWriter(); - - // Documentation template handling - m_headerScripts = - config->getString(HtmlGenerator::format() + Config::dot + CONFIG_HEADERSCRIPTS); - m_headerStyles = config->getString(HtmlGenerator::format() + Config::dot + CONFIG_HEADERSTYLES); - - // Retrieve the config for the navigation bar - m_homepage = config->getString(CONFIG_NAVIGATION + Config::dot + CONFIG_HOMEPAGE); - - m_hometitle = config->getString(CONFIG_NAVIGATION + Config::dot + CONFIG_HOMETITLE, m_homepage); - - m_landingpage = config->getString(CONFIG_NAVIGATION + Config::dot + CONFIG_LANDINGPAGE); - - m_landingtitle = - config->getString(CONFIG_NAVIGATION + Config::dot + CONFIG_LANDINGTITLE, m_landingpage); - - m_cppclassespage = config->getString(CONFIG_NAVIGATION + Config::dot + CONFIG_CPPCLASSESPAGE); - - m_cppclassestitle = config->getString(CONFIG_NAVIGATION + Config::dot + CONFIG_CPPCLASSESTITLE, - QLatin1String("C++ Classes")); - - m_qmltypespage = config->getString(CONFIG_NAVIGATION + Config::dot + CONFIG_QMLTYPESPAGE); - - m_qmltypestitle = config->getString(CONFIG_NAVIGATION + Config::dot + CONFIG_QMLTYPESTITLE, - QLatin1String("QML Types")); - - m_buildversion = config->getString(CONFIG_BUILDVERSION); -} - -/*! - Gracefully terminates the HTML output generator. - */ -void HtmlGenerator::terminateGenerator() -{ - Generator::terminateGenerator(); -} - -QString HtmlGenerator::format() -{ - return "HTML"; -} - -/*! - If qdoc is in the \c {-prepare} phase, traverse the primary - tree to generate the index file for the current module. - - If qdoc is in the \c {-generate} phase, traverse the primary - tree to generate all the HTML documentation for the current - module. Then generate the help file and the tag file. - */ -void HtmlGenerator::generateDocs() -{ - Node *qflags = m_qdb->findClassNode(QStringList("QFlags")); - if (qflags) - m_qflagsHref = linkForNode(qflags, nullptr); - if (!config->preparing()) - Generator::generateDocs(); - - if (!config->generating()) { - QString fileBase = - m_project.toLower().simplified().replace(QLatin1Char(' '), QLatin1Char('-')); - m_qdb->generateIndex(outputDir() + QLatin1Char('/') + fileBase + ".index", m_projectUrl, - m_projectDescription, this); - } - - if (!config->preparing()) { - m_helpProjectWriter->generate(); - m_manifestWriter->generateManifestFiles(); - /* - Generate the XML tag file, if it was requested. - */ - if (!tagFile_.isEmpty()) { - TagFileWriter tagFileWriter; - tagFileWriter.generateTagFile(tagFile_, this); - } - } -} - -/*! - Generate an html file with the contents of a C++ or QML source file. - */ -void HtmlGenerator::generateExampleFilePage(const Node *en, const QString &file, CodeMarker *marker) -{ - SubTitleSize subTitleSize = LargeSubTitle; - QString fullTitle = en->fullTitle(); - - beginFilePage(en, linkForExampleFile(file)); - generateHeader(fullTitle, en, marker); - generateTitle(fullTitle, Text() << en->subtitle(), subTitleSize, en, marker); - - Text text; - Quoter quoter; - Doc::quoteFromFile(en->doc().location(), quoter, file); - QString code = quoter.quoteTo(en->location(), QString(), QString()); - CodeMarker *codeMarker = CodeMarker::markerForFileName(file); - text << Atom(codeMarker->atomType(), code); - Atom a(codeMarker->atomType(), code); - - generateText(text, en, codeMarker); - endFilePage(); -} - -/*! - Generate html from an instance of Atom. - */ -qsizetype HtmlGenerator::generateAtom(const Atom *atom, const Node *relative, CodeMarker *marker) -{ - qsizetype idx, skipAhead = 0; - static bool in_para = false; - Node::Genus genus = Node::DontCare; - - switch (atom->type()) { - case Atom::AutoLink: { - QString name = atom->string(); - if (relative && relative->name() == name.replace(QLatin1String("()"), QLatin1String())) { - out() << protectEnc(atom->string()); - break; - } - // Allow auto-linking to nodes in API reference - genus = Node::API; - } - Q_FALLTHROUGH(); - case Atom::NavAutoLink: - if (!m_inLink && !m_inContents && !m_inSectionHeading) { - const Node *node = nullptr; - QString link = getAutoLink(atom, relative, &node, genus); - if (link.isEmpty()) { - if (autolinkErrors() && relative) - relative->doc().location().warning( - QStringLiteral("Can't autolink to '%1'").arg(atom->string())); - } else if (node && node->isDeprecated()) { - if (relative && (relative->parent() != node) && !relative->isDeprecated()) - link.clear(); - } - if (link.isEmpty()) { - out() << protectEnc(atom->string()); - } else { - beginLink(link, node, relative); - generateLink(atom, marker); - endLink(); - } - } else { - out() << protectEnc(atom->string()); - } - break; - case Atom::BaseName: - break; - case Atom::BriefLeft: - if (!hasBrief(relative)) { - skipAhead = skipAtoms(atom, Atom::BriefRight); - break; - } - out() << "<p>"; - rewritePropertyBrief(atom, relative); - break; - case Atom::BriefRight: - if (hasBrief(relative)) - out() << "</p>\n"; - break; - case Atom::C: - // This may at one time have been used to mark up C++ code but it is - // now widely used to write teletype text. As a result, text marked - // with the \c command is not passed to a code marker. - out() << formattingLeftMap()[ATOM_FORMATTING_TELETYPE]; - out() << protectEnc(plainCode(atom->string())); - out() << formattingRightMap()[ATOM_FORMATTING_TELETYPE]; - break; - case Atom::CaptionLeft: - out() << "<p class=\"figCaption\">"; - in_para = true; - break; - case Atom::CaptionRight: - endLink(); - if (in_para) { - out() << "</p>\n"; - in_para = false; - } - break; - case Atom::Qml: - out() << "<pre class=\"qml\">" - << trimmedTrailing(highlightedCode(indent(m_codeIndent, atom->string()), relative, - false, Node::QML), - m_codePrefix, m_codeSuffix) - << "</pre>\n"; - break; - case Atom::JavaScript: - out() << "<pre class=\"js\">" - << trimmedTrailing(highlightedCode(indent(m_codeIndent, atom->string()), relative, - false, Node::JS), - m_codePrefix, m_codeSuffix) - << "</pre>\n"; - break; - case Atom::CodeNew: - out() << "<p>you can rewrite it as</p>\n"; - Q_FALLTHROUGH(); - case Atom::Code: - out() << "<pre class=\"cpp\">" - << trimmedTrailing(highlightedCode(indent(m_codeIndent, atom->string()), relative), - m_codePrefix, m_codeSuffix) - << "</pre>\n"; - break; - case Atom::CodeOld: - out() << "<p>For example, if you have code like</p>\n"; - Q_FALLTHROUGH(); - case Atom::CodeBad: - out() << "<pre class=\"cpp plain\">" - << trimmedTrailing(protectEnc(plainCode(indent(m_codeIndent, atom->string()))), - m_codePrefix, m_codeSuffix) - << "</pre>\n"; - break; - case Atom::DivLeft: - out() << "<div"; - if (!atom->string().isEmpty()) - out() << ' ' << atom->string(); - out() << '>'; - break; - case Atom::DivRight: - out() << "</div>"; - break; - case Atom::FootnoteLeft: - // ### For now - if (in_para) { - out() << "</p>\n"; - in_para = false; - } - out() << "<!-- "; - break; - case Atom::FootnoteRight: - // ### For now - out() << "-->\n"; - break; - case Atom::FormatElse: - case Atom::FormatEndif: - case Atom::FormatIf: - break; - case Atom::FormattingLeft: - if (atom->string().startsWith("span ")) { - out() << '<' + atom->string() << '>'; - } else - out() << formattingLeftMap()[atom->string()]; - if (atom->string() == ATOM_FORMATTING_PARAMETER) { - if (atom->next() != nullptr && atom->next()->type() == Atom::String) { - QRegularExpression subscriptRegExp("^([a-z]+)_([0-9n])$"); - auto match = subscriptRegExp.match(atom->next()->string()); - if (match.hasMatch()) { - out() << match.captured(1) << "<sub>" << match.captured(2) - << "</sub>"; - skipAhead = 1; - } - } - } - break; - case Atom::FormattingRight: - if (atom->string() == ATOM_FORMATTING_LINK) { - endLink(); - } else if (atom->string().startsWith("span ")) { - out() << "</span>"; - } else { - out() << formattingRightMap()[atom->string()]; - } - break; - case Atom::AnnotatedList: { - const CollectionNode *cn = m_qdb->getCollectionNode(atom->string(), Node::Group); - if (cn) - generateList(cn, marker, atom->string()); - } break; - case Atom::GeneratedList: - if (atom->string() == QLatin1String("annotatedclasses")) { - generateAnnotatedList(relative, marker, m_qdb->getCppClasses().values()); - } else if (atom->string() == QLatin1String("annotatedexamples")) { - generateAnnotatedLists(relative, marker, m_qdb->getExamples()); - } else if (atom->string() == QLatin1String("annotatedattributions")) { - generateAnnotatedLists(relative, marker, m_qdb->getAttributions()); - } else if (atom->string() == QLatin1String("classes")) { - generateCompactList(Generic, relative, m_qdb->getCppClasses(), true, - QStringLiteral("")); - } else if (atom->string().contains("classes ")) { - QString rootName = atom->string().mid(atom->string().indexOf("classes") + 7).trimmed(); - generateCompactList(Generic, relative, m_qdb->getCppClasses(), true, rootName); - } else if (atom->string() == QLatin1String("qmlbasictypes")) { - generateCompactList(Generic, relative, m_qdb->getQmlBasicTypes(), true, - QStringLiteral("")); - } else if (atom->string() == QLatin1String("qmltypes")) { - generateCompactList(Generic, relative, m_qdb->getQmlTypes(), true, QStringLiteral("")); - } else if ((idx = atom->string().indexOf(QStringLiteral("bymodule"))) != -1) { - QString moduleName = atom->string().mid(idx + 8).trimmed(); - Node::NodeType type = typeFromString(atom); - QDocDatabase *qdb = QDocDatabase::qdocDB(); - const CollectionNode *cn = qdb->getCollectionNode(moduleName, type); - if (cn) { - if (type == Node::Module) { - NodeMap m; - cn->getMemberClasses(m); - if (!m.isEmpty()) { - generateAnnotatedList(relative, marker, m.values()); - } - } else - generateAnnotatedList(relative, marker, cn->members()); - } - } else if (atom->string().startsWith("examplefiles") - || atom->string().startsWith("exampleimages")) { - if (relative->isExample()) { - qDebug() << "GENERATE FILE LIST CALLED" << relative->name() << atom->string(); - } else - relative->location().warning(QString("'\\generatelist %1' can only be used with " - "'\\example' topic command") - .arg(atom->string())); - } else if (atom->string() == QLatin1String("classhierarchy")) { - generateClassHierarchy(relative, m_qdb->getCppClasses()); - } else if (atom->string() == QLatin1String("obsoleteclasses")) { - generateCompactList(Generic, relative, m_qdb->getObsoleteClasses(), false, - QStringLiteral("Q")); - } else if (atom->string() == QLatin1String("obsoleteqmltypes")) { - generateCompactList(Generic, relative, m_qdb->getObsoleteQmlTypes(), false, - QStringLiteral("")); - } else if (atom->string() == QLatin1String("obsoletecppmembers")) { - generateCompactList(Obsolete, relative, m_qdb->getClassesWithObsoleteMembers(), false, - QStringLiteral("Q")); - } else if (atom->string() == QLatin1String("obsoleteqmlmembers")) { - generateCompactList(Obsolete, relative, m_qdb->getQmlTypesWithObsoleteMembers(), false, - QStringLiteral("")); - } else if (atom->string() == QLatin1String("functionindex")) { - generateFunctionIndex(relative); - } else if (atom->string() == QLatin1String("attributions")) { - generateAnnotatedList(relative, marker, m_qdb->getAttributions().values()); - } else if (atom->string() == QLatin1String("legalese")) { - generateLegaleseList(relative, marker); - } else if (atom->string() == QLatin1String("overviews")) { - generateList(relative, marker, "overviews"); - } else if (atom->string() == QLatin1String("cpp-modules")) { - generateList(relative, marker, "cpp-modules"); - } else if (atom->string() == QLatin1String("qml-modules")) { - generateList(relative, marker, "qml-modules"); - } else if (atom->string() == QLatin1String("namespaces")) { - generateAnnotatedList(relative, marker, m_qdb->getNamespaces().values()); - } else if (atom->string() == QLatin1String("related")) { - generateList(relative, marker, "related"); - } else { - const CollectionNode *cn = m_qdb->getCollectionNode(atom->string(), Node::Group); - if (cn) { - if (!generateGroupList(const_cast<CollectionNode *>(cn))) - relative->location().warning( - QString("'\\generatelist %1' group is empty").arg(atom->string())); - } else { - relative->location().warning( - QString("'\\generatelist %1' no such group").arg(atom->string())); - } - } - break; - case Atom::SinceList: { - const NodeMultiMap &nsmap = m_qdb->getSinceMap(atom->string()); - if (nsmap.isEmpty()) - break; - - const NodeMultiMap &ncmap = m_qdb->getClassMap(atom->string()); - const NodeMultiMap &nqcmap = m_qdb->getQmlTypeMap(atom->string()); - - Sections sections(nsmap); - out() << "<ul>\n"; - const QList<Section> sinceSections = sections.sinceSections(); - for (const auto §ion : sinceSections) { - if (!section.members().isEmpty()) { - out() << "<li>" - << "<a href=\"#" << Doc::canonicalTitle(section.title()) << "\">" - << section.title() << "</a></li>\n"; - } - } - out() << "</ul>\n"; - - int index = 0; - for (const auto §ion : sinceSections) { - if (!section.members().isEmpty()) { - out() << "<h3 id=\"" << Doc::canonicalTitle(section.title()) << "\">" - << protectEnc(section.title()) << "</h3>\n"; - if (index == Sections::SinceClasses) - generateCompactList(Generic, relative, ncmap, false, QStringLiteral("Q")); - else if (index == Sections::SinceQmlTypes) - generateCompactList(Generic, relative, nqcmap, false, QStringLiteral("")); - else if (index == Sections::SinceMemberFunctions) { - ParentMaps parentmaps; - ParentMaps::iterator pmap; - const QList<Node *> &members = section.members(); - for (const auto &member : members) { - Node *parent = (*member).parent(); - pmap = parentmaps.find(parent); - if (pmap == parentmaps.end()) - pmap = parentmaps.insert(parent, NodeMultiMap()); - pmap->insert(member->name(), member); - } - for (auto map = parentmaps.begin(); map != parentmaps.end(); ++map) { - NodeVector nv = map->values().toVector(); - out() << "<p>Class "; - - out() << "<a href=\"" << linkForNode(map.key(), relative) << "\">"; - QStringList pieces = map.key()->fullName().split("::"); - out() << protectEnc(pieces.last()); - out() << "</a>" - << ":</p>\n"; - - generateSection(nv, relative, marker); - out() << "<br/>"; - } - } else { - generateSection(section.members(), relative, marker); - } - } - ++index; - } - } break; - case Atom::BR: - out() << "<br />\n"; - break; - case Atom::HR: - out() << "<hr />\n"; - break; - case Atom::Image: - case Atom::InlineImage: { - QString fileName = imageFileName(relative, atom->string()); - QString text; - if (atom->next() != nullptr) - text = atom->next()->string(); - if (atom->type() == Atom::Image) - out() << "<p class=\"centerAlign\">"; - if (fileName.isEmpty()) { - relative->location().warning( - QStringLiteral("Missing image: %1").arg(protectEnc(atom->string()))); - out() << "<font color=\"red\">[Missing image " << protectEnc(atom->string()) - << "]</font>"; - } else { - QString prefix; - out() << "<img src=\"" << protectEnc(prefix + fileName) << '"'; - if (!text.isEmpty()) - out() << " alt=\"" << protectEnc(text) << '"'; - else - out() << " alt=\"\""; - out() << " />"; - m_helpProjectWriter->addExtraFile(fileName); - setImageFileName(relative, fileName); - } - if (atom->type() == Atom::Image) - out() << "</p>"; - } break; - case Atom::ImageText: - break; - // Admonitions - case Atom::ImportantLeft: - case Atom::NoteLeft: - case Atom::WarningLeft: { - QString admonType = atom->typeString(); - // Remove 'Left' from atom type to get the admonition type - admonType.chop(4); - out() << "<div class=\"admonition " << admonType.toLower() << "\">\n" - << "<p>"; - out() << formattingLeftMap()[ATOM_FORMATTING_BOLD]; - out() << admonType << ": "; - out() << formattingRightMap()[ATOM_FORMATTING_BOLD]; - } break; - case Atom::ImportantRight: - case Atom::NoteRight: - case Atom::WarningRight: - out() << "</p>\n" - << "</div>\n"; - break; - case Atom::LegaleseLeft: - out() << "<div class=\"LegaleseLeft\">"; - break; - case Atom::LegaleseRight: - out() << "</div>"; - break; - case Atom::LineBreak: - out() << "<br/>"; - break; - case Atom::Link: - case Atom::NavLink: { - const Node *node = nullptr; - QString link = getLink(atom, relative, &node); - if (link.isEmpty() && (node != relative) && !noLinkErrors()) { - relative->doc().location().warning( - QStringLiteral("Can't link to '%1'").arg(atom->string())); - } else { - node = nullptr; - } - beginLink(link, node, relative); - skipAhead = 1; - } break; - case Atom::ExampleFileLink: { - QString link = linkForExampleFile(atom->string()); - if (link.isEmpty() && !noLinkErrors()) - relative->doc().location().warning( - QStringLiteral("Can't link to '%1'").arg(atom->string())); - beginLink(link); - skipAhead = 1; - } break; - case Atom::ExampleImageLink: { - QString link = atom->string(); - if (link.isEmpty() && !noLinkErrors()) - relative->doc().location().warning( - QStringLiteral("Can't link to '%1'").arg(atom->string())); - link = "images/used-in-examples/" + link; - beginLink(link); - skipAhead = 1; - } break; - case Atom::LinkNode: { - const Node *node = CodeMarker::nodeForString(atom->string()); - beginLink(linkForNode(node, relative), node, relative); - skipAhead = 1; - } break; - case Atom::ListLeft: - if (in_para) { - out() << "</p>\n"; - in_para = false; - } - if (atom->string() == ATOM_LIST_BULLET) { - out() << "<ul>\n"; - } else if (atom->string() == ATOM_LIST_TAG) { - out() << "<dl>\n"; - } else if (atom->string() == ATOM_LIST_VALUE) { - out() << R"(<div class="table"><table class="valuelist">)"; - m_threeColumnEnumValueTable = isThreeColumnEnumValueTable(atom); - if (m_threeColumnEnumValueTable) { - if (++m_numTableRows % 2 == 1) - out() << R"(<tr valign="top" class="odd">)"; - else - out() << R"(<tr valign="top" class="even">)"; - - out() << "<th class=\"tblConst\">Constant</th>"; - - // If not in \enum topic, skip the value column - if (relative->isEnumType()) - out() << "<th class=\"tblval\">Value</th>"; - - out() << "<th class=\"tbldscr\">Description</th></tr>\n"; - } else { - out() << "<tr><th class=\"tblConst\">Constant</th><th " - "class=\"tblVal\">Value</th></tr>\n"; - } - } else { - QString olType; - if (atom->string() == ATOM_LIST_UPPERALPHA) { - olType = "A"; - } else if (atom->string() == ATOM_LIST_LOWERALPHA) { - olType = "a"; - } else if (atom->string() == ATOM_LIST_UPPERROMAN) { - olType = "I"; - } else if (atom->string() == ATOM_LIST_LOWERROMAN) { - olType = "i"; - } else { // (atom->string() == ATOM_LIST_NUMERIC) - olType = "1"; - } - - if (atom->next() != nullptr && atom->next()->string().toInt() > 1) { - out() << QString(R"(<ol class="%1" type="%1" start="%2">)") - .arg(olType, atom->next()->string()); - } else - out() << QString(R"(<ol class="%1" type="%1">)").arg(olType); - } - break; - case Atom::ListItemNumber: - break; - case Atom::ListTagLeft: - if (atom->string() == ATOM_LIST_TAG) { - out() << "<dt>"; - } else { // (atom->string() == ATOM_LIST_VALUE) - QPair<QString, int> pair = getAtomListValue(atom); - skipAhead = pair.second; - QString t = protectEnc(plainCode(marker->markedUpEnumValue(pair.first, relative))); - out() << "<tr><td class=\"topAlign\"><code>" << t << "</code>"; - - if (relative->isEnumType()) { - out() << "</td><td class=\"topAlign tblval\">"; - const auto *enume = static_cast<const EnumNode *>(relative); - QString itemValue = enume->itemValue(atom->next()->string()); - if (itemValue.isEmpty()) - out() << '?'; - else - out() << "<code>" << protectEnc(itemValue) << "</code>"; - } - } - break; - case Atom::SinceTagRight: - case Atom::ListTagRight: - if (atom->string() == ATOM_LIST_TAG) - out() << "</dt>\n"; - break; - case Atom::ListItemLeft: - if (atom->string() == ATOM_LIST_TAG) { - out() << "<dd>"; - } else if (atom->string() == ATOM_LIST_VALUE) { - if (m_threeColumnEnumValueTable) { - out() << "</td><td class=\"topAlign\">"; - if (matchAhead(atom, Atom::ListItemRight)) - out() << " "; - } - } else { - out() << "<li>"; - } - if (matchAhead(atom, Atom::ParaLeft)) - skipAhead = 1; - break; - case Atom::ListItemRight: - if (atom->string() == ATOM_LIST_TAG) { - out() << "</dd>\n"; - } else if (atom->string() == ATOM_LIST_VALUE) { - out() << "</td></tr>\n"; - } else { - out() << "</li>\n"; - } - break; - case Atom::ListRight: - if (atom->string() == ATOM_LIST_BULLET) { - out() << "</ul>\n"; - } else if (atom->string() == ATOM_LIST_TAG) { - out() << "</dl>\n"; - } else if (atom->string() == ATOM_LIST_VALUE) { - out() << "</table></div>\n"; - } else { - out() << "</ol>\n"; - } - break; - case Atom::Nop: - break; - case Atom::ParaLeft: - out() << "<p>"; - in_para = true; - break; - case Atom::ParaRight: - endLink(); - if (in_para) { - out() << "</p>\n"; - in_para = false; - } - // if (!matchAhead(atom, Atom::ListItemRight) && !matchAhead(atom, Atom::TableItemRight)) - // out() << "</p>\n"; - break; - case Atom::QuotationLeft: - out() << "<blockquote>"; - break; - case Atom::QuotationRight: - out() << "</blockquote>\n"; - break; - case Atom::RawString: - out() << atom->string(); - break; - case Atom::SectionLeft: - case Atom::SectionRight: - break; - case Atom::SectionHeadingLeft: { - int unit = atom->string().toInt() + hOffset(relative); - out() << "<h" + QString::number(unit) + QLatin1Char(' ') << "id=\"" - << Doc::canonicalTitle(Text::sectionHeading(atom).toString()) << "\">"; - m_inSectionHeading = true; - break; - } - case Atom::SectionHeadingRight: - out() << "</h" + QString::number(atom->string().toInt() + hOffset(relative)) + ">\n"; - m_inSectionHeading = false; - break; - case Atom::SidebarLeft: - Q_FALLTHROUGH(); - case Atom::SidebarRight: - break; - case Atom::String: - if (m_inLink && !m_inContents && !m_inSectionHeading) { - generateLink(atom, marker); - } else { - out() << protectEnc(atom->string()); - } - break; - case Atom::TableLeft: { - QPair<QString, QString> pair = getTableWidthAttr(atom); - QString attr = pair.second; - QString width = pair.first; - - if (in_para) { - out() << "</p>\n"; - in_para = false; - } - - out() << R"(<div class="table"><table class=")" << attr << '"'; - if (!width.isEmpty()) - out() << " width=\"" << width << '"'; - out() << ">\n "; - m_numTableRows = 0; - } break; - case Atom::TableRight: - out() << "</table></div>\n"; - break; - case Atom::TableHeaderLeft: - out() << "<thead><tr class=\"qt-style\">"; - m_inTableHeader = true; - break; - case Atom::TableHeaderRight: - out() << "</tr>"; - if (matchAhead(atom, Atom::TableHeaderLeft)) { - skipAhead = 1; - out() << "\n<tr class=\"qt-style\">"; - } else { - out() << "</thead>\n"; - m_inTableHeader = false; - } - break; - case Atom::TableRowLeft: - if (!atom->string().isEmpty()) - out() << "<tr " << atom->string() << '>'; - else if (++m_numTableRows % 2 == 1) - out() << R"(<tr valign="top" class="odd">)"; - else - out() << R"(<tr valign="top" class="even">)"; - break; - case Atom::TableRowRight: - out() << "</tr>\n"; - break; - case Atom::TableItemLeft: { - if (m_inTableHeader) - out() << "<th "; - else - out() << "<td "; - - for (int i = 0; i < atom->count(); ++i) { - if (i > 0) - out() << ' '; - const QString &p = atom->string(i); - if (p.contains('=')) { - out() << p; - } else { - QStringList spans = p.split(QLatin1Char(',')); - if (spans.size() == 2) { - if (spans.at(0) != "1") - out() << " colspan=\"" << spans.at(0) << '"'; - if (spans.at(1) != "1") - out() << " rowspan=\"" << spans.at(1) << '"'; - } - } - } - out() << '>'; - if (matchAhead(atom, Atom::ParaLeft)) - skipAhead = 1; - } break; - case Atom::TableItemRight: - if (m_inTableHeader) - out() << "</th>"; - else { - out() << "</td>"; - } - if (matchAhead(atom, Atom::ParaLeft)) - skipAhead = 1; - break; - case Atom::TableOfContents: - Q_FALLTHROUGH(); - case Atom::Keyword: - break; - case Atom::Target: - out() << "<span id=\"" << Doc::canonicalTitle(atom->string()) << "\"></span>"; - break; - case Atom::UnhandledFormat: - out() << "<b class=\"redFont\"><Missing HTML></b>"; - break; - case Atom::UnknownCommand: - out() << R"(<b class="redFont"><code>\)" << protectEnc(atom->string()) << "</code></b>"; - break; - case Atom::QmlText: - case Atom::EndQmlText: - // don't do anything with these. They are just tags. - break; - case Atom::CodeQuoteArgument: - case Atom::CodeQuoteCommand: - case Atom::SnippetCommand: - case Atom::SnippetIdentifier: - case Atom::SnippetLocation: - // no HTML output (ignore) - break; - default: - unknownAtom(atom); - } - return skipAhead; -} - -/*! - Generate a reference page for the C++ class, namespace, or - header file documented in \a node using the code \a marker - provided. - */ -void HtmlGenerator::generateCppReferencePage(Aggregate *aggregate, CodeMarker *marker) -{ - QString title; - QString rawTitle; - QString fullTitle; - NamespaceNode *ns = nullptr; - SectionVector *summarySections = nullptr; - SectionVector *detailsSections = nullptr; - - Sections sections(aggregate); - QString word = aggregate->typeWord(true); - QString templateDecl = aggregate->templateDecl(); - if (aggregate->isNamespace()) { - rawTitle = aggregate->plainName(); - fullTitle = aggregate->plainFullName(); - title = rawTitle + " Namespace"; - ns = static_cast<NamespaceNode *>(aggregate); - summarySections = §ions.stdSummarySections(); - detailsSections = §ions.stdDetailsSections(); - } else if (aggregate->isClassNode()) { - rawTitle = aggregate->plainName(); - fullTitle = aggregate->plainFullName(); - title = rawTitle + QLatin1Char(' ') + word; - summarySections = §ions.stdCppClassSummarySections(); - detailsSections = §ions.stdCppClassDetailsSections(); - } else if (aggregate->isHeader()) { - title = fullTitle = rawTitle = aggregate->fullTitle(); - summarySections = §ions.stdSummarySections(); - detailsSections = §ions.stdDetailsSections(); - } - - Text subtitleText; - if (rawTitle != fullTitle || !templateDecl.isEmpty()) { - if (aggregate->isClassNode()) { - if (!templateDecl.isEmpty()) - subtitleText << templateDecl + QLatin1Char(' '); - subtitleText << aggregate->typeWord(false) + QLatin1Char(' '); - const QStringList ancestors = fullTitle.split(QLatin1String("::")); - for (const auto &a : ancestors) { - if (a == rawTitle) { - subtitleText << a; - break; - } else { - subtitleText << Atom(Atom::AutoLink, a) << "::"; - } - } - } else { - subtitleText << fullTitle; - } - } - - generateHeader(title, aggregate, marker); - generateTableOfContents(aggregate, marker, summarySections); - generateTitle(title, subtitleText, SmallSubTitle, aggregate, marker); - if (ns && !ns->hasDoc() && ns->docNode()) { - NamespaceNode *NS = ns->docNode(); - Text brief; - brief << "The " << ns->name() << " namespace includes the following elements from module " - << ns->tree()->camelCaseModuleName() << ". The full namespace is " - << "documented in module " << NS->tree()->camelCaseModuleName() - << Atom(Atom::LinkNode, CodeMarker::stringForNode(NS)) - << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK) << Atom(Atom::String, " here.") - << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK); - out() << "<p>"; - generateText(brief, ns, marker); - out() << "</p>\n"; - } else - generateBrief(aggregate, marker); - - const auto parentIsClass = aggregate->parent()->isClassNode(); - - if (!parentIsClass) - generateRequisites(aggregate, marker); - generateStatus(aggregate, marker); - if (parentIsClass) - generateSince(aggregate, marker); - - QString membersLink = generateAllMembersFile(Sections::allMembersSection(), marker); - if (!membersLink.isEmpty()) { - openUnorderedList(); - out() << "<li><a href=\"" << membersLink << "\">" - << "List of all members, including inherited members</a></li>\n"; - } - QString obsoleteLink = generateObsoleteMembersFile(sections, marker); - if (!obsoleteLink.isEmpty()) { - openUnorderedList(); - out() << "<li><a href=\"" << obsoleteLink << "\">" - << "Deprecated members</a></li>\n"; - } - closeUnorderedList(); - generateThreadSafeness(aggregate, marker); - - bool needOtherSection = false; - - for (const auto §ion : qAsConst(*summarySections)) { - if (section.members().isEmpty() && section.reimplementedMembers().isEmpty()) { - if (!section.inheritedMembers().isEmpty()) - needOtherSection = true; - } else { - if (!section.members().isEmpty()) { - QString ref = registerRef(section.title().toLower()); - out() << "<h2 id=\"" << ref << "\">" << protectEnc(section.title()) << "</h2>\n"; - generateSection(section.members(), aggregate, marker); - } - if (!section.reimplementedMembers().isEmpty()) { - QString name = QString("Reimplemented ") + section.title(); - QString ref = registerRef(name.toLower()); - out() << "<h2 id=\"" << ref << "\">" << protectEnc(name) << "</h2>\n"; - generateSection(section.reimplementedMembers(), aggregate, marker); - } - - if (!section.inheritedMembers().isEmpty()) { - out() << "<ul>\n"; - generateSectionInheritedList(section, aggregate); - out() << "</ul>\n"; - } - } - } - - if (needOtherSection) { - out() << "<h3>Additional Inherited Members</h3>\n" - "<ul>\n"; - - for (const auto §ion : qAsConst(*summarySections)) { - if (section.members().isEmpty() && !section.inheritedMembers().isEmpty()) - generateSectionInheritedList(section, aggregate); - } - out() << "</ul>\n"; - } - - if (aggregate->doc().isEmpty()) { - QString command = "documentation"; - if (aggregate->isClassNode()) - command = R"('\class' comment)"; - if (!ns || ns->isDocumentedHere()) { - aggregate->location().warning( - QStringLiteral("No %1 for '%2'").arg(command, aggregate->plainSignature())); - } - } else { - generateExtractionMark(aggregate, DetailedDescriptionMark); - out() << "<div class=\"descr\">\n" - << "<h2 id=\"" << registerRef("details") << "\">" - << "Detailed Description" - << "</h2>\n"; - generateBody(aggregate, marker); - out() << "</div>\n"; - generateAlsoList(aggregate, marker); - generateMaintainerList(aggregate, marker); - generateExtractionMark(aggregate, EndMark); - } - - for (const auto §ion : qAsConst(*detailsSections)) { - bool headerGenerated = false; - if (section.isEmpty()) - continue; - - const QList<Node *> &members = section.members(); - for (const auto &member : members) { - if (member->access() == Access::Private) // ### check necessary? - continue; - if (!headerGenerated) { - if (!section.divClass().isEmpty()) - out() << "<div class=\"" << section.divClass() << "\">\n"; - out() << "<h2>" << protectEnc(section.title()) << "</h2>\n"; - headerGenerated = true; - } - if (!member->isClassNode()) - generateDetailedMember(member, aggregate, marker); - else { - out() << "<h3> class "; - generateFullName(member, aggregate); - out() << "</h3>"; - generateBrief(member, marker, aggregate); - } - - QStringList names; - names << member->name(); - if (member->isFunction()) { - const auto *func = reinterpret_cast<const FunctionNode *>(member); - if (func->isSomeCtor() || func->isDtor() || func->overloadNumber() != 0) - names.clear(); - } else if (member->isProperty()) { - const auto *prop = reinterpret_cast<const PropertyNode *>(member); - if (!prop->getters().isEmpty() && !names.contains(prop->getters().first()->name())) - names << prop->getters().first()->name(); - if (!prop->setters().isEmpty()) - names << prop->setters().first()->name(); - if (!prop->resetters().isEmpty()) - names << prop->resetters().first()->name(); - if (!prop->notifiers().isEmpty()) - names << prop->notifiers().first()->name(); - } else if (member->isEnumType()) { - const auto *enume = reinterpret_cast<const EnumNode *>(member); - if (enume->flagsType()) - names << enume->flagsType()->name(); - const auto &enumItemNameList = enume->doc().enumItemNames(); - const auto &omitEnumItemNameList = enume->doc().omitEnumItemNames(); - const auto items = QSet<QString>(enumItemNameList.cbegin(), enumItemNameList.cend()) - - QSet<QString>(omitEnumItemNameList.cbegin(), omitEnumItemNameList.cend()); - for (const QString &enumName : items) { - names << plainCode(marker->markedUpEnumValue(enumName, enume)); - } - } - } - if (headerGenerated && !section.divClass().isEmpty()) - out() << "</div>\n"; - } - generateFooter(aggregate); -} - -void HtmlGenerator::generateProxyPage(Aggregate *aggregate, CodeMarker *marker) -{ - Q_ASSERT(aggregate->isProxyNode()); - - QString title; - QString rawTitle; - QString fullTitle; - Text subtitleText; - SectionVector *summarySections = nullptr; - SectionVector *detailsSections = nullptr; - - Sections sections(aggregate); - rawTitle = aggregate->plainName(); - fullTitle = aggregate->plainFullName(); - title = rawTitle + " Proxy Page"; - summarySections = §ions.stdSummarySections(); - detailsSections = §ions.stdDetailsSections(); - generateHeader(title, aggregate, marker); - generateTitle(title, subtitleText, SmallSubTitle, aggregate, marker); - generateBrief(aggregate, marker); - for (auto it = summarySections->constBegin(); it != summarySections->constEnd(); ++it) { - if (!it->members().isEmpty()) { - QString ref = registerRef(it->title().toLower()); - out() << "<h2 id=\"" << ref << "\">" << protectEnc(it->title()) << "</h2>\n"; - generateSection(it->members(), aggregate, marker); - } - } - - if (!aggregate->doc().isEmpty()) { - generateExtractionMark(aggregate, DetailedDescriptionMark); - out() << "<div class=\"descr\">\n" - << "<h2 id=\"" << registerRef("details") << "\">" - << "Detailed Description" - << "</h2>\n"; - generateBody(aggregate, marker); - out() << "</div>\n"; - generateAlsoList(aggregate, marker); - generateMaintainerList(aggregate, marker); - generateExtractionMark(aggregate, EndMark); - } - - for (const auto §ion : qAsConst(*detailsSections)) { - if (section.isEmpty()) - continue; - - if (!section.divClass().isEmpty()) - out() << "<div class=\"" << section.divClass() << "\">\n"; - out() << "<h2>" << protectEnc(section.title()) << "</h2>\n"; - - const QList<Node *> &members = section.members(); - for (const auto &member : members) { - if (!member->isPrivate()) { // ### check necessary? - if (!member->isClassNode()) - generateDetailedMember(member, aggregate, marker); - else { - out() << "<h3> class "; - generateFullName(member, aggregate); - out() << "</h3>"; - generateBrief(member, marker, aggregate); - } - - QStringList names; - names << member->name(); - if (member->isFunction()) { - const auto *func = reinterpret_cast<const FunctionNode *>(member); - if (func->isSomeCtor() || func->isDtor() || func->overloadNumber() != 0) - names.clear(); - } else if (member->isEnumType()) { - const auto *enume = reinterpret_cast<const EnumNode *>(member); - if (enume->flagsType()) - names << enume->flagsType()->name(); - const auto &enumItemNameList = enume->doc().enumItemNames(); - const auto &omitEnumItemNameList = enume->doc().omitEnumItemNames(); - const auto items = - QSet<QString>(enumItemNameList.cbegin(), enumItemNameList.cend()) - - QSet<QString>(omitEnumItemNameList.cbegin(), - omitEnumItemNameList.cend()); - for (const QString &enumName : items) - names << plainCode(marker->markedUpEnumValue(enumName, enume)); - } - } - } - if (!section.divClass().isEmpty()) - out() << "</div>\n"; - } - generateFooter(aggregate); -} - -/*! - Generate the HTML page for a QML type. \qcn is the QML type. - \marker is the code markeup object. - */ -void HtmlGenerator::generateQmlTypePage(QmlTypeNode *qcn, CodeMarker *marker) -{ - Generator::setQmlTypeContext(qcn); - SubTitleSize subTitleSize = LargeSubTitle; - QString htmlTitle = qcn->fullTitle(); - if (qcn->isJsType()) - htmlTitle += " JavaScript Type"; - else - htmlTitle += " QML Type"; - - generateHeader(htmlTitle, qcn, marker); - Sections sections(qcn); - generateTableOfContents(qcn, marker, §ions.stdQmlTypeSummarySections()); - marker = CodeMarker::markerForLanguage(QLatin1String("QML")); - generateTitle(htmlTitle, Text() << qcn->subtitle(), subTitleSize, qcn, marker); - generateBrief(qcn, marker); - generateQmlRequisites(qcn, marker); - - QString allQmlMembersLink = generateAllQmlMembersFile(sections, marker); - QString obsoleteLink = generateObsoleteQmlMembersFile(sections, marker); - if (!allQmlMembersLink.isEmpty() || !obsoleteLink.isEmpty()) { - out() << "<ul>\n"; - if (!allQmlMembersLink.isEmpty()) { - out() << "<li><a href=\"" << allQmlMembersLink << "\">" - << "List of all members, including inherited members</a></li>\n"; - } - if (!obsoleteLink.isEmpty()) { - out() << "<li><a href=\"" << obsoleteLink << "\">" - << "Deprecated members</a></li>\n"; - } - out() << "</ul>\n"; - } - - const QList<Section> &stdQmlTypeSummarySections = sections.stdQmlTypeSummarySections(); - for (const auto §ion : stdQmlTypeSummarySections) { - if (!section.isEmpty()) { - QString ref = registerRef(section.title().toLower()); - out() << "<h2 id=\"" << ref << "\">" << protectEnc(section.title()) << "</h2>\n"; - generateQmlSummary(section.members(), qcn, marker); - } - } - - generateExtractionMark(qcn, DetailedDescriptionMark); - out() << "<h2 id=\"" << registerRef("details") << "\">" - << "Detailed Description" - << "</h2>\n"; - generateBody(qcn, marker); - ClassNode *cn = qcn->classNode(); - if (cn) - generateQmlText(cn->doc().body(), cn, marker, qcn->name()); - generateAlsoList(qcn, marker); - generateExtractionMark(qcn, EndMark); - - const QList<Section> &stdQmlTypeDetailsSections = sections.stdQmlTypeDetailsSections(); - for (const auto §ion : stdQmlTypeDetailsSections) { - if (!section.isEmpty()) { - out() << "<h2>" << protectEnc(section.title()) << "</h2>\n"; - const QList<Node *> &members = section.members(); - for (const auto member : members) { - generateDetailedQmlMember(member, qcn, marker); - out() << "<br/>\n"; - } - } - } - generateFooter(qcn); - Generator::setQmlTypeContext(nullptr); -} - -/*! - Generate the HTML page for the QML basic type represented - by the QML basic type node \a qbtn. - */ -void HtmlGenerator::generateQmlBasicTypePage(QmlBasicTypeNode *qbtn, CodeMarker *marker) -{ - SubTitleSize subTitleSize = LargeSubTitle; - QString htmlTitle = qbtn->fullTitle(); - if (qbtn->isJsType()) - htmlTitle += " JavaScript Basic Type"; - else - htmlTitle += " QML Basic Type"; - - marker = CodeMarker::markerForLanguage(QLatin1String("QML")); - - generateHeader(htmlTitle, qbtn, marker); - Sections sections(qbtn); - generateTableOfContents(qbtn, marker, §ions.stdQmlTypeSummarySections()); - generateTitle(htmlTitle, Text() << qbtn->subtitle(), subTitleSize, qbtn, marker); - - const QList<Section> &stdQmlTypeSummarySections = sections.stdQmlTypeSummarySections(); - for (const auto §ion : stdQmlTypeSummarySections) { - if (!section.isEmpty()) { - const QString &ref = registerRef(section.title().toLower()); - out() << "<h2 id=\"" << ref << "\">" << protectEnc(section.title()) << "</h2>\n"; - generateQmlSummary(section.members(), qbtn, marker); - } - } - - generateExtractionMark(qbtn, DetailedDescriptionMark); - out() << R"(<div class="descr" id=")" << registerRef("details") - << "\">\n"; - - generateBody(qbtn, marker); - out() << "</div>\n"; - generateAlsoList(qbtn, marker); - generateExtractionMark(qbtn, EndMark); - - const QList<Section> &stdQmlTypeDetailsSections = sections.stdQmlTypeDetailsSections(); - for (const auto §ion : stdQmlTypeDetailsSections) { - if (!section.isEmpty()) { - out() << "<h2>" << protectEnc(section.title()) << "</h2>\n"; - const QList<Node *> &members = section.members(); - for (const auto member : members) { - generateDetailedQmlMember(member, qbtn, marker); - out() << "<br/>\n"; - } - } - } - generateFooter(qbtn); -} - -/*! - Generate the HTML page for an entity that doesn't map - to any underlying parsable C++, QML, or Javascript element. - */ -void HtmlGenerator::generatePageNode(PageNode *pn, CodeMarker *marker) -{ - SubTitleSize subTitleSize = LargeSubTitle; - QString fullTitle = pn->fullTitle(); - - generateHeader(fullTitle, pn, marker); - /* - Generate the TOC for the new doc format. - Don't generate a TOC for the home page. - */ - if ((pn->name() != QLatin1String("index.html"))) - generateTableOfContents(pn, marker, nullptr); - - generateTitle(fullTitle, Text() << pn->subtitle(), subTitleSize, pn, marker); - if (pn->isExample()) { - generateBrief(pn, marker, nullptr, false); - } - - generateExtractionMark(pn, DetailedDescriptionMark); - out() << R"(<div class="descr" id=")" << registerRef("details") - << "\">\n"; - - generateBody(pn, marker); - out() << "</div>\n"; - generateAlsoList(pn, marker); - generateExtractionMark(pn, EndMark); - - generateFooter(pn); -} - -/*! - Generate the HTML page for a group, module, or QML module. - */ -void HtmlGenerator::generateCollectionNode(CollectionNode *cn, CodeMarker *marker) -{ - SubTitleSize subTitleSize = LargeSubTitle; - QString fullTitle = cn->fullTitle(); - QString ref; - - generateHeader(fullTitle, cn, marker); - generateTableOfContents(cn, marker, nullptr); - generateTitle(fullTitle, Text() << cn->subtitle(), subTitleSize, cn, marker); - - // Generate brief for C++ modules, status for all modules. - if (cn->genus() != Node::DOC && cn->genus() != Node::DontCare) { - if (cn->isModule()) - generateBrief(cn, marker); - generateStatus(cn, marker); - generateSince(cn, marker); - } - - if (cn->isModule()) { - if (!cn->noAutoList()) { - NodeMap nmm; - cn->getMemberNamespaces(nmm); - if (!nmm.isEmpty()) { - ref = registerRef("namespaces"); - out() << "<h2 id=\"" << ref << "\">Namespaces</h2>\n"; - generateAnnotatedList(cn, marker, nmm.values()); - } - nmm.clear(); - cn->getMemberClasses(nmm); - if (!nmm.isEmpty()) { - ref = registerRef("classes"); - out() << "<h2 id=\"" << ref << "\">Classes</h2>\n"; - generateAnnotatedList(cn, marker, nmm.values()); - } - } - } - - if (cn->isModule() && !cn->doc().briefText().isEmpty()) { - generateExtractionMark(cn, DetailedDescriptionMark); - ref = registerRef("details"); - out() << "<div class=\"descr\">\n"; - out() << "<h2 id=\"" << ref << "\">" - << "Detailed Description" - << "</h2>\n"; - } else { - generateExtractionMark(cn, DetailedDescriptionMark); - out() << R"(<div class="descr" id=")" << registerRef("details") - << "\">\n"; - } - - generateBody(cn, marker); - out() << "</div>\n"; - generateAlsoList(cn, marker); - generateExtractionMark(cn, EndMark); - - if (!cn->noAutoList()) { - if (cn->isGroup() || cn->isQmlModule() || cn->isJsModule()) - generateAnnotatedList(cn, marker, cn->members()); - } - generateFooter(cn); -} - -/*! - Generate the HTML page for a generic collection. This is usually - a collection of C++ elements that are related to an element in - a different module. - */ -void HtmlGenerator::generateGenericCollectionPage(CollectionNode *cn, CodeMarker *marker) -{ - SubTitleSize subTitleSize = LargeSubTitle; - QString fullTitle = cn->name(); - - generateHeader(fullTitle, cn, marker); - generateTitle(fullTitle, Text() << cn->subtitle(), subTitleSize, cn, marker); - - Text brief; - brief << "Each function or type documented here is related to a class or " - << "namespace that is documented in a different module. The reference " - << "page for that class or namespace will link to the function or type " - << "on this page."; - out() << "<p>"; - generateText(brief, cn, marker); - out() << "</p>\n"; - - const QList<Node *> members = cn->members(); - for (const auto &member : members) - generateDetailedMember(member, cn, marker); - - generateFooter(cn); -} - -/*! - Returns "html" for this subclass of Generator. - */ -QString HtmlGenerator::fileExtension() const -{ - return "html"; -} - -/*! - Output navigation list in the html file. - */ -void HtmlGenerator::generateNavigationBar(const QString &title, const Node *node, - CodeMarker *marker, const QString &buildversion, - bool tableItems) -{ - if (m_noNavigationBar || node == nullptr) - return; - - Text navigationbar; - - // Set list item types based on the navigation bar type - Atom::AtomType itemLeft = tableItems ? Atom::TableItemLeft : Atom::ListItemLeft; - Atom::AtomType itemRight = tableItems ? Atom::TableItemRight : Atom::ListItemRight; - - if (m_hometitle == title) - return; - if (!m_homepage.isEmpty()) - navigationbar << Atom(itemLeft) << Atom(Atom::NavLink, m_homepage) - << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK) - << Atom(Atom::String, m_hometitle) - << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK) << Atom(itemRight); - if (!m_landingpage.isEmpty() && m_landingtitle != title) - navigationbar << Atom(itemLeft) << Atom(Atom::NavLink, m_landingpage) - << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK) - << Atom(Atom::String, m_landingtitle) - << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK) << Atom(itemRight); - - if (node->isClassNode()) { - if (!m_cppclassespage.isEmpty() && !m_cppclassestitle.isEmpty()) - navigationbar << Atom(itemLeft) << Atom(Atom::NavLink, m_cppclassespage) - << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK) - << Atom(Atom::String, m_cppclassestitle) - << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK) << Atom(itemRight); - - if (!node->name().isEmpty()) - navigationbar << Atom(itemLeft) << Atom(Atom::String, node->name()) << Atom(itemRight); - } else if (node->isQmlType() || node->isQmlBasicType() || node->isJsType() - || node->isJsBasicType()) { - if (!m_qmltypespage.isEmpty() && !m_qmltypestitle.isEmpty()) - navigationbar << Atom(itemLeft) << Atom(Atom::NavLink, m_qmltypespage) - << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK) - << Atom(Atom::String, m_qmltypestitle) - << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK) << Atom(itemRight) - << Atom(itemLeft) << Atom(Atom::String, title) << Atom(itemRight); - } else { - if (node->isPageNode()) { - QStringList groups = static_cast<const PageNode *>(node)->groupNames(); - if (groups.length() == 1) { - const Node *groupNode = - m_qdb->findNodeByNameAndType(QStringList(groups[0]), &Node::isGroup); - if (groupNode && !groupNode->title().isEmpty()) { - navigationbar << Atom(itemLeft) << Atom(Atom::NavLink, groupNode->name()) - << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK) - << Atom(Atom::String, groupNode->title()) - << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK) - << Atom(itemRight); - } - } - } - if (!navigationbar.isEmpty()) { - navigationbar << Atom(itemLeft) << Atom(Atom::String, title) << Atom(itemRight); - } - } - - generateText(navigationbar, node, marker); - - if (buildversion.isEmpty()) - return; - - navigationbar.clear(); - - if (tableItems) { - out() << "</tr></table><table class=\"buildversion\"><tr>\n" - << R"(<td id="buildversion" width="100%" align="right">)"; - } else { - out() << "<li id=\"buildversion\">"; - } - - // Link buildversion string to navigation.landingpage - if (!m_landingpage.isEmpty() && m_landingtitle != title) { - navigationbar << Atom(Atom::NavLink, m_landingpage) - << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK) - << Atom(Atom::String, buildversion) - << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK); - generateText(navigationbar, node, marker); - } else { - out() << buildversion; - } - if (tableItems) - out() << "</td>\n"; - else - out() << "</li>\n"; -} - -void HtmlGenerator::generateHeader(const QString &title, const Node *node, CodeMarker *marker) -{ - out() << "<!DOCTYPE html>\n"; - out() << QString("<html lang=\"%1\">\n").arg(naturalLanguage); - out() << "<head>\n"; - out() << " <meta charset=\"utf-8\">\n"; - if (node && !node->doc().location().isEmpty()) - out() << "<!-- " << node->doc().location().fileName() << " -->\n"; - - // determine the rest of the <title> element content: "title | titleSuffix version" - QString titleSuffix; - if (!m_landingtitle.isEmpty()) { - // for normal pages: "title | landingtitle version" - titleSuffix = m_landingtitle; - } else if (!m_hometitle.isEmpty()) { - // for pages that set the homepage title but not landing page title: - // "title | hometitle version" - if (title != m_hometitle) - titleSuffix = m_hometitle; - } else if (!m_project.isEmpty()) { - // for projects outside of Qt or Qt 5: "title | project version" - if (title != m_project) - titleSuffix = m_project; - } else - // default: "title | Qt version" - titleSuffix = QLatin1String("Qt "); - - if (title == titleSuffix) - titleSuffix.clear(); - - QString divider; - if (!titleSuffix.isEmpty() && !title.isEmpty()) - divider = QLatin1String(" | "); - - // Generating page title - out() << " <title>" << protectEnc(title) << divider << titleSuffix; - - // append a full version to the suffix if neither suffix nor title - // include (a prefix of) version information - QVersionNumber projectVersion = QVersionNumber::fromString(m_qdb->version()); - if (!projectVersion.isNull()) { - QVersionNumber titleVersion; - QRegularExpression re(QLatin1String(R"(\d+\.\d+)")); - const QString &versionedTitle = titleSuffix.isEmpty() ? title : titleSuffix; - auto match = re.match(versionedTitle); - if (match.hasMatch()) - titleVersion = QVersionNumber::fromString(match.captured()); - if (titleVersion.isNull() || !titleVersion.isPrefixOf(projectVersion)) - out() << QLatin1Char(' ') << projectVersion.toString(); - } - out() << "</title>\n"; - - // Include style sheet and script links. - out() << m_headerStyles; - out() << m_headerScripts; - if (m_endHeader.isEmpty()) - out() << "</head>\n<body>\n"; - else - out() << m_endHeader; - - out() << QString(m_postHeader).replace("\\" + COMMAND_VERSION, m_qdb->version()); - bool usingTable = m_postHeader.trimmed().endsWith(QLatin1String("<tr>")); - generateNavigationBar(title, node, marker, m_buildversion, usingTable); - out() << QString(m_postPostHeader).replace("\\" + COMMAND_VERSION, m_qdb->version()); - - m_navigationLinks.clear(); - refMap.clear(); - - if (node && !node->links().empty()) { - QPair<QString, QString> linkPair; - QPair<QString, QString> anchorPair; - const Node *linkNode; - bool useSeparator = false; - - if (node->links().contains(Node::PreviousLink)) { - linkPair = node->links()[Node::PreviousLink]; - linkNode = m_qdb->findNodeForTarget(linkPair.first, node); - if (linkNode == nullptr && !noLinkErrors()) - node->doc().location().warning( - QStringLiteral("Cannot link to '%1'").arg(linkPair.first)); - if (linkNode == nullptr || linkNode == node) - anchorPair = linkPair; - else - anchorPair = anchorForNode(linkNode); - - out() << R"( <link rel="prev" href=")" << anchorPair.first << "\" />\n"; - - m_navigationLinks += R"(<a class="prevPage" href=")" + anchorPair.first + "\">"; - if (linkPair.first == linkPair.second && !anchorPair.second.isEmpty()) - m_navigationLinks += protect(anchorPair.second); - else - m_navigationLinks += protect(linkPair.second); - m_navigationLinks += "</a>\n"; - useSeparator = !m_navigationSeparator.isEmpty(); - } - if (node->links().contains(Node::NextLink)) { - linkPair = node->links()[Node::NextLink]; - linkNode = m_qdb->findNodeForTarget(linkPair.first, node); - if (linkNode == nullptr && !noLinkErrors()) - node->doc().location().warning( - QStringLiteral("Cannot link to '%1'").arg(linkPair.first)); - if (linkNode == nullptr || linkNode == node) - anchorPair = linkPair; - else - anchorPair = anchorForNode(linkNode); - - out() << R"( <link rel="next" href=")" << anchorPair.first << "\" />\n"; - - if (useSeparator) - m_navigationLinks += m_navigationSeparator; - - m_navigationLinks += R"(<a class="nextPage" href=")" + anchorPair.first + "\">"; - if (linkPair.first == linkPair.second && !anchorPair.second.isEmpty()) - m_navigationLinks += protect(anchorPair.second); - else - m_navigationLinks += protect(linkPair.second); - m_navigationLinks += "</a>\n"; - } - if (node->links().contains(Node::StartLink)) { - linkPair = node->links()[Node::StartLink]; - linkNode = m_qdb->findNodeForTarget(linkPair.first, node); - if (linkNode == nullptr && !noLinkErrors()) - node->doc().location().warning( - QStringLiteral("Cannot link to '%1'").arg(linkPair.first)); - if (linkNode == nullptr || linkNode == node) - anchorPair = linkPair; - else - anchorPair = anchorForNode(linkNode); - out() << R"( <link rel="start" href=")" << anchorPair.first << "\" />\n"; - } - } - - if (node && !node->links().empty()) - out() << "<p class=\"naviNextPrevious headerNavi\">\n" << m_navigationLinks << "</p>\n"; -} - -void HtmlGenerator::generateTitle(const QString &title, const Text &subtitle, - SubTitleSize subTitleSize, const Node *relative, - CodeMarker *marker) -{ - out() << QString(m_prologue).replace("\\" + COMMAND_VERSION, m_qdb->version()); - if (!title.isEmpty()) - out() << "<h1 class=\"title\">" << protectEnc(title) << "</h1>\n"; - if (!subtitle.isEmpty()) { - out() << "<span"; - if (subTitleSize == SmallSubTitle) - out() << " class=\"small-subtitle\">"; - else - out() << " class=\"subtitle\">"; - generateText(subtitle, relative, marker); - out() << "</span>\n"; - } -} - -void HtmlGenerator::generateFooter(const Node *node) -{ - if (node && !node->links().empty()) - out() << "<p class=\"naviNextPrevious footerNavi\">\n" << m_navigationLinks << "</p>\n"; - - out() << QString(m_footer).replace("\\" + COMMAND_VERSION, m_qdb->version()) - << QString(m_address).replace("\\" + COMMAND_VERSION, m_qdb->version()); - - out() << "</body>\n"; - out() << "</html>\n"; -} - -/*! -Lists the required imports and includes in a table. -The number of rows is known. -*/ -void HtmlGenerator::generateRequisites(Aggregate *aggregate, CodeMarker *marker) -{ - QMap<QString, Text> requisites; - Text text; - - const QString headerText = "Header"; - const QString sinceText = "Since"; - const QString inheritedBytext = "Inherited By"; - const QString inheritsText = "Inherits"; - const QString instantiatedByText = "Instantiated By"; - const QString qtVariableText = "qmake"; - const QString cmakeText = "CMake"; - - // The order of the requisites matter - const QStringList requisiteorder { headerText, cmakeText, qtVariableText, sinceText, - instantiatedByText, inheritsText, inheritedBytext }; - - addIncludeFilesToMap(aggregate, marker, requisites, &text, headerText); - addSinceToMap(aggregate, requisites, &text, sinceText); - - if (aggregate->isClassNode() || aggregate->isNamespace()) { - addCMakeInfoToMap(aggregate, requisites, &text, cmakeText); - addQtVariableToMap(aggregate, requisites, &text, qtVariableText); - } - - if (aggregate->isClassNode()) { - auto *classe = dynamic_cast<ClassNode *>(aggregate); - if (classe->qmlElement() != nullptr && !classe->isInternal()) - addInstantiatedByToMap(requisites, &text, instantiatedByText, classe); - - addInheritsToMap(requisites, &text, inheritsText, classe); - addInheritedByToMap(requisites, &text, inheritedBytext, classe); - } - - if (!requisites.isEmpty()) { - // generate the table - generateTheTable(requisiteorder, requisites, headerText, aggregate, marker); - } -} - -/*! - * \internal - */ -void HtmlGenerator::generateTheTable(const QStringList &requisiteOrder, - const QMap<QString, Text> &requisites, - const QString &headerText, const Aggregate *aggregate, - CodeMarker *marker) -{ - out() << "<div class=\"table\"><table class=\"alignedsummary\">\n"; - - for (auto it = requisiteOrder.constBegin(); it != requisiteOrder.constEnd(); ++it) { - - if (requisites.contains(*it)) { - out() << "<tr>" - << "<td class=\"memItemLeft rightAlign topAlign\"> " << *it - << ":" - "</td><td class=\"memItemRight bottomAlign\"> "; - - if (*it == headerText) - out() << requisites.value(*it).toString(); - else - generateText(requisites.value(*it), aggregate, marker); - out() << "</td></tr>"; - } - } - out() << "</table></div>\n"; -} - -/*! - * \internal - * Adds inherited by information to the map. - */ -void HtmlGenerator::addInheritedByToMap(QMap<QString, Text> &requisites, Text *text, - const QString &inheritedBytext, ClassNode *classe) -{ - if (!classe->derivedClasses().isEmpty()) { - text->clear(); - *text << Atom::ParaLeft; - int count = appendSortedNames(*text, classe, classe->derivedClasses()); - *text << Atom::ParaRight; - if (count > 0) - requisites.insert(inheritedBytext, *text); - } -} - -/*! - * \internal - * Adds base classes to the map. - */ -void HtmlGenerator::addInheritsToMap(QMap<QString, Text> &requisites, Text *text, - const QString &inheritsText, ClassNode *classe) -{ - if (!classe->baseClasses().isEmpty()) { - int index = 0; - text->clear(); - const auto baseClasses = classe->baseClasses(); - for (const auto &cls : baseClasses) { - if (cls.m_node) { - appendFullName(*text, cls.m_node, classe); - - if (cls.m_access == Access::Protected) { - *text << " (protected)"; - } else if (cls.m_access == Access::Private) { - *text << " (private)"; - } - *text << Utilities::comma(index++, classe->baseClasses().count()); - } - } - *text << Atom::ParaRight; - if (index > 0) - requisites.insert(inheritsText, *text); - } -} - -/*! - * \internal - * Add the instantiated by information to the map. - */ -void HtmlGenerator::addInstantiatedByToMap(QMap<QString, Text> &requisites, Text *text, - const QString &instantiatedByText, - ClassNode *classe) const -{ - if (text != nullptr) { - text->clear(); - *text << Atom(Atom::LinkNode, CodeMarker::stringForNode(classe->qmlElement())) - << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK) - << Atom(Atom::String, classe->qmlElement()->name()) - << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK); - requisites.insert(instantiatedByText, *text); - } -} - -/*! - * \internal - * Adds the CMake package and link library information to the map. - */ -void HtmlGenerator::addCMakeInfoToMap(const Aggregate *aggregate, QMap<QString, Text> &requisites, - Text *text, const QString &CMakeInfo) const -{ - if (!aggregate->physicalModuleName().isEmpty() && text != nullptr) { - const CollectionNode *cn = - m_qdb->getCollectionNode(aggregate->physicalModuleName(), Node::Module); - if (cn && !cn->qtCMakeComponent().isEmpty()) { - text->clear(); - const QString qtComponent = "Qt" + QString::number(QT_VERSION_MAJOR); - const QString findPackageText = "find_package(" + qtComponent + " COMPONENTS " - + cn->qtCMakeComponent() + " REQUIRED)"; - const QString targetLinkLibrariesText = "target_link_libraries(mytarget PRIVATE " - + qtComponent + "::" + cn->qtCMakeComponent() + ")"; - const Atom lineBreak = Atom(Atom::RawString, " <br/>\n"); - *text << findPackageText << lineBreak << targetLinkLibrariesText; - requisites.insert(CMakeInfo, *text); - } - } -} - -/*! - * \internal - * Adds the Qt variable (from the \\qtvariable command) to the map. - */ -void HtmlGenerator::addQtVariableToMap(const Aggregate *aggregate, QMap<QString, Text> &requisites, - Text *text, const QString &qtVariableText) const -{ - if (!aggregate->physicalModuleName().isEmpty()) { - const CollectionNode *cn = - m_qdb->getCollectionNode(aggregate->physicalModuleName(), Node::Module); - - if (cn && !cn->qtVariable().isEmpty()) { - text->clear(); - *text << "QT += " + cn->qtVariable(); - requisites.insert(qtVariableText, *text); - } - } -} - -/*! - * \internal - * Adds the since information (from the \\since command) to the map. - * - */ -void HtmlGenerator::addSinceToMap(const Aggregate *aggregate, QMap<QString, Text> &requisites, - Text *text, const QString &sinceText) const -{ - if (!aggregate->since().isEmpty() && text != nullptr) { - text->clear(); - *text << formatSince(aggregate) << Atom::ParaRight; - requisites.insert(sinceText, *text); - } -} - -/*! - * \internal - * Adds the includes (from the \\includefile command) to the map. - */ -void HtmlGenerator::addIncludeFilesToMap(const Aggregate *aggregate, CodeMarker *marker, - QMap<QString, Text> &requisites, Text *text, - const QString &headerText) -{ - QStringList includeFiles = aggregate->includeFiles(); - includeFiles.erase(std::remove_if(includeFiles.begin(), includeFiles.end(), - [](auto includeFile) { return includeFile.isEmpty(); }), includeFiles.end()); - - if (!includeFiles.isEmpty() && text != nullptr) { - text->clear(); - *text << highlightedCode( - indent(m_codeIndent, marker->markedUpIncludes(includeFiles)), - aggregate); - requisites.insert(headerText, *text); - } -} - -/*! -Lists the required imports and includes in a table. -The number of rows is known. -*/ -void HtmlGenerator::generateQmlRequisites(QmlTypeNode *qcn, CodeMarker *marker) -{ - if (qcn == nullptr) - return; - QMap<QString, Text> requisites; - Text text; - - const QString importText = "Import Statement:"; - const QString sinceText = "Since:"; - const QString inheritedBytext = "Inherited By:"; - const QString inheritsText = "Inherits:"; - const QString instantiatesText = "Instantiates:"; - - // add the module name and version to the map - QString logicalModuleVersion; - const CollectionNode *collection = qcn->logicalModule(); - - // skip import statement of \internal collections - if (!collection || !collection->isInternal() || m_showInternal) { - logicalModuleVersion = - collection ? collection->logicalModuleVersion() : qcn->logicalModuleVersion(); - - if (qcn->logicalModuleName().isEmpty()) - qcn->doc().location().warning(QStringLiteral("Could not resolve QML import " - "statement for type '%1'") - .arg(qcn->name()), - QStringLiteral("Maybe you forgot to use the " - "'\\%1' command?") - .arg(COMMAND_INQMLMODULE)); - - text.clear(); - text << "import " + qcn->logicalModuleName(); - if (!logicalModuleVersion.isEmpty()) - text << QLatin1Char(' ') + logicalModuleVersion; - requisites.insert(importText, text); - } - - // add the since and project into the map - if (!qcn->since().isEmpty()) { - text.clear(); - text << formatSince(qcn) << Atom::ParaRight; - requisites.insert(sinceText, text); - } - - // add the instantiates to the map - ClassNode *cn = qcn->classNode(); - if (cn && !cn->isInternal()) { - text.clear(); - text << Atom(Atom::LinkNode, CodeMarker::stringForNode(cn)); - text << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK); - text << Atom(Atom::String, cn->name()); - text << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK); - requisites.insert(instantiatesText, text); - } - - // add the inherits to the map - QmlTypeNode *base = qcn->qmlBaseNode(); - while (base && base->isInternal()) { - base = base->qmlBaseNode(); - } - if (base) { - text.clear(); - text << Atom::ParaLeft << Atom(Atom::LinkNode, CodeMarker::stringForNode(base)) - << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK) << Atom(Atom::String, base->name()) - << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK) << Atom::ParaRight; - requisites.insert(inheritsText, text); - } - - // add the inherited-by to the map - NodeList subs; - QmlTypeNode::subclasses(qcn, subs); - if (!subs.isEmpty()) { - text.clear(); - text << Atom::ParaLeft; - int count = appendSortedQmlNames(text, qcn, subs); - text << Atom::ParaRight; - if (count > 0) - requisites.insert(inheritedBytext, text); - } - - // The order of the requisites matter - const QStringList requisiteorder { importText, sinceText, instantiatesText, inheritsText, - inheritedBytext }; - - if (!requisites.isEmpty()) { - // generate the table - out() << "<div class=\"table\"><table class=\"alignedsummary\">\n"; - for (const auto &requisite : requisiteorder) { - - if (requisites.contains(requisite)) { - out() << "<tr>" - << "<td class=\"memItemLeft rightAlign topAlign\"> " << requisite - << "</td><td class=\"memItemRight bottomAlign\"> "; - - if (requisite == importText) - out() << requisites.value(requisite).toString(); - else - generateText(requisites.value(requisite), qcn, marker); - out() << "</td></tr>"; - } - } - out() << "</table></div>"; - } -} - -void HtmlGenerator::generateBrief(const Node *node, CodeMarker *marker, const Node *relative, - bool addLink) -{ - Text brief = node->doc().briefText(); - - if (!brief.isEmpty()) { - if (!brief.lastAtom()->string().endsWith('.')) { - brief << Atom(Atom::String, "."); - node->doc().location().warning( - QStringLiteral("'\\brief' statement does not end with a full stop.")); - } - generateExtractionMark(node, BriefMark); - out() << "<p>"; - generateText(brief, node, marker); - - if (addLink) { - if (!relative || node == relative) - out() << " <a href=\"#"; - else - out() << " <a href=\"" << linkForNode(node, relative) << '#'; - out() << registerRef("details") << "\">More...</a>"; - } - - out() << "</p>\n"; - generateExtractionMark(node, EndMark); - } -} - -/*! - Revised for the new doc format. - Generates a table of contents beginning at \a node. - */ -void HtmlGenerator::generateTableOfContents(const Node *node, CodeMarker *marker, - QList<Section> *sections) -{ - QList<Atom *> toc; - if (node->doc().hasTableOfContents()) - toc = node->doc().tableOfContents(); - if (tocDepth == 0 || (toc.isEmpty() && !sections && !node->isModule())) { - generateSidebar(); - return; - } - - int sectionNumber = 1; - int detailsBase = 0; - - // disable nested links in table of contents - m_inContents = true; - m_inLink = true; - - out() << "<div class=\"sidebar\">\n"; - out() << "<div class=\"toc\">\n"; - out() << "<h3 id=\"toc\">Contents</h3>\n"; - - if (node->isModule()) { - openUnorderedList(); - if (!static_cast<const CollectionNode *>(node)->noAutoList()) { - if (node->hasNamespaces()) { - out() << "<li class=\"level" << sectionNumber << "\"><a href=\"#" - << registerRef("namespaces") << "\">Namespaces</a></li>\n"; - } - if (node->hasClasses()) { - out() << "<li class=\"level" << sectionNumber << "\"><a href=\"#" - << registerRef("classes") << "\">Classes</a></li>\n"; - } - } - out() << "<li class=\"level" << sectionNumber << "\"><a href=\"#" << registerRef("details") - << "\">Detailed Description</a></li>\n"; - for (const auto &entry : qAsConst(toc)) { - if (entry->string().toInt() == 1) { - detailsBase = 1; - break; - } - } - } else if (sections - && (node->isClassNode() || node->isNamespace() || node->isQmlType() || - node->isJsType() || node->isQmlBasicType() || node->isJsBasicType())) { - for (const auto §ion : qAsConst(*sections)) { - if (!section.members().isEmpty()) { - openUnorderedList(); - out() << "<li class=\"level" << sectionNumber << "\"><a href=\"#" - << registerRef(section.plural()) << "\">" << section.title() << "</a></li>\n"; - } - if (!section.reimplementedMembers().isEmpty()) { - openUnorderedList(); - QString ref = QString("Reimplemented ") + section.plural(); - out() << "<li class=\"level" << sectionNumber << "\"><a href=\"#" - << registerRef(ref.toLower()) << "\">" - << QString("Reimplemented ") + section.title() << "</a></li>\n"; - } - } - if (!node->isNamespace() || node->hasDoc()) { - openUnorderedList(); - out() << "<li class=\"level" << sectionNumber << "\"><a href=\"#" - << registerRef("details") << "\">Detailed Description</a></li>\n"; - } - for (const auto &entry : toc) { - if (entry->string().toInt() == 1) { - detailsBase = 1; - break; - } - } - } - - for (const auto &atom : toc) { - sectionNumber = atom->string().toInt() + detailsBase; - // restrict the ToC depth to the one set by the HTML.tocdepth variable or - // print all levels if tocDepth is not set. - if (sectionNumber <= tocDepth || tocDepth < 0) { - openUnorderedList(); - int numAtoms; - Text headingText = Text::sectionHeading(atom); - QString s = headingText.toString(); - out() << "<li class=\"level" << sectionNumber << "\">"; - out() << "<a href=\"" << '#' << Doc::canonicalTitle(s) << "\">"; - generateAtomList(headingText.firstAtom(), node, marker, true, numAtoms); - out() << "</a></li>\n"; - } - } - closeUnorderedList(); - out() << "</div>\n"; - out() << R"(<div class="sidebar-content" id="sidebar-content"></div>)"; - out() << "</div>\n"; - m_inContents = false; - m_inLink = false; -} - -/*! - Outputs a placeholder div where the style can add customized sidebar content. - */ -void HtmlGenerator::generateSidebar() -{ - out() << "<div class=\"sidebar\">"; - out() << R"(<div class="sidebar-content" id="sidebar-content"></div>)"; - out() << "</div>\n"; -} - -QString HtmlGenerator::generateAllMembersFile(const Section §ion, CodeMarker *marker) -{ - if (section.isEmpty()) - return QString(); - - const Aggregate *aggregate = section.aggregate(); - QString fileName = fileBase(aggregate) + "-members." + fileExtension(); - beginSubPage(aggregate, fileName); - QString title = "List of All Members for " + aggregate->name(); - generateHeader(title, aggregate, marker); - generateSidebar(); - generateTitle(title, Text(), SmallSubTitle, aggregate, marker); - out() << "<p>This is the complete list of members for "; - generateFullName(aggregate, nullptr); - out() << ", including inherited members.</p>\n"; - - generateSectionList(section, aggregate, marker); - - generateFooter(); - endSubPage(); - return fileName; -} - -/*! - This function creates an html page on which are listed all - the members of the QML class used to generte the \a sections, - including the inherited members. The \a marker is used for - formatting stuff. - */ -QString HtmlGenerator::generateAllQmlMembersFile(const Sections §ions, CodeMarker *marker) -{ - - if (sections.allMembersSection().isEmpty()) - return QString(); - - const Aggregate *aggregate = sections.aggregate(); - QString fileName = fileBase(aggregate) + "-members." + fileExtension(); - beginSubPage(aggregate, fileName); - QString title = "List of All Members for " + aggregate->name(); - generateHeader(title, aggregate, marker); - generateSidebar(); - generateTitle(title, Text(), SmallSubTitle, aggregate, marker); - out() << "<p>This is the complete list of members for "; - generateFullName(aggregate, nullptr); - out() << ", including inherited members.</p>\n"; - - ClassKeysNodesList &cknl = sections.allMembersSection().classKeysNodesList(); - if (!cknl.isEmpty()) { - for (int i = 0; i < cknl.size(); i++) { - ClassKeysNodes *ckn = cknl[i]; - const QmlTypeNode *qcn = ckn->first; - KeysAndNodes &kn = ckn->second; - QStringList &keys = kn.first; - NodeVector &nodes = kn.second; - if (nodes.isEmpty()) - continue; - if (i != 0) { - out() << "<p>The following members are inherited from "; - generateFullName(qcn, nullptr); - out() << ".</p>\n"; - } - openUnorderedList(); - for (int j = 0; j < keys.size(); j++) { - Node *node = nodes[j]; - if (node->access() == Access::Private || node->isInternal()) - continue; - if (node->isSharingComment() && node->sharedCommentNode()->isPropertyGroup()) - continue; - - std::function<void(Node *)> generate = [&](Node *n) { - out() << "<li class=\"fn\">"; - generateQmlItem(n, aggregate, marker, true); - if (n->isDefault()) - out() << " [default]"; - else if (n->isAttached()) - out() << " [attached]"; - // Indent property group members - if (n->isPropertyGroup()) { - out() << "<ul>\n"; - const QList<Node *> &collective = - static_cast<SharedCommentNode *>(n)->collective(); - std::for_each(collective.begin(), collective.end(), generate); - out() << "</ul>\n"; - } - out() << "</li>\n"; - }; - generate(node); - } - closeUnorderedList(); - } - } - - generateFooter(); - endSubPage(); - return fileName; -} - -QString HtmlGenerator::generateObsoleteMembersFile(const Sections §ions, CodeMarker *marker) -{ - SectionPtrVector summary_spv; - SectionPtrVector details_spv; - if (!sections.hasObsoleteMembers(&summary_spv, &details_spv)) - return QString(); - - Aggregate *aggregate = sections.aggregate(); - QString title = "Obsolete Members for " + aggregate->name(); - QString fileName = fileBase(aggregate) + "-obsolete." + fileExtension(); - QString link; - if (useOutputSubdirs() && !Generator::outputSubdir().isEmpty()) - link = QString("../" + Generator::outputSubdir() + QLatin1Char('/')); - link += fileName; - aggregate->setObsoleteLink(link); - - beginSubPage(aggregate, fileName); - generateHeader(title, aggregate, marker); - generateSidebar(); - generateTitle(title, Text(), SmallSubTitle, aggregate, marker); - - out() << "<p><b>The following members of class " - << "<a href=\"" << linkForNode(aggregate, nullptr) << "\">" - << protectEnc(aggregate->name()) << "</a>" - << " are deprecated.</b> " - << "They are provided to keep old source code working. " - << "We strongly advise against using them in new code.</p>\n"; - - for (const auto §ion : summary_spv) { - out() << "<h2>" << protectEnc(section->title()) << "</h2>\n"; - generateSectionList(*section, aggregate, marker, Section::Obsolete); - } - - for (const auto §ion : details_spv) { - out() << "<h2>" << protectEnc(section->title()) << "</h2>\n"; - - const NodeVector &members = section->obsoleteMembers(); - for (const auto &member : members) { - if (member->access() != Access::Private) - generateDetailedMember(member, aggregate, marker); - } - } - - generateFooter(); - endSubPage(); - return fileName; -} - -/*! - Generates a separate file where deprecated members of the QML - type \a qcn are listed. The \a marker is used to generate - the section lists, which are then traversed and output here. - - Note that this function currently only handles correctly the - case where \a status is \c {Section::Obsolete}. - */ -QString HtmlGenerator::generateObsoleteQmlMembersFile(const Sections §ions, CodeMarker *marker) -{ - SectionPtrVector summary_spv; - SectionPtrVector details_spv; - if (!sections.hasObsoleteMembers(&summary_spv, &details_spv)) - return QString(); - - Aggregate *aggregate = sections.aggregate(); - QString title = "Obsolete Members for " + aggregate->name(); - QString fileName = fileBase(aggregate) + "-obsolete." + fileExtension(); - QString link; - if (useOutputSubdirs() && !Generator::outputSubdir().isEmpty()) - link = QString("../" + Generator::outputSubdir() + QLatin1Char('/')); - link += fileName; - aggregate->setObsoleteLink(link); - - beginSubPage(aggregate, fileName); - generateHeader(title, aggregate, marker); - generateSidebar(); - generateTitle(title, Text(), SmallSubTitle, aggregate, marker); - - out() << "<p><b>The following members of QML type " - << "<a href=\"" << linkForNode(aggregate, nullptr) << "\">" - << protectEnc(aggregate->name()) << "</a>" - << " are deprecated.</b> " - << "They are provided to keep old source code working. " - << "We strongly advise against using them in new code.</p>\n"; - - for (const auto §ion : summary_spv) { - QString ref = registerRef(section->title().toLower()); - out() << "<h2 id=\"" << ref << "\">" << protectEnc(section->title()) << "</h2>\n"; - generateQmlSummary(section->obsoleteMembers(), aggregate, marker); - } - - for (const auto §ion : details_spv) { - out() << "<h2>" << protectEnc(section->title()) << "</h2>\n"; - const NodeVector &members = section->obsoleteMembers(); - for (const auto &member : members) { - generateDetailedQmlMember(member, aggregate, marker); - out() << "<br/>\n"; - } - } - - generateFooter(); - endSubPage(); - return fileName; -} - -void HtmlGenerator::generateClassHierarchy(const Node *relative, NodeMultiMap &classMap) -{ - if (classMap.isEmpty()) - return; - - NodeMap topLevel; - for (const auto &it : classMap) { - auto *classe = static_cast<ClassNode *>(it); - if (classe->baseClasses().isEmpty()) - topLevel.insert(classe->name(), classe); - } - - QStack<NodeMap> stack; - stack.push(topLevel); - - out() << "<ul>\n"; - while (!stack.isEmpty()) { - if (stack.top().isEmpty()) { - stack.pop(); - out() << "</ul>\n"; - } else { - ClassNode *child = static_cast<ClassNode *>(*stack.top().begin()); - out() << "<li>"; - generateFullName(child, relative); - out() << "</li>\n"; - stack.top().erase(stack.top().begin()); - - NodeMap newTop; - const auto derivedClasses = child->derivedClasses(); - for (const RelatedClass &d : derivedClasses) { - if (d.m_node && d.m_node->isInAPI()) - newTop.insert(d.m_node->name(), d.m_node); - } - if (!newTop.isEmpty()) { - stack.push(newTop); - out() << "<ul>\n"; - } - } - } -} - -/*! - Outputs an annotated list of the nodes in \a unsortedNodes. - A two-column table is output. - */ -void HtmlGenerator::generateAnnotatedList(const Node *relative, CodeMarker *marker, - const NodeList &unsortedNodes) -{ - if (unsortedNodes.isEmpty() || relative == nullptr) - return; - - NodeMultiMap nmm; - bool allInternal = true; - for (auto *node : unsortedNodes) { - if (!node->isInternal() && !node->isDeprecated()) { - allInternal = false; - nmm.insert(node->fullName(relative), node); - } - } - if (allInternal) - return; - out() << "<div class=\"table\"><table class=\"annotated\">\n"; - int row = 0; - NodeList nodes = nmm.values(); - std::sort(nodes.begin(), nodes.end(), Node::nodeNameLessThan); - - for (const auto *node : qAsConst(nodes)) { - if (++row % 2 == 1) - out() << "<tr class=\"odd topAlign\">"; - else - out() << "<tr class=\"even topAlign\">"; - out() << "<td class=\"tblName\"><p>"; - generateFullName(node, relative); - out() << "</p></td>"; - - if (!node->isTextPageNode()) { - Text brief = node->doc().trimmedBriefText(node->name()); - if (!brief.isEmpty()) { - out() << "<td class=\"tblDescr\"><p>"; - generateText(brief, node, marker); - out() << "</p></td>"; - } else if (!node->reconstitutedBrief().isEmpty()) { - out() << "<td class=\"tblDescr\"><p>"; - out() << node->reconstitutedBrief(); - out() << "</p></td>"; - } - } else { - out() << "<td class=\"tblDescr\"><p>"; - if (!node->reconstitutedBrief().isEmpty()) { - out() << node->reconstitutedBrief(); - } else - out() << protectEnc(node->doc().briefText().toString()); - out() << "</p></td>"; - } - out() << "</tr>\n"; - } - out() << "</table></div>\n"; -} - -/*! - Outputs a series of annotated lists from the nodes in \a nmm, - divided into sections based by the key names in the multimap. - */ -void HtmlGenerator::generateAnnotatedLists(const Node *relative, CodeMarker *marker, - const NodeMultiMap &nmm) -{ - const auto &uniqueKeys = nmm.uniqueKeys(); - for (const QString &name : uniqueKeys) { - if (!name.isEmpty()) { - out() << "<h2 id=\"" << registerRef(name.toLower()) << "\">" << protectEnc(name) - << "</h2>\n"; - } - generateAnnotatedList(relative, marker, nmm.values(name)); - } -} - -/*! - This function finds the common prefix of the names of all - the classes in the class map \a nmm and then generates a - compact list of the class names alphabetized on the part - of the name not including the common prefix. You can tell - the function to use \a commonPrefix as the common prefix, - but normally you let it figure it out itself by looking at - the name of the first and last classes in the class map - \a nmm. - */ -void HtmlGenerator::generateCompactList(ListType listType, const Node *relative, - const NodeMultiMap &nmm, bool includeAlphabet, - const QString &commonPrefix) -{ - if (nmm.isEmpty()) - return; - - const int NumParagraphs = 37; // '0' to '9', 'A' to 'Z', '_' - qsizetype commonPrefixLen = commonPrefix.length(); - - /* - Divide the data into 37 paragraphs: 0, ..., 9, A, ..., Z, - underscore (_). QAccel will fall in paragraph 10 (A) and - QXtWidget in paragraph 33 (X). This is the only place where we - assume that NumParagraphs is 37. Each paragraph is a NodeMultiMap. - */ - NodeMultiMap paragraph[NumParagraphs + 1]; - QString paragraphName[NumParagraphs + 1]; - QSet<char> usedParagraphNames; - - for (auto c = nmm.constBegin(); c != nmm.constEnd(); ++c) { - QStringList pieces = c.key().split("::"); - int idx = commonPrefixLen; - if (idx > 0 && !pieces.last().startsWith(commonPrefix, Qt::CaseInsensitive)) - idx = 0; - QString last = pieces.last().toLower(); - QString key = last.mid(idx); - - int paragraphNr = NumParagraphs - 1; - - if (key[0].digitValue() != -1) { - paragraphNr = key[0].digitValue(); - } else if (key[0] >= QLatin1Char('a') && key[0] <= QLatin1Char('z')) { - paragraphNr = 10 + key[0].unicode() - 'a'; - } - - paragraphName[paragraphNr] = key[0].toUpper(); - usedParagraphNames.insert(key[0].toLower().cell()); - paragraph[paragraphNr].insert(last, c.value()); - } - - /* - Each paragraph j has a size: paragraph[j].count(). In the - discussion, we will assume paragraphs 0 to 5 will have sizes - 3, 1, 4, 1, 5, 9. - - We now want to compute the paragraph offset. Paragraphs 0 to 6 - start at offsets 0, 3, 4, 8, 9, 14, 23. - */ - qsizetype paragraphOffset[NumParagraphs + 1]; // 37 + 1 - paragraphOffset[0] = 0; - for (int i = 0; i < NumParagraphs; i++) // i = 0..36 - paragraphOffset[i + 1] = paragraphOffset[i] + paragraph[i].count(); - - /* - Output the alphabet as a row of links. - */ - if (includeAlphabet) { - out() << "<p class=\"centerAlign functionIndex\"><b>"; - for (int i = 0; i < 26; i++) { - QChar ch('a' + i); - if (usedParagraphNames.contains(char('a' + i))) - out() << QString("<a href=\"#%1\">%2</a> ").arg(ch).arg(ch.toUpper()); - } - out() << "</b></p>\n"; - } - - /* - Output a <div> element to contain all the <dl> elements. - */ - out() << "<div class=\"flowListDiv\">\n"; - m_numTableRows = 0; - - int curParNr = 0; - int curParOffset = 0; - QString previousName; - bool multipleOccurrences = false; - - for (int i = 0; i < nmm.count(); i++) { - while ((curParNr < NumParagraphs) && (curParOffset == paragraph[curParNr].count())) { - ++curParNr; - curParOffset = 0; - } - - /* - Starting a new paragraph means starting a new <dl>. - */ - if (curParOffset == 0) { - if (i > 0) - out() << "</dl>\n"; - if (++m_numTableRows % 2 == 1) - out() << "<dl class=\"flowList odd\">"; - else - out() << "<dl class=\"flowList even\">"; - out() << "<dt class=\"alphaChar\""; - if (includeAlphabet) - out() << QString(" id=\"%1\"").arg(paragraphName[curParNr][0].toLower()); - out() << "><b>" << paragraphName[curParNr] << "</b></dt>\n"; - } - - /* - Output a <dd> for the current offset in the current paragraph. - */ - out() << "<dd>"; - if ((curParNr < NumParagraphs) && !paragraphName[curParNr].isEmpty()) { - NodeMultiMap::Iterator it; - NodeMultiMap::Iterator next; - it = paragraph[curParNr].begin(); - for (int j = 0; j < curParOffset; j++) - ++it; - - if (listType == Generic) { - /* - Previously, we used generateFullName() for this, but we - require some special formatting. - */ - out() << "<a href=\"" << linkForNode(it.value(), relative) << "\">"; - } else if (listType == Obsolete) { - QString fileName = fileBase(it.value()) + "-obsolete." + fileExtension(); - QString link; - if (useOutputSubdirs()) { - link = QString("../" + it.value()->outputSubdirectory() + QLatin1Char('/')); - } - link += fileName; - out() << "<a href=\"" << link << "\">"; - } - - QStringList pieces; - if (it.value()->isQmlType() || it.value()->isJsType()) { - QString name = it.value()->name(); - next = it; - ++next; - if (name != previousName) - multipleOccurrences = false; - if ((next != paragraph[curParNr].end()) && (name == next.value()->name())) { - multipleOccurrences = true; - previousName = name; - } - if (multipleOccurrences) - name += ": " + it.value()->tree()->camelCaseModuleName(); - pieces << name; - } else - pieces = it.value()->fullName(relative).split("::"); - out() << protectEnc(pieces.last()); - out() << "</a>"; - if (pieces.size() > 1) { - out() << " ("; - generateFullName(it.value()->parent(), relative); - out() << ')'; - } - } - out() << "</dd>\n"; - curParOffset++; - } - if (nmm.count() > 0) - out() << "</dl>\n"; - - out() << "</div>\n"; -} - -void HtmlGenerator::generateFunctionIndex(const Node *relative) -{ - out() << "<p class=\"centerAlign functionIndex\"><b>"; - for (int i = 0; i < 26; i++) { - QChar ch('a' + i); - out() << QString("<a href=\"#%1\">%2</a> ").arg(ch).arg(ch.toUpper()); - } - out() << "</b></p>\n"; - - char nextLetter = 'a'; - - out() << "<ul>\n"; - NodeMapMap &funcIndex = m_qdb->getFunctionIndex(); - for (auto fnMap = funcIndex.constBegin(); fnMap != funcIndex.constEnd(); ++fnMap) { - const QString &key = fnMap.key(); - const QChar firstLetter = key.isEmpty() ? QChar('A') : key.front(); - Q_ASSERT_X(firstLetter.unicode() < 256, "generateFunctionIndex", - "Only valid C++ identifiers were expected"); - const char currentLetter = firstLetter.isLower() ? firstLetter.unicode() : nextLetter - 1; - - if (currentLetter < nextLetter) { - out() << "<li>"; - } else { - // TODO: This is not covered by our tests - while (nextLetter < currentLetter) - out() << QStringLiteral("<li id=\"%1\"></li>").arg(nextLetter++); - Q_ASSERT(nextLetter == currentLetter); - out() << QStringLiteral("<li id=\"%1\">").arg(nextLetter++); - } - out() << protectEnc(key) << ':'; - - for (auto it = (*fnMap).constBegin(); it != (*fnMap).constEnd(); ++it) { - out() << ' '; - generateFullName((*it)->parent(), relative, *it); - } - out() << "</li>\n"; - } - while (nextLetter <= 'z') - out() << QStringLiteral("<li id=\"%1\"></li>").arg(nextLetter++); - out() << "</ul>\n"; -} - -void HtmlGenerator::generateLegaleseList(const Node *relative, CodeMarker *marker) -{ - TextToNodeMap &legaleseTexts = m_qdb->getLegaleseTexts(); - for (auto it = legaleseTexts.cbegin(), end = legaleseTexts.cend(); it != end; ++it) { - Text text = it.key(); - generateText(text, relative, marker); - out() << "<ul>\n"; - do { - out() << "<li>"; - generateFullName(it.value(), relative); - out() << "</li>\n"; - ++it; - } while (it != legaleseTexts.constEnd() && it.key() == text); - out() << "</ul>\n"; - } -} - -void HtmlGenerator::generateQmlItem(const Node *node, const Node *relative, CodeMarker *marker, - bool summary) -{ - QString marked = marker->markedUpQmlItem(node, summary); - QRegularExpression templateTag("(<[^@>]*>)"); - auto match = templateTag.match(marked); - if (match.hasMatch()) { - QString contents = protectEnc(match.captured(1)); - marked.replace(match.capturedStart(1), match.capturedLength(1), contents); - } - - // Look for the _ character in the member name followed by a number (or n): - // this is intended to be rendered as a subscript. - marked.replace(QRegularExpression("<@param>([a-z]+)_([0-9]+|n)</@param>"), "<i>\\1<sub>\\2</sub></i>"); - // Replace some markup by HTML tags. Do both the opening and the closing tag - // in one go (instead of <@param> and </@param> separately, for instance). - marked.replace("@param>", "i>"); - - marked.replace("@extra>", "code>"); - - if (summary) { - marked.remove("<@name>"); - marked.remove("</@name>"); - marked.remove("<@type>"); - marked.remove("</@type>"); - } - out() << highlightedCode(marked, relative, false, Node::QML); -} - -/*! - This function generates a simple bullet list for the members - of collection node \a {cn}. The collection node must be a group - and must not be empty. If it is empty, nothing is output, and - false is returned. Otherewise, the list is generated and true is returned. - */ -bool HtmlGenerator::generateGroupList(CollectionNode *cn) -{ - m_qdb->mergeCollections(cn); - if (cn->members().isEmpty()) - return false; - out() << "<ul>\n"; - const auto members = cn->members(); - for (const auto *node : members) { - out() << "<li>" - << "<a href=\"#" << Doc::canonicalTitle(node->title()) << "\">" << node->title() - << "</a></li>\n"; - } - out() << "</ul>\n"; - return true; -} - -void HtmlGenerator::generateList(const Node *relative, CodeMarker *marker, const QString &selector) -{ - CNMap cnm; - Node::NodeType type = Node::NoType; - if (selector == QLatin1String("overviews")) - type = Node::Group; - else if (selector == QLatin1String("cpp-modules")) - type = Node::Module; - else if (selector == QLatin1String("qml-modules")) - type = Node::QmlModule; - else if (selector == QLatin1String("js-modules")) - type = Node::JsModule; - if (type != Node::NoType) { - NodeList nodeList; - m_qdb->mergeCollections(type, cnm, relative); - const auto collectionList = cnm.values(); - nodeList.reserve(collectionList.size()); - for (auto *collectionNode : collectionList) - nodeList.append(collectionNode); - generateAnnotatedList(relative, marker, nodeList); - } else { - /* - \generatelist {selector} is only allowed in a - comment where the topic is \group, \module, - \qmlmodule, or \jsmodule - */ - if (relative && !relative->isCollectionNode()) { - relative->doc().location().warning( - QStringLiteral("\\generatelist {%1} is only allowed in \\group, " - "\\module, \\qmlmodule, and \\jsmodule comments.") - .arg(selector)); - return; - } - auto *node = const_cast<Node *>(relative); - auto *collectionNode = static_cast<CollectionNode *>(node); - m_qdb->mergeCollections(collectionNode); - generateAnnotatedList(collectionNode, marker, collectionNode->members()); - } -} - -void HtmlGenerator::generateSection(const NodeVector &nv, const Node *relative, CodeMarker *marker) -{ - bool alignNames = true; - if (!nv.isEmpty()) { - bool twoColumn = false; - if (nv.first()->isProperty()) { - twoColumn = (nv.count() >= 5); - alignNames = false; - } - if (alignNames) { - out() << "<div class=\"table\"><table class=\"alignedsummary\">\n"; - } else { - if (twoColumn) - out() << "<div class=\"table\"><table class=\"propsummary\">\n" - << "<tr><td class=\"topAlign\">"; - out() << "<ul>\n"; - } - - int i = 0; - for (const auto &member : nv) { - if (member->access() == Access::Private) - continue; - - if (alignNames) { - out() << "<tr><td class=\"memItemLeft rightAlign topAlign\"> "; - } else { - if (twoColumn && i == (nv.count() + 1) / 2) - out() << "</ul></td><td class=\"topAlign\"><ul>\n"; - out() << "<li class=\"fn\">"; - } - - generateSynopsis(member, relative, marker, Section::Summary, alignNames); - if (alignNames) - out() << "</td></tr>\n"; - else - out() << "</li>\n"; - i++; - } - if (alignNames) - out() << "</table></div>\n"; - else { - out() << "</ul>\n"; - if (twoColumn) - out() << "</td></tr>\n</table></div>\n"; - } - } -} - -void HtmlGenerator::generateSectionList(const Section §ion, const Node *relative, - CodeMarker *marker, Section::Status status) -{ - bool alignNames = true; - const NodeVector &members = - (status == Section::Obsolete ? section.obsoleteMembers() : section.members()); - if (!members.isEmpty()) { - bool hasPrivateSignals = false; - bool isInvokable = false; - bool twoColumn = false; - if (section.style() == Section::AllMembers) { - alignNames = false; - twoColumn = (members.count() >= 16); - } else if (members.first()->isProperty()) { - twoColumn = (members.count() >= 5); - alignNames = false; - } - if (alignNames) { - out() << "<div class=\"table\"><table class=\"alignedsummary\">\n"; - } else { - if (twoColumn) - out() << "<div class=\"table\"><table class=\"propsummary\">\n" - << "<tr><td class=\"topAlign\">"; - out() << "<ul>\n"; - } - - int i = 0; - for (const auto &member : members) { - if (member->access() == Access::Private) - continue; - - if (alignNames) { - out() << "<tr><td class=\"memItemLeft topAlign rightAlign\"> "; - } else { - if (twoColumn && i == (members.count() + 1) / 2) - out() << "</ul></td><td class=\"topAlign\"><ul>\n"; - out() << "<li class=\"fn\">"; - } - - QString prefix; - const QStringList &keys = section.keys(status); - if (!keys.isEmpty()) { - prefix = keys.at(i).mid(1); - prefix = prefix.left(keys.at(i).indexOf("::") + 1); - } - generateSynopsis(member, relative, marker, section.style(), alignNames, &prefix); - if (member->isFunction()) { - const auto *fn = static_cast<const FunctionNode *>(member); - if (fn->isPrivateSignal()) { - hasPrivateSignals = true; - if (alignNames) - out() << "</td><td class=\"memItemRight bottomAlign\">[see note below]"; - } else if (fn->isInvokable()) { - isInvokable = true; - if (alignNames) - out() << "</td><td class=\"memItemRight bottomAlign\">[see note below]"; - } - } - if (alignNames) - out() << "</td></tr>\n"; - else - out() << "</li>\n"; - i++; - } - if (alignNames) - out() << "</table></div>\n"; - else { - out() << "</ul>\n"; - if (twoColumn) - out() << "</td></tr>\n</table></div>\n"; - } - if (alignNames) { - if (hasPrivateSignals) - generateAddendum(relative, Generator::PrivateSignal, marker); - if (isInvokable) - generateAddendum(relative, Generator::Invokable, marker); - } - } - - if (status != Section::Obsolete && section.style() == Section::Summary - && !section.inheritedMembers().isEmpty()) { - out() << "<ul>\n"; - generateSectionInheritedList(section, relative); - out() << "</ul>\n"; - } -} - -void HtmlGenerator::generateSectionInheritedList(const Section §ion, const Node *relative) -{ - const QList<QPair<Aggregate *, int>> &inheritedMembers = section.inheritedMembers(); - for (const auto &member : inheritedMembers) { - out() << "<li class=\"fn\">"; - out() << member.second << ' '; - if (member.second == 1) { - out() << section.singular(); - } else { - out() << section.plural(); - } - out() << " inherited from <a href=\"" << fileName(member.first) << '#' - << Generator::cleanRef(section.title().toLower()) << "\">" - << protectEnc(member.first->plainFullName(relative)) << "</a></li>\n"; - } -} - -void HtmlGenerator::generateSynopsis(const Node *node, const Node *relative, CodeMarker *marker, - Section::Style style, bool alignNames, const QString *prefix) -{ - QString marked = marker->markedUpSynopsis(node, relative, style); - - if (prefix) - marked.prepend(*prefix); - QRegularExpression templateTag("(<[^@>]*>)"); - auto match = templateTag.match(marked); - if (match.hasMatch()) { - QString contents = protectEnc(match.captured(1)); - marked.replace(match.capturedStart(1), match.capturedLength(1), contents); - } - - marked.replace(QRegularExpression("<@param>([a-z]+)_([1-9n])</@param>"), "<i>\\1<sub>\\2</sub></i>"); - marked.replace("<@param>", "<i>"); - marked.replace("</@param>", "</i>"); - - if (style == Section::Summary) { - marked.remove("<@name>"); // was "<b>" - marked.remove("</@name>"); // was "</b>" - } - - if (style == Section::AllMembers) { - QRegularExpression extraRegExp("<@extra>.*</@extra>", QRegularExpression::InvertedGreedinessOption); - marked.remove(extraRegExp); - } else { - marked.replace("<@extra>", "<code>"); - marked.replace("</@extra>", "</code>"); - } - - if (style != Section::Details) { - marked.remove("<@type>"); - marked.remove("</@type>"); - } - - out() << highlightedCode(marked, relative, alignNames); -} - -QString HtmlGenerator::highlightedCode(const QString &markedCode, const Node *relative, - bool alignNames, Node::Genus genus) -{ - QString src = markedCode; - QString html; - html.reserve(src.size()); - QStringView arg; - QStringView par1; - - const QChar charLangle = '<'; - const QChar charAt = '@'; - - static const QString typeTag("type"); - static const QString headerTag("headerfile"); - static const QString funcTag("func"); - static const QString linkTag("link"); - - // replace all <@link> tags: "(<@link node=\"([^\"]+)\">).*(</@link>)" - // replace all <@func> tags: "(<@func target=\"([^\"]*)\">)(.*)(</@func>)" - // replace all "(<@(type|headerfile)(?: +[^>]*)?>)(.*)(</@\\2>)" tags - bool done = false; - for (int i = 0, srcSize = src.size(); i < srcSize;) { - if (src.at(i) == charLangle && src.at(i + 1) == charAt) { - if (alignNames && !done) { - html += QLatin1String("</td><td class=\"memItemRight bottomAlign\">"); - done = true; - } - i += 2; - if (parseArg(src, linkTag, &i, srcSize, &arg, &par1)) { - html += QLatin1String("<b>"); - const Node *n = CodeMarker::nodeForString(par1.toString()); - QString link = linkForNode(n, relative); - addLink(link, arg, &html); - html += QLatin1String("</b>"); - } else if (parseArg(src, funcTag, &i, srcSize, &arg, &par1)) { - const FunctionNode *fn = m_qdb->findFunctionNode(par1.toString(), relative, genus); - QString link = linkForNode(fn, relative); - addLink(link, arg, &html); - par1 = QStringView(); - } else if (parseArg(src, typeTag, &i, srcSize, &arg, &par1)) { - par1 = QStringView(); - const Node *n = m_qdb->findTypeNode(arg.toString(), relative, genus); - html += QLatin1String("<span class=\"type\">"); - if (n && (n->isQmlBasicType() || n->isJsBasicType())) { - if (relative && (relative->genus() == n->genus() || genus == n->genus())) - addLink(linkForNode(n, relative), arg, &html); - else - html += arg; - } else - addLink(linkForNode(n, relative), arg, &html); - html += QLatin1String("</span>"); - } else if (parseArg(src, headerTag, &i, srcSize, &arg, &par1)) { - par1 = QStringView(); - if (arg.startsWith(QLatin1Char('&'))) - html += arg; - else { - const Node *n = m_qdb->findNodeForInclude(QStringList(arg.toString())); - if (n && n != relative) - addLink(linkForNode(n, relative), arg, &html); - else - html += arg; - } - } else { - html += charLangle; - html += charAt; - } - } else { - html += src.at(i++); - } - } - - // replace all - // "<@comment>" -> "<span class=\"comment\">"; - // "<@preprocessor>" -> "<span class=\"preprocessor\">"; - // "<@string>" -> "<span class=\"string\">"; - // "<@char>" -> "<span class=\"char\">"; - // "<@number>" -> "<span class=\"number\">"; - // "<@op>" -> "<span class=\"operator\">"; - // "<@type>" -> "<span class=\"type\">"; - // "<@name>" -> "<span class=\"name\">"; - // "<@keyword>" -> "<span class=\"keyword\">"; - // "</@(?:comment|preprocessor|string|char|number|op|type|name|keyword)>" -> "</span>" - src = html; - html = QString(); - html.reserve(src.size()); - static const QLatin1String spanTags[] = { - QLatin1String("comment>"), QLatin1String("<span class=\"comment\">"), - QLatin1String("preprocessor>"), QLatin1String("<span class=\"preprocessor\">"), - QLatin1String("string>"), QLatin1String("<span class=\"string\">"), - QLatin1String("char>"), QLatin1String("<span class=\"char\">"), - QLatin1String("number>"), QLatin1String("<span class=\"number\">"), - QLatin1String("op>"), QLatin1String("<span class=\"operator\">"), - QLatin1String("type>"), QLatin1String("<span class=\"type\">"), - QLatin1String("name>"), QLatin1String("<span class=\"name\">"), - QLatin1String("keyword>"), QLatin1String("<span class=\"keyword\">") - }; - int nTags = 9; - // Update the upper bound of k in the following code to match the length - // of the above array. - for (int i = 0, n = src.size(); i < n;) { - if (src.at(i) == QLatin1Char('<')) { - if (src.at(i + 1) == QLatin1Char('@')) { - i += 2; - bool handled = false; - for (int k = 0; k != nTags; ++k) { - const QLatin1String &tag = spanTags[2 * k]; - if (i + tag.size() <= src.length() && tag == QStringView(src).mid(i, tag.size())) { - html += spanTags[2 * k + 1]; - i += tag.size(); - handled = true; - break; - } - } - if (!handled) { - // drop 'our' unknown tags (the ones still containing '@') - while (i < n && src.at(i) != QLatin1Char('>')) - ++i; - ++i; - } - continue; - } else if (src.at(i + 1) == QLatin1Char('/') && src.at(i + 2) == QLatin1Char('@')) { - i += 3; - bool handled = false; - for (int k = 0; k != nTags; ++k) { - const QLatin1String &tag = spanTags[2 * k]; - if (i + tag.size() <= src.length() && tag == QStringView(src).mid(i, tag.size())) { - html += QLatin1String("</span>"); - i += tag.size(); - handled = true; - break; - } - } - if (!handled) { - // drop 'our' unknown tags (the ones still containing '@') - while (i < n && src.at(i) != QLatin1Char('>')) - ++i; - ++i; - } - continue; - } - } - html += src.at(i); - ++i; - } - return html; -} - -void HtmlGenerator::generateLink(const Atom *atom, CodeMarker *marker) -{ - auto match = m_funcLeftParen.match(atom->string()); - if (match.hasMatch() && marker->recognizeLanguage("Cpp")) { - // hack for C++: move () outside of link - qsizetype k = match.capturedStart(1); - out() << protectEnc(atom->string().left(k)); - if (m_link.isEmpty()) { - if (showBrokenLinks) - out() << "</i>"; - } else { - out() << "</a>"; - } - m_inLink = false; - out() << protectEnc(atom->string().mid(k)); - } else { - out() << protectEnc(atom->string()); - } -} - -QString HtmlGenerator::protectEnc(const QString &string) -{ - return protect(string); -} - -QString HtmlGenerator::protect(const QString &string) -{ -#define APPEND(x) \ - if (html.isEmpty()) { \ - html = string; \ - html.truncate(i); \ - } \ - html += (x); - - QString html; - qsizetype n = string.length(); - - for (int i = 0; i < n; ++i) { - QChar ch = string.at(i); - - if (ch == QLatin1Char('&')) { - APPEND("&"); - } else if (ch == QLatin1Char('<')) { - APPEND("<"); - } else if (ch == QLatin1Char('>')) { - APPEND(">"); - } else if (ch == QLatin1Char('"')) { - APPEND("""); - } else if ((ch == QLatin1Char('*') && i + 1 < n && string.at(i) == QLatin1Char('/')) - || (ch == QLatin1Char('.') && i > 2 && string.at(i - 2) == QLatin1Char('.'))) { - // we escape '*/' and the last dot in 'e.g.' and 'i.e.' for the Javadoc generator - APPEND("&#x"); - html += QString::number(ch.unicode(), 16); - html += QLatin1Char(';'); - } else { - if (!html.isEmpty()) - html += ch; - } - } - - if (!html.isEmpty()) - return html; - return string; - -#undef APPEND -} - -QString HtmlGenerator::fileBase(const Node *node) const -{ - QString result = Generator::fileBase(node); - if (!node->isAggregate() && node->isDeprecated()) - result += QLatin1String("-obsolete"); - return result; -} - -QString HtmlGenerator::fileName(const Node *node) -{ - if (node->isExternalPage()) - return node->name(); - return Generator::fileName(node); -} - -void HtmlGenerator::generateFullName(const Node *apparentNode, const Node *relative, - const Node *actualNode) -{ - if (actualNode == nullptr) - actualNode = apparentNode; - bool link = !linkForNode(actualNode, relative).isEmpty(); - if (link) { - out() << "<a href=\"" << linkForNode(actualNode, relative); - if (actualNode->isDeprecated()) - out() << "\" class=\"obsolete"; - out() << "\">"; - } - out() << protectEnc(apparentNode->fullName(relative)); - if (link) - out() << "</a>"; -} - -void HtmlGenerator::generateDetailedMember(const Node *node, const PageNode *relative, - CodeMarker *marker) -{ - const EnumNode *etn; - generateExtractionMark(node, MemberMark); - QString nodeRef = nullptr; - if (node->isSharedCommentNode()) { - const auto *scn = reinterpret_cast<const SharedCommentNode *>(node); - const QList<Node *> &collective = scn->collective(); - if (collective.size() > 1) - out() << "<div class=\"fngroup\">\n"; - for (const auto *sharedNode : collective) { - nodeRef = refForNode(sharedNode); - out() << R"(<h3 class="fn fngroupitem" id=")" << nodeRef << "\">"; - generateSynopsis(sharedNode, relative, marker, Section::Details); - out() << "</h3>"; - } - if (collective.size() > 1) - out() << "</div>"; - out() << '\n'; - } else { - nodeRef = refForNode(node); - if (node->isEnumType() && (etn = static_cast<const EnumNode *>(node))->flagsType()) { - out() << R"(<h3 class="flags" id=")" << nodeRef << "\">"; - generateSynopsis(etn, relative, marker, Section::Details); - out() << "<br/>"; - generateSynopsis(etn->flagsType(), relative, marker, Section::Details); - out() << "</h3>\n"; - } else { - out() << R"(<h3 class="fn" id=")" << nodeRef << "\">"; - generateSynopsis(node, relative, marker, Section::Details); - out() << "</h3>" << '\n'; - } - } - - generateStatus(node, marker); - generateBody(node, marker); - generateOverloadedSignal(node, marker); - generateThreadSafeness(node, marker); - generateSince(node, marker); - - if (node->isProperty()) { - const auto property = static_cast<const PropertyNode *>(node); - if (property->propertyType() == PropertyNode::Standard) { - Section section(Section::Accessors, Section::Active); - - section.appendMembers(property->getters().toVector()); - section.appendMembers(property->setters().toVector()); - section.appendMembers(property->resetters().toVector()); - - if (!section.members().isEmpty()) { - out() << "<p><b>Access functions:</b></p>\n"; - generateSectionList(section, node, marker); - } - - Section notifiers(Section::Accessors, Section::Active); - notifiers.appendMembers(property->notifiers().toVector()); - - if (!notifiers.members().isEmpty()) { - out() << "<p><b>Notifier signal:</b></p>\n"; - generateSectionList(notifiers, node, marker); - } - } - } else if (node->isEnumType()) { - const auto *enumTypeNode = static_cast<const EnumNode *>(node); - if (enumTypeNode->flagsType()) { - out() << "<p>The " << protectEnc(enumTypeNode->flagsType()->name()) - << " type is a typedef for " - << "<a href=\"" << m_qflagsHref << "\">QFlags</a><" - << protectEnc(enumTypeNode->name()) << ">. It stores an OR combination of " - << protectEnc(enumTypeNode->name()) << " values.</p>\n"; - } - } - generateAlsoList(node, marker); - generateExtractionMark(node, EndMark); -} - -/*! - This version of the function is called when outputting the link - to an example file or example image, where the \a link is known - to be correct. - */ -void HtmlGenerator::beginLink(const QString &link) -{ - m_link = link; - if (m_link.isEmpty()) { - if (showBrokenLinks) - out() << "<i>"; - } - out() << "<a href=\"" << m_link << "\">"; - m_inLink = true; -} - -void HtmlGenerator::beginLink(const QString &link, const Node *node, const Node *relative) -{ - m_link = link; - if (m_link.isEmpty()) { - if (showBrokenLinks) - out() << "<i>"; - } else if (node == nullptr || (relative != nullptr && node->status() == relative->status())) - out() << "<a href=\"" << m_link << "\">"; - else if (node->isDeprecated()) - out() << "<a href=\"" << m_link << "\" class=\"obsolete\">"; - else - out() << "<a href=\"" << m_link << "\">"; - m_inLink = true; -} - -void HtmlGenerator::endLink() -{ - if (m_inLink) { - if (m_link.isEmpty() && showBrokenLinks) { - out() << "</i>"; - } else { - out() << "</a>"; - } - } - m_inLink = false; -} - -/*! - Generates the summary list for the \a members. Only used for - sections of QML element documentation. - */ -void HtmlGenerator::generateQmlSummary(const NodeVector &members, const Node *relative, - CodeMarker *marker) -{ - if (!members.isEmpty()) { - out() << "<ul>\n"; - for (const auto &member : members) { - out() << "<li class=\"fn\">"; - generateQmlItem(member, relative, marker, true); - if (member->isPropertyGroup()) { - const auto *scn = static_cast<const SharedCommentNode *>(member); - if (scn->count() > 0) { - out() << "<ul>\n"; - const QList<Node *> &sharedNodes = scn->collective(); - for (const auto &node : sharedNodes) { - if (node->isQmlProperty() || node->isJsProperty()) { - out() << "<li class=\"fn\">"; - generateQmlItem(node, relative, marker, true); - out() << "</li>\n"; - } - } - out() << "</ul>\n"; - } - } - out() << "</li>\n"; - } - out() << "</ul>\n"; - } -} - -/*! - Outputs the html detailed documentation for a section - on a QML element reference page. - */ -void HtmlGenerator::generateDetailedQmlMember(Node *node, const Aggregate *relative, - CodeMarker *marker) -{ - generateExtractionMark(node, MemberMark); - - QString qmlItemHeader("<div class=\"qmlproto\">\n" - "<div class=\"table\"><table class=\"qmlname\">\n"); - - QString qmlItemStart("<tr valign=\"top\" class=\"odd\" id=\"%1\">\n" - "<td class=\"%2\"><p>\n"); - QString qmlItemEnd("</p></td></tr>\n"); - - QString qmlItemFooter("</table></div></div>\n"); - - std::function<void(QmlPropertyNode *)> generateQmlProperty = [&](QmlPropertyNode *n) { - out() << qmlItemStart.arg(refForNode(n), "tblQmlPropNode"); - - if (!n->isReadOnlySet() && n->declarativeCppNode()) - n->markReadOnly(!n->isWritable()); - - QStringList extra; - if (n->isDefault()) - extra << "default"; - else if (n->isReadOnly()) - extra << "read-only"; - else if (n->isRequired()) - extra << "required"; - else if (!n->defaultValue().isEmpty()) { - extra << "default: " + n->defaultValue(); - } - - if (!n->since().isEmpty()) { - if (!extra.isEmpty()) - extra.last().append(','); - extra << "since " + n->since(); - } - - if (!extra.isEmpty()) - out() << QString("<span class=\"qmlextra\">[%1] </span>") - .arg(extra.join(QLatin1Char(' '))); - - generateQmlItem(n, relative, marker, false); - out() << qmlItemEnd; - }; - - std::function<void(Node *)> generateQmlMethod = [&](Node *n) { - out() << qmlItemStart.arg(refForNode(n), "tblQmlFuncNode"); - generateSynopsis(n, relative, marker, Section::Details, false); - out() << qmlItemEnd; - }; - - out() << "<div class=\"qmlitem\">"; - if (node->isPropertyGroup()) { - const auto *scn = static_cast<const SharedCommentNode *>(node); - out() << qmlItemHeader; - if (!scn->name().isEmpty()) { - const QString nodeRef = refForNode(scn); - out() << R"(<tr valign="top" class="even" id=")" << nodeRef << "\">"; - out() << "<th class=\"centerAlign\"><p>"; - out() << "<b>" << scn->name() << " group</b>"; - out() << "</p></th></tr>\n"; - } - const QList<Node *> sharedNodes = scn->collective(); - for (const auto &sharedNode : sharedNodes) { - if (sharedNode->isQmlProperty() || sharedNode->isJsProperty()) - generateQmlProperty(static_cast<QmlPropertyNode *>(sharedNode)); - } - out() << qmlItemFooter; - } else if (node->isQmlProperty() || node->isJsProperty()) { - out() << qmlItemHeader; - generateQmlProperty(static_cast<QmlPropertyNode *>(node)); - out() << qmlItemFooter; - } else if (node->isSharedCommentNode()) { - const auto *scn = reinterpret_cast<const SharedCommentNode *>(node); - const QList<Node *> &sharedNodes = scn->collective(); - if (sharedNodes.size() > 1) - out() << "<div class=\"fngroup\">\n"; - out() << qmlItemHeader; - for (const auto &sharedNode : sharedNodes) { - if (sharedNode->isFunction(Node::QML) || sharedNode->isFunction(Node::JS)) - generateQmlMethod(sharedNode); - else if (sharedNode->isQmlProperty() || sharedNode->isJsProperty()) - generateQmlProperty(static_cast<QmlPropertyNode *>(sharedNode)); - } - out() << qmlItemFooter; - if (sharedNodes.size() > 1) - out() << "</div>"; // fngroup - } else { // assume the node is a method/signal handler - out() << qmlItemHeader; - generateQmlMethod(node); - out() << qmlItemFooter; - } - - out() << "<div class=\"qmldoc\">"; - generateStatus(node, marker); - generateBody(node, marker); - generateThreadSafeness(node, marker); - generateSince(node, marker); - generateAlsoList(node, marker); - out() << "</div></div>"; - generateExtractionMark(node, EndMark); -} - -void HtmlGenerator::generateExtractionMark(const Node *node, ExtractionMarkType markType) -{ - if (markType != EndMark) { - out() << "<!-- $$$" + node->name(); - if (markType == MemberMark) { - if (node->isFunction()) { - const auto *func = static_cast<const FunctionNode *>(node); - if (!func->hasAssociatedProperties()) { - if (func->overloadNumber() == 0) - out() << "[overload1]"; - out() << "$$$" + func->name() + func->parameters().rawSignature().remove(' '); - } - } else if (node->isProperty()) { - out() << "-prop"; - const auto *prop = static_cast<const PropertyNode *>(node); - const NodeList &list = prop->functions(); - for (const auto *propFuncNode : list) { - if (propFuncNode->isFunction()) { - const auto *func = static_cast<const FunctionNode *>(propFuncNode); - out() << "$$$" + func->name() - + func->parameters().rawSignature().remove(' '); - } - } - } else if (node->isEnumType()) { - const auto *enumNode = static_cast<const EnumNode *>(node); - const auto &items = enumNode->items(); - for (const auto &item : items) - out() << "$$$" + item.name(); - } - } else if (markType == BriefMark) { - out() << "-brief"; - } else if (markType == DetailedDescriptionMark) { - out() << "-description"; - } - out() << " -->\n"; - } else { - out() << "<!-- @@@" + node->name() + " -->\n"; - } -} - -QT_END_NAMESPACE |