1 /****************************************************************************
2 **
3 ** Copyright (C) 2015 The Qt Company Ltd.
4 ** Contact: http://www.qt.io/licensing/
5 **
6 ** This file is part of the tools applications of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see http://www.qt.io/terms-conditions. For further
15 ** information use the contact form at http://www.qt.io/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 or version 3 as published by the Free
20 ** Software Foundation and appearing in the file LICENSE.LGPLv21 and
21 ** LICENSE.LGPLv3 included in the packaging of this file. Please review the
22 ** following information to ensure the GNU Lesser General Public License
23 ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
24 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25 **
26 ** As a special exception, The Qt Company gives you certain additional
27 ** rights. These rights are described in The Qt Company LGPL Exception
28 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29 **
30 ** GNU General Public License Usage
31 ** Alternatively, this file may be used under the terms of the GNU
32 ** General Public License version 3.0 as published by the Free Software
33 ** Foundation and appearing in the file LICENSE.GPL included in the
34 ** packaging of this file.  Please review the following information to
35 ** ensure the GNU General Public License version 3.0 requirements will be
36 ** met: http://www.gnu.org/copyleft/gpl.html.
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 
addLink(const QString & linkTarget,const QStringRef & nestedStuff,QString * res)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 
HtmlGenerator()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 
~HtmlGenerator()110 HtmlGenerator::~HtmlGenerator()
111 {
112     if (helpProjectWriter)
113         delete helpProjectWriter;
114 }
115 
initializeGenerator(const Config & config)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 
terminateGenerator()227 void HtmlGenerator::terminateGenerator()
228 {
229     Generator::terminateGenerator();
230 }
231 
format()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  */
generateTree(const Tree * tree)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 
startText(const Node *,CodeMarker *)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  */
generateAtom(const Atom * atom,const Node * relative,CodeMarker * marker)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                 else
763                     out() << " alt=\"\"";
764                 out() << " />";
765                 helpProjectWriter->addExtraFile(fileName);
766                 if ((relative->type() == Node::Fake) &&
767                     (relative->subType() == Node::Example)) {
768                     const ExampleNode* cen = static_cast<const ExampleNode*>(relative);
769                     if (cen->imageFileName().isEmpty()) {
770                         ExampleNode* en = const_cast<ExampleNode*>(cen);
771                         en->setImageFileName(fileName);
772                         ExampleNode::exampleNodeMap.insert(en->title(),en);
773                     }
774                 }
775             }
776             if (atom->type() == Atom::Image)
777                 out() << "</p>";
778         }
779         break;
780     case Atom::ImageText:
781         break;
782     case Atom::LegaleseLeft:
783         out() << "<div class=\"LegaleseLeft\">";
784         break;
785     case Atom::LegaleseRight:
786         out() << "</div>";
787         break;
788     case Atom::LineBreak:
789         out() << "<br/>";
790         break;
791     case Atom::Link:
792         {
793             const Node *node = 0;
794             QString myLink = getLink(atom, relative, marker, &node);
795             if (myLink.isEmpty()) {
796                 relative->doc().location().warning(tr("Cannot link to '%1' in %2")
797                         .arg(atom->string())
798                         .arg(marker->plainFullName(relative)));
799             }
800             beginLink(myLink, node, relative, marker);
801             skipAhead = 1;
802         }
803         break;
804     case Atom::LinkNode:
805         {
806             const Node *node = CodeMarker::nodeForString(atom->string());
807             beginLink(linkForNode(node, relative), node, relative, marker);
808             skipAhead = 1;
809         }
810         break;
811     case Atom::ListLeft:
812         if (in_para) {
813             out() << "</p>\n";
814             in_para = false;
815         }
816         if (atom->string() == ATOM_LIST_BULLET) {
817             out() << "<ul>\n";
818         }
819         else if (atom->string() == ATOM_LIST_TAG) {
820             out() << "<dl>\n";
821         }
822         else if (atom->string() == ATOM_LIST_VALUE) {
823             threeColumnEnumValueTable = isThreeColumnEnumValueTable(atom);
824             if (threeColumnEnumValueTable) {
825                 out() << "<table class=\"valuelist\">";
826                 if (++numTableRows % 2 == 1)
827                         out() << "<tr valign=\"top\" class=\"odd\">";
828                 else
829                         out() << "<tr valign=\"top\" class=\"even\">";
830 
831                 out() << "<th class=\"tblConst\">Constant</th>"
832                       << "<th class=\"tblval\">Value</th>"
833                       << "<th class=\"tbldscr\">Description</th></tr>\n";
834             }
835             else {
836                 out() << "<table class=\"valuelist\">"
837                       << "<tr><th class=\"tblConst\">Constant</th><th class=\"tblVal\">Value</th></tr>\n";
838             }
839         }
840         else {
841             out() << "<ol class=";
842             if (atom->string() == ATOM_LIST_UPPERALPHA) {
843                 out() << "\"A\"";
844             } /* why type? changed to */
845             else if (atom->string() == ATOM_LIST_LOWERALPHA) {
846                 out() << "\"a\"";
847             }
848             else if (atom->string() == ATOM_LIST_UPPERROMAN) {
849                 out() << "\"I\"";
850             }
851             else if (atom->string() == ATOM_LIST_LOWERROMAN) {
852                 out() << "\"i\"";
853             }
854             else { // (atom->string() == ATOM_LIST_NUMERIC)
855                 out() << "\"1\"";
856             }
857             if (atom->next() != 0 && atom->next()->string().toInt() != 1)
858                 out() << " start=\"" << atom->next()->string() << "\"";
859             out() << ">\n";
860         }
861         break;
862     case Atom::ListItemNumber:
863         break;
864     case Atom::ListTagLeft:
865         if (atom->string() == ATOM_LIST_TAG) {
866             out() << "<dt>";
867         }
868         else { // (atom->string() == ATOM_LIST_VALUE)
869             // ### Trenton
870 
871             out() << "<tr><td class=\"topAlign\"><tt>"
872                   << protectEnc(plainCode(marker->markedUpEnumValue(atom->next()->string(),
873                                                                  relative)))
874                   << "</tt></td><td class=\"topAlign\">";
875 
876             QString itemValue;
877             if (relative->type() == Node::Enum) {
878                 const EnumNode *enume = static_cast<const EnumNode *>(relative);
879                 itemValue = enume->itemValue(atom->next()->string());
880             }
881 
882             if (itemValue.isEmpty())
883                 out() << "?";
884             else
885                 out() << "<tt>" << protectEnc(itemValue) << "</tt>";
886 
887             skipAhead = 1;
888         }
889         break;
890     case Atom::ListTagRight:
891         if (atom->string() == ATOM_LIST_TAG)
892             out() << "</dt>\n";
893         break;
894     case Atom::ListItemLeft:
895         if (atom->string() == ATOM_LIST_TAG) {
896             out() << "<dd>";
897         }
898         else if (atom->string() == ATOM_LIST_VALUE) {
899             if (threeColumnEnumValueTable) {
900                 out() << "</td><td class=\"topAlign\">";
901                 if (matchAhead(atom, Atom::ListItemRight))
902                     out() << "&nbsp;";
903             }
904         }
905         else {
906             out() << "<li>";
907         }
908         if (matchAhead(atom, Atom::ParaLeft))
909             skipAhead = 1;
910         break;
911     case Atom::ListItemRight:
912         if (atom->string() == ATOM_LIST_TAG) {
913             out() << "</dd>\n";
914         }
915         else if (atom->string() == ATOM_LIST_VALUE) {
916             out() << "</td></tr>\n";
917         }
918         else {
919             out() << "</li>\n";
920         }
921         break;
922     case Atom::ListRight:
923         if (atom->string() == ATOM_LIST_BULLET) {
924             out() << "</ul>\n";
925         }
926         else if (atom->string() == ATOM_LIST_TAG) {
927             out() << "</dl>\n";
928         }
929         else if (atom->string() == ATOM_LIST_VALUE) {
930             out() << "</table>\n";
931         }
932         else {
933             out() << "</ol>\n";
934         }
935         break;
936     case Atom::Nop:
937         break;
938     case Atom::ParaLeft:
939         out() << "<p>";
940         in_para = true;
941         break;
942     case Atom::ParaRight:
943         endLink();
944         if (in_para) {
945             out() << "</p>\n";
946             in_para = false;
947         }
948         //if (!matchAhead(atom, Atom::ListItemRight) && !matchAhead(atom, Atom::TableItemRight))
949         //    out() << "</p>\n";
950         break;
951     case Atom::QuotationLeft:
952         out() << "<blockquote>";
953         break;
954     case Atom::QuotationRight:
955         out() << "</blockquote>\n";
956         break;
957     case Atom::RawString:
958         out() << atom->string();
959         break;
960     case Atom::SectionLeft:
961         out() << "<a name=\"" << Doc::canonicalTitle(Text::sectionHeading(atom).toString())
962               << "\"></a>" << divNavTop << "\n";
963         break;
964     case Atom::SectionRight:
965         break;
966     case Atom::SectionHeadingLeft:
967         out() << "<h" + QString::number(atom->string().toInt() + hOffset(relative)) + ">";
968         inSectionHeading = true;
969         break;
970     case Atom::SectionHeadingRight:
971         out() << "</h" + QString::number(atom->string().toInt() + hOffset(relative)) + ">\n";
972         inSectionHeading = false;
973         break;
974     case Atom::SidebarLeft:
975         break;
976     case Atom::SidebarRight:
977         break;
978     case Atom::String:
979         if (inLink && !inContents && !inSectionHeading) {
980             generateLink(atom, relative, marker);
981         }
982         else {
983             out() << protectEnc(atom->string());
984         }
985         break;
986     case Atom::TableLeft:
987         if (in_para) {
988             out() << "</p>\n";
989             in_para = false;
990         }
991         if (!atom->string().isEmpty()) {
992             if (atom->string().contains("%")) {
993                 out() << "<table class=\"generic\" width=\""
994                       << atom->string() << "\">\n ";
995             }
996             else {
997                 out() << "<table class=\"generic\">\n";
998             }
999         }
1000         else {
1001             out() << "<table class=\"generic\">\n";
1002         }
1003         numTableRows = 0;
1004         break;
1005     case Atom::TableRight:
1006         out() << "</table>\n";
1007         break;
1008     case Atom::TableHeaderLeft:
1009         out() << "<thead><tr class=\"qt-style\">";
1010         inTableHeader = true;
1011         break;
1012     case Atom::TableHeaderRight:
1013         out() << "</tr>";
1014         if (matchAhead(atom, Atom::TableHeaderLeft)) {
1015             skipAhead = 1;
1016             out() << "\n<tr class=\"qt-style\">";
1017         }
1018         else {
1019             out() << "</thead>\n";
1020             inTableHeader = false;
1021         }
1022         break;
1023     case Atom::TableRowLeft:
1024         if (!atom->string().isEmpty())
1025             out() << "<tr " << atom->string() << ">";
1026         else if (++numTableRows % 2 == 1)
1027             out() << "<tr valign=\"top\" class=\"odd\">";
1028         else
1029             out() << "<tr valign=\"top\" class=\"even\">";
1030         break;
1031     case Atom::TableRowRight:
1032         out() << "</tr>\n";
1033         break;
1034     case Atom::TableItemLeft:
1035         {
1036             if (inTableHeader)
1037                 out() << "<th ";
1038             else
1039                 out() << "<td ";
1040 
1041             for (int i=0; i<atom->count(); ++i) {
1042                 if (i > 0)
1043                     out() << " ";
1044                 QString p = atom->string(i);
1045                 if (p.contains('=')) {
1046                     out() << p;
1047                 }
1048                 else {
1049                     QStringList spans = p.split(",");
1050                     if (spans.size() == 2) {
1051                         if (spans.at(0) != "1")
1052                             out() << " colspan=\"" << spans.at(0) << "\"";
1053                         if (spans.at(1) != "1")
1054                             out() << " rowspan=\"" << spans.at(1) << "\"";
1055                     }
1056                 }
1057             }
1058             if (inTableHeader)
1059                 out() << ">";
1060             else {
1061                 out() << ">";
1062                 //out() << "><p>";
1063             }
1064             if (matchAhead(atom, Atom::ParaLeft))
1065                 skipAhead = 1;
1066         }
1067         break;
1068     case Atom::TableItemRight:
1069         if (inTableHeader)
1070             out() << "</th>";
1071         else {
1072             out() << "</td>";
1073             //out() << "</p></td>";
1074         }
1075         if (matchAhead(atom, Atom::ParaLeft))
1076             skipAhead = 1;
1077         break;
1078     case Atom::TableOfContents:
1079         break;
1080     case Atom::Target:
1081         out() << "<a name=\"" << Doc::canonicalTitle(atom->string()) << "\"></a>";
1082         break;
1083     case Atom::UnhandledFormat:
1084         out() << "<b class=\"redFont\">&lt;Missing HTML&gt;</b>";
1085         break;
1086     case Atom::UnknownCommand:
1087         out() << "<b class=\"redFont\"><code>\\" << protectEnc(atom->string())
1088               << "</code></b>";
1089         break;
1090 #ifdef QDOC_QML
1091     case Atom::QmlText:
1092     case Atom::EndQmlText:
1093         // don't do anything with these. They are just tags.
1094         break;
1095 #endif
1096     default:
1097         unknownAtom(atom);
1098     }
1099     return skipAhead;
1100 }
1101 
1102 /*!
1103   Generate a reference page for a C++ class.
1104  */
generateClassLikeNode(const InnerNode * inner,CodeMarker * marker)1105 void HtmlGenerator::generateClassLikeNode(const InnerNode *inner,
1106                                           CodeMarker *marker)
1107 {
1108     QList<Section> sections;
1109     QList<Section>::ConstIterator s;
1110 
1111     const ClassNode *classe = 0;
1112 
1113     QString title;
1114     QString rawTitle;
1115     QString fullTitle;
1116     if (inner->type() == Node::Namespace) {
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  */
generateFakeNode(const FakeNode * fake,CodeMarker * marker)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  */
fileExtension(const Node *) const1503 QString HtmlGenerator::fileExtension(const Node * /* node */) const
1504 {
1505     return "html";
1506 }
1507 
1508 /*!
1509   Output breadcrumb list in the html file.
1510  */
generateBreadCrumbs(const QString & title,const Node * node,CodeMarker * marker)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 
generateHeader(const QString & title,const Node * node,CodeMarker * marker)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 += "<a class=\"prevPage\" 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 += "<a class=\"nextPage\" 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 
generateTitle(const QString & title,const Text & subTitle,SubTitleSize subTitleSize,const Node * relative,CodeMarker * marker)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 
generateFooter(const Node * node)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 
generateBrief(const Node * node,CodeMarker * marker,const Node * relative)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 
generateIncludes(const InnerNode * inner,CodeMarker * marker)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  */
generateTableOfContents(const Node * node,CodeMarker * marker,QList<Section> * sections)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 
generateListOfAllMemberFile(const InnerNode * inner,CodeMarker * marker)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  */
generateAllQmlMembersFile(const QmlClassNode * qml_cn,CodeMarker * marker)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 
generateLowStatusMemberFile(const InnerNode * inner,CodeMarker * marker,CodeMarker::Status status)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 
generateClassHierarchy(const Node * relative,CodeMarker * marker,const QMap<QString,const Node * > & classMap)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 
generateAnnotatedList(const Node * relative,CodeMarker * marker,const NodeMap & nodeMap)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  */
generateCompactList(const Node * relative,CodeMarker * marker,const NodeMap & classMap,bool includeAlphabet,QString commonPrefix)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 
generateFunctionIndex(const Node * relative,CodeMarker * marker)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 
generateLegaleseList(const Node * relative,CodeMarker * marker)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
generateQmlItem(const Node * node,const Node * relative,CodeMarker * marker,bool summary)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 
generateOverviewList(const Node * relative,CodeMarker *)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 
generateSection(const NodeList & nl,const Node * relative,CodeMarker * marker,CodeMarker::SynopsisStyle style)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 
generateSectionList(const Section & section,const Node * relative,CodeMarker * marker,CodeMarker::SynopsisStyle style)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 
generateSectionInheritedList(const Section & section,const Node * relative,CodeMarker * marker)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 
generateSynopsis(const Node * node,const Node * relative,CodeMarker * marker,CodeMarker::SynopsisStyle style,bool alignNames)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 
highlightedCode(const QString & markedCode,CodeMarker * marker,const Node * relative,bool alignNames,const Node * self)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 
generateLink(const Atom * atom,const Node *,CodeMarker * marker)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 
cleanRef(const QString & ref)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 
registerRef(const QString & ref)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 
protectEnc(const QString & string)3030 QString HtmlGenerator::protectEnc(const QString &string)
3031 {
3032     return protect(string, outputEncoding);
3033 }
3034 
protect(const QString & string,const QString & outputEncoding)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 
fileBase(const Node * node) const3078 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 
fileName(const Node * node)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 
refForNode(const Node * node)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 
linkForNode(const Node * node,const Node * relative)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 
refForAtom(Atom * atom,const Node *)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 
generateFullName(const Node * apparentNode,const Node * relative,CodeMarker * marker,const Node * actualNode)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 
generateDetailedMember(const Node * node,const InnerNode * relative,CodeMarker * marker)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
3249     generateExtractionMark(node, MemberMark);
3250     if (node->type() == Node::Enum
3251             && (enume = static_cast<const EnumNode *>(node))->flagsType()) {
3252 #ifdef GENERATE_MAC_REFS
3253         generateMacRef(enume->flagsType(), marker);
3254 #endif
3255         out() << "<h3 class=\"flags\">";
3256         out() << "<a name=\"" + refForNode(node) + "\"></a>";
3257         generateSynopsis(enume, relative, marker, CodeMarker::Detailed);
3258         out() << "<br/>";
3259         generateSynopsis(enume->flagsType(),
3260                          relative,
3261                          marker,
3262                          CodeMarker::Detailed);
3263         out() << "</h3>\n";
3264     }
3265     else {
3266         out() << "<h3 class=\"fn\">";
3267         out() << "<a name=\"" + refForNode(node) + "\"></a>";
3268         generateSynopsis(node, relative, marker, CodeMarker::Detailed);
3269         out() << "</h3>" << divNavTop << "\n";
3270     }
3271 
3272     generateStatus(node, marker);
3273     generateBody(node, marker);
3274     generateThreadSafeness(node, marker);
3275     generateSince(node, marker);
3276 
3277     if (node->type() == Node::Property) {
3278         const PropertyNode *property = static_cast<const PropertyNode *>(node);
3279         Section section;
3280 
3281         section.members += property->getters();
3282         section.members += property->setters();
3283         section.members += property->resetters();
3284 
3285         if (!section.members.isEmpty()) {
3286             out() << "<p><b>Access functions:</b></p>\n";
3287             generateSectionList(section, node, marker, CodeMarker::Accessors);
3288         }
3289 
3290         Section notifiers;
3291         notifiers.members += property->notifiers();
3292 
3293         if (!notifiers.members.isEmpty()) {
3294             out() << "<p><b>Notifier signal:</b></p>\n";
3295             //out() << "<p>This signal is emitted when the property value is changed.</p>\n";
3296             generateSectionList(notifiers, node, marker, CodeMarker::Accessors);
3297         }
3298     }
3299     else if (node->type() == Node::Enum) {
3300         const EnumNode *enume = static_cast<const EnumNode *>(node);
3301         if (enume->flagsType()) {
3302             out() << "<p>The " << protectEnc(enume->flagsType()->name())
3303                   << " type is a typedef for "
3304                   << "<a href=\"qflags.html\">QFlags</a>&lt;"
3305                   << protectEnc(enume->name())
3306                   << "&gt;. It stores an OR combination of "
3307                   << protectEnc(enume->name())
3308                   << " values.</p>\n";
3309         }
3310     }
3311     generateAlsoList(node, marker);
3312     generateExtractionMark(node, EndMark);
3313 }
3314 
findAllClasses(const InnerNode * node)3315 void HtmlGenerator::findAllClasses(const InnerNode *node)
3316 {
3317     NodeList::const_iterator c = node->childNodes().constBegin();
3318     while (c != node->childNodes().constEnd()) {
3319         if ((*c)->access() != Node::Private && (*c)->url().isEmpty()) {
3320             if ((*c)->type() == Node::Class && !(*c)->doc().isEmpty()) {
3321                 QString className = (*c)->name();
3322                 if ((*c)->parent() &&
3323                     (*c)->parent()->type() == Node::Namespace &&
3324                     !(*c)->parent()->name().isEmpty())
3325                     className = (*c)->parent()->name()+"::"+className;
3326 
3327                 if (!(static_cast<const ClassNode *>(*c))->hideFromMainList()) {
3328                     if ((*c)->status() == Node::Compat) {
3329                         compatClasses.insert(className, *c);
3330                     }
3331                     else if ((*c)->status() == Node::Obsolete) {
3332                         obsoleteClasses.insert(className, *c);
3333                     }
3334                     else {
3335                         nonCompatClasses.insert(className, *c);
3336                         if ((*c)->status() == Node::Main)
3337                             mainClasses.insert(className, *c);
3338                     }
3339                 }
3340 
3341                 QString moduleName = (*c)->moduleName();
3342                 if (moduleName == "Qt3SupportLight") {
3343                     moduleClassMap[moduleName].insert((*c)->name(), *c);
3344                     moduleName = "Qt3Support";
3345                 }
3346                 if (!moduleName.isEmpty())
3347                     moduleClassMap[moduleName].insert((*c)->name(), *c);
3348 
3349                 QString serviceName =
3350                     (static_cast<const ClassNode *>(*c))->serviceName();
3351                 if (!serviceName.isEmpty())
3352                     serviceClasses.insert(serviceName, *c);
3353             }
3354             else if ((*c)->type() == Node::Fake &&
3355                      (*c)->subType() == Node::QmlClass &&
3356                      !(*c)->doc().isEmpty()) {
3357                 QString qmlClassName = (*c)->name();
3358                 // Remove the "QML:" prefix if present.
3359                 if (qmlClassName.startsWith(QLatin1String("QML:")))
3360                     qmlClasses.insert(qmlClassName.mid(4),*c);
3361                 else
3362                     qmlClasses.insert(qmlClassName,*c);
3363             }
3364             else if ((*c)->isInnerNode()) {
3365                 findAllClasses(static_cast<InnerNode *>(*c));
3366             }
3367         }
3368         ++c;
3369     }
3370 }
3371 
findAllFunctions(const InnerNode * node)3372 void HtmlGenerator::findAllFunctions(const InnerNode *node)
3373 {
3374     NodeList::ConstIterator c = node->childNodes().begin();
3375     while (c != node->childNodes().end()) {
3376         if ((*c)->access() != Node::Private) {
3377             if ((*c)->isInnerNode() && (*c)->url().isEmpty()) {
3378                 findAllFunctions(static_cast<const InnerNode *>(*c));
3379             }
3380             else if ((*c)->type() == Node::Function) {
3381                 const FunctionNode *func = static_cast<const FunctionNode *>(*c);
3382                 if ((func->status() > Node::Obsolete) &&
3383                     !func->isInternal() &&
3384                     (func->metaness() != FunctionNode::Ctor) &&
3385                     (func->metaness() != FunctionNode::Dtor)) {
3386                     funcIndex[(*c)->name()].insert(myTree->fullDocumentName((*c)->parent()), *c);
3387                 }
3388             }
3389         }
3390         ++c;
3391     }
3392 }
3393 
findAllLegaleseTexts(const InnerNode * node)3394 void HtmlGenerator::findAllLegaleseTexts(const InnerNode *node)
3395 {
3396     NodeList::ConstIterator c = node->childNodes().begin();
3397     while (c != node->childNodes().end()) {
3398         if ((*c)->access() != Node::Private) {
3399             if (!(*c)->doc().legaleseText().isEmpty())
3400                 legaleseTexts.insertMulti((*c)->doc().legaleseText(), *c);
3401             if ((*c)->isInnerNode())
3402                 findAllLegaleseTexts(static_cast<const InnerNode *>(*c));
3403         }
3404         ++c;
3405     }
3406 }
3407 
findAllNamespaces(const InnerNode * node)3408 void HtmlGenerator::findAllNamespaces(const InnerNode *node)
3409 {
3410     NodeList::ConstIterator c = node->childNodes().begin();
3411     while (c != node->childNodes().end()) {
3412         if ((*c)->access() != Node::Private) {
3413             if ((*c)->isInnerNode() && (*c)->url().isEmpty()) {
3414                 findAllNamespaces(static_cast<const InnerNode *>(*c));
3415                 if ((*c)->type() == Node::Namespace) {
3416                     const NamespaceNode *nspace = static_cast<const NamespaceNode *>(*c);
3417                     // Ensure that the namespace's name is not empty (the root
3418                     // namespace has no name).
3419                     if (!nspace->name().isEmpty()) {
3420                         namespaceIndex.insert(nspace->name(), *c);
3421                         QString moduleName = (*c)->moduleName();
3422                         if (moduleName == "Qt3SupportLight") {
3423                             moduleNamespaceMap[moduleName].insert((*c)->name(), *c);
3424                             moduleName = "Qt3Support";
3425                         }
3426                         if (!moduleName.isEmpty())
3427                             moduleNamespaceMap[moduleName].insert((*c)->name(), *c);
3428                     }
3429                 }
3430             }
3431         }
3432         ++c;
3433     }
3434 }
3435 
hOffset(const Node * node)3436 int HtmlGenerator::hOffset(const Node *node)
3437 {
3438     switch (node->type()) {
3439     case Node::Namespace:
3440     case Node::Class:
3441         return 2;
3442     case Node::Fake:
3443         return 1;
3444     case Node::Enum:
3445     case Node::Typedef:
3446     case Node::Function:
3447     case Node::Property:
3448     default:
3449         return 3;
3450     }
3451 }
3452 
isThreeColumnEnumValueTable(const Atom * atom)3453 bool HtmlGenerator::isThreeColumnEnumValueTable(const Atom *atom)
3454 {
3455     while (atom != 0 && !(atom->type() == Atom::ListRight && atom->string() == ATOM_LIST_VALUE)) {
3456         if (atom->type() == Atom::ListItemLeft && !matchAhead(atom, Atom::ListItemRight))
3457             return true;
3458         atom = atom->next();
3459     }
3460     return false;
3461 }
3462 
findNodeForTarget(const QString & target,const Node * relative,CodeMarker * marker,const Atom * atom)3463 const Node *HtmlGenerator::findNodeForTarget(const QString &target,
3464                                              const Node *relative,
3465                                              CodeMarker *marker,
3466                                              const Atom *atom)
3467 {
3468     const Node *node = 0;
3469 
3470     if (target.isEmpty()) {
3471         node = relative;
3472     }
3473     else if (target.endsWith(".html")) {
3474         node = myTree->root()->findNode(target, Node::Fake);
3475     }
3476     else if (marker) {
3477         node = marker->resolveTarget(target, myTree, relative);
3478         if (!node)
3479             node = myTree->findFakeNodeByTitle(target);
3480         if (!node && atom) {
3481             node = myTree->findUnambiguousTarget(target,
3482                 *const_cast<Atom**>(&atom));
3483         }
3484     }
3485 
3486     if (!node)
3487         relative->doc().location().warning(tr("Cannot link to '%1'").arg(target));
3488 
3489     return node;
3490 }
3491 
anchorForNode(const Node * node)3492 const QPair<QString,QString> HtmlGenerator::anchorForNode(const Node *node)
3493 {
3494     QPair<QString,QString> anchorPair;
3495 
3496     anchorPair.first = PageGenerator::fileName(node);
3497     if (node->type() == Node::Fake) {
3498         const FakeNode *fakeNode = static_cast<const FakeNode*>(node);
3499         anchorPair.second = fakeNode->title();
3500     }
3501 
3502     return anchorPair;
3503 }
3504 
getLink(const Atom * atom,const Node * relative,CodeMarker * marker,const Node ** node)3505 QString HtmlGenerator::getLink(const Atom *atom,
3506                                const Node *relative,
3507                                CodeMarker *marker,
3508                                const Node** node)
3509 {
3510     QString link;
3511     *node = 0;
3512     inObsoleteLink = false;
3513 
3514     if (atom->string().contains(":") &&
3515             (atom->string().startsWith("file:")
3516              || atom->string().startsWith("http:")
3517              || atom->string().startsWith("https:")
3518              || atom->string().startsWith("ftp:")
3519              || atom->string().startsWith("mailto:"))) {
3520 
3521         link = atom->string();
3522     }
3523     else {
3524         QStringList path;
3525         if (atom->string().contains('#')) {
3526             path = atom->string().split('#');
3527         }
3528         else {
3529             path.append(atom->string());
3530         }
3531 
3532         Atom *targetAtom = 0;
3533 
3534         QString first = path.first().trimmed();
3535         if (first.isEmpty()) {
3536             *node = relative;
3537         }
3538         else if (first.endsWith(".html")) {
3539             *node = myTree->root()->findNode(first, Node::Fake);
3540         }
3541         else {
3542             *node = marker->resolveTarget(first, myTree, relative);
3543             if (!*node) {
3544                 *node = myTree->findFakeNodeByTitle(first);
3545             }
3546             if (!*node) {
3547                 *node = myTree->findUnambiguousTarget(first, targetAtom);
3548             }
3549         }
3550 
3551         if (*node) {
3552             if (!(*node)->url().isEmpty())
3553                 return (*node)->url();
3554             else
3555                 path.removeFirst();
3556         }
3557         else {
3558             *node = relative;
3559         }
3560 
3561         if (*node) {
3562             if ((*node)->status() == Node::Obsolete) {
3563                 if (relative) {
3564                     if (relative->parent() != *node) {
3565                         if (relative->status() != Node::Obsolete) {
3566                             bool porting = false;
3567                             if (relative->type() == Node::Fake) {
3568                                 const FakeNode* fake = static_cast<const FakeNode*>(relative);
3569                                 if (fake->title().startsWith("Porting"))
3570                                     porting = true;
3571                             }
3572                             QString name = marker->plainFullName(relative);
3573                             if (!porting && !name.startsWith("Q3")) {
3574                                 if (obsoleteLinks) {
3575                                     relative->doc().location().warning(tr("Link to obsolete item '%1' in %2")
3576                                                                        .arg(atom->string())
3577                                                                        .arg(name));
3578                                 }
3579                                 inObsoleteLink = true;
3580                             }
3581                         }
3582                     }
3583                 }
3584                 else {
3585                     qDebug() << "Link to Obsolete entity"
3586                              << (*node)->name() << "no relative";
3587                 }
3588             }
3589         }
3590 
3591         while (!path.isEmpty()) {
3592             targetAtom = myTree->findTarget(path.first(), *node);
3593             if (targetAtom == 0)
3594                 break;
3595             path.removeFirst();
3596         }
3597 
3598         if (path.isEmpty()) {
3599             link = linkForNode(*node, relative);
3600             if (*node && (*node)->subType() == Node::Image)
3601                 link = "images/used-in-examples/" + link;
3602             if (targetAtom)
3603                 link += "#" + refForAtom(targetAtom, *node);
3604         }
3605     }
3606     return link;
3607 }
3608 
generateIndex(const QString & fileBase,const QString & url,const QString & title)3609 void HtmlGenerator::generateIndex(const QString &fileBase,
3610                                   const QString &url,
3611                                   const QString &title)
3612 {
3613     myTree->generateIndex(outputDir() + "/" + fileBase + ".index", url, title);
3614 }
3615 
generateStatus(const Node * node,CodeMarker * marker)3616 void HtmlGenerator::generateStatus(const Node *node, CodeMarker *marker)
3617 {
3618     Text text;
3619 
3620     switch (node->status()) {
3621     case Node::Obsolete:
3622         if (node->isInnerNode())
3623             Generator::generateStatus(node, marker);
3624         break;
3625     case Node::Compat:
3626         if (node->isInnerNode()) {
3627             text << Atom::ParaLeft
3628                  << Atom(Atom::FormattingLeft,ATOM_FORMATTING_BOLD)
3629                  << "This "
3630                  << typeString(node)
3631                  << " is part of the Qt 3 support library."
3632                  << Atom(Atom::FormattingRight, ATOM_FORMATTING_BOLD)
3633                  << " It is provided to keep old source code working. "
3634                  << "We strongly advise against "
3635                  << "using it in new code. See ";
3636 
3637             const FakeNode *fakeNode = myTree->findFakeNodeByTitle("Porting To Qt 4");
3638             Atom *targetAtom = 0;
3639             if (fakeNode && node->type() == Node::Class) {
3640                 QString oldName(node->name());
3641                 targetAtom = myTree->findTarget(oldName.replace("3", ""),
3642                                                 fakeNode);
3643             }
3644 
3645             if (targetAtom) {
3646                 text << Atom(Atom::Link, linkForNode(fakeNode, node) + "#" +
3647                                          refForAtom(targetAtom, fakeNode));
3648             }
3649             else
3650                 text << Atom(Atom::Link, "Porting to Qt 4");
3651 
3652             text << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK)
3653                  << Atom(Atom::String, "Porting to Qt 4")
3654                  << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK)
3655                  << " for more information."
3656                  << Atom::ParaRight;
3657         }
3658         generateText(text, node, marker);
3659         break;
3660     default:
3661         Generator::generateStatus(node, marker);
3662     }
3663 }
3664 
3665 #ifdef GENERATE_MAC_REFS
3666 /*
3667   No longer valid.
3668  */
generateMacRef(const Node * node,CodeMarker * marker)3669 void HtmlGenerator::generateMacRef(const Node *node, CodeMarker *marker)
3670 {
3671     if (!pleaseGenerateMacRef || marker == 0)
3672         return;
3673 
3674     QStringList macRefs = marker->macRefsForNode(node);
3675     foreach (const QString &macRef, macRefs)
3676         out() << "<a name=\"" << "//apple_ref/" << macRef << "\"></a>\n";
3677 }
3678 #endif
3679 
beginLink(const QString & link,const Node * node,const Node * relative,CodeMarker * marker)3680 void HtmlGenerator::beginLink(const QString &link,
3681                               const Node *node,
3682                               const Node *relative,
3683                               CodeMarker *marker)
3684 {
3685     Q_UNUSED(marker)
3686     Q_UNUSED(relative)
3687 
3688     this->link = link;
3689     if (link.isEmpty()) {
3690         if (showBrokenLinks)
3691             out() << "<i>";
3692     }
3693     else if (node == 0 || (relative != 0 &&
3694                            node->status() == relative->status())) {
3695         out() << "<a href=\"" << link << "\">";
3696     }
3697     else {
3698         switch (node->status()) {
3699         case Node::Obsolete:
3700             out() << "<a href=\"" << link << "\" class=\"obsolete\">";
3701             break;
3702         case Node::Compat:
3703             out() << "<a href=\"" << link << "\" class=\"compat\">";
3704             break;
3705         default:
3706             out() << "<a href=\"" << link << "\">";
3707         }
3708     }
3709     inLink = true;
3710 }
3711 
endLink()3712 void HtmlGenerator::endLink()
3713 {
3714     if (inLink) {
3715         if (link.isEmpty()) {
3716             if (showBrokenLinks)
3717                 out() << "</i>";
3718         }
3719         else {
3720             if (inObsoleteLink) {
3721                 out() << "<sup>(obsolete)</sup>";
3722             }
3723             out() << "</a>";
3724         }
3725     }
3726     inLink = false;
3727     inObsoleteLink = false;
3728 }
3729 
3730 /*!
3731   Generates the summary for the \a section. Only used for
3732   sections of QML element documentation.
3733 
3734   Currently handles only the QML property group.
3735  */
generateQmlSummary(const Section & section,const Node * relative,CodeMarker * marker)3736 void HtmlGenerator::generateQmlSummary(const Section& section,
3737                                        const Node *relative,
3738                                        CodeMarker *marker)
3739 {
3740     if (!section.members.isEmpty()) {
3741         out() << "<ul>\n";
3742         NodeList::ConstIterator m;
3743         m = section.members.begin();
3744         while (m != section.members.end()) {
3745             out() << "<li class=\"fn\">";
3746             generateQmlItem(*m,relative,marker,true);
3747             out() << "</li>\n";
3748             ++m;
3749         }
3750         out() << "</ul>\n";
3751     }
3752 }
3753 
3754 /*!
3755   Outputs the html detailed documentation for a section
3756   on a QML element reference page.
3757  */
generateDetailedQmlMember(const Node * node,const InnerNode * relative,CodeMarker * marker)3758 void HtmlGenerator::generateDetailedQmlMember(const Node *node,
3759                                               const InnerNode *relative,
3760                                               CodeMarker *marker)
3761 {
3762     const QmlPropertyNode* qpn = 0;
3763 #ifdef GENERATE_MAC_REFS
3764     generateMacRef(node, marker);
3765 #endif
3766     generateExtractionMark(node, MemberMark);
3767     out() << "<div class=\"qmlitem\">";
3768     if (node->subType() == Node::QmlPropertyGroup) {
3769         const QmlPropGroupNode* qpgn = static_cast<const QmlPropGroupNode*>(node);
3770         NodeList::ConstIterator p = qpgn->childNodes().begin();
3771         out() << "<div class=\"qmlproto\">";
3772         out() << "<table class=\"qmlname\">";
3773 
3774         while (p != qpgn->childNodes().end()) {
3775             if ((*p)->type() == Node::QmlProperty) {
3776                 qpn = static_cast<const QmlPropertyNode*>(*p);
3777                 if (++numTableRows % 2 == 1)
3778                     out() << "<tr valign=\"top\" class=\"odd\">";
3779                 else
3780                     out() << "<tr valign=\"top\" class=\"even\">";
3781 
3782                 out() << "<td class=\"tblQmlPropNode\"><p>";
3783 
3784                 out() << "<a name=\"" + refForNode(qpn) + "\"></a>";
3785 
3786                 const ClassNode* cn = qpn->declarativeCppNode();
3787                 if (cn && !qpn->isWritable(myTree)) {
3788                     out() << "<span class=\"qmlreadonly\">read-only</span>";
3789                 }
3790                 if (qpgn->isDefault())
3791                     out() << "<span class=\"qmldefault\">default</span>";
3792                 generateQmlItem(qpn, relative, marker, false);
3793                 out() << "</p></td></tr>";
3794             }
3795             ++p;
3796         }
3797         out() << "</table>";
3798         out() << "</div>";
3799     }
3800     else if (node->type() == Node::QmlSignal) {
3801         const FunctionNode* qsn = static_cast<const FunctionNode*>(node);
3802         out() << "<div class=\"qmlproto\">";
3803         out() << "<table class=\"qmlname\">";
3804         //out() << "<tr>";
3805         if (++numTableRows % 2 == 1)
3806             out() << "<tr valign=\"top\" class=\"odd\">";
3807         else
3808             out() << "<tr valign=\"top\" class=\"even\">";
3809         out() << "<td class=\"tblQmlFuncNode\"><p>";
3810         out() << "<a name=\"" + refForNode(qsn) + "\"></a>";
3811         generateSynopsis(qsn,relative,marker,CodeMarker::Detailed,false);
3812         //generateQmlItem(qsn,relative,marker,false);
3813         out() << "</p></td></tr>";
3814         out() << "</table>";
3815         out() << "</div>";
3816     }
3817     else if (node->type() == Node::QmlMethod) {
3818         const FunctionNode* qmn = static_cast<const FunctionNode*>(node);
3819         out() << "<div class=\"qmlproto\">";
3820         out() << "<table class=\"qmlname\">";
3821         //out() << "<tr>";
3822         if (++numTableRows % 2 == 1)
3823             out() << "<tr valign=\"top\" class=\"odd\">";
3824         else
3825             out() << "<tr valign=\"top\" class=\"even\">";
3826         out() << "<td class=\"tblQmlFuncNode\"><p>";
3827         out() << "<a name=\"" + refForNode(qmn) + "\"></a>";
3828         generateSynopsis(qmn,relative,marker,CodeMarker::Detailed,false);
3829         out() << "</p></td></tr>";
3830         out() << "</table>";
3831         out() << "</div>";
3832     }
3833     out() << "<div class=\"qmldoc\">";
3834     generateStatus(node, marker);
3835     generateBody(node, marker);
3836     generateThreadSafeness(node, marker);
3837     generateSince(node, marker);
3838     generateAlsoList(node, marker);
3839     out() << "</div>";
3840     out() << "</div>";
3841     generateExtractionMark(node, EndMark);
3842 }
3843 
3844 /*!
3845   Output the "Inherits" line for the QML element,
3846   if there should be one.
3847  */
generateQmlInherits(const QmlClassNode * cn,CodeMarker * marker)3848 void HtmlGenerator::generateQmlInherits(const QmlClassNode* cn,
3849                                         CodeMarker* marker)
3850 {
3851     if (cn && !cn->links().empty()) {
3852         if (cn->links().contains(Node::InheritsLink)) {
3853             QPair<QString,QString> linkPair;
3854             linkPair = cn->links()[Node::InheritsLink];
3855             QStringList strList(linkPair.first);
3856             const Node* n = myTree->findNode(strList,Node::Fake);
3857             if (n && n->subType() == Node::QmlClass) {
3858                 const QmlClassNode* qcn = static_cast<const QmlClassNode*>(n);
3859                 Text text;
3860                 text << Atom::ParaLeft << "Inherits ";
3861                 text << Atom(Atom::LinkNode,CodeMarker::stringForNode(qcn));
3862                 text << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK);
3863                 text << Atom(Atom::String, linkPair.second);
3864                 text << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK);
3865                 text << Atom::ParaRight;
3866                 generateText(text, cn, marker);
3867             }
3868         }
3869     }
3870 }
3871 
3872 /*!
3873   Output the "Inherit by" list for the QML element,
3874   if it is inherited by any other elements.
3875  */
generateQmlInheritedBy(const QmlClassNode * cn,CodeMarker * marker)3876 void HtmlGenerator::generateQmlInheritedBy(const QmlClassNode* cn,
3877                                            CodeMarker* marker)
3878 {
3879     if (cn) {
3880         NodeList subs;
3881         QmlClassNode::subclasses(cn->name(),subs);
3882         if (!subs.isEmpty()) {
3883             Text text;
3884             text << Atom::ParaLeft << "Inherited by ";
3885             appendSortedQmlNames(text,cn,subs,marker);
3886             text << Atom::ParaRight;
3887             generateText(text, cn, marker);
3888         }
3889     }
3890 }
3891 
3892 /*!
3893   Output the "[Xxx instantiates the C++ class QmlGraphicsXxx]"
3894   line for the QML element, if there should be one.
3895 
3896   If there is no class node, or if the class node status
3897   is set to Node::Internal, do nothing.
3898  */
generateQmlInstantiates(const QmlClassNode * qcn,CodeMarker * marker)3899 void HtmlGenerator::generateQmlInstantiates(const QmlClassNode* qcn,
3900                                             CodeMarker* marker)
3901 {
3902     const ClassNode* cn = qcn->classNode();
3903     if (cn && (cn->status() != Node::Internal)) {
3904         Text text;
3905         text << Atom::ParaLeft;
3906         text << Atom(Atom::LinkNode,CodeMarker::stringForNode(qcn));
3907         text << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK);
3908         QString name = qcn->name();
3909         if (name.startsWith(QLatin1String("QML:")))
3910             name = name.mid(4); // remove the "QML:" prefix
3911         text << Atom(Atom::String, name);
3912         text << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK);
3913         text << " instantiates the C++ class ";
3914         text << Atom(Atom::LinkNode,CodeMarker::stringForNode(cn));
3915         text << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK);
3916         text << Atom(Atom::String, cn->name());
3917         text << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK);
3918         text << Atom::ParaRight;
3919         generateText(text, qcn, marker);
3920     }
3921 }
3922 
3923 /*!
3924   Output the "[QmlGraphicsXxx is instantiated by QML element Xxx]"
3925   line for the class, if there should be one.
3926 
3927   If there is no QML element, or if the class node status
3928   is set to Node::Internal, do nothing.
3929  */
generateInstantiatedBy(const ClassNode * cn,CodeMarker * marker)3930 void HtmlGenerator::generateInstantiatedBy(const ClassNode* cn,
3931                                            CodeMarker* marker)
3932 {
3933     if (cn &&  cn->status() != Node::Internal && !cn->qmlElement().isEmpty()) {
3934         const Node* n = myTree->root()->findNode(cn->qmlElement(),Node::Fake);
3935         if (n && n->subType() == Node::QmlClass) {
3936             Text text;
3937             text << Atom::ParaLeft;
3938             text << Atom(Atom::LinkNode,CodeMarker::stringForNode(cn));
3939             text << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK);
3940             text << Atom(Atom::String, cn->name());
3941             text << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK);
3942             text << " is instantiated by QML element ";
3943             text << Atom(Atom::LinkNode,CodeMarker::stringForNode(n));
3944             text << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK);
3945             text << Atom(Atom::String, n->name());
3946             text << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK);
3947             text << Atom::ParaRight;
3948             generateText(text, cn, marker);
3949         }
3950     }
3951 }
3952 
3953 /*!
3954   Generate the <page> element for the given \a node using the \a writer.
3955   Return true if a <page> element was written; otherwise return false.
3956  */
generatePageElement(QXmlStreamWriter & writer,const Node * node,CodeMarker * marker) const3957 bool HtmlGenerator::generatePageElement(QXmlStreamWriter& writer,
3958                                         const Node* node,
3959                                         CodeMarker* marker) const
3960 {
3961     if (node->pageType() == Node::NoPageType)
3962         return false;
3963     if (node->name().isEmpty())
3964         return true;
3965     if (node->access() == Node::Private)
3966         return false;
3967 
3968     QString guid = QUuid::createUuid().toString();
3969     QString url = PageGenerator::fileName(node);
3970     QString title;
3971     QString rawTitle;
3972     QString fullTitle;
3973     QStringList pageWords;
3974     QXmlStreamAttributes attributes;
3975 
3976     writer.writeStartElement("page");
3977 
3978     if (node->isInnerNode()) {
3979         const InnerNode* inner = static_cast<const InnerNode*>(node);
3980         if (!inner->pageKeywords().isEmpty())
3981             pageWords << inner->pageKeywords();
3982 
3983         switch (node->type()) {
3984         case Node::Fake:
3985             {
3986                 const FakeNode* fake = static_cast<const FakeNode*>(node);
3987                 title = fake->fullTitle();
3988                 pageWords << title;
3989                 break;
3990             }
3991         case Node::Class:
3992             {
3993                 title = node->name() + " Class Reference";
3994                 pageWords << node->name() << "class" << "reference";
3995                 break;
3996             }
3997         case Node::Namespace:
3998             {
3999                 rawTitle = marker->plainName(inner);
4000                 fullTitle = marker->plainFullName(inner);
4001                 title = rawTitle + " Namespace Reference";
4002                 pageWords << rawTitle << "namespace" << "reference";
4003                 break;
4004             }
4005         default:
4006             title = node->name();
4007             pageWords << title;
4008             break;
4009         }
4010     }
4011     else {
4012         switch (node->type()) {
4013         case Node::Enum:
4014             {
4015                 title = node->name() + " Enum Reference";
4016                 pageWords << node->name() << "enum" << "type";
4017                 url += "#" + node->name() + "-enum";
4018                 break;
4019             }
4020         case Node::Function:
4021             {
4022                 title = node->name() + " Function Reference";
4023                 pageWords << node->name() << "function";
4024                 url += "#" + node->name();
4025                 break;
4026             }
4027         case Node::Property:
4028             {
4029                 title = node->name() + " Property Reference";
4030                 pageWords << node->name() << "property";
4031                 url += "#" + node->name() + "-prop";
4032                 break;
4033             }
4034         case Node::Typedef:
4035             {
4036                 title = node->name() + " Type Reference";
4037                 pageWords << node->name() << "typedef" << "type";
4038                 url += "#" + node->name();
4039                 break;
4040             }
4041         default:
4042             title = node->name();
4043             pageWords << title;
4044             break;
4045         }
4046 
4047         Node* parent = node->parent();
4048         if (parent && ((parent->type() == Node::Class) ||
4049                        (parent->type() == Node::Namespace))) {
4050             pageWords << parent->name();
4051         }
4052     }
4053 
4054     writer.writeAttribute("id",guid);
4055     writer.writeStartElement("pageWords");
4056     writer.writeCharacters(pageWords.join(" "));
4057 
4058     writer.writeEndElement();
4059     writer.writeStartElement("pageTitle");
4060     writer.writeCharacters(title);
4061     writer.writeEndElement();
4062     writer.writeStartElement("pageUrl");
4063     writer.writeCharacters(url);
4064     writer.writeEndElement();
4065     writer.writeStartElement("pageType");
4066     switch (node->pageType()) {
4067     case Node::ApiPage:
4068         writer.writeCharacters("APIPage");
4069         break;
4070     case Node::ArticlePage:
4071         writer.writeCharacters("Article");
4072         break;
4073     case Node::ExamplePage:
4074         writer.writeCharacters("Example");
4075         break;
4076     default:
4077         break;
4078     }
4079     writer.writeEndElement();
4080     writer.writeEndElement();
4081 
4082     if (node->type() == Node::Fake && node->doc().hasTableOfContents()) {
4083         QList<Atom*> toc = node->doc().tableOfContents();
4084         if (!toc.isEmpty()) {
4085             for (int i = 0; i < toc.size(); ++i) {
4086                 Text headingText = Text::sectionHeading(toc.at(i));
4087                 QString s = headingText.toString();
4088                 writer.writeStartElement("page");
4089                 guid = QUuid::createUuid().toString();
4090                 QString internalUrl = url + "#" + Doc::canonicalTitle(s);
4091                 writer.writeAttribute("id",guid);
4092                 writer.writeStartElement("pageWords");
4093                 writer.writeCharacters(pageWords.join(" "));
4094                 writer.writeCharacters(" ");
4095                 writer.writeCharacters(s);
4096                 writer.writeEndElement();
4097                 writer.writeStartElement("pageTitle");
4098                 writer.writeCharacters(s);
4099                 writer.writeEndElement();
4100                 writer.writeStartElement("pageUrl");
4101                 writer.writeCharacters(internalUrl);
4102                 writer.writeEndElement();
4103                 writer.writeStartElement("pageType");
4104                 writer.writeCharacters("Article");
4105                 writer.writeEndElement();
4106                 writer.writeEndElement();
4107             }
4108         }
4109     }
4110     return true;
4111 }
4112 
4113 /*!
4114   Traverse the tree recursively and generate the <keyword>
4115   elements.
4116  */
generatePageElements(QXmlStreamWriter & writer,const Node * node,CodeMarker * marker) const4117 void HtmlGenerator::generatePageElements(QXmlStreamWriter& writer, const Node* node, CodeMarker* marker) const
4118 {
4119     if (generatePageElement(writer, node, marker)) {
4120 
4121         if (node->isInnerNode()) {
4122             const InnerNode *inner = static_cast<const InnerNode *>(node);
4123 
4124             // Recurse to write an element for this child node and all its children.
4125             foreach (const Node *child, inner->childNodes())
4126                 generatePageElements(writer, child, marker);
4127         }
4128     }
4129 }
4130 
4131 /*!
4132   Outputs the file containing the index used for searching the html docs.
4133  */
generatePageIndex(const QString & fileName) const4134 void HtmlGenerator::generatePageIndex(const QString& fileName) const
4135 {
4136     QFile file(fileName);
4137     if (!file.open(QFile::WriteOnly | QFile::Text))
4138         return ;
4139 
4140     CodeMarker *marker = CodeMarker::markerForFileName(fileName);
4141 
4142     QXmlStreamWriter writer(&file);
4143     writer.setAutoFormatting(true);
4144     writer.writeStartDocument();
4145     writer.writeStartElement("qtPageIndex");
4146 
4147     generatePageElements(writer, myTree->root(), marker);
4148 
4149     writer.writeEndElement(); // qtPageIndex
4150     writer.writeEndDocument();
4151     file.close();
4152 }
4153 
generateExtractionMark(const Node * node,ExtractionMarkType markType)4154 void HtmlGenerator::generateExtractionMark(const Node *node, ExtractionMarkType markType)
4155 {
4156     if (markType != EndMark) {
4157         out() << "<!-- $$$" + node->name();
4158         if (markType == MemberMark) {
4159             if (node->type() == Node::Function) {
4160                 const FunctionNode *func = static_cast<const FunctionNode *>(node);
4161                 if (!func->associatedProperty()) {
4162                     if (func->overloadNumber() == 1)
4163                         out() << "[overload1]";
4164                     out() << "$$$" + func->name() + func->rawParameters().remove(' ');
4165                 }
4166             } else if (node->type() == Node::Property) {
4167                 out() << "-prop";
4168                 const PropertyNode *prop = static_cast<const PropertyNode *>(node);
4169                 const NodeList &list = prop->functions();
4170                 foreach (const Node *propFuncNode, list) {
4171                     if (propFuncNode->type() == Node::Function) {
4172                         const FunctionNode *func = static_cast<const FunctionNode *>(propFuncNode);
4173                         out() << "$$$" + func->name() + func->rawParameters().remove(' ');
4174                     }
4175                 }
4176             } else if (node->type() == Node::Enum) {
4177                 const EnumNode *enumNode = static_cast<const EnumNode *>(node);
4178                 foreach (const EnumItem &item, enumNode->items())
4179                     out() << "$$$" + item.name();
4180             }
4181         } else if (markType == BriefMark) {
4182             out() << "-brief";
4183         } else if (markType == DetailedDescriptionMark) {
4184             out() << "-description";
4185         }
4186         out() << " -->\n";
4187     } else {
4188         out() << "<!-- @@@" + node->name() + " -->\n";
4189     }
4190 }
4191 
4192 /*!
4193   Returns the full document location for HTML-based documentation.
4194  */
fullDocumentLocation(const Node * node)4195 QString HtmlGenerator::fullDocumentLocation(const Node *node)
4196 {
4197     if (!node)
4198         return "";
4199     if (!node->url().isEmpty())
4200         return node->url();
4201 
4202     QString parentName;
4203     QString anchorRef;
4204 
4205     if (node->type() == Node::Namespace) {
4206 
4207         // The root namespace has no name - check for this before creating
4208         // an attribute containing the location of any documentation.
4209 
4210         if (!node->fileBase().isEmpty())
4211             parentName = node->fileBase() + ".html";
4212         else
4213             return "";
4214     }
4215     else if (node->type() == Node::Fake) {
4216         if ((node->subType() == Node::QmlClass) ||
4217             (node->subType() == Node::QmlBasicType)) {
4218             QString fb = node->fileBase();
4219             if (fb.startsWith(Generator::outputPrefix(QLatin1String("QML"))))
4220                 return fb + ".html";
4221             else
4222                 return Generator::outputPrefix(QLatin1String("QML")) + node->fileBase() + QLatin1String(".html");
4223         }
4224         else
4225             parentName = node->fileBase() + ".html";
4226     }
4227     else if (node->fileBase().isEmpty())
4228         return "";
4229 
4230     Node *parentNode = 0;
4231 
4232     if ((parentNode = node->relates()))
4233         parentName = fullDocumentLocation(node->relates());
4234     else if ((parentNode = node->parent())) {
4235         if (parentNode->subType() == Node::QmlPropertyGroup) {
4236             parentNode = parentNode->parent();
4237             parentName = fullDocumentLocation(parentNode);
4238         }
4239         else
4240             parentName = fullDocumentLocation(node->parent());
4241     }
4242 
4243     switch (node->type()) {
4244         case Node::Class:
4245         case Node::Namespace:
4246             if (parentNode && !parentNode->name().isEmpty())
4247                 parentName = parentName.replace(".html", "") + "-"
4248                            + node->fileBase().toLower() + ".html";
4249             else
4250                 parentName = node->fileBase() + ".html";
4251             break;
4252         case Node::Function:
4253             {
4254                 /*
4255                   Functions can be destructors, overloaded, or
4256                   have associated properties.
4257                 */
4258                 const FunctionNode *functionNode =
4259                     static_cast<const FunctionNode *>(node);
4260 
4261                 if (functionNode->metaness() == FunctionNode::Dtor)
4262                     anchorRef = "#dtor." + functionNode->name().mid(1);
4263 
4264                 else if (functionNode->associatedProperty())
4265                     return fullDocumentLocation(functionNode->associatedProperty());
4266 
4267                 else if (functionNode->overloadNumber() > 1)
4268                     anchorRef = "#" + functionNode->name()
4269                               + "-" + QString::number(functionNode->overloadNumber());
4270                 else
4271                     anchorRef = "#" + functionNode->name();
4272             }
4273 
4274             /*
4275               Use node->name() instead of node->fileBase() as
4276               the latter returns the name in lower-case. For
4277               HTML anchors, we need to preserve the case.
4278             */
4279             break;
4280         case Node::Enum:
4281             anchorRef = "#" + node->name() + "-enum";
4282             break;
4283         case Node::Typedef:
4284             anchorRef = "#" + node->name() + "-typedef";
4285             break;
4286         case Node::Property:
4287             anchorRef = "#" + node->name() + "-prop";
4288             break;
4289         case Node::QmlProperty:
4290             anchorRef = "#" + node->name() + "-prop";
4291             break;
4292         case Node::QmlSignal:
4293             anchorRef = "#" + node->name() + "-signal";
4294             break;
4295         case Node::QmlMethod:
4296             anchorRef = "#" + node->name() + "-method";
4297             break;
4298         case Node::Variable:
4299             anchorRef = "#" + node->name() + "-var";
4300             break;
4301         case Node::Target:
4302             anchorRef = "#" + Doc::canonicalTitle(node->name());
4303             break;
4304         case Node::Fake:
4305             {
4306             /*
4307               Use node->fileBase() for fake nodes because they are represented
4308               by pages whose file names are lower-case.
4309             */
4310             parentName = node->fileBase();
4311             parentName.replace("/", "-").replace(".", "-");
4312             parentName += ".html";
4313             }
4314             break;
4315         default:
4316             break;
4317     }
4318 
4319     // Various objects can be compat (deprecated) or obsolete.
4320     if (node->type() != Node::Class && node->type() != Node::Namespace) {
4321         switch (node->status()) {
4322         case Node::Compat:
4323             parentName.replace(".html", "-qt3.html");
4324             break;
4325         case Node::Obsolete:
4326             parentName.replace(".html", "-obsolete.html");
4327             break;
4328         default:
4329             ;
4330         }
4331     }
4332 
4333     return parentName.toLower() + anchorRef;
4334 }
4335 
4336 /*!
4337   This function outputs one or more manifest files in XML.
4338   They are used by Creator.
4339  */
generateManifestFiles()4340 void HtmlGenerator::generateManifestFiles()
4341 {
4342     generateManifestFile("examples", "example");
4343     generateManifestFile("demos", "demo");
4344     ExampleNode::exampleNodeMap.clear();
4345 }
4346 
4347 /*!
4348   This function is called by generaqteManiferstFile(), once
4349   for each manifest file to be generated. \a manifest is the
4350   type of manifest file.
4351  */
generateManifestFile(QString manifest,QString element)4352 void HtmlGenerator::generateManifestFile(QString manifest, QString element)
4353 {
4354     if (ExampleNode::exampleNodeMap.isEmpty())
4355         return;
4356     QString fileName = manifest +"-manifest.xml";
4357     QFile file(outputDir() + "/" + fileName);
4358     if (!file.open(QFile::WriteOnly | QFile::Text))
4359         return ;
4360     bool demos = false;
4361     if (manifest == "demos")
4362         demos = true;
4363 
4364     bool proceed = false;
4365     ExampleNodeMap::Iterator i = ExampleNode::exampleNodeMap.begin();
4366     while (i != ExampleNode::exampleNodeMap.end()) {
4367         const ExampleNode* en = i.value();
4368         if (demos) {
4369             if (en->name().startsWith("demos")) {
4370                 proceed = true;
4371                 break;
4372             }
4373         }
4374         else if (!en->name().startsWith("demos")) {
4375             proceed = true;
4376             break;
4377         }
4378         ++i;
4379     }
4380     if (!proceed)
4381         return;
4382 
4383     QXmlStreamWriter writer(&file);
4384     writer.setAutoFormatting(true);
4385     writer.writeStartDocument();
4386     writer.writeStartElement("instructionals");
4387     writer.writeAttribute("module", project);
4388     writer.writeStartElement(manifest);
4389 
4390     i = ExampleNode::exampleNodeMap.begin();
4391     while (i != ExampleNode::exampleNodeMap.end()) {
4392         const ExampleNode* en = i.value();
4393         if (demos) {
4394             if (!en->name().startsWith("demos")) {
4395                 ++i;
4396                 continue;
4397             }
4398         }
4399         else if (en->name().startsWith("demos")) {
4400             ++i;
4401             continue;
4402         }
4403         writer.writeStartElement(element);
4404         writer.writeAttribute("name", en->title());
4405         //QString docUrl = projectUrl + "/" + en->fileBase() + ".html";
4406         QString docUrl = manifestDir + en->fileBase() + ".html";
4407         writer.writeAttribute("docUrl", docUrl);
4408 
4409         QDir installDir(QLibraryInfo::location(QLibraryInfo::DataPath));
4410         QDir buildDir(QString::fromLocal8Bit(qgetenv("QT_BUILD_TREE")));
4411         QDir sourceDir(QString::fromLocal8Bit(qgetenv("QT_SOURCE_TREE")));
4412 
4413         QString relativePath;
4414         if (buildDir.exists() && sourceDir.exists()
4415             // shadow build, but no prefix build
4416             && installDir == buildDir && buildDir != sourceDir) {
4417             relativePath = QString("../%1/%2/").arg(buildDir.relativeFilePath(sourceDir.path()));
4418             if (demos)
4419                 relativePath = relativePath.arg("demos");
4420             else
4421                 relativePath = relativePath.arg("examples");
4422         }
4423 
4424 
4425         foreach (const Node* child, en->childNodes()) {
4426             if (child->subType() == Node::File) {
4427                 QString file = child->name();
4428                 if (file.endsWith(".pro") || file.endsWith(".qmlproject")) {
4429                     if (file.startsWith("demos/"))
4430                         file = file.mid(6);
4431                     if (!relativePath.isEmpty())
4432                         file.prepend(relativePath);
4433                     writer.writeAttribute("projectPath", file);
4434                     break;
4435                 }
4436             }
4437         }
4438         //writer.writeAttribute("imageUrl", projectUrl + "/" + en->imageFileName());
4439         writer.writeAttribute("imageUrl", manifestDir + en->imageFileName());
4440         writer.writeStartElement("description");
4441         Text brief = en->doc().briefText();
4442         if (!brief.isEmpty())
4443             writer.writeCDATA(brief.toString());
4444         else
4445             writer.writeCDATA(QString("No description available"));
4446         writer.writeEndElement(); // description
4447         QStringList tags = en->title().toLower().split(" ");
4448         if (!tags.isEmpty()) {
4449             writer.writeStartElement("tags");
4450             bool wrote_one = false;
4451             for (int n=0; n<tags.size(); ++n) {
4452                 QString tag = tags.at(n);
4453                 if (tag.at(0).isDigit())
4454                     continue;
4455                 if (tag.at(0) == '-')
4456                     continue;
4457                 if (tag.startsWith("example"))
4458                     continue;
4459                 if (tag.startsWith("chapter"))
4460                     continue;
4461                 if (tag.endsWith(":"))
4462                     tag.chop(1);
4463                 if (n>0 && wrote_one)
4464                     writer.writeCharacters(",");
4465                 writer.writeCharacters(tag);
4466                 wrote_one = true;
4467             }
4468             writer.writeEndElement(); // tags
4469         }
4470 
4471         QString ename = en->name().mid(en->name().lastIndexOf('/')+1);
4472         QSet<QString> usedNames;
4473         foreach (const Node* child, en->childNodes()) {
4474             if (child->subType() == Node::File) {
4475                 QString file = child->name();
4476                 QString fileName = file.mid(file.lastIndexOf('/')+1);
4477                 QString baseName = fileName;
4478                 if ((fileName.count(QChar('.')) > 0) &&
4479                     (fileName.endsWith(".cpp") ||
4480                      fileName.endsWith(".h") ||
4481                      fileName.endsWith(".qml")))
4482                     baseName.truncate(baseName.lastIndexOf(QChar('.')));
4483                 if (baseName.toLower() == ename) {
4484                     if (!usedNames.contains(fileName)) {
4485                         writer.writeStartElement("fileToOpen");
4486                         if (file.startsWith("demos/"))
4487                             file = file.mid(6);
4488                         writer.writeCharacters(file);
4489                         writer.writeEndElement(); // fileToOpen
4490                         usedNames.insert(fileName);
4491                     }
4492                 }
4493                 else if (fileName.toLower().endsWith("main.cpp") ||
4494                          fileName.toLower().endsWith("main.qml")) {
4495                     if (!usedNames.contains(fileName)) {
4496                         writer.writeStartElement("fileToOpen");
4497                         if (file.startsWith("demos/"))
4498                             file = file.mid(6);
4499                         writer.writeCharacters(file);
4500                         writer.writeEndElement(); // fileToOpen
4501                         usedNames.insert(fileName);
4502                     }
4503                 }
4504             }
4505         }
4506         if (!en->dependencies().isEmpty()) {
4507             for (int idx=0; idx<en->dependencies().size(); ++idx) {
4508                 writer.writeStartElement("dependency");
4509                 QString file(en->dependencies()[idx]);
4510                 if (!relativePath.isEmpty())
4511                     file.prepend(relativePath);
4512                 writer.writeCharacters(file);
4513                 writer.writeEndElement(); // dependency
4514             }
4515         }
4516         writer.writeEndElement(); // example
4517         ++i;
4518     }
4519 
4520     writer.writeEndElement(); // examples
4521     writer.writeEndElement(); // instructionals
4522     writer.writeEndDocument();
4523     file.close();
4524 }
4525 
4526 QT_END_NAMESPACE
4527