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