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