1 /****************************************************************************
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
6 ** This file is part of the tools applications of the Qt Toolkit.
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and Digia. For licensing terms and
14 ** conditions see http://qt.digia.com/licensing. For further information
15 ** use the contact form at http://qt.digia.com/contact-us.
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 2.1 requirements
23 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25 ** In addition, as a special exception, Digia gives you certain additional
26 ** rights. These rights are described in the Digia Qt LGPL Exception
27 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29 ** GNU General Public License Usage
30 ** Alternatively, this file may be used under the terms of the GNU
31 ** General Public License version 3.0 as published by the Free Software
32 ** Foundation and appearing in the file LICENSE.GPL included in the
33 ** packaging of this file. Please review the following information to
34 ** ensure the GNU General Public License version 3.0 requirements will be
35 ** met: http://www.gnu.org/copyleft/gpl.html.
40 ****************************************************************************/
46 #include "codemarker.h"
47 #include "codeparser.h"
48 #include "helpprojectwriter.h"
49 #include "htmlgenerator.h"
51 #include "qdocdatabase.h"
52 #include "separator.h"
57 #include <qiterator.h>
58 #include <qtextcodec.h>
63 #define COMMAND_VERSION Doc::alias("version")
64 int HtmlGenerator::id = 0;
65 bool HtmlGenerator::debugging_on = false;
67 QString HtmlGenerator::divNavTop;
69 static bool showBrokenLinks = false;
71 static QRegExp linkTag("(<@link node=\"([^\"]+)\">).*(</@link>)");
72 static QRegExp funcTag("(<@func target=\"([^\"]*)\">)(.*)(</@func>)");
73 static QRegExp typeTag("(<@(type|headerfile|func)(?: +[^>]*)?>)(.*)(</@\\2>)");
74 static QRegExp spanTag("</@(?:comment|preprocessor|string|char|number|op|type|name|keyword)>");
75 static QRegExp unknownTag("</?@[^>]*>");
77 static void addLink(const QString &linkTarget,
78 const QStringRef &nestedStuff,
81 if (!linkTarget.isEmpty()) {
94 Constructs the HTML output generator.
96 HtmlGenerator::HtmlGenerator()
97 : helpProjectWriter(0),
98 inObsoleteLink(false),
99 funcLeftParen("\\S(\\()"),
105 Destroys the HTML output generator. Deletes the singleton
106 instance of HelpProjectWriter.
108 HtmlGenerator::~HtmlGenerator()
110 if (helpProjectWriter)
111 delete helpProjectWriter;
115 Initializes the HTML output generator's data structures
116 from the configuration class \a config.
118 void HtmlGenerator::initializeGenerator(const Config &config)
120 static const struct {
125 { ATOM_FORMATTING_BOLD, "<b>", "</b>" },
126 { ATOM_FORMATTING_INDEX, "<!--", "-->" },
127 { ATOM_FORMATTING_ITALIC, "<i>", "</i>" },
128 { ATOM_FORMATTING_PARAMETER, "<i>", "</i>" },
129 { ATOM_FORMATTING_SUBSCRIPT, "<sub>", "</sub>" },
130 { ATOM_FORMATTING_SUPERSCRIPT, "<sup>", "</sup>" },
131 { ATOM_FORMATTING_TELETYPE, "<tt>", "</tt>" },
132 { ATOM_FORMATTING_UICONTROL, "<b>", "</b>" },
133 { ATOM_FORMATTING_UNDERLINE, "<u>", "</u>" },
137 Generator::initializeGenerator(config);
138 obsoleteLinks = config.getBool(QLatin1String(CONFIG_OBSOLETELINKS));
139 setImageFileExtensions(QStringList() << "png" << "jpg" << "jpeg" << "gif");
141 while (defaults[i].key) {
142 formattingLeftMap().insert(defaults[i].key, defaults[i].left);
143 formattingRightMap().insert(defaults[i].key, defaults[i].right);
147 style = config.getString(HtmlGenerator::format() +
150 endHeader = config.getString(HtmlGenerator::format() +
153 postHeader = config.getString(HtmlGenerator::format() +
155 HTMLGENERATOR_POSTHEADER);
156 postPostHeader = config.getString(HtmlGenerator::format() +
158 HTMLGENERATOR_POSTPOSTHEADER);
159 footer = config.getString(HtmlGenerator::format() +
161 HTMLGENERATOR_FOOTER);
162 address = config.getString(HtmlGenerator::format() +
164 HTMLGENERATOR_ADDRESS);
165 pleaseGenerateMacRef = config.getBool(HtmlGenerator::format() +
167 HTMLGENERATOR_GENERATEMACREFS);
168 noBreadCrumbs = config.getBool(HtmlGenerator::format() +
170 HTMLGENERATOR_NOBREADCRUMBS);
172 project = config.getString(CONFIG_PROJECT);
174 projectDescription = config.getString(CONFIG_DESCRIPTION);
175 if (projectDescription.isEmpty() && !project.isEmpty())
176 projectDescription = project + " Reference Documentation";
178 projectUrl = config.getString(CONFIG_URL);
179 tagFile_ = config.getString(CONFIG_TAGFILE);
181 outputEncoding = config.getString(CONFIG_OUTPUTENCODING);
182 if (outputEncoding.isEmpty())
183 outputEncoding = QLatin1String("UTF-8");
184 outputCodec = QTextCodec::codecForName(outputEncoding.toLocal8Bit());
186 naturalLanguage = config.getString(CONFIG_NATURALLANGUAGE);
187 if (naturalLanguage.isEmpty())
188 naturalLanguage = QLatin1String("en");
190 QSet<QString> editionNames = config.subVars(CONFIG_EDITION);
191 QSet<QString>::ConstIterator edition = editionNames.constBegin();
192 while (edition != editionNames.constEnd()) {
193 QString editionName = *edition;
194 QStringList editionModules = config.getStringList(CONFIG_EDITION +
199 QStringList editionGroups = config.getStringList(CONFIG_EDITION +
205 if (!editionModules.isEmpty())
206 editionModuleMap[editionName] = editionModules;
207 if (!editionGroups.isEmpty())
208 editionGroupMap[editionName] = editionGroups;
213 codeIndent = config.getInt(CONFIG_CODEINDENT);
215 helpProjectWriter = new HelpProjectWriter(config, project.toLower() + ".qhp", this);
217 // Documentation template handling
218 headerScripts = config.getString(HtmlGenerator::format() + Config::dot + CONFIG_HEADERSCRIPTS);
219 headerStyles = config.getString(HtmlGenerator::format() + Config::dot + CONFIG_HEADERSTYLES);
221 QString prefix = CONFIG_QHP + Config::dot + "Qt" + Config::dot;
222 manifestDir = "qthelp://" + config.getString(prefix + "namespace");
223 manifestDir += QLatin1Char('/') + config.getString(prefix + "virtualFolder") + QLatin1Char('/');
228 Gracefully terminates the HTML output generator.
230 void HtmlGenerator::terminateGenerator()
232 Generator::terminateGenerator();
235 QString HtmlGenerator::format()
241 Traverses the database generating all the HTML documentation.
243 void HtmlGenerator::generateTree()
245 qdb_->buildCollections();
246 Generator::generateTree();
247 generateCollisionPages();
249 QString fileBase = project.toLower().simplified().replace(QLatin1Char(' '), QLatin1Char('-'));
250 qdb_->generateIndex(outputDir() + QLatin1Char('/') + fileBase + ".index",
255 helpProjectWriter->generate();
256 generateManifestFiles();
258 Generate the XML tag file, if it was requested.
260 qdb_->generateTagFile(tagFile_, this);
264 Generate html from an instance of Atom.
266 int HtmlGenerator::generateAtom(const Atom *atom, const Node *relative, CodeMarker *marker)
269 static bool in_para = false;
271 if (Generator::debugging()) {
274 switch (atom->type()) {
275 case Atom::AbstractLeft:
277 relative->doc().location().warning(tr("\abstract is not implemented."));
279 Location::information(tr("\abstract is not implemented."));
281 case Atom::AbstractRight:
284 if (!inLink_ && !inContents_ && !inSectionHeading_) {
285 const Node *node = 0;
286 QString link = getLink(atom, relative, &node);
287 if (!link.isEmpty()) {
288 beginLink(link, node, relative);
289 generateLink(atom, marker);
293 out() << protectEnc(atom->string());
297 out() << protectEnc(atom->string());
302 case Atom::BriefLeft:
303 if (relative->type() == Node::Document) {
304 if (relative->subType() != Node::Example) {
305 skipAhead = skipAtoms(atom, Atom::BriefRight);
311 if (relative->type() == Node::Property ||
312 relative->type() == Node::Variable) {
315 while (atom != 0 && atom->type() != Atom::BriefRight) {
316 if (atom->type() == Atom::String ||
317 atom->type() == Atom::AutoLink)
318 str += atom->string();
322 str[0] = str[0].toLower();
323 if (str.endsWith(QLatin1Char('.')))
324 str.truncate(str.length() - 1);
326 if (relative->type() == Node::Property)
330 QStringList words = str.split(QLatin1Char(' '));
331 if (!(words.first() == "contains" || words.first() == "specifies"
332 || words.first() == "describes" || words.first() == "defines"
333 || words.first() == "holds" || words.first() == "determines"))
340 case Atom::BriefRight:
341 if (relative->type() != Node::Document)
345 // This may at one time have been used to mark up C++ code but it is
346 // now widely used to write teletype text. As a result, text marked
347 // with the \c command is not passed to a code marker.
348 out() << formattingLeftMap()[ATOM_FORMATTING_TELETYPE];
350 out() << protectEnc(plainCode(atom->string()));
353 out() << protectEnc(plainCode(atom->string()));
355 out() << formattingRightMap()[ATOM_FORMATTING_TELETYPE];
357 case Atom::CaptionLeft:
358 out() << "<p class=\"figCaption\">";
361 case Atom::CaptionRight:
369 out() << "<pre class=\"cpp\">"
370 << trimmedTrailing(highlightedCode(indent(codeIndent,atom->string()),relative))
374 out() << "<pre class=\"qml\">"
375 << trimmedTrailing(highlightedCode(indent(codeIndent,atom->string()),relative))
378 case Atom::JavaScript:
379 out() << "<pre class=\"js\">"
380 << trimmedTrailing(highlightedCode(indent(codeIndent,atom->string()),relative))
384 out() << "<p>you can rewrite it as</p>\n"
385 << "<pre class=\"cpp\">"
386 << trimmedTrailing(highlightedCode(indent(codeIndent,atom->string()),relative))
390 out() << "<p>For example, if you have code like</p>\n";
393 out() << "<pre class=\"cpp\">"
394 << trimmedTrailing(protectEnc(plainCode(indent(codeIndent,atom->string()))))
399 if (!atom->string().isEmpty())
400 out() << ' ' << atom->string();
406 case Atom::FootnoteLeft:
414 case Atom::FootnoteRight:
418 case Atom::FormatElse:
419 case Atom::FormatEndif:
422 case Atom::FormattingLeft:
423 if (atom->string().startsWith("span ")) {
424 out() << '<' + atom->string() << '>';
427 out() << formattingLeftMap()[atom->string()];
428 if (atom->string() == ATOM_FORMATTING_PARAMETER) {
429 if (atom->next() != 0 && atom->next()->type() == Atom::String) {
430 QRegExp subscriptRegExp("([a-z]+)_([0-9n])");
431 if (subscriptRegExp.exactMatch(atom->next()->string())) {
432 out() << subscriptRegExp.cap(1) << "<sub>"
433 << subscriptRegExp.cap(2) << "</sub>";
439 case Atom::FormattingRight:
440 if (atom->string() == ATOM_FORMATTING_LINK) {
443 else if (atom->string().startsWith("span ")) {
447 out() << formattingRightMap()[atom->string()];
450 case Atom::AnnotatedList:
453 qdb_->getGroup(atom->string(), nodeMap);
454 generateAnnotatedList(relative, marker, nodeMap);
457 case Atom::GeneratedList:
458 if (atom->string() == "annotatedclasses") {
459 generateAnnotatedList(relative, marker, qdb_->getCppClasses());
461 else if (atom->string() == "classes") {
462 generateCompactList(relative, qdb_->getCppClasses(), true);
464 else if (atom->string() == "qmlclasses") {
465 generateCompactList(relative, qdb_->getQmlTypes(), true);
467 else if (atom->string().contains("classesbymodule")) {
468 QString arg = atom->string().trimmed();
469 QString moduleName = atom->string().mid(atom->string().indexOf("classesbymodule") + 15).trimmed();
470 QDocDatabase* qdb = QDocDatabase::qdocDB();
471 DocNode* dn = qdb->findModule(moduleName);
474 dn->getMemberClasses(m);
476 generateAnnotatedList(relative, marker, m);
480 else if (atom->string().contains("classesbyedition")) {
481 QString arg = atom->string().trimmed();
482 QString editionName = atom->string().mid(atom->string().indexOf("classesbyedition") + 16).trimmed();
483 if (editionModuleMap.contains(editionName)) {
484 QDocDatabase* qdb = QDocDatabase::qdocDB();
485 // Add all classes in the modules listed for that edition.
486 NodeMap editionClasses;
487 DocNodeMap::const_iterator i = qdb->modules().begin();
488 while (i != qdb->modules().end()) {
490 DocNode* dn = i.value();
491 dn->getMemberClasses(m);
493 editionClasses.unite(m);
498 // Add additional groups and remove groups of classes that
499 // should be excluded from the edition.
501 const NodeMultiMap& groups = qdb_->groups();
502 foreach (const QString &groupName, editionGroupMap[editionName]) {
503 QList<Node *> groupClasses;
504 if (groupName.startsWith(QLatin1Char('-'))) {
505 groupClasses = groups.values(groupName.mid(1));
506 foreach (const Node *node, groupClasses)
507 editionClasses.remove(node->name());
510 groupClasses = groups.values(groupName);
511 foreach (const Node *node, groupClasses)
512 editionClasses.insert(node->name(), node);
515 generateAnnotatedList(relative, marker, editionClasses);
518 else if (atom->string() == "classhierarchy") {
519 generateClassHierarchy(relative, qdb_->getCppClasses());
521 else if (atom->string() == "compatclasses") {
522 generateCompactList(relative, qdb_->getCompatibilityClasses(), false);
524 else if (atom->string() == "obsoleteclasses") {
525 generateCompactList(relative, qdb_->getObsoleteClasses(), false);
527 else if (atom->string() == "functionindex") {
528 generateFunctionIndex(relative);
530 else if (atom->string() == "legalese") {
531 generateLegaleseList(relative, marker);
533 else if (atom->string() == "mainclasses") {
534 generateCompactList(relative, qdb_->getMainClasses(), true);
536 else if (atom->string() == "services") {
537 generateCompactList(relative, qdb_->getServiceClasses(), false);
539 else if (atom->string() == "overviews") {
540 generateOverviewList(relative);
542 else if (atom->string() == "namespaces") {
543 generateAnnotatedList(relative, marker, qdb_->getNamespaces());
545 else if (atom->string() == "related") {
546 const DocNode *dn = static_cast<const DocNode *>(relative);
547 if (dn && !dn->members().isEmpty()) {
548 NodeMap groupMembersMap;
549 foreach (const Node *node, dn->members()) {
550 if (node->type() == Node::Document)
551 groupMembersMap[node->fullName(relative)] = node;
553 generateAnnotatedList(dn, marker, groupMembersMap);
556 else if (atom->string() == "relatedinline") {
557 const DocNode *dn = static_cast<const DocNode *>(relative);
558 if (dn && !dn->members().isEmpty()) {
559 // Reverse the list into the original scan order.
560 // Should be sorted. But on what? It may not be a
561 // regular class or page definition.
562 QList<const Node *> list;
563 foreach (const Node *node, dn->members())
565 foreach (const Node *node, list)
566 generateBody(node, marker);
570 case Atom::SinceList:
572 const NodeMultiMap& nsmap = qdb_->getSinceMap(atom->string());
573 const NodeMap& ncmap = qdb_->getClassMap(atom->string());
574 const NodeMap& nqcmap = qdb_->getQmlTypeMap(atom->string());
576 if (!nsmap.isEmpty()) {
577 QList<Section> sections;
578 QList<Section>::ConstIterator s;
580 for (int i=0; i<LastSinceType; ++i)
581 sections.append(Section(sinceTitle(i),QString(),QString(),QString()));
583 NodeMultiMap::const_iterator n = nsmap.constBegin();
584 while (n != nsmap.constEnd()) {
585 const Node* node = n.value();
586 switch (node->type()) {
588 if (node->subType() == Node::QmlClass) {
589 sections[QmlClass].appendMember((Node*)node);
592 case Node::Namespace:
593 sections[Namespace].appendMember((Node*)node);
596 sections[Class].appendMember((Node*)node);
599 sections[Enum].appendMember((Node*)node);
602 sections[Typedef].appendMember((Node*)node);
604 case Node::Function: {
605 const FunctionNode* fn = static_cast<const FunctionNode*>(node);
607 sections[Macro].appendMember((Node*)node);
609 Node* p = fn->parent();
611 if (p->type() == Node::Class)
612 sections[MemberFunction].appendMember((Node*)node);
613 else if (p->type() == Node::Namespace) {
614 if (p->name().isEmpty())
615 sections[GlobalFunction].appendMember((Node*)node);
617 sections[NamespaceFunction].appendMember((Node*)node);
620 sections[GlobalFunction].appendMember((Node*)node);
623 sections[GlobalFunction].appendMember((Node*)node);
628 sections[Property].appendMember((Node*)node);
631 sections[Variable].appendMember((Node*)node);
633 case Node::QmlProperty:
634 sections[QmlProperty].appendMember((Node*)node);
636 case Node::QmlSignal:
637 sections[QmlSignal].appendMember((Node*)node);
639 case Node::QmlSignalHandler:
640 sections[QmlSignalHandler].appendMember((Node*)node);
642 case Node::QmlMethod:
643 sections[QmlMethod].appendMember((Node*)node);
652 s = sections.constBegin();
653 while (s != sections.constEnd()) {
654 if (!(*s).members.isEmpty()) {
658 << Doc::canonicalTitle((*s).name)
668 s = sections.constBegin();
669 while (s != sections.constEnd()) {
670 if (!(*s).members.isEmpty()) {
671 out() << "<a name=\""
672 << Doc::canonicalTitle((*s).name)
674 out() << "<h3>" << protectEnc((*s).name) << "</h3>\n";
676 generateCompactList(0, ncmap, false, QString("Q"));
677 else if (idx == QmlClass)
678 generateCompactList(0, nqcmap, false, QString("Q"));
679 else if (idx == MemberFunction) {
680 ParentMaps parentmaps;
681 ParentMaps::iterator pmap;
682 NodeList::const_iterator i = s->members.constBegin();
683 while (i != s->members.constEnd()) {
684 Node* p = (*i)->parent();
685 pmap = parentmaps.find(p);
686 if (pmap == parentmaps.end())
687 pmap = parentmaps.insert(p,NodeMultiMap());
688 pmap->insert((*i)->name(),(*i));
691 pmap = parentmaps.begin();
692 while (pmap != parentmaps.end()) {
693 NodeList nlist = pmap->values();
694 out() << "<p>Class ";
696 out() << "<a href=\""
697 << linkForNode(pmap.key(), 0)
699 QStringList pieces = pmap.key()->fullName().split("::");
700 out() << protectEnc(pieces.last());
701 out() << "</a>" << ":</p>\n";
703 generateSection(nlist, 0, marker, CodeMarker::Summary);
709 generateSection(s->members, 0, marker, CodeMarker::Summary);
724 case Atom::InlineImage:
726 QString fileName = imageFileName(relative, atom->string());
728 if (atom->next() != 0)
729 text = atom->next()->string();
730 if (atom->type() == Atom::Image)
731 out() << "<p class=\"centerAlign\">";
732 if (fileName.isEmpty()) {
733 relative->location().warning(tr("Missing image: %1").arg(protectEnc(atom->string())));
734 out() << "<font color=\"red\">[Missing image "
735 << protectEnc(atom->string()) << "]</font>";
739 out() << "<img src=\"" << protectEnc(prefix + fileName) << '"';
741 out() << " alt=\"" << protectEnc(text) << '"';
743 out() << " alt=\"\"";
745 helpProjectWriter->addExtraFile(fileName);
746 if ((relative->type() == Node::Document) &&
747 (relative->subType() == Node::Example)) {
748 const ExampleNode* cen = static_cast<const ExampleNode*>(relative);
749 if (cen->imageFileName().isEmpty()) {
750 ExampleNode* en = const_cast<ExampleNode*>(cen);
751 en->setImageFileName(fileName);
755 if (atom->type() == Atom::Image)
759 case Atom::ImageText:
761 case Atom::ImportantLeft:
763 out() << formattingLeftMap()[ATOM_FORMATTING_BOLD];
764 out() << "Important: ";
765 out() << formattingRightMap()[ATOM_FORMATTING_BOLD];
767 case Atom::ImportantRight:
772 out() << formattingLeftMap()[ATOM_FORMATTING_BOLD];
774 out() << formattingRightMap()[ATOM_FORMATTING_BOLD];
776 case Atom::NoteRight:
779 case Atom::LegaleseLeft:
780 out() << "<div class=\"LegaleseLeft\">";
782 case Atom::LegaleseRight:
785 case Atom::LineBreak:
790 const Node *node = 0;
791 QString myLink = getLink(atom, relative, &node);
792 if (myLink.isEmpty()) {
793 myLink = getCollisionLink(atom);
794 if (myLink.isEmpty()) {
795 relative->doc().location().warning(tr("Can't create link to '%1'")
796 .arg(atom->string()));
801 beginLink(myLink, node, relative);
807 const Node *node = CodeMarker::nodeForString(atom->string());
808 beginLink(linkForNode(node, relative), node, relative);
817 if (atom->string() == ATOM_LIST_BULLET) {
820 else if (atom->string() == ATOM_LIST_TAG) {
823 else if (atom->string() == ATOM_LIST_VALUE) {
824 threeColumnEnumValueTable_ = isThreeColumnEnumValueTable(atom);
825 if (threeColumnEnumValueTable_) {
826 out() << "<table class=\"valuelist\">";
827 if (++numTableRows_ % 2 == 1)
828 out() << "<tr valign=\"top\" class=\"odd\">";
830 out() << "<tr valign=\"top\" class=\"even\">";
832 out() << "<th class=\"tblConst\">Constant</th>"
833 << "<th class=\"tblval\">Value</th>"
834 << "<th class=\"tbldscr\">Description</th></tr>\n";
837 out() << "<table class=\"valuelist\">"
838 << "<tr><th class=\"tblConst\">Constant</th><th class=\"tblVal\">Value</th></tr>\n";
842 out() << "<ol class=";
843 if (atom->string() == ATOM_LIST_UPPERALPHA) {
845 } /* why type? changed to */
846 else if (atom->string() == ATOM_LIST_LOWERALPHA) {
849 else if (atom->string() == ATOM_LIST_UPPERROMAN) {
852 else if (atom->string() == ATOM_LIST_LOWERROMAN) {
855 else { // (atom->string() == ATOM_LIST_NUMERIC)
858 if (atom->next() != 0 && atom->next()->string().toInt() != 1)
859 out() << " start=\"" << atom->next()->string() << '"';
863 case Atom::ListItemNumber:
865 case Atom::ListTagLeft:
866 if (atom->string() == ATOM_LIST_TAG) {
869 else { // (atom->string() == ATOM_LIST_VALUE)
872 QString t= protectEnc(plainCode(marker->markedUpEnumValue(atom->next()->string(),relative)));
873 out() << "<tr><td class=\"topAlign\"><tt>" << t << "</tt></td><td class=\"topAlign\">";
876 if (relative->type() == Node::Enum) {
877 const EnumNode *enume = static_cast<const EnumNode *>(relative);
878 itemValue = enume->itemValue(atom->next()->string());
881 if (itemValue.isEmpty())
884 out() << "<tt>" << protectEnc(itemValue) << "</tt>";
889 case Atom::ListTagRight:
890 if (atom->string() == ATOM_LIST_TAG)
893 case Atom::ListItemLeft:
894 if (atom->string() == ATOM_LIST_TAG) {
897 else if (atom->string() == ATOM_LIST_VALUE) {
898 if (threeColumnEnumValueTable_) {
899 out() << "</td><td class=\"topAlign\">";
900 if (matchAhead(atom, Atom::ListItemRight))
907 if (matchAhead(atom, Atom::ParaLeft))
910 case Atom::ListItemRight:
911 if (atom->string() == ATOM_LIST_TAG) {
914 else if (atom->string() == ATOM_LIST_VALUE) {
915 out() << "</td></tr>\n";
921 case Atom::ListRight:
922 if (atom->string() == ATOM_LIST_BULLET) {
925 else if (atom->string() == ATOM_LIST_TAG) {
928 else if (atom->string() == ATOM_LIST_VALUE) {
929 out() << "</table>\n";
941 case Atom::ParaRight:
947 //if (!matchAhead(atom, Atom::ListItemRight) && !matchAhead(atom, Atom::TableItemRight))
948 // out() << "</p>\n";
950 case Atom::QuotationLeft:
951 out() << "<blockquote>";
953 case Atom::QuotationRight:
954 out() << "</blockquote>\n";
956 case Atom::RawString:
957 out() << atom->string();
959 case Atom::SectionLeft:
960 out() << "<a name=\"" << Doc::canonicalTitle(Text::sectionHeading(atom).toString())
961 << "\"></a>" << divNavTop << '\n';
963 case Atom::SectionRight:
965 case Atom::SectionHeadingLeft:
966 out() << "<h" + QString::number(atom->string().toInt() + hOffset(relative)) + QLatin1Char('>');
967 inSectionHeading_ = true;
969 case Atom::SectionHeadingRight:
970 out() << "</h" + QString::number(atom->string().toInt() + hOffset(relative)) + ">\n";
971 inSectionHeading_ = false;
973 case Atom::SidebarLeft:
975 case Atom::SidebarRight:
978 if (inLink_ && !inContents_ && !inSectionHeading_) {
979 generateLink(atom, marker);
982 out() << protectEnc(atom->string());
985 case Atom::TableLeft:
988 QString attr = "generic";
994 if (atom->count() > 0) {
995 p1 = atom->string(0);
996 if (atom->count() > 1)
997 p2 = atom->string(1);
1000 if (p1 == "borderless")
1002 else if (p1.contains("%"))
1005 if (!p2.isEmpty()) {
1006 if (p2 == "borderless")
1008 else if (p2.contains("%"))
1011 out() << "<table class=\"" << attr << "\"";
1012 if (!width.isEmpty())
1013 out() << " width=\"" << width << "\"";
1018 case Atom::TableRight:
1019 out() << "</table>\n";
1021 case Atom::TableHeaderLeft:
1022 out() << "<thead><tr class=\"qt-style\">";
1023 inTableHeader_ = true;
1025 case Atom::TableHeaderRight:
1027 if (matchAhead(atom, Atom::TableHeaderLeft)) {
1029 out() << "\n<tr class=\"qt-style\">";
1032 out() << "</thead>\n";
1033 inTableHeader_ = false;
1036 case Atom::TableRowLeft:
1037 if (!atom->string().isEmpty())
1038 out() << "<tr " << atom->string() << '>';
1039 else if (++numTableRows_ % 2 == 1)
1040 out() << "<tr valign=\"top\" class=\"odd\">";
1042 out() << "<tr valign=\"top\" class=\"even\">";
1044 case Atom::TableRowRight:
1047 case Atom::TableItemLeft:
1054 for (int i=0; i<atom->count(); ++i) {
1057 QString p = atom->string(i);
1058 if (p.contains('=')) {
1062 QStringList spans = p.split(",");
1063 if (spans.size() == 2) {
1064 if (spans.at(0) != "1")
1065 out() << " colspan=\"" << spans.at(0) << '"';
1066 if (spans.at(1) != "1")
1067 out() << " rowspan=\"" << spans.at(1) << '"';
1077 if (matchAhead(atom, Atom::ParaLeft))
1081 case Atom::TableItemRight:
1086 //out() << "</p></td>";
1088 if (matchAhead(atom, Atom::ParaLeft))
1091 case Atom::TableOfContents:
1094 out() << "<a name=\"" << Doc::canonicalTitle(atom->string()) << "\"></a>";
1096 case Atom::UnhandledFormat:
1097 out() << "<b class=\"redFont\"><Missing HTML></b>";
1099 case Atom::UnknownCommand:
1100 out() << "<b class=\"redFont\"><code>\\" << protectEnc(atom->string())
1104 case Atom::EndQmlText:
1105 // don't do anything with these. They are just tags.
1114 Generate a reference page for a C++ class.
1116 void HtmlGenerator::generateClassLikeNode(InnerNode* inner, CodeMarker* marker)
1118 QList<Section> sections;
1119 QList<Section>::ConstIterator s;
1121 ClassNode* classe = 0;
1126 if (inner->type() == Node::Namespace) {
1127 rawTitle = inner->plainName();
1128 fullTitle = inner->plainFullName();
1129 title = rawTitle + " Namespace";
1131 else if (inner->type() == Node::Class) {
1132 classe = static_cast<ClassNode*>(inner);
1133 rawTitle = inner->plainName();
1134 fullTitle = inner->plainFullName();
1135 title = rawTitle + " Class";
1139 if (rawTitle != fullTitle)
1140 subtitleText << "(" << Atom(Atom::AutoLink, fullTitle) << ")" << Atom(Atom::LineBreak);
1142 generateHeader(title, inner, marker);
1143 sections = marker->sections(inner, CodeMarker::Summary, CodeMarker::Okay);
1144 generateTableOfContents(inner,marker,§ions);
1145 generateTitle(title, subtitleText, SmallSubTitle, inner, marker);
1146 generateBrief(inner, marker);
1147 generateIncludes(inner, marker);
1148 generateStatus(inner, marker);
1150 generateInherits(classe, marker);
1151 generateInheritedBy(classe, marker);
1152 if (classe->qmlElement() != 0)
1153 generateInstantiatedBy(classe,marker);
1155 generateThreadSafeness(inner, marker);
1156 generateSince(inner, marker);
1160 QString membersLink = generateListOfAllMemberFile(inner, marker);
1161 if (!membersLink.isEmpty())
1162 out() << "<li><a href=\"" << membersLink << "\">"
1163 << "List of all members, including inherited members</a></li>\n";
1165 QString obsoleteLink = generateLowStatusMemberFile(inner,
1167 CodeMarker::Obsolete);
1168 if (!obsoleteLink.isEmpty())
1169 out() << "<li><a href=\"" << obsoleteLink << "\">"
1170 << "Obsolete members</a></li>\n";
1172 QString compatLink = generateLowStatusMemberFile(inner,
1174 CodeMarker::Compat);
1175 if (!compatLink.isEmpty())
1176 out() << "<li><a href=\"" << compatLink << "\">"
1177 << "Compatibility members</a></li>\n";
1181 bool needOtherSection = false;
1184 sections is built above for the call to generateTableOfContents().
1186 s = sections.constBegin();
1187 while (s != sections.constEnd()) {
1188 if (s->members.isEmpty() && s->reimpMembers.isEmpty()) {
1189 if (!s->inherited.isEmpty())
1190 needOtherSection = true;
1193 if (!s->members.isEmpty()) {
1194 // out() << "<hr />\n";
1195 out() << "<a name=\""
1196 << registerRef((*s).name.toLower())
1197 << "\"></a>" << divNavTop << "\n";
1198 out() << "<h2>" << protectEnc((*s).name) << "</h2>\n";
1199 generateSection(s->members, inner, marker, CodeMarker::Summary);
1201 if (!s->reimpMembers.isEmpty()) {
1202 QString name = QString("Reimplemented ") + (*s).name;
1203 // out() << "<hr />\n";
1204 out() << "<a name=\""
1205 << registerRef(name.toLower())
1206 << "\"></a>" << divNavTop << "\n";
1207 out() << "<h2>" << protectEnc(name) << "</h2>\n";
1208 generateSection(s->reimpMembers, inner, marker, CodeMarker::Summary);
1211 if (!s->inherited.isEmpty()) {
1213 generateSectionInheritedList(*s, inner);
1220 if (needOtherSection) {
1221 out() << "<h3>Additional Inherited Members</h3>\n"
1224 s = sections.constBegin();
1225 while (s != sections.constEnd()) {
1226 if (s->members.isEmpty() && !s->inherited.isEmpty())
1227 generateSectionInheritedList(*s, inner);
1233 out() << "<a name=\"" << registerRef("details") << "\"></a>" << divNavTop << '\n';
1235 if (!inner->doc().isEmpty()) {
1236 generateExtractionMark(inner, DetailedDescriptionMark);
1237 //out() << "<hr />\n"
1238 out() << "<div class=\"descr\">\n" // QTBUG-9504
1239 << "<h2>" << "Detailed Description" << "</h2>\n";
1240 generateBody(inner, marker);
1241 out() << "</div>\n"; // QTBUG-9504
1242 generateAlsoList(inner, marker);
1243 generateMaintainerList(inner, marker);
1244 generateExtractionMark(inner, EndMark);
1247 sections = marker->sections(inner, CodeMarker::Detailed, CodeMarker::Okay);
1248 s = sections.constBegin();
1249 while (s != sections.constEnd()) {
1250 //out() << "<hr />\n";
1251 if (!(*s).divClass.isEmpty())
1252 out() << "<div class=\"" << (*s).divClass << "\">\n"; // QTBUG-9504
1253 out() << "<h2>" << protectEnc((*s).name) << "</h2>\n";
1255 NodeList::ConstIterator m = (*s).members.constBegin();
1256 while (m != (*s).members.constEnd()) {
1257 if ((*m)->access() != Node::Private) { // ### check necessary?
1258 if ((*m)->type() != Node::Class)
1259 generateDetailedMember(*m, inner, marker);
1261 out() << "<h3> class ";
1262 generateFullName(*m, inner);
1264 generateBrief(*m, marker, inner);
1268 names << (*m)->name();
1269 if ((*m)->type() == Node::Function) {
1270 const FunctionNode *func = reinterpret_cast<const FunctionNode *>(*m);
1271 if (func->metaness() == FunctionNode::Ctor ||
1272 func->metaness() == FunctionNode::Dtor ||
1273 func->overloadNumber() != 1)
1276 else if ((*m)->type() == Node::Property) {
1277 const PropertyNode *prop = reinterpret_cast<const PropertyNode *>(*m);
1278 if (!prop->getters().isEmpty() &&
1279 !names.contains(prop->getters().first()->name()))
1280 names << prop->getters().first()->name();
1281 if (!prop->setters().isEmpty())
1282 names << prop->setters().first()->name();
1283 if (!prop->resetters().isEmpty())
1284 names << prop->resetters().first()->name();
1285 if (!prop->notifiers().isEmpty())
1286 names << prop->notifiers().first()->name();
1288 else if ((*m)->type() == Node::Enum) {
1289 const EnumNode *enume = reinterpret_cast<const EnumNode*>(*m);
1290 if (enume->flagsType())
1291 names << enume->flagsType()->name();
1293 foreach (const QString &enumName,
1294 enume->doc().enumItemNames().toSet() -
1295 enume->doc().omitEnumItemNames().toSet())
1296 names << plainCode(marker->markedUpEnumValue(enumName,
1302 if (!(*s).divClass.isEmpty())
1303 out() << "</div>\n"; // QTBUG-9504
1306 generateFooter(inner);
1310 We delayed generation of the disambiguation pages until now, after
1311 all the other pages have been generated. We do this because we might
1312 encounter a link command that tries to link to a target on a QML
1313 component page, but the link doesn't specify the module identifer
1314 for the component, and the component name without a module
1315 identifier is ambiguous. When such a link is found, qdoc can't find
1316 the target, so it appends the target to the NameCollisionNode. After
1317 the tree has been traversed and all these ambiguous links have been
1318 added to the name collision nodes, this function is called. The list
1319 of collision nodes is traversed here, and the disambiguation page for
1320 each collision is generated. The disambiguation page will not only
1321 disambiguate links to the component pages, but it will also disambiguate
1322 links to properties, section headers, etc.
1324 void HtmlGenerator::generateCollisionPages()
1326 if (collisionNodes.isEmpty())
1329 for (int i=0; i<collisionNodes.size(); ++i) {
1330 NameCollisionNode* ncn = collisionNodes.at(i);
1334 NodeList collisions;
1335 const NodeList& nl = ncn->childNodes();
1336 if (!nl.isEmpty()) {
1337 NodeList::ConstIterator it = nl.constBegin();
1338 while (it != nl.constEnd()) {
1339 if (!(*it)->isInternal())
1340 collisions.append(*it);
1344 if (collisions.size() <= 1)
1347 ncn->clearCurrentChild();
1348 beginSubPage(ncn, Generator::fileName(ncn));
1349 QString fullTitle = ncn->fullTitle();
1350 QString htmlTitle = fullTitle;
1351 CodeMarker* marker = CodeMarker::markerForFileName(ncn->location().filePath());
1352 if (ncn->isQmlNode()) {
1353 // Replace the marker with a QML code marker.
1354 if (ncn->isQmlNode())
1355 marker = CodeMarker::markerForLanguage(QLatin1String("QML"));
1358 generateHeader(htmlTitle, ncn, marker);
1359 if (!fullTitle.isEmpty())
1360 out() << "<h1 class=\"title\">" << protectEnc(fullTitle) << "</h1>\n";
1363 for (int i=0; i<collisions.size(); ++i) {
1364 Node* n = collisions.at(i);
1366 if (!n->qmlModuleIdentifier().isEmpty())
1367 t = n->qmlModuleIdentifier() + "::";
1368 t += protectEnc(fullTitle);
1369 nm.insertMulti(t,n);
1371 generateAnnotatedList(ncn, marker, nm, true);
1373 QList<QString> targets;
1374 if (!ncn->linkTargets().isEmpty()) {
1375 QMap<QString,QString>::ConstIterator t = ncn->linkTargets().constBegin();
1376 while (t != ncn->linkTargets().constEnd()) {
1378 for (int i=0; i<collisions.size(); ++i) {
1379 InnerNode* n = static_cast<InnerNode*>(collisions.at(i));
1380 if (n->findChildNodeByName(t.key())) {
1383 targets.append(t.key());
1391 if (!targets.isEmpty()) {
1392 QList<QString>::ConstIterator t = targets.constBegin();
1393 while (t != targets.constEnd()) {
1394 out() << "<a name=\"" << Doc::canonicalTitle(*t) << "\"></a>";
1395 out() << "<h2 class=\"title\">" << protectEnc(*t) << "</h2>\n";
1397 for (int i=0; i<collisions.size(); ++i) {
1398 InnerNode* n = static_cast<InnerNode*>(collisions.at(i));
1399 Node* p = n->findChildNodeByName(*t);
1401 QString link = linkForNode(p,0);
1403 if (!n->qmlModuleIdentifier().isEmpty())
1404 label = n->qmlModuleIdentifier() + "::";
1405 label += n->name() + "::" + p->name();
1407 out() << "<a href=\"" << link << "\">";
1408 out() << protectEnc(label) << "</a>";
1417 generateFooter(ncn);
1423 Generate the HTML page for an entity that doesn't map
1424 to any underlying parsable C++ class or QML component.
1426 void HtmlGenerator::generateDocNode(DocNode* dn, CodeMarker* marker)
1429 If the document node is a page node, and if the page type
1430 is DITA map page, write the node's contents as a dita
1431 map and return without doing anything else.
1433 if (dn->subType() == Node::Page && dn->pageType() == Node::DitaMapPage) {
1434 const DitaMapNode* dmn = static_cast<const DitaMapNode*>(dn);
1439 SubTitleSize subTitleSize = LargeSubTitle;
1440 QList<Section> sections;
1441 QList<Section>::const_iterator s;
1442 QString fullTitle = dn->fullTitle();
1443 QString htmlTitle = fullTitle;
1445 if (dn->subType() == Node::File && !dn->subTitle().isEmpty()) {
1446 subTitleSize = SmallSubTitle;
1447 htmlTitle += " (" + dn->subTitle() + QLatin1Char(')');
1449 else if (dn->subType() == Node::QmlBasicType) {
1450 fullTitle = "QML Basic Type: " + fullTitle;
1451 htmlTitle = fullTitle;
1453 // Replace the marker with a QML code marker.
1454 marker = CodeMarker::markerForLanguage(QLatin1String("QML"));
1457 generateHeader(htmlTitle, dn, marker);
1459 Generate the TOC for the new doc format.
1460 Don't generate a TOC for the home page.
1462 QmlClassNode* qml_cn = 0;
1463 if (dn->subType() == Node::QmlClass) {
1464 qml_cn = static_cast<QmlClassNode*>(dn);
1465 sections = marker->qmlSections(qml_cn,CodeMarker::Summary);
1466 generateTableOfContents(dn,marker,§ions);
1468 // Replace the marker with a QML code marker.
1469 marker = CodeMarker::markerForLanguage(QLatin1String("QML"));
1471 else if (dn->subType() != Node::Collision && dn->name() != QString("index.html"))
1472 generateTableOfContents(dn,marker,0);
1474 generateTitle(fullTitle,
1475 Text() << dn->subTitle(),
1480 if (dn->subType() == Node::Module) {
1481 // Generate brief text and status for modules.
1482 generateBrief(dn, marker);
1483 generateStatus(dn, marker);
1484 generateSince(dn, marker);
1487 dn->getMemberNamespaces(nm);
1488 if (!nm.isEmpty()) {
1489 out() << "<a name=\"" << registerRef("namespaces") << "\"></a>" << divNavTop << '\n';
1490 out() << "<h2>Namespaces</h2>\n";
1491 generateAnnotatedList(dn, marker, nm);
1494 dn->getMemberClasses(nm);
1495 if (!nm.isEmpty()) {
1496 out() << "<a name=\"" << registerRef("classes") << "\"></a>" << divNavTop << '\n';
1497 out() << "<h2>Classes</h2>\n";
1498 generateAnnotatedList(dn, marker, nm);
1502 else if (dn->subType() == Node::HeaderFile) {
1503 // Generate brief text and status for modules.
1504 generateBrief(dn, marker);
1505 generateStatus(dn, marker);
1506 generateSince(dn, marker);
1510 QString membersLink = generateListOfAllMemberFile(dn, marker);
1511 if (!membersLink.isEmpty())
1512 out() << "<li><a href=\"" << membersLink << "\">"
1513 << "List of all members, including inherited members</a></li>\n";
1515 QString obsoleteLink = generateLowStatusMemberFile(dn,
1517 CodeMarker::Obsolete);
1518 if (!obsoleteLink.isEmpty())
1519 out() << "<li><a href=\"" << obsoleteLink << "\">"
1520 << "Obsolete members</a></li>\n";
1522 QString compatLink = generateLowStatusMemberFile(dn,
1524 CodeMarker::Compat);
1525 if (!compatLink.isEmpty())
1526 out() << "<li><a href=\"" << compatLink << "\">"
1527 << "Compatibility members</a></li>\n";
1531 else if (dn->subType() == Node::QmlClass) {
1532 const_cast<DocNode*>(dn)->setCurrentChild();
1533 ClassNode* cn = qml_cn->classNode();
1534 generateBrief(qml_cn, marker);
1535 generateQmlInherits(qml_cn, marker);
1536 generateQmlInheritedBy(qml_cn, marker);
1537 generateQmlInstantiates(qml_cn, marker);
1538 generateSince(qml_cn, marker);
1540 QString allQmlMembersLink = generateAllQmlMembersFile(qml_cn, marker);
1541 if (!allQmlMembersLink.isEmpty()) {
1543 out() << "<li><a href=\"" << allQmlMembersLink << "\">"
1544 << "List of all members, including inherited members</a></li>\n";
1548 s = sections.constBegin();
1549 while (s != sections.constEnd()) {
1550 out() << "<a name=\"" << registerRef((*s).name.toLower())
1551 << "\"></a>" << divNavTop << '\n';
1552 out() << "<h2>" << protectEnc((*s).name) << "</h2>\n";
1553 generateQmlSummary(*s,dn,marker);
1557 generateExtractionMark(dn, DetailedDescriptionMark);
1558 out() << "<a name=\"" << registerRef("details") << "\"></a>" << divNavTop << '\n';
1559 out() << "<h2>" << "Detailed Description" << "</h2>\n";
1560 generateBody(dn, marker);
1562 generateQmlText(cn->doc().body(), cn, marker, dn->name());
1563 generateAlsoList(dn, marker);
1564 generateExtractionMark(dn, EndMark);
1565 //out() << "<hr />\n";
1567 sections = marker->qmlSections(qml_cn,CodeMarker::Detailed);
1568 s = sections.constBegin();
1569 while (s != sections.constEnd()) {
1570 out() << "<h2>" << protectEnc((*s).name) << "</h2>\n";
1571 NodeList::ConstIterator m = (*s).members.constBegin();
1572 while (m != (*s).members.constEnd()) {
1573 generateDetailedQmlMember(*m, dn, marker);
1580 const_cast<DocNode*>(dn)->clearCurrentChild();
1584 sections = marker->sections(dn, CodeMarker::Summary, CodeMarker::Okay);
1585 s = sections.constBegin();
1586 while (s != sections.constEnd()) {
1587 out() << "<a name=\"" << registerRef((*s).name) << "\"></a>" << divNavTop << '\n';
1588 out() << "<h2>" << protectEnc((*s).name) << "</h2>\n";
1589 generateSectionList(*s, dn, marker, CodeMarker::Summary);
1593 Text brief = dn->doc().briefText();
1594 if (dn->subType() == Node::Module && !brief.isEmpty()) {
1595 generateExtractionMark(dn, DetailedDescriptionMark);
1596 out() << "<a name=\"" << registerRef("details") << "\"></a>" << divNavTop << '\n';
1597 out() << "<div class=\"descr\">\n"; // QTBUG-9504
1598 out() << "<h2>" << "Detailed Description" << "</h2>\n";
1601 generateExtractionMark(dn, DetailedDescriptionMark);
1602 out() << "<div class=\"descr\"> <a name=\"" << registerRef("details") << "\"></a>\n"; // QTBUG-9504
1605 generateBody(dn, marker);
1606 out() << "</div>\n"; // QTBUG-9504
1607 generateAlsoList(dn, marker);
1608 generateExtractionMark(dn, EndMark);
1610 if ((dn->subType() == Node::Group) && !dn->members().isEmpty()) {
1611 NodeMap groupMembersMap;
1612 foreach (const Node *node, dn->members()) {
1613 if (node->type() == Node::Class || node->type() == Node::Namespace)
1614 groupMembersMap[node->name()] = node;
1616 generateAnnotatedList(dn, marker, groupMembersMap);
1618 else if ((dn->subType() == Node::QmlModule) && !dn->members().isEmpty()) {
1619 NodeMap qmlModuleMembersMap;
1620 foreach (const Node* node, dn->members()) {
1621 if (node->type() == Node::Document && node->subType() == Node::QmlClass)
1622 qmlModuleMembersMap[node->name()] = node;
1624 generateAnnotatedList(dn, marker, qmlModuleMembersMap);
1627 sections = marker->sections(dn, CodeMarker::Detailed, CodeMarker::Okay);
1628 s = sections.constBegin();
1629 while (s != sections.constEnd()) {
1630 //out() << "<hr />\n";
1631 out() << "<h2>" << protectEnc((*s).name) << "</h2>\n";
1633 NodeList::ConstIterator m = (*s).members.constBegin();
1634 while (m != (*s).members.constEnd()) {
1635 generateDetailedMember(*m, dn, marker);
1644 Returns "html" for this subclass of Generator.
1646 QString HtmlGenerator::fileExtension() const
1652 Output breadcrumb list in the html file.
1654 void HtmlGenerator::generateBreadCrumbs(const QString &title,
1662 if (node->type() == Node::Class) {
1663 const ClassNode *cn = static_cast<const ClassNode *>(node);
1664 QString name = node->moduleName();
1665 breadcrumbs << Atom(Atom::ListItemLeft)
1666 << Atom(Atom::Link, QLatin1String("All Modules"))
1667 << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK)
1668 << Atom(Atom::String, QLatin1String("Modules"))
1669 << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK)
1670 << Atom(Atom::ListItemRight);
1671 if (!name.isEmpty())
1672 breadcrumbs << Atom(Atom::ListItemLeft)
1673 << Atom(Atom::AutoLink, name)
1674 << Atom(Atom::ListItemRight);
1675 if (!cn->name().isEmpty())
1676 breadcrumbs << Atom(Atom::ListItemLeft)
1677 << Atom(Atom::String, protectEnc(cn->name()))
1678 << Atom(Atom::ListItemRight);
1680 else if (node->type() == Node::Document) {
1681 const DocNode* fn = static_cast<const DocNode*>(node);
1682 if (node->subType() == Node::Module) {
1683 breadcrumbs << Atom(Atom::ListItemLeft)
1684 << Atom(Atom::Link, QLatin1String("All Modules"))
1685 << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK)
1686 << Atom(Atom::String, QLatin1String("Modules"))
1687 << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK)
1688 << Atom(Atom::ListItemRight);
1689 QString name = node->name();
1690 if (!name.isEmpty())
1691 breadcrumbs << Atom(Atom::ListItemLeft)
1692 << Atom(Atom::String, protectEnc(name))
1693 << Atom(Atom::ListItemRight);
1695 else if (node->subType() == Node::Group) {
1696 if (fn->name() == QString("modules"))
1697 breadcrumbs << Atom(Atom::String, QLatin1String("Modules"));
1699 breadcrumbs << Atom(Atom::ListItemLeft)
1700 << Atom(Atom::String, protectEnc(title))
1701 << Atom(Atom::ListItemRight);
1703 else if (node->subType() == Node::Page) {
1704 if (fn->name() == QString("qdeclarativeexamples.html")) {
1705 breadcrumbs << Atom(Atom::ListItemLeft)
1706 << Atom(Atom::Link, QLatin1String("Qt Examples"))
1707 << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK)
1708 << Atom(Atom::String, QLatin1String("Examples"))
1709 << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK)
1710 << Atom(Atom::ListItemRight);
1711 breadcrumbs << Atom(Atom::ListItemLeft)
1712 << Atom(Atom::AutoLink, QLatin1String("QML Examples & Demos"))
1713 << Atom(Atom::ListItemRight);
1715 else if (fn->name().startsWith("examples-")) {
1716 breadcrumbs << Atom(Atom::ListItemLeft)
1717 << Atom(Atom::Link, QLatin1String("Qt Examples"))
1718 << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK)
1719 << Atom(Atom::String, QLatin1String("Examples"))
1720 << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK)
1721 << Atom(Atom::ListItemRight);
1722 breadcrumbs << Atom(Atom::ListItemLeft)
1723 << Atom(Atom::String, protectEnc(title))
1724 << Atom(Atom::ListItemRight);
1726 else if (fn->name() == QString("namespaces.html"))
1727 breadcrumbs << Atom(Atom::String, QLatin1String("Namespaces"));
1729 breadcrumbs << Atom(Atom::ListItemLeft)
1730 << Atom(Atom::String, protectEnc(title))
1731 << Atom(Atom::ListItemRight);
1733 else if (node->subType() == Node::QmlClass) {
1734 breadcrumbs << Atom(Atom::ListItemLeft)
1735 << Atom(Atom::AutoLink, QLatin1String("Basic QML Types"))
1736 << Atom(Atom::ListItemRight);
1737 breadcrumbs << Atom(Atom::ListItemLeft)
1738 << Atom(Atom::String, protectEnc(title))
1739 << Atom(Atom::ListItemRight);
1741 else if (node->subType() == Node::Example) {
1742 breadcrumbs << Atom(Atom::ListItemLeft)
1743 << Atom(Atom::Link, QLatin1String("Qt Examples"))
1744 << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK)
1745 << Atom(Atom::String, QLatin1String("Examples"))
1746 << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK)
1747 << Atom(Atom::ListItemRight);
1748 QStringList sl = fn->name().split('/');
1749 if (sl.contains("declarative"))
1750 breadcrumbs << Atom(Atom::ListItemLeft)
1751 << Atom(Atom::AutoLink, QLatin1String("QML Examples & Demos"))
1752 << Atom(Atom::ListItemRight);
1754 QString name = protectEnc("examples-" + sl.at(0) + ".html"); // this generates an empty link
1755 QString t = CodeParser::titleFromName(name);
1757 breadcrumbs << Atom(Atom::ListItemLeft)
1758 << Atom(Atom::String, protectEnc(title))
1759 << Atom(Atom::ListItemRight);
1762 else if (node->type() == Node::Namespace) {
1763 breadcrumbs << Atom(Atom::ListItemLeft)
1764 << Atom(Atom::Link, QLatin1String("All Namespaces"))
1765 << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK)
1766 << Atom(Atom::String, QLatin1String("Namespaces"))
1767 << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK)
1768 << Atom(Atom::ListItemRight);
1769 breadcrumbs << Atom(Atom::ListItemLeft)
1770 << Atom(Atom::String, protectEnc(title))
1771 << Atom(Atom::ListItemRight);
1774 generateText(breadcrumbs, node, marker);
1777 void HtmlGenerator::generateHeader(const QString& title,
1781 out() << QString("<?xml version=\"1.0\" encoding=\"%1\"?>\n").arg(outputEncoding);
1782 out() << "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n";
1783 out() << QString("<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"%1\" lang=\"%1\">\n").arg(naturalLanguage);
1784 out() << "<head>\n";
1785 out() << " <meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\" />\n";
1786 if (node && !node->doc().location().isEmpty())
1787 out() << "<!-- " << node->doc().location().fileName() << " -->\n";
1789 QString shortVersion = qdb_->version();
1790 if (shortVersion.count(QChar('.')) == 2)
1791 shortVersion.truncate(shortVersion.lastIndexOf(QChar('.')));
1792 if (!project.isEmpty())
1793 shortVersion = project + QLatin1Char(' ') + shortVersion + QLatin1String(": ");
1795 shortVersion = QLatin1String("Qt ") + shortVersion + QLatin1String(": ");
1797 // Generating page title
1798 out() << " <title>" << shortVersion << protectEnc(title) << "</title>\n";
1800 // Include style sheet and script links.
1801 out() << headerStyles;
1802 out() << headerScripts;
1803 if (endHeader.isEmpty())
1804 out() << "</head>\n<body>\n";
1808 #ifdef GENERATE_MAC_REFS
1810 generateMacRef(node, marker);
1813 out() << QString(postHeader).replace("\\" + COMMAND_VERSION, qdb_->version());
1814 generateBreadCrumbs(title,node,marker);
1815 out() << QString(postPostHeader).replace("\\" + COMMAND_VERSION, qdb_->version());
1817 navigationLinks.clear();
1819 if (node && !node->links().empty()) {
1820 QPair<QString,QString> linkPair;
1821 QPair<QString,QString> anchorPair;
1822 const Node *linkNode;
1824 if (node->links().contains(Node::PreviousLink)) {
1825 linkPair = node->links()[Node::PreviousLink];
1826 linkNode = qdb_->findNodeForTarget(linkPair.first, node);
1828 node->doc().location().warning(tr("Cannot link to '%1'").arg(linkPair.first));
1829 if (!linkNode || linkNode == node)
1830 anchorPair = linkPair;
1832 anchorPair = anchorForNode(linkNode);
1834 out() << " <link rel=\"prev\" href=\""
1835 << anchorPair.first << "\" />\n";
1837 navigationLinks += "<a class=\"prevPage\" href=\"" + anchorPair.first + "\">";
1838 if (linkPair.first == linkPair.second && !anchorPair.second.isEmpty())
1839 navigationLinks += protect(anchorPair.second);
1841 navigationLinks += protect(linkPair.second);
1842 navigationLinks += "</a>\n";
1844 if (node->links().contains(Node::NextLink)) {
1845 linkPair = node->links()[Node::NextLink];
1846 linkNode = qdb_->findNodeForTarget(linkPair.first, node);
1848 node->doc().location().warning(tr("Cannot link to '%1'").arg(linkPair.first));
1849 if (!linkNode || linkNode == node)
1850 anchorPair = linkPair;
1852 anchorPair = anchorForNode(linkNode);
1854 out() << " <link rel=\"next\" href=\""
1855 << anchorPair.first << "\" />\n";
1857 navigationLinks += "<a class=\"nextPage\" href=\"" + anchorPair.first + "\">";
1858 if (linkPair.first == linkPair.second && !anchorPair.second.isEmpty())
1859 navigationLinks += protect(anchorPair.second);
1861 navigationLinks += protect(linkPair.second);
1862 navigationLinks += "</a>\n";
1864 if (node->links().contains(Node::StartLink)) {
1865 linkPair = node->links()[Node::StartLink];
1866 linkNode = qdb_->findNodeForTarget(linkPair.first, node);
1868 node->doc().location().warning(tr("Cannot link to '%1'").arg(linkPair.first));
1869 if (!linkNode || linkNode == node)
1870 anchorPair = linkPair;
1872 anchorPair = anchorForNode(linkNode);
1873 out() << " <link rel=\"start\" href=\""
1874 << anchorPair.first << "\" />\n";
1878 if (node && !node->links().empty())
1879 out() << "<p class=\"naviNextPrevious headerNavi\">\n" << navigationLinks << "</p><p/>\n";
1882 void HtmlGenerator::generateTitle(const QString& title,
1883 const Text &subTitle,
1884 SubTitleSize subTitleSize,
1885 const Node *relative,
1888 if (!title.isEmpty())
1889 out() << "<h1 class=\"title\">" << protectEnc(title) << "</h1>\n";
1890 if (!subTitle.isEmpty()) {
1892 if (subTitleSize == SmallSubTitle)
1893 out() << " class=\"small-subtitle\">";
1895 out() << " class=\"subtitle\">";
1896 generateText(subTitle, relative, marker);
1897 out() << "</span>\n";
1901 void HtmlGenerator::generateFooter(const Node *node)
1903 if (node && !node->links().empty())
1904 out() << "<p class=\"naviNextPrevious footerNavi\">\n" << navigationLinks << "</p>\n";
1906 out() << QString(footer).replace("\\" + COMMAND_VERSION, qdb_->version())
1907 << QString(address).replace("\\" + COMMAND_VERSION, qdb_->version());
1909 out() << "</body>\n";
1910 out() << "</html>\n";
1913 void HtmlGenerator::generateBrief(const Node *node, CodeMarker *marker,
1914 const Node *relative)
1916 Text brief = node->doc().briefText();
1917 if (!brief.isEmpty()) {
1918 generateExtractionMark(node, BriefMark);
1920 generateText(brief, node, marker);
1922 if (!relative || node == relative)
1923 out() << " <a href=\"#";
1925 out() << " <a href=\"" << linkForNode(node, relative) << '#';
1926 out() << registerRef("details") << "\">More...</a></p>\n";
1929 generateExtractionMark(node, EndMark);
1933 void HtmlGenerator::generateIncludes(const InnerNode *inner, CodeMarker *marker)
1935 if (!inner->includes().isEmpty()) {
1936 out() << "<pre class=\"cpp\">"
1937 << trimmedTrailing(highlightedCode(indent(codeIndent,
1938 marker->markedUpIncludes(inner->includes())),
1945 Revised for the new doc format.
1946 Generates a table of contents beginning at \a node.
1948 void HtmlGenerator::generateTableOfContents(const Node *node,
1950 QList<Section>* sections)
1953 if (node->doc().hasTableOfContents())
1954 toc = node->doc().tableOfContents();
1955 if (toc.isEmpty() && !sections && (node->subType() != Node::Module))
1958 QStringList sectionNumber;
1959 int detailsBase = 0;
1961 // disable nested links in table of contents
1965 out() << "<div class=\"toc\">\n";
1966 out() << "<h3><a name=\"toc\">Contents</a></h3>\n";
1967 sectionNumber.append("1");
1970 if (node->subType() == Node::Module) {
1971 if (node->hasNamespaces()) {
1972 out() << "<li class=\"level"
1973 << sectionNumber.size()
1975 << registerRef("namespaces")
1976 << "\">Namespaces</a></li>\n";
1978 if (node->hasClasses()) {
1979 out() << "<li class=\"level"
1980 << sectionNumber.size()
1982 << registerRef("classes")
1983 << "\">Classes</a></li>\n";
1985 out() << "<li class=\"level"
1986 << sectionNumber.size()
1988 << registerRef("details")
1989 << "\">Detailed Description</a></li>\n";
1990 for (int i = 0; i < toc.size(); ++i) {
1991 if (toc.at(i)->string().toInt() == 1) {
1997 else if (sections && ((node->type() == Node::Class) ||
1998 (node->type() == Node::Namespace) ||
1999 (node->subType() == Node::QmlClass))) {
2000 QList<Section>::ConstIterator s = sections->constBegin();
2001 while (s != sections->constEnd()) {
2002 if (!s->members.isEmpty() || !s->reimpMembers.isEmpty()) {
2003 out() << "<li class=\"level"
2004 << sectionNumber.size()
2006 << registerRef((*s).pluralMember)
2007 << "\">" << (*s).name
2012 out() << "<li class=\"level"
2013 << sectionNumber.size()
2015 << registerRef("details")
2016 << "\">Detailed Description</a></li>\n";
2017 for (int i = 0; i < toc.size(); ++i) {
2018 if (toc.at(i)->string().toInt() == 1) {
2025 for (int i = 0; i < toc.size(); ++i) {
2026 Atom *atom = toc.at(i);
2027 int nextLevel = atom->string().toInt() + detailsBase;
2028 if (nextLevel >= 0) {
2029 if (sectionNumber.size() < nextLevel) {
2031 sectionNumber.append("1");
2032 } while (sectionNumber.size() < nextLevel);
2035 while (sectionNumber.size() > nextLevel) {
2036 sectionNumber.removeLast();
2038 sectionNumber.last() = QString::number(sectionNumber.last().toInt() + 1);
2042 Text headingText = Text::sectionHeading(atom);
2043 QString s = headingText.toString();
2044 out() << "<li class=\"level"
2045 << sectionNumber.size()
2047 out() << "<a href=\""
2049 << Doc::canonicalTitle(s)
2051 generateAtomList(headingText.firstAtom(), node, marker, true, numAtoms);
2052 out() << "</a></li>\n";
2054 while (!sectionNumber.isEmpty()) {
2055 sectionNumber.removeLast();
2058 out() << "</div>\n";
2059 inContents_ = false;
2063 QString HtmlGenerator::generateListOfAllMemberFile(const InnerNode *inner,
2066 QList<Section> sections;
2067 QList<Section>::ConstIterator s;
2069 sections = marker->sections(inner,
2070 CodeMarker::Subpage,
2072 if (sections.isEmpty())
2075 QString fileName = fileBase(inner) + "-members." + fileExtension();
2076 beginSubPage(inner, fileName);
2077 QString title = "List of All Members for " + inner->name();
2078 generateHeader(title, inner, marker);
2079 generateTitle(title, Text(), SmallSubTitle, inner, marker);
2080 out() << "<p>This is the complete list of members for ";
2081 generateFullName(inner, 0);
2082 out() << ", including inherited members.</p>\n";
2084 Section section = sections.first();
2085 generateSectionList(section, 0, marker, CodeMarker::Subpage);
2093 This function creates an html page on which are listed all
2094 the members of QML class \a qml_cn, including the inherited
2095 members. The \a marker is used for formatting stuff.
2097 QString HtmlGenerator::generateAllQmlMembersFile(const QmlClassNode* qml_cn,
2100 QList<Section> sections;
2101 QList<Section>::ConstIterator s;
2103 sections = marker->qmlSections(qml_cn,CodeMarker::Subpage);
2104 if (sections.isEmpty())
2107 QString fileName = fileBase(qml_cn) + "-members." + fileExtension();
2108 beginSubPage(qml_cn, fileName);
2109 QString title = "List of All Members for " + qml_cn->name();
2110 generateHeader(title, qml_cn, marker);
2111 generateTitle(title, Text(), SmallSubTitle, qml_cn, marker);
2112 out() << "<p>This is the complete list of members for ";
2113 generateFullName(qml_cn, 0);
2114 out() << ", including inherited members.</p>\n";
2116 Section section = sections.first();
2117 generateSectionList(section, 0, marker, CodeMarker::Subpage);
2124 QString HtmlGenerator::generateLowStatusMemberFile(const InnerNode *inner,
2126 CodeMarker::Status status)
2128 QList<Section> sections = marker->sections(inner,
2129 CodeMarker::Summary,
2131 QMutableListIterator<Section> j(sections);
2132 while (j.hasNext()) {
2133 if (j.next().members.size() == 0)
2136 if (sections.isEmpty())
2144 if (status == CodeMarker::Compat) {
2145 title = "Compatibility Members for " + inner->name();
2146 fileName = fileBase(inner) + "-compat." + fileExtension();
2149 title = "Obsolete Members for " + inner->name();
2150 fileName = fileBase(inner) + "-obsolete." + fileExtension();
2153 beginSubPage(inner, fileName);
2154 generateHeader(title, inner, marker);
2155 generateTitle(title, Text(), SmallSubTitle, inner, marker);
2157 if (status == CodeMarker::Compat) {
2158 out() << "<p><b>The following class members are part of the "
2159 "Qt compatibility layer.</b> We advise against "
2160 "using them in new code.</p>\n";
2163 out() << "<p><b>The following class members are obsolete.</b> "
2164 << "They are provided to keep old source code working. "
2165 << "We strongly advise against using them in new code.</p>\n";
2168 out() << "<p><ul><li><a href=\""
2169 << linkForNode(inner, 0) << "\">"
2170 << protectEnc(inner->name())
2171 << " class reference</a></li></ul></p>\n";
2173 for (i = 0; i < sections.size(); ++i) {
2174 out() << "<h2>" << protectEnc(sections.at(i).name) << "</h2>\n";
2175 generateSectionList(sections.at(i), inner, marker, CodeMarker::Summary);
2178 sections = marker->sections(inner, CodeMarker::Detailed, status);
2179 for (i = 0; i < sections.size(); ++i) {
2180 //out() << "<hr />\n";
2181 out() << "<h2>" << protectEnc(sections.at(i).name) << "</h2>\n";
2183 NodeList::ConstIterator m = sections.at(i).members.constBegin();
2184 while (m != sections.at(i).members.constEnd()) {
2185 if ((*m)->access() != Node::Private)
2186 generateDetailedMember(*m, inner, marker);
2196 void HtmlGenerator::generateClassHierarchy(const Node *relative, const NodeMap& classMap)
2198 if (classMap.isEmpty())
2202 NodeMap::ConstIterator c = classMap.constBegin();
2203 while (c != classMap.constEnd()) {
2204 const ClassNode *classe = static_cast<const ClassNode *>(*c);
2205 if (classe->baseClasses().isEmpty())
2206 topLevel.insert(classe->name(), classe);
2210 QStack<NodeMap > stack;
2211 stack.push(topLevel);
2214 while (!stack.isEmpty()) {
2215 if (stack.top().isEmpty()) {
2220 const ClassNode *child =
2221 static_cast<const ClassNode *>(*stack.top().constBegin());
2223 generateFullName(child, relative);
2225 stack.top().erase(stack.top().begin());
2228 foreach (const RelatedClass &d, child->derivedClasses()) {
2229 if (d.access != Node::Private && !d.node->doc().isEmpty())
2230 newTop.insert(d.node->name(), d.node);
2232 if (!newTop.isEmpty()) {
2240 void HtmlGenerator::generateAnnotatedList(const Node *relative,
2242 const NodeMap &nodeMap,
2245 out() << "<table class=\"annotated\">\n";
2248 foreach (const QString &name, nodeMap.keys()) {
2249 const Node *node = nodeMap[name];
2251 if (node->status() == Node::Obsolete)
2254 if (allOdd || (++row % 2 == 1))
2255 out() << "<tr class=\"odd topAlign\">";
2257 out() << "<tr class=\"even topAlign\">";
2258 out() << "<td class=\"tblName\"><p>";
2259 generateFullName(node, relative);
2260 out() << "</p></td>";
2262 if (!(node->type() == Node::Document)) {
2263 Text brief = node->doc().trimmedBriefText(name);
2264 if (!brief.isEmpty()) {
2265 out() << "<td class=\"tblDescr\"><p>";
2266 generateText(brief, node, marker);
2267 out() << "</p></td>";
2271 out() << "<td class=\"tblDescr\"><p>";
2272 out() << protectEnc(node->doc().briefText().toString());
2273 out() << "</p></td>";
2277 out() << "</table>\n";
2281 This function finds the common prefix of the names of all
2282 the classes in \a classMap and then generates a compact
2283 list of the class names alphabetized on the part of the
2284 name not including the common prefix. You can tell the
2285 function to use \a comonPrefix as the common prefix, but
2286 normally you let it figure it out itself by looking at
2287 the name of the first and last classes in \a classMap.
2289 void HtmlGenerator::generateCompactList(const Node *relative,
2290 const NodeMap &classMap,
2291 bool includeAlphabet,
2292 QString commonPrefix)
2294 const int NumParagraphs = 37; // '0' to '9', 'A' to 'Z', '_'
2296 if (classMap.isEmpty())
2300 If commonPrefix is not empty, then the caller knows what
2301 the common prefix is and has passed it in, so just use that
2302 one. But if the commonPrefix is empty (it normally is), then
2303 compute a common prefix using this simple algorithm. Note we
2304 assume the prefix length is 1, i.e. we will have a single
2305 character as the common prefix.
2307 int commonPrefixLen = commonPrefix.length();
2308 if (commonPrefixLen == 0) {
2309 QVector<int> count(26);
2310 for (int i=0; i<26; ++i)
2313 NodeMap::const_iterator iter = classMap.constBegin();
2314 while (iter != classMap.constEnd()) {
2315 if (!iter.key().contains("::")) {
2316 QChar c = iter.key()[0];
2317 if ((c >= 'A') && (c <= 'Z')) {
2318 int idx = c.unicode() - QChar('A').unicode();
2326 for (int i=0; i<26; ++i) {
2327 if (count[i] > highest) {
2332 idx += QChar('A').unicode();
2334 commonPrefix = common;
2335 commonPrefixLen = 1;
2339 Divide the data into 37 paragraphs: 0, ..., 9, A, ..., Z,
2340 underscore (_). QAccel will fall in paragraph 10 (A) and
2341 QXtWidget in paragraph 33 (X). This is the only place where we
2342 assume that NumParagraphs is 37. Each paragraph is a NodeMap.
2344 NodeMap paragraph[NumParagraphs+1];
2345 QString paragraphName[NumParagraphs+1];
2346 QSet<char> usedParagraphNames;
2348 NodeMap::ConstIterator c = classMap.constBegin();
2349 while (c != classMap.constEnd()) {
2350 QStringList pieces = c.key().split("::");
2352 int idx = commonPrefixLen;
2353 if (!pieces.last().startsWith(commonPrefix))
2355 if (pieces.size() == 1)
2356 key = pieces.last().mid(idx).toLower();
2358 key = pieces.last().toLower();
2360 int paragraphNr = NumParagraphs - 1;
2362 if (key[0].digitValue() != -1) {
2363 paragraphNr = key[0].digitValue();
2365 else if (key[0] >= QLatin1Char('a') && key[0] <= QLatin1Char('z')) {
2366 paragraphNr = 10 + key[0].unicode() - 'a';
2369 paragraphName[paragraphNr] = key[0].toUpper();
2370 usedParagraphNames.insert(key[0].toLower().cell());
2371 paragraph[paragraphNr].insert(key, c.value());
2376 Each paragraph j has a size: paragraph[j].count(). In the
2377 discussion, we will assume paragraphs 0 to 5 will have sizes
2380 We now want to compute the paragraph offset. Paragraphs 0 to 6
2381 start at offsets 0, 3, 4, 8, 9, 14, 23.
2383 int paragraphOffset[NumParagraphs + 1]; // 37 + 1
2384 paragraphOffset[0] = 0;
2385 for (int i=0; i<NumParagraphs; i++) // i = 0..36
2386 paragraphOffset[i+1] = paragraphOffset[i] + paragraph[i].count();
2389 Output the alphabet as a row of links.
2391 if (includeAlphabet) {
2392 out() << "<p class=\"centerAlign functionIndex\"><b>";
2393 for (int i = 0; i < 26; i++) {
2395 if (usedParagraphNames.contains(char('a' + i)))
2396 out() << QString("<a href=\"#%1\">%2</a> ").arg(ch).arg(ch.toUpper());
2398 out() << "</b></p>\n";
2402 Output a <div> element to contain all the <dl> elements.
2404 out() << "<div class=\"flowListDiv\">\n";
2408 int curParOffset = 0;
2410 for (int i=0; i<classMap.count(); i++) {
2411 while ((curParNr < NumParagraphs) &&
2412 (curParOffset == paragraph[curParNr].count())) {
2418 Starting a new paragraph means starting a new <dl>.
2420 if (curParOffset == 0) {
2423 if (++numTableRows_ % 2 == 1)
2424 out() << "<dl class=\"flowList odd\">";
2426 out() << "<dl class=\"flowList even\">";
2427 out() << "<dt class=\"alphaChar\">";
2428 if (includeAlphabet) {
2429 QChar c = paragraphName[curParNr][0].toLower();
2430 out() << QString("<a name=\"%1\"></a>").arg(c);
2433 << paragraphName[curParNr]
2439 Output a <dd> for the current offset in the current paragraph.
2442 if ((curParNr < NumParagraphs) &&
2443 !paragraphName[curParNr].isEmpty()) {
2444 NodeMap::Iterator it;
2445 it = paragraph[curParNr].begin();
2446 for (int i=0; i<curParOffset; i++)
2450 Previously, we used generateFullName() for this, but we
2451 require some special formatting.
2453 out() << "<a href=\"" << linkForNode(it.value(), relative) << "\">";
2456 if (it.value()->subType() == Node::QmlClass)
2457 pieces << it.value()->name();
2459 pieces = it.value()->fullName(relative).split("::");
2460 out() << protectEnc(pieces.last());
2462 if (pieces.size() > 1) {
2464 generateFullName(it.value()->parent(), relative);
2471 if (classMap.count() > 0)
2474 out() << "</div>\n";
2477 void HtmlGenerator::generateFunctionIndex(const Node *relative)
2479 out() << "<p class=\"centerAlign functionIndex\"><b>";
2480 for (int i = 0; i < 26; i++) {
2482 out() << QString("<a href=\"#%1\">%2</a> ").arg(ch).arg(ch.toUpper());
2484 out() << "</b></p>\n";
2486 char nextLetter = 'a';
2490 NodeMapMap funcIndex = qdb_->getFunctionIndex();
2491 QMap<QString, NodeMap >::ConstIterator f = funcIndex.constBegin();
2492 while (f != funcIndex.constEnd()) {
2494 out() << protectEnc(f.key()) << ':';
2496 currentLetter = f.key()[0].unicode();
2497 while (islower(currentLetter) && currentLetter >= nextLetter) {
2498 out() << QString("<a name=\"%1\"></a>").arg(nextLetter);
2502 NodeMap::ConstIterator s = (*f).constBegin();
2503 while (s != (*f).constEnd()) {
2505 generateFullName((*s)->parent(), relative, *s);
2515 void HtmlGenerator::generateLegaleseList(const Node *relative, CodeMarker *marker)
2517 TextToNodeMap& legaleseTexts = qdb_->getLegaleseTexts();
2518 QMap<Text, const Node *>::ConstIterator it = legaleseTexts.constBegin();
2519 while (it != legaleseTexts.constEnd()) {
2520 Text text = it.key();
2521 //out() << "<hr />\n";
2522 generateText(text, relative, marker);
2526 generateFullName(it.value(), relative);
2529 } while (it != legaleseTexts.constEnd() && it.key() == text);
2534 void HtmlGenerator::generateQmlItem(const Node *node,
2535 const Node *relative,
2539 QString marked = marker->markedUpQmlItem(node,summary);
2540 QRegExp templateTag("(<[^@>]*>)");
2541 if (marked.indexOf(templateTag) != -1) {
2542 QString contents = protectEnc(marked.mid(templateTag.pos(1),
2543 templateTag.cap(1).length()));
2544 marked.replace(templateTag.pos(1), templateTag.cap(1).length(),
2547 marked.replace(QRegExp("<@param>([a-z]+)_([1-9n])</@param>"),
2548 "<i>\\1<sub>\\2</sub></i>");
2549 marked.replace("<@param>", "<i>");
2550 marked.replace("</@param>", "</i>");
2553 marked.replace("@name>", "b>");
2555 marked.replace("<@extra>", "<tt>");
2556 marked.replace("</@extra>", "</tt>");
2559 marked.remove("<@type>");
2560 marked.remove("</@type>");
2562 out() << highlightedCode(marked, relative, false, node);
2565 void HtmlGenerator::generateOverviewList(const Node *relative)
2567 QMap<const DocNode *, QMap<QString, DocNode *> > docNodeMap;
2568 QMap<QString, const DocNode *> groupTitlesMap;
2569 QMap<QString, DocNode *> uncategorizedNodeMap;
2570 QRegExp singleDigit("\\b([0-9])\\b");
2572 const NodeList children = qdb_->treeRoot()->childNodes();
2573 foreach (Node *child, children) {
2574 if (child->type() == Node::Document && child != relative) {
2575 DocNode *docNode = static_cast<DocNode *>(child);
2577 // Check whether the page is part of a group or is the group
2580 bool isGroupPage = false;
2581 if (docNode->doc().metaCommandsUsed().contains("group")) {
2582 group = docNode->doc().metaCommandArgs("group")[0].first;
2586 // there are too many examples; they would clutter the list
2587 if (docNode->subType() == Node::Example)
2590 // not interested either in individual (Qt Designer etc.) manual chapters
2591 if (docNode->links().contains(Node::ContentsLink))
2594 // Discard external nodes.
2595 if (docNode->subType() == Node::ExternalPage)
2598 QString sortKey = docNode->fullTitle().toLower();
2599 if (sortKey.startsWith("the "))
2600 sortKey.remove(0, 4);
2601 sortKey.replace(singleDigit, "0\\1");
2603 if (!group.isEmpty()) {
2605 // If we encounter a group definition page, we add all
2606 // the pages in that group to the list for that group.
2607 foreach (Node *member, docNode->members()) {
2608 if (member->type() != Node::Document)
2610 DocNode *page = static_cast<DocNode *>(member);
2612 QString sortKey = page->fullTitle().toLower();
2613 if (sortKey.startsWith("the "))
2614 sortKey.remove(0, 4);
2615 sortKey.replace(singleDigit, "0\\1");
2616 docNodeMap[const_cast<const DocNode *>(docNode)].insert(sortKey, page);
2617 groupTitlesMap[docNode->fullTitle()] = const_cast<const DocNode *>(docNode);
2621 else if (!isGroupPage) {
2622 // If we encounter a page that belongs to a group then
2623 // we add that page to the list for that group.
2624 const DocNode* gn = qdb_->findGroupNode(QStringList(group));
2626 docNodeMap[gn].insert(sortKey, docNode);
2632 // We now list all the pages found that belong to groups.
2633 // If only certain pages were found for a group, but the definition page
2634 // for that group wasn't listed, the list of pages will be intentionally
2635 // incomplete. However, if the group definition page was listed, all the
2636 // pages in that group are listed for completeness.
2638 if (!docNodeMap.isEmpty()) {
2639 foreach (const QString &groupTitle, groupTitlesMap.keys()) {
2640 const DocNode *groupNode = groupTitlesMap[groupTitle];
2641 out() << QString("<h3><a href=\"%1\">%2</a></h3>\n").arg(
2642 linkForNode(groupNode, relative)).arg(
2643 protectEnc(groupNode->fullTitle()));
2645 if (docNodeMap[groupNode].count() == 0)
2650 foreach (const DocNode *docNode, docNodeMap[groupNode]) {
2651 QString title = docNode->fullTitle();
2652 if (title.startsWith("The "))
2654 out() << "<li><a href=\"" << linkForNode(docNode, relative) << "\">"
2655 << protectEnc(title) << "</a></li>\n";
2661 if (!uncategorizedNodeMap.isEmpty()) {
2662 out() << QString("<h3>Miscellaneous</h3>\n");
2664 foreach (const DocNode *docNode, uncategorizedNodeMap) {
2665 QString title = docNode->fullTitle();
2666 if (title.startsWith("The "))
2668 out() << "<li><a href=\"" << linkForNode(docNode, relative) << "\">"
2669 << protectEnc(title) << "</a></li>\n";
2675 void HtmlGenerator::generateSection(const NodeList& nl,
2676 const Node *relative,
2678 CodeMarker::SynopsisStyle style)
2680 bool alignNames = true;
2681 if (!nl.isEmpty()) {
2682 bool twoColumn = false;
2683 if (style == CodeMarker::Subpage) {
2685 twoColumn = (nl.count() >= 16);
2687 else if (nl.first()->type() == Node::Property) {
2688 twoColumn = (nl.count() >= 5);
2692 out() << "<table class=\"alignedsummary\">\n";
2696 out() << "<table class=\"propsummary\">\n"
2697 << "<tr><td class=\"topAlign\">";
2702 NodeList::ConstIterator m = nl.constBegin();
2703 while (m != nl.constEnd()) {
2704 if ((*m)->access() == Node::Private) {
2710 out() << "<tr><td class=\"memItemLeft rightAlign topAlign\"> ";
2713 if (twoColumn && i == (int) (nl.count() + 1) / 2)
2714 out() << "</ul></td><td class=\"topAlign\"><ul>\n";
2715 out() << "<li class=\"fn\">";
2718 generateSynopsis(*m, relative, marker, style, alignNames);
2720 out() << "</td></tr>\n";
2727 out() << "</table>\n";
2731 out() << "</td></tr>\n</table>\n";
2736 void HtmlGenerator::generateSectionList(const Section& section,
2737 const Node *relative,
2739 CodeMarker::SynopsisStyle style)
2741 bool alignNames = true;
2742 if (!section.members.isEmpty()) {
2743 bool twoColumn = false;
2744 if (style == CodeMarker::Subpage) {
2746 twoColumn = (section.members.count() >= 16);
2748 else if (section.members.first()->type() == Node::Property) {
2749 twoColumn = (section.members.count() >= 5);
2753 out() << "<table class=\"alignedsummary\">\n";
2757 out() << "<table class=\"propsummary\">\n"
2758 << "<tr><td class=\"topAlign\">";
2763 NodeList::ConstIterator m = section.members.constBegin();
2764 while (m != section.members.constEnd()) {
2765 if ((*m)->access() == Node::Private) {
2771 out() << "<tr><td class=\"memItemLeft topAlign rightAlign\"> ";
2774 if (twoColumn && i == (int) (section.members.count() + 1) / 2)
2775 out() << "</ul></td><td class=\"topAlign\"><ul>\n";
2776 out() << "<li class=\"fn\">";
2780 if (!section.keys.isEmpty()) {
2781 prefix = section.keys.at(i).mid(1);
2782 prefix = prefix.left(section.keys.at(i).indexOf("::")+1);
2784 generateSynopsis(*m, relative, marker, style, alignNames, &prefix);
2786 out() << "</td></tr>\n";
2793 out() << "</table>\n";
2797 out() << "</td></tr>\n</table>\n";
2801 if (style == CodeMarker::Summary && !section.inherited.isEmpty()) {
2803 generateSectionInheritedList(section, relative);
2808 void HtmlGenerator::generateSectionInheritedList(const Section& section, const Node *relative)
2810 QList<QPair<InnerNode *, int> >::ConstIterator p = section.inherited.constBegin();
2811 while (p != section.inherited.constEnd()) {
2812 out() << "<li class=\"fn\">";
2813 out() << (*p).second << ' ';
2814 if ((*p).second == 1) {
2815 out() << section.singularMember;
2818 out() << section.pluralMember;
2820 out() << " inherited from <a href=\"" << fileName((*p).first)
2821 << '#' << HtmlGenerator::cleanRef(section.name.toLower()) << "\">"
2822 << protectEnc((*p).first->plainFullName(relative))
2828 void HtmlGenerator::generateSynopsis(const Node *node,
2829 const Node *relative,
2831 CodeMarker::SynopsisStyle style,
2833 const QString* prefix)
2835 QString marked = marker->markedUpSynopsis(node, relative, style);
2838 marked.prepend(*prefix);
2839 QRegExp templateTag("(<[^@>]*>)");
2840 if (marked.indexOf(templateTag) != -1) {
2841 QString contents = protectEnc(marked.mid(templateTag.pos(1),
2842 templateTag.cap(1).length()));
2843 marked.replace(templateTag.pos(1), templateTag.cap(1).length(),
2846 marked.replace(QRegExp("<@param>([a-z]+)_([1-9n])</@param>"),
2847 "<i>\\1<sub>\\2</sub></i>");
2848 marked.replace("<@param>", "<i> ");
2849 marked.replace("</@param>", "</i>");
2851 if (style == CodeMarker::Summary) {
2852 marked.remove("<@name>"); // was "<b>"
2853 marked.remove("</@name>"); // was "</b>"
2856 if (style == CodeMarker::Subpage) {
2857 QRegExp extraRegExp("<@extra>.*</@extra>");
2858 extraRegExp.setMinimal(true);
2859 marked.remove(extraRegExp);
2861 marked.replace("<@extra>", "<tt>");
2862 marked.replace("</@extra>", "</tt>");
2865 if (style != CodeMarker::Detailed) {
2866 marked.remove("<@type>");
2867 marked.remove("</@type>");
2870 out() << highlightedCode(marked, relative, alignNames);
2873 QString HtmlGenerator::highlightedCode(const QString& markedCode,
2874 const Node* relative,
2878 QString src = markedCode;
2883 const QChar charLangle = '<';
2884 const QChar charAt = '@';
2886 static const QString typeTag("type");
2887 static const QString headerTag("headerfile");
2888 static const QString funcTag("func");
2889 static const QString linkTag("link");
2891 // replace all <@link> tags: "(<@link node=\"([^\"]+)\">).*(</@link>)"
2893 for (int i = 0, srcSize = src.size(); i < srcSize;) {
2894 if (src.at(i) == charLangle && src.at(i + 1) == charAt) {
2895 if (alignNames && !done) {
2896 html += "</td><td class=\"memItemRight bottomAlign\">";
2900 if (parseArg(src, linkTag, &i, srcSize, &arg, &par1)) {
2902 const Node* n = CodeMarker::nodeForString(par1.toString());
2903 QString link = linkForNode(n, relative);
2904 addLink(link, arg, &html);
2913 html += src.at(i++);
2917 // replace all <@func> tags: "(<@func target=\"([^\"]*)\">)(.*)(</@func>)"
2920 for (int i = 0, srcSize = src.size(); i < srcSize;) {
2921 if (src.at(i) == charLangle && src.at(i + 1) == charAt) {
2923 if (parseArg(src, funcTag, &i, srcSize, &arg, &par1)) {
2925 const Node* n = qdb_->resolveTarget(par1.toString(), relative);
2926 QString link = linkForNode(n, relative);
2927 addLink(link, arg, &html);
2928 par1 = QStringRef();
2936 html += src.at(i++);
2940 // replace all "(<@(type|headerfile|func)(?: +[^>]*)?>)(.*)(</@\\2>)" tags
2944 for (int i=0, srcSize=src.size(); i<srcSize;) {
2945 if (src.at(i) == charLangle && src.at(i+1) == charAt) {
2947 bool handled = false;
2948 if (parseArg(src, typeTag, &i, srcSize, &arg, &par1)) {
2949 par1 = QStringRef();
2950 const Node* n = qdb_->resolveTarget(arg.toString(), relative, self);
2951 html += QLatin1String("<span class=\"type\">");
2952 if (n && n->subType() == Node::QmlBasicType) {
2953 if (relative && relative->subType() == Node::QmlClass)
2954 addLink(linkForNode(n,relative), arg, &html);
2956 html += arg.toString();
2959 addLink(linkForNode(n,relative), arg, &html);
2960 html += QLatin1String("</span>");
2963 else if (parseArg(src, headerTag, &i, srcSize, &arg, &par1)) {
2964 par1 = QStringRef();
2965 const Node* n = qdb_->resolveTarget(arg.toString(), relative);
2966 addLink(linkForNode(n,relative), arg, &html);
2969 else if (parseArg(src, funcTag, &i, srcSize, &arg, &par1)) {
2970 par1 = QStringRef();
2971 const Node* n = qdb_->resolveTarget(arg.toString(), relative);
2972 addLink(linkForNode(n,relative), arg, &html);
2982 html += src.at(i++);
2987 // "<@comment>" -> "<span class=\"comment\">";
2988 // "<@preprocessor>" -> "<span class=\"preprocessor\">";
2989 // "<@string>" -> "<span class=\"string\">";
2990 // "<@char>" -> "<span class=\"char\">";
2991 // "<@number>" -> "<span class=\"number\">";
2992 // "<@op>" -> "<span class=\"operator\">";
2993 // "<@type>" -> "<span class=\"type\">";
2994 // "<@name>" -> "<span class=\"name\">";
2995 // "<@keyword>" -> "<span class=\"keyword\">";
2996 // "</@(?:comment|preprocessor|string|char|number|op|type|name|keyword)>" -> "</span>"
2999 static const QString spanTags[] = {
3000 "<@comment>", "<span class=\"comment\">",
3001 "<@preprocessor>", "<span class=\"preprocessor\">",
3002 "<@string>", "<span class=\"string\">",
3003 "<@char>", "<span class=\"char\">",
3004 "<@number>", "<span class=\"number\">",
3005 "<@op>", "<span class=\"operator\">",
3006 "<@type>", "<span class=\"type\">",
3007 "<@name>", "<span class=\"name\">",
3008 "<@keyword>", "<span class=\"keyword\">",
3009 "</@comment>", "</span>",
3010 "</@preprocessor>", "</span>",
3011 "</@string>", "</span>",
3012 "</@char>", "</span>",
3013 "</@number>", "</span>",
3014 "</@op>", "</span>",
3015 "</@type>", "</span>",
3016 "</@name>", "</span>",
3017 "</@keyword>", "</span>",
3019 // Update the upper bound of k in the following code to match the length
3020 // of the above array.
3021 for (int i = 0, n = src.size(); i < n;) {
3022 if (src.at(i) == charLangle) {
3023 bool handled = false;
3024 for (int k = 0; k != 18; ++k) {
3025 const QString & tag = spanTags[2 * k];
3026 if (tag == QStringRef(&src, i, tag.length())) {
3027 html += spanTags[2 * k + 1];
3035 if (src.at(i) == charAt ||
3036 (src.at(i) == QLatin1Char('/') && src.at(i + 1) == charAt)) {
3037 // drop 'our' unknown tags (the ones still containing '@')
3038 while (i < n && src.at(i) != QLatin1Char('>'))
3043 // retain all others
3056 void HtmlGenerator::generateLink(const Atom* atom, CodeMarker* marker)
3058 static QRegExp camelCase("[A-Z][A-Z][a-z]|[a-z][A-Z0-9]|_");
3060 if (funcLeftParen.indexIn(atom->string()) != -1 && marker->recognizeLanguage("Cpp")) {
3061 // hack for C++: move () outside of link
3062 int k = funcLeftParen.pos(1);
3063 out() << protectEnc(atom->string().left(k));
3064 if (link_.isEmpty()) {
3065 if (showBrokenLinks)
3071 out() << protectEnc(atom->string().mid(k));
3073 out() << protectEnc(atom->string());
3077 QString HtmlGenerator::cleanRef(const QString& ref)
3084 clean.reserve(ref.size() + 20);
3085 const QChar c = ref[0];
3086 const uint u = c.unicode();
3088 if ((u >= 'a' && u <= 'z') ||
3089 (u >= 'A' && u <= 'Z') ||
3090 (u >= '0' && u <= '9')) {
3092 } else if (u == '~') {
3094 } else if (u == '_') {
3095 clean += "underscore.";
3097 clean += QLatin1Char('A');
3100 for (int i = 1; i < (int) ref.length(); i++) {
3101 const QChar c = ref[i];
3102 const uint u = c.unicode();
3103 if ((u >= 'a' && u <= 'z') ||
3104 (u >= 'A' && u <= 'Z') ||
3105 (u >= '0' && u <= '9') || u == '-' ||
3106 u == '_' || u == ':' || u == '.') {
3108 } else if (c.isSpace()) {
3109 clean += QLatin1Char('-');
3110 } else if (u == '!') {
3112 } else if (u == '&') {
3114 } else if (u == '<') {
3116 } else if (u == '=') {
3118 } else if (u == '>') {
3120 } else if (u == '#') {
3121 clean += QLatin1Char('#');
3123 clean += QLatin1Char('-');
3124 clean += QString::number((int)u, 16);
3130 QString HtmlGenerator::registerRef(const QString& ref)
3132 QString clean = HtmlGenerator::cleanRef(ref);
3135 QString& prevRef = refMap[clean.toLower()];
3136 if (prevRef.isEmpty()) {
3139 } else if (prevRef == ref) {
3142 clean += QLatin1Char('x');
3147 QString HtmlGenerator::protectEnc(const QString &string)
3149 return protect(string, outputEncoding);
3152 QString HtmlGenerator::protect(const QString &string, const QString &outputEncoding)
3155 if (html.isEmpty()) { \
3162 int n = string.length();
3164 for (int i = 0; i < n; ++i) {
3165 QChar ch = string.at(i);
3167 if (ch == QLatin1Char('&')) {
3169 } else if (ch == QLatin1Char('<')) {
3171 } else if (ch == QLatin1Char('>')) {
3173 } else if (ch == QLatin1Char('"')) {
3175 } else if ((outputEncoding == "ISO-8859-1" && ch.unicode() > 0x007F)
3176 || (ch == QLatin1Char('*') && i + 1 < n && string.at(i) == QLatin1Char('/'))
3177 || (ch == QLatin1Char('.') && i > 2 && string.at(i - 2) == QLatin1Char('.'))) {
3178 // we escape '*/' and the last dot in 'e.g.' and 'i.e.' for the Javadoc generator
3180 html += QString::number(ch.unicode(), 16);
3181 html += QLatin1Char(';');
3183 if (!html.isEmpty())
3188 if (!html.isEmpty())
3195 QString HtmlGenerator::fileBase(const Node *node) const
3199 result = Generator::fileBase(node);
3201 if (!node->isInnerNode()) {
3202 switch (node->status()) {
3204 result += "-compat";
3206 case Node::Obsolete:
3207 result += "-obsolete";
3216 QString HtmlGenerator::fileName(const Node *node)
3218 if (node->type() == Node::Document) {
3219 if (static_cast<const DocNode *>(node)->subType() == Node::ExternalPage)
3220 return node->name();
3221 if (static_cast<const DocNode *>(node)->subType() == Node::Image)
3222 return node->name();
3224 return Generator::fileName(node);
3227 QString HtmlGenerator::refForNode(const Node *node)
3229 const FunctionNode *func;
3230 const TypedefNode *typedeffe;
3233 switch (node->type()) {
3234 case Node::Namespace:
3239 ref = node->name() + "-enum";
3242 typedeffe = static_cast<const TypedefNode *>(node);
3243 if (typedeffe->associatedEnum()) {
3244 return refForNode(typedeffe->associatedEnum());
3247 ref = node->name() + "-typedef";
3250 case Node::Function:
3251 func = static_cast<const FunctionNode *>(node);
3252 if (func->associatedProperty()) {
3253 return refForNode(func->associatedProperty());
3257 if (func->overloadNumber() != 1)
3258 ref += QLatin1Char('-') + QString::number(func->overloadNumber());
3261 case Node::Document:
3262 if (node->subType() != Node::QmlPropertyGroup)
3264 case Node::QmlProperty:
3265 case Node::Property:
3266 ref = node->name() + "-prop";
3268 case Node::QmlSignal:
3269 ref = node->name() + "-signal";
3271 case Node::QmlSignalHandler:
3272 ref = node->name() + "-signal-handler";
3274 case Node::QmlMethod:
3275 func = static_cast<const FunctionNode *>(node);
3276 ref = func->name() + "-method";
3277 if (func->overloadNumber() != 1)
3278 ref += QLatin1Char('-') + QString::number(func->overloadNumber());
3280 case Node::Variable:
3281 ref = node->name() + "-var";
3284 return registerRef(ref);
3287 #define DEBUG_ABSTRACT 0
3290 Construct the link string for the \a node and return it.
3291 The \a relative node is use to decide the link we are
3292 generating is in the same file as the target. Note the
3293 relative node can be 0, which pretty much guarantees
3294 that the link and the target aren't in the same file.
3296 QString HtmlGenerator::linkForNode(const Node *node, const Node *relative)
3298 if (node == 0 || node == relative)
3300 if (!node->url().isEmpty())
3302 if (fileBase(node).isEmpty())
3304 if (node->access() == Node::Private)
3307 QString fn = fileName(node);
3308 if (node && relative && node->parent() != relative) {
3309 if (node->parent()->subType() == Node::QmlClass && relative->subType() == Node::QmlClass) {
3310 if (node->parent()->isAbstract()) {
3312 This is a bit of a hack. What we discover with
3313 the three 'if' statements immediately above,
3314 is that node's parent is marked \qmlabstract
3315 but the link appears in a qdoc comment for a
3316 subclass of the node's parent. This means the
3317 link should refer to the file for the relative
3318 node, not the file for node.
3320 fn = fileName(relative);
3326 if (!node->isInnerNode() || node->subType() == Node::QmlPropertyGroup) {
3327 QString ref = refForNode(node);
3328 if (relative && fn == fileName(relative) && ref == refForNode(relative))
3331 link += QLatin1Char('#');
3335 If the output is going to subdirectories, then if the
3336 two nodes will be output to different directories, then
3337 the link must go up to the parent directory and then
3338 back down into the other subdirectory.
3340 if (node && relative && (node != relative)) {
3341 if (node->outputSubdirectory() != relative->outputSubdirectory())
3342 link.prepend(QString("../" + node->outputSubdirectory() + QLatin1Char('/')));
3347 void HtmlGenerator::generateFullName(const Node *apparentNode, const Node *relative, const Node *actualNode)
3349 if (actualNode == 0)
3350 actualNode = apparentNode;
3351 out() << "<a href=\"" << linkForNode(actualNode, relative);
3352 if (true || relative == 0 || relative->status() != actualNode->status()) {
3353 switch (actualNode->status()) {
3354 case Node::Obsolete:
3355 out() << "\" class=\"obsolete";
3358 out() << "\" class=\"compat";
3365 out() << protectEnc(apparentNode->fullName(relative));
3369 void HtmlGenerator::generateDetailedMember(const Node *node,
3370 const InnerNode *relative,
3373 const EnumNode *enume;
3375 #ifdef GENERATE_MAC_REFS
3376 generateMacRef(node, marker);
3378 generateExtractionMark(node, MemberMark);
3379 if (node->type() == Node::Enum
3380 && (enume = static_cast<const EnumNode *>(node))->flagsType()) {
3381 #ifdef GENERATE_MAC_REFS
3382 generateMacRef(enume->flagsType(), marker);
3384 out() << "<h3 class=\"flags\">";
3385 out() << "<a name=\"" + refForNode(node) + "\"></a>";
3386 generateSynopsis(enume, relative, marker, CodeMarker::Detailed);
3388 generateSynopsis(enume->flagsType(),
3391 CodeMarker::Detailed);
3395 out() << "<h3 class=\"fn\">";
3396 out() << "<a name=\"" + refForNode(node) + "\"></a>";
3397 generateSynopsis(node, relative, marker, CodeMarker::Detailed);
3398 out() << "</h3>" << divNavTop << '\n';
3401 generateStatus(node, marker);
3402 generateBody(node, marker);
3403 generateThreadSafeness(node, marker);
3404 generateSince(node, marker);
3406 if (node->type() == Node::Property) {
3407 const PropertyNode *property = static_cast<const PropertyNode *>(node);
3410 section.members += property->getters();
3411 section.members += property->setters();
3412 section.members += property->resetters();
3414 if (!section.members.isEmpty()) {
3415 out() << "<p><b>Access functions:</b></p>\n";
3416 generateSectionList(section, node, marker, CodeMarker::Accessors);
3420 notifiers.members += property->notifiers();
3422 if (!notifiers.members.isEmpty()) {
3423 out() << "<p><b>Notifier signal:</b></p>\n";
3424 //out() << "<p>This signal is emitted when the property value is changed.</p>\n";
3425 generateSectionList(notifiers, node, marker, CodeMarker::Accessors);
3428 else if (node->type() == Node::Enum) {
3429 const EnumNode *enume = static_cast<const EnumNode *>(node);
3430 if (enume->flagsType()) {
3431 out() << "<p>The " << protectEnc(enume->flagsType()->name())
3432 << " type is a typedef for "
3433 << "<a href=\"qflags.html\">QFlags</a><"
3434 << protectEnc(enume->name())
3435 << ">. It stores an OR combination of "
3436 << protectEnc(enume->name())
3437 << " values.</p>\n";
3440 generateAlsoList(node, marker);
3441 generateExtractionMark(node, EndMark);
3444 int HtmlGenerator::hOffset(const Node *node)
3446 switch (node->type()) {
3447 case Node::Namespace:
3450 case Node::Document:
3454 case Node::Function:
3455 case Node::Property:
3461 bool HtmlGenerator::isThreeColumnEnumValueTable(const Atom *atom)
3463 while (atom != 0 && !(atom->type() == Atom::ListRight && atom->string() == ATOM_LIST_VALUE)) {
3464 if (atom->type() == Atom::ListItemLeft && !matchAhead(atom, Atom::ListItemRight))
3466 atom = atom->next();
3472 const QPair<QString,QString> HtmlGenerator::anchorForNode(const Node *node)
3474 QPair<QString,QString> anchorPair;
3476 anchorPair.first = Generator::fileName(node);
3477 if (node->type() == Node::Document) {
3478 const DocNode *docNode = static_cast<const DocNode*>(node);
3479 anchorPair.second = docNode->title();
3485 QString HtmlGenerator::getLink(const Atom *atom, const Node *relative, const Node** node)
3489 inObsoleteLink = false;
3491 if (atom->string().contains(QLatin1Char(':')) &&
3492 (atom->string().startsWith("file:")
3493 || atom->string().startsWith("http:")
3494 || atom->string().startsWith("https:")
3495 || atom->string().startsWith("ftp:")
3496 || atom->string().startsWith("mailto:"))) {
3498 link = atom->string();
3502 if (atom->string().contains('#')) {
3503 path = atom->string().split('#');
3506 path.append(atom->string());
3510 QString first = path.first().trimmed();
3511 if (first.isEmpty()) {
3514 else if (first.endsWith(".html")) {
3516 This is not a recursive search. That's ok in
3517 this case, because we are searching for a page
3518 node, which must be a direct child of the tree
3521 *node = qdb_->treeRoot()->findChildNodeByNameAndType(first, Node::Document);
3524 *node = qdb_->resolveTarget(first, relative);
3526 *node = qdb_->findDocNodeByTitle(first, relative);
3529 *node = qdb_->findUnambiguousTarget(first, ref, relative);
3533 if (!(*node)->url().isEmpty()) {
3534 return (*node)->url();
3545 if ((*node)->status() == Node::Obsolete) {
3547 if (relative->parent() != *node) {
3548 if (relative->status() != Node::Obsolete) {
3549 bool porting = false;
3550 if (relative->type() == Node::Document) {
3551 const DocNode* dn = static_cast<const DocNode*>(relative);
3552 if (dn->title().startsWith("Porting"))
3555 QString name = relative->plainFullName();
3556 if (!porting && !name.startsWith("Q3")) {
3557 if (obsoleteLinks) {
3558 relative->doc().location().warning(tr("Link to obsolete item '%1' in %2")
3559 .arg(atom->string())
3562 inObsoleteLink = true;
3568 qDebug() << "Link to Obsolete entity"
3569 << (*node)->name() << "no relative";
3575 This loop really only makes sense if *node is not 0.
3576 In that case, The node *node points to represents a
3577 qdoc page, so the link will ultimately point to some
3578 target on that page. This loop finds that target on
3579 the page that *node represents. ref is that target.
3581 while (!path.isEmpty()) {
3582 ref = qdb_->findTarget(path.first(), *node);
3589 Given that *node is not null, we now cconstruct a link
3590 to the page that *node represents, and then if there is
3591 a target on that page, we connect the target to the link
3594 if (path.isEmpty()) {
3595 link = linkForNode(*node, relative);
3596 if (*node && (*node)->subType() == Node::Image)
3597 link = "images/used-in-examples/" + link;
3598 if (!ref.isEmpty()) {
3599 link += QLatin1Char('#') + ref;
3606 void HtmlGenerator::generateStatus(const Node *node, CodeMarker *marker)
3610 switch (node->status()) {
3611 case Node::Obsolete:
3612 if (node->isInnerNode())
3613 Generator::generateStatus(node, marker);
3616 if (node->isInnerNode()) {
3617 text << Atom::ParaLeft
3618 << Atom(Atom::FormattingLeft,ATOM_FORMATTING_BOLD)
3621 << " is part of the Qt 3 support library."
3622 << Atom(Atom::FormattingRight, ATOM_FORMATTING_BOLD)
3623 << " It is provided to keep old source code working. "
3624 << "We strongly advise against "
3625 << "using it in new code. See ";
3627 const DocNode *docNode = qdb_->findDocNodeByTitle("Porting To Qt 4");
3629 if (docNode && node->type() == Node::Class) {
3630 QString oldName(node->name());
3631 oldName.remove(QLatin1Char('3'));
3632 ref = qdb_->findTarget(oldName, docNode);
3635 if (!ref.isEmpty()) {
3636 text << Atom(Atom::Link, linkForNode(docNode, node) + QLatin1Char('#') + ref);
3639 text << Atom(Atom::Link, "Porting to Qt 4");
3641 text << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK)
3642 << Atom(Atom::String, "Porting to Qt 4")
3643 << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK)
3644 << " for more information."
3647 generateText(text, node, marker);
3650 Generator::generateStatus(node, marker);
3654 #ifdef GENERATE_MAC_REFS
3658 void HtmlGenerator::generateMacRef(const Node *node, CodeMarker *marker)
3660 if (!pleaseGenerateMacRef || marker == 0)
3663 QStringList macRefs = marker->macRefsForNode(node);
3664 foreach (const QString &macRef, macRefs)
3665 out() << "<a name=\"" << "//apple_ref/" << macRef << "\"></a>\n";
3669 void HtmlGenerator::beginLink(const QString &link, const Node *node, const Node *relative)
3672 if (link_.isEmpty()) {
3673 if (showBrokenLinks)
3676 else if (node == 0 ||
3677 (relative != 0 && node->status() == relative->status())) {
3678 out() << "<a href=\"" << link_ << "\">";
3681 switch (node->status()) {
3682 case Node::Obsolete:
3683 out() << "<a href=\"" << link_ << "\" class=\"obsolete\">";
3686 out() << "<a href=\"" << link_ << "\" class=\"compat\">";
3689 out() << "<a href=\"" << link_ << "\">";
3695 void HtmlGenerator::endLink()
3698 if (link_.isEmpty()) {
3699 if (showBrokenLinks)
3703 if (inObsoleteLink) {
3704 out() << "<sup>(obsolete)</sup>";
3710 inObsoleteLink = false;
3714 Generates the summary for the \a section. Only used for
3715 sections of QML element documentation.
3717 void HtmlGenerator::generateQmlSummary(const Section& section,
3718 const Node *relative,
3721 if (!section.members.isEmpty()) {
3723 NodeList::ConstIterator m;
3724 m = section.members.constBegin();
3725 while (m != section.members.constEnd()) {
3726 out() << "<li class=\"fn\">";
3727 generateQmlItem(*m,relative,marker,true);
3736 Outputs the html detailed documentation for a section
3737 on a QML element reference page.
3739 void HtmlGenerator::generateDetailedQmlMember(Node *node,
3740 const InnerNode *relative,
3743 QmlPropertyNode* qpn = 0;
3744 #ifdef GENERATE_MAC_REFS
3745 generateMacRef(node, marker);
3747 generateExtractionMark(node, MemberMark);
3748 out() << "<div class=\"qmlitem\">";
3749 if (node->subType() == Node::QmlPropertyGroup) {
3750 const QmlPropGroupNode* qpgn = static_cast<const QmlPropGroupNode*>(node);
3751 NodeList::ConstIterator p = qpgn->childNodes().constBegin();
3752 out() << "<div class=\"qmlproto\">";
3753 out() << "<table class=\"qmlname\">";
3754 while (p != qpgn->childNodes().constEnd()) {
3755 if ((*p)->type() == Node::QmlProperty) {
3756 qpn = static_cast<QmlPropertyNode*>(*p);
3757 out() << "<tr valign=\"top\" class=\"odd\">";
3758 out() << "<td class=\"tblQmlPropNode\"><p>";
3759 out() << "<a name=\"" + refForNode(qpn) + "\"></a>";
3761 if (!qpn->isWritable(qdb_))
3762 out() << "<span class=\"qmlreadonly\">read-only</span>";
3763 if (qpn->isDefault())
3764 out() << "<span class=\"qmldefault\">default</span>";
3765 generateQmlItem(qpn, relative, marker, false);
3766 out() << "</p></td></tr>";
3770 out() << "</table>";
3773 else if (node->type() == Node::QmlProperty) {
3774 qpn = static_cast<QmlPropertyNode*>(node);
3776 If the QML property node has a single subproperty,
3777 override, replace qpn with that override node and
3780 if (qpn->qmlPropNodes().size() == 1) {
3781 Node* n = qpn->qmlPropNodes().at(0);
3782 if (n->type() == Node::QmlProperty)
3783 qpn = static_cast<QmlPropertyNode*>(n);
3786 Now qpn either has no overrides, or it has more
3787 than 1. If it has none, proceed to output as nortmal.
3789 if (qpn->qmlPropNodes().isEmpty()) {
3790 out() << "<div class=\"qmlproto\">";
3791 out() << "<table class=\"qmlname\">";
3792 out() << "<tr valign=\"top\" class=\"odd\">";
3793 out() << "<td class=\"tblQmlPropNode\"><p>";
3794 out() << "<a name=\"" + refForNode(qpn) + "\"></a>";
3795 if (!qpn->isReadOnlySet()) {
3796 if (qpn->declarativeCppNode())
3797 qpn->setReadOnly(!qpn->isWritable(qdb_));
3799 if (qpn->isReadOnly())
3800 out() << "<span class=\"qmlreadonly\">read-only</span>";
3801 if (qpn->isDefault())
3802 out() << "<span class=\"qmldefault\">default</span>";
3803 generateQmlItem(qpn, relative, marker, false);
3804 out() << "</p></td></tr>";
3805 out() << "</table>";
3810 The QML property node has multiple override nodes.
3811 Process the whole list as we would for a QML property
3814 NodeList::ConstIterator p = qpn->qmlPropNodes().constBegin();
3815 out() << "<div class=\"qmlproto\">";
3816 out() << "<table class=\"qmlname\">";
3817 while (p != qpn->qmlPropNodes().constEnd()) {
3818 if ((*p)->type() == Node::QmlProperty) {
3819 QmlPropertyNode* q = static_cast<QmlPropertyNode*>(*p);
3820 out() << "<tr valign=\"top\" class=\"odd\">";
3821 out() << "<td class=\"tblQmlPropNode\"><p>";
3822 out() << "<a name=\"" + refForNode(q) + "\"></a>";
3823 if (!qpn->isReadOnlySet())
3824 qpn->setReadOnly(!qpn->isWritable(qdb_));
3825 if (qpn->isReadOnly())
3826 out() << "<span class=\"qmlreadonly\">read-only</span>";
3827 if (qpn->isDefault())
3828 out() << "<span class=\"qmldefault\">default</span>";
3829 generateQmlItem(q, relative, marker, false);
3830 out() << "</p></td></tr>";
3834 out() << "</table>";
3838 else if (node->type() == Node::QmlSignal) {
3839 const FunctionNode* qsn = static_cast<const FunctionNode*>(node);
3840 out() << "<div class=\"qmlproto\">";
3841 out() << "<table class=\"qmlname\">";
3842 out() << "<tr valign=\"top\" class=\"odd\">";
3843 out() << "<td class=\"tblQmlFuncNode\"><p>";
3844 out() << "<a name=\"" + refForNode(qsn) + "\"></a>";
3845 generateSynopsis(qsn,relative,marker,CodeMarker::Detailed,false);
3846 out() << "</p></td></tr>";
3847 out() << "</table>";
3850 else if (node->type() == Node::QmlSignalHandler) {
3851 const FunctionNode* qshn = static_cast<const FunctionNode*>(node);
3852 out() << "<div class=\"qmlproto\">";
3853 out() << "<table class=\"qmlname\">";
3854 out() << "<tr valign=\"top\" class=\"odd\">";
3855 out() << "<td class=\"tblQmlFuncNode\"><p>";
3856 out() << "<a name=\"" + refForNode(qshn) + "\"></a>";
3857 generateSynopsis(qshn,relative,marker,CodeMarker::Detailed,false);
3858 out() << "</p></td></tr>";
3859 out() << "</table>";
3862 else if (node->type() == Node::QmlMethod) {
3863 const FunctionNode* qmn = static_cast<const FunctionNode*>(node);
3864 out() << "<div class=\"qmlproto\">";
3865 out() << "<table class=\"qmlname\">";
3866 out() << "<tr valign=\"top\" class=\"odd\">";
3867 out() << "<td class=\"tblQmlFuncNode\"><p>";
3868 out() << "<a name=\"" + refForNode(qmn) + "\"></a>";
3869 generateSynopsis(qmn,relative,marker,CodeMarker::Detailed,false);
3870 out() << "</p></td></tr>";
3871 out() << "</table>";
3874 out() << "<div class=\"qmldoc\">";
3875 generateStatus(node, marker);
3876 generateBody(node, marker);
3877 generateThreadSafeness(node, marker);
3878 generateSince(node, marker);
3879 generateAlsoList(node, marker);
3882 generateExtractionMark(node, EndMark);
3886 Output the "Inherits" line for the QML element,
3887 if there should be one.
3889 void HtmlGenerator::generateQmlInherits(const QmlClassNode* qcn, CodeMarker* marker)
3893 const DocNode* base = qcn->qmlBase();
3896 text << Atom::ParaLeft << "Inherits ";
3897 text << Atom(Atom::LinkNode,CodeMarker::stringForNode(base));
3898 text << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK);
3899 text << Atom(Atom::String, base->name());
3900 text << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK);
3901 text << Atom::ParaRight;
3902 generateText(text, qcn, marker);
3907 Output the "[Xxx instantiates the C++ class QmlGraphicsXxx]"
3908 line for the QML element, if there should be one.
3910 If there is no class node, or if the class node status
3911 is set to Node::Internal, do nothing.
3913 void HtmlGenerator::generateQmlInstantiates(QmlClassNode* qcn, CodeMarker* marker)
3915 ClassNode* cn = qcn->classNode();
3916 if (cn && (cn->status() != Node::Internal)) {
3918 text << Atom::ParaLeft;
3919 text << Atom(Atom::LinkNode,CodeMarker::stringForNode(qcn));
3920 text << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK);
3921 QString name = qcn->name();
3923 Remove the "QML:" prefix, if present.
3924 It shouldn't be present anymore.
3926 if (name.startsWith(QLatin1String("QML:")))
3927 name = name.mid(4); // remove the "QML:" prefix
3928 text << Atom(Atom::String, name);
3929 text << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK);
3930 text << " instantiates the C++ class ";
3931 text << Atom(Atom::LinkNode,CodeMarker::stringForNode(cn));
3932 text << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK);
3933 text << Atom(Atom::String, cn->name());
3934 text << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK);
3935 text << Atom::ParaRight;
3936 generateText(text, qcn, marker);
3941 Output the "[QmlGraphicsXxx is instantiated by QML Type Xxx]"
3942 line for the class, if there should be one.
3944 If there is no QML element, or if the class node status
3945 is set to Node::Internal, do nothing.
3947 void HtmlGenerator::generateInstantiatedBy(ClassNode* cn, CodeMarker* marker)
3949 if (cn && cn->status() != Node::Internal && cn->qmlElement() != 0) {
3950 const QmlClassNode* qcn = cn->qmlElement();
3952 text << Atom::ParaLeft;
3953 text << Atom(Atom::LinkNode,CodeMarker::stringForNode(cn));
3954 text << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK);
3955 text << Atom(Atom::String, cn->name());
3956 text << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK);
3957 text << " is instantiated by QML Type ";
3958 text << Atom(Atom::LinkNode,CodeMarker::stringForNode(qcn));
3959 text << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK);
3960 text << Atom(Atom::String, qcn->name());
3961 text << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK);
3962 text << Atom::ParaRight;
3963 generateText(text, cn, marker);
3967 void HtmlGenerator::generateExtractionMark(const Node *node, ExtractionMarkType markType)
3969 if (markType != EndMark) {
3970 out() << "<!-- $$$" + node->name();
3971 if (markType == MemberMark) {
3972 if (node->type() == Node::Function) {
3973 const FunctionNode *func = static_cast<const FunctionNode *>(node);
3974 if (!func->associatedProperty()) {
3975 if (func->overloadNumber() == 1)
3976 out() << "[overload1]";
3977 out() << "$$$" + func->name() + func->rawParameters().remove(' ');
3979 } else if (node->type() == Node::Property) {
3981 const PropertyNode *prop = static_cast<const PropertyNode *>(node);
3982 const NodeList &list = prop->functions();
3983 foreach (const Node *propFuncNode, list) {
3984 if (propFuncNode->type() == Node::Function) {
3985 const FunctionNode *func = static_cast<const FunctionNode *>(propFuncNode);
3986 out() << "$$$" + func->name() + func->rawParameters().remove(' ');
3989 } else if (node->type() == Node::Enum) {
3990 const EnumNode *enumNode = static_cast<const EnumNode *>(node);
3991 foreach (const EnumItem &item, enumNode->items())
3992 out() << "$$$" + item.name();
3994 } else if (markType == BriefMark) {
3996 } else if (markType == DetailedDescriptionMark) {
3997 out() << "-description";
4001 out() << "<!-- @@@" + node->name() + " -->\n";
4007 This function outputs one or more manifest files in XML.
4008 They are used by Creator.
4010 void HtmlGenerator::generateManifestFiles()
4012 generateManifestFile("examples", "example");
4013 generateManifestFile("demos", "demo");
4014 ExampleNode::exampleNodeMap.clear();
4018 This function is called by generaqteManiferstFile(), once
4019 for each manifest file to be generated. \a manifest is the
4020 type of manifest file.
4022 void HtmlGenerator::generateManifestFile(QString manifest, QString element)
4024 if (ExampleNode::exampleNodeMap.isEmpty())
4026 QString fileName = manifest +"-manifest.xml";
4027 QFile file(outputDir() + QLatin1Char('/') + fileName);
4028 if (!file.open(QFile::WriteOnly | QFile::Text))
4031 if (manifest == "demos")
4034 bool proceed = false;
4035 ExampleNodeMap::Iterator i = ExampleNode::exampleNodeMap.begin();
4036 while (i != ExampleNode::exampleNodeMap.end()) {
4037 const ExampleNode* en = i.value();
4039 if (en->name().startsWith("demos")) {
4044 else if (!en->name().startsWith("demos")) {
4053 QXmlStreamWriter writer(&file);
4054 writer.setAutoFormatting(true);
4055 writer.writeStartDocument();
4056 writer.writeStartElement("instructionals");
4057 writer.writeAttribute("module", project);
4058 writer.writeStartElement(manifest);
4060 i = ExampleNode::exampleNodeMap.begin();
4061 while (i != ExampleNode::exampleNodeMap.end()) {
4062 const ExampleNode* en = i.value();
4064 if (!en->name().startsWith("demos")) {
4069 else if (en->name().startsWith("demos")) {
4073 writer.writeStartElement(element);
4074 writer.writeAttribute("name", en->title());
4075 QString docUrl = manifestDir + fileBase(en) + ".html";
4076 writer.writeAttribute("docUrl", docUrl);
4077 QStringList proFiles;
4078 foreach (const Node* child, en->childNodes()) {
4079 if (child->subType() == Node::File) {
4080 QString file = child->name();
4081 if (file.endsWith(".pro") || file.endsWith(".qmlproject")) {
4082 if (file.startsWith("demos/"))
4088 if (!proFiles.isEmpty()) {
4089 if (proFiles.size() == 1) {
4090 writer.writeAttribute("projectPath", proFiles[0]);
4093 QString exampleName = en->name().split('/').last();
4094 bool proWithExampleNameFound = false;
4095 for (int j = 0; j < proFiles.size(); j++)
4097 if (proFiles[j].endsWith(QStringLiteral("%1/%1.pro").arg(exampleName))
4098 || proFiles[j].endsWith(QStringLiteral("%1/%1.qmlproject").arg(exampleName))) {
4099 writer.writeAttribute("projectPath", proFiles[j]);
4100 proWithExampleNameFound = true;
4104 if (!proWithExampleNameFound)
4105 writer.writeAttribute("projectPath", proFiles[0]);
4108 if (!en->imageFileName().isEmpty())
4109 writer.writeAttribute("imageUrl", manifestDir + en->imageFileName());
4110 writer.writeStartElement("description");
4111 Text brief = en->doc().briefText();
4112 if (!brief.isEmpty())
4113 writer.writeCDATA(brief.toString());
4115 writer.writeCDATA(QString("No description available"));
4116 writer.writeEndElement(); // description
4117 QStringList tags = en->title().toLower().split(QLatin1Char(' '));
4118 if (!tags.isEmpty()) {
4119 writer.writeStartElement("tags");
4120 bool wrote_one = false;
4121 for (int n=0; n<tags.size(); ++n) {
4122 QString tag = tags.at(n);
4123 if (tag.at(0).isDigit())
4125 if (tag.at(0) == '-')
4127 if (tag.startsWith("example"))
4129 if (tag.startsWith("chapter"))
4131 if (tag.endsWith(QLatin1Char(':')))
4133 if (n>0 && wrote_one)
4134 writer.writeCharacters(",");
4135 writer.writeCharacters(tag);
4138 writer.writeEndElement(); // tags
4141 QString ename = en->name().mid(en->name().lastIndexOf('/')+1);
4142 QSet<QString> usedNames;
4143 foreach (const Node* child, en->childNodes()) {
4144 if (child->subType() == Node::File) {
4145 QString file = child->name();
4146 QString fileName = file.mid(file.lastIndexOf('/')+1);
4147 QString baseName = fileName;
4148 if ((fileName.count(QChar('.')) > 0) &&
4149 (fileName.endsWith(".cpp") ||
4150 fileName.endsWith(".h") ||
4151 fileName.endsWith(".qml")))
4152 baseName.truncate(baseName.lastIndexOf(QChar('.')));
4153 if (baseName.compare(ename, Qt::CaseInsensitive) == 0) {
4154 if (!usedNames.contains(fileName)) {
4155 writer.writeStartElement("fileToOpen");
4156 if (file.startsWith("demos/"))
4158 writer.writeCharacters(file);
4159 writer.writeEndElement(); // fileToOpen
4160 usedNames.insert(fileName);
4163 else if (fileName.toLower().endsWith("main.cpp") ||
4164 fileName.toLower().endsWith("main.qml")) {
4165 if (!usedNames.contains(fileName)) {
4166 writer.writeStartElement("fileToOpen");
4167 if (file.startsWith("demos/"))
4169 writer.writeCharacters(file);
4170 writer.writeEndElement(); // fileToOpen
4171 usedNames.insert(fileName);
4176 writer.writeEndElement(); // example
4180 writer.writeEndElement(); // examples
4181 writer.writeEndElement(); // instructionals
4182 writer.writeEndDocument();
4187 Find global entities that have documentation but no
4188 \e{relates} comand. Report these as errors if they
4189 are not also marked \e {internal}.
4195 subtype: External page
4197 subtype: Header file
4200 subtype: QML basic type
4204 void HtmlGenerator::reportOrphans(const InnerNode* parent)
4206 const NodeList& children = parent->childNodes();
4207 if (children.size() == 0)
4212 for (int i=0; i<children.size(); ++i) {
4213 Node* child = children[i];
4214 if (!child || child->isInternal() || child->doc().isEmpty())
4216 if (child->relates()) {
4218 message = child->relates()->name();
4222 message = "has documentation but no \\relates command";
4224 switch (child->type()) {
4225 case Node::Namespace:
4229 case Node::Document:
4230 switch (child->subType()) {
4233 case Node::HeaderFile:
4245 case Node::ExternalPage:
4247 case Node::QmlClass:
4249 case Node::QmlPropertyGroup:
4251 case Node::QmlBasicType:
4253 case Node::QmlModule:
4255 case Node::Collision:
4263 child->location().warning(tr("Global enum, %1, %2").arg(child->name()).arg(message));
4267 child->location().warning(tr("Global typedef, %1, %2").arg(child->name()).arg(message));
4269 case Node::Function:
4271 const FunctionNode* fn = static_cast<const FunctionNode*>(child);
4273 child->location().warning(tr("Global macro, %1, %2").arg(child->name()).arg(message));
4275 child->location().warning(tr("Global function, %1(), %2").arg(child->name()).arg(message));
4278 case Node::Property:
4280 case Node::Variable:
4282 child->location().warning(tr("Global variable, %1, %2").arg(child->name()).arg(message));
4284 case Node::QmlProperty:
4286 child->location().warning(tr("Global QML property, %1, %2").arg(child->name()).arg(message));
4288 case Node::QmlSignal:
4290 child->location().warning(tr("Global QML, signal, %1 %2").arg(child->name()).arg(message));
4292 case Node::QmlSignalHandler:
4294 child->location().warning(tr("Global QML signal handler, %1, %2").arg(child->name()).arg(message));
4296 case Node::QmlMethod:
4298 child->location().warning(tr("Global QML method, %1, %2").arg(child->name()).arg(message));
4307 Returns a reference to the XML stream writer currently in use.
4308 There is one XML stream writer open for each XML file being
4309 written, and they are kept on a stack. The one on top of the
4310 stack is the one being written to at the moment. In the HTML
4311 output generator, it is perhaps impossible for there to ever
4312 be more than one writer open.
4314 QXmlStreamWriter& HtmlGenerator::xmlWriter()
4316 return *xmlWriterStack.top();
4320 This function is only called for writing ditamaps.
4322 Calls beginSubPage() in the base class to open the file.
4323 Then creates a new XML stream writer using the IO device
4324 from opened file and pushes the XML writer onto a stackj.
4325 Creates the file named \a fileName in the output directory.
4326 Attaches a QTextStream to the created file, which is written
4327 to all over the place using out(). Finally, it sets some
4328 parameters in the XML writer and calls writeStartDocument().
4330 It also ensures that a GUID map is created for the output file.
4332 void HtmlGenerator::beginDitamapPage(const InnerNode* node, const QString& fileName)
4334 Generator::beginSubPage(node,fileName);
4335 QXmlStreamWriter* writer = new QXmlStreamWriter(out().device());
4336 xmlWriterStack.push(writer);
4337 writer->setAutoFormatting(true);
4338 writer->setAutoFormattingIndent(4);
4339 writer->writeStartDocument();
4343 This function is only called for writing ditamaps.
4345 Calls writeEndDocument() and then pops the XML stream writer
4346 off the stack and deletes it. Then it calls endSubPage() in
4347 the base class to close the device.
4349 void HtmlGenerator::endDitamapPage()
4351 xmlWriter().writeEndDocument();
4352 delete xmlWriterStack.pop();
4353 Generator::endSubPage();
4357 This function is only called for writing ditamaps.
4359 Creates the DITA map from the topicrefs in \a node,
4360 which is a DitaMapNode.
4362 void HtmlGenerator::writeDitaMap(const DitaMapNode* node)
4364 beginDitamapPage(node,node->name());
4366 QString doctype = "<!DOCTYPE map PUBLIC \"-//OASIS//DTD DITA Map//EN\" \"map.dtd\">";
4368 xmlWriter().writeDTD(doctype);
4369 xmlWriter().writeStartElement("map");
4370 xmlWriter().writeStartElement("topicmeta");
4371 xmlWriter().writeStartElement("shortdesc");
4372 xmlWriter().writeCharacters(node->title());
4373 xmlWriter().writeEndElement(); // </shortdesc>
4374 xmlWriter().writeEndElement(); // </topicmeta>
4375 DitaRefList map = node->map();
4381 Write the \a ditarefs to the current output file.
4383 void HtmlGenerator::writeDitaRefs(const DitaRefList& ditarefs)
4385 foreach (DitaRef* t, ditarefs) {
4387 xmlWriter().writeStartElement("mapref");
4389 xmlWriter().writeStartElement("topicref");
4390 xmlWriter().writeAttribute("navtitle",t->navtitle());
4391 if (t->href().isEmpty()) {
4392 const DocNode* fn = qdb_->findDocNodeByTitle(t->navtitle());
4394 xmlWriter().writeAttribute("href",fileName(fn));
4397 xmlWriter().writeAttribute("href",t->href());
4398 if (t->subrefs() && !t->subrefs()->isEmpty())
4399 writeDitaRefs(*(t->subrefs()));
4400 xmlWriter().writeEndElement(); // </topicref> or </mapref>