1 /****************************************************************************
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
6 ** This file is part of the tools applications of the Qt Toolkit.
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and Digia. For licensing terms and
14 ** conditions see http://qt.digia.com/licensing. For further information
15 ** use the contact form at http://qt.digia.com/contact-us.
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 2.1 requirements
23 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25 ** In addition, as a special exception, Digia gives you certain additional
26 ** rights. These rights are described in the Digia Qt LGPL Exception
27 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29 ** GNU General Public License Usage
30 ** Alternatively, this file may be used under the terms of the GNU
31 ** General Public License version 3.0 as published by the Free Software
32 ** Foundation and appearing in the file LICENSE.GPL included in the
33 ** packaging of this file. Please review the following information to
34 ** ensure the GNU General Public License version 3.0 requirements will be
35 ** met: http://www.gnu.org/copyleft/gpl.html.
40 ****************************************************************************/
46 #include "codemarker.h"
47 #include "codeparser.h"
48 #include "helpprojectwriter.h"
49 #include "htmlgenerator.h"
51 #include "qdocdatabase.h"
52 #include "separator.h"
57 #include <qiterator.h>
58 #include <qtextcodec.h>
63 #define COMMAND_VERSION Doc::alias("version")
64 int HtmlGenerator::id = 0;
65 bool HtmlGenerator::debugging_on = false;
67 QString HtmlGenerator::divNavTop;
69 static bool showBrokenLinks = false;
71 static QRegExp linkTag("(<@link node=\"([^\"]+)\">).*(</@link>)");
72 static QRegExp funcTag("(<@func target=\"([^\"]*)\">)(.*)(</@func>)");
73 static QRegExp typeTag("(<@(type|headerfile|func)(?: +[^>]*)?>)(.*)(</@\\2>)");
74 static QRegExp spanTag("</@(?:comment|preprocessor|string|char|number|op|type|name|keyword)>");
75 static QRegExp unknownTag("</?@[^>]*>");
77 static void addLink(const QString &linkTarget,
78 const QStringRef &nestedStuff,
81 if (!linkTarget.isEmpty()) {
94 Constructs the HTML output generator.
96 HtmlGenerator::HtmlGenerator()
97 : helpProjectWriter(0),
98 inObsoleteLink(false),
99 funcLeftParen("\\S(\\()"),
105 Destroys the HTML output generator. Deletes the singleton
106 instance of HelpProjectWriter.
108 HtmlGenerator::~HtmlGenerator()
110 if (helpProjectWriter)
111 delete helpProjectWriter;
115 Initializes the HTML output generator's data structures
116 from the configuration class \a config.
118 void HtmlGenerator::initializeGenerator(const Config &config)
120 static const struct {
125 { ATOM_FORMATTING_BOLD, "<b>", "</b>" },
126 { ATOM_FORMATTING_INDEX, "<!--", "-->" },
127 { ATOM_FORMATTING_ITALIC, "<i>", "</i>" },
128 { ATOM_FORMATTING_PARAMETER, "<i>", "</i>" },
129 { ATOM_FORMATTING_SUBSCRIPT, "<sub>", "</sub>" },
130 { ATOM_FORMATTING_SUPERSCRIPT, "<sup>", "</sup>" },
131 { ATOM_FORMATTING_TELETYPE, "<tt>", "</tt>" },
132 { ATOM_FORMATTING_UICONTROL, "<b>", "</b>" },
133 { ATOM_FORMATTING_UNDERLINE, "<u>", "</u>" },
137 Generator::initializeGenerator(config);
138 obsoleteLinks = config.getBool(QLatin1String(CONFIG_OBSOLETELINKS));
139 setImageFileExtensions(QStringList() << "png" << "jpg" << "jpeg" << "gif");
141 while (defaults[i].key) {
142 formattingLeftMap().insert(defaults[i].key, defaults[i].left);
143 formattingRightMap().insert(defaults[i].key, defaults[i].right);
147 style = config.getString(HtmlGenerator::format() +
150 endHeader = config.getString(HtmlGenerator::format() +
153 postHeader = config.getString(HtmlGenerator::format() +
155 HTMLGENERATOR_POSTHEADER);
156 postPostHeader = config.getString(HtmlGenerator::format() +
158 HTMLGENERATOR_POSTPOSTHEADER);
159 footer = config.getString(HtmlGenerator::format() +
161 HTMLGENERATOR_FOOTER);
162 address = config.getString(HtmlGenerator::format() +
164 HTMLGENERATOR_ADDRESS);
165 pleaseGenerateMacRef = config.getBool(HtmlGenerator::format() +
167 HTMLGENERATOR_GENERATEMACREFS);
168 noBreadCrumbs = config.getBool(HtmlGenerator::format() +
170 HTMLGENERATOR_NOBREADCRUMBS);
172 project = config.getString(CONFIG_PROJECT);
174 projectDescription = config.getString(CONFIG_DESCRIPTION);
175 if (projectDescription.isEmpty() && !project.isEmpty())
176 projectDescription = project + " Reference Documentation";
178 projectUrl = config.getString(CONFIG_URL);
179 tagFile_ = config.getString(CONFIG_TAGFILE);
181 outputEncoding = config.getString(CONFIG_OUTPUTENCODING);
182 if (outputEncoding.isEmpty())
183 outputEncoding = QLatin1String("UTF-8");
184 outputCodec = QTextCodec::codecForName(outputEncoding.toLocal8Bit());
186 naturalLanguage = config.getString(CONFIG_NATURALLANGUAGE);
187 if (naturalLanguage.isEmpty())
188 naturalLanguage = QLatin1String("en");
190 QSet<QString> editionNames = config.subVars(CONFIG_EDITION);
191 QSet<QString>::ConstIterator edition = editionNames.constBegin();
192 while (edition != editionNames.constEnd()) {
193 QString editionName = *edition;
194 QStringList editionModules = config.getStringList(CONFIG_EDITION +
199 QStringList editionGroups = config.getStringList(CONFIG_EDITION +
205 if (!editionModules.isEmpty())
206 editionModuleMap[editionName] = editionModules;
207 if (!editionGroups.isEmpty())
208 editionGroupMap[editionName] = editionGroups;
213 codeIndent = config.getInt(CONFIG_CODEINDENT);
215 helpProjectWriter = new HelpProjectWriter(config, project.toLower() + ".qhp", this);
217 // Documentation template handling
218 headerScripts = config.getString(HtmlGenerator::format() + Config::dot + CONFIG_HEADERSCRIPTS);
219 headerStyles = config.getString(HtmlGenerator::format() + Config::dot + CONFIG_HEADERSTYLES);
221 QString prefix = CONFIG_QHP + Config::dot + "Qt" + Config::dot;
222 manifestDir = "qthelp://" + config.getString(prefix + "namespace");
223 manifestDir += QLatin1Char('/') + config.getString(prefix + "virtualFolder") + QLatin1Char('/');
228 Gracefully terminates the HTML output generator.
230 void HtmlGenerator::terminateGenerator()
232 Generator::terminateGenerator();
235 QString HtmlGenerator::format()
241 Traverses the database generating all the HTML documentation.
243 void HtmlGenerator::generateTree()
245 qdb_->buildCollections();
246 if (!runPrepareOnly()) {
247 Generator::generateTree();
248 generateCollisionPages();
251 if (!runGenerateOnly()) {
252 QString fileBase = project.toLower().simplified().replace(QLatin1Char(' '), QLatin1Char('-'));
253 qdb_->generateIndex(outputDir() + QLatin1Char('/') + fileBase + ".index",
259 if (!runPrepareOnly()) {
260 helpProjectWriter->generate();
261 generateManifestFiles();
263 Generate the XML tag file, if it was requested.
265 qdb_->generateTagFile(tagFile_, this);
270 Generate html from an instance of Atom.
272 int HtmlGenerator::generateAtom(const Atom *atom, const Node *relative, CodeMarker *marker)
275 static bool in_para = false;
277 if (Generator::debugging()) {
280 switch (atom->type()) {
281 case Atom::AbstractLeft:
283 relative->doc().location().warning(tr("\abstract is not implemented."));
285 Location::information(tr("\abstract is not implemented."));
287 case Atom::AbstractRight:
290 if (!inLink_ && !inContents_ && !inSectionHeading_) {
291 const Node *node = 0;
292 QString link = getLink(atom, relative, &node);
293 if (!link.isEmpty()) {
294 beginLink(link, node, relative);
295 generateLink(atom, marker);
299 out() << protectEnc(atom->string());
303 out() << protectEnc(atom->string());
308 case Atom::BriefLeft:
309 if (relative->type() == Node::Document) {
310 if (relative->subType() != Node::Example) {
311 skipAhead = skipAtoms(atom, Atom::BriefRight);
317 if (relative->type() == Node::Property ||
318 relative->type() == Node::Variable) {
321 while (atom != 0 && atom->type() != Atom::BriefRight) {
322 if (atom->type() == Atom::String ||
323 atom->type() == Atom::AutoLink)
324 str += atom->string();
328 str[0] = str[0].toLower();
329 if (str.endsWith(QLatin1Char('.')))
330 str.truncate(str.length() - 1);
332 if (relative->type() == Node::Property)
336 QStringList words = str.split(QLatin1Char(' '));
337 if (!(words.first() == "contains" || words.first() == "specifies"
338 || words.first() == "describes" || words.first() == "defines"
339 || words.first() == "holds" || words.first() == "determines"))
346 case Atom::BriefRight:
347 if (relative->type() != Node::Document)
351 // This may at one time have been used to mark up C++ code but it is
352 // now widely used to write teletype text. As a result, text marked
353 // with the \c command is not passed to a code marker.
354 out() << formattingLeftMap()[ATOM_FORMATTING_TELETYPE];
356 out() << protectEnc(plainCode(atom->string()));
359 out() << protectEnc(plainCode(atom->string()));
361 out() << formattingRightMap()[ATOM_FORMATTING_TELETYPE];
363 case Atom::CaptionLeft:
364 out() << "<p class=\"figCaption\">";
367 case Atom::CaptionRight:
375 out() << "<pre class=\"cpp\">"
376 << trimmedTrailing(highlightedCode(indent(codeIndent,atom->string()),relative))
380 out() << "<pre class=\"qml\">"
381 << trimmedTrailing(highlightedCode(indent(codeIndent,atom->string()),relative))
384 case Atom::JavaScript:
385 out() << "<pre class=\"js\">"
386 << trimmedTrailing(highlightedCode(indent(codeIndent,atom->string()),relative))
390 out() << "<p>you can rewrite it as</p>\n"
391 << "<pre class=\"cpp\">"
392 << trimmedTrailing(highlightedCode(indent(codeIndent,atom->string()),relative))
396 out() << "<p>For example, if you have code like</p>\n";
399 out() << "<pre class=\"cpp\">"
400 << trimmedTrailing(protectEnc(plainCode(indent(codeIndent,atom->string()))))
405 if (!atom->string().isEmpty())
406 out() << ' ' << atom->string();
412 case Atom::FootnoteLeft:
420 case Atom::FootnoteRight:
424 case Atom::FormatElse:
425 case Atom::FormatEndif:
428 case Atom::FormattingLeft:
429 if (atom->string().startsWith("span ")) {
430 out() << '<' + atom->string() << '>';
433 out() << formattingLeftMap()[atom->string()];
434 if (atom->string() == ATOM_FORMATTING_PARAMETER) {
435 if (atom->next() != 0 && atom->next()->type() == Atom::String) {
436 QRegExp subscriptRegExp("([a-z]+)_([0-9n])");
437 if (subscriptRegExp.exactMatch(atom->next()->string())) {
438 out() << subscriptRegExp.cap(1) << "<sub>"
439 << subscriptRegExp.cap(2) << "</sub>";
445 case Atom::FormattingRight:
446 if (atom->string() == ATOM_FORMATTING_LINK) {
449 else if (atom->string().startsWith("span ")) {
453 out() << formattingRightMap()[atom->string()];
456 case Atom::AnnotatedList:
459 qdb_->getGroup(atom->string(), nodeMap);
460 generateAnnotatedList(relative, marker, nodeMap);
463 case Atom::GeneratedList:
464 if (atom->string() == "annotatedclasses") {
465 generateAnnotatedList(relative, marker, qdb_->getCppClasses());
467 else if (atom->string() == "classes") {
468 generateCompactList(relative, qdb_->getCppClasses(), true);
470 else if (atom->string() == "qmlclasses") {
471 generateCompactList(relative, qdb_->getQmlTypes(), true);
473 else if (atom->string().contains("classesbymodule")) {
474 QString arg = atom->string().trimmed();
475 QString moduleName = atom->string().mid(atom->string().indexOf("classesbymodule") + 15).trimmed();
476 QDocDatabase* qdb = QDocDatabase::qdocDB();
477 DocNode* dn = qdb->findModule(moduleName);
480 dn->getMemberClasses(m);
482 generateAnnotatedList(relative, marker, m);
486 else if (atom->string().contains("classesbyedition")) {
487 QString arg = atom->string().trimmed();
488 QString editionName = atom->string().mid(atom->string().indexOf("classesbyedition") + 16).trimmed();
489 if (editionModuleMap.contains(editionName)) {
490 QDocDatabase* qdb = QDocDatabase::qdocDB();
491 // Add all classes in the modules listed for that edition.
492 NodeMap editionClasses;
493 DocNodeMap::const_iterator i = qdb->modules().begin();
494 while (i != qdb->modules().end()) {
496 DocNode* dn = i.value();
497 dn->getMemberClasses(m);
499 editionClasses.unite(m);
504 // Add additional groups and remove groups of classes that
505 // should be excluded from the edition.
507 const NodeMultiMap& groups = qdb_->groups();
508 foreach (const QString &groupName, editionGroupMap[editionName]) {
509 QList<Node *> groupClasses;
510 if (groupName.startsWith(QLatin1Char('-'))) {
511 groupClasses = groups.values(groupName.mid(1));
512 foreach (const Node *node, groupClasses)
513 editionClasses.remove(node->name());
516 groupClasses = groups.values(groupName);
517 foreach (const Node *node, groupClasses)
518 editionClasses.insert(node->name(), node);
521 generateAnnotatedList(relative, marker, editionClasses);
524 else if (atom->string() == "classhierarchy") {
525 generateClassHierarchy(relative, qdb_->getCppClasses());
527 else if (atom->string() == "compatclasses") {
528 generateCompactList(relative, qdb_->getCompatibilityClasses(), false);
530 else if (atom->string() == "obsoleteclasses") {
531 generateCompactList(relative, qdb_->getObsoleteClasses(), false);
533 else if (atom->string() == "functionindex") {
534 generateFunctionIndex(relative);
536 else if (atom->string() == "legalese") {
537 generateLegaleseList(relative, marker);
539 else if (atom->string() == "mainclasses") {
540 generateCompactList(relative, qdb_->getMainClasses(), true);
542 else if (atom->string() == "services") {
543 generateCompactList(relative, qdb_->getServiceClasses(), false);
545 else if (atom->string() == "overviews") {
546 generateOverviewList(relative);
548 else if (atom->string() == "namespaces") {
549 generateAnnotatedList(relative, marker, qdb_->getNamespaces());
551 else if (atom->string() == "related") {
552 const DocNode *dn = static_cast<const DocNode *>(relative);
553 if (dn && !dn->members().isEmpty()) {
554 NodeMap groupMembersMap;
555 foreach (const Node *node, dn->members()) {
556 if (node->type() == Node::Document)
557 groupMembersMap[node->fullName(relative)] = node;
559 generateAnnotatedList(dn, marker, groupMembersMap);
562 else if (atom->string() == "relatedinline") {
563 const DocNode *dn = static_cast<const DocNode *>(relative);
564 if (dn && !dn->members().isEmpty()) {
565 // Reverse the list into the original scan order.
566 // Should be sorted. But on what? It may not be a
567 // regular class or page definition.
568 QList<const Node *> list;
569 foreach (const Node *node, dn->members())
571 foreach (const Node *node, list)
572 generateBody(node, marker);
576 case Atom::SinceList:
578 const NodeMultiMap& nsmap = qdb_->getSinceMap(atom->string());
579 const NodeMap& ncmap = qdb_->getClassMap(atom->string());
580 const NodeMap& nqcmap = qdb_->getQmlTypeMap(atom->string());
582 if (!nsmap.isEmpty()) {
583 QList<Section> sections;
584 QList<Section>::ConstIterator s;
586 for (int i=0; i<LastSinceType; ++i)
587 sections.append(Section(sinceTitle(i),QString(),QString(),QString()));
589 NodeMultiMap::const_iterator n = nsmap.constBegin();
590 while (n != nsmap.constEnd()) {
591 const Node* node = n.value();
592 switch (node->type()) {
594 if (node->subType() == Node::QmlClass) {
595 sections[QmlClass].appendMember((Node*)node);
598 case Node::Namespace:
599 sections[Namespace].appendMember((Node*)node);
602 sections[Class].appendMember((Node*)node);
605 sections[Enum].appendMember((Node*)node);
608 sections[Typedef].appendMember((Node*)node);
610 case Node::Function: {
611 const FunctionNode* fn = static_cast<const FunctionNode*>(node);
613 sections[Macro].appendMember((Node*)node);
615 Node* p = fn->parent();
617 if (p->type() == Node::Class)
618 sections[MemberFunction].appendMember((Node*)node);
619 else if (p->type() == Node::Namespace) {
620 if (p->name().isEmpty())
621 sections[GlobalFunction].appendMember((Node*)node);
623 sections[NamespaceFunction].appendMember((Node*)node);
626 sections[GlobalFunction].appendMember((Node*)node);
629 sections[GlobalFunction].appendMember((Node*)node);
634 sections[Property].appendMember((Node*)node);
637 sections[Variable].appendMember((Node*)node);
639 case Node::QmlProperty:
640 sections[QmlProperty].appendMember((Node*)node);
642 case Node::QmlSignal:
643 sections[QmlSignal].appendMember((Node*)node);
645 case Node::QmlSignalHandler:
646 sections[QmlSignalHandler].appendMember((Node*)node);
648 case Node::QmlMethod:
649 sections[QmlMethod].appendMember((Node*)node);
658 s = sections.constBegin();
659 while (s != sections.constEnd()) {
660 if (!(*s).members.isEmpty()) {
664 << Doc::canonicalTitle((*s).name)
674 s = sections.constBegin();
675 while (s != sections.constEnd()) {
676 if (!(*s).members.isEmpty()) {
677 out() << "<a name=\""
678 << Doc::canonicalTitle((*s).name)
680 out() << "<h3>" << protectEnc((*s).name) << "</h3>\n";
682 generateCompactList(0, ncmap, false, QString("Q"));
683 else if (idx == QmlClass)
684 generateCompactList(0, nqcmap, false, QString("Q"));
685 else if (idx == MemberFunction) {
686 ParentMaps parentmaps;
687 ParentMaps::iterator pmap;
688 NodeList::const_iterator i = s->members.constBegin();
689 while (i != s->members.constEnd()) {
690 Node* p = (*i)->parent();
691 pmap = parentmaps.find(p);
692 if (pmap == parentmaps.end())
693 pmap = parentmaps.insert(p,NodeMultiMap());
694 pmap->insert((*i)->name(),(*i));
697 pmap = parentmaps.begin();
698 while (pmap != parentmaps.end()) {
699 NodeList nlist = pmap->values();
700 out() << "<p>Class ";
702 out() << "<a href=\""
703 << linkForNode(pmap.key(), 0)
705 QStringList pieces = pmap.key()->fullName().split("::");
706 out() << protectEnc(pieces.last());
707 out() << "</a>" << ":</p>\n";
709 generateSection(nlist, 0, marker, CodeMarker::Summary);
715 generateSection(s->members, 0, marker, CodeMarker::Summary);
730 case Atom::InlineImage:
732 QString fileName = imageFileName(relative, atom->string());
734 if (atom->next() != 0)
735 text = atom->next()->string();
736 if (atom->type() == Atom::Image)
737 out() << "<p class=\"centerAlign\">";
738 if (fileName.isEmpty()) {
739 relative->location().warning(tr("Missing image: %1").arg(protectEnc(atom->string())));
740 out() << "<font color=\"red\">[Missing image "
741 << protectEnc(atom->string()) << "]</font>";
745 out() << "<img src=\"" << protectEnc(prefix + fileName) << '"';
747 out() << " alt=\"" << protectEnc(text) << '"';
749 out() << " alt=\"\"";
751 helpProjectWriter->addExtraFile(fileName);
752 if ((relative->type() == Node::Document) &&
753 (relative->subType() == Node::Example)) {
754 const ExampleNode* cen = static_cast<const ExampleNode*>(relative);
755 if (cen->imageFileName().isEmpty()) {
756 ExampleNode* en = const_cast<ExampleNode*>(cen);
757 en->setImageFileName(fileName);
761 if (atom->type() == Atom::Image)
765 case Atom::ImageText:
767 case Atom::ImportantLeft:
769 out() << formattingLeftMap()[ATOM_FORMATTING_BOLD];
770 out() << "Important: ";
771 out() << formattingRightMap()[ATOM_FORMATTING_BOLD];
773 case Atom::ImportantRight:
778 out() << formattingLeftMap()[ATOM_FORMATTING_BOLD];
780 out() << formattingRightMap()[ATOM_FORMATTING_BOLD];
782 case Atom::NoteRight:
785 case Atom::LegaleseLeft:
786 out() << "<div class=\"LegaleseLeft\">";
788 case Atom::LegaleseRight:
791 case Atom::LineBreak:
796 const Node *node = 0;
797 QString myLink = getLink(atom, relative, &node);
798 if (myLink.isEmpty()) {
799 myLink = getCollisionLink(atom);
800 if (myLink.isEmpty() && !noLinkErrors()) {
801 relative->doc().location().warning(tr("Can't link to '%1'").arg(atom->string()));
806 beginLink(myLink, node, relative);
812 const Node *node = CodeMarker::nodeForString(atom->string());
813 beginLink(linkForNode(node, relative), node, relative);
822 if (atom->string() == ATOM_LIST_BULLET) {
825 else if (atom->string() == ATOM_LIST_TAG) {
828 else if (atom->string() == ATOM_LIST_VALUE) {
829 threeColumnEnumValueTable_ = isThreeColumnEnumValueTable(atom);
830 if (threeColumnEnumValueTable_) {
831 out() << "<table class=\"valuelist\">";
832 if (++numTableRows_ % 2 == 1)
833 out() << "<tr valign=\"top\" class=\"odd\">";
835 out() << "<tr valign=\"top\" class=\"even\">";
837 out() << "<th class=\"tblConst\">Constant</th>"
838 << "<th class=\"tblval\">Value</th>"
839 << "<th class=\"tbldscr\">Description</th></tr>\n";
842 out() << "<table class=\"valuelist\">"
843 << "<tr><th class=\"tblConst\">Constant</th><th class=\"tblVal\">Value</th></tr>\n";
847 out() << "<ol class=";
848 if (atom->string() == ATOM_LIST_UPPERALPHA) {
850 } /* why type? changed to */
851 else if (atom->string() == ATOM_LIST_LOWERALPHA) {
854 else if (atom->string() == ATOM_LIST_UPPERROMAN) {
857 else if (atom->string() == ATOM_LIST_LOWERROMAN) {
860 else { // (atom->string() == ATOM_LIST_NUMERIC)
863 if (atom->next() != 0 && atom->next()->string().toInt() != 1)
864 out() << " start=\"" << atom->next()->string() << '"';
868 case Atom::ListItemNumber:
870 case Atom::ListTagLeft:
871 if (atom->string() == ATOM_LIST_TAG) {
874 else { // (atom->string() == ATOM_LIST_VALUE)
877 QString t= protectEnc(plainCode(marker->markedUpEnumValue(atom->next()->string(),relative)));
878 out() << "<tr><td class=\"topAlign\"><tt>" << t << "</tt></td><td class=\"topAlign\">";
881 if (relative->type() == Node::Enum) {
882 const EnumNode *enume = static_cast<const EnumNode *>(relative);
883 itemValue = enume->itemValue(atom->next()->string());
886 if (itemValue.isEmpty())
889 out() << "<tt>" << protectEnc(itemValue) << "</tt>";
894 case Atom::ListTagRight:
895 if (atom->string() == ATOM_LIST_TAG)
898 case Atom::ListItemLeft:
899 if (atom->string() == ATOM_LIST_TAG) {
902 else if (atom->string() == ATOM_LIST_VALUE) {
903 if (threeColumnEnumValueTable_) {
904 out() << "</td><td class=\"topAlign\">";
905 if (matchAhead(atom, Atom::ListItemRight))
912 if (matchAhead(atom, Atom::ParaLeft))
915 case Atom::ListItemRight:
916 if (atom->string() == ATOM_LIST_TAG) {
919 else if (atom->string() == ATOM_LIST_VALUE) {
920 out() << "</td></tr>\n";
926 case Atom::ListRight:
927 if (atom->string() == ATOM_LIST_BULLET) {
930 else if (atom->string() == ATOM_LIST_TAG) {
933 else if (atom->string() == ATOM_LIST_VALUE) {
934 out() << "</table>\n";
946 case Atom::ParaRight:
952 //if (!matchAhead(atom, Atom::ListItemRight) && !matchAhead(atom, Atom::TableItemRight))
953 // out() << "</p>\n";
955 case Atom::QuotationLeft:
956 out() << "<blockquote>";
958 case Atom::QuotationRight:
959 out() << "</blockquote>\n";
961 case Atom::RawString:
962 out() << atom->string();
964 case Atom::SectionLeft:
965 out() << "<a name=\"" << Doc::canonicalTitle(Text::sectionHeading(atom).toString())
966 << "\"></a>" << divNavTop << '\n';
968 case Atom::SectionRight:
970 case Atom::SectionHeadingLeft:
971 out() << "<h" + QString::number(atom->string().toInt() + hOffset(relative)) + QLatin1Char('>');
972 inSectionHeading_ = true;
974 case Atom::SectionHeadingRight:
975 out() << "</h" + QString::number(atom->string().toInt() + hOffset(relative)) + ">\n";
976 inSectionHeading_ = false;
978 case Atom::SidebarLeft:
980 case Atom::SidebarRight:
983 if (inLink_ && !inContents_ && !inSectionHeading_) {
984 generateLink(atom, marker);
987 out() << protectEnc(atom->string());
990 case Atom::TableLeft:
993 QString attr = "generic";
999 if (atom->count() > 0) {
1000 p1 = atom->string(0);
1001 if (atom->count() > 1)
1002 p2 = atom->string(1);
1004 if (!p1.isEmpty()) {
1005 if (p1 == "borderless")
1007 else if (p1.contains("%"))
1010 if (!p2.isEmpty()) {
1011 if (p2 == "borderless")
1013 else if (p2.contains("%"))
1016 out() << "<table class=\"" << attr << "\"";
1017 if (!width.isEmpty())
1018 out() << " width=\"" << width << "\"";
1023 case Atom::TableRight:
1024 out() << "</table>\n";
1026 case Atom::TableHeaderLeft:
1027 out() << "<thead><tr class=\"qt-style\">";
1028 inTableHeader_ = true;
1030 case Atom::TableHeaderRight:
1032 if (matchAhead(atom, Atom::TableHeaderLeft)) {
1034 out() << "\n<tr class=\"qt-style\">";
1037 out() << "</thead>\n";
1038 inTableHeader_ = false;
1041 case Atom::TableRowLeft:
1042 if (!atom->string().isEmpty())
1043 out() << "<tr " << atom->string() << '>';
1044 else if (++numTableRows_ % 2 == 1)
1045 out() << "<tr valign=\"top\" class=\"odd\">";
1047 out() << "<tr valign=\"top\" class=\"even\">";
1049 case Atom::TableRowRight:
1052 case Atom::TableItemLeft:
1059 for (int i=0; i<atom->count(); ++i) {
1062 QString p = atom->string(i);
1063 if (p.contains('=')) {
1067 QStringList spans = p.split(",");
1068 if (spans.size() == 2) {
1069 if (spans.at(0) != "1")
1070 out() << " colspan=\"" << spans.at(0) << '"';
1071 if (spans.at(1) != "1")
1072 out() << " rowspan=\"" << spans.at(1) << '"';
1082 if (matchAhead(atom, Atom::ParaLeft))
1086 case Atom::TableItemRight:
1091 //out() << "</p></td>";
1093 if (matchAhead(atom, Atom::ParaLeft))
1096 case Atom::TableOfContents:
1099 out() << "<a name=\"" << Doc::canonicalTitle(atom->string()) << "\"></a>";
1101 case Atom::UnhandledFormat:
1102 out() << "<b class=\"redFont\"><Missing HTML></b>";
1104 case Atom::UnknownCommand:
1105 out() << "<b class=\"redFont\"><code>\\" << protectEnc(atom->string())
1109 case Atom::EndQmlText:
1110 // don't do anything with these. They are just tags.
1119 Generate a reference page for a C++ class.
1121 void HtmlGenerator::generateClassLikeNode(InnerNode* inner, CodeMarker* marker)
1123 QList<Section> sections;
1124 QList<Section>::ConstIterator s;
1126 ClassNode* classe = 0;
1131 if (inner->type() == Node::Namespace) {
1132 rawTitle = inner->plainName();
1133 fullTitle = inner->plainFullName();
1134 title = rawTitle + " Namespace";
1136 else if (inner->type() == Node::Class) {
1137 classe = static_cast<ClassNode*>(inner);
1138 rawTitle = inner->plainName();
1139 fullTitle = inner->plainFullName();
1140 title = rawTitle + " Class";
1144 if (rawTitle != fullTitle)
1145 subtitleText << "(" << Atom(Atom::AutoLink, fullTitle) << ")" << Atom(Atom::LineBreak);
1147 generateHeader(title, inner, marker);
1148 sections = marker->sections(inner, CodeMarker::Summary, CodeMarker::Okay);
1149 generateTableOfContents(inner,marker,§ions);
1150 generateTitle(title, subtitleText, SmallSubTitle, inner, marker);
1151 generateBrief(inner, marker);
1152 generateIncludes(inner, marker);
1153 generateStatus(inner, marker);
1155 generateInherits(classe, marker);
1156 generateInheritedBy(classe, marker);
1157 if (classe->qmlElement() != 0)
1158 generateInstantiatedBy(classe,marker);
1160 generateThreadSafeness(inner, marker);
1161 generateSince(inner, marker);
1165 QString membersLink = generateListOfAllMemberFile(inner, marker);
1166 if (!membersLink.isEmpty())
1167 out() << "<li><a href=\"" << membersLink << "\">"
1168 << "List of all members, including inherited members</a></li>\n";
1170 QString obsoleteLink = generateLowStatusMemberFile(inner,
1172 CodeMarker::Obsolete);
1173 if (!obsoleteLink.isEmpty())
1174 out() << "<li><a href=\"" << obsoleteLink << "\">"
1175 << "Obsolete members</a></li>\n";
1177 QString compatLink = generateLowStatusMemberFile(inner,
1179 CodeMarker::Compat);
1180 if (!compatLink.isEmpty())
1181 out() << "<li><a href=\"" << compatLink << "\">"
1182 << "Compatibility members</a></li>\n";
1186 bool needOtherSection = false;
1189 sections is built above for the call to generateTableOfContents().
1191 s = sections.constBegin();
1192 while (s != sections.constEnd()) {
1193 if (s->members.isEmpty() && s->reimpMembers.isEmpty()) {
1194 if (!s->inherited.isEmpty())
1195 needOtherSection = true;
1198 if (!s->members.isEmpty()) {
1199 // out() << "<hr />\n";
1200 out() << "<a name=\""
1201 << registerRef((*s).name.toLower())
1202 << "\"></a>" << divNavTop << "\n";
1203 out() << "<h2>" << protectEnc((*s).name) << "</h2>\n";
1204 generateSection(s->members, inner, marker, CodeMarker::Summary);
1206 if (!s->reimpMembers.isEmpty()) {
1207 QString name = QString("Reimplemented ") + (*s).name;
1208 // out() << "<hr />\n";
1209 out() << "<a name=\""
1210 << registerRef(name.toLower())
1211 << "\"></a>" << divNavTop << "\n";
1212 out() << "<h2>" << protectEnc(name) << "</h2>\n";
1213 generateSection(s->reimpMembers, inner, marker, CodeMarker::Summary);
1216 if (!s->inherited.isEmpty()) {
1218 generateSectionInheritedList(*s, inner);
1225 if (needOtherSection) {
1226 out() << "<h3>Additional Inherited Members</h3>\n"
1229 s = sections.constBegin();
1230 while (s != sections.constEnd()) {
1231 if (s->members.isEmpty() && !s->inherited.isEmpty())
1232 generateSectionInheritedList(*s, inner);
1238 out() << "<a name=\"" << registerRef("details") << "\"></a>" << divNavTop << '\n';
1240 if (!inner->doc().isEmpty()) {
1241 generateExtractionMark(inner, DetailedDescriptionMark);
1242 //out() << "<hr />\n"
1243 out() << "<div class=\"descr\">\n" // QTBUG-9504
1244 << "<h2>" << "Detailed Description" << "</h2>\n";
1245 generateBody(inner, marker);
1246 out() << "</div>\n"; // QTBUG-9504
1247 generateAlsoList(inner, marker);
1248 generateMaintainerList(inner, marker);
1249 generateExtractionMark(inner, EndMark);
1252 sections = marker->sections(inner, CodeMarker::Detailed, CodeMarker::Okay);
1253 s = sections.constBegin();
1254 while (s != sections.constEnd()) {
1255 //out() << "<hr />\n";
1256 if (!(*s).divClass.isEmpty())
1257 out() << "<div class=\"" << (*s).divClass << "\">\n"; // QTBUG-9504
1258 out() << "<h2>" << protectEnc((*s).name) << "</h2>\n";
1260 NodeList::ConstIterator m = (*s).members.constBegin();
1261 while (m != (*s).members.constEnd()) {
1262 if ((*m)->access() != Node::Private) { // ### check necessary?
1263 if ((*m)->type() != Node::Class)
1264 generateDetailedMember(*m, inner, marker);
1266 out() << "<h3> class ";
1267 generateFullName(*m, inner);
1269 generateBrief(*m, marker, inner);
1273 names << (*m)->name();
1274 if ((*m)->type() == Node::Function) {
1275 const FunctionNode *func = reinterpret_cast<const FunctionNode *>(*m);
1276 if (func->metaness() == FunctionNode::Ctor ||
1277 func->metaness() == FunctionNode::Dtor ||
1278 func->overloadNumber() != 1)
1281 else if ((*m)->type() == Node::Property) {
1282 const PropertyNode *prop = reinterpret_cast<const PropertyNode *>(*m);
1283 if (!prop->getters().isEmpty() &&
1284 !names.contains(prop->getters().first()->name()))
1285 names << prop->getters().first()->name();
1286 if (!prop->setters().isEmpty())
1287 names << prop->setters().first()->name();
1288 if (!prop->resetters().isEmpty())
1289 names << prop->resetters().first()->name();
1290 if (!prop->notifiers().isEmpty())
1291 names << prop->notifiers().first()->name();
1293 else if ((*m)->type() == Node::Enum) {
1294 const EnumNode *enume = reinterpret_cast<const EnumNode*>(*m);
1295 if (enume->flagsType())
1296 names << enume->flagsType()->name();
1298 foreach (const QString &enumName,
1299 enume->doc().enumItemNames().toSet() -
1300 enume->doc().omitEnumItemNames().toSet())
1301 names << plainCode(marker->markedUpEnumValue(enumName,
1307 if (!(*s).divClass.isEmpty())
1308 out() << "</div>\n"; // QTBUG-9504
1311 generateFooter(inner);
1315 We delayed generation of the disambiguation pages until now, after
1316 all the other pages have been generated. We do this because we might
1317 encounter a link command that tries to link to a target on a QML
1318 component page, but the link doesn't specify the module identifer
1319 for the component, and the component name without a module
1320 identifier is ambiguous. When such a link is found, qdoc can't find
1321 the target, so it appends the target to the NameCollisionNode. After
1322 the tree has been traversed and all these ambiguous links have been
1323 added to the name collision nodes, this function is called. The list
1324 of collision nodes is traversed here, and the disambiguation page for
1325 each collision is generated. The disambiguation page will not only
1326 disambiguate links to the component pages, but it will also disambiguate
1327 links to properties, section headers, etc.
1329 void HtmlGenerator::generateCollisionPages()
1331 if (collisionNodes.isEmpty())
1334 for (int i=0; i<collisionNodes.size(); ++i) {
1335 NameCollisionNode* ncn = collisionNodes.at(i);
1339 NodeList collisions;
1340 const NodeList& nl = ncn->childNodes();
1341 if (!nl.isEmpty()) {
1342 NodeList::ConstIterator it = nl.constBegin();
1343 while (it != nl.constEnd()) {
1344 if (!(*it)->isInternal())
1345 collisions.append(*it);
1349 if (collisions.size() <= 1)
1352 ncn->clearCurrentChild();
1353 beginSubPage(ncn, Generator::fileName(ncn));
1354 QString fullTitle = ncn->fullTitle();
1355 QString htmlTitle = fullTitle;
1356 CodeMarker* marker = CodeMarker::markerForFileName(ncn->location().filePath());
1357 if (ncn->isQmlNode()) {
1358 // Replace the marker with a QML code marker.
1359 if (ncn->isQmlNode())
1360 marker = CodeMarker::markerForLanguage(QLatin1String("QML"));
1363 generateHeader(htmlTitle, ncn, marker);
1364 if (!fullTitle.isEmpty())
1365 out() << "<h1 class=\"title\">" << protectEnc(fullTitle) << "</h1>\n";
1368 for (int i=0; i<collisions.size(); ++i) {
1369 Node* n = collisions.at(i);
1371 if (!n->qmlModuleIdentifier().isEmpty())
1372 t = n->qmlModuleIdentifier() + "::";
1373 t += protectEnc(fullTitle);
1374 nm.insertMulti(t,n);
1376 generateAnnotatedList(ncn, marker, nm, true);
1378 QList<QString> targets;
1379 if (!ncn->linkTargets().isEmpty()) {
1380 QMap<QString,QString>::ConstIterator t = ncn->linkTargets().constBegin();
1381 while (t != ncn->linkTargets().constEnd()) {
1383 for (int i=0; i<collisions.size(); ++i) {
1384 InnerNode* n = static_cast<InnerNode*>(collisions.at(i));
1385 if (n->findChildNodeByName(t.key())) {
1388 targets.append(t.key());
1396 if (!targets.isEmpty()) {
1397 QList<QString>::ConstIterator t = targets.constBegin();
1398 while (t != targets.constEnd()) {
1399 out() << "<a name=\"" << Doc::canonicalTitle(*t) << "\"></a>";
1400 out() << "<h2 class=\"title\">" << protectEnc(*t) << "</h2>\n";
1402 for (int i=0; i<collisions.size(); ++i) {
1403 InnerNode* n = static_cast<InnerNode*>(collisions.at(i));
1404 Node* p = n->findChildNodeByName(*t);
1406 QString link = linkForNode(p,0);
1408 if (!n->qmlModuleIdentifier().isEmpty())
1409 label = n->qmlModuleIdentifier() + "::";
1410 label += n->name() + "::" + p->name();
1412 out() << "<a href=\"" << link << "\">";
1413 out() << protectEnc(label) << "</a>";
1422 generateFooter(ncn);
1428 Generate the HTML page for an entity that doesn't map
1429 to any underlying parsable C++ class or QML component.
1431 void HtmlGenerator::generateDocNode(DocNode* dn, CodeMarker* marker)
1434 If the document node is a page node, and if the page type
1435 is DITA map page, write the node's contents as a dita
1436 map and return without doing anything else.
1438 if (dn->subType() == Node::Page && dn->pageType() == Node::DitaMapPage) {
1439 const DitaMapNode* dmn = static_cast<const DitaMapNode*>(dn);
1444 SubTitleSize subTitleSize = LargeSubTitle;
1445 QList<Section> sections;
1446 QList<Section>::const_iterator s;
1447 QString fullTitle = dn->fullTitle();
1448 QString htmlTitle = fullTitle;
1450 if (dn->subType() == Node::File && !dn->subTitle().isEmpty()) {
1451 subTitleSize = SmallSubTitle;
1452 htmlTitle += " (" + dn->subTitle() + QLatin1Char(')');
1454 else if (dn->subType() == Node::QmlBasicType) {
1455 fullTitle = "QML Basic Type: " + fullTitle;
1456 htmlTitle = fullTitle;
1458 // Replace the marker with a QML code marker.
1459 marker = CodeMarker::markerForLanguage(QLatin1String("QML"));
1462 generateHeader(htmlTitle, dn, marker);
1464 Generate the TOC for the new doc format.
1465 Don't generate a TOC for the home page.
1467 QmlClassNode* qml_cn = 0;
1468 if (dn->subType() == Node::QmlClass) {
1469 qml_cn = static_cast<QmlClassNode*>(dn);
1470 sections = marker->qmlSections(qml_cn,CodeMarker::Summary);
1471 generateTableOfContents(dn,marker,§ions);
1473 // Replace the marker with a QML code marker.
1474 marker = CodeMarker::markerForLanguage(QLatin1String("QML"));
1476 else if (dn->subType() != Node::Collision && dn->name() != QString("index.html"))
1477 generateTableOfContents(dn,marker,0);
1479 generateTitle(fullTitle,
1480 Text() << dn->subTitle(),
1485 if (dn->subType() == Node::Module) {
1486 // Generate brief text and status for modules.
1487 generateBrief(dn, marker);
1488 generateStatus(dn, marker);
1489 generateSince(dn, marker);
1492 dn->getMemberNamespaces(nm);
1493 if (!nm.isEmpty()) {
1494 out() << "<a name=\"" << registerRef("namespaces") << "\"></a>" << divNavTop << '\n';
1495 out() << "<h2>Namespaces</h2>\n";
1496 generateAnnotatedList(dn, marker, nm);
1499 dn->getMemberClasses(nm);
1500 if (!nm.isEmpty()) {
1501 out() << "<a name=\"" << registerRef("classes") << "\"></a>" << divNavTop << '\n';
1502 out() << "<h2>Classes</h2>\n";
1503 generateAnnotatedList(dn, marker, nm);
1507 else if (dn->subType() == Node::HeaderFile) {
1508 // Generate brief text and status for modules.
1509 generateBrief(dn, marker);
1510 generateStatus(dn, marker);
1511 generateSince(dn, marker);
1515 QString membersLink = generateListOfAllMemberFile(dn, marker);
1516 if (!membersLink.isEmpty())
1517 out() << "<li><a href=\"" << membersLink << "\">"
1518 << "List of all members, including inherited members</a></li>\n";
1520 QString obsoleteLink = generateLowStatusMemberFile(dn,
1522 CodeMarker::Obsolete);
1523 if (!obsoleteLink.isEmpty())
1524 out() << "<li><a href=\"" << obsoleteLink << "\">"
1525 << "Obsolete members</a></li>\n";
1527 QString compatLink = generateLowStatusMemberFile(dn,
1529 CodeMarker::Compat);
1530 if (!compatLink.isEmpty())
1531 out() << "<li><a href=\"" << compatLink << "\">"
1532 << "Compatibility members</a></li>\n";
1536 else if (dn->subType() == Node::QmlClass) {
1537 const_cast<DocNode*>(dn)->setCurrentChild();
1538 ClassNode* cn = qml_cn->classNode();
1539 generateBrief(qml_cn, marker);
1540 generateQmlInherits(qml_cn, marker);
1541 generateQmlInheritedBy(qml_cn, marker);
1542 generateQmlInstantiates(qml_cn, marker);
1543 generateSince(qml_cn, marker);
1545 QString allQmlMembersLink = generateAllQmlMembersFile(qml_cn, marker);
1546 if (!allQmlMembersLink.isEmpty()) {
1548 out() << "<li><a href=\"" << allQmlMembersLink << "\">"
1549 << "List of all members, including inherited members</a></li>\n";
1553 s = sections.constBegin();
1554 while (s != sections.constEnd()) {
1555 out() << "<a name=\"" << registerRef((*s).name.toLower())
1556 << "\"></a>" << divNavTop << '\n';
1557 out() << "<h2>" << protectEnc((*s).name) << "</h2>\n";
1558 generateQmlSummary(*s,dn,marker);
1562 generateExtractionMark(dn, DetailedDescriptionMark);
1563 out() << "<a name=\"" << registerRef("details") << "\"></a>" << divNavTop << '\n';
1564 out() << "<h2>" << "Detailed Description" << "</h2>\n";
1565 generateBody(dn, marker);
1567 generateQmlText(cn->doc().body(), cn, marker, dn->name());
1568 generateAlsoList(dn, marker);
1569 generateExtractionMark(dn, EndMark);
1570 //out() << "<hr />\n";
1572 sections = marker->qmlSections(qml_cn,CodeMarker::Detailed);
1573 s = sections.constBegin();
1574 while (s != sections.constEnd()) {
1575 out() << "<h2>" << protectEnc((*s).name) << "</h2>\n";
1576 NodeList::ConstIterator m = (*s).members.constBegin();
1577 while (m != (*s).members.constEnd()) {
1578 generateDetailedQmlMember(*m, dn, marker);
1585 const_cast<DocNode*>(dn)->clearCurrentChild();
1589 sections = marker->sections(dn, CodeMarker::Summary, CodeMarker::Okay);
1590 s = sections.constBegin();
1591 while (s != sections.constEnd()) {
1592 out() << "<a name=\"" << registerRef((*s).name) << "\"></a>" << divNavTop << '\n';
1593 out() << "<h2>" << protectEnc((*s).name) << "</h2>\n";
1594 generateSectionList(*s, dn, marker, CodeMarker::Summary);
1598 Text brief = dn->doc().briefText();
1599 if (dn->subType() == Node::Module && !brief.isEmpty()) {
1600 generateExtractionMark(dn, DetailedDescriptionMark);
1601 out() << "<a name=\"" << registerRef("details") << "\"></a>" << divNavTop << '\n';
1602 out() << "<div class=\"descr\">\n"; // QTBUG-9504
1603 out() << "<h2>" << "Detailed Description" << "</h2>\n";
1606 generateExtractionMark(dn, DetailedDescriptionMark);
1607 out() << "<div class=\"descr\"> <a name=\"" << registerRef("details") << "\"></a>\n"; // QTBUG-9504
1610 generateBody(dn, marker);
1611 out() << "</div>\n"; // QTBUG-9504
1612 generateAlsoList(dn, marker);
1613 generateExtractionMark(dn, EndMark);
1615 if ((dn->subType() == Node::Group) && !dn->members().isEmpty()) {
1616 NodeMap groupMembersMap;
1617 foreach (const Node *node, dn->members()) {
1618 if (node->type() == Node::Class || node->type() == Node::Namespace)
1619 groupMembersMap[node->name()] = node;
1621 generateAnnotatedList(dn, marker, groupMembersMap);
1623 else if ((dn->subType() == Node::QmlModule) && !dn->members().isEmpty()) {
1624 NodeMap qmlModuleMembersMap;
1625 foreach (const Node* node, dn->members()) {
1626 if (node->type() == Node::Document && node->subType() == Node::QmlClass)
1627 qmlModuleMembersMap[node->name()] = node;
1629 generateAnnotatedList(dn, marker, qmlModuleMembersMap);
1632 sections = marker->sections(dn, CodeMarker::Detailed, CodeMarker::Okay);
1633 s = sections.constBegin();
1634 while (s != sections.constEnd()) {
1635 //out() << "<hr />\n";
1636 out() << "<h2>" << protectEnc((*s).name) << "</h2>\n";
1638 NodeList::ConstIterator m = (*s).members.constBegin();
1639 while (m != (*s).members.constEnd()) {
1640 generateDetailedMember(*m, dn, marker);
1649 Returns "html" for this subclass of Generator.
1651 QString HtmlGenerator::fileExtension() const
1657 Output breadcrumb list in the html file.
1659 void HtmlGenerator::generateBreadCrumbs(const QString &title,
1667 if (node->type() == Node::Class) {
1668 const ClassNode *cn = static_cast<const ClassNode *>(node);
1669 QString name = node->moduleName();
1670 breadcrumbs << Atom(Atom::ListItemLeft)
1671 << Atom(Atom::Link, QLatin1String("All Modules"))
1672 << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK)
1673 << Atom(Atom::String, QLatin1String("Modules"))
1674 << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK)
1675 << Atom(Atom::ListItemRight);
1676 if (!name.isEmpty())
1677 breadcrumbs << Atom(Atom::ListItemLeft)
1678 << Atom(Atom::AutoLink, name)
1679 << Atom(Atom::ListItemRight);
1680 if (!cn->name().isEmpty())
1681 breadcrumbs << Atom(Atom::ListItemLeft)
1682 << Atom(Atom::String, protectEnc(cn->name()))
1683 << Atom(Atom::ListItemRight);
1685 else if (node->type() == Node::Document) {
1686 const DocNode* fn = static_cast<const DocNode*>(node);
1687 if (node->subType() == Node::Module) {
1688 breadcrumbs << Atom(Atom::ListItemLeft)
1689 << Atom(Atom::Link, QLatin1String("All Modules"))
1690 << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK)
1691 << Atom(Atom::String, QLatin1String("Modules"))
1692 << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK)
1693 << Atom(Atom::ListItemRight);
1694 QString name = node->name();
1695 if (!name.isEmpty())
1696 breadcrumbs << Atom(Atom::ListItemLeft)
1697 << Atom(Atom::String, protectEnc(name))
1698 << Atom(Atom::ListItemRight);
1700 else if (node->subType() == Node::Group) {
1701 if (fn->name() == QString("modules"))
1702 breadcrumbs << Atom(Atom::String, QLatin1String("Modules"));
1704 breadcrumbs << Atom(Atom::ListItemLeft)
1705 << Atom(Atom::String, protectEnc(title))
1706 << Atom(Atom::ListItemRight);
1708 else if (node->subType() == Node::Page) {
1709 if (fn->name() == QString("qdeclarativeexamples.html")) {
1710 breadcrumbs << Atom(Atom::ListItemLeft)
1711 << Atom(Atom::Link, QLatin1String("Qt Examples"))
1712 << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK)
1713 << Atom(Atom::String, QLatin1String("Examples"))
1714 << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK)
1715 << Atom(Atom::ListItemRight);
1716 breadcrumbs << Atom(Atom::ListItemLeft)
1717 << Atom(Atom::AutoLink, QLatin1String("QML Examples & Demos"))
1718 << Atom(Atom::ListItemRight);
1720 else if (fn->name().startsWith("examples-")) {
1721 breadcrumbs << Atom(Atom::ListItemLeft)
1722 << Atom(Atom::Link, QLatin1String("Qt Examples"))
1723 << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK)
1724 << Atom(Atom::String, QLatin1String("Examples"))
1725 << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK)
1726 << Atom(Atom::ListItemRight);
1727 breadcrumbs << Atom(Atom::ListItemLeft)
1728 << Atom(Atom::String, protectEnc(title))
1729 << Atom(Atom::ListItemRight);
1731 else if (fn->name() == QString("namespaces.html"))
1732 breadcrumbs << Atom(Atom::String, QLatin1String("Namespaces"));
1734 breadcrumbs << Atom(Atom::ListItemLeft)
1735 << Atom(Atom::String, protectEnc(title))
1736 << Atom(Atom::ListItemRight);
1738 else if (node->subType() == Node::QmlClass) {
1739 breadcrumbs << Atom(Atom::ListItemLeft)
1740 << Atom(Atom::AutoLink, QLatin1String("Basic QML Types"))
1741 << Atom(Atom::ListItemRight);
1742 breadcrumbs << Atom(Atom::ListItemLeft)
1743 << Atom(Atom::String, protectEnc(title))
1744 << Atom(Atom::ListItemRight);
1746 else if (node->subType() == Node::Example) {
1747 breadcrumbs << Atom(Atom::ListItemLeft)
1748 << Atom(Atom::Link, QLatin1String("Qt Examples"))
1749 << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK)
1750 << Atom(Atom::String, QLatin1String("Examples"))
1751 << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK)
1752 << Atom(Atom::ListItemRight);
1753 QStringList sl = fn->name().split('/');
1754 if (sl.contains("declarative"))
1755 breadcrumbs << Atom(Atom::ListItemLeft)
1756 << Atom(Atom::AutoLink, QLatin1String("QML Examples & Demos"))
1757 << Atom(Atom::ListItemRight);
1759 QString name = protectEnc("examples-" + sl.at(0) + ".html"); // this generates an empty link
1760 QString t = CodeParser::titleFromName(name);
1762 breadcrumbs << Atom(Atom::ListItemLeft)
1763 << Atom(Atom::String, protectEnc(title))
1764 << Atom(Atom::ListItemRight);
1767 else if (node->type() == Node::Namespace) {
1768 breadcrumbs << Atom(Atom::ListItemLeft)
1769 << Atom(Atom::Link, QLatin1String("All Namespaces"))
1770 << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK)
1771 << Atom(Atom::String, QLatin1String("Namespaces"))
1772 << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK)
1773 << Atom(Atom::ListItemRight);
1774 breadcrumbs << Atom(Atom::ListItemLeft)
1775 << Atom(Atom::String, protectEnc(title))
1776 << Atom(Atom::ListItemRight);
1779 generateText(breadcrumbs, node, marker);
1782 void HtmlGenerator::generateHeader(const QString& title,
1786 out() << QString("<?xml version=\"1.0\" encoding=\"%1\"?>\n").arg(outputEncoding);
1787 out() << "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n";
1788 out() << QString("<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"%1\" lang=\"%1\">\n").arg(naturalLanguage);
1789 out() << "<head>\n";
1790 out() << " <meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\" />\n";
1791 if (node && !node->doc().location().isEmpty())
1792 out() << "<!-- " << node->doc().location().fileName() << " -->\n";
1794 QString shortVersion = qdb_->version();
1795 if (shortVersion.count(QChar('.')) == 2)
1796 shortVersion.truncate(shortVersion.lastIndexOf(QChar('.')));
1797 if (!project.isEmpty())
1798 shortVersion = project + QLatin1Char(' ') + shortVersion + QLatin1String(": ");
1800 shortVersion = QLatin1String("Qt ") + shortVersion + QLatin1String(": ");
1802 // Generating page title
1803 out() << " <title>" << shortVersion << protectEnc(title) << "</title>\n";
1805 // Include style sheet and script links.
1806 out() << headerStyles;
1807 out() << headerScripts;
1808 if (endHeader.isEmpty())
1809 out() << "</head>\n<body>\n";
1813 #ifdef GENERATE_MAC_REFS
1815 generateMacRef(node, marker);
1818 out() << QString(postHeader).replace("\\" + COMMAND_VERSION, qdb_->version());
1819 generateBreadCrumbs(title,node,marker);
1820 out() << QString(postPostHeader).replace("\\" + COMMAND_VERSION, qdb_->version());
1822 navigationLinks.clear();
1824 if (node && !node->links().empty()) {
1825 QPair<QString,QString> linkPair;
1826 QPair<QString,QString> anchorPair;
1827 const Node *linkNode;
1829 if (node->links().contains(Node::PreviousLink)) {
1830 linkPair = node->links()[Node::PreviousLink];
1831 linkNode = qdb_->findNodeForTarget(linkPair.first, node);
1833 node->doc().location().warning(tr("Cannot link to '%1'").arg(linkPair.first));
1834 if (!linkNode || linkNode == node)
1835 anchorPair = linkPair;
1837 anchorPair = anchorForNode(linkNode);
1839 out() << " <link rel=\"prev\" href=\""
1840 << anchorPair.first << "\" />\n";
1842 navigationLinks += "<a class=\"prevPage\" href=\"" + anchorPair.first + "\">";
1843 if (linkPair.first == linkPair.second && !anchorPair.second.isEmpty())
1844 navigationLinks += protect(anchorPair.second);
1846 navigationLinks += protect(linkPair.second);
1847 navigationLinks += "</a>\n";
1849 if (node->links().contains(Node::NextLink)) {
1850 linkPair = node->links()[Node::NextLink];
1851 linkNode = qdb_->findNodeForTarget(linkPair.first, node);
1853 node->doc().location().warning(tr("Cannot link to '%1'").arg(linkPair.first));
1854 if (!linkNode || linkNode == node)
1855 anchorPair = linkPair;
1857 anchorPair = anchorForNode(linkNode);
1859 out() << " <link rel=\"next\" href=\""
1860 << anchorPair.first << "\" />\n";
1862 navigationLinks += "<a class=\"nextPage\" href=\"" + anchorPair.first + "\">";
1863 if (linkPair.first == linkPair.second && !anchorPair.second.isEmpty())
1864 navigationLinks += protect(anchorPair.second);
1866 navigationLinks += protect(linkPair.second);
1867 navigationLinks += "</a>\n";
1869 if (node->links().contains(Node::StartLink)) {
1870 linkPair = node->links()[Node::StartLink];
1871 linkNode = qdb_->findNodeForTarget(linkPair.first, node);
1873 node->doc().location().warning(tr("Cannot link to '%1'").arg(linkPair.first));
1874 if (!linkNode || linkNode == node)
1875 anchorPair = linkPair;
1877 anchorPair = anchorForNode(linkNode);
1878 out() << " <link rel=\"start\" href=\""
1879 << anchorPair.first << "\" />\n";
1883 if (node && !node->links().empty())
1884 out() << "<p class=\"naviNextPrevious headerNavi\">\n" << navigationLinks << "</p><p/>\n";
1887 void HtmlGenerator::generateTitle(const QString& title,
1888 const Text &subTitle,
1889 SubTitleSize subTitleSize,
1890 const Node *relative,
1893 if (!title.isEmpty())
1894 out() << "<h1 class=\"title\">" << protectEnc(title) << "</h1>\n";
1895 if (!subTitle.isEmpty()) {
1897 if (subTitleSize == SmallSubTitle)
1898 out() << " class=\"small-subtitle\">";
1900 out() << " class=\"subtitle\">";
1901 generateText(subTitle, relative, marker);
1902 out() << "</span>\n";
1906 void HtmlGenerator::generateFooter(const Node *node)
1908 if (node && !node->links().empty())
1909 out() << "<p class=\"naviNextPrevious footerNavi\">\n" << navigationLinks << "</p>\n";
1911 out() << QString(footer).replace("\\" + COMMAND_VERSION, qdb_->version())
1912 << QString(address).replace("\\" + COMMAND_VERSION, qdb_->version());
1914 out() << "</body>\n";
1915 out() << "</html>\n";
1918 void HtmlGenerator::generateBrief(const Node *node, CodeMarker *marker,
1919 const Node *relative)
1921 Text brief = node->doc().briefText();
1922 if (!brief.isEmpty()) {
1923 generateExtractionMark(node, BriefMark);
1925 generateText(brief, node, marker);
1927 if (!relative || node == relative)
1928 out() << " <a href=\"#";
1930 out() << " <a href=\"" << linkForNode(node, relative) << '#';
1931 out() << registerRef("details") << "\">More...</a></p>\n";
1934 generateExtractionMark(node, EndMark);
1938 void HtmlGenerator::generateIncludes(const InnerNode *inner, CodeMarker *marker)
1940 if (!inner->includes().isEmpty()) {
1941 out() << "<pre class=\"cpp\">"
1942 << trimmedTrailing(highlightedCode(indent(codeIndent,
1943 marker->markedUpIncludes(inner->includes())),
1950 Revised for the new doc format.
1951 Generates a table of contents beginning at \a node.
1953 void HtmlGenerator::generateTableOfContents(const Node *node,
1955 QList<Section>* sections)
1958 if (node->doc().hasTableOfContents())
1959 toc = node->doc().tableOfContents();
1960 if (toc.isEmpty() && !sections && (node->subType() != Node::Module))
1963 QStringList sectionNumber;
1964 int detailsBase = 0;
1966 // disable nested links in table of contents
1970 out() << "<div class=\"toc\">\n";
1971 out() << "<h3><a name=\"toc\">Contents</a></h3>\n";
1972 sectionNumber.append("1");
1975 if (node->subType() == Node::Module) {
1976 if (node->hasNamespaces()) {
1977 out() << "<li class=\"level"
1978 << sectionNumber.size()
1980 << registerRef("namespaces")
1981 << "\">Namespaces</a></li>\n";
1983 if (node->hasClasses()) {
1984 out() << "<li class=\"level"
1985 << sectionNumber.size()
1987 << registerRef("classes")
1988 << "\">Classes</a></li>\n";
1990 out() << "<li class=\"level"
1991 << sectionNumber.size()
1993 << registerRef("details")
1994 << "\">Detailed Description</a></li>\n";
1995 for (int i = 0; i < toc.size(); ++i) {
1996 if (toc.at(i)->string().toInt() == 1) {
2002 else if (sections && ((node->type() == Node::Class) ||
2003 (node->type() == Node::Namespace) ||
2004 (node->subType() == Node::QmlClass))) {
2005 QList<Section>::ConstIterator s = sections->constBegin();
2006 while (s != sections->constEnd()) {
2007 if (!s->members.isEmpty() || !s->reimpMembers.isEmpty()) {
2008 out() << "<li class=\"level"
2009 << sectionNumber.size()
2011 << registerRef((*s).pluralMember)
2012 << "\">" << (*s).name
2017 out() << "<li class=\"level"
2018 << sectionNumber.size()
2020 << registerRef("details")
2021 << "\">Detailed Description</a></li>\n";
2022 for (int i = 0; i < toc.size(); ++i) {
2023 if (toc.at(i)->string().toInt() == 1) {
2030 for (int i = 0; i < toc.size(); ++i) {
2031 Atom *atom = toc.at(i);
2032 int nextLevel = atom->string().toInt() + detailsBase;
2033 if (nextLevel >= 0) {
2034 if (sectionNumber.size() < nextLevel) {
2036 sectionNumber.append("1");
2037 } while (sectionNumber.size() < nextLevel);
2040 while (sectionNumber.size() > nextLevel) {
2041 sectionNumber.removeLast();
2043 sectionNumber.last() = QString::number(sectionNumber.last().toInt() + 1);
2047 Text headingText = Text::sectionHeading(atom);
2048 QString s = headingText.toString();
2049 out() << "<li class=\"level"
2050 << sectionNumber.size()
2052 out() << "<a href=\""
2054 << Doc::canonicalTitle(s)
2056 generateAtomList(headingText.firstAtom(), node, marker, true, numAtoms);
2057 out() << "</a></li>\n";
2059 while (!sectionNumber.isEmpty()) {
2060 sectionNumber.removeLast();
2063 out() << "</div>\n";
2064 inContents_ = false;
2068 QString HtmlGenerator::generateListOfAllMemberFile(const InnerNode *inner,
2071 QList<Section> sections;
2072 QList<Section>::ConstIterator s;
2074 sections = marker->sections(inner,
2075 CodeMarker::Subpage,
2077 if (sections.isEmpty())
2080 QString fileName = fileBase(inner) + "-members." + fileExtension();
2081 beginSubPage(inner, fileName);
2082 QString title = "List of All Members for " + inner->name();
2083 generateHeader(title, inner, marker);
2084 generateTitle(title, Text(), SmallSubTitle, inner, marker);
2085 out() << "<p>This is the complete list of members for ";
2086 generateFullName(inner, 0);
2087 out() << ", including inherited members.</p>\n";
2089 Section section = sections.first();
2090 generateSectionList(section, 0, marker, CodeMarker::Subpage);
2098 This function creates an html page on which are listed all
2099 the members of QML class \a qml_cn, including the inherited
2100 members. The \a marker is used for formatting stuff.
2102 QString HtmlGenerator::generateAllQmlMembersFile(const QmlClassNode* qml_cn,
2105 QList<Section> sections;
2106 QList<Section>::ConstIterator s;
2108 sections = marker->qmlSections(qml_cn,CodeMarker::Subpage);
2109 if (sections.isEmpty())
2112 QString fileName = fileBase(qml_cn) + "-members." + fileExtension();
2113 beginSubPage(qml_cn, fileName);
2114 QString title = "List of All Members for " + qml_cn->name();
2115 generateHeader(title, qml_cn, marker);
2116 generateTitle(title, Text(), SmallSubTitle, qml_cn, marker);
2117 out() << "<p>This is the complete list of members for ";
2118 generateFullName(qml_cn, 0);
2119 out() << ", including inherited members.</p>\n";
2121 Section section = sections.first();
2122 generateSectionList(section, 0, marker, CodeMarker::Subpage);
2129 QString HtmlGenerator::generateLowStatusMemberFile(const InnerNode *inner,
2131 CodeMarker::Status status)
2133 QList<Section> sections = marker->sections(inner,
2134 CodeMarker::Summary,
2136 QMutableListIterator<Section> j(sections);
2137 while (j.hasNext()) {
2138 if (j.next().members.size() == 0)
2141 if (sections.isEmpty())
2149 if (status == CodeMarker::Compat) {
2150 title = "Compatibility Members for " + inner->name();
2151 fileName = fileBase(inner) + "-compat." + fileExtension();
2154 title = "Obsolete Members for " + inner->name();
2155 fileName = fileBase(inner) + "-obsolete." + fileExtension();
2158 beginSubPage(inner, fileName);
2159 generateHeader(title, inner, marker);
2160 generateTitle(title, Text(), SmallSubTitle, inner, marker);
2162 if (status == CodeMarker::Compat) {
2163 out() << "<p><b>The following class members are part of the "
2164 "Qt compatibility layer.</b> We advise against "
2165 "using them in new code.</p>\n";
2168 out() << "<p><b>The following class members are obsolete.</b> "
2169 << "They are provided to keep old source code working. "
2170 << "We strongly advise against using them in new code.</p>\n";
2173 out() << "<p><ul><li><a href=\""
2174 << linkForNode(inner, 0) << "\">"
2175 << protectEnc(inner->name())
2176 << " class reference</a></li></ul></p>\n";
2178 for (i = 0; i < sections.size(); ++i) {
2179 out() << "<h2>" << protectEnc(sections.at(i).name) << "</h2>\n";
2180 generateSectionList(sections.at(i), inner, marker, CodeMarker::Summary);
2183 sections = marker->sections(inner, CodeMarker::Detailed, status);
2184 for (i = 0; i < sections.size(); ++i) {
2185 //out() << "<hr />\n";
2186 out() << "<h2>" << protectEnc(sections.at(i).name) << "</h2>\n";
2188 NodeList::ConstIterator m = sections.at(i).members.constBegin();
2189 while (m != sections.at(i).members.constEnd()) {
2190 if ((*m)->access() != Node::Private)
2191 generateDetailedMember(*m, inner, marker);
2201 void HtmlGenerator::generateClassHierarchy(const Node *relative, const NodeMap& classMap)
2203 if (classMap.isEmpty())
2207 NodeMap::ConstIterator c = classMap.constBegin();
2208 while (c != classMap.constEnd()) {
2209 const ClassNode *classe = static_cast<const ClassNode *>(*c);
2210 if (classe->baseClasses().isEmpty())
2211 topLevel.insert(classe->name(), classe);
2215 QStack<NodeMap > stack;
2216 stack.push(topLevel);
2219 while (!stack.isEmpty()) {
2220 if (stack.top().isEmpty()) {
2225 const ClassNode *child =
2226 static_cast<const ClassNode *>(*stack.top().constBegin());
2228 generateFullName(child, relative);
2230 stack.top().erase(stack.top().begin());
2233 foreach (const RelatedClass &d, child->derivedClasses()) {
2234 if (d.access != Node::Private && !d.node->doc().isEmpty())
2235 newTop.insert(d.node->name(), d.node);
2237 if (!newTop.isEmpty()) {
2245 void HtmlGenerator::generateAnnotatedList(const Node *relative,
2247 const NodeMap &nodeMap,
2250 out() << "<table class=\"annotated\">\n";
2253 foreach (const QString &name, nodeMap.keys()) {
2254 const Node *node = nodeMap[name];
2256 if (node->status() == Node::Obsolete)
2259 if (allOdd || (++row % 2 == 1))
2260 out() << "<tr class=\"odd topAlign\">";
2262 out() << "<tr class=\"even topAlign\">";
2263 out() << "<td class=\"tblName\"><p>";
2264 generateFullName(node, relative);
2265 out() << "</p></td>";
2267 if (!(node->type() == Node::Document)) {
2268 Text brief = node->doc().trimmedBriefText(name);
2269 if (!brief.isEmpty()) {
2270 out() << "<td class=\"tblDescr\"><p>";
2271 generateText(brief, node, marker);
2272 out() << "</p></td>";
2276 out() << "<td class=\"tblDescr\"><p>";
2277 out() << protectEnc(node->doc().briefText().toString());
2278 out() << "</p></td>";
2282 out() << "</table>\n";
2286 This function finds the common prefix of the names of all
2287 the classes in \a classMap and then generates a compact
2288 list of the class names alphabetized on the part of the
2289 name not including the common prefix. You can tell the
2290 function to use \a comonPrefix as the common prefix, but
2291 normally you let it figure it out itself by looking at
2292 the name of the first and last classes in \a classMap.
2294 void HtmlGenerator::generateCompactList(const Node *relative,
2295 const NodeMap &classMap,
2296 bool includeAlphabet,
2297 QString commonPrefix)
2299 const int NumParagraphs = 37; // '0' to '9', 'A' to 'Z', '_'
2301 if (classMap.isEmpty())
2305 If commonPrefix is not empty, then the caller knows what
2306 the common prefix is and has passed it in, so just use that
2307 one. But if the commonPrefix is empty (it normally is), then
2308 compute a common prefix using this simple algorithm. Note we
2309 assume the prefix length is 1, i.e. we will have a single
2310 character as the common prefix.
2312 int commonPrefixLen = commonPrefix.length();
2313 if (commonPrefixLen == 0) {
2314 QVector<int> count(26);
2315 for (int i=0; i<26; ++i)
2318 NodeMap::const_iterator iter = classMap.constBegin();
2319 while (iter != classMap.constEnd()) {
2320 if (!iter.key().contains("::")) {
2321 QChar c = iter.key()[0];
2322 if ((c >= 'A') && (c <= 'Z')) {
2323 int idx = c.unicode() - QChar('A').unicode();
2331 for (int i=0; i<26; ++i) {
2332 if (count[i] > highest) {
2337 idx += QChar('A').unicode();
2339 commonPrefix = common;
2340 commonPrefixLen = 1;
2344 Divide the data into 37 paragraphs: 0, ..., 9, A, ..., Z,
2345 underscore (_). QAccel will fall in paragraph 10 (A) and
2346 QXtWidget in paragraph 33 (X). This is the only place where we
2347 assume that NumParagraphs is 37. Each paragraph is a NodeMap.
2349 NodeMap paragraph[NumParagraphs+1];
2350 QString paragraphName[NumParagraphs+1];
2351 QSet<char> usedParagraphNames;
2353 NodeMap::ConstIterator c = classMap.constBegin();
2354 while (c != classMap.constEnd()) {
2355 QStringList pieces = c.key().split("::");
2357 int idx = commonPrefixLen;
2358 if (!pieces.last().startsWith(commonPrefix))
2360 if (pieces.size() == 1)
2361 key = pieces.last().mid(idx).toLower();
2363 key = pieces.last().toLower();
2365 int paragraphNr = NumParagraphs - 1;
2367 if (key[0].digitValue() != -1) {
2368 paragraphNr = key[0].digitValue();
2370 else if (key[0] >= QLatin1Char('a') && key[0] <= QLatin1Char('z')) {
2371 paragraphNr = 10 + key[0].unicode() - 'a';
2374 paragraphName[paragraphNr] = key[0].toUpper();
2375 usedParagraphNames.insert(key[0].toLower().cell());
2376 paragraph[paragraphNr].insert(key, c.value());
2381 Each paragraph j has a size: paragraph[j].count(). In the
2382 discussion, we will assume paragraphs 0 to 5 will have sizes
2385 We now want to compute the paragraph offset. Paragraphs 0 to 6
2386 start at offsets 0, 3, 4, 8, 9, 14, 23.
2388 int paragraphOffset[NumParagraphs + 1]; // 37 + 1
2389 paragraphOffset[0] = 0;
2390 for (int i=0; i<NumParagraphs; i++) // i = 0..36
2391 paragraphOffset[i+1] = paragraphOffset[i] + paragraph[i].count();
2394 Output the alphabet as a row of links.
2396 if (includeAlphabet) {
2397 out() << "<p class=\"centerAlign functionIndex\"><b>";
2398 for (int i = 0; i < 26; i++) {
2400 if (usedParagraphNames.contains(char('a' + i)))
2401 out() << QString("<a href=\"#%1\">%2</a> ").arg(ch).arg(ch.toUpper());
2403 out() << "</b></p>\n";
2407 Output a <div> element to contain all the <dl> elements.
2409 out() << "<div class=\"flowListDiv\">\n";
2413 int curParOffset = 0;
2415 for (int i=0; i<classMap.count(); i++) {
2416 while ((curParNr < NumParagraphs) &&
2417 (curParOffset == paragraph[curParNr].count())) {
2423 Starting a new paragraph means starting a new <dl>.
2425 if (curParOffset == 0) {
2428 if (++numTableRows_ % 2 == 1)
2429 out() << "<dl class=\"flowList odd\">";
2431 out() << "<dl class=\"flowList even\">";
2432 out() << "<dt class=\"alphaChar\">";
2433 if (includeAlphabet) {
2434 QChar c = paragraphName[curParNr][0].toLower();
2435 out() << QString("<a name=\"%1\"></a>").arg(c);
2438 << paragraphName[curParNr]
2444 Output a <dd> for the current offset in the current paragraph.
2447 if ((curParNr < NumParagraphs) &&
2448 !paragraphName[curParNr].isEmpty()) {
2449 NodeMap::Iterator it;
2450 it = paragraph[curParNr].begin();
2451 for (int i=0; i<curParOffset; i++)
2455 Previously, we used generateFullName() for this, but we
2456 require some special formatting.
2458 out() << "<a href=\"" << linkForNode(it.value(), relative) << "\">";
2461 if (it.value()->subType() == Node::QmlClass)
2462 pieces << it.value()->name();
2464 pieces = it.value()->fullName(relative).split("::");
2465 out() << protectEnc(pieces.last());
2467 if (pieces.size() > 1) {
2469 generateFullName(it.value()->parent(), relative);
2476 if (classMap.count() > 0)
2479 out() << "</div>\n";
2482 void HtmlGenerator::generateFunctionIndex(const Node *relative)
2484 out() << "<p class=\"centerAlign functionIndex\"><b>";
2485 for (int i = 0; i < 26; i++) {
2487 out() << QString("<a href=\"#%1\">%2</a> ").arg(ch).arg(ch.toUpper());
2489 out() << "</b></p>\n";
2491 char nextLetter = 'a';
2495 NodeMapMap funcIndex = qdb_->getFunctionIndex();
2496 QMap<QString, NodeMap >::ConstIterator f = funcIndex.constBegin();
2497 while (f != funcIndex.constEnd()) {
2499 out() << protectEnc(f.key()) << ':';
2501 currentLetter = f.key()[0].unicode();
2502 while (islower(currentLetter) && currentLetter >= nextLetter) {
2503 out() << QString("<a name=\"%1\"></a>").arg(nextLetter);
2507 NodeMap::ConstIterator s = (*f).constBegin();
2508 while (s != (*f).constEnd()) {
2510 generateFullName((*s)->parent(), relative, *s);
2520 void HtmlGenerator::generateLegaleseList(const Node *relative, CodeMarker *marker)
2522 TextToNodeMap& legaleseTexts = qdb_->getLegaleseTexts();
2523 QMap<Text, const Node *>::ConstIterator it = legaleseTexts.constBegin();
2524 while (it != legaleseTexts.constEnd()) {
2525 Text text = it.key();
2526 //out() << "<hr />\n";
2527 generateText(text, relative, marker);
2531 generateFullName(it.value(), relative);
2534 } while (it != legaleseTexts.constEnd() && it.key() == text);
2539 void HtmlGenerator::generateQmlItem(const Node *node,
2540 const Node *relative,
2544 QString marked = marker->markedUpQmlItem(node,summary);
2545 QRegExp templateTag("(<[^@>]*>)");
2546 if (marked.indexOf(templateTag) != -1) {
2547 QString contents = protectEnc(marked.mid(templateTag.pos(1),
2548 templateTag.cap(1).length()));
2549 marked.replace(templateTag.pos(1), templateTag.cap(1).length(),
2552 marked.replace(QRegExp("<@param>([a-z]+)_([1-9n])</@param>"),
2553 "<i>\\1<sub>\\2</sub></i>");
2554 marked.replace("<@param>", "<i>");
2555 marked.replace("</@param>", "</i>");
2558 marked.replace("@name>", "b>");
2560 marked.replace("<@extra>", "<tt>");
2561 marked.replace("</@extra>", "</tt>");
2564 marked.remove("<@type>");
2565 marked.remove("</@type>");
2567 out() << highlightedCode(marked, relative, false, node);
2570 void HtmlGenerator::generateOverviewList(const Node *relative)
2572 QMap<const DocNode *, QMap<QString, DocNode *> > docNodeMap;
2573 QMap<QString, const DocNode *> groupTitlesMap;
2574 QMap<QString, DocNode *> uncategorizedNodeMap;
2575 QRegExp singleDigit("\\b([0-9])\\b");
2577 const NodeList children = qdb_->treeRoot()->childNodes();
2578 foreach (Node *child, children) {
2579 if (child->type() == Node::Document && child != relative) {
2580 DocNode *docNode = static_cast<DocNode *>(child);
2582 // Check whether the page is part of a group or is the group
2585 bool isGroupPage = false;
2586 if (docNode->doc().metaCommandsUsed().contains("group")) {
2587 group = docNode->doc().metaCommandArgs("group")[0].first;
2591 // there are too many examples; they would clutter the list
2592 if (docNode->subType() == Node::Example)
2595 // not interested either in individual (Qt Designer etc.) manual chapters
2596 if (docNode->links().contains(Node::ContentsLink))
2599 // Discard external nodes.
2600 if (docNode->subType() == Node::ExternalPage)
2603 QString sortKey = docNode->fullTitle().toLower();
2604 if (sortKey.startsWith("the "))
2605 sortKey.remove(0, 4);
2606 sortKey.replace(singleDigit, "0\\1");
2608 if (!group.isEmpty()) {
2610 // If we encounter a group definition page, we add all
2611 // the pages in that group to the list for that group.
2612 foreach (Node *member, docNode->members()) {
2613 if (member->type() != Node::Document)
2615 DocNode *page = static_cast<DocNode *>(member);
2617 QString sortKey = page->fullTitle().toLower();
2618 if (sortKey.startsWith("the "))
2619 sortKey.remove(0, 4);
2620 sortKey.replace(singleDigit, "0\\1");
2621 docNodeMap[const_cast<const DocNode *>(docNode)].insert(sortKey, page);
2622 groupTitlesMap[docNode->fullTitle()] = const_cast<const DocNode *>(docNode);
2626 else if (!isGroupPage) {
2627 // If we encounter a page that belongs to a group then
2628 // we add that page to the list for that group.
2629 const DocNode* gn = qdb_->findGroupNode(QStringList(group));
2631 docNodeMap[gn].insert(sortKey, docNode);
2637 // We now list all the pages found that belong to groups.
2638 // If only certain pages were found for a group, but the definition page
2639 // for that group wasn't listed, the list of pages will be intentionally
2640 // incomplete. However, if the group definition page was listed, all the
2641 // pages in that group are listed for completeness.
2643 if (!docNodeMap.isEmpty()) {
2644 foreach (const QString &groupTitle, groupTitlesMap.keys()) {
2645 const DocNode *groupNode = groupTitlesMap[groupTitle];
2646 out() << QString("<h3><a href=\"%1\">%2</a></h3>\n").arg(
2647 linkForNode(groupNode, relative)).arg(
2648 protectEnc(groupNode->fullTitle()));
2650 if (docNodeMap[groupNode].count() == 0)
2655 foreach (const DocNode *docNode, docNodeMap[groupNode]) {
2656 QString title = docNode->fullTitle();
2657 if (title.startsWith("The "))
2659 out() << "<li><a href=\"" << linkForNode(docNode, relative) << "\">"
2660 << protectEnc(title) << "</a></li>\n";
2666 if (!uncategorizedNodeMap.isEmpty()) {
2667 out() << QString("<h3>Miscellaneous</h3>\n");
2669 foreach (const DocNode *docNode, uncategorizedNodeMap) {
2670 QString title = docNode->fullTitle();
2671 if (title.startsWith("The "))
2673 out() << "<li><a href=\"" << linkForNode(docNode, relative) << "\">"
2674 << protectEnc(title) << "</a></li>\n";
2680 void HtmlGenerator::generateSection(const NodeList& nl,
2681 const Node *relative,
2683 CodeMarker::SynopsisStyle style)
2685 bool alignNames = true;
2686 if (!nl.isEmpty()) {
2687 bool twoColumn = false;
2688 if (style == CodeMarker::Subpage) {
2690 twoColumn = (nl.count() >= 16);
2692 else if (nl.first()->type() == Node::Property) {
2693 twoColumn = (nl.count() >= 5);
2697 out() << "<table class=\"alignedsummary\">\n";
2701 out() << "<table class=\"propsummary\">\n"
2702 << "<tr><td class=\"topAlign\">";
2707 NodeList::ConstIterator m = nl.constBegin();
2708 while (m != nl.constEnd()) {
2709 if ((*m)->access() == Node::Private) {
2715 out() << "<tr><td class=\"memItemLeft rightAlign topAlign\"> ";
2718 if (twoColumn && i == (int) (nl.count() + 1) / 2)
2719 out() << "</ul></td><td class=\"topAlign\"><ul>\n";
2720 out() << "<li class=\"fn\">";
2723 generateSynopsis(*m, relative, marker, style, alignNames);
2725 out() << "</td></tr>\n";
2732 out() << "</table>\n";
2736 out() << "</td></tr>\n</table>\n";
2741 void HtmlGenerator::generateSectionList(const Section& section,
2742 const Node *relative,
2744 CodeMarker::SynopsisStyle style)
2746 bool alignNames = true;
2747 if (!section.members.isEmpty()) {
2748 bool twoColumn = false;
2749 if (style == CodeMarker::Subpage) {
2751 twoColumn = (section.members.count() >= 16);
2753 else if (section.members.first()->type() == Node::Property) {
2754 twoColumn = (section.members.count() >= 5);
2758 out() << "<table class=\"alignedsummary\">\n";
2762 out() << "<table class=\"propsummary\">\n"
2763 << "<tr><td class=\"topAlign\">";
2768 NodeList::ConstIterator m = section.members.constBegin();
2769 while (m != section.members.constEnd()) {
2770 if ((*m)->access() == Node::Private) {
2776 out() << "<tr><td class=\"memItemLeft topAlign rightAlign\"> ";
2779 if (twoColumn && i == (int) (section.members.count() + 1) / 2)
2780 out() << "</ul></td><td class=\"topAlign\"><ul>\n";
2781 out() << "<li class=\"fn\">";
2785 if (!section.keys.isEmpty()) {
2786 prefix = section.keys.at(i).mid(1);
2787 prefix = prefix.left(section.keys.at(i).indexOf("::")+1);
2789 generateSynopsis(*m, relative, marker, style, alignNames, &prefix);
2791 out() << "</td></tr>\n";
2798 out() << "</table>\n";
2802 out() << "</td></tr>\n</table>\n";
2806 if (style == CodeMarker::Summary && !section.inherited.isEmpty()) {
2808 generateSectionInheritedList(section, relative);
2813 void HtmlGenerator::generateSectionInheritedList(const Section& section, const Node *relative)
2815 QList<QPair<InnerNode *, int> >::ConstIterator p = section.inherited.constBegin();
2816 while (p != section.inherited.constEnd()) {
2817 out() << "<li class=\"fn\">";
2818 out() << (*p).second << ' ';
2819 if ((*p).second == 1) {
2820 out() << section.singularMember;
2823 out() << section.pluralMember;
2825 out() << " inherited from <a href=\"" << fileName((*p).first)
2826 << '#' << HtmlGenerator::cleanRef(section.name.toLower()) << "\">"
2827 << protectEnc((*p).first->plainFullName(relative))
2833 void HtmlGenerator::generateSynopsis(const Node *node,
2834 const Node *relative,
2836 CodeMarker::SynopsisStyle style,
2838 const QString* prefix)
2840 QString marked = marker->markedUpSynopsis(node, relative, style);
2843 marked.prepend(*prefix);
2844 QRegExp templateTag("(<[^@>]*>)");
2845 if (marked.indexOf(templateTag) != -1) {
2846 QString contents = protectEnc(marked.mid(templateTag.pos(1),
2847 templateTag.cap(1).length()));
2848 marked.replace(templateTag.pos(1), templateTag.cap(1).length(),
2851 marked.replace(QRegExp("<@param>([a-z]+)_([1-9n])</@param>"),
2852 "<i>\\1<sub>\\2</sub></i>");
2853 marked.replace("<@param>", "<i> ");
2854 marked.replace("</@param>", "</i>");
2856 if (style == CodeMarker::Summary) {
2857 marked.remove("<@name>"); // was "<b>"
2858 marked.remove("</@name>"); // was "</b>"
2861 if (style == CodeMarker::Subpage) {
2862 QRegExp extraRegExp("<@extra>.*</@extra>");
2863 extraRegExp.setMinimal(true);
2864 marked.remove(extraRegExp);
2866 marked.replace("<@extra>", "<tt>");
2867 marked.replace("</@extra>", "</tt>");
2870 if (style != CodeMarker::Detailed) {
2871 marked.remove("<@type>");
2872 marked.remove("</@type>");
2875 out() << highlightedCode(marked, relative, alignNames);
2878 QString HtmlGenerator::highlightedCode(const QString& markedCode,
2879 const Node* relative,
2883 QString src = markedCode;
2888 const QChar charLangle = '<';
2889 const QChar charAt = '@';
2891 static const QString typeTag("type");
2892 static const QString headerTag("headerfile");
2893 static const QString funcTag("func");
2894 static const QString linkTag("link");
2896 // replace all <@link> tags: "(<@link node=\"([^\"]+)\">).*(</@link>)"
2898 for (int i = 0, srcSize = src.size(); i < srcSize;) {
2899 if (src.at(i) == charLangle && src.at(i + 1) == charAt) {
2900 if (alignNames && !done) {
2901 html += "</td><td class=\"memItemRight bottomAlign\">";
2905 if (parseArg(src, linkTag, &i, srcSize, &arg, &par1)) {
2907 const Node* n = CodeMarker::nodeForString(par1.toString());
2908 QString link = linkForNode(n, relative);
2909 addLink(link, arg, &html);
2918 html += src.at(i++);
2922 // replace all <@func> tags: "(<@func target=\"([^\"]*)\">)(.*)(</@func>)"
2925 for (int i = 0, srcSize = src.size(); i < srcSize;) {
2926 if (src.at(i) == charLangle && src.at(i + 1) == charAt) {
2928 if (parseArg(src, funcTag, &i, srcSize, &arg, &par1)) {
2930 const Node* n = qdb_->resolveTarget(par1.toString(), relative);
2931 QString link = linkForNode(n, relative);
2932 addLink(link, arg, &html);
2933 par1 = QStringRef();
2941 html += src.at(i++);
2945 // replace all "(<@(type|headerfile|func)(?: +[^>]*)?>)(.*)(</@\\2>)" tags
2949 for (int i=0, srcSize=src.size(); i<srcSize;) {
2950 if (src.at(i) == charLangle && src.at(i+1) == charAt) {
2952 bool handled = false;
2953 if (parseArg(src, typeTag, &i, srcSize, &arg, &par1)) {
2954 par1 = QStringRef();
2955 const Node* n = qdb_->resolveTarget(arg.toString(), relative, self);
2956 html += QLatin1String("<span class=\"type\">");
2957 if (n && n->subType() == Node::QmlBasicType) {
2958 if (relative && relative->subType() == Node::QmlClass)
2959 addLink(linkForNode(n,relative), arg, &html);
2961 html += arg.toString();
2964 addLink(linkForNode(n,relative), arg, &html);
2965 html += QLatin1String("</span>");
2968 else if (parseArg(src, headerTag, &i, srcSize, &arg, &par1)) {
2969 par1 = QStringRef();
2970 const Node* n = qdb_->resolveTarget(arg.toString(), relative);
2971 addLink(linkForNode(n,relative), arg, &html);
2974 else if (parseArg(src, funcTag, &i, srcSize, &arg, &par1)) {
2975 par1 = QStringRef();
2976 const Node* n = qdb_->resolveTarget(arg.toString(), relative);
2977 addLink(linkForNode(n,relative), arg, &html);
2987 html += src.at(i++);
2992 // "<@comment>" -> "<span class=\"comment\">";
2993 // "<@preprocessor>" -> "<span class=\"preprocessor\">";
2994 // "<@string>" -> "<span class=\"string\">";
2995 // "<@char>" -> "<span class=\"char\">";
2996 // "<@number>" -> "<span class=\"number\">";
2997 // "<@op>" -> "<span class=\"operator\">";
2998 // "<@type>" -> "<span class=\"type\">";
2999 // "<@name>" -> "<span class=\"name\">";
3000 // "<@keyword>" -> "<span class=\"keyword\">";
3001 // "</@(?:comment|preprocessor|string|char|number|op|type|name|keyword)>" -> "</span>"
3004 static const QString spanTags[] = {
3005 "<@comment>", "<span class=\"comment\">",
3006 "<@preprocessor>", "<span class=\"preprocessor\">",
3007 "<@string>", "<span class=\"string\">",
3008 "<@char>", "<span class=\"char\">",
3009 "<@number>", "<span class=\"number\">",
3010 "<@op>", "<span class=\"operator\">",
3011 "<@type>", "<span class=\"type\">",
3012 "<@name>", "<span class=\"name\">",
3013 "<@keyword>", "<span class=\"keyword\">",
3014 "</@comment>", "</span>",
3015 "</@preprocessor>", "</span>",
3016 "</@string>", "</span>",
3017 "</@char>", "</span>",
3018 "</@number>", "</span>",
3019 "</@op>", "</span>",
3020 "</@type>", "</span>",
3021 "</@name>", "</span>",
3022 "</@keyword>", "</span>",
3024 // Update the upper bound of k in the following code to match the length
3025 // of the above array.
3026 for (int i = 0, n = src.size(); i < n;) {
3027 if (src.at(i) == charLangle) {
3028 bool handled = false;
3029 for (int k = 0; k != 18; ++k) {
3030 const QString & tag = spanTags[2 * k];
3031 if (tag == QStringRef(&src, i, tag.length())) {
3032 html += spanTags[2 * k + 1];
3040 if (src.at(i) == charAt ||
3041 (src.at(i) == QLatin1Char('/') && src.at(i + 1) == charAt)) {
3042 // drop 'our' unknown tags (the ones still containing '@')
3043 while (i < n && src.at(i) != QLatin1Char('>'))
3048 // retain all others
3061 void HtmlGenerator::generateLink(const Atom* atom, CodeMarker* marker)
3063 static QRegExp camelCase("[A-Z][A-Z][a-z]|[a-z][A-Z0-9]|_");
3065 if (funcLeftParen.indexIn(atom->string()) != -1 && marker->recognizeLanguage("Cpp")) {
3066 // hack for C++: move () outside of link
3067 int k = funcLeftParen.pos(1);
3068 out() << protectEnc(atom->string().left(k));
3069 if (link_.isEmpty()) {
3070 if (showBrokenLinks)
3076 out() << protectEnc(atom->string().mid(k));
3078 out() << protectEnc(atom->string());
3082 QString HtmlGenerator::cleanRef(const QString& ref)
3089 clean.reserve(ref.size() + 20);
3090 const QChar c = ref[0];
3091 const uint u = c.unicode();
3093 if ((u >= 'a' && u <= 'z') ||
3094 (u >= 'A' && u <= 'Z') ||
3095 (u >= '0' && u <= '9')) {
3097 } else if (u == '~') {
3099 } else if (u == '_') {
3100 clean += "underscore.";
3102 clean += QLatin1Char('A');
3105 for (int i = 1; i < (int) ref.length(); i++) {
3106 const QChar c = ref[i];
3107 const uint u = c.unicode();
3108 if ((u >= 'a' && u <= 'z') ||
3109 (u >= 'A' && u <= 'Z') ||
3110 (u >= '0' && u <= '9') || u == '-' ||
3111 u == '_' || u == ':' || u == '.') {
3113 } else if (c.isSpace()) {
3114 clean += QLatin1Char('-');
3115 } else if (u == '!') {
3117 } else if (u == '&') {
3119 } else if (u == '<') {
3121 } else if (u == '=') {
3123 } else if (u == '>') {
3125 } else if (u == '#') {
3126 clean += QLatin1Char('#');
3128 clean += QLatin1Char('-');
3129 clean += QString::number((int)u, 16);
3135 QString HtmlGenerator::registerRef(const QString& ref)
3137 QString clean = HtmlGenerator::cleanRef(ref);
3140 QString& prevRef = refMap[clean.toLower()];
3141 if (prevRef.isEmpty()) {
3144 } else if (prevRef == ref) {
3147 clean += QLatin1Char('x');
3152 QString HtmlGenerator::protectEnc(const QString &string)
3154 return protect(string, outputEncoding);
3157 QString HtmlGenerator::protect(const QString &string, const QString &outputEncoding)
3160 if (html.isEmpty()) { \
3167 int n = string.length();
3169 for (int i = 0; i < n; ++i) {
3170 QChar ch = string.at(i);
3172 if (ch == QLatin1Char('&')) {
3174 } else if (ch == QLatin1Char('<')) {
3176 } else if (ch == QLatin1Char('>')) {
3178 } else if (ch == QLatin1Char('"')) {
3180 } else if ((outputEncoding == "ISO-8859-1" && ch.unicode() > 0x007F)
3181 || (ch == QLatin1Char('*') && i + 1 < n && string.at(i) == QLatin1Char('/'))
3182 || (ch == QLatin1Char('.') && i > 2 && string.at(i - 2) == QLatin1Char('.'))) {
3183 // we escape '*/' and the last dot in 'e.g.' and 'i.e.' for the Javadoc generator
3185 html += QString::number(ch.unicode(), 16);
3186 html += QLatin1Char(';');
3188 if (!html.isEmpty())
3193 if (!html.isEmpty())
3200 QString HtmlGenerator::fileBase(const Node *node) const
3204 result = Generator::fileBase(node);
3206 if (!node->isInnerNode()) {
3207 switch (node->status()) {
3209 result += "-compat";
3211 case Node::Obsolete:
3212 result += "-obsolete";
3221 QString HtmlGenerator::fileName(const Node *node)
3223 if (node->type() == Node::Document) {
3224 if (static_cast<const DocNode *>(node)->subType() == Node::ExternalPage)
3225 return node->name();
3226 if (static_cast<const DocNode *>(node)->subType() == Node::Image)
3227 return node->name();
3229 return Generator::fileName(node);
3232 QString HtmlGenerator::refForNode(const Node *node)
3234 const FunctionNode *func;
3235 const TypedefNode *typedeffe;
3238 switch (node->type()) {
3239 case Node::Namespace:
3244 ref = node->name() + "-enum";
3247 typedeffe = static_cast<const TypedefNode *>(node);
3248 if (typedeffe->associatedEnum()) {
3249 return refForNode(typedeffe->associatedEnum());
3252 ref = node->name() + "-typedef";
3255 case Node::Function:
3256 func = static_cast<const FunctionNode *>(node);
3257 if (func->associatedProperty()) {
3258 return refForNode(func->associatedProperty());
3262 if (func->overloadNumber() != 1)
3263 ref += QLatin1Char('-') + QString::number(func->overloadNumber());
3266 case Node::Document:
3267 if (node->subType() != Node::QmlPropertyGroup)
3269 case Node::QmlProperty:
3270 case Node::Property:
3271 ref = node->name() + "-prop";
3273 case Node::QmlSignal:
3274 ref = node->name() + "-signal";
3276 case Node::QmlSignalHandler:
3277 ref = node->name() + "-signal-handler";
3279 case Node::QmlMethod:
3280 func = static_cast<const FunctionNode *>(node);
3281 ref = func->name() + "-method";
3282 if (func->overloadNumber() != 1)
3283 ref += QLatin1Char('-') + QString::number(func->overloadNumber());
3285 case Node::Variable:
3286 ref = node->name() + "-var";
3289 return registerRef(ref);
3292 #define DEBUG_ABSTRACT 0
3295 Construct the link string for the \a node and return it.
3296 The \a relative node is use to decide the link we are
3297 generating is in the same file as the target. Note the
3298 relative node can be 0, which pretty much guarantees
3299 that the link and the target aren't in the same file.
3301 QString HtmlGenerator::linkForNode(const Node *node, const Node *relative)
3303 if (node == 0 || node == relative)
3305 if (!node->url().isEmpty())
3307 if (fileBase(node).isEmpty())
3309 if (node->access() == Node::Private)
3312 QString fn = fileName(node);
3313 if (node && relative && node->parent() != relative) {
3314 if (node->parent()->subType() == Node::QmlClass && relative->subType() == Node::QmlClass) {
3315 if (node->parent()->isAbstract()) {
3317 This is a bit of a hack. What we discover with
3318 the three 'if' statements immediately above,
3319 is that node's parent is marked \qmlabstract
3320 but the link appears in a qdoc comment for a
3321 subclass of the node's parent. This means the
3322 link should refer to the file for the relative
3323 node, not the file for node.
3325 fn = fileName(relative);
3331 if (!node->isInnerNode() || node->subType() == Node::QmlPropertyGroup) {
3332 QString ref = refForNode(node);
3333 if (relative && fn == fileName(relative) && ref == refForNode(relative))
3336 link += QLatin1Char('#');
3340 If the output is going to subdirectories, then if the
3341 two nodes will be output to different directories, then
3342 the link must go up to the parent directory and then
3343 back down into the other subdirectory.
3345 if (node && relative && (node != relative)) {
3346 if (node->outputSubdirectory() != relative->outputSubdirectory())
3347 link.prepend(QString("../" + node->outputSubdirectory() + QLatin1Char('/')));
3352 void HtmlGenerator::generateFullName(const Node *apparentNode, const Node *relative, const Node *actualNode)
3354 if (actualNode == 0)
3355 actualNode = apparentNode;
3356 out() << "<a href=\"" << linkForNode(actualNode, relative);
3357 if (true || relative == 0 || relative->status() != actualNode->status()) {
3358 switch (actualNode->status()) {
3359 case Node::Obsolete:
3360 out() << "\" class=\"obsolete";
3363 out() << "\" class=\"compat";
3370 out() << protectEnc(apparentNode->fullName(relative));
3374 void HtmlGenerator::generateDetailedMember(const Node *node,
3375 const InnerNode *relative,
3378 const EnumNode *enume;
3380 #ifdef GENERATE_MAC_REFS
3381 generateMacRef(node, marker);
3383 generateExtractionMark(node, MemberMark);
3384 if (node->type() == Node::Enum
3385 && (enume = static_cast<const EnumNode *>(node))->flagsType()) {
3386 #ifdef GENERATE_MAC_REFS
3387 generateMacRef(enume->flagsType(), marker);
3389 out() << "<h3 class=\"flags\">";
3390 out() << "<a name=\"" + refForNode(node) + "\"></a>";
3391 generateSynopsis(enume, relative, marker, CodeMarker::Detailed);
3393 generateSynopsis(enume->flagsType(),
3396 CodeMarker::Detailed);
3400 out() << "<h3 class=\"fn\">";
3401 out() << "<a name=\"" + refForNode(node) + "\"></a>";
3402 generateSynopsis(node, relative, marker, CodeMarker::Detailed);
3403 out() << "</h3>" << divNavTop << '\n';
3406 generateStatus(node, marker);
3407 generateBody(node, marker);
3408 generateThreadSafeness(node, marker);
3409 generateSince(node, marker);
3411 if (node->type() == Node::Property) {
3412 const PropertyNode *property = static_cast<const PropertyNode *>(node);
3415 section.members += property->getters();
3416 section.members += property->setters();
3417 section.members += property->resetters();
3419 if (!section.members.isEmpty()) {
3420 out() << "<p><b>Access functions:</b></p>\n";
3421 generateSectionList(section, node, marker, CodeMarker::Accessors);
3425 notifiers.members += property->notifiers();
3427 if (!notifiers.members.isEmpty()) {
3428 out() << "<p><b>Notifier signal:</b></p>\n";
3429 //out() << "<p>This signal is emitted when the property value is changed.</p>\n";
3430 generateSectionList(notifiers, node, marker, CodeMarker::Accessors);
3433 else if (node->type() == Node::Enum) {
3434 const EnumNode *enume = static_cast<const EnumNode *>(node);
3435 if (enume->flagsType()) {
3436 out() << "<p>The " << protectEnc(enume->flagsType()->name())
3437 << " type is a typedef for "
3438 << "<a href=\"qflags.html\">QFlags</a><"
3439 << protectEnc(enume->name())
3440 << ">. It stores an OR combination of "
3441 << protectEnc(enume->name())
3442 << " values.</p>\n";
3445 generateAlsoList(node, marker);
3446 generateExtractionMark(node, EndMark);
3449 int HtmlGenerator::hOffset(const Node *node)
3451 switch (node->type()) {
3452 case Node::Namespace:
3455 case Node::Document:
3459 case Node::Function:
3460 case Node::Property:
3466 bool HtmlGenerator::isThreeColumnEnumValueTable(const Atom *atom)
3468 while (atom != 0 && !(atom->type() == Atom::ListRight && atom->string() == ATOM_LIST_VALUE)) {
3469 if (atom->type() == Atom::ListItemLeft && !matchAhead(atom, Atom::ListItemRight))
3471 atom = atom->next();
3477 const QPair<QString,QString> HtmlGenerator::anchorForNode(const Node *node)
3479 QPair<QString,QString> anchorPair;
3481 anchorPair.first = Generator::fileName(node);
3482 if (node->type() == Node::Document) {
3483 const DocNode *docNode = static_cast<const DocNode*>(node);
3484 anchorPair.second = docNode->title();
3490 QString HtmlGenerator::getLink(const Atom *atom, const Node *relative, const Node** node)
3494 inObsoleteLink = false;
3496 if (atom->string().contains(QLatin1Char(':')) &&
3497 (atom->string().startsWith("file:")
3498 || atom->string().startsWith("http:")
3499 || atom->string().startsWith("https:")
3500 || atom->string().startsWith("ftp:")
3501 || atom->string().startsWith("mailto:"))) {
3503 link = atom->string();
3507 if (atom->string().contains('#')) {
3508 path = atom->string().split('#');
3511 path.append(atom->string());
3515 QString first = path.first().trimmed();
3516 if (first.isEmpty()) {
3519 else if (first.endsWith(".html")) {
3521 This is not a recursive search. That's ok in
3522 this case, because we are searching for a page
3523 node, which must be a direct child of the tree
3526 *node = qdb_->treeRoot()->findChildNodeByNameAndType(first, Node::Document);
3529 *node = qdb_->resolveTarget(first, relative);
3531 *node = qdb_->findDocNodeByTitle(first, relative);
3534 *node = qdb_->findUnambiguousTarget(first, ref, relative);
3538 if (!(*node)->url().isEmpty()) {
3539 return (*node)->url();
3550 if ((*node)->status() == Node::Obsolete) {
3552 if (relative->parent() != *node) {
3553 if (relative->status() != Node::Obsolete) {
3554 bool porting = false;
3555 if (relative->type() == Node::Document) {
3556 const DocNode* dn = static_cast<const DocNode*>(relative);
3557 if (dn->title().startsWith("Porting"))
3560 QString name = relative->plainFullName();
3561 if (!porting && !name.startsWith("Q3")) {
3562 if (obsoleteLinks) {
3563 relative->doc().location().warning(tr("Link to obsolete item '%1' in %2")
3564 .arg(atom->string())
3567 inObsoleteLink = true;
3573 qDebug() << "Link to Obsolete entity"
3574 << (*node)->name() << "no relative";
3580 This loop really only makes sense if *node is not 0.
3581 In that case, The node *node points to represents a
3582 qdoc page, so the link will ultimately point to some
3583 target on that page. This loop finds that target on
3584 the page that *node represents. ref is that target.
3586 while (!path.isEmpty()) {
3587 ref = qdb_->findTarget(path.first(), *node);
3594 Given that *node is not null, we now cconstruct a link
3595 to the page that *node represents, and then if there is
3596 a target on that page, we connect the target to the link
3599 if (path.isEmpty()) {
3600 link = linkForNode(*node, relative);
3601 if (*node && (*node)->subType() == Node::Image)
3602 link = "images/used-in-examples/" + link;
3603 if (!ref.isEmpty()) {
3604 link += QLatin1Char('#') + ref;
3611 void HtmlGenerator::generateStatus(const Node *node, CodeMarker *marker)
3615 switch (node->status()) {
3616 case Node::Obsolete:
3617 if (node->isInnerNode())
3618 Generator::generateStatus(node, marker);
3621 if (node->isInnerNode()) {
3622 text << Atom::ParaLeft
3623 << Atom(Atom::FormattingLeft,ATOM_FORMATTING_BOLD)
3626 << " is part of the Qt 3 support library."
3627 << Atom(Atom::FormattingRight, ATOM_FORMATTING_BOLD)
3628 << " It is provided to keep old source code working. "
3629 << "We strongly advise against "
3630 << "using it in new code. See ";
3632 const DocNode *docNode = qdb_->findDocNodeByTitle("Porting To Qt 4");
3634 if (docNode && node->type() == Node::Class) {
3635 QString oldName(node->name());
3636 oldName.remove(QLatin1Char('3'));
3637 ref = qdb_->findTarget(oldName, docNode);
3640 if (!ref.isEmpty()) {
3641 text << Atom(Atom::Link, linkForNode(docNode, node) + QLatin1Char('#') + ref);
3644 text << Atom(Atom::Link, "Porting to Qt 4");
3646 text << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK)
3647 << Atom(Atom::String, "Porting to Qt 4")
3648 << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK)
3649 << " for more information."
3652 generateText(text, node, marker);
3655 Generator::generateStatus(node, marker);
3659 #ifdef GENERATE_MAC_REFS
3663 void HtmlGenerator::generateMacRef(const Node *node, CodeMarker *marker)
3665 if (!pleaseGenerateMacRef || marker == 0)
3668 QStringList macRefs = marker->macRefsForNode(node);
3669 foreach (const QString &macRef, macRefs)
3670 out() << "<a name=\"" << "//apple_ref/" << macRef << "\"></a>\n";
3674 void HtmlGenerator::beginLink(const QString &link, const Node *node, const Node *relative)
3677 if (link_.isEmpty()) {
3678 if (showBrokenLinks)
3681 else if (node == 0 ||
3682 (relative != 0 && node->status() == relative->status())) {
3683 out() << "<a href=\"" << link_ << "\">";
3686 switch (node->status()) {
3687 case Node::Obsolete:
3688 out() << "<a href=\"" << link_ << "\" class=\"obsolete\">";
3691 out() << "<a href=\"" << link_ << "\" class=\"compat\">";
3694 out() << "<a href=\"" << link_ << "\">";
3700 void HtmlGenerator::endLink()
3703 if (link_.isEmpty()) {
3704 if (showBrokenLinks)
3708 if (inObsoleteLink) {
3709 out() << "<sup>(obsolete)</sup>";
3715 inObsoleteLink = false;
3719 Generates the summary for the \a section. Only used for
3720 sections of QML element documentation.
3722 void HtmlGenerator::generateQmlSummary(const Section& section,
3723 const Node *relative,
3726 if (!section.members.isEmpty()) {
3728 NodeList::ConstIterator m;
3729 m = section.members.constBegin();
3730 while (m != section.members.constEnd()) {
3731 out() << "<li class=\"fn\">";
3732 generateQmlItem(*m,relative,marker,true);
3741 Outputs the html detailed documentation for a section
3742 on a QML element reference page.
3744 void HtmlGenerator::generateDetailedQmlMember(Node *node,
3745 const InnerNode *relative,
3748 QmlPropertyNode* qpn = 0;
3749 #ifdef GENERATE_MAC_REFS
3750 generateMacRef(node, marker);
3752 generateExtractionMark(node, MemberMark);
3753 out() << "<div class=\"qmlitem\">";
3754 if (node->subType() == Node::QmlPropertyGroup) {
3755 const QmlPropGroupNode* qpgn = static_cast<const QmlPropGroupNode*>(node);
3756 NodeList::ConstIterator p = qpgn->childNodes().constBegin();
3757 out() << "<div class=\"qmlproto\">";
3758 out() << "<table class=\"qmlname\">";
3759 while (p != qpgn->childNodes().constEnd()) {
3760 if ((*p)->type() == Node::QmlProperty) {
3761 qpn = static_cast<QmlPropertyNode*>(*p);
3762 out() << "<tr valign=\"top\" class=\"odd\">";
3763 out() << "<td class=\"tblQmlPropNode\"><p>";
3764 out() << "<a name=\"" + refForNode(qpn) + "\"></a>";
3766 if (!qpn->isWritable(qdb_))
3767 out() << "<span class=\"qmlreadonly\">read-only</span>";
3768 if (qpn->isDefault())
3769 out() << "<span class=\"qmldefault\">default</span>";
3770 generateQmlItem(qpn, relative, marker, false);
3771 out() << "</p></td></tr>";
3775 out() << "</table>";
3778 else if (node->type() == Node::QmlProperty) {
3779 qpn = static_cast<QmlPropertyNode*>(node);
3781 If the QML property node has a single subproperty,
3782 override, replace qpn with that override node and
3785 if (qpn->qmlPropNodes().size() == 1) {
3786 Node* n = qpn->qmlPropNodes().at(0);
3787 if (n->type() == Node::QmlProperty)
3788 qpn = static_cast<QmlPropertyNode*>(n);
3791 Now qpn either has no overrides, or it has more
3792 than 1. If it has none, proceed to output as nortmal.
3794 if (qpn->qmlPropNodes().isEmpty()) {
3795 out() << "<div class=\"qmlproto\">";
3796 out() << "<table class=\"qmlname\">";
3797 out() << "<tr valign=\"top\" class=\"odd\">";
3798 out() << "<td class=\"tblQmlPropNode\"><p>";
3799 out() << "<a name=\"" + refForNode(qpn) + "\"></a>";
3800 if (!qpn->isReadOnlySet()) {
3801 if (qpn->declarativeCppNode())
3802 qpn->setReadOnly(!qpn->isWritable(qdb_));
3804 if (qpn->isReadOnly())
3805 out() << "<span class=\"qmlreadonly\">read-only</span>";
3806 if (qpn->isDefault())
3807 out() << "<span class=\"qmldefault\">default</span>";
3808 generateQmlItem(qpn, relative, marker, false);
3809 out() << "</p></td></tr>";
3810 out() << "</table>";
3815 The QML property node has multiple override nodes.
3816 Process the whole list as we would for a QML property
3819 NodeList::ConstIterator p = qpn->qmlPropNodes().constBegin();
3820 out() << "<div class=\"qmlproto\">";
3821 out() << "<table class=\"qmlname\">";
3822 while (p != qpn->qmlPropNodes().constEnd()) {
3823 if ((*p)->type() == Node::QmlProperty) {
3824 QmlPropertyNode* q = static_cast<QmlPropertyNode*>(*p);
3825 out() << "<tr valign=\"top\" class=\"odd\">";
3826 out() << "<td class=\"tblQmlPropNode\"><p>";
3827 out() << "<a name=\"" + refForNode(q) + "\"></a>";
3828 if (!qpn->isReadOnlySet())
3829 qpn->setReadOnly(!qpn->isWritable(qdb_));
3830 if (qpn->isReadOnly())
3831 out() << "<span class=\"qmlreadonly\">read-only</span>";
3832 if (qpn->isDefault())
3833 out() << "<span class=\"qmldefault\">default</span>";
3834 generateQmlItem(q, relative, marker, false);
3835 out() << "</p></td></tr>";
3839 out() << "</table>";
3843 else if (node->type() == Node::QmlSignal) {
3844 const FunctionNode* qsn = static_cast<const FunctionNode*>(node);
3845 out() << "<div class=\"qmlproto\">";
3846 out() << "<table class=\"qmlname\">";
3847 out() << "<tr valign=\"top\" class=\"odd\">";
3848 out() << "<td class=\"tblQmlFuncNode\"><p>";
3849 out() << "<a name=\"" + refForNode(qsn) + "\"></a>";
3850 generateSynopsis(qsn,relative,marker,CodeMarker::Detailed,false);
3851 out() << "</p></td></tr>";
3852 out() << "</table>";
3855 else if (node->type() == Node::QmlSignalHandler) {
3856 const FunctionNode* qshn = static_cast<const FunctionNode*>(node);
3857 out() << "<div class=\"qmlproto\">";
3858 out() << "<table class=\"qmlname\">";
3859 out() << "<tr valign=\"top\" class=\"odd\">";
3860 out() << "<td class=\"tblQmlFuncNode\"><p>";
3861 out() << "<a name=\"" + refForNode(qshn) + "\"></a>";
3862 generateSynopsis(qshn,relative,marker,CodeMarker::Detailed,false);
3863 out() << "</p></td></tr>";
3864 out() << "</table>";
3867 else if (node->type() == Node::QmlMethod) {
3868 const FunctionNode* qmn = static_cast<const FunctionNode*>(node);
3869 out() << "<div class=\"qmlproto\">";
3870 out() << "<table class=\"qmlname\">";
3871 out() << "<tr valign=\"top\" class=\"odd\">";
3872 out() << "<td class=\"tblQmlFuncNode\"><p>";
3873 out() << "<a name=\"" + refForNode(qmn) + "\"></a>";
3874 generateSynopsis(qmn,relative,marker,CodeMarker::Detailed,false);
3875 out() << "</p></td></tr>";
3876 out() << "</table>";
3879 out() << "<div class=\"qmldoc\">";
3880 generateStatus(node, marker);
3881 generateBody(node, marker);
3882 generateThreadSafeness(node, marker);
3883 generateSince(node, marker);
3884 generateAlsoList(node, marker);
3887 generateExtractionMark(node, EndMark);
3891 Output the "Inherits" line for the QML element,
3892 if there should be one.
3894 void HtmlGenerator::generateQmlInherits(const QmlClassNode* qcn, CodeMarker* marker)
3898 const DocNode* base = qcn->qmlBase();
3901 text << Atom::ParaLeft << "Inherits ";
3902 text << Atom(Atom::LinkNode,CodeMarker::stringForNode(base));
3903 text << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK);
3904 text << Atom(Atom::String, base->name());
3905 text << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK);
3906 text << Atom::ParaRight;
3907 generateText(text, qcn, marker);
3912 Output the "[Xxx instantiates the C++ class QmlGraphicsXxx]"
3913 line for the QML element, if there should be one.
3915 If there is no class node, or if the class node status
3916 is set to Node::Internal, do nothing.
3918 void HtmlGenerator::generateQmlInstantiates(QmlClassNode* qcn, CodeMarker* marker)
3920 ClassNode* cn = qcn->classNode();
3921 if (cn && (cn->status() != Node::Internal)) {
3923 text << Atom::ParaLeft;
3924 text << Atom(Atom::LinkNode,CodeMarker::stringForNode(qcn));
3925 text << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK);
3926 QString name = qcn->name();
3928 Remove the "QML:" prefix, if present.
3929 It shouldn't be present anymore.
3931 if (name.startsWith(QLatin1String("QML:")))
3932 name = name.mid(4); // remove the "QML:" prefix
3933 text << Atom(Atom::String, name);
3934 text << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK);
3935 text << " instantiates the C++ class ";
3936 text << Atom(Atom::LinkNode,CodeMarker::stringForNode(cn));
3937 text << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK);
3938 text << Atom(Atom::String, cn->name());
3939 text << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK);
3940 text << Atom::ParaRight;
3941 generateText(text, qcn, marker);
3946 Output the "[QmlGraphicsXxx is instantiated by QML Type Xxx]"
3947 line for the class, if there should be one.
3949 If there is no QML element, or if the class node status
3950 is set to Node::Internal, do nothing.
3952 void HtmlGenerator::generateInstantiatedBy(ClassNode* cn, CodeMarker* marker)
3954 if (cn && cn->status() != Node::Internal && cn->qmlElement() != 0) {
3955 const QmlClassNode* qcn = cn->qmlElement();
3957 text << Atom::ParaLeft;
3958 text << Atom(Atom::LinkNode,CodeMarker::stringForNode(cn));
3959 text << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK);
3960 text << Atom(Atom::String, cn->name());
3961 text << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK);
3962 text << " is instantiated by QML Type ";
3963 text << Atom(Atom::LinkNode,CodeMarker::stringForNode(qcn));
3964 text << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK);
3965 text << Atom(Atom::String, qcn->name());
3966 text << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK);
3967 text << Atom::ParaRight;
3968 generateText(text, cn, marker);
3972 void HtmlGenerator::generateExtractionMark(const Node *node, ExtractionMarkType markType)
3974 if (markType != EndMark) {
3975 out() << "<!-- $$$" + node->name();
3976 if (markType == MemberMark) {
3977 if (node->type() == Node::Function) {
3978 const FunctionNode *func = static_cast<const FunctionNode *>(node);
3979 if (!func->associatedProperty()) {
3980 if (func->overloadNumber() == 1)
3981 out() << "[overload1]";
3982 out() << "$$$" + func->name() + func->rawParameters().remove(' ');
3984 } else if (node->type() == Node::Property) {
3986 const PropertyNode *prop = static_cast<const PropertyNode *>(node);
3987 const NodeList &list = prop->functions();
3988 foreach (const Node *propFuncNode, list) {
3989 if (propFuncNode->type() == Node::Function) {
3990 const FunctionNode *func = static_cast<const FunctionNode *>(propFuncNode);
3991 out() << "$$$" + func->name() + func->rawParameters().remove(' ');
3994 } else if (node->type() == Node::Enum) {
3995 const EnumNode *enumNode = static_cast<const EnumNode *>(node);
3996 foreach (const EnumItem &item, enumNode->items())
3997 out() << "$$$" + item.name();
3999 } else if (markType == BriefMark) {
4001 } else if (markType == DetailedDescriptionMark) {
4002 out() << "-description";
4006 out() << "<!-- @@@" + node->name() + " -->\n";
4012 This function outputs one or more manifest files in XML.
4013 They are used by Creator.
4015 void HtmlGenerator::generateManifestFiles()
4017 generateManifestFile("examples", "example");
4018 generateManifestFile("demos", "demo");
4019 ExampleNode::exampleNodeMap.clear();
4023 This function is called by generaqteManiferstFile(), once
4024 for each manifest file to be generated. \a manifest is the
4025 type of manifest file.
4027 void HtmlGenerator::generateManifestFile(QString manifest, QString element)
4029 if (ExampleNode::exampleNodeMap.isEmpty())
4031 QString fileName = manifest +"-manifest.xml";
4032 QFile file(outputDir() + QLatin1Char('/') + fileName);
4033 if (!file.open(QFile::WriteOnly | QFile::Text))
4036 if (manifest == "demos")
4039 bool proceed = false;
4040 ExampleNodeMap::Iterator i = ExampleNode::exampleNodeMap.begin();
4041 while (i != ExampleNode::exampleNodeMap.end()) {
4042 const ExampleNode* en = i.value();
4044 if (en->name().startsWith("demos")) {
4049 else if (!en->name().startsWith("demos")) {
4058 QXmlStreamWriter writer(&file);
4059 writer.setAutoFormatting(true);
4060 writer.writeStartDocument();
4061 writer.writeStartElement("instructionals");
4062 writer.writeAttribute("module", project);
4063 writer.writeStartElement(manifest);
4065 i = ExampleNode::exampleNodeMap.begin();
4066 while (i != ExampleNode::exampleNodeMap.end()) {
4067 const ExampleNode* en = i.value();
4069 if (!en->name().startsWith("demos")) {
4074 else if (en->name().startsWith("demos")) {
4078 writer.writeStartElement(element);
4079 writer.writeAttribute("name", en->title());
4080 QString docUrl = manifestDir + fileBase(en) + ".html";
4081 writer.writeAttribute("docUrl", docUrl);
4082 QStringList proFiles;
4083 foreach (const Node* child, en->childNodes()) {
4084 if (child->subType() == Node::File) {
4085 QString file = child->name();
4086 if (file.endsWith(".pro") || file.endsWith(".qmlproject")) {
4087 if (file.startsWith("demos/"))
4093 if (!proFiles.isEmpty()) {
4094 if (proFiles.size() == 1) {
4095 writer.writeAttribute("projectPath", proFiles[0]);
4098 QString exampleName = en->name().split('/').last();
4099 bool proWithExampleNameFound = false;
4100 for (int j = 0; j < proFiles.size(); j++)
4102 if (proFiles[j].endsWith(QStringLiteral("%1/%1.pro").arg(exampleName))
4103 || proFiles[j].endsWith(QStringLiteral("%1/%1.qmlproject").arg(exampleName))) {
4104 writer.writeAttribute("projectPath", proFiles[j]);
4105 proWithExampleNameFound = true;
4109 if (!proWithExampleNameFound)
4110 writer.writeAttribute("projectPath", proFiles[0]);
4113 if (!en->imageFileName().isEmpty())
4114 writer.writeAttribute("imageUrl", manifestDir + en->imageFileName());
4115 writer.writeStartElement("description");
4116 Text brief = en->doc().briefText();
4117 if (!brief.isEmpty())
4118 writer.writeCDATA(brief.toString());
4120 writer.writeCDATA(QString("No description available"));
4121 writer.writeEndElement(); // description
4122 QStringList tags = en->title().toLower().split(QLatin1Char(' '));
4123 if (!tags.isEmpty()) {
4124 writer.writeStartElement("tags");
4125 bool wrote_one = false;
4126 for (int n=0; n<tags.size(); ++n) {
4127 QString tag = tags.at(n);
4128 if (tag.at(0).isDigit())
4130 if (tag.at(0) == '-')
4132 if (tag.startsWith("example"))
4134 if (tag.startsWith("chapter"))
4136 if (tag.endsWith(QLatin1Char(':')))
4138 if (n>0 && wrote_one)
4139 writer.writeCharacters(",");
4140 writer.writeCharacters(tag);
4143 writer.writeEndElement(); // tags
4146 QString ename = en->name().mid(en->name().lastIndexOf('/')+1);
4147 QSet<QString> usedNames;
4148 foreach (const Node* child, en->childNodes()) {
4149 if (child->subType() == Node::File) {
4150 QString file = child->name();
4151 QString fileName = file.mid(file.lastIndexOf('/')+1);
4152 QString baseName = fileName;
4153 if ((fileName.count(QChar('.')) > 0) &&
4154 (fileName.endsWith(".cpp") ||
4155 fileName.endsWith(".h") ||
4156 fileName.endsWith(".qml")))
4157 baseName.truncate(baseName.lastIndexOf(QChar('.')));
4158 if (baseName.compare(ename, Qt::CaseInsensitive) == 0) {
4159 if (!usedNames.contains(fileName)) {
4160 writer.writeStartElement("fileToOpen");
4161 if (file.startsWith("demos/"))
4163 writer.writeCharacters(file);
4164 writer.writeEndElement(); // fileToOpen
4165 usedNames.insert(fileName);
4168 else if (fileName.toLower().endsWith("main.cpp") ||
4169 fileName.toLower().endsWith("main.qml")) {
4170 if (!usedNames.contains(fileName)) {
4171 writer.writeStartElement("fileToOpen");
4172 if (file.startsWith("demos/"))
4174 writer.writeCharacters(file);
4175 writer.writeEndElement(); // fileToOpen
4176 usedNames.insert(fileName);
4181 writer.writeEndElement(); // example
4185 writer.writeEndElement(); // examples
4186 writer.writeEndElement(); // instructionals
4187 writer.writeEndDocument();
4192 Find global entities that have documentation but no
4193 \e{relates} comand. Report these as errors if they
4194 are not also marked \e {internal}.
4200 subtype: External page
4202 subtype: Header file
4205 subtype: QML basic type
4209 void HtmlGenerator::reportOrphans(const InnerNode* parent)
4211 const NodeList& children = parent->childNodes();
4212 if (children.size() == 0)
4217 for (int i=0; i<children.size(); ++i) {
4218 Node* child = children[i];
4219 if (!child || child->isInternal() || child->doc().isEmpty())
4221 if (child->relates()) {
4223 message = child->relates()->name();
4227 message = "has documentation but no \\relates command";
4229 switch (child->type()) {
4230 case Node::Namespace:
4234 case Node::Document:
4235 switch (child->subType()) {
4238 case Node::HeaderFile:
4250 case Node::ExternalPage:
4252 case Node::QmlClass:
4254 case Node::QmlPropertyGroup:
4256 case Node::QmlBasicType:
4258 case Node::QmlModule:
4260 case Node::Collision:
4268 child->location().warning(tr("Global enum, %1, %2").arg(child->name()).arg(message));
4272 child->location().warning(tr("Global typedef, %1, %2").arg(child->name()).arg(message));
4274 case Node::Function:
4276 const FunctionNode* fn = static_cast<const FunctionNode*>(child);
4278 child->location().warning(tr("Global macro, %1, %2").arg(child->name()).arg(message));
4280 child->location().warning(tr("Global function, %1(), %2").arg(child->name()).arg(message));
4283 case Node::Property:
4285 case Node::Variable:
4287 child->location().warning(tr("Global variable, %1, %2").arg(child->name()).arg(message));
4289 case Node::QmlProperty:
4291 child->location().warning(tr("Global QML property, %1, %2").arg(child->name()).arg(message));
4293 case Node::QmlSignal:
4295 child->location().warning(tr("Global QML, signal, %1 %2").arg(child->name()).arg(message));
4297 case Node::QmlSignalHandler:
4299 child->location().warning(tr("Global QML signal handler, %1, %2").arg(child->name()).arg(message));
4301 case Node::QmlMethod:
4303 child->location().warning(tr("Global QML method, %1, %2").arg(child->name()).arg(message));
4312 Returns a reference to the XML stream writer currently in use.
4313 There is one XML stream writer open for each XML file being
4314 written, and they are kept on a stack. The one on top of the
4315 stack is the one being written to at the moment. In the HTML
4316 output generator, it is perhaps impossible for there to ever
4317 be more than one writer open.
4319 QXmlStreamWriter& HtmlGenerator::xmlWriter()
4321 return *xmlWriterStack.top();
4325 This function is only called for writing ditamaps.
4327 Calls beginSubPage() in the base class to open the file.
4328 Then creates a new XML stream writer using the IO device
4329 from opened file and pushes the XML writer onto a stackj.
4330 Creates the file named \a fileName in the output directory.
4331 Attaches a QTextStream to the created file, which is written
4332 to all over the place using out(). Finally, it sets some
4333 parameters in the XML writer and calls writeStartDocument().
4335 It also ensures that a GUID map is created for the output file.
4337 void HtmlGenerator::beginDitamapPage(const InnerNode* node, const QString& fileName)
4339 Generator::beginSubPage(node,fileName);
4340 QXmlStreamWriter* writer = new QXmlStreamWriter(out().device());
4341 xmlWriterStack.push(writer);
4342 writer->setAutoFormatting(true);
4343 writer->setAutoFormattingIndent(4);
4344 writer->writeStartDocument();
4348 This function is only called for writing ditamaps.
4350 Calls writeEndDocument() and then pops the XML stream writer
4351 off the stack and deletes it. Then it calls endSubPage() in
4352 the base class to close the device.
4354 void HtmlGenerator::endDitamapPage()
4356 xmlWriter().writeEndDocument();
4357 delete xmlWriterStack.pop();
4358 Generator::endSubPage();
4362 This function is only called for writing ditamaps.
4364 Creates the DITA map from the topicrefs in \a node,
4365 which is a DitaMapNode.
4367 void HtmlGenerator::writeDitaMap(const DitaMapNode* node)
4369 beginDitamapPage(node,node->name());
4371 QString doctype = "<!DOCTYPE map PUBLIC \"-//OASIS//DTD DITA Map//EN\" \"map.dtd\">";
4373 xmlWriter().writeDTD(doctype);
4374 xmlWriter().writeStartElement("map");
4375 xmlWriter().writeStartElement("topicmeta");
4376 xmlWriter().writeStartElement("shortdesc");
4377 xmlWriter().writeCharacters(node->title());
4378 xmlWriter().writeEndElement(); // </shortdesc>
4379 xmlWriter().writeEndElement(); // </topicmeta>
4380 DitaRefList map = node->map();
4386 Write the \a ditarefs to the current output file.
4388 void HtmlGenerator::writeDitaRefs(const DitaRefList& ditarefs)
4390 foreach (DitaRef* t, ditarefs) {
4392 xmlWriter().writeStartElement("mapref");
4394 xmlWriter().writeStartElement("topicref");
4395 xmlWriter().writeAttribute("navtitle",t->navtitle());
4396 if (t->href().isEmpty()) {
4397 const DocNode* fn = qdb_->findDocNodeByTitle(t->navtitle());
4399 xmlWriter().writeAttribute("href",fileName(fn));
4402 xmlWriter().writeAttribute("href",t->href());
4403 if (t->subrefs() && !t->subrefs()->isEmpty())
4404 writeDitaRefs(*(t->subrefs()));
4405 xmlWriter().writeEndElement(); // </topicref> or </mapref>