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 ** GNU Lesser General Public License Usage
10 ** This file may be used under the terms of the GNU Lesser General Public
11 ** License version 2.1 as published by the Free Software Foundation and
12 ** appearing in the file LICENSE.LGPL included in the packaging of this
13 ** file. Please review the following information to ensure the GNU Lesser
14 ** General Public License version 2.1 requirements will be met:
15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
17 ** In addition, as a special exception, Nokia gives you certain additional
18 ** rights. These rights are described in the Nokia Qt LGPL Exception
19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
21 ** GNU General Public License Usage
22 ** Alternatively, this file may be used under the terms of the GNU General
23 ** Public License version 3.0 as published by the Free Software Foundation
24 ** and appearing in the file LICENSE.GPL included in the packaging of this
25 ** file. Please review the following information to ensure the GNU General
26 ** Public License version 3.0 requirements will be met:
27 ** http://www.gnu.org/copyleft/gpl.html.
30 ** Alternatively, this file may be used in accordance with the terms and
31 ** conditions contained in a signed written agreement between you and Nokia.
40 ****************************************************************************/
46 #include "codemarker.h"
47 #include "codeparser.h"
48 #include "helpprojectwriter.h"
49 #include "htmlgenerator.h"
51 #include "separator.h"
56 #include <qiterator.h>
57 #include <qtextcodec.h>
62 #define COMMAND_VERSION Doc::alias("version")
63 int HtmlGenerator::id = 0;
64 bool HtmlGenerator::debugging_on = false;
66 QString HtmlGenerator::divNavTop;
68 static bool showBrokenLinks = false;
70 static QRegExp linkTag("(<@link node=\"([^\"]+)\">).*(</@link>)");
71 static QRegExp funcTag("(<@func target=\"([^\"]*)\">)(.*)(</@func>)");
72 static QRegExp typeTag("(<@(type|headerfile|func)(?: +[^>]*)?>)(.*)(</@\\2>)");
73 static QRegExp spanTag("</@(?:comment|preprocessor|string|char|number|op|type|name|keyword)>");
74 static QRegExp unknownTag("</?@[^>]*>");
76 static void addLink(const QString &linkTarget,
77 const QStringRef &nestedStuff,
80 if (!linkTarget.isEmpty()) {
93 HtmlGenerator::HtmlGenerator()
94 : helpProjectWriter(0),
96 inObsoleteLink(false),
98 inSectionHeading(false),
101 threeColumnEnumValueTable(true),
102 funcLeftParen("\\S(\\()"),
108 The destructor deletes the instance of HelpProjectWriter.
110 HtmlGenerator::~HtmlGenerator()
112 if (helpProjectWriter)
113 delete helpProjectWriter;
116 void HtmlGenerator::initializeGenerator(const Config &config)
118 static const struct {
123 { ATOM_FORMATTING_BOLD, "<b>", "</b>" },
124 { ATOM_FORMATTING_INDEX, "<!--", "-->" },
125 { ATOM_FORMATTING_ITALIC, "<i>", "</i>" },
126 { ATOM_FORMATTING_PARAMETER, "<i>", "</i>" },
127 { ATOM_FORMATTING_SUBSCRIPT, "<sub>", "</sub>" },
128 { ATOM_FORMATTING_SUPERSCRIPT, "<sup>", "</sup>" },
129 { ATOM_FORMATTING_TELETYPE, "<tt>", "</tt>" },
130 { ATOM_FORMATTING_UICONTROL, "<b>", "</b>" },
131 { ATOM_FORMATTING_UNDERLINE, "<u>", "</u>" },
135 Generator::initializeGenerator(config);
136 obsoleteLinks = config.getBool(QLatin1String(CONFIG_OBSOLETELINKS));
137 setImageFileExtensions(QStringList() << "png" << "jpg" << "jpeg" << "gif");
139 while (defaults[i].key) {
140 formattingLeftMap().insert(defaults[i].key, defaults[i].left);
141 formattingRightMap().insert(defaults[i].key, defaults[i].right);
145 style = config.getString(HtmlGenerator::format() +
148 endHeader = config.getString(HtmlGenerator::format() +
151 postHeader = config.getString(HtmlGenerator::format() +
153 HTMLGENERATOR_POSTHEADER);
154 postPostHeader = config.getString(HtmlGenerator::format() +
156 HTMLGENERATOR_POSTPOSTHEADER);
157 footer = config.getString(HtmlGenerator::format() +
159 HTMLGENERATOR_FOOTER);
160 address = config.getString(HtmlGenerator::format() +
162 HTMLGENERATOR_ADDRESS);
163 pleaseGenerateMacRef = config.getBool(HtmlGenerator::format() +
165 HTMLGENERATOR_GENERATEMACREFS);
166 noBreadCrumbs = config.getBool(HtmlGenerator::format() +
168 HTMLGENERATOR_NOBREADCRUMBS);
170 project = config.getString(CONFIG_PROJECT);
172 projectDescription = config.getString(CONFIG_DESCRIPTION);
173 if (projectDescription.isEmpty() && !project.isEmpty())
174 projectDescription = project + " Reference Documentation";
176 projectUrl = config.getString(CONFIG_URL);
178 outputEncoding = config.getString(CONFIG_OUTPUTENCODING);
179 if (outputEncoding.isEmpty())
180 outputEncoding = QLatin1String("ISO-8859-1");
181 outputCodec = QTextCodec::codecForName(outputEncoding.toLocal8Bit());
183 naturalLanguage = config.getString(CONFIG_NATURALLANGUAGE);
184 if (naturalLanguage.isEmpty())
185 naturalLanguage = QLatin1String("en");
187 QSet<QString> editionNames = config.subVars(CONFIG_EDITION);
188 QSet<QString>::ConstIterator edition = editionNames.constBegin();
189 while (edition != editionNames.constEnd()) {
190 QString editionName = *edition;
191 QStringList editionModules = config.getStringList(CONFIG_EDITION +
196 QStringList editionGroups = config.getStringList(CONFIG_EDITION +
202 if (!editionModules.isEmpty())
203 editionModuleMap[editionName] = editionModules;
204 if (!editionGroups.isEmpty())
205 editionGroupMap[editionName] = editionGroups;
210 codeIndent = config.getInt(CONFIG_CODEINDENT);
212 helpProjectWriter = new HelpProjectWriter(config,
216 // Documentation template handling
217 headerScripts = config.getString(HtmlGenerator::format() + Config::dot +
218 CONFIG_HEADERSCRIPTS);
219 headerStyles = config.getString(HtmlGenerator::format() +
221 CONFIG_HEADERSTYLES);
223 QString prefix = CONFIG_QHP + Config::dot + "Qt" + Config::dot;
224 manifestDir = "qthelp://" + config.getString(prefix + "namespace");
225 manifestDir += QLatin1Char('/') + config.getString(prefix + "virtualFolder") + QLatin1Char('/');
228 If the output files will be in subdirectores in the output
229 directory, change the references to files in the style and
230 scripts subdirectories that appear in the headerscipts and
231 headerstyles string so that they link to the correct files,
232 whic means prepending "../" to each
234 if (!baseDir().isEmpty()) {
235 headerScripts = headerScripts.replace("style/","../style/");
236 headerScripts = headerScripts.replace("scripts/","../scripts/");
237 headerStyles = headerStyles.replace("style/","../style/");
238 headerStyles = headerStyles.replace("scripts/","../scripts/");
242 void HtmlGenerator::terminateGenerator()
244 Generator::terminateGenerator();
247 QString HtmlGenerator::format()
253 This is where the HTML files are written.
254 \note The HTML file generation is done in the base class,
255 PageGenerator::generateTree().
257 void HtmlGenerator::generateTree(Tree *tree)
260 nonCompatClasses.clear();
262 compatClasses.clear();
263 obsoleteClasses.clear();
264 moduleClassMap.clear();
265 moduleNamespaceMap.clear();
267 legaleseTexts.clear();
268 serviceClasses.clear();
271 findAllClasses(tree->root());
272 findAllFunctions(tree->root());
273 findAllLegaleseTexts(tree->root());
274 findAllNamespaces(tree->root());
275 findAllSince(tree->root());
277 Generator::generateTree(tree);
278 //reportOrphans(tree->root());
279 generateCollisionPages();
281 QString fileBase = project.toLower().simplified().replace(QLatin1Char(' '), QLatin1Char('-'));
282 generateIndex(fileBase, projectUrl, projectDescription);
284 helpProjectWriter->generate(tree_);
285 generateManifestFiles();
288 void HtmlGenerator::startText(const Node * /* relative */,
289 CodeMarker * /* marker */)
293 inSectionHeading = false;
294 inTableHeader = false;
296 threeColumnEnumValueTable = true;
298 sectionNumber.clear();
302 Generate html from an instance of Atom.
304 int HtmlGenerator::generateAtom(const Atom *atom,
305 const Node *relative,
309 static bool in_para = false;
311 switch (atom->type()) {
312 case Atom::AbstractLeft:
314 relative->doc().location().warning(tr("\abstract is not implemented."));
316 Location::information(tr("\abstract is not implemented."));
318 case Atom::AbstractRight:
321 if (!inLink && !inContents && !inSectionHeading) {
322 const Node *node = 0;
323 QString link = getLink(atom, relative, marker, &node);
324 if (!link.isEmpty()) {
325 beginLink(link, node, relative, marker);
326 generateLink(atom, relative, marker);
330 out() << protectEnc(atom->string());
334 out() << protectEnc(atom->string());
339 case Atom::BriefLeft:
340 if (relative->type() == Node::Fake) {
341 if (relative->subType() != Node::Example) {
342 skipAhead = skipAtoms(atom, Atom::BriefRight);
348 if (relative->type() == Node::Property ||
349 relative->type() == Node::Variable) {
352 while (atom != 0 && atom->type() != Atom::BriefRight) {
353 if (atom->type() == Atom::String ||
354 atom->type() == Atom::AutoLink)
355 str += atom->string();
359 str[0] = str[0].toLower();
360 if (str.endsWith(QLatin1Char('.')))
361 str.truncate(str.length() - 1);
363 if (relative->type() == Node::Property)
367 QStringList words = str.split(QLatin1Char(' '));
368 if (!(words.first() == "contains" || words.first() == "specifies"
369 || words.first() == "describes" || words.first() == "defines"
370 || words.first() == "holds" || words.first() == "determines"))
377 case Atom::BriefRight:
378 if (relative->type() != Node::Fake)
382 // This may at one time have been used to mark up C++ code but it is
383 // now widely used to write teletype text. As a result, text marked
384 // with the \c command is not passed to a code marker.
385 out() << formattingLeftMap()[ATOM_FORMATTING_TELETYPE];
387 out() << protectEnc(plainCode(atom->string()));
390 out() << protectEnc(plainCode(atom->string()));
392 out() << formattingRightMap()[ATOM_FORMATTING_TELETYPE];
394 case Atom::CaptionLeft:
395 out() << "<p class=\"figCaption\">";
398 case Atom::CaptionRight:
406 out() << "<pre class=\"cpp\">"
407 << trimmedTrailing(highlightedCode(indent(codeIndent,atom->string()),
412 out() << "<pre class=\"qml\">"
413 << trimmedTrailing(highlightedCode(indent(codeIndent,atom->string()),
417 case Atom::JavaScript:
418 out() << "<pre class=\"js\">"
419 << trimmedTrailing(highlightedCode(indent(codeIndent,atom->string()),
424 out() << "<p>you can rewrite it as</p>\n"
425 << "<pre class=\"cpp\">"
426 << trimmedTrailing(highlightedCode(indent(codeIndent,atom->string()),
431 out() << "<p>For example, if you have code like</p>\n";
434 out() << "<pre class=\"cpp\">"
435 << trimmedTrailing(protectEnc(plainCode(indent(codeIndent,atom->string()))))
440 if (!atom->string().isEmpty())
441 out() << ' ' << atom->string();
447 case Atom::FootnoteLeft:
455 case Atom::FootnoteRight:
459 case Atom::FormatElse:
460 case Atom::FormatEndif:
463 case Atom::FormattingLeft:
464 if (atom->string().startsWith("span ")) {
465 out() << '<' + atom->string() << '>';
468 out() << formattingLeftMap()[atom->string()];
469 if (atom->string() == ATOM_FORMATTING_PARAMETER) {
470 if (atom->next() != 0 && atom->next()->type() == Atom::String) {
471 QRegExp subscriptRegExp("([a-z]+)_([0-9n])");
472 if (subscriptRegExp.exactMatch(atom->next()->string())) {
473 out() << subscriptRegExp.cap(1) << "<sub>"
474 << subscriptRegExp.cap(2) << "</sub>";
480 case Atom::FormattingRight:
481 if (atom->string() == ATOM_FORMATTING_LINK) {
484 else if (atom->string().startsWith("span ")) {
488 out() << formattingRightMap()[atom->string()];
491 case Atom::AnnotatedList:
493 QList<Node*> values = tree_->groups().values(atom->string());
495 for (int i = 0; i < values.size(); ++i) {
496 const Node* n = values.at(i);
497 if ((n->status() != Node::Internal) && (n->access() != Node::Private)) {
498 nodeMap.insert(n->nameForLists(),n);
501 generateAnnotatedList(relative, marker, nodeMap);
504 case Atom::GeneratedList:
505 if (atom->string() == "annotatedclasses") {
506 generateAnnotatedList(relative, marker, nonCompatClasses);
508 else if (atom->string() == "classes") {
509 generateCompactList(relative, marker, nonCompatClasses, true);
511 else if (atom->string() == "qmlclasses") {
512 generateCompactList(relative, marker, qmlClasses, true);
514 else if (atom->string().contains("classesbymodule")) {
515 QString arg = atom->string().trimmed();
516 QString moduleName = atom->string().mid(atom->string().indexOf(
517 "classesbymodule") + 15).trimmed();
518 if (moduleClassMap.contains(moduleName))
519 generateAnnotatedList(relative, marker, moduleClassMap[moduleName]);
521 else if (atom->string().contains("classesbyedition")) {
523 QString arg = atom->string().trimmed();
524 QString editionName = atom->string().mid(atom->string().indexOf(
525 "classesbyedition") + 16).trimmed();
527 if (editionModuleMap.contains(editionName)) {
529 // Add all classes in the modules listed for that edition.
530 NodeMap editionClasses;
531 foreach (const QString &moduleName, editionModuleMap[editionName]) {
532 if (moduleClassMap.contains(moduleName))
533 editionClasses.unite(moduleClassMap[moduleName]);
536 // Add additional groups and remove groups of classes that
537 // should be excluded from the edition.
539 QMultiMap <QString, Node *> groups = tree_->groups();
540 foreach (const QString &groupName, editionGroupMap[editionName]) {
541 QList<Node *> groupClasses;
542 if (groupName.startsWith(QLatin1Char('-'))) {
543 groupClasses = groups.values(groupName.mid(1));
544 foreach (const Node *node, groupClasses)
545 editionClasses.remove(node->name());
548 groupClasses = groups.values(groupName);
549 foreach (const Node *node, groupClasses)
550 editionClasses.insert(node->name(), node);
553 generateAnnotatedList(relative, marker, editionClasses);
556 else if (atom->string() == "classhierarchy") {
557 generateClassHierarchy(relative, marker, nonCompatClasses);
559 else if (atom->string() == "compatclasses") {
560 generateCompactList(relative, marker, compatClasses, false);
562 else if (atom->string() == "obsoleteclasses") {
563 generateCompactList(relative, marker, obsoleteClasses, false);
565 else if (atom->string() == "functionindex") {
566 generateFunctionIndex(relative, marker);
568 else if (atom->string() == "legalese") {
569 generateLegaleseList(relative, marker);
571 else if (atom->string() == "mainclasses") {
572 generateCompactList(relative, marker, mainClasses, true);
574 else if (atom->string() == "services") {
575 generateCompactList(relative, marker, serviceClasses, false);
577 else if (atom->string() == "overviews") {
578 generateOverviewList(relative, marker);
580 else if (atom->string() == "namespaces") {
581 generateAnnotatedList(relative, marker, namespaceIndex);
583 else if (atom->string() == "related") {
584 const FakeNode *fake = static_cast<const FakeNode *>(relative);
585 if (fake && !fake->groupMembers().isEmpty()) {
586 NodeMap groupMembersMap;
587 foreach (const Node *node, fake->groupMembers()) {
588 if (node->type() == Node::Fake)
589 groupMembersMap[fullName(node, relative, marker)] = node;
591 generateAnnotatedList(fake, marker, groupMembersMap);
594 else if (atom->string() == "relatedinline") {
595 const FakeNode *fake = static_cast<const FakeNode *>(relative);
596 if (fake && !fake->groupMembers().isEmpty()) {
597 // Reverse the list into the original scan order.
598 // Should be sorted. But on what? It may not be a
599 // regular class or page definition.
600 QList<const Node *> list;
601 foreach (const Node *node, fake->groupMembers())
603 foreach (const Node *node, list)
604 generateBody(node, marker);
608 case Atom::SinceList:
610 NewSinceMaps::const_iterator nsmap;
611 nsmap = newSinceMaps.constFind(atom->string());
612 NewClassMaps::const_iterator ncmap;
613 ncmap = newClassMaps.constFind(atom->string());
614 NewClassMaps::const_iterator nqcmap;
615 nqcmap = newQmlClassMaps.constFind(atom->string());
617 if ((nsmap != newSinceMaps.constEnd()) && !nsmap.value().isEmpty()) {
618 QList<Section> sections;
619 QList<Section>::ConstIterator s;
621 for (int i=0; i<LastSinceType; ++i)
622 sections.append(Section(sinceTitle(i),QString(),QString(),QString()));
624 NodeMultiMap::const_iterator n = nsmap.value().constBegin();
626 while (n != nsmap.value().constEnd()) {
628 const Node* node = n.value();
629 switch (node->type()) {
631 if (node->subType() == Node::QmlClass) {
632 sections[QmlClass].appendMember((Node*)node);
635 case Node::Namespace:
636 sections[Namespace].appendMember((Node*)node);
639 sections[Class].appendMember((Node*)node);
642 sections[Enum].appendMember((Node*)node);
645 sections[Typedef].appendMember((Node*)node);
647 case Node::Function: {
648 const FunctionNode* fn = static_cast<const FunctionNode*>(node);
650 sections[Macro].appendMember((Node*)node);
652 Node* p = fn->parent();
654 if (p->type() == Node::Class)
655 sections[MemberFunction].appendMember((Node*)node);
656 else if (p->type() == Node::Namespace) {
657 if (p->name().isEmpty())
658 sections[GlobalFunction].appendMember((Node*)node);
660 sections[NamespaceFunction].appendMember((Node*)node);
663 sections[GlobalFunction].appendMember((Node*)node);
666 sections[GlobalFunction].appendMember((Node*)node);
671 sections[Property].appendMember((Node*)node);
674 sections[Variable].appendMember((Node*)node);
676 case Node::QmlProperty:
677 sections[QmlProperty].appendMember((Node*)node);
679 case Node::QmlSignal:
680 sections[QmlSignal].appendMember((Node*)node);
682 case Node::QmlSignalHandler:
683 sections[QmlSignalHandler].appendMember((Node*)node);
685 case Node::QmlMethod:
686 sections[QmlMethod].appendMember((Node*)node);
695 First generate the table of contents.
698 s = sections.constBegin();
699 while (s != sections.constEnd()) {
700 if (!(*s).members.isEmpty()) {
704 << Doc::canonicalTitle((*s).name)
714 s = sections.constBegin();
715 while (s != sections.constEnd()) {
716 if (!(*s).members.isEmpty()) {
717 out() << "<a name=\""
718 << Doc::canonicalTitle((*s).name)
720 out() << "<h3>" << protectEnc((*s).name) << "</h3>\n";
722 generateCompactList(0, marker, ncmap.value(), false, QString("Q"));
723 else if (idx == QmlClass)
724 generateCompactList(0, marker, nqcmap.value(), false, QString("Q"));
725 else if (idx == MemberFunction) {
726 ParentMaps parentmaps;
727 ParentMaps::iterator pmap;
728 NodeList::const_iterator i = s->members.constBegin();
729 while (i != s->members.constEnd()) {
730 Node* p = (*i)->parent();
731 pmap = parentmaps.find(p);
732 if (pmap == parentmaps.end())
733 pmap = parentmaps.insert(p,NodeMultiMap());
734 pmap->insert((*i)->name(),(*i));
737 pmap = parentmaps.begin();
738 while (pmap != parentmaps.end()) {
739 NodeList nlist = pmap->values();
740 out() << "<p>Class ";
742 out() << "<a href=\""
743 << linkForNode(pmap.key(), 0)
745 QStringList pieces = fullName(pmap.key(), 0, marker).split("::");
746 out() << protectEnc(pieces.last());
747 out() << "</a>" << ":</p>\n";
749 generateSection(nlist, 0, marker, CodeMarker::Summary);
755 generateSection(s->members, 0, marker, CodeMarker::Summary);
770 case Atom::InlineImage:
772 QString fileName = imageFileName(relative, atom->string());
774 if (atom->next() != 0)
775 text = atom->next()->string();
776 if (atom->type() == Atom::Image)
777 out() << "<p class=\"centerAlign\">";
778 if (fileName.isEmpty()) {
779 relative->location().warning(tr("Missing image: %1").arg(protectEnc(atom->string())));
780 out() << "<font color=\"red\">[Missing image "
781 << protectEnc(atom->string()) << "]</font>";
785 if (!baseDir().isEmpty())
787 out() << "<img src=\"" << protectEnc(prefix + fileName) << '"';
789 out() << " alt=\"" << protectEnc(text) << '"';
791 out() << " alt=\"\"";
793 helpProjectWriter->addExtraFile(fileName);
794 if ((relative->type() == Node::Fake) &&
795 (relative->subType() == Node::Example)) {
796 const ExampleNode* cen = static_cast<const ExampleNode*>(relative);
797 if (cen->imageFileName().isEmpty()) {
798 ExampleNode* en = const_cast<ExampleNode*>(cen);
799 en->setImageFileName(fileName);
803 if (atom->type() == Atom::Image)
807 case Atom::ImageText:
809 case Atom::ImportantLeft:
811 out() << formattingLeftMap()[ATOM_FORMATTING_BOLD];
812 out() << "Important: ";
813 out() << formattingRightMap()[ATOM_FORMATTING_BOLD];
815 case Atom::ImportantRight:
820 out() << formattingLeftMap()[ATOM_FORMATTING_BOLD];
822 out() << formattingRightMap()[ATOM_FORMATTING_BOLD];
824 case Atom::NoteRight:
827 case Atom::LegaleseLeft:
828 out() << "<div class=\"LegaleseLeft\">";
830 case Atom::LegaleseRight:
833 case Atom::LineBreak:
838 const Node *node = 0;
839 QString myLink = getLink(atom, relative, marker, &node);
840 if (myLink.isEmpty()) {
841 myLink = getCollisionLink(atom);
842 if (myLink.isEmpty()) {
843 relative->doc().location().warning(tr("Can't create link to '%1'")
844 .arg(atom->string()));
849 beginLink(myLink, node, relative, marker);
855 const Node *node = CodeMarker::nodeForString(atom->string());
856 beginLink(linkForNode(node, relative), node, relative, marker);
865 if (atom->string() == ATOM_LIST_BULLET) {
868 else if (atom->string() == ATOM_LIST_TAG) {
871 else if (atom->string() == ATOM_LIST_VALUE) {
872 threeColumnEnumValueTable = isThreeColumnEnumValueTable(atom);
873 if (threeColumnEnumValueTable) {
874 out() << "<table class=\"valuelist\">";
875 if (++numTableRows % 2 == 1)
876 out() << "<tr valign=\"top\" class=\"odd\">";
878 out() << "<tr valign=\"top\" class=\"even\">";
880 out() << "<th class=\"tblConst\">Constant</th>"
881 << "<th class=\"tblval\">Value</th>"
882 << "<th class=\"tbldscr\">Description</th></tr>\n";
885 out() << "<table class=\"valuelist\">"
886 << "<tr><th class=\"tblConst\">Constant</th><th class=\"tblVal\">Value</th></tr>\n";
890 out() << "<ol class=";
891 if (atom->string() == ATOM_LIST_UPPERALPHA) {
893 } /* why type? changed to */
894 else if (atom->string() == ATOM_LIST_LOWERALPHA) {
897 else if (atom->string() == ATOM_LIST_UPPERROMAN) {
900 else if (atom->string() == ATOM_LIST_LOWERROMAN) {
903 else { // (atom->string() == ATOM_LIST_NUMERIC)
906 if (atom->next() != 0 && atom->next()->string().toInt() != 1)
907 out() << " start=\"" << atom->next()->string() << '"';
911 case Atom::ListItemNumber:
913 case Atom::ListTagLeft:
914 if (atom->string() == ATOM_LIST_TAG) {
917 else { // (atom->string() == ATOM_LIST_VALUE)
920 out() << "<tr><td class=\"topAlign\"><tt>"
921 << protectEnc(plainCode(marker->markedUpEnumValue(atom->next()->string(),
923 << "</tt></td><td class=\"topAlign\">";
926 if (relative->type() == Node::Enum) {
927 const EnumNode *enume = static_cast<const EnumNode *>(relative);
928 itemValue = enume->itemValue(atom->next()->string());
931 if (itemValue.isEmpty())
934 out() << "<tt>" << protectEnc(itemValue) << "</tt>";
939 case Atom::ListTagRight:
940 if (atom->string() == ATOM_LIST_TAG)
943 case Atom::ListItemLeft:
944 if (atom->string() == ATOM_LIST_TAG) {
947 else if (atom->string() == ATOM_LIST_VALUE) {
948 if (threeColumnEnumValueTable) {
949 out() << "</td><td class=\"topAlign\">";
950 if (matchAhead(atom, Atom::ListItemRight))
957 if (matchAhead(atom, Atom::ParaLeft))
960 case Atom::ListItemRight:
961 if (atom->string() == ATOM_LIST_TAG) {
964 else if (atom->string() == ATOM_LIST_VALUE) {
965 out() << "</td></tr>\n";
971 case Atom::ListRight:
972 if (atom->string() == ATOM_LIST_BULLET) {
975 else if (atom->string() == ATOM_LIST_TAG) {
978 else if (atom->string() == ATOM_LIST_VALUE) {
979 out() << "</table>\n";
991 case Atom::ParaRight:
997 //if (!matchAhead(atom, Atom::ListItemRight) && !matchAhead(atom, Atom::TableItemRight))
998 // out() << "</p>\n";
1000 case Atom::QuotationLeft:
1001 out() << "<blockquote>";
1003 case Atom::QuotationRight:
1004 out() << "</blockquote>\n";
1006 case Atom::RawString:
1007 out() << atom->string();
1009 case Atom::SectionLeft:
1010 out() << "<a name=\"" << Doc::canonicalTitle(Text::sectionHeading(atom).toString())
1011 << "\"></a>" << divNavTop << '\n';
1013 case Atom::SectionRight:
1015 case Atom::SectionHeadingLeft:
1016 out() << "<h" + QString::number(atom->string().toInt() + hOffset(relative)) + QLatin1Char('>');
1017 inSectionHeading = true;
1019 case Atom::SectionHeadingRight:
1020 out() << "</h" + QString::number(atom->string().toInt() + hOffset(relative)) + ">\n";
1021 inSectionHeading = false;
1023 case Atom::SidebarLeft:
1025 case Atom::SidebarRight:
1028 if (inLink && !inContents && !inSectionHeading) {
1029 generateLink(atom, relative, marker);
1032 out() << protectEnc(atom->string());
1035 case Atom::TableLeft:
1038 QString attr = "generic";
1044 if (atom->count() > 0) {
1045 p1 = atom->string(0);
1046 if (atom->count() > 1)
1047 p2 = atom->string(1);
1049 if (!p1.isEmpty()) {
1050 if (p1 == "borderless")
1052 else if (p1.contains("%"))
1055 if (!p2.isEmpty()) {
1056 if (p2 == "borderless")
1058 else if (p2.contains("%"))
1061 out() << "<table class=\"" << attr << "\"";
1062 if (!width.isEmpty())
1063 out() << " width=\"" << width << "\"";
1068 case Atom::TableRight:
1069 out() << "</table>\n";
1071 case Atom::TableHeaderLeft:
1072 out() << "<thead><tr class=\"qt-style\">";
1073 inTableHeader = true;
1075 case Atom::TableHeaderRight:
1077 if (matchAhead(atom, Atom::TableHeaderLeft)) {
1079 out() << "\n<tr class=\"qt-style\">";
1082 out() << "</thead>\n";
1083 inTableHeader = false;
1086 case Atom::TableRowLeft:
1087 if (!atom->string().isEmpty())
1088 out() << "<tr " << atom->string() << '>';
1089 else if (++numTableRows % 2 == 1)
1090 out() << "<tr valign=\"top\" class=\"odd\">";
1092 out() << "<tr valign=\"top\" class=\"even\">";
1094 case Atom::TableRowRight:
1097 case Atom::TableItemLeft:
1104 for (int i=0; i<atom->count(); ++i) {
1107 QString p = atom->string(i);
1108 if (p.contains('=')) {
1112 QStringList spans = p.split(",");
1113 if (spans.size() == 2) {
1114 if (spans.at(0) != "1")
1115 out() << " colspan=\"" << spans.at(0) << '"';
1116 if (spans.at(1) != "1")
1117 out() << " rowspan=\"" << spans.at(1) << '"';
1127 if (matchAhead(atom, Atom::ParaLeft))
1131 case Atom::TableItemRight:
1136 //out() << "</p></td>";
1138 if (matchAhead(atom, Atom::ParaLeft))
1141 case Atom::TableOfContents:
1144 out() << "<a name=\"" << Doc::canonicalTitle(atom->string()) << "\"></a>";
1146 case Atom::UnhandledFormat:
1147 out() << "<b class=\"redFont\"><Missing HTML></b>";
1149 case Atom::UnknownCommand:
1150 out() << "<b class=\"redFont\"><code>\\" << protectEnc(atom->string())
1154 case Atom::EndQmlText:
1155 // don't do anything with these. They are just tags.
1164 Generate a reference page for a C++ class.
1166 void HtmlGenerator::generateClassLikeNode(InnerNode* inner, CodeMarker* marker)
1168 QList<Section> sections;
1169 QList<Section>::ConstIterator s;
1171 ClassNode* classe = 0;
1176 if (inner->type() == Node::Namespace) {
1177 rawTitle = marker->plainName(inner);
1178 fullTitle = marker->plainFullName(inner);
1179 title = rawTitle + " Namespace";
1181 else if (inner->type() == Node::Class) {
1182 classe = static_cast<ClassNode*>(inner);
1183 rawTitle = marker->plainName(inner);
1184 fullTitle = marker->plainFullName(inner);
1185 title = rawTitle + " Class";
1189 if (rawTitle != fullTitle)
1190 subtitleText << "(" << Atom(Atom::AutoLink, fullTitle) << ")"
1191 << Atom(Atom::LineBreak);
1193 generateHeader(title, inner, marker);
1194 sections = marker->sections(inner, CodeMarker::Summary, CodeMarker::Okay);
1195 generateTableOfContents(inner,marker,§ions);
1196 generateTitle(title, subtitleText, SmallSubTitle, inner, marker);
1197 generateBrief(inner, marker);
1198 generateIncludes(inner, marker);
1199 generateStatus(inner, marker);
1201 generateInherits(classe, marker);
1202 generateInheritedBy(classe, marker);
1203 if (classe->qmlElement() != 0)
1204 generateInstantiatedBy(classe,marker);
1206 generateThreadSafeness(inner, marker);
1207 generateSince(inner, marker);
1211 QString membersLink = generateListOfAllMemberFile(inner, marker);
1212 if (!membersLink.isEmpty())
1213 out() << "<li><a href=\"" << membersLink << "\">"
1214 << "List of all members, including inherited members</a></li>\n";
1216 QString obsoleteLink = generateLowStatusMemberFile(inner,
1218 CodeMarker::Obsolete);
1219 if (!obsoleteLink.isEmpty())
1220 out() << "<li><a href=\"" << obsoleteLink << "\">"
1221 << "Obsolete members</a></li>\n";
1223 QString compatLink = generateLowStatusMemberFile(inner,
1225 CodeMarker::Compat);
1226 if (!compatLink.isEmpty())
1227 out() << "<li><a href=\"" << compatLink << "\">"
1228 << "Compatibility members</a></li>\n";
1232 bool needOtherSection = false;
1235 sections is built above for the call to generateTableOfContents().
1237 s = sections.constBegin();
1238 while (s != sections.constEnd()) {
1239 if (s->members.isEmpty() && s->reimpMembers.isEmpty()) {
1240 if (!s->inherited.isEmpty())
1241 needOtherSection = true;
1244 if (!s->members.isEmpty()) {
1245 // out() << "<hr />\n";
1246 out() << "<a name=\""
1247 << registerRef((*s).name.toLower())
1248 << "\"></a>" << divNavTop << "\n";
1249 out() << "<h2>" << protectEnc((*s).name) << "</h2>\n";
1250 generateSection(s->members, inner, marker, CodeMarker::Summary);
1252 if (!s->reimpMembers.isEmpty()) {
1253 QString name = QString("Reimplemented ") + (*s).name;
1254 // out() << "<hr />\n";
1255 out() << "<a name=\""
1256 << registerRef(name.toLower())
1257 << "\"></a>" << divNavTop << "\n";
1258 out() << "<h2>" << protectEnc(name) << "</h2>\n";
1259 generateSection(s->reimpMembers, inner, marker, CodeMarker::Summary);
1262 if (!s->inherited.isEmpty()) {
1264 generateSectionInheritedList(*s, inner, marker);
1271 if (needOtherSection) {
1272 out() << "<h3>Additional Inherited Members</h3>\n"
1275 s = sections.constBegin();
1276 while (s != sections.constEnd()) {
1277 if (s->members.isEmpty() && !s->inherited.isEmpty())
1278 generateSectionInheritedList(*s, inner, marker);
1284 out() << "<a name=\"" << registerRef("details") << "\"></a>" << divNavTop << '\n';
1286 if (!inner->doc().isEmpty()) {
1287 generateExtractionMark(inner, DetailedDescriptionMark);
1288 //out() << "<hr />\n"
1289 out() << "<div class=\"descr\">\n" // QTBUG-9504
1290 << "<h2>" << "Detailed Description" << "</h2>\n";
1291 generateBody(inner, marker);
1292 out() << "</div>\n"; // QTBUG-9504
1293 generateAlsoList(inner, marker);
1294 generateMaintainerList(inner, marker);
1295 generateExtractionMark(inner, EndMark);
1298 sections = marker->sections(inner, CodeMarker::Detailed, CodeMarker::Okay);
1299 s = sections.constBegin();
1300 while (s != sections.constEnd()) {
1301 //out() << "<hr />\n";
1302 if (!(*s).divClass.isEmpty())
1303 out() << "<div class=\"" << (*s).divClass << "\">\n"; // QTBUG-9504
1304 out() << "<h2>" << protectEnc((*s).name) << "</h2>\n";
1306 NodeList::ConstIterator m = (*s).members.constBegin();
1307 while (m != (*s).members.constEnd()) {
1308 if ((*m)->access() != Node::Private) { // ### check necessary?
1309 if ((*m)->type() != Node::Class)
1310 generateDetailedMember(*m, inner, marker);
1312 out() << "<h3> class ";
1313 generateFullName(*m, inner, marker);
1315 generateBrief(*m, marker, inner);
1319 names << (*m)->name();
1320 if ((*m)->type() == Node::Function) {
1321 const FunctionNode *func = reinterpret_cast<const FunctionNode *>(*m);
1322 if (func->metaness() == FunctionNode::Ctor ||
1323 func->metaness() == FunctionNode::Dtor ||
1324 func->overloadNumber() != 1)
1327 else if ((*m)->type() == Node::Property) {
1328 const PropertyNode *prop = reinterpret_cast<const PropertyNode *>(*m);
1329 if (!prop->getters().isEmpty() &&
1330 !names.contains(prop->getters().first()->name()))
1331 names << prop->getters().first()->name();
1332 if (!prop->setters().isEmpty())
1333 names << prop->setters().first()->name();
1334 if (!prop->resetters().isEmpty())
1335 names << prop->resetters().first()->name();
1336 if (!prop->notifiers().isEmpty())
1337 names << prop->notifiers().first()->name();
1339 else if ((*m)->type() == Node::Enum) {
1340 const EnumNode *enume = reinterpret_cast<const EnumNode*>(*m);
1341 if (enume->flagsType())
1342 names << enume->flagsType()->name();
1344 foreach (const QString &enumName,
1345 enume->doc().enumItemNames().toSet() -
1346 enume->doc().omitEnumItemNames().toSet())
1347 names << plainCode(marker->markedUpEnumValue(enumName,
1353 if (!(*s).divClass.isEmpty())
1354 out() << "</div>\n"; // QTBUG-9504
1357 generateFooter(inner);
1361 We delayed generation of the disambiguation pages until now, after
1362 all the other pages have been generated. We do this because we might
1363 encounter a link command that tries to link to a target on a QML
1364 component page, but the link doesn't specify the module identifer
1365 for the component, and the component name without a module
1366 identifier is ambiguous. When such a link is found, qdoc can't find
1367 the target, so it appends the target to the NameCollisionNode. After
1368 the tree has been traversed and all these ambiguous links have been
1369 added to the name collision nodes, this function is called. The list
1370 of collision nodes is traversed here, and the disambiguation page for
1371 each collision is generated. The disambiguation page will not only
1372 disambiguate links to the component pages, but it will also disambiguate
1373 links to properties, section headers, etc.
1375 void HtmlGenerator::generateCollisionPages()
1377 if (collisionNodes.isEmpty())
1380 for (int i=0; i<collisionNodes.size(); ++i) {
1381 NameCollisionNode* ncn = collisionNodes.at(i);
1385 NodeList collisions;
1386 const NodeList& nl = ncn->childNodes();
1387 if (!nl.isEmpty()) {
1388 NodeList::ConstIterator it = nl.constBegin();
1389 while (it != nl.constEnd()) {
1390 if (!(*it)->isInternal())
1391 collisions.append(*it);
1395 if (collisions.size() <= 1)
1398 ncn->clearCurrentChild();
1399 beginSubPage(ncn, Generator::fileName(ncn));
1400 QString fullTitle = ncn->fullTitle();
1401 QString htmlTitle = fullTitle;
1402 CodeMarker* marker = CodeMarker::markerForFileName(ncn->location().filePath());
1403 if (ncn->isQmlNode()) {
1404 // Replace the marker with a QML code marker.
1405 if (ncn->isQmlNode())
1406 marker = CodeMarker::markerForLanguage(QLatin1String("QML"));
1409 generateHeader(htmlTitle, ncn, marker);
1410 if (!fullTitle.isEmpty())
1411 out() << "<h1 class=\"title\">" << protectEnc(fullTitle) << "</h1>\n";
1414 for (int i=0; i<collisions.size(); ++i) {
1415 Node* n = collisions.at(i);
1417 if (!n->qmlModuleIdentifier().isEmpty())
1418 t = n->qmlModuleIdentifier() + "::";
1419 t += protectEnc(fullTitle);
1420 nm.insertMulti(t,n);
1422 generateAnnotatedList(ncn, marker, nm, true);
1424 QList<QString> targets;
1425 if (!ncn->linkTargets().isEmpty()) {
1426 QMap<QString,QString>::ConstIterator t = ncn->linkTargets().constBegin();
1427 while (t != ncn->linkTargets().constEnd()) {
1429 for (int i=0; i<collisions.size(); ++i) {
1430 InnerNode* n = static_cast<InnerNode*>(collisions.at(i));
1431 if (n->findChildNodeByName(t.key())) {
1434 targets.append(t.key());
1442 if (!targets.isEmpty()) {
1443 QList<QString>::ConstIterator t = targets.constBegin();
1444 while (t != targets.constEnd()) {
1445 out() << "<a name=\"" << Doc::canonicalTitle(*t) << "\"></a>";
1446 out() << "<h2 class=\"title\">" << protectEnc(*t) << "</h2>\n";
1448 for (int i=0; i<collisions.size(); ++i) {
1449 InnerNode* n = static_cast<InnerNode*>(collisions.at(i));
1450 Node* p = n->findChildNodeByName(*t);
1452 QString link = linkForNode(p,0);
1454 if (!n->qmlModuleIdentifier().isEmpty())
1455 label = n->qmlModuleIdentifier() + "::";
1456 label += n->name() + "::" + p->name();
1458 out() << "<a href=\"" << link << "\">";
1459 out() << protectEnc(label) << "</a>";
1468 generateFooter(ncn);
1474 Generate the HTML page for an entity that doesn't map
1475 to any underlying parsable C++ class or QML component.
1477 void HtmlGenerator::generateFakeNode(FakeNode* fake, CodeMarker* marker)
1480 If the fake node is a page node, and if the page type
1481 is DITA map page, write the node's contents as a dita
1482 map and return without doing anything else.
1484 if (fake->subType() == Node::Page && fake->pageType() == Node::DitaMapPage) {
1485 const DitaMapNode* dmn = static_cast<const DitaMapNode*>(fake);
1490 SubTitleSize subTitleSize = LargeSubTitle;
1491 QList<Section> sections;
1492 QList<Section>::const_iterator s;
1493 QString fullTitle = fake->fullTitle();
1494 QString htmlTitle = fullTitle;
1496 if (fake->subType() == Node::File && !fake->subTitle().isEmpty()) {
1497 subTitleSize = SmallSubTitle;
1498 htmlTitle += " (" + fake->subTitle() + QLatin1Char(')');
1500 else if (fake->subType() == Node::QmlBasicType) {
1501 fullTitle = "QML Basic Type: " + fullTitle;
1502 htmlTitle = fullTitle;
1504 // Replace the marker with a QML code marker.
1505 marker = CodeMarker::markerForLanguage(QLatin1String("QML"));
1508 generateHeader(htmlTitle, fake, marker);
1510 Generate the TOC for the new doc format.
1511 Don't generate a TOC for the home page.
1513 QmlClassNode* qml_cn = 0;
1514 if (fake->subType() == Node::QmlClass) {
1515 qml_cn = static_cast<QmlClassNode*>(fake);
1516 sections = marker->qmlSections(qml_cn,CodeMarker::Summary);
1517 generateTableOfContents(fake,marker,§ions);
1519 // Replace the marker with a QML code marker.
1520 marker = CodeMarker::markerForLanguage(QLatin1String("QML"));
1522 else if (fake->subType() != Node::Collision && fake->name() != QString("index.html"))
1523 generateTableOfContents(fake,marker,0);
1525 generateTitle(fullTitle,
1526 Text() << fake->subTitle(),
1531 if (fake->subType() == Node::Module) {
1532 // Generate brief text and status for modules.
1533 generateBrief(fake, marker);
1534 generateStatus(fake, marker);
1535 generateSince(fake, marker);
1537 if (moduleNamespaceMap.contains(fake->name())) {
1538 out() << "<a name=\"" << registerRef("namespaces") << "\"></a>" << divNavTop << '\n';
1539 out() << "<h2>Namespaces</h2>\n";
1540 generateAnnotatedList(fake, marker, moduleNamespaceMap[fake->name()]);
1542 if (moduleClassMap.contains(fake->name())) {
1543 out() << "<a name=\"" << registerRef("classes") << "\"></a>" << divNavTop << '\n';
1544 out() << "<h2>Classes</h2>\n";
1545 generateAnnotatedList(fake, marker, moduleClassMap[fake->name()]);
1548 else if (fake->subType() == Node::HeaderFile) {
1549 // Generate brief text and status for modules.
1550 generateBrief(fake, marker);
1551 generateStatus(fake, marker);
1552 generateSince(fake, marker);
1556 QString membersLink = generateListOfAllMemberFile(fake, marker);
1557 if (!membersLink.isEmpty())
1558 out() << "<li><a href=\"" << membersLink << "\">"
1559 << "List of all members, including inherited members</a></li>\n";
1561 QString obsoleteLink = generateLowStatusMemberFile(fake,
1563 CodeMarker::Obsolete);
1564 if (!obsoleteLink.isEmpty())
1565 out() << "<li><a href=\"" << obsoleteLink << "\">"
1566 << "Obsolete members</a></li>\n";
1568 QString compatLink = generateLowStatusMemberFile(fake,
1570 CodeMarker::Compat);
1571 if (!compatLink.isEmpty())
1572 out() << "<li><a href=\"" << compatLink << "\">"
1573 << "Compatibility members</a></li>\n";
1577 else if (fake->subType() == Node::QmlClass) {
1578 const_cast<FakeNode*>(fake)->setCurrentChild();
1579 ClassNode* cn = qml_cn->classNode();
1580 generateBrief(qml_cn, marker);
1581 generateQmlInherits(qml_cn, marker);
1582 generateQmlInheritedBy(qml_cn, marker);
1583 generateQmlInstantiates(qml_cn, marker);
1584 generateSince(qml_cn, marker);
1586 QString allQmlMembersLink = generateAllQmlMembersFile(qml_cn, marker);
1587 if (!allQmlMembersLink.isEmpty()) {
1589 out() << "<li><a href=\"" << allQmlMembersLink << "\">"
1590 << "List of all members, including inherited members</a></li>\n";
1594 s = sections.constBegin();
1595 while (s != sections.constEnd()) {
1596 out() << "<a name=\"" << registerRef((*s).name.toLower())
1597 << "\"></a>" << divNavTop << '\n';
1598 out() << "<h2>" << protectEnc((*s).name) << "</h2>\n";
1599 generateQmlSummary(*s,fake,marker);
1603 generateExtractionMark(fake, DetailedDescriptionMark);
1604 out() << "<a name=\"" << registerRef("details") << "\"></a>" << divNavTop << '\n';
1605 out() << "<h2>" << "Detailed Description" << "</h2>\n";
1606 generateBody(fake, marker);
1608 generateQmlText(cn->doc().body(), cn, marker, fake->name());
1609 generateAlsoList(fake, marker);
1610 generateExtractionMark(fake, EndMark);
1611 //out() << "<hr />\n";
1613 sections = marker->qmlSections(qml_cn,CodeMarker::Detailed);
1614 s = sections.constBegin();
1615 while (s != sections.constEnd()) {
1616 out() << "<h2>" << protectEnc((*s).name) << "</h2>\n";
1617 NodeList::ConstIterator m = (*s).members.constBegin();
1618 while (m != (*s).members.constEnd()) {
1619 generateDetailedQmlMember(*m, fake, marker);
1625 generateFooter(fake);
1626 const_cast<FakeNode*>(fake)->clearCurrentChild();
1630 sections = marker->sections(fake, CodeMarker::Summary, CodeMarker::Okay);
1631 s = sections.constBegin();
1632 while (s != sections.constEnd()) {
1633 out() << "<a name=\"" << registerRef((*s).name) << "\"></a>" << divNavTop << '\n';
1634 out() << "<h2>" << protectEnc((*s).name) << "</h2>\n";
1635 generateSectionList(*s, fake, marker, CodeMarker::Summary);
1639 Text brief = fake->doc().briefText();
1640 if (fake->subType() == Node::Module && !brief.isEmpty()) {
1641 generateExtractionMark(fake, DetailedDescriptionMark);
1642 out() << "<a name=\"" << registerRef("details") << "\"></a>" << divNavTop << '\n';
1643 out() << "<div class=\"descr\">\n"; // QTBUG-9504
1644 out() << "<h2>" << "Detailed Description" << "</h2>\n";
1647 generateExtractionMark(fake, DetailedDescriptionMark);
1648 out() << "<div class=\"descr\"> <a name=\"" << registerRef("details") << "\"></a>\n"; // QTBUG-9504
1651 generateBody(fake, marker);
1652 out() << "</div>\n"; // QTBUG-9504
1653 generateAlsoList(fake, marker);
1654 generateExtractionMark(fake, EndMark);
1656 if ((fake->subType() == Node::Group) && !fake->groupMembers().isEmpty()) {
1657 NodeMap groupMembersMap;
1658 foreach (const Node *node, fake->groupMembers()) {
1659 if (node->type() == Node::Class || node->type() == Node::Namespace)
1660 groupMembersMap[node->name()] = node;
1662 generateAnnotatedList(fake, marker, groupMembersMap);
1664 else if ((fake->subType() == Node::QmlModule) && !fake->qmlModuleMembers().isEmpty()) {
1665 NodeMap qmlModuleMembersMap;
1666 foreach (const Node* node, fake->qmlModuleMembers()) {
1667 if (node->type() == Node::Fake && node->subType() == Node::QmlClass)
1668 qmlModuleMembersMap[node->name()] = node;
1670 generateAnnotatedList(fake, marker, qmlModuleMembersMap);
1673 sections = marker->sections(fake, CodeMarker::Detailed, CodeMarker::Okay);
1674 s = sections.constBegin();
1675 while (s != sections.constEnd()) {
1676 //out() << "<hr />\n";
1677 out() << "<h2>" << protectEnc((*s).name) << "</h2>\n";
1679 NodeList::ConstIterator m = (*s).members.constBegin();
1680 while (m != (*s).members.constEnd()) {
1681 generateDetailedMember(*m, fake, marker);
1686 generateFooter(fake);
1690 Returns "html" for this subclass of Generator.
1692 QString HtmlGenerator::fileExtension() const
1698 Output breadcrumb list in the html file.
1700 void HtmlGenerator::generateBreadCrumbs(const QString &title,
1708 if (node->type() == Node::Class) {
1709 const ClassNode *cn = static_cast<const ClassNode *>(node);
1710 QString name = node->moduleName();
1711 breadcrumbs << Atom(Atom::ListItemLeft)
1712 << Atom(Atom::Link, QLatin1String("All Modules"))
1713 << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK)
1714 << Atom(Atom::String, QLatin1String("Modules"))
1715 << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK)
1716 << Atom(Atom::ListItemRight);
1717 if (!name.isEmpty())
1718 breadcrumbs << Atom(Atom::ListItemLeft)
1719 << Atom(Atom::AutoLink, name)
1720 << Atom(Atom::ListItemRight);
1721 if (!cn->name().isEmpty())
1722 breadcrumbs << Atom(Atom::ListItemLeft)
1723 << Atom(Atom::String, protectEnc(cn->name()))
1724 << Atom(Atom::ListItemRight);
1726 else if (node->type() == Node::Fake) {
1727 const FakeNode* fn = static_cast<const FakeNode*>(node);
1728 if (node->subType() == Node::Module) {
1729 breadcrumbs << Atom(Atom::ListItemLeft)
1730 << Atom(Atom::Link, QLatin1String("All Modules"))
1731 << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK)
1732 << Atom(Atom::String, QLatin1String("Modules"))
1733 << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK)
1734 << Atom(Atom::ListItemRight);
1735 QString name = node->name();
1736 if (!name.isEmpty())
1737 breadcrumbs << Atom(Atom::ListItemLeft)
1738 << Atom(Atom::String, protectEnc(name))
1739 << Atom(Atom::ListItemRight);
1741 else if (node->subType() == Node::Group) {
1742 if (fn->name() == QString("modules"))
1743 breadcrumbs << Atom(Atom::String, QLatin1String("Modules"));
1745 breadcrumbs << Atom(Atom::ListItemLeft)
1746 << Atom(Atom::String, protectEnc(title))
1747 << Atom(Atom::ListItemRight);
1749 else if (node->subType() == Node::Page) {
1750 if (fn->name() == QString("qdeclarativeexamples.html")) {
1751 breadcrumbs << Atom(Atom::ListItemLeft)
1752 << Atom(Atom::Link, QLatin1String("Qt Examples"))
1753 << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK)
1754 << Atom(Atom::String, QLatin1String("Examples"))
1755 << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK)
1756 << Atom(Atom::ListItemRight);
1757 breadcrumbs << Atom(Atom::ListItemLeft)
1758 << Atom(Atom::AutoLink, QLatin1String("QML Examples & Demos"))
1759 << Atom(Atom::ListItemRight);
1761 else if (fn->name().startsWith("examples-")) {
1762 breadcrumbs << Atom(Atom::ListItemLeft)
1763 << Atom(Atom::Link, QLatin1String("Qt Examples"))
1764 << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK)
1765 << Atom(Atom::String, QLatin1String("Examples"))
1766 << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK)
1767 << Atom(Atom::ListItemRight);
1768 breadcrumbs << Atom(Atom::ListItemLeft)
1769 << Atom(Atom::String, protectEnc(title))
1770 << Atom(Atom::ListItemRight);
1772 else if (fn->name() == QString("namespaces.html"))
1773 breadcrumbs << Atom(Atom::String, QLatin1String("Namespaces"));
1775 breadcrumbs << Atom(Atom::ListItemLeft)
1776 << Atom(Atom::String, protectEnc(title))
1777 << Atom(Atom::ListItemRight);
1779 else if (node->subType() == Node::QmlClass) {
1780 breadcrumbs << Atom(Atom::ListItemLeft)
1781 << Atom(Atom::AutoLink, QLatin1String("Basic QML Types"))
1782 << Atom(Atom::ListItemRight);
1783 breadcrumbs << Atom(Atom::ListItemLeft)
1784 << Atom(Atom::String, protectEnc(title))
1785 << Atom(Atom::ListItemRight);
1787 else if (node->subType() == Node::Example) {
1788 breadcrumbs << Atom(Atom::ListItemLeft)
1789 << Atom(Atom::Link, QLatin1String("Qt Examples"))
1790 << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK)
1791 << Atom(Atom::String, QLatin1String("Examples"))
1792 << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK)
1793 << Atom(Atom::ListItemRight);
1794 QStringList sl = fn->name().split('/');
1795 if (sl.contains("declarative"))
1796 breadcrumbs << Atom(Atom::ListItemLeft)
1797 << Atom(Atom::AutoLink, QLatin1String("QML Examples & Demos"))
1798 << Atom(Atom::ListItemRight);
1800 QString name = protectEnc("examples-" + sl.at(0) + ".html"); // this generates an empty link
1801 QString t = CodeParser::titleFromName(name);
1803 breadcrumbs << Atom(Atom::ListItemLeft)
1804 << Atom(Atom::String, protectEnc(title))
1805 << Atom(Atom::ListItemRight);
1808 else if (node->type() == Node::Namespace) {
1809 breadcrumbs << Atom(Atom::ListItemLeft)
1810 << Atom(Atom::Link, QLatin1String("All Namespaces"))
1811 << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK)
1812 << Atom(Atom::String, QLatin1String("Namespaces"))
1813 << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK)
1814 << Atom(Atom::ListItemRight);
1815 breadcrumbs << Atom(Atom::ListItemLeft)
1816 << Atom(Atom::String, protectEnc(title))
1817 << Atom(Atom::ListItemRight);
1820 generateText(breadcrumbs, node, marker);
1823 void HtmlGenerator::generateHeader(const QString& title,
1827 out() << QString("<?xml version=\"1.0\" encoding=\"%1\"?>\n").arg(outputEncoding);
1828 out() << "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\n";
1829 out() << QString("<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"%1\" lang=\"%1\">\n").arg(naturalLanguage);
1830 out() << "<head>\n";
1831 out() << " <meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\" />\n";
1832 if (node && !node->doc().location().isEmpty())
1833 out() << "<!-- " << node->doc().location().fileName() << " -->\n";
1835 QString shortVersion = tree_->version();
1836 if (shortVersion.count(QChar('.')) == 2)
1837 shortVersion.truncate(shortVersion.lastIndexOf(QChar('.')));
1838 if (!project.isEmpty())
1839 shortVersion = project + QLatin1Char(' ') + shortVersion + QLatin1String(": ");
1841 shortVersion = QLatin1String("Qt ") + shortVersion + QLatin1String(": ");
1843 // Generating page title
1844 out() << " <title>" << shortVersion << protectEnc(title) << "</title>\n";
1846 // Include style sheet and script links.
1847 out() << headerStyles;
1848 out() << headerScripts;
1851 #ifdef GENERATE_MAC_REFS
1853 generateMacRef(node, marker);
1856 out() << QString(postHeader).replace("\\" + COMMAND_VERSION, tree_->version());
1857 generateBreadCrumbs(title,node,marker);
1858 out() << QString(postPostHeader).replace("\\" + COMMAND_VERSION, tree_->version());
1860 navigationLinks.clear();
1862 if (node && !node->links().empty()) {
1863 QPair<QString,QString> linkPair;
1864 QPair<QString,QString> anchorPair;
1865 const Node *linkNode;
1867 if (node->links().contains(Node::PreviousLink)) {
1868 linkPair = node->links()[Node::PreviousLink];
1869 linkNode = findNodeForTarget(linkPair.first, node, marker);
1870 if (!linkNode || linkNode == node)
1871 anchorPair = linkPair;
1873 anchorPair = anchorForNode(linkNode);
1875 out() << " <link rel=\"prev\" href=\""
1876 << anchorPair.first << "\" />\n";
1878 navigationLinks += "<a class=\"prevPage\" href=\"" + anchorPair.first + "\">";
1879 if (linkPair.first == linkPair.second && !anchorPair.second.isEmpty())
1880 navigationLinks += protect(anchorPair.second);
1882 navigationLinks += protect(linkPair.second);
1883 navigationLinks += "</a>\n";
1885 if (node->links().contains(Node::NextLink)) {
1886 linkPair = node->links()[Node::NextLink];
1887 linkNode = findNodeForTarget(linkPair.first, node, marker);
1888 if (!linkNode || linkNode == node)
1889 anchorPair = linkPair;
1891 anchorPair = anchorForNode(linkNode);
1893 out() << " <link rel=\"next\" href=\""
1894 << anchorPair.first << "\" />\n";
1896 navigationLinks += "<a class=\"nextPage\" href=\"" + anchorPair.first + "\">";
1897 if (linkPair.first == linkPair.second && !anchorPair.second.isEmpty())
1898 navigationLinks += protect(anchorPair.second);
1900 navigationLinks += protect(linkPair.second);
1901 navigationLinks += "</a>\n";
1903 if (node->links().contains(Node::StartLink)) {
1904 linkPair = node->links()[Node::StartLink];
1905 linkNode = findNodeForTarget(linkPair.first, node, marker);
1906 if (!linkNode || linkNode == node)
1907 anchorPair = linkPair;
1909 anchorPair = anchorForNode(linkNode);
1910 out() << " <link rel=\"start\" href=\""
1911 << anchorPair.first << "\" />\n";
1915 if (node && !node->links().empty())
1916 out() << "<p class=\"naviNextPrevious headerNavi\">\n" << navigationLinks << "</p><p/>\n";
1919 void HtmlGenerator::generateTitle(const QString& title,
1920 const Text &subTitle,
1921 SubTitleSize subTitleSize,
1922 const Node *relative,
1925 if (!title.isEmpty())
1926 out() << "<h1 class=\"title\">" << protectEnc(title) << "</h1>\n";
1927 if (!subTitle.isEmpty()) {
1929 if (subTitleSize == SmallSubTitle)
1930 out() << " class=\"small-subtitle\">";
1932 out() << " class=\"subtitle\">";
1933 generateText(subTitle, relative, marker);
1934 out() << "</span>\n";
1938 void HtmlGenerator::generateFooter(const Node *node)
1940 if (node && !node->links().empty())
1941 out() << "<p class=\"naviNextPrevious footerNavi\">\n" << navigationLinks << "</p>\n";
1943 out() << QString(footer).replace("\\" + COMMAND_VERSION, tree_->version())
1944 << QString(address).replace("\\" + COMMAND_VERSION, tree_->version());
1946 out() << "</body>\n";
1947 out() << "</html>\n";
1950 void HtmlGenerator::generateBrief(const Node *node, CodeMarker *marker,
1951 const Node *relative)
1953 Text brief = node->doc().briefText();
1954 if (!brief.isEmpty()) {
1955 generateExtractionMark(node, BriefMark);
1957 generateText(brief, node, marker);
1959 if (!relative || node == relative)
1960 out() << " <a href=\"#";
1962 out() << " <a href=\"" << linkForNode(node, relative) << '#';
1963 out() << registerRef("details") << "\">More...</a></p>\n";
1966 generateExtractionMark(node, EndMark);
1970 void HtmlGenerator::generateIncludes(const InnerNode *inner, CodeMarker *marker)
1972 if (!inner->includes().isEmpty()) {
1973 out() << "<pre class=\"cpp\">"
1974 << trimmedTrailing(highlightedCode(indent(codeIndent,
1975 marker->markedUpIncludes(inner->includes())),
1982 Revised for the new doc format.
1983 Generates a table of contents beginning at \a node.
1985 void HtmlGenerator::generateTableOfContents(const Node *node,
1987 QList<Section>* sections)
1990 if (node->doc().hasTableOfContents())
1991 toc = node->doc().tableOfContents();
1992 if (toc.isEmpty() && !sections && (node->subType() != Node::Module))
1995 QStringList sectionNumber;
1996 int detailsBase = 0;
1998 // disable nested links in table of contents
2002 out() << "<div class=\"toc\">\n";
2003 out() << "<h3><a name=\"toc\">Contents</a></h3>\n";
2004 sectionNumber.append("1");
2007 if (node->subType() == Node::Module) {
2008 if (moduleNamespaceMap.contains(node->name())) {
2009 out() << "<li class=\"level"
2010 << sectionNumber.size()
2012 << registerRef("namespaces")
2013 << "\">Namespaces</a></li>\n";
2015 if (moduleClassMap.contains(node->name())) {
2016 out() << "<li class=\"level"
2017 << sectionNumber.size()
2019 << registerRef("classes")
2020 << "\">Classes</a></li>\n";
2022 out() << "<li class=\"level"
2023 << sectionNumber.size()
2025 << registerRef("details")
2026 << "\">Detailed Description</a></li>\n";
2027 for (int i = 0; i < toc.size(); ++i) {
2028 if (toc.at(i)->string().toInt() == 1) {
2034 else if (sections && ((node->type() == Node::Class) ||
2035 (node->type() == Node::Namespace) ||
2036 (node->subType() == Node::QmlClass))) {
2037 QList<Section>::ConstIterator s = sections->constBegin();
2038 while (s != sections->constEnd()) {
2039 if (!s->members.isEmpty() || !s->reimpMembers.isEmpty()) {
2040 out() << "<li class=\"level"
2041 << sectionNumber.size()
2043 << registerRef((*s).pluralMember)
2044 << "\">" << (*s).name
2049 out() << "<li class=\"level"
2050 << sectionNumber.size()
2052 << registerRef("details")
2053 << "\">Detailed Description</a></li>\n";
2054 for (int i = 0; i < toc.size(); ++i) {
2055 if (toc.at(i)->string().toInt() == 1) {
2062 for (int i = 0; i < toc.size(); ++i) {
2063 Atom *atom = toc.at(i);
2064 int nextLevel = atom->string().toInt() + detailsBase;
2065 if (nextLevel >= 0) {
2066 if (sectionNumber.size() < nextLevel) {
2068 sectionNumber.append("1");
2069 } while (sectionNumber.size() < nextLevel);
2072 while (sectionNumber.size() > nextLevel) {
2073 sectionNumber.removeLast();
2075 sectionNumber.last() = QString::number(sectionNumber.last().toInt() + 1);
2079 Text headingText = Text::sectionHeading(atom);
2080 QString s = headingText.toString();
2081 out() << "<li class=\"level"
2082 << sectionNumber.size()
2084 out() << "<a href=\""
2086 << Doc::canonicalTitle(s)
2088 generateAtomList(headingText.firstAtom(), node, marker, true, numAtoms);
2089 out() << "</a></li>\n";
2091 while (!sectionNumber.isEmpty()) {
2092 sectionNumber.removeLast();
2095 out() << "</div>\n";
2100 QString HtmlGenerator::generateListOfAllMemberFile(const InnerNode *inner,
2103 QList<Section> sections;
2104 QList<Section>::ConstIterator s;
2106 sections = marker->sections(inner,
2107 CodeMarker::Subpage,
2109 if (sections.isEmpty())
2112 QString fileName = fileBase(inner) + "-members." + fileExtension();
2113 beginSubPage(inner, fileName);
2114 QString title = "List of All Members for " + inner->name();
2115 generateHeader(title, inner, marker);
2116 generateTitle(title, Text(), SmallSubTitle, inner, marker);
2117 out() << "<p>This is the complete list of members for ";
2118 generateFullName(inner, 0, marker);
2119 out() << ", including inherited members.</p>\n";
2121 Section section = sections.first();
2122 generateSectionList(section, 0, marker, CodeMarker::Subpage);
2130 This function creates an html page on which are listed all
2131 the members of QML class \a qml_cn, including the inherited
2132 members. The \a marker is used for formatting stuff.
2134 QString HtmlGenerator::generateAllQmlMembersFile(const QmlClassNode* qml_cn,
2137 QList<Section> sections;
2138 QList<Section>::ConstIterator s;
2140 sections = marker->qmlSections(qml_cn,CodeMarker::Subpage);
2141 if (sections.isEmpty())
2144 QString fileName = fileBase(qml_cn) + "-members." + fileExtension();
2145 beginSubPage(qml_cn, fileName);
2146 QString title = "List of All Members for " + qml_cn->name();
2147 generateHeader(title, qml_cn, marker);
2148 generateTitle(title, Text(), SmallSubTitle, qml_cn, marker);
2149 out() << "<p>This is the complete list of members for ";
2150 generateFullName(qml_cn, 0, marker);
2151 out() << ", including inherited members.</p>\n";
2153 Section section = sections.first();
2154 generateSectionList(section, 0, marker, CodeMarker::Subpage);
2161 QString HtmlGenerator::generateLowStatusMemberFile(const InnerNode *inner,
2163 CodeMarker::Status status)
2165 QList<Section> sections = marker->sections(inner,
2166 CodeMarker::Summary,
2168 QMutableListIterator<Section> j(sections);
2169 while (j.hasNext()) {
2170 if (j.next().members.size() == 0)
2173 if (sections.isEmpty())
2181 if (status == CodeMarker::Compat) {
2182 title = "Compatibility Members for " + inner->name();
2183 fileName = fileBase(inner) + "-compat." + fileExtension();
2186 title = "Obsolete Members for " + inner->name();
2187 fileName = fileBase(inner) + "-obsolete." + fileExtension();
2190 beginSubPage(inner, fileName);
2191 generateHeader(title, inner, marker);
2192 generateTitle(title, Text(), SmallSubTitle, inner, marker);
2194 if (status == CodeMarker::Compat) {
2195 out() << "<p><b>The following class members are part of the "
2196 "Qt compatibility layer.</b> We advise against "
2197 "using them in new code.</p>\n";
2200 out() << "<p><b>The following class members are obsolete.</b> "
2201 << "They are provided to keep old source code working. "
2202 << "We strongly advise against using them in new code.</p>\n";
2205 out() << "<p><ul><li><a href=\""
2206 << linkForNode(inner, 0) << "\">"
2207 << protectEnc(inner->name())
2208 << " class reference</a></li></ul></p>\n";
2210 for (i = 0; i < sections.size(); ++i) {
2211 out() << "<h2>" << protectEnc(sections.at(i).name) << "</h2>\n";
2212 generateSectionList(sections.at(i), inner, marker, CodeMarker::Summary);
2215 sections = marker->sections(inner, CodeMarker::Detailed, status);
2216 for (i = 0; i < sections.size(); ++i) {
2217 //out() << "<hr />\n";
2218 out() << "<h2>" << protectEnc(sections.at(i).name) << "</h2>\n";
2220 NodeList::ConstIterator m = sections.at(i).members.constBegin();
2221 while (m != sections.at(i).members.constEnd()) {
2222 if ((*m)->access() != Node::Private)
2223 generateDetailedMember(*m, inner, marker);
2233 void HtmlGenerator::generateClassHierarchy(const Node *relative,
2235 const QMap<QString,const Node*> &classMap)
2237 if (classMap.isEmpty())
2241 NodeMap::ConstIterator c = classMap.constBegin();
2242 while (c != classMap.constEnd()) {
2243 const ClassNode *classe = static_cast<const ClassNode *>(*c);
2244 if (classe->baseClasses().isEmpty())
2245 topLevel.insert(classe->name(), classe);
2249 QStack<NodeMap > stack;
2250 stack.push(topLevel);
2253 while (!stack.isEmpty()) {
2254 if (stack.top().isEmpty()) {
2259 const ClassNode *child =
2260 static_cast<const ClassNode *>(*stack.top().constBegin());
2262 generateFullName(child, relative, marker);
2264 stack.top().erase(stack.top().begin());
2267 foreach (const RelatedClass &d, child->derivedClasses()) {
2268 if (d.access != Node::Private && !d.node->doc().isEmpty())
2269 newTop.insert(d.node->name(), d.node);
2271 if (!newTop.isEmpty()) {
2279 void HtmlGenerator::generateAnnotatedList(const Node *relative,
2281 const NodeMap &nodeMap,
2284 out() << "<table class=\"annotated\">\n";
2287 foreach (const QString &name, nodeMap.keys()) {
2288 const Node *node = nodeMap[name];
2290 if (node->status() == Node::Obsolete)
2293 if (allOdd || (++row % 2 == 1))
2294 out() << "<tr class=\"odd topAlign\">";
2296 out() << "<tr class=\"even topAlign\">";
2297 out() << "<td class=\"tblName\"><p>";
2298 generateFullName(node, relative, marker);
2299 out() << "</p></td>";
2301 if (!(node->type() == Node::Fake)) {
2302 Text brief = node->doc().trimmedBriefText(name);
2303 if (!brief.isEmpty()) {
2304 out() << "<td class=\"tblDescr\"><p>";
2305 generateText(brief, node, marker);
2306 out() << "</p></td>";
2310 out() << "<td class=\"tblDescr\"><p>";
2311 out() << protectEnc(node->doc().briefText().toString());
2312 out() << "</p></td>";
2316 out() << "</table>\n";
2320 This function finds the common prefix of the names of all
2321 the classes in \a classMap and then generates a compact
2322 list of the class names alphabetized on the part of the
2323 name not including the common prefix. You can tell the
2324 function to use \a comonPrefix as the common prefix, but
2325 normally you let it figure it out itself by looking at
2326 the name of the first and last classes in \a classMap.
2328 void HtmlGenerator::generateCompactList(const Node *relative,
2330 const NodeMap &classMap,
2331 bool includeAlphabet,
2332 QString commonPrefix)
2334 const int NumParagraphs = 37; // '0' to '9', 'A' to 'Z', '_'
2336 if (classMap.isEmpty())
2340 If commonPrefix is not empty, then the caller knows what
2341 the common prefix is and has passed it in, so just use that
2342 one. But if the commonPrefix is empty (it normally is), then
2343 compute a common prefix using this simple algorithm. Note we
2344 assume the prefix length is 1, i.e. we will have a single
2345 character as the common prefix.
2347 int commonPrefixLen = commonPrefix.length();
2348 if (commonPrefixLen == 0) {
2349 QVector<int> count(26);
2350 for (int i=0; i<26; ++i)
2353 NodeMap::const_iterator iter = classMap.constBegin();
2354 while (iter != classMap.constEnd()) {
2355 if (!iter.key().contains("::")) {
2356 QChar c = iter.key()[0];
2357 if ((c >= 'A') && (c <= 'Z')) {
2358 int idx = c.unicode() - QChar('A').unicode();
2366 for (int i=0; i<26; ++i) {
2367 if (count[i] > highest) {
2372 idx += QChar('A').unicode();
2374 commonPrefix = common;
2375 commonPrefixLen = 1;
2379 Divide the data into 37 paragraphs: 0, ..., 9, A, ..., Z,
2380 underscore (_). QAccel will fall in paragraph 10 (A) and
2381 QXtWidget in paragraph 33 (X). This is the only place where we
2382 assume that NumParagraphs is 37. Each paragraph is a NodeMap.
2384 NodeMap paragraph[NumParagraphs+1];
2385 QString paragraphName[NumParagraphs+1];
2386 QSet<char> usedParagraphNames;
2388 NodeMap::ConstIterator c = classMap.constBegin();
2389 while (c != classMap.constEnd()) {
2390 QStringList pieces = c.key().split("::");
2392 int idx = commonPrefixLen;
2393 if (!pieces.last().startsWith(commonPrefix))
2395 if (pieces.size() == 1)
2396 key = pieces.last().mid(idx).toLower();
2398 key = pieces.last().toLower();
2400 int paragraphNr = NumParagraphs - 1;
2402 if (key[0].digitValue() != -1) {
2403 paragraphNr = key[0].digitValue();
2405 else if (key[0] >= QLatin1Char('a') && key[0] <= QLatin1Char('z')) {
2406 paragraphNr = 10 + key[0].unicode() - 'a';
2409 paragraphName[paragraphNr] = key[0].toUpper();
2410 usedParagraphNames.insert(key[0].toLower().cell());
2411 paragraph[paragraphNr].insert(key, c.value());
2416 Each paragraph j has a size: paragraph[j].count(). In the
2417 discussion, we will assume paragraphs 0 to 5 will have sizes
2420 We now want to compute the paragraph offset. Paragraphs 0 to 6
2421 start at offsets 0, 3, 4, 8, 9, 14, 23.
2423 int paragraphOffset[NumParagraphs + 1]; // 37 + 1
2424 paragraphOffset[0] = 0;
2425 for (int i=0; i<NumParagraphs; i++) // i = 0..36
2426 paragraphOffset[i+1] = paragraphOffset[i] + paragraph[i].count();
2429 Output the alphabet as a row of links.
2431 if (includeAlphabet) {
2432 out() << "<p class=\"centerAlign functionIndex\"><b>";
2433 for (int i = 0; i < 26; i++) {
2435 if (usedParagraphNames.contains(char('a' + i)))
2436 out() << QString("<a href=\"#%1\">%2</a> ").arg(ch).arg(ch.toUpper());
2438 out() << "</b></p>\n";
2442 Output a <div> element to contain all the <dl> elements.
2444 out() << "<div class=\"flowListDiv\">\n";
2448 int curParOffset = 0;
2450 for (int i=0; i<classMap.count(); i++) {
2451 while ((curParNr < NumParagraphs) &&
2452 (curParOffset == paragraph[curParNr].count())) {
2458 Starting a new paragraph means starting a new <dl>.
2460 if (curParOffset == 0) {
2463 if (++numTableRows % 2 == 1)
2464 out() << "<dl class=\"flowList odd\">";
2466 out() << "<dl class=\"flowList even\">";
2467 out() << "<dt class=\"alphaChar\">";
2468 if (includeAlphabet) {
2469 QChar c = paragraphName[curParNr][0].toLower();
2470 out() << QString("<a name=\"%1\"></a>").arg(c);
2473 << paragraphName[curParNr]
2479 Output a <dd> for the current offset in the current paragraph.
2482 if ((curParNr < NumParagraphs) &&
2483 !paragraphName[curParNr].isEmpty()) {
2484 NodeMap::Iterator it;
2485 it = paragraph[curParNr].begin();
2486 for (int i=0; i<curParOffset; i++)
2490 Previously, we used generateFullName() for this, but we
2491 require some special formatting.
2493 out() << "<a href=\"" << linkForNode(it.value(), relative) << "\">";
2496 if (it.value()->subType() == Node::QmlClass)
2497 pieces << it.value()->name();
2499 pieces = fullName(it.value(), relative, marker).split("::");
2500 out() << protectEnc(pieces.last());
2502 if (pieces.size() > 1) {
2504 generateFullName(it.value()->parent(), relative, marker);
2511 if (classMap.count() > 0)
2514 out() << "</div>\n";
2517 void HtmlGenerator::generateFunctionIndex(const Node *relative,
2520 out() << "<p class=\"centerAlign functionIndex\"><b>";
2521 for (int i = 0; i < 26; i++) {
2523 out() << QString("<a href=\"#%1\">%2</a> ").arg(ch).arg(ch.toUpper());
2525 out() << "</b></p>\n";
2527 char nextLetter = 'a';
2533 QMap<QString, NodeMap >::ConstIterator f = funcIndex.constBegin();
2534 while (f != funcIndex.constEnd()) {
2540 out() << protectEnc(f.key()) << ':';
2542 currentLetter = f.key()[0].unicode();
2543 while (islower(currentLetter) && currentLetter >= nextLetter) {
2544 out() << QString("<a name=\"%1\"></a>").arg(nextLetter);
2548 NodeMap::ConstIterator s = (*f).constBegin();
2549 while (s != (*f).constEnd()) {
2551 generateFullName((*s)->parent(), relative, marker, *s);
2567 void HtmlGenerator::generateLegaleseList(const Node *relative,
2570 QMap<Text, const Node *>::ConstIterator it = legaleseTexts.constBegin();
2571 while (it != legaleseTexts.constEnd()) {
2572 Text text = it.key();
2573 //out() << "<hr />\n";
2574 generateText(text, relative, marker);
2578 generateFullName(it.value(), relative, marker);
2581 } while (it != legaleseTexts.constEnd() && it.key() == text);
2586 void HtmlGenerator::generateQmlItem(const Node *node,
2587 const Node *relative,
2591 QString marked = marker->markedUpQmlItem(node,summary);
2592 QRegExp templateTag("(<[^@>]*>)");
2593 if (marked.indexOf(templateTag) != -1) {
2594 QString contents = protectEnc(marked.mid(templateTag.pos(1),
2595 templateTag.cap(1).length()));
2596 marked.replace(templateTag.pos(1), templateTag.cap(1).length(),
2599 marked.replace(QRegExp("<@param>([a-z]+)_([1-9n])</@param>"),
2600 "<i>\\1<sub>\\2</sub></i>");
2601 marked.replace("<@param>", "<i>");
2602 marked.replace("</@param>", "</i>");
2605 marked.replace("@name>", "b>");
2607 marked.replace("<@extra>", "<tt>");
2608 marked.replace("</@extra>", "</tt>");
2611 marked.remove("<@type>");
2612 marked.remove("</@type>");
2614 out() << highlightedCode(marked, marker, relative, false, node);
2617 void HtmlGenerator::generateOverviewList(const Node *relative, CodeMarker * /* marker */)
2619 QMap<const FakeNode *, QMap<QString, FakeNode *> > fakeNodeMap;
2620 QMap<QString, const FakeNode *> groupTitlesMap;
2621 QMap<QString, FakeNode *> uncategorizedNodeMap;
2622 QRegExp singleDigit("\\b([0-9])\\b");
2624 const NodeList children = tree_->root()->childNodes();
2625 foreach (Node *child, children) {
2626 if (child->type() == Node::Fake && child != relative) {
2627 FakeNode *fakeNode = static_cast<FakeNode *>(child);
2629 // Check whether the page is part of a group or is the group
2632 bool isGroupPage = false;
2633 if (fakeNode->doc().metaCommandsUsed().contains("group")) {
2634 group = fakeNode->doc().metaCommandArgs("group")[0].first;
2638 // there are too many examples; they would clutter the list
2639 if (fakeNode->subType() == Node::Example)
2642 // not interested either in individual (Qt Designer etc.) manual chapters
2643 if (fakeNode->links().contains(Node::ContentsLink))
2646 // Discard external nodes.
2647 if (fakeNode->subType() == Node::ExternalPage)
2650 QString sortKey = fakeNode->fullTitle().toLower();
2651 if (sortKey.startsWith("the "))
2652 sortKey.remove(0, 4);
2653 sortKey.replace(singleDigit, "0\\1");
2655 if (!group.isEmpty()) {
2657 // If we encounter a group definition page, we add all
2658 // the pages in that group to the list for that group.
2659 foreach (Node *member, fakeNode->groupMembers()) {
2660 if (member->type() != Node::Fake)
2662 FakeNode *page = static_cast<FakeNode *>(member);
2664 QString sortKey = page->fullTitle().toLower();
2665 if (sortKey.startsWith("the "))
2666 sortKey.remove(0, 4);
2667 sortKey.replace(singleDigit, "0\\1");
2668 fakeNodeMap[const_cast<const FakeNode *>(fakeNode)].insert(sortKey, page);
2669 groupTitlesMap[fakeNode->fullTitle()] = const_cast<const FakeNode *>(fakeNode);
2673 else if (!isGroupPage) {
2674 // If we encounter a page that belongs to a group then
2675 // we add that page to the list for that group.
2676 const FakeNode* gn = tree_->findGroupNode(QStringList(group));
2678 fakeNodeMap[gn].insert(sortKey, fakeNode);
2684 // We now list all the pages found that belong to groups.
2685 // If only certain pages were found for a group, but the definition page
2686 // for that group wasn't listed, the list of pages will be intentionally
2687 // incomplete. However, if the group definition page was listed, all the
2688 // pages in that group are listed for completeness.
2690 if (!fakeNodeMap.isEmpty()) {
2691 foreach (const QString &groupTitle, groupTitlesMap.keys()) {
2692 const FakeNode *groupNode = groupTitlesMap[groupTitle];
2693 out() << QString("<h3><a href=\"%1\">%2</a></h3>\n").arg(
2694 linkForNode(groupNode, relative)).arg(
2695 protectEnc(groupNode->fullTitle()));
2697 if (fakeNodeMap[groupNode].count() == 0)
2702 foreach (const FakeNode *fakeNode, fakeNodeMap[groupNode]) {
2703 QString title = fakeNode->fullTitle();
2704 if (title.startsWith("The "))
2706 out() << "<li><a href=\"" << linkForNode(fakeNode, relative) << "\">"
2707 << protectEnc(title) << "</a></li>\n";
2713 if (!uncategorizedNodeMap.isEmpty()) {
2714 out() << QString("<h3>Miscellaneous</h3>\n");
2716 foreach (const FakeNode *fakeNode, uncategorizedNodeMap) {
2717 QString title = fakeNode->fullTitle();
2718 if (title.startsWith("The "))
2720 out() << "<li><a href=\"" << linkForNode(fakeNode, relative) << "\">"
2721 << protectEnc(title) << "</a></li>\n";
2727 void HtmlGenerator::generateSection(const NodeList& nl,
2728 const Node *relative,
2730 CodeMarker::SynopsisStyle style)
2732 bool alignNames = true;
2733 if (!nl.isEmpty()) {
2734 bool twoColumn = false;
2735 if (style == CodeMarker::Subpage) {
2737 twoColumn = (nl.count() >= 16);
2739 else if (nl.first()->type() == Node::Property) {
2740 twoColumn = (nl.count() >= 5);
2744 out() << "<table class=\"alignedsummary\">\n";
2748 out() << "<table class=\"propsummary\">\n"
2749 << "<tr><td class=\"topAlign\">";
2754 NodeList::ConstIterator m = nl.constBegin();
2755 while (m != nl.constEnd()) {
2756 if ((*m)->access() == Node::Private) {
2762 out() << "<tr><td class=\"memItemLeft rightAlign topAlign\"> ";
2765 if (twoColumn && i == (int) (nl.count() + 1) / 2)
2766 out() << "</ul></td><td class=\"topAlign\"><ul>\n";
2767 out() << "<li class=\"fn\">";
2770 generateSynopsis(*m, relative, marker, style, alignNames);
2772 out() << "</td></tr>\n";
2779 out() << "</table>\n";
2783 out() << "</td></tr>\n</table>\n";
2788 void HtmlGenerator::generateSectionList(const Section& section,
2789 const Node *relative,
2791 CodeMarker::SynopsisStyle style)
2793 bool alignNames = true;
2794 if (!section.members.isEmpty()) {
2795 bool twoColumn = false;
2796 if (style == CodeMarker::Subpage) {
2798 twoColumn = (section.members.count() >= 16);
2800 else if (section.members.first()->type() == Node::Property) {
2801 twoColumn = (section.members.count() >= 5);
2805 out() << "<table class=\"alignedsummary\">\n";
2809 out() << "<table class=\"propsummary\">\n"
2810 << "<tr><td class=\"topAlign\">";
2815 NodeList::ConstIterator m = section.members.constBegin();
2816 while (m != section.members.constEnd()) {
2817 if ((*m)->access() == Node::Private) {
2823 out() << "<tr><td class=\"memItemLeft topAlign rightAlign\"> ";
2826 if (twoColumn && i == (int) (section.members.count() + 1) / 2)
2827 out() << "</ul></td><td class=\"topAlign\"><ul>\n";
2828 out() << "<li class=\"fn\">";
2832 if (!section.keys.isEmpty()) {
2833 prefix = section.keys.at(i).mid(1);
2834 prefix = prefix.left(section.keys.at(i).indexOf("::")+1);
2836 generateSynopsis(*m, relative, marker, style, alignNames, &prefix);
2838 out() << "</td></tr>\n";
2845 out() << "</table>\n";
2849 out() << "</td></tr>\n</table>\n";
2853 if (style == CodeMarker::Summary && !section.inherited.isEmpty()) {
2855 generateSectionInheritedList(section, relative, marker);
2860 void HtmlGenerator::generateSectionInheritedList(const Section& section,
2861 const Node *relative,
2864 QList<QPair<InnerNode *, int> >::ConstIterator p = section.inherited.constBegin();
2865 while (p != section.inherited.constEnd()) {
2866 out() << "<li class=\"fn\">";
2867 out() << (*p).second << ' ';
2868 if ((*p).second == 1) {
2869 out() << section.singularMember;
2872 out() << section.pluralMember;
2874 out() << " inherited from <a href=\"" << fileName((*p).first)
2875 << '#' << HtmlGenerator::cleanRef(section.name.toLower()) << "\">"
2876 << protectEnc(marker->plainFullName((*p).first, relative))
2882 void HtmlGenerator::generateSynopsis(const Node *node,
2883 const Node *relative,
2885 CodeMarker::SynopsisStyle style,
2887 const QString* prefix)
2889 QString marked = marker->markedUpSynopsis(node, relative, style);
2892 marked.prepend(*prefix);
2893 QRegExp templateTag("(<[^@>]*>)");
2894 if (marked.indexOf(templateTag) != -1) {
2895 QString contents = protectEnc(marked.mid(templateTag.pos(1),
2896 templateTag.cap(1).length()));
2897 marked.replace(templateTag.pos(1), templateTag.cap(1).length(),
2900 marked.replace(QRegExp("<@param>([a-z]+)_([1-9n])</@param>"),
2901 "<i>\\1<sub>\\2</sub></i>");
2902 marked.replace("<@param>", "<i> ");
2903 marked.replace("</@param>", "</i>");
2905 if (style == CodeMarker::Summary) {
2906 marked.remove("<@name>"); // was "<b>"
2907 marked.remove("</@name>"); // was "</b>"
2910 if (style == CodeMarker::Subpage) {
2911 QRegExp extraRegExp("<@extra>.*</@extra>");
2912 extraRegExp.setMinimal(true);
2913 marked.remove(extraRegExp);
2915 marked.replace("<@extra>", "<tt>");
2916 marked.replace("</@extra>", "</tt>");
2919 if (style != CodeMarker::Detailed) {
2920 marked.remove("<@type>");
2921 marked.remove("</@type>");
2924 out() << highlightedCode(marked, marker, relative, alignNames);
2927 QString HtmlGenerator::highlightedCode(const QString& markedCode,
2929 const Node* relative,
2933 QString src = markedCode;
2938 const QChar charLangle = '<';
2939 const QChar charAt = '@';
2941 static const QString typeTag("type");
2942 static const QString headerTag("headerfile");
2943 static const QString funcTag("func");
2944 static const QString linkTag("link");
2946 // replace all <@link> tags: "(<@link node=\"([^\"]+)\">).*(</@link>)"
2948 for (int i = 0, srcSize = src.size(); i < srcSize;) {
2949 if (src.at(i) == charLangle && src.at(i + 1) == charAt) {
2950 if (alignNames && !done) {
2951 html += "</td><td class=\"memItemRight bottomAlign\">";
2955 if (parseArg(src, linkTag, &i, srcSize, &arg, &par1)) {
2957 const Node* n = CodeMarker::nodeForString(par1.toString());
2958 QString link = linkForNode(n, relative);
2959 addLink(link, arg, &html);
2968 html += src.at(i++);
2972 // replace all <@func> tags: "(<@func target=\"([^\"]*)\">)(.*)(</@func>)"
2975 for (int i = 0, srcSize = src.size(); i < srcSize;) {
2976 if (src.at(i) == charLangle && src.at(i + 1) == charAt) {
2978 if (parseArg(src, funcTag, &i, srcSize, &arg, &par1)) {
2980 const Node* n = marker->resolveTarget(par1.toString(),
2983 QString link = linkForNode(n, relative);
2984 addLink(link, arg, &html);
2985 par1 = QStringRef();
2993 html += src.at(i++);
2997 // replace all "(<@(type|headerfile|func)(?: +[^>]*)?>)(.*)(</@\\2>)" tags
3001 for (int i=0, srcSize=src.size(); i<srcSize;) {
3002 if (src.at(i) == charLangle && src.at(i+1) == charAt) {
3004 bool handled = false;
3005 if (parseArg(src, typeTag, &i, srcSize, &arg, &par1)) {
3006 par1 = QStringRef();
3007 const Node* n = marker->resolveTarget(arg.toString(), tree_, relative, self);
3008 html += QLatin1String("<span class=\"type\">");
3009 if (n && n->subType() == Node::QmlBasicType) {
3010 if (relative && relative->subType() == Node::QmlClass)
3011 addLink(linkForNode(n,relative), arg, &html);
3013 html += arg.toString();
3016 addLink(linkForNode(n,relative), arg, &html);
3017 html += QLatin1String("</span>");
3020 else if (parseArg(src, headerTag, &i, srcSize, &arg, &par1)) {
3021 par1 = QStringRef();
3022 const Node* n = marker->resolveTarget(arg.toString(), tree_, relative);
3023 addLink(linkForNode(n,relative), arg, &html);
3026 else if (parseArg(src, funcTag, &i, srcSize, &arg, &par1)) {
3027 par1 = QStringRef();
3028 const Node* n = marker->resolveTarget(arg.toString(), tree_, relative);
3029 addLink(linkForNode(n,relative), arg, &html);
3039 html += src.at(i++);
3044 // "<@comment>" -> "<span class=\"comment\">";
3045 // "<@preprocessor>" -> "<span class=\"preprocessor\">";
3046 // "<@string>" -> "<span class=\"string\">";
3047 // "<@char>" -> "<span class=\"char\">";
3048 // "<@number>" -> "<span class=\"number\">";
3049 // "<@op>" -> "<span class=\"operator\">";
3050 // "<@type>" -> "<span class=\"type\">";
3051 // "<@name>" -> "<span class=\"name\">";
3052 // "<@keyword>" -> "<span class=\"keyword\">";
3053 // "</@(?:comment|preprocessor|string|char|number|op|type|name|keyword)>" -> "</span>"
3056 static const QString spanTags[] = {
3057 "<@comment>", "<span class=\"comment\">",
3058 "<@preprocessor>", "<span class=\"preprocessor\">",
3059 "<@string>", "<span class=\"string\">",
3060 "<@char>", "<span class=\"char\">",
3061 "<@number>", "<span class=\"number\">",
3062 "<@op>", "<span class=\"operator\">",
3063 "<@type>", "<span class=\"type\">",
3064 "<@name>", "<span class=\"name\">",
3065 "<@keyword>", "<span class=\"keyword\">",
3066 "</@comment>", "</span>",
3067 "</@preprocessor>", "</span>",
3068 "</@string>", "</span>",
3069 "</@char>", "</span>",
3070 "</@number>", "</span>",
3071 "</@op>", "</span>",
3072 "</@type>", "</span>",
3073 "</@name>", "</span>",
3074 "</@keyword>", "</span>",
3076 // Update the upper bound of k in the following code to match the length
3077 // of the above array.
3078 for (int i = 0, n = src.size(); i < n;) {
3079 if (src.at(i) == charLangle) {
3080 bool handled = false;
3081 for (int k = 0; k != 18; ++k) {
3082 const QString & tag = spanTags[2 * k];
3083 if (tag == QStringRef(&src, i, tag.length())) {
3084 html += spanTags[2 * k + 1];
3092 if (src.at(i) == charAt ||
3093 (src.at(i) == QLatin1Char('/') && src.at(i + 1) == charAt)) {
3094 // drop 'our' unknown tags (the ones still containing '@')
3095 while (i < n && src.at(i) != QLatin1Char('>'))
3100 // retain all others
3113 void HtmlGenerator::generateLink(const Atom* atom,
3114 const Node* /* relative */,
3117 static QRegExp camelCase("[A-Z][A-Z][a-z]|[a-z][A-Z0-9]|_");
3119 if (funcLeftParen.indexIn(atom->string()) != -1 && marker->recognizeLanguage("Cpp")) {
3120 // hack for C++: move () outside of link
3121 int k = funcLeftParen.pos(1);
3122 out() << protectEnc(atom->string().left(k));
3123 if (link.isEmpty()) {
3124 if (showBrokenLinks)
3130 out() << protectEnc(atom->string().mid(k));
3132 out() << protectEnc(atom->string());
3136 QString HtmlGenerator::cleanRef(const QString& ref)
3143 clean.reserve(ref.size() + 20);
3144 const QChar c = ref[0];
3145 const uint u = c.unicode();
3147 if ((u >= 'a' && u <= 'z') ||
3148 (u >= 'A' && u <= 'Z') ||
3149 (u >= '0' && u <= '9')) {
3151 } else if (u == '~') {
3153 } else if (u == '_') {
3154 clean += "underscore.";
3156 clean += QLatin1Char('A');
3159 for (int i = 1; i < (int) ref.length(); i++) {
3160 const QChar c = ref[i];
3161 const uint u = c.unicode();
3162 if ((u >= 'a' && u <= 'z') ||
3163 (u >= 'A' && u <= 'Z') ||
3164 (u >= '0' && u <= '9') || u == '-' ||
3165 u == '_' || u == ':' || u == '.') {
3167 } else if (c.isSpace()) {
3168 clean += QLatin1Char('-');
3169 } else if (u == '!') {
3171 } else if (u == '&') {
3173 } else if (u == '<') {
3175 } else if (u == '=') {
3177 } else if (u == '>') {
3179 } else if (u == '#') {
3180 clean += QLatin1Char('#');
3182 clean += QLatin1Char('-');
3183 clean += QString::number((int)u, 16);
3189 QString HtmlGenerator::registerRef(const QString& ref)
3191 QString clean = HtmlGenerator::cleanRef(ref);
3194 QString& prevRef = refMap[clean.toLower()];
3195 if (prevRef.isEmpty()) {
3198 } else if (prevRef == ref) {
3201 clean += QLatin1Char('x');
3206 QString HtmlGenerator::protectEnc(const QString &string)
3208 return protect(string, outputEncoding);
3211 QString HtmlGenerator::protect(const QString &string, const QString &outputEncoding)
3214 if (html.isEmpty()) { \
3221 int n = string.length();
3223 for (int i = 0; i < n; ++i) {
3224 QChar ch = string.at(i);
3226 if (ch == QLatin1Char('&')) {
3228 } else if (ch == QLatin1Char('<')) {
3230 } else if (ch == QLatin1Char('>')) {
3232 } else if (ch == QLatin1Char('"')) {
3234 } else if ((outputEncoding == "ISO-8859-1" && ch.unicode() > 0x007F)
3235 || (ch == QLatin1Char('*') && i + 1 < n && string.at(i) == QLatin1Char('/'))
3236 || (ch == QLatin1Char('.') && i > 2 && string.at(i - 2) == QLatin1Char('.'))) {
3237 // we escape '*/' and the last dot in 'e.g.' and 'i.e.' for the Javadoc generator
3239 html += QString::number(ch.unicode(), 16);
3240 html += QLatin1Char(';');
3242 if (!html.isEmpty())
3247 if (!html.isEmpty())
3254 QString HtmlGenerator::fileBase(const Node *node) const
3258 result = Generator::fileBase(node);
3260 if (!node->isInnerNode()) {
3261 switch (node->status()) {
3263 result += "-compat";
3265 case Node::Obsolete:
3266 result += "-obsolete";
3275 QString HtmlGenerator::fileName(const Node *node)
3277 if (node->type() == Node::Fake) {
3278 if (static_cast<const FakeNode *>(node)->subType() == Node::ExternalPage)
3279 return node->name();
3280 if (static_cast<const FakeNode *>(node)->subType() == Node::Image)
3281 return node->name();
3283 return Generator::fileName(node);
3286 QString HtmlGenerator::refForNode(const Node *node)
3288 const FunctionNode *func;
3289 const TypedefNode *typedeffe;
3292 switch (node->type()) {
3293 case Node::Namespace:
3298 ref = node->name() + "-enum";
3301 typedeffe = static_cast<const TypedefNode *>(node);
3302 if (typedeffe->associatedEnum()) {
3303 return refForNode(typedeffe->associatedEnum());
3306 ref = node->name() + "-typedef";
3309 case Node::Function:
3310 func = static_cast<const FunctionNode *>(node);
3311 if (func->associatedProperty()) {
3312 return refForNode(func->associatedProperty());
3316 if (func->overloadNumber() != 1)
3317 ref += QLatin1Char('-') + QString::number(func->overloadNumber());
3321 if (node->subType() != Node::QmlPropertyGroup)
3323 case Node::QmlProperty:
3324 case Node::Property:
3325 ref = node->name() + "-prop";
3327 case Node::QmlSignal:
3328 ref = node->name() + "-signal";
3330 case Node::QmlSignalHandler:
3331 ref = node->name() + "-signal-handler";
3333 case Node::QmlMethod:
3334 func = static_cast<const FunctionNode *>(node);
3335 ref = func->name() + "-method";
3336 if (func->overloadNumber() != 1)
3337 ref += QLatin1Char('-') + QString::number(func->overloadNumber());
3339 case Node::Variable:
3340 ref = node->name() + "-var";
3343 return registerRef(ref);
3346 #define DEBUG_ABSTRACT 0
3349 Construct the link string for the \a node and return it.
3350 The \a relative node is use to decide the link we are
3351 generating is in the same file as the target. Note the
3352 relative node can be 0, which pretty much guarantees
3353 that the link and the target aren't in the same file.
3355 QString HtmlGenerator::linkForNode(const Node *node, const Node *relative)
3357 if (node == 0 || node == relative)
3359 if (!node->url().isEmpty())
3361 if (fileBase(node).isEmpty())
3363 if (node->access() == Node::Private)
3366 QString fn = fileName(node);
3367 if (node && relative && node->parent() != relative) {
3368 if (node->parent()->subType() == Node::QmlClass && relative->subType() == Node::QmlClass) {
3369 if (node->parent()->isAbstract()) {
3371 This is a bit of a hack. What we discover with
3372 the three 'if' statements immediately above,
3373 is that node's parent is marked \qmlabstract
3374 but the link appears in a qdoc comment for a
3375 subclass of the node's parent. This means the
3376 link should refer to the file for the relative
3377 node, not the file for node.
3379 fn = fileName(relative);
3381 qDebug() << "ABSTRACT:" << node->parent()->name()
3382 << node->name() << relative->name()
3383 << node->parent()->type() << node->parent()->subType()
3384 << relative->type() << relative->subType() << outFileName();
3391 if (!node->isInnerNode() || node->subType() == Node::QmlPropertyGroup) {
3392 QString ref = refForNode(node);
3393 if (relative && fn == fileName(relative) && ref == refForNode(relative))
3396 link += QLatin1Char('#');
3400 If the output is going to subdirectories, then if the
3401 two nodes will be output to different directories, then
3402 the link must go up to the parent directory and then
3403 back down into the other subdirectory.
3405 if (node && relative && (node != relative)) {
3406 if (node->outputSubdirectory() != relative->outputSubdirectory())
3407 link.prepend(QString("../" + node->outputSubdirectory() + QLatin1Char('/')));
3412 QString HtmlGenerator::refForAtom(Atom *atom, const Node * /* node */)
3414 if (atom->type() == Atom::SectionLeft) {
3415 return Doc::canonicalTitle(Text::sectionHeading(atom).toString());
3417 else if (atom->type() == Atom::Target) {
3418 return Doc::canonicalTitle(atom->string());
3425 void HtmlGenerator::generateFullName(const Node *apparentNode,
3426 const Node *relative,
3428 const Node *actualNode)
3430 if (actualNode == 0)
3431 actualNode = apparentNode;
3432 out() << "<a href=\"" << linkForNode(actualNode, relative);
3433 if (true || relative == 0 || relative->status() != actualNode->status()) {
3434 switch (actualNode->status()) {
3435 case Node::Obsolete:
3436 out() << "\" class=\"obsolete";
3439 out() << "\" class=\"compat";
3446 out() << protectEnc(fullName(apparentNode, relative, marker));
3450 void HtmlGenerator::generateDetailedMember(const Node *node,
3451 const InnerNode *relative,
3454 const EnumNode *enume;
3456 #ifdef GENERATE_MAC_REFS
3457 generateMacRef(node, marker);
3459 generateExtractionMark(node, MemberMark);
3460 if (node->type() == Node::Enum
3461 && (enume = static_cast<const EnumNode *>(node))->flagsType()) {
3462 #ifdef GENERATE_MAC_REFS
3463 generateMacRef(enume->flagsType(), marker);
3465 out() << "<h3 class=\"flags\">";
3466 out() << "<a name=\"" + refForNode(node) + "\"></a>";
3467 generateSynopsis(enume, relative, marker, CodeMarker::Detailed);
3469 generateSynopsis(enume->flagsType(),
3472 CodeMarker::Detailed);
3476 out() << "<h3 class=\"fn\">";
3477 out() << "<a name=\"" + refForNode(node) + "\"></a>";
3478 generateSynopsis(node, relative, marker, CodeMarker::Detailed);
3479 out() << "</h3>" << divNavTop << '\n';
3482 generateStatus(node, marker);
3483 generateBody(node, marker);
3484 generateThreadSafeness(node, marker);
3485 generateSince(node, marker);
3487 if (node->type() == Node::Property) {
3488 const PropertyNode *property = static_cast<const PropertyNode *>(node);
3491 section.members += property->getters();
3492 section.members += property->setters();
3493 section.members += property->resetters();
3495 if (!section.members.isEmpty()) {
3496 out() << "<p><b>Access functions:</b></p>\n";
3497 generateSectionList(section, node, marker, CodeMarker::Accessors);
3501 notifiers.members += property->notifiers();
3503 if (!notifiers.members.isEmpty()) {
3504 out() << "<p><b>Notifier signal:</b></p>\n";
3505 //out() << "<p>This signal is emitted when the property value is changed.</p>\n";
3506 generateSectionList(notifiers, node, marker, CodeMarker::Accessors);
3509 else if (node->type() == Node::Enum) {
3510 const EnumNode *enume = static_cast<const EnumNode *>(node);
3511 if (enume->flagsType()) {
3512 out() << "<p>The " << protectEnc(enume->flagsType()->name())
3513 << " type is a typedef for "
3514 << "<a href=\"qflags.html\">QFlags</a><"
3515 << protectEnc(enume->name())
3516 << ">. It stores an OR combination of "
3517 << protectEnc(enume->name())
3518 << " values.</p>\n";
3521 generateAlsoList(node, marker);
3522 generateExtractionMark(node, EndMark);
3525 void HtmlGenerator::findAllClasses(const InnerNode *node)
3527 NodeList::const_iterator c = node->childNodes().constBegin();
3528 while (c != node->childNodes().constEnd()) {
3529 if ((*c)->access() != Node::Private && (*c)->url().isEmpty()) {
3530 if ((*c)->type() == Node::Class && !(*c)->doc().isEmpty()) {
3531 QString className = (*c)->name();
3532 if ((*c)->parent() &&
3533 (*c)->parent()->type() == Node::Namespace &&
3534 !(*c)->parent()->name().isEmpty())
3535 className = (*c)->parent()->name()+"::"+className;
3537 if (!(static_cast<const ClassNode *>(*c))->hideFromMainList()) {
3538 if ((*c)->status() == Node::Compat) {
3539 compatClasses.insert(className, *c);
3541 else if ((*c)->status() == Node::Obsolete) {
3542 obsoleteClasses.insert(className, *c);
3545 nonCompatClasses.insert(className, *c);
3546 if ((*c)->status() == Node::Main)
3547 mainClasses.insert(className, *c);
3551 QString moduleName = (*c)->moduleName();
3552 if (moduleName == "Qt3SupportLight") {
3553 moduleClassMap[moduleName].insert((*c)->name(), *c);
3554 moduleName = "Qt3Support";
3556 if (!moduleName.isEmpty())
3557 moduleClassMap[moduleName].insert((*c)->name(), *c);
3559 QString serviceName =
3560 (static_cast<const ClassNode *>(*c))->serviceName();
3561 if (!serviceName.isEmpty())
3562 serviceClasses.insert(serviceName, *c);
3564 else if ((*c)->type() == Node::Fake &&
3565 (*c)->subType() == Node::QmlClass &&
3566 !(*c)->doc().isEmpty()) {
3567 QString qmlClassName = (*c)->name();
3569 Remove the "QML:" prefix, if present.
3570 It shouldn't be present anymore.
3572 if (qmlClassName.startsWith(QLatin1String("QML:")))
3573 qmlClasses.insert(qmlClassName.mid(4),*c);
3575 qmlClasses.insert(qmlClassName,*c);
3577 else if ((*c)->isInnerNode()) {
3578 findAllClasses(static_cast<InnerNode *>(*c));
3585 void HtmlGenerator::findAllFunctions(const InnerNode *node)
3587 NodeList::ConstIterator c = node->childNodes().constBegin();
3588 while (c != node->childNodes().constEnd()) {
3589 if ((*c)->access() != Node::Private) {
3590 if ((*c)->isInnerNode() && (*c)->url().isEmpty()) {
3591 findAllFunctions(static_cast<const InnerNode *>(*c));
3593 else if ((*c)->type() == Node::Function) {
3594 const FunctionNode *func = static_cast<const FunctionNode *>(*c);
3595 if ((func->status() > Node::Obsolete) &&
3596 !func->isInternal() &&
3597 (func->metaness() != FunctionNode::Ctor) &&
3598 (func->metaness() != FunctionNode::Dtor)) {
3599 funcIndex[(*c)->name()].insert((*c)->parent()->fullDocumentName(), *c);
3607 void HtmlGenerator::findAllLegaleseTexts(const InnerNode *node)
3609 NodeList::ConstIterator c = node->childNodes().constBegin();
3610 while (c != node->childNodes().constEnd()) {
3611 if ((*c)->access() != Node::Private) {
3612 if (!(*c)->doc().legaleseText().isEmpty())
3613 legaleseTexts.insertMulti((*c)->doc().legaleseText(), *c);
3614 if ((*c)->isInnerNode())
3615 findAllLegaleseTexts(static_cast<const InnerNode *>(*c));
3621 void HtmlGenerator::findAllNamespaces(const InnerNode *node)
3623 NodeList::ConstIterator c = node->childNodes().constBegin();
3624 while (c != node->childNodes().constEnd()) {
3625 if ((*c)->access() != Node::Private) {
3626 if ((*c)->isInnerNode() && (*c)->url().isEmpty()) {
3627 findAllNamespaces(static_cast<const InnerNode *>(*c));
3628 if ((*c)->type() == Node::Namespace) {
3629 const NamespaceNode *nspace = static_cast<const NamespaceNode *>(*c);
3630 // Ensure that the namespace's name is not empty (the root
3631 // namespace has no name).
3632 if (!nspace->name().isEmpty()) {
3633 namespaceIndex.insert(nspace->name(), *c);
3634 QString moduleName = (*c)->moduleName();
3635 if (moduleName == "Qt3SupportLight") {
3636 moduleNamespaceMap[moduleName].insert((*c)->name(), *c);
3637 moduleName = "Qt3Support";
3639 if (!moduleName.isEmpty())
3640 moduleNamespaceMap[moduleName].insert((*c)->name(), *c);
3649 int HtmlGenerator::hOffset(const Node *node)
3651 switch (node->type()) {
3652 case Node::Namespace:
3659 case Node::Function:
3660 case Node::Property:
3666 bool HtmlGenerator::isThreeColumnEnumValueTable(const Atom *atom)
3668 while (atom != 0 && !(atom->type() == Atom::ListRight && atom->string() == ATOM_LIST_VALUE)) {
3669 if (atom->type() == Atom::ListItemLeft && !matchAhead(atom, Atom::ListItemRight))
3671 atom = atom->next();
3676 const Node *HtmlGenerator::findNodeForTarget(const QString &target,
3677 const Node *relative,
3681 const Node *node = 0;
3683 if (target.isEmpty()) {
3686 else if (target.endsWith(".html")) {
3687 node = tree_->root()->findChildNodeByNameAndType(target, Node::Fake);
3690 node = marker->resolveTarget(target, tree_, relative);
3692 node = tree_->findFakeNodeByTitle(target, relative);
3694 if (!node && atom) {
3695 node = tree_->findUnambiguousTarget(target, *const_cast<Atom**>(&atom), relative);
3700 relative->doc().location().warning(tr("Cannot link to '%1'").arg(target));
3704 const QPair<QString,QString> HtmlGenerator::anchorForNode(const Node *node)
3706 QPair<QString,QString> anchorPair;
3708 anchorPair.first = Generator::fileName(node);
3709 if (node->type() == Node::Fake) {
3710 const FakeNode *fakeNode = static_cast<const FakeNode*>(node);
3711 anchorPair.second = fakeNode->title();
3717 QString HtmlGenerator::getLink(const Atom *atom,
3718 const Node *relative,
3724 inObsoleteLink = false;
3726 if (atom->string().contains(QLatin1Char(':')) &&
3727 (atom->string().startsWith("file:")
3728 || atom->string().startsWith("http:")
3729 || atom->string().startsWith("https:")
3730 || atom->string().startsWith("ftp:")
3731 || atom->string().startsWith("mailto:"))) {
3733 link = atom->string();
3737 if (atom->string().contains('#')) {
3738 path = atom->string().split('#');
3741 path.append(atom->string());
3744 Atom *targetAtom = 0;
3745 QString first = path.first().trimmed();
3746 if (first.isEmpty()) {
3749 else if (first.endsWith(".html")) {
3751 This is not a recursive search. That's ok in
3752 this case, because we are searching for a page
3753 node, which must be a direct child of the tree
3756 *node = tree_->root()->findChildNodeByNameAndType(first, Node::Fake);
3759 *node = marker->resolveTarget(first, tree_, relative);
3761 *node = tree_->findFakeNodeByTitle(first, relative);
3764 *node = tree_->findUnambiguousTarget(first, targetAtom, relative);
3768 if (!(*node)->url().isEmpty()) {
3769 return (*node)->url();
3780 if ((*node)->status() == Node::Obsolete) {
3782 if (relative->parent() != *node) {
3783 if (relative->status() != Node::Obsolete) {
3784 bool porting = false;
3785 if (relative->type() == Node::Fake) {
3786 const FakeNode* fake = static_cast<const FakeNode*>(relative);
3787 if (fake->title().startsWith("Porting"))
3790 QString name = marker->plainFullName(relative);
3791 if (!porting && !name.startsWith("Q3")) {
3792 if (obsoleteLinks) {
3793 relative->doc().location().warning(tr("Link to obsolete item '%1' in %2")
3794 .arg(atom->string())
3797 inObsoleteLink = true;
3803 qDebug() << "Link to Obsolete entity"
3804 << (*node)->name() << "no relative";
3810 This loop really only makes sense if *node is not 0.
3811 In that case, The node *node points to represents a
3812 qdoc page, so the link will ultimately point to some
3813 target on that page. This loop finds that target on
3814 the page that *node represents. targetAtom is that
3817 while (!path.isEmpty()) {
3818 targetAtom = tree_->findTarget(path.first(), *node);
3819 if (targetAtom == 0)
3825 Given that *node is not null, we now cconstruct a link
3826 to the page that *node represents, and then if there is
3827 a target on that page, we connect the target to the link
3830 if (path.isEmpty()) {
3831 link = linkForNode(*node, relative);
3832 if (*node && (*node)->subType() == Node::Image)
3833 link = "images/used-in-examples/" + link;
3835 link += QLatin1Char('#') + refForAtom(targetAtom, *node);
3838 If the output is going to subdirectories, then if the
3839 two nodes will be output to different directories, then
3840 the link must go up to the parent directory and then
3841 back down into the other subdirectory.
3843 if (link.startsWith("images/")) {
3844 link.prepend(QString("../"));
3846 else if (*node && relative && (*node != relative)) {
3847 if ((*node)->outputSubdirectory() != relative->outputSubdirectory()) {
3848 link.prepend(QString("../" + (*node)->outputSubdirectory() + QLatin1Char('/')));
3855 void HtmlGenerator::generateIndex(const QString &fileBase,
3857 const QString &title)
3859 tree_->generateIndex(outputDir() + QLatin1Char('/') + fileBase + ".index", url, title);
3862 void HtmlGenerator::generateStatus(const Node *node, CodeMarker *marker)
3866 switch (node->status()) {
3867 case Node::Obsolete:
3868 if (node->isInnerNode())
3869 Generator::generateStatus(node, marker);
3872 if (node->isInnerNode()) {
3873 text << Atom::ParaLeft
3874 << Atom(Atom::FormattingLeft,ATOM_FORMATTING_BOLD)
3877 << " is part of the Qt 3 support library."
3878 << Atom(Atom::FormattingRight, ATOM_FORMATTING_BOLD)
3879 << " It is provided to keep old source code working. "
3880 << "We strongly advise against "
3881 << "using it in new code. See ";
3883 const FakeNode *fakeNode = tree_->findFakeNodeByTitle("Porting To Qt 4");
3884 Atom *targetAtom = 0;
3885 if (fakeNode && node->type() == Node::Class) {
3886 QString oldName(node->name());
3887 oldName.remove(QLatin1Char('3'));
3888 targetAtom = tree_->findTarget(oldName,
3893 text << Atom(Atom::Link, linkForNode(fakeNode, node) + QLatin1Char('#') +
3894 refForAtom(targetAtom, fakeNode));
3897 text << Atom(Atom::Link, "Porting to Qt 4");
3899 text << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK)
3900 << Atom(Atom::String, "Porting to Qt 4")
3901 << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK)
3902 << " for more information."
3905 generateText(text, node, marker);
3908 Generator::generateStatus(node, marker);
3912 #ifdef GENERATE_MAC_REFS
3916 void HtmlGenerator::generateMacRef(const Node *node, CodeMarker *marker)
3918 if (!pleaseGenerateMacRef || marker == 0)
3921 QStringList macRefs = marker->macRefsForNode(node);
3922 foreach (const QString &macRef, macRefs)
3923 out() << "<a name=\"" << "//apple_ref/" << macRef << "\"></a>\n";
3927 void HtmlGenerator::beginLink(const QString &link,
3929 const Node *relative,
3936 if (link.isEmpty()) {
3937 if (showBrokenLinks)
3940 else if (node == 0 ||
3941 (relative != 0 && node->status() == relative->status())) {
3942 out() << "<a href=\"" << link << "\">";
3945 switch (node->status()) {
3946 case Node::Obsolete:
3947 out() << "<a href=\"" << link << "\" class=\"obsolete\">";
3950 out() << "<a href=\"" << link << "\" class=\"compat\">";
3953 out() << "<a href=\"" << link << "\">";
3959 void HtmlGenerator::endLink()
3962 if (link.isEmpty()) {
3963 if (showBrokenLinks)
3967 if (inObsoleteLink) {
3968 out() << "<sup>(obsolete)</sup>";
3974 inObsoleteLink = false;
3978 Generates the summary for the \a section. Only used for
3979 sections of QML element documentation.
3981 void HtmlGenerator::generateQmlSummary(const Section& section,
3982 const Node *relative,
3985 if (!section.members.isEmpty()) {
3987 NodeList::ConstIterator m;
3988 m = section.members.constBegin();
3989 while (m != section.members.constEnd()) {
3990 out() << "<li class=\"fn\">";
3991 generateQmlItem(*m,relative,marker,true);
4000 Outputs the html detailed documentation for a section
4001 on a QML element reference page.
4003 void HtmlGenerator::generateDetailedQmlMember(Node *node,
4004 const InnerNode *relative,
4007 QmlPropertyNode* qpn = 0;
4008 #ifdef GENERATE_MAC_REFS
4009 generateMacRef(node, marker);
4011 generateExtractionMark(node, MemberMark);
4012 out() << "<div class=\"qmlitem\">";
4013 if (node->subType() == Node::QmlPropertyGroup) {
4014 const QmlPropGroupNode* qpgn = static_cast<const QmlPropGroupNode*>(node);
4015 NodeList::ConstIterator p = qpgn->childNodes().constBegin();
4016 out() << "<div class=\"qmlproto\">";
4017 out() << "<table class=\"qmlname\">";
4018 while (p != qpgn->childNodes().constEnd()) {
4019 if ((*p)->type() == Node::QmlProperty) {
4020 qpn = static_cast<QmlPropertyNode*>(*p);
4021 out() << "<tr valign=\"top\" class=\"odd\">";
4022 out() << "<td class=\"tblQmlPropNode\"><p>";
4023 out() << "<a name=\"" + refForNode(qpn) + "\"></a>";
4025 if (!qpn->isWritable(tree_))
4026 out() << "<span class=\"qmlreadonly\">read-only</span>";
4027 if (qpn->isDefault())
4028 out() << "<span class=\"qmldefault\">default</span>";
4029 generateQmlItem(qpn, relative, marker, false);
4030 out() << "</p></td></tr>";
4034 out() << "</table>";
4037 else if (node->type() == Node::QmlProperty) {
4038 qpn = static_cast<QmlPropertyNode*>(node);
4040 If the QML property node has a single subproperty,
4041 override, replace qpn with that override node and
4044 if (qpn->qmlPropNodes().size() == 1) {
4045 Node* n = qpn->qmlPropNodes().at(0);
4046 if (n->type() == Node::QmlProperty)
4047 qpn = static_cast<QmlPropertyNode*>(n);
4050 Now qpn either has no overrides, or it has more
4051 than 1. If it has none, proceed to output as nortmal.
4053 if (qpn->qmlPropNodes().isEmpty()) {
4054 out() << "<div class=\"qmlproto\">";
4055 out() << "<table class=\"qmlname\">";
4056 out() << "<tr valign=\"top\" class=\"odd\">";
4057 out() << "<td class=\"tblQmlPropNode\"><p>";
4058 out() << "<a name=\"" + refForNode(qpn) + "\"></a>";
4059 if (!qpn->isReadOnlySet()) {
4060 if (qpn->declarativeCppNode())
4061 qpn->setReadOnly(!qpn->isWritable(tree_));
4063 if (qpn->isReadOnly())
4064 out() << "<span class=\"qmlreadonly\">read-only</span>";
4065 if (qpn->isDefault())
4066 out() << "<span class=\"qmldefault\">default</span>";
4067 generateQmlItem(qpn, relative, marker, false);
4068 out() << "</p></td></tr>";
4069 out() << "</table>";
4074 The QML property node has multiple override nodes.
4075 Process the whole list as we would for a QML property
4078 NodeList::ConstIterator p = qpn->qmlPropNodes().constBegin();
4079 out() << "<div class=\"qmlproto\">";
4080 out() << "<table class=\"qmlname\">";
4081 while (p != qpn->qmlPropNodes().constEnd()) {
4082 if ((*p)->type() == Node::QmlProperty) {
4083 QmlPropertyNode* q = static_cast<QmlPropertyNode*>(*p);
4084 out() << "<tr valign=\"top\" class=\"odd\">";
4085 out() << "<td class=\"tblQmlPropNode\"><p>";
4086 out() << "<a name=\"" + refForNode(q) + "\"></a>";
4087 if (!qpn->isReadOnlySet())
4088 qpn->setReadOnly(!qpn->isWritable(tree_));
4089 if (qpn->isReadOnly())
4090 out() << "<span class=\"qmlreadonly\">read-only</span>";
4091 if (qpn->isDefault())
4092 out() << "<span class=\"qmldefault\">default</span>";
4093 generateQmlItem(q, relative, marker, false);
4094 out() << "</p></td></tr>";
4098 out() << "</table>";
4102 else if (node->type() == Node::QmlSignal) {
4103 const FunctionNode* qsn = static_cast<const FunctionNode*>(node);
4104 out() << "<div class=\"qmlproto\">";
4105 out() << "<table class=\"qmlname\">";
4106 out() << "<tr valign=\"top\" class=\"odd\">";
4107 out() << "<td class=\"tblQmlFuncNode\"><p>";
4108 out() << "<a name=\"" + refForNode(qsn) + "\"></a>";
4109 generateSynopsis(qsn,relative,marker,CodeMarker::Detailed,false);
4110 out() << "</p></td></tr>";
4111 out() << "</table>";
4114 else if (node->type() == Node::QmlSignalHandler) {
4115 const FunctionNode* qshn = static_cast<const FunctionNode*>(node);
4116 out() << "<div class=\"qmlproto\">";
4117 out() << "<table class=\"qmlname\">";
4118 out() << "<tr valign=\"top\" class=\"odd\">";
4119 out() << "<td class=\"tblQmlFuncNode\"><p>";
4120 out() << "<a name=\"" + refForNode(qshn) + "\"></a>";
4121 generateSynopsis(qshn,relative,marker,CodeMarker::Detailed,false);
4122 out() << "</p></td></tr>";
4123 out() << "</table>";
4126 else if (node->type() == Node::QmlMethod) {
4127 const FunctionNode* qmn = static_cast<const FunctionNode*>(node);
4128 out() << "<div class=\"qmlproto\">";
4129 out() << "<table class=\"qmlname\">";
4130 out() << "<tr valign=\"top\" class=\"odd\">";
4131 out() << "<td class=\"tblQmlFuncNode\"><p>";
4132 out() << "<a name=\"" + refForNode(qmn) + "\"></a>";
4133 generateSynopsis(qmn,relative,marker,CodeMarker::Detailed,false);
4134 out() << "</p></td></tr>";
4135 out() << "</table>";
4138 out() << "<div class=\"qmldoc\">";
4139 generateStatus(node, marker);
4140 generateBody(node, marker);
4141 generateThreadSafeness(node, marker);
4142 generateSince(node, marker);
4143 generateAlsoList(node, marker);
4146 generateExtractionMark(node, EndMark);
4150 Output the "Inherits" line for the QML element,
4151 if there should be one.
4153 void HtmlGenerator::generateQmlInherits(const QmlClassNode* qcn, CodeMarker* marker)
4157 const FakeNode* base = qcn->qmlBase();
4160 text << Atom::ParaLeft << "Inherits ";
4161 text << Atom(Atom::LinkNode,CodeMarker::stringForNode(base));
4162 text << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK);
4163 text << Atom(Atom::String, base->name());
4164 text << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK);
4165 text << Atom::ParaRight;
4166 generateText(text, qcn, marker);
4171 Output the "[Xxx instantiates the C++ class QmlGraphicsXxx]"
4172 line for the QML element, if there should be one.
4174 If there is no class node, or if the class node status
4175 is set to Node::Internal, do nothing.
4177 void HtmlGenerator::generateQmlInstantiates(QmlClassNode* qcn, CodeMarker* marker)
4179 ClassNode* cn = qcn->classNode();
4180 if (cn && (cn->status() != Node::Internal)) {
4182 text << Atom::ParaLeft;
4183 text << Atom(Atom::LinkNode,CodeMarker::stringForNode(qcn));
4184 text << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK);
4185 QString name = qcn->name();
4187 Remove the "QML:" prefix, if present.
4188 It shouldn't be present anymore.
4190 if (name.startsWith(QLatin1String("QML:")))
4191 name = name.mid(4); // remove the "QML:" prefix
4192 text << Atom(Atom::String, name);
4193 text << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK);
4194 text << " instantiates the C++ class ";
4195 text << Atom(Atom::LinkNode,CodeMarker::stringForNode(cn));
4196 text << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK);
4197 text << Atom(Atom::String, cn->name());
4198 text << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK);
4199 text << Atom::ParaRight;
4200 generateText(text, qcn, marker);
4205 Output the "[QmlGraphicsXxx is instantiated by QML Type Xxx]"
4206 line for the class, if there should be one.
4208 If there is no QML element, or if the class node status
4209 is set to Node::Internal, do nothing.
4211 void HtmlGenerator::generateInstantiatedBy(ClassNode* cn, CodeMarker* marker)
4213 if (cn && cn->status() != Node::Internal && cn->qmlElement() != 0) {
4214 const QmlClassNode* qcn = cn->qmlElement();
4216 text << Atom::ParaLeft;
4217 text << Atom(Atom::LinkNode,CodeMarker::stringForNode(cn));
4218 text << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK);
4219 text << Atom(Atom::String, cn->name());
4220 text << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK);
4221 text << " is instantiated by QML Type ";
4222 text << Atom(Atom::LinkNode,CodeMarker::stringForNode(qcn));
4223 text << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK);
4224 text << Atom(Atom::String, qcn->name());
4225 text << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK);
4226 text << Atom::ParaRight;
4227 generateText(text, cn, marker);
4231 void HtmlGenerator::generateExtractionMark(const Node *node, ExtractionMarkType markType)
4233 if (markType != EndMark) {
4234 out() << "<!-- $$$" + node->name();
4235 if (markType == MemberMark) {
4236 if (node->type() == Node::Function) {
4237 const FunctionNode *func = static_cast<const FunctionNode *>(node);
4238 if (!func->associatedProperty()) {
4239 if (func->overloadNumber() == 1)
4240 out() << "[overload1]";
4241 out() << "$$$" + func->name() + func->rawParameters().remove(' ');
4243 } else if (node->type() == Node::Property) {
4245 const PropertyNode *prop = static_cast<const PropertyNode *>(node);
4246 const NodeList &list = prop->functions();
4247 foreach (const Node *propFuncNode, list) {
4248 if (propFuncNode->type() == Node::Function) {
4249 const FunctionNode *func = static_cast<const FunctionNode *>(propFuncNode);
4250 out() << "$$$" + func->name() + func->rawParameters().remove(' ');
4253 } else if (node->type() == Node::Enum) {
4254 const EnumNode *enumNode = static_cast<const EnumNode *>(node);
4255 foreach (const EnumItem &item, enumNode->items())
4256 out() << "$$$" + item.name();
4258 } else if (markType == BriefMark) {
4260 } else if (markType == DetailedDescriptionMark) {
4261 out() << "-description";
4265 out() << "<!-- @@@" + node->name() + " -->\n";
4271 This function outputs one or more manifest files in XML.
4272 They are used by Creator.
4274 void HtmlGenerator::generateManifestFiles()
4276 generateManifestFile("examples", "example");
4277 generateManifestFile("demos", "demo");
4278 ExampleNode::exampleNodeMap.clear();
4282 This function is called by generaqteManiferstFile(), once
4283 for each manifest file to be generated. \a manifest is the
4284 type of manifest file.
4286 void HtmlGenerator::generateManifestFile(QString manifest, QString element)
4288 if (ExampleNode::exampleNodeMap.isEmpty())
4290 QString fileName = manifest +"-manifest.xml";
4291 QFile file(outputDir() + QLatin1Char('/') + fileName);
4292 if (!file.open(QFile::WriteOnly | QFile::Text))
4295 if (manifest == "demos")
4298 bool proceed = false;
4299 ExampleNodeMap::Iterator i = ExampleNode::exampleNodeMap.begin();
4300 while (i != ExampleNode::exampleNodeMap.end()) {
4301 const ExampleNode* en = i.value();
4303 if (en->name().startsWith("demos")) {
4308 else if (!en->name().startsWith("demos")) {
4317 QXmlStreamWriter writer(&file);
4318 writer.setAutoFormatting(true);
4319 writer.writeStartDocument();
4320 writer.writeStartElement("instructionals");
4321 writer.writeAttribute("module", project);
4322 writer.writeStartElement(manifest);
4324 i = ExampleNode::exampleNodeMap.begin();
4325 while (i != ExampleNode::exampleNodeMap.end()) {
4326 const ExampleNode* en = i.value();
4328 if (!en->name().startsWith("demos")) {
4333 else if (en->name().startsWith("demos")) {
4337 writer.writeStartElement(element);
4338 writer.writeAttribute("name", en->title());
4339 QString docUrl = manifestDir + en->fileBase() + ".html";
4340 writer.writeAttribute("docUrl", docUrl);
4341 QStringList proFiles;
4342 foreach (const Node* child, en->childNodes()) {
4343 if (child->subType() == Node::File) {
4344 QString file = child->name();
4345 if (file.endsWith(".pro") || file.endsWith(".qmlproject")) {
4346 if (file.startsWith("demos/"))
4352 if (!proFiles.isEmpty()) {
4353 if (proFiles.size() == 1) {
4354 writer.writeAttribute("projectPath", proFiles[0]);
4357 QString exampleName = en->name().split('/').last();
4358 bool proWithExampleNameFound = false;
4359 for (int j = 0; j < proFiles.size(); j++)
4361 if (proFiles[j].endsWith(QStringLiteral("%1/%1.pro").arg(exampleName))
4362 || proFiles[j].endsWith(QStringLiteral("%1/%1.qmlproject").arg(exampleName))) {
4363 writer.writeAttribute("projectPath", proFiles[j]);
4364 proWithExampleNameFound = true;
4368 if (!proWithExampleNameFound)
4369 writer.writeAttribute("projectPath", proFiles[0]);
4372 if (!en->imageFileName().isEmpty())
4373 writer.writeAttribute("imageUrl", manifestDir + en->imageFileName());
4374 writer.writeStartElement("description");
4375 Text brief = en->doc().briefText();
4376 if (!brief.isEmpty())
4377 writer.writeCDATA(brief.toString());
4379 writer.writeCDATA(QString("No description available"));
4380 writer.writeEndElement(); // description
4381 QStringList tags = en->title().toLower().split(QLatin1Char(' '));
4382 if (!tags.isEmpty()) {
4383 writer.writeStartElement("tags");
4384 bool wrote_one = false;
4385 for (int n=0; n<tags.size(); ++n) {
4386 QString tag = tags.at(n);
4387 if (tag.at(0).isDigit())
4389 if (tag.at(0) == '-')
4391 if (tag.startsWith("example"))
4393 if (tag.startsWith("chapter"))
4395 if (tag.endsWith(QLatin1Char(':')))
4397 if (n>0 && wrote_one)
4398 writer.writeCharacters(",");
4399 writer.writeCharacters(tag);
4402 writer.writeEndElement(); // tags
4405 QString ename = en->name().mid(en->name().lastIndexOf('/')+1);
4406 QSet<QString> usedNames;
4407 foreach (const Node* child, en->childNodes()) {
4408 if (child->subType() == Node::File) {
4409 QString file = child->name();
4410 QString fileName = file.mid(file.lastIndexOf('/')+1);
4411 QString baseName = fileName;
4412 if ((fileName.count(QChar('.')) > 0) &&
4413 (fileName.endsWith(".cpp") ||
4414 fileName.endsWith(".h") ||
4415 fileName.endsWith(".qml")))
4416 baseName.truncate(baseName.lastIndexOf(QChar('.')));
4417 if (baseName.compare(ename, Qt::CaseInsensitive) == 0) {
4418 if (!usedNames.contains(fileName)) {
4419 writer.writeStartElement("fileToOpen");
4420 if (file.startsWith("demos/"))
4422 writer.writeCharacters(file);
4423 writer.writeEndElement(); // fileToOpen
4424 usedNames.insert(fileName);
4427 else if (fileName.toLower().endsWith("main.cpp") ||
4428 fileName.toLower().endsWith("main.qml")) {
4429 if (!usedNames.contains(fileName)) {
4430 writer.writeStartElement("fileToOpen");
4431 if (file.startsWith("demos/"))
4433 writer.writeCharacters(file);
4434 writer.writeEndElement(); // fileToOpen
4435 usedNames.insert(fileName);
4440 writer.writeEndElement(); // example
4444 writer.writeEndElement(); // examples
4445 writer.writeEndElement(); // instructionals
4446 writer.writeEndDocument();
4451 Find global entities that have documentation but no
4452 \e{relates} comand. Report these as errors if they
4453 are not also marked \e {internal}.
4459 subtype: External page
4461 subtype: Header file
4464 subtype: QML basic type
4468 void HtmlGenerator::reportOrphans(const InnerNode* parent)
4470 const NodeList& children = parent->childNodes();
4471 if (children.size() == 0)
4476 for (int i=0; i<children.size(); ++i) {
4477 Node* child = children[i];
4478 if (!child || child->isInternal() || child->doc().isEmpty())
4480 if (child->relates()) {
4482 message = child->relates()->name();
4486 message = "has documentation but no \\relates command";
4488 switch (child->type()) {
4489 case Node::Namespace:
4494 switch (child->subType()) {
4497 case Node::HeaderFile:
4509 case Node::ExternalPage:
4511 case Node::QmlClass:
4513 case Node::QmlPropertyGroup:
4515 case Node::QmlBasicType:
4517 case Node::QmlModule:
4519 case Node::Collision:
4527 child->location().warning(tr("Global enum, %1, %2").arg(child->name()).arg(message));
4531 child->location().warning(tr("Global typedef, %1, %2").arg(child->name()).arg(message));
4533 case Node::Function:
4535 const FunctionNode* fn = static_cast<const FunctionNode*>(child);
4537 child->location().warning(tr("Global macro, %1, %2").arg(child->name()).arg(message));
4539 child->location().warning(tr("Global function, %1(), %2").arg(child->name()).arg(message));
4542 case Node::Property:
4544 case Node::Variable:
4546 child->location().warning(tr("Global variable, %1, %2").arg(child->name()).arg(message));
4548 case Node::QmlProperty:
4550 child->location().warning(tr("Global QML property, %1, %2").arg(child->name()).arg(message));
4552 case Node::QmlSignal:
4554 child->location().warning(tr("Global QML, signal, %1 %2").arg(child->name()).arg(message));
4556 case Node::QmlSignalHandler:
4558 child->location().warning(tr("Global QML signal handler, %1, %2").arg(child->name()).arg(message));
4560 case Node::QmlMethod:
4562 child->location().warning(tr("Global QML method, %1, %2").arg(child->name()).arg(message));
4571 Returns a reference to the XML stream writer currently in use.
4572 There is one XML stream writer open for each XML file being
4573 written, and they are kept on a stack. The one on top of the
4574 stack is the one being written to at the moment. In the HTML
4575 output generator, it is perhaps impossible for there to ever
4576 be more than one writer open.
4578 QXmlStreamWriter& HtmlGenerator::xmlWriter()
4580 return *xmlWriterStack.top();
4584 This function is only called for writing ditamaps.
4586 Calls beginSubPage() in the base class to open the file.
4587 Then creates a new XML stream writer using the IO device
4588 from opened file and pushes the XML writer onto a stackj.
4589 Creates the file named \a fileName in the output directory.
4590 Attaches a QTextStream to the created file, which is written
4591 to all over the place using out(). Finally, it sets some
4592 parameters in the XML writer and calls writeStartDocument().
4594 It also ensures that a GUID map is created for the output file.
4596 void HtmlGenerator::beginDitamapPage(const InnerNode* node, const QString& fileName)
4598 Generator::beginSubPage(node,fileName);
4599 QXmlStreamWriter* writer = new QXmlStreamWriter(out().device());
4600 xmlWriterStack.push(writer);
4601 writer->setAutoFormatting(true);
4602 writer->setAutoFormattingIndent(4);
4603 writer->writeStartDocument();
4607 This function is only called for writing ditamaps.
4609 Calls writeEndDocument() and then pops the XML stream writer
4610 off the stack and deletes it. Then it calls endSubPage() in
4611 the base class to close the device.
4613 void HtmlGenerator::endDitamapPage()
4615 xmlWriter().writeEndDocument();
4616 delete xmlWriterStack.pop();
4617 Generator::endSubPage();
4621 This function is only called for writing ditamaps.
4623 Creates the DITA map from the topicrefs in \a node,
4624 which is a DitaMapNode.
4626 void HtmlGenerator::writeDitaMap(const DitaMapNode* node)
4628 beginDitamapPage(node,node->name());
4630 QString doctype = "<!DOCTYPE map PUBLIC \"-//OASIS//DTD DITA Map//EN\" \"map.dtd\">";
4632 xmlWriter().writeDTD(doctype);
4633 xmlWriter().writeStartElement("map");
4634 xmlWriter().writeStartElement("topicmeta");
4635 xmlWriter().writeStartElement("shortdesc");
4636 xmlWriter().writeCharacters(node->title());
4637 xmlWriter().writeEndElement(); // </shortdesc>
4638 xmlWriter().writeEndElement(); // </topicmeta>
4639 DitaRefList map = node->map();
4645 Write the \a ditarefs to the current output file.
4647 void HtmlGenerator::writeDitaRefs(const DitaRefList& ditarefs)
4649 foreach (DitaRef* t, ditarefs) {
4651 xmlWriter().writeStartElement("mapref");
4653 xmlWriter().writeStartElement("topicref");
4654 xmlWriter().writeAttribute("navtitle",t->navtitle());
4655 if (t->href().isEmpty()) {
4656 const FakeNode* fn = tree_->findFakeNodeByTitle(t->navtitle());
4658 xmlWriter().writeAttribute("href",fileName(fn));
4661 xmlWriter().writeAttribute("href",t->href());
4662 if (t->subrefs() && !t->subrefs()->isEmpty())
4663 writeDitaRefs(*(t->subrefs()));
4664 xmlWriter().writeEndElement(); // </topicref> or </mapref>