summaryrefslogtreecommitdiffstats
path: root/src/tools/qdoc/htmlgenerator.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/tools/qdoc/htmlgenerator.cpp')
-rw-r--r--src/tools/qdoc/htmlgenerator.cpp295
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,&sections);
+ 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, &sections);
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,&sections);
+ 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)
{