Merge remote-tracking branch 'qt-doc-review/master'
[qt:qt.git] / tools / qdoc3 / htmlgenerator.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
6 **
7 ** This file is part of the tools applications of the Qt Toolkit.
8 **
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** GNU Lesser General Public License Usage
11 ** This file may be used under the terms of the GNU Lesser General Public
12 ** License version 2.1 as published by the Free Software Foundation and
13 ** appearing in the file LICENSE.LGPL included in the packaging of this
14 ** file. Please review the following information to ensure the GNU Lesser
15 ** General Public License version 2.1 requirements will be met:
16 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
17 **
18 ** In addition, as a special exception, Nokia gives you certain additional
19 ** rights. These rights are described in the Nokia Qt LGPL Exception
20 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
21 **
22 ** GNU General Public License Usage
23 ** Alternatively, this file may be used under the terms of the GNU General
24 ** Public License version 3.0 as published by the Free Software Foundation
25 ** and appearing in the file LICENSE.GPL included in the packaging of this
26 ** file. Please review the following information to ensure the GNU General
27 ** Public License version 3.0 requirements will be met:
28 ** http://www.gnu.org/copyleft/gpl.html.
29 **
30 ** Other Usage
31 ** Alternatively, this file may be used in accordance with the terms and
32 ** conditions contained in a signed written agreement between you and Nokia.
33 **
34 **
35 **
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 /*
43   htmlgenerator.cpp
44 */
45
46 #include "codemarker.h"
47 #include "codeparser.h"
48 #include "helpprojectwriter.h"
49 #include "htmlgenerator.h"
50 #include "node.h"
51 #include "separator.h"
52 #include "tree.h"
53 #include <ctype.h>
54
55 #include <qdebug.h>
56 #include <qlist.h>
57 #include <qiterator.h>
58 #include <qtextcodec.h>
59 #include <qlibraryinfo.h>
60 #include <QUuid>
61
62 QT_BEGIN_NAMESPACE
63
64 #define COMMAND_VERSION                 Doc::alias("version")
65 int HtmlGenerator::id = 0;
66 bool HtmlGenerator::debugging_on = false;
67
68 QString HtmlGenerator::divNavTop = "";
69
70 static bool showBrokenLinks = false;
71
72 static QRegExp linkTag("(<@link node=\"([^\"]+)\">).*(</@link>)");
73 static QRegExp funcTag("(<@func target=\"([^\"]*)\">)(.*)(</@func>)");
74 static QRegExp typeTag("(<@(type|headerfile|func)(?: +[^>]*)?>)(.*)(</@\\2>)");
75 static QRegExp spanTag("</@(?:comment|preprocessor|string|char|number|op|type|name|keyword)>");
76 static QRegExp unknownTag("</?@[^>]*>");
77
78 static void addLink(const QString &linkTarget,
79                     const QStringRef &nestedStuff,
80                     QString *res)
81 {
82     if (!linkTarget.isEmpty()) {
83         *res += "<a href=\"";
84         *res += linkTarget;
85         *res += "\">";
86         *res += nestedStuff;
87         *res += "</a>";
88     }
89     else {
90         *res += nestedStuff;
91     }
92 }
93
94
95 HtmlGenerator::HtmlGenerator()
96     : helpProjectWriter(0),
97       inLink(false),
98       inObsoleteLink(false),
99       inContents(false),
100       inSectionHeading(false),
101       inTableHeader(false),
102       numTableRows(0),
103       threeColumnEnumValueTable(true),
104       funcLeftParen("\\S(\\()"),
105       myTree(0),
106       obsoleteLinks(false)
107 {
108 }
109
110 HtmlGenerator::~HtmlGenerator()
111 {
112     if (helpProjectWriter)
113         delete helpProjectWriter;
114 }
115
116 void HtmlGenerator::initializeGenerator(const Config &config)
117 {
118     static const struct {
119         const char *key;
120         const char *left;
121         const char *right;
122     } defaults[] = {
123         { ATOM_FORMATTING_BOLD, "<b>", "</b>" },
124         { ATOM_FORMATTING_INDEX, "<!--", "-->" },
125         { ATOM_FORMATTING_ITALIC, "<i>", "</i>" },
126         { ATOM_FORMATTING_PARAMETER, "<i>", "</i>" },
127         { ATOM_FORMATTING_SUBSCRIPT, "<sub>", "</sub>" },
128         { ATOM_FORMATTING_SUPERSCRIPT, "<sup>", "</sup>" },
129         { ATOM_FORMATTING_TELETYPE, "<tt>", "</tt>" },
130         { ATOM_FORMATTING_UNDERLINE, "<u>", "</u>" },
131         { 0, 0, 0 }
132     };
133
134     Generator::initializeGenerator(config);
135     obsoleteLinks = config.getBool(QLatin1String(CONFIG_OBSOLETELINKS));
136     setImageFileExtensions(QStringList() << "png" << "jpg" << "jpeg" << "gif");
137     int i = 0;
138     while (defaults[i].key) {
139         formattingLeftMap().insert(defaults[i].key, defaults[i].left);
140         formattingRightMap().insert(defaults[i].key, defaults[i].right);
141         i++;
142     }
143
144     style = config.getString(HtmlGenerator::format() +
145                              Config::dot +
146                              CONFIG_STYLE);
147     endHeader = config.getString(HtmlGenerator::format() +
148                                  Config::dot +
149                                  CONFIG_ENDHEADER);
150     postHeader = config.getString(HtmlGenerator::format() +
151                                   Config::dot +
152                                   HTMLGENERATOR_POSTHEADER);
153     postPostHeader = config.getString(HtmlGenerator::format() +
154                                       Config::dot +
155                                       HTMLGENERATOR_POSTPOSTHEADER);
156     footer = config.getString(HtmlGenerator::format() +
157                               Config::dot +
158                               HTMLGENERATOR_FOOTER);
159     address = config.getString(HtmlGenerator::format() +
160                                Config::dot +
161                                HTMLGENERATOR_ADDRESS);
162     pleaseGenerateMacRef = config.getBool(HtmlGenerator::format() +
163                                           Config::dot +
164                                           HTMLGENERATOR_GENERATEMACREFS);
165     noBreadCrumbs = config.getBool(HtmlGenerator::format() +
166                                    Config::dot +
167                                    HTMLGENERATOR_NOBREADCRUMBS);
168
169     project = config.getString(CONFIG_PROJECT);
170
171     projectDescription = config.getString(CONFIG_DESCRIPTION);
172     if (projectDescription.isEmpty() && !project.isEmpty())
173         projectDescription = project + " Reference Documentation";
174
175     projectUrl = config.getString(CONFIG_URL);
176
177     outputEncoding = config.getString(CONFIG_OUTPUTENCODING);
178     if (outputEncoding.isEmpty())
179         outputEncoding = QLatin1String("ISO-8859-1");
180     outputCodec = QTextCodec::codecForName(outputEncoding.toLocal8Bit());
181
182     naturalLanguage = config.getString(CONFIG_NATURALLANGUAGE);
183     if (naturalLanguage.isEmpty())
184         naturalLanguage = QLatin1String("en");
185
186     QSet<QString> editionNames = config.subVars(CONFIG_EDITION);
187     QSet<QString>::ConstIterator edition = editionNames.begin();
188     while (edition != editionNames.end()) {
189         QString editionName = *edition;
190         QStringList editionModules = config.getStringList(CONFIG_EDITION +
191                                                           Config::dot +
192                                                           editionName +
193                                                           Config::dot +
194                                                           "modules");
195         QStringList editionGroups = config.getStringList(CONFIG_EDITION +
196                                                          Config::dot +
197                                                          editionName +
198                                                          Config::dot +
199                                                          "groups");
200
201         if (!editionModules.isEmpty())
202             editionModuleMap[editionName] = editionModules;
203         if (!editionGroups.isEmpty())
204             editionGroupMap[editionName] = editionGroups;
205
206         ++edition;
207     }
208
209     codeIndent = config.getInt(CONFIG_CODEINDENT);
210
211     helpProjectWriter = new HelpProjectWriter(config,
212                                               project.toLower() +
213                                               ".qhp");
214
215     // Documentation template handling
216     headerScripts = config.getString(HtmlGenerator::format() + Config::dot +
217                                    CONFIG_HEADERSCRIPTS);
218     headerStyles = config.getString(HtmlGenerator::format() +
219                                        Config::dot +
220                                        CONFIG_HEADERSTYLES);
221     
222     QString prefix = CONFIG_QHP + Config::dot + "Qt" + Config::dot;
223     manifestDir = "qthelp://" + config.getString(prefix + "namespace");
224     manifestDir += "/" + config.getString(prefix + "virtualFolder") + "/";
225 }
226
227 void HtmlGenerator::terminateGenerator()
228 {
229     Generator::terminateGenerator();
230 }
231
232 QString HtmlGenerator::format()
233 {
234     return "HTML";
235 }
236
237 /*!
238   This is where the HTML files are written.
239   \note The HTML file generation is done in the base class,
240   PageGenerator::generateTree().
241  */
242 void HtmlGenerator::generateTree(const Tree *tree)
243 {
244     myTree = tree;
245     nonCompatClasses.clear();
246     mainClasses.clear();
247     compatClasses.clear();
248     obsoleteClasses.clear();
249     moduleClassMap.clear();
250     moduleNamespaceMap.clear();
251     funcIndex.clear();
252     legaleseTexts.clear();
253     serviceClasses.clear();
254     qmlClasses.clear();
255     findAllClasses(tree->root());
256     findAllFunctions(tree->root());
257     findAllLegaleseTexts(tree->root());
258     findAllNamespaces(tree->root());
259     findAllSince(tree->root());
260
261     PageGenerator::generateTree(tree);
262
263     QString fileBase = project.toLower().simplified().replace(" ", "-");
264     generateIndex(fileBase, projectUrl, projectDescription);
265     generatePageIndex(outputDir() + "/" + fileBase + ".pageindex");
266
267     helpProjectWriter->generate(myTree);
268     generateManifestFiles();
269 }
270
271 void HtmlGenerator::startText(const Node * /* relative */,
272                               CodeMarker * /* marker */)
273 {
274     inLink = false;
275     inContents = false;
276     inSectionHeading = false;
277     inTableHeader = false;
278     numTableRows = 0;
279     threeColumnEnumValueTable = true;
280     link.clear();
281     sectionNumber.clear();
282 }
283
284 /*!
285   Generate html from an instance of Atom.
286  */
287 int HtmlGenerator::generateAtom(const Atom *atom,
288                                 const Node *relative,
289                                 CodeMarker *marker)
290 {
291     int skipAhead = 0;
292     static bool in_para = false;
293
294     switch (atom->type()) {
295     case Atom::AbstractLeft:
296         if (relative)
297             relative->doc().location().warning(tr("\abstract is not implemented."));
298         else
299             Location::information(tr("\abstract is not implemented."));
300         break;
301     case Atom::AbstractRight:
302         break;
303     case Atom::AutoLink:
304         if (!inLink && !inContents && !inSectionHeading) {
305             const Node *node = 0;
306             QString link = getLink(atom, relative, marker, &node);
307             if (!link.isEmpty()) {
308                 beginLink(link, node, relative, marker);
309                 generateLink(atom, relative, marker);
310                 endLink();
311             }
312             else {
313                 out() << protectEnc(atom->string());
314             }
315         }
316         else {
317             out() << protectEnc(atom->string());
318         }
319         break;
320     case Atom::BaseName:
321         break;
322     case Atom::BriefLeft:
323         if (relative->type() == Node::Fake) {
324             if (relative->subType() != Node::Example) {
325                 skipAhead = skipAtoms(atom, Atom::BriefRight);
326                 break;
327             }
328         }
329
330         out() << "<p>";
331         if (relative->type() == Node::Property ||
332             relative->type() == Node::Variable) {
333             QString str;
334             atom = atom->next();
335             while (atom != 0 && atom->type() != Atom::BriefRight) {
336                 if (atom->type() == Atom::String ||
337                     atom->type() == Atom::AutoLink)
338                     str += atom->string();
339                 skipAhead++;
340                 atom = atom->next();
341             }
342             str[0] = str[0].toLower();
343             if (str.right(1) == ".")
344                 str.truncate(str.length() - 1);
345             out() << "This ";
346             if (relative->type() == Node::Property)
347                 out() << "property";
348             else
349                 out() << "variable";
350             QStringList words = str.split(" ");
351             if (!(words.first() == "contains" || words.first() == "specifies"
352                 || words.first() == "describes" || words.first() == "defines"
353                 || words.first() == "holds" || words.first() == "determines"))
354                 out() << " holds ";
355             else
356                 out() << " ";
357             out() << str << ".";
358         }
359         break;
360     case Atom::BriefRight:
361         if (relative->type() != Node::Fake)
362             out() << "</p>\n";
363         break;
364     case Atom::C:
365         // This may at one time have been used to mark up C++ code but it is
366         // now widely used to write teletype text. As a result, text marked
367         // with the \c command is not passed to a code marker.
368         out() << formattingLeftMap()[ATOM_FORMATTING_TELETYPE];
369         if (inLink) {
370             out() << protectEnc(plainCode(atom->string()));
371         }
372         else {
373             out() << protectEnc(plainCode(atom->string()));
374         }
375         out() << formattingRightMap()[ATOM_FORMATTING_TELETYPE];
376         break;
377     case Atom::CaptionLeft:
378         out() << "<p class=\"figCaption\">";
379         in_para = true;
380         break;
381     case Atom::CaptionRight:
382         endLink();
383         if (in_para) {
384             out() << "</p>\n";
385             in_para = false;
386         }
387         break;
388     case Atom::Code:
389         out() << "<pre class=\"cpp\">"
390               << trimmedTrailing(highlightedCode(indent(codeIndent,atom->string()),
391                                                  marker,relative))
392               << "</pre>\n";
393         break;
394 #ifdef QDOC_QML
395     case Atom::Qml:
396         out() << "<pre class=\"qml\">"
397               << trimmedTrailing(highlightedCode(indent(codeIndent,atom->string()),
398                                                  marker,relative))
399               << "</pre>\n";
400         break;
401     case Atom::JavaScript:
402         out() << "<pre class=\"js\">"
403               << trimmedTrailing(highlightedCode(indent(codeIndent,atom->string()),
404                                                  marker,relative))
405               << "</pre>\n";
406         break;
407 #endif
408     case Atom::CodeNew:
409         out() << "<p>you can rewrite it as</p>\n"
410               << "<pre class=\"cpp\">"
411               << trimmedTrailing(highlightedCode(indent(codeIndent,atom->string()),
412                                                  marker,relative))
413               << "</pre>\n";
414         break;
415     case Atom::CodeOld:
416         out() << "<p>For example, if you have code like</p>\n";
417         // fallthrough
418     case Atom::CodeBad:
419         out() << "<pre class=\"cpp\">"
420               << trimmedTrailing(protectEnc(plainCode(indent(codeIndent,atom->string()))))
421               << "</pre>\n";
422         break;
423     case Atom::DivLeft:
424         out() << "<div";
425         if (!atom->string().isEmpty())
426             out() << " " << atom->string();
427         out() << ">";
428         break;
429     case Atom::DivRight:
430         out() << "</div>";
431         break;
432     case Atom::FootnoteLeft:
433         // ### For now
434         if (in_para) {
435             out() << "</p>\n";
436             in_para = false;
437         }
438         out() << "<!-- ";
439         break;
440     case Atom::FootnoteRight:
441         // ### For now
442         out() << "-->";
443         break;
444     case Atom::FormatElse:
445     case Atom::FormatEndif:
446     case Atom::FormatIf:
447         break;
448     case Atom::FormattingLeft:
449         if (atom->string().startsWith("span ")) {
450             out() << "<" + atom->string() << ">";
451         }
452         else
453             out() << formattingLeftMap()[atom->string()];
454         if (atom->string() == ATOM_FORMATTING_PARAMETER) {
455             if (atom->next() != 0 && atom->next()->type() == Atom::String) {
456                 QRegExp subscriptRegExp("([a-z]+)_([0-9n])");
457                 if (subscriptRegExp.exactMatch(atom->next()->string())) {
458                     out() << subscriptRegExp.cap(1) << "<sub>"
459                           << subscriptRegExp.cap(2) << "</sub>";
460                     skipAhead = 1;
461                 }
462             }
463         }
464         break;
465     case Atom::FormattingRight:
466         if (atom->string() == ATOM_FORMATTING_LINK) {
467             endLink();
468         }
469         else if (atom->string().startsWith("span ")) {
470             out() << "</span>";
471         }
472         else {
473             out() << formattingRightMap()[atom->string()];
474         }
475         break;
476     case Atom::AnnotatedList:
477         {
478             QList<Node*> values = myTree->groups().values(atom->string());
479             NodeMap nodeMap;
480             for (int i = 0; i < values.size(); ++i) {
481                 const Node* n = values.at(i);
482                 if ((n->status() != Node::Internal) && (n->access() != Node::Private)) {
483                     nodeMap.insert(n->nameForLists(),n);
484                 }
485             }
486             generateAnnotatedList(relative, marker, nodeMap);
487         }
488         break;
489     case Atom::GeneratedList:
490         if (atom->string() == "annotatedclasses") {
491             generateAnnotatedList(relative, marker, nonCompatClasses);
492         }
493         else if (atom->string() == "classes") {
494             generateCompactList(relative, marker, nonCompatClasses, true);
495         }
496         else if (atom->string() == "qmlclasses") {
497             generateCompactList(relative, marker, qmlClasses, true);
498         }
499         else if (atom->string().contains("classesbymodule")) {
500             QString arg = atom->string().trimmed();
501             QString moduleName = atom->string().mid(atom->string().indexOf(
502                 "classesbymodule") + 15).trimmed();
503             if (moduleClassMap.contains(moduleName))
504                 generateAnnotatedList(relative, marker, moduleClassMap[moduleName]);
505         }
506         else if (atom->string().contains("classesbyedition")) {
507
508             QString arg = atom->string().trimmed();
509             QString editionName = atom->string().mid(atom->string().indexOf(
510                 "classesbyedition") + 16).trimmed();
511
512             if (editionModuleMap.contains(editionName)) {
513
514                 // Add all classes in the modules listed for that edition.
515                 NodeMap editionClasses;
516                 foreach (const QString &moduleName, editionModuleMap[editionName]) {
517                     if (moduleClassMap.contains(moduleName))
518                         editionClasses.unite(moduleClassMap[moduleName]);
519                 }
520
521                 // Add additional groups and remove groups of classes that
522                 // should be excluded from the edition.
523
524                 QMultiMap <QString, Node *> groups = myTree->groups();
525                 foreach (const QString &groupName, editionGroupMap[editionName]) {
526                     QList<Node *> groupClasses;
527                     if (groupName.startsWith("-")) {
528                         groupClasses = groups.values(groupName.mid(1));
529                         foreach (const Node *node, groupClasses)
530                             editionClasses.remove(node->name());
531                     }
532                     else {
533                         groupClasses = groups.values(groupName);
534                         foreach (const Node *node, groupClasses)
535                             editionClasses.insert(node->name(), node);
536                     }
537                 }
538                 generateAnnotatedList(relative, marker, editionClasses);
539             }
540         }
541         else if (atom->string() == "classhierarchy") {
542             generateClassHierarchy(relative, marker, nonCompatClasses);
543         }
544         else if (atom->string() == "compatclasses") {
545             generateCompactList(relative, marker, compatClasses, false);
546         }
547         else if (atom->string() == "obsoleteclasses") {
548             generateCompactList(relative, marker, obsoleteClasses, false);
549         }
550         else if (atom->string() == "functionindex") {
551             generateFunctionIndex(relative, marker);
552         }
553         else if (atom->string() == "legalese") {
554             generateLegaleseList(relative, marker);
555         }
556         else if (atom->string() == "mainclasses") {
557             generateCompactList(relative, marker, mainClasses, true);
558         }
559         else if (atom->string() == "services") {
560             generateCompactList(relative, marker, serviceClasses, false);
561         }
562         else if (atom->string() == "overviews") {
563             generateOverviewList(relative, marker);
564         }
565         else if (atom->string() == "namespaces") {
566             generateAnnotatedList(relative, marker, namespaceIndex);
567         }
568         else if (atom->string() == "related") {
569             const FakeNode *fake = static_cast<const FakeNode *>(relative);
570             if (fake && !fake->groupMembers().isEmpty()) {
571                 NodeMap groupMembersMap;
572                 foreach (const Node *node, fake->groupMembers()) {
573                     if (node->type() == Node::Fake)
574                         groupMembersMap[fullName(node, relative, marker)] = node;
575                 }
576                 generateAnnotatedList(fake, marker, groupMembersMap);
577             }
578         }
579         else if (atom->string() == "relatedinline") {
580             const FakeNode *fake = static_cast<const FakeNode *>(relative);
581             if (fake && !fake->groupMembers().isEmpty()) {
582                 // Reverse the list into the original scan order.
583                 // Should be sorted.  But on what?  It may not be a
584                 // regular class or page definition.
585                 QList<const Node *> list;
586                 foreach (const Node *node, fake->groupMembers())
587                     list.prepend(node);
588                 foreach (const Node *node, list)
589                     generateBody(node, marker);
590             }
591         }
592         break;
593     case Atom::SinceList:
594         {
595             NewSinceMaps::const_iterator nsmap;
596             nsmap = newSinceMaps.find(atom->string());
597             NewClassMaps::const_iterator ncmap;
598             ncmap = newClassMaps.find(atom->string());
599             NewClassMaps::const_iterator nqcmap;
600             nqcmap = newQmlClassMaps.find(atom->string());
601
602             if ((nsmap != newSinceMaps.constEnd()) && !nsmap.value().isEmpty()) {
603                 QList<Section> sections;
604                 QList<Section>::ConstIterator s;
605
606                 for (int i=0; i<LastSinceType; ++i)
607                     sections.append(Section(sinceTitle(i),QString(),QString(),QString()));
608
609                 NodeMultiMap::const_iterator n = nsmap.value().constBegin();
610
611                 while (n != nsmap.value().constEnd()) {
612
613                     const Node* node = n.value();
614                     switch (node->type()) {
615                       case Node::Fake:
616                           if (node->subType() == Node::QmlClass) {
617                               sections[QmlClass].appendMember((Node*)node);
618                           }
619                           break;
620                       case Node::Namespace:
621                           sections[Namespace].appendMember((Node*)node);
622                           break;
623                       case Node::Class: 
624                           sections[Class].appendMember((Node*)node);
625                           break;
626                       case Node::Enum: 
627                           sections[Enum].appendMember((Node*)node);
628                           break;
629                       case Node::Typedef: 
630                           sections[Typedef].appendMember((Node*)node);
631                           break;
632                       case Node::Function: {
633                           const FunctionNode* fn = static_cast<const FunctionNode*>(node);
634                           if (fn->isMacro())
635                               sections[Macro].appendMember((Node*)node);
636                           else {
637                               Node* p = fn->parent();
638                               if (p) {
639                                   if (p->type() == Node::Class)
640                                       sections[MemberFunction].appendMember((Node*)node);
641                                   else if (p->type() == Node::Namespace) {
642                                       if (p->name().isEmpty())
643                                           sections[GlobalFunction].appendMember((Node*)node);
644                                       else
645                                           sections[NamespaceFunction].appendMember((Node*)node);
646                                   }
647                                   else
648                                       sections[GlobalFunction].appendMember((Node*)node);
649                               }
650                               else
651                                   sections[GlobalFunction].appendMember((Node*)node);
652                           }
653                           break;
654                       }
655                       case Node::Property:
656                           sections[Property].appendMember((Node*)node);
657                           break;
658                       case Node::Variable: 
659                           sections[Variable].appendMember((Node*)node);
660                           break;
661                       case Node::QmlProperty:
662                           sections[QmlProperty].appendMember((Node*)node);
663                           break;
664                       case Node::QmlSignal:
665                           sections[QmlSignal].appendMember((Node*)node);
666                           break;
667                       case Node::QmlMethod:
668                           sections[QmlMethod].appendMember((Node*)node);
669                           break;
670                       default:
671                           break;
672                     }
673                     ++n;
674                 }
675
676                 /*
677                   First generate the table of contents.
678                  */
679                 out() << "<ul>\n";
680                 s = sections.constBegin();
681                 while (s != sections.constEnd()) {
682                     if (!(*s).members.isEmpty()) {
683
684                         out() << "<li>"
685                               << "<a href=\"#"
686                               << Doc::canonicalTitle((*s).name)
687                               << "\">"
688                               << (*s).name
689                               << "</a></li>\n";
690                     }
691                     ++s;
692                 }
693                 out() << "</ul>\n";
694
695                 int idx = 0;
696                 s = sections.constBegin();
697                 while (s != sections.constEnd()) {
698                     if (!(*s).members.isEmpty()) {
699                         out() << "<a name=\""
700                               << Doc::canonicalTitle((*s).name)
701                               << "\"></a>\n";
702                         out() << "<h3>" << protectEnc((*s).name) << "</h3>\n";
703                         if (idx == Class)
704                             generateCompactList(0, marker, ncmap.value(), false, QString("Q"));
705                         else if (idx == QmlClass)
706                             generateCompactList(0, marker, nqcmap.value(), false, QString("Q"));
707                         else if (idx == MemberFunction) {
708                             ParentMaps parentmaps;
709                             ParentMaps::iterator pmap;
710                             NodeList::const_iterator i = s->members.constBegin();
711                             while (i != s->members.constEnd()) {
712                                 Node* p = (*i)->parent();
713                                 pmap = parentmaps.find(p);
714                                 if (pmap == parentmaps.end())
715                                     pmap = parentmaps.insert(p,NodeMultiMap());
716                                 pmap->insert((*i)->name(),(*i));
717                                 ++i;
718                             }
719                             pmap = parentmaps.begin();
720                             while (pmap != parentmaps.end()) {
721                                 NodeList nlist = pmap->values();
722                                 out() << "<p>Class ";
723
724                                 out() << "<a href=\""
725                                       << linkForNode(pmap.key(), 0)
726                                       << "\">";
727                                 QStringList pieces = fullName(pmap.key(), 0, marker).split("::");
728                                 out() << protectEnc(pieces.last());
729                                 out() << "</a>"  << ":</p>\n";
730
731                                 generateSection(nlist, 0, marker, CodeMarker::Summary);
732                                 out() << "<br/>";
733                                 ++pmap;
734                             }
735                         }
736                         else
737                             generateSection(s->members, 0, marker, CodeMarker::Summary);
738                      }
739                     ++idx;
740                     ++s;
741                 }
742             }
743         }
744         break;
745     case Atom::Image:
746     case Atom::InlineImage:
747         {
748             QString fileName = imageFileName(relative, atom->string());
749             QString text;
750             if (atom->next() != 0)
751                 text = atom->next()->string();
752             if (atom->type() == Atom::Image)
753                 out() << "<p class=\"centerAlign\">";
754             if (fileName.isEmpty()) {
755                 out() << "<font color=\"red\">[Missing image "
756                       << protectEnc(atom->string()) << "]</font>";
757             }
758             else {
759                 out() << "<img src=\"" << protectEnc(fileName) << "\"";
760                 if (!text.isEmpty())
761                     out() << " alt=\"" << protectEnc(text) << "\"";
762                 out() << " />";
763                 helpProjectWriter->addExtraFile(fileName);
764                 if ((relative->type() == Node::Fake) &&
765                     (relative->subType() == Node::Example)) {
766                     const ExampleNode* cen = static_cast<const ExampleNode*>(relative);
767                     if (cen->imageFileName().isEmpty()) {
768                         ExampleNode* en = const_cast<ExampleNode*>(cen);
769                         en->setImageFileName(fileName);
770                         ExampleNode::exampleNodeMap.insert(en->title(),en);
771                     }
772                 }
773             }
774             if (atom->type() == Atom::Image)
775                 out() << "</p>";
776         }
777         break;
778     case Atom::ImageText:
779         break;
780     case Atom::LegaleseLeft:
781         out() << "<div class=\"LegaleseLeft\">";
782         break;
783     case Atom::LegaleseRight:
784         out() << "</div>";
785         break;
786     case Atom::LineBreak:
787         out() << "<br/>";
788         break;
789     case Atom::Link:
790         {
791             const Node *node = 0;
792             QString myLink = getLink(atom, relative, marker, &node);
793             if (myLink.isEmpty()) {
794                 relative->doc().location().warning(tr("Cannot link to '%1' in %2")
795                         .arg(atom->string())
796                         .arg(marker->plainFullName(relative)));
797             }
798             beginLink(myLink, node, relative, marker);
799             skipAhead = 1;
800         }
801         break;
802     case Atom::LinkNode:
803         {
804             const Node *node = CodeMarker::nodeForString(atom->string());
805             beginLink(linkForNode(node, relative), node, relative, marker);
806             skipAhead = 1;
807         }
808         break;
809     case Atom::ListLeft:
810         if (in_para) {
811             out() << "</p>\n";
812             in_para = false;
813         }
814         if (atom->string() == ATOM_LIST_BULLET) {
815             out() << "<ul>\n";
816         }
817         else if (atom->string() == ATOM_LIST_TAG) {
818             out() << "<dl>\n";
819         }
820         else if (atom->string() == ATOM_LIST_VALUE) {
821             threeColumnEnumValueTable = isThreeColumnEnumValueTable(atom);
822             if (threeColumnEnumValueTable) {
823                 out() << "<table class=\"valuelist\">";
824                 if (++numTableRows % 2 == 1)
825                         out() << "<tr valign=\"top\" class=\"odd\">";
826                 else
827                         out() << "<tr valign=\"top\" class=\"even\">";
828
829                 out() << "<th class=\"tblConst\">Constant</th>"
830                       << "<th class=\"tblval\">Value</th>"
831                       << "<th class=\"tbldscr\">Description</th></tr>\n";
832             }
833             else {
834                 out() << "<table class=\"valuelist\">"
835                       << "<tr><th class=\"tblConst\">Constant</th><th class=\"tblVal\">Value</th></tr>\n";
836             }
837         }
838         else {
839             out() << "<ol class=";
840             if (atom->string() == ATOM_LIST_UPPERALPHA) {
841                 out() << "\"A\"";
842             } /* why type? changed to */
843             else if (atom->string() == ATOM_LIST_LOWERALPHA) {
844                 out() << "\"a\"";
845             }
846             else if (atom->string() == ATOM_LIST_UPPERROMAN) {
847                 out() << "\"I\"";
848             }
849             else if (atom->string() == ATOM_LIST_LOWERROMAN) {
850                 out() << "\"i\"";
851             }
852             else { // (atom->string() == ATOM_LIST_NUMERIC)
853                 out() << "\"1\"";
854             }
855             if (atom->next() != 0 && atom->next()->string().toInt() != 1)
856                 out() << " start=\"" << atom->next()->string() << "\"";
857             out() << ">\n";
858         }
859         break;
860     case Atom::ListItemNumber:
861         break;
862     case Atom::ListTagLeft:
863         if (atom->string() == ATOM_LIST_TAG) {
864             out() << "<dt>";
865         }
866         else { // (atom->string() == ATOM_LIST_VALUE)
867             // ### Trenton
868
869             out() << "<tr><td class=\"topAlign\"><tt>"
870                   << protectEnc(plainCode(marker->markedUpEnumValue(atom->next()->string(),
871                                                                  relative)))
872                   << "</tt></td><td class=\"topAlign\">";
873
874             QString itemValue;
875             if (relative->type() == Node::Enum) {
876                 const EnumNode *enume = static_cast<const EnumNode *>(relative);
877                 itemValue = enume->itemValue(atom->next()->string());
878             }
879
880             if (itemValue.isEmpty())
881                 out() << "?";
882             else
883                 out() << "<tt>" << protectEnc(itemValue) << "</tt>";
884
885             skipAhead = 1;
886         }
887         break;
888     case Atom::ListTagRight:
889         if (atom->string() == ATOM_LIST_TAG)
890             out() << "</dt>\n";
891         break;
892     case Atom::ListItemLeft:
893         if (atom->string() == ATOM_LIST_TAG) {
894             out() << "<dd>";
895         }
896         else if (atom->string() == ATOM_LIST_VALUE) {
897             if (threeColumnEnumValueTable) {
898                 out() << "</td><td class=\"topAlign\">";
899                 if (matchAhead(atom, Atom::ListItemRight))
900                     out() << "&nbsp;";
901             }
902         }
903         else {
904             out() << "<li>";
905         }
906         if (matchAhead(atom, Atom::ParaLeft))
907             skipAhead = 1;
908         break;
909     case Atom::ListItemRight:
910         if (atom->string() == ATOM_LIST_TAG) {
911             out() << "</dd>\n";
912         }
913         else if (atom->string() == ATOM_LIST_VALUE) {
914             out() << "</td></tr>\n";
915         }
916         else {
917             out() << "</li>\n";
918         }
919         break;
920     case Atom::ListRight:
921         if (atom->string() == ATOM_LIST_BULLET) {
922             out() << "</ul>\n";
923         }
924         else if (atom->string() == ATOM_LIST_TAG) {
925             out() << "</dl>\n";
926         }
927         else if (atom->string() == ATOM_LIST_VALUE) {
928             out() << "</table>\n";
929         }
930         else {
931             out() << "</ol>\n";
932         }
933         break;
934     case Atom::Nop:
935         break;
936     case Atom::ParaLeft:
937         out() << "<p>";
938         in_para = true;
939         break;
940     case Atom::ParaRight:
941         endLink();
942         if (in_para) {
943             out() << "</p>\n";
944             in_para = false;
945         }
946         //if (!matchAhead(atom, Atom::ListItemRight) && !matchAhead(atom, Atom::TableItemRight))
947         //    out() << "</p>\n";
948         break;
949     case Atom::QuotationLeft:
950         out() << "<blockquote>";
951         break;
952     case Atom::QuotationRight:
953         out() << "</blockquote>\n";
954         break;
955     case Atom::RawString:
956         out() << atom->string();
957         break;
958     case Atom::SectionLeft:
959         out() << "<a name=\"" << Doc::canonicalTitle(Text::sectionHeading(atom).toString())
960               << "\"></a>" << divNavTop << "\n";
961         break;
962     case Atom::SectionRight:
963         break;
964     case Atom::SectionHeadingLeft:
965         out() << "<h" + QString::number(atom->string().toInt() + hOffset(relative)) + ">";
966         inSectionHeading = true;
967         break;
968     case Atom::SectionHeadingRight:
969         out() << "</h" + QString::number(atom->string().toInt() + hOffset(relative)) + ">\n";
970         inSectionHeading = false;
971         break;
972     case Atom::SidebarLeft:
973         break;
974     case Atom::SidebarRight:
975         break;
976     case Atom::String:
977         if (inLink && !inContents && !inSectionHeading) {
978             generateLink(atom, relative, marker);
979         }
980         else {
981             out() << protectEnc(atom->string());
982         }
983         break;
984     case Atom::TableLeft:
985         if (in_para) {
986             out() << "</p>\n";
987             in_para = false;
988         }
989         if (!atom->string().isEmpty()) {
990             if (atom->string().contains("%")) {
991                 out() << "<table class=\"generic\" width=\""
992                       << atom->string() << "\">\n ";
993             }
994             else {
995                 out() << "<table class=\"generic\">\n";
996             }
997         }
998         else {
999             out() << "<table class=\"generic\">\n";
1000         }
1001         numTableRows = 0;
1002         break;
1003     case Atom::TableRight:
1004         out() << "</table>\n";
1005         break;
1006     case Atom::TableHeaderLeft:
1007         out() << "<thead><tr class=\"qt-style\">";
1008         inTableHeader = true;
1009         break;
1010     case Atom::TableHeaderRight:
1011         out() << "</tr>";
1012         if (matchAhead(atom, Atom::TableHeaderLeft)) {
1013             skipAhead = 1;
1014             out() << "\n<tr class=\"qt-style\">";
1015         }
1016         else {
1017             out() << "</thead>\n";
1018             inTableHeader = false;
1019         }
1020         break;
1021     case Atom::TableRowLeft:
1022         if (!atom->string().isEmpty())
1023             out() << "<tr " << atom->string() << ">";
1024         else if (++numTableRows % 2 == 1)
1025             out() << "<tr valign=\"top\" class=\"odd\">";
1026         else
1027             out() << "<tr valign=\"top\" class=\"even\">";
1028         break;
1029     case Atom::TableRowRight:
1030         out() << "</tr>\n";
1031         break;
1032     case Atom::TableItemLeft:
1033         {
1034             if (inTableHeader)
1035                 out() << "<th ";
1036             else
1037                 out() << "<td ";
1038
1039             for (int i=0; i<atom->count(); ++i) {
1040                 if (i > 0)
1041                     out() << " ";
1042                 QString p = atom->string(i);
1043                 if (p.contains('=')) {
1044                     out() << p;
1045                 }
1046                 else {
1047                     QStringList spans = p.split(",");
1048                     if (spans.size() == 2) {
1049                         if (spans.at(0) != "1")
1050                             out() << " colspan=\"" << spans.at(0) << "\"";
1051                         if (spans.at(1) != "1")
1052                             out() << " rowspan=\"" << spans.at(1) << "\"";
1053                     }
1054                 }
1055             }
1056             if (inTableHeader)
1057                 out() << ">";
1058             else {
1059                 out() << ">"; 
1060                 //out() << "><p>"; 
1061             }
1062             if (matchAhead(atom, Atom::ParaLeft))
1063                 skipAhead = 1;
1064         }
1065         break;
1066     case Atom::TableItemRight:
1067         if (inTableHeader)
1068             out() << "</th>";
1069         else {
1070             out() << "</td>";
1071             //out() << "</p></td>";
1072         }
1073         if (matchAhead(atom, Atom::ParaLeft))
1074             skipAhead = 1;
1075         break;
1076     case Atom::TableOfContents:
1077         break;
1078     case Atom::Target:
1079         out() << "<a name=\"" << Doc::canonicalTitle(atom->string()) << "\"></a>";
1080         break;
1081     case Atom::UnhandledFormat:
1082         out() << "<b class=\"redFont\">&lt;Missing HTML&gt;</b>";
1083         break;
1084     case Atom::UnknownCommand:
1085         out() << "<b class=\"redFont\"><code>\\" << protectEnc(atom->string())
1086               << "</code></b>";
1087         break;
1088 #ifdef QDOC_QML
1089     case Atom::QmlText:
1090     case Atom::EndQmlText:
1091         // don't do anything with these. They are just tags.
1092         break;
1093 #endif
1094     default:
1095         unknownAtom(atom);
1096     }
1097     return skipAhead;
1098 }
1099
1100 /*!
1101   Generate a reference page for a C++ class.
1102  */
1103 void HtmlGenerator::generateClassLikeNode(const InnerNode *inner,
1104                                           CodeMarker *marker)
1105 {
1106     QList<Section> sections;
1107     QList<Section>::ConstIterator s;
1108
1109     const ClassNode *classe = 0;
1110     const NamespaceNode *namespasse = 0;
1111
1112     QString title;
1113     QString rawTitle;
1114     QString fullTitle;
1115     if (inner->type() == Node::Namespace) {
1116         namespasse = static_cast<const NamespaceNode *>(inner);
1117         rawTitle = marker->plainName(inner);
1118         fullTitle = marker->plainFullName(inner);
1119         title = rawTitle + " Namespace";
1120     }
1121     else if (inner->type() == Node::Class) {
1122         classe = static_cast<const ClassNode *>(inner);
1123         rawTitle = marker->plainName(inner);
1124         fullTitle = marker->plainFullName(inner);
1125         title = rawTitle + " Class Reference";
1126     }
1127
1128     Text subtitleText;
1129     if (rawTitle != fullTitle)
1130         subtitleText << "(" << Atom(Atom::AutoLink, fullTitle) << ")"
1131                      << Atom(Atom::LineBreak);
1132
1133     generateHeader(title, inner, marker);
1134     sections = marker->sections(inner, CodeMarker::Summary, CodeMarker::Okay);
1135     generateTableOfContents(inner,marker,&sections);
1136     generateTitle(title, subtitleText, SmallSubTitle, inner, marker);    
1137     generateBrief(inner, marker);
1138     generateIncludes(inner, marker);
1139     generateStatus(inner, marker);
1140     if (classe) {
1141         generateInherits(classe, marker);
1142         generateInheritedBy(classe, marker);
1143 #ifdef QDOC_QML
1144         if (!classe->qmlElement().isEmpty()) {
1145             generateInstantiatedBy(classe,marker);
1146         }
1147 #endif
1148     }
1149     generateThreadSafeness(inner, marker);
1150     generateSince(inner, marker);
1151
1152     out() << "<ul>\n";
1153
1154     QString membersLink = generateListOfAllMemberFile(inner, marker);
1155     if (!membersLink.isEmpty())
1156         out() << "<li><a href=\"" << membersLink << "\">"
1157               << "List of all members, including inherited members</a></li>\n";
1158
1159     QString obsoleteLink = generateLowStatusMemberFile(inner,
1160                                                        marker,
1161                                                        CodeMarker::Obsolete);
1162     if (!obsoleteLink.isEmpty())
1163         out() << "<li><a href=\"" << obsoleteLink << "\">"
1164               << "Obsolete members</a></li>\n";
1165
1166     QString compatLink = generateLowStatusMemberFile(inner,
1167                                                      marker,
1168                                                      CodeMarker::Compat);
1169     if (!compatLink.isEmpty())
1170         out() << "<li><a href=\"" << compatLink << "\">"
1171               << "Qt 3 support members</a></li>\n";
1172
1173     out() << "</ul>\n";
1174
1175     bool needOtherSection = false;
1176
1177     /*
1178       sections is built above for the call to generateTableOfContents().
1179      */
1180     s = sections.begin();
1181     while (s != sections.end()) {
1182         if (s->members.isEmpty() && s->reimpMembers.isEmpty()) {
1183             if (!s->inherited.isEmpty())
1184                 needOtherSection = true;
1185         }
1186         else {
1187             if (!s->members.isEmpty()) {
1188                // out() << "<hr />\n";
1189                 out() << "<a name=\""
1190                       << registerRef((*s).name.toLower())
1191                       << "\"></a>" << divNavTop << "\n";
1192                 out() << "<h2>" << protectEnc((*s).name) << "</h2>\n";
1193                 generateSection(s->members, inner, marker, CodeMarker::Summary);
1194             }
1195             if (!s->reimpMembers.isEmpty()) {
1196                 QString name = QString("Reimplemented ") + (*s).name;
1197               //  out() << "<hr />\n";
1198                 out() << "<a name=\""
1199                       << registerRef(name.toLower())
1200                       << "\"></a>" << divNavTop << "\n";
1201                 out() << "<h2>" << protectEnc(name) << "</h2>\n";
1202                 generateSection(s->reimpMembers, inner, marker, CodeMarker::Summary);
1203             }
1204
1205             if (!s->inherited.isEmpty()) {
1206                 out() << "<ul>\n";
1207                 generateSectionInheritedList(*s, inner, marker);
1208                 out() << "</ul>\n";
1209             }
1210         }
1211         ++s;
1212     }
1213
1214     if (needOtherSection) {
1215         out() << "<h3>Additional Inherited Members</h3>\n"
1216                  "<ul>\n";
1217
1218         s = sections.begin();
1219         while (s != sections.end()) {
1220             if (s->members.isEmpty() && !s->inherited.isEmpty())
1221                 generateSectionInheritedList(*s, inner, marker);
1222             ++s;
1223         }
1224         out() << "</ul>\n";
1225     }
1226
1227     out() << "<a name=\"" << registerRef("details") << "\"></a>" << divNavTop << "\n";
1228
1229     if (!inner->doc().isEmpty()) {
1230         generateExtractionMark(inner, DetailedDescriptionMark);
1231         //out() << "<hr />\n"
1232         out() << "<div class=\"descr\">\n" // QTBUG-9504
1233               << "<h2>" << "Detailed Description" << "</h2>\n";
1234         generateBody(inner, marker);
1235         out() << "</div>\n"; // QTBUG-9504
1236         generateAlsoList(inner, marker);
1237         generateMaintainerList(inner, marker);
1238         generateExtractionMark(inner, EndMark);
1239     }
1240
1241     sections = marker->sections(inner, CodeMarker::Detailed, CodeMarker::Okay);
1242     s = sections.begin();
1243     while (s != sections.end()) {
1244         //out() << "<hr />\n";
1245         if (!(*s).divClass.isEmpty())
1246             out() << "<div class=\"" << (*s).divClass << "\">\n"; // QTBUG-9504
1247         out() << "<h2>" << protectEnc((*s).name) << "</h2>\n";
1248
1249         NodeList::ConstIterator m = (*s).members.begin();
1250         while (m != (*s).members.end()) {
1251             if ((*m)->access() != Node::Private) { // ### check necessary?
1252                 if ((*m)->type() != Node::Class)
1253                     generateDetailedMember(*m, inner, marker);
1254                 else {
1255                     out() << "<h3> class ";
1256                     generateFullName(*m, inner, marker);
1257                     out() << "</h3>";
1258                     generateBrief(*m, marker, inner);
1259                 }
1260
1261                 QStringList names;
1262                 names << (*m)->name();
1263                 if ((*m)->type() == Node::Function) {
1264                     const FunctionNode *func = reinterpret_cast<const FunctionNode *>(*m);
1265                     if (func->metaness() == FunctionNode::Ctor ||
1266                         func->metaness() == FunctionNode::Dtor ||
1267                         func->overloadNumber() != 1)
1268                         names.clear();
1269                 }
1270                 else if ((*m)->type() == Node::Property) {
1271                     const PropertyNode *prop = reinterpret_cast<const PropertyNode *>(*m);
1272                     if (!prop->getters().isEmpty() &&
1273                         !names.contains(prop->getters().first()->name()))
1274                         names << prop->getters().first()->name();
1275                     if (!prop->setters().isEmpty())
1276                         names << prop->setters().first()->name();
1277                     if (!prop->resetters().isEmpty())
1278                         names << prop->resetters().first()->name();
1279                 }
1280                 else if ((*m)->type() == Node::Enum) {
1281                     const EnumNode *enume = reinterpret_cast<const EnumNode*>(*m);
1282                     if (enume->flagsType())
1283                         names << enume->flagsType()->name();
1284
1285                     foreach (const QString &enumName,
1286                              enume->doc().enumItemNames().toSet() -
1287                              enume->doc().omitEnumItemNames().toSet())
1288                         names << plainCode(marker->markedUpEnumValue(enumName,
1289                                                                      enume));
1290                 }
1291             }
1292             ++m;
1293         }
1294         if (!(*s).divClass.isEmpty())
1295             out() << "</div>\n"; // QTBUG-9504
1296         ++s;
1297     }
1298     generateFooter(inner);
1299 }
1300
1301 /*!
1302   Generate the HTML page for a qdoc file that doesn't map
1303   to an underlying C++ file.
1304  */
1305 void HtmlGenerator::generateFakeNode(const FakeNode *fake, CodeMarker *marker)
1306 {
1307     SubTitleSize subTitleSize = LargeSubTitle;
1308
1309     QList<Section> sections;
1310     QList<Section>::const_iterator s;
1311
1312     QString fullTitle = fake->fullTitle();
1313     QString htmlTitle = fullTitle;
1314     if (fake->subType() == Node::File && !fake->subTitle().isEmpty()) {
1315         subTitleSize = SmallSubTitle;
1316         htmlTitle += " (" + fake->subTitle() + ")";
1317     }
1318     else if (fake->subType() == Node::QmlBasicType) {
1319         fullTitle = "QML Basic Type: " + fullTitle;
1320         htmlTitle = fullTitle;
1321
1322         // Replace the marker with a QML code marker.
1323         marker = CodeMarker::markerForLanguage(QLatin1String("QML"));
1324     }
1325
1326     generateHeader(htmlTitle, fake, marker);
1327         
1328     /*
1329       Generate the TOC for the new doc format.
1330       Don't generate a TOC for the home page.
1331     */
1332     const QmlClassNode* qml_cn = 0;
1333     if (fake->subType() == Node::QmlClass) {
1334         qml_cn = static_cast<const QmlClassNode*>(fake);
1335         sections = marker->qmlSections(qml_cn,CodeMarker::Summary,0);
1336         generateTableOfContents(fake,marker,&sections);
1337
1338         // Replace the marker with a QML code marker.
1339         marker = CodeMarker::markerForLanguage(QLatin1String("QML"));
1340     }
1341     else if (fake->name() != QString("index.html"))
1342         generateTableOfContents(fake,marker,0);
1343
1344     generateTitle(fullTitle,
1345                   Text() << fake->subTitle(),
1346                   subTitleSize,
1347                   fake,
1348                   marker);
1349
1350     if (fake->subType() == Node::Module) {
1351         // Generate brief text and status for modules.
1352         generateBrief(fake, marker);
1353         generateStatus(fake, marker);
1354         generateSince(fake, marker);
1355
1356         if (moduleNamespaceMap.contains(fake->name())) {
1357             out() << "<a name=\"" << registerRef("namespaces") << "\"></a>" << divNavTop << "\n";
1358             out() << "<h2>Namespaces</h2>\n";
1359             generateAnnotatedList(fake, marker, moduleNamespaceMap[fake->name()]);
1360         }
1361         if (moduleClassMap.contains(fake->name())) {
1362             out() << "<a name=\"" << registerRef("classes") << "\"></a>" << divNavTop << "\n";
1363             out() << "<h2>Classes</h2>\n";
1364             generateAnnotatedList(fake, marker, moduleClassMap[fake->name()]);
1365         }
1366     }
1367     else if (fake->subType() == Node::HeaderFile) {
1368         // Generate brief text and status for modules.
1369         generateBrief(fake, marker);
1370         generateStatus(fake, marker);
1371         generateSince(fake, marker);
1372
1373         out() << "<ul>\n";
1374
1375         QString membersLink = generateListOfAllMemberFile(fake, marker);
1376         if (!membersLink.isEmpty())
1377             out() << "<li><a href=\"" << membersLink << "\">"
1378                   << "List of all members, including inherited members</a></li>\n";
1379
1380         QString obsoleteLink = generateLowStatusMemberFile(fake,
1381                                                            marker,
1382                                                            CodeMarker::Obsolete);
1383         if (!obsoleteLink.isEmpty())
1384             out() << "<li><a href=\"" << obsoleteLink << "\">"
1385                   << "Obsolete members</a></li>\n";
1386
1387         QString compatLink = generateLowStatusMemberFile(fake,
1388                                                          marker,
1389                                                          CodeMarker::Compat);
1390         if (!compatLink.isEmpty())
1391             out() << "<li><a href=\"" << compatLink << "\">"
1392                   << "Qt 3 support members</a></li>\n";
1393
1394         out() << "</ul>\n";
1395     }
1396 #ifdef QDOC_QML
1397     else if (fake->subType() == Node::QmlClass) {
1398         const ClassNode* cn = qml_cn->classNode();
1399         generateBrief(qml_cn, marker);
1400         generateQmlInherits(qml_cn, marker);
1401         generateQmlInheritedBy(qml_cn, marker);
1402         generateQmlInstantiates(qml_cn, marker);
1403         generateSince(qml_cn, marker);
1404
1405         QString allQmlMembersLink = generateAllQmlMembersFile(qml_cn, marker);
1406         if (!allQmlMembersLink.isEmpty()) {
1407             out() << "<ul>\n";
1408             out() << "<li><a href=\"" << allQmlMembersLink << "\">"
1409                   << "List of all members, including inherited members</a></li>\n";
1410             out() << "</ul>\n";
1411         }
1412
1413         s = sections.begin();
1414         while (s != sections.end()) {
1415             out() << "<a name=\"" << registerRef((*s).name.toLower())
1416                   << "\"></a>" << divNavTop << "\n";
1417             out() << "<h2>" << protectEnc((*s).name) << "</h2>\n";
1418             generateQmlSummary(*s,fake,marker);
1419             ++s;
1420         }
1421
1422         generateExtractionMark(fake, DetailedDescriptionMark);
1423         out() << "<a name=\"" << registerRef("details") << "\"></a>" << divNavTop << "\n";
1424         out() << "<h2>" << "Detailed Description" << "</h2>\n";
1425         generateBody(fake, marker);
1426         if (cn)
1427             generateQmlText(cn->doc().body(), cn, marker, fake->name());
1428         generateAlsoList(fake, marker);
1429         generateExtractionMark(fake, EndMark);
1430         //out() << "<hr />\n";
1431
1432         sections = marker->qmlSections(qml_cn,CodeMarker::Detailed,0);
1433         s = sections.begin();
1434         while (s != sections.end()) {
1435             out() << "<h2>" << protectEnc((*s).name) << "</h2>\n";
1436             NodeList::ConstIterator m = (*s).members.begin();
1437             while (m != (*s).members.end()) {
1438                 generateDetailedQmlMember(*m, fake, marker);
1439                 out() << "<br/>\n";
1440                 ++m;
1441             }
1442             ++s;
1443         }
1444         generateFooter(fake);
1445         return;
1446     }
1447 #endif
1448
1449     sections = marker->sections(fake, CodeMarker::Summary, CodeMarker::Okay);
1450     s = sections.begin();
1451     while (s != sections.end()) {
1452         out() << "<a name=\"" << registerRef((*s).name) << "\"></a>" << divNavTop << "\n";
1453         out() << "<h2>" << protectEnc((*s).name) << "</h2>\n";
1454         generateSectionList(*s, fake, marker, CodeMarker::Summary);
1455         ++s;
1456     }
1457
1458     Text brief = fake->doc().briefText();
1459     if (fake->subType() == Node::Module && !brief.isEmpty()) {
1460         generateExtractionMark(fake, DetailedDescriptionMark);
1461         out() << "<a name=\"" << registerRef("details") << "\"></a>" << divNavTop << "\n";
1462         out() << "<div class=\"descr\">\n"; // QTBUG-9504
1463         out() << "<h2>" << "Detailed Description" << "</h2>\n";
1464     }
1465     else {
1466         generateExtractionMark(fake, DetailedDescriptionMark);
1467         out() << "<div class=\"descr\"> <a name=\"" << registerRef("details") << "\"></a>\n"; // QTBUG-9504
1468     }
1469
1470     generateBody(fake, marker);
1471     out() << "</div>\n"; // QTBUG-9504
1472     generateAlsoList(fake, marker);
1473     generateExtractionMark(fake, EndMark);
1474
1475     if (!fake->groupMembers().isEmpty()) {
1476         NodeMap groupMembersMap;
1477         foreach (const Node *node, fake->groupMembers()) {
1478             if (node->type() == Node::Class || node->type() == Node::Namespace)
1479                 groupMembersMap[node->name()] = node;
1480         }
1481         generateAnnotatedList(fake, marker, groupMembersMap);
1482     }
1483
1484     sections = marker->sections(fake, CodeMarker::Detailed, CodeMarker::Okay);
1485     s = sections.begin();
1486     while (s != sections.end()) {
1487         //out() << "<hr />\n";
1488         out() << "<h2>" << protectEnc((*s).name) << "</h2>\n";
1489
1490         NodeList::ConstIterator m = (*s).members.begin();
1491         while (m != (*s).members.end()) {
1492             generateDetailedMember(*m, fake, marker);
1493             ++m;
1494         }
1495         ++s;
1496     }
1497     generateFooter(fake);
1498 }
1499
1500 /*!
1501   Returns "html" for this subclass of Generator.
1502  */
1503 QString HtmlGenerator::fileExtension(const Node * /* node */) const
1504 {
1505     return "html";
1506 }
1507
1508 /*!
1509   Output breadcrumb list in the html file.
1510  */
1511 void HtmlGenerator::generateBreadCrumbs(const QString &title,
1512                                         const Node *node,
1513                                         CodeMarker *marker)
1514 {
1515     if (noBreadCrumbs)
1516         return;
1517     
1518     Text breadcrumbs;
1519     if (node->type() == Node::Class) {
1520         const ClassNode *cn = static_cast<const ClassNode *>(node);
1521         QString name =  node->moduleName();
1522         breadcrumbs << Atom(Atom::ListItemLeft)
1523                     << Atom(Atom::Link, QLatin1String("All Modules"))
1524                     << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK)
1525                     << Atom(Atom::String, QLatin1String("Modules"))
1526                     << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK)
1527                     << Atom(Atom::ListItemRight);
1528         if (!name.isEmpty())
1529             breadcrumbs << Atom(Atom::ListItemLeft)
1530                         << Atom(Atom::AutoLink, name)
1531                         << Atom(Atom::ListItemRight);
1532         if (!cn->name().isEmpty())
1533             breadcrumbs << Atom(Atom::ListItemLeft)
1534                         << Atom(Atom::String, protectEnc(cn->name()))
1535                         << Atom(Atom::ListItemRight);
1536     }
1537     else if (node->type() == Node::Fake) {
1538         const FakeNode* fn = static_cast<const FakeNode*>(node);
1539         if (node->subType() == Node::Module) {
1540             breadcrumbs << Atom(Atom::ListItemLeft)
1541                         << Atom(Atom::Link, QLatin1String("All Modules"))
1542                         << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK)
1543                         << Atom(Atom::String, QLatin1String("Modules"))
1544                         << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK)
1545                         << Atom(Atom::ListItemRight);
1546             QString name =  node->name();
1547             if (!name.isEmpty())
1548                 breadcrumbs << Atom(Atom::ListItemLeft)
1549                             << Atom(Atom::String, protectEnc(name))
1550                             << Atom(Atom::ListItemRight);
1551         }
1552         else if (node->subType() == Node::Group) {
1553             if (fn->name() == QString("modules"))
1554                 breadcrumbs << Atom(Atom::String, QLatin1String("Modules"));
1555             else
1556                 breadcrumbs << Atom(Atom::ListItemLeft)
1557                             << Atom(Atom::String, protectEnc(title))
1558                             << Atom(Atom::ListItemRight);
1559         }
1560         else if (node->subType() == Node::Page) {
1561             if (fn->name() == QString("qdeclarativeexamples.html")) {
1562                 breadcrumbs << Atom(Atom::ListItemLeft)
1563                             << Atom(Atom::Link, QLatin1String("Qt Examples"))
1564                             << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK)
1565                             << Atom(Atom::String, QLatin1String("Examples"))
1566                             << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK)
1567                             << Atom(Atom::ListItemRight);
1568                 breadcrumbs << Atom(Atom::ListItemLeft)
1569                             << Atom(Atom::AutoLink, QLatin1String("QML Examples & Demos"))
1570                             << Atom(Atom::ListItemRight);
1571             }
1572             else if (fn->name().startsWith("examples-")) {
1573                 breadcrumbs << Atom(Atom::ListItemLeft)
1574                             << Atom(Atom::Link, QLatin1String("Qt Examples"))
1575                             << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK)
1576                             << Atom(Atom::String, QLatin1String("Examples"))
1577                             << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK)
1578                             << Atom(Atom::ListItemRight);
1579                 breadcrumbs << Atom(Atom::ListItemLeft)
1580                             << Atom(Atom::String, protectEnc(title))
1581                             << Atom(Atom::ListItemRight);
1582             }
1583             else if (fn->name() == QString("namespaces.html"))
1584                 breadcrumbs << Atom(Atom::String, QLatin1String("Namespaces"));
1585             else
1586                 breadcrumbs << Atom(Atom::ListItemLeft)
1587                             << Atom(Atom::String, protectEnc(title))
1588                             << Atom(Atom::ListItemRight);
1589         }
1590         else if (node->subType() == Node::QmlClass) {
1591                 breadcrumbs << Atom(Atom::ListItemLeft)
1592                             << Atom(Atom::AutoLink, QLatin1String("QML Elements"))
1593                             << Atom(Atom::ListItemRight);
1594                 breadcrumbs << Atom(Atom::ListItemLeft)
1595                             << Atom(Atom::String, protectEnc(title))
1596                             << Atom(Atom::ListItemRight);
1597         }
1598         else if (node->subType() == Node::Example) {
1599             breadcrumbs << Atom(Atom::ListItemLeft)
1600                         << Atom(Atom::Link, QLatin1String("Qt Examples"))
1601                         << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK)
1602                         << Atom(Atom::String, QLatin1String("Examples"))
1603                         << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK)
1604                         << Atom(Atom::ListItemRight);
1605             QStringList sl = fn->name().split('/');
1606             if (sl.contains("declarative"))
1607                 breadcrumbs << Atom(Atom::ListItemLeft)
1608                             << Atom(Atom::AutoLink, QLatin1String("QML Examples & Demos"))
1609                             << Atom(Atom::ListItemRight);
1610             else {
1611                 QString name = protectEnc("examples-" + sl.at(0) + ".html"); // this generates an empty link
1612                 QString t = CodeParser::titleFromName(name);
1613             }
1614             breadcrumbs << Atom(Atom::ListItemLeft)
1615                         << Atom(Atom::String, protectEnc(title))
1616                         << Atom(Atom::ListItemRight);
1617         }
1618     }
1619     else if (node->type() == Node::Namespace) {
1620         breadcrumbs << Atom(Atom::ListItemLeft)
1621                     << Atom(Atom::Link, QLatin1String("All Namespaces"))
1622                     << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK)
1623                     << Atom(Atom::String, QLatin1String("Namespaces"))
1624                     << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK)
1625                     << Atom(Atom::ListItemRight);
1626         breadcrumbs << Atom(Atom::ListItemLeft)
1627                     << Atom(Atom::String, protectEnc(title))
1628                     << Atom(Atom::ListItemRight);
1629     }
1630
1631     generateText(breadcrumbs, node, marker);
1632 }
1633
1634 void HtmlGenerator::generateHeader(const QString& title,
1635                                    const Node *node,
1636                                    CodeMarker *marker)
1637 {
1638     out() << QString("<?xml version=\"1.0\" encoding=\"%1\"?>\n").arg(outputEncoding);
1639     out() << "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\n";
1640     out() << QString("<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"%1\" lang=\"%1\">\n").arg(naturalLanguage);
1641     out() << "<head>\n";
1642     out() << "  <meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\" />\n";
1643     if (node && !node->doc().location().isEmpty())
1644         out() << "<!-- " << node->doc().location().fileName() << " -->\n";
1645
1646     QString shortVersion = myTree->version();
1647     if (shortVersion.count(QChar('.')) == 2)
1648         shortVersion.truncate(shortVersion.lastIndexOf(QChar('.')));
1649     if (!project.isEmpty())
1650         shortVersion = project + QLatin1String(" ") + shortVersion + QLatin1String(": ");
1651     else
1652         shortVersion = QLatin1String("Qt ") + shortVersion + QLatin1String(": ");
1653
1654     // Generating page title
1655     out() << "  <title>" << shortVersion << protectEnc(title) << "</title>\n";
1656
1657     // Include style sheet and script links.
1658     out() << headerStyles;
1659     out() << headerScripts;
1660     out() << endHeader;
1661
1662 #ifdef GENERATE_MAC_REFS    
1663     if (mainPage)
1664         generateMacRef(node, marker);
1665 #endif   
1666  
1667     out() << QString(postHeader).replace("\\" + COMMAND_VERSION, myTree->version());
1668     generateBreadCrumbs(title,node,marker);
1669     out() << QString(postPostHeader).replace("\\" + COMMAND_VERSION, myTree->version());
1670
1671     navigationLinks.clear();
1672
1673     if (node && !node->links().empty()) {
1674         QPair<QString,QString> linkPair;
1675         QPair<QString,QString> anchorPair;
1676         const Node *linkNode;
1677
1678         if (node->links().contains(Node::PreviousLink)) {
1679             linkPair = node->links()[Node::PreviousLink];
1680             linkNode = findNodeForTarget(linkPair.first, node, marker);
1681             if (!linkNode || linkNode == node)
1682                 anchorPair = linkPair;
1683             else
1684                 anchorPair = anchorForNode(linkNode);
1685
1686             out() << "  <link rel=\"prev\" href=\""
1687                   << anchorPair.first << "\" />\n";
1688
1689             navigationLinks += "[Previous: <a href=\"" + anchorPair.first + "\">";
1690             if (linkPair.first == linkPair.second && !anchorPair.second.isEmpty())
1691                 navigationLinks += protect(anchorPair.second);
1692             else
1693                 navigationLinks += protect(linkPair.second);
1694             navigationLinks += "</a>]\n";
1695         }
1696         if (node->links().contains(Node::NextLink)) {
1697             linkPair = node->links()[Node::NextLink];
1698             linkNode = findNodeForTarget(linkPair.first, node, marker);
1699             if (!linkNode || linkNode == node)
1700                 anchorPair = linkPair;
1701             else
1702                 anchorPair = anchorForNode(linkNode);
1703
1704             out() << "  <link rel=\"next\" href=\""
1705                   << anchorPair.first << "\" />\n";
1706
1707             navigationLinks += "[Next: <a href=\"" + anchorPair.first + "\">";
1708             if (linkPair.first == linkPair.second && !anchorPair.second.isEmpty())
1709                 navigationLinks += protect(anchorPair.second);
1710             else
1711                 navigationLinks += protect(linkPair.second);
1712             navigationLinks += "</a>]\n";
1713         }
1714         if (node->links().contains(Node::StartLink)) {
1715             linkPair = node->links()[Node::StartLink];
1716             linkNode = findNodeForTarget(linkPair.first, node, marker);
1717             if (!linkNode || linkNode == node)
1718                 anchorPair = linkPair;
1719             else
1720                 anchorPair = anchorForNode(linkNode);
1721             out() << "  <link rel=\"start\" href=\""
1722                   << anchorPair.first << "\" />\n";
1723         }
1724     }
1725
1726     if (node && !node->links().empty())
1727         out() << "<p class=\"naviNextPrevious headerNavi\">\n" << navigationLinks << "</p><p/>\n";
1728 }
1729
1730 void HtmlGenerator::generateTitle(const QString& title,
1731                                   const Text &subTitle,
1732                                   SubTitleSize subTitleSize,
1733                                   const Node *relative,
1734                                   CodeMarker *marker)
1735 {
1736     if (!title.isEmpty())
1737         out() << "<h1 class=\"title\">" << protectEnc(title) << "</h1>\n";
1738     if (!subTitle.isEmpty()) {
1739         out() << "<span";
1740         if (subTitleSize == SmallSubTitle)
1741             out() << " class=\"small-subtitle\">";
1742         else
1743             out() << " class=\"subtitle\">";
1744         generateText(subTitle, relative, marker);
1745         out() << "</span>\n";
1746     }
1747 }
1748
1749 void HtmlGenerator::generateFooter(const Node *node)
1750 {
1751     if (node && !node->links().empty())
1752         out() << "<p class=\"naviNextPrevious footerNavi\">\n" << navigationLinks << "</p>\n";
1753
1754     out() << QString(footer).replace("\\" + COMMAND_VERSION, myTree->version())
1755           << QString(address).replace("\\" + COMMAND_VERSION, myTree->version());
1756
1757     out() << "</body>\n";
1758     out() << "</html>\n";
1759 }
1760
1761 void HtmlGenerator::generateBrief(const Node *node, CodeMarker *marker,
1762                                   const Node *relative)
1763 {
1764     Text brief = node->doc().briefText();
1765     if (!brief.isEmpty()) {
1766         generateExtractionMark(node, BriefMark);
1767         out() << "<p>";
1768         generateText(brief, node, marker);
1769
1770         if (!relative || node == relative)
1771             out() << " <a href=\"#";
1772         else
1773             out() << " <a href=\"" << linkForNode(node, relative) << "#";
1774         out() << registerRef("details") << "\">More...</a></p>\n";
1775
1776
1777         generateExtractionMark(node, EndMark);
1778     }
1779 }
1780
1781 void HtmlGenerator::generateIncludes(const InnerNode *inner, CodeMarker *marker)
1782 {
1783     if (!inner->includes().isEmpty()) {
1784         out() << "<pre class=\"cpp\">"
1785               << trimmedTrailing(highlightedCode(indent(codeIndent,
1786                                                         marker->markedUpIncludes(inner->includes())),
1787                                                  marker,inner))
1788               << "</pre>";
1789     }
1790 }
1791
1792 /*!
1793   Revised for the new doc format.
1794   Generates a table of contents beginning at \a node.
1795  */
1796 void HtmlGenerator::generateTableOfContents(const Node *node,
1797                                             CodeMarker *marker,
1798                                             QList<Section>* sections)
1799 {
1800     QList<Atom*> toc;
1801     if (node->doc().hasTableOfContents()) 
1802         toc = node->doc().tableOfContents();
1803     if (toc.isEmpty() && !sections && (node->subType() != Node::Module))
1804         return;
1805     
1806     QStringList sectionNumber;
1807     int detailsBase = 0;
1808
1809     // disable nested links in table of contents
1810     inContents = true;
1811     inLink = true;
1812
1813     out() << "<div class=\"toc\">\n";
1814     out() << "<h3><a name=\"toc\">Contents</a></h3>\n";
1815     sectionNumber.append("1");
1816     out() << "<ul>\n";
1817
1818     if (node->subType() == Node::Module) {
1819         if (moduleNamespaceMap.contains(node->name())) {
1820             out() << "<li class=\"level"
1821                   << sectionNumber.size()
1822                   << "\"><a href=\"#"
1823                   << registerRef("namespaces")
1824                   << "\">Namespaces</a></li>\n";
1825         }
1826         if (moduleClassMap.contains(node->name())) {
1827             out() << "<li class=\"level"
1828                   << sectionNumber.size()
1829                   << "\"><a href=\"#"
1830                   << registerRef("classes")
1831                   << "\">Classes</a></li>\n";
1832         }
1833         out() << "<li class=\"level"
1834               << sectionNumber.size()
1835               << "\"><a href=\"#"
1836               << registerRef("details")
1837               << "\">Detailed Description</a></li>\n";
1838         for (int i = 0; i < toc.size(); ++i) {
1839             if (toc.at(i)->string().toInt() == 1) {
1840                 detailsBase = 1;
1841                 break;
1842             }
1843         }
1844     }
1845     else if (sections && ((node->type() == Node::Class) ||
1846                           (node->type() == Node::Namespace) ||
1847                           (node->subType() == Node::QmlClass))) {
1848         QList<Section>::ConstIterator s = sections->begin();
1849         while (s != sections->end()) {
1850             if (!s->members.isEmpty() || !s->reimpMembers.isEmpty()) {
1851                 out() << "<li class=\"level"
1852                       << sectionNumber.size()
1853                       << "\"><a href=\"#"
1854                       << registerRef((*s).pluralMember)
1855                       << "\">" << (*s).name
1856                       << "</a></li>\n";
1857             }
1858             ++s;
1859         }
1860         out() << "<li class=\"level"
1861               << sectionNumber.size()
1862               << "\"><a href=\"#"
1863               << registerRef("details")
1864               << "\">Detailed Description</a></li>\n";
1865         for (int i = 0; i < toc.size(); ++i) {
1866             if (toc.at(i)->string().toInt() == 1) {
1867                 detailsBase = 1;
1868                 break;
1869             }
1870         }
1871     }
1872
1873     for (int i = 0; i < toc.size(); ++i) {
1874         Atom *atom = toc.at(i);
1875         int nextLevel = atom->string().toInt() + detailsBase;
1876         if (sectionNumber.size() < nextLevel) {
1877             do {
1878                 sectionNumber.append("1");
1879             } while (sectionNumber.size() < nextLevel);
1880         }
1881         else {
1882             while (sectionNumber.size() > nextLevel) {
1883                 sectionNumber.removeLast();
1884             }
1885             sectionNumber.last() = QString::number(sectionNumber.last().toInt() + 1);
1886         }
1887         int numAtoms;
1888         Text headingText = Text::sectionHeading(atom);
1889         QString s = headingText.toString();
1890         out() << "<li class=\"level"
1891               << sectionNumber.size()
1892               << "\">";
1893         out() << "<a href=\""
1894               << "#"
1895               << Doc::canonicalTitle(s)
1896               << "\">";
1897         generateAtomList(headingText.firstAtom(), node, marker, true, numAtoms);
1898         out() << "</a></li>\n";
1899     }
1900     while (!sectionNumber.isEmpty()) {
1901         sectionNumber.removeLast();
1902     }
1903     out() << "</ul>\n";
1904     out() << "</div>\n";
1905     inContents = false;
1906     inLink = false;
1907 }
1908
1909 QString HtmlGenerator::generateListOfAllMemberFile(const InnerNode *inner,
1910                                                    CodeMarker *marker)
1911 {
1912     QList<Section> sections;
1913     QList<Section>::ConstIterator s;
1914
1915     sections = marker->sections(inner,
1916                                 CodeMarker::SeparateList,
1917                                 CodeMarker::Okay);
1918     if (sections.isEmpty())
1919         return QString();
1920
1921     QString fileName = fileBase(inner) + "-members." + fileExtension(inner);
1922     beginSubPage(inner->location(), fileName);
1923     QString title = "List of All Members for " + inner->name();
1924     generateHeader(title, inner, marker);
1925     generateTitle(title, Text(), SmallSubTitle, inner, marker);
1926     out() << "<p>This is the complete list of members for ";
1927     generateFullName(inner, 0, marker);
1928     out() << ", including inherited members.</p>\n";
1929
1930     Section section = sections.first();
1931     generateSectionList(section, 0, marker, CodeMarker::SeparateList);
1932
1933     generateFooter();
1934     endSubPage();
1935     return fileName;
1936 }
1937
1938 /*!
1939   This function creates an html page on which are listed all
1940   the members of QML class \a qml_cn, including the inherited
1941   members. The \a marker is used for formatting stuff.
1942  */
1943 QString HtmlGenerator::generateAllQmlMembersFile(const QmlClassNode* qml_cn,
1944                                                  CodeMarker* marker)
1945 {
1946     QList<Section> sections;
1947     QList<Section>::ConstIterator s;
1948
1949     sections = marker->qmlSections(qml_cn,CodeMarker::SeparateList,myTree);
1950     if (sections.isEmpty())
1951         return QString();
1952
1953     QString fileName = fileBase(qml_cn) + "-members." + fileExtension(qml_cn);
1954     beginSubPage(qml_cn->location(), fileName);
1955     QString title = "List of All Members for " + qml_cn->name();
1956     generateHeader(title, qml_cn, marker);
1957     generateTitle(title, Text(), SmallSubTitle, qml_cn, marker);
1958     out() << "<p>This is the complete list of members for ";
1959     generateFullName(qml_cn, 0, marker);
1960     out() << ", including inherited members.</p>\n";
1961
1962     Section section = sections.first();
1963     generateSectionList(section, 0, marker, CodeMarker::SeparateList);
1964
1965     generateFooter();
1966     endSubPage();
1967     return fileName;
1968 }
1969
1970 QString HtmlGenerator::generateLowStatusMemberFile(const InnerNode *inner,
1971                                                    CodeMarker *marker,
1972                                                    CodeMarker::Status status)
1973 {
1974     QList<Section> sections = marker->sections(inner,
1975                                                CodeMarker::Summary,
1976                                                status);
1977     QMutableListIterator<Section> j(sections);
1978     while (j.hasNext()) {
1979         if (j.next().members.size() == 0)
1980             j.remove();
1981     }
1982     if (sections.isEmpty())
1983         return QString();
1984
1985     int i;
1986
1987     QString title;
1988     QString fileName;
1989
1990     if (status == CodeMarker::Compat) {
1991         title = "Qt 3 Support Members for " + inner->name();
1992         fileName = fileBase(inner) + "-qt3." + fileExtension(inner);
1993     }
1994     else {
1995         title = "Obsolete Members for " + inner->name();
1996         fileName = fileBase(inner) + "-obsolete." + fileExtension(inner);
1997     }
1998
1999     beginSubPage(inner->location(), fileName);
2000     generateHeader(title, inner, marker);
2001     generateTitle(title, Text(), SmallSubTitle, inner, marker);
2002
2003     if (status == CodeMarker::Compat) {
2004         out() << "<p><b>The following class members are part of the "
2005                  "<a href=\"qt3support.html\">Qt 3 support layer</a>.</b> "
2006                  "They are provided to help you port old code to Qt 4. We advise against "
2007                  "using them in new code.</p>\n";
2008     }
2009     else {
2010         out() << "<p><b>The following class members are obsolete.</b> "
2011               << "They are provided to keep old source code working. "
2012               << "We strongly advise against using them in new code.</p>\n";
2013     }
2014
2015     out() << "<p><ul><li><a href=\""
2016           << linkForNode(inner, 0) << "\">"
2017           << protectEnc(inner->name())
2018           << " class reference</a></li></ul></p>\n";
2019
2020     for (i = 0; i < sections.size(); ++i) {
2021         out() << "<h2>" << protectEnc(sections.at(i).name) << "</h2>\n";
2022         generateSectionList(sections.at(i), inner, marker, CodeMarker::Summary);
2023     }
2024
2025     sections = marker->sections(inner, CodeMarker::Detailed, status);
2026     for (i = 0; i < sections.size(); ++i) {
2027         //out() << "<hr />\n";
2028         out() << "<h2>" << protectEnc(sections.at(i).name) << "</h2>\n";
2029
2030         NodeList::ConstIterator m = sections.at(i).members.begin();
2031         while (m != sections.at(i).members.end()) {
2032             if ((*m)->access() != Node::Private)
2033                 generateDetailedMember(*m, inner, marker);
2034             ++m;
2035         }
2036     }
2037
2038     generateFooter();
2039     endSubPage();
2040     return fileName;
2041 }
2042
2043 void HtmlGenerator::generateClassHierarchy(const Node *relative,
2044                                            CodeMarker *marker,
2045                                            const QMap<QString,const Node*> &classMap)
2046 {
2047     if (classMap.isEmpty())
2048         return;
2049
2050     NodeMap topLevel;
2051     NodeMap::ConstIterator c = classMap.begin();
2052     while (c != classMap.end()) {
2053         const ClassNode *classe = static_cast<const ClassNode *>(*c);
2054         if (classe->baseClasses().isEmpty())
2055             topLevel.insert(classe->name(), classe);
2056         ++c;
2057     }
2058
2059     QStack<NodeMap > stack;
2060     stack.push(topLevel);
2061
2062     out() << "<ul>\n";
2063     while (!stack.isEmpty()) {
2064         if (stack.top().isEmpty()) {
2065             stack.pop();
2066             out() << "</ul>\n";
2067         }
2068         else {
2069             const ClassNode *child =
2070                 static_cast<const ClassNode *>(*stack.top().begin());
2071             out() << "<li>";
2072             generateFullName(child, relative, marker);
2073             out() << "</li>\n";
2074             stack.top().erase(stack.top().begin());
2075
2076             NodeMap newTop;
2077             foreach (const RelatedClass &d, child->derivedClasses()) {
2078                 if (d.access != Node::Private && !d.node->doc().isEmpty())
2079                     newTop.insert(d.node->name(), d.node);
2080             }
2081             if (!newTop.isEmpty()) {
2082                 stack.push(newTop);
2083                 out() << "<ul>\n";
2084             }
2085         }
2086     }
2087 }
2088
2089 void HtmlGenerator::generateAnnotatedList(const Node *relative,
2090                                           CodeMarker *marker,
2091                                           const NodeMap &nodeMap)
2092 {
2093     out() << "<table class=\"annotated\">\n";
2094
2095     int row = 0;
2096     foreach (const QString &name, nodeMap.keys()) {
2097         const Node *node = nodeMap[name];
2098
2099         if (node->status() == Node::Obsolete)
2100             continue;
2101
2102         if (++row % 2 == 1)
2103             out() << "<tr class=\"odd topAlign\">";
2104         else
2105             out() << "<tr class=\"even topAlign\">";
2106         out() << "<td class=\"tblName\"><p>";
2107         generateFullName(node, relative, marker);
2108         out() << "</p></td>";
2109
2110         if (!(node->type() == Node::Fake)) {
2111             Text brief = node->doc().trimmedBriefText(name);
2112             if (!brief.isEmpty()) {
2113                 out() << "<td class=\"tblDescr\"><p>";
2114                 generateText(brief, node, marker);
2115                 out() << "</p></td>";
2116             }
2117         }
2118         else {
2119             out() << "<td class=\"tblDescr\"><p>";
2120             out() << protectEnc(node->doc().briefText().toString());
2121             out() << "</p></td>";
2122         }
2123         out() << "</tr>\n";
2124     }
2125     out() << "</table>\n";
2126 }
2127
2128 /*!
2129   This function finds the common prefix of the names of all
2130   the classes in \a classMap and then generates a compact
2131   list of the class names alphabetized on the part of the
2132   name not including the common prefix. You can tell the
2133   function to use \a comonPrefix as the common prefix, but
2134   normally you let it figure it out itself by looking at
2135   the name of the first and last classes in \a classMap.
2136  */
2137 void HtmlGenerator::generateCompactList(const Node *relative,
2138                                         CodeMarker *marker,
2139                                         const NodeMap &classMap,
2140                                         bool includeAlphabet,
2141                                         QString commonPrefix)
2142 {
2143     const int NumParagraphs = 37; // '0' to '9', 'A' to 'Z', '_'
2144
2145     if (classMap.isEmpty())
2146         return;
2147
2148     /*
2149       If commonPrefix is not empty, then the caller knows what
2150       the common prefix is and has passed it in, so just use that
2151       one.
2152      */
2153     int commonPrefixLen = commonPrefix.length();
2154     if (commonPrefixLen == 0) {
2155         QString first;
2156         QString last;
2157         
2158         /*
2159           The caller didn't pass in a common prefix, so get the common
2160           prefix by looking at the class names of the first and last
2161           classes in the class map. Discard any namespace names and
2162           just use the bare class names. For Qt, the prefix is "Q".
2163
2164           Note that the algorithm used here to derive the common prefix
2165           from the first and last classes in alphabetical order (QAccel
2166           and QXtWidget in Qt 2.1), fails if either class name does not
2167           begin with Q.
2168         */
2169
2170         NodeMap::const_iterator iter = classMap.begin();
2171         while (iter != classMap.end()) {
2172             if (!iter.key().contains("::")) {
2173                 first = iter.key();
2174                 break;
2175             }
2176             ++iter;
2177         }
2178
2179         if (first.isEmpty())
2180             first = classMap.begin().key();
2181
2182         iter = classMap.end();
2183         while (iter != classMap.begin()) {
2184             --iter;
2185             if (!iter.key().contains("::")) {
2186                 last = iter.key();
2187                 break;
2188             }
2189         }
2190
2191         if (last.isEmpty())
2192             last = classMap.begin().key();
2193
2194         if (classMap.size() > 1) {
2195             while (commonPrefixLen < first.length() + 1 &&
2196                    commonPrefixLen < last.length() + 1 &&
2197                    first[commonPrefixLen] == last[commonPrefixLen])
2198                 ++commonPrefixLen;
2199         }
2200
2201         commonPrefix = first.left(commonPrefixLen);
2202     }
2203
2204     /*
2205       Divide the data into 37 paragraphs: 0, ..., 9, A, ..., Z,
2206       underscore (_). QAccel will fall in paragraph 10 (A) and
2207       QXtWidget in paragraph 33 (X). This is the only place where we
2208       assume that NumParagraphs is 37. Each paragraph is a NodeMap.
2209     */
2210     NodeMap paragraph[NumParagraphs+1];
2211     QString paragraphName[NumParagraphs+1];
2212     QSet<char> usedParagraphNames;
2213
2214     NodeMap::ConstIterator c = classMap.begin();
2215     while (c != classMap.end()) {
2216         QStringList pieces = c.key().split("::");
2217         QString key;
2218         int idx = commonPrefixLen;
2219         if (!pieces.last().startsWith(commonPrefix))
2220             idx = 0;
2221         if (pieces.size() == 1)
2222             key = pieces.last().mid(idx).toLower();
2223         else
2224             key = pieces.last().toLower();
2225
2226         int paragraphNr = NumParagraphs - 1;
2227
2228         if (key[0].digitValue() != -1) {
2229             paragraphNr = key[0].digitValue();
2230         }
2231         else if (key[0] >= QLatin1Char('a') && key[0] <= QLatin1Char('z')) {
2232             paragraphNr = 10 + key[0].unicode() - 'a';
2233         }
2234
2235         paragraphName[paragraphNr] = key[0].toUpper();
2236         usedParagraphNames.insert(key[0].toLower().cell());
2237         paragraph[paragraphNr].insert(key, c.value());
2238         ++c;
2239     }
2240
2241     /*
2242       Each paragraph j has a size: paragraph[j].count(). In the
2243       discussion, we will assume paragraphs 0 to 5 will have sizes
2244       3, 1, 4, 1, 5, 9.
2245
2246       We now want to compute the paragraph offset. Paragraphs 0 to 6
2247       start at offsets 0, 3, 4, 8, 9, 14, 23.
2248     */
2249     int paragraphOffset[NumParagraphs + 1];     // 37 + 1
2250     paragraphOffset[0] = 0;
2251     for (int i=0; i<NumParagraphs; i++)         // i = 0..36
2252         paragraphOffset[i+1] = paragraphOffset[i] + paragraph[i].count();
2253
2254     /*
2255       Output the alphabet as a row of links.
2256      */
2257     if (includeAlphabet) {
2258         out() << "<p  class=\"centerAlign functionIndex\"><b>";
2259         for (int i = 0; i < 26; i++) {
2260             QChar ch('a' + i);
2261             if (usedParagraphNames.contains(char('a' + i)))
2262                 out() << QString("<a href=\"#%1\">%2</a>&nbsp;").arg(ch).arg(ch.toUpper());
2263         }
2264         out() << "</b></p>\n";
2265     }
2266
2267     /*
2268       Output a <div> element to contain all the <dl> elements.
2269      */
2270     out() << "<div class=\"flowListDiv\">\n";
2271     numTableRows = 0;
2272
2273     int curParNr = 0;
2274     int curParOffset = 0;
2275
2276     for (int i=0; i<classMap.count(); i++) {
2277         while ((curParNr < NumParagraphs) &&
2278                (curParOffset == paragraph[curParNr].count())) {
2279             ++curParNr;
2280             curParOffset = 0;
2281         }
2282
2283         /*
2284           Starting a new paragraph means starting a new <dl>.
2285         */
2286         if (curParOffset == 0) {
2287             if (i > 0)
2288                 out() << "</dl>\n";
2289             if (++numTableRows % 2 == 1)
2290                 out() << "<dl class=\"flowList odd\">";
2291             else
2292                 out() << "<dl class=\"flowList even\">";
2293             out() << "<dt class=\"alphaChar\">";
2294             if (includeAlphabet) {
2295                 QChar c = paragraphName[curParNr][0].toLower();
2296                 out() << QString("<a name=\"%1\"></a>").arg(c);
2297             }
2298             out() << "<b>"
2299                   << paragraphName[curParNr]
2300                   << "</b>";
2301             out() << "</dt>\n";
2302         }
2303
2304         /*
2305           Output a <dd> for the current offset in the current paragraph.
2306          */
2307         out() << "<dd>";
2308         if ((curParNr < NumParagraphs) &&
2309             !paragraphName[curParNr].isEmpty()) {
2310             NodeMap::Iterator it;
2311             it = paragraph[curParNr].begin();
2312             for (int i=0; i<curParOffset; i++)
2313                 ++it;
2314
2315             /*
2316               Previously, we used generateFullName() for this, but we
2317               require some special formatting.
2318             */
2319             out() << "<a href=\"" << linkForNode(it.value(), relative) << "\">";
2320             
2321             QStringList pieces;
2322             if (it.value()->subType() == Node::QmlClass)
2323                 pieces << it.value()->name();
2324             else
2325                 pieces = fullName(it.value(), relative, marker).split("::");
2326             out() << protectEnc(pieces.last());
2327             out() << "</a>";
2328             if (pieces.size() > 1) {
2329                 out() << " (";
2330                 generateFullName(it.value()->parent(), relative, marker);
2331                 out() << ")";
2332             }
2333         }
2334         out() << "</dd>\n";
2335         curParOffset++;
2336     }
2337     if (classMap.count() > 0)
2338         out() << "</dl>\n";
2339
2340     out() << "</div>\n";
2341 }
2342
2343 void HtmlGenerator::generateFunctionIndex(const Node *relative,
2344                                           CodeMarker *marker)
2345 {
2346     out() << "<p  class=\"centerAlign functionIndex\"><b>";
2347     for (int i = 0; i < 26; i++) {
2348         QChar ch('a' + i);
2349         out() << QString("<a href=\"#%1\">%2</a>&nbsp;").arg(ch).arg(ch.toUpper());
2350     }
2351     out() << "</b></p>\n";
2352
2353     char nextLetter = 'a';
2354     char currentLetter;
2355
2356 #if 1
2357     out() << "<ul>\n";
2358 #endif
2359     QMap<QString, NodeMap >::ConstIterator f = funcIndex.begin();
2360     while (f != funcIndex.end()) {
2361 #if 1
2362         out() << "<li>";
2363 #else
2364         out() << "<p>";
2365 #endif
2366         out() << protectEnc(f.key()) << ":";
2367
2368         currentLetter = f.key()[0].unicode();
2369         while (islower(currentLetter) && currentLetter >= nextLetter) {
2370             out() << QString("<a name=\"%1\"></a>").arg(nextLetter);
2371             nextLetter++;
2372         }
2373
2374         NodeMap::ConstIterator s = (*f).begin();
2375         while (s != (*f).end()) {
2376             out() << " ";
2377             generateFullName((*s)->parent(), relative, marker, *s);
2378             ++s;
2379         }
2380 #if 1
2381         out() << "</li>";
2382 #else
2383         out() << "</p>";
2384 #endif
2385         out() << "\n";
2386         ++f;
2387     }
2388 #if 1
2389     out() << "</ul>\n";
2390 #endif
2391 }
2392
2393 void HtmlGenerator::generateLegaleseList(const Node *relative,
2394                                          CodeMarker *marker)
2395 {
2396     QMap<Text, const Node *>::ConstIterator it = legaleseTexts.begin();
2397     while (it != legaleseTexts.end()) {
2398         Text text = it.key();
2399         //out() << "<hr />\n";
2400         generateText(text, relative, marker);
2401         out() << "<ul>\n";
2402         do {
2403             out() << "<li>";
2404             generateFullName(it.value(), relative, marker);
2405             out() << "</li>\n";
2406             ++it;
2407         } while (it != legaleseTexts.end() && it.key() == text);
2408         out() << "</ul>\n";
2409     }
2410 }
2411
2412 #ifdef QDOC_QML
2413 void HtmlGenerator::generateQmlItem(const Node *node,
2414                                     const Node *relative,
2415                                     CodeMarker *marker,
2416                                     bool summary)
2417
2418     QString marked = marker->markedUpQmlItem(node,summary);
2419     QRegExp templateTag("(<[^@>]*>)");
2420     if (marked.indexOf(templateTag) != -1) {
2421         QString contents = protectEnc(marked.mid(templateTag.pos(1),
2422                                               templateTag.cap(1).length()));
2423         marked.replace(templateTag.pos(1), templateTag.cap(1).length(),
2424                         contents);
2425     }
2426     marked.replace(QRegExp("<@param>([a-z]+)_([1-9n])</@param>"),
2427                    "<i>\\1<sub>\\2</sub></i>");
2428     marked.replace("<@param>", "<i>");
2429     marked.replace("</@param>", "</i>");
2430
2431     if (summary)
2432         marked.replace("@name>", "b>");
2433
2434     marked.replace("<@extra>", "<tt>");
2435     marked.replace("</@extra>", "</tt>");
2436
2437     if (summary) {
2438         marked.replace("<@type>", "");
2439         marked.replace("</@type>", "");
2440     }
2441     out() << highlightedCode(marked, marker, relative, false, node);
2442 }
2443 #endif
2444
2445 void HtmlGenerator::generateOverviewList(const Node *relative, CodeMarker * /* marker */)
2446 {
2447     QMap<const FakeNode *, QMap<QString, FakeNode *> > fakeNodeMap;
2448     QMap<QString, const FakeNode *> groupTitlesMap;
2449     QMap<QString, FakeNode *> uncategorizedNodeMap;
2450     QRegExp singleDigit("\\b([0-9])\\b");
2451
2452     const NodeList children = myTree->root()->childNodes();
2453     foreach (Node *child, children) {
2454         if (child->type() == Node::Fake && child != relative) {
2455             FakeNode *fakeNode = static_cast<FakeNode *>(child);
2456
2457             // Check whether the page is part of a group or is the group
2458             // definition page.
2459             QString group;
2460             bool isGroupPage = false;
2461             if (fakeNode->doc().metaCommandsUsed().contains("group")) {
2462                 group = fakeNode->doc().metaCommandArgs("group")[0];
2463                 isGroupPage = true;
2464             }
2465
2466             // there are too many examples; they would clutter the list
2467             if (fakeNode->subType() == Node::Example)
2468                 continue;
2469
2470             // not interested either in individual (Qt Designer etc.) manual chapters
2471             if (fakeNode->links().contains(Node::ContentsLink))
2472                 continue;
2473
2474             // Discard external nodes.
2475             if (fakeNode->subType() == Node::ExternalPage)
2476                 continue;
2477
2478             QString sortKey = fakeNode->fullTitle().toLower();
2479             if (sortKey.startsWith("the "))
2480                 sortKey.remove(0, 4);
2481             sortKey.replace(singleDigit, "0\\1");
2482
2483             if (!group.isEmpty()) {
2484                 if (isGroupPage) {
2485                     // If we encounter a group definition page, we add all
2486                     // the pages in that group to the list for that group.
2487                     foreach (Node *member, fakeNode->groupMembers()) {
2488                         if (member->type() != Node::Fake)
2489                             continue;
2490                         FakeNode *page = static_cast<FakeNode *>(member);
2491                         if (page) {
2492                             QString sortKey = page->fullTitle().toLower();
2493                             if (sortKey.startsWith("the "))
2494                                 sortKey.remove(0, 4);
2495                             sortKey.replace(singleDigit, "0\\1");
2496                             fakeNodeMap[const_cast<const FakeNode *>(fakeNode)].insert(sortKey, page);
2497                             groupTitlesMap[fakeNode->fullTitle()] = const_cast<const FakeNode *>(fakeNode);
2498                         }
2499                     }
2500                 }
2501                 else if (!isGroupPage) {
2502                     // If we encounter a page that belongs to a group then
2503                     // we add that page to the list for that group.
2504                     const FakeNode *groupNode = static_cast<const FakeNode *>(myTree->root()->findNode(group, Node::Fake));
2505                     if (groupNode)
2506                         fakeNodeMap[groupNode].insert(sortKey, fakeNode);
2507                     //else
2508                     //    uncategorizedNodeMap.insert(sortKey, fakeNode);
2509                 }// else
2510                 //    uncategorizedNodeMap.insert(sortKey, fakeNode);
2511             }// else
2512             //    uncategorizedNodeMap.insert(sortKey, fakeNode);
2513         }
2514     }
2515
2516     // We now list all the pages found that belong to groups.
2517     // If only certain pages were found for a group, but the definition page
2518     // for that group wasn't listed, the list of pages will be intentionally
2519     // incomplete. However, if the group definition page was listed, all the
2520     // pages in that group are listed for completeness.
2521
2522     if (!fakeNodeMap.isEmpty()) {
2523         foreach (const QString &groupTitle, groupTitlesMap.keys()) {
2524             const FakeNode *groupNode = groupTitlesMap[groupTitle];
2525             out() << QString("<h3><a href=\"%1\">%2</a></h3>\n").arg(
2526                         linkForNode(groupNode, relative)).arg(
2527                         protectEnc(groupNode->fullTitle()));
2528
2529             if (fakeNodeMap[groupNode].count() == 0)
2530                 continue;
2531
2532             out() << "<ul>\n";
2533
2534             foreach (const FakeNode *fakeNode, fakeNodeMap[groupNode]) {
2535                 QString title = fakeNode->fullTitle();
2536                 if (title.startsWith("The "))
2537                     title.remove(0, 4);
2538                 out() << "<li><a href=\"" << linkForNode(fakeNode, relative) << "\">"
2539                       << protectEnc(title) << "</a></li>\n";
2540             }
2541             out() << "</ul>\n";
2542         }
2543     }
2544
2545     if (!uncategorizedNodeMap.isEmpty()) {
2546         out() << QString("<h3>Miscellaneous</h3>\n");
2547         out() << "<ul>\n";
2548         foreach (const FakeNode *fakeNode, uncategorizedNodeMap) {
2549             QString title = fakeNode->fullTitle();
2550             if (title.startsWith("The "))
2551                 title.remove(0, 4);
2552             out() << "<li><a href=\"" << linkForNode(fakeNode, relative) << "\">"
2553                   << protectEnc(title) << "</a></li>\n";
2554         }
2555         out() << "</ul>\n";
2556     }
2557 }
2558
2559 void HtmlGenerator::generateSection(const NodeList& nl,
2560                                     const Node *relative,
2561                                     CodeMarker *marker,
2562                                     CodeMarker::SynopsisStyle style)
2563 {
2564     bool alignNames = true;
2565     if (!nl.isEmpty()) {
2566         bool twoColumn = false;
2567         if (style == CodeMarker::SeparateList) {
2568             alignNames = false;
2569             twoColumn = (nl.count() >= 16);
2570         }
2571         else if (nl.first()->type() == Node::Property) {
2572             twoColumn = (nl.count() >= 5);
2573             alignNames = false;
2574         }
2575         if (alignNames) {
2576             out() << "<table class=\"alignedsummary\">\n";
2577         }
2578         else {
2579             if (twoColumn)
2580                 out() << "<table class=\"propsummary\">\n"
2581                       << "<tr><td class=\"topAlign\">";
2582             out() << "<ul>\n";
2583         }
2584
2585         int i = 0;
2586         NodeList::ConstIterator m = nl.begin();
2587         while (m != nl.end()) {
2588             if ((*m)->access() == Node::Private) {
2589                 ++m;
2590                 continue;
2591             }
2592
2593             if (alignNames) {
2594                 out() << "<tr><td class=\"memItemLeft rightAlign topAlign\"> ";
2595             }
2596             else {
2597                 if (twoColumn && i == (int) (nl.count() + 1) / 2)
2598                     out() << "</ul></td><td class=\"topAlign\"><ul>\n";
2599                 out() << "<li class=\"fn\">";
2600             }
2601
2602             generateSynopsis(*m, relative, marker, style, alignNames);
2603             if (alignNames)
2604                 out() << "</td></tr>\n";
2605             else
2606                 out() << "</li>\n";
2607             i++;
2608             ++m;
2609         }
2610         if (alignNames)
2611             out() << "</table>\n";
2612         else {
2613             out() << "</ul>\n";
2614             if (twoColumn)
2615                 out() << "</td></tr>\n</table>\n";
2616         }
2617     }
2618 }
2619
2620 void HtmlGenerator::generateSectionList(const Section& section,
2621                                         const Node *relative,
2622                                         CodeMarker *marker,
2623                                         CodeMarker::SynopsisStyle style)
2624 {
2625     bool alignNames = true;
2626     if (!section.members.isEmpty()) {
2627         bool twoColumn = false;
2628         if (style == CodeMarker::SeparateList) {
2629             alignNames = false;
2630             twoColumn = (section.members.count() >= 16);
2631         }
2632         else if (section.members.first()->type() == Node::Property) {
2633             twoColumn = (section.members.count() >= 5);
2634             alignNames = false;
2635         }
2636         if (alignNames) {
2637             out() << "<table class=\"alignedsummary\">\n";
2638         }
2639         else {
2640             if (twoColumn)
2641                 out() << "<table class=\"propsummary\">\n"
2642                       << "<tr><td class=\"topAlign\">";
2643             out() << "<ul>\n";
2644         }
2645
2646         int i = 0;
2647         NodeList::ConstIterator m = section.members.begin();
2648         while (m != section.members.end()) {
2649             if ((*m)->access() == Node::Private) {
2650                 ++m;
2651                 continue;
2652             }
2653
2654             if (alignNames) {
2655                 out() << "<tr><td class=\"memItemLeft topAlign rightAlign\"> ";
2656             }
2657             else {
2658                 if (twoColumn && i == (int) (section.members.count() + 1) / 2)
2659                     out() << "</ul></td><td class=\"topAlign\"><ul>\n";
2660                 out() << "<li class=\"fn\">";
2661             }
2662
2663             generateSynopsis(*m, relative, marker, style, alignNames);
2664             if (alignNames)
2665                 out() << "</td></tr>\n";
2666             else
2667                 out() << "</li>\n";
2668             i++;
2669             ++m;
2670         }
2671         if (alignNames)
2672             out() << "</table>\n";
2673         else {
2674             out() << "</ul>\n";
2675             if (twoColumn)
2676                 out() << "</td></tr>\n</table>\n";
2677         }
2678     }
2679
2680     if (style == CodeMarker::Summary && !section.inherited.isEmpty()) {
2681         out() << "<ul>\n";
2682         generateSectionInheritedList(section, relative, marker);
2683         out() << "</ul>\n";
2684     }
2685 }
2686
2687 void HtmlGenerator::generateSectionInheritedList(const Section& section,
2688                                                  const Node *relative,
2689                                                  CodeMarker *marker)
2690 {
2691     QList<QPair<ClassNode *, int> >::ConstIterator p = section.inherited.begin();
2692     while (p != section.inherited.end()) {
2693         out() << "<li class=\"fn\">";
2694         out() << (*p).second << " ";
2695         if ((*p).second == 1) {
2696             out() << section.singularMember;
2697         }
2698         else {
2699             out() << section.pluralMember;
2700         }
2701         out() << " inherited from <a href=\"" << fileName((*p).first)
2702               << "#" << HtmlGenerator::cleanRef(section.name.toLower()) << "\">"
2703               << protectEnc(marker->plainFullName((*p).first, relative))
2704               << "</a></li>\n";
2705         ++p;
2706     }
2707 }
2708
2709 void HtmlGenerator::generateSynopsis(const Node *node,
2710                                      const Node *relative,
2711                                      CodeMarker *marker,
2712                                      CodeMarker::SynopsisStyle style,
2713                                      bool alignNames)
2714 {
2715     QString marked = marker->markedUpSynopsis(node, relative, style);
2716     QRegExp templateTag("(<[^@>]*>)");
2717     if (marked.indexOf(templateTag) != -1) {
2718         QString contents = protectEnc(marked.mid(templateTag.pos(1),
2719                                               templateTag.cap(1).length()));
2720         marked.replace(templateTag.pos(1), templateTag.cap(1).length(),
2721                         contents);
2722     }
2723     marked.replace(QRegExp("<@param>([a-z]+)_([1-9n])</@param>"),
2724                    "<i>\\1<sub>\\2</sub></i>");
2725     marked.replace("<@param>", "<i>");
2726     marked.replace("</@param>", "</i>");
2727
2728     if (style == CodeMarker::Summary) {
2729         marked.replace("<@name>", "");   // was "<b>"
2730         marked.replace("</@name>", "");  // was "</b>"
2731     }
2732
2733     if (style == CodeMarker::SeparateList) {
2734         QRegExp extraRegExp("<@extra>.*</@extra>");
2735         extraRegExp.setMinimal(true);
2736         marked.replace(extraRegExp, "");
2737     } else {
2738         marked.replace("<@extra>", "<tt>");
2739         marked.replace("</@extra>", "</tt>");
2740     }
2741
2742     if (style != CodeMarker::Detailed) {
2743         marked.replace("<@type>", "");
2744         marked.replace("</@type>", "");
2745     }
2746     out() << highlightedCode(marked, marker, relative, alignNames);
2747 }
2748
2749 QString HtmlGenerator::highlightedCode(const QString& markedCode,
2750                                        CodeMarker* marker,
2751                                        const Node* relative,
2752                                        bool alignNames,
2753                                        const Node* self)
2754 {
2755     QString src = markedCode;
2756     QString html;
2757     QStringRef arg;
2758     QStringRef par1;
2759
2760     const QChar charLangle = '<';
2761     const QChar charAt = '@';
2762
2763     static const QString typeTag("type");
2764     static const QString headerTag("headerfile");
2765     static const QString funcTag("func");
2766     static const QString linkTag("link");
2767
2768     // replace all <@link> tags: "(<@link node=\"([^\"]+)\">).*(</@link>)"
2769     bool done = false;
2770     for (int i = 0, srcSize = src.size(); i < srcSize;) {
2771         if (src.at(i) == charLangle && src.at(i + 1) == charAt) {
2772             if (alignNames && !done) {
2773                 html += "</td><td class=\"memItemRight bottomAlign\">";
2774                 done = true;
2775             }
2776             i += 2;
2777             if (parseArg(src, linkTag, &i, srcSize, &arg, &par1)) {
2778                 html += "<b>";
2779                 const Node* n = CodeMarker::nodeForString(par1.toString());
2780                 QString link = linkForNode(n, relative);
2781                 addLink(link, arg, &html);
2782                 html += "</b>";
2783             }
2784             else {
2785                 html += charLangle;
2786                 html += charAt;
2787             }
2788         }
2789         else {
2790             html += src.at(i++);
2791         }
2792     }
2793
2794
2795     // replace all <@func> tags: "(<@func target=\"([^\"]*)\">)(.*)(</@func>)"
2796     src = html;
2797     html = QString();
2798     for (int i = 0, srcSize = src.size(); i < srcSize;) {
2799         if (src.at(i) == charLangle && src.at(i + 1) == charAt) {
2800             i += 2;
2801             if (parseArg(src, funcTag, &i, srcSize, &arg, &par1)) {
2802
2803                 const Node* n = marker->resolveTarget(par1.toString(),
2804                                                       myTree,
2805                                                       relative);
2806                 QString link = linkForNode(n, relative);
2807                 addLink(link, arg, &html);
2808                 par1 = QStringRef();
2809             }
2810             else {
2811                 html += charLangle;
2812                 html += charAt;
2813             }
2814         }
2815         else {
2816             html += src.at(i++);
2817         }
2818     }
2819
2820     // replace all "(<@(type|headerfile|func)(?: +[^>]*)?>)(.*)(</@\\2>)" tags
2821     src = html;
2822     html = QString();
2823
2824     for (int i=0, srcSize=src.size(); i<srcSize;) {
2825         if (src.at(i) == charLangle && src.at(i+1) == charAt) {
2826             i += 2;
2827             bool handled = false;
2828             if (parseArg(src, typeTag, &i, srcSize, &arg, &par1)) {
2829                 par1 = QStringRef();
2830                 const Node* n = marker->resolveTarget(arg.toString(), myTree, relative, self);
2831                 html += QLatin1String("<span class=\"type\">");
2832                 if (n && n->subType() == Node::QmlBasicType) {
2833                     if (relative && relative->subType() == Node::QmlClass)
2834                         addLink(linkForNode(n,relative), arg, &html);
2835                     else 
2836                         html += arg.toString();
2837                 }
2838                 else
2839                     addLink(linkForNode(n,relative), arg, &html);
2840                 html += QLatin1String("</span>");
2841                 handled = true;
2842             }
2843             else if (parseArg(src, headerTag, &i, srcSize, &arg, &par1)) {
2844                 par1 = QStringRef();
2845                 const Node* n = marker->resolveTarget(arg.toString(), myTree, relative);
2846                 addLink(linkForNode(n,relative), arg, &html);
2847                 handled = true;
2848             }
2849             else if (parseArg(src, funcTag, &i, srcSize, &arg, &par1)) {
2850                 par1 = QStringRef();
2851                 const Node* n = marker->resolveTarget(arg.toString(), myTree, relative);
2852                 addLink(linkForNode(n,relative), arg, &html);
2853                 handled = true;
2854             }
2855
2856             if (!handled) {
2857                 html += charLangle;
2858                 html += charAt;
2859             }
2860         }
2861         else {
2862             html += src.at(i++);
2863         }
2864     }
2865
2866     // replace all
2867     // "<@comment>" -> "<span class=\"comment\">";
2868     // "<@preprocessor>" -> "<span class=\"preprocessor\">";
2869     // "<@string>" -> "<span class=\"string\">";
2870     // "<@char>" -> "<span class=\"char\">";
2871     // "<@number>" -> "<span class=\"number\">";
2872     // "<@op>" -> "<span class=\"operator\">";
2873     // "<@type>" -> "<span class=\"type\">";
2874     // "<@name>" -> "<span class=\"name\">";
2875     // "<@keyword>" -> "<span class=\"keyword\">";
2876     // "</@(?:comment|preprocessor|string|char|number|op|type|name|keyword)>" -> "</span>"
2877     src = html;
2878     html = QString();
2879     static const QString spanTags[] = {
2880         "<@comment>",       "<span class=\"comment\">",
2881         "<@preprocessor>",  "<span class=\"preprocessor\">",
2882         "<@string>",        "<span class=\"string\">",
2883         "<@char>",          "<span class=\"char\">",
2884         "<@number>",        "<span class=\"number\">",
2885         "<@op>",            "<span class=\"operator\">",
2886         "<@type>",          "<span class=\"type\">",
2887         "<@name>",          "<span class=\"name\">",
2888         "<@keyword>",       "<span class=\"keyword\">",
2889         "</@comment>",      "</span>",
2890         "</@preprocessor>", "</span>",
2891         "</@string>",       "</span>",
2892         "</@char>",         "</span>",
2893         "</@number>",       "</span>",
2894         "</@op>",           "</span>",
2895         "</@type>",         "</span>",
2896         "</@name>",         "</span>",
2897         "</@keyword>",      "</span>",
2898     };
2899     // Update the upper bound of k in the following code to match the length
2900     // of the above array.
2901     for (int i = 0, n = src.size(); i < n;) {
2902         if (src.at(i) == charLangle) {
2903             bool handled = false;
2904             for (int k = 0; k != 18; ++k) {
2905                 const QString & tag = spanTags[2 * k];
2906                 if (tag == QStringRef(&src, i, tag.length())) {
2907                     html += spanTags[2 * k + 1];
2908                     i += tag.length();
2909                     handled = true;
2910                     break;
2911                 }
2912             }
2913             if (!handled) {
2914                 ++i;
2915                 if (src.at(i) == charAt ||
2916                     (src.at(i) == QLatin1Char('/') && src.at(i + 1) == charAt)) {
2917                     // drop 'our' unknown tags (the ones still containing '@')
2918                     while (i < n && src.at(i) != QLatin1Char('>'))
2919                         ++i;
2920                     ++i;
2921                 }
2922                 else {
2923                     // retain all others
2924                     html += charLangle;
2925                 }
2926             }
2927         }
2928         else {
2929             html += src.at(i);
2930             ++i;
2931         }
2932     }
2933
2934     return html;
2935 }
2936
2937 void HtmlGenerator::generateLink(const Atom* atom,
2938                                  const Node* /* relative */,
2939                                  CodeMarker* marker)
2940 {
2941     static QRegExp camelCase("[A-Z][A-Z][a-z]|[a-z][A-Z0-9]|_");
2942
2943     if (funcLeftParen.indexIn(atom->string()) != -1 && marker->recognizeLanguage("Cpp")) {
2944         // hack for C++: move () outside of link
2945         int k = funcLeftParen.pos(1);
2946         out() << protectEnc(atom->string().left(k));
2947         if (link.isEmpty()) {
2948             if (showBrokenLinks)
2949                 out() << "</i>";
2950         } else {
2951             out() << "</a>";
2952         }
2953         inLink = false;
2954         out() << protectEnc(atom->string().mid(k));
2955     } else {
2956         out() << protectEnc(atom->string());
2957     }
2958 }
2959
2960 QString HtmlGenerator::cleanRef(const QString& ref)
2961 {
2962     QString clean;
2963
2964     if (ref.isEmpty())
2965         return clean;
2966
2967     clean.reserve(ref.size() + 20);
2968     const QChar c = ref[0];
2969     const uint u = c.unicode();
2970
2971     if ((u >= 'a' && u <= 'z') ||
2972          (u >= 'A' && u <= 'Z') ||
2973          (u >= '0' && u <= '9')) {
2974         clean += c;
2975     } else if (u == '~') {
2976         clean += "dtor.";
2977     } else if (u == '_') {
2978         clean += "underscore.";
2979     } else {
2980         clean += "A";
2981     }
2982
2983     for (int i = 1; i < (int) ref.length(); i++) {
2984         const QChar c = ref[i];
2985         const uint u = c.unicode();
2986         if ((u >= 'a' && u <= 'z') ||
2987              (u >= 'A' && u <= 'Z') ||
2988              (u >= '0' && u <= '9') || u == '-' ||
2989              u == '_' || u == ':' || u == '.') {
2990             clean += c;
2991         } else if (c.isSpace()) {
2992             clean += "-";
2993         } else if (u == '!') {
2994             clean += "-not";
2995         } else if (u == '&') {
2996             clean += "-and";
2997         } else if (u == '<') {
2998             clean += "-lt";
2999         } else if (u == '=') {
3000             clean += "-eq";
3001         } else if (u == '>') {
3002             clean += "-gt";
3003         } else if (u == '#') {
3004             clean += "#";
3005         } else {
3006             clean += "-";
3007             clean += QString::number((int)u, 16);
3008         }
3009     }
3010     return clean;
3011 }
3012
3013 QString HtmlGenerator::registerRef(const QString& ref)
3014 {
3015     QString clean = HtmlGenerator::cleanRef(ref);
3016
3017     for (;;) {
3018         QString& prevRef = refMap[clean.toLower()];
3019         if (prevRef.isEmpty()) {
3020             prevRef = ref;
3021             break;
3022         } else if (prevRef == ref) {
3023             break;
3024         }
3025         clean += "x";
3026     }
3027     return clean;
3028 }
3029
3030 QString HtmlGenerator::protectEnc(const QString &string)
3031 {
3032     return protect(string, outputEncoding);
3033 }
3034
3035 QString HtmlGenerator::protect(const QString &string, const QString &outputEncoding)
3036 {
3037 #define APPEND(x) \
3038     if (html.isEmpty()) { \
3039         html = string; \
3040         html.truncate(i); \
3041     } \
3042     html += (x);
3043
3044     QString html;
3045     int n = string.length();
3046
3047     for (int i = 0; i < n; ++i) {
3048         QChar ch = string.at(i);
3049
3050         if (ch == QLatin1Char('&')) {
3051             APPEND("&amp;");
3052         } else if (ch == QLatin1Char('<')) {
3053             APPEND("&lt;");
3054         } else if (ch == QLatin1Char('>')) {
3055             APPEND("&gt;");
3056         } else if (ch == QLatin1Char('"')) {
3057             APPEND("&quot;");
3058         } else if ((outputEncoding == "ISO-8859-1" && ch.unicode() > 0x007F)
3059                    || (ch == QLatin1Char('*') && i + 1 < n && string.at(i) == QLatin1Char('/'))
3060                    || (ch == QLatin1Char('.') && i > 2 && string.at(i - 2) == QLatin1Char('.'))) {
3061             // we escape '*/' and the last dot in 'e.g.' and 'i.e.' for the Javadoc generator
3062             APPEND("&#x");
3063             html += QString::number(ch.unicode(), 16);
3064             html += QLatin1Char(';');
3065         } else {
3066             if (!html.isEmpty())
3067                 html += ch;
3068         }
3069     }
3070
3071     if (!html.isEmpty())
3072         return html;
3073     return string;
3074
3075 #undef APPEND
3076 }
3077
3078 QString HtmlGenerator::fileBase(const Node *node) const
3079 {
3080     QString result;
3081
3082     result = PageGenerator::fileBase(node);
3083
3084     if (!node->isInnerNode()) {
3085         switch (node->status()) {
3086         case Node::Compat:
3087             result += "-qt3";
3088             break;
3089         case Node::Obsolete:
3090             result += "-obsolete";
3091             break;
3092         default:
3093             ;
3094         }
3095     }
3096     return result;
3097 }
3098
3099 QString HtmlGenerator::fileName(const Node *node)
3100 {
3101     if (node->type() == Node::Fake) {
3102         if (static_cast<const FakeNode *>(node)->subType() == Node::ExternalPage)
3103             return node->name();
3104         if (static_cast<const FakeNode *>(node)->subType() == Node::Image)
3105             return node->name();
3106     }
3107     return PageGenerator::fileName(node);
3108 }
3109
3110 QString HtmlGenerator::refForNode(const Node *node)
3111 {
3112     const FunctionNode *func;
3113     const TypedefNode *typedeffe;
3114     QString ref;
3115
3116     switch (node->type()) {
3117     case Node::Namespace:
3118     case Node::Class:
3119     default:
3120         break;
3121     case Node::Enum:
3122         ref = node->name() + "-enum";
3123         break;
3124     case Node::Typedef:
3125         typedeffe = static_cast<const TypedefNode *>(node);
3126         if (typedeffe->associatedEnum()) {
3127             return refForNode(typedeffe->associatedEnum());
3128         }
3129         else {
3130             ref = node->name() + "-typedef";
3131         }
3132         break;
3133     case Node::Function:
3134         func = static_cast<const FunctionNode *>(node);
3135         if (func->associatedProperty()) {
3136             return refForNode(func->associatedProperty());
3137         }
3138         else {
3139             ref = func->name();
3140             if (func->overloadNumber() != 1)
3141                 ref += "-" + QString::number(func->overloadNumber());
3142         }
3143         break;
3144 #ifdef QDOC_QML        
3145     case Node::Fake:
3146         if (node->subType() != Node::QmlPropertyGroup)
3147             break;
3148     case Node::QmlProperty:
3149 #endif        
3150     case Node::Property:
3151         ref = node->name() + "-prop";
3152         break;
3153 #ifdef QDOC_QML
3154     case Node::QmlSignal:
3155         ref = node->name() + "-signal";
3156         break;
3157     case Node::QmlMethod:
3158         ref = node->name() + "-method";
3159         break;
3160 #endif        
3161     case Node::Variable:
3162         ref = node->name() + "-var";
3163         break;
3164     case Node::Target:
3165         return protectEnc(node->name());
3166     }
3167     return registerRef(ref);
3168 }
3169
3170 QString HtmlGenerator::linkForNode(const Node *node, const Node *relative)
3171 {
3172     QString link;
3173     QString fn;
3174     QString ref;
3175
3176     if (node == 0 || node == relative)
3177         return QString();
3178     if (!node->url().isEmpty())
3179         return node->url();
3180     if (fileBase(node).isEmpty())
3181         return QString();
3182     if (node->access() == Node::Private)
3183         return QString();
3184  
3185     fn = fileName(node);
3186 /*    if (!node->url().isEmpty())
3187         return fn;*/
3188
3189     link += fn;
3190
3191     if (!node->isInnerNode() || node->subType() == Node::QmlPropertyGroup) {
3192         ref = refForNode(node);
3193         if (relative && fn == fileName(relative) && ref == refForNode(relative))
3194             return QString();
3195
3196         link += "#";
3197         link += ref;
3198     }
3199     return link;
3200 }
3201
3202 QString HtmlGenerator::refForAtom(Atom *atom, const Node * /* node */)
3203 {
3204     if (atom->type() == Atom::SectionLeft) {
3205         return Doc::canonicalTitle(Text::sectionHeading(atom).toString());
3206     }
3207     else if (atom->type() == Atom::Target) {
3208         return Doc::canonicalTitle(atom->string());
3209     }
3210     else {
3211         return QString();
3212     }
3213 }
3214
3215 void HtmlGenerator::generateFullName(const Node *apparentNode,
3216                                      const Node *relative,
3217                                      CodeMarker *marker,
3218                                      const Node *actualNode)
3219 {
3220     if (actualNode == 0)
3221         actualNode = apparentNode;
3222     out() << "<a href=\"" << linkForNode(actualNode, relative);
3223     if (true || relative == 0 || relative->status() != actualNode->status()) {
3224         switch (actualNode->status()) {
3225         case Node::Obsolete:
3226             out() << "\" class=\"obsolete";
3227             break;
3228         case Node::Compat:
3229             out() << "\" class=\"compat";
3230             break;
3231         default:
3232             ;
3233         }
3234     }
3235     out() << "\">";
3236     out() << protectEnc(fullName(apparentNode, relative, marker));
3237     out() << "</a>";
3238 }
3239
3240 void HtmlGenerator::generateDetailedMember(const Node *node,
3241                                            const InnerNode *relative,
3242                                            CodeMarker *marker)
3243 {
3244     const EnumNode *enume;
3245
3246 #ifdef GENERATE_MAC_REFS    
3247     generateMacRef(node, marker);
3248 #endif