source: trunk/tools/qdoc3/htmlgenerator.cpp

Last change on this file was 846, checked in by Dmitry A. Kuminov, 14 years ago

trunk: Merged in qt 4.7.2 sources from branches/vendor/nokia/qt.

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