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