/**************************************************************************** ** ** 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 #include #include #include #include #include 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=\"([^\"]+)\">).*()"); static QRegExp funcTag("(<@func target=\"([^\"]*)\">)(.*)()"); static QRegExp typeTag("(<@(type|headerfile|func)(?: +[^>]*)?>)(.*)()"); static QRegExp spanTag(""); static QRegExp unknownTag("]*>"); static void addLink(const QString &linkTarget, const QStringRef &nestedStuff, QString *res) { if (!linkTarget.isEmpty()) { *res += ""; *res += nestedStuff; *res += ""; } 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, "", "" }, { ATOM_FORMATTING_INDEX, "" }, { ATOM_FORMATTING_ITALIC, "", "" }, { ATOM_FORMATTING_PARAMETER, "", "" }, { ATOM_FORMATTING_SUBSCRIPT, "", "" }, { ATOM_FORMATTING_SUPERSCRIPT, "", "" }, { ATOM_FORMATTING_TELETYPE, "", "" }, { ATOM_FORMATTING_UNDERLINE, "", "" }, { 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); noBreadCrumbs = config.getBool(HtmlGenerator::format() + Config::dot + HTMLGENERATOR_NOBREADCRUMBS); 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 editionNames = config.subVars(CONFIG_EDITION); QSet::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); generateManifestFiles(); } 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) { if (relative->subType() != Node::Example) { skipAhead = skipAtoms(atom, Atom::BriefRight); break; } } out() << "

"; 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() << "

\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() << "

"; in_para = true; break; case Atom::CaptionRight: endLink(); if (in_para) { out() << "

\n"; in_para = false; } break; case Atom::Code: out() << "
"
              << trimmedTrailing(highlightedCode(indent(codeIndent,atom->string()),
                                                 marker,relative))
              << "
\n"; break; #ifdef QDOC_QML case Atom::Qml: out() << "
"
              << trimmedTrailing(highlightedCode(indent(codeIndent,atom->string()),
                                                 marker,relative))
              << "
\n"; break; case Atom::JavaScript: out() << "
"
              << trimmedTrailing(highlightedCode(indent(codeIndent,atom->string()),
                                                 marker,relative))
              << "
\n"; break; #endif case Atom::CodeNew: out() << "

you can rewrite it as

\n" << "
"
              << trimmedTrailing(highlightedCode(indent(codeIndent,atom->string()),
                                                 marker,relative))
              << "
\n"; break; case Atom::CodeOld: out() << "

For example, if you have code like

\n"; // fallthrough case Atom::CodeBad: out() << "
"
              << trimmedTrailing(protectEnc(plainCode(indent(codeIndent,atom->string()))))
              << "
\n"; break; case Atom::DivLeft: out() << "string().isEmpty()) out() << " " << atom->string(); out() << ">"; break; case Atom::DivRight: out() << ""; break; case Atom::FootnoteLeft: // ### For now if (in_para) { out() << "

\n"; in_para = false; } 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) << "" << subscriptRegExp.cap(2) << ""; skipAhead = 1; } } } break; case Atom::FormattingRight: if (atom->string() == ATOM_FORMATTING_LINK) { endLink(); } else if (atom->string().startsWith("span ")) { out() << ""; } else { out() << formattingRightMap()[atom->string()]; } break; case Atom::AnnotatedList: { QList 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 groups = myTree->groups(); foreach (const QString &groupName, editionGroupMap[editionName]) { QList 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(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(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 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
sections; QList
::ConstIterator s; for (int i=0; itype()) { 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(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() << "
    \n"; s = sections.constBegin(); while (s != sections.constEnd()) { if (!(*s).members.isEmpty()) { out() << "
  • " << "" << (*s).name << "
  • \n"; } ++s; } out() << "
\n"; int idx = 0; s = sections.constBegin(); while (s != sections.constEnd()) { if (!(*s).members.isEmpty()) { out() << "\n"; out() << "

" << protectEnc((*s).name) << "

\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() << "

Class "; out() << ""; QStringList pieces = fullName(pmap.key(), 0, marker).split("::"); out() << protectEnc(pieces.last()); out() << "" << ":

\n"; generateSection(nlist, 0, marker, CodeMarker::Summary); out() << "
"; ++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() << "

"; if (fileName.isEmpty()) { out() << "[Missing image " << protectEnc(atom->string()) << "]"; } else { out() << "\"""; helpProjectWriter->addExtraFile(fileName); if ((relative->type() == Node::Fake) && (relative->subType() == Node::Example)) { const ExampleNode* cen = static_cast(relative); if (cen->imageFileName().isEmpty()) { ExampleNode* en = const_cast(cen); en->setImageFileName(fileName); ExampleNode::exampleNodeMap.insert(en->title(),en); } } } if (atom->type() == Atom::Image) out() << "

"; } break; case Atom::ImageText: break; case Atom::LegaleseLeft: out() << "
"; break; case Atom::LegaleseRight: out() << "
"; break; case Atom::LineBreak: out() << "
"; 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() << "

\n"; in_para = false; } if (atom->string() == ATOM_LIST_BULLET) { out() << "
    \n"; } else if (atom->string() == ATOM_LIST_TAG) { out() << "
    \n"; } else if (atom->string() == ATOM_LIST_VALUE) { threeColumnEnumValueTable = isThreeColumnEnumValueTable(atom); if (threeColumnEnumValueTable) { out() << ""; if (++numTableRows % 2 == 1) out() << ""; else out() << ""; out() << "" << "" << "\n"; } else { out() << "
    ConstantValueDescription
    " << "\n"; } } else { out() << "
      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() << "
      "; } else { // (atom->string() == ATOM_LIST_VALUE) // ### Trenton out() << "
    \n"; } else { out() << "\n"; } break; case Atom::ListRight: if (atom->string() == ATOM_LIST_BULLET) { out() << "\n"; } else if (atom->string() == ATOM_LIST_TAG) { out() << "\n"; } else if (atom->string() == ATOM_LIST_VALUE) { out() << "
    ConstantValue
    " << protectEnc(plainCode(marker->markedUpEnumValue(atom->next()->string(), relative))) << ""; QString itemValue; if (relative->type() == Node::Enum) { const EnumNode *enume = static_cast(relative); itemValue = enume->itemValue(atom->next()->string()); } if (itemValue.isEmpty()) out() << "?"; else out() << "" << protectEnc(itemValue) << ""; skipAhead = 1; } break; case Atom::ListTagRight: if (atom->string() == ATOM_LIST_TAG) out() << "\n"; break; case Atom::ListItemLeft: if (atom->string() == ATOM_LIST_TAG) { out() << "
    "; } else if (atom->string() == ATOM_LIST_VALUE) { if (threeColumnEnumValueTable) { out() << "
    "; if (matchAhead(atom, Atom::ListItemRight)) out() << " "; } } else { out() << "
  • "; } if (matchAhead(atom, Atom::ParaLeft)) skipAhead = 1; break; case Atom::ListItemRight: if (atom->string() == ATOM_LIST_TAG) { out() << "\n"; } else if (atom->string() == ATOM_LIST_VALUE) { out() << "
  • \n"; } else { out() << "\n"; } break; case Atom::Nop: break; case Atom::ParaLeft: out() << "

    "; in_para = true; break; case Atom::ParaRight: endLink(); if (in_para) { out() << "

    \n"; in_para = false; } //if (!matchAhead(atom, Atom::ListItemRight) && !matchAhead(atom, Atom::TableItemRight)) // out() << "

    \n"; break; case Atom::QuotationLeft: out() << "
    "; break; case Atom::QuotationRight: out() << "
    \n"; break; case Atom::RawString: out() << atom->string(); break; case Atom::SectionLeft: out() << "" << divNavTop << "\n"; break; case Atom::SectionRight: break; case Atom::SectionHeadingLeft: out() << "string().toInt() + hOffset(relative)) + ">"; inSectionHeading = true; break; case Atom::SectionHeadingRight: out() << "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() << "

    \n"; in_para = false; } if (!atom->string().isEmpty()) { if (atom->string().contains("%")) { out() << "string() << "\">\n "; } else { out() << "
    \n"; } } else { out() << "
    \n"; } numTableRows = 0; break; case Atom::TableRight: out() << "
    \n"; break; case Atom::TableHeaderLeft: out() << ""; inTableHeader = true; break; case Atom::TableHeaderRight: out() << ""; if (matchAhead(atom, Atom::TableHeaderLeft)) { skipAhead = 1; out() << "\n"; } else { out() << "\n"; inTableHeader = false; } break; case Atom::TableRowLeft: if (!atom->string().isEmpty()) out() << "string() << ">"; else if (++numTableRows % 2 == 1) out() << ""; else out() << ""; break; case Atom::TableRowRight: out() << "\n"; break; case Atom::TableItemLeft: { if (inTableHeader) out() << "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() << ">

    "; } if (matchAhead(atom, Atom::ParaLeft)) skipAhead = 1; } break; case Atom::TableItemRight: if (inTableHeader) out() << ""; else { out() << ""; //out() << "

    "; } if (matchAhead(atom, Atom::ParaLeft)) skipAhead = 1; break; case Atom::TableOfContents: break; case Atom::Target: out() << "string()) << "\">"; break; case Atom::UnhandledFormat: out() << "<Missing HTML>"; break; case Atom::UnknownCommand: out() << "\\" << protectEnc(atom->string()) << ""; 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
    sections; QList
    ::ConstIterator s; const ClassNode *classe = 0; const NamespaceNode *namespasse = 0; QString title; QString rawTitle; QString fullTitle; if (inner->type() == Node::Namespace) { namespasse = static_cast(inner); rawTitle = marker->plainName(inner); fullTitle = marker->plainFullName(inner); title = rawTitle + " Namespace"; } else if (inner->type() == Node::Class) { classe = static_cast(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() << "\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() << "
    \n"; out() << "" << divNavTop << "\n"; out() << "

    " << protectEnc((*s).name) << "

    \n"; generateSection(s->members, inner, marker, CodeMarker::Summary); } if (!s->reimpMembers.isEmpty()) { QString name = QString("Reimplemented ") + (*s).name; // out() << "
    \n"; out() << "" << divNavTop << "\n"; out() << "

    " << protectEnc(name) << "

    \n"; generateSection(s->reimpMembers, inner, marker, CodeMarker::Summary); } if (!s->inherited.isEmpty()) { out() << "
      \n"; generateSectionInheritedList(*s, inner, marker); out() << "
    \n"; } } ++s; } if (needOtherSection) { out() << "

    Additional Inherited Members

    \n" "
      \n"; s = sections.begin(); while (s != sections.end()) { if (s->members.isEmpty() && !s->inherited.isEmpty()) generateSectionInheritedList(*s, inner, marker); ++s; } out() << "
    \n"; } out() << "" << divNavTop << "\n"; if (!inner->doc().isEmpty()) { generateExtractionMark(inner, DetailedDescriptionMark); //out() << "
    \n" out() << "
    \n" // QTBUG-9504 << "

    " << "Detailed Description" << "

    \n"; generateBody(inner, marker); out() << "
    \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() << "
    \n"; if (!(*s).divClass.isEmpty()) out() << "
    \n"; // QTBUG-9504 out() << "

    " << protectEnc((*s).name) << "

    \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() << "

    class "; generateFullName(*m, inner, marker); out() << "

    "; generateBrief(*m, marker, inner); } QStringList names; names << (*m)->name(); if ((*m)->type() == Node::Function) { const FunctionNode *func = reinterpret_cast(*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(*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(*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() << "
    \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
    sections; QList
    ::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(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() << "" << divNavTop << "\n"; out() << "

    Namespaces

    \n"; generateAnnotatedList(fake, marker, moduleNamespaceMap[fake->name()]); } if (moduleClassMap.contains(fake->name())) { out() << "" << divNavTop << "\n"; out() << "

    Classes

    \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() << "\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() << "\n"; } s = sections.begin(); while (s != sections.end()) { out() << "" << divNavTop << "\n"; out() << "

    " << protectEnc((*s).name) << "

    \n"; generateQmlSummary(*s,fake,marker); ++s; } generateExtractionMark(fake, DetailedDescriptionMark); out() << "" << divNavTop << "\n"; out() << "

    " << "Detailed Description" << "

    \n"; generateBody(fake, marker); if (cn) generateQmlText(cn->doc().body(), cn, marker, fake->name()); generateAlsoList(fake, marker); generateExtractionMark(fake, EndMark); //out() << "
    \n"; sections = marker->qmlSections(qml_cn,CodeMarker::Detailed,0); s = sections.begin(); while (s != sections.end()) { out() << "

    " << protectEnc((*s).name) << "

    \n"; NodeList::ConstIterator m = (*s).members.begin(); while (m != (*s).members.end()) { generateDetailedQmlMember(*m, fake, marker); out() << "
    \n"; ++m; } ++s; } generateFooter(fake); return; } #endif sections = marker->sections(fake, CodeMarker::Summary, CodeMarker::Okay); s = sections.begin(); while (s != sections.end()) { out() << "" << divNavTop << "\n"; out() << "

    " << protectEnc((*s).name) << "

    \n"; generateSectionList(*s, fake, marker, CodeMarker::Summary); ++s; } Text brief = fake->doc().briefText(); if (fake->subType() == Node::Module && !brief.isEmpty()) { generateExtractionMark(fake, DetailedDescriptionMark); out() << "" << divNavTop << "\n"; out() << "
    \n"; // QTBUG-9504 out() << "

    " << "Detailed Description" << "

    \n"; } else { generateExtractionMark(fake, DetailedDescriptionMark); out() << "
    \n"; // QTBUG-9504 } generateBody(fake, marker); out() << "
    \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() << "
    \n"; out() << "

    " << protectEnc((*s).name) << "

    \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) { if (noBreadCrumbs) return; Text breadcrumbs; if (node->type() == Node::Class) { const ClassNode *cn = static_cast(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(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("\n").arg(outputEncoding); out() << "\n"; out() << QString("\n").arg(naturalLanguage); out() << "\n"; out() << " \n"; if (node && !node->doc().location().isEmpty()) out() << "\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() << " " << shortVersion << protectEnc(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 linkPair; QPair 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() << " \n"; navigationLinks += "[Previous: "; if (linkPair.first == linkPair.second && !anchorPair.second.isEmpty()) navigationLinks += protect(anchorPair.second); else navigationLinks += protect(linkPair.second); navigationLinks += "]\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() << " \n"; navigationLinks += "[Next: "; if (linkPair.first == linkPair.second && !anchorPair.second.isEmpty()) navigationLinks += protect(anchorPair.second); else navigationLinks += protect(linkPair.second); navigationLinks += "]\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() << " \n"; } } if (node && !node->links().empty()) out() << "

    \n" << navigationLinks << "

    \n"; } void HtmlGenerator::generateTitle(const QString& title, const Text &subTitle, SubTitleSize subTitleSize, const Node *relative, CodeMarker *marker) { if (!title.isEmpty()) out() << "

    " << protectEnc(title) << "

    \n"; if (!subTitle.isEmpty()) { out() << ""; else out() << " class=\"subtitle\">"; generateText(subTitle, relative, marker); out() << "\n"; } } void HtmlGenerator::generateFooter(const Node *node) { if (node && !node->links().empty()) out() << "

    \n" << navigationLinks << "

    \n"; out() << QString(footer).replace("\\" + COMMAND_VERSION, myTree->version()) << QString(address).replace("\\" + COMMAND_VERSION, myTree->version()); out() << "\n"; out() << "\n"; } void HtmlGenerator::generateBrief(const Node *node, CodeMarker *marker, const Node *relative) { Text brief = node->doc().briefText(); if (!brief.isEmpty()) { generateExtractionMark(node, BriefMark); out() << "

    "; generateText(brief, node, marker); if (!relative || node == relative) out() << " More...

    \n"; generateExtractionMark(node, EndMark); } } void HtmlGenerator::generateIncludes(const InnerNode *inner, CodeMarker *marker) { if (!inner->includes().isEmpty()) { out() << "
    "
                  << trimmedTrailing(highlightedCode(indent(codeIndent,
                                                            marker->markedUpIncludes(inner->includes())),
                                                     marker,inner))
                  << "
    "; } } /*! Revised for the new doc format. Generates a table of contents beginning at \a node. */ void HtmlGenerator::generateTableOfContents(const Node *node, CodeMarker *marker, QList
    * sections) { QList toc; if (node->doc().hasTableOfContents()) toc = node->doc().tableOfContents(); if (toc.isEmpty() && !sections && (node->subType() != Node::Module)) return; QStringList sectionNumber; int detailsBase = 0; // disable nested links in table of contents inContents = true; inLink = true; out() << "
    \n"; out() << "

    Contents

    \n"; sectionNumber.append("1"); out() << "
      \n"; if (node->subType() == Node::Module) { if (moduleNamespaceMap.contains(node->name())) { out() << "
    • Namespaces
    • \n"; } if (moduleClassMap.contains(node->name())) { out() << "
    • Classes
    • \n"; } out() << "
    • Detailed Description
    • \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
      ::ConstIterator s = sections->begin(); while (s != sections->end()) { if (!s->members.isEmpty() || !s->reimpMembers.isEmpty()) { out() << "
    • " << (*s).name << "
    • \n"; } ++s; } out() << "
    • Detailed Description
    • \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() << "
    • "; out() << ""; generateAtomList(headingText.firstAtom(), node, marker, true, numAtoms); out() << "
    • \n"; } while (!sectionNumber.isEmpty()) { sectionNumber.removeLast(); } out() << "
    \n"; out() << "
    \n"; inContents = false; inLink = false; } QString HtmlGenerator::generateListOfAllMemberFile(const InnerNode *inner, CodeMarker *marker) { QList
    sections; QList
    ::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() << "

    This is the complete list of members for "; generateFullName(inner, 0, marker); out() << ", including inherited members.

    \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
    sections; QList
    ::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() << "

    This is the complete list of members for "; generateFullName(qml_cn, 0, marker); out() << ", including inherited members.

    \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
    sections = marker->sections(inner, CodeMarker::Summary, status); QMutableListIterator
    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() << "

    The following class members are part of the " "Qt 3 support layer. " "They are provided to help you port old code to Qt 4. We advise against " "using them in new code.

    \n"; } else { out() << "

    The following class members are obsolete. " << "They are provided to keep old source code working. " << "We strongly advise against using them in new code.

    \n"; } out() << "

    \n"; for (i = 0; i < sections.size(); ++i) { out() << "

    " << protectEnc(sections.at(i).name) << "

    \n"; generateSectionList(sections.at(i), inner, marker, CodeMarker::Summary); } sections = marker->sections(inner, CodeMarker::Detailed, status); for (i = 0; i < sections.size(); ++i) { //out() << "
    \n"; out() << "

    " << protectEnc(sections.at(i).name) << "

    \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 &classMap) { if (classMap.isEmpty()) return; NodeMap topLevel; NodeMap::ConstIterator c = classMap.begin(); while (c != classMap.end()) { const ClassNode *classe = static_cast(*c); if (classe->baseClasses().isEmpty()) topLevel.insert(classe->name(), classe); ++c; } QStack stack; stack.push(topLevel); out() << "
      \n"; while (!stack.isEmpty()) { if (stack.top().isEmpty()) { stack.pop(); out() << "
    \n"; } else { const ClassNode *child = static_cast(*stack.top().begin()); out() << "
  • "; generateFullName(child, relative, marker); out() << "
  • \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() << "
      \n"; } } } } void HtmlGenerator::generateAnnotatedList(const Node *relative, CodeMarker *marker, const NodeMap &nodeMap) { out() << "\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() << ""; else out() << ""; out() << ""; if (!(node->type() == Node::Fake)) { Text brief = node->doc().trimmedBriefText(name); if (!brief.isEmpty()) { out() << ""; } } else { out() << ""; } out() << "\n"; } out() << "

      "; generateFullName(node, relative, marker); out() << "

      "; generateText(brief, node, marker); out() << "

      "; out() << protectEnc(node->doc().briefText().toString()); out() << "

      \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 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"; for (int i = 0; i < 26; i++) { QChar ch('a' + i); if (usedParagraphNames.contains(char('a' + i))) out() << QString("%2 ").arg(ch).arg(ch.toUpper()); } out() << "

      \n"; } /* Output a
      element to contain all the
      elements. */ out() << "
      \n"; numTableRows = 0; int curParNr = 0; int curParOffset = 0; for (int i=0; i. */ if (curParOffset == 0) { if (i > 0) out() << "
      \n"; if (++numTableRows % 2 == 1) out() << "
      "; else out() << "
      "; out() << "
      "; if (includeAlphabet) { QChar c = paragraphName[curParNr][0].toLower(); out() << QString("").arg(c); } out() << "" << paragraphName[curParNr] << ""; out() << "
      \n"; } /* Output a
      for the current offset in the current paragraph. */ out() << "
      "; if ((curParNr < NumParagraphs) && !paragraphName[curParNr].isEmpty()) { NodeMap::Iterator it; it = paragraph[curParNr].begin(); for (int i=0; i"; 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() << ""; if (pieces.size() > 1) { out() << " ("; generateFullName(it.value()->parent(), relative, marker); out() << ")"; } } out() << "
      \n"; curParOffset++; } if (classMap.count() > 0) out() << "
      \n"; out() << "
      \n"; } void HtmlGenerator::generateFunctionIndex(const Node *relative, CodeMarker *marker) { out() << "

      "; for (int i = 0; i < 26; i++) { QChar ch('a' + i); out() << QString("%2 ").arg(ch).arg(ch.toUpper()); } out() << "

      \n"; char nextLetter = 'a'; char currentLetter; #if 1 out() << "
        \n"; #endif QMap::ConstIterator f = funcIndex.begin(); while (f != funcIndex.end()) { #if 1 out() << "
      • "; #else out() << "

        "; #endif out() << protectEnc(f.key()) << ":"; currentLetter = f.key()[0].unicode(); while (islower(currentLetter) && currentLetter >= nextLetter) { out() << QString("").arg(nextLetter); nextLetter++; } NodeMap::ConstIterator s = (*f).begin(); while (s != (*f).end()) { out() << " "; generateFullName((*s)->parent(), relative, marker, *s); ++s; } #if 1 out() << "

      • "; #else out() << "

        "; #endif out() << "\n"; ++f; } #if 1 out() << "
      \n"; #endif } void HtmlGenerator::generateLegaleseList(const Node *relative, CodeMarker *marker) { QMap::ConstIterator it = legaleseTexts.begin(); while (it != legaleseTexts.end()) { Text text = it.key(); //out() << "
      \n"; generateText(text, relative, marker); out() << "
        \n"; do { out() << "
      • "; generateFullName(it.value(), relative, marker); out() << "
      • \n"; ++it; } while (it != legaleseTexts.end() && it.key() == text); out() << "
      \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])"), "\\1\\2"); marked.replace("<@param>", ""); marked.replace("", ""); if (summary) marked.replace("@name>", "b>"); marked.replace("<@extra>", ""); marked.replace("", ""); if (summary) { marked.replace("<@type>", ""); marked.replace("", ""); } out() << highlightedCode(marked, marker, relative, false, node); } #endif void HtmlGenerator::generateOverviewList(const Node *relative, CodeMarker * /* marker */) { QMap > fakeNodeMap; QMap groupTitlesMap; QMap 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(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(member); if (page) { QString sortKey = page->fullTitle().toLower(); if (sortKey.startsWith("the ")) sortKey.remove(0, 4); sortKey.replace(singleDigit, "0\\1"); fakeNodeMap[const_cast(fakeNode)].insert(sortKey, page); groupTitlesMap[fakeNode->fullTitle()] = const_cast(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(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("

      %2

      \n").arg( linkForNode(groupNode, relative)).arg( protectEnc(groupNode->fullTitle())); if (fakeNodeMap[groupNode].count() == 0) continue; out() << "
        \n"; foreach (const FakeNode *fakeNode, fakeNodeMap[groupNode]) { QString title = fakeNode->fullTitle(); if (title.startsWith("The ")) title.remove(0, 4); out() << "
      • " << protectEnc(title) << "
      • \n"; } out() << "
      \n"; } } if (!uncategorizedNodeMap.isEmpty()) { out() << QString("

      Miscellaneous

      \n"); out() << "
        \n"; foreach (const FakeNode *fakeNode, uncategorizedNodeMap) { QString title = fakeNode->fullTitle(); if (title.startsWith("The ")) title.remove(0, 4); out() << "
      • " << protectEnc(title) << "
      • \n"; } out() << "
      \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() << "\n"; } else { if (twoColumn) out() << "
      \n" << "\n"; else out() << "\n"; i++; ++m; } if (alignNames) out() << "
      "; out() << "
        \n"; } int i = 0; NodeList::ConstIterator m = nl.begin(); while (m != nl.end()) { if ((*m)->access() == Node::Private) { ++m; continue; } if (alignNames) { out() << "
      "; } else { if (twoColumn && i == (int) (nl.count() + 1) / 2) out() << "
        \n"; out() << "
      • "; } generateSynopsis(*m, relative, marker, style, alignNames); if (alignNames) out() << "
      \n"; else { out() << "
    \n"; if (twoColumn) out() << "\n\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() << "\n"; } else { if (twoColumn) out() << "
    \n" << "\n"; else out() << "\n"; i++; ++m; } if (alignNames) out() << "
    "; out() << "
      \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() << "
    "; } else { if (twoColumn && i == (int) (section.members.count() + 1) / 2) out() << "
      \n"; out() << "
    • "; } generateSynopsis(*m, relative, marker, style, alignNames); if (alignNames) out() << "
    \n"; else { out() << "
\n"; if (twoColumn) out() << "\n\n"; } } if (style == CodeMarker::Summary && !section.inherited.isEmpty()) { out() << "
    \n"; generateSectionInheritedList(section, relative, marker); out() << "
\n"; } } void HtmlGenerator::generateSectionInheritedList(const Section& section, const Node *relative, CodeMarker *marker) { QList >::ConstIterator p = section.inherited.begin(); while (p != section.inherited.end()) { out() << "
  • "; out() << (*p).second << " "; if ((*p).second == 1) { out() << section.singularMember; } else { out() << section.pluralMember; } out() << " inherited from " << protectEnc(marker->plainFullName((*p).first, relative)) << "
  • \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])"), "\\1\\2"); marked.replace("<@param>", ""); marked.replace("", ""); if (style == CodeMarker::Summary) { marked.replace("<@name>", ""); // was "" marked.replace("", ""); // was "" } if (style == CodeMarker::SeparateList) { QRegExp extraRegExp("<@extra>.*"); extraRegExp.setMinimal(true); marked.replace(extraRegExp, ""); } else { marked.replace("<@extra>", ""); marked.replace("", ""); } if (style != CodeMarker::Detailed) { marked.replace("<@type>", ""); marked.replace("", ""); } 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=\"([^\"]+)\">).*()" 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 += ""; done = true; } i += 2; if (parseArg(src, linkTag, &i, srcSize, &arg, &par1)) { html += ""; const Node* n = CodeMarker::nodeForString(par1.toString()); QString link = linkForNode(n, relative); addLink(link, arg, &html); html += ""; } else { html += charLangle; html += charAt; } } else { html += src.at(i++); } } // replace all <@func> tags: "(<@func target=\"([^\"]*)\">)(.*)()" 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)(?: +[^>]*)?>)(.*)()" tags src = html; html = QString(); for (int i=0, srcSize=src.size(); iresolveTarget(arg.toString(), myTree, relative, self); html += QLatin1String(""); 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(""); 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>" -> ""; // "<@preprocessor>" -> ""; // "<@string>" -> ""; // "<@char>" -> ""; // "<@number>" -> ""; // "<@op>" -> ""; // "<@type>" -> ""; // "<@name>" -> ""; // "<@keyword>" -> ""; // "" -> "" src = html; html = QString(); static const QString spanTags[] = { "<@comment>", "", "<@preprocessor>", "", "<@string>", "", "<@char>", "", "<@number>", "", "<@op>", "", "<@type>", "", "<@name>", "", "<@keyword>", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", }; // 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() << ""; } else { out() << ""; } 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(node)->subType() == Node::ExternalPage) return node->name(); if (static_cast(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(node); if (typedeffe->associatedEnum()) { return refForNode(typedeffe->associatedEnum()); } else { ref = node->name() + "-typedef"; } break; case Node::Function: func = static_cast(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() << "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() << ""; } 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(node))->flagsType()) { #ifdef GENERATE_MAC_REFS generateMacRef(enume->flagsType(), marker); #endif out() << "

    "; out() << ""; generateSynopsis(enume, relative, marker, CodeMarker::Detailed); out() << "
    "; generateSynopsis(enume->flagsType(), relative, marker, CodeMarker::Detailed); out() << "

    \n"; } else { out() << "

    "; out() << ""; generateSynopsis(node, relative, marker, CodeMarker::Detailed); out() << "

    " << divNavTop << "\n"; } generateStatus(node, marker); generateBody(node, marker); generateThreadSafeness(node, marker); generateSince(node, marker); if (node->type() == Node::Property) { const PropertyNode *property = static_cast(node); Section section; section.members += property->getters(); section.members += property->setters(); section.members += property->resetters(); if (!section.members.isEmpty()) { out() << "

    Access functions:

    \n"; generateSectionList(section, node, marker, CodeMarker::Accessors); } Section notifiers; notifiers.members += property->notifiers(); if (!notifiers.members.isEmpty()) { out() << "

    Notifier signal:

    \n"; //out() << "

    This signal is emitted when the property value is changed.

    \n"; generateSectionList(notifiers, node, marker, CodeMarker::Accessors); } } else if (node->type() == Node::Enum) { const EnumNode *enume = static_cast(node); if (enume->flagsType()) { out() << "

    The " << protectEnc(enume->flagsType()->name()) << " type is a typedef for " << "QFlags<" << protectEnc(enume->name()) << ">. It stores an OR combination of " << protectEnc(enume->name()) << " values.

    \n"; } } generateAlsoList(node, marker); generateExtractionMark(node, EndMark); } 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(*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(*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(*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(*c)); } else if ((*c)->type() == Node::Function) { const FunctionNode *func = static_cast(*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(*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(*c)); if ((*c)->type() == Node::Namespace) { const NamespaceNode *nspace = static_cast(*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)); } } if (!node) relative->doc().location().warning(tr("Cannot link to '%1'").arg(target)); return node; } const QPair HtmlGenerator::anchorForNode(const Node *node) { QPair anchorPair; anchorPair.first = PageGenerator::fileName(node); if (node->type() == Node::Fake) { const FakeNode *fakeNode = static_cast(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(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() << "\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() << ""; } else if (node == 0 || (relative != 0 && node->status() == relative->status())) { out() << ""; } else { switch (node->status()) { case Node::Obsolete: out() << ""; break; case Node::Compat: out() << ""; break; default: out() << ""; } } inLink = true; } void HtmlGenerator::endLink() { if (inLink) { if (link.isEmpty()) { if (showBrokenLinks) out() << ""; } else { if (inObsoleteLink) { out() << "(obsolete)"; } out() << ""; } } inLink = false; inObsoleteLink = false; } /*! 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() << "
      \n"; NodeList::ConstIterator m; m = section.members.begin(); while (m != section.members.end()) { out() << "
    • "; generateQmlItem(*m,relative,marker,true); out() << "
    • \n"; ++m; } out() << "
    \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() << "
    "; if (node->subType() == Node::QmlPropertyGroup) { const QmlPropGroupNode* qpgn = static_cast(node); NodeList::ConstIterator p = qpgn->childNodes().begin(); out() << "
    "; out() << ""; while (p != qpgn->childNodes().end()) { if ((*p)->type() == Node::QmlProperty) { qpn = static_cast(*p); if (++numTableRows % 2 == 1) out() << ""; else out() << ""; out() << ""; } ++p; } out() << "

    "; out() << ""; const ClassNode* cn = qpn->declarativeCppNode(); if (cn && !qpn->isWritable(myTree)) { out() << "read-only"; } if (qpgn->isDefault()) out() << "default"; generateQmlItem(qpn, relative, marker, false); out() << "

    "; out() << "
    "; } else if (node->type() == Node::QmlSignal) { const FunctionNode* qsn = static_cast(node); out() << "
    "; out() << ""; //out() << ""; if (++numTableRows % 2 == 1) out() << ""; else out() << ""; out() << ""; out() << "

    "; out() << ""; generateSynopsis(qsn,relative,marker,CodeMarker::Detailed,false); //generateQmlItem(qsn,relative,marker,false); out() << "

    "; out() << "
    "; } else if (node->type() == Node::QmlMethod) { const FunctionNode* qmn = static_cast(node); out() << "
    "; out() << ""; //out() << ""; if (++numTableRows % 2 == 1) out() << ""; else out() << ""; out() << ""; out() << "

    "; out() << ""; generateSynopsis(qmn,relative,marker,CodeMarker::Detailed,false); out() << "

    "; out() << "
    "; } out() << "
    "; generateStatus(node, marker); generateBody(node, marker); generateThreadSafeness(node, marker); generateSince(node, marker); generateAlsoList(node, marker); out() << "
    "; out() << "
    "; 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 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(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 element for the given \a node using the \a writer. Return true if a 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(node); if (!inner->pageKeywords().isEmpty()) pageWords << inner->pageKeywords(); switch (node->type()) { case Node::Fake: { const FakeNode* fake = static_cast(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 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 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(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() << "\n"; } else { out() << "\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) { 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 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(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; } /*! This function outputs one or more manifest files in XML. They are used by Creator. */ void HtmlGenerator::generateManifestFiles() { generateManifestFile("examples", "example"); generateManifestFile("demos", "demo"); ExampleNode::exampleNodeMap.clear(); } /*! This function is called by generaqteManiferstFile(), once for each manifest file to be generated. \a manifest is the type of manifest file. */ void HtmlGenerator::generateManifestFile(QString manifest, QString element) { if (ExampleNode::exampleNodeMap.isEmpty()) return; QString fileName = manifest +"-manifest.xml"; QFile file(outputDir() + "/" + fileName); if (!file.open(QFile::WriteOnly | QFile::Text)) return ; bool demos = false; if (manifest == "demos") demos = true; bool proceed = false; ExampleNodeMap::Iterator i = ExampleNode::exampleNodeMap.begin(); while (i != ExampleNode::exampleNodeMap.end()) { const ExampleNode* en = i.value(); if (demos) { if (en->name().startsWith("demos")) { proceed = true; break; } } else if (!en->name().startsWith("demos")) { proceed = true; break; } ++i; } if (!proceed) return; QXmlStreamWriter writer(&file); writer.setAutoFormatting(true); writer.writeStartDocument(); writer.writeStartElement("instructionals"); writer.writeAttribute("module", project); writer.writeStartElement(manifest); i = ExampleNode::exampleNodeMap.begin(); while (i != ExampleNode::exampleNodeMap.end()) { const ExampleNode* en = i.value(); if (demos) { if (!en->name().startsWith("demos")) { ++i; continue; } } else if (en->name().startsWith("demos")) { ++i; continue; } writer.writeStartElement(element); writer.writeAttribute("name", en->title()); //QString docUrl = projectUrl + "/" + en->fileBase() + ".html"; QString docUrl = "%REPLACEME%/" + en->fileBase() + ".html"; writer.writeAttribute("docUrl", docUrl); foreach (const Node* child, en->childNodes()) { if (child->subType() == Node::File) { QString file = child->name(); if (file.endsWith(".pro") || file.endsWith(".qmlproject")) { if (file.startsWith("demos/")) file = file.mid(6); writer.writeAttribute("projectPath", file); break; } } } //writer.writeAttribute("imageUrl", projectUrl + "/" + en->imageFileName()); writer.writeAttribute("imageUrl", "%REPLACEME%/" + en->imageFileName()); writer.writeStartElement("description"); Text brief = en->doc().briefText(); if (!brief.isEmpty()) writer.writeCDATA(brief.toString()); else writer.writeCDATA(QString("No description available")); writer.writeEndElement(); // description QStringList tags = en->title().toLower().split(" "); if (!tags.isEmpty()) { writer.writeStartElement("tags"); bool wrote_one = false; for (int n=0; n0 && wrote_one) writer.writeCharacters(","); writer.writeCharacters(tag); wrote_one = true; } writer.writeEndElement(); // tags } QString ename = en->name().mid(en->name().lastIndexOf('/')+1); QSet usedNames; foreach (const Node* child, en->childNodes()) { if (child->subType() == Node::File) { QString file = child->name(); QString fileName = file.mid(file.lastIndexOf('/')+1); QString baseName = fileName; if ((fileName.count(QChar('.')) > 0) && (fileName.endsWith(".cpp") || fileName.endsWith(".h") || fileName.endsWith(".qml"))) baseName.truncate(baseName.lastIndexOf(QChar('.'))); if (baseName.toLower() == ename) { if (!usedNames.contains(fileName)) { writer.writeStartElement("fileToOpen"); if (file.startsWith("demos/")) file = file.mid(6); writer.writeCharacters(file); writer.writeEndElement(); // fileToOpen usedNames.insert(fileName); } } else if (fileName.toLower().endsWith("main.cpp") || fileName.toLower().endsWith("main.qml")) { if (!usedNames.contains(fileName)) { writer.writeStartElement("fileToOpen"); if (file.startsWith("demos/")) file = file.mid(6); writer.writeCharacters(file); writer.writeEndElement(); // fileToOpen usedNames.insert(fileName); } } } } writer.writeEndElement(); // example ++i; } writer.writeEndElement(); // examples writer.writeEndElement(); // instructionals writer.writeEndDocument(); file.close(); } QT_END_NAMESPACE