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

Last change on this file since 441 was 2, checked in by Dmitry A. Kuminov, 16 years ago

Initially imported qt-all-opensource-src-4.5.1 from Trolltech.

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