diff options
Diffstat (limited to 'src/tools/qdoc/htmlgenerator.cpp')
-rw-r--r-- | src/tools/qdoc/htmlgenerator.cpp | 295 |
1 files changed, 238 insertions, 57 deletions
diff --git a/src/tools/qdoc/htmlgenerator.cpp b/src/tools/qdoc/htmlgenerator.cpp index 0f5bf26e71..bc79d5ce7f 100644 --- a/src/tools/qdoc/htmlgenerator.cpp +++ b/src/tools/qdoc/htmlgenerator.cpp @@ -100,8 +100,10 @@ HtmlGenerator::HtmlGenerator() */ HtmlGenerator::~HtmlGenerator() { - if (helpProjectWriter) + if (helpProjectWriter) { delete helpProjectWriter; + helpProjectWriter = 0; + } } /*! @@ -130,6 +132,11 @@ void HtmlGenerator::initializeGenerator(const Config &config) Generator::initializeGenerator(config); obsoleteLinks = config.getBool(CONFIG_OBSOLETELINKS); 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(defaults[i].key, defaults[i].left); @@ -215,7 +222,12 @@ void HtmlGenerator::initializeGenerator(const Config &config) // The following line was changed to fix QTBUG-27798 //codeIndent = config.getInt(CONFIG_CODEINDENT); - helpProjectWriter = new HelpProjectWriter(config, project.toLower() + ".qhp", this); + /* + The help file write should be allocated once and only once + per qdoc execution. + */ + if (helpProjectWriter == 0) + helpProjectWriter = new HelpProjectWriter(config, project.toLower() + ".qhp", this); // Documentation template handling headerScripts = config.getString(HtmlGenerator::format() + Config::dot + CONFIG_HEADERSCRIPTS); @@ -263,6 +275,20 @@ QString HtmlGenerator::format() } /*! + Generate targets for any \keyword commands that were seen + in the qdoc comment for the \a node. + */ +void HtmlGenerator::generateKeywordAnchors(const Node* node) +{ + if (!node->doc().isEmpty()) { + const QList<Atom*>& keywords = node->doc().keywords(); + foreach (Atom* a, keywords) { + out() << "<a name=\"" << Doc::canonicalTitle(a->string()) << "\"></a>"; + } + } +} + +/*! Traverses the current tree generating all the HTML documentation. */ void HtmlGenerator::generateDocs() @@ -270,10 +296,12 @@ void HtmlGenerator::generateDocs() Node* qflags = qdb_->findClassNode(QStringList("QFlags")); if (qflags) qflagsHref_ = linkForNode(qflags,0); - if (!runPrepareOnly()) + if (!preparing()) Generator::generateDocs(); + if (Generator::generating() && Generator::writeQaPages()) + generateQAPage(); - if (!runGenerateOnly()) { + if (!generating()) { QString fileBase = project.toLower().simplified().replace(QLatin1Char(' '), QLatin1Char('-')); qdb_->generateIndex(outputDir() + QLatin1Char('/') + fileBase + ".index", projectUrl, @@ -282,7 +310,7 @@ void HtmlGenerator::generateDocs() true); } - if (!runPrepareOnly()) { + if (!preparing()) { helpProjectWriter->generate(); generateManifestFiles(); /* @@ -293,6 +321,144 @@ void HtmlGenerator::generateDocs() } /*! + Output the module's Quality Assurance page. + */ +void HtmlGenerator::generateQAPage() +{ + NamespaceNode* node = qdb_->primaryTreeRoot(); + beginSubPage(node, "aaa-" + defaultModuleName().toLower() + "-qa-page.html"); + CodeMarker* marker = CodeMarker::markerForFileName(node->location().filePath()); + QString title = "Quality Assurance Page for " + defaultModuleName(); + QString t = "Quality assurance information for checking the " + defaultModuleName() + " documentation."; + generateHeader(title, node, marker); + generateTitle(title, Text() << t, LargeSubTitle, node, marker); + + QStringList strings; + QVector<int> counts; + QString depends = qdb_->getLinkCounts(strings, counts); + if (!strings.isEmpty()) { + t = "Intermodule Link Counts"; + QString ref = registerRef(t); + out() << "<a name=\"" << ref << "\"></a>" << divNavTop << '\n'; + out() << "<h2 id=\"" << ref << "\">" << protectEnc(t) << "</h2>\n"; + out() << "<table class=\"valuelist\"><tr valign=\"top\" " + << "class=\"even\"><th class=\"tblConst\">Destination Module</th>" + << "<th class=\"tblval\">Link Count</th></tr>\n"; + QString fileName; + for (int i = 0; i< strings.size(); ++i) { + fileName = generateLinksToLinksPage(strings.at(i), marker); + out() << "<tr><td class=\"topAlign\"><tt>" + << "<a href=\"" << fileName << "\">" + << strings.at(i) << "</a>" + << "</tt></td><td class=\"topAlign\"><tt>" << counts.at(i) + << "</tt></td></tr>\n"; + } + int count = 0; + fileName = generateLinksToBrokenLinksPage(marker, count); + if (count != 0) { + out() << "<tr><td class=\"topAlign\"><tt>" + << "<a href=\"" << fileName << "\">" + << "Broken Links" << "</a>" + << "</tt></td><td class=\"topAlign\"><tt>" << count + << "</tt></td></tr>\n"; + + } + + out() << "</table>\n"; + t = "The Optimal \"depends\" Variable"; + out() << "<h2>" << protectEnc(t) << "</h2>\n"; + t = "Consider replacing the depends variable in " + defaultModuleName().toLower() + + ".qdocconf with this one, if the two are not identical:"; + out() << "<p>" << protectEnc(t) << "</p>\n"; + out() << "<p>" << protectEnc(depends) << "</p>\n"; + } + generateFooter(); + endSubPage(); +} + +/*! + This function writes an html file containing a list of + links to links that originate in the current module and + go to targets in the specified \a module. The \a marker + is used for the same thing the marker is always used for. + */ +QString HtmlGenerator::generateLinksToLinksPage(const QString& module, CodeMarker* marker) +{ + NamespaceNode* node = qdb_->primaryTreeRoot(); + QString fileName = "aaa-links-to-" + module + ".html"; + beginSubPage(node, fileName); + QString title = "Links from " + defaultModuleName() + " to " + module; + generateHeader(title, node, marker); + generateTitle(title, Text(), SmallSubTitle, node, marker); + out() << "<p>This is a list of links from " << defaultModuleName() + << " to " << module << ". "; + out() << "Click on a link to go to the location of the link. The link is marked "; + out() << "with red asterisks. "; + out() << "Click on the marked link to see if it goes to the right place.</p>\n"; + TargetList* tlist = qdb_->getTargetList(module); + if (tlist) { + out() << "<table class=\"valuelist\"><tr valign=\"top\" class=\"odd\"><th class=\"tblConst\">Link to link...</th><th class=\"tblval\">In file...</th><th class=\"tbldscr\">Somewhere after line number...</th></tr>\n"; + foreach (TargetLoc* t, *tlist) { + // e.g.: <a name="link-8421"></a><a href="layout.html">Layout Management</a> + out() << "<tr><td class=\"topAlign\">"; + out() << "<a href=\"" << t->fileName_ << "#" << t->target_ << "\">"; + out() << t->text_ << "</a></td>"; + out() << "<td class=\"topAlign\">"; + QString f = t->loc_->doc().location().filePath(); + out() << f << "</td>"; + out() << "<td class=\"topAlign\">"; + out() << t->loc_->doc().location().lineNo() << "</td></tr>\n"; + } + out() << "</table>\n"; + } + generateFooter(); + endSubPage(); + return fileName; +} + +/*! + This function writes an html file containing a list of + links to broken links that originate in the current + module and go nowwhere. It returns the name of the file + it generates, and it sets \a count to the number of + broken links that were found. The \a marker is used for + the same thing the marker is always used for. + */ +QString HtmlGenerator::generateLinksToBrokenLinksPage(CodeMarker* marker, int& count) +{ + QString fileName; + NamespaceNode* node = qdb_->primaryTreeRoot(); + TargetList* tlist = qdb_->getTargetList("broken"); + if (tlist && !tlist->isEmpty()) { + count = tlist->size(); + fileName = "aaa-links-to-broken-links.html"; + beginSubPage(node, fileName); + QString title = "Broken links in " + defaultModuleName(); + generateHeader(title, node, marker); + generateTitle(title, Text(), SmallSubTitle, node, marker); + out() << "<p>This is a list of broken links in " << defaultModuleName() << ". "; + out() << "Click on a link to go to the broken link. "; + out() << "The link's target could not be found.</p>\n"; + out() << "<table class=\"valuelist\"><tr valign=\"top\" class=\"odd\"><th class=\"tblConst\">Link to broken link...</th><th class=\"tblval\">In file...</th><th class=\"tbldscr\">Somewhere after line number...</th></tr>\n"; + foreach (TargetLoc* t, *tlist) { + // e.g.: <a name="link-8421"></a><a href="layout.html">Layout Management</a> + out() << "<tr><td class=\"topAlign\">"; + out() << "<a href=\"" << t->fileName_ << "#" << t->target_ << "\">"; + out() << t->text_ << "</a></td>"; + out() << "<td class=\"topAlign\">"; + QString f = t->loc_->doc().location().filePath(); + out() << f << "</td>"; + out() << "<td class=\"topAlign\">"; + out() << t->loc_->doc().location().lineNo() << "</td></tr>\n"; + } + out() << "</table>\n"; + generateFooter(); + endSubPage(); + } + return fileName; +} + +/*! Generate html from an instance of Atom. */ int HtmlGenerator::generateAtom(const Atom *atom, const Node *relative, CodeMarker *marker) @@ -310,6 +476,7 @@ int HtmlGenerator::generateAtom(const Atom *atom, const Node *relative, CodeMark case Atom::AbstractRight: break; case Atom::AutoLink: + case Atom::NavAutoLink: if (!inLink_ && !inContents_ && !inSectionHeading_) { const Node *node = 0; QString link = getAutoLink(atom, relative, &node); @@ -321,9 +488,15 @@ int HtmlGenerator::generateAtom(const Atom *atom, const Node *relative, CodeMark if ((relative->parent() != node) && !relative->isObsolete()) link.clear(); } - if (link.isEmpty()) + if (link.isEmpty()) { out() << protectEnc(atom->string()); + } else { + if (Generator::writeQaPages() && node && (atom->type() != Atom::NavAutoLink)) { + QString text = atom->string(); + QString target = qdb_->getNewLinkTarget(relative, node, outFileName(), text); + out() << "<a id=\"" << Doc::canonicalTitle(target) << "\" class=\"qa-mark\"></a>"; + } beginLink(link, node, relative); generateLink(atom, marker); endLink(); @@ -806,14 +979,31 @@ int HtmlGenerator::generateAtom(const Atom *atom, const Node *relative, CodeMark out() << "<br/>"; break; case Atom::Link: + case Atom::NavLink: { inObsoleteLink = false; const Node *node = 0; QString link = getLink(atom, relative, &node); if (link.isEmpty() && (node != relative) && !noLinkErrors()) { relative->doc().location().warning(tr("Can't link to '%1'").arg(atom->string())); + if (Generator::writeQaPages() && (atom->type() != Atom::NavAutoLink)) { + QString text = atom->next()->next()->string(); + QString target = qdb_->getNewLinkTarget(relative, node, outFileName(), text, true); + out() << "<a id=\"" << Doc::canonicalTitle(target) << "\" class=\"qa-mark\"></a>"; + } } else { + if (Generator::writeQaPages() && node && (atom->type() != Atom::NavLink)) { + QString text = atom->next()->next()->string(); + QString target = qdb_->getNewLinkTarget(relative, node, outFileName(), text); + out() << "<a id=\"" << Doc::canonicalTitle(target) << "\" class=\"qa-mark\"></a>"; + } + /* + mws saw this on 17/10/2014. + Is this correct? Setting node to 0 means the + following test always fails. Did we decide to + no longer warn about linking to obsolete things? + */ node = 0; if (node && node->status() == Node::Obsolete) { if ((relative->parent() != node) && !relative->isObsolete()) { @@ -1126,6 +1316,8 @@ int HtmlGenerator::generateAtom(const Atom *atom, const Node *relative, CodeMark break; case Atom::TableOfContents: break; + case Atom::Keyword: + break; case Atom::Target: out() << "<a name=\"" << Doc::canonicalTitle(atom->string()) << "\"></a>"; break; @@ -1173,8 +1365,10 @@ void HtmlGenerator::generateClassLikeNode(InnerNode* inner, CodeMarker* marker) subtitleText << "(" << Atom(Atom::AutoLink, fullTitle) << ")" << Atom(Atom::LineBreak); generateHeader(title, inner, marker); + sections = marker->sections(inner, CodeMarker::Summary, CodeMarker::Okay); generateTableOfContents(inner,marker,§ions); + generateKeywordAnchors(inner); generateTitle(title, subtitleText, SmallSubTitle, inner, marker); generateBrief(inner, marker); generateRequisites(inner, marker); @@ -1347,6 +1541,7 @@ void HtmlGenerator::generateQmlTypePage(QmlClassNode* qcn, CodeMarker* marker) QList<Section> sections = marker->qmlSections(qcn, CodeMarker::Summary); generateTableOfContents(qcn, marker, §ions); marker = CodeMarker::markerForLanguage(QLatin1String("QML")); + generateKeywordAnchors(qcn); generateTitle(htmlTitle, Text() << qcn->subTitle(), subTitleSize, qcn, marker); generateBrief(qcn, marker); generateQmlRequisites(qcn, marker); @@ -1418,6 +1613,7 @@ void HtmlGenerator::generateQmlBasicTypePage(QmlBasicTypeNode* qbtn, CodeMarker* generateHeader(htmlTitle, qbtn, marker); QList<Section> sections = marker->sections(qbtn, CodeMarker::Summary, CodeMarker::Okay); generateTableOfContents(qbtn,marker,§ions); + generateKeywordAnchors(qbtn); generateTitle(htmlTitle, Text() << qbtn->subTitle(), subTitleSize, @@ -1463,6 +1659,7 @@ void HtmlGenerator::generateDocNode(DocNode* dn, CodeMarker* marker) if ((dn->name() != QStringLiteral("index.html"))) generateTableOfContents(dn,marker,0); + generateKeywordAnchors(dn); generateTitle(fullTitle, Text() << dn->subTitle(), subTitleSize, @@ -1547,6 +1744,7 @@ void HtmlGenerator::generateCollectionNode(CollectionNode* cn, CodeMarker* marke generateHeader(fullTitle, cn, marker); generateTableOfContents(cn,marker,0); + generateKeywordAnchors(cn); generateTitle(fullTitle, Text() << cn->subTitle(), subTitleSize, cn, marker); if (cn->isModule()) { @@ -1647,11 +1845,11 @@ void HtmlGenerator::generateNavigationBar(const QString &title, return; if (!homepage.isEmpty()) navigationbar << Atom(Atom::ListItemLeft) - << Atom(Atom::AutoLink, homepage) + << Atom(Atom::NavAutoLink, homepage) << Atom(Atom::ListItemRight); if (!landingpage.isEmpty() && landingpage != title) navigationbar << Atom(Atom::ListItemLeft) - << Atom(Atom::AutoLink, landingpage) + << Atom(Atom::NavAutoLink, landingpage) << Atom(Atom::ListItemRight); if (node->isClass()) { @@ -1660,7 +1858,7 @@ void HtmlGenerator::generateNavigationBar(const QString &title, if (!cppclassespage.isEmpty()) navigationbar << Atom(Atom::ListItemLeft) - << Atom(Atom::Link, cppclassespage) + << Atom(Atom::NavLink, cppclassespage) << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK) << Atom(Atom::String, QLatin1String("C++ Classes")) << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK) @@ -1674,7 +1872,7 @@ void HtmlGenerator::generateNavigationBar(const QString &title, else if (node->isQmlType() || node->isQmlBasicType()) { if (!qmltypespage.isEmpty()) navigationbar << Atom(Atom::ListItemLeft) - << Atom(Atom::Link, qmltypespage) + << Atom(Atom::NavLink, qmltypespage) << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK) << Atom(Atom::String, QLatin1String("QML Types")) << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK) @@ -1686,7 +1884,7 @@ void HtmlGenerator::generateNavigationBar(const QString &title, else { if (node->isExampleFile()) { navigationbar << Atom(Atom::ListItemLeft) - << Atom(Atom::Link, node->parent()->name()) + << Atom(Atom::NavLink, node->parent()->name()) << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK) << Atom(Atom::String, node->parent()->title()) << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK) @@ -2321,7 +2519,7 @@ QString HtmlGenerator::generateListOfAllMemberFile(const InnerNode *inner, out() << ", including inherited members.</p>\n"; Section section = sections.first(); - generateSectionList(section, 0, marker, CodeMarker::Subpage); + generateSectionList(section, inner, marker, CodeMarker::Subpage); generateFooter(); endSubPage(); @@ -2378,7 +2576,7 @@ QString HtmlGenerator::generateAllQmlMembersFile(QmlClassNode* qml_cn, CodeMarke prefix = keys.at(j).mid(1); prefix = prefix.left(keys.at(j).indexOf("::")+1); } - generateQmlItem(nodes[j], qcn, marker, true); + generateQmlItem(nodes[j], qml_cn, marker, true); if (nodes[j]->isAttached()) out() << " [attached]"; //generateSynopsis(nodes[j], qcn, marker, CodeMarker::Subpage, false, &prefix); @@ -2807,8 +3005,9 @@ void HtmlGenerator::generateCompactList(ListType listType, else if (listType == Obsolete) { QString fileName = fileBase(it.value()) + "-obsolete." + fileExtension(); QString link; - if (useOutputSubdirs()) + if (useOutputSubdirs()) { link = QString("../" + it.value()->outputSubdirectory() + QLatin1Char('/')); + } link += fileName; out() << "<a href=\"" << link << "\">"; } @@ -2848,7 +3047,7 @@ void HtmlGenerator::generateFunctionIndex(const Node *relative) char currentLetter; out() << "<ul>\n"; - NodeMapMap funcIndex = qdb_->getFunctionIndex(); + NodeMapMap& funcIndex = qdb_->getFunctionIndex(); QMap<QString, NodeMap >::ConstIterator f = funcIndex.constBegin(); while (f != funcIndex.constEnd()) { out() << "<li>"; @@ -3667,34 +3866,7 @@ QString HtmlGenerator::getLink(const Atom *atom, const Node *relative, const Nod if (t.startsWith("mailto:")) return t; } - - QString ref; - - *node = qdb_->findNodeForAtom(atom, relative, ref); - if (!(*node)) - return QString(); - - QString url = (*node)->url(); - if (!url.isEmpty()) { - if (ref.isEmpty()) - return url; - int hashtag = url.lastIndexOf(QChar('#')); - if (hashtag != -1) - url.truncate(hashtag); - return url + "#" + ref; - } - /* - Given that *node is not null, we now cconstruct a link - to the page that *node represents, and then if we found - a target on that page, we connect the target to the link - with '#'. - */ - QString link = linkForNode(*node, relative); - if (*node && (*node)->subType() == Node::Image) - link = "images/used-in-examples/" + link; - if (!ref.isEmpty()) - link += QLatin1Char('#') + ref; - return link; + return getAutoLink(atom, relative, node); } /*! @@ -3712,29 +3884,28 @@ QString HtmlGenerator::getLink(const Atom *atom, const Node *relative, const Nod QString HtmlGenerator::getAutoLink(const Atom *atom, const Node *relative, const Node** node) { QString ref; - QString link; *node = qdb_->findNodeForAtom(atom, relative, ref); if (!(*node)) return QString(); - QString url = (*node)->url(); - if (!url.isEmpty()) { - if (ref.isEmpty()) - return url; - int hashtag = url.lastIndexOf(QChar('#')); + QString link = (*node)->url(); + if (link.isEmpty()) { + link = linkForNode(*node, relative); + if ((*node)->subType() == Node::Image) + link = "images/used-in-examples/" + link; + if (!ref.isEmpty()) + link += QLatin1Char('#') + ref; + } + else if (!ref.isEmpty()) { + int hashtag = link.lastIndexOf(QChar('#')); if (hashtag != -1) - url.truncate(hashtag); - return url + "#" + ref; + link.truncate(hashtag); + link += "#" + ref; } - - link = linkForNode(*node, relative); - if (!ref.isEmpty()) - link += QLatin1Char('#') + ref; return link; } - /*! Construct the link string for the \a node and return it. The \a relative node is use to decide the link we are @@ -3789,7 +3960,12 @@ QString HtmlGenerator::linkForNode(const Node *node, const Node *relative) if (node && relative && (node != relative)) { if (useOutputSubdirs() && !node->isExternalPage() && node->outputSubdirectory() != relative->outputSubdirectory()) { - link.prepend(QString("../" + node->outputSubdirectory() + QLatin1Char('/'))); + if (link.startsWith(QString(node->outputSubdirectory() + QLatin1Char('/')))) { + link.prepend(QString("../")); + } + else { + link.prepend(QString("../" + node->outputSubdirectory() + QLatin1Char('/'))); + } } } return link; @@ -3827,6 +4003,7 @@ void HtmlGenerator::generateDetailedMember(const Node *node, generateMacRef(node, marker); #endif generateExtractionMark(node, MemberMark); + generateKeywordAnchors(node); QString nodeRef = refForNode(node); if (node->type() == Node::Enum && (enume = static_cast<const EnumNode *>(node))->flagsType()) { @@ -4063,6 +4240,7 @@ void HtmlGenerator::generateDetailedQmlMember(Node *node, generateMacRef(node, marker); #endif generateExtractionMark(node, MemberMark); + generateKeywordAnchors(node); out() << "<div class=\"qmlitem\">"; QString nodeRef = refForNode(node); if (node->type() == Node::QmlPropertyGroup) { @@ -4516,6 +4694,9 @@ void HtmlGenerator::generateManifestFile(QString manifest, QString element) Reads metacontent - additional attributes and tags to apply when generating manifest files, read from config. Takes the configuration class \a config as a parameter. + + The manifest metacontent map is cleared immediately after + the manifest files have been generated. */ void HtmlGenerator::readManifestMetaContent(const Config &config) { |