/****************************************************************************
**
** Copyright (C) 2015 The Qt Company Ltd.
** Contact: http://www.qt.io/licensing/
**
** This file is part of the tools applications of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL21$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see http://www.qt.io/terms-conditions. For further
** information use the contact form at http://www.qt.io/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 or version 3 as published by the Free
** Software Foundation and appearing in the file LICENSE.LGPLv21 and
** LICENSE.LGPLv3 included in the packaging of this file. Please review the
** following information to ensure the GNU Lesser General Public License
** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** As a special exception, The Qt Company gives you certain additional
** rights. These rights are described in The Qt Company LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** $QT_END_LICENSE$
**
****************************************************************************/
/*
htmlgenerator.cpp
*/
#include "codemarker.h"
#include "codeparser.h"
#include "helpprojectwriter.h"
#include "htmlgenerator.h"
#include "node.h"
#include "qdocdatabase.h"
#include "separator.h"
#include "tree.h"
#include " << protectEnc(t) << " " << protectEnc(depends) << " 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. 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. ";
if (relative->type() == Node::Property ||
relative->type() == Node::Variable) {
QString str;
atom = atom->next();
while (atom != 0 && atom->type() != Atom::BriefRight) {
if (atom->type() == Atom::String ||
atom->type() == Atom::AutoLink)
str += atom->string();
skipAhead++;
atom = atom->next();
}
str[0] = str[0].toLower();
if (str.endsWith(QLatin1Char('.')))
str.truncate(str.length() - 1);
out() << "This ";
if (relative->type() == Node::Property)
out() << "property";
else
out() << "variable";
QStringList words = str.split(QLatin1Char(' '));
if (!(words.first() == QLatin1String("contains") || words.first() == QLatin1String("specifies")
|| words.first() == QLatin1String("describes") || words.first() == QLatin1String("defines")
|| words.first() == QLatin1String("holds") || words.first() == QLatin1String("determines")))
out() << " holds ";
else
out() << ' ';
out() << str << '.';
}
break;
case Atom::BriefRight:
if (!relative->isDocumentNode())
out() << " ";
in_para = true;
break;
case Atom::CaptionRight:
endLink();
if (in_para) {
out() << " you can rewrite it as For example, if you have code like", "
" }, // tag is not supported in HTML5
{ ATOM_FORMATTING_UICONTROL, "", "" },
{ ATOM_FORMATTING_UNDERLINE, "", "" },
{ 0, 0, 0 }
};
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(QLatin1String(defaults[i].key), QLatin1String(defaults[i].left));
formattingRightMap().insert(QLatin1String(defaults[i].key), QLatin1String(defaults[i].right));
i++;
}
style = config.getString(HtmlGenerator::format() +
Config::dot +
CONFIG_STYLE);
endHeader = config.getString(HtmlGenerator::format() +
Config::dot +
CONFIG_ENDHEADER);
postHeader = config.getString(HtmlGenerator::format() +
Config::dot +
HTMLGENERATOR_POSTHEADER);
postPostHeader = config.getString(HtmlGenerator::format() +
Config::dot +
HTMLGENERATOR_POSTPOSTHEADER);
prologue = config.getString(HtmlGenerator::format() +
Config::dot +
HTMLGENERATOR_PROLOGUE);
footer = config.getString(HtmlGenerator::format() +
Config::dot +
HTMLGENERATOR_FOOTER);
address = config.getString(HtmlGenerator::format() +
Config::dot +
HTMLGENERATOR_ADDRESS);
pleaseGenerateMacRef = config.getBool(HtmlGenerator::format() +
Config::dot +
HTMLGENERATOR_GENERATEMACREFS);
noNavigationBar = config.getBool(HtmlGenerator::format() +
Config::dot +
HTMLGENERATOR_NONAVIGATIONBAR);
tocDepth = config.getInt(HtmlGenerator::format() +
Config::dot +
HTMLGENERATOR_TOCDEPTH);
project = config.getString(CONFIG_PROJECT);
projectDescription = config.getString(CONFIG_DESCRIPTION);
if (projectDescription.isEmpty() && !project.isEmpty())
projectDescription = project + QLatin1String(" Reference Documentation");
projectUrl = config.getString(CONFIG_URL);
tagFile_ = config.getString(CONFIG_TAGFILE);
#ifndef QT_NO_TEXTCODEC
outputEncoding = config.getString(CONFIG_OUTPUTENCODING);
if (outputEncoding.isEmpty())
outputEncoding = QLatin1String("UTF-8");
outputCodec = QTextCodec::codecForName(outputEncoding.toLocal8Bit());
#endif
naturalLanguage = config.getString(CONFIG_NATURALLANGUAGE);
if (naturalLanguage.isEmpty())
naturalLanguage = QLatin1String("en");
QSet" << protectEnc(t) << "
\n";
out() << "
\n";
t = "The Optimal \"depends\" Variable";
out() << " \n";
QString fileName;
for (int i = 0; i< strings.size(); ++i) {
fileName = generateLinksToLinksPage(strings.at(i), marker);
out() << "Destination Module "
<< "Link Count \n";
}
int count = 0;
fileName = generateLinksToBrokenLinksPage(marker, count);
if (count != 0) {
out() << ""
<< ""
<< strings.at(i) << ""
<< " " << counts.at(i)
<< " \n";
}
out() << ""
<< ""
<< "Broken Links" << ""
<< " " << count
<< " " << protectEnc(t) << "
\n";
t = "Consider replacing the depends variable in " + defaultModuleName().toLower() +
".qdocconf with this one, if the two are not identical:";
out() << "
\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() << " \n";
foreach (TargetLoc* t, *tlist) {
// e.g.: Layout Management
out() << "Link to link... In file... Somewhere after line number... \n";
}
out() << "";
out() << "fileName_ << "#" << t->target_ << "\">";
out() << t->text_ << " ";
out() << "";
QString f = t->loc_->doc().location().filePath();
out() << f << " ";
out() << "";
out() << t->loc_->doc().location().lineNo() << "
\n";
generateFooter();
endSubPage();
}
return fileName;
}
/*!
Generate html from an instance of Atom.
*/
int HtmlGenerator::generateAtom(const Atom *atom, const Node *relative, CodeMarker *marker)
{
int idx, skipAhead = 0;
static bool in_para = false;
switch (atom->type()) {
case Atom::AutoLink:
case Atom::NavAutoLink:
if (!inLink_ && !inContents_ && !inSectionHeading_) {
const Node *node = 0;
QString link = getAutoLink(atom, relative, &node);
if (link.isEmpty()) {
if (autolinkErrors())
relative->doc().location().warning(tr("Can't autolink to '%1'").arg(atom->string()));
}
else if (node && node->status() == Node::Obsolete) {
if ((relative->parent() != node) && !relative->isObsolete())
link.clear();
}
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() << "";
}
beginLink(link, node, relative);
generateLink(atom, marker);
endLink();
}
}
else {
out() << protectEnc(atom->string());
}
break;
case Atom::BaseName:
break;
case Atom::BriefLeft:
// Do not output the brief for QML nodes, doc nodes or collections
// (groups, modules, qml module nodes)
if (relative->isQmlType() ||
relative->isDocumentNode() ||
relative->isCollectionNode() ||
relative->isJsType()) {
skipAhead = skipAtoms(atom, Atom::BriefRight);
break;
}
out() << " \n";
foreach (TargetLoc* t, *tlist) {
// e.g.: Layout Management
out() << "Link to broken link... In file... Somewhere after line number... \n";
}
out() << "";
out() << "fileName_ << "#" << t->target_ << "\">";
out() << t->text_ << " ";
out() << "";
QString f = t->loc_->doc().location().filePath();
out() << f << " ";
out() << "";
out() << t->loc_->doc().location().lineNo() << " "
<< trimmedTrailing(highlightedCode(indent(codeIndent,atom->string()),relative), codePrefix, codeSuffix)
<< "
\n";
break;
case Atom::Qml:
out() << ""
<< trimmedTrailing(highlightedCode(indent(codeIndent,atom->string()),relative), codePrefix, codeSuffix)
<< "
\n";
break;
case Atom::JavaScript:
out() << ""
<< trimmedTrailing(highlightedCode(indent(codeIndent,atom->string()),relative), codePrefix, codeSuffix)
<< "
\n";
break;
case Atom::CodeNew:
out() << ""
<< trimmedTrailing(highlightedCode(indent(codeIndent,atom->string()),relative), codePrefix, codeSuffix)
<< "
\n";
break;
case Atom::CodeOld:
out() << ""
<< trimmedTrailing(protectEnc(plainCode(indent(codeIndent,atom->string()))), codePrefix, codeSuffix)
<< "
\n";
break;
case Atom::DivLeft:
out() << "
Class "; out() << ""; QStringList pieces = pmap.key()->fullName().split("::"); out() << protectEnc(pieces.last()); out() << "" << ":
\n"; generateSection(nlist, 0, marker, CodeMarker::Summary); out() << "";
if (fileName.isEmpty()) {
relative->location().warning(tr("Missing image: %1").arg(protectEnc(atom->string())));
out() << "[Missing image "
<< protectEnc(atom->string()) << "]";
}
else {
QString prefix;
out() << "";
helpProjectWriter->addExtraFile(fileName);
if (relative->isExample()) {
const ExampleNode* cen = static_cast
"; out() << formattingLeftMap()[ATOM_FORMATTING_BOLD]; out() << "Important: "; out() << formattingRightMap()[ATOM_FORMATTING_BOLD]; break; case Atom::ImportantRight: out() << "
"; break; case Atom::NoteLeft: out() << ""; out() << formattingLeftMap()[ATOM_FORMATTING_BOLD]; out() << "Note: "; out() << formattingRightMap()[ATOM_FORMATTING_BOLD]; break; case Atom::NoteRight: out() << "
"; break; case Atom::LegaleseLeft: out() << "Constant | "; // If not in \enum topic, skip the value column if (relative->type() == Node::Enum) out() << "Value | "; out() << "Description |
---|---|---|
Constant | Value | |
" << t << " ";
if (relative->type() == Node::Enum) {
out() << " | ";
const EnumNode *enume = static_cast" << protectEnc(itemValue) << " ";
}
skipAhead = 1;
}
break;
case Atom::ListTagRight:
if (atom->string() == ATOM_LIST_TAG)
out() << "\n";
break;
case Atom::ListItemLeft:
if (atom->string() == ATOM_LIST_TAG) {
out() << " | ";
if (matchAhead(atom, Atom::ListItemRight))
out() << " ";
}
}
else {
out() << " |
"; in_para = true; break; case Atom::ParaRight: endLink(); if (in_para) { out() << "
\n"; in_para = false; } //if (!matchAhead(atom, Atom::ListItemRight) && !matchAhead(atom, Atom::TableItemRight)) // out() << "\n"; break; case Atom::QuotationLeft: out() << ""; break; case Atom::QuotationRight: out() << "\n"; break; case Atom::RawString: out() << atom->string(); break; case Atom::SectionLeft: out() << "" << divNavTop << '\n'; break; case Atom::SectionRight: break; case Atom::SectionHeadingLeft: { int unit = atom->string().toInt() + hOffset(relative); out() << "
"; } if (matchAhead(atom, Atom::ParaLeft)) skipAhead = 1; } break; case Atom::TableItemRight: if (inTableHeader_) out() << "
\\" << protectEnc(atom->string())
<< "
";
break;
case Atom::QmlText:
case Atom::EndQmlText:
// don't do anything with these. They are just tags.
break;
default:
unknownAtom(atom);
}
return skipAhead;
}
/*!
Generate a reference page for a C++ class or a C++ namespace.
*/
void HtmlGenerator::generateClassLikeNode(Aggregate* inner, CodeMarker* marker)
{
QList" << buildversion << " | \n"; } else { out() << "|||||||||||||||
" << *i << ":" " | "; if (*i == headerText) out() << requisites.value(*i).toString(); else generateText(requisites.value(*i), inner, marker); out() << " |
" << *i << " | ";
if (*i == importText)
out()< |
"; generateText(brief, node, marker); if (!relative || node == relative) out() << " More...
\n"; generateExtractionMark(node, EndMark); } } void HtmlGenerator::generateIncludes(const Aggregate *inner, CodeMarker *marker) { if (!inner->includes().isEmpty()) { out() << "" << trimmedTrailing(highlightedCode(indent(codeIndent, marker->markedUpIncludes(inner->includes())), inner), codePrefix, codeSuffix) << ""; } } /*! Revised for the new doc format. Generates a table of contents beginning at \a node. */ void HtmlGenerator::generateTableOfContents(const Node *node, CodeMarker *marker, QList
This is the complete list of members for "; generateFullName(inner, 0); out() << ", including inherited members.
\n"; Section section = sections.first(); generateSectionList(section, inner, marker, CodeMarker::Subpage); generateFooter(); endSubPage(); return fileName; } /*! This function creates an html page on which are listed all the members of QML class \a qml_cn, including the inherited members. The \a marker is used for formatting stuff. */ QString HtmlGenerator::generateAllQmlMembersFile(QmlTypeNode* qml_cn, CodeMarker* marker) { QListThis is the complete list of members for "; generateFullName(qml_cn, 0); out() << ", including inherited members.
\n"; ClassKeysNodesList& cknl = sections.first().classKeysNodesList_; if (!cknl.isEmpty()) { for (int i=0; iThe following members are inherited from "; generateFullName(qcn,0); out() << ".
\n"; } out() << "The following members of class " << "" << protectEnc(inner->name()) << "" << " are part of the " "Qt compatibility layer. We advise against " "using them in new code.
\n"; } else { out() << "The following members of class " << "" << protectEnc(inner->name()) << "" << " are obsolete. " << "They are provided to keep old source code working. " << "We strongly advise against using them in new code.
\n"; } for (i = 0; i < sections.size(); ++i) { out() << "The following members of QML type " << "" << protectEnc(qcn->name()) << "" << " are obsolete. " << "They are provided to keep old source code working. " << "We strongly advise against using them in new code.
\n"; QList"; generateFullName(node, relative); out() << " | ";
if (!node->isDocumentNode()) {
Text brief = node->doc().trimmedBriefText(node->name());
if (!brief.isEmpty()) {
out() << ""; generateText(brief, node, marker); out() << " | ";
}
else if (!node->reconstitutedBrief().isEmpty()) {
out() << ""; out() << node->reconstitutedBrief(); out() << " | ";
}
}
else {
out() << ""; if (!node->reconstitutedBrief().isEmpty()) { out() << node->reconstitutedBrief(); } else out() << protectEnc(node->doc().briefText().toString()); out() << " | ";
}
out() << "
"; for (int i = 0; i < 26; i++) { QChar ch('a' + i); out() << QString("%2 ").arg(ch).arg(ch.toUpper()); } out() << "
\n"; char nextLetter = 'a'; char currentLetter; out() << "");
marked.replace("@extra>", "
");
if (summary) {
marked.remove("<@type>");
marked.remove("@type>");
}
out() << highlightedCode(marked, relative, false);
}
void HtmlGenerator::generateList(const Node* relative, CodeMarker* marker, const QString& selector)
{
CNMap cnm;
Node::Genus genus = Node::DontCare;
if (selector == QLatin1String("overviews"))
genus = Node::DOC;
else if (selector == QLatin1String("cpp-modules"))
genus = Node::CPP;
else if (selector == QLatin1String("qml-modules"))
genus = Node::QML;
else if (selector == QLatin1String("js-modules"))
genus = Node::JS;
if (genus != Node::DontCare) {
NodeList nl;
qdb_->mergeCollections(genus, cnm, relative);
CollectionList cl = cnm.values();
foreach (CollectionNode* cn, cl)
nl.append(cn);
generateAnnotatedList(relative, marker, nl);
}
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(tr("\\generatelist {%1} is only allowed in \\group, "
"\\module, \\qmlmodule, and \\jsmodule comments.").arg(selector));
return;
}
Node* n = const_cast";
out() << "
";
}
else {
if (twoColumn && i == (int) (nl.count() + 1) / 2)
out() << " |
|
";
out() << "
";
}
else {
if (twoColumn && i == (int) (section.members.count() + 1) / 2)
out() << " |
[see note below]";
}
}
if (alignNames)
out() << " | |
");
marked.replace("@extra>", "
");
}
if (style != CodeMarker::Detailed) {
marked.remove("<@type>");
marked.remove("@type>");
}
out() << highlightedCode(marked, relative, alignNames);
}
QString HtmlGenerator::highlightedCode(const QString& markedCode,
const Node* relative,
bool alignNames)
{
QString src = markedCode;
QString html;
html.reserve(src.size());
QStringRef arg;
QStringRef 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("Access functions:
\n"; generateSectionList(section, node, marker, CodeMarker::Accessors); } Section notifiers; notifiers.members += property->notifiers(); if (!notifiers.members.isEmpty()) { out() << "Notifier signal:
\n"; //out() << "This signal is emitted when the property value is changed.
\n"; generateSectionList(notifiers, node, marker, CodeMarker::Accessors); } } else if (node->isFunction()) { const FunctionNode* fn = static_castThe " << protectEnc(etn->flagsType()->name()) << " type is a typedef for " << "QFlags<" << protectEnc(etn->name()) << ">. It stores an OR combination of " << protectEnc(etn->name()) << " values.
\n"; } } generateAlsoList(node, marker); generateExtractionMark(node, EndMark); } int HtmlGenerator::hOffset(const Node *node) { switch (node->type()) { case Node::Namespace: case Node::Class: return 2; case Node::QmlBasicType: case Node::QmlType: case Node::Document: return 1; case Node::Enum: case Node::Typedef: case Node::Function: case Node::Property: default: return 3; } } bool HtmlGenerator::isThreeColumnEnumValueTable(const Atom *atom) { while (atom != 0 && !(atom->type() == Atom::ListRight && atom->string() == ATOM_LIST_VALUE)) { if (atom->type() == Atom::ListItemLeft && !matchAhead(atom, Atom::ListItemRight)) return true; atom = atom->next(); } return false; } const QPairNote: "; foreach (const PropertyNode* pn, fn->associatedProperties()) { QString msg; switch (pn->role(fn)) { case PropertyNode::Getter: msg = QStringLiteral("Getter function "); break; case PropertyNode::Setter: msg = QStringLiteral("Setter function "); break; case PropertyNode::Resetter: msg = QStringLiteral("Resetter function "); break; case PropertyNode::Notifier: msg = QStringLiteral("Notifier signal "); break; default: break; } QString link = linkForNode(pn, 0); out() << msg << "for property " << pn->name() << ". "; } out() << "
"; } } QT_END_NAMESPACE