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];
1710 linkNode = findNodeForTarget(linkPair.first, node, marker);
1711 if (!linkNode || linkNode == node)
1712 anchorPair = linkPair;
1713 else
1714 anchorPair = anchorForNode(linkNode);
1715
1716 out() << " <link rel=\"next\" href=\""
1717 << anchorPair.first << "\" />\n";
1718
1719 navigationLinks += "[Next: <a href=\"" + anchorPair.first + "\">";
1720 if (linkPair.first == linkPair.second && !anchorPair.second.isEmpty())
1721 navigationLinks += protect(anchorPair.second);
1722 else
1723 navigationLinks += protect(linkPair.second);
1724 navigationLinks += "</a>]\n";
1725 }
1726 if (node->links().contains(Node::IndexLink)) {
1727 linkPair = node->links()[Node::IndexLink];
1728 linkNode = findNodeForTarget(linkPair.first, node, marker);
1729 if (!linkNode || linkNode == node)
1730 anchorPair = linkPair;
1731 else
1732 anchorPair = anchorForNode(linkNode);
1733 out() << " <link rel=\"index\" href=\""
1734 << anchorPair.first << "\" />\n";
1735 }
1736 if (node->links().contains(Node::StartLink)) {
1737 linkPair = node->links()[Node::StartLink];
1738 linkNode = findNodeForTarget(linkPair.first, node, marker);
1739 if (!linkNode || linkNode == node)
1740 anchorPair = linkPair;
1741 else
1742 anchorPair = anchorForNode(linkNode);
1743 out() << " <link rel=\"start\" href=\""
1744 << anchorPair.first << "\" />\n";
1745 }
1746 }
1747
1748 foreach (const QString &stylesheet, stylesheets) {
1749 out() << " <link href=\"" << stylesheet << "\" rel=\"stylesheet\" "
1750 << "type=\"text/css\" />\n";
1751 }
1752
1753 foreach (const QString &customHeadElement, customHeadElements) {
1754 out() << " " << customHeadElement << "\n";
1755 }
1756
1757 out() << "</head>\n"
1758 "<body>\n";
1759 if (mainPage)
1760 generateMacRef(node, marker);
1761 out() << QString(postHeader).replace("\\" + COMMAND_VERSION, myTree->version());
1762
1763
1764 if (node && !node->links().empty())
1765 out() << "<p>\n" << navigationLinks << "</p>\n";
1766}
1767
1768void HtmlGenerator::generateTitle(const QString& title,
1769 const Text &subTitle,
1770 SubTitleSize subTitleSize,
1771 const Node *relative,
1772 CodeMarker *marker)
1773{
1774 out() << "<h1 class=\"title\">" << protect(title);
1775 if (!subTitle.isEmpty()) {
1776 out() << "<br />";
1777 if (subTitleSize == SmallSubTitle)
1778 out() << "<span class=\"small-subtitle\">";
1779 else
1780 out() << "<span class=\"subtitle\">";
1781 generateText(subTitle, relative, marker);
1782 out() << "</span>\n";
1783 }
1784 out() << "</h1>\n";
1785}
1786
1787void HtmlGenerator::generateFooter(const Node *node)
1788{
1789 if (node && !node->links().empty())
1790 out() << "<p>\n" << navigationLinks << "</p>\n";
1791
1792 out() << QString(footer).replace("\\" + COMMAND_VERSION, myTree->version())
1793 << QString(address).replace("\\" + COMMAND_VERSION, myTree->version())
1794 << "</body>\n"
1795 "</html>\n";
1796}
1797
1798void HtmlGenerator::generateBrief(const Node *node, CodeMarker *marker,
1799 const Node *relative)
1800{
1801 Text brief = node->doc().briefText();
1802 if (!brief.isEmpty()) {
1803 out() << "<p>";
1804 generateText(brief, node, marker);
1805 if (!relative || node == relative)
1806 out() << " <a href=\"#";
1807 else
1808 out() << " <a href=\"" << linkForNode(node, relative) << "#";
1809 out() << registerRef("details") << "\">More...</a></p>\n";
1810 }
1811}
1812
1813void HtmlGenerator::generateIncludes(const InnerNode *inner, CodeMarker *marker)
1814{
1815 if (!inner->includes().isEmpty()) {
1816 out() << "<pre>"
1817 << trimmedTrailing(highlightedCode(indent(codeIndent,
1818 marker->markedUpIncludes(inner->includes())),
1819 marker,inner))
1820 << "</pre>";
1821 }
1822}
1823
1824void HtmlGenerator::generateTableOfContents(const Node *node,
1825 CodeMarker *marker,
1826 Doc::SectioningUnit sectioningUnit,
1827 int numColumns,
1828 const Node *relative)
1829
1830{
1831 if (!node->doc().hasTableOfContents())
1832 return;
1833 QList<Atom *> toc = node->doc().tableOfContents();
1834 if (toc.isEmpty())
1835 return;
1836
1837 QString nodeName = "";
1838 if (node != relative)
1839 nodeName = node->name();
1840
1841 QStringList sectionNumber;
1842 int columnSize = 0;
1843
1844 QString tdTag;
1845 if (numColumns > 1) {
1846 tdTag = "<td width=\"" + QString::number((100 + numColumns - 1) / numColumns) + "%\">";
1847 out() << "<p><table class=\"toc\" width=\"100%\">\n<tr valign=\"top\">"
1848 << tdTag << "\n";
1849 }
1850
1851 // disable nested links in table of contents
1852 inContents = true;
1853 inLink = true;
1854
1855 for (int i = 0; i < toc.size(); ++i) {
1856 Atom *atom = toc.at(i);
1857
1858 int nextLevel = atom->string().toInt();
1859 if (nextLevel > (int)sectioningUnit)
1860 continue;
1861
1862 if (sectionNumber.size() < nextLevel) {
1863 do {
1864 out() << "<ul>";
1865 sectionNumber.append("1");
1866 } while (sectionNumber.size() < nextLevel);
1867 }
1868 else {
1869 while (sectionNumber.size() > nextLevel) {
1870 out() << "</ul>\n";
1871 sectionNumber.removeLast();
1872 }
1873 sectionNumber.last() = QString::number(sectionNumber.last().toInt() + 1);
1874 }
1875 int numAtoms;
1876 Text headingText = Text::sectionHeading(atom);
1877
1878 if (sectionNumber.size() == 1 && columnSize > toc.size() / numColumns) {
1879 out() << "</ul></td>" << tdTag << "<ul>\n";
1880 columnSize = 0;
1881 }
1882 out() << "<li>";
1883 out() << "<a href=\""
1884 << nodeName
1885 << "#"
1886 << Doc::canonicalTitle(headingText.toString())
1887 << "\">";
1888 generateAtomList(headingText.firstAtom(), node, marker, true, numAtoms);
1889 out() << "</a></li>\n";
1890
1891 ++columnSize;
1892 }
1893 while (!sectionNumber.isEmpty()) {
1894 out() << "</ul>\n";
1895 sectionNumber.removeLast();
1896 }
1897
1898 if (numColumns > 1)
1899 out() << "</td></tr></table></p>\n";
1900
1901 inContents = false;
1902 inLink = false;
1903}
1904
1905#if 0
1906void HtmlGenerator::generateNavigationBar(const NavigationBar& bar,
1907 const Node *node,
1908 CodeMarker *marker)
1909{
1910 if (bar.prev.begin() != 0 || bar.current.begin() != 0 ||
1911 bar.next.begin() != 0) {
1912 out() << "<p align=\"right\">";
1913 if (bar.prev.begin() != 0) {
1914#if 0
1915 out() << "[<a href=\"" << section.previousBaseName()
1916 << ".html\">Prev: ";
1917 generateText(section.previousHeading(), node, marker);
1918 out() << "</a>]\n";
1919#endif
1920 }
1921 if (bar.current.begin() != 0) {
1922 out() << "[<a href=\"" << "home"
1923 << ".html\">Home</a>]\n";
1924 }
1925 if (bar.next.begin() != 0) {
1926 out() << "[<a href=\"" << fileBase(node, bar.next)
1927 << ".html\">Next: ";
1928 generateText(Text::sectionHeading(bar.next.begin()), node, marker);
1929 out() << "</a>]\n";
1930 }
1931 out() << "</p>\n";
1932 }
1933}
1934#endif
1935
1936QString HtmlGenerator::generateListOfAllMemberFile(const InnerNode *inner,
1937 CodeMarker *marker)
1938{
1939 QList<Section> sections;
1940 QList<Section>::ConstIterator s;
1941
1942 sections = marker->sections(inner,
1943 CodeMarker::SeparateList,
1944 CodeMarker::Okay);
1945 if (sections.isEmpty())
1946 return QString();
1947
1948 QString fileName = fileBase(inner) + "-members." + fileExtension(inner);
1949 beginSubPage(inner->location(), fileName);
1950 QString title = "List of All Members for " + inner->name();
1951 generateHeader(title, inner, marker, false);
1952 generateTitle(title, Text(), SmallSubTitle, inner, marker);
1953 out() << "<p>This is the complete list of members for ";
1954 generateFullName(inner, 0, marker);
1955 out() << ", including inherited members.</p>\n";
1956
1957 Section section = sections.first();
1958 generateSectionList(section, 0, marker, CodeMarker::SeparateList);
1959
1960 generateFooter();
1961 endSubPage();
1962 return fileName;
1963}
1964
1965QString HtmlGenerator::generateLowStatusMemberFile(const InnerNode *inner,
1966 CodeMarker *marker,
1967 CodeMarker::Status status)
1968{
1969 QList<Section> sections = marker->sections(inner,
1970 CodeMarker::Summary,
1971 status);
1972 QMutableListIterator<Section> j(sections);
1973 while (j.hasNext()) {
1974 if (j.next().members.size() == 0)
1975 j.remove();
1976 }
1977 if (sections.isEmpty())
1978 return QString();
1979
1980 int i;
1981
1982 QString title;
1983 QString fileName;
1984
1985 if (status == CodeMarker::Compat) {
1986 title = "Qt 3 Support Members for " + inner->name();
1987 fileName = fileBase(inner) + "-qt3." + fileExtension(inner);
1988 }
1989 else {
1990 title = "Obsolete Members for " + inner->name();
1991 fileName = fileBase(inner) + "-obsolete." + fileExtension(inner);
1992 }
1993
1994 beginSubPage(inner->location(), fileName);
1995 generateHeader(title, inner, marker, false);
1996 generateTitle(title, Text(), SmallSubTitle, inner, marker);
1997
1998 if (status == CodeMarker::Compat) {
1999 out() << "<p><b>The following class members are part of the "
2000 "<a href=\"qt3support.html\">Qt 3 support layer</a>.</b> "
2001 "They are provided to help you port old code to Qt 4. We advise against "
2002 "using them in new code.</p>\n";
2003 }
2004 else {
2005 out() << "<p><b>The following class members are obsolete.</b> "
2006 << "They are provided to keep old source code working. "
2007 << "We strongly advise against using them in new code.</p>\n";
2008 }
2009
2010 out() << "<p><ul><li><a href=\""
2011 << linkForNode(inner, 0) << "\">"
2012 << protect(inner->name())
2013 << " class reference</a></li></ul></p>\n";
2014
2015 for (i = 0; i < sections.size(); ++i) {
2016 out() << "<h2>" << protect(sections.at(i).name) << "</h2>\n";
2017 generateSectionList(sections.at(i), inner, marker, CodeMarker::Summary);
2018 }
2019
2020 sections = marker->sections(inner, CodeMarker::Detailed, status);
2021 for (i = 0; i < sections.size(); ++i) {
2022 out() << "<hr />\n";
2023 out() << "<h2>" << protect(sections.at(i).name) << "</h2>\n";
2024
2025 NodeList::ConstIterator m = sections.at(i).members.begin();
2026 while (m != sections.at(i).members.end()) {
2027 if ((*m)->access() != Node::Private)
2028 generateDetailedMember(*m, inner, marker);
2029 ++m;
2030 }
2031 }
2032
2033 generateFooter();
2034 endSubPage();
2035 return fileName;
2036}
2037
2038void HtmlGenerator::generateClassHierarchy(const Node *relative,
2039 CodeMarker *marker,
2040 const QMap<QString,const Node*> &classMap)
2041{
2042 if (classMap.isEmpty())
2043 return;
2044
2045 NodeMap topLevel;
2046 NodeMap::ConstIterator c = classMap.begin();
2047 while (c != classMap.end()) {
2048 const ClassNode *classe = static_cast<const ClassNode *>(*c);
2049 if (classe->baseClasses().isEmpty())
2050 topLevel.insert(classe->name(), classe);
2051 ++c;
2052 }
2053
2054 QStack<NodeMap > stack;
2055 stack.push(topLevel);
2056
2057 out() << "<ul>\n";
2058 while (!stack.isEmpty()) {
2059 if (stack.top().isEmpty()) {
2060 stack.pop();
2061 out() << "</ul>\n";
2062 }
2063 else {
2064 const ClassNode *child =
2065 static_cast<const ClassNode *>(*stack.top().begin());
2066 out() << "<li>";
2067 generateFullName(child, relative, marker);
2068 out() << "</li>\n";
2069 stack.top().erase(stack.top().begin());
2070
2071 NodeMap newTop;
2072 foreach (const RelatedClass &d, child->derivedClasses()) {
2073 if (d.access != Node::Private)
2074 newTop.insert(d.node->name(), d.node);
2075 }
2076 if (!newTop.isEmpty()) {
2077 stack.push(newTop);
2078 out() << "<ul>\n";
2079 }
2080 }
2081 }
2082}
2083
2084void HtmlGenerator::generateAnnotatedList(const Node *relative,
2085 CodeMarker *marker,
2086 const NodeMap &nodeMap)
2087{
2088 out() << "<p><table width=\"100%\" class=\"annotated\" cellpadding=\"2\" "
2089 << "cellspacing=\"1\" border=\"0\">\n";
2090
2091 int row = 0;
2092 foreach (const QString &name, nodeMap.keys()) {
2093 const Node *node = nodeMap[name];
2094
2095 if (node->status() == Node::Obsolete)
2096 continue;
2097
2098 if (++row % 2 == 1)
2099 out() << "<tr valign=\"top\" class=\"odd\">";
2100 else
2101 out() << "<tr valign=\"top\" class=\"even\">";
2102 out() << "<th>";
2103 generateFullName(node, relative, marker);
2104 out() << "</th>";
2105
2106 if (!(node->type() == Node::Fake)) {
2107 Text brief = node->doc().trimmedBriefText(name);
2108 if (!brief.isEmpty()) {
2109 out() << "<td>";
2110 generateText(brief, node, marker);
2111 out() << "</td>";
2112 }
2113 }
2114 else {
2115 out() << "<td>";
2116 out() << protect(node->doc().briefText().toString());
2117 out() << "</td>";
2118 }
2119 out() << "</tr>\n";
2120 }
2121 out() << "</table></p>\n";
2122}
2123
2124/*!
2125 This function finds the common prefix of the names of all
2126 the classes in \a classMap and then generates a compact
2127 list of the class names alphabetized on the part of the
2128 name not including the common prefix. You can tell the
2129 function to use \a comonPrefix as the common prefix, but
2130 normally you let it figure it out itself by looking at
2131 the name of the first and last classes in \a classMap.
2132 */
2133void HtmlGenerator::generateCompactList(const Node *relative,
2134 CodeMarker *marker,
2135 const NodeMap &classMap,
2136 QString commonPrefix)
2137{
2138 const int NumParagraphs = 37; // '0' to '9', 'A' to 'Z', '_'
2139 const int NumColumns = 4; // number of columns in the result
2140
2141 if (classMap.isEmpty())
2142 return;
2143
2144 /*
2145 If commonPrefix is not empty, then the caller knows what
2146 the common prefix is and has passed it in, so just use that
2147 one.
2148 */
2149 int commonPrefixLen = commonPrefix.length();
2150 if (commonPrefixLen == 0) {
2151 QString first;
2152 QString last;
2153
2154 /*
2155 The caller didn't pass in a common prefix, so get the common
2156 prefix by looking at the class names of the first and last
2157 classes in the class map. Discard any namespace names and
2158 just use the bare class names. For Qt, the prefix is "Q".
2159
2160 Note that the algorithm used here to derive the common prefix
2161 from the first and last classes in alphabetical order (QAccel
2162 and QXtWidget in Qt 2.1), fails if either class name does not
2163 begin with Q.
2164 */
2165
2166 NodeMap::const_iterator iter = classMap.begin();
2167 while (iter != classMap.end()) {
2168 if (!iter.key().contains("::")) {
2169 first = iter.key();
2170 break;
2171 }
2172 ++iter;
2173 }
2174
2175 if (first.isEmpty())
2176 first = classMap.begin().key();
2177
2178 iter = classMap.end();
2179 while (iter != classMap.begin()) {
2180 --iter;
2181 if (!iter.key().contains("::")) {
2182 last = iter.key();
2183 break;
2184 }
2185 }
2186
2187 if (last.isEmpty())
2188 last = classMap.begin().key();
2189
2190 if (classMap.size() > 1) {
2191 while (commonPrefixLen < first.length() + 1 &&
2192 commonPrefixLen < last.length() + 1 &&
2193 first[commonPrefixLen] == last[commonPrefixLen])
2194 ++commonPrefixLen;
2195 }
2196
2197 commonPrefix = first.left(commonPrefixLen);
2198 }
2199
2200 /*
2201 Divide the data into 37 paragraphs: 0, ..., 9, A, ..., Z,
2202 underscore (_). QAccel will fall in paragraph 10 (A) and
2203 QXtWidget in paragraph 33 (X). This is the only place where we
2204 assume that NumParagraphs is 37. Each paragraph is a NodeMap.
2205 */
2206 NodeMap paragraph[NumParagraphs+1];
2207 QString paragraphName[NumParagraphs+1];
2208
2209 NodeMap::ConstIterator c = classMap.begin();
2210 while (c != classMap.end()) {
2211 QStringList pieces = c.key().split("::");
2212 QString key;
2213 int idx = commonPrefixLen;
2214 if (!pieces.last().startsWith(commonPrefix))
2215 idx = 0;
2216 if (pieces.size() == 1)
2217 key = pieces.last().mid(idx).toLower();
2218 else
2219 key = pieces.last().toLower();
2220
2221 int paragraphNo = NumParagraphs - 1;
2222
2223 if (key[0].digitValue() != -1) {
2224 paragraphNo = key[0].digitValue();
2225 }
2226 else if (key[0] >= QLatin1Char('a') && key[0] <= QLatin1Char('z')) {
2227 paragraphNo = 10 + key[0].unicode() - 'a';
2228 }
2229
2230 paragraphName[paragraphNo] = key[0].toUpper();
2231 paragraph[paragraphNo].insert(key, c.value());
2232 ++c;
2233 }
2234
2235 /*
2236 Each paragraph j has a size: paragraph[j].count(). In the
2237 discussion, we will assume paragraphs 0 to 5 will have sizes
2238 3, 1, 4, 1, 5, 9.
2239
2240 We now want to compute the paragraph offset. Paragraphs 0 to 6
2241 start at offsets 0, 3, 4, 8, 9, 14, 23.
2242 */
2243 int paragraphOffset[NumParagraphs + 1]; // 37 + 1
2244 int i, j, k;
2245
2246 paragraphOffset[0] = 0;
2247 for (j = 0; j < NumParagraphs; j++) // j = 0..36
2248 paragraphOffset[j + 1] = paragraphOffset[j] + paragraph[j].count();
2249
2250 int firstOffset[NumColumns + 1]; // 4 + 1
2251 int currentOffset[NumColumns]; // 4
2252 int currentParagraphNo[NumColumns]; // 4
2253 int currentOffsetInParagraph[NumColumns]; // 4
2254
2255 int numRows = (classMap.count() + NumColumns - 1) / NumColumns;
2256 int curParagNo = 0;
2257
2258 for (i = 0; i < NumColumns; i++) { // i = 0..3
2259 firstOffset[i] = qMin(i * numRows, classMap.size());
2260 currentOffset[i] = firstOffset[i];
2261
2262 for (j = curParagNo; j < NumParagraphs; j++) {
2263 if (paragraphOffset[j] > firstOffset[i])
2264 break;
2265 if (paragraphOffset[j] <= firstOffset[i])
2266 curParagNo = j;
2267 }
2268 currentParagraphNo[i] = curParagNo;
2269 currentOffsetInParagraph[i] = firstOffset[i] -
2270 paragraphOffset[curParagNo];
2271 }
2272 firstOffset[NumColumns] = classMap.count();
2273
2274 out() << "<p><table class=\"generic\" width=\"100%\">\n";
2275 for (k = 0; k < numRows; k++) {
2276 out() << "<tr>\n";
2277 for (i = 0; i < NumColumns; i++) {
2278 if (currentOffset[i] >= firstOffset[i + 1]) {
2279 // this column is finished
2280 out() << "<td>\n</td>\n";
2281 }
2282 else {
2283 while ((currentParagraphNo[i] < NumParagraphs) &&
2284 (currentOffsetInParagraph[i] == paragraph[currentParagraphNo[i]].count())) {
2285 ++currentParagraphNo[i];
2286 currentOffsetInParagraph[i] = 0;
2287 }
2288#if 0
2289 if (currentParagraphNo[i] >= NumParagraphs) {
2290 qDebug() << "### Internal error ###" << __FILE__ << __LINE__
2291 << currentParagraphNo[i] << NumParagraphs;
2292 currentParagraphNo[i] = NumParagraphs - 1;
2293 }
2294#endif
2295 out() << "<td align=\"right\">";
2296 if (currentOffsetInParagraph[i] == 0) {
2297 // start a new paragraph
2298 out() << "<b>"
2299 << paragraphName[currentParagraphNo[i]]
2300 << "&nbsp;</b>";
2301 }
2302 out() << "</td>\n";
2303
2304 out() << "<td>";
2305 if ((currentParagraphNo[i] < NumParagraphs) &&
2306 !paragraphName[currentParagraphNo[i]].isEmpty()) {
2307 NodeMap::Iterator it;
2308 it = paragraph[currentParagraphNo[i]].begin();
2309 for (j = 0; j < currentOffsetInParagraph[i]; j++)
2310 ++it;
2311
2312 // Previously, we used generateFullName() for this, but we
2313 // require some special formatting.
2314 out() << "<a href=\""
2315 << linkForNode(it.value(), relative)
2316 << "\">";
2317 QStringList pieces = fullName(it.value(), relative, marker).split("::");
2318 out() << protect(pieces.last());
2319 out() << "</a>";
2320 if (pieces.size() > 1) {
2321 out() << " (";
2322 generateFullName(it.value()->parent(), relative, marker);
2323 out() << ")";
2324 }
2325 }
2326 out() << "</td>\n";
2327
2328 currentOffset[i]++;
2329 currentOffsetInParagraph[i]++;
2330 }
2331 }
2332 out() << "</tr>\n";
2333 }
2334 out() << "</table></p>\n";
2335}
2336
2337void HtmlGenerator::generateFunctionIndex(const Node *relative,
2338 CodeMarker *marker)
2339{
2340 out() << "<p align=\"center\"><font size=\"+1\"><b>";
2341 for (int i = 0; i < 26; i++) {
2342 QChar ch('a' + i);
2343 out() << QString("<a href=\"#%1\">%2</a>&nbsp;").arg(ch).arg(ch.toUpper());
2344 }
2345 out() << "</b></font></p>\n";
2346
2347 char nextLetter = 'a';
2348 char currentLetter;
2349
2350#if 1
2351 out() << "<ul>\n";
2352#endif
2353 QMap<QString, NodeMap >::ConstIterator f = funcIndex.begin();
2354 while (f != funcIndex.end()) {
2355#if 1
2356 out() << "<li>";
2357#else
2358 out() << "<p>";
2359#endif
2360 out() << protect(f.key()) << ":";
2361
2362 currentLetter = f.key()[0].unicode();
2363 while (islower(currentLetter) && currentLetter >= nextLetter) {
2364 out() << QString("<a name=\"%1\"></a>").arg(nextLetter);
2365 nextLetter++;
2366 }
2367
2368 NodeMap::ConstIterator s = (*f).begin();
2369 while (s != (*f).end()) {
2370 out() << " ";
2371 generateFullName((*s)->parent(), relative, marker, *s);
2372 ++s;
2373 }
2374#if 1
2375 out() << "</li>";
2376#else
2377 out() << "</p>";
2378#endif
2379 out() << "\n";
2380 ++f;
2381 }
2382#if 1
2383 out() << "</ul>\n";
2384#endif
2385}
2386
2387void HtmlGenerator::generateLegaleseList(const Node *relative,
2388 CodeMarker *marker)
2389{
2390 QMap<Text, const Node *>::ConstIterator it = legaleseTexts.begin();
2391 while (it != legaleseTexts.end()) {
2392 Text text = it.key();
2393 out() << "<hr />\n";
2394 generateText(text, relative, marker);
2395 out() << "<ul>\n";
2396 do {
2397 out() << "<li>";
2398 generateFullName(it.value(), relative, marker);
2399 out() << "</li>\n";
2400 ++it;
2401 } while (it != legaleseTexts.end() && it.key() == text);
2402 out() << "</ul>\n";
2403 }
2404}
2405
2406/*void HtmlGenerator::generateSynopsis(const Node *node,
2407 const Node *relative,
2408 CodeMarker *marker,
2409 CodeMarker::SynopsisStyle style)
2410{
2411 QString marked = marker->markedUpSynopsis(node, relative, style);
2412 QRegExp templateTag("(<[^@>]*>)");
2413 if (marked.indexOf(templateTag) != -1) {
2414 QString contents = protect(marked.mid(templateTag.pos(1),
2415 templateTag.cap(1).length()));
2416 marked.replace(templateTag.pos(1), templateTag.cap(1).length(),
2417 contents);
2418 }
2419 marked.replace(QRegExp("<@param>([a-z]+)_([1-9n])</@param>"),
2420 "<i>\\1<sub>\\2</sub></i>");
2421 marked.replace("<@param>", "<i>");
2422 marked.replace("</@param>", "</i>");
2423
2424 if (style == CodeMarker::Summary)
2425 marked.replace("@name>", "b>");
2426
2427 if (style == CodeMarker::SeparateList) {
2428 QRegExp extraRegExp("<@extra>.*</@extra>");
2429 extraRegExp.setMinimal(true);
2430 marked.replace(extraRegExp, "");
2431 }
2432 else {
2433 marked.replace("<@extra>", "&nbsp;&nbsp;<tt>");
2434 marked.replace("</@extra>", "</tt>");
2435 }
2436
2437 if (style != CodeMarker::Detailed) {
2438 marked.replace("<@type>", "");
2439 marked.replace("</@type>", "");
2440 }
2441 out() << highlightedCode(marked, marker, relative);
2442}*/
2443
2444#ifdef QDOC_QML
2445void HtmlGenerator::generateQmlItem(const Node *node,
2446 const Node *relative,
2447 CodeMarker *marker,
2448 bool summary)
2449{
2450 QString marked = marker->markedUpQmlItem(node,summary);
2451 QRegExp templateTag("(<[^@>]*>)");
2452 if (marked.indexOf(templateTag) != -1) {
2453 QString contents = protect(marked.mid(templateTag.pos(1),
2454 templateTag.cap(1).length()));
2455 marked.replace(templateTag.pos(1), templateTag.cap(1).length(),
2456 contents);
2457 }
2458 marked.replace(QRegExp("<@param>([a-z]+)_([1-9n])</@param>"),
2459 "<i>\\1<sub>\\2</sub></i>");
2460 marked.replace("<@param>", "<i>");
2461 marked.replace("</@param>", "</i>");
2462
2463 if (summary)
2464 marked.replace("@name>", "b>");
2465
2466 marked.replace("<@extra>", "&nbsp;&nbsp;<tt>");
2467 marked.replace("</@extra>", "</tt>");
2468
2469 if (summary) {
2470 marked.replace("<@type>", "");
2471 marked.replace("</@type>", "");
2472 }
2473 out() << highlightedCode(marked, marker, relative);
2474}
2475#endif
2476
2477void HtmlGenerator::generateOverviewList(const Node *relative, CodeMarker * /* marker */)
2478{
2479 QMap<const FakeNode *, QMap<QString, FakeNode *> > fakeNodeMap;
2480 QMap<QString, const FakeNode *> groupTitlesMap;
2481 QMap<QString, FakeNode *> uncategorizedNodeMap;
2482 QRegExp singleDigit("\\b([0-9])\\b");
2483
2484 const NodeList children = myTree->root()->childNodes();
2485 foreach (Node *child, children) {
2486 if (child->type() == Node::Fake && child != relative) {
2487 FakeNode *fakeNode = static_cast<FakeNode *>(child);
2488
2489 // Check whether the page is part of a group or is the group
2490 // definition page.
2491 QString group;
2492 bool isGroupPage = false;
2493 if (fakeNode->doc().metaCommandsUsed().contains("group")) {
2494 group = fakeNode->doc().metaCommandArgs("group")[0];
2495 isGroupPage = true;
2496 }
2497
2498 // there are too many examples; they would clutter the list
2499 if (fakeNode->subType() == Node::Example)
2500 continue;
2501
2502 // not interested either in individual (Qt Designer etc.) manual chapters
2503 if (fakeNode->links().contains(Node::ContentsLink))
2504 continue;
2505
2506 // Discard external nodes.
2507 if (fakeNode->subType() == Node::ExternalPage)
2508 continue;
2509
2510 QString sortKey = fakeNode->fullTitle().toLower();
2511 if (sortKey.startsWith("the "))
2512 sortKey.remove(0, 4);
2513 sortKey.replace(singleDigit, "0\\1");
2514
2515 if (!group.isEmpty()) {
2516 if (isGroupPage) {
2517 // If we encounter a group definition page, we add all
2518 // the pages in that group to the list for that group.
2519 foreach (Node *member, fakeNode->groupMembers()) {
2520 if (member->type() != Node::Fake)
2521 continue;
2522 FakeNode *page = static_cast<FakeNode *>(member);
2523 if (page) {
2524 QString sortKey = page->fullTitle().toLower();
2525 if (sortKey.startsWith("the "))
2526 sortKey.remove(0, 4);
2527 sortKey.replace(singleDigit, "0\\1");
2528 fakeNodeMap[const_cast<const FakeNode *>(fakeNode)].insert(sortKey, page);
2529 groupTitlesMap[fakeNode->fullTitle()] = const_cast<const FakeNode *>(fakeNode);
2530 }
2531 }
2532 }
2533 else if (!isGroupPage) {
2534 // If we encounter a page that belongs to a group then
2535 // we add that page to the list for that group.
2536 const FakeNode *groupNode = static_cast<const FakeNode *>(myTree->root()->findNode(group, Node::Fake));
2537 if (groupNode)
2538 fakeNodeMap[groupNode].insert(sortKey, fakeNode);
2539 //else
2540 // uncategorizedNodeMap.insert(sortKey, fakeNode);
2541 }// else
2542 // uncategorizedNodeMap.insert(sortKey, fakeNode);
2543 }// else
2544 // uncategorizedNodeMap.insert(sortKey, fakeNode);
2545 }
2546 }
2547
2548 // We now list all the pages found that belong to groups.
2549 // If only certain pages were found for a group, but the definition page
2550 // for that group wasn't listed, the list of pages will be intentionally
2551 // incomplete. However, if the group definition page was listed, all the
2552 // pages in that group are listed for completeness.
2553
2554 if (!fakeNodeMap.isEmpty()) {
2555 foreach (const QString &groupTitle, groupTitlesMap.keys()) {
2556 const FakeNode *groupNode = groupTitlesMap[groupTitle];
2557 out() << QString("<h3><a href=\"%1\">%2</a></h3>\n").arg(
2558 linkForNode(groupNode, relative)).arg(
2559 protect(groupNode->fullTitle()));
2560
2561 if (fakeNodeMap[groupNode].count() == 0)
2562 continue;
2563
2564 out() << "<ul>\n";
2565
2566 foreach (const FakeNode *fakeNode, fakeNodeMap[groupNode]) {
2567 QString title = fakeNode->fullTitle();
2568 if (title.startsWith("The "))
2569 title.remove(0, 4);
2570 out() << "<li><a href=\"" << linkForNode(fakeNode, relative) << "\">"
2571 << protect(title) << "</a></li>\n";
2572 }
2573 out() << "</ul>\n";
2574 }
2575 }
2576
2577 if (!uncategorizedNodeMap.isEmpty()) {
2578 out() << QString("<h3>Miscellaneous</h3>\n");
2579 out() << "<ul>\n";
2580 foreach (const FakeNode *fakeNode, uncategorizedNodeMap) {
2581 QString title = fakeNode->fullTitle();
2582 if (title.startsWith("The "))
2583 title.remove(0, 4);
2584 out() << "<li><a href=\"" << linkForNode(fakeNode, relative) << "\">"
2585 << protect(title) << "</a></li>\n";
2586 }
2587 out() << "</ul>\n";
2588 }
2589}
2590
2591#ifdef QDOC_NAME_ALIGNMENT
2592void HtmlGenerator::generateSection(const NodeList& nl,
2593 const Node *relative,
2594 CodeMarker *marker,
2595 CodeMarker::SynopsisStyle style)
2596{
2597 bool name_alignment = true;
2598 if (!nl.isEmpty()) {
2599 bool twoColumn = false;
2600 if (style == CodeMarker::SeparateList) {
2601 name_alignment = false;
2602 twoColumn = (nl.count() >= 16);
2603 }
2604 else if (nl.first()->type() == Node::Property) {
2605 twoColumn = (nl.count() >= 5);
2606 name_alignment = false;
2607 }
2608 if (name_alignment) {
2609 out() << "<table class=\"alignedsummary\" border=\"0\" cellpadding=\"0\" "
2610 << "cellspacing=\"0\" width=\"100%\">\n";
2611 }
2612 else {
2613 if (twoColumn)
2614 out() << "<p><table class=\"propsummary\" width=\"100%\" "
2615 << "border=\"0\" cellpadding=\"0\""
2616 << " cellspacing=\"0\">\n"
2617 << "<tr><td width=\"45%\" valign=\"top\">";
2618 out() << "<ul>\n";
2619 }
2620
2621 int i = 0;
2622 NodeList::ConstIterator m = nl.begin();
2623 while (m != nl.end()) {
2624 if ((*m)->access() == Node::Private) {
2625 ++m;
2626 continue;
2627 }
2628
2629 if (name_alignment) {
2630 out() << "<tr><td class=\"memItemLeft\" "
2631 << "align=\"right\" valign=\"top\">";
2632 }
2633 else {
2634 if (twoColumn && i == (int) (nl.count() + 1) / 2)
2635 out() << "</ul></td><td valign=\"top\"><ul>\n";
2636 out() << "<li><div class=\"fn\">";
2637 }
2638
2639 generateSynopsis(*m, relative, marker, style, name_alignment);
2640 if (name_alignment)
2641 out() << "</td></tr>\n";
2642 else
2643 out() << "</div></li>\n";
2644 i++;
2645 ++m;
2646 }
2647 if (name_alignment)
2648 out() << "</table>\n";
2649 else {
2650 out() << "</ul>\n";
2651 if (twoColumn)
2652 out() << "</td></tr>\n</table></p>\n";
2653 }
2654 }
2655}
2656
2657void HtmlGenerator::generateSectionList(const Section& section,
2658 const Node *relative,
2659 CodeMarker *marker,
2660 CodeMarker::SynopsisStyle style)
2661{
2662 bool name_alignment = true;
2663 if (!section.members.isEmpty()) {
2664 bool twoColumn = false;
2665 if (style == CodeMarker::SeparateList) {
2666 name_alignment = false;
2667 twoColumn = (section.members.count() >= 16);
2668 }
2669 else if (section.members.first()->type() == Node::Property) {
2670 twoColumn = (section.members.count() >= 5);
2671 name_alignment = false;
2672 }
2673 if (name_alignment) {
2674 out() << "<table class=\"alignedsummary\" border=\"0\" cellpadding=\"0\" "
2675 << "cellspacing=\"0\" width=\"100%\">\n";
2676 }
2677 else {
2678 if (twoColumn)
2679 out() << "<p><table class=\"propsummary\" width=\"100%\" "
2680 << "border=\"0\" cellpadding=\"0\""
2681 << " cellspacing=\"0\">\n"
2682 << "<tr><td width=\"45%\" valign=\"top\">";
2683 out() << "<ul>\n";
2684 }
2685
2686 int i = 0;
2687 NodeList::ConstIterator m = section.members.begin();
2688 while (m != section.members.end()) {
2689 if ((*m)->access() == Node::Private) {
2690 ++m;
2691 continue;
2692 }
2693
2694 if (name_alignment) {
2695 out() << "<tr><td class=\"memItemLeft\" "
2696 << "align=\"right\" valign=\"top\">";
2697 }
2698 else {
2699 if (twoColumn && i == (int) (section.members.count() + 1) / 2)
2700 out() << "</ul></td><td valign=\"top\"><ul>\n";
2701 out() << "<li><div class=\"fn\">";
2702 }
2703
2704 generateSynopsis(*m, relative, marker, style, name_alignment);
2705 if (name_alignment)
2706 out() << "</td></tr>\n";
2707 else
2708 out() << "</div></li>\n";
2709 i++;
2710 ++m;
2711 }
2712 if (name_alignment)
2713 out() << "</table>\n";
2714 else {
2715 out() << "</ul>\n";
2716 if (twoColumn)
2717 out() << "</td></tr>\n</table></p>\n";
2718 }
2719 }
2720
2721 if (style == CodeMarker::Summary && !section.inherited.isEmpty()) {
2722 out() << "<ul>\n";
2723 generateSectionInheritedList(section, relative, marker, name_alignment);
2724 out() << "</ul>\n";
2725 }
2726}
2727
2728void HtmlGenerator::generateSectionInheritedList(const Section& section,
2729 const Node *relative,
2730 CodeMarker *marker,
2731 bool nameAlignment)
2732{
2733 QList<QPair<ClassNode *, int> >::ConstIterator p = section.inherited.begin();
2734 while (p != section.inherited.end()) {
2735 if (nameAlignment)
2736 out() << "<li><div bar=\"2\" class=\"fn\"></div>";
2737 else
2738 out() << "<li><div class=\"fn\"></div>";
2739 out() << (*p).second << " ";
2740 if ((*p).second == 1) {
2741 out() << section.singularMember;
2742 }
2743 else {
2744 out() << section.pluralMember;
2745 }
2746 out() << " inherited from <a href=\"" << fileName((*p).first)
2747 << "#" << HtmlGenerator::cleanRef(section.name.toLower()) << "\">"
2748 << protect(marker->plainFullName((*p).first, relative))
2749 << "</a></li>\n";
2750 ++p;
2751 }
2752}
2753
2754void HtmlGenerator::generateSynopsis(const Node *node,
2755 const Node *relative,
2756 CodeMarker *marker,
2757 CodeMarker::SynopsisStyle style,
2758 bool nameAlignment)
2759{
2760 QString marked = marker->markedUpSynopsis(node, relative, style);
2761 QRegExp templateTag("(<[^@>]*>)");
2762 if (marked.indexOf(templateTag) != -1) {
2763 QString contents = protect(marked.mid(templateTag.pos(1),
2764 templateTag.cap(1).length()));
2765 marked.replace(templateTag.pos(1), templateTag.cap(1).length(),
2766 contents);
2767 }
2768 marked.replace(QRegExp("<@param>([a-z]+)_([1-9n])</@param>"),
2769 "<i>\\1<sub>\\2</sub></i>");
2770 marked.replace("<@param>", "<i>");
2771 marked.replace("</@param>", "</i>");
2772
2773 if (style == CodeMarker::Summary) {
2774 marked.replace("<@name>", ""); // was "<b>"
2775 marked.replace("</@name>", ""); // was "</b>"
2776 }
2777
2778 if (style == CodeMarker::SeparateList) {
2779 QRegExp extraRegExp("<@extra>.*</@extra>");
2780 extraRegExp.setMinimal(true);
2781 marked.replace(extraRegExp, "");
2782 } else {
2783 marked.replace("<@extra>", "&nbsp;&nbsp;<tt>");
2784 marked.replace("</@extra>", "</tt>");
2785 }
2786
2787 if (style != CodeMarker::Detailed) {
2788 marked.replace("<@type>", "");
2789 marked.replace("</@type>", "");
2790 }
2791 out() << highlightedCode(marked, marker, relative, style, nameAlignment);
2792}
2793
2794QString HtmlGenerator::highlightedCode(const QString& markedCode,
2795 CodeMarker *marker,
2796 const Node *relative,
2797 CodeMarker::SynopsisStyle ,
2798 bool nameAlignment)
2799{
2800 QString src = markedCode;
2801 QString html;
2802 QStringRef arg;
2803 QStringRef par1;
2804
2805 const QChar charLangle = '<';
2806 const QChar charAt = '@';
2807
2808 // replace all <@link> tags: "(<@link node=\"([^\"]+)\">).*(</@link>)"
2809 static const QString linkTag("link");
2810 bool done = false;
2811 for (int i = 0, n = src.size(); i < n;) {
2812 if (src.at(i) == charLangle && src.at(i + 1).unicode() == '@') {
2813 if (nameAlignment && !done) {// && (i != 0)) Why was this here?
2814 html += "</td><td class=\"memItemRight\" valign=\"bottom\">";
2815 done = true;
2816 }
2817 i += 2;
2818 if (parseArg(src, linkTag, &i, n, &arg, &par1)) {
2819 html += "<b>";
2820 QString link = linkForNode(
2821 CodeMarker::nodeForString(par1.toString()), relative);
2822 addLink(link, arg, &html);
2823 html += "</b>";
2824 }
2825 else {
2826 html += charLangle;
2827 html += charAt;
2828 }
2829 }
2830 else {
2831 html += src.at(i++);
2832 }
2833 }
2834
2835
2836 if (slow) {
2837 // is this block ever used at all?
2838 // replace all <@func> tags: "(<@func target=\"([^\"]*)\">)(.*)(</@func>)"
2839 src = html;
2840 html = QString();
2841 static const QString funcTag("func");
2842 for (int i = 0, n = src.size(); i < n;) {
2843 if (src.at(i) == charLangle && src.at(i + 1) == charAt) {
2844 i += 2;
2845 if (parseArg(src, funcTag, &i, n, &arg, &par1)) {
2846 QString link = linkForNode(
2847 marker->resolveTarget(par1.toString(),
2848 myTree,
2849 relative),
2850 relative);
2851 addLink(link, arg, &html);
2852 par1 = QStringRef();
2853 }
2854 else {
2855 html += charLangle;
2856 html += charAt;
2857 }
2858 }
2859 else {
2860 html += src.at(i++);
2861 }
2862 }
2863 }
2864
2865 // replace all "(<@(type|headerfile|func)(?: +[^>]*)?>)(.*)(</@\\2>)" tags
2866 src = html;
2867 html = QString();
2868 static const QString typeTags[] = { "type", "headerfile", "func" };
2869 for (int i = 0, n = src.size(); i < n;) {
2870 if (src.at(i) == charLangle && src.at(i + 1) == charAt) {
2871 i += 2;
2872 bool handled = false;
2873 for (int k = 0; k != 3; ++k) {
2874 if (parseArg(src, typeTags[k], &i, n, &arg, &par1)) {
2875 par1 = QStringRef();
2876 QString link = linkForNode(
2877 marker->resolveTarget(arg.toString(), myTree, relative),
2878 relative);
2879 addLink(link, arg, &html);
2880 handled = true;
2881 break;
2882 }
2883 }
2884 if (!handled) {
2885 html += charLangle;
2886 html += charAt;
2887 }
2888 }
2889 else {
2890 html += src.at(i++);
2891 }
2892 }
2893
2894 // replace all
2895 // "<@comment>" -> "<span class=\"comment\">";
2896 // "<@preprocessor>" -> "<span class=\"preprocessor\">";
2897 // "<@string>" -> "<span class=\"string\">";
2898 // "<@char>" -> "<span class=\"char\">";
2899 // "</@(?:comment|preprocessor|string|char)>" -> "</span>"
2900 src = html;
2901 html = QString();
2902 static const QString spanTags[] = {
2903 "<@comment>", "<span class=\"comment\">",
2904 "<@preprocessor>", "<span class=\"preprocessor\">",
2905 "<@string>", "<span class=\"string\">",
2906 "<@char>", "<span class=\"char\">",
2907 "</@comment>", "</span>",
2908 "</@preprocessor>","</span>",
2909 "</@string>", "</span>",
2910 "</@char>", "</span>"
2911 // "<@char>", "<font color=blue>",
2912 // "</@char>", "</font>",
2913 // "<@func>", "<font color=green>",
2914 // "</@func>", "</font>",
2915 // "<@id>", "<i>",
2916 // "</@id>", "</i>",
2917 // "<@keyword>", "<b>",
2918 // "</@keyword>", "</b>",
2919 // "<@number>", "<font color=yellow>",
2920 // "</@number>", "</font>",
2921 // "<@op>", "<b>",
2922 // "</@op>", "</b>",
2923 // "<@param>", "<i>",
2924 // "</@param>", "</i>",
2925 // "<@string>", "<font color=green>",
2926 // "</@string>", "</font>",
2927 };
2928 for (int i = 0, n = src.size(); i < n;) {
2929 if (src.at(i) == charLangle) {
2930 bool handled = false;
2931 for (int k = 0; k != 8; ++k) {
2932 const QString & tag = spanTags[2 * k];
2933 if (tag == QStringRef(&src, i, tag.length())) {
2934 html += spanTags[2 * k + 1];
2935 i += tag.length();
2936 handled = true;
2937 break;
2938 }
2939 }
2940 if (!handled) {
2941 ++i;
2942 if (src.at(i) == charAt ||
2943 (src.at(i) == QLatin1Char('/') && src.at(i + 1) == charAt)) {
2944 // drop 'our' unknown tags (the ones still containing '@')
2945 while (i < n && src.at(i) != QLatin1Char('>'))
2946 ++i;
2947 ++i;
2948 }
2949 else {
2950 // retain all others
2951 html += charLangle;
2952 }
2953 }
2954 }
2955 else {
2956 html += src.at(i);
2957 ++i;
2958 }
2959 }
2960
2961 return html;
2962}
2963
2964#else
2965void HtmlGenerator::generateSectionList(const Section& section,
2966 const Node *relative,
2967 CodeMarker *marker,
2968 CodeMarker::SynopsisStyle style)
2969{
2970 if (!section.members.isEmpty()) {
2971 bool twoColumn = false;
2972 if (style == CodeMarker::SeparateList) {
2973 twoColumn = (section.members.count() >= 16);
2974 }
2975 else if (section.members.first()->type() == Node::Property) {
2976 twoColumn = (section.members.count() >= 5);
2977 }
2978 if (twoColumn)
2979 out() << "<p><table class=\"generic\" width=\"100%\" border=\"0\" "
2980 << "cellpadding=\"0\" cellspacing=\"0\">\n"
2981 << "<tr><td width=\"45%\" valign=\"top\">";
2982 out() << "<ul>\n";
2983
2984 int i = 0;
2985 NodeList::ConstIterator m = section.members.begin();
2986 while (m != section.members.end()) {
2987 if ((*m)->access() == Node::Private) {
2988 ++m;
2989 continue;
2990 }
2991
2992 if (twoColumn && i == (int) (section.members.count() + 1) / 2)
2993 out() << "</ul></td><td valign=\"top\"><ul>\n";
2994
2995 out() << "<li><div class=\"fn\"></div>";
2996 if (style == CodeMarker::Accessors)
2997 out() << "<b>";
2998 generateSynopsis(*m, relative, marker, style);
2999 if (style == CodeMarker::Accessors)
3000 out() << "</b>";
3001 out() << "</li>\n";
3002 i++;
3003 ++m;
3004 }
3005 out() << "</ul>\n";
3006 if (twoColumn)
3007 out() << "</td></tr>\n</table></p>\n";
3008 }
3009
3010 if (style == CodeMarker::Summary && !section.inherited.isEmpty()) {
3011 out() << "<ul>\n";
3012 generateSectionInheritedList(section, relative, marker);
3013 out() << "</ul>\n";
3014 }
3015}
3016
3017void HtmlGenerator::generateSectionInheritedList(const Section& section,
3018 const Node *relative,
3019 CodeMarker *marker)
3020{
3021 QList<QPair<ClassNode *, int> >::ConstIterator p = section.inherited.begin();
3022 while (p != section.inherited.end()) {
3023 out() << "<li><div bar=\"2\" class=\"fn\"></div>";
3024 out() << (*p).second << " ";
3025 if ((*p).second == 1) {
3026 out() << section.singularMember;
3027 } else {
3028 out() << section.pluralMember;
3029 }
3030 out() << " inherited from <a href=\"" << fileName((*p).first)
3031 << "#" << HtmlGenerator::cleanRef(section.name.toLower()) << "\">"
3032 << protect(marker->plainFullName((*p).first, relative))
3033 << "</a></li>\n";
3034 ++p;
3035 }
3036}
3037
3038void HtmlGenerator::generateSynopsis(const Node *node,
3039 const Node *relative,
3040 CodeMarker *marker,
3041 CodeMarker::SynopsisStyle style)
3042{
3043 QString marked = marker->markedUpSynopsis(node, relative, style);
3044 QRegExp templateTag("(<[^@>]*>)");
3045 if (marked.indexOf(templateTag) != -1) {
3046 QString contents = protect(marked.mid(templateTag.pos(1),
3047 templateTag.cap(1).length()));
3048 marked.replace(templateTag.pos(1), templateTag.cap(1).length(),
3049 contents);
3050 }
3051 marked.replace(QRegExp("<@param>([a-z]+)_([1-9n])</@param>"), "<i>\\1<sub>\\2</sub></i>");
3052 marked.replace("<@param>", "<i>");
3053 marked.replace("</@param>", "</i>");
3054
3055 if (style == CodeMarker::Summary)
3056 marked.replace("@name>", "b>");
3057
3058 if (style == CodeMarker::SeparateList) {
3059 QRegExp extraRegExp("<@extra>.*</@extra>");
3060 extraRegExp.setMinimal(true);
3061 marked.replace(extraRegExp, "");
3062 } else {
3063 marked.replace("<@extra>", "&nbsp;&nbsp;<tt>");
3064 marked.replace("</@extra>", "</tt>");
3065 }
3066
3067 if (style != CodeMarker::Detailed) {
3068 marked.replace("<@type>", "");
3069 marked.replace("</@type>", "");
3070 }
3071 out() << highlightedCode(marked, marker, relative);
3072}
3073
3074QString HtmlGenerator::highlightedCode(const QString& markedCode,
3075 CodeMarker *marker,
3076 const Node *relative)
3077{
3078 QString src = markedCode;
3079 QString html;
3080 QStringRef arg;
3081 QStringRef par1;
3082
3083 const QChar charLangle = '<';
3084 const QChar charAt = '@';
3085
3086 // replace all <@link> tags: "(<@link node=\"([^\"]+)\">).*(</@link>)"
3087 static const QString linkTag("link");
3088 for (int i = 0, n = src.size(); i < n;) {
3089 if (src.at(i) == charLangle && src.at(i + 1) == charAt) {
3090 i += 2;
3091 if (parseArg(src, linkTag, &i, n, &arg, &par1)) {
3092 const Node* node = CodeMarker::nodeForString(par1.toString());
3093 QString link = linkForNode(node, relative);
3094 addLink(link, arg, &html);
3095 }
3096 else {
3097 html += charLangle;
3098 html += charAt;
3099 }
3100 }
3101 else {
3102 html += src.at(i++);
3103 }
3104 }
3105
3106 if (slow) {
3107 // is this block ever used at all?
3108 // replace all <@func> tags: "(<@func target=\"([^\"]*)\">)(.*)(</@func>)"
3109 src = html;
3110 html = QString();
3111 static const QString funcTag("func");
3112 for (int i = 0, n = src.size(); i < n;) {
3113 if (src.at(i) == charLangle && src.at(i + 1) == charAt) {
3114 i += 2;
3115 if (parseArg(src, funcTag, &i, n, &arg, &par1)) {
3116 QString link = linkForNode(
3117 marker->resolveTarget(par1.toString(),
3118 myTree,
3119 relative),
3120 relative);
3121 addLink(link, arg, &html);
3122 par1 = QStringRef();
3123 }
3124 else {
3125 html += charLangle;
3126 html += charAt;
3127 }
3128 }
3129 else {
3130 html += src.at(i++);
3131 }
3132 }
3133 }
3134
3135 // replace all "(<@(type|headerfile|func)(?: +[^>]*)?>)(.*)(</@\\2>)" tags
3136 src = html;
3137 html = QString();
3138 static const QString typeTags[] = { "type", "headerfile", "func" };
3139 for (int i = 0, n = src.size(); i < n;) {
3140 if (src.at(i) == charLangle && src.at(i + 1) == charAt) {
3141 i += 2;
3142 bool handled = false;
3143 for (int k = 0; k != 3; ++k) {
3144 if (parseArg(src, typeTags[k], &i, n, &arg, &par1)) {
3145 par1 = QStringRef();
3146 QString link = linkForNode(
3147 marker->resolveTarget(arg.toString(), myTree, relative),
3148 relative);
3149 addLink(link, arg, &html);
3150 handled = true;
3151 break;
3152 }
3153 }
3154 if (!handled) {
3155 html += charLangle;
3156 html += charAt;
3157 }
3158 }
3159 else {
3160 html += src.at(i++);
3161 }
3162 }
3163
3164 // replace all
3165 // "<@comment>" -> "<span class=\"comment\">";
3166 // "<@preprocessor>" -> "<span class=\"preprocessor\">";
3167 // "<@string>" -> "<span class=\"string\">";
3168 // "<@char>" -> "<span class=\"char\">";
3169 // "</@(?:comment|preprocessor|string|char)>" -> "</span>"
3170 src = html;
3171 html = QString();
3172 static const QString spanTags[] = {
3173 "<@comment>", "<span class=\"comment\">",
3174 "<@preprocessor>", "<span class=\"preprocessor\">",
3175 "<@string>", "<span class=\"string\">",
3176 "<@char>", "<span class=\"char\">",
3177 "</@comment>", "</span>",
3178 "</@preprocessor>","</span>",
3179 "</@string>", "</span>",
3180 "</@char>", "</span>"
3181 // "<@char>", "<font color=blue>",
3182 // "</@char>", "</font>",
3183 // "<@func>", "<font color=green>",
3184 // "</@func>", "</font>",
3185 // "<@id>", "<i>",
3186 // "</@id>", "</i>",
3187 // "<@keyword>", "<b>",
3188 // "</@keyword>", "</b>",
3189 // "<@number>", "<font color=yellow>",
3190 // "</@number>", "</font>",
3191 // "<@op>", "<b>",
3192 // "</@op>", "</b>",
3193 // "<@param>", "<i>",
3194 // "</@param>", "</i>",
3195 // "<@string>", "<font color=green>",
3196 // "</@string>", "</font>",
3197 };
3198 for (int i = 0, n = src.size(); i < n;) {
3199 if (src.at(i) == charLangle) {
3200 bool handled = false;
3201 for (int k = 0; k != 8; ++k) {
3202 const QString & tag = spanTags[2 * k];
3203 if (tag == QStringRef(&src, i, tag.length())) {
3204 html += spanTags[2 * k + 1];
3205 i += tag.length();
3206 handled = true;
3207 break;
3208 }
3209 }
3210 if (!handled) {
3211 ++i;
3212 if (src.at(i) == charAt ||
3213 (src.at(i) == QLatin1Char('/') && src.at(i + 1) == charAt)) {
3214 // drop 'our' unknown tags (the ones still containing '@')
3215 while (i < n && src.at(i) != QLatin1Char('>'))
3216 ++i;
3217 ++i;
3218 }
3219 else {
3220 // retain all others
3221 html += charLangle;
3222 }
3223 }
3224 }
3225 else {
3226 html += src.at(i);
3227 ++i;
3228 }
3229 }
3230
3231 return html;
3232}
3233#endif
3234
3235void HtmlGenerator::generateLink(const Atom* atom,
3236 const Node* /* relative */,
3237 CodeMarker* marker)
3238{
3239 static QRegExp camelCase("[A-Z][A-Z][a-z]|[a-z][A-Z0-9]|_");
3240
3241 if (funcLeftParen.indexIn(atom->string()) != -1 && marker->recognizeLanguage("Cpp")) {
3242 // hack for C++: move () outside of link
3243 int k = funcLeftParen.pos(1);
3244 out() << protect(atom->string().left(k));
3245 if (link.isEmpty()) {
3246 if (showBrokenLinks)
3247 out() << "</i>";
3248 } else {
3249 out() << "</a>";
3250 }
3251 inLink = false;
3252 out() << protect(atom->string().mid(k));
3253 } else if (marker->recognizeLanguage("Java")) {
3254 // hack for Java: remove () and use <tt> when appropriate
3255 bool func = atom->string().endsWith("()");
3256 bool tt = (func || atom->string().contains(camelCase));
3257 if (tt)
3258 out() << "<tt>";
3259 if (func) {
3260 out() << protect(atom->string().left(atom->string().length() - 2));
3261 } else {
3262 out() << protect(atom->string());
3263 }
3264 out() << "</tt>";
3265 } else {
3266 out() << protect(atom->string());
3267 }
3268}
3269
3270QString HtmlGenerator::cleanRef(const QString& ref)
3271{
3272 QString clean;
3273
3274 if (ref.isEmpty())
3275 return clean;
3276
3277 clean.reserve(ref.size() + 20);
3278 const QChar c = ref[0];
3279 const uint u = c.unicode();
3280
3281 if ((u >= 'a' && u <= 'z') ||
3282 (u >= 'A' && u <= 'Z') ||
3283 (u >= '0' && u <= '9')) {
3284 clean += c;
3285 } else if (u == '~') {
3286 clean += "dtor.";
3287 } else if (u == '_') {
3288 clean += "underscore.";
3289 } else {
3290 clean += "A";
3291 }
3292
3293 for (int i = 1; i < (int) ref.length(); i++) {
3294 const QChar c = ref[i];
3295 const uint u = c.unicode();
3296 if ((u >= 'a' && u <= 'z') ||
3297 (u >= 'A' && u <= 'Z') ||
3298 (u >= '0' && u <= '9') || u == '-' ||
3299 u == '_' || u == ':' || u == '.') {
3300 clean += c;
3301 } else if (c.isSpace()) {
3302 clean += "-";
3303 } else if (u == '!') {
3304 clean += "-not";
3305 } else if (u == '&') {
3306 clean += "-and";
3307 } else if (u == '<') {
3308 clean += "-lt";
3309 } else if (u == '=') {
3310 clean += "-eq";
3311 } else if (u == '>') {
3312 clean += "-gt";
3313 } else if (u == '#') {
3314 clean += "#";
3315 } else {
3316 clean += "-";
3317 clean += QString::number((int)u, 16);
3318 }
3319 }
3320 return clean;
3321}
3322
3323QString HtmlGenerator::registerRef(const QString& ref)
3324{
3325 QString clean = HtmlGenerator::cleanRef(ref);
3326
3327 for (;;) {
3328 QString& prevRef = refMap[clean.toLower()];
3329 if (prevRef.isEmpty()) {
3330 prevRef = ref;
3331 break;
3332 } else if (prevRef == ref) {
3333 break;
3334 }
3335 clean += "x";
3336 }
3337 return clean;
3338}
3339
3340QString HtmlGenerator::protect(const QString& string)
3341{
3342#define APPEND(x) \
3343 if (html.isEmpty()) { \
3344 html = string; \
3345 html.truncate(i); \
3346 } \
3347 html += (x);
3348
3349 QString html;
3350 int n = string.length();
3351
3352 for (int i = 0; i < n; ++i) {
3353 QChar ch = string.at(i);
3354
3355 if (ch == QLatin1Char('&')) {
3356 APPEND("&amp;");
3357 } else if (ch == QLatin1Char('<')) {
3358 APPEND("&lt;");
3359 } else if (ch == QLatin1Char('>')) {
3360 APPEND("&gt;");
3361 } else if (ch == QLatin1Char('"')) {
3362 APPEND("&quot;");
3363 } else if (ch.unicode() > 0x007F
3364 || (ch == QLatin1Char('*') && i + 1 < n && string.at(i) == QLatin1Char('/'))
3365 || (ch == QLatin1Char('.') && i > 2 && string.at(i - 2) == QLatin1Char('.'))) {
3366 // we escape '*/' and the last dot in 'e.g.' and 'i.e.' for the Javadoc generator
3367 APPEND("&#x");
3368 html += QString::number(ch.unicode(), 16);
3369 html += QLatin1Char(';');
3370 } else {
3371 if (!html.isEmpty())
3372 html += ch;
3373 }
3374 }
3375
3376 if (!html.isEmpty())
3377 return html;
3378 return string;
3379
3380#undef APPEND
3381}
3382
3383QString HtmlGenerator::fileBase(const Node *node)
3384{
3385 QString result;
3386
3387 result = PageGenerator::fileBase(node);
3388
3389 if (!node->isInnerNode()) {
3390 switch (node->status()) {
3391 case Node::Compat:
3392 result += "-qt3";
3393 break;
3394 case Node::Obsolete:
3395 result += "-obsolete";
3396 break;
3397 default:
3398 ;
3399 }
3400 }
3401 return result;
3402}
3403
3404#if 0
3405QString HtmlGenerator::fileBase(const Node *node,
3406 const SectionIterator& section)
3407{
3408 QStringList::ConstIterator s = section.sectionNumber().end();
3409 QStringList::ConstIterator b = section.baseNameStack().end();
3410
3411 QString suffix;
3412 QString base = fileBase(node);
3413
3414 while (s != section.sectionNumber().begin()) {
3415 --s;
3416 --b;
3417 if (!(*b).isEmpty()) {
3418 base = *b;
3419 break;
3420 }
3421 suffix.prepend("-" + *s);
3422 }
3423 return base + suffix;
3424}
3425#endif
3426
3427QString HtmlGenerator::fileName(const Node *node)
3428{
3429 if (node->type() == Node::Fake) {
3430 if (static_cast<const FakeNode *>(node)->subType() == Node::ExternalPage)
3431 return node->name();
3432 if (static_cast<const FakeNode *>(node)->subType() == Node::Image)
3433 return node->name();
3434 }
3435 return PageGenerator::fileName(node);
3436}
3437
3438QString HtmlGenerator::refForNode(const Node *node)
3439{
3440 const FunctionNode *func;
3441 const TypedefNode *typedeffe;
3442 QString ref;
3443
3444 switch (node->type()) {
3445 case Node::Namespace:
3446 case Node::Class:
3447 default:
3448 break;
3449 case Node::Enum:
3450 ref = node->name() + "-enum";
3451 break;
3452 case Node::Typedef:
3453 typedeffe = static_cast<const TypedefNode *>(node);
3454 if (typedeffe->associatedEnum()) {
3455 return refForNode(typedeffe->associatedEnum());
3456 }
3457 else {
3458 ref = node->name() + "-typedef";
3459 }
3460 break;
3461 case Node::Function:
3462 func = static_cast<const FunctionNode *>(node);
3463 if (func->associatedProperty()) {
3464 return refForNode(func->associatedProperty());
3465 }
3466 else {
3467 ref = func->name();
3468 if (func->overloadNumber() != 1)
3469 ref += "-" + QString::number(func->overloadNumber());
3470 }
3471 break;
3472#ifdef QDOC_QML
3473 case Node::Fake:
3474 if (node->subType() != Node::QmlPropertyGroup)
3475 break;
3476 case Node::QmlProperty:
3477#endif
3478 case Node::Property:
3479 ref = node->name() + "-prop";
3480 break;
3481#ifdef QDOC_QML
3482 case Node::QmlSignal:
3483 ref = node->name() + "-signal";
3484 break;
3485 case Node::QmlMethod:
3486 ref = node->name() + "-method";
3487 break;
3488#endif
3489 case Node::Variable:
3490 ref = node->name() + "-var";
3491 break;
3492 case Node::Target:
3493 return protect(node->name());
3494 }
3495 return registerRef(ref);
3496}
3497
3498QString HtmlGenerator::linkForNode(const Node *node, const Node *relative)
3499{
3500 QString link;
3501 QString fn;
3502 QString ref;
3503
3504 if (node == 0 || node == relative)
3505 return QString();
3506 if (!node->url().isEmpty())
3507 return node->url();
3508 if (fileBase(node).isEmpty())
3509 return QString();
3510 if (node->access() == Node::Private)
3511 return QString();
3512
3513 fn = fileName(node);
3514/* if (!node->url().isEmpty())
3515 return fn;*/
3516#if 0
3517 // ### reintroduce this test, without breaking .dcf files
3518 if (fn != outFileName())
3519#endif
3520 link += fn;
3521
3522 if (!node->isInnerNode() || node->subType() == Node::QmlPropertyGroup) {
3523 ref = refForNode(node);
3524 if (relative && fn == fileName(relative) && ref == refForNode(relative))
3525 return QString();
3526
3527 link += "#";
3528 link += ref;
3529 }
3530 return link;
3531}
3532
3533QString HtmlGenerator::refForAtom(Atom *atom, const Node * /* node */)
3534{
3535 if (atom->type() == Atom::SectionLeft) {
3536 return Doc::canonicalTitle(Text::sectionHeading(atom).toString());
3537 }
3538 else if (atom->type() == Atom::Target) {
3539 return Doc::canonicalTitle(atom->string());
3540 }
3541 else {
3542 return QString();
3543 }
3544}
3545
3546void HtmlGenerator::generateFullName(const Node *apparentNode,
3547 const Node *relative,
3548 CodeMarker *marker,
3549 const Node *actualNode)
3550{
3551 if (actualNode == 0)
3552 actualNode = apparentNode;
3553 out() << "<a href=\"" << linkForNode(actualNode, relative);
3554 if (true || relative == 0 || relative->status() != actualNode->status()) {
3555 switch (actualNode->status()) {
3556 case Node::Obsolete:
3557 out() << "\" class=\"obsolete";
3558 break;
3559 case Node::Compat:
3560 out() << "\" class=\"compat";
3561 break;
3562 default:
3563 ;
3564 }
3565 }
3566 out() << "\">";
3567 out() << protect(fullName(apparentNode, relative, marker));
3568 out() << "</a>";
3569}
3570
3571void HtmlGenerator::generateDetailedMember(const Node *node,
3572 const InnerNode *relative,
3573 CodeMarker *marker)
3574{
3575 const EnumNode *enume;
3576
3577 generateMacRef(node, marker);
3578 if (node->type() == Node::Enum
3579 && (enume = static_cast<const EnumNode *>(node))->flagsType()) {
3580 generateMacRef(enume->flagsType(), marker);
3581 out() << "<h3 class=\"flags\">";
3582 out() << "<a name=\"" + refForNode(node) + "\"></a>";
3583 generateSynopsis(enume, relative, marker, CodeMarker::Detailed);
3584 out() << "<br />";
3585 generateSynopsis(enume->flagsType(),
3586 relative,
3587 marker,
3588 CodeMarker::Detailed);
3589 out() << "</h3>\n";
3590 }
3591 else {
3592 out() << "<h3 class=\"fn\">";
3593 out() << "<a name=\"" + refForNode(node) + "\"></a>";
3594 generateSynopsis(node, relative, marker, CodeMarker::Detailed);
3595 out() << "</h3>\n";
3596 }
3597
3598 generateStatus(node, marker);
3599 generateBody(node, marker);
3600 generateThreadSafeness(node, marker);
3601 generateSince(node, marker);
3602
3603 if (node->type() == Node::Property) {
3604 const PropertyNode *property = static_cast<const PropertyNode *>(node);
3605 Section section;
3606
3607 section.members += property->getters();
3608 section.members += property->setters();
3609 section.members += property->resetters();
3610
3611 if (!section.members.isEmpty()) {
3612 out() << "<p><b>Access functions:</b></p>\n";
3613 generateSectionList(section, node, marker, CodeMarker::Accessors);
3614 }
3615
3616 Section notifiers;
3617 notifiers.members += property->notifiers();
3618
3619 if (!notifiers.members.isEmpty()) {
3620 out() << "<p><b>Notifier signal:</b></p>\n";
3621 //out() << "<p>This signal is emitted when the property value is changed.</p>\n";
3622 generateSectionList(notifiers, node, marker, CodeMarker::Accessors);
3623 }
3624 }
3625 else if (node->type() == Node::Enum) {
3626 const EnumNode *enume = static_cast<const EnumNode *>(node);
3627 if (enume->flagsType()) {
3628 out() << "<p>The " << protect(enume->flagsType()->name())
3629 << " type is a typedef for "
3630 << "<a href=\"qflags.html\">QFlags</a>&lt;"
3631 << protect(enume->name())
3632 << "&gt;. It stores an OR combination of "
3633 << protect(enume->name())
3634 << " values.</p>\n";
3635 }
3636 }
3637 generateAlsoList(node, marker);
3638}
3639
3640void HtmlGenerator::findAllClasses(const InnerNode *node)
3641{
3642 NodeList::const_iterator c = node->childNodes().constBegin();
3643 while (c != node->childNodes().constEnd()) {
3644 if ((*c)->access() != Node::Private && (*c)->url().isEmpty()) {
3645 if ((*c)->type() == Node::Class && !(*c)->doc().isEmpty()) {
3646 QString className = (*c)->name();
3647 if ((*c)->parent() &&
3648 (*c)->parent()->type() == Node::Namespace &&
3649 !(*c)->parent()->name().isEmpty())
3650 className = (*c)->parent()->name()+"::"+className;
3651
3652 if (!(static_cast<const ClassNode *>(*c))->hideFromMainList()) {
3653 if ((*c)->status() == Node::Compat) {
3654 compatClasses.insert(className, *c);
3655 }
3656 else if ((*c)->status() == Node::Obsolete) {
3657 obsoleteClasses.insert(className, *c);
3658 }
3659 else {
3660 nonCompatClasses.insert(className, *c);
3661 if ((*c)->status() == Node::Main)
3662 mainClasses.insert(className, *c);
3663 }
3664 }
3665
3666 QString moduleName = (*c)->moduleName();
3667 if (moduleName == "Qt3SupportLight") {
3668 moduleClassMap[moduleName].insert((*c)->name(), *c);
3669 moduleName = "Qt3Support";
3670 }
3671 if (!moduleName.isEmpty())
3672 moduleClassMap[moduleName].insert((*c)->name(), *c);
3673
3674 QString serviceName =
3675 (static_cast<const ClassNode *>(*c))->serviceName();
3676 if (!serviceName.isEmpty())
3677 serviceClasses.insert(serviceName, *c);
3678 }
3679 else if ((*c)->isInnerNode()) {
3680 findAllClasses(static_cast<InnerNode *>(*c));
3681 }
3682 }
3683 ++c;
3684 }
3685}
3686
3687/*!
3688 For generating the "New Classes... in 4.6" section on the
3689 What's New in 4.6" page.
3690 */
3691void HtmlGenerator::findAllSince(const InnerNode *node)
3692{
3693 NodeList::const_iterator child = node->childNodes().constBegin();
3694 while (child != node->childNodes().constEnd()) {
3695 QString sinceVersion = (*child)->since();
3696 if (((*child)->access() != Node::Private) && !sinceVersion.isEmpty()) {
3697 NewSinceMaps::iterator nsmap = newSinceMaps.find(sinceVersion);
3698 if (nsmap == newSinceMaps.end())
3699 nsmap = newSinceMaps.insert(sinceVersion,NodeMultiMap());
3700 NewClassMaps::iterator ncmap = newClassMaps.find(sinceVersion);
3701 if (ncmap == newClassMaps.end())
3702 ncmap = newClassMaps.insert(sinceVersion,NodeMap());
3703
3704 if ((*child)->type() == Node::Function) {
3705 FunctionNode *func = static_cast<FunctionNode *>(*child);
3706 if ((func->status() > Node::Obsolete) &&
3707 (func->metaness() != FunctionNode::Ctor) &&
3708 (func->metaness() != FunctionNode::Dtor)) {
3709 nsmap.value().insert(func->name(),(*child));
3710 }
3711 }
3712 else if ((*child)->url().isEmpty()) {
3713 if ((*child)->type() == Node::Class && !(*child)->doc().isEmpty()) {
3714 QString className = (*child)->name();
3715 if ((*child)->parent() &&
3716 (*child)->parent()->type() == Node::Namespace &&
3717 !(*child)->parent()->name().isEmpty())
3718 className = (*child)->parent()->name()+"::"+className;
3719 nsmap.value().insert(className,(*child));
3720 ncmap.value().insert(className,(*child));
3721 }
3722 }
3723 else {
3724 QString name = (*child)->name();
3725 if ((*child)->parent() &&
3726 (*child)->parent()->type() == Node::Namespace &&
3727 !(*child)->parent()->name().isEmpty())
3728 name = (*child)->parent()->name()+"::"+name;
3729 nsmap.value().insert(name,(*child));
3730 }
3731 if ((*child)->isInnerNode()) {
3732 findAllSince(static_cast<InnerNode *>(*child));
3733 }
3734 }
3735 ++child;
3736 }
3737}
3738
3739#if 0
3740 const QRegExp versionSeparator("[\\-\\.]");
3741 const int minorIndex = version.indexOf(versionSeparator);
3742 const int patchIndex = version.indexOf(versionSeparator, minorIndex+1);
3743 version = version.left(patchIndex);
3744#endif
3745
3746void HtmlGenerator::findAllFunctions(const InnerNode *node)
3747{
3748 NodeList::ConstIterator c = node->childNodes().begin();
3749 while (c != node->childNodes().end()) {
3750 if ((*c)->access() != Node::Private) {
3751 if ((*c)->isInnerNode() && (*c)->url().isEmpty()) {
3752 findAllFunctions(static_cast<const InnerNode *>(*c));
3753 }
3754 else if ((*c)->type() == Node::Function) {
3755 const FunctionNode *func = static_cast<const FunctionNode *>(*c);
3756 if ((func->status() > Node::Obsolete) &&
3757 (func->metaness() != FunctionNode::Ctor) &&
3758 (func->metaness() != FunctionNode::Dtor)) {
3759 funcIndex[(*c)->name()].insert(myTree->fullDocumentName((*c)->parent()), *c);
3760 }
3761 }
3762 }
3763 ++c;
3764 }
3765}
3766
3767void HtmlGenerator::findAllLegaleseTexts(const InnerNode *node)
3768{
3769 NodeList::ConstIterator c = node->childNodes().begin();
3770 while (c != node->childNodes().end()) {
3771 if ((*c)->access() != Node::Private) {
3772 if (!(*c)->doc().legaleseText().isEmpty())
3773 legaleseTexts.insertMulti((*c)->doc().legaleseText(), *c);
3774 if ((*c)->isInnerNode())
3775 findAllLegaleseTexts(static_cast<const InnerNode *>(*c));
3776 }
3777 ++c;
3778 }
3779}
3780
3781void HtmlGenerator::findAllNamespaces(const InnerNode *node)
3782{
3783 NodeList::ConstIterator c = node->childNodes().begin();
3784 while (c != node->childNodes().end()) {
3785 if ((*c)->access() != Node::Private) {
3786 if ((*c)->isInnerNode() && (*c)->url().isEmpty()) {
3787 findAllNamespaces(static_cast<const InnerNode *>(*c));
3788 if ((*c)->type() == Node::Namespace) {
3789 const NamespaceNode *nspace = static_cast<const NamespaceNode *>(*c);
3790 // Ensure that the namespace's name is not empty (the root
3791 // namespace has no name).
3792 if (!nspace->name().isEmpty()) {
3793 namespaceIndex.insert(nspace->name(), *c);
3794 QString moduleName = (*c)->moduleName();
3795 if (moduleName == "Qt3SupportLight") {
3796 moduleNamespaceMap[moduleName].insert((*c)->name(), *c);
3797 moduleName = "Qt3Support";
3798 }
3799 if (!moduleName.isEmpty())
3800 moduleNamespaceMap[moduleName].insert((*c)->name(), *c);
3801 }
3802 }
3803 }
3804 }
3805 ++c;
3806 }
3807}
3808
3809#ifdef ZZZ_QDOC_QML
3810/*!
3811 This function finds all the qml element nodes and
3812 stores them in a map for later use.
3813 */
3814void HtmlGenerator::findAllQmlClasses(const InnerNode *node)
3815{
3816 NodeList::const_iterator c = node->childNodes().constBegin();
3817 while (c != node->childNodes().constEnd()) {
3818 if ((*c)->type() == Node::Fake) {
3819 const FakeNode* fakeNode = static_cast<const FakeNode *>(*c);
3820 if (fakeNode->subType() == Node::QmlClass) {
3821 const QmlClassNode* qmlNode =
3822 static_cast<const QmlClassNode*>(fakeNode);
3823 const Node* n = qmlNode->classNode();
3824 }
3825 qmlClasses.insert(fakeNode->name(),*c);
3826 }
3827 ++c;
3828 }
3829}
3830#endif
3831
3832int HtmlGenerator::hOffset(const Node *node)
3833{
3834 switch (node->type()) {
3835 case Node::Namespace:
3836 case Node::Class:
3837 return 2;
3838 case Node::Fake:
3839 if (node->doc().briefText().isEmpty())
3840 return 1;
3841 else
3842 return 2;
3843 case Node::Enum:
3844 case Node::Typedef:
3845 case Node::Function:
3846 case Node::Property:
3847 default:
3848 return 3;
3849 }
3850}
3851
3852bool HtmlGenerator::isThreeColumnEnumValueTable(const Atom *atom)
3853{
3854 while (atom != 0 && !(atom->type() == Atom::ListRight && atom->string() == ATOM_LIST_VALUE)) {
3855 if (atom->type() == Atom::ListItemLeft && !matchAhead(atom, Atom::ListItemRight))
3856 return true;
3857 atom = atom->next();
3858 }
3859 return false;
3860}
3861
3862const Node *HtmlGenerator::findNodeForTarget(const QString &target,
3863 const Node *relative,
3864 CodeMarker *marker,
3865 const Atom *atom)
3866{
3867 const Node *node = 0;
3868
3869 if (target.isEmpty()) {
3870 node = relative;
3871 }
3872 else if (target.endsWith(".html")) {
3873 node = myTree->root()->findNode(target, Node::Fake);
3874 }
3875 else if (marker) {
3876 node = marker->resolveTarget(target, myTree, relative);
3877 if (!node)
3878 node = myTree->findFakeNodeByTitle(target);
3879 if (!node && atom) {
3880 node = myTree->findUnambiguousTarget(target,
3881 *const_cast<Atom**>(&atom));
3882 }
3883 }
3884
3885 if (!node)
3886 relative->doc().location().warning(tr("Cannot link to '%1'").arg(target));
3887
3888 return node;
3889}
3890
3891const QPair<QString,QString> HtmlGenerator::anchorForNode(const Node *node)
3892{
3893 QPair<QString,QString> anchorPair;
3894
3895 anchorPair.first = PageGenerator::fileName(node);
3896 if (node->type() == Node::Fake) {
3897 const FakeNode *fakeNode = static_cast<const FakeNode*>(node);
3898 anchorPair.second = fakeNode->title();
3899 }
3900
3901 return anchorPair;
3902}
3903
3904QString HtmlGenerator::getLink(const Atom *atom,
3905 const Node *relative,
3906 CodeMarker *marker,
3907 const Node** node)
3908{
3909 QString link;
3910 *node = 0;
3911 inObsoleteLink = false;
3912
3913 if (atom->string().contains(":") &&
3914 (atom->string().startsWith("file:")
3915 || atom->string().startsWith("http:")
3916 || atom->string().startsWith("https:")
3917 || atom->string().startsWith("ftp:")
3918 || atom->string().startsWith("mailto:"))) {
3919
3920 link = atom->string();
3921 }
3922 else {
3923 QStringList path;
3924 if (atom->string().contains('#')) {
3925 path = atom->string().split('#');
3926 }
3927 else {
3928 path.append(atom->string());
3929 }
3930
3931 Atom *targetAtom = 0;
3932
3933 QString first = path.first().trimmed();
3934 if (first.isEmpty()) {
3935 *node = relative;
3936 }
3937 else if (first.endsWith(".html")) {
3938 *node = myTree->root()->findNode(first, Node::Fake);
3939 }
3940 else {
3941 *node = marker->resolveTarget(first, myTree, relative);
3942 if (!*node)
3943 *node = myTree->findFakeNodeByTitle(first);
3944 if (!*node)
3945 *node = myTree->findUnambiguousTarget(first, targetAtom);
3946 }
3947
3948 if (*node) {
3949 if (!(*node)->url().isEmpty())
3950 return (*node)->url();
3951 else
3952 path.removeFirst();
3953 }
3954 else {
3955 *node = relative;
3956 }
3957
3958 if (*node) {
3959 if ((*node)->status() == Node::Obsolete) {
3960 if (relative) {
3961 if (relative->parent() != *node) {
3962 if (relative->status() != Node::Obsolete) {
3963 bool porting = false;
3964 if (relative->type() == Node::Fake) {
3965 const FakeNode* fake = static_cast<const FakeNode*>(relative);
3966 if (fake->title().startsWith("Porting"))
3967 porting = true;
3968 }
3969 QString name = marker->plainFullName(relative);
3970 if (!porting && !name.startsWith("Q3")) {
3971 if (obsoleteLinks) {
3972 relative->doc().location().warning(tr("Link to obsolete item '%1' in %2")
3973 .arg(atom->string())
3974 .arg(name));
3975 }
3976 inObsoleteLink = true;
3977 }
3978 }
3979 }
3980 }
3981 else {
3982 qDebug() << "Link to Obsolete entity"
3983 << (*node)->name() << "no relative";
3984 }
3985 }
3986#if 0
3987 else if ((*node)->status() == Node::Deprecated) {
3988 qDebug() << "Link to Deprecated entity";
3989 }
3990 else if ((*node)->status() == Node::Internal) {
3991 qDebug() << "Link to Internal entity";
3992 }
3993#endif
3994 }
3995
3996 while (!path.isEmpty()) {
3997 targetAtom = myTree->findTarget(path.first(), *node);
3998 if (targetAtom == 0)
3999 break;
4000 path.removeFirst();
4001 }
4002
4003 if (path.isEmpty()) {
4004 link = linkForNode(*node, relative);
4005 if (*node && (*node)->subType() == Node::Image)
4006 link = "images/used-in-examples/" + link;
4007 if (targetAtom)
4008 link += "#" + refForAtom(targetAtom, *node);
4009 }
4010 }
4011 return link;
4012}
4013
4014void HtmlGenerator::generateDcf(const QString &fileBase,
4015 const QString &startPage,
4016 const QString &title,
4017 DcfSection &dcfRoot)
4018{
4019 dcfRoot.ref = startPage;
4020 dcfRoot.title = title;
4021 generateDcfSections(dcfRoot, outputDir() + "/" + fileBase + ".dcf", fileBase + "/reference");
4022}
4023
4024void HtmlGenerator::generateIndex(const QString &fileBase,
4025 const QString &url,
4026 const QString &title)
4027{
4028 myTree->generateIndex(outputDir() + "/" + fileBase + ".index", url, title);
4029}
4030
4031void HtmlGenerator::generateStatus(const Node *node, CodeMarker *marker)
4032{
4033 Text text;
4034
4035 switch (node->status()) {
4036 case Node::Obsolete:
4037 if (node->isInnerNode())
4038 Generator::generateStatus(node, marker);
4039 break;
4040 case Node::Compat:
4041 if (node->isInnerNode()) {
4042 text << Atom::ParaLeft
4043 << Atom(Atom::FormattingLeft,ATOM_FORMATTING_BOLD)
4044 << "This "
4045 << typeString(node)
4046 << " is part of the Qt 3 support library."
4047 << Atom(Atom::FormattingRight, ATOM_FORMATTING_BOLD)
4048 << " It is provided to keep old source code working. "
4049 << "We strongly advise against "
4050 << "using it in new code. See ";
4051
4052 const FakeNode *fakeNode = myTree->findFakeNodeByTitle("Porting To Qt 4");
4053 Atom *targetAtom = 0;
4054 if (fakeNode && node->type() == Node::Class) {
4055 QString oldName(node->name());
4056 targetAtom = myTree->findTarget(oldName.replace("3", ""),
4057 fakeNode);
4058 }
4059
4060 if (targetAtom) {
4061 text << Atom(Atom::Link, linkForNode(fakeNode, node) + "#" +
4062 refForAtom(targetAtom, fakeNode));
4063 }
4064 else
4065 text << Atom(Atom::Link, "Porting to Qt 4");
4066
4067 text << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK)
4068 << Atom(Atom::String, "Porting to Qt 4")
4069 << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK)
4070 << " for more information."
4071 << Atom::ParaRight;
4072 }
4073 generateText(text, node, marker);
4074 break;
4075 default:
4076 Generator::generateStatus(node, marker);
4077 }
4078}
4079
4080void HtmlGenerator::generateMacRef(const Node *node, CodeMarker *marker)
4081{
4082 if (!pleaseGenerateMacRef || marker == 0)
4083 return;
4084
4085 QStringList macRefs = marker->macRefsForNode(node);
4086 foreach (const QString &macRef, macRefs)
4087 out() << "<a name=\"" << "//apple_ref/" << macRef << "\"></a>\n";
4088}
4089
4090void HtmlGenerator::beginLink(const QString &link,
4091 const Node *node,
4092 const Node *relative,
4093 CodeMarker *marker)
4094{
4095 Q_UNUSED(marker)
4096 Q_UNUSED(relative)
4097
4098 this->link = link;
4099 if (link.isEmpty()) {
4100 if (showBrokenLinks)
4101 out() << "<i>";
4102 }
4103 else if (node == 0 || (relative != 0 &&
4104 node->status() == relative->status())) {
4105 out() << "<a href=\"" << link << "\">";
4106 }
4107 else {
4108 switch (node->status()) {
4109 case Node::Obsolete:
4110 out() << "<a href=\"" << link << "\" class=\"obsolete\">";
4111 break;
4112 case Node::Compat:
4113 out() << "<a href=\"" << link << "\" class=\"compat\">";
4114 break;
4115 default:
4116 out() << "<a href=\"" << link << "\">";
4117 }
4118 }
4119 inLink = true;
4120}
4121
4122void HtmlGenerator::endLink()
4123{
4124 if (inLink) {
4125 if (link.isEmpty()) {
4126 if (showBrokenLinks)
4127 out() << "</i>";
4128 }
4129 else {
4130 if (inObsoleteLink) {
4131 out() << "<sup>(obsolete)</sup>";
4132 }
4133 out() << "</a>";
4134 }
4135 }
4136 inLink = false;
4137 inObsoleteLink = false;
4138}
4139
4140QT_END_NAMESPACE
4141
4142#ifdef QDOC_QML
4143
4144/*!
4145 Generates the summary for for the \a section. Only used for
4146 sections of QML element documentation.
4147
4148 Currently handles only the QML property group.
4149 */
4150void HtmlGenerator::generateQmlSummary(const Section& section,
4151 const Node *relative,
4152 CodeMarker *marker)
4153{
4154 if (!section.members.isEmpty()) {
4155 NodeList::ConstIterator m;
4156 int count = section.members.size();
4157 bool twoColumn = false;
4158 if (section.members.first()->type() == Node::QmlProperty) {
4159 twoColumn = (count >= 5);
4160 }
4161 if (twoColumn)
4162 out() << "<p><table width=\"100%\" border=\"0\" cellpadding=\"0\""
4163 " cellspacing=\"0\">\n"
4164 << "<tr><td width=\"45%\" valign=\"top\">";
4165 out() << "<ul>\n";
4166
4167 int row = 0;
4168 m = section.members.begin();
4169 while (m != section.members.end()) {
4170 if (twoColumn && row == (int) (count + 1) / 2)
4171 out() << "</ul></td><td valign=\"top\"><ul>\n";
4172 out() << "<li><div class=\"fn\"></div>";
4173 generateQmlItem(*m,relative,marker,true);
4174 out() << "</li>\n";
4175 row++;
4176 ++m;
4177 }
4178 out() << "</ul>\n";
4179 if (twoColumn)
4180 out() << "</td></tr>\n</table></p>\n";
4181 }
4182}
4183
4184/*!
4185 Outputs the html detailed documentation for a section
4186 on a QML element reference page.
4187 */
4188void HtmlGenerator::generateDetailedQmlMember(const Node *node,
4189 const InnerNode *relative,
4190 CodeMarker *marker)
4191{
4192 const QmlPropertyNode* qpn = 0;
4193 generateMacRef(node, marker);
4194 out() << "<div class=\"qmlitem\">";
4195 if (node->subType() == Node::QmlPropertyGroup) {
4196 const QmlPropGroupNode* qpgn = static_cast<const QmlPropGroupNode*>(node);
4197 NodeList::ConstIterator p = qpgn->childNodes().begin();
4198 out() << "<div class=\"qmlproto\">";
4199 out() << "<table width=\"100%\" class=\"qmlname\">";
4200
4201 while (p != qpgn->childNodes().end()) {
4202 if ((*p)->type() == Node::QmlProperty) {
4203 qpn = static_cast<const QmlPropertyNode*>(*p);
4204 out() << "<tr><td>";
4205 out() << "<a name=\"" + refForNode(qpn) + "\"></a>";
4206 if (!qpn->isWritable())
4207 out() << "<span class=\"qmlreadonly\">read-only</span>";
4208 generateQmlItem(qpn, relative, marker, false);
4209 out() << "</td></tr>";
4210 if (qpgn->isDefault()) {
4211 out() << "</table>"
4212 << "</div></div>"
4213 << "<div class=\"qmlitem\">"
4214 << "<div class=\"qmlproto\">"
4215 << "<table class=\"qmlname\">"
4216 << "<tr><td><font color=\"green\">"
4217 << "default</font></td></tr>";
4218 }
4219 }
4220 ++p;
4221 }
4222 out() << "</table>";
4223 out() << "</div>";
4224 }
4225 else if (node->type() == Node::QmlSignal) {
4226 const FunctionNode* qsn = static_cast<const FunctionNode*>(node);
4227 out() << "<div class=\"qmlproto\">";
4228 out() << "<table class=\"qmlname\">";
4229 out() << "<tr><td>";
4230 out() << "<a name=\"" + refForNode(qsn) + "\"></a>";
4231 generateSynopsis(qsn,relative,marker,CodeMarker::Detailed,false);
4232 //generateQmlItem(qsn,relative,marker,false);
4233 out() << "</td></tr>";
4234 out() << "</table>";
4235 out() << "</div>";
4236 }
4237 else if (node->type() == Node::QmlMethod) {
4238 const FunctionNode* qmn = static_cast<const FunctionNode*>(node);
4239 out() << "<div class=\"qmlproto\">";
4240 out() << "<table class=\"qmlname\">";
4241 out() << "<tr><td>";
4242 out() << "<a name=\"" + refForNode(qmn) + "\"></a>";
4243 generateSynopsis(qmn,relative,marker,CodeMarker::Detailed,false);
4244 out() << "</td></tr>";
4245 out() << "</table>";
4246 out() << "</div>";
4247 }
4248 out() << "<div class=\"qmldoc\">";
4249 generateStatus(node, marker);
4250 generateBody(node, marker);
4251 generateThreadSafeness(node, marker);
4252 generateSince(node, marker);
4253 generateAlsoList(node, marker);
4254 out() << "</div>";
4255 out() << "</div>";
4256}
4257
4258/*!
4259 Output the "Inherits" line for the QML element,
4260 if there should be one.
4261 */
4262void HtmlGenerator::generateQmlInherits(const QmlClassNode* cn,
4263 CodeMarker* marker)
4264{
4265 if (cn && !cn->links().empty()) {
4266 if (cn->links().contains(Node::InheritsLink)) {
4267 QPair<QString,QString> linkPair;
4268 linkPair = cn->links()[Node::InheritsLink];
4269 QStringList strList(linkPair.first);
4270 const Node* n = myTree->findNode(strList,Node::Fake);
4271 if (n && n->subType() == Node::QmlClass) {
4272 const QmlClassNode* qcn = static_cast<const QmlClassNode*>(n);
4273 out() << "<p style=\"text-align: center\">";
4274 Text text;
4275 text << "[Inherits ";
4276 text << Atom(Atom::LinkNode,CodeMarker::stringForNode(qcn));
4277 text << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK);
4278 text << Atom(Atom::String, linkPair.second);
4279 text << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK);
4280 text << "]";
4281 generateText(text, cn, marker);
4282 out() << "</p>";
4283 }
4284 }
4285 }
4286}
4287
4288/*!
4289 Output the "[Xxx instantiates the C++ class QmlGraphicsXxx]"
4290 line for the QML element, if there should be one.
4291
4292 If there is no class node, or if the class node status
4293 is set to Node::Internal, do nothing.
4294 */
4295void HtmlGenerator::generateQmlInstantiates(const QmlClassNode* qcn,
4296 CodeMarker* marker)
4297{
4298 const ClassNode* cn = qcn->classNode();
4299 if (cn && (cn->status() != Node::Internal)) {
4300 out() << "<p style=\"text-align: center\">";
4301 Text text;
4302 text << "[";
4303 text << Atom(Atom::LinkNode,CodeMarker::stringForNode(qcn));
4304 text << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK);
4305 text << Atom(Atom::String, qcn->name());
4306 text << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK);
4307 text << " instantiates the C++ class ";
4308 text << Atom(Atom::LinkNode,CodeMarker::stringForNode(cn));
4309 text << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK);
4310 text << Atom(Atom::String, cn->name());
4311 text << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK);
4312 text << "]";
4313 generateText(text, qcn, marker);
4314 out() << "</p>";
4315 }
4316}
4317
4318/*!
4319 Output the "[QmlGraphicsXxx is instantiated by QML element Xxx]"
4320 line for the class, if there should be one.
4321
4322 If there is no QML element, or if the class node status
4323 is set to Node::Internal, do nothing.
4324 */
4325void HtmlGenerator::generateInstantiatedBy(const ClassNode* cn,
4326 CodeMarker* marker)
4327{
4328 if (cn && cn->status() != Node::Internal && !cn->qmlElement().isEmpty()) {
4329 const Node* n = myTree->root()->findNode(cn->qmlElement(),Node::Fake);
4330 if (n && n->subType() == Node::QmlClass) {
4331 out() << "<p style=\"text-align: center\">";
4332 Text text;
4333 text << "[";
4334 text << Atom(Atom::LinkNode,CodeMarker::stringForNode(cn));
4335 text << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK);
4336 text << Atom(Atom::String, cn->name());
4337 text << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK);
4338 text << " is instantiated by QML element ";
4339 text << Atom(Atom::LinkNode,CodeMarker::stringForNode(n));
4340 text << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK);
4341 text << Atom(Atom::String, n->name());
4342 text << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK);
4343 text << "]";
4344 generateText(text, cn, marker);
4345 out() << "</p>";
4346 }
4347 }
4348}
4349
4350#endif
Note: See TracBrowser for help on using the repository browser.