source: trunk/tools/qdoc3/htmlgenerator.cpp@ 814

Last change on this file since 814 was 651, checked in by Dmitry A. Kuminov, 15 years ago

trunk: Merged in qt 4.6.2 sources.

File size: 151.3 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
4** All rights reserved.
5** Contact: Nokia Corporation ([email protected])
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 [email protected].
38** $QT_END_LICENSE$
39**
40****************************************************************************/
41
42/*
43 htmlgenerator.cpp
44*/
45
46#include "codemarker.h"
47#include "helpprojectwriter.h"
48#include "htmlgenerator.h"
49#include "node.h"
50#include "separator.h"
51#include "tree.h"
52#include <ctype.h>
53
54#include <qdebug.h>
55#include <qlist.h>
56#include <qiterator.h>
57
58QT_BEGIN_NAMESPACE
59
60#define COMMAND_VERSION Doc::alias("version")
61
62QString HtmlGenerator::sinceTitles[] =
63 {
64 " New Namespaces",
65 " New Classes",
66 " New Member Functions",
67 " New Functions in Namespaces",
68 " New Global Functions",
69 " New Macros",
70 " New Enum Types",
71 " New Typedefs",
72 " New Properties",
73 " New Variables",
74 " New Qml Properties",
75 " New Qml Signals",
76 " New Qml Methods",
77 ""
78 };
79
80static bool showBrokenLinks = false;
81
82static QRegExp linkTag("(<@link node=\"([^\"]+)\">).*(</@link>)");
83static QRegExp funcTag("(<@func target=\"([^\"]*)\">)(.*)(</@func>)");
84static QRegExp typeTag("(<@(type|headerfile|func)(?: +[^>]*)?>)(.*)(</@\\2>)");
85static QRegExp spanTag("</@(?:comment|preprocessor|string|char)>");
86static QRegExp unknownTag("</?@[^>]*>");
87
88bool parseArg(const QString &src,
89 const QString &tag,
90 int *pos,
91 int n,
92 QStringRef *contents,
93 QStringRef *par1 = 0,
94 bool debug = false)
95{
96#define SKIP_CHAR(c) \
97 if (debug) \
98 qDebug() << "looking for " << c << " at " << QString(src.data() + i, n - i); \
99 if (i >= n || src[i] != c) { \
100 if (debug) \
101 qDebug() << " char '" << c << "' not found"; \
102 return false; \
103 } \
104 ++i;
105
106
107#define SKIP_SPACE \
108 while (i < n && src[i] == ' ') \
109 ++i;
110
111 int i = *pos;
112 int j = i;
113
114 // assume "<@" has been parsed outside
115 //SKIP_CHAR('<');
116 //SKIP_CHAR('@');
117
118 if (tag != QStringRef(&src, i, tag.length())) {
119 if (0 && debug)
120 qDebug() << "tag " << tag << " not found at " << i;
121 return false;
122 }
123
124 if (debug)
125 qDebug() << "haystack:" << src << "needle:" << tag << "i:" <<i;
126
127 // skip tag
128 i += tag.length();
129
130 // parse stuff like: linkTag("(<@link node=\"([^\"]+)\">).*(</@link>)");
131 if (par1) {
132 SKIP_SPACE;
133 // read parameter name
134 j = i;
135 while (i < n && src[i].isLetter())
136 ++i;
137 if (src[i] == '=') {
138 if (debug)
139 qDebug() << "read parameter" << QString(src.data() + j, i - j);
140 SKIP_CHAR('=');
141 SKIP_CHAR('"');
142 // skip parameter name
143 j = i;
144 while (i < n && src[i] != '"')
145 ++i;
146 *par1 = QStringRef(&src, j, i - j);
147 SKIP_CHAR('"');
148 SKIP_SPACE;
149 } else {
150 if (debug)
151 qDebug() << "no optional parameter found";
152 }
153 }
154 SKIP_SPACE;
155 SKIP_CHAR('>');
156
157 // find contents up to closing "</@tag>
158 j = i;
159 for (; true; ++i) {
160 if (i + 4 + tag.length() > n)
161 return false;
162 if (src[i] != '<')
163 continue;
164 if (src[i + 1] != '/')
165 continue;
166 if (src[i + 2] != '@')
167 continue;
168 if (tag != QStringRef(&src, i + 3, tag.length()))
169 continue;
170 if (src[i + 3 + tag.length()] != '>')
171 continue;
172 break;
173 }
174
175 *contents = QStringRef(&src, j, i - j);
176
177 i += tag.length() + 4;
178
179 *pos = i;
180 if (debug)
181 qDebug() << " tag " << tag << " found: pos now: " << i;
182 return true;
183#undef SKIP_CHAR
184}
185
186static void addLink(const QString &linkTarget,
187 const QStringRef &nestedStuff,
188 QString *res)
189{
190 if (!linkTarget.isEmpty()) {
191 *res += "<a href=\"";
192 *res += linkTarget;
193 *res += "\">";
194 *res += nestedStuff;
195 *res += "</a>";
196 }
197 else {
198 *res += nestedStuff;
199 }
200}
201
202
203HtmlGenerator::HtmlGenerator()
204 : helpProjectWriter(0), inLink(false), inContents(false),
205 inSectionHeading(false), inTableHeader(false), numTableRows(0),
206 threeColumnEnumValueTable(true), funcLeftParen("\\S(\\()"),
207 myTree(0), slow(false), obsoleteLinks(false)
208{
209}
210
211HtmlGenerator::~HtmlGenerator()
212{
213 if (helpProjectWriter)
214 delete helpProjectWriter;
215}
216
217void HtmlGenerator::initializeGenerator(const Config &config)
218{
219 static const struct {
220 const char *key;
221 const char *left;
222 const char *right;
223 } defaults[] = {
224 { ATOM_FORMATTING_BOLD, "<b>", "</b>" },
225 { ATOM_FORMATTING_INDEX, "<!--", "-->" },
226 { ATOM_FORMATTING_ITALIC, "<i>", "</i>" },
227 { ATOM_FORMATTING_PARAMETER, "<i>", "</i>" },
228 { ATOM_FORMATTING_SUBSCRIPT, "<sub>", "</sub>" },
229 { ATOM_FORMATTING_SUPERSCRIPT, "<sup>", "</sup>" },
230 { ATOM_FORMATTING_TELETYPE, "<tt>", "</tt>" },
231 { ATOM_FORMATTING_UNDERLINE, "<u>", "</u>" },
232 { 0, 0, 0 }
233 };
234
235 Generator::initializeGenerator(config);
236 obsoleteLinks = config.getBool(QLatin1String(CONFIG_OBSOLETELINKS));
237 setImageFileExtensions(QStringList() << "png" << "jpg" << "jpeg" << "gif");
238 int i = 0;
239 while (defaults[i].key) {
240 formattingLeftMap().insert(defaults[i].key, defaults[i].left);
241 formattingRightMap().insert(defaults[i].key, defaults[i].right);
242 i++;
243 }
244
245 style = config.getString(HtmlGenerator::format() +
246 Config::dot +
247 HTMLGENERATOR_STYLE);
248 postHeader = config.getString(HtmlGenerator::format() +
249 Config::dot +
250 HTMLGENERATOR_POSTHEADER);
251 footer = config.getString(HtmlGenerator::format() +
252 Config::dot +
253 HTMLGENERATOR_FOOTER);
254 address = config.getString(HtmlGenerator::format() +
255 Config::dot +
256 HTMLGENERATOR_ADDRESS);
257 pleaseGenerateMacRef = config.getBool(HtmlGenerator::format() +
258 Config::dot +
259 HTMLGENERATOR_GENERATEMACREFS);
260
261 project = config.getString(CONFIG_PROJECT);
262
263 projectDescription = config.getString(CONFIG_DESCRIPTION);
264 if (projectDescription.isEmpty() && !project.isEmpty())
265 projectDescription = project + " Reference Documentation";
266
267 projectUrl = config.getString(CONFIG_URL);
268
269 QSet<QString> editionNames = config.subVars(CONFIG_EDITION);
270 QSet<QString>::ConstIterator edition = editionNames.begin();
271 while (edition != editionNames.end()) {
272 QString editionName = *edition;
273 QStringList editionModules = config.getStringList(CONFIG_EDITION +
274 Config::dot +
275 editionName +
276 Config::dot +
277 "modules");
278 QStringList editionGroups = config.getStringList(CONFIG_EDITION +
279 Config::dot +
280 editionName +
281 Config::dot +
282 "groups");
283
284 if (!editionModules.isEmpty())
285 editionModuleMap[editionName] = editionModules;
286 if (!editionGroups.isEmpty())
287 editionGroupMap[editionName] = editionGroups;
288
289 ++edition;
290 }
291
292 slow = config.getBool(CONFIG_SLOW);
293
294 stylesheets = config.getStringList(HtmlGenerator::format() +
295 Config::dot +
296 HTMLGENERATOR_STYLESHEETS);
297 customHeadElements = config.getStringList(HtmlGenerator::format() +
298 Config::dot +
299 HTMLGENERATOR_CUSTOMHEADELEMENTS);
300 codeIndent = config.getInt(CONFIG_CODEINDENT);
301
302 helpProjectWriter = new HelpProjectWriter(config,
303 project.toLower() +
304 ".qhp");
305}
306
307void HtmlGenerator::terminateGenerator()
308{
309 Generator::terminateGenerator();
310}
311
312QString HtmlGenerator::format()
313{
314 return "HTML";
315}
316
317/*!
318 This is where the html files and dcf files are written.
319 \note The html file generation is done in the base class,
320 PageGenerator::generateTree().
321 */
322void HtmlGenerator::generateTree(const Tree *tree, CodeMarker *marker)
323{
324 // Copy the stylesheets from the directory containing the qdocconf file.
325 // ### This should be changed to use a special directory in doc/src.
326 QStringList::ConstIterator styleIter = stylesheets.begin();
327 QDir configPath = QDir::current();
328 while (styleIter != stylesheets.end()) {
329 QString filePath = configPath.absoluteFilePath(*styleIter);
330 Config::copyFile(Location(), filePath, filePath, outputDir());
331 ++styleIter;
332 }
333
334 myTree = tree;
335 nonCompatClasses.clear();
336 mainClasses.clear();
337 compatClasses.clear();
338 obsoleteClasses.clear();
339 moduleClassMap.clear();
340 moduleNamespaceMap.clear();
341 funcIndex.clear();
342 legaleseTexts.clear();
343 serviceClasses.clear();
344 findAllClasses(tree->root());
345 findAllFunctions(tree->root());
346 findAllLegaleseTexts(tree->root());
347 findAllNamespaces(tree->root());
348#ifdef ZZZ_QDOC_QML
349 findAllQmlClasses(tree->root());
350#endif
351 findAllSince(tree->root());
352
353 PageGenerator::generateTree(tree, marker);
354
355 dcfClassesRoot.ref = "classes.html";
356 dcfClassesRoot.title = "Classes";
357 qSort(dcfClassesRoot.subsections);
358
359 dcfOverviewsRoot.ref = "overviews.html";
360 dcfOverviewsRoot.title = "Overviews";
361 qSort(dcfOverviewsRoot.subsections);
362
363 dcfExamplesRoot.ref = "examples.html";
364 dcfExamplesRoot.title = "Tutorial & Examples";
365 qSort(dcfExamplesRoot.subsections);
366
367 DcfSection qtRoot;
368 appendDcfSubSection(&qtRoot, dcfClassesRoot);
369 appendDcfSubSection(&qtRoot, dcfOverviewsRoot);
370 appendDcfSubSection(&qtRoot, dcfExamplesRoot);
371
372 generateDcf(project.toLower().simplified().replace(" ", "-"),
373 "index.html",
374 projectDescription, qtRoot);
375 generateDcf("designer",
376 "designer-manual.html",
377 "Qt Designer Manual",
378 dcfDesignerRoot);
379 generateDcf("linguist",
380 "linguist-manual.html",
381 "Qt Linguist Manual",
382 dcfLinguistRoot);
383 generateDcf("assistant",
384 "assistant-manual.html",
385 "Qt Assistant Manual",
386 dcfAssistantRoot);
387 generateDcf("qmake",
388 "qmake-manual.html",
389 "qmake Manual",
390 dcfQmakeRoot);
391
392 generateIndex(project.toLower().simplified().replace(" ", "-"),
393 projectUrl,
394 projectDescription);
395
396 helpProjectWriter->generate(myTree);
397}
398
399void HtmlGenerator::startText(const Node * /* relative */,
400 CodeMarker * /* marker */)
401{
402 inLink = false;
403 inContents = false;
404 inSectionHeading = false;
405 inTableHeader = false;
406 numTableRows = 0;
407 threeColumnEnumValueTable = true;
408 link.clear();
409 sectionNumber.clear();
410}
411
412int HtmlGenerator::generateAtom(const Atom *atom,
413 const Node *relative,
414 CodeMarker *marker)
415{
416 int skipAhead = 0;
417 static bool in_para = false;
418
419 switch (atom->type()) {
420 case Atom::AbstractLeft:
421 break;
422 case Atom::AbstractRight:
423 break;
424 case Atom::AutoLink:
425 if (!inLink && !inContents && !inSectionHeading) {
426 const Node *node = 0;
427 QString link = getLink(atom, relative, marker, &node);
428 if (!link.isEmpty()) {
429 beginLink(link, node, relative, marker);
430 generateLink(atom, relative, marker);
431 endLink();
432 }
433 else {
434 out() << protect(atom->string());
435 }
436 }
437 else {
438 out() << protect(atom->string());
439 }
440 break;
441 case Atom::BaseName:
442 break;
443 case Atom::BriefLeft:
444 if (relative->type() == Node::Fake) {
445 skipAhead = skipAtoms(atom, Atom::BriefRight);
446 break;
447 }
448
449 out() << "<p>";
450 if (relative->type() == Node::Property ||
451 relative->type() == Node::Variable) {
452 QString str;
453 atom = atom->next();
454 while (atom != 0 && atom->type() != Atom::BriefRight) {
455 if (atom->type() == Atom::String ||
456 atom->type() == Atom::AutoLink)
457 str += atom->string();
458 skipAhead++;
459 atom = atom->next();
460 }
461 str[0] = str[0].toLower();
462 if (str.right(1) == ".")
463 str.truncate(str.length() - 1);
464 out() << "This ";
465 if (relative->type() == Node::Property)
466 out() << "property";
467 else
468 out() << "variable";
469 QStringList words = str.split(" ");
470 if (!(words.first() == "contains" || words.first() == "specifies"
471 || words.first() == "describes" || words.first() == "defines"
472 || words.first() == "holds" || words.first() == "determines"))
473 out() << " holds ";
474 else
475 out() << " ";
476 out() << str << ".";
477 }
478 break;
479 case Atom::BriefRight:
480 if (relative->type() != Node::Fake)
481 out() << "</p>\n";
482 break;
483 case Atom::C:
484 out() << formattingLeftMap()[ATOM_FORMATTING_TELETYPE];
485 if (inLink) {
486 out() << protect(plainCode(atom->string()));
487 }
488 else {
489 out() << highlightedCode(atom->string(), marker, relative);
490 }
491 out() << formattingRightMap()[ATOM_FORMATTING_TELETYPE];
492 break;
493 case Atom::Code:
494 out() << "<pre>"
495 << trimmedTrailing(highlightedCode(indent(codeIndent,atom->string()),
496 marker,relative))
497 << "</pre>\n";
498 break;
499#ifdef QDOC_QML
500 case Atom::Qml:
501 out() << "<pre>"
502 << trimmedTrailing(highlightedCode(indent(codeIndent,atom->string()),
503 marker,relative))
504 << "</pre>\n";
505 break;
506#endif
507 case Atom::CodeNew:
508 out() << "<p>you can rewrite it as</p>\n"
509 << "<pre>"
510 << trimmedTrailing(highlightedCode(indent(codeIndent,atom->string()),
511 marker,relative))
512 << "</pre>\n";
513 break;
514 case Atom::CodeOld:
515 out() << "<p>For example, if you have code like</p>\n";
516 // fallthrough
517 case Atom::CodeBad:
518 out() << "<pre><font color=\"#404040\">"
519 << trimmedTrailing(protect(plainCode(indent(codeIndent,atom->string()))))
520 << "</font></pre>\n";
521 break;
522 case Atom::FootnoteLeft:
523 // ### For now
524 if (in_para) {
525 out() << "</p>\n";
526 in_para = false;
527 }
528 out() << "<!-- ";
529 break;
530 case Atom::FootnoteRight:
531 // ### For now
532 out() << "-->";
533 break;
534 case Atom::FormatElse:
535 case Atom::FormatEndif:
536 case Atom::FormatIf:
537 break;
538 case Atom::FormattingLeft:
539 out() << formattingLeftMap()[atom->string()];
540 if (atom->string() == ATOM_FORMATTING_PARAMETER) {
541 if (atom->next() != 0 && atom->next()->type() == Atom::String) {
542 QRegExp subscriptRegExp("([a-z]+)_([0-9n])");
543 if (subscriptRegExp.exactMatch(atom->next()->string())) {
544 out() << subscriptRegExp.cap(1) << "<sub>"
545 << subscriptRegExp.cap(2) << "</sub>";
546 skipAhead = 1;
547 }
548 }
549 }
550 break;
551 case Atom::FormattingRight:
552 if (atom->string() == ATOM_FORMATTING_LINK) {
553 endLink();
554 }
555 else {
556 out() << formattingRightMap()[atom->string()];
557 }
558 break;
559 case Atom::AnnotatedList:
560 {
561 QList<Node*> values = myTree->groups().values(atom->string());
562 NodeMap nodeMap;
563 for (int i = 0; i < values.size(); ++i) {
564 const Node* n = values.at(i);
565 if ((n->status() != Node::Internal) && (n->access() != Node::Private)) {
566 nodeMap.insert(n->nameForLists(),n);
567 }
568 }
569 generateAnnotatedList(relative, marker, nodeMap);
570 }
571 break;
572 case Atom::GeneratedList:
573 if (atom->string() == "annotatedclasses") {
574 generateAnnotatedList(relative, marker, nonCompatClasses);
575 }
576 else if (atom->string() == "classes") {
577 generateCompactList(relative, marker, nonCompatClasses);
578 }
579 else if (atom->string().contains("classesbymodule")) {
580 QString arg = atom->string().trimmed();
581 QString moduleName = atom->string().mid(atom->string().indexOf(
582 "classesbymodule") + 15).trimmed();
583 if (moduleClassMap.contains(moduleName))
584 generateAnnotatedList(relative, marker, moduleClassMap[moduleName]);
585 }
586 else if (atom->string().contains("classesbyedition")) {
587
588 QString arg = atom->string().trimmed();
589 QString editionName = atom->string().mid(atom->string().indexOf(
590 "classesbyedition") + 16).trimmed();
591
592 if (editionModuleMap.contains(editionName)) {
593
594 // Add all classes in the modules listed for that edition.
595 NodeMap editionClasses;
596 foreach (const QString &moduleName, editionModuleMap[editionName]) {
597 if (moduleClassMap.contains(moduleName))
598 editionClasses.unite(moduleClassMap[moduleName]);
599 }
600
601 // Add additional groups and remove groups of classes that
602 // should be excluded from the edition.
603
604 QMultiMap <QString, Node *> groups = myTree->groups();
605 foreach (const QString &groupName, editionGroupMap[editionName]) {
606 QList<Node *> groupClasses;
607 if (groupName.startsWith("-")) {
608 groupClasses = groups.values(groupName.mid(1));
609 foreach (const Node *node, groupClasses)
610 editionClasses.remove(node->name());
611 }
612 else {
613 groupClasses = groups.values(groupName);
614 foreach (const Node *node, groupClasses)
615 editionClasses.insert(node->name(), node);
616 }
617 }
618 generateAnnotatedList(relative, marker, editionClasses);
619 }
620 }
621 else if (atom->string() == "classhierarchy") {
622 generateClassHierarchy(relative, marker, nonCompatClasses);
623 }
624 else if (atom->string() == "compatclasses") {
625 generateCompactList(relative, marker, compatClasses);
626 }
627 else if (atom->string() == "obsoleteclasses") {
628 generateCompactList(relative, marker, obsoleteClasses);
629 }
630 else if (atom->string() == "functionindex") {
631 generateFunctionIndex(relative, marker);
632 }
633 else if (atom->string() == "legalese") {
634 generateLegaleseList(relative, marker);
635 }
636 else if (atom->string() == "mainclasses") {
637 generateCompactList(relative, marker, mainClasses);
638 }
639 else if (atom->string() == "services") {
640 generateCompactList(relative, marker, serviceClasses);
641 }
642 else if (atom->string() == "overviews") {
643 generateOverviewList(relative, marker);
644 }
645 else if (atom->string() == "namespaces") {
646 generateAnnotatedList(relative, marker, namespaceIndex);
647 }
648 else if (atom->string() == "related") {
649 const FakeNode *fake = static_cast<const FakeNode *>(relative);
650 if (fake && !fake->groupMembers().isEmpty()) {
651 NodeMap groupMembersMap;
652 foreach (const Node *node, fake->groupMembers()) {
653 if (node->type() == Node::Fake)
654 groupMembersMap[fullName(node, relative, marker)] = node;
655 }
656 generateAnnotatedList(fake, marker, groupMembersMap);
657 }
658 }
659 else if (atom->string() == "relatedinline") {
660 const FakeNode *fake = static_cast<const FakeNode *>(relative);
661 if (fake && !fake->groupMembers().isEmpty()) {
662 // Reverse the list into the original scan order.
663 // Should be sorted. But on what? It may not be a
664 // regular class or page definition.
665 QList<const Node *> list;
666 foreach (const Node *node, fake->groupMembers())
667 list.prepend(node);
668 foreach (const Node *node, list)
669 generateBody(node, marker);
670 }
671 }
672 break;
673 case Atom::SinceList:
674 {
675 NewSinceMaps::const_iterator nsmap;
676 nsmap = newSinceMaps.find(atom->string());
677 NewClassMaps::const_iterator ncmap;
678 ncmap = newClassMaps.find(atom->string());
679 if ((nsmap != newSinceMaps.constEnd()) && !nsmap.value().isEmpty()) {
680 QList<Section> sections;
681 QList<Section>::ConstIterator s;
682 for (int i=0; i<LastSinceType; ++i)
683 sections.append(Section(sinceTitle(i),QString(),QString()));
684
685 NodeMultiMap::const_iterator n = nsmap.value().constBegin();
686 while (n != nsmap.value().constEnd()) {
687 const Node* node = n.value();
688 switch (node->type()) {
689 case Node::Namespace:
690 sections[Namespace].appendMember((Node*)node);
691 break;
692 case Node::Class:
693 sections[Class].appendMember((Node*)node);
694 break;
695 case Node::Enum:
696 sections[Enum].appendMember((Node*)node);
697 break;
698 case Node::Typedef:
699 sections[Typedef].appendMember((Node*)node);
700 break;
701 case Node::Function: {
702 const FunctionNode* fn = static_cast<const FunctionNode*>(node);
703 if (fn->isMacro())
704 sections[Macro].appendMember((Node*)node);
705 else {
706 Node* p = fn->parent();
707 if (p) {
708 if (p->type() == Node::Class)
709 sections[MemberFunction].appendMember((Node*)node);
710 else if (p->type() == Node::Namespace) {
711 if (p->name().isEmpty())
712 sections[GlobalFunction].appendMember((Node*)node);
713 else
714 sections[NamespaceFunction].appendMember((Node*)node);
715 }
716 else
717 sections[GlobalFunction].appendMember((Node*)node);
718 }
719 else
720 sections[GlobalFunction].appendMember((Node*)node);
721 }
722 break;
723 }
724 case Node::Property:
725 sections[Property].appendMember((Node*)node);
726 break;
727 case Node::Variable:
728 sections[Variable].appendMember((Node*)node);
729 break;
730 case Node::QmlProperty:
731 sections[QmlProperty].appendMember((Node*)node);
732 break;
733 case Node::QmlSignal:
734 sections[QmlSignal].appendMember((Node*)node);
735 break;
736 case Node::QmlMethod:
737 sections[QmlMethod].appendMember((Node*)node);
738 break;
739 default:
740 break;
741 }
742 ++n;
743 }
744
745 /*
746 First generate the table of contents.
747 */
748 out() << "<ul>\n";
749 s = sections.constBegin();
750 while (s != sections.constEnd()) {
751 if (!(*s).members.isEmpty()) {
752
753 out() << "<li>"
754 << "<a href=\"#"
755 << Doc::canonicalTitle((*s).name)
756 << "\">"
757 << (*s).name
758 << "</a></li>\n";
759 }
760 ++s;
761 }
762 out() << "</ul>\n";
763
764 int idx = 0;
765 s = sections.constBegin();
766 while (s != sections.constEnd()) {
767 if (!(*s).members.isEmpty()) {
768 out() << "<a name=\""
769 << Doc::canonicalTitle((*s).name)
770 << "\"></a>\n";
771 out() << "<h3>" << protect((*s).name) << "</h3>\n";
772 if (idx == Class)
773 generateCompactList(0, marker, ncmap.value(), QString("Q"));
774 else if (idx == MemberFunction) {
775 ParentMaps parentmaps;
776 ParentMaps::iterator pmap;
777 NodeList::const_iterator i = s->members.constBegin();
778 while (i != s->members.constEnd()) {
779 Node* p = (*i)->parent();
780 pmap = parentmaps.find(p);
781 if (pmap == parentmaps.end())
782 pmap = parentmaps.insert(p,NodeMultiMap());
783 pmap->insert((*i)->name(),(*i));
784 ++i;
785 }
786 pmap = parentmaps.begin();
787 while (pmap != parentmaps.end()) {
788 NodeList nlist = pmap->values();
789 out() << "<p>Class ";
790
791 out() << "<a href=\""
792 << linkForNode(pmap.key(), 0)
793 << "\">";
794 QStringList pieces = fullName(pmap.key(), 0, marker).split("::");
795 out() << protect(pieces.last());
796 out() << "</a>" << ":</p>\n";
797
798 generateSection(nlist, 0, marker, CodeMarker::Summary);
799 out() << "<br />";
800 ++pmap;
801 }
802 }
803 else
804 generateSection(s->members, 0, marker, CodeMarker::Summary);
805 }
806 ++idx;
807 ++s;
808 }
809 }
810 }
811 break;
812 case Atom::Image:
813 case Atom::InlineImage:
814 {
815 QString fileName = imageFileName(relative, atom->string());
816 QString text;
817 if (atom->next() != 0)
818 text = atom->next()->string();
819 if (atom->type() == Atom::Image)
820 out() << "<p align=\"center\">";
821 if (fileName.isEmpty()) {
822 out() << "<font color=\"red\">[Missing image "
823 << protect(atom->string()) << "]</font>";
824 }
825 else {
826 out() << "<img src=\"" << protect(fileName) << "\"";
827 if (!text.isEmpty())
828 out() << " alt=\"" << protect(text) << "\"";
829 out() << " />";
830 helpProjectWriter->addExtraFile(fileName);
831 }
832 if (atom->type() == Atom::Image)
833 out() << "</p>";
834 }
835 break;
836 case Atom::ImageText:
837 break;
838 case Atom::LegaleseLeft:
839 out() << "<div style=\"padding: 0.5em; background: #e0e0e0; color: black\">";
840 break;
841 case Atom::LegaleseRight:
842 out() << "</div>";
843 break;
844 case Atom::LineBreak:
845 out() << "<br />";
846 break;
847 case Atom::Link:
848 {
849 const Node *node = 0;
850 QString myLink = getLink(atom, relative, marker, &node);
851 if (myLink.isEmpty()) {
852 relative->doc().location().warning(tr("Cannot link to '%1' in %2")
853 .arg(atom->string())
854 .arg(marker->plainFullName(relative)));
855 }
856 beginLink(myLink, node, relative, marker);
857 skipAhead = 1;
858 }
859 break;
860 case Atom::LinkNode:
861 {
862 const Node *node = CodeMarker::nodeForString(atom->string());
863 beginLink(linkForNode(node, relative), node, relative, marker);
864 skipAhead = 1;
865 }
866 break;
867 case Atom::ListLeft:
868 if (in_para) {
869 out() << "</p>\n";
870 in_para = false;
871 }
872 if (atom->string() == ATOM_LIST_BULLET) {
873 out() << "<ul>\n";
874 }
875 else if (atom->string() == ATOM_LIST_TAG) {
876 out() << "<dl>\n";
877 }
878 else if (atom->string() == ATOM_LIST_VALUE) {
879 threeColumnEnumValueTable = isThreeColumnEnumValueTable(atom);
880 if (threeColumnEnumValueTable) {
881 out() << "<p><table class=\"valuelist\" border=\"1\" cellpadding=\"2\" "
882 << "cellspacing=\"1\" width=\"100%\">\n"
883 << "<tr><th width=\"25%\">Constant</th>"
884 << "<th width=\"15%\">Value</th>"
885 << "<th width=\"60%\">Description</th></tr>\n";
886 }
887 else {
888 out() << "<p><table class=\"valuelist\" border=\"1\" cellpadding=\"2\" "
889 << "cellspacing=\"1\" width=\"40%\">\n"
890 << "<tr><th width=\"60%\">Constant</th><th "
891 << "width=\"40%\">Value</th></tr>\n";
892 }
893 }
894 else {
895 out() << "<ol type=";
896 if (atom->string() == ATOM_LIST_UPPERALPHA) {
897 out() << "\"A\"";
898 }
899 else if (atom->string() == ATOM_LIST_LOWERALPHA) {
900 out() << "\"a\"";
901 }
902 else if (atom->string() == ATOM_LIST_UPPERROMAN) {
903 out() << "\"I\"";
904 }
905 else if (atom->string() == ATOM_LIST_LOWERROMAN) {
906 out() << "\"i\"";
907 }
908 else { // (atom->string() == ATOM_LIST_NUMERIC)
909 out() << "\"1\"";
910 }
911 if (atom->next() != 0 && atom->next()->string().toInt() != 1)
912 out() << " start=\"" << atom->next()->string() << "\"";
913 out() << ">\n";
914 }
915 break;
916 case Atom::ListItemNumber:
917 break;
918 case Atom::ListTagLeft:
919 if (atom->string() == ATOM_LIST_TAG) {
920 out() << "<dt>";
921 }
922 else { // (atom->string() == ATOM_LIST_VALUE)
923 // ### Trenton
924
925 out() << "<tr><td valign=\"top\"><tt>"
926 << protect(plainCode(marker->markedUpEnumValue(atom->next()->string(),
927 relative)))
928 << "</tt></td><td align=\"center\" valign=\"top\">";
929
930 QString itemValue;
931 if (relative->type() == Node::Enum) {
932 const EnumNode *enume = static_cast<const EnumNode *>(relative);
933 itemValue = enume->itemValue(atom->next()->string());
934 }
935
936 if (itemValue.isEmpty())
937 out() << "?";
938 else
939 out() << "<tt>" << protect(itemValue) << "</tt>";
940
941 skipAhead = 1;
942 }
943 break;
944 case Atom::ListTagRight:
945 if (atom->string() == ATOM_LIST_TAG)
946 out() << "</dt>\n";
947 break;
948 case Atom::ListItemLeft:
949 if (atom->string() == ATOM_LIST_TAG) {
950 out() << "<dd>";
951 }
952 else if (atom->string() == ATOM_LIST_VALUE) {
953 if (threeColumnEnumValueTable) {
954 out() << "</td><td valign=\"top\">";
955 if (matchAhead(atom, Atom::ListItemRight))
956 out() << "&nbsp;";
957 }
958 }
959 else {
960 out() << "<li>";
961 }
962 if (matchAhead(atom, Atom::ParaLeft))
963 skipAhead = 1;
964 break;
965 case Atom::ListItemRight:
966 if (atom->string() == ATOM_LIST_TAG) {
967 out() << "</dd>\n";
968 }
969 else if (atom->string() == ATOM_LIST_VALUE) {
970 out() << "</td></tr>\n";
971 }
972 else {
973 out() << "</li>\n";
974 }
975 break;
976 case Atom::ListRight:
977 if (atom->string() == ATOM_LIST_BULLET) {
978 out() << "</ul>\n";
979 }
980 else if (atom->string() == ATOM_LIST_TAG) {
981 out() << "</dl>\n";
982 }
983 else if (atom->string() == ATOM_LIST_VALUE) {
984 out() << "</table></p>\n";
985 }
986 else {
987 out() << "</ol>\n";
988 }
989 break;
990 case Atom::Nop:
991 break;
992 case Atom::ParaLeft:
993 out() << "<p>";
994 in_para = true;
995 break;
996 case Atom::ParaRight:
997 endLink();
998 if (in_para) {
999 out() << "</p>\n";
1000 in_para = false;
1001 }
1002 //if (!matchAhead(atom, Atom::ListItemRight) && !matchAhead(atom, Atom::TableItemRight))
1003 // out() << "</p>\n";
1004 break;
1005 case Atom::QuotationLeft:
1006 out() << "<blockquote>";
1007 break;
1008 case Atom::QuotationRight:
1009 out() << "</blockquote>\n";
1010 break;
1011 case Atom::RawString:
1012 out() << atom->string();
1013 break;
1014 case Atom::SectionLeft:
1015#if 0
1016 {
1017 int nextLevel = atom->string().toInt();
1018 if (sectionNumber.size() < nextLevel) {
1019 do {
1020 sectionNumber.append("1");
1021 } while (sectionNumber.size() < nextLevel);
1022 }
1023 else {
1024 while (sectionNumber.size() > nextLevel) {
1025 sectionNumber.removeLast();
1026 }
1027 sectionNumber.last() = QString::number(sectionNumber.last().toInt() + 1);
1028 }
1029 out() << "<a name=\"sec-" << sectionNumber.join("-") << "\"></a>\n";
1030 }
1031#else
1032 out() << "<a name=\"" << Doc::canonicalTitle(Text::sectionHeading(atom).toString())
1033 << "\"></a>\n";
1034#endif
1035 break;
1036 case Atom::SectionRight:
1037 break;
1038 case Atom::SectionHeadingLeft:
1039 out() << "<h" + QString::number(atom->string().toInt() + hOffset(relative)) + ">";
1040 inSectionHeading = true;
1041 break;
1042 case Atom::SectionHeadingRight:
1043 out() << "</h" + QString::number(atom->string().toInt() + hOffset(relative)) + ">\n";
1044 inSectionHeading = false;
1045 break;
1046 case Atom::SidebarLeft:
1047 break;
1048 case Atom::SidebarRight:
1049 break;
1050 case Atom::String:
1051 if (inLink && !inContents && !inSectionHeading) {
1052 generateLink(atom, relative, marker);
1053 }
1054 else {
1055 out() << protect(atom->string());
1056 }
1057 break;
1058 case Atom::TableLeft:
1059 if (in_para) {
1060 out() << "</p>\n";
1061 in_para = false;
1062 }
1063 if (!atom->string().isEmpty()) {
1064 if (atom->string().contains("%"))
1065 out() << "<p><table class=\"generic\" width=\"" << atom->string() << "\" "
1066 << "align=\"center\" cellpadding=\"2\" "
1067 << "cellspacing=\"1\" border=\"0\">\n";
1068 else {
1069 out() << "<p><table class=\"generic\" align=\"center\" cellpadding=\"2\" "
1070 << "cellspacing=\"1\" border=\"0\">\n";
1071 }
1072 }
1073 else {
1074 out() << "<p><table class=\"generic\" align=\"center\" cellpadding=\"2\" "
1075 << "cellspacing=\"1\" border=\"0\">\n";
1076 }
1077 numTableRows = 0;
1078 break;
1079 case Atom::TableRight:
1080 out() << "</table></p>\n";
1081 break;
1082 case Atom::TableHeaderLeft:
1083 out() << "<thead><tr valign=\"top\" class=\"qt-style\">";
1084 inTableHeader = true;
1085 break;
1086 case Atom::TableHeaderRight:
1087 out() << "</tr>";
1088 if (matchAhead(atom, Atom::TableHeaderLeft)) {
1089 skipAhead = 1;
1090 out() << "\n<tr valign=\"top\" class=\"qt-style\">";
1091 }
1092 else {
1093 out() << "</thead>\n";
1094 inTableHeader = false;
1095 }
1096 break;
1097 case Atom::TableRowLeft:
1098 if (++numTableRows % 2 == 1)
1099 out() << "<tr valign=\"top\" class=\"odd\">";
1100 else
1101 out() << "<tr valign=\"top\" class=\"even\">";
1102 break;
1103 case Atom::TableRowRight:
1104 out() << "</tr>\n";
1105 break;
1106 case Atom::TableItemLeft:
1107 {
1108 if (inTableHeader)
1109 out() << "<th";
1110 else
1111 out() << "<td";
1112
1113 QStringList spans = atom->string().split(",");
1114 if (spans.size() == 2) {
1115 if (spans.at(0) != "1")
1116 out() << " colspan=\"" << spans.at(0) << "\"";
1117 if (spans.at(1) != "1")
1118 out() << " rowspan=\"" << spans.at(1) << "\"";
1119 out() << ">";
1120 }
1121 if (matchAhead(atom, Atom::ParaLeft))
1122 skipAhead = 1;
1123 }
1124 break;
1125 case Atom::TableItemRight:
1126 if (inTableHeader)
1127 out() << "</th>";
1128 else
1129 out() << "</td>";
1130 if (matchAhead(atom, Atom::ParaLeft))
1131 skipAhead = 1;
1132 break;
1133 case Atom::TableOfContents:
1134 {
1135 int numColumns = 1;
1136 const Node *node = relative;
1137
1138 Doc::SectioningUnit sectioningUnit = Doc::Section4;
1139 QStringList params = atom->string().split(",");
1140 QString columnText = params.at(0);
1141 QStringList pieces = columnText.split(" ", QString::SkipEmptyParts);
1142 if (pieces.size() >= 2) {
1143 columnText = pieces.at(0);
1144 pieces.pop_front();
1145 QString path = pieces.join(" ").trimmed();
1146 node = findNodeForTarget(path, relative, marker, atom);
1147 }
1148
1149 if (params.size() == 2) {
1150 numColumns = qMax(columnText.toInt(), numColumns);
1151 sectioningUnit = (Doc::SectioningUnit)params.at(1).toInt();
1152 }
1153
1154 if (node)
1155 generateTableOfContents(node,
1156 marker,
1157 sectioningUnit,
1158 numColumns,
1159 relative);
1160 }
1161 break;
1162 case Atom::Target:
1163 out() << "<a name=\"" << Doc::canonicalTitle(atom->string()) << "\"></a>";
1164 break;
1165 case Atom::UnhandledFormat:
1166 out() << "<font color=\"red\"><b>&lt;Missing HTML&gt;</b></font>";
1167 break;
1168 case Atom::UnknownCommand:
1169 out() << "<font color=\"red\"><b><code>\\" << protect(atom->string())
1170 << "</code></b></font>";
1171 break;
1172#ifdef QDOC_QML
1173 case Atom::QmlText:
1174 case Atom::EndQmlText:
1175 // don't do anything with these. They are just tags.
1176 break;
1177#endif
1178 default:
1179 unknownAtom(atom);
1180 }
1181 return skipAhead;
1182}
1183
1184void HtmlGenerator::generateClassLikeNode(const InnerNode *inner,
1185 CodeMarker *marker)
1186{
1187 QList<Section> sections;
1188 QList<Section>::ConstIterator s;
1189
1190 const ClassNode *classe = 0;
1191 const NamespaceNode *namespasse = 0;
1192
1193 QString title;
1194 QString rawTitle;
1195 QString fullTitle;
1196 if (inner->type() == Node::Namespace) {
1197 namespasse = static_cast<const NamespaceNode *>(inner);
1198 rawTitle = marker->plainName(inner);
1199 fullTitle = marker->plainFullName(inner);
1200 title = rawTitle + " Namespace Reference";
1201 }
1202 else if (inner->type() == Node::Class) {
1203 classe = static_cast<const ClassNode *>(inner);
1204 rawTitle = marker->plainName(inner);
1205 fullTitle = marker->plainFullName(inner);
1206 title = rawTitle + " Class Reference";
1207 }
1208
1209 DcfSection classSection;
1210 classSection.title = title;
1211 classSection.ref = linkForNode(inner, 0);
1212 classSection.keywords += qMakePair(inner->name(), classSection.ref);
1213
1214 Text subtitleText;
1215 if (rawTitle != fullTitle)
1216 subtitleText << "(" << Atom(Atom::AutoLink, fullTitle) << ")"
1217 << Atom(Atom::LineBreak);
1218
1219 QString fixedModule = inner->moduleName();
1220 if (fixedModule == "Qt3SupportLight")
1221 fixedModule = "Qt3Support";
1222 if (!fixedModule.isEmpty())
1223 subtitleText << "[" << Atom(Atom::AutoLink, fixedModule) << " module]";
1224
1225 if (fixedModule.isEmpty()) {
1226 QMultiMap<QString, QString> publicGroups = myTree->publicGroups();
1227 QList<QString> groupNames = publicGroups.values(inner->name());
1228 if (!groupNames.isEmpty()) {
1229 qSort(groupNames.begin(), groupNames.end());
1230 subtitleText << "[";
1231 for (int j=0; j<groupNames.count(); j++) {
1232 subtitleText << Atom(Atom::AutoLink, groupNames[j]);
1233 if (j<groupNames.count()-1)
1234 subtitleText <<", ";
1235 }
1236 subtitleText << "]";
1237 }
1238 }
1239
1240 generateHeader(title, inner, marker, true);
1241 generateTitle(title, subtitleText, SmallSubTitle, inner, marker);
1242
1243#ifdef QDOC_QML
1244 if (classe && !classe->qmlElement().isEmpty()) {
1245 generateInstantiatedBy(classe,marker);
1246 }
1247#endif
1248
1249 generateBrief(inner, marker);
1250 generateIncludes(inner, marker);
1251 generateStatus(inner, marker);
1252 if (classe) {
1253 generateModuleWarning(classe, marker);
1254 generateInherits(classe, marker);
1255 generateInheritedBy(classe, marker);
1256 }
1257 generateThreadSafeness(inner, marker);
1258 generateSince(inner, marker);
1259
1260 out() << "<ul>\n";
1261
1262 QString membersLink = generateListOfAllMemberFile(inner, marker);
1263 if (!membersLink.isEmpty())
1264 out() << "<li><a href=\"" << membersLink << "\">"
1265 << "List of all members, including inherited members</a></li>\n";
1266
1267 QString obsoleteLink = generateLowStatusMemberFile(inner,
1268 marker,
1269 CodeMarker::Obsolete);
1270 if (!obsoleteLink.isEmpty())
1271 out() << "<li><a href=\"" << obsoleteLink << "\">"
1272 << "Obsolete members</a></li>\n";
1273
1274 QString compatLink = generateLowStatusMemberFile(inner,
1275 marker,
1276 CodeMarker::Compat);
1277 if (!compatLink.isEmpty())
1278 out() << "<li><a href=\"" << compatLink << "\">"
1279 << "Qt 3 support members</a></li>\n";
1280
1281 out() << "</ul>\n";
1282
1283 bool needOtherSection = false;
1284
1285 sections = marker->sections(inner, CodeMarker::Summary, CodeMarker::Okay);
1286 s = sections.begin();
1287 while (s != sections.end()) {
1288 if (s->members.isEmpty() && s->reimpMembers.isEmpty()) {
1289 if (!s->inherited.isEmpty())
1290 needOtherSection = true;
1291 }
1292 else {
1293 if (!s->members.isEmpty()) {
1294 out() << "<hr />\n";
1295 out() << "<a name=\""
1296 << registerRef((*s).name.toLower())
1297 << "\"></a>\n";
1298 out() << "<h2>" << protect((*s).name) << "</h2>\n";
1299 generateSection(s->members, inner, marker, CodeMarker::Summary);
1300 }
1301 if (!s->reimpMembers.isEmpty()) {
1302 QString name = QString("Reimplemented ") + (*s).name;
1303 out() << "<hr />\n";
1304 out() << "<a name=\""
1305 << registerRef(name.toLower())
1306 << "\"></a>\n";
1307 out() << "<h2>" << protect(name) << "</h2>\n";
1308 generateSection(s->reimpMembers, inner, marker, CodeMarker::Summary);
1309 }
1310
1311 if (!s->inherited.isEmpty()) {
1312 out() << "<ul>\n";
1313 generateSectionInheritedList(*s, inner, marker, true);
1314 out() << "</ul>\n";
1315 }
1316 }
1317 ++s;
1318 }
1319
1320 if (needOtherSection) {
1321 out() << "<h3>Additional Inherited Members</h3>\n"
1322 "<ul>\n";
1323
1324 s = sections.begin();
1325 while (s != sections.end()) {
1326 if (s->members.isEmpty() && !s->inherited.isEmpty())
1327 generateSectionInheritedList(*s, inner, marker);
1328 ++s;
1329 }
1330 out() << "</ul>\n";
1331 }
1332
1333 out() << "<a name=\"" << registerRef("details") << "\"></a>\n";
1334
1335 if (!inner->doc().isEmpty()) {
1336 out() << "<hr />\n"
1337 << "<h2>" << "Detailed Description" << "</h2>\n";
1338 generateBody(inner, marker);
1339 generateAlsoList(inner, marker);
1340 }
1341
1342 sections = marker->sections(inner, CodeMarker::Detailed, CodeMarker::Okay);
1343 s = sections.begin();
1344 while (s != sections.end()) {
1345 out() << "<hr />\n";
1346 out() << "<h2>" << protect((*s).name) << "</h2>\n";
1347
1348 NodeList::ConstIterator m = (*s).members.begin();
1349 while (m != (*s).members.end()) {
1350 if ((*m)->access() != Node::Private) { // ### check necessary?
1351 if ((*m)->type() != Node::Class)
1352 generateDetailedMember(*m, inner, marker);
1353 else {
1354 out() << "<h3> class ";
1355 generateFullName(*m, inner, marker);
1356 out() << "</h3>";
1357 generateBrief(*m, marker, inner);
1358 }
1359
1360 QStringList names;
1361 names << (*m)->name();
1362 if ((*m)->type() == Node::Function) {
1363 const FunctionNode *func = reinterpret_cast<const FunctionNode *>(*m);
1364 if (func->metaness() == FunctionNode::Ctor ||
1365 func->metaness() == FunctionNode::Dtor ||
1366 func->overloadNumber() != 1)
1367 names.clear();
1368 }
1369 else if ((*m)->type() == Node::Property) {
1370 const PropertyNode *prop = reinterpret_cast<const PropertyNode *>(*m);
1371 if (!prop->getters().isEmpty() &&
1372 !names.contains(prop->getters().first()->name()))
1373 names << prop->getters().first()->name();
1374 if (!prop->setters().isEmpty())
1375 names << prop->setters().first()->name();
1376 if (!prop->resetters().isEmpty())
1377 names << prop->resetters().first()->name();
1378 }
1379 else if ((*m)->type() == Node::Enum) {
1380 const EnumNode *enume = reinterpret_cast<const EnumNode*>(*m);
1381 if (enume->flagsType())
1382 names << enume->flagsType()->name();
1383
1384 foreach (const QString &enumName,
1385 enume->doc().enumItemNames().toSet() -
1386 enume->doc().omitEnumItemNames().toSet())
1387 names << plainCode(marker->markedUpEnumValue(enumName,
1388 enume));
1389 }
1390 foreach (const QString &name, names)
1391 classSection.keywords += qMakePair(name,linkForNode(*m,0));
1392 }
1393 ++m;
1394 }
1395 ++s;
1396 }
1397 generateFooter(inner);
1398
1399 if (!membersLink.isEmpty()) {
1400 DcfSection membersSection;
1401 membersSection.title = "List of all members";
1402 membersSection.ref = membersLink;
1403 appendDcfSubSection(&classSection, membersSection);
1404 }
1405 if (!obsoleteLink.isEmpty()) {
1406 DcfSection obsoleteSection;
1407 obsoleteSection.title = "Obsolete members";
1408 obsoleteSection.ref = obsoleteLink;
1409 appendDcfSubSection(&classSection, obsoleteSection);
1410 }
1411 if (!compatLink.isEmpty()) {
1412 DcfSection compatSection;
1413 compatSection.title = "Qt 3 support members";
1414 compatSection.ref = compatLink;
1415 appendDcfSubSection(&classSection, compatSection);
1416 }
1417
1418 appendDcfSubSection(&dcfClassesRoot, classSection);
1419}
1420
1421void HtmlGenerator::generateFakeNode(const FakeNode *fake, CodeMarker *marker)
1422{
1423 SubTitleSize subTitleSize = LargeSubTitle;
1424 DcfSection fakeSection;
1425 fakeSection.title = fake->fullTitle();
1426 fakeSection.ref = linkForNode(fake, 0);
1427
1428 QList<Section> sections;
1429 QList<Section>::const_iterator s;
1430
1431 QString htmlTitle = fake->fullTitle();
1432 if (fake->subType() == Node::File && !fake->subTitle().isEmpty()) {
1433 subTitleSize = SmallSubTitle;
1434 htmlTitle += " (" + fake->subTitle() + ")";
1435 }
1436
1437 generateHeader(htmlTitle, fake, marker, true);
1438 generateTitle(fake->fullTitle(),
1439 Text() << fake->subTitle(),
1440 subTitleSize,
1441 fake,
1442 marker);
1443
1444 if (fake->subType() == Node::Module) {
1445 // Generate brief text and status for modules.
1446 generateBrief(fake, marker);
1447 generateStatus(fake, marker);
1448
1449 if (moduleNamespaceMap.contains(fake->name())) {
1450 out() << "<h2>Namespaces</h2>\n";
1451 generateAnnotatedList(fake, marker, moduleNamespaceMap[fake->name()]);
1452 }
1453 if (moduleClassMap.contains(fake->name())) {
1454 out() << "<h2>Classes</h2>\n";
1455 generateAnnotatedList(fake, marker, moduleClassMap[fake->name()]);
1456 }
1457 }
1458 else if (fake->subType() == Node::HeaderFile) {
1459 // Generate brief text and status for modules.
1460 generateBrief(fake, marker);
1461 generateStatus(fake, marker);
1462
1463 out() << "<ul>\n";
1464
1465 QString membersLink = generateListOfAllMemberFile(fake, marker);
1466 if (!membersLink.isEmpty())
1467 out() << "<li><a href=\"" << membersLink << "\">"
1468 << "List of all members, including inherited members</a></li>\n";
1469
1470 QString obsoleteLink = generateLowStatusMemberFile(fake,
1471 marker,
1472 CodeMarker::Obsolete);
1473 if (!obsoleteLink.isEmpty())
1474 out() << "<li><a href=\"" << obsoleteLink << "\">"
1475 << "Obsolete members</a></li>\n";
1476
1477 QString compatLink = generateLowStatusMemberFile(fake,
1478 marker,
1479 CodeMarker::Compat);
1480 if (!compatLink.isEmpty())
1481 out() << "<li><a href=\"" << compatLink << "\">"
1482 << "Qt 3 support members</a></li>\n";
1483
1484 out() << "</ul>\n";
1485
1486 if (!membersLink.isEmpty()) {
1487 DcfSection membersSection;
1488 membersSection.title = "List of all members";
1489 membersSection.ref = membersLink;
1490 appendDcfSubSection(&fakeSection, membersSection);
1491 }
1492 if (!obsoleteLink.isEmpty()) {
1493 DcfSection obsoleteSection;
1494 obsoleteSection.title = "Obsolete members";
1495 obsoleteSection.ref = obsoleteLink;
1496 appendDcfSubSection(&fakeSection, obsoleteSection);
1497 }
1498 if (!compatLink.isEmpty()) {
1499 DcfSection compatSection;
1500 compatSection.title = "Qt 3 support members";
1501 compatSection.ref = compatLink;
1502 appendDcfSubSection(&fakeSection, compatSection);
1503 }
1504 }
1505#ifdef QDOC_QML
1506 else if (fake->subType() == Node::QmlClass) {
1507 const QmlClassNode* qml_cn = static_cast<const QmlClassNode*>(fake);
1508 const ClassNode* cn = qml_cn->classNode();
1509 generateQmlInherits(qml_cn, marker);
1510 generateQmlInstantiates(qml_cn, marker);
1511 generateBrief(qml_cn, marker);
1512 sections = marker->qmlSections(qml_cn,CodeMarker::Summary);
1513 s = sections.begin();
1514 while (s != sections.end()) {
1515 out() << "<a name=\"" << registerRef((*s).name) << "\"></a>\n";
1516 out() << "<h2>" << protect((*s).name) << "</h2>\n";
1517 generateQmlSummary(*s,fake,marker);
1518 ++s;
1519 }
1520
1521 out() << "<a name=\"" << registerRef("details") << "\"></a>\n";
1522 out() << "<h2>" << "Detailed Description" << "</h2>\n";
1523 generateBody(fake, marker);
1524 if (cn)
1525 generateQmlText(cn->doc().body(), cn, marker, fake->name());
1526 generateAlsoList(fake, marker);
1527 out() << "<hr />\n";
1528
1529 sections = marker->qmlSections(qml_cn,CodeMarker::Detailed);
1530 s = sections.begin();
1531 while (s != sections.end()) {
1532 out() << "<h2>" << protect((*s).name) << "</h2>\n";
1533 NodeList::ConstIterator m = (*s).members.begin();
1534 while (m != (*s).members.end()) {
1535 generateDetailedQmlMember(*m, fake, marker);
1536 out() << "<br />\n";
1537 fakeSection.keywords += qMakePair((*m)->name(),
1538 linkForNode(*m,0));
1539 ++m;
1540 }
1541 ++s;
1542 }
1543 generateFooter(fake);
1544 return;
1545 }
1546#endif
1547
1548 sections = marker->sections(fake, CodeMarker::Summary, CodeMarker::Okay);
1549 s = sections.begin();
1550 while (s != sections.end()) {
1551 out() << "<a name=\"" << registerRef((*s).name) << "\"></a>\n";
1552 out() << "<h2>" << protect((*s).name) << "</h2>\n";
1553 generateSectionList(*s, fake, marker, CodeMarker::Summary);
1554 ++s;
1555 }
1556
1557 Text brief = fake->doc().briefText();
1558 if (fake->subType() == Node::Module && !brief.isEmpty()) {
1559 out() << "<a name=\"" << registerRef("details") << "\"></a>\n";
1560 out() << "<h2>" << "Detailed Description" << "</h2>\n";
1561 }
1562
1563 generateBody(fake, marker);
1564 generateAlsoList(fake, marker);
1565
1566 if (!fake->groupMembers().isEmpty()) {
1567 NodeMap groupMembersMap;
1568 foreach (const Node *node, fake->groupMembers()) {
1569 if (node->type() == Node::Class || node->type() == Node::Namespace)
1570 groupMembersMap[node->name()] = node;
1571 }
1572 generateAnnotatedList(fake, marker, groupMembersMap);
1573 }
1574
1575 fakeSection.keywords += qMakePair(fakeSection.title, fakeSection.ref);
1576
1577 sections = marker->sections(fake, CodeMarker::Detailed, CodeMarker::Okay);
1578 s = sections.begin();
1579 while (s != sections.end()) {
1580 out() << "<hr />\n";
1581 out() << "<h2>" << protect((*s).name) << "</h2>\n";
1582
1583 NodeList::ConstIterator m = (*s).members.begin();
1584 while (m != (*s).members.end()) {
1585 generateDetailedMember(*m, fake, marker);
1586 fakeSection.keywords += qMakePair((*m)->name(), linkForNode(*m, 0));
1587 ++m;
1588 }
1589 ++s;
1590 }
1591 generateFooter(fake);
1592
1593 if (fake->subType() == Node::Example) {
1594 appendDcfSubSection(&dcfExamplesRoot, fakeSection);
1595 }
1596 else if (fake->subType() != Node::File) {
1597 QString contentsPage = fake->links().value(Node::ContentsLink).first;
1598
1599 if (contentsPage == "Qt Designer Manual") {
1600 appendDcfSubSection(&dcfDesignerRoot, fakeSection);
1601 }
1602 else if (contentsPage == "Qt Linguist Manual") {
1603 appendDcfSubSection(&dcfLinguistRoot, fakeSection);
1604 }
1605 else if (contentsPage == "Qt Assistant Manual") {
1606 appendDcfSubSection(&dcfAssistantRoot, fakeSection);
1607 }
1608 else if (contentsPage == "qmake Manual") {
1609 appendDcfSubSection(&dcfQmakeRoot, fakeSection);
1610 }
1611 else {
1612 appendDcfSubSection(&dcfOverviewsRoot, fakeSection);
1613 }
1614 }
1615}
1616
1617QString HtmlGenerator::fileExtension(const Node * /* node */)
1618{
1619 return "html";
1620}
1621
1622void HtmlGenerator::generateHeader(const QString& title,
1623 const Node *node,
1624 CodeMarker *marker,
1625 bool mainPage)
1626{
1627 out() << "<?xml version=\"1.0\" encoding=\"iso-8859-1\"?>\n";
1628
1629 out() << "<!DOCTYPE html\n"
1630 " PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"DTD/xhtml1-strict.dtd\">\n"
1631 "<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\" lang=\"en\">\n";
1632
1633 QString shortVersion;
1634 if ((project != "Qtopia") && (project != "Qt Extended")) {
1635 shortVersion = project + " " + shortVersion + ": ";
1636 if (node && !node->doc().location().isEmpty())
1637 out() << "<!-- " << node->doc().location().fileName() << " -->\n";
1638
1639 shortVersion = myTree->version();
1640 if (shortVersion.count(QChar('.')) == 2)
1641 shortVersion.truncate(shortVersion.lastIndexOf(QChar('.')));
1642 if (!shortVersion.isEmpty()) {
1643 if (project == "QSA")
1644 shortVersion = "QSA " + shortVersion + ": ";
1645 else
1646 shortVersion = "Qt " + shortVersion + ": ";
1647 }
1648 }
1649
1650 out() << "<head>\n"
1651 " <title>" << shortVersion << protect(title) << "</title>\n";
1652 if (!style.isEmpty())
1653 out() << " <style type=\"text/css\">" << style << "</style>\n";
1654
1655 const QMap<QString, QString> &metaMap = node->doc().metaTagMap();
1656 if (!metaMap.isEmpty()) {
1657 QMapIterator<QString, QString> i(metaMap);
1658 while (i.hasNext()) {
1659 i.next();
1660 out() << " <meta name=\"" << protect(i.key()) << "\" contents=\""
1661 << protect(i.value()) << "\" />\n";
1662 }
1663 }
1664
1665 navigationLinks.clear();
1666
1667 if (node && !node->links().empty()) {
1668 QPair<QString,QString> linkPair;
1669 QPair<QString,QString> anchorPair;
1670 const Node *linkNode;
1671
1672 if (node->links().contains(Node::PreviousLink)) {
1673 linkPair = node->links()[Node::PreviousLink];
1674 linkNode = findNodeForTarget(linkPair.first, node, marker);
1675 if (!linkNode || linkNode == node)
1676 anchorPair = linkPair;
1677 else
1678 anchorPair = anchorForNode(linkNode);
1679
1680 out() << " <link rel=\"prev\" href=\""
1681 << anchorPair.first << "\" />\n";
1682
1683 navigationLinks += "[Previous: <a href=\"" + anchorPair.first + "\">";
1684 if (linkPair.first == linkPair.second && !anchorPair.second.isEmpty())
1685 navigationLinks += protect(anchorPair.second);
1686 else
1687 navigationLinks += protect(linkPair.second);
1688 navigationLinks += "</a>]\n";
1689 }
1690 if (node->links().contains(Node::ContentsLink)) {
1691 linkPair = node->links()[Node::ContentsLink];
1692 linkNode = findNodeForTarget(linkPair.first, node, marker);
1693 if (!linkNode || linkNode == node)
1694 anchorPair = linkPair;
1695 else
1696 anchorPair = anchorForNode(linkNode);
1697
1698 out() << " <link rel=\"contents\" href=\""
1699 << anchorPair.first << "\" />\n";
1700
1701 navigationLinks += "[<a href=\"" + anchorPair.first + "\">";
1702 if (linkPair.first == linkPair.second && !anchorPair.second.isEmpty())
1703 navigationLinks += protect(anchorPair.second);
1704 else
1705 navigationLinks += protect(linkPair.second);
1706 navigationLinks += "</a>]\n";
1707 }
1708 if (node->links().contains(Node::NextLink)) {
1709 linkPair = node->links()[Node::NextLink];