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 | |
62 | QT_BEGIN_NAMESPACE |
63 | |
64 | #define COMMAND_VERSION Doc::alias("version") |
65 | int HtmlGenerator::id = 0; |
66 | bool HtmlGenerator::debugging_on = false; |
67 | |
68 | QString HtmlGenerator::divNavTop = ""; |
69 | |
70 | static bool showBrokenLinks = false; |
71 | |
72 | static QRegExp linkTag("(<@link node=\"([^\"]+)\">).*(</@link>)"); |
73 | static QRegExp funcTag("(<@func target=\"([^\"]*)\">)(.*)(</@func>)"); |
74 | static QRegExp typeTag("(<@(type|headerfile|func)(?: +[^>]*)?>)(.*)(</@\\2>)"); |
75 | static QRegExp spanTag("</@(?:comment|preprocessor|string|char|number|op|type|name|keyword)>"); |
76 | static QRegExp unknownTag("</?@[^>]*>"); |
77 | |
78 | static 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 | |
95 | HtmlGenerator::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 | |
110 | HtmlGenerator::~HtmlGenerator() |
111 | { |
112 | if (helpProjectWriter) |
113 | delete helpProjectWriter; |
114 | } |
115 | |
116 | void 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 | |
227 | void HtmlGenerator::terminateGenerator() |
228 | { |
229 | Generator::terminateGenerator(); |
230 | } |
231 | |
232 | QString 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 | */ |
242 | void 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 | |
271 | void 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 | */ |
287 | int 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() << " "; |
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\"><Missing HTML></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 | */ |
1105 | void 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,§ions); |
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 | */ |
1305 | void 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,§ions); |
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 | */ |
1503 | QString HtmlGenerator::fileExtension(const Node * /* node */) const |
1504 | { |
1505 | return "html"; |
1506 | } |
1507 | |
1508 | /*! |
1509 | Output breadcrumb list in the html file. |
1510 | */ |
1511 | void 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 | |
1634 | void 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( |
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 | |
1730 | void 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 | |
1749 | void 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 | |
1761 | void 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 | |
1781 | void 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 | */ |
1796 | void 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 | |
1909 | QString 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 | */ |
1943 | QString 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 | |
1970 | QString 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 | |
2043 | void 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 | |
2089 | void 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 | */ |
2137 | void 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( |
2232 | paragraphNr = 10 + key[0].unicode() - |
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( |
2261 | if (usedParagraphNames.contains(char( |
2262 | out() << QString("<a href=\"#%1\">%2</a> ").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 | |
2343 | void 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( |
2349 | out() << QString("<a href=\"#%1\">%2</a> ").arg(ch).arg(ch.toUpper()); |
2350 | } |
2351 | out() << "</b></p>\n"; |
2352 | |
2353 | char nextLetter = |
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 | |
2393 | void 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 |
2413 | void 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 | |
2445 | void 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 | |
2559 | void 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 | |
2620 | void 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 | |
2687 | void 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 | |
2709 | void 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 | |
2749 | QString 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( |
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 | |
2937 | void 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 | |
2960 | QString 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 >= |
2972 | (u >= |
2973 | (u >= |
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 >= |
2987 | (u >= |
2988 | (u >= |
2989 | 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 | |
3013 | QString 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 | |
3030 | QString HtmlGenerator::protectEnc(const QString &string) |
3031 | { |
3032 | return protect(string, outputEncoding); |
3033 | } |
3034 | |
3035 | QString 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("&"); |
3052 | } else if (ch == QLatin1Char( |
3053 | APPEND("<"); |
3054 | } else if (ch == QLatin1Char( |
3055 | APPEND(">"); |
3056 | } else if (ch == QLatin1Char( |
3057 | APPEND("""); |
3058 | } else if ((outputEncoding == "ISO-8859-1"&& ch.unicode() > 0x007F) |
3059 | || (ch == QLatin1Char( |
3060 | || (ch == 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 | |
3078 | QString 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 | |
3099 | QString 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 | |
3110 | QString 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 | |
3170 | QString 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 | |
3202 | QString 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 | |
3215 | void 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 | |
3240 | void 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><" |
3305 | << protectEnc(enume->name()) |
3306 | << ">. 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 | |
3315 | void 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 | |
3372 | void 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 | |
3394 | void 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 | |
3408 | void 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 | |
3436 | int 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 | |
3453 | bool 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 | |
3463 | const 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 | |
3492 | const 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 | |
3505 | QString 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 | |
3609 | void HtmlGenerator::generateIndex(const QString &fileBase, |
3610 | const QString &url, |
3611 | const QString &title) |
3612 | { |
3613 | myTree->generateIndex(outputDir() + "/"+ fileBase + ".index", url, title); |
3614 | } |
3615 | |
3616 | void 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 | */ |
3669 | void 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 | |
3680 | void 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 | |
3712 | void 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 | */ |
3736 | void 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 | */ |
3758 | void 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 | */ |
3848 | void 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 | */ |
3876 | void 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 | */ |
3899 | void 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 | */ |
3930 | void 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 | */ |
3957 | bool 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 | */ |
4117 | void 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 | */ |
4134 | void 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 | |
4154 | void 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 | */ |
4195 | QString 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 | */ |
4340 | void 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 | */ |
4352 | void 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( |
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( |
4477 | QString baseName = fileName; |
4478 | if ((fileName.count(QChar( |
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 | |
4526 | QT_END_NAMESPACE |
4527 |
Warning: That file was not part of the compilation database. It may have many parsing errors.