/**************************************************************************** | |
** | |
** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). | |
** All rights reserved. | |
** Contact: Nokia Corporation (qt-info@nokia.com) | |
** | |
** This file is part of the tools applications of the Qt Toolkit. | |
** | |
** $QT_BEGIN_LICENSE:LGPL$ | |
** GNU Lesser General Public License Usage | |
** This file may be used under the terms of the GNU Lesser General Public | |
** License version 2.1 as published by the Free Software Foundation and | |
** appearing in the file LICENSE.LGPL included in the packaging of this | |
** file. Please review the following information to ensure the GNU Lesser | |
** General Public License version 2.1 requirements will be met: | |
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. | |
** | |
** In addition, as a special exception, Nokia gives you certain additional | |
** rights. These rights are described in the Nokia Qt LGPL Exception | |
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. | |
** | |
** GNU General Public License Usage | |
** Alternatively, this file may be used under the terms of the GNU General | |
** Public License version 3.0 as published by the Free Software Foundation | |
** and appearing in the file LICENSE.GPL included in the packaging of this | |
** file. Please review the following information to ensure the GNU General | |
** Public License version 3.0 requirements will be met: | |
** http://www.gnu.org/copyleft/gpl.html. | |
** | |
** Other Usage | |
** Alternatively, this file may be used in accordance with the terms and | |
** conditions contained in a signed written agreement between you and Nokia. | |
** | |
** | |
** | |
** | |
** | |
** $QT_END_LICENSE$ | |
** | |
****************************************************************************/ | |
/* | |
htmlgenerator.cpp | |
*/ | |
#include "codemarker.h" | |
#include "codeparser.h" | |
#include "helpprojectwriter.h" | |
#include "htmlgenerator.h" | |
#include "node.h" | |
#include "separator.h" | |
#include "tree.h" | |
#include <ctype.h> | |
#include <qdebug.h> | |
#include <qlist.h> | |
#include <qiterator.h> | |
#include <qtextcodec.h> | |
#include <QUuid> | |
QT_BEGIN_NAMESPACE | |
#define COMMAND_VERSION Doc::alias("version") | |
int HtmlGenerator::id = 0; | |
bool HtmlGenerator::debugging_on = false; | |
QString HtmlGenerator::divNavTop = ""; | |
static bool showBrokenLinks = false; | |
static QRegExp linkTag("(<@link node=\"([^\"]+)\">).*(</@link>)"); | |
static QRegExp funcTag("(<@func target=\"([^\"]*)\">)(.*)(</@func>)"); | |
static QRegExp typeTag("(<@(type|headerfile|func)(?: +[^>]*)?>)(.*)(</@\\2>)"); | |
static QRegExp spanTag("</@(?:comment|preprocessor|string|char|number|op|type|name|keyword)>"); | |
static QRegExp unknownTag("</?@[^>]*>"); | |
static void addLink(const QString &linkTarget, | |
const QStringRef &nestedStuff, | |
QString *res) | |
{ | |
if (!linkTarget.isEmpty()) { | |
*res += "<a href=\""; | |
*res += linkTarget; | |
*res += "\">"; | |
*res += nestedStuff; | |
*res += "</a>"; | |
} | |
else { | |
*res += nestedStuff; | |
} | |
} | |
HtmlGenerator::HtmlGenerator() | |
: helpProjectWriter(0), | |
inLink(false), | |
inObsoleteLink(false), | |
inContents(false), | |
inSectionHeading(false), | |
inTableHeader(false), | |
numTableRows(0), | |
threeColumnEnumValueTable(true), | |
funcLeftParen("\\S(\\()"), | |
myTree(0), | |
obsoleteLinks(false) | |
{ | |
} | |
HtmlGenerator::~HtmlGenerator() | |
{ | |
if (helpProjectWriter) | |
delete helpProjectWriter; | |
} | |
void HtmlGenerator::initializeGenerator(const Config &config) | |
{ | |
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, "<tt>", "</tt>" }, | |
{ ATOM_FORMATTING_UNDERLINE, "<u>", "</u>" }, | |
{ 0, 0, 0 } | |
}; | |
Generator::initializeGenerator(config); | |
obsoleteLinks = config.getBool(QLatin1String(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); | |
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); | |
project = config.getString(CONFIG_PROJECT); | |
projectDescription = config.getString(CONFIG_DESCRIPTION); | |
if (projectDescription.isEmpty() && !project.isEmpty()) | |
projectDescription = project + " Reference Documentation"; | |
projectUrl = config.getString(CONFIG_URL); | |
outputEncoding = config.getString(CONFIG_OUTPUTENCODING); | |
if (outputEncoding.isEmpty()) | |
outputEncoding = QLatin1String("ISO-8859-1"); | |
outputCodec = QTextCodec::codecForName(outputEncoding.toLocal8Bit()); | |
naturalLanguage = config.getString(CONFIG_NATURALLANGUAGE); | |
if (naturalLanguage.isEmpty()) | |
naturalLanguage = QLatin1String("en"); | |
QSet<QString> editionNames = config.subVars(CONFIG_EDITION); | |
QSet<QString>::ConstIterator edition = editionNames.begin(); | |
while (edition != editionNames.end()) { | |
QString editionName = *edition; | |
QStringList editionModules = config.getStringList(CONFIG_EDITION + | |
Config::dot + | |
editionName + | |
Config::dot + | |
"modules"); | |
QStringList editionGroups = config.getStringList(CONFIG_EDITION + | |
Config::dot + | |
editionName + | |
Config::dot + | |
"groups"); | |
if (!editionModules.isEmpty()) | |
editionModuleMap[editionName] = editionModules; | |
if (!editionGroups.isEmpty()) | |
editionGroupMap[editionName] = editionGroups; | |
++edition; | |
} | |
codeIndent = config.getInt(CONFIG_CODEINDENT); | |
helpProjectWriter = new HelpProjectWriter(config, | |
project.toLower() + | |
".qhp"); | |
// Documentation template handling | |
headerScripts = config.getString(HtmlGenerator::format() + Config::dot + | |
CONFIG_HEADERSCRIPTS); | |
headerStyles = config.getString(HtmlGenerator::format() + | |
Config::dot + | |
CONFIG_HEADERSTYLES); | |
} | |
void HtmlGenerator::terminateGenerator() | |
{ | |
Generator::terminateGenerator(); | |
} | |
QString HtmlGenerator::format() | |
{ | |
return "HTML"; | |
} | |
/*! | |
This is where the HTML files are written. | |
\note The HTML file generation is done in the base class, | |
PageGenerator::generateTree(). | |
*/ | |
void HtmlGenerator::generateTree(const Tree *tree) | |
{ | |
myTree = tree; | |
nonCompatClasses.clear(); | |
mainClasses.clear(); | |
compatClasses.clear(); | |
obsoleteClasses.clear(); | |
moduleClassMap.clear(); | |
moduleNamespaceMap.clear(); | |
funcIndex.clear(); | |
legaleseTexts.clear(); | |
serviceClasses.clear(); | |
qmlClasses.clear(); | |
findAllClasses(tree->root()); | |
findAllFunctions(tree->root()); | |
findAllLegaleseTexts(tree->root()); | |
findAllNamespaces(tree->root()); | |
findAllSince(tree->root()); | |
PageGenerator::generateTree(tree); | |
QString fileBase = project.toLower().simplified().replace(" ", "-"); | |
generateIndex(fileBase, projectUrl, projectDescription); | |
generatePageIndex(outputDir() + "/" + fileBase + ".pageindex"); | |
helpProjectWriter->generate(myTree); | |
} | |
void HtmlGenerator::startText(const Node * /* relative */, | |
CodeMarker * /* marker */) | |
{ | |
inLink = false; | |
inContents = false; | |
inSectionHeading = false; | |
inTableHeader = false; | |
numTableRows = 0; | |
threeColumnEnumValueTable = true; | |
link.clear(); | |
sectionNumber.clear(); | |
} | |
/*! | |
Generate html from an instance of Atom. | |
*/ | |
int HtmlGenerator::generateAtom(const Atom *atom, | |
const Node *relative, | |
CodeMarker *marker) | |
{ | |
int skipAhead = 0; | |
static bool in_para = false; | |
switch (atom->type()) { | |
case Atom::AbstractLeft: | |
if (relative) | |
relative->doc().location().warning(tr("\abstract is not implemented.")); | |
else | |
Location::information(tr("\abstract is not implemented.")); | |
break; | |
case Atom::AbstractRight: | |
break; | |
case Atom::AutoLink: | |
if (!inLink && !inContents && !inSectionHeading) { | |
const Node *node = 0; | |
QString link = getLink(atom, relative, marker, &node); | |
if (!link.isEmpty()) { | |
beginLink(link, node, relative, marker); | |
generateLink(atom, relative, marker); | |
endLink(); | |
} | |
else { | |
out() << protectEnc(atom->string()); | |
} | |
} | |
else { | |
out() << protectEnc(atom->string()); | |
} | |
break; | |
case Atom::BaseName: | |
break; | |
case Atom::BriefLeft: | |
if (relative->type() == Node::Fake) { | |
skipAhead = skipAtoms(atom, Atom::BriefRight); | |
break; | |
} | |
out() << "<p>"; | |
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.right(1) == ".") | |
str.truncate(str.length() - 1); | |
out() << "This "; | |
if (relative->type() == Node::Property) | |
out() << "property"; | |
else | |
out() << "variable"; | |
QStringList words = str.split(" "); | |
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->type() != Node::Fake) | |
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]; | |
if (inLink) { | |
out() << protectEnc(plainCode(atom->string())); | |
} | |
else { | |
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::Code: | |
out() << "<pre class=\"cpp\">" | |
<< trimmedTrailing(highlightedCode(indent(codeIndent,atom->string()), | |
marker,relative)) | |
<< "</pre>\n"; | |
break; | |
#ifdef QDOC_QML | |
case Atom::Qml: | |
out() << "<pre class=\"qml\">" | |
<< trimmedTrailing(highlightedCode(indent(codeIndent,atom->string()), | |
marker,relative)) | |
<< "</pre>\n"; | |
break; | |
case Atom::JavaScript: | |
out() << "<pre class=\"js\">" | |
<< trimmedTrailing(highlightedCode(indent(codeIndent,atom->string()), | |
marker,relative)) | |
<< "</pre>\n"; | |
break; | |
#endif | |
case Atom::CodeNew: | |
out() << "<p>you can rewrite it as</p>\n" | |
<< "<pre class=\"cpp\">" | |
<< trimmedTrailing(highlightedCode(indent(codeIndent,atom->string()), | |
marker,relative)) | |
<< "</pre>\n"; | |
break; | |
case Atom::CodeOld: | |
out() << "<p>For example, if you have code like</p>\n"; | |
// fallthrough | |
case Atom::CodeBad: | |
out() << "<pre class=\"cpp\">" | |
<< trimmedTrailing(protectEnc(plainCode(indent(codeIndent,atom->string())))) | |
<< "</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() << "-->"; | |
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() != 0 && atom->next()->type() == Atom::String) { | |
QRegExp subscriptRegExp("([a-z]+)_([0-9n])"); | |
if (subscriptRegExp.exactMatch(atom->next()->string())) { | |
out() << subscriptRegExp.cap(1) << "<sub>" | |
<< subscriptRegExp.cap(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: | |
{ | |
QList<Node*> values = myTree->groups().values(atom->string()); | |
NodeMap nodeMap; | |
for (int i = 0; i < values.size(); ++i) { | |
const Node* n = values.at(i); | |
if ((n->status() != Node::Internal) && (n->access() != Node::Private)) { | |
nodeMap.insert(n->nameForLists(),n); | |
} | |
} | |
generateAnnotatedList(relative, marker, nodeMap); | |
} | |
break; | |
case Atom::GeneratedList: | |
if (atom->string() == "annotatedclasses") { | |
generateAnnotatedList(relative, marker, nonCompatClasses); | |
} | |
else if (atom->string() == "classes") { | |
generateCompactList(relative, marker, nonCompatClasses, true); | |
} | |
else if (atom->string() == "qmlclasses") { | |
generateCompactList(relative, marker, qmlClasses, true); | |
} | |
else if (atom->string().contains("classesbymodule")) { | |
QString arg = atom->string().trimmed(); | |
QString moduleName = atom->string().mid(atom->string().indexOf( | |
"classesbymodule") + 15).trimmed(); | |
if (moduleClassMap.contains(moduleName)) | |
generateAnnotatedList(relative, marker, moduleClassMap[moduleName]); | |
} | |
else if (atom->string().contains("classesbyedition")) { | |
QString arg = atom->string().trimmed(); | |
QString editionName = atom->string().mid(atom->string().indexOf( | |
"classesbyedition") + 16).trimmed(); | |
if (editionModuleMap.contains(editionName)) { | |
// Add all classes in the modules listed for that edition. | |
NodeMap editionClasses; | |
foreach (const QString &moduleName, editionModuleMap[editionName]) { | |
if (moduleClassMap.contains(moduleName)) | |
editionClasses.unite(moduleClassMap[moduleName]); | |
} | |
// Add additional groups and remove groups of classes that | |
// should be excluded from the edition. | |
QMultiMap <QString, Node *> groups = myTree->groups(); | |
foreach (const QString &groupName, editionGroupMap[editionName]) { | |
QList<Node *> groupClasses; | |
if (groupName.startsWith("-")) { | |
groupClasses = groups.values(groupName.mid(1)); | |
foreach (const Node *node, groupClasses) | |
editionClasses.remove(node->name()); | |
} | |
else { | |
groupClasses = groups.values(groupName); | |
foreach (const Node *node, groupClasses) | |
editionClasses.insert(node->name(), node); | |
} | |
} | |
generateAnnotatedList(relative, marker, editionClasses); | |
} | |
} | |
else if (atom->string() == "classhierarchy") { | |
generateClassHierarchy(relative, marker, nonCompatClasses); | |
} | |
else if (atom->string() == "compatclasses") { | |
generateCompactList(relative, marker, compatClasses, false); | |
} | |
else if (atom->string() == "obsoleteclasses") { | |
generateCompactList(relative, marker, obsoleteClasses, false); | |
} | |
else if (atom->string() == "functionindex") { | |
generateFunctionIndex(relative, marker); | |
} | |
else if (atom->string() == "legalese") { | |
generateLegaleseList(relative, marker); | |
} | |
else if (atom->string() == "mainclasses") { | |
generateCompactList(relative, marker, mainClasses, true); | |
} | |
else if (atom->string() == "services") { | |
generateCompactList(relative, marker, serviceClasses, false); | |
} | |
else if (atom->string() == "overviews") { | |
generateOverviewList(relative, marker); | |
} | |
else if (atom->string() == "namespaces") { | |
generateAnnotatedList(relative, marker, namespaceIndex); | |
} | |
else if (atom->string() == "related") { | |
const FakeNode *fake = static_cast<const FakeNode *>(relative); | |
if (fake && !fake->groupMembers().isEmpty()) { | |
NodeMap groupMembersMap; | |
foreach (const Node *node, fake->groupMembers()) { | |
if (node->type() == Node::Fake) | |
groupMembersMap[fullName(node, relative, marker)] = node; | |
} | |
generateAnnotatedList(fake, marker, groupMembersMap); | |
} | |
} | |
else if (atom->string() == "relatedinline") { | |
const FakeNode *fake = static_cast<const FakeNode *>(relative); | |
if (fake && !fake->groupMembers().isEmpty()) { | |
// Reverse the list into the original scan order. | |
// Should be sorted. But on what? It may not be a | |
// regular class or page definition. | |
QList<const Node *> list; | |
foreach (const Node *node, fake->groupMembers()) | |
list.prepend(node); | |
foreach (const Node *node, list) | |
generateBody(node, marker); | |
} | |
} | |
break; | |
case Atom::SinceList: | |
{ | |
NewSinceMaps::const_iterator nsmap; | |
nsmap = newSinceMaps.find(atom->string()); | |
NewClassMaps::const_iterator ncmap; | |
ncmap = newClassMaps.find(atom->string()); | |
NewClassMaps::const_iterator nqcmap; | |
nqcmap = newQmlClassMaps.find(atom->string()); | |
if ((nsmap != newSinceMaps.constEnd()) && !nsmap.value().isEmpty()) { | |
QList<Section> sections; | |
QList<Section>::ConstIterator s; | |
for (int i=0; i<LastSinceType; ++i) | |
sections.append(Section(sinceTitle(i),QString(),QString(),QString())); | |
NodeMultiMap::const_iterator n = nsmap.value().constBegin(); | |
while (n != nsmap.value().constEnd()) { | |
const Node* node = n.value(); | |
switch (node->type()) { | |
case Node::Fake: | |
if (node->subType() == Node::QmlClass) { | |
sections[QmlClass].appendMember((Node*)node); | |
} | |
break; | |
case Node::Namespace: | |
sections[Namespace].appendMember((Node*)node); | |
break; | |
case Node::Class: | |
sections[Class].appendMember((Node*)node); | |
break; | |
case Node::Enum: | |
sections[Enum].appendMember((Node*)node); | |
break; | |
case Node::Typedef: | |
sections[Typedef].appendMember((Node*)node); | |
break; | |
case Node::Function: { | |
const FunctionNode* fn = static_cast<const FunctionNode*>(node); | |
if (fn->isMacro()) | |
sections[Macro].appendMember((Node*)node); | |
else { | |
Node* p = fn->parent(); | |
if (p) { | |
if (p->type() == Node::Class) | |
sections[MemberFunction].appendMember((Node*)node); | |
else if (p->type() == Node::Namespace) { | |
if (p->name().isEmpty()) | |
sections[GlobalFunction].appendMember((Node*)node); | |
else | |
sections[NamespaceFunction].appendMember((Node*)node); | |
} | |
else | |
sections[GlobalFunction].appendMember((Node*)node); | |
} | |
else | |
sections[GlobalFunction].appendMember((Node*)node); | |
} | |
break; | |
} | |
case Node::Property: | |
sections[Property].appendMember((Node*)node); | |
break; | |
case Node::Variable: | |
sections[Variable].appendMember((Node*)node); | |
break; | |
case Node::QmlProperty: | |
sections[QmlProperty].appendMember((Node*)node); | |
break; | |
case Node::QmlSignal: | |
sections[QmlSignal].appendMember((Node*)node); | |
break; | |
case Node::QmlMethod: | |
sections[QmlMethod].appendMember((Node*)node); | |
break; | |
default: | |
break; | |
} | |
++n; | |
} | |
/* | |
First generate the table of contents. | |
*/ | |
out() << "<ul>\n"; | |
s = sections.constBegin(); | |
while (s != sections.constEnd()) { | |
if (!(*s).members.isEmpty()) { | |
out() << "<li>" | |
<< "<a href=\"#" | |
<< Doc::canonicalTitle((*s).name) | |
<< "\">" | |
<< (*s).name | |
<< "</a></li>\n"; | |
} | |
++s; | |
} | |
out() << "</ul>\n"; | |
int idx = 0; | |
s = sections.constBegin(); | |
while (s != sections.constEnd()) { | |
if (!(*s).members.isEmpty()) { | |
out() << "<a name=\"" | |
<< Doc::canonicalTitle((*s).name) | |
<< "\"></a>\n"; | |
out() << "<h3>" << protectEnc((*s).name) << "</h3>\n"; | |
if (idx == Class) | |
generateCompactList(0, marker, ncmap.value(), false, QString("Q")); | |
else if (idx == QmlClass) | |
generateCompactList(0, marker, nqcmap.value(), false, QString("Q")); | |
else if (idx == MemberFunction) { | |
ParentMaps parentmaps; | |
ParentMaps::iterator pmap; | |
NodeList::const_iterator i = s->members.constBegin(); | |
while (i != s->members.constEnd()) { | |
Node* p = (*i)->parent(); | |
pmap = parentmaps.find(p); | |
if (pmap == parentmaps.end()) | |
pmap = parentmaps.insert(p,NodeMultiMap()); | |
pmap->insert((*i)->name(),(*i)); | |
++i; | |
} | |
pmap = parentmaps.begin(); | |
while (pmap != parentmaps.end()) { | |
NodeList nlist = pmap->values(); | |
out() << "<p>Class "; | |
out() << "<a href=\"" | |
<< linkForNode(pmap.key(), 0) | |
<< "\">"; | |
QStringList pieces = fullName(pmap.key(), 0, marker).split("::"); | |
out() << protectEnc(pieces.last()); | |
out() << "</a>" << ":</p>\n"; | |
generateSection(nlist, 0, marker, CodeMarker::Summary); | |
out() << "<br/>"; | |
++pmap; | |
} | |
} | |
else | |
generateSection(s->members, 0, marker, CodeMarker::Summary); | |
} | |
++idx; | |
++s; | |
} | |
} | |
} | |
break; | |
case Atom::Image: | |
case Atom::InlineImage: | |
{ | |
QString fileName = imageFileName(relative, atom->string()); | |
QString text; | |
if (atom->next() != 0) | |
text = atom->next()->string(); | |
if (atom->type() == Atom::Image) | |
out() << "<p class=\"centerAlign\">"; | |
if (fileName.isEmpty()) { | |
out() << "<font color=\"red\">[Missing image " | |
<< protectEnc(atom->string()) << "]</font>"; | |
} | |
else { | |
out() << "<img src=\"" << protectEnc(fileName) << "\""; | |
if (!text.isEmpty()) | |
out() << " alt=\"" << protectEnc(text) << "\""; | |
out() << " />"; | |
helpProjectWriter->addExtraFile(fileName); | |
} | |
if (atom->type() == Atom::Image) | |
out() << "</p>"; | |
} | |
break; | |
case Atom::ImageText: | |
break; | |
case Atom::LegaleseLeft: | |
out() << "<div class=\"LegaleseLeft\">"; | |
break; | |
case Atom::LegaleseRight: | |
out() << "</div>"; | |
break; | |
case Atom::LineBreak: | |
out() << "<br/>"; | |
break; | |
case Atom::Link: | |
{ | |
const Node *node = 0; | |
QString myLink = getLink(atom, relative, marker, &node); | |
if (myLink.isEmpty()) { | |
relative->doc().location().warning(tr("Cannot link to '%1' in %2") | |
.arg(atom->string()) | |
.arg(marker->plainFullName(relative))); | |
} | |
beginLink(myLink, node, relative, marker); | |
skipAhead = 1; | |
} | |
break; | |
case Atom::LinkNode: | |
{ | |
const Node *node = CodeMarker::nodeForString(atom->string()); | |
beginLink(linkForNode(node, relative), node, relative, marker); | |
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) { | |
threeColumnEnumValueTable = isThreeColumnEnumValueTable(atom); | |
if (threeColumnEnumValueTable) { | |
out() << "<table class=\"valuelist\">"; | |
if (++numTableRows % 2 == 1) | |
out() << "<tr valign=\"top\" class=\"odd\">"; | |
else | |
out() << "<tr valign=\"top\" class=\"even\">"; | |
out() << "<th class=\"tblConst\">Constant</th>" | |
<< "<th class=\"tblval\">Value</th>" | |
<< "<th class=\"tbldscr\">Description</th></tr>\n"; | |
} | |
else { | |
out() << "<table class=\"valuelist\">" | |
<< "<tr><th class=\"tblConst\">Constant</th><th class=\"tblVal\">Value</th></tr>\n"; | |
} | |
} | |
else { | |
out() << "<ol class="; | |
if (atom->string() == ATOM_LIST_UPPERALPHA) { | |
out() << "\"A\""; | |
} /* why type? changed to */ | |
else if (atom->string() == ATOM_LIST_LOWERALPHA) { | |
out() << "\"a\""; | |
} | |
else if (atom->string() == ATOM_LIST_UPPERROMAN) { | |
out() << "\"I\""; | |
} | |
else if (atom->string() == ATOM_LIST_LOWERROMAN) { | |
out() << "\"i\""; | |
} | |
else { // (atom->string() == ATOM_LIST_NUMERIC) | |
out() << "\"1\""; | |
} | |
if (atom->next() != 0 && atom->next()->string().toInt() != 1) | |
out() << " start=\"" << atom->next()->string() << "\""; | |
out() << ">\n"; | |
} | |
break; | |
case Atom::ListItemNumber: | |
break; | |
case Atom::ListTagLeft: | |
if (atom->string() == ATOM_LIST_TAG) { | |
out() << "<dt>"; | |
} | |
else { // (atom->string() == ATOM_LIST_VALUE) | |
// ### Trenton | |
out() << "<tr><td class=\"topAlign\"><tt>" | |
<< protectEnc(plainCode(marker->markedUpEnumValue(atom->next()->string(), | |
relative))) | |
<< "</tt></td><td class=\"topAlign\">"; | |
QString itemValue; | |
if (relative->type() == Node::Enum) { | |
const EnumNode *enume = static_cast<const EnumNode *>(relative); | |
itemValue = enume->itemValue(atom->next()->string()); | |
} | |
if (itemValue.isEmpty()) | |
out() << "?"; | |
else | |
out() << "<tt>" << protectEnc(itemValue) << "</tt>"; | |
skipAhead = 1; | |
} | |
break; | |
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 (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>\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: | |
out() << "<a name=\"" << Doc::canonicalTitle(Text::sectionHeading(atom).toString()) | |
<< "\"></a>" << divNavTop << "\n"; | |
break; | |
case Atom::SectionRight: | |
break; | |
case Atom::SectionHeadingLeft: | |
out() << "<h" + QString::number(atom->string().toInt() + hOffset(relative)) + ">"; | |
inSectionHeading = true; | |
break; | |
case Atom::SectionHeadingRight: | |
out() << "</h" + QString::number(atom->string().toInt() + hOffset(relative)) + ">\n"; | |
inSectionHeading = false; | |
break; | |
case Atom::SidebarLeft: | |
break; | |
case Atom::SidebarRight: | |
break; | |
case Atom::String: | |
if (inLink && !inContents && !inSectionHeading) { | |
generateLink(atom, relative, marker); | |
} | |
else { | |
out() << protectEnc(atom->string()); | |
} | |
break; | |
case Atom::TableLeft: | |
if (in_para) { | |
out() << "</p>\n"; | |
in_para = false; | |
} | |
if (!atom->string().isEmpty()) { | |
if (atom->string().contains("%")) { | |
out() << "<table class=\"generic\" width=\"" | |
<< atom->string() << "\">\n "; | |
} | |
else { | |
out() << "<table class=\"generic\">\n"; | |
} | |
} | |
else { | |
out() << "<table class=\"generic\">\n"; | |
} | |
numTableRows = 0; | |
break; | |
case Atom::TableRight: | |
out() << "</table>\n"; | |
break; | |
case Atom::TableHeaderLeft: | |
out() << "<thead><tr class=\"qt-style\">"; | |
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"; | |
inTableHeader = false; | |
} | |
break; | |
case Atom::TableRowLeft: | |
if (!atom->string().isEmpty()) | |
out() << "<tr " << atom->string() << ">"; | |
else if (++numTableRows % 2 == 1) | |
out() << "<tr valign=\"top\" class=\"odd\">"; | |
else | |
out() << "<tr valign=\"top\" class=\"even\">"; | |
break; | |
case Atom::TableRowRight: | |
out() << "</tr>\n"; | |
break; | |
case Atom::TableItemLeft: | |
{ | |
if (inTableHeader) | |
out() << "<th "; | |
else | |
out() << "<td "; | |
for (int i=0; i<atom->count(); ++i) { | |
if (i > 0) | |
out() << " "; | |
QString p = atom->string(i); | |
if (p.contains('=')) { | |
out() << p; | |
} | |
else { | |
QStringList spans = p.split(","); | |
if (spans.size() == 2) { | |
if (spans.at(0) != "1") | |
out() << " colspan=\"" << spans.at(0) << "\""; | |
if (spans.at(1) != "1") | |
out() << " rowspan=\"" << spans.at(1) << "\""; | |
} | |
} | |
} | |
if (inTableHeader) | |
out() << ">"; | |
else { | |
out() << ">"; | |
//out() << "><p>"; | |
} | |
if (matchAhead(atom, Atom::ParaLeft)) | |
skipAhead = 1; | |
} | |
break; | |
case Atom::TableItemRight: | |
if (inTableHeader) | |
out() << "</th>"; | |
else { | |
out() << "</td>"; | |
//out() << "</p></td>"; | |
} | |
if (matchAhead(atom, Atom::ParaLeft)) | |
skipAhead = 1; | |
break; | |
case Atom::TableOfContents: | |
break; | |
case Atom::Target: | |
out() << "<a name=\"" << Doc::canonicalTitle(atom->string()) << "\"></a>"; | |
break; | |
case Atom::UnhandledFormat: | |
out() << "<b class=\"redFont\"><Missing HTML></b>"; | |
break; | |
case Atom::UnknownCommand: | |
out() << "<b class=\"redFont\"><code>\\" << protectEnc(atom->string()) | |
<< "</code></b>"; | |
break; | |
#ifdef QDOC_QML | |
case Atom::QmlText: | |
case Atom::EndQmlText: | |
// don't do anything with these. They are just tags. | |
break; | |
#endif | |
default: | |
unknownAtom(atom); | |
} | |
return skipAhead; | |
} | |
/*! | |
Generate a reference page for a C++ class. | |
*/ | |
void HtmlGenerator::generateClassLikeNode(const InnerNode *inner, | |
CodeMarker *marker) | |
{ | |
QList<Section> sections; | |
QList<Section>::ConstIterator s; | |
const ClassNode *classe = 0; | |
const NamespaceNode *namespasse = 0; | |
QString title; | |
QString rawTitle; | |
QString fullTitle; | |
if (inner->type() == Node::Namespace) { | |
namespasse = static_cast<const NamespaceNode *>(inner); | |
rawTitle = marker->plainName(inner); | |
fullTitle = marker->plainFullName(inner); | |
title = rawTitle + " Namespace"; | |
} | |
else if (inner->type() == Node::Class) { | |
classe = static_cast<const ClassNode *>(inner); | |
rawTitle = marker->plainName(inner); | |
fullTitle = marker->plainFullName(inner); | |
title = rawTitle + " Class Reference"; | |
} | |
Text subtitleText; | |
if (rawTitle != fullTitle) | |
subtitleText << "(" << Atom(Atom::AutoLink, fullTitle) << ")" | |
<< Atom(Atom::LineBreak); | |
generateHeader(title, inner, marker); | |
sections = marker->sections(inner, CodeMarker::Summary, CodeMarker::Okay); | |
generateTableOfContents(inner,marker,§ions); | |
generateTitle(title, subtitleText, SmallSubTitle, inner, marker); | |
generateBrief(inner, marker); | |
generateIncludes(inner, marker); | |
generateStatus(inner, marker); | |
if (classe) { | |
generateInherits(classe, marker); | |
generateInheritedBy(classe, marker); | |
#ifdef QDOC_QML | |
if (!classe->qmlElement().isEmpty()) { | |
generateInstantiatedBy(classe,marker); | |
} | |
#endif | |
} | |
generateThreadSafeness(inner, marker); | |
generateSince(inner, marker); | |
out() << "<ul>\n"; | |
QString membersLink = generateListOfAllMemberFile(inner, marker); | |
if (!membersLink.isEmpty()) | |
out() << "<li><a href=\"" << membersLink << "\">" | |
<< "List of all members, including inherited members</a></li>\n"; | |
QString obsoleteLink = generateLowStatusMemberFile(inner, | |
marker, | |
CodeMarker::Obsolete); | |
if (!obsoleteLink.isEmpty()) | |
out() << "<li><a href=\"" << obsoleteLink << "\">" | |
<< "Obsolete members</a></li>\n"; | |
QString compatLink = generateLowStatusMemberFile(inner, | |
marker, | |
CodeMarker::Compat); | |
if (!compatLink.isEmpty()) | |
out() << "<li><a href=\"" << compatLink << "\">" | |
<< "Qt 3 support members</a></li>\n"; | |
out() << "</ul>\n"; | |
bool needOtherSection = false; | |
/* | |
sections is built above for the call to generateTableOfContents(). | |
*/ | |
s = sections.begin(); | |
while (s != sections.end()) { | |
if (s->members.isEmpty() && s->reimpMembers.isEmpty()) { | |
if (!s->inherited.isEmpty()) | |
needOtherSection = true; | |
} | |
else { | |
if (!s->members.isEmpty()) { | |
// out() << "<hr />\n"; | |
out() << "<a name=\"" | |
<< registerRef((*s).name.toLower()) | |
<< "\"></a>" << divNavTop << "\n"; | |
out() << "<h2>" << protectEnc((*s).name) << "</h2>\n"; | |
generateSection(s->members, inner, marker, CodeMarker::Summary); | |
} | |
if (!s->reimpMembers.isEmpty()) { | |
QString name = QString("Reimplemented ") + (*s).name; | |
// out() << "<hr />\n"; | |
out() << "<a name=\"" | |
<< registerRef(name.toLower()) | |
<< "\"></a>" << divNavTop << "\n"; | |
out() << "<h2>" << protectEnc(name) << "</h2>\n"; | |
generateSection(s->reimpMembers, inner, marker, CodeMarker::Summary); | |
} | |
if (!s->inherited.isEmpty()) { | |
out() << "<ul>\n"; | |
generateSectionInheritedList(*s, inner, marker); | |
out() << "</ul>\n"; | |
} | |
} | |
++s; | |
} | |
if (needOtherSection) { | |
out() << "<h3>Additional Inherited Members</h3>\n" | |
"<ul>\n"; | |
s = sections.begin(); | |
while (s != sections.end()) { | |
if (s->members.isEmpty() && !s->inherited.isEmpty()) | |
generateSectionInheritedList(*s, inner, marker); | |
++s; | |
} | |
out() << "</ul>\n"; | |
} | |
out() << "<a name=\"" << registerRef("details") << "\"></a>" << divNavTop << "\n"; | |
if (!inner->doc().isEmpty()) { | |
generateExtractionMark(inner, DetailedDescriptionMark); | |
//out() << "<hr />\n" | |
out() << "<div class=\"descr\">\n" // QTBUG-9504 | |
<< "<h2>" << "Detailed Description" << "</h2>\n"; | |
generateBody(inner, marker); | |
out() << "</div>\n"; // QTBUG-9504 | |
generateAlsoList(inner, marker); | |
generateMaintainerList(inner, marker); | |
generateExtractionMark(inner, EndMark); | |
} | |
sections = marker->sections(inner, CodeMarker::Detailed, CodeMarker::Okay); | |
s = sections.begin(); | |
while (s != sections.end()) { | |
//out() << "<hr />\n"; | |
if (!(*s).divClass.isEmpty()) | |
out() << "<div class=\"" << (*s).divClass << "\">\n"; // QTBUG-9504 | |
out() << "<h2>" << protectEnc((*s).name) << "</h2>\n"; | |
NodeList::ConstIterator m = (*s).members.begin(); | |
while (m != (*s).members.end()) { | |
if ((*m)->access() != Node::Private) { // ### check necessary? | |
if ((*m)->type() != Node::Class) | |
generateDetailedMember(*m, inner, marker); | |
else { | |
out() << "<h3> class "; | |
generateFullName(*m, inner, marker); | |
out() << "</h3>"; | |
generateBrief(*m, marker, inner); | |
} | |
QStringList names; | |
names << (*m)->name(); | |
if ((*m)->type() == Node::Function) { | |
const FunctionNode *func = reinterpret_cast<const FunctionNode *>(*m); | |
if (func->metaness() == FunctionNode::Ctor || | |
func->metaness() == FunctionNode::Dtor || | |
func->overloadNumber() != 1) | |
names.clear(); | |
} | |
else if ((*m)->type() == Node::Property) { | |
const PropertyNode *prop = reinterpret_cast<const PropertyNode *>(*m); | |
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(); | |
} | |
else if ((*m)->type() == Node::Enum) { | |
const EnumNode *enume = reinterpret_cast<const EnumNode*>(*m); | |
if (enume->flagsType()) | |
names << enume->flagsType()->name(); | |
foreach (const QString &enumName, | |
enume->doc().enumItemNames().toSet() - | |
enume->doc().omitEnumItemNames().toSet()) | |
names << plainCode(marker->markedUpEnumValue(enumName, | |
enume)); | |
} | |
} | |
++m; | |
} | |
if (!(*s).divClass.isEmpty()) | |
out() << "</div>\n"; // QTBUG-9504 | |
++s; | |
} | |
generateFooter(inner); | |
} | |
/*! | |
Generate the HTML page for a qdoc file that doesn't map | |
to an underlying C++ file. | |
*/ | |
void HtmlGenerator::generateFakeNode(const FakeNode *fake, CodeMarker *marker) | |
{ | |
SubTitleSize subTitleSize = LargeSubTitle; | |
QList<Section> sections; | |
QList<Section>::const_iterator s; | |
QString fullTitle = fake->fullTitle(); | |
QString htmlTitle = fullTitle; | |
if (fake->subType() == Node::File && !fake->subTitle().isEmpty()) { | |
subTitleSize = SmallSubTitle; | |
htmlTitle += " (" + fake->subTitle() + ")"; | |
} | |
else if (fake->subType() == Node::QmlBasicType) { | |
fullTitle = "QML Basic Type: " + fullTitle; | |
htmlTitle = fullTitle; | |
// Replace the marker with a QML code marker. | |
marker = CodeMarker::markerForLanguage(QLatin1String("QML")); | |
} | |
generateHeader(htmlTitle, fake, marker); | |
/* | |
Generate the TOC for the new doc format. | |
Don't generate a TOC for the home page. | |
*/ | |
const QmlClassNode* qml_cn = 0; | |
if (fake->subType() == Node::QmlClass) { | |
qml_cn = static_cast<const QmlClassNode*>(fake); | |
sections = marker->qmlSections(qml_cn,CodeMarker::Summary,0); | |
generateTableOfContents(fake,marker,§ions); | |
// Replace the marker with a QML code marker. | |
marker = CodeMarker::markerForLanguage(QLatin1String("QML")); | |
} | |
else if (fake->name() != QString("index.html")) | |
generateTableOfContents(fake,marker,0); | |
generateTitle(fullTitle, | |
Text() << fake->subTitle(), | |
subTitleSize, | |
fake, | |
marker); | |
if (fake->subType() == Node::Module) { | |
// Generate brief text and status for modules. | |
generateBrief(fake, marker); | |
generateStatus(fake, marker); | |
generateSince(fake, marker); | |
if (moduleNamespaceMap.contains(fake->name())) { | |
out() << "<a name=\"" << registerRef("namespaces") << "\"></a>" << divNavTop << "\n"; | |
out() << "<h2>Namespaces</h2>\n"; | |
generateAnnotatedList(fake, marker, moduleNamespaceMap[fake->name()]); | |
} | |
if (moduleClassMap.contains(fake->name())) { | |
out() << "<a name=\"" << registerRef("classes") << "\"></a>" << divNavTop << "\n"; | |
out() << "<h2>Classes</h2>\n"; | |
generateAnnotatedList(fake, marker, moduleClassMap[fake->name()]); | |
} | |
} | |
else if (fake->subType() == Node::HeaderFile) { | |
// Generate brief text and status for modules. | |
generateBrief(fake, marker); | |
generateStatus(fake, marker); | |
generateSince(fake, marker); | |
out() << "<ul>\n"; | |
QString membersLink = generateListOfAllMemberFile(fake, marker); | |
if (!membersLink.isEmpty()) | |
out() << "<li><a href=\"" << membersLink << "\">" | |
<< "List of all members, including inherited members</a></li>\n"; | |
QString obsoleteLink = generateLowStatusMemberFile(fake, | |
marker, | |
CodeMarker::Obsolete); | |
if (!obsoleteLink.isEmpty()) | |
out() << "<li><a href=\"" << obsoleteLink << "\">" | |
<< "Obsolete members</a></li>\n"; | |
QString compatLink = generateLowStatusMemberFile(fake, | |
marker, | |
CodeMarker::Compat); | |
if (!compatLink.isEmpty()) | |
out() << "<li><a href=\"" << compatLink << "\">" | |
<< "Qt 3 support members</a></li>\n"; | |
out() << "</ul>\n"; | |
} | |
#ifdef QDOC_QML | |
else if (fake->subType() == Node::QmlClass) { | |
const ClassNode* cn = qml_cn->classNode(); | |
generateBrief(qml_cn, marker); | |
generateQmlInherits(qml_cn, marker); | |
generateQmlInheritedBy(qml_cn, marker); | |
generateQmlInstantiates(qml_cn, marker); | |
generateSince(qml_cn, marker); | |
QString allQmlMembersLink = generateAllQmlMembersFile(qml_cn, marker); | |
if (!allQmlMembersLink.isEmpty()) { | |
out() << "<ul>\n"; | |
out() << "<li><a href=\"" << allQmlMembersLink << "\">" | |
<< "List of all members, including inherited members</a></li>\n"; | |
out() << "</ul>\n"; | |
} | |
s = sections.begin(); | |
while (s != sections.end()) { | |
out() << "<a name=\"" << registerRef((*s).name.toLower()) | |
<< "\"></a>" << divNavTop << "\n"; | |
out() << "<h2>" << protectEnc((*s).name) << "</h2>\n"; | |
generateQmlSummary(*s,fake,marker); | |
++s; | |
} | |
generateExtractionMark(fake, DetailedDescriptionMark); | |
out() << "<a name=\"" << registerRef("details") << "\"></a>" << divNavTop << "\n"; | |
out() << "<h2>" << "Detailed Description" << "</h2>\n"; | |
generateBody(fake, marker); | |
if (cn) | |
generateQmlText(cn->doc().body(), cn, marker, fake->name()); | |
generateAlsoList(fake, marker); | |
generateExtractionMark(fake, EndMark); | |
//out() << "<hr />\n"; | |
sections = marker->qmlSections(qml_cn,CodeMarker::Detailed,0); | |
s = sections.begin(); | |
while (s != sections.end()) { | |
out() << "<h2>" << protectEnc((*s).name) << "</h2>\n"; | |
NodeList::ConstIterator m = (*s).members.begin(); | |
while (m != (*s).members.end()) { | |
generateDetailedQmlMember(*m, fake, marker); | |
out() << "<br/>\n"; | |
++m; | |
} | |
++s; | |
} | |
generateFooter(fake); | |
return; | |
} | |
#endif | |
sections = marker->sections(fake, CodeMarker::Summary, CodeMarker::Okay); | |
s = sections.begin(); | |
while (s != sections.end()) { | |
out() << "<a name=\"" << registerRef((*s).name) << "\"></a>" << divNavTop << "\n"; | |
out() << "<h2>" << protectEnc((*s).name) << "</h2>\n"; | |
generateSectionList(*s, fake, marker, CodeMarker::Summary); | |
++s; | |
} | |
Text brief = fake->doc().briefText(); | |
if (fake->subType() == Node::Module && !brief.isEmpty()) { | |
generateExtractionMark(fake, DetailedDescriptionMark); | |
out() << "<a name=\"" << registerRef("details") << "\"></a>" << divNavTop << "\n"; | |
out() << "<div class=\"descr\">\n"; // QTBUG-9504 | |
out() << "<h2>" << "Detailed Description" << "</h2>\n"; | |
} | |
else { | |
generateExtractionMark(fake, DetailedDescriptionMark); | |
out() << "<div class=\"descr\"> <a name=\"" << registerRef("details") << "\"></a>\n"; // QTBUG-9504 | |
} | |
generateBody(fake, marker); | |
out() << "</div>\n"; // QTBUG-9504 | |
generateAlsoList(fake, marker); | |
generateExtractionMark(fake, EndMark); | |
if (!fake->groupMembers().isEmpty()) { | |
NodeMap groupMembersMap; | |
foreach (const Node *node, fake->groupMembers()) { | |
if (node->type() == Node::Class || node->type() == Node::Namespace) | |
groupMembersMap[node->name()] = node; | |
} | |
generateAnnotatedList(fake, marker, groupMembersMap); | |
} | |
sections = marker->sections(fake, CodeMarker::Detailed, CodeMarker::Okay); | |
s = sections.begin(); | |
while (s != sections.end()) { | |
//out() << "<hr />\n"; | |
out() << "<h2>" << protectEnc((*s).name) << "</h2>\n"; | |
NodeList::ConstIterator m = (*s).members.begin(); | |
while (m != (*s).members.end()) { | |
generateDetailedMember(*m, fake, marker); | |
++m; | |
} | |
++s; | |
} | |
generateFooter(fake); | |
} | |
/*! | |
Returns "html" for this subclass of Generator. | |
*/ | |
QString HtmlGenerator::fileExtension(const Node * /* node */) const | |
{ | |
return "html"; | |
} | |
/*! | |
Output breadcrumb list in the html file. | |
*/ | |
void HtmlGenerator::generateBreadCrumbs(const QString &title, | |
const Node *node, | |
CodeMarker *marker) | |
{ | |
Text breadcrumbs; | |
if (node->type() == Node::Class) { | |
const ClassNode *cn = static_cast<const ClassNode *>(node); | |
QString name = node->moduleName(); | |
breadcrumbs << Atom(Atom::ListItemLeft) | |
<< Atom(Atom::Link, QLatin1String("All Modules")) | |
<< Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK) | |
<< Atom(Atom::String, QLatin1String("Modules")) | |
<< Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK) | |
<< Atom(Atom::ListItemRight); | |
if (!name.isEmpty()) | |
breadcrumbs << Atom(Atom::ListItemLeft) | |
<< Atom(Atom::AutoLink, name) | |
<< Atom(Atom::ListItemRight); | |
if (!cn->name().isEmpty()) | |
breadcrumbs << Atom(Atom::ListItemLeft) | |
<< Atom(Atom::String, protectEnc(cn->name())) | |
<< Atom(Atom::ListItemRight); | |
} | |
else if (node->type() == Node::Fake) { | |
const FakeNode* fn = static_cast<const FakeNode*>(node); | |
if (node->subType() == Node::Module) { | |
breadcrumbs << Atom(Atom::ListItemLeft) | |
<< Atom(Atom::Link, QLatin1String("All Modules")) | |
<< Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK) | |
<< Atom(Atom::String, QLatin1String("Modules")) | |
<< Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK) | |
<< Atom(Atom::ListItemRight); | |
QString name = node->name(); | |
if (!name.isEmpty()) | |
breadcrumbs << Atom(Atom::ListItemLeft) | |
<< Atom(Atom::String, protectEnc(name)) | |
<< Atom(Atom::ListItemRight); | |
} | |
else if (node->subType() == Node::Group) { | |
if (fn->name() == QString("modules")) | |
breadcrumbs << Atom(Atom::String, QLatin1String("Modules")); | |
else | |
breadcrumbs << Atom(Atom::ListItemLeft) | |
<< Atom(Atom::String, protectEnc(title)) | |
<< Atom(Atom::ListItemRight); | |
} | |
else if (node->subType() == Node::Page) { | |
if (fn->name() == QString("qdeclarativeexamples.html")) { | |
breadcrumbs << Atom(Atom::ListItemLeft) | |
<< Atom(Atom::Link, QLatin1String("Qt Examples")) | |
<< Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK) | |
<< Atom(Atom::String, QLatin1String("Examples")) | |
<< Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK) | |
<< Atom(Atom::ListItemRight); | |
breadcrumbs << Atom(Atom::ListItemLeft) | |
<< Atom(Atom::AutoLink, QLatin1String("QML Examples & Demos")) | |
<< Atom(Atom::ListItemRight); | |
} | |
else if (fn->name().startsWith("examples-")) { | |
breadcrumbs << Atom(Atom::ListItemLeft) | |
<< Atom(Atom::Link, QLatin1String("Qt Examples")) | |
<< Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK) | |
<< Atom(Atom::String, QLatin1String("Examples")) | |
<< Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK) | |
<< Atom(Atom::ListItemRight); | |
breadcrumbs << Atom(Atom::ListItemLeft) | |
<< Atom(Atom::String, protectEnc(title)) | |
<< Atom(Atom::ListItemRight); | |
} | |
else if (fn->name() == QString("namespaces.html")) | |
breadcrumbs << Atom(Atom::String, QLatin1String("Namespaces")); | |
else | |
breadcrumbs << Atom(Atom::ListItemLeft) | |
<< Atom(Atom::String, protectEnc(title)) | |
<< Atom(Atom::ListItemRight); | |
} | |
else if (node->subType() == Node::QmlClass) { | |
breadcrumbs << Atom(Atom::ListItemLeft) | |
<< Atom(Atom::AutoLink, QLatin1String("QML Elements")) | |
<< Atom(Atom::ListItemRight); | |
breadcrumbs << Atom(Atom::ListItemLeft) | |
<< Atom(Atom::String, protectEnc(title)) | |
<< Atom(Atom::ListItemRight); | |
} | |
else if (node->subType() == Node::Example) { | |
breadcrumbs << Atom(Atom::ListItemLeft) | |
<< Atom(Atom::Link, QLatin1String("Qt Examples")) | |
<< Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK) | |
<< Atom(Atom::String, QLatin1String("Examples")) | |
<< Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK) | |
<< Atom(Atom::ListItemRight); | |
QStringList sl = fn->name().split('/'); | |
if (sl.contains("declarative")) | |
breadcrumbs << Atom(Atom::ListItemLeft) | |
<< Atom(Atom::AutoLink, QLatin1String("QML Examples & Demos")) | |
<< Atom(Atom::ListItemRight); | |
else { | |
QString name = protectEnc("examples-" + sl.at(0) + ".html"); // this generates an empty link | |
QString t = CodeParser::titleFromName(name); | |
} | |
breadcrumbs << Atom(Atom::ListItemLeft) | |
<< Atom(Atom::String, protectEnc(title)) | |
<< Atom(Atom::ListItemRight); | |
} | |
} | |
else if (node->type() == Node::Namespace) { | |
breadcrumbs << Atom(Atom::ListItemLeft) | |
<< Atom(Atom::Link, QLatin1String("All Namespaces")) | |
<< Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK) | |
<< Atom(Atom::String, QLatin1String("Namespaces")) | |
<< Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK) | |
<< Atom(Atom::ListItemRight); | |
breadcrumbs << Atom(Atom::ListItemLeft) | |
<< Atom(Atom::String, protectEnc(title)) | |
<< Atom(Atom::ListItemRight); | |
} | |
generateText(breadcrumbs, node, marker); | |
} | |
void HtmlGenerator::generateHeader(const QString& title, | |
const Node *node, | |
CodeMarker *marker) | |
{ | |
out() << QString("<?xml version=\"1.0\" encoding=\"%1\"?>\n").arg(outputEncoding); | |
out() << "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\n"; | |
out() << QString("<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"%1\" lang=\"%1\">\n").arg(naturalLanguage); | |
out() << "<head>\n"; | |
out() << " <meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\" />\n"; | |
if (node && !node->doc().location().isEmpty()) | |
out() << "<!-- " << node->doc().location().fileName() << " -->\n"; | |
QString shortVersion = myTree->version(); | |
if (shortVersion.count(QChar('.')) == 2) | |
shortVersion.truncate(shortVersion.lastIndexOf(QChar('.'))); | |
if (!project.isEmpty()) | |
shortVersion = project + QLatin1String(" ") + shortVersion + QLatin1String(": "); | |
else | |
shortVersion = QLatin1String("Qt ") + shortVersion + QLatin1String(": "); | |
// Generating page title | |
out() << " <title>" << shortVersion << protectEnc(title) << "</title>\n"; | |
// Include style sheet and script links. | |
out() << headerStyles; | |
out() << headerScripts; | |
out() << endHeader; | |
#ifdef GENERATE_MAC_REFS | |
if (mainPage) | |
generateMacRef(node, marker); | |
#endif | |
out() << QString(postHeader).replace("\\" + COMMAND_VERSION, myTree->version()); | |
generateBreadCrumbs(title,node,marker); | |
out() << QString(postPostHeader).replace("\\" + COMMAND_VERSION, myTree->version()); | |
navigationLinks.clear(); | |
if (node && !node->links().empty()) { | |
QPair<QString,QString> linkPair; | |
QPair<QString,QString> anchorPair; | |
const Node *linkNode; | |
if (node->links().contains(Node::PreviousLink)) { | |
linkPair = node->links()[Node::PreviousLink]; | |
linkNode = findNodeForTarget(linkPair.first, node, marker); | |
if (!linkNode || linkNode == node) | |
anchorPair = linkPair; | |
else | |
anchorPair = anchorForNode(linkNode); | |
out() << " <link rel=\"prev\" href=\"" | |
<< anchorPair.first << "\" />\n"; | |
navigationLinks += "[Previous: <a href=\"" + anchorPair.first + "\">"; | |
if (linkPair.first == linkPair.second && !anchorPair.second.isEmpty()) | |
navigationLinks += protect(anchorPair.second); | |
else | |
navigationLinks += protect(linkPair.second); | |
navigationLinks += "</a>]\n"; | |
} | |
if (node->links().contains(Node::NextLink)) { | |
linkPair = node->links()[Node::NextLink]; | |
linkNode = findNodeForTarget(linkPair.first, node, marker); | |
if (!linkNode || linkNode == node) | |
anchorPair = linkPair; | |
else | |
anchorPair = anchorForNode(linkNode); | |
out() << " <link rel=\"next\" href=\"" | |
<< anchorPair.first << "\" />\n"; | |
navigationLinks += "[Next: <a href=\"" + anchorPair.first + "\">"; | |
if (linkPair.first == linkPair.second && !anchorPair.second.isEmpty()) | |
navigationLinks += protect(anchorPair.second); | |
else | |
navigationLinks += protect(linkPair.second); | |
navigationLinks += "</a>]\n"; | |
} | |
if (node->links().contains(Node::StartLink)) { | |
linkPair = node->links()[Node::StartLink]; | |
linkNode = findNodeForTarget(linkPair.first, node, marker); | |
if (!linkNode || linkNode == node) | |
anchorPair = linkPair; | |
else | |
anchorPair = anchorForNode(linkNode); | |
out() << " <link rel=\"start\" href=\"" | |
<< anchorPair.first << "\" />\n"; | |
} | |
} | |
if (node && !node->links().empty()) | |
out() << "<p class=\"naviNextPrevious headerNavi\">\n" << navigationLinks << "</p><p/>\n"; | |
} | |
void HtmlGenerator::generateTitle(const QString& title, | |
const Text &subTitle, | |
SubTitleSize subTitleSize, | |
const Node *relative, | |
CodeMarker *marker) | |
{ | |
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" << navigationLinks << "</p>\n"; | |
out() << QString(footer).replace("\\" + COMMAND_VERSION, myTree->version()) | |
<< QString(address).replace("\\" + COMMAND_VERSION, myTree->version()); | |
out() << "</body>\n"; | |
out() << "</html>\n"; | |
} | |
void HtmlGenerator::generateBrief(const Node *node, CodeMarker *marker, | |
const Node *relative) | |
{ | |
Text brief = node->doc().briefText(); | |
if (!brief.isEmpty()) { | |
generateExtractionMark(node, BriefMark); | |
out() << "<p>"; | |
generateText(brief, node, marker); | |
if (!relative || node == relative) | |
out() << " <a href=\"#"; | |
else | |
out() << " <a href=\"" << linkForNode(node, relative) << "#"; | |
out() << registerRef("details") << "\">More...</a></p>\n"; | |
generateExtractionMark(node, EndMark); | |
} | |
} | |
void HtmlGenerator::generateIncludes(const InnerNode *inner, CodeMarker *marker) | |
{ | |
if (!inner->includes().isEmpty()) { | |
out() << "<pre class=\"cpp\">" | |
<< trimmedTrailing(highlightedCode(indent(codeIndent, | |
marker->markedUpIncludes(inner->includes())), | |
marker,inner)) | |
<< "</pre>"; | |
} | |
} | |
/*! | |
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 (toc.isEmpty() && !sections && (node->subType() != Node::Module)) | |
return; | |
bool debug = false; | |
QStringList sectionNumber; | |
int detailsBase = 0; | |
// disable nested links in table of contents | |
inContents = true; | |
inLink = true; | |
out() << "<div class=\"toc\">\n"; | |
out() << "<h3><a name=\"toc\">Contents</a></h3>\n"; | |
sectionNumber.append("1"); | |
out() << "<ul>\n"; | |
if (node->subType() == Node::Module) { | |
if (moduleNamespaceMap.contains(node->name())) { | |
out() << "<li class=\"level" | |
<< sectionNumber.size() | |
<< "\"><a href=\"#" | |
<< registerRef("namespaces") | |
<< "\">Namespaces</a></li>\n"; | |
} | |
if (moduleClassMap.contains(node->name())) { | |
out() << "<li class=\"level" | |
<< sectionNumber.size() | |
<< "\"><a href=\"#" | |
<< registerRef("classes") | |
<< "\">Classes</a></li>\n"; | |
} | |
out() << "<li class=\"level" | |
<< sectionNumber.size() | |
<< "\"><a href=\"#" | |
<< registerRef("details") | |
<< "\">Detailed Description</a></li>\n"; | |
for (int i = 0; i < toc.size(); ++i) { | |
if (toc.at(i)->string().toInt() == 1) { | |
detailsBase = 1; | |
break; | |
} | |
} | |
} | |
else if (sections && ((node->type() == Node::Class) || | |
(node->type() == Node::Namespace) || | |
(node->subType() == Node::QmlClass))) { | |
QList<Section>::ConstIterator s = sections->begin(); | |
while (s != sections->end()) { | |
if (!s->members.isEmpty() || !s->reimpMembers.isEmpty()) { | |
out() << "<li class=\"level" | |
<< sectionNumber.size() | |
<< "\"><a href=\"#" | |
<< registerRef((*s).pluralMember) | |
<< "\">" << (*s).name | |
<< "</a></li>\n"; | |
} | |
++s; | |
} | |
out() << "<li class=\"level" | |
<< sectionNumber.size() | |
<< "\"><a href=\"#" | |
<< registerRef("details") | |
<< "\">Detailed Description</a></li>\n"; | |
for (int i = 0; i < toc.size(); ++i) { | |
if (toc.at(i)->string().toInt() == 1) { | |
detailsBase = 1; | |
break; | |
} | |
} | |
} | |
for (int i = 0; i < toc.size(); ++i) { | |
Atom *atom = toc.at(i); | |
int nextLevel = atom->string().toInt() + detailsBase; | |
if (sectionNumber.size() < nextLevel) { | |
do { | |
sectionNumber.append("1"); | |
} while (sectionNumber.size() < nextLevel); | |
} | |
else { | |
while (sectionNumber.size() > nextLevel) { | |
sectionNumber.removeLast(); | |
} | |
sectionNumber.last() = QString::number(sectionNumber.last().toInt() + 1); | |
} | |
int numAtoms; | |
Text headingText = Text::sectionHeading(atom); | |
QString s = headingText.toString(); | |
out() << "<li class=\"level" | |
<< sectionNumber.size() | |
<< "\">"; | |
out() << "<a href=\"" | |
<< "#" | |
<< Doc::canonicalTitle(s) | |
<< "\">"; | |
generateAtomList(headingText.firstAtom(), node, marker, true, numAtoms); | |
out() << "</a></li>\n"; | |
} | |
while (!sectionNumber.isEmpty()) { | |
sectionNumber.removeLast(); | |
} | |
out() << "</ul>\n"; | |
out() << "</div>\n"; | |
inContents = false; | |
inLink = false; | |
} | |
QString HtmlGenerator::generateListOfAllMemberFile(const InnerNode *inner, | |
CodeMarker *marker) | |
{ | |
QList<Section> sections; | |
QList<Section>::ConstIterator s; | |
sections = marker->sections(inner, | |
CodeMarker::SeparateList, | |
CodeMarker::Okay); | |
if (sections.isEmpty()) | |
return QString(); | |
QString fileName = fileBase(inner) + "-members." + fileExtension(inner); | |
beginSubPage(inner->location(), fileName); | |
QString title = "List of All Members for " + inner->name(); | |
generateHeader(title, inner, marker); | |
generateTitle(title, Text(), SmallSubTitle, inner, marker); | |
out() << "<p>This is the complete list of members for "; | |
generateFullName(inner, 0, marker); | |
out() << ", including inherited members.</p>\n"; | |
Section section = sections.first(); | |
generateSectionList(section, 0, marker, CodeMarker::SeparateList); | |
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(const QmlClassNode* qml_cn, | |
CodeMarker* marker) | |
{ | |
QList<Section> sections; | |
QList<Section>::ConstIterator s; | |
sections = marker->qmlSections(qml_cn,CodeMarker::SeparateList,myTree); | |
if (sections.isEmpty()) | |
return QString(); | |
QString fileName = fileBase(qml_cn) + "-members." + fileExtension(qml_cn); | |
beginSubPage(qml_cn->location(), fileName); | |
QString title = "List of All Members for " + qml_cn->name(); | |
generateHeader(title, qml_cn, marker); | |
generateTitle(title, Text(), SmallSubTitle, qml_cn, marker); | |
out() << "<p>This is the complete list of members for "; | |
generateFullName(qml_cn, 0, marker); | |
out() << ", including inherited members.</p>\n"; | |
Section section = sections.first(); | |
generateSectionList(section, 0, marker, CodeMarker::SeparateList); | |
generateFooter(); | |
endSubPage(); | |
return fileName; | |
} | |
QString HtmlGenerator::generateLowStatusMemberFile(const InnerNode *inner, | |
CodeMarker *marker, | |
CodeMarker::Status status) | |
{ | |
QList<Section> sections = marker->sections(inner, | |
CodeMarker::Summary, | |
status); | |
QMutableListIterator<Section> j(sections); | |
while (j.hasNext()) { | |
if (j.next().members.size() == 0) | |
j.remove(); | |
} | |
if (sections.isEmpty()) | |
return QString(); | |
int i; | |
QString title; | |
QString fileName; | |
if (status == CodeMarker::Compat) { | |
title = "Qt 3 Support Members for " + inner->name(); | |
fileName = fileBase(inner) + "-qt3." + fileExtension(inner); | |
} | |
else { | |
title = "Obsolete Members for " + inner->name(); | |
fileName = fileBase(inner) + "-obsolete." + fileExtension(inner); | |
} | |
beginSubPage(inner->location(), fileName); | |
generateHeader(title, inner, marker); | |
generateTitle(title, Text(), SmallSubTitle, inner, marker); | |
if (status == CodeMarker::Compat) { | |
out() << "<p><b>The following class members are part of the " | |
"<a href=\"qt3support.html\">Qt 3 support layer</a>.</b> " | |
"They are provided to help you port old code to Qt 4. We advise against " | |
"using them in new code.</p>\n"; | |
} | |
else { | |
out() << "<p><b>The following class members are obsolete.</b> " | |
<< "They are provided to keep old source code working. " | |
<< "We strongly advise against using them in new code.</p>\n"; | |
} | |
out() << "<p><ul><li><a href=\"" | |
<< linkForNode(inner, 0) << "\">" | |
<< protectEnc(inner->name()) | |
<< " class reference</a></li></ul></p>\n"; | |
for (i = 0; i < sections.size(); ++i) { | |
out() << "<h2>" << protectEnc(sections.at(i).name) << "</h2>\n"; | |
generateSectionList(sections.at(i), inner, marker, CodeMarker::Summary); | |
} | |
sections = marker->sections(inner, CodeMarker::Detailed, status); | |
for (i = 0; i < sections.size(); ++i) { | |
//out() << "<hr />\n"; | |
out() << "<h2>" << protectEnc(sections.at(i).name) << "</h2>\n"; | |
NodeList::ConstIterator m = sections.at(i).members.begin(); | |
while (m != sections.at(i).members.end()) { | |
if ((*m)->access() != Node::Private) | |
generateDetailedMember(*m, inner, marker); | |
++m; | |
} | |
} | |
generateFooter(); | |
endSubPage(); | |
return fileName; | |
} | |
void HtmlGenerator::generateClassHierarchy(const Node *relative, | |
CodeMarker *marker, | |
const QMap<QString,const Node*> &classMap) | |
{ | |
if (classMap.isEmpty()) | |
return; | |
NodeMap topLevel; | |
NodeMap::ConstIterator c = classMap.begin(); | |
while (c != classMap.end()) { | |
const ClassNode *classe = static_cast<const ClassNode *>(*c); | |
if (classe->baseClasses().isEmpty()) | |
topLevel.insert(classe->name(), classe); | |
++c; | |
} | |
QStack<NodeMap > stack; | |
stack.push(topLevel); | |
out() << "<ul>\n"; | |
while (!stack.isEmpty()) { | |
if (stack.top().isEmpty()) { | |
stack.pop(); | |
out() << "</ul>\n"; | |
} | |
else { | |
const ClassNode *child = | |
static_cast<const ClassNode *>(*stack.top().begin()); | |
out() << "<li>"; | |
generateFullName(child, relative, marker); | |
out() << "</li>\n"; | |
stack.top().erase(stack.top().begin()); | |
NodeMap newTop; | |
foreach (const RelatedClass &d, child->derivedClasses()) { | |
if (d.access != Node::Private && !d.node->doc().isEmpty()) | |
newTop.insert(d.node->name(), d.node); | |
} | |
if (!newTop.isEmpty()) { | |
stack.push(newTop); | |
out() << "<ul>\n"; | |
} | |
} | |
} | |
} | |
void HtmlGenerator::generateAnnotatedList(const Node *relative, | |
CodeMarker *marker, | |
const NodeMap &nodeMap) | |
{ | |
out() << "<table class=\"annotated\">\n"; | |
int row = 0; | |
foreach (const QString &name, nodeMap.keys()) { | |
const Node *node = nodeMap[name]; | |
if (node->status() == Node::Obsolete) | |
continue; | |
if (++row % 2 == 1) | |
out() << "<tr class=\"odd topAlign\">"; | |
else | |
out() << "<tr class=\"even topAlign\">"; | |
out() << "<td class=\"tblName\"><p>"; | |
generateFullName(node, relative, marker); | |
out() << "</p></td>"; | |
if (!(node->type() == Node::Fake)) { | |
Text brief = node->doc().trimmedBriefText(name); | |
if (!brief.isEmpty()) { | |
out() << "<td class=\"tblDescr\"><p>"; | |
generateText(brief, node, marker); | |
out() << "</p></td>"; | |
} | |
} | |
else { | |
out() << "<td class=\"tblDescr\"><p>"; | |
out() << protectEnc(node->doc().briefText().toString()); | |
out() << "</p></td>"; | |
} | |
out() << "</tr>\n"; | |
} | |
out() << "</table>\n"; | |
} | |
/*! | |
This function finds the common prefix of the names of all | |
the classes in \a classMap 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 comonPrefix 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 \a classMap. | |
*/ | |
void HtmlGenerator::generateCompactList(const Node *relative, | |
CodeMarker *marker, | |
const NodeMap &classMap, | |
bool includeAlphabet, | |
QString commonPrefix) | |
{ | |
const int NumParagraphs = 37; // '0' to '9', 'A' to 'Z', '_' | |
if (classMap.isEmpty()) | |
return; | |
/* | |
If commonPrefix is not empty, then the caller knows what | |
the common prefix is and has passed it in, so just use that | |
one. | |
*/ | |
int commonPrefixLen = commonPrefix.length(); | |
if (commonPrefixLen == 0) { | |
QString first; | |
QString last; | |
/* | |
The caller didn't pass in a common prefix, so get the common | |
prefix by looking at the class names of the first and last | |
classes in the class map. Discard any namespace names and | |
just use the bare class names. For Qt, the prefix is "Q". | |
Note that the algorithm used here to derive the common prefix | |
from the first and last classes in alphabetical order (QAccel | |
and QXtWidget in Qt 2.1), fails if either class name does not | |
begin with Q. | |
*/ | |
NodeMap::const_iterator iter = classMap.begin(); | |
while (iter != classMap.end()) { | |
if (!iter.key().contains("::")) { | |
first = iter.key(); | |
break; | |
} | |
++iter; | |
} | |
if (first.isEmpty()) | |
first = classMap.begin().key(); | |
iter = classMap.end(); | |
while (iter != classMap.begin()) { | |
--iter; | |
if (!iter.key().contains("::")) { | |
last = iter.key(); | |
break; | |
} | |
} | |
if (last.isEmpty()) | |
last = classMap.begin().key(); | |
if (classMap.size() > 1) { | |
while (commonPrefixLen < first.length() + 1 && | |
commonPrefixLen < last.length() + 1 && | |
first[commonPrefixLen] == last[commonPrefixLen]) | |
++commonPrefixLen; | |
} | |
commonPrefix = first.left(commonPrefixLen); | |
} | |
/* | |
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 NodeMap. | |
*/ | |
NodeMap paragraph[NumParagraphs+1]; | |
QString paragraphName[NumParagraphs+1]; | |
QSet<char> usedParagraphNames; | |
NodeMap::ConstIterator c = classMap.begin(); | |
while (c != classMap.end()) { | |
QStringList pieces = c.key().split("::"); | |
QString key; | |
int idx = commonPrefixLen; | |
if (!pieces.last().startsWith(commonPrefix)) | |
idx = 0; | |
if (pieces.size() == 1) | |
key = pieces.last().mid(idx).toLower(); | |
else | |
key = pieces.last().toLower(); | |
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(key, c.value()); | |
++c; | |
} | |
/* | |
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. | |
*/ | |
int 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"; | |
numTableRows = 0; | |
int curParNr = 0; | |
int curParOffset = 0; | |
for (int i=0; i<classMap.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 (++numTableRows % 2 == 1) | |
out() << "<dl class=\"flowList odd\">"; | |
else | |
out() << "<dl class=\"flowList even\">"; | |
out() << "<dt class=\"alphaChar\">"; | |
if (includeAlphabet) { | |
QChar c = paragraphName[curParNr][0].toLower(); | |
out() << QString("<a name=\"%1\"></a>").arg(c); | |
} | |
out() << "<b>" | |
<< paragraphName[curParNr] | |
<< "</b>"; | |
out() << "</dt>\n"; | |
} | |
/* | |
Output a <dd> for the current offset in the current paragraph. | |
*/ | |
out() << "<dd>"; | |
if ((curParNr < NumParagraphs) && | |
!paragraphName[curParNr].isEmpty()) { | |
NodeMap::Iterator it; | |
it = paragraph[curParNr].begin(); | |
for (int i=0; i<curParOffset; i++) | |
++it; | |
/* | |
Previously, we used generateFullName() for this, but we | |
require some special formatting. | |
*/ | |
out() << "<a href=\"" << linkForNode(it.value(), relative) << "\">"; | |
QStringList pieces; | |
if (it.value()->subType() == Node::QmlClass) | |
pieces << it.value()->name(); | |
else | |
pieces = fullName(it.value(), relative, marker).split("::"); | |
out() << protectEnc(pieces.last()); | |
out() << "</a>"; | |
if (pieces.size() > 1) { | |
out() << " ("; | |
generateFullName(it.value()->parent(), relative, marker); | |
out() << ")"; | |
} | |
} | |
out() << "</dd>\n"; | |
curParOffset++; | |
} | |
if (classMap.count() > 0) | |
out() << "</dl>\n"; | |
out() << "</div>\n"; | |
} | |
void HtmlGenerator::generateFunctionIndex(const Node *relative, | |
CodeMarker *marker) | |
{ | |
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'; | |
char currentLetter; | |
#if 1 | |
out() << "<ul>\n"; | |
#endif | |
QMap<QString, NodeMap >::ConstIterator f = funcIndex.begin(); | |
while (f != funcIndex.end()) { | |
#if 1 | |
out() << "<li>"; | |
#else | |
out() << "<p>"; | |
#endif | |
out() << protectEnc(f.key()) << ":"; | |
currentLetter = f.key()[0].unicode(); | |
while (islower(currentLetter) && currentLetter >= nextLetter) { | |
out() << QString("<a name=\"%1\"></a>").arg(nextLetter); | |
nextLetter++; | |
} | |
NodeMap::ConstIterator s = (*f).begin(); | |
while (s != (*f).end()) { | |
out() << " "; | |
generateFullName((*s)->parent(), relative, marker, *s); | |
++s; | |
} | |
#if 1 | |
out() << "</li>"; | |
#else | |
out() << "</p>"; | |
#endif | |
out() << "\n"; | |
++f; | |
} | |
#if 1 | |
out() << "</ul>\n"; | |
#endif | |
} | |
void HtmlGenerator::generateLegaleseList(const Node *relative, | |
CodeMarker *marker) | |
{ | |
QMap<Text, const Node *>::ConstIterator it = legaleseTexts.begin(); | |
while (it != legaleseTexts.end()) { | |
Text text = it.key(); | |
//out() << "<hr />\n"; | |
generateText(text, relative, marker); | |
out() << "<ul>\n"; | |
do { | |
out() << "<li>"; | |
generateFullName(it.value(), relative, marker); | |
out() << "</li>\n"; | |
++it; | |
} while (it != legaleseTexts.end() && it.key() == text); | |
out() << "</ul>\n"; | |
} | |
} | |
#ifdef QDOC_QML | |
void HtmlGenerator::generateQmlItem(const Node *node, | |
const Node *relative, | |
CodeMarker *marker, | |
bool summary) | |
{ | |
QString marked = marker->markedUpQmlItem(node,summary); | |
QRegExp templateTag("(<[^@>]*>)"); | |
if (marked.indexOf(templateTag) != -1) { | |
QString contents = protectEnc(marked.mid(templateTag.pos(1), | |
templateTag.cap(1).length())); | |
marked.replace(templateTag.pos(1), templateTag.cap(1).length(), | |
contents); | |
} | |
marked.replace(QRegExp("<@param>([a-z]+)_([1-9n])</@param>"), | |
"<i>\\1<sub>\\2</sub></i>"); | |
marked.replace("<@param>", "<i>"); | |
marked.replace("</@param>", "</i>"); | |
if (summary) | |
marked.replace("@name>", "b>"); | |
marked.replace("<@extra>", "<tt>"); | |
marked.replace("</@extra>", "</tt>"); | |
if (summary) { | |
marked.replace("<@type>", ""); | |
marked.replace("</@type>", ""); | |
} | |
out() << highlightedCode(marked, marker, relative, false, node); | |
} | |
#endif | |
void HtmlGenerator::generateOverviewList(const Node *relative, CodeMarker * /* marker */) | |
{ | |
QMap<const FakeNode *, QMap<QString, FakeNode *> > fakeNodeMap; | |
QMap<QString, const FakeNode *> groupTitlesMap; | |
QMap<QString, FakeNode *> uncategorizedNodeMap; | |
QRegExp singleDigit("\\b([0-9])\\b"); | |
const NodeList children = myTree->root()->childNodes(); | |
foreach (Node *child, children) { | |
if (child->type() == Node::Fake && child != relative) { | |
FakeNode *fakeNode = static_cast<FakeNode *>(child); | |
// Check whether the page is part of a group or is the group | |
// definition page. | |
QString group; | |
bool isGroupPage = false; | |
if (fakeNode->doc().metaCommandsUsed().contains("group")) { | |
group = fakeNode->doc().metaCommandArgs("group")[0]; | |
isGroupPage = true; | |
} | |
// there are too many examples; they would clutter the list | |
if (fakeNode->subType() == Node::Example) | |
continue; | |
// not interested either in individual (Qt Designer etc.) manual chapters | |
if (fakeNode->links().contains(Node::ContentsLink)) | |
continue; | |
// Discard external nodes. | |
if (fakeNode->subType() == Node::ExternalPage) | |
continue; | |
QString sortKey = fakeNode->fullTitle().toLower(); | |
if (sortKey.startsWith("the ")) | |
sortKey.remove(0, 4); | |
sortKey.replace(singleDigit, "0\\1"); | |
if (!group.isEmpty()) { | |
if (isGroupPage) { | |
// If we encounter a group definition page, we add all | |
// the pages in that group to the list for that group. | |
foreach (Node *member, fakeNode->groupMembers()) { | |
if (member->type() != Node::Fake) | |
continue; | |
FakeNode *page = static_cast<FakeNode *>(member); | |
if (page) { | |
QString sortKey = page->fullTitle().toLower(); | |
if (sortKey.startsWith("the ")) | |
sortKey.remove(0, 4); | |
sortKey.replace(singleDigit, "0\\1"); | |
fakeNodeMap[const_cast<const FakeNode *>(fakeNode)].insert(sortKey, page); | |
groupTitlesMap[fakeNode->fullTitle()] = const_cast<const FakeNode *>(fakeNode); | |
} | |
} | |
} | |
else if (!isGroupPage) { | |
// If we encounter a page that belongs to a group then | |
// we add that page to the list for that group. | |
const FakeNode *groupNode = static_cast<const FakeNode *>(myTree->root()->findNode(group, Node::Fake)); | |
if (groupNode) | |
fakeNodeMap[groupNode].insert(sortKey, fakeNode); | |
//else | |
// uncategorizedNodeMap.insert(sortKey, fakeNode); | |
}// else | |
// uncategorizedNodeMap.insert(sortKey, fakeNode); | |
}// else | |
// uncategorizedNodeMap.insert(sortKey, fakeNode); | |
} | |
} | |
// We now list all the pages found that belong to groups. | |
// If only certain pages were found for a group, but the definition page | |
// for that group wasn't listed, the list of pages will be intentionally | |
// incomplete. However, if the group definition page was listed, all the | |
// pages in that group are listed for completeness. | |
if (!fakeNodeMap.isEmpty()) { | |
foreach (const QString &groupTitle, groupTitlesMap.keys()) { | |
const FakeNode *groupNode = groupTitlesMap[groupTitle]; | |
out() << QString("<h3><a href=\"%1\">%2</a></h3>\n").arg( | |
linkForNode(groupNode, relative)).arg( | |
protectEnc(groupNode->fullTitle())); | |
if (fakeNodeMap[groupNode].count() == 0) | |
continue; | |
out() << "<ul>\n"; | |
foreach (const FakeNode *fakeNode, fakeNodeMap[groupNode]) { | |
QString title = fakeNode->fullTitle(); | |
if (title.startsWith("The ")) | |
title.remove(0, 4); | |
out() << "<li><a href=\"" << linkForNode(fakeNode, relative) << "\">" | |
<< protectEnc(title) << "</a></li>\n"; | |
} | |
out() << "</ul>\n"; | |
} | |
} | |
if (!uncategorizedNodeMap.isEmpty()) { | |
out() << QString("<h3>Miscellaneous</h3>\n"); | |
out() << "<ul>\n"; | |
foreach (const FakeNode *fakeNode, uncategorizedNodeMap) { | |
QString title = fakeNode->fullTitle(); | |
if (title.startsWith("The ")) | |
title.remove(0, 4); | |
out() << "<li><a href=\"" << linkForNode(fakeNode, relative) << "\">" | |
<< protectEnc(title) << "</a></li>\n"; | |
} | |
out() << "</ul>\n"; | |
} | |
} | |
void HtmlGenerator::generateSection(const NodeList& nl, | |
const Node *relative, | |
CodeMarker *marker, | |
CodeMarker::SynopsisStyle style) | |
{ | |
bool alignNames = true; | |
if (!nl.isEmpty()) { | |
bool twoColumn = false; | |
if (style == CodeMarker::SeparateList) { | |
alignNames = false; | |
twoColumn = (nl.count() >= 16); | |
} | |
else if (nl.first()->type() == Node::Property) { | |
twoColumn = (nl.count() >= 5); | |
alignNames = false; | |
} | |
if (alignNames) { | |
out() << "<table class=\"alignedsummary\">\n"; | |
} | |
else { | |
if (twoColumn) | |
out() << "<table class=\"propsummary\">\n" | |
<< "<tr><td class=\"topAlign\">"; | |
out() << "<ul>\n"; | |
} | |
int i = 0; | |
NodeList::ConstIterator m = nl.begin(); | |
while (m != nl.end()) { | |
if ((*m)->access() == Node::Private) { | |
++m; | |
continue; | |
} | |
if (alignNames) { | |
out() << "<tr><td class=\"memItemLeft rightAlign topAlign\"> "; | |
} | |
else { | |
if (twoColumn && i == (int) (nl.count() + 1) / 2) | |
out() << "</ul></td><td class=\"topAlign\"><ul>\n"; | |
out() << "<li class=\"fn\">"; | |
} | |
generateSynopsis(*m, relative, marker, style, alignNames); | |
if (alignNames) | |
out() << "</td></tr>\n"; | |
else | |
out() << "</li>\n"; | |
i++; | |
++m; | |
} | |
if (alignNames) | |
out() << "</table>\n"; | |
else { | |
out() << "</ul>\n"; | |
if (twoColumn) | |
out() << "</td></tr>\n</table>\n"; | |
} | |
} | |
} | |
void HtmlGenerator::generateSectionList(const Section& section, | |
const Node *relative, | |
CodeMarker *marker, | |
CodeMarker::SynopsisStyle style) | |
{ | |
bool alignNames = true; | |
if (!section.members.isEmpty()) { | |
bool twoColumn = false; | |
if (style == CodeMarker::SeparateList) { | |
alignNames = false; | |
twoColumn = (section.members.count() >= 16); | |
} | |
else if (section.members.first()->type() == Node::Property) { | |
twoColumn = (section.members.count() >= 5); | |
alignNames = false; | |
} | |
if (alignNames) { | |
out() << "<table class=\"alignedsummary\">\n"; | |
} | |
else { | |
if (twoColumn) | |
out() << "<table class=\"propsummary\">\n" | |
<< "<tr><td class=\"topAlign\">"; | |
out() << "<ul>\n"; | |
} | |
int i = 0; | |
NodeList::ConstIterator m = section.members.begin(); | |
while (m != section.members.end()) { | |
if ((*m)->access() == Node::Private) { | |
++m; | |
continue; | |
} | |
if (alignNames) { | |
out() << "<tr><td class=\"memItemLeft topAlign rightAlign\"> "; | |
} | |
else { | |
if (twoColumn && i == (int) (section.members.count() + 1) / 2) | |
out() << "</ul></td><td class=\"topAlign\"><ul>\n"; | |
out() << "<li class=\"fn\">"; | |
} | |
generateSynopsis(*m, relative, marker, style, alignNames); | |
if (alignNames) | |
out() << "</td></tr>\n"; | |
else | |
out() << "</li>\n"; | |
i++; | |
++m; | |
} | |
if (alignNames) | |
out() << "</table>\n"; | |
else { | |
out() << "</ul>\n"; | |
if (twoColumn) | |
out() << "</td></tr>\n</table>\n"; | |
} | |
} | |
if (style == CodeMarker::Summary && !section.inherited.isEmpty()) { | |
out() << "<ul>\n"; | |
generateSectionInheritedList(section, relative, marker); | |
out() << "</ul>\n"; | |
} | |
} | |
void HtmlGenerator::generateSectionInheritedList(const Section& section, | |
const Node *relative, | |
CodeMarker *marker) | |
{ | |
QList<QPair<ClassNode *, int> >::ConstIterator p = section.inherited.begin(); | |
while (p != section.inherited.end()) { | |
out() << "<li class=\"fn\">"; | |
out() << (*p).second << " "; | |
if ((*p).second == 1) { | |
out() << section.singularMember; | |
} | |
else { | |
out() << section.pluralMember; | |
} | |
out() << " inherited from <a href=\"" << fileName((*p).first) | |
<< "#" << HtmlGenerator::cleanRef(section.name.toLower()) << "\">" | |
<< protectEnc(marker->plainFullName((*p).first, relative)) | |
<< "</a></li>\n"; | |
++p; | |
} | |
} | |
void HtmlGenerator::generateSynopsis(const Node *node, | |
const Node *relative, | |
CodeMarker *marker, | |
CodeMarker::SynopsisStyle style, | |
bool alignNames) | |
{ | |
QString marked = marker->markedUpSynopsis(node, relative, style); | |
QRegExp templateTag("(<[^@>]*>)"); | |
if (marked.indexOf(templateTag) != -1) { | |
QString contents = protectEnc(marked.mid(templateTag.pos(1), | |
templateTag.cap(1).length())); | |
marked.replace(templateTag.pos(1), templateTag.cap(1).length(), | |
contents); | |
} | |
marked.replace(QRegExp("<@param>([a-z]+)_([1-9n])</@param>"), | |
"<i>\\1<sub>\\2</sub></i>"); | |
marked.replace("<@param>", "<i>"); | |
marked.replace("</@param>", "</i>"); | |
if (style == CodeMarker::Summary) { | |
marked.replace("<@name>", ""); // was "<b>" | |
marked.replace("</@name>", ""); // was "</b>" | |
} | |
if (style == CodeMarker::SeparateList) { | |
QRegExp extraRegExp("<@extra>.*</@extra>"); | |
extraRegExp.setMinimal(true); | |
marked.replace(extraRegExp, ""); | |
} else { | |
marked.replace("<@extra>", "<tt>"); | |
marked.replace("</@extra>", "</tt>"); | |
} | |
if (style != CodeMarker::Detailed) { | |
marked.replace("<@type>", ""); | |
marked.replace("</@type>", ""); | |
} | |
out() << highlightedCode(marked, marker, relative, alignNames); | |
} | |
QString HtmlGenerator::highlightedCode(const QString& markedCode, | |
CodeMarker* marker, | |
const Node* relative, | |
bool alignNames, | |
const Node* self) | |
{ | |
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 += "</td><td class=\"memItemRight bottomAlign\">"; | |
done = true; | |
} | |
i += 2; | |
if (parseArg(src, linkTag, &i, srcSize, &arg, &par1)) { | |
html += "<b>"; | |
const Node* n = CodeMarker::nodeForString(par1.toString()); | |
QString link = linkForNode(n, relative); | |
addLink(link, arg, &html); | |
html += "</b>"; | |
} | |
else { | |
html += charLangle; | |
html += charAt; | |
} | |
} | |
else { | |
html += src.at(i++); | |
} | |
} | |
// replace all <@func> tags: "(<@func target=\"([^\"]*)\">)(.*)(</@func>)" | |
src = html; | |
html = QString(); | |
for (int i = 0, srcSize = src.size(); i < srcSize;) { | |
if (src.at(i) == charLangle && src.at(i + 1) == charAt) { | |
i += 2; | |
if (parseArg(src, funcTag, &i, srcSize, &arg, &par1)) { | |
const Node* n = marker->resolveTarget(par1.toString(), | |
myTree, | |
relative); | |
QString link = linkForNode(n, relative); | |
addLink(link, arg, &html); | |
par1 = QStringRef(); | |
} | |
else { | |
html += charLangle; | |
html += charAt; | |
} | |
} | |
else { | |
html += src.at(i++); | |
} | |
} | |
// replace all "(<@(type|headerfile|func)(?: +[^>]*)?>)(.*)(</@\\2>)" tags | |
src = html; | |
html = QString(); | |
for (int i=0, srcSize=src.size(); i<srcSize;) { | |
if (src.at(i) == charLangle && src.at(i+1) == charAt) { | |
i += 2; | |
bool handled = false; | |
if (parseArg(src, typeTag, &i, srcSize, &arg, &par1)) { | |
par1 = QStringRef(); | |
const Node* n = marker->resolveTarget(arg.toString(), myTree, relative, self); | |
html += QLatin1String("<span class=\"type\">"); | |
if (n && n->subType() == Node::QmlBasicType) { | |
if (relative && relative->subType() == Node::QmlClass) | |
addLink(linkForNode(n,relative), arg, &html); | |
else | |
html += arg.toString(); | |
} | |
else | |
addLink(linkForNode(n,relative), arg, &html); | |
html += QLatin1String("</span>"); | |
handled = true; | |
} | |
else if (parseArg(src, headerTag, &i, srcSize, &arg, &par1)) { | |
par1 = QStringRef(); | |
const Node* n = marker->resolveTarget(arg.toString(), myTree, relative); | |
addLink(linkForNode(n,relative), arg, &html); | |
handled = true; | |
} | |
else if (parseArg(src, funcTag, &i, srcSize, &arg, &par1)) { | |
par1 = QStringRef(); | |
const Node* n = marker->resolveTarget(arg.toString(), myTree, relative); | |
addLink(linkForNode(n,relative), arg, &html); | |
handled = true; | |
} | |
if (!handled) { | |
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(); | |
static const QString spanTags[] = { | |
"<@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>", "</span>", | |
"</@preprocessor>", "</span>", | |
"</@string>", "</span>", | |
"</@char>", "</span>", | |
"</@number>", "</span>", | |
"</@op>", "</span>", | |
"</@type>", "</span>", | |
"</@name>", "</span>", | |
"</@keyword>", "</span>", | |
}; | |
// 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) == charLangle) { | |
bool handled = false; | |
for (int k = 0; k != 18; ++k) { | |
const QString & tag = spanTags[2 * k]; | |
if (tag == QStringRef(&src, i, tag.length())) { | |
html += spanTags[2 * k + 1]; | |
i += tag.length(); | |
handled = true; | |
break; | |
} | |
} | |
if (!handled) { | |
++i; | |
if (src.at(i) == charAt || | |
(src.at(i) == QLatin1Char('/') && src.at(i + 1) == charAt)) { | |
// drop 'our' unknown tags (the ones still containing '@') | |
while (i < n && src.at(i) != QLatin1Char('>')) | |
++i; | |
++i; | |
} | |
else { | |
// retain all others | |
html += charLangle; | |
} | |
} | |
} | |
else { | |
html += src.at(i); | |
++i; | |
} | |
} | |
return html; | |
} | |
void HtmlGenerator::generateLink(const Atom* atom, | |
const Node* /* relative */, | |
CodeMarker* marker) | |
{ | |
static QRegExp camelCase("[A-Z][A-Z][a-z]|[a-z][A-Z0-9]|_"); | |
if (funcLeftParen.indexIn(atom->string()) != -1 && marker->recognizeLanguage("Cpp")) { | |
// hack for C++: move () outside of link | |
int k = funcLeftParen.pos(1); | |
out() << protectEnc(atom->string().left(k)); | |
if (link.isEmpty()) { | |
if (showBrokenLinks) | |
out() << "</i>"; | |
} else { | |
out() << "</a>"; | |
} | |
inLink = false; | |
out() << protectEnc(atom->string().mid(k)); | |
} else { | |
out() << protectEnc(atom->string()); | |
} | |
} | |
QString HtmlGenerator::cleanRef(const QString& ref) | |
{ | |
QString clean; | |
if (ref.isEmpty()) | |
return clean; | |
clean.reserve(ref.size() + 20); | |
const QChar c = ref[0]; | |
const uint u = c.unicode(); | |
if ((u >= 'a' && u <= 'z') || | |
(u >= 'A' && u <= 'Z') || | |
(u >= '0' && u <= '9')) { | |
clean += c; | |
} else if (u == '~') { | |
clean += "dtor."; | |
} else if (u == '_') { | |
clean += "underscore."; | |
} else { | |
clean += "A"; | |
} | |
for (int i = 1; i < (int) ref.length(); i++) { | |
const QChar c = ref[i]; | |
const uint u = c.unicode(); | |
if ((u >= 'a' && u <= 'z') || | |
(u >= 'A' && u <= 'Z') || | |
(u >= '0' && u <= '9') || u == '-' || | |
u == '_' || u == ':' || u == '.') { | |
clean += c; | |
} else if (c.isSpace()) { | |
clean += "-"; | |
} else if (u == '!') { | |
clean += "-not"; | |
} else if (u == '&') { | |
clean += "-and"; | |
} else if (u == '<') { | |
clean += "-lt"; | |
} else if (u == '=') { | |
clean += "-eq"; | |
} else if (u == '>') { | |
clean += "-gt"; | |
} else if (u == '#') { | |
clean += "#"; | |
} else { | |
clean += "-"; | |
clean += QString::number((int)u, 16); | |
} | |
} | |
return clean; | |
} | |
QString HtmlGenerator::registerRef(const QString& ref) | |
{ | |
QString clean = HtmlGenerator::cleanRef(ref); | |
for (;;) { | |
QString& prevRef = refMap[clean.toLower()]; | |
if (prevRef.isEmpty()) { | |
prevRef = ref; | |
break; | |
} else if (prevRef == ref) { | |
break; | |
} | |
clean += "x"; | |
} | |
return clean; | |
} | |
QString HtmlGenerator::protectEnc(const QString &string) | |
{ | |
return protect(string, outputEncoding); | |
} | |
QString HtmlGenerator::protect(const QString &string, const QString &outputEncoding) | |
{ | |
#define APPEND(x) \ | |
if (html.isEmpty()) { \ | |
html = string; \ | |
html.truncate(i); \ | |
} \ | |
html += (x); | |
QString html; | |
int 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 ((outputEncoding == "ISO-8859-1" && ch.unicode() > 0x007F) | |
|| (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; | |
result = PageGenerator::fileBase(node); | |
if (!node->isInnerNode()) { | |
switch (node->status()) { | |
case Node::Compat: | |
result += "-qt3"; | |
break; | |
case Node::Obsolete: | |
result += "-obsolete"; | |
break; | |
default: | |
; | |
} | |
} | |
return result; | |
} | |
QString HtmlGenerator::fileName(const Node *node) | |
{ | |
if (node->type() == Node::Fake) { | |
if (static_cast<const FakeNode *>(node)->subType() == Node::ExternalPage) | |
return node->name(); | |
if (static_cast<const FakeNode *>(node)->subType() == Node::Image) | |
return node->name(); | |
} | |
return PageGenerator::fileName(node); | |
} | |
QString HtmlGenerator::refForNode(const Node *node) | |
{ | |
const FunctionNode *func; | |
const TypedefNode *typedeffe; | |
QString ref; | |
switch (node->type()) { | |
case Node::Namespace: | |
case Node::Class: | |
default: | |
break; | |
case Node::Enum: | |
ref = node->name() + "-enum"; | |
break; | |
case Node::Typedef: | |
typedeffe = static_cast<const TypedefNode *>(node); | |
if (typedeffe->associatedEnum()) { | |
return refForNode(typedeffe->associatedEnum()); | |
} | |
else { | |
ref = node->name() + "-typedef"; | |
} | |
break; | |
case Node::Function: | |
func = static_cast<const FunctionNode *>(node); | |
if (func->associatedProperty()) { | |
return refForNode(func->associatedProperty()); | |
} | |
else { | |
ref = func->name(); | |
if (func->overloadNumber() != 1) | |
ref += "-" + QString::number(func->overloadNumber()); | |
} | |
break; | |
#ifdef QDOC_QML | |
case Node::Fake: | |
if (node->subType() != Node::QmlPropertyGroup) | |
break; | |
case Node::QmlProperty: | |
#endif | |
case Node::Property: | |
ref = node->name() + "-prop"; | |
break; | |
#ifdef QDOC_QML | |
case Node::QmlSignal: | |
ref = node->name() + "-signal"; | |
break; | |
case Node::QmlMethod: | |
ref = node->name() + "-method"; | |
break; | |
#endif | |
case Node::Variable: | |
ref = node->name() + "-var"; | |
break; | |
case Node::Target: | |
return protectEnc(node->name()); | |
} | |
return registerRef(ref); | |
} | |
QString HtmlGenerator::linkForNode(const Node *node, const Node *relative) | |
{ | |
QString link; | |
QString fn; | |
QString ref; | |
if (node == 0 || node == relative) | |
return QString(); | |
if (!node->url().isEmpty()) | |
return node->url(); | |
if (fileBase(node).isEmpty()) | |
return QString(); | |
if (node->access() == Node::Private) | |
return QString(); | |
fn = fileName(node); | |
/* if (!node->url().isEmpty()) | |
return fn;*/ | |
link += fn; | |
if (!node->isInnerNode() || node->subType() == Node::QmlPropertyGroup) { | |
ref = refForNode(node); | |
if (relative && fn == fileName(relative) && ref == refForNode(relative)) | |
return QString(); | |
link += "#"; | |
link += ref; | |
} | |
return link; | |
} | |
QString HtmlGenerator::refForAtom(Atom *atom, const Node * /* node */) | |
{ | |
if (atom->type() == Atom::SectionLeft) { | |
return Doc::canonicalTitle(Text::sectionHeading(atom).toString()); | |
} | |
else if (atom->type() == Atom::Target) { | |
return Doc::canonicalTitle(atom->string()); | |
} | |
else { | |
return QString(); | |
} | |
} | |
void HtmlGenerator::generateFullName(const Node *apparentNode, | |
const Node *relative, | |
CodeMarker *marker, | |
const Node *actualNode) | |
{ | |
if (actualNode == 0) | |
actualNode = apparentNode; | |
out() << "<a href=\"" << linkForNode(actualNode, relative); | |
if (true || relative == 0 || relative->status() != actualNode->status()) { | |
switch (actualNode->status()) { | |
case Node::Obsolete: | |
out() << "\" class=\"obsolete"; | |
break; | |
case Node::Compat: | |
out() << "\" class=\"compat"; | |
break; | |
default: | |
; | |
} | |
} | |
out() << "\">"; | |
out() << protectEnc(fullName(apparentNode, relative, marker)); | |
out() << "</a>"; | |
} | |
void HtmlGenerator::generateDetailedMember(const Node *node, | |
const InnerNode *relative, | |
CodeMarker *marker) | |
{ | |
const EnumNode *enume; | |
#ifdef GENERATE_MAC_REFS | |
generateMacRef(node, marker); | |
#endif | |
generateExtractionMark(node, MemberMark); | |
if (node->type() == Node::Enum | |
&& (enume = static_cast<const EnumNode *>(node))->flagsType()) { | |
#ifdef GENERATE_MAC_REFS | |
generateMacRef(enume->flagsType(), marker); | |
#endif | |
out() << "<h3 class=\"flags\">"; | |
out() << "<a name=\"" + refForNode(node) + "\"></a>"; | |
generateSynopsis(enume, relative, marker, CodeMarker::Detailed); | |
out() << "<br/>"; | |
generateSynopsis(enume->flagsType(), | |
relative, | |
marker, | |
CodeMarker::Detailed); | |
out() << "</h3>\n"; | |
} | |
else { | |
out() << "<h3 class=\"fn\">"; | |
out() << "<a name=\"" + refForNode(node) + "\"></a>"; | |
generateSynopsis(node, relative, marker, CodeMarker::Detailed); | |
out() << "</h3>" << divNavTop << "\n"; | |
} | |
generateStatus(node, marker); | |
generateBody(node, marker); | |
generateThreadSafeness(node, marker); | |
generateSince(node, marker); | |
if (node->type() == Node::Property) { | |
const PropertyNode *property = static_cast<const PropertyNode *>(node); | |
Section section; | |
section.members += property->getters(); | |
section.members += property->setters(); | |
section.members += property->resetters(); | |
if (!section.members.isEmpty()) { | |
out() << "<p><b>Access functions:</b></p>\n"; | |
generateSectionList(section, node, marker, CodeMarker::Accessors); | |
} | |
Section notifiers; | |
notifiers.members += property->notifiers(); | |
if (!notifiers.members.isEmpty()) { | |
out() << "<p><b>Notifier signal:</b></p>\n"; | |
//out() << "<p>This signal is emitted when the property value is changed.</p>\n"; | |
generateSectionList(notifiers, node, marker, CodeMarker::Accessors); | |
} | |
} | |
else if (node->type() == Node::Enum) { | |
const EnumNode *enume = static_cast<const EnumNode *>(node); | |
if (enume->flagsType()) { | |
out() << "<p>The " << protectEnc(enume->flagsType()->name()) | |
<< " type is a typedef for " | |
<< "<a href=\"qflags.html\">QFlags</a><" | |
<< protectEnc(enume->name()) | |
<< ">. It stores an OR combination of " | |
<< protectEnc(enume->name()) | |
<< " values.</p>\n"; | |
} | |
} | |
generateAlsoList(node, marker); | |
generateExtractionMark(node, EndMark); | |
} | |
void HtmlGenerator::findAllClasses(const InnerNode *node) | |
{ | |
NodeList::const_iterator c = node->childNodes().constBegin(); | |
while (c != node->childNodes().constEnd()) { | |
if ((*c)->access() != Node::Private && (*c)->url().isEmpty()) { | |
if ((*c)->type() == Node::Class && !(*c)->doc().isEmpty()) { | |
QString className = (*c)->name(); | |
if ((*c)->parent() && | |
(*c)->parent()->type() == Node::Namespace && | |
!(*c)->parent()->name().isEmpty()) | |
className = (*c)->parent()->name()+"::"+className; | |
if (!(static_cast<const ClassNode *>(*c))->hideFromMainList()) { | |
if ((*c)->status() == Node::Compat) { | |
compatClasses.insert(className, *c); | |
} | |
else if ((*c)->status() == Node::Obsolete) { | |
obsoleteClasses.insert(className, *c); | |
} | |
else { | |
nonCompatClasses.insert(className, *c); | |
if ((*c)->status() == Node::Main) | |
mainClasses.insert(className, *c); | |
} | |
} | |
QString moduleName = (*c)->moduleName(); | |
if (moduleName == "Qt3SupportLight") { | |
moduleClassMap[moduleName].insert((*c)->name(), *c); | |
moduleName = "Qt3Support"; | |
} | |
if (!moduleName.isEmpty()) | |
moduleClassMap[moduleName].insert((*c)->name(), *c); | |
QString serviceName = | |
(static_cast<const ClassNode *>(*c))->serviceName(); | |
if (!serviceName.isEmpty()) | |
serviceClasses.insert(serviceName, *c); | |
} | |
else if ((*c)->type() == Node::Fake && | |
(*c)->subType() == Node::QmlClass && | |
!(*c)->doc().isEmpty()) { | |
QString qmlClassName = (*c)->name(); | |
// Remove the "QML:" prefix if present. | |
if (qmlClassName.startsWith(QLatin1String("QML:"))) | |
qmlClasses.insert(qmlClassName.mid(4),*c); | |
else | |
qmlClasses.insert(qmlClassName,*c); | |
} | |
else if ((*c)->isInnerNode()) { | |
findAllClasses(static_cast<InnerNode *>(*c)); | |
} | |
} | |
++c; | |
} | |
} | |
void HtmlGenerator::findAllFunctions(const InnerNode *node) | |
{ | |
NodeList::ConstIterator c = node->childNodes().begin(); | |
while (c != node->childNodes().end()) { | |
if ((*c)->access() != Node::Private) { | |
if ((*c)->isInnerNode() && (*c)->url().isEmpty()) { | |
findAllFunctions(static_cast<const InnerNode *>(*c)); | |
} | |
else if ((*c)->type() == Node::Function) { | |
const FunctionNode *func = static_cast<const FunctionNode *>(*c); | |
if ((func->status() > Node::Obsolete) && | |
!func->isInternal() && | |
(func->metaness() != FunctionNode::Ctor) && | |
(func->metaness() != FunctionNode::Dtor)) { | |
funcIndex[(*c)->name()].insert(myTree->fullDocumentName((*c)->parent()), *c); | |
} | |
} | |
} | |
++c; | |
} | |
} | |
void HtmlGenerator::findAllLegaleseTexts(const InnerNode *node) | |
{ | |
NodeList::ConstIterator c = node->childNodes().begin(); | |
while (c != node->childNodes().end()) { | |
if ((*c)->access() != Node::Private) { | |
if (!(*c)->doc().legaleseText().isEmpty()) | |
legaleseTexts.insertMulti((*c)->doc().legaleseText(), *c); | |
if ((*c)->isInnerNode()) | |
findAllLegaleseTexts(static_cast<const InnerNode *>(*c)); | |
} | |
++c; | |
} | |
} | |
void HtmlGenerator::findAllNamespaces(const InnerNode *node) | |
{ | |
NodeList::ConstIterator c = node->childNodes().begin(); | |
while (c != node->childNodes().end()) { | |
if ((*c)->access() != Node::Private) { | |
if ((*c)->isInnerNode() && (*c)->url().isEmpty()) { | |
findAllNamespaces(static_cast<const InnerNode *>(*c)); | |
if ((*c)->type() == Node::Namespace) { | |
const NamespaceNode *nspace = static_cast<const NamespaceNode *>(*c); | |
// Ensure that the namespace's name is not empty (the root | |
// namespace has no name). | |
if (!nspace->name().isEmpty()) { | |
namespaceIndex.insert(nspace->name(), *c); | |
QString moduleName = (*c)->moduleName(); | |
if (moduleName == "Qt3SupportLight") { | |
moduleNamespaceMap[moduleName].insert((*c)->name(), *c); | |
moduleName = "Qt3Support"; | |
} | |
if (!moduleName.isEmpty()) | |
moduleNamespaceMap[moduleName].insert((*c)->name(), *c); | |
} | |
} | |
} | |
} | |
++c; | |
} | |
} | |
int HtmlGenerator::hOffset(const Node *node) | |
{ | |
switch (node->type()) { | |
case Node::Namespace: | |
case Node::Class: | |
return 2; | |
case Node::Fake: | |
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 Node *HtmlGenerator::findNodeForTarget(const QString &target, | |
const Node *relative, | |
CodeMarker *marker, | |
const Atom *atom) | |
{ | |
const Node *node = 0; | |
if (target.isEmpty()) { | |
node = relative; | |
} | |
else if (target.endsWith(".html")) { | |
node = myTree->root()->findNode(target, Node::Fake); | |
} | |
else if (marker) { | |
node = marker->resolveTarget(target, myTree, relative); | |
if (!node) | |
node = myTree->findFakeNodeByTitle(target); | |
if (!node && atom) { | |
node = myTree->findUnambiguousTarget(target, | |
*const_cast<Atom**>(&atom)); | |
} | |
} | |
if (!node) | |
relative->doc().location().warning(tr("Cannot link to '%1'").arg(target)); | |
return node; | |
} | |
const QPair<QString,QString> HtmlGenerator::anchorForNode(const Node *node) | |
{ | |
QPair<QString,QString> anchorPair; | |
anchorPair.first = PageGenerator::fileName(node); | |
if (node->type() == Node::Fake) { | |
const FakeNode *fakeNode = static_cast<const FakeNode*>(node); | |
anchorPair.second = fakeNode->title(); | |
} | |
return anchorPair; | |
} | |
QString HtmlGenerator::getLink(const Atom *atom, | |
const Node *relative, | |
CodeMarker *marker, | |
const Node** node) | |
{ | |
QString link; | |
*node = 0; | |
inObsoleteLink = false; | |
if (atom->string().contains(":") && | |
(atom->string().startsWith("file:") | |
|| atom->string().startsWith("http:") | |
|| atom->string().startsWith("https:") | |
|| atom->string().startsWith("ftp:") | |
|| atom->string().startsWith("mailto:"))) { | |
link = atom->string(); | |
} | |
else { | |
QStringList path; | |
if (atom->string().contains('#')) { | |
path = atom->string().split('#'); | |
} | |
else { | |
path.append(atom->string()); | |
} | |
Atom *targetAtom = 0; | |
QString first = path.first().trimmed(); | |
if (first.isEmpty()) { | |
*node = relative; | |
} | |
else if (first.endsWith(".html")) { | |
*node = myTree->root()->findNode(first, Node::Fake); | |
} | |
else { | |
*node = marker->resolveTarget(first, myTree, relative); | |
if (!*node) { | |
*node = myTree->findFakeNodeByTitle(first); | |
} | |
if (!*node) { | |
*node = myTree->findUnambiguousTarget(first, targetAtom); | |
} | |
} | |
if (*node) { | |
if (!(*node)->url().isEmpty()) | |
return (*node)->url(); | |
else | |
path.removeFirst(); | |
} | |
else { | |
*node = relative; | |
} | |
if (*node) { | |
if ((*node)->status() == Node::Obsolete) { | |
if (relative) { | |
if (relative->parent() != *node) { | |
if (relative->status() != Node::Obsolete) { | |
bool porting = false; | |
if (relative->type() == Node::Fake) { | |
const FakeNode* fake = static_cast<const FakeNode*>(relative); | |
if (fake->title().startsWith("Porting")) | |
porting = true; | |
} | |
QString name = marker->plainFullName(relative); | |
if (!porting && !name.startsWith("Q3")) { | |
if (obsoleteLinks) { | |
relative->doc().location().warning(tr("Link to obsolete item '%1' in %2") | |
.arg(atom->string()) | |
.arg(name)); | |
} | |
inObsoleteLink = true; | |
} | |
} | |
} | |
} | |
else { | |
qDebug() << "Link to Obsolete entity" | |
<< (*node)->name() << "no relative"; | |
} | |
} | |
} | |
while (!path.isEmpty()) { | |
targetAtom = myTree->findTarget(path.first(), *node); | |
if (targetAtom == 0) | |
break; | |
path.removeFirst(); | |
} | |
if (path.isEmpty()) { | |
link = linkForNode(*node, relative); | |
if (*node && (*node)->subType() == Node::Image) | |
link = "images/used-in-examples/" + link; | |
if (targetAtom) | |
link += "#" + refForAtom(targetAtom, *node); | |
} | |
} | |
return link; | |
} | |
void HtmlGenerator::generateIndex(const QString &fileBase, | |
const QString &url, | |
const QString &title) | |
{ | |
myTree->generateIndex(outputDir() + "/" + fileBase + ".index", url, title); | |
} | |
void HtmlGenerator::generateStatus(const Node *node, CodeMarker *marker) | |
{ | |
Text text; | |
switch (node->status()) { | |
case Node::Obsolete: | |
if (node->isInnerNode()) | |
Generator::generateStatus(node, marker); | |
break; | |
case Node::Compat: | |
if (node->isInnerNode()) { | |
text << Atom::ParaLeft | |
<< Atom(Atom::FormattingLeft,ATOM_FORMATTING_BOLD) | |
<< "This " | |
<< typeString(node) | |
<< " is part of the Qt 3 support library." | |
<< Atom(Atom::FormattingRight, ATOM_FORMATTING_BOLD) | |
<< " It is provided to keep old source code working. " | |
<< "We strongly advise against " | |
<< "using it in new code. See "; | |
const FakeNode *fakeNode = myTree->findFakeNodeByTitle("Porting To Qt 4"); | |
Atom *targetAtom = 0; | |
if (fakeNode && node->type() == Node::Class) { | |
QString oldName(node->name()); | |
targetAtom = myTree->findTarget(oldName.replace("3", ""), | |
fakeNode); | |
} | |
if (targetAtom) { | |
text << Atom(Atom::Link, linkForNode(fakeNode, node) + "#" + | |
refForAtom(targetAtom, fakeNode)); | |
} | |
else | |
text << Atom(Atom::Link, "Porting to Qt 4"); | |
text << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK) | |
<< Atom(Atom::String, "Porting to Qt 4") | |
<< Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK) | |
<< " for more information." | |
<< Atom::ParaRight; | |
} | |
generateText(text, node, marker); | |
break; | |
default: | |
Generator::generateStatus(node, marker); | |
} | |
} | |
#ifdef GENERATE_MAC_REFS | |
/* | |
No longer valid. | |
*/ | |
void HtmlGenerator::generateMacRef(const Node *node, CodeMarker *marker) | |
{ | |
if (!pleaseGenerateMacRef || marker == 0) | |
return; | |
QStringList macRefs = marker->macRefsForNode(node); | |
foreach (const QString &macRef, macRefs) | |
out() << "<a name=\"" << "//apple_ref/" << macRef << "\"></a>\n"; | |
} | |
#endif | |
void HtmlGenerator::beginLink(const QString &link, | |
const Node *node, | |
const Node *relative, | |
CodeMarker *marker) | |
{ | |
Q_UNUSED(marker) | |
Q_UNUSED(relative) | |
this->link = link; | |
if (link.isEmpty()) { | |
if (showBrokenLinks) | |
out() << "<i>"; | |
} | |
else if (node == 0 || (relative != 0 && | |
node->status() == relative->status())) { | |
out() << "<a href=\"" << link << "\">"; | |
} | |
else { | |
switch (node->status()) { | |
case Node::Obsolete: | |
out() << "<a href=\"" << link << "\" class=\"obsolete\">"; | |
break; | |
case Node::Compat: | |
out() << "<a href=\"" << link << "\" class=\"compat\">"; | |
break; | |
default: | |
out() << "<a href=\"" << link << "\">"; | |
} | |
} | |
inLink = true; | |
} | |
void HtmlGenerator::endLink() | |
{ | |
if (inLink) { | |
if (link.isEmpty()) { | |
if (showBrokenLinks) | |
out() << "</i>"; | |
} | |
else { | |
if (inObsoleteLink) { | |
out() << "<sup>(obsolete)</sup>"; | |
} | |
out() << "</a>"; | |
} | |
} | |
inLink = false; | |
inObsoleteLink = false; | |
} | |
#ifdef QDOC_QML | |
/*! | |
Generates the summary for the \a section. Only used for | |
sections of QML element documentation. | |
Currently handles only the QML property group. | |
*/ | |
void HtmlGenerator::generateQmlSummary(const Section& section, | |
const Node *relative, | |
CodeMarker *marker) | |
{ | |
if (!section.members.isEmpty()) { | |
out() << "<ul>\n"; | |
NodeList::ConstIterator m; | |
m = section.members.begin(); | |
while (m != section.members.end()) { | |
out() << "<li class=\"fn\">"; | |
generateQmlItem(*m,relative,marker,true); | |
out() << "</li>\n"; | |
++m; | |
} | |
out() << "</ul>\n"; | |
} | |
} | |
/*! | |
Outputs the html detailed documentation for a section | |
on a QML element reference page. | |
*/ | |
void HtmlGenerator::generateDetailedQmlMember(const Node *node, | |
const InnerNode *relative, | |
CodeMarker *marker) | |
{ | |
const QmlPropertyNode* qpn = 0; | |
#ifdef GENERATE_MAC_REFS | |
generateMacRef(node, marker); | |
#endif | |
generateExtractionMark(node, MemberMark); | |
out() << "<div class=\"qmlitem\">"; | |
if (node->subType() == Node::QmlPropertyGroup) { | |
const QmlPropGroupNode* qpgn = static_cast<const QmlPropGroupNode*>(node); | |
NodeList::ConstIterator p = qpgn->childNodes().begin(); | |
out() << "<div class=\"qmlproto\">"; | |
out() << "<table class=\"qmlname\">"; | |
while (p != qpgn->childNodes().end()) { | |
if ((*p)->type() == Node::QmlProperty) { | |
qpn = static_cast<const QmlPropertyNode*>(*p); | |
if (++numTableRows % 2 == 1) | |
out() << "<tr valign=\"top\" class=\"odd\">"; | |
else | |
out() << "<tr valign=\"top\" class=\"even\">"; | |
out() << "<td class=\"tblQmlPropNode\"><p>"; | |
out() << "<a name=\"" + refForNode(qpn) + "\"></a>"; | |
if (!qpn->isWritable(myTree)) { | |
out() << "<span class=\"qmlreadonly\">read-only</span>"; | |
} | |
if (qpgn->isDefault()) | |
out() << "<span class=\"qmldefault\">default</span>"; | |
generateQmlItem(qpn, relative, marker, false); | |
out() << "</p></td></tr>"; | |
} | |
++p; | |
} | |
out() << "</table>"; | |
out() << "</div>"; | |
} | |
else if (node->type() == Node::QmlSignal) { | |
const FunctionNode* qsn = static_cast<const FunctionNode*>(node); | |
out() << "<div class=\"qmlproto\">"; | |
out() << "<table class=\"qmlname\">"; | |
//out() << "<tr>"; | |
if (++numTableRows % 2 == 1) | |
out() << "<tr valign=\"top\" class=\"odd\">"; | |
else | |
out() << "<tr valign=\"top\" class=\"even\">"; | |
out() << "<td class=\"tblQmlFuncNode\"><p>"; | |
out() << "<a name=\"" + refForNode(qsn) + "\"></a>"; | |
generateSynopsis(qsn,relative,marker,CodeMarker::Detailed,false); | |
//generateQmlItem(qsn,relative,marker,false); | |
out() << "</p></td></tr>"; | |
out() << "</table>"; | |
out() << "</div>"; | |
} | |
else if (node->type() == Node::QmlMethod) { | |
const FunctionNode* qmn = static_cast<const FunctionNode*>(node); | |
out() << "<div class=\"qmlproto\">"; | |
out() << "<table class=\"qmlname\">"; | |
//out() << "<tr>"; | |
if (++numTableRows % 2 == 1) | |
out() << "<tr valign=\"top\" class=\"odd\">"; | |
else | |
out() << "<tr valign=\"top\" class=\"even\">"; | |
out() << "<td class=\"tblQmlFuncNode\"><p>"; | |
out() << "<a name=\"" + refForNode(qmn) + "\"></a>"; | |
generateSynopsis(qmn,relative,marker,CodeMarker::Detailed,false); | |
out() << "</p></td></tr>"; | |
out() << "</table>"; | |
out() << "</div>"; | |
} | |
out() << "<div class=\"qmldoc\">"; | |
generateStatus(node, marker); | |
generateBody(node, marker); | |
generateThreadSafeness(node, marker); | |
generateSince(node, marker); | |
generateAlsoList(node, marker); | |
out() << "</div>"; | |
out() << "</div>"; | |
generateExtractionMark(node, EndMark); | |
} | |
/*! | |
Output the "Inherits" line for the QML element, | |
if there should be one. | |
*/ | |
void HtmlGenerator::generateQmlInherits(const QmlClassNode* cn, | |
CodeMarker* marker) | |
{ | |
if (cn && !cn->links().empty()) { | |
if (cn->links().contains(Node::InheritsLink)) { | |
QPair<QString,QString> linkPair; | |
linkPair = cn->links()[Node::InheritsLink]; | |
QStringList strList(linkPair.first); | |
const Node* n = myTree->findNode(strList,Node::Fake); | |
if (n && n->subType() == Node::QmlClass) { | |
const QmlClassNode* qcn = static_cast<const QmlClassNode*>(n); | |
Text text; | |
text << Atom::ParaLeft << "Inherits "; | |
text << Atom(Atom::LinkNode,CodeMarker::stringForNode(qcn)); | |
text << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK); | |
text << Atom(Atom::String, linkPair.second); | |
text << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK); | |
text << Atom::ParaRight; | |
generateText(text, cn, marker); | |
} | |
} | |
} | |
} | |
/*! | |
Output the "Inherit by" list for the QML element, | |
if it is inherited by any other elements. | |
*/ | |
void HtmlGenerator::generateQmlInheritedBy(const QmlClassNode* cn, | |
CodeMarker* marker) | |
{ | |
if (cn) { | |
NodeList subs; | |
QmlClassNode::subclasses(cn->name(),subs); | |
if (!subs.isEmpty()) { | |
Text text; | |
text << Atom::ParaLeft << "Inherited by "; | |
appendSortedQmlNames(text,cn,subs,marker); | |
text << Atom::ParaRight; | |
generateText(text, cn, marker); | |
} | |
} | |
} | |
/*! | |
Output the "[Xxx instantiates the C++ class QmlGraphicsXxx]" | |
line for the QML element, if there should be one. | |
If there is no class node, or if the class node status | |
is set to Node::Internal, do nothing. | |
*/ | |
void HtmlGenerator::generateQmlInstantiates(const QmlClassNode* qcn, | |
CodeMarker* marker) | |
{ | |
const ClassNode* cn = qcn->classNode(); | |
if (cn && (cn->status() != Node::Internal)) { | |
Text text; | |
text << Atom::ParaLeft; | |
text << Atom(Atom::LinkNode,CodeMarker::stringForNode(qcn)); | |
text << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK); | |
QString name = qcn->name(); | |
if (name.startsWith(QLatin1String("QML:"))) | |
name = name.mid(4); // remove the "QML:" prefix | |
text << Atom(Atom::String, name); | |
text << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK); | |
text << " instantiates the C++ class "; | |
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); | |
text << Atom::ParaRight; | |
generateText(text, qcn, marker); | |
} | |
} | |
/*! | |
Output the "[QmlGraphicsXxx is instantiated by QML element Xxx]" | |
line for the class, if there should be one. | |
If there is no QML element, or if the class node status | |
is set to Node::Internal, do nothing. | |
*/ | |
void HtmlGenerator::generateInstantiatedBy(const ClassNode* cn, | |
CodeMarker* marker) | |
{ | |
if (cn && cn->status() != Node::Internal && !cn->qmlElement().isEmpty()) { | |
const Node* n = myTree->root()->findNode(cn->qmlElement(),Node::Fake); | |
if (n && n->subType() == Node::QmlClass) { | |
Text text; | |
text << Atom::ParaLeft; | |
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); | |
text << " is instantiated by QML element "; | |
text << Atom(Atom::LinkNode,CodeMarker::stringForNode(n)); | |
text << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK); | |
text << Atom(Atom::String, n->name()); | |
text << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK); | |
text << Atom::ParaRight; | |
generateText(text, cn, marker); | |
} | |
} | |
} | |
/*! | |
Generate the <page> element for the given \a node using the \a writer. | |
Return true if a <page> element was written; otherwise return false. | |
*/ | |
bool HtmlGenerator::generatePageElement(QXmlStreamWriter& writer, | |
const Node* node, | |
CodeMarker* marker) const | |
{ | |
if (node->pageType() == Node::NoPageType) | |
return false; | |
if (node->name().isEmpty()) | |
return true; | |
if (node->access() == Node::Private) | |
return false; | |
QString guid = QUuid::createUuid().toString(); | |
QString url = PageGenerator::fileName(node); | |
QString title; | |
QString rawTitle; | |
QString fullTitle; | |
QStringList pageWords; | |
QXmlStreamAttributes attributes; | |
writer.writeStartElement("page"); | |
if (node->isInnerNode()) { | |
const InnerNode* inner = static_cast<const InnerNode*>(node); | |
if (!inner->pageKeywords().isEmpty()) | |
pageWords << inner->pageKeywords(); | |
switch (node->type()) { | |
case Node::Fake: | |
{ | |
const FakeNode* fake = static_cast<const FakeNode*>(node); | |
title = fake->fullTitle(); | |
pageWords << title; | |
break; | |
} | |
case Node::Class: | |
{ | |
title = node->name() + " Class Reference"; | |
pageWords << node->name() << "class" << "reference"; | |
break; | |
} | |
case Node::Namespace: | |
{ | |
rawTitle = marker->plainName(inner); | |
fullTitle = marker->plainFullName(inner); | |
title = rawTitle + " Namespace Reference"; | |
pageWords << rawTitle << "namespace" << "reference"; | |
break; | |
} | |
default: | |
title = node->name(); | |
pageWords << title; | |
break; | |
} | |
} | |
else { | |
switch (node->type()) { | |
case Node::Enum: | |
{ | |
title = node->name() + " Enum Reference"; | |
pageWords << node->name() << "enum" << "type"; | |
url += "#" + node->name() + "-enum"; | |
break; | |
} | |
case Node::Function: | |
{ | |
title = node->name() + " Function Reference"; | |
pageWords << node->name() << "function"; | |
url += "#" + node->name(); | |
break; | |
} | |
case Node::Property: | |
{ | |
title = node->name() + " Property Reference"; | |
pageWords << node->name() << "property"; | |
url += "#" + node->name() + "-prop"; | |
break; | |
} | |
case Node::Typedef: | |
{ | |
title = node->name() + " Type Reference"; | |
pageWords << node->name() << "typedef" << "type"; | |
url += "#" + node->name(); | |
break; | |
} | |
default: | |
title = node->name(); | |
pageWords << title; | |
break; | |
} | |
Node* parent = node->parent(); | |
if (parent && ((parent->type() == Node::Class) || | |
(parent->type() == Node::Namespace))) { | |
pageWords << parent->name(); | |
} | |
} | |
writer.writeAttribute("id",guid); | |
writer.writeStartElement("pageWords"); | |
writer.writeCharacters(pageWords.join(" ")); | |
writer.writeEndElement(); | |
writer.writeStartElement("pageTitle"); | |
writer.writeCharacters(title); | |
writer.writeEndElement(); | |
writer.writeStartElement("pageUrl"); | |
writer.writeCharacters(url); | |
writer.writeEndElement(); | |
writer.writeStartElement("pageType"); | |
switch (node->pageType()) { | |
case Node::ApiPage: | |
writer.writeCharacters("APIPage"); | |
break; | |
case Node::ArticlePage: | |
writer.writeCharacters("Article"); | |
break; | |
case Node::ExamplePage: | |
writer.writeCharacters("Example"); | |
break; | |
default: | |
break; | |
} | |
writer.writeEndElement(); | |
writer.writeEndElement(); | |
if (node->type() == Node::Fake && node->doc().hasTableOfContents()) { | |
QList<Atom*> toc = node->doc().tableOfContents(); | |
if (!toc.isEmpty()) { | |
for (int i = 0; i < toc.size(); ++i) { | |
Text headingText = Text::sectionHeading(toc.at(i)); | |
QString s = headingText.toString(); | |
writer.writeStartElement("page"); | |
guid = QUuid::createUuid().toString(); | |
QString internalUrl = url + "#" + Doc::canonicalTitle(s); | |
writer.writeAttribute("id",guid); | |
writer.writeStartElement("pageWords"); | |
writer.writeCharacters(pageWords.join(" ")); | |
writer.writeCharacters(" "); | |
writer.writeCharacters(s); | |
writer.writeEndElement(); | |
writer.writeStartElement("pageTitle"); | |
writer.writeCharacters(s); | |
writer.writeEndElement(); | |
writer.writeStartElement("pageUrl"); | |
writer.writeCharacters(internalUrl); | |
writer.writeEndElement(); | |
writer.writeStartElement("pageType"); | |
writer.writeCharacters("Article"); | |
writer.writeEndElement(); | |
writer.writeEndElement(); | |
} | |
} | |
} | |
return true; | |
} | |
/*! | |
Traverse the tree recursively and generate the <keyword> | |
elements. | |
*/ | |
void HtmlGenerator::generatePageElements(QXmlStreamWriter& writer, const Node* node, CodeMarker* marker) const | |
{ | |
if (generatePageElement(writer, node, marker)) { | |
if (node->isInnerNode()) { | |
const InnerNode *inner = static_cast<const InnerNode *>(node); | |
// Recurse to write an element for this child node and all its children. | |
foreach (const Node *child, inner->childNodes()) | |
generatePageElements(writer, child, marker); | |
} | |
} | |
} | |
/*! | |
Outputs the file containing the index used for searching the html docs. | |
*/ | |
void HtmlGenerator::generatePageIndex(const QString& fileName) const | |
{ | |
QFile file(fileName); | |
if (!file.open(QFile::WriteOnly | QFile::Text)) | |
return ; | |
CodeMarker *marker = CodeMarker::markerForFileName(fileName); | |
QXmlStreamWriter writer(&file); | |
writer.setAutoFormatting(true); | |
writer.writeStartDocument(); | |
writer.writeStartElement("qtPageIndex"); | |
generatePageElements(writer, myTree->root(), marker); | |
writer.writeEndElement(); // qtPageIndex | |
writer.writeEndDocument(); | |
file.close(); | |
} | |
void HtmlGenerator::generateExtractionMark(const Node *node, ExtractionMarkType markType) | |
{ | |
if (markType != EndMark) { | |
out() << "<!-- $$$" + node->name(); | |
if (markType == MemberMark) { | |
if (node->type() == Node::Function) { | |
const FunctionNode *func = static_cast<const FunctionNode *>(node); | |
if (!func->associatedProperty()) { | |
if (func->overloadNumber() == 1) | |
out() << "[overload1]"; | |
out() << "$$$" + func->name() + func->rawParameters().remove(' '); | |
} | |
} else if (node->type() == Node::Property) { | |
out() << "-prop"; | |
const PropertyNode *prop = static_cast<const PropertyNode *>(node); | |
const NodeList &list = prop->functions(); | |
foreach (const Node *propFuncNode, list) { | |
if (propFuncNode->type() == Node::Function) { | |
const FunctionNode *func = static_cast<const FunctionNode *>(propFuncNode); | |
out() << "$$$" + func->name() + func->rawParameters().remove(' '); | |
} | |
} | |
} else if (node->type() == Node::Enum) { | |
const EnumNode *enumNode = static_cast<const EnumNode *>(node); | |
foreach (const EnumItem &item, enumNode->items()) | |
out() << "$$$" + item.name(); | |
} | |
} else if (markType == BriefMark) { | |
out() << "-brief"; | |
} else if (markType == DetailedDescriptionMark) { | |
out() << "-description"; | |
} | |
out() << " -->\n"; | |
} else { | |
out() << "<!-- @@@" + node->name() + " -->\n"; | |
} | |
} | |
/*! | |
Returns the full document location for HTML-based documentation. | |
*/ | |
QString HtmlGenerator::fullDocumentLocation(const Node *node) | |
{ | |
if (!node) | |
return ""; | |
if (!node->url().isEmpty()) | |
return node->url(); | |
QString parentName; | |
QString anchorRef; | |
if (node->type() == Node::Namespace) { | |
// The root namespace has no name - check for this before creating | |
// an attribute containing the location of any documentation. | |
if (!node->fileBase().isEmpty()) | |
parentName = node->fileBase() + ".html"; | |
else | |
return ""; | |
} | |
else if (node->type() == Node::Fake) { | |
#ifdef QDOC_QML | |
if ((node->subType() == Node::QmlClass) || | |
(node->subType() == Node::QmlBasicType)) { | |
QString fb = node->fileBase(); | |
if (fb.startsWith(Generator::outputPrefix(QLatin1String("QML")))) | |
return fb + ".html"; | |
else | |
return Generator::outputPrefix(QLatin1String("QML")) + node->fileBase() + QLatin1String(".html"); | |
} else | |
#endif | |
parentName = node->fileBase() + ".html"; | |
} | |
else if (node->fileBase().isEmpty()) | |
return ""; | |
Node *parentNode = 0; | |
if ((parentNode = node->relates())) | |
parentName = fullDocumentLocation(node->relates()); | |
else if ((parentNode = node->parent())) { | |
if (parentNode->subType() == Node::QmlPropertyGroup) { | |
parentNode = parentNode->parent(); | |
parentName = fullDocumentLocation(parentNode); | |
} | |
else | |
parentName = fullDocumentLocation(node->parent()); | |
} | |
switch (node->type()) { | |
case Node::Class: | |
case Node::Namespace: | |
if (parentNode && !parentNode->name().isEmpty()) | |
parentName = parentName.replace(".html", "") + "-" | |
+ node->fileBase().toLower() + ".html"; | |
else | |
parentName = node->fileBase() + ".html"; | |
break; | |
case Node::Function: | |
{ | |
/* | |
Functions can be destructors, overloaded, or | |
have associated properties. | |
*/ | |
const FunctionNode *functionNode = | |
static_cast<const FunctionNode *>(node); | |
if (functionNode->metaness() == FunctionNode::Dtor) | |
anchorRef = "#dtor." + functionNode->name().mid(1); | |
else if (functionNode->associatedProperty()) | |
return fullDocumentLocation(functionNode->associatedProperty()); | |
else if (functionNode->overloadNumber() > 1) | |
anchorRef = "#" + functionNode->name() | |
+ "-" + QString::number(functionNode->overloadNumber()); | |
else | |
anchorRef = "#" + functionNode->name(); | |
} | |
/* | |
Use node->name() instead of node->fileBase() as | |
the latter returns the name in lower-case. For | |
HTML anchors, we need to preserve the case. | |
*/ | |
break; | |
case Node::Enum: | |
anchorRef = "#" + node->name() + "-enum"; | |
break; | |
case Node::Typedef: | |
anchorRef = "#" + node->name() + "-typedef"; | |
break; | |
case Node::Property: | |
anchorRef = "#" + node->name() + "-prop"; | |
break; | |
case Node::QmlProperty: | |
anchorRef = "#" + node->name() + "-prop"; | |
break; | |
case Node::QmlSignal: | |
anchorRef = "#" + node->name() + "-signal"; | |
break; | |
case Node::QmlMethod: | |
anchorRef = "#" + node->name() + "-method"; | |
break; | |
case Node::Variable: | |
anchorRef = "#" + node->name() + "-var"; | |
break; | |
case Node::Target: | |
anchorRef = "#" + Doc::canonicalTitle(node->name()); | |
break; | |
case Node::Fake: | |
{ | |
/* | |
Use node->fileBase() for fake nodes because they are represented | |
by pages whose file names are lower-case. | |
*/ | |
parentName = node->fileBase(); | |
parentName.replace("/", "-").replace(".", "-"); | |
parentName += ".html"; | |
} | |
break; | |
default: | |
break; | |
} | |
// Various objects can be compat (deprecated) or obsolete. | |
if (node->type() != Node::Class && node->type() != Node::Namespace) { | |
switch (node->status()) { | |
case Node::Compat: | |
parentName.replace(".html", "-qt3.html"); | |
break; | |
case Node::Obsolete: | |
parentName.replace(".html", "-obsolete.html"); | |
break; | |
default: | |
; | |
} | |
} | |
return parentName.toLower() + anchorRef; | |
} | |
#endif | |
QT_END_NAMESPACE |