Warning: That file was not part of the compilation database. It may have many parsing errors.

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

Warning: That file was not part of the compilation database. It may have many parsing errors.