/****************************************************************************
**
** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** 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 Digia. For licensing terms and
** conditions see http://qt.digia.com/licensing. For further information
** use the contact form at http://qt.digia.com/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.
**
** In addition, as a special exception, Digia gives you certain additional
** rights. These rights are described in the Digia Qt 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 ";
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() == "contains" || words.first() == "specifies"
|| words.first() == "describes" || words.first() == "defines"
|| words.first() == "holds" || words.first() == "determines"))
out() << " holds ";
else
out() << ' ';
out() << str << '.';
}
break;
case Atom::BriefRight:
if (!relative->isDocNode())
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");
int i = 0;
while (defaults[i].key) {
formattingLeftMap().insert(defaults[i].key, defaults[i].left);
formattingRightMap().insert(defaults[i].key, 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 + " 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"
<< trimmedTrailing(highlightedCode(indent(codeIndent,atom->string()),relative))
<< "
\n";
break;
case Atom::Qml:
out() << ""
<< trimmedTrailing(highlightedCode(indent(codeIndent,atom->string()),relative))
<< "
\n";
break;
case Atom::JavaScript:
out() << ""
<< trimmedTrailing(highlightedCode(indent(codeIndent,atom->string()),relative))
<< "
\n";
break;
case Atom::CodeNew:
out() << ""
<< trimmedTrailing(highlightedCode(indent(codeIndent,atom->string()),relative))
<< "
\n";
break;
case Atom::CodeOld:
out() << ""
<< trimmedTrailing(protectEnc(plainCode(indent(codeIndent,atom->string()))))
<< "
\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(InnerNode* inner, CodeMarker* marker)
{
QList\n" << navigationLinks << "
\n"; } void HtmlGenerator::generateTitle(const QString& title, const Text &subTitle, SubTitleSize subTitleSize, const Node *relative, CodeMarker *marker) { out() << QString(prologue).replace("\\" + COMMAND_VERSION, qdb_->version()); if (!title.isEmpty()) out() << "\n" << navigationLinks << "
\n"; out() << QString(footer).replace("\\" + COMMAND_VERSION, qdb_->version()) << QString(address).replace("\\" + COMMAND_VERSION, qdb_->version()); out() << "\n"; out() << "\n"; } /*! Lists the required imports and includes in a table. The number of rows is known, so this path is simpler than the generateSection() path. */ void HtmlGenerator::generateRequisites(InnerNode *inner, CodeMarker *marker) { QMap" << *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 InnerNode *inner, CodeMarker *marker) { if (!inner->includes().isEmpty()) { out() << "" << trimmedTrailing(highlightedCode(indent(codeIndent, marker->markedUpIncludes(inner->includes())), inner)) << ""; } } /*! 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, 0, 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(QmlClassNode* 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->isDocNode()) {
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)
{
NodeList nl;
CollectionList cl;
QRegExp singleDigit("\\b([0-9])\\b");
if (selector == "overviews") {
CNMap groups;
qdb_->mergeCollections(Node::Group, groups, relative);
cl = groups.values();
foreach (CollectionNode* cn, cl)
nl.append(cn);
generateAnnotatedList(relative, marker, nl);
}
else if (selector == "cpp-modules") {
CNMap modules;
qdb_->mergeCollections(Node::Module, modules, relative);
cl = modules.values();
foreach (CollectionNode* cn, cl)
nl.append(cn);
generateAnnotatedList(relative, marker, nl);
}
else if (selector == "qml-modules") {
CNMap qmlModules;
qdb_->mergeCollections(Node::QmlModule, qmlModules, relative);
cl = qmlModules.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, or
\qmlmodule.
*/
if (!relative || !relative->isCollectionNode()) {
relative->doc().location().warning(tr("\\generatelist {%1} is only allowed in \\group, \\module, and \\qmlmodule comments.").arg(selector));
return;
}
if (selector == "related") {
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() << " |
|
");
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;
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>)"
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 += "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->type() == Node::Enum) { const EnumNode *enume = static_castThe " << protectEnc(enume->flagsType()->name()) << " type is a typedef for " << "QFlags<" << protectEnc(enume->name()) << ">. It stores an OR combination of " << protectEnc(enume->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 QPair