qdoc: Removed several #if 0 blocks.
[profile/ivi/qtbase.git] / src / tools / qdoc / htmlgenerator.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
5 **
6 ** This file is part of the tools applications of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** GNU Lesser General Public License Usage
10 ** This file may be used under the terms of the GNU Lesser General Public
11 ** License version 2.1 as published by the Free Software Foundation and
12 ** appearing in the file LICENSE.LGPL included in the packaging of this
13 ** file. Please review the following information to ensure the GNU Lesser
14 ** General Public License version 2.1 requirements will be met:
15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
16 **
17 ** In addition, as a special exception, Nokia gives you certain additional
18 ** rights. These rights are described in the Nokia Qt LGPL Exception
19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
20 **
21 ** GNU General Public License Usage
22 ** Alternatively, this file may be used under the terms of the GNU General
23 ** Public License version 3.0 as published by the Free Software Foundation
24 ** and appearing in the file LICENSE.GPL included in the packaging of this
25 ** file. Please review the following information to ensure the GNU General
26 ** Public License version 3.0 requirements will be met:
27 ** http://www.gnu.org/copyleft/gpl.html.
28 **
29 ** Other Usage
30 ** Alternatively, this file may be used in accordance with the terms and
31 ** conditions contained in a signed written agreement between you and Nokia.
32 **
33 **
34 **
35 **
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 /*
43   htmlgenerator.cpp
44 */
45
46 #include "codemarker.h"
47 #include "codeparser.h"
48 #include "helpprojectwriter.h"
49 #include "htmlgenerator.h"
50 #include "node.h"
51 #include "separator.h"
52 #include "tree.h"
53 #include <ctype.h>
54 #include <qdebug.h>
55 #include <qlist.h>
56 #include <qiterator.h>
57 #include <qtextcodec.h>
58 #include <QUuid>
59
60 QT_BEGIN_NAMESPACE
61
62 #define COMMAND_VERSION                 Doc::alias("version")
63 int HtmlGenerator::id = 0;
64 bool HtmlGenerator::debugging_on = false;
65
66 QString HtmlGenerator::divNavTop;
67
68 static bool showBrokenLinks = false;
69
70 static QRegExp linkTag("(<@link node=\"([^\"]+)\">).*(</@link>)");
71 static QRegExp funcTag("(<@func target=\"([^\"]*)\">)(.*)(</@func>)");
72 static QRegExp typeTag("(<@(type|headerfile|func)(?: +[^>]*)?>)(.*)(</@\\2>)");
73 static QRegExp spanTag("</@(?:comment|preprocessor|string|char|number|op|type|name|keyword)>");
74 static QRegExp unknownTag("</?@[^>]*>");
75
76 static void addLink(const QString &linkTarget,
77                     const QStringRef &nestedStuff,
78                     QString *res)
79 {
80     if (!linkTarget.isEmpty()) {
81         *res += "<a href=\"";
82         *res += linkTarget;
83         *res += "\">";
84         *res += nestedStuff;
85         *res += "</a>";
86     }
87     else {
88         *res += nestedStuff;
89     }
90 }
91
92
93 HtmlGenerator::HtmlGenerator()
94     : helpProjectWriter(0),
95       inLink(false),
96       inObsoleteLink(false),
97       inContents(false),
98       inSectionHeading(false),
99       inTableHeader(false),
100       numTableRows(0),
101       threeColumnEnumValueTable(true),
102       funcLeftParen("\\S(\\()"),
103       obsoleteLinks(false)
104 {
105 }
106
107 /*!
108   The destructor deletes the instance of HelpProjectWriter.
109  */
110 HtmlGenerator::~HtmlGenerator()
111 {
112     if (helpProjectWriter)
113         delete helpProjectWriter;
114 }
115
116 void HtmlGenerator::initializeGenerator(const Config &config)
117 {
118     static const struct {
119         const char *key;
120         const char *left;
121         const char *right;
122     } defaults[] = {
123         { ATOM_FORMATTING_BOLD, "<b>", "</b>" },
124         { ATOM_FORMATTING_INDEX, "<!--", "-->" },
125         { ATOM_FORMATTING_ITALIC, "<i>", "</i>" },
126         { ATOM_FORMATTING_PARAMETER, "<i>", "</i>" },
127         { ATOM_FORMATTING_SUBSCRIPT, "<sub>", "</sub>" },
128         { ATOM_FORMATTING_SUPERSCRIPT, "<sup>", "</sup>" },
129         { ATOM_FORMATTING_TELETYPE, "<tt>", "</tt>" },
130         { ATOM_FORMATTING_UICONTROL, "<b>", "</b>" },
131         { ATOM_FORMATTING_UNDERLINE, "<u>", "</u>" },
132         { 0, 0, 0 }
133     };
134
135     Generator::initializeGenerator(config);
136     obsoleteLinks = config.getBool(QLatin1String(CONFIG_OBSOLETELINKS));
137     setImageFileExtensions(QStringList() << "png" << "jpg" << "jpeg" << "gif");
138     int i = 0;
139     while (defaults[i].key) {
140         formattingLeftMap().insert(defaults[i].key, defaults[i].left);
141         formattingRightMap().insert(defaults[i].key, defaults[i].right);
142         i++;
143     }
144
145     style = config.getString(HtmlGenerator::format() +
146                              Config::dot +
147                              CONFIG_STYLE);
148     endHeader = config.getString(HtmlGenerator::format() +
149                                  Config::dot +
150                                  CONFIG_ENDHEADER);
151     postHeader = config.getString(HtmlGenerator::format() +
152                                   Config::dot +
153                                   HTMLGENERATOR_POSTHEADER);
154     postPostHeader = config.getString(HtmlGenerator::format() +
155                                       Config::dot +
156                                       HTMLGENERATOR_POSTPOSTHEADER);
157     footer = config.getString(HtmlGenerator::format() +
158                               Config::dot +
159                               HTMLGENERATOR_FOOTER);
160     address = config.getString(HtmlGenerator::format() +
161                                Config::dot +
162                                HTMLGENERATOR_ADDRESS);
163     pleaseGenerateMacRef = config.getBool(HtmlGenerator::format() +
164                                           Config::dot +
165                                           HTMLGENERATOR_GENERATEMACREFS);
166     noBreadCrumbs = config.getBool(HtmlGenerator::format() +
167                                    Config::dot +
168                                    HTMLGENERATOR_NOBREADCRUMBS);
169
170     project = config.getString(CONFIG_PROJECT);
171
172     projectDescription = config.getString(CONFIG_DESCRIPTION);
173     if (projectDescription.isEmpty() && !project.isEmpty())
174         projectDescription = project + " Reference Documentation";
175
176     projectUrl = config.getString(CONFIG_URL);
177
178     outputEncoding = config.getString(CONFIG_OUTPUTENCODING);
179     if (outputEncoding.isEmpty())
180         outputEncoding = QLatin1String("ISO-8859-1");
181     outputCodec = QTextCodec::codecForName(outputEncoding.toLocal8Bit());
182
183     naturalLanguage = config.getString(CONFIG_NATURALLANGUAGE);
184     if (naturalLanguage.isEmpty())
185         naturalLanguage = QLatin1String("en");
186
187     QSet<QString> editionNames = config.subVars(CONFIG_EDITION);
188     QSet<QString>::ConstIterator edition = editionNames.constBegin();
189     while (edition != editionNames.constEnd()) {
190         QString editionName = *edition;
191         QStringList editionModules = config.getStringList(CONFIG_EDITION +
192                                                           Config::dot +
193                                                           editionName +
194                                                           Config::dot +
195                                                           "modules");
196         QStringList editionGroups = config.getStringList(CONFIG_EDITION +
197                                                          Config::dot +
198                                                          editionName +
199                                                          Config::dot +
200                                                          "groups");
201
202         if (!editionModules.isEmpty())
203             editionModuleMap[editionName] = editionModules;
204         if (!editionGroups.isEmpty())
205             editionGroupMap[editionName] = editionGroups;
206
207         ++edition;
208     }
209
210     codeIndent = config.getInt(CONFIG_CODEINDENT);
211
212     helpProjectWriter = new HelpProjectWriter(config,
213                                               project.toLower() +
214                                               ".qhp");
215
216     // Documentation template handling
217     headerScripts = config.getString(HtmlGenerator::format() + Config::dot +
218                                      CONFIG_HEADERSCRIPTS);
219     headerStyles = config.getString(HtmlGenerator::format() +
220                                     Config::dot +
221                                     CONFIG_HEADERSTYLES);
222
223     QString prefix = CONFIG_QHP + Config::dot + "Qt" + Config::dot;
224     manifestDir = "qthelp://" + config.getString(prefix + "namespace");
225     manifestDir += QLatin1Char('/') + config.getString(prefix + "virtualFolder") + QLatin1Char('/');
226
227     /*
228       If the output files will be in subdirectores in the output
229       directory, change the references to files in the style and
230       scripts subdirectories that appear in the headerscipts and
231       headerstyles string so that they link to the correct files,
232       whic means prepending "../" to each
233      */
234     if (!baseDir().isEmpty()) {
235         headerScripts = headerScripts.replace("style/","../style/");
236         headerScripts = headerScripts.replace("scripts/","../scripts/");
237         headerStyles = headerStyles.replace("style/","../style/");
238         headerStyles = headerStyles.replace("scripts/","../scripts/");
239     }
240 }
241
242 void HtmlGenerator::terminateGenerator()
243 {
244     Generator::terminateGenerator();
245 }
246
247 QString HtmlGenerator::format()
248 {
249     return "HTML";
250 }
251
252 /*!
253   This is where the HTML files are written.
254   \note The HTML file generation is done in the base class,
255   PageGenerator::generateTree().
256  */
257 void HtmlGenerator::generateTree(Tree *tree)
258 {
259     tree_ = tree;
260     nonCompatClasses.clear();
261     mainClasses.clear();
262     compatClasses.clear();
263     obsoleteClasses.clear();
264     moduleClassMap.clear();
265     moduleNamespaceMap.clear();
266     funcIndex.clear();
267     legaleseTexts.clear();
268     serviceClasses.clear();
269     qmlClasses.clear();
270
271     findAllClasses(tree->root());
272     findAllFunctions(tree->root());
273     findAllLegaleseTexts(tree->root());
274     findAllNamespaces(tree->root());
275     findAllSince(tree->root());
276
277     Generator::generateTree(tree);
278     //reportOrphans(tree->root());
279     generateCollisionPages();
280
281     QString fileBase = project.toLower().simplified().replace(QLatin1Char(' '), QLatin1Char('-'));
282     generateIndex(fileBase, projectUrl, projectDescription);
283
284     helpProjectWriter->generate(tree_);
285     generateManifestFiles();
286 }
287
288 void HtmlGenerator::startText(const Node * /* relative */,
289                               CodeMarker * /* marker */)
290 {
291     inLink = false;
292     inContents = false;
293     inSectionHeading = false;
294     inTableHeader = false;
295     numTableRows = 0;
296     threeColumnEnumValueTable = true;
297     link.clear();
298     sectionNumber.clear();
299 }
300
301 /*!
302   Generate html from an instance of Atom.
303  */
304 int HtmlGenerator::generateAtom(const Atom *atom,
305                                 const Node *relative,
306                                 CodeMarker *marker)
307 {
308     int skipAhead = 0;
309     static bool in_para = false;
310
311     switch (atom->type()) {
312     case Atom::AbstractLeft:
313         if (relative)
314             relative->doc().location().warning(tr("\abstract is not implemented."));
315         else
316             Location::information(tr("\abstract is not implemented."));
317         break;
318     case Atom::AbstractRight:
319         break;
320     case Atom::AutoLink:
321         if (!inLink && !inContents && !inSectionHeading) {
322             const Node *node = 0;
323             QString link = getLink(atom, relative, marker, &node);
324             if (!link.isEmpty()) {
325                 beginLink(link, node, relative, marker);
326                 generateLink(atom, relative, marker);
327                 endLink();
328             }
329             else {
330                 out() << protectEnc(atom->string());
331             }
332         }
333         else {
334             out() << protectEnc(atom->string());
335         }
336         break;
337     case Atom::BaseName:
338         break;
339     case Atom::BriefLeft:
340         if (relative->type() == Node::Fake) {
341             if (relative->subType() != Node::Example) {
342                 skipAhead = skipAtoms(atom, Atom::BriefRight);
343                 break;
344             }
345         }
346
347         out() << "<p>";
348         if (relative->type() == Node::Property ||
349                 relative->type() == Node::Variable) {
350             QString str;
351             atom = atom->next();
352             while (atom != 0 && atom->type() != Atom::BriefRight) {
353                 if (atom->type() == Atom::String ||
354                         atom->type() == Atom::AutoLink)
355                     str += atom->string();
356                 skipAhead++;
357                 atom = atom->next();
358             }
359             str[0] = str[0].toLower();
360             if (str.endsWith(QLatin1Char('.')))
361                 str.truncate(str.length() - 1);
362             out() << "This ";
363             if (relative->type() == Node::Property)
364                 out() << "property";
365             else
366                 out() << "variable";
367             QStringList words = str.split(QLatin1Char(' '));
368             if (!(words.first() == "contains" || words.first() == "specifies"
369                   || words.first() == "describes" || words.first() == "defines"
370                   || words.first() == "holds" || words.first() == "determines"))
371                 out() << " holds ";
372             else
373                 out() << ' ';
374             out() << str << '.';
375         }
376         break;
377     case Atom::BriefRight:
378         if (relative->type() != Node::Fake)
379             out() << "</p>\n";
380         break;
381     case Atom::C:
382         // This may at one time have been used to mark up C++ code but it is
383         // now widely used to write teletype text. As a result, text marked
384         // with the \c command is not passed to a code marker.
385         out() << formattingLeftMap()[ATOM_FORMATTING_TELETYPE];
386         if (inLink) {
387             out() << protectEnc(plainCode(atom->string()));
388         }
389         else {
390             out() << protectEnc(plainCode(atom->string()));
391         }
392         out() << formattingRightMap()[ATOM_FORMATTING_TELETYPE];
393         break;
394     case Atom::CaptionLeft:
395         out() << "<p class=\"figCaption\">";
396         in_para = true;
397         break;
398     case Atom::CaptionRight:
399         endLink();
400         if (in_para) {
401             out() << "</p>\n";
402             in_para = false;
403         }
404         break;
405     case Atom::Code:
406         out() << "<pre class=\"cpp\">"
407               << trimmedTrailing(highlightedCode(indent(codeIndent,atom->string()),
408                                                  marker,relative))
409               << "</pre>\n";
410         break;
411     case Atom::Qml:
412         out() << "<pre class=\"qml\">"
413               << trimmedTrailing(highlightedCode(indent(codeIndent,atom->string()),
414                                                  marker,relative))
415               << "</pre>\n";
416         break;
417     case Atom::JavaScript:
418         out() << "<pre class=\"js\">"
419               << trimmedTrailing(highlightedCode(indent(codeIndent,atom->string()),
420                                                  marker,relative))
421               << "</pre>\n";
422         break;
423     case Atom::CodeNew:
424         out() << "<p>you can rewrite it as</p>\n"
425               << "<pre class=\"cpp\">"
426               << trimmedTrailing(highlightedCode(indent(codeIndent,atom->string()),
427                                                  marker,relative))
428               << "</pre>\n";
429         break;
430     case Atom::CodeOld:
431         out() << "<p>For example, if you have code like</p>\n";
432         // fallthrough
433     case Atom::CodeBad:
434         out() << "<pre class=\"cpp\">"
435               << trimmedTrailing(protectEnc(plainCode(indent(codeIndent,atom->string()))))
436               << "</pre>\n";
437         break;
438     case Atom::DivLeft:
439         out() << "<div";
440         if (!atom->string().isEmpty())
441             out() << ' ' << atom->string();
442         out() << '>';
443         break;
444     case Atom::DivRight:
445         out() << "</div>";
446         break;
447     case Atom::FootnoteLeft:
448         // ### For now
449         if (in_para) {
450             out() << "</p>\n";
451             in_para = false;
452         }
453         out() << "<!-- ";
454         break;
455     case Atom::FootnoteRight:
456         // ### For now
457         out() << "-->";
458         break;
459     case Atom::FormatElse:
460     case Atom::FormatEndif:
461     case Atom::FormatIf:
462         break;
463     case Atom::FormattingLeft:
464         if (atom->string().startsWith("span ")) {
465             out() << '<' + atom->string() << '>';
466         }
467         else
468             out() << formattingLeftMap()[atom->string()];
469         if (atom->string() == ATOM_FORMATTING_PARAMETER) {
470             if (atom->next() != 0 && atom->next()->type() == Atom::String) {
471                 QRegExp subscriptRegExp("([a-z]+)_([0-9n])");
472                 if (subscriptRegExp.exactMatch(atom->next()->string())) {
473                     out() << subscriptRegExp.cap(1) << "<sub>"
474                           << subscriptRegExp.cap(2) << "</sub>";
475                     skipAhead = 1;
476                 }
477             }
478         }
479         break;
480     case Atom::FormattingRight:
481         if (atom->string() == ATOM_FORMATTING_LINK) {
482             endLink();
483         }
484         else if (atom->string().startsWith("span ")) {
485             out() << "</span>";
486         }
487         else {
488             out() << formattingRightMap()[atom->string()];
489         }
490         break;
491     case Atom::AnnotatedList:
492     {
493         QList<Node*> values = tree_->groups().values(atom->string());
494         NodeMap nodeMap;
495         for (int i = 0; i < values.size(); ++i) {
496             const Node* n = values.at(i);
497             if ((n->status() != Node::Internal) && (n->access() != Node::Private)) {
498                 nodeMap.insert(n->nameForLists(),n);
499             }
500         }
501         generateAnnotatedList(relative, marker, nodeMap);
502     }
503         break;
504     case Atom::GeneratedList:
505         if (atom->string() == "annotatedclasses") {
506             generateAnnotatedList(relative, marker, nonCompatClasses);
507         }
508         else if (atom->string() == "classes") {
509             generateCompactList(relative, marker, nonCompatClasses, true);
510         }
511         else if (atom->string() == "qmlclasses") {
512             generateCompactList(relative, marker, qmlClasses, true);
513         }
514         else if (atom->string().contains("classesbymodule")) {
515             QString arg = atom->string().trimmed();
516             QString moduleName = atom->string().mid(atom->string().indexOf(
517                                                         "classesbymodule") + 15).trimmed();
518             if (moduleClassMap.contains(moduleName))
519                 generateAnnotatedList(relative, marker, moduleClassMap[moduleName]);
520         }
521         else if (atom->string().contains("classesbyedition")) {
522
523             QString arg = atom->string().trimmed();
524             QString editionName = atom->string().mid(atom->string().indexOf(
525                                                          "classesbyedition") + 16).trimmed();
526
527             if (editionModuleMap.contains(editionName)) {
528
529                 // Add all classes in the modules listed for that edition.
530                 NodeMap editionClasses;
531                 foreach (const QString &moduleName, editionModuleMap[editionName]) {
532                     if (moduleClassMap.contains(moduleName))
533                         editionClasses.unite(moduleClassMap[moduleName]);
534                 }
535
536                 // Add additional groups and remove groups of classes that
537                 // should be excluded from the edition.
538
539                 QMultiMap <QString, Node *> groups = tree_->groups();
540                 foreach (const QString &groupName, editionGroupMap[editionName]) {
541                     QList<Node *> groupClasses;
542                     if (groupName.startsWith(QLatin1Char('-'))) {
543                         groupClasses = groups.values(groupName.mid(1));
544                         foreach (const Node *node, groupClasses)
545                             editionClasses.remove(node->name());
546                     }
547                     else {
548                         groupClasses = groups.values(groupName);
549                         foreach (const Node *node, groupClasses)
550                             editionClasses.insert(node->name(), node);
551                     }
552                 }
553                 generateAnnotatedList(relative, marker, editionClasses);
554             }
555         }
556         else if (atom->string() == "classhierarchy") {
557             generateClassHierarchy(relative, marker, nonCompatClasses);
558         }
559         else if (atom->string() == "compatclasses") {
560             generateCompactList(relative, marker, compatClasses, false);
561         }
562         else if (atom->string() == "obsoleteclasses") {
563             generateCompactList(relative, marker, obsoleteClasses, false);
564         }
565         else if (atom->string() == "functionindex") {
566             generateFunctionIndex(relative, marker);
567         }
568         else if (atom->string() == "legalese") {
569             generateLegaleseList(relative, marker);
570         }
571         else if (atom->string() == "mainclasses") {
572             generateCompactList(relative, marker, mainClasses, true);
573         }
574         else if (atom->string() == "services") {
575             generateCompactList(relative, marker, serviceClasses, false);
576         }
577         else if (atom->string() == "overviews") {
578             generateOverviewList(relative, marker);
579         }
580         else if (atom->string() == "namespaces") {
581             generateAnnotatedList(relative, marker, namespaceIndex);
582         }
583         else if (atom->string() == "related") {
584             const FakeNode *fake = static_cast<const FakeNode *>(relative);
585             if (fake && !fake->groupMembers().isEmpty()) {
586                 NodeMap groupMembersMap;
587                 foreach (const Node *node, fake->groupMembers()) {
588                     if (node->type() == Node::Fake)
589                         groupMembersMap[fullName(node, relative, marker)] = node;
590                 }
591                 generateAnnotatedList(fake, marker, groupMembersMap);
592             }
593         }
594         else if (atom->string() == "relatedinline") {
595             const FakeNode *fake = static_cast<const FakeNode *>(relative);
596             if (fake && !fake->groupMembers().isEmpty()) {
597                 // Reverse the list into the original scan order.
598                 // Should be sorted.  But on what?  It may not be a
599                 // regular class or page definition.
600                 QList<const Node *> list;
601                 foreach (const Node *node, fake->groupMembers())
602                     list.prepend(node);
603                 foreach (const Node *node, list)
604                     generateBody(node, marker);
605             }
606         }
607         break;
608     case Atom::SinceList:
609     {
610         NewSinceMaps::const_iterator nsmap;
611         nsmap = newSinceMaps.constFind(atom->string());
612         NewClassMaps::const_iterator ncmap;
613         ncmap = newClassMaps.constFind(atom->string());
614         NewClassMaps::const_iterator nqcmap;
615         nqcmap = newQmlClassMaps.constFind(atom->string());
616
617         if ((nsmap != newSinceMaps.constEnd()) && !nsmap.value().isEmpty()) {
618             QList<Section> sections;
619             QList<Section>::ConstIterator s;
620
621             for (int i=0; i<LastSinceType; ++i)
622                 sections.append(Section(sinceTitle(i),QString(),QString(),QString()));
623
624             NodeMultiMap::const_iterator n = nsmap.value().constBegin();
625
626             while (n != nsmap.value().constEnd()) {
627
628                 const Node* node = n.value();
629                 switch (node->type()) {
630                 case Node::Fake:
631                     if (node->subType() == Node::QmlClass) {
632                         sections[QmlClass].appendMember((Node*)node);
633                     }
634                     break;
635                 case Node::Namespace:
636                     sections[Namespace].appendMember((Node*)node);
637                     break;
638                 case Node::Class:
639                     sections[Class].appendMember((Node*)node);
640                     break;
641                 case Node::Enum:
642                     sections[Enum].appendMember((Node*)node);
643                     break;
644                 case Node::Typedef:
645                     sections[Typedef].appendMember((Node*)node);
646                     break;
647                 case Node::Function: {
648                     const FunctionNode* fn = static_cast<const FunctionNode*>(node);
649                     if (fn->isMacro())
650                         sections[Macro].appendMember((Node*)node);
651                     else {
652                         Node* p = fn->parent();
653                         if (p) {
654                             if (p->type() == Node::Class)
655                                 sections[MemberFunction].appendMember((Node*)node);
656                             else if (p->type() == Node::Namespace) {
657                                 if (p->name().isEmpty())
658                                     sections[GlobalFunction].appendMember((Node*)node);
659                                 else
660                                     sections[NamespaceFunction].appendMember((Node*)node);
661                             }
662                             else
663                                 sections[GlobalFunction].appendMember((Node*)node);
664                         }
665                         else
666                             sections[GlobalFunction].appendMember((Node*)node);
667                     }
668                     break;
669                 }
670                 case Node::Property:
671                     sections[Property].appendMember((Node*)node);
672                     break;
673                 case Node::Variable:
674                     sections[Variable].appendMember((Node*)node);
675                     break;
676                 case Node::QmlProperty:
677                     sections[QmlProperty].appendMember((Node*)node);
678                     break;
679                 case Node::QmlSignal:
680                     sections[QmlSignal].appendMember((Node*)node);
681                     break;
682                 case Node::QmlSignalHandler:
683                     sections[QmlSignalHandler].appendMember((Node*)node);
684                     break;
685                 case Node::QmlMethod:
686                     sections[QmlMethod].appendMember((Node*)node);
687                     break;
688                 default:
689                     break;
690                 }
691                 ++n;
692             }
693
694             /*
695                   First generate the table of contents.
696                  */
697             out() << "<ul>\n";
698             s = sections.constBegin();
699             while (s != sections.constEnd()) {
700                 if (!(*s).members.isEmpty()) {
701
702                     out() << "<li>"
703                           << "<a href=\"#"
704                           << Doc::canonicalTitle((*s).name)
705                           << "\">"
706                           << (*s).name
707                           << "</a></li>\n";
708                 }
709                 ++s;
710             }
711             out() << "</ul>\n";
712
713             int idx = 0;
714             s = sections.constBegin();
715             while (s != sections.constEnd()) {
716                 if (!(*s).members.isEmpty()) {
717                     out() << "<a name=\""
718                           << Doc::canonicalTitle((*s).name)
719                           << "\"></a>\n";
720                     out() << "<h3>" << protectEnc((*s).name) << "</h3>\n";
721                     if (idx == Class)
722                         generateCompactList(0, marker, ncmap.value(), false, QString("Q"));
723                     else if (idx == QmlClass)
724                         generateCompactList(0, marker, nqcmap.value(), false, QString("Q"));
725                     else if (idx == MemberFunction) {
726                         ParentMaps parentmaps;
727                         ParentMaps::iterator pmap;
728                         NodeList::const_iterator i = s->members.constBegin();
729                         while (i != s->members.constEnd()) {
730                             Node* p = (*i)->parent();
731                             pmap = parentmaps.find(p);
732                             if (pmap == parentmaps.end())
733                                 pmap = parentmaps.insert(p,NodeMultiMap());
734                             pmap->insert((*i)->name(),(*i));
735                             ++i;
736                         }
737                         pmap = parentmaps.begin();
738                         while (pmap != parentmaps.end()) {
739                             NodeList nlist = pmap->values();
740                             out() << "<p>Class ";
741
742                             out() << "<a href=\""
743                                   << linkForNode(pmap.key(), 0)
744                                   << "\">";
745                             QStringList pieces = fullName(pmap.key(), 0, marker).split("::");
746                             out() << protectEnc(pieces.last());
747                             out() << "</a>"  << ":</p>\n";
748
749                             generateSection(nlist, 0, marker, CodeMarker::Summary);
750                             out() << "<br/>";
751                             ++pmap;
752                         }
753                     }
754                     else
755                         generateSection(s->members, 0, marker, CodeMarker::Summary);
756                 }
757                 ++idx;
758                 ++s;
759             }
760         }
761     }
762         break;
763     case Atom::BR:
764         out() << "<br />\n";
765         break;
766     case Atom::HR:
767         out() << "<hr />\n";
768         break;
769     case Atom::Image:
770     case Atom::InlineImage:
771     {
772         QString fileName = imageFileName(relative, atom->string());
773         QString text;
774         if (atom->next() != 0)
775             text = atom->next()->string();
776         if (atom->type() == Atom::Image)
777             out() << "<p class=\"centerAlign\">";
778         if (fileName.isEmpty()) {
779             relative->location().warning(tr("Missing image: %1").arg(protectEnc(atom->string())));
780             out() << "<font color=\"red\">[Missing image "
781                   << protectEnc(atom->string()) << "]</font>";
782         }
783         else {
784             QString prefix = "";
785             if (!baseDir().isEmpty())
786                 prefix = "../";
787             out() << "<img src=\"" << protectEnc(prefix + fileName) << '"';
788             if (!text.isEmpty())
789                 out() << " alt=\"" << protectEnc(text) << '"';
790             else
791                 out() << " alt=\"\"";
792             out() << " />";
793             helpProjectWriter->addExtraFile(fileName);
794             if ((relative->type() == Node::Fake) &&
795                     (relative->subType() == Node::Example)) {
796                 const ExampleNode* cen = static_cast<const ExampleNode*>(relative);
797                 if (cen->imageFileName().isEmpty()) {
798                     ExampleNode* en = const_cast<ExampleNode*>(cen);
799                     en->setImageFileName(fileName);
800                 }
801             }
802         }
803         if (atom->type() == Atom::Image)
804             out() << "</p>";
805     }
806         break;
807     case Atom::ImageText:
808         break;
809     case Atom::ImportantLeft:
810         out() << "<p>";
811         out() << formattingLeftMap()[ATOM_FORMATTING_BOLD];
812         out() << "Important: ";
813         out() << formattingRightMap()[ATOM_FORMATTING_BOLD];
814         break;
815     case Atom::ImportantRight:
816         out() << "</p>";
817         break;
818     case Atom::NoteLeft:
819         out() << "<p>";
820         out() << formattingLeftMap()[ATOM_FORMATTING_BOLD];
821         out() << "Note: ";
822         out() << formattingRightMap()[ATOM_FORMATTING_BOLD];
823         break;
824     case Atom::NoteRight:
825         out() << "</p>";
826         break;
827     case Atom::LegaleseLeft:
828         out() << "<div class=\"LegaleseLeft\">";
829         break;
830     case Atom::LegaleseRight:
831         out() << "</div>";
832         break;
833     case Atom::LineBreak:
834         out() << "<br/>";
835         break;
836     case Atom::Link:
837     {
838         const Node *node = 0;
839         QString myLink = getLink(atom, relative, marker, &node);
840         if (myLink.isEmpty()) {
841             myLink = getCollisionLink(atom);
842             if (myLink.isEmpty()) {
843                 relative->doc().location().warning(tr("Can't create link to '%1'")
844                                                    .arg(atom->string()));
845             }
846             else
847                 node = 0;
848         }
849         beginLink(myLink, node, relative, marker);
850         skipAhead = 1;
851     }
852         break;
853     case Atom::LinkNode:
854     {
855         const Node *node = CodeMarker::nodeForString(atom->string());
856         beginLink(linkForNode(node, relative), node, relative, marker);
857         skipAhead = 1;
858     }
859         break;
860     case Atom::ListLeft:
861         if (in_para) {
862             out() << "</p>\n";
863             in_para = false;
864         }
865         if (atom->string() == ATOM_LIST_BULLET) {
866             out() << "<ul>\n";
867         }
868         else if (atom->string() == ATOM_LIST_TAG) {
869             out() << "<dl>\n";
870         }
871         else if (atom->string() == ATOM_LIST_VALUE) {
872             threeColumnEnumValueTable = isThreeColumnEnumValueTable(atom);
873             if (threeColumnEnumValueTable) {
874                 out() << "<table class=\"valuelist\">";
875                 if (++numTableRows % 2 == 1)
876                     out() << "<tr valign=\"top\" class=\"odd\">";
877                 else
878                     out() << "<tr valign=\"top\" class=\"even\">";
879
880                 out() << "<th class=\"tblConst\">Constant</th>"
881                       << "<th class=\"tblval\">Value</th>"
882                       << "<th class=\"tbldscr\">Description</th></tr>\n";
883             }
884             else {
885                 out() << "<table class=\"valuelist\">"
886                       << "<tr><th class=\"tblConst\">Constant</th><th class=\"tblVal\">Value</th></tr>\n";
887             }
888         }
889         else {
890             out() << "<ol class=";
891             if (atom->string() == ATOM_LIST_UPPERALPHA) {
892                 out() << "\"A\"";
893             } /* why type? changed to */
894             else if (atom->string() == ATOM_LIST_LOWERALPHA) {
895                 out() << "\"a\"";
896             }
897             else if (atom->string() == ATOM_LIST_UPPERROMAN) {
898                 out() << "\"I\"";
899             }
900             else if (atom->string() == ATOM_LIST_LOWERROMAN) {
901                 out() << "\"i\"";
902             }
903             else { // (atom->string() == ATOM_LIST_NUMERIC)
904                 out() << "\"1\"";
905             }
906             if (atom->next() != 0 && atom->next()->string().toInt() != 1)
907                 out() << " start=\"" << atom->next()->string() << '"';
908             out() << ">\n";
909         }
910         break;
911     case Atom::ListItemNumber:
912         break;
913     case Atom::ListTagLeft:
914         if (atom->string() == ATOM_LIST_TAG) {
915             out() << "<dt>";
916         }
917         else { // (atom->string() == ATOM_LIST_VALUE)
918             // ### Trenton
919
920             out() << "<tr><td class=\"topAlign\"><tt>"
921                   << protectEnc(plainCode(marker->markedUpEnumValue(atom->next()->string(),
922                                                                     relative)))
923                   << "</tt></td><td class=\"topAlign\">";
924
925             QString itemValue;
926             if (relative->type() == Node::Enum) {
927                 const EnumNode *enume = static_cast<const EnumNode *>(relative);
928                 itemValue = enume->itemValue(atom->next()->string());
929             }
930
931             if (itemValue.isEmpty())
932                 out() << '?';
933             else
934                 out() << "<tt>" << protectEnc(itemValue) << "</tt>";
935
936             skipAhead = 1;
937         }
938         break;
939     case Atom::ListTagRight:
940         if (atom->string() == ATOM_LIST_TAG)
941             out() << "</dt>\n";
942         break;
943     case Atom::ListItemLeft:
944         if (atom->string() == ATOM_LIST_TAG) {
945             out() << "<dd>";
946         }
947         else if (atom->string() == ATOM_LIST_VALUE) {
948             if (threeColumnEnumValueTable) {
949                 out() << "</td><td class=\"topAlign\">";
950                 if (matchAhead(atom, Atom::ListItemRight))
951                     out() << "&nbsp;";
952             }
953         }
954         else {
955             out() << "<li>";
956         }
957         if (matchAhead(atom, Atom::ParaLeft))
958             skipAhead = 1;
959         break;
960     case Atom::ListItemRight:
961         if (atom->string() == ATOM_LIST_TAG) {
962             out() << "</dd>\n";
963         }
964         else if (atom->string() == ATOM_LIST_VALUE) {
965             out() << "</td></tr>\n";
966         }
967         else {
968             out() << "</li>\n";
969         }
970         break;
971     case Atom::ListRight:
972         if (atom->string() == ATOM_LIST_BULLET) {
973             out() << "</ul>\n";
974         }
975         else if (atom->string() == ATOM_LIST_TAG) {
976             out() << "</dl>\n";
977         }
978         else if (atom->string() == ATOM_LIST_VALUE) {
979             out() << "</table>\n";
980         }
981         else {
982             out() << "</ol>\n";
983         }
984         break;
985     case Atom::Nop:
986         break;
987     case Atom::ParaLeft:
988         out() << "<p>";
989         in_para = true;
990         break;
991     case Atom::ParaRight:
992         endLink();
993         if (in_para) {
994             out() << "</p>\n";
995             in_para = false;
996         }
997         //if (!matchAhead(atom, Atom::ListItemRight) && !matchAhead(atom, Atom::TableItemRight))
998         //    out() << "</p>\n";
999         break;
1000     case Atom::QuotationLeft:
1001         out() << "<blockquote>";
1002         break;
1003     case Atom::QuotationRight:
1004         out() << "</blockquote>\n";
1005         break;
1006     case Atom::RawString:
1007         out() << atom->string();
1008         break;
1009     case Atom::SectionLeft:
1010         out() << "<a name=\"" << Doc::canonicalTitle(Text::sectionHeading(atom).toString())
1011               << "\"></a>" << divNavTop << '\n';
1012         break;
1013     case Atom::SectionRight:
1014         break;
1015     case Atom::SectionHeadingLeft:
1016         out() << "<h" + QString::number(atom->string().toInt() + hOffset(relative)) + QLatin1Char('>');
1017         inSectionHeading = true;
1018         break;
1019     case Atom::SectionHeadingRight:
1020         out() << "</h" + QString::number(atom->string().toInt() + hOffset(relative)) + ">\n";
1021         inSectionHeading = false;
1022         break;
1023     case Atom::SidebarLeft:
1024         break;
1025     case Atom::SidebarRight:
1026         break;
1027     case Atom::String:
1028         if (inLink && !inContents && !inSectionHeading) {
1029             generateLink(atom, relative, marker);
1030         }
1031         else {
1032             out() << protectEnc(atom->string());
1033         }
1034         break;
1035     case Atom::TableLeft:
1036     {
1037         QString p1, p2;
1038         QString attr = "generic";
1039         QString width;
1040         if (in_para) {
1041             out() << "</p>\n";
1042             in_para = false;
1043         }
1044         if (atom->count() > 0) {
1045             p1 = atom->string(0);
1046             if (atom->count() > 1)
1047                 p2 = atom->string(1);
1048         }
1049         if (!p1.isEmpty()) {
1050             if (p1 == "borderless")
1051                 attr = p1;
1052             else if (p1.contains("%"))
1053                 width = p1;
1054         }
1055         if (!p2.isEmpty()) {
1056             if (p2 == "borderless")
1057                 attr = p2;
1058             else if (p2.contains("%"))
1059                 width = p2;
1060         }
1061         out() << "<table class=\"" << attr << "\"";
1062         if (!width.isEmpty())
1063             out() << " width=\"" << width << "\"";
1064         out() << ">\n ";
1065         numTableRows = 0;
1066     }
1067         break;
1068     case Atom::TableRight:
1069         out() << "</table>\n";
1070         break;
1071     case Atom::TableHeaderLeft:
1072         out() << "<thead><tr class=\"qt-style\">";
1073         inTableHeader = true;
1074         break;
1075     case Atom::TableHeaderRight:
1076         out() << "</tr>";
1077         if (matchAhead(atom, Atom::TableHeaderLeft)) {
1078             skipAhead = 1;
1079             out() << "\n<tr class=\"qt-style\">";
1080         }
1081         else {
1082             out() << "</thead>\n";
1083             inTableHeader = false;
1084         }
1085         break;
1086     case Atom::TableRowLeft:
1087         if (!atom->string().isEmpty())
1088             out() << "<tr " << atom->string() << '>';
1089         else if (++numTableRows % 2 == 1)
1090             out() << "<tr valign=\"top\" class=\"odd\">";
1091         else
1092             out() << "<tr valign=\"top\" class=\"even\">";
1093         break;
1094     case Atom::TableRowRight:
1095         out() << "</tr>\n";
1096         break;
1097     case Atom::TableItemLeft:
1098     {
1099         if (inTableHeader)
1100             out() << "<th ";
1101         else
1102             out() << "<td ";
1103
1104         for (int i=0; i<atom->count(); ++i) {
1105             if (i > 0)
1106                 out() << ' ';
1107             QString p = atom->string(i);
1108             if (p.contains('=')) {
1109                 out() << p;
1110             }
1111             else {
1112                 QStringList spans = p.split(",");
1113                 if (spans.size() == 2) {
1114                     if (spans.at(0) != "1")
1115                         out() << " colspan=\"" << spans.at(0) << '"';
1116                     if (spans.at(1) != "1")
1117                         out() << " rowspan=\"" << spans.at(1) << '"';
1118                 }
1119             }
1120         }
1121         if (inTableHeader)
1122             out() << '>';
1123         else {
1124             out() << '>';
1125             //out() << "><p>";
1126         }
1127         if (matchAhead(atom, Atom::ParaLeft))
1128             skipAhead = 1;
1129     }
1130         break;
1131     case Atom::TableItemRight:
1132         if (inTableHeader)
1133             out() << "</th>";
1134         else {
1135             out() << "</td>";
1136             //out() << "</p></td>";
1137         }
1138         if (matchAhead(atom, Atom::ParaLeft))
1139             skipAhead = 1;
1140         break;
1141     case Atom::TableOfContents:
1142         break;
1143     case Atom::Target:
1144         out() << "<a name=\"" << Doc::canonicalTitle(atom->string()) << "\"></a>";
1145         break;
1146     case Atom::UnhandledFormat:
1147         out() << "<b class=\"redFont\">&lt;Missing HTML&gt;</b>";
1148         break;
1149     case Atom::UnknownCommand:
1150         out() << "<b class=\"redFont\"><code>\\" << protectEnc(atom->string())
1151               << "</code></b>";
1152         break;
1153     case Atom::QmlText:
1154     case Atom::EndQmlText:
1155         // don't do anything with these. They are just tags.
1156         break;
1157     default:
1158         unknownAtom(atom);
1159     }
1160     return skipAhead;
1161 }
1162
1163 /*!
1164   Generate a reference page for a C++ class.
1165  */
1166 void HtmlGenerator::generateClassLikeNode(InnerNode* inner, CodeMarker* marker)
1167 {
1168     QList<Section> sections;
1169     QList<Section>::ConstIterator s;
1170
1171     ClassNode* classe = 0;
1172
1173     QString title;
1174     QString rawTitle;
1175     QString fullTitle;
1176     if (inner->type() == Node::Namespace) {
1177         rawTitle = marker->plainName(inner);
1178         fullTitle = marker->plainFullName(inner);
1179         title = rawTitle + " Namespace";
1180     }
1181     else if (inner->type() == Node::Class) {
1182         classe = static_cast<ClassNode*>(inner);
1183         rawTitle = marker->plainName(inner);
1184         fullTitle = marker->plainFullName(inner);
1185         title = rawTitle + " Class";
1186     }
1187
1188     Text subtitleText;
1189     if (rawTitle != fullTitle)
1190         subtitleText << "(" << Atom(Atom::AutoLink, fullTitle) << ")"
1191                      << Atom(Atom::LineBreak);
1192
1193     generateHeader(title, inner, marker);
1194     sections = marker->sections(inner, CodeMarker::Summary, CodeMarker::Okay);
1195     generateTableOfContents(inner,marker,&sections);
1196     generateTitle(title, subtitleText, SmallSubTitle, inner, marker);
1197     generateBrief(inner, marker);
1198     generateIncludes(inner, marker);
1199     generateStatus(inner, marker);
1200     if (classe) {
1201         generateInherits(classe, marker);
1202         generateInheritedBy(classe, marker);
1203         if (classe->qmlElement() != 0)
1204             generateInstantiatedBy(classe,marker);
1205     }
1206     generateThreadSafeness(inner, marker);
1207     generateSince(inner, marker);
1208
1209     out() << "<ul>\n";
1210
1211     QString membersLink = generateListOfAllMemberFile(inner, marker);
1212     if (!membersLink.isEmpty())
1213         out() << "<li><a href=\"" << membersLink << "\">"
1214               << "List of all members, including inherited members</a></li>\n";
1215
1216     QString obsoleteLink = generateLowStatusMemberFile(inner,
1217                                                        marker,
1218                                                        CodeMarker::Obsolete);
1219     if (!obsoleteLink.isEmpty())
1220         out() << "<li><a href=\"" << obsoleteLink << "\">"
1221               << "Obsolete members</a></li>\n";
1222
1223     QString compatLink = generateLowStatusMemberFile(inner,
1224                                                      marker,
1225                                                      CodeMarker::Compat);
1226     if (!compatLink.isEmpty())
1227         out() << "<li><a href=\"" << compatLink << "\">"
1228               << "Compatibility members</a></li>\n";
1229
1230     out() << "</ul>\n";
1231
1232     bool needOtherSection = false;
1233
1234     /*
1235       sections is built above for the call to generateTableOfContents().
1236      */
1237     s = sections.constBegin();
1238     while (s != sections.constEnd()) {
1239         if (s->members.isEmpty() && s->reimpMembers.isEmpty()) {
1240             if (!s->inherited.isEmpty())
1241                 needOtherSection = true;
1242         }
1243         else {
1244             if (!s->members.isEmpty()) {
1245                 // out() << "<hr />\n";
1246                 out() << "<a name=\""
1247                       << registerRef((*s).name.toLower())
1248                       << "\"></a>" << divNavTop << "\n";
1249                 out() << "<h2>" << protectEnc((*s).name) << "</h2>\n";
1250                 generateSection(s->members, inner, marker, CodeMarker::Summary);
1251             }
1252             if (!s->reimpMembers.isEmpty()) {
1253                 QString name = QString("Reimplemented ") + (*s).name;
1254                 //  out() << "<hr />\n";
1255                 out() << "<a name=\""
1256                       << registerRef(name.toLower())
1257                       << "\"></a>" << divNavTop << "\n";
1258                 out() << "<h2>" << protectEnc(name) << "</h2>\n";
1259                 generateSection(s->reimpMembers, inner, marker, CodeMarker::Summary);
1260             }
1261
1262             if (!s->inherited.isEmpty()) {
1263                 out() << "<ul>\n";
1264                 generateSectionInheritedList(*s, inner, marker);
1265                 out() << "</ul>\n";
1266             }
1267         }
1268         ++s;
1269     }
1270
1271     if (needOtherSection) {
1272         out() << "<h3>Additional Inherited Members</h3>\n"
1273                  "<ul>\n";
1274
1275         s = sections.constBegin();
1276         while (s != sections.constEnd()) {
1277             if (s->members.isEmpty() && !s->inherited.isEmpty())
1278                 generateSectionInheritedList(*s, inner, marker);
1279             ++s;
1280         }
1281         out() << "</ul>\n";
1282     }
1283
1284     out() << "<a name=\"" << registerRef("details") << "\"></a>" << divNavTop << '\n';
1285
1286     if (!inner->doc().isEmpty()) {
1287         generateExtractionMark(inner, DetailedDescriptionMark);
1288         //out() << "<hr />\n"
1289         out() << "<div class=\"descr\">\n" // QTBUG-9504
1290               << "<h2>" << "Detailed Description" << "</h2>\n";
1291         generateBody(inner, marker);
1292         out() << "</div>\n"; // QTBUG-9504
1293         generateAlsoList(inner, marker);
1294         generateMaintainerList(inner, marker);
1295         generateExtractionMark(inner, EndMark);
1296     }
1297
1298     sections = marker->sections(inner, CodeMarker::Detailed, CodeMarker::Okay);
1299     s = sections.constBegin();
1300     while (s != sections.constEnd()) {
1301         //out() << "<hr />\n";
1302         if (!(*s).divClass.isEmpty())
1303             out() << "<div class=\"" << (*s).divClass << "\">\n"; // QTBUG-9504
1304         out() << "<h2>" << protectEnc((*s).name) << "</h2>\n";
1305
1306         NodeList::ConstIterator m = (*s).members.constBegin();
1307         while (m != (*s).members.constEnd()) {
1308             if ((*m)->access() != Node::Private) { // ### check necessary?
1309                 if ((*m)->type() != Node::Class)
1310                     generateDetailedMember(*m, inner, marker);
1311                 else {
1312                     out() << "<h3> class ";
1313                     generateFullName(*m, inner, marker);
1314                     out() << "</h3>";
1315                     generateBrief(*m, marker, inner);
1316                 }
1317
1318                 QStringList names;
1319                 names << (*m)->name();
1320                 if ((*m)->type() == Node::Function) {
1321                     const FunctionNode *func = reinterpret_cast<const FunctionNode *>(*m);
1322                     if (func->metaness() == FunctionNode::Ctor ||
1323                             func->metaness() == FunctionNode::Dtor ||
1324                             func->overloadNumber() != 1)
1325                         names.clear();
1326                 }
1327                 else if ((*m)->type() == Node::Property) {
1328                     const PropertyNode *prop = reinterpret_cast<const PropertyNode *>(*m);
1329                     if (!prop->getters().isEmpty() &&
1330                             !names.contains(prop->getters().first()->name()))
1331                         names << prop->getters().first()->name();
1332                     if (!prop->setters().isEmpty())
1333                         names << prop->setters().first()->name();
1334                     if (!prop->resetters().isEmpty())
1335                         names << prop->resetters().first()->name();
1336                     if (!prop->notifiers().isEmpty())
1337                         names << prop->notifiers().first()->name();
1338                 }
1339                 else if ((*m)->type() == Node::Enum) {
1340                     const EnumNode *enume = reinterpret_cast<const EnumNode*>(*m);
1341                     if (enume->flagsType())
1342                         names << enume->flagsType()->name();
1343
1344                     foreach (const QString &enumName,
1345                              enume->doc().enumItemNames().toSet() -
1346                              enume->doc().omitEnumItemNames().toSet())
1347                         names << plainCode(marker->markedUpEnumValue(enumName,
1348                                                                      enume));
1349                 }
1350             }
1351             ++m;
1352         }
1353         if (!(*s).divClass.isEmpty())
1354             out() << "</div>\n"; // QTBUG-9504
1355         ++s;
1356     }
1357     generateFooter(inner);
1358 }
1359
1360 /*!
1361   We delayed generation of the disambiguation pages until now, after
1362   all the other pages have been generated. We do this because we might
1363   encounter a link command that tries to link to a target on a QML
1364   component page, but the link doesn't specify the module identifer
1365   for the component, and the component name without a module
1366   identifier is ambiguous. When such a link is found, qdoc can't find
1367   the target, so it appends the target to the NameCollisionNode. After
1368   the tree has been traversed and all these ambiguous links have been
1369   added to the name collision nodes, this function is called. The list
1370   of collision nodes is traversed here, and the disambiguation page for
1371   each collision is generated. The disambiguation page will not only
1372   disambiguate links to the component pages, but it will also disambiguate
1373   links to properties, section headers, etc.
1374  */
1375 void HtmlGenerator::generateCollisionPages()
1376 {
1377     if (collisionNodes.isEmpty())
1378         return;
1379
1380     for (int i=0; i<collisionNodes.size(); ++i) {
1381         NameCollisionNode* ncn = collisionNodes.at(i);
1382         if (!ncn)
1383             continue;
1384
1385         NodeList collisions;
1386         const NodeList& nl = ncn->childNodes();
1387         if (!nl.isEmpty()) {
1388             NodeList::ConstIterator it = nl.constBegin();
1389             while (it != nl.constEnd()) {
1390                 if (!(*it)->isInternal())
1391                     collisions.append(*it);
1392                 ++it;
1393             }
1394         }
1395         if (collisions.size() <= 1)
1396             continue;
1397
1398         ncn->clearCurrentChild();
1399         beginSubPage(ncn, Generator::fileName(ncn));
1400         QString fullTitle = ncn->fullTitle();
1401         QString htmlTitle = fullTitle;
1402         CodeMarker* marker = CodeMarker::markerForFileName(ncn->location().filePath());
1403         if (ncn->isQmlNode()) {
1404             // Replace the marker with a QML code marker.
1405             if (ncn->isQmlNode())
1406                 marker = CodeMarker::markerForLanguage(QLatin1String("QML"));
1407         }
1408
1409         generateHeader(htmlTitle, ncn, marker);
1410         if (!fullTitle.isEmpty())
1411             out() << "<h1 class=\"title\">" << protectEnc(fullTitle) << "</h1>\n";
1412
1413         NodeMap nm;
1414         for (int i=0; i<collisions.size(); ++i) {
1415             Node* n = collisions.at(i);
1416             QString t;
1417             if (!n->qmlModuleIdentifier().isEmpty())
1418                 t = n->qmlModuleIdentifier() + "::";
1419             t += protectEnc(fullTitle);
1420             nm.insertMulti(t,n);
1421         }
1422         generateAnnotatedList(ncn, marker, nm, true);
1423
1424         QList<QString> targets;
1425         if (!ncn->linkTargets().isEmpty()) {
1426             QMap<QString,QString>::ConstIterator t = ncn->linkTargets().constBegin();
1427             while (t != ncn->linkTargets().constEnd()) {
1428                 int count = 0;
1429                 for (int i=0; i<collisions.size(); ++i) {
1430                     InnerNode* n = static_cast<InnerNode*>(collisions.at(i));
1431                     if (n->findChildNodeByName(t.key())) {
1432                         ++count;
1433                         if (count > 1) {
1434                             targets.append(t.key());
1435                             break;
1436                         }
1437                     }
1438                 }
1439                 ++t;
1440             }
1441         }
1442         if (!targets.isEmpty()) {
1443             QList<QString>::ConstIterator t = targets.constBegin();
1444             while (t != targets.constEnd()) {
1445                 out() << "<a name=\"" << Doc::canonicalTitle(*t) << "\"></a>";
1446                 out() << "<h2 class=\"title\">" << protectEnc(*t) << "</h2>\n";
1447                 out() << "<ul>\n";
1448                 for (int i=0; i<collisions.size(); ++i) {
1449                     InnerNode* n = static_cast<InnerNode*>(collisions.at(i));
1450                     Node* p = n->findChildNodeByName(*t);
1451                     if (p) {
1452                         QString link = linkForNode(p,0);
1453                         QString label;
1454                         if (!n->qmlModuleIdentifier().isEmpty())
1455                             label = n->qmlModuleIdentifier() + "::";
1456                         label += n->name() + "::" + p->name();
1457                         out() << "<li>";
1458                         out() << "<a href=\"" << link << "\">";
1459                         out() << protectEnc(label) << "</a>";
1460                         out() << "</li>\n";
1461                     }
1462                 }
1463                 out() << "</ul>\n";
1464                 ++t;
1465             }
1466         }
1467
1468         generateFooter(ncn);
1469         endSubPage();
1470     }
1471 }
1472
1473 /*!
1474   Generate the HTML page for an entity that doesn't map
1475   to any underlying parsable C++ class or QML component.
1476  */
1477 void HtmlGenerator::generateFakeNode(FakeNode* fake, CodeMarker* marker)
1478 {
1479     /*
1480       If the fake node is a page node, and if the page type
1481       is DITA map page, write the node's contents as a dita
1482       map and return without doing anything else.
1483      */
1484     if (fake->subType() == Node::Page && fake->pageType() == Node::DitaMapPage) {
1485         const DitaMapNode* dmn = static_cast<const DitaMapNode*>(fake);
1486         writeDitaMap(dmn);
1487         return;
1488     }
1489
1490     SubTitleSize subTitleSize = LargeSubTitle;
1491     QList<Section> sections;
1492     QList<Section>::const_iterator s;
1493     QString fullTitle = fake->fullTitle();
1494     QString htmlTitle = fullTitle;
1495
1496     if (fake->subType() == Node::File && !fake->subTitle().isEmpty()) {
1497         subTitleSize = SmallSubTitle;
1498         htmlTitle += " (" + fake->subTitle() + QLatin1Char(')');
1499     }
1500     else if (fake->subType() == Node::QmlBasicType) {
1501         fullTitle = "QML Basic Type: " + fullTitle;
1502         htmlTitle = fullTitle;
1503
1504         // Replace the marker with a QML code marker.
1505         marker = CodeMarker::markerForLanguage(QLatin1String("QML"));
1506     }
1507
1508     generateHeader(htmlTitle, fake, marker);
1509     /*
1510       Generate the TOC for the new doc format.
1511       Don't generate a TOC for the home page.
1512     */
1513     QmlClassNode* qml_cn = 0;
1514     if (fake->subType() == Node::QmlClass) {
1515         qml_cn = static_cast<QmlClassNode*>(fake);
1516         sections = marker->qmlSections(qml_cn,CodeMarker::Summary);
1517         generateTableOfContents(fake,marker,&sections);
1518
1519         // Replace the marker with a QML code marker.
1520         marker = CodeMarker::markerForLanguage(QLatin1String("QML"));
1521     }
1522     else if (fake->subType() != Node::Collision && fake->name() != QString("index.html"))
1523         generateTableOfContents(fake,marker,0);
1524
1525     generateTitle(fullTitle,
1526                   Text() << fake->subTitle(),
1527                   subTitleSize,
1528                   fake,
1529                   marker);
1530
1531     if (fake->subType() == Node::Module) {
1532         // Generate brief text and status for modules.
1533         generateBrief(fake, marker);
1534         generateStatus(fake, marker);
1535         generateSince(fake, marker);
1536
1537         if (moduleNamespaceMap.contains(fake->name())) {
1538             out() << "<a name=\"" << registerRef("namespaces") << "\"></a>" << divNavTop << '\n';
1539             out() << "<h2>Namespaces</h2>\n";
1540             generateAnnotatedList(fake, marker, moduleNamespaceMap[fake->name()]);
1541         }
1542         if (moduleClassMap.contains(fake->name())) {
1543             out() << "<a name=\"" << registerRef("classes") << "\"></a>" << divNavTop << '\n';
1544             out() << "<h2>Classes</h2>\n";
1545             generateAnnotatedList(fake, marker, moduleClassMap[fake->name()]);
1546         }
1547     }
1548     else if (fake->subType() == Node::HeaderFile) {
1549         // Generate brief text and status for modules.
1550         generateBrief(fake, marker);
1551         generateStatus(fake, marker);
1552         generateSince(fake, marker);
1553
1554         out() << "<ul>\n";
1555
1556         QString membersLink = generateListOfAllMemberFile(fake, marker);
1557         if (!membersLink.isEmpty())
1558             out() << "<li><a href=\"" << membersLink << "\">"
1559                   << "List of all members, including inherited members</a></li>\n";
1560
1561         QString obsoleteLink = generateLowStatusMemberFile(fake,
1562                                                            marker,
1563                                                            CodeMarker::Obsolete);
1564         if (!obsoleteLink.isEmpty())
1565             out() << "<li><a href=\"" << obsoleteLink << "\">"
1566                   << "Obsolete members</a></li>\n";
1567
1568         QString compatLink = generateLowStatusMemberFile(fake,
1569                                                          marker,
1570                                                          CodeMarker::Compat);
1571         if (!compatLink.isEmpty())
1572             out() << "<li><a href=\"" << compatLink << "\">"
1573                   << "Compatibility members</a></li>\n";
1574
1575         out() << "</ul>\n";
1576     }
1577     else if (fake->subType() == Node::QmlClass) {
1578         const_cast<FakeNode*>(fake)->setCurrentChild();
1579         ClassNode* cn = qml_cn->classNode();
1580         generateBrief(qml_cn, marker);
1581         generateQmlInherits(qml_cn, marker);
1582         generateQmlInheritedBy(qml_cn, marker);
1583         generateQmlInstantiates(qml_cn, marker);
1584         generateSince(qml_cn, marker);
1585
1586         QString allQmlMembersLink = generateAllQmlMembersFile(qml_cn, marker);
1587         if (!allQmlMembersLink.isEmpty()) {
1588             out() << "<ul>\n";
1589             out() << "<li><a href=\"" << allQmlMembersLink << "\">"
1590                   << "List of all members, including inherited members</a></li>\n";
1591             out() << "</ul>\n";
1592         }
1593
1594         s = sections.constBegin();
1595         while (s != sections.constEnd()) {
1596             out() << "<a name=\"" << registerRef((*s).name.toLower())
1597                   << "\"></a>" << divNavTop << '\n';
1598             out() << "<h2>" << protectEnc((*s).name) << "</h2>\n";
1599             generateQmlSummary(*s,fake,marker);
1600             ++s;
1601         }
1602
1603         generateExtractionMark(fake, DetailedDescriptionMark);
1604         out() << "<a name=\"" << registerRef("details") << "\"></a>" << divNavTop << '\n';
1605         out() << "<h2>" << "Detailed Description" << "</h2>\n";
1606         generateBody(fake, marker);
1607         if (cn)
1608             generateQmlText(cn->doc().body(), cn, marker, fake->name());
1609         generateAlsoList(fake, marker);
1610         generateExtractionMark(fake, EndMark);
1611         //out() << "<hr />\n";
1612
1613         sections = marker->qmlSections(qml_cn,CodeMarker::Detailed);
1614         s = sections.constBegin();
1615         while (s != sections.constEnd()) {
1616             out() << "<h2>" << protectEnc((*s).name) << "</h2>\n";
1617             NodeList::ConstIterator m = (*s).members.constBegin();
1618             while (m != (*s).members.constEnd()) {
1619                 generateDetailedQmlMember(*m, fake, marker);
1620                 out() << "<br/>\n";
1621                 ++m;
1622             }
1623             ++s;
1624         }
1625         generateFooter(fake);
1626         const_cast<FakeNode*>(fake)->clearCurrentChild();
1627         return;
1628     }
1629
1630     sections = marker->sections(fake, CodeMarker::Summary, CodeMarker::Okay);
1631     s = sections.constBegin();
1632     while (s != sections.constEnd()) {
1633         out() << "<a name=\"" << registerRef((*s).name) << "\"></a>" << divNavTop << '\n';
1634         out() << "<h2>" << protectEnc((*s).name) << "</h2>\n";
1635         generateSectionList(*s, fake, marker, CodeMarker::Summary);
1636         ++s;
1637     }
1638
1639     Text brief = fake->doc().briefText();
1640     if (fake->subType() == Node::Module && !brief.isEmpty()) {
1641         generateExtractionMark(fake, DetailedDescriptionMark);
1642         out() << "<a name=\"" << registerRef("details") << "\"></a>" << divNavTop << '\n';
1643         out() << "<div class=\"descr\">\n"; // QTBUG-9504
1644         out() << "<h2>" << "Detailed Description" << "</h2>\n";
1645     }
1646     else {
1647         generateExtractionMark(fake, DetailedDescriptionMark);
1648         out() << "<div class=\"descr\"> <a name=\"" << registerRef("details") << "\"></a>\n"; // QTBUG-9504
1649     }
1650
1651     generateBody(fake, marker);
1652     out() << "</div>\n"; // QTBUG-9504
1653     generateAlsoList(fake, marker);
1654     generateExtractionMark(fake, EndMark);
1655
1656     if ((fake->subType() == Node::Group) && !fake->groupMembers().isEmpty()) {
1657         NodeMap groupMembersMap;
1658         foreach (const Node *node, fake->groupMembers()) {
1659             if (node->type() == Node::Class || node->type() == Node::Namespace)
1660                 groupMembersMap[node->name()] = node;
1661         }
1662         generateAnnotatedList(fake, marker, groupMembersMap);
1663     }
1664     else if ((fake->subType() == Node::QmlModule) && !fake->qmlModuleMembers().isEmpty()) {
1665         NodeMap qmlModuleMembersMap;
1666         foreach (const Node* node, fake->qmlModuleMembers()) {
1667             if (node->type() == Node::Fake && node->subType() == Node::QmlClass)
1668                 qmlModuleMembersMap[node->name()] = node;
1669         }
1670         generateAnnotatedList(fake, marker, qmlModuleMembersMap);
1671     }
1672
1673     sections = marker->sections(fake, CodeMarker::Detailed, CodeMarker::Okay);
1674     s = sections.constBegin();
1675     while (s != sections.constEnd()) {
1676         //out() << "<hr />\n";
1677         out() << "<h2>" << protectEnc((*s).name) << "</h2>\n";
1678
1679         NodeList::ConstIterator m = (*s).members.constBegin();
1680         while (m != (*s).members.constEnd()) {
1681             generateDetailedMember(*m, fake, marker);
1682             ++m;
1683         }
1684         ++s;
1685     }
1686     generateFooter(fake);
1687 }
1688
1689 /*!
1690   Returns "html" for this subclass of Generator.
1691  */
1692 QString HtmlGenerator::fileExtension() const
1693 {
1694     return "html";
1695 }
1696
1697 /*!
1698   Output breadcrumb list in the html file.
1699  */
1700 void HtmlGenerator::generateBreadCrumbs(const QString &title,
1701                                         const Node *node,
1702                                         CodeMarker *marker)
1703 {
1704     if (noBreadCrumbs)
1705         return;
1706
1707     Text breadcrumbs;
1708     if (node->type() == Node::Class) {
1709         const ClassNode *cn = static_cast<const ClassNode *>(node);
1710         QString name =  node->moduleName();
1711         breadcrumbs << Atom(Atom::ListItemLeft)
1712                     << Atom(Atom::Link, QLatin1String("All Modules"))
1713                     << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK)
1714                     << Atom(Atom::String, QLatin1String("Modules"))
1715                     << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK)
1716                     << Atom(Atom::ListItemRight);
1717         if (!name.isEmpty())
1718             breadcrumbs << Atom(Atom::ListItemLeft)
1719                         << Atom(Atom::AutoLink, name)
1720                         << Atom(Atom::ListItemRight);
1721         if (!cn->name().isEmpty())
1722             breadcrumbs << Atom(Atom::ListItemLeft)
1723                         << Atom(Atom::String, protectEnc(cn->name()))
1724                         << Atom(Atom::ListItemRight);
1725     }
1726     else if (node->type() == Node::Fake) {
1727         const FakeNode* fn = static_cast<const FakeNode*>(node);
1728         if (node->subType() == Node::Module) {
1729             breadcrumbs << Atom(Atom::ListItemLeft)
1730                         << Atom(Atom::Link, QLatin1String("All Modules"))
1731                         << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK)
1732                         << Atom(Atom::String, QLatin1String("Modules"))
1733                         << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK)
1734                         << Atom(Atom::ListItemRight);
1735             QString name =  node->name();
1736             if (!name.isEmpty())
1737                 breadcrumbs << Atom(Atom::ListItemLeft)
1738                             << Atom(Atom::String, protectEnc(name))
1739                             << Atom(Atom::ListItemRight);
1740         }
1741         else if (node->subType() == Node::Group) {
1742             if (fn->name() == QString("modules"))
1743                 breadcrumbs << Atom(Atom::String, QLatin1String("Modules"));
1744             else
1745                 breadcrumbs << Atom(Atom::ListItemLeft)
1746                             << Atom(Atom::String, protectEnc(title))
1747                             << Atom(Atom::ListItemRight);
1748         }
1749         else if (node->subType() == Node::Page) {
1750             if (fn->name() == QString("qdeclarativeexamples.html")) {
1751                 breadcrumbs << Atom(Atom::ListItemLeft)
1752                             << Atom(Atom::Link, QLatin1String("Qt Examples"))
1753                             << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK)
1754                             << Atom(Atom::String, QLatin1String("Examples"))
1755                             << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK)
1756                             << Atom(Atom::ListItemRight);
1757                 breadcrumbs << Atom(Atom::ListItemLeft)
1758                             << Atom(Atom::AutoLink, QLatin1String("QML Examples & Demos"))
1759                             << Atom(Atom::ListItemRight);
1760             }
1761             else if (fn->name().startsWith("examples-")) {
1762                 breadcrumbs << Atom(Atom::ListItemLeft)
1763                             << Atom(Atom::Link, QLatin1String("Qt Examples"))
1764                             << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK)
1765                             << Atom(Atom::String, QLatin1String("Examples"))
1766                             << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK)
1767                             << Atom(Atom::ListItemRight);
1768                 breadcrumbs << Atom(Atom::ListItemLeft)
1769                             << Atom(Atom::String, protectEnc(title))
1770                             << Atom(Atom::ListItemRight);
1771             }
1772             else if (fn->name() == QString("namespaces.html"))
1773                 breadcrumbs << Atom(Atom::String, QLatin1String("Namespaces"));
1774             else
1775                 breadcrumbs << Atom(Atom::ListItemLeft)
1776                             << Atom(Atom::String, protectEnc(title))
1777                             << Atom(Atom::ListItemRight);
1778         }
1779         else if (node->subType() == Node::QmlClass) {
1780             breadcrumbs << Atom(Atom::ListItemLeft)
1781                         << Atom(Atom::AutoLink, QLatin1String("Basic QML Types"))
1782                         << Atom(Atom::ListItemRight);
1783             breadcrumbs << Atom(Atom::ListItemLeft)
1784                         << Atom(Atom::String, protectEnc(title))
1785                         << Atom(Atom::ListItemRight);
1786         }
1787         else if (node->subType() == Node::Example) {
1788             breadcrumbs << Atom(Atom::ListItemLeft)
1789                         << Atom(Atom::Link, QLatin1String("Qt Examples"))
1790                         << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK)
1791                         << Atom(Atom::String, QLatin1String("Examples"))
1792                         << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK)
1793                         << Atom(Atom::ListItemRight);
1794             QStringList sl = fn->name().split('/');
1795             if (sl.contains("declarative"))
1796                 breadcrumbs << Atom(Atom::ListItemLeft)
1797                             << Atom(Atom::AutoLink, QLatin1String("QML Examples & Demos"))
1798                             << Atom(Atom::ListItemRight);
1799             else {
1800                 QString name = protectEnc("examples-" + sl.at(0) + ".html"); // this generates an empty link
1801                 QString t = CodeParser::titleFromName(name);
1802             }
1803             breadcrumbs << Atom(Atom::ListItemLeft)
1804                         << Atom(Atom::String, protectEnc(title))
1805                         << Atom(Atom::ListItemRight);
1806         }
1807     }
1808     else if (node->type() == Node::Namespace) {
1809         breadcrumbs << Atom(Atom::ListItemLeft)
1810                     << Atom(Atom::Link, QLatin1String("All Namespaces"))
1811                     << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK)
1812                     << Atom(Atom::String, QLatin1String("Namespaces"))
1813                     << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK)
1814                     << Atom(Atom::ListItemRight);
1815         breadcrumbs << Atom(Atom::ListItemLeft)
1816                     << Atom(Atom::String, protectEnc(title))
1817                     << Atom(Atom::ListItemRight);
1818     }
1819
1820     generateText(breadcrumbs, node, marker);
1821 }
1822
1823 void HtmlGenerator::generateHeader(const QString& title,
1824                                    const Node *node,
1825                                    CodeMarker *marker)
1826 {
1827     out() << QString("<?xml version=\"1.0\" encoding=\"%1\"?>\n").arg(outputEncoding);
1828     out() << "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\n";
1829     out() << QString("<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"%1\" lang=\"%1\">\n").arg(naturalLanguage);
1830     out() << "<head>\n";
1831     out() << "  <meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\" />\n";
1832     if (node && !node->doc().location().isEmpty())
1833         out() << "<!-- " << node->doc().location().fileName() << " -->\n";
1834
1835     QString shortVersion = tree_->version();
1836     if (shortVersion.count(QChar('.')) == 2)
1837         shortVersion.truncate(shortVersion.lastIndexOf(QChar('.')));
1838     if (!project.isEmpty())
1839         shortVersion = project + QLatin1Char(' ') + shortVersion + QLatin1String(": ");
1840     else
1841         shortVersion = QLatin1String("Qt ") + shortVersion + QLatin1String(": ");
1842
1843     // Generating page title
1844     out() << "  <title>" << shortVersion << protectEnc(title) << "</title>\n";
1845
1846     // Include style sheet and script links.
1847     out() << headerStyles;
1848     out() << headerScripts;
1849     out() << endHeader;
1850
1851 #ifdef GENERATE_MAC_REFS
1852     if (mainPage)
1853         generateMacRef(node, marker);
1854 #endif
1855
1856     out() << QString(postHeader).replace("\\" + COMMAND_VERSION, tree_->version());
1857     generateBreadCrumbs(title,node,marker);
1858     out() << QString(postPostHeader).replace("\\" + COMMAND_VERSION, tree_->version());
1859
1860     navigationLinks.clear();
1861
1862     if (node && !node->links().empty()) {
1863         QPair<QString,QString> linkPair;
1864         QPair<QString,QString> anchorPair;
1865         const Node *linkNode;
1866
1867         if (node->links().contains(Node::PreviousLink)) {
1868             linkPair = node->links()[Node::PreviousLink];
1869             linkNode = findNodeForTarget(linkPair.first, node, marker);
1870             if (!linkNode || linkNode == node)
1871                 anchorPair = linkPair;
1872             else
1873                 anchorPair = anchorForNode(linkNode);
1874
1875             out() << "  <link rel=\"prev\" href=\""
1876                   << anchorPair.first << "\" />\n";
1877
1878             navigationLinks += "<a class=\"prevPage\" href=\"" + anchorPair.first + "\">";
1879             if (linkPair.first == linkPair.second && !anchorPair.second.isEmpty())
1880                 navigationLinks += protect(anchorPair.second);
1881             else
1882                 navigationLinks += protect(linkPair.second);
1883             navigationLinks += "</a>\n";
1884         }
1885         if (node->links().contains(Node::NextLink)) {
1886             linkPair = node->links()[Node::NextLink];
1887             linkNode = findNodeForTarget(linkPair.first, node, marker);
1888             if (!linkNode || linkNode == node)
1889                 anchorPair = linkPair;
1890             else
1891                 anchorPair = anchorForNode(linkNode);
1892
1893             out() << "  <link rel=\"next\" href=\""
1894                   << anchorPair.first << "\" />\n";
1895
1896             navigationLinks += "<a class=\"nextPage\" href=\"" + anchorPair.first + "\">";
1897             if (linkPair.first == linkPair.second && !anchorPair.second.isEmpty())
1898                 navigationLinks += protect(anchorPair.second);
1899             else
1900                 navigationLinks += protect(linkPair.second);
1901             navigationLinks += "</a>\n";
1902         }
1903         if (node->links().contains(Node::StartLink)) {
1904             linkPair = node->links()[Node::StartLink];
1905             linkNode = findNodeForTarget(linkPair.first, node, marker);
1906             if (!linkNode || linkNode == node)
1907                 anchorPair = linkPair;
1908             else
1909                 anchorPair = anchorForNode(linkNode);
1910             out() << "  <link rel=\"start\" href=\""
1911                   << anchorPair.first << "\" />\n";
1912         }
1913     }
1914
1915     if (node && !node->links().empty())
1916         out() << "<p class=\"naviNextPrevious headerNavi\">\n" << navigationLinks << "</p><p/>\n";
1917 }
1918
1919 void HtmlGenerator::generateTitle(const QString& title,
1920                                   const Text &subTitle,
1921                                   SubTitleSize subTitleSize,
1922                                   const Node *relative,
1923                                   CodeMarker *marker)
1924 {
1925     if (!title.isEmpty())
1926         out() << "<h1 class=\"title\">" << protectEnc(title) << "</h1>\n";
1927     if (!subTitle.isEmpty()) {
1928         out() << "<span";
1929         if (subTitleSize == SmallSubTitle)
1930             out() << " class=\"small-subtitle\">";
1931         else
1932             out() << " class=\"subtitle\">";
1933         generateText(subTitle, relative, marker);
1934         out() << "</span>\n";
1935     }
1936 }
1937
1938 void HtmlGenerator::generateFooter(const Node *node)
1939 {
1940     if (node && !node->links().empty())
1941         out() << "<p class=\"naviNextPrevious footerNavi\">\n" << navigationLinks << "</p>\n";
1942
1943     out() << QString(footer).replace("\\" + COMMAND_VERSION, tree_->version())
1944           << QString(address).replace("\\" + COMMAND_VERSION, tree_->version());
1945
1946     out() << "</body>\n";
1947     out() << "</html>\n";
1948 }
1949
1950 void HtmlGenerator::generateBrief(const Node *node, CodeMarker *marker,
1951                                   const Node *relative)
1952 {
1953     Text brief = node->doc().briefText();
1954     if (!brief.isEmpty()) {
1955         generateExtractionMark(node, BriefMark);
1956         out() << "<p>";
1957         generateText(brief, node, marker);
1958
1959         if (!relative || node == relative)
1960             out() << " <a href=\"#";
1961         else
1962             out() << " <a href=\"" << linkForNode(node, relative) << '#';
1963         out() << registerRef("details") << "\">More...</a></p>\n";
1964
1965
1966         generateExtractionMark(node, EndMark);
1967     }
1968 }
1969
1970 void HtmlGenerator::generateIncludes(const InnerNode *inner, CodeMarker *marker)
1971 {
1972     if (!inner->includes().isEmpty()) {
1973         out() << "<pre class=\"cpp\">"
1974               << trimmedTrailing(highlightedCode(indent(codeIndent,
1975                                                         marker->markedUpIncludes(inner->includes())),
1976                                                  marker,inner))
1977               << "</pre>";
1978     }
1979 }
1980
1981 /*!
1982   Revised for the new doc format.
1983   Generates a table of contents beginning at \a node.
1984  */
1985 void HtmlGenerator::generateTableOfContents(const Node *node,
1986                                             CodeMarker *marker,
1987                                             QList<Section>* sections)
1988 {
1989     QList<Atom*> toc;
1990     if (node->doc().hasTableOfContents())
1991         toc = node->doc().tableOfContents();
1992     if (toc.isEmpty() && !sections && (node->subType() != Node::Module))
1993         return;
1994
1995     QStringList sectionNumber;
1996     int detailsBase = 0;
1997
1998     // disable nested links in table of contents
1999     inContents = true;
2000     inLink = true;
2001
2002     out() << "<div class=\"toc\">\n";
2003     out() << "<h3><a name=\"toc\">Contents</a></h3>\n";
2004     sectionNumber.append("1");
2005     out() << "<ul>\n";
2006
2007     if (node->subType() == Node::Module) {
2008         if (moduleNamespaceMap.contains(node->name())) {
2009             out() << "<li class=\"level"
2010                   << sectionNumber.size()
2011                   << "\"><a href=\"#"
2012                   << registerRef("namespaces")
2013                   << "\">Namespaces</a></li>\n";
2014         }
2015         if (moduleClassMap.contains(node->name())) {
2016             out() << "<li class=\"level"
2017                   << sectionNumber.size()
2018                   << "\"><a href=\"#"
2019                   << registerRef("classes")
2020                   << "\">Classes</a></li>\n";
2021         }
2022         out() << "<li class=\"level"
2023               << sectionNumber.size()
2024               << "\"><a href=\"#"
2025               << registerRef("details")
2026               << "\">Detailed Description</a></li>\n";
2027         for (int i = 0; i < toc.size(); ++i) {
2028             if (toc.at(i)->string().toInt() == 1) {
2029                 detailsBase = 1;
2030                 break;
2031             }
2032         }
2033     }
2034     else if (sections && ((node->type() == Node::Class) ||
2035                           (node->type() == Node::Namespace) ||
2036                           (node->subType() == Node::QmlClass))) {
2037         QList<Section>::ConstIterator s = sections->constBegin();
2038         while (s != sections->constEnd()) {
2039             if (!s->members.isEmpty() || !s->reimpMembers.isEmpty()) {
2040                 out() << "<li class=\"level"
2041                       << sectionNumber.size()
2042                       << "\"><a href=\"#"
2043                       << registerRef((*s).pluralMember)
2044                       << "\">" << (*s).name
2045                       << "</a></li>\n";
2046             }
2047             ++s;
2048         }
2049         out() << "<li class=\"level"
2050               << sectionNumber.size()
2051               << "\"><a href=\"#"
2052               << registerRef("details")
2053               << "\">Detailed Description</a></li>\n";
2054         for (int i = 0; i < toc.size(); ++i) {
2055             if (toc.at(i)->string().toInt() == 1) {
2056                 detailsBase = 1;
2057                 break;
2058             }
2059         }
2060     }
2061
2062     for (int i = 0; i < toc.size(); ++i) {
2063         Atom *atom = toc.at(i);
2064         int nextLevel = atom->string().toInt() + detailsBase;
2065         if (nextLevel >= 0) {
2066             if (sectionNumber.size() < nextLevel) {
2067                 do {
2068                     sectionNumber.append("1");
2069                 } while (sectionNumber.size() < nextLevel);
2070             }
2071             else {
2072                 while (sectionNumber.size() > nextLevel) {
2073                     sectionNumber.removeLast();
2074                 }
2075                 sectionNumber.last() = QString::number(sectionNumber.last().toInt() + 1);
2076             }
2077         }
2078         int numAtoms;
2079         Text headingText = Text::sectionHeading(atom);
2080         QString s = headingText.toString();
2081         out() << "<li class=\"level"
2082               << sectionNumber.size()
2083               << "\">";
2084         out() << "<a href=\""
2085               << '#'
2086               << Doc::canonicalTitle(s)
2087               << "\">";
2088         generateAtomList(headingText.firstAtom(), node, marker, true, numAtoms);
2089         out() << "</a></li>\n";
2090     }
2091     while (!sectionNumber.isEmpty()) {
2092         sectionNumber.removeLast();
2093     }
2094     out() << "</ul>\n";
2095     out() << "</div>\n";
2096     inContents = false;
2097     inLink = false;
2098 }
2099
2100 QString HtmlGenerator::generateListOfAllMemberFile(const InnerNode *inner,
2101                                                    CodeMarker *marker)
2102 {
2103     QList<Section> sections;
2104     QList<Section>::ConstIterator s;
2105
2106     sections = marker->sections(inner,
2107                                 CodeMarker::Subpage,
2108                                 CodeMarker::Okay);
2109     if (sections.isEmpty())
2110         return QString();
2111
2112     QString fileName = fileBase(inner) + "-members." + fileExtension();
2113     beginSubPage(inner, fileName);
2114     QString title = "List of All Members for " + inner->name();
2115     generateHeader(title, inner, marker);
2116     generateTitle(title, Text(), SmallSubTitle, inner, marker);
2117     out() << "<p>This is the complete list of members for ";
2118     generateFullName(inner, 0, marker);
2119     out() << ", including inherited members.</p>\n";
2120
2121     Section section = sections.first();
2122     generateSectionList(section, 0, marker, CodeMarker::Subpage);
2123
2124     generateFooter();
2125     endSubPage();
2126     return fileName;
2127 }
2128
2129 /*!
2130   This function creates an html page on which are listed all
2131   the members of QML class \a qml_cn, including the inherited
2132   members. The \a marker is used for formatting stuff.
2133  */
2134 QString HtmlGenerator::generateAllQmlMembersFile(const QmlClassNode* qml_cn,
2135                                                  CodeMarker* marker)
2136 {
2137     QList<Section> sections;
2138     QList<Section>::ConstIterator s;
2139
2140     sections = marker->qmlSections(qml_cn,CodeMarker::Subpage);
2141     if (sections.isEmpty())
2142         return QString();
2143
2144     QString fileName = fileBase(qml_cn) + "-members." + fileExtension();
2145     beginSubPage(qml_cn, fileName);
2146     QString title = "List of All Members for " + qml_cn->name();
2147     generateHeader(title, qml_cn, marker);
2148     generateTitle(title, Text(), SmallSubTitle, qml_cn, marker);
2149     out() << "<p>This is the complete list of members for ";
2150     generateFullName(qml_cn, 0, marker);
2151     out() << ", including inherited members.</p>\n";
2152
2153     Section section = sections.first();
2154     generateSectionList(section, 0, marker, CodeMarker::Subpage);
2155
2156     generateFooter();
2157     endSubPage();
2158     return fileName;
2159 }
2160
2161 QString HtmlGenerator::generateLowStatusMemberFile(const InnerNode *inner,
2162                                                    CodeMarker *marker,
2163                                                    CodeMarker::Status status)
2164 {
2165     QList<Section> sections = marker->sections(inner,
2166                                                CodeMarker::Summary,
2167                                                status);
2168     QMutableListIterator<Section> j(sections);
2169     while (j.hasNext()) {
2170         if (j.next().members.size() == 0)
2171             j.remove();
2172     }
2173     if (sections.isEmpty())
2174         return QString();
2175
2176     int i;
2177
2178     QString title;
2179     QString fileName;
2180
2181     if (status == CodeMarker::Compat) {
2182         title = "Compatibility Members for " + inner->name();
2183         fileName = fileBase(inner) + "-compat." + fileExtension();
2184     }
2185     else {
2186         title = "Obsolete Members for " + inner->name();
2187         fileName = fileBase(inner) + "-obsolete." + fileExtension();
2188     }
2189
2190     beginSubPage(inner, fileName);
2191     generateHeader(title, inner, marker);
2192     generateTitle(title, Text(), SmallSubTitle, inner, marker);
2193
2194     if (status == CodeMarker::Compat) {
2195         out() << "<p><b>The following class members are part of the "
2196                  "Qt compatibility layer.</b> We advise against "
2197                  "using them in new code.</p>\n";
2198     }
2199     else {
2200         out() << "<p><b>The following class members are obsolete.</b> "
2201               << "They are provided to keep old source code working. "
2202               << "We strongly advise against using them in new code.</p>\n";
2203     }
2204
2205     out() << "<p><ul><li><a href=\""
2206           << linkForNode(inner, 0) << "\">"
2207           << protectEnc(inner->name())
2208           << " class reference</a></li></ul></p>\n";
2209
2210     for (i = 0; i < sections.size(); ++i) {
2211         out() << "<h2>" << protectEnc(sections.at(i).name) << "</h2>\n";
2212         generateSectionList(sections.at(i), inner, marker, CodeMarker::Summary);
2213     }
2214
2215     sections = marker->sections(inner, CodeMarker::Detailed, status);
2216     for (i = 0; i < sections.size(); ++i) {
2217         //out() << "<hr />\n";
2218         out() << "<h2>" << protectEnc(sections.at(i).name) << "</h2>\n";
2219
2220         NodeList::ConstIterator m = sections.at(i).members.constBegin();
2221         while (m != sections.at(i).members.constEnd()) {
2222             if ((*m)->access() != Node::Private)
2223                 generateDetailedMember(*m, inner, marker);
2224             ++m;
2225         }
2226     }
2227
2228     generateFooter();
2229     endSubPage();
2230     return fileName;
2231 }
2232
2233 void HtmlGenerator::generateClassHierarchy(const Node *relative,
2234                                            CodeMarker *marker,
2235                                            const QMap<QString,const Node*> &classMap)
2236 {
2237     if (classMap.isEmpty())
2238         return;
2239
2240     NodeMap topLevel;
2241     NodeMap::ConstIterator c = classMap.constBegin();
2242     while (c != classMap.constEnd()) {
2243         const ClassNode *classe = static_cast<const ClassNode *>(*c);
2244         if (classe->baseClasses().isEmpty())
2245             topLevel.insert(classe->name(), classe);
2246         ++c;
2247     }
2248
2249     QStack<NodeMap > stack;
2250     stack.push(topLevel);
2251
2252     out() << "<ul>\n";
2253     while (!stack.isEmpty()) {
2254         if (stack.top().isEmpty()) {
2255             stack.pop();
2256             out() << "</ul>\n";
2257         }
2258         else {
2259             const ClassNode *child =
2260                     static_cast<const ClassNode *>(*stack.top().constBegin());
2261             out() << "<li>";
2262             generateFullName(child, relative, marker);
2263             out() << "</li>\n";
2264             stack.top().erase(stack.top().begin());
2265
2266             NodeMap newTop;
2267             foreach (const RelatedClass &d, child->derivedClasses()) {
2268                 if (d.access != Node::Private && !d.node->doc().isEmpty())
2269                     newTop.insert(d.node->name(), d.node);
2270             }
2271             if (!newTop.isEmpty()) {
2272                 stack.push(newTop);
2273                 out() << "<ul>\n";
2274             }
2275         }
2276     }
2277 }
2278
2279 void HtmlGenerator::generateAnnotatedList(const Node *relative,
2280                                           CodeMarker *marker,
2281                                           const NodeMap &nodeMap,
2282                                           bool allOdd)
2283 {
2284     out() << "<table class=\"annotated\">\n";
2285
2286     int row = 0;
2287     foreach (const QString &name, nodeMap.keys()) {
2288         const Node *node = nodeMap[name];
2289
2290         if (node->status() == Node::Obsolete)
2291             continue;
2292
2293         if (allOdd || (++row % 2 == 1))
2294             out() << "<tr class=\"odd topAlign\">";
2295         else
2296             out() << "<tr class=\"even topAlign\">";
2297         out() << "<td class=\"tblName\"><p>";
2298         generateFullName(node, relative, marker);
2299         out() << "</p></td>";
2300
2301         if (!(node->type() == Node::Fake)) {
2302             Text brief = node->doc().trimmedBriefText(name);
2303             if (!brief.isEmpty()) {
2304                 out() << "<td class=\"tblDescr\"><p>";
2305                 generateText(brief, node, marker);
2306                 out() << "</p></td>";
2307             }
2308         }
2309         else {
2310             out() << "<td class=\"tblDescr\"><p>";
2311             out() << protectEnc(node->doc().briefText().toString());
2312             out() << "</p></td>";
2313         }
2314         out() << "</tr>\n";
2315     }
2316     out() << "</table>\n";
2317 }
2318
2319 /*!
2320   This function finds the common prefix of the names of all
2321   the classes in \a classMap and then generates a compact
2322   list of the class names alphabetized on the part of the
2323   name not including the common prefix. You can tell the
2324   function to use \a comonPrefix as the common prefix, but
2325   normally you let it figure it out itself by looking at
2326   the name of the first and last classes in \a classMap.
2327  */
2328 void HtmlGenerator::generateCompactList(const Node *relative,
2329                                         CodeMarker *marker,
2330                                         const NodeMap &classMap,
2331                                         bool includeAlphabet,
2332                                         QString commonPrefix)
2333 {
2334     const int NumParagraphs = 37; // '0' to '9', 'A' to 'Z', '_'
2335
2336     if (classMap.isEmpty())
2337         return;
2338
2339     /*
2340       If commonPrefix is not empty, then the caller knows what
2341       the common prefix is and has passed it in, so just use that
2342       one. But if the commonPrefix is empty (it normally is), then
2343       compute a common prefix using this simple algorithm. Note we
2344       assume the prefix length is 1, i.e. we will have a single
2345       character as the common prefix.
2346      */
2347     int commonPrefixLen = commonPrefix.length();
2348     if (commonPrefixLen == 0) {
2349         QVector<int> count(26);
2350         for (int i=0; i<26; ++i)
2351             count[i] = 0;
2352
2353         NodeMap::const_iterator iter = classMap.constBegin();
2354         while (iter != classMap.constEnd()) {
2355             if (!iter.key().contains("::")) {
2356                 QChar c = iter.key()[0];
2357                 if ((c >= 'A') && (c <= 'Z')) {
2358                     int idx = c.unicode() - QChar('A').unicode();
2359                     ++count[idx];
2360                 }
2361             }
2362             ++iter;
2363         }
2364         int highest = 0;
2365         int idx = -1;
2366         for (int i=0; i<26; ++i) {
2367             if (count[i] > highest) {
2368                 highest = count[i];
2369                 idx = i;
2370             }
2371         }
2372         idx += QChar('A').unicode();
2373         QChar common(idx);
2374         commonPrefix = common;
2375         commonPrefixLen = 1;
2376     }
2377
2378     /*
2379       Divide the data into 37 paragraphs: 0, ..., 9, A, ..., Z,
2380       underscore (_). QAccel will fall in paragraph 10 (A) and
2381       QXtWidget in paragraph 33 (X). This is the only place where we
2382       assume that NumParagraphs is 37. Each paragraph is a NodeMap.
2383     */
2384     NodeMap paragraph[NumParagraphs+1];
2385     QString paragraphName[NumParagraphs+1];
2386     QSet<char> usedParagraphNames;
2387
2388     NodeMap::ConstIterator c = classMap.constBegin();
2389     while (c != classMap.constEnd()) {
2390         QStringList pieces = c.key().split("::");
2391         QString key;
2392         int idx = commonPrefixLen;
2393         if (!pieces.last().startsWith(commonPrefix))
2394             idx = 0;
2395         if (pieces.size() == 1)
2396             key = pieces.last().mid(idx).toLower();
2397         else
2398             key = pieces.last().toLower();
2399
2400         int paragraphNr = NumParagraphs - 1;
2401
2402         if (key[0].digitValue() != -1) {
2403             paragraphNr = key[0].digitValue();
2404         }
2405         else if (key[0] >= QLatin1Char('a') && key[0] <= QLatin1Char('z')) {
2406             paragraphNr = 10 + key[0].unicode() - 'a';
2407         }
2408
2409         paragraphName[paragraphNr] = key[0].toUpper();
2410         usedParagraphNames.insert(key[0].toLower().cell());
2411         paragraph[paragraphNr].insert(key, c.value());
2412         ++c;
2413     }
2414
2415     /*
2416       Each paragraph j has a size: paragraph[j].count(). In the
2417       discussion, we will assume paragraphs 0 to 5 will have sizes
2418       3, 1, 4, 1, 5, 9.
2419
2420       We now want to compute the paragraph offset. Paragraphs 0 to 6
2421       start at offsets 0, 3, 4, 8, 9, 14, 23.
2422     */
2423     int paragraphOffset[NumParagraphs + 1];     // 37 + 1
2424     paragraphOffset[0] = 0;
2425     for (int i=0; i<NumParagraphs; i++)         // i = 0..36
2426         paragraphOffset[i+1] = paragraphOffset[i] + paragraph[i].count();
2427
2428     /*
2429       Output the alphabet as a row of links.
2430      */
2431     if (includeAlphabet) {
2432         out() << "<p  class=\"centerAlign functionIndex\"><b>";
2433         for (int i = 0; i < 26; i++) {
2434             QChar ch('a' + i);
2435             if (usedParagraphNames.contains(char('a' + i)))
2436                 out() << QString("<a href=\"#%1\">%2</a>&nbsp;").arg(ch).arg(ch.toUpper());
2437         }
2438         out() << "</b></p>\n";
2439     }
2440
2441     /*
2442       Output a <div> element to contain all the <dl> elements.
2443      */
2444     out() << "<div class=\"flowListDiv\">\n";
2445     numTableRows = 0;
2446
2447     int curParNr = 0;
2448     int curParOffset = 0;
2449
2450     for (int i=0; i<classMap.count(); i++) {
2451         while ((curParNr < NumParagraphs) &&
2452                (curParOffset == paragraph[curParNr].count())) {
2453             ++curParNr;
2454             curParOffset = 0;
2455         }
2456
2457         /*
2458           Starting a new paragraph means starting a new <dl>.
2459         */
2460         if (curParOffset == 0) {
2461             if (i > 0)
2462                 out() << "</dl>\n";
2463             if (++numTableRows % 2 == 1)
2464                 out() << "<dl class=\"flowList odd\">";
2465             else
2466                 out() << "<dl class=\"flowList even\">";
2467             out() << "<dt class=\"alphaChar\">";
2468             if (includeAlphabet) {
2469                 QChar c = paragraphName[curParNr][0].toLower();
2470                 out() << QString("<a name=\"%1\"></a>").arg(c);
2471             }
2472             out() << "<b>"
2473                   << paragraphName[curParNr]
2474                   << "</b>";
2475             out() << "</dt>\n";
2476         }
2477
2478         /*
2479           Output a <dd> for the current offset in the current paragraph.
2480          */
2481         out() << "<dd>";
2482         if ((curParNr < NumParagraphs) &&
2483                 !paragraphName[curParNr].isEmpty()) {
2484             NodeMap::Iterator it;
2485             it = paragraph[curParNr].begin();
2486             for (int i=0; i<curParOffset; i++)
2487                 ++it;
2488
2489             /*
2490               Previously, we used generateFullName() for this, but we
2491               require some special formatting.
2492             */
2493             out() << "<a href=\"" << linkForNode(it.value(), relative) << "\">";
2494
2495             QStringList pieces;
2496             if (it.value()->subType() == Node::QmlClass)
2497                 pieces << it.value()->name();
2498             else
2499                 pieces = fullName(it.value(), relative, marker).split("::");
2500             out() << protectEnc(pieces.last());
2501             out() << "</a>";
2502             if (pieces.size() > 1) {
2503                 out() << " (";
2504                 generateFullName(it.value()->parent(), relative, marker);
2505                 out() << ')';
2506             }
2507         }
2508         out() << "</dd>\n";
2509         curParOffset++;
2510     }
2511     if (classMap.count() > 0)
2512         out() << "</dl>\n";
2513
2514     out() << "</div>\n";
2515 }
2516
2517 void HtmlGenerator::generateFunctionIndex(const Node *relative,
2518                                           CodeMarker *marker)
2519 {
2520     out() << "<p  class=\"centerAlign functionIndex\"><b>";
2521     for (int i = 0; i < 26; i++) {
2522         QChar ch('a' + i);
2523         out() << QString("<a href=\"#%1\">%2</a>&nbsp;").arg(ch).arg(ch.toUpper());
2524     }
2525     out() << "</b></p>\n";
2526
2527     char nextLetter = 'a';
2528     char currentLetter;
2529
2530 #if 1
2531     out() << "<ul>\n";
2532 #endif
2533     QMap<QString, NodeMap >::ConstIterator f = funcIndex.constBegin();
2534     while (f != funcIndex.constEnd()) {
2535 #if 1
2536         out() << "<li>";
2537 #else
2538         out() << "<p>";
2539 #endif
2540         out() << protectEnc(f.key()) << ':';
2541
2542         currentLetter = f.key()[0].unicode();
2543         while (islower(currentLetter) && currentLetter >= nextLetter) {
2544             out() << QString("<a name=\"%1\"></a>").arg(nextLetter);
2545             nextLetter++;
2546         }
2547
2548         NodeMap::ConstIterator s = (*f).constBegin();
2549         while (s != (*f).constEnd()) {
2550             out() << ' ';
2551             generateFullName((*s)->parent(), relative, marker, *s);
2552             ++s;
2553         }
2554 #if 1
2555         out() << "</li>";
2556 #else
2557         out() << "</p>";
2558 #endif
2559         out() << '\n';
2560         ++f;
2561     }
2562 #if 1
2563     out() << "</ul>\n";
2564 #endif
2565 }
2566
2567 void HtmlGenerator::generateLegaleseList(const Node *relative,
2568                                          CodeMarker *marker)
2569 {
2570     QMap<Text, const Node *>::ConstIterator it = legaleseTexts.constBegin();
2571     while (it != legaleseTexts.constEnd()) {
2572         Text text = it.key();
2573         //out() << "<hr />\n";
2574         generateText(text, relative, marker);
2575         out() << "<ul>\n";
2576         do {
2577             out() << "<li>";
2578             generateFullName(it.value(), relative, marker);
2579             out() << "</li>\n";
2580             ++it;
2581         } while (it != legaleseTexts.constEnd() && it.key() == text);
2582         out() << "</ul>\n";
2583     }
2584 }
2585
2586 void HtmlGenerator::generateQmlItem(const Node *node,
2587                                     const Node *relative,
2588                                     CodeMarker *marker,
2589                                     bool summary)
2590 {
2591     QString marked = marker->markedUpQmlItem(node,summary);
2592     QRegExp templateTag("(<[^@>]*>)");
2593     if (marked.indexOf(templateTag) != -1) {
2594         QString contents = protectEnc(marked.mid(templateTag.pos(1),
2595                                                  templateTag.cap(1).length()));
2596         marked.replace(templateTag.pos(1), templateTag.cap(1).length(),
2597                        contents);
2598     }
2599     marked.replace(QRegExp("<@param>([a-z]+)_([1-9n])</@param>"),
2600                    "<i>\\1<sub>\\2</sub></i>");
2601     marked.replace("<@param>", "<i>");
2602     marked.replace("</@param>", "</i>");
2603
2604     if (summary)
2605         marked.replace("@name>", "b>");
2606
2607     marked.replace("<@extra>", "<tt>");
2608     marked.replace("</@extra>", "</tt>");
2609
2610     if (summary) {
2611         marked.remove("<@type>");
2612         marked.remove("</@type>");
2613     }
2614     out() << highlightedCode(marked, marker, relative, false, node);
2615 }
2616
2617 void HtmlGenerator::generateOverviewList(const Node *relative, CodeMarker * /* marker */)
2618 {
2619     QMap<const FakeNode *, QMap<QString, FakeNode *> > fakeNodeMap;
2620     QMap<QString, const FakeNode *> groupTitlesMap;
2621     QMap<QString, FakeNode *> uncategorizedNodeMap;
2622     QRegExp singleDigit("\\b([0-9])\\b");
2623
2624     const NodeList children = tree_->root()->childNodes();
2625     foreach (Node *child, children) {
2626         if (child->type() == Node::Fake && child != relative) {
2627             FakeNode *fakeNode = static_cast<FakeNode *>(child);
2628
2629             // Check whether the page is part of a group or is the group
2630             // definition page.
2631             QString group;
2632             bool isGroupPage = false;
2633             if (fakeNode->doc().metaCommandsUsed().contains("group")) {
2634                 group = fakeNode->doc().metaCommandArgs("group")[0].first;
2635                 isGroupPage = true;
2636             }
2637
2638             // there are too many examples; they would clutter the list
2639             if (fakeNode->subType() == Node::Example)
2640                 continue;
2641
2642             // not interested either in individual (Qt Designer etc.) manual chapters
2643             if (fakeNode->links().contains(Node::ContentsLink))
2644                 continue;
2645
2646             // Discard external nodes.
2647             if (fakeNode->subType() == Node::ExternalPage)
2648                 continue;
2649
2650             QString sortKey = fakeNode->fullTitle().toLower();
2651             if (sortKey.startsWith("the "))
2652                 sortKey.remove(0, 4);
2653             sortKey.replace(singleDigit, "0\\1");
2654
2655             if (!group.isEmpty()) {
2656                 if (isGroupPage) {
2657                     // If we encounter a group definition page, we add all
2658                     // the pages in that group to the list for that group.
2659                     foreach (Node *member, fakeNode->groupMembers()) {
2660                         if (member->type() != Node::Fake)
2661                             continue;
2662                         FakeNode *page = static_cast<FakeNode *>(member);
2663                         if (page) {
2664                             QString sortKey = page->fullTitle().toLower();
2665                             if (sortKey.startsWith("the "))
2666                                 sortKey.remove(0, 4);
2667                             sortKey.replace(singleDigit, "0\\1");
2668                             fakeNodeMap[const_cast<const FakeNode *>(fakeNode)].insert(sortKey, page);
2669                             groupTitlesMap[fakeNode->fullTitle()] = const_cast<const FakeNode *>(fakeNode);
2670                         }
2671                     }
2672                 }
2673                 else if (!isGroupPage) {
2674                     // If we encounter a page that belongs to a group then
2675                     // we add that page to the list for that group.
2676                     const FakeNode* gn = tree_->findGroupNode(QStringList(group));
2677                     if (gn)
2678                         fakeNodeMap[gn].insert(sortKey, fakeNode);
2679                 }
2680             }
2681         }
2682     }
2683
2684     // We now list all the pages found that belong to groups.
2685     // If only certain pages were found for a group, but the definition page
2686     // for that group wasn't listed, the list of pages will be intentionally
2687     // incomplete. However, if the group definition page was listed, all the
2688     // pages in that group are listed for completeness.
2689
2690     if (!fakeNodeMap.isEmpty()) {
2691         foreach (const QString &groupTitle, groupTitlesMap.keys()) {
2692             const FakeNode *groupNode = groupTitlesMap[groupTitle];
2693             out() << QString("<h3><a href=\"%1\">%2</a></h3>\n").arg(
2694                          linkForNode(groupNode, relative)).arg(
2695                          protectEnc(groupNode->fullTitle()));
2696
2697             if (fakeNodeMap[groupNode].count() == 0)
2698                 continue;
2699
2700             out() << "<ul>\n";
2701
2702             foreach (const FakeNode *fakeNode, fakeNodeMap[groupNode]) {
2703                 QString title = fakeNode->fullTitle();
2704                 if (title.startsWith("The "))
2705                     title.remove(0, 4);
2706                 out() << "<li><a href=\"" << linkForNode(fakeNode, relative) << "\">"
2707                       << protectEnc(title) << "</a></li>\n";
2708             }
2709             out() << "</ul>\n";
2710         }
2711     }
2712
2713     if (!uncategorizedNodeMap.isEmpty()) {
2714         out() << QString("<h3>Miscellaneous</h3>\n");
2715         out() << "<ul>\n";
2716         foreach (const FakeNode *fakeNode, uncategorizedNodeMap) {
2717             QString title = fakeNode->fullTitle();
2718             if (title.startsWith("The "))
2719                 title.remove(0, 4);
2720             out() << "<li><a href=\"" << linkForNode(fakeNode, relative) << "\">"
2721                   << protectEnc(title) << "</a></li>\n";
2722         }
2723         out() << "</ul>\n";
2724     }
2725 }
2726
2727 void HtmlGenerator::generateSection(const NodeList& nl,
2728                                     const Node *relative,
2729                                     CodeMarker *marker,
2730                                     CodeMarker::SynopsisStyle style)
2731 {
2732     bool alignNames = true;
2733     if (!nl.isEmpty()) {
2734         bool twoColumn = false;
2735         if (style == CodeMarker::Subpage) {
2736             alignNames = false;
2737             twoColumn = (nl.count() >= 16);
2738         }
2739         else if (nl.first()->type() == Node::Property) {
2740             twoColumn = (nl.count() >= 5);
2741             alignNames = false;
2742         }
2743         if (alignNames) {
2744             out() << "<table class=\"alignedsummary\">\n";
2745         }
2746         else {
2747             if (twoColumn)
2748                 out() << "<table class=\"propsummary\">\n"
2749                       << "<tr><td class=\"topAlign\">";
2750             out() << "<ul>\n";
2751         }
2752
2753         int i = 0;
2754         NodeList::ConstIterator m = nl.constBegin();
2755         while (m != nl.constEnd()) {
2756             if ((*m)->access() == Node::Private) {
2757                 ++m;
2758                 continue;
2759             }
2760
2761             if (alignNames) {
2762                 out() << "<tr><td class=\"memItemLeft rightAlign topAlign\"> ";
2763             }
2764             else {
2765                 if (twoColumn && i == (int) (nl.count() + 1) / 2)
2766                     out() << "</ul></td><td class=\"topAlign\"><ul>\n";
2767                 out() << "<li class=\"fn\">";
2768             }
2769
2770             generateSynopsis(*m, relative, marker, style, alignNames);
2771             if (alignNames)
2772                 out() << "</td></tr>\n";
2773             else
2774                 out() << "</li>\n";
2775             i++;
2776             ++m;
2777         }
2778         if (alignNames)
2779             out() << "</table>\n";
2780         else {
2781             out() << "</ul>\n";
2782             if (twoColumn)
2783                 out() << "</td></tr>\n</table>\n";
2784         }
2785     }
2786 }
2787
2788 void HtmlGenerator::generateSectionList(const Section& section,
2789                                         const Node *relative,
2790                                         CodeMarker *marker,
2791                                         CodeMarker::SynopsisStyle style)
2792 {
2793     bool alignNames = true;
2794     if (!section.members.isEmpty()) {
2795         bool twoColumn = false;
2796         if (style == CodeMarker::Subpage) {
2797             alignNames = false;
2798             twoColumn = (section.members.count() >= 16);
2799         }
2800         else if (section.members.first()->type() == Node::Property) {
2801             twoColumn = (section.members.count() >= 5);
2802             alignNames = false;
2803         }
2804         if (alignNames) {
2805             out() << "<table class=\"alignedsummary\">\n";
2806         }
2807         else {
2808             if (twoColumn)
2809                 out() << "<table class=\"propsummary\">\n"
2810                       << "<tr><td class=\"topAlign\">";
2811             out() << "<ul>\n";
2812         }
2813
2814         int i = 0;
2815         NodeList::ConstIterator m = section.members.constBegin();
2816         while (m != section.members.constEnd()) {
2817             if ((*m)->access() == Node::Private) {
2818                 ++m;
2819                 continue;
2820             }
2821
2822             if (alignNames) {
2823                 out() << "<tr><td class=\"memItemLeft topAlign rightAlign\"> ";
2824             }
2825             else {
2826                 if (twoColumn && i == (int) (section.members.count() + 1) / 2)
2827                     out() << "</ul></td><td class=\"topAlign\"><ul>\n";
2828                 out() << "<li class=\"fn\">";
2829             }
2830
2831             QString prefix;
2832             if (!section.keys.isEmpty()) {
2833                 prefix = section.keys.at(i).mid(1);
2834                 prefix = prefix.left(section.keys.at(i).indexOf("::")+1);
2835             }
2836             generateSynopsis(*m, relative, marker, style, alignNames, &prefix);
2837             if (alignNames)
2838                 out() << "</td></tr>\n";
2839             else
2840                 out() << "</li>\n";
2841             i++;
2842             ++m;
2843         }
2844         if (alignNames)
2845             out() << "</table>\n";
2846         else {
2847             out() << "</ul>\n";
2848             if (twoColumn)
2849                 out() << "</td></tr>\n</table>\n";
2850         }
2851     }
2852
2853     if (style == CodeMarker::Summary && !section.inherited.isEmpty()) {
2854         out() << "<ul>\n";
2855         generateSectionInheritedList(section, relative, marker);
2856         out() << "</ul>\n";
2857     }
2858 }
2859
2860 void HtmlGenerator::generateSectionInheritedList(const Section& section,
2861                                                  const Node *relative,
2862                                                  CodeMarker *marker)
2863 {
2864     QList<QPair<InnerNode *, int> >::ConstIterator p = section.inherited.constBegin();
2865     while (p != section.inherited.constEnd()) {
2866         out() << "<li class=\"fn\">";
2867         out() << (*p).second << ' ';
2868         if ((*p).second == 1) {
2869             out() << section.singularMember;
2870         }
2871         else {
2872             out() << section.pluralMember;
2873         }
2874         out() << " inherited from <a href=\"" << fileName((*p).first)
2875               << '#' << HtmlGenerator::cleanRef(section.name.toLower()) << "\">"
2876               << protectEnc(marker->plainFullName((*p).first, relative))
2877               << "</a></li>\n";
2878         ++p;
2879     }
2880 }
2881
2882 void HtmlGenerator::generateSynopsis(const Node *node,
2883                                      const Node *relative,
2884                                      CodeMarker *marker,
2885                                      CodeMarker::SynopsisStyle style,
2886                                      bool alignNames,
2887                                      const QString* prefix)
2888 {
2889     QString marked = marker->markedUpSynopsis(node, relative, style);
2890
2891     if (prefix)
2892         marked.prepend(*prefix);
2893     QRegExp templateTag("(<[^@>]*>)");
2894     if (marked.indexOf(templateTag) != -1) {
2895         QString contents = protectEnc(marked.mid(templateTag.pos(1),
2896                                                  templateTag.cap(1).length()));
2897         marked.replace(templateTag.pos(1), templateTag.cap(1).length(),
2898                        contents);
2899     }
2900     marked.replace(QRegExp("<@param>([a-z]+)_([1-9n])</@param>"),
2901                    "<i>\\1<sub>\\2</sub></i>");
2902     marked.replace("<@param>", "<i> ");
2903     marked.replace("</@param>", "</i>");
2904
2905     if (style == CodeMarker::Summary) {
2906         marked.remove("<@name>");   // was "<b>"
2907         marked.remove("</@name>");  // was "</b>"
2908     }
2909
2910     if (style == CodeMarker::Subpage) {
2911         QRegExp extraRegExp("<@extra>.*</@extra>");
2912         extraRegExp.setMinimal(true);
2913         marked.remove(extraRegExp);
2914     } else {
2915         marked.replace("<@extra>", "<tt>");
2916         marked.replace("</@extra>", "</tt>");
2917     }
2918
2919     if (style != CodeMarker::Detailed) {
2920         marked.remove("<@type>");
2921         marked.remove("</@type>");
2922     }
2923
2924     out() << highlightedCode(marked, marker, relative, alignNames);
2925 }
2926
2927 QString HtmlGenerator::highlightedCode(const QString& markedCode,
2928                                        CodeMarker* marker,
2929                                        const Node* relative,
2930                                        bool alignNames,
2931                                        const Node* self)
2932 {
2933     QString src = markedCode;
2934     QString html;
2935     QStringRef arg;
2936     QStringRef par1;
2937
2938     const QChar charLangle = '<';
2939     const QChar charAt = '@';
2940
2941     static const QString typeTag("type");
2942     static const QString headerTag("headerfile");
2943     static const QString funcTag("func");
2944     static const QString linkTag("link");
2945
2946     // replace all <@link> tags: "(<@link node=\"([^\"]+)\">).*(</@link>)"
2947     bool done = false;
2948     for (int i = 0, srcSize = src.size(); i < srcSize;) {
2949         if (src.at(i) == charLangle && src.at(i + 1) == charAt) {
2950             if (alignNames && !done) {
2951                 html += "</td><td class=\"memItemRight bottomAlign\">";
2952                 done = true;
2953             }
2954             i += 2;
2955             if (parseArg(src, linkTag, &i, srcSize, &arg, &par1)) {
2956                 html += "<b>";
2957                 const Node* n = CodeMarker::nodeForString(par1.toString());
2958                 QString link = linkForNode(n, relative);
2959                 addLink(link, arg, &html);
2960                 html += "</b>";
2961             }
2962             else {
2963                 html += charLangle;
2964                 html += charAt;
2965             }
2966         }
2967         else {
2968             html += src.at(i++);
2969         }
2970     }
2971
2972     // replace all <@func> tags: "(<@func target=\"([^\"]*)\">)(.*)(</@func>)"
2973     src = html;
2974     html = QString();
2975     for (int i = 0, srcSize = src.size(); i < srcSize;) {
2976         if (src.at(i) == charLangle && src.at(i + 1) == charAt) {
2977             i += 2;
2978             if (parseArg(src, funcTag, &i, srcSize, &arg, &par1)) {
2979
2980                 const Node* n = marker->resolveTarget(par1.toString(),
2981                                                       tree_,
2982                                                       relative);
2983                 QString link = linkForNode(n, relative);
2984                 addLink(link, arg, &html);
2985                 par1 = QStringRef();
2986             }
2987             else {
2988                 html += charLangle;
2989                 html += charAt;
2990             }
2991         }
2992         else {
2993             html += src.at(i++);
2994         }
2995     }
2996
2997     // replace all "(<@(type|headerfile|func)(?: +[^>]*)?>)(.*)(</@\\2>)" tags
2998     src = html;
2999     html = QString();
3000
3001     for (int i=0, srcSize=src.size(); i<srcSize;) {
3002         if (src.at(i) == charLangle && src.at(i+1) == charAt) {
3003             i += 2;
3004             bool handled = false;
3005             if (parseArg(src, typeTag, &i, srcSize, &arg, &par1)) {
3006                 par1 = QStringRef();
3007                 const Node* n = marker->resolveTarget(arg.toString(), tree_, relative, self);
3008                 html += QLatin1String("<span class=\"type\">");
3009                 if (n && n->subType() == Node::QmlBasicType) {
3010                     if (relative && relative->subType() == Node::QmlClass)
3011                         addLink(linkForNode(n,relative), arg, &html);
3012                     else
3013                         html += arg.toString();
3014                 }
3015                 else
3016                     addLink(linkForNode(n,relative), arg, &html);
3017                 html += QLatin1String("</span>");
3018                 handled = true;
3019             }
3020             else if (parseArg(src, headerTag, &i, srcSize, &arg, &par1)) {
3021                 par1 = QStringRef();
3022                 const Node* n = marker->resolveTarget(arg.toString(), tree_, relative);
3023                 addLink(linkForNode(n,relative), arg, &html);
3024                 handled = true;
3025             }
3026             else if (parseArg(src, funcTag, &i, srcSize, &arg, &par1)) {
3027                 par1 = QStringRef();
3028                 const Node* n = marker->resolveTarget(arg.toString(), tree_, relative);
3029                 addLink(linkForNode(n,relative), arg, &html);
3030                 handled = true;
3031             }
3032
3033             if (!handled) {
3034                 html += charLangle;
3035                 html += charAt;
3036             }
3037         }
3038         else {
3039             html += src.at(i++);
3040         }
3041     }
3042
3043     // replace all
3044     // "<@comment>" -> "<span class=\"comment\">";
3045     // "<@preprocessor>" -> "<span class=\"preprocessor\">";
3046     // "<@string>" -> "<span class=\"string\">";
3047     // "<@char>" -> "<span class=\"char\">";
3048     // "<@number>" -> "<span class=\"number\">";
3049     // "<@op>" -> "<span class=\"operator\">";
3050     // "<@type>" -> "<span class=\"type\">";
3051     // "<@name>" -> "<span class=\"name\">";
3052     // "<@keyword>" -> "<span class=\"keyword\">";
3053     // "</@(?:comment|preprocessor|string|char|number|op|type|name|keyword)>" -> "</span>"
3054     src = html;
3055     html = QString();
3056     static const QString spanTags[] = {
3057         "<@comment>",       "<span class=\"comment\">",
3058         "<@preprocessor>",  "<span class=\"preprocessor\">",
3059         "<@string>",        "<span class=\"string\">",
3060         "<@char>",          "<span class=\"char\">",
3061         "<@number>",        "<span class=\"number\">",
3062         "<@op>",            "<span class=\"operator\">",
3063         "<@type>",          "<span class=\"type\">",
3064         "<@name>",          "<span class=\"name\">",
3065         "<@keyword>",       "<span class=\"keyword\">",
3066         "</@comment>",      "</span>",
3067         "</@preprocessor>", "</span>",
3068         "</@string>",       "</span>",
3069         "</@char>",         "</span>",
3070         "</@number>",       "</span>",
3071         "</@op>",           "</span>",
3072         "</@type>",         "</span>",
3073         "</@name>",         "</span>",
3074         "</@keyword>",      "</span>",
3075     };
3076     // Update the upper bound of k in the following code to match the length
3077     // of the above array.
3078     for (int i = 0, n = src.size(); i < n;) {
3079         if (src.at(i) == charLangle) {
3080             bool handled = false;
3081             for (int k = 0; k != 18; ++k) {
3082                 const QString & tag = spanTags[2 * k];
3083                 if (tag == QStringRef(&src, i, tag.length())) {
3084                     html += spanTags[2 * k + 1];
3085                     i += tag.length();
3086                     handled = true;
3087                     break;
3088                 }
3089             }
3090             if (!handled) {
3091                 ++i;
3092                 if (src.at(i) == charAt ||
3093                         (src.at(i) == QLatin1Char('/') && src.at(i + 1) == charAt)) {
3094                     // drop 'our' unknown tags (the ones still containing '@')
3095                     while (i < n && src.at(i) != QLatin1Char('>'))
3096                         ++i;
3097                     ++i;
3098                 }
3099                 else {
3100                     // retain all others
3101                     html += charLangle;
3102                 }
3103             }
3104         }
3105         else {
3106             html += src.at(i);
3107             ++i;
3108         }
3109     }
3110     return html;
3111 }
3112
3113 void HtmlGenerator::generateLink(const Atom* atom,
3114                                  const Node* /* relative */,
3115                                  CodeMarker* marker)
3116 {
3117     static QRegExp camelCase("[A-Z][A-Z][a-z]|[a-z][A-Z0-9]|_");
3118
3119     if (funcLeftParen.indexIn(atom->string()) != -1 && marker->recognizeLanguage("Cpp")) {
3120         // hack for C++: move () outside of link
3121         int k = funcLeftParen.pos(1);
3122         out() << protectEnc(atom->string().left(k));
3123         if (link.isEmpty()) {
3124             if (showBrokenLinks)
3125                 out() << "</i>";
3126         } else {
3127             out() << "</a>";
3128         }
3129         inLink = false;
3130         out() << protectEnc(atom->string().mid(k));
3131     } else {
3132         out() << protectEnc(atom->string());
3133     }
3134 }
3135
3136 QString HtmlGenerator::cleanRef(const QString& ref)
3137 {
3138     QString clean;
3139
3140     if (ref.isEmpty())
3141         return clean;
3142
3143     clean.reserve(ref.size() + 20);
3144     const QChar c = ref[0];
3145     const uint u = c.unicode();
3146
3147     if ((u >= 'a' && u <= 'z') ||
3148             (u >= 'A' && u <= 'Z') ||
3149             (u >= '0' && u <= '9')) {
3150         clean += c;
3151     } else if (u == '~') {
3152         clean += "dtor.";
3153     } else if (u == '_') {
3154         clean += "underscore.";
3155     } else {
3156         clean += QLatin1Char('A');
3157     }
3158
3159     for (int i = 1; i < (int) ref.length(); i++) {
3160         const QChar c = ref[i];
3161         const uint u = c.unicode();
3162         if ((u >= 'a' && u <= 'z') ||
3163                 (u >= 'A' && u <= 'Z') ||
3164                 (u >= '0' && u <= '9') || u == '-' ||
3165                 u == '_' || u == ':' || u == '.') {
3166             clean += c;
3167         } else if (c.isSpace()) {
3168             clean += QLatin1Char('-');
3169         } else if (u == '!') {
3170             clean += "-not";
3171         } else if (u == '&') {
3172             clean += "-and";
3173         } else if (u == '<') {
3174             clean += "-lt";
3175         } else if (u == '=') {
3176             clean += "-eq";
3177         } else if (u == '>') {
3178             clean += "-gt";
3179         } else if (u == '#') {
3180             clean += QLatin1Char('#');
3181         } else {
3182             clean += QLatin1Char('-');
3183             clean += QString::number((int)u, 16);
3184         }
3185     }
3186     return clean;
3187 }
3188
3189 QString HtmlGenerator::registerRef(const QString& ref)
3190 {
3191     QString clean = HtmlGenerator::cleanRef(ref);
3192
3193     for (;;) {
3194         QString& prevRef = refMap[clean.toLower()];
3195         if (prevRef.isEmpty()) {
3196             prevRef = ref;
3197             break;
3198         } else if (prevRef == ref) {
3199             break;
3200         }
3201         clean += QLatin1Char('x');
3202     }
3203     return clean;
3204 }
3205
3206 QString HtmlGenerator::protectEnc(const QString &string)
3207 {
3208     return protect(string, outputEncoding);
3209 }
3210
3211 QString HtmlGenerator::protect(const QString &string, const QString &outputEncoding)
3212 {
3213 #define APPEND(x) \
3214     if (html.isEmpty()) { \
3215     html = string; \
3216     html.truncate(i); \
3217 } \
3218     html += (x);
3219
3220     QString html;
3221     int n = string.length();
3222
3223     for (int i = 0; i < n; ++i) {
3224         QChar ch = string.at(i);
3225
3226         if (ch == QLatin1Char('&')) {
3227             APPEND("&amp;");
3228         } else if (ch == QLatin1Char('<')) {
3229             APPEND("&lt;");
3230         } else if (ch == QLatin1Char('>')) {
3231             APPEND("&gt;");
3232         } else if (ch == QLatin1Char('"')) {
3233             APPEND("&quot;");
3234         } else if ((outputEncoding == "ISO-8859-1" && ch.unicode() > 0x007F)
3235                    || (ch == QLatin1Char('*') && i + 1 < n && string.at(i) == QLatin1Char('/'))
3236                    || (ch == QLatin1Char('.') && i > 2 && string.at(i - 2) == QLatin1Char('.'))) {
3237             // we escape '*/' and the last dot in 'e.g.' and 'i.e.' for the Javadoc generator
3238             APPEND("&#x");
3239             html += QString::number(ch.unicode(), 16);
3240             html += QLatin1Char(';');
3241         } else {
3242             if (!html.isEmpty())
3243                 html += ch;
3244         }
3245     }
3246
3247     if (!html.isEmpty())
3248         return html;
3249     return string;
3250
3251 #undef APPEND
3252 }
3253
3254 QString HtmlGenerator::fileBase(const Node *node) const
3255 {
3256     QString result;
3257
3258     result = Generator::fileBase(node);
3259
3260     if (!node->isInnerNode()) {
3261         switch (node->status()) {
3262         case Node::Compat:
3263             result += "-compat";
3264             break;
3265         case Node::Obsolete:
3266             result += "-obsolete";
3267             break;
3268         default:
3269             ;
3270         }
3271     }
3272     return result;
3273 }
3274
3275 QString HtmlGenerator::fileName(const Node *node)
3276 {
3277     if (node->type() == Node::Fake) {
3278         if (static_cast<const FakeNode *>(node)->subType() == Node::ExternalPage)
3279             return node->name();
3280         if (static_cast<const FakeNode *>(node)->subType() == Node::Image)
3281             return node->name();
3282     }
3283     return Generator::fileName(node);
3284 }
3285
3286 QString HtmlGenerator::refForNode(const Node *node)
3287 {
3288     const FunctionNode *func;
3289     const TypedefNode *typedeffe;
3290     QString ref;
3291
3292     switch (node->type()) {
3293     case Node::Namespace:
3294     case Node::Class:
3295     default:
3296         break;
3297     case Node::Enum:
3298         ref = node->name() + "-enum";
3299         break;
3300     case Node::Typedef:
3301         typedeffe = static_cast<const TypedefNode *>(node);
3302         if (typedeffe->associatedEnum()) {
3303             return refForNode(typedeffe->associatedEnum());
3304         }
3305         else {
3306             ref = node->name() + "-typedef";
3307         }
3308         break;
3309     case Node::Function:
3310         func = static_cast<const FunctionNode *>(node);
3311         if (func->associatedProperty()) {
3312             return refForNode(func->associatedProperty());
3313         }
3314         else {
3315             ref = func->name();
3316             if (func->overloadNumber() != 1)
3317                 ref += QLatin1Char('-') + QString::number(func->overloadNumber());
3318         }
3319         break;
3320     case Node::Fake:
3321         if (node->subType() != Node::QmlPropertyGroup)
3322             break;
3323     case Node::QmlProperty:
3324     case Node::Property:
3325         ref = node->name() + "-prop";
3326         break;
3327     case Node::QmlSignal:
3328         ref = node->name() + "-signal";
3329         break;
3330     case Node::QmlSignalHandler:
3331         ref = node->name() + "-signal-handler";
3332         break;
3333     case Node::QmlMethod:
3334         func = static_cast<const FunctionNode *>(node);
3335         ref = func->name() + "-method";
3336         if (func->overloadNumber() != 1)
3337             ref += QLatin1Char('-') + QString::number(func->overloadNumber());
3338         break;
3339     case Node::Variable:
3340         ref = node->name() + "-var";
3341         break;
3342     }
3343     return registerRef(ref);
3344 }
3345
3346 #define DEBUG_ABSTRACT 0
3347
3348 /*!
3349   Construct the link string for the \a node and return it.
3350   The \a relative node is use to decide the link we are
3351   generating is in the same file as the target. Note the
3352   relative node can be 0, which pretty much guarantees
3353   that the link and the target aren't in the same file.
3354   */
3355 QString HtmlGenerator::linkForNode(const Node *node, const Node *relative)
3356 {
3357     if (node == 0 || node == relative)
3358         return QString();
3359     if (!node->url().isEmpty())
3360         return node->url();
3361     if (fileBase(node).isEmpty())
3362         return QString();
3363     if (node->access() == Node::Private)
3364         return QString();
3365
3366     QString fn = fileName(node);
3367     if (node && relative && node->parent() != relative) {
3368         if (node->parent()->subType() == Node::QmlClass && relative->subType() == Node::QmlClass) {
3369             if (node->parent()->isAbstract()) {
3370                 /*
3371                   This is a bit of a hack. What we discover with
3372                   the three 'if' statements immediately above,
3373                   is that node's parent is marked \qmlabstract
3374                   but the link appears in a qdoc comment for a
3375                   subclass of the node's parent. This means the
3376                   link should refer to the file for the relative
3377                   node, not the file for node.
3378                  */
3379                 fn = fileName(relative);
3380 #if DEBUG_ABSTRACT
3381                 qDebug() << "ABSTRACT:" << node->parent()->name()
3382                          << node->name() << relative->name()
3383                          << node->parent()->type() << node->parent()->subType()
3384                          << relative->type() << relative->subType() << outFileName();
3385 #endif
3386             }
3387         }
3388     }
3389     QString link = fn;
3390
3391     if (!node->isInnerNode() || node->subType() == Node::QmlPropertyGroup) {
3392         QString ref = refForNode(node);
3393         if (relative && fn == fileName(relative) && ref == refForNode(relative))
3394             return QString();
3395
3396         link += QLatin1Char('#');
3397         link += ref;
3398     }
3399     /*
3400       If the output is going to subdirectories, then if the
3401       two nodes will be output to different directories, then
3402       the link must go up to the parent directory and then
3403       back down into the other subdirectory.
3404      */
3405     if (node && relative && (node != relative)) {
3406         if (node->outputSubdirectory() != relative->outputSubdirectory())
3407             link.prepend(QString("../" + node->outputSubdirectory() + QLatin1Char('/')));
3408     }
3409     return link;
3410 }
3411
3412 QString HtmlGenerator::refForAtom(Atom *atom, const Node * /* node */)
3413 {
3414     if (atom->type() == Atom::SectionLeft) {
3415         return Doc::canonicalTitle(Text::sectionHeading(atom).toString());
3416     }
3417     else if (atom->type() == Atom::Target) {
3418         return Doc::canonicalTitle(atom->string());
3419     }
3420     else {
3421         return QString();
3422     }
3423 }
3424
3425 void HtmlGenerator::generateFullName(const Node *apparentNode,
3426                                      const Node *relative,
3427                                      CodeMarker *marker,
3428                                      const Node *actualNode)
3429 {
3430     if (actualNode == 0)
3431         actualNode = apparentNode;
3432     out() << "<a href=\"" << linkForNode(actualNode, relative);
3433     if (true || relative == 0 || relative->status() != actualNode->status()) {
3434         switch (actualNode->status()) {
3435         case Node::Obsolete:
3436             out() << "\" class=\"obsolete";
3437             break;
3438         case Node::Compat:
3439             out() << "\" class=\"compat";
3440             break;
3441         default:
3442             ;
3443         }
3444     }
3445     out() << "\">";
3446     out() << protectEnc(fullName(apparentNode, relative, marker));
3447     out() << "</a>";
3448 }
3449
3450 void HtmlGenerator::generateDetailedMember(const Node *node,
3451                                            const InnerNode *relative,
3452                                            CodeMarker *marker)
3453 {
3454     const EnumNode *enume;
3455
3456 #ifdef GENERATE_MAC_REFS
3457     generateMacRef(node, marker);
3458 #endif
3459     generateExtractionMark(node, MemberMark);
3460     if (node->type() == Node::Enum
3461             && (enume = static_cast<const EnumNode *>(node))->flagsType()) {
3462 #ifdef GENERATE_MAC_REFS
3463         generateMacRef(enume->flagsType(), marker);
3464 #endif
3465         out() << "<h3 class=\"flags\">";
3466         out() << "<a name=\"" + refForNode(node) + "\"></a>";
3467         generateSynopsis(enume, relative, marker, CodeMarker::Detailed);
3468         out() << "<br/>";
3469         generateSynopsis(enume->flagsType(),
3470                          relative,
3471                          marker,
3472                          CodeMarker::Detailed);
3473         out() << "</h3>\n";
3474     }
3475     else {
3476         out() << "<h3 class=\"fn\">";
3477         out() << "<a name=\"" + refForNode(node) + "\"></a>";
3478         generateSynopsis(node, relative, marker, CodeMarker::Detailed);
3479         out() << "</h3>" << divNavTop << '\n';
3480     }
3481
3482     generateStatus(node, marker);
3483     generateBody(node, marker);
3484     generateThreadSafeness(node, marker);
3485     generateSince(node, marker);
3486
3487     if (node->type() == Node::Property) {
3488         const PropertyNode *property = static_cast<const PropertyNode *>(node);
3489         Section section;
3490
3491         section.members += property->getters();
3492         section.members += property->setters();
3493         section.members += property->resetters();
3494
3495         if (!section.members.isEmpty()) {
3496             out() << "<p><b>Access functions:</b></p>\n";
3497             generateSectionList(section, node, marker, CodeMarker::Accessors);
3498         }
3499
3500         Section notifiers;
3501         notifiers.members += property->notifiers();
3502
3503         if (!notifiers.members.isEmpty()) {
3504             out() << "<p><b>Notifier signal:</b></p>\n";
3505             //out() << "<p>This signal is emitted when the property value is changed.</p>\n";
3506             generateSectionList(notifiers, node, marker, CodeMarker::Accessors);
3507         }
3508     }
3509     else if (node->type() == Node::Enum) {
3510         const EnumNode *enume = static_cast<const EnumNode *>(node);
3511         if (enume->flagsType()) {
3512             out() << "<p>The " << protectEnc(enume->flagsType()->name())
3513                   << " type is a typedef for "
3514                   << "<a href=\"qflags.html\">QFlags</a>&lt;"
3515                   << protectEnc(enume->name())
3516                   << "&gt;. It stores an OR combination of "
3517                   << protectEnc(enume->name())
3518                   << " values.</p>\n";
3519         }
3520     }
3521     generateAlsoList(node, marker);
3522     generateExtractionMark(node, EndMark);
3523 }
3524
3525 void HtmlGenerator::findAllClasses(const InnerNode *node)
3526 {
3527     NodeList::const_iterator c = node->childNodes().constBegin();
3528     while (c != node->childNodes().constEnd()) {
3529         if ((*c)->access() != Node::Private && (*c)->url().isEmpty()) {
3530             if ((*c)->type() == Node::Class && !(*c)->doc().isEmpty()) {
3531                 QString className = (*c)->name();
3532                 if ((*c)->parent() &&
3533                         (*c)->parent()->type() == Node::Namespace &&
3534                         !(*c)->parent()->name().isEmpty())
3535                     className = (*c)->parent()->name()+"::"+className;
3536
3537                 if (!(static_cast<const ClassNode *>(*c))->hideFromMainList()) {
3538                     if ((*c)->status() == Node::Compat) {
3539                         compatClasses.insert(className, *c);
3540                     }
3541                     else if ((*c)->status() == Node::Obsolete) {
3542                         obsoleteClasses.insert(className, *c);
3543                     }
3544                     else {
3545                         nonCompatClasses.insert(className, *c);
3546                         if ((*c)->status() == Node::Main)
3547                             mainClasses.insert(className, *c);
3548                     }
3549                 }
3550
3551                 QString moduleName = (*c)->moduleName();
3552                 if (moduleName == "Qt3SupportLight") {
3553                     moduleClassMap[moduleName].insert((*c)->name(), *c);
3554                     moduleName = "Qt3Support";
3555                 }
3556                 if (!moduleName.isEmpty())
3557                     moduleClassMap[moduleName].insert((*c)->name(), *c);
3558
3559                 QString serviceName =
3560                         (static_cast<const ClassNode *>(*c))->serviceName();
3561                 if (!serviceName.isEmpty())
3562                     serviceClasses.insert(serviceName, *c);
3563             }
3564             else if ((*c)->type() == Node::Fake &&
3565                      (*c)->subType() == Node::QmlClass &&
3566                      !(*c)->doc().isEmpty()) {
3567                 QString qmlClassName = (*c)->name();
3568                 /*
3569                   Remove the "QML:" prefix, if present.
3570                   It shouldn't be present anymore.
3571                 */
3572                 if (qmlClassName.startsWith(QLatin1String("QML:")))
3573                     qmlClasses.insert(qmlClassName.mid(4),*c);
3574                 else
3575                     qmlClasses.insert(qmlClassName,*c);
3576             }
3577             else if ((*c)->isInnerNode()) {
3578                 findAllClasses(static_cast<InnerNode *>(*c));
3579             }
3580         }
3581         ++c;
3582     }
3583 }
3584
3585 void HtmlGenerator::findAllFunctions(const InnerNode *node)
3586 {
3587     NodeList::ConstIterator c = node->childNodes().constBegin();
3588     while (c != node->childNodes().constEnd()) {
3589         if ((*c)->access() != Node::Private) {
3590             if ((*c)->isInnerNode() && (*c)->url().isEmpty()) {
3591                 findAllFunctions(static_cast<const InnerNode *>(*c));
3592             }
3593             else if ((*c)->type() == Node::Function) {
3594                 const FunctionNode *func = static_cast<const FunctionNode *>(*c);
3595                 if ((func->status() > Node::Obsolete) &&
3596                         !func->isInternal() &&
3597                         (func->metaness() != FunctionNode::Ctor) &&
3598                         (func->metaness() != FunctionNode::Dtor)) {
3599                     funcIndex[(*c)->name()].insert((*c)->parent()->fullDocumentName(), *c);
3600                 }
3601             }
3602         }
3603         ++c;
3604     }
3605 }
3606
3607 void HtmlGenerator::findAllLegaleseTexts(const InnerNode *node)
3608 {
3609     NodeList::ConstIterator c = node->childNodes().constBegin();
3610     while (c != node->childNodes().constEnd()) {
3611         if ((*c)->access() != Node::Private) {
3612             if (!(*c)->doc().legaleseText().isEmpty())
3613                 legaleseTexts.insertMulti((*c)->doc().legaleseText(), *c);
3614             if ((*c)->isInnerNode())
3615                 findAllLegaleseTexts(static_cast<const InnerNode *>(*c));
3616         }
3617         ++c;
3618     }
3619 }
3620
3621 void HtmlGenerator::findAllNamespaces(const InnerNode *node)
3622 {
3623     NodeList::ConstIterator c = node->childNodes().constBegin();
3624     while (c != node->childNodes().constEnd()) {
3625         if ((*c)->access() != Node::Private) {
3626             if ((*c)->isInnerNode() && (*c)->url().isEmpty()) {
3627                 findAllNamespaces(static_cast<const InnerNode *>(*c));
3628                 if ((*c)->type() == Node::Namespace) {
3629                     const NamespaceNode *nspace = static_cast<const NamespaceNode *>(*c);
3630                     // Ensure that the namespace's name is not empty (the root
3631                     // namespace has no name).
3632                     if (!nspace->name().isEmpty()) {
3633                         namespaceIndex.insert(nspace->name(), *c);
3634                         QString moduleName = (*c)->moduleName();
3635                         if (moduleName == "Qt3SupportLight") {
3636                             moduleNamespaceMap[moduleName].insert((*c)->name(), *c);
3637                             moduleName = "Qt3Support";
3638                         }
3639                         if (!moduleName.isEmpty())
3640                             moduleNamespaceMap[moduleName].insert((*c)->name(), *c);
3641                     }
3642                 }
3643             }
3644         }
3645         ++c;
3646     }
3647 }
3648
3649 int HtmlGenerator::hOffset(const Node *node)
3650 {
3651     switch (node->type()) {
3652     case Node::Namespace:
3653     case Node::Class:
3654         return 2;
3655     case Node::Fake:
3656         return 1;
3657     case Node::Enum:
3658     case Node::Typedef:
3659     case Node::Function:
3660     case Node::Property:
3661     default:
3662         return 3;
3663     }
3664 }
3665
3666 bool HtmlGenerator::isThreeColumnEnumValueTable(const Atom *atom)
3667 {
3668     while (atom != 0 && !(atom->type() == Atom::ListRight && atom->string() == ATOM_LIST_VALUE)) {
3669         if (atom->type() == Atom::ListItemLeft && !matchAhead(atom, Atom::ListItemRight))
3670             return true;
3671         atom = atom->next();
3672     }
3673     return false;
3674 }
3675
3676 const Node *HtmlGenerator::findNodeForTarget(const QString &target,
3677                                              const Node *relative,
3678                                              CodeMarker *marker,
3679                                              const Atom *atom)
3680 {
3681     const Node *node = 0;
3682
3683     if (target.isEmpty()) {
3684         node = relative;
3685     }
3686     else if (target.endsWith(".html")) {
3687         node = tree_->root()->findChildNodeByNameAndType(target, Node::Fake);
3688     }
3689     else if (marker) {
3690         node = marker->resolveTarget(target, tree_, relative);
3691         if (!node) {
3692             node = tree_->findFakeNodeByTitle(target, relative);
3693         }
3694         if (!node && atom) {
3695             node = tree_->findUnambiguousTarget(target, *const_cast<Atom**>(&atom), relative);
3696         }
3697     }
3698
3699     if (!node)
3700         relative->doc().location().warning(tr("Cannot link to '%1'").arg(target));
3701     return node;
3702 }
3703
3704 const QPair<QString,QString> HtmlGenerator::anchorForNode(const Node *node)
3705 {
3706     QPair<QString,QString> anchorPair;
3707
3708     anchorPair.first = Generator::fileName(node);
3709     if (node->type() == Node::Fake) {
3710         const FakeNode *fakeNode = static_cast<const FakeNode*>(node);
3711         anchorPair.second = fakeNode->title();
3712     }
3713
3714     return anchorPair;
3715 }
3716
3717 QString HtmlGenerator::getLink(const Atom *atom,
3718                                const Node *relative,
3719                                CodeMarker *marker,
3720                                const Node** node)
3721 {
3722     QString link;
3723     *node = 0;
3724     inObsoleteLink = false;
3725
3726     if (atom->string().contains(QLatin1Char(':')) &&
3727             (atom->string().startsWith("file:")
3728              || atom->string().startsWith("http:")
3729              || atom->string().startsWith("https:")
3730              || atom->string().startsWith("ftp:")
3731              || atom->string().startsWith("mailto:"))) {
3732
3733         link = atom->string();
3734     }
3735     else {
3736         QStringList path;
3737         if (atom->string().contains('#')) {
3738             path = atom->string().split('#');
3739         }
3740         else {
3741             path.append(atom->string());
3742         }
3743
3744         Atom *targetAtom = 0;
3745         QString first = path.first().trimmed();
3746         if (first.isEmpty()) {
3747             *node = relative;
3748         }
3749         else if (first.endsWith(".html")) {
3750             /*
3751               This is not a recursive search. That's ok in
3752               this case, because we are searching for a page
3753               node, which must be a direct child of the tree
3754               root.
3755             */
3756             *node = tree_->root()->findChildNodeByNameAndType(first, Node::Fake);
3757         }
3758         else {
3759             *node = marker->resolveTarget(first, tree_, relative);
3760             if (!*node) {
3761                 *node = tree_->findFakeNodeByTitle(first, relative);
3762             }
3763             if (!*node) {
3764                 *node = tree_->findUnambiguousTarget(first, targetAtom, relative);
3765             }
3766         }
3767         if (*node) {
3768             if (!(*node)->url().isEmpty()) {
3769                 return (*node)->url();
3770             }
3771             else {
3772                 path.removeFirst();
3773             }
3774         }
3775         else {
3776             *node = relative;
3777         }
3778
3779         if (*node) {
3780             if ((*node)->status() == Node::Obsolete) {
3781                 if (relative) {
3782                     if (relative->parent() != *node) {
3783                         if (relative->status() != Node::Obsolete) {
3784                             bool porting = false;
3785                             if (relative->type() == Node::Fake) {
3786                                 const FakeNode* fake = static_cast<const FakeNode*>(relative);
3787                                 if (fake->title().startsWith("Porting"))
3788                                     porting = true;
3789                             }
3790                             QString name = marker->plainFullName(relative);
3791                             if (!porting && !name.startsWith("Q3")) {
3792                                 if (obsoleteLinks) {
3793                                     relative->doc().location().warning(tr("Link to obsolete item '%1' in %2")
3794                                                                        .arg(atom->string())
3795                                                                        .arg(name));
3796                                 }
3797                                 inObsoleteLink = true;
3798                             }
3799                         }
3800                     }
3801                 }
3802                 else {
3803                     qDebug() << "Link to Obsolete entity"
3804                              << (*node)->name() << "no relative";
3805                 }
3806             }
3807         }
3808
3809         /*
3810           This loop really only makes sense if *node is not 0.
3811           In that case, The node *node points to represents a
3812           qdoc page, so the link will ultimately point to some
3813           target on that page. This loop finds that target on
3814           the page that *node represents. targetAtom is that
3815           target.
3816          */
3817         while (!path.isEmpty()) {
3818             targetAtom = tree_->findTarget(path.first(), *node);
3819             if (targetAtom == 0)
3820                 break;
3821             path.removeFirst();
3822         }
3823
3824         /*
3825           Given that *node is not null, we now cconstruct a link
3826           to the page that *node represents, and then if there is
3827           a target on that page, we connect the target to the link
3828           with '#'.
3829          */
3830         if (path.isEmpty()) {
3831             link = linkForNode(*node, relative);
3832             if (*node && (*node)->subType() == Node::Image)
3833                 link = "images/used-in-examples/" + link;
3834             if (targetAtom)
3835                 link += QLatin1Char('#') + refForAtom(targetAtom, *node);
3836         }
3837         /*
3838           If the output is going to subdirectories, then if the
3839           two nodes will be output to different directories, then
3840           the link must go up to the parent directory and then
3841           back down into the other subdirectory.
3842         */
3843         if (link.startsWith("images/")) {
3844             link.prepend(QString("../"));
3845         }
3846         else if (*node && relative && (*node != relative)) {
3847             if ((*node)->outputSubdirectory() != relative->outputSubdirectory()) {
3848                 link.prepend(QString("../" + (*node)->outputSubdirectory() + QLatin1Char('/')));
3849             }
3850         }
3851     }
3852     return link;
3853 }
3854
3855 void HtmlGenerator::generateIndex(const QString &fileBase,
3856                                   const QString &url,
3857                                   const QString &title)
3858 {
3859     tree_->generateIndex(outputDir() + QLatin1Char('/') + fileBase + ".index", url, title);
3860 }
3861
3862 void HtmlGenerator::generateStatus(const Node *node, CodeMarker *marker)
3863 {
3864     Text text;
3865
3866     switch (node->status()) {
3867     case Node::Obsolete:
3868         if (node->isInnerNode())
3869             Generator::generateStatus(node, marker);
3870         break;
3871     case Node::Compat:
3872         if (node->isInnerNode()) {
3873             text << Atom::ParaLeft
3874                  << Atom(Atom::FormattingLeft,ATOM_FORMATTING_BOLD)
3875                  << "This "
3876                  << typeString(node)
3877                  << " is part of the Qt 3 support library."
3878                  << Atom(Atom::FormattingRight, ATOM_FORMATTING_BOLD)
3879                  << " It is provided to keep old source code working. "
3880                  << "We strongly advise against "
3881                  << "using it in new code. See ";
3882
3883             const FakeNode *fakeNode = tree_->findFakeNodeByTitle("Porting To Qt 4");
3884             Atom *targetAtom = 0;
3885             if (fakeNode && node->type() == Node::Class) {
3886                 QString oldName(node->name());
3887                 oldName.remove(QLatin1Char('3'));
3888                 targetAtom = tree_->findTarget(oldName,
3889                                                 fakeNode);
3890             }
3891
3892             if (targetAtom) {
3893                 text << Atom(Atom::Link, linkForNode(fakeNode, node) + QLatin1Char('#') +
3894                              refForAtom(targetAtom, fakeNode));
3895             }
3896             else
3897                 text << Atom(Atom::Link, "Porting to Qt 4");
3898
3899             text << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK)
3900                  << Atom(Atom::String, "Porting to Qt 4")
3901                  << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK)
3902                  << " for more information."
3903                  << Atom::ParaRight;
3904         }
3905         generateText(text, node, marker);
3906         break;
3907     default:
3908         Generator::generateStatus(node, marker);
3909     }
3910 }
3911
3912 #ifdef GENERATE_MAC_REFS
3913 /*
3914   No longer valid.
3915  */
3916 void HtmlGenerator::generateMacRef(const Node *node, CodeMarker *marker)
3917 {
3918     if (!pleaseGenerateMacRef || marker == 0)
3919         return;
3920
3921     QStringList macRefs = marker->macRefsForNode(node);
3922     foreach (const QString &macRef, macRefs)
3923         out() << "<a name=\"" << "//apple_ref/" << macRef << "\"></a>\n";
3924 }
3925 #endif
3926
3927 void HtmlGenerator::beginLink(const QString &link,
3928                               const Node *node,
3929                               const Node *relative,
3930                               CodeMarker *marker)
3931 {
3932     Q_UNUSED(marker)
3933     Q_UNUSED(relative)
3934
3935     this->link = link;
3936     if (link.isEmpty()) {
3937         if (showBrokenLinks)
3938             out() << "<i>";
3939     }
3940     else if (node == 0 ||
3941              (relative != 0 && node->status() == relative->status())) {
3942         out() << "<a href=\"" << link << "\">";
3943     }
3944     else {
3945         switch (node->status()) {
3946         case Node::Obsolete:
3947             out() << "<a href=\"" << link << "\" class=\"obsolete\">";
3948             break;
3949         case Node::Compat:
3950             out() << "<a href=\"" << link << "\" class=\"compat\">";
3951             break;
3952         default:
3953             out() << "<a href=\"" << link << "\">";
3954         }
3955     }
3956     inLink = true;
3957 }
3958
3959 void HtmlGenerator::endLink()
3960 {
3961     if (inLink) {
3962         if (link.isEmpty()) {
3963             if (showBrokenLinks)
3964                 out() << "</i>";
3965         }
3966         else {
3967             if (inObsoleteLink) {
3968                 out() << "<sup>(obsolete)</sup>";
3969             }
3970             out() << "</a>";
3971         }
3972     }
3973     inLink = false;
3974     inObsoleteLink = false;
3975 }
3976
3977 /*!
3978   Generates the summary for the \a section. Only used for
3979   sections of QML element documentation.
3980  */
3981 void HtmlGenerator::generateQmlSummary(const Section& section,
3982                                        const Node *relative,
3983                                        CodeMarker *marker)
3984 {
3985     if (!section.members.isEmpty()) {
3986         out() << "<ul>\n";
3987         NodeList::ConstIterator m;
3988         m = section.members.constBegin();
3989         while (m != section.members.constEnd()) {
3990             out() << "<li class=\"fn\">";
3991             generateQmlItem(*m,relative,marker,true);
3992             out() << "</li>\n";
3993             ++m;
3994         }
3995         out() << "</ul>\n";
3996     }
3997 }
3998
3999 /*!
4000   Outputs the html detailed documentation for a section
4001   on a QML element reference page.
4002  */
4003 void HtmlGenerator::generateDetailedQmlMember(Node *node,
4004                                               const InnerNode *relative,
4005                                               CodeMarker *marker)
4006 {
4007     QmlPropertyNode* qpn = 0;
4008 #ifdef GENERATE_MAC_REFS
4009     generateMacRef(node, marker);
4010 #endif
4011     generateExtractionMark(node, MemberMark);
4012     out() << "<div class=\"qmlitem\">";
4013     if (node->subType() == Node::QmlPropertyGroup) {
4014         const QmlPropGroupNode* qpgn = static_cast<const QmlPropGroupNode*>(node);
4015         NodeList::ConstIterator p = qpgn->childNodes().constBegin();
4016         out() << "<div class=\"qmlproto\">";
4017         out() << "<table class=\"qmlname\">";
4018         while (p != qpgn->childNodes().constEnd()) {
4019             if ((*p)->type() == Node::QmlProperty) {
4020                 qpn = static_cast<QmlPropertyNode*>(*p);
4021                 out() << "<tr valign=\"top\" class=\"odd\">";
4022                 out() << "<td class=\"tblQmlPropNode\"><p>";
4023                 out() << "<a name=\"" + refForNode(qpn) + "\"></a>";
4024
4025                 if (!qpn->isWritable(tree_))
4026                     out() << "<span class=\"qmlreadonly\">read-only</span>";
4027                 if (qpn->isDefault())
4028                     out() << "<span class=\"qmldefault\">default</span>";
4029                 generateQmlItem(qpn, relative, marker, false);
4030                 out() << "</p></td></tr>";
4031             }
4032             ++p;
4033         }
4034         out() << "</table>";
4035         out() << "</div>";
4036     }
4037     else if (node->type() == Node::QmlProperty) {
4038         qpn = static_cast<QmlPropertyNode*>(node);
4039         /*
4040           If the QML property node has a single subproperty,
4041           override, replace qpn with that override node and
4042           proceed as normal.
4043          */
4044         if (qpn->qmlPropNodes().size() == 1) {
4045             Node* n = qpn->qmlPropNodes().at(0);
4046             if (n->type() == Node::QmlProperty)
4047                 qpn = static_cast<QmlPropertyNode*>(n);
4048         }
4049         /*
4050           Now qpn either has no overrides, or it has more
4051           than 1. If it has none, proceed to output as nortmal.
4052          */
4053         if (qpn->qmlPropNodes().isEmpty()) {
4054             out() << "<div class=\"qmlproto\">";
4055             out() << "<table class=\"qmlname\">";
4056             out() << "<tr valign=\"top\" class=\"odd\">";
4057             out() << "<td class=\"tblQmlPropNode\"><p>";
4058             out() << "<a name=\"" + refForNode(qpn) + "\"></a>";
4059             if (!qpn->isReadOnlySet()) {
4060                 if (qpn->declarativeCppNode())
4061                     qpn->setReadOnly(!qpn->isWritable(tree_));
4062             }
4063             if (qpn->isReadOnly())
4064                 out() << "<span class=\"qmlreadonly\">read-only</span>";
4065             if (qpn->isDefault())
4066                 out() << "<span class=\"qmldefault\">default</span>";
4067             generateQmlItem(qpn, relative, marker, false);
4068             out() << "</p></td></tr>";
4069             out() << "</table>";
4070             out() << "</div>";
4071         }
4072         else {
4073             /*
4074               The QML property node has multiple override nodes.
4075               Process the whole list as we would for a QML property
4076               group.
4077              */
4078             NodeList::ConstIterator p = qpn->qmlPropNodes().constBegin();
4079             out() << "<div class=\"qmlproto\">";
4080             out() << "<table class=\"qmlname\">";
4081             while (p != qpn->qmlPropNodes().constEnd()) {
4082                 if ((*p)->type() == Node::QmlProperty) {
4083                     QmlPropertyNode* q = static_cast<QmlPropertyNode*>(*p);
4084                     out() << "<tr valign=\"top\" class=\"odd\">";
4085                     out() << "<td class=\"tblQmlPropNode\"><p>";
4086                     out() << "<a name=\"" + refForNode(q) + "\"></a>";
4087                     if (!qpn->isReadOnlySet())
4088                         qpn->setReadOnly(!qpn->isWritable(tree_));
4089                     if (qpn->isReadOnly())
4090                         out() << "<span class=\"qmlreadonly\">read-only</span>";
4091                     if (qpn->isDefault())
4092                         out() << "<span class=\"qmldefault\">default</span>";
4093                     generateQmlItem(q, relative, marker, false);
4094                     out() << "</p></td></tr>";
4095                 }
4096                 ++p;
4097             }
4098             out() << "</table>";
4099             out() << "</div>";
4100         }
4101     }
4102     else if (node->type() == Node::QmlSignal) {
4103         const FunctionNode* qsn = static_cast<const FunctionNode*>(node);
4104         out() << "<div class=\"qmlproto\">";
4105         out() << "<table class=\"qmlname\">";
4106         out() << "<tr valign=\"top\" class=\"odd\">";
4107         out() << "<td class=\"tblQmlFuncNode\"><p>";
4108         out() << "<a name=\"" + refForNode(qsn) + "\"></a>";
4109         generateSynopsis(qsn,relative,marker,CodeMarker::Detailed,false);
4110         out() << "</p></td></tr>";
4111         out() << "</table>";
4112         out() << "</div>";
4113     }
4114     else if (node->type() == Node::QmlSignalHandler) {
4115         const FunctionNode* qshn = static_cast<const FunctionNode*>(node);
4116         out() << "<div class=\"qmlproto\">";
4117         out() << "<table class=\"qmlname\">";
4118         out() << "<tr valign=\"top\" class=\"odd\">";
4119         out() << "<td class=\"tblQmlFuncNode\"><p>";
4120         out() << "<a name=\"" + refForNode(qshn) + "\"></a>";
4121         generateSynopsis(qshn,relative,marker,CodeMarker::Detailed,false);
4122         out() << "</p></td></tr>";
4123         out() << "</table>";
4124         out() << "</div>";
4125     }
4126     else if (node->type() == Node::QmlMethod) {
4127         const FunctionNode* qmn = static_cast<const FunctionNode*>(node);
4128         out() << "<div class=\"qmlproto\">";
4129         out() << "<table class=\"qmlname\">";
4130         out() << "<tr valign=\"top\" class=\"odd\">";
4131         out() << "<td class=\"tblQmlFuncNode\"><p>";
4132         out() << "<a name=\"" + refForNode(qmn) + "\"></a>";
4133         generateSynopsis(qmn,relative,marker,CodeMarker::Detailed,false);
4134         out() << "</p></td></tr>";
4135         out() << "</table>";
4136         out() << "</div>";
4137     }
4138     out() << "<div class=\"qmldoc\">";
4139     generateStatus(node, marker);
4140     generateBody(node, marker);
4141     generateThreadSafeness(node, marker);
4142     generateSince(node, marker);
4143     generateAlsoList(node, marker);
4144     out() << "</div>";
4145     out() << "</div>";
4146     generateExtractionMark(node, EndMark);
4147 }
4148
4149 /*!
4150   Output the "Inherits" line for the QML element,
4151   if there should be one.
4152  */
4153 void HtmlGenerator::generateQmlInherits(const QmlClassNode* qcn, CodeMarker* marker)
4154 {
4155     if (!qcn)
4156         return;
4157     const FakeNode* base = qcn->qmlBase();
4158     if (base) {
4159         Text text;
4160         text << Atom::ParaLeft << "Inherits ";
4161         text << Atom(Atom::LinkNode,CodeMarker::stringForNode(base));
4162         text << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK);
4163         text << Atom(Atom::String, base->name());
4164         text << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK);
4165         text << Atom::ParaRight;
4166         generateText(text, qcn, marker);
4167     }
4168 }
4169
4170 /*!
4171   Output the "[Xxx instantiates the C++ class QmlGraphicsXxx]"
4172   line for the QML element, if there should be one.
4173
4174   If there is no class node, or if the class node status
4175   is set to Node::Internal, do nothing.
4176  */
4177 void HtmlGenerator::generateQmlInstantiates(QmlClassNode* qcn, CodeMarker* marker)
4178 {
4179     ClassNode* cn = qcn->classNode();
4180     if (cn && (cn->status() != Node::Internal)) {
4181         Text text;
4182         text << Atom::ParaLeft;
4183         text << Atom(Atom::LinkNode,CodeMarker::stringForNode(qcn));
4184         text << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK);
4185         QString name = qcn->name();
4186         /*
4187           Remove the "QML:" prefix, if present.
4188           It shouldn't be present anymore.
4189         */
4190         if (name.startsWith(QLatin1String("QML:")))
4191             name = name.mid(4); // remove the "QML:" prefix
4192         text << Atom(Atom::String, name);
4193         text << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK);
4194         text << " instantiates the C++ class ";
4195         text << Atom(Atom::LinkNode,CodeMarker::stringForNode(cn));
4196         text << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK);
4197         text << Atom(Atom::String, cn->name());
4198         text << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK);
4199         text << Atom::ParaRight;
4200         generateText(text, qcn, marker);
4201     }
4202 }
4203
4204 /*!
4205   Output the "[QmlGraphicsXxx is instantiated by QML Type Xxx]"
4206   line for the class, if there should be one.
4207
4208   If there is no QML element, or if the class node status
4209   is set to Node::Internal, do nothing.
4210  */
4211 void HtmlGenerator::generateInstantiatedBy(ClassNode* cn, CodeMarker* marker)
4212 {
4213     if (cn &&  cn->status() != Node::Internal && cn->qmlElement() != 0) {
4214         const QmlClassNode* qcn = cn->qmlElement();
4215         Text text;
4216         text << Atom::ParaLeft;
4217         text << Atom(Atom::LinkNode,CodeMarker::stringForNode(cn));
4218         text << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK);
4219         text << Atom(Atom::String, cn->name());
4220         text << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK);
4221         text << " is instantiated by QML Type ";
4222         text << Atom(Atom::LinkNode,CodeMarker::stringForNode(qcn));
4223         text << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK);
4224         text << Atom(Atom::String, qcn->name());
4225         text << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK);
4226         text << Atom::ParaRight;
4227         generateText(text, cn, marker);
4228     }
4229 }
4230
4231 void HtmlGenerator::generateExtractionMark(const Node *node, ExtractionMarkType markType)
4232 {
4233     if (markType != EndMark) {
4234         out() << "<!-- $$$" + node->name();
4235         if (markType == MemberMark) {
4236             if (node->type() == Node::Function) {
4237                 const FunctionNode *func = static_cast<const FunctionNode *>(node);
4238                 if (!func->associatedProperty()) {
4239                     if (func->overloadNumber() == 1)
4240                         out() << "[overload1]";
4241                     out() << "$$$" + func->name() + func->rawParameters().remove(' ');
4242                 }
4243             } else if (node->type() == Node::Property) {
4244                 out() << "-prop";
4245                 const PropertyNode *prop = static_cast<const PropertyNode *>(node);
4246                 const NodeList &list = prop->functions();
4247                 foreach (const Node *propFuncNode, list) {
4248                     if (propFuncNode->type() == Node::Function) {
4249                         const FunctionNode *func = static_cast<const FunctionNode *>(propFuncNode);
4250                         out() << "$$$" + func->name() + func->rawParameters().remove(' ');
4251                     }
4252                 }
4253             } else if (node->type() == Node::Enum) {
4254                 const EnumNode *enumNode = static_cast<const EnumNode *>(node);
4255                 foreach (const EnumItem &item, enumNode->items())
4256                     out() << "$$$" + item.name();
4257             }
4258         } else if (markType == BriefMark) {
4259             out() << "-brief";
4260         } else if (markType == DetailedDescriptionMark) {
4261             out() << "-description";
4262         }
4263         out() << " -->\n";
4264     } else {
4265         out() << "<!-- @@@" + node->name() + " -->\n";
4266     }
4267 }
4268
4269
4270 /*!
4271   This function outputs one or more manifest files in XML.
4272   They are used by Creator.
4273  */
4274 void HtmlGenerator::generateManifestFiles()
4275 {
4276     generateManifestFile("examples", "example");
4277     generateManifestFile("demos", "demo");
4278     ExampleNode::exampleNodeMap.clear();
4279 }
4280
4281 /*!
4282   This function is called by generaqteManiferstFile(), once
4283   for each manifest file to be generated. \a manifest is the
4284   type of manifest file.
4285  */
4286 void HtmlGenerator::generateManifestFile(QString manifest, QString element)
4287 {
4288     if (ExampleNode::exampleNodeMap.isEmpty())
4289         return;
4290     QString fileName = manifest +"-manifest.xml";
4291     QFile file(outputDir() + QLatin1Char('/') + fileName);
4292     if (!file.open(QFile::WriteOnly | QFile::Text))
4293         return ;
4294     bool demos = false;
4295     if (manifest == "demos")
4296         demos = true;
4297
4298     bool proceed = false;
4299     ExampleNodeMap::Iterator i = ExampleNode::exampleNodeMap.begin();
4300     while (i != ExampleNode::exampleNodeMap.end()) {
4301         const ExampleNode* en = i.value();
4302         if (demos) {
4303             if (en->name().startsWith("demos")) {
4304                 proceed = true;
4305                 break;
4306             }
4307         }
4308         else if (!en->name().startsWith("demos")) {
4309             proceed = true;
4310             break;
4311         }
4312         ++i;
4313     }
4314     if (!proceed)
4315         return;
4316
4317     QXmlStreamWriter writer(&file);
4318     writer.setAutoFormatting(true);
4319     writer.writeStartDocument();
4320     writer.writeStartElement("instructionals");
4321     writer.writeAttribute("module", project);
4322     writer.writeStartElement(manifest);
4323
4324     i = ExampleNode::exampleNodeMap.begin();
4325     while (i != ExampleNode::exampleNodeMap.end()) {
4326         const ExampleNode* en = i.value();
4327         if (demos) {
4328             if (!en->name().startsWith("demos")) {
4329                 ++i;
4330                 continue;
4331             }
4332         }
4333         else if (en->name().startsWith("demos")) {
4334             ++i;
4335             continue;
4336         }
4337         writer.writeStartElement(element);
4338         writer.writeAttribute("name", en->title());
4339         QString docUrl = manifestDir + en->fileBase() + ".html";
4340         writer.writeAttribute("docUrl", docUrl);
4341         QStringList proFiles;
4342         foreach (const Node* child, en->childNodes()) {
4343             if (child->subType() == Node::File) {
4344                 QString file = child->name();
4345                 if (file.endsWith(".pro") || file.endsWith(".qmlproject")) {
4346                     if (file.startsWith("demos/"))
4347                         file = file.mid(6);
4348                     proFiles << file;
4349                 }
4350             }
4351         }
4352         if (!proFiles.isEmpty()) {
4353             if (proFiles.size() == 1) {
4354                 writer.writeAttribute("projectPath", proFiles[0]);
4355             }
4356             else {
4357                 QString exampleName = en->name().split('/').last();
4358                 bool proWithExampleNameFound = false;
4359                 for (int j = 0; j < proFiles.size(); j++)
4360                 {
4361                     if (proFiles[j].endsWith(QStringLiteral("%1/%1.pro").arg(exampleName))
4362                             || proFiles[j].endsWith(QStringLiteral("%1/%1.qmlproject").arg(exampleName))) {
4363                         writer.writeAttribute("projectPath", proFiles[j]);
4364                         proWithExampleNameFound = true;
4365                         break;
4366                     }
4367                 }
4368                 if (!proWithExampleNameFound)
4369                     writer.writeAttribute("projectPath", proFiles[0]);
4370             }
4371         }
4372         if (!en->imageFileName().isEmpty())
4373             writer.writeAttribute("imageUrl", manifestDir + en->imageFileName());
4374         writer.writeStartElement("description");
4375         Text brief = en->doc().briefText();
4376         if (!brief.isEmpty())
4377             writer.writeCDATA(brief.toString());
4378         else
4379             writer.writeCDATA(QString("No description available"));
4380         writer.writeEndElement(); // description
4381         QStringList tags = en->title().toLower().split(QLatin1Char(' '));
4382         if (!tags.isEmpty()) {
4383             writer.writeStartElement("tags");
4384             bool wrote_one = false;
4385             for (int n=0; n<tags.size(); ++n) {
4386                 QString tag = tags.at(n);
4387                 if (tag.at(0).isDigit())
4388                     continue;
4389                 if (tag.at(0) == '-')
4390                     continue;
4391                 if (tag.startsWith("example"))
4392                     continue;
4393                 if (tag.startsWith("chapter"))
4394                     continue;
4395                 if (tag.endsWith(QLatin1Char(':')))
4396                     tag.chop(1);
4397                 if (n>0 && wrote_one)
4398                     writer.writeCharacters(",");
4399                 writer.writeCharacters(tag);
4400                 wrote_one = true;
4401             }
4402             writer.writeEndElement(); // tags
4403         }
4404
4405         QString ename = en->name().mid(en->name().lastIndexOf('/')+1);
4406         QSet<QString> usedNames;
4407         foreach (const Node* child, en->childNodes()) {
4408             if (child->subType() == Node::File) {
4409                 QString file = child->name();
4410                 QString fileName = file.mid(file.lastIndexOf('/')+1);
4411                 QString baseName = fileName;
4412                 if ((fileName.count(QChar('.')) > 0) &&
4413                         (fileName.endsWith(".cpp") ||
4414                          fileName.endsWith(".h") ||
4415                          fileName.endsWith(".qml")))
4416                     baseName.truncate(baseName.lastIndexOf(QChar('.')));
4417                 if (baseName.compare(ename, Qt::CaseInsensitive) == 0) {
4418                     if (!usedNames.contains(fileName)) {
4419                         writer.writeStartElement("fileToOpen");
4420                         if (file.startsWith("demos/"))
4421                             file = file.mid(6);
4422                         writer.writeCharacters(file);
4423                         writer.writeEndElement(); // fileToOpen
4424                         usedNames.insert(fileName);
4425                     }
4426                 }
4427                 else if (fileName.toLower().endsWith("main.cpp") ||
4428                          fileName.toLower().endsWith("main.qml")) {
4429                     if (!usedNames.contains(fileName)) {
4430                         writer.writeStartElement("fileToOpen");
4431                         if (file.startsWith("demos/"))
4432                             file = file.mid(6);
4433                         writer.writeCharacters(file);
4434                         writer.writeEndElement(); // fileToOpen
4435                         usedNames.insert(fileName);
4436                     }
4437                 }
4438             }
4439         }
4440         writer.writeEndElement(); // example
4441         ++i;
4442     }
4443
4444     writer.writeEndElement(); // examples
4445     writer.writeEndElement(); // instructionals
4446     writer.writeEndDocument();
4447     file.close();
4448 }
4449
4450 /*!
4451   Find global entities that have documentation but no
4452   \e{relates} comand. Report these as errors if they
4453   are not also marked \e {internal}.
4454
4455   type: Class
4456   type: Namespace
4457
4458   subtype: Example
4459   subtype: External page
4460   subtype: Group
4461   subtype: Header file
4462   subtype: Module
4463   subtype: Page
4464   subtype: QML basic type
4465   subtype: QML class
4466   subtype: QML module
4467  */
4468 void HtmlGenerator::reportOrphans(const InnerNode* parent)
4469 {
4470     const NodeList& children = parent->childNodes();
4471     if (children.size() == 0)
4472         return;
4473
4474     bool related;
4475     QString message;
4476     for (int i=0; i<children.size(); ++i) {
4477         Node* child = children[i];
4478         if (!child || child->isInternal() || child->doc().isEmpty())
4479             continue;
4480         if (child->relates()) {
4481             related = true;
4482             message = child->relates()->name();
4483         }
4484         else {
4485             related = false;
4486             message = "has documentation but no \\relates command";
4487         }
4488         switch (child->type()) {
4489         case Node::Namespace:
4490             break;
4491         case Node::Class:
4492             break;
4493         case Node::Fake:
4494             switch (child->subType()) {
4495             case Node::Example:
4496                 break;
4497             case Node::HeaderFile:
4498                 break;
4499             case Node::File:
4500                 break;
4501             case Node::Image:
4502                 break;
4503             case Node::Group:
4504                 break;
4505             case Node::Module:
4506                 break;
4507             case Node::Page:
4508                 break;
4509             case Node::ExternalPage:
4510                 break;
4511             case Node::QmlClass:
4512                 break;
4513             case Node::QmlPropertyGroup:
4514                 break;
4515             case Node::QmlBasicType:
4516                 break;
4517             case Node::QmlModule:
4518                 break;
4519             case Node::Collision:
4520                 break;
4521             default:
4522                 break;
4523             }
4524             break;
4525         case Node::Enum:
4526             if (!related)
4527                 child->location().warning(tr("Global enum, %1, %2").arg(child->name()).arg(message));
4528             break;
4529         case Node::Typedef:
4530             if (!related)
4531                 child->location().warning(tr("Global typedef, %1, %2").arg(child->name()).arg(message));
4532             break;
4533         case Node::Function:
4534             if (!related) {
4535                 const FunctionNode* fn = static_cast<const FunctionNode*>(child);
4536                 if (fn->isMacro())
4537                     child->location().warning(tr("Global macro, %1, %2").arg(child->name()).arg(message));
4538                 else
4539                     child->location().warning(tr("Global function, %1(), %2").arg(child->name()).arg(message));
4540             }
4541             break;
4542         case Node::Property:
4543             break;
4544         case Node::Variable:
4545             if (!related)
4546                 child->location().warning(tr("Global variable, %1, %2").arg(child->name()).arg(message));
4547             break;
4548         case Node::QmlProperty:
4549             if (!related)
4550                 child->location().warning(tr("Global QML property, %1, %2").arg(child->name()).arg(message));
4551             break;
4552         case Node::QmlSignal:
4553             if (!related)
4554                 child->location().warning(tr("Global QML, signal, %1 %2").arg(child->name()).arg(message));
4555             break;
4556         case Node::QmlSignalHandler:
4557             if (!related)
4558                 child->location().warning(tr("Global QML signal handler, %1, %2").arg(child->name()).arg(message));
4559             break;
4560         case Node::QmlMethod:
4561             if (!related)
4562                 child->location().warning(tr("Global QML method, %1, %2").arg(child->name()).arg(message));
4563             break;
4564         default:
4565             break;
4566         }
4567     }
4568 }
4569
4570 /*!
4571   Returns a reference to the XML stream writer currently in use.
4572   There is one XML stream writer open for each XML file being
4573   written, and they are kept on a stack. The one on top of the
4574   stack is the one being written to at the moment. In the HTML
4575   output generator, it is perhaps impossible for there to ever
4576   be more than one writer open.
4577  */
4578 QXmlStreamWriter& HtmlGenerator::xmlWriter()
4579 {
4580     return *xmlWriterStack.top();
4581 }
4582
4583 /*!
4584   This function is only called for writing ditamaps.
4585
4586   Calls beginSubPage() in the base class to open the file.
4587   Then creates a new XML stream writer using the IO device
4588   from opened file and pushes the XML writer onto a stackj.
4589   Creates the file named \a fileName in the output directory.
4590   Attaches a QTextStream to the created file, which is written
4591   to all over the place using out(). Finally, it sets some
4592   parameters in the XML writer and calls writeStartDocument().
4593
4594   It also ensures that a GUID map is created for the output file.
4595  */
4596 void HtmlGenerator::beginDitamapPage(const InnerNode* node, const QString& fileName)
4597 {
4598     Generator::beginSubPage(node,fileName);
4599     QXmlStreamWriter* writer = new QXmlStreamWriter(out().device());
4600     xmlWriterStack.push(writer);
4601     writer->setAutoFormatting(true);
4602     writer->setAutoFormattingIndent(4);
4603     writer->writeStartDocument();
4604 }
4605
4606 /*!
4607   This function is only called for writing ditamaps.
4608
4609   Calls writeEndDocument() and then pops the XML stream writer
4610   off the stack and deletes it. Then it calls endSubPage() in
4611   the base class to close the device.
4612  */
4613 void HtmlGenerator::endDitamapPage()
4614 {
4615     xmlWriter().writeEndDocument();
4616     delete xmlWriterStack.pop();
4617     Generator::endSubPage();
4618 }
4619
4620 /*!
4621   This function is only called for writing ditamaps.
4622
4623   Creates the DITA map from the topicrefs in \a node,
4624   which is a DitaMapNode.
4625  */
4626 void HtmlGenerator::writeDitaMap(const DitaMapNode* node)
4627 {
4628     beginDitamapPage(node,node->name());
4629
4630     QString doctype = "<!DOCTYPE map PUBLIC \"-//OASIS//DTD DITA Map//EN\" \"map.dtd\">";
4631
4632     xmlWriter().writeDTD(doctype);
4633     xmlWriter().writeStartElement("map");
4634     xmlWriter().writeStartElement("topicmeta");
4635     xmlWriter().writeStartElement("shortdesc");
4636     xmlWriter().writeCharacters(node->title());
4637     xmlWriter().writeEndElement(); // </shortdesc>
4638     xmlWriter().writeEndElement(); // </topicmeta>
4639     DitaRefList map = node->map();
4640     writeDitaRefs(map);
4641     endDitamapPage();
4642 }
4643
4644 /*!
4645   Write the \a ditarefs to the current output file.
4646  */
4647 void HtmlGenerator::writeDitaRefs(const DitaRefList& ditarefs)
4648 {
4649     foreach (DitaRef* t, ditarefs) {
4650         if (t->isMapRef())
4651             xmlWriter().writeStartElement("mapref");
4652         else
4653             xmlWriter().writeStartElement("topicref");
4654         xmlWriter().writeAttribute("navtitle",t->navtitle());
4655         if (t->href().isEmpty()) {
4656             const FakeNode* fn = tree_->findFakeNodeByTitle(t->navtitle());
4657             if (fn)
4658                 xmlWriter().writeAttribute("href",fileName(fn));
4659         }
4660         else
4661             xmlWriter().writeAttribute("href",t->href());
4662         if (t->subrefs() && !t->subrefs()->isEmpty())
4663             writeDitaRefs(*(t->subrefs()));
4664         xmlWriter().writeEndElement(); // </topicref> or </mapref>
4665     }
4666 }
4667
4668 QT_END_NAMESPACE