Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
docbookgenerator.cpp
Go to the documentation of this file.
1// Copyright (C) 2019 Thibaut Cuvelier
2// Copyright (C) 2021 The Qt Company Ltd.
3// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
4
6
7#include "access.h"
8#include "aggregate.h"
9#include "classnode.h"
10#include "codemarker.h"
11#include "collectionnode.h"
13#include "config.h"
14#include "enumnode.h"
15#include "examplenode.h"
16#include "functionnode.h"
17#include "generator.h"
18#include "node.h"
19#include "propertynode.h"
20#include "quoter.h"
21#include "qdocdatabase.h"
24#include "typedefnode.h"
25#include "variablenode.h"
26
27#include <QtCore/qlist.h>
28#include <QtCore/qmap.h>
29#include <QtCore/quuid.h>
30#include <QtCore/qurl.h>
31#include <QtCore/qregularexpression.h>
32#include <QtCore/qversionnumber.h>
33
34#include <cctype>
35
37
38using namespace Qt::StringLiterals;
39
40static const char dbNamespace[] = "http://docbook.org/ns/docbook";
41static const char xlinkNamespace[] = "http://www.w3.org/1999/xlink";
42static const char itsNamespace[] = "http://www.w3.org/2005/11/its";
43
45
46inline void DocBookGenerator::newLine()
47{
48 m_writer->writeCharacters("\n");
49}
50
51void DocBookGenerator::writeXmlId(const QString &id)
52{
53 if (id.isEmpty())
54 return;
55
56 m_writer->writeAttribute("xml:id", registerRef(id, true));
57}
58
59void DocBookGenerator::writeXmlId(const Node *node)
60{
61 if (!node)
62 return;
63
64 // Specifically for nodes, do not use the same code path as for QString
65 // inputs, as refForNode calls registerRef in all cases. Calling
66 // registerRef a second time adds a character to "disambiguate" the two IDs
67 // (the one returned by refForNode, then the one that is written as
68 // xml:id).
69 QString id = Generator::cleanRef(refForNode(node), true);
70 if (!id.isEmpty())
71 m_writer->writeAttribute("xml:id", id);
72}
73
74void DocBookGenerator::startSectionBegin(const QString &id)
75{
76 m_hasSection = true;
77
78 m_writer->writeStartElement(dbNamespace, "section");
79 writeXmlId(id);
80 newLine();
81 m_writer->writeStartElement(dbNamespace, "title");
82}
83
84void DocBookGenerator::startSectionBegin(const Node *node)
85{
86 m_writer->writeStartElement(dbNamespace, "section");
87 writeXmlId(node);
88 newLine();
89 m_writer->writeStartElement(dbNamespace, "title");
90}
91
92void DocBookGenerator::startSectionEnd()
93{
94 m_writer->writeEndElement(); // title
95 newLine();
96}
97
98void DocBookGenerator::startSection(const QString &id, const QString &title)
99{
100 startSectionBegin(id);
101 m_writer->writeCharacters(title);
102 startSectionEnd();
103}
104
105void DocBookGenerator::startSection(const Node *node, const QString &title)
106{
107 startSectionBegin(node);
108 m_writer->writeCharacters(title);
109 startSectionEnd();
110}
111
112void DocBookGenerator::startSection(const QString &title)
113{
114 // No xml:id given: down the calls, "" is interpreted as "no ID".
115 startSection("", title);
116}
117
118void DocBookGenerator::endSection()
119{
120 m_writer->writeEndElement(); // section
121 newLine();
122}
123
124void DocBookGenerator::writeAnchor(const QString &id)
125{
126 if (id.isEmpty())
127 return;
128
129 m_writer->writeEmptyElement(dbNamespace, "anchor");
130 writeXmlId(id);
131 newLine();
132}
133
134/*!
135 Initializes the DocBook output generator's data structures
136 from the configuration (Config).
137 */
139{
140 // Excerpts from HtmlGenerator::initializeGenerator.
142 m_config = &Config::instance();
143
144 m_project = m_config->get(CONFIG_PROJECT).asString();
145
146 m_projectDescription = m_config->get(CONFIG_DESCRIPTION).asString();
147 if (m_projectDescription.isEmpty() && !m_project.isEmpty())
148 m_projectDescription = m_project + QLatin1String(" Reference Documentation");
149
150 m_naturalLanguage = m_config->get(CONFIG_NATURALLANGUAGE).asString();
151 if (m_naturalLanguage.isEmpty())
152 m_naturalLanguage = QLatin1String("en");
153
154 m_buildVersion = m_config->get(CONFIG_BUILDVERSION).asString();
155 m_useDocBook52 = m_config->get(CONFIG_DOCBOOKEXTENSIONS).asBool() ||
156 m_config->get(format() + Config::dot + "usedocbookextensions").asBool();
157 m_useITS = m_config->get(format() + Config::dot + "its").asBool();
158}
159
161{
162 return "DocBook";
163}
164
165/*!
166 Returns "xml" for this subclass of Generator.
167 */
169{
170 return "xml";
171}
172
173/*!
174 Generate the documentation for \a relative. i.e. \a relative
175 is the node that represents the entity where a qdoc comment
176 was found, and \a text represents the qdoc comment.
177 */
178bool DocBookGenerator::generateText(const Text &text, const Node *relative)
179{
180 // From Generator::generateText.
181 if (!text.firstAtom())
182 return false;
183
184 int numAtoms = 0;
186 generateAtomList(text.firstAtom(), relative, nullptr, true, numAtoms);
188 return true;
189}
190
191QString removeCodeMarkers(const QString& code) {
192 QString rewritten = code;
193 static const QRegularExpression re("(<@[^>&]*>)|(<\\/@[^&>]*>)");
194 rewritten.replace(re, "");
195 return rewritten;
196}
197
198/*!
199 Generate DocBook from an instance of Atom.
200 */
202{
203 Q_ASSERT(m_writer);
204 // From HtmlGenerator::generateAtom, without warning generation.
205 int idx = 0;
206 int skipAhead = 0;
207 Node::Genus genus = Node::DontCare;
208
209 switch (atom->type()) {
210 case Atom::AutoLink:
211 // Allow auto-linking to nodes in API reference
212 genus = Node::API;
213 Q_FALLTHROUGH();
216 const Node *node = nullptr;
217 QString link = getAutoLink(atom, relative, &node, genus);
218 if (!link.isEmpty() && node && node->isDeprecated()
219 && relative->parent() != node && !relative->isDeprecated()) {
220 link.clear();
221 }
222 if (link.isEmpty()) {
223 m_writer->writeCharacters(atom->string());
224 } else {
225 beginLink(link, node, relative);
226 generateLink(atom);
227 endLink();
228 }
229 } else {
230 m_writer->writeCharacters(atom->string());
231 }
232 break;
233 case Atom::BaseName:
234 break;
235 case Atom::BriefLeft:
236 if (!hasBrief(relative)) {
237 skipAhead = skipAtoms(atom, Atom::BriefRight);
238 break;
239 }
240 m_writer->writeStartElement(dbNamespace, "para");
241 m_inPara = true;
242 rewritePropertyBrief(atom, relative);
243 break;
244 case Atom::BriefRight:
245 if (hasBrief(relative)) {
246 m_writer->writeEndElement(); // para
247 m_inPara = false;
248 newLine();
249 }
250 break;
251 case Atom::C:
252 // This may at one time have been used to mark up C++ code but it is
253 // now widely used to write teletype text. As a result, text marked
254 // with the \c command is not passed to a code marker.
255 if (m_inTeletype)
256 m_writer->writeCharacters(plainCode(atom->string()));
257 else
258 m_writer->writeTextElement(dbNamespace, "code", plainCode(atom->string()));
259 break;
260 case Atom::CaptionLeft:
261 m_writer->writeStartElement(dbNamespace, "title");
262 break;
264 endLink();
265 m_writer->writeEndElement(); // title
266 newLine();
267 break;
268 case Atom::Qml:
269 m_writer->writeStartElement(dbNamespace, "programlisting");
270 m_writer->writeAttribute("language", "qml");
271 if (m_useITS)
272 m_writer->writeAttribute(itsNamespace, "translate", "no");
273 m_writer->writeCharacters(plainCode(removeCodeMarkers(atom->string())));
274 m_writer->writeEndElement(); // programlisting
275 newLine();
276 break;
277 case Atom::Code:
278 m_writer->writeStartElement(dbNamespace, "programlisting");
279 m_writer->writeAttribute("language", "cpp");
280 if (m_useITS)
281 m_writer->writeAttribute(itsNamespace, "translate", "no");
282 m_writer->writeCharacters(plainCode(removeCodeMarkers(atom->string())));
283 m_writer->writeEndElement(); // programlisting
284 newLine();
285 break;
286 case Atom::CodeBad:
287 m_writer->writeStartElement(dbNamespace, "programlisting");
288 m_writer->writeAttribute("language", "cpp");
289 m_writer->writeAttribute("role", "bad");
290 if (m_useITS)
291 m_writer->writeAttribute(itsNamespace, "translate", "no");
292 m_writer->writeCharacters(plainCode(removeCodeMarkers(atom->string())));
293 m_writer->writeEndElement(); // programlisting
294 newLine();
295 break;
298 break;
299 case Atom::DivLeft:
300 case Atom::DivRight:
301 break;
302 case Atom::FootnoteLeft:
303 m_writer->writeStartElement(dbNamespace, "footnote");
304 newLine();
305 m_writer->writeStartElement(dbNamespace, "para");
306 m_inPara = true;
307 break;
308 case Atom::FootnoteRight:
309 m_writer->writeEndElement(); // para
310 m_inPara = false;
311 newLine();
312 m_writer->writeEndElement(); // footnote
313 break;
314 case Atom::FormatElse:
316 case Atom::FormatIf:
317 break;
319 if (atom->string() == ATOM_FORMATTING_BOLD) {
320 m_writer->writeStartElement(dbNamespace, "emphasis");
321 m_writer->writeAttribute("role", "bold");
322 } else if (atom->string() == ATOM_FORMATTING_ITALIC) {
323 m_writer->writeStartElement(dbNamespace, "emphasis");
324 } else if (atom->string() == ATOM_FORMATTING_UNDERLINE) {
325 m_writer->writeStartElement(dbNamespace, "emphasis");
326 m_writer->writeAttribute("role", "underline");
327 } else if (atom->string() == ATOM_FORMATTING_SUBSCRIPT) {
328 m_writer->writeStartElement(dbNamespace, "subscript");
329 } else if (atom->string() == ATOM_FORMATTING_SUPERSCRIPT) {
330 m_writer->writeStartElement(dbNamespace, "superscript");
331 } else if (atom->string() == ATOM_FORMATTING_TELETYPE
332 || atom->string() == ATOM_FORMATTING_PARAMETER) {
333 m_writer->writeStartElement(dbNamespace, "code");
334 if (m_useITS)
335 m_writer->writeAttribute(itsNamespace, "translate", "no");
336
337 if (atom->string() == ATOM_FORMATTING_PARAMETER)
338 m_writer->writeAttribute("role", "parameter");
339 else // atom->string() == ATOM_FORMATTING_TELETYPE
340 m_inTeletype = true;
341 } else if (atom->string() == ATOM_FORMATTING_UICONTROL) {
342 m_writer->writeStartElement(dbNamespace, "guilabel");
343 if (m_useITS)
344 m_writer->writeAttribute(itsNamespace, "translate", "no");
345 } else if (atom->string() == ATOM_FORMATTING_TRADEMARK) {
346 m_writer->writeStartElement(dbNamespace,
347 appendTrademark(atom->find(Atom::FormattingRight)) ?
348 "trademark" : "phrase");
349 if (m_useITS)
350 m_writer->writeAttribute(itsNamespace, "translate", "no");
351 } else {
352 relative->location().warning(QStringLiteral("Unsupported formatting: %1").arg(atom->string()));
353 }
354 break;
356 if (atom->string() == ATOM_FORMATTING_BOLD || atom->string() == ATOM_FORMATTING_ITALIC
357 || atom->string() == ATOM_FORMATTING_UNDERLINE
358 || atom->string() == ATOM_FORMATTING_SUBSCRIPT
359 || atom->string() == ATOM_FORMATTING_SUPERSCRIPT
360 || atom->string() == ATOM_FORMATTING_TELETYPE
361 || atom->string() == ATOM_FORMATTING_PARAMETER
362 || atom->string() == ATOM_FORMATTING_UICONTROL
363 || atom->string() == ATOM_FORMATTING_TRADEMARK) {
364 m_writer->writeEndElement();
365 } else if (atom->string() == ATOM_FORMATTING_LINK) {
366 if (atom->string() == ATOM_FORMATTING_TELETYPE)
367 m_inTeletype = false;
368 endLink();
369 } else {
370 relative->location().warning(QStringLiteral("Unsupported formatting: %1").arg(atom->string()));
371 }
372 break;
373 case Atom::AnnotatedList: {
374 if (const CollectionNode *cn = m_qdb->getCollectionNode(atom->string(), Node::Group))
375 generateList(cn, atom->string(), Generator::sortOrder(atom->strings().last()));
376 } break;
377 case Atom::GeneratedList: {
378 const auto sortOrder{Generator::sortOrder(atom->strings().last())};
379 bool hasGeneratedSomething = false;
380 if (atom->string() == QLatin1String("annotatedclasses")
381 || atom->string() == QLatin1String("attributions")
382 || atom->string() == QLatin1String("namespaces")) {
383 const NodeMultiMap things = atom->string() == QLatin1String("annotatedclasses")
385 : atom->string() == QLatin1String("attributions") ? m_qdb->getAttributions()
387 generateAnnotatedList(relative, things.values(), atom->string(), Auto, sortOrder);
388 hasGeneratedSomething = !things.isEmpty();
389 } else if (atom->string() == QLatin1String("annotatedexamples")
390 || atom->string() == QLatin1String("annotatedattributions")) {
391 const NodeMultiMap things = atom->string() == QLatin1String("annotatedexamples")
394 generateAnnotatedLists(relative, things, atom->string());
395 hasGeneratedSomething = !things.isEmpty();
396 } else if (atom->string() == QLatin1String("classes")
397 || atom->string() == QLatin1String("qmlbasictypes") // deprecated!
398 || atom->string() == QLatin1String("qmlvaluetypes")
399 || atom->string() == QLatin1String("qmltypes")) {
400 const NodeMultiMap things = atom->string() == QLatin1String("classes")
402 : (atom->string() == QLatin1String("qmlvaluetypes")
403 || atom->string() == QLatin1String("qmlbasictypes"))
406 generateCompactList(relative, things, true, QString(), atom->string());
407 hasGeneratedSomething = !things.isEmpty();
408 } else if (atom->string().contains("classes ")) {
409 QString rootName = atom->string().mid(atom->string().indexOf("classes") + 7).trimmed();
411
412 hasGeneratedSomething = !things.isEmpty();
413 generateCompactList(relative, things, true, rootName, atom->string());
414 } else if ((idx = atom->string().indexOf(QStringLiteral("bymodule"))) != -1) {
415 QString moduleName = atom->string().mid(idx + 8).trimmed();
416 Node::NodeType moduleType = typeFromString(atom);
418 if (const CollectionNode *cn = qdb->getCollectionNode(moduleName, moduleType)) {
419 NodeMap map;
420 switch (moduleType) {
421 case Node::Module:
422 // classesbymodule <module_name>
423 map = cn->getMembers([](const Node *n){ return n->isClassNode(); });
424 break;
425 case Node::QmlModule:
426 if (atom->string().contains(QLatin1String("qmlvaluetypes")))
427 map = cn->getMembers(Node::QmlValueType); // qmlvaluetypesbymodule <module_name>
428 else
429 map = cn->getMembers(Node::QmlType); // qmltypesbymodule <module_name>
430 break;
431 default: // fall back to generating all members
432 generateAnnotatedList(relative, cn->members(), atom->string(), Auto, sortOrder);
433 hasGeneratedSomething = !cn->members().isEmpty();
434 break;
435 }
436 if (!map.isEmpty()) {
437 generateAnnotatedList(relative, map.values(), atom->string(), Auto, sortOrder);
438 hasGeneratedSomething = true;
439 }
440 }
441 } else if (atom->string() == QLatin1String("classhierarchy")) {
442 generateClassHierarchy(relative, m_qdb->getCppClasses());
443 hasGeneratedSomething = !m_qdb->getCppClasses().isEmpty();
444 } else if (atom->string().startsWith("obsolete")) {
445 QString prefix = atom->string().contains("cpp") ? QStringLiteral("Q") : QString();
446 const NodeMultiMap &things = atom->string() == QLatin1String("obsoleteclasses")
448 : atom->string() == QLatin1String("obsoleteqmltypes")
450 : atom->string() == QLatin1String("obsoletecppmembers")
453 generateCompactList(relative, things, false, prefix, atom->string());
454 hasGeneratedSomething = !things.isEmpty();
455 } else if (atom->string() == QLatin1String("functionindex")) {
456 generateFunctionIndex(relative);
457 hasGeneratedSomething = !m_qdb->getFunctionIndex().isEmpty();
458 } else if (atom->string() == QLatin1String("legalese")) {
459 generateLegaleseList(relative);
460 hasGeneratedSomething = !m_qdb->getLegaleseTexts().isEmpty();
461 } else if (atom->string() == QLatin1String("overviews")
462 || atom->string() == QLatin1String("cpp-modules")
463 || atom->string() == QLatin1String("qml-modules")
464 || atom->string() == QLatin1String("related")) {
465 generateList(relative, atom->string());
466 hasGeneratedSomething = true; // Approximation, because there is
467 // some nontrivial logic in generateList.
468 } else if (const auto *cn = m_qdb->getCollectionNode(atom->string(), Node::Group); cn) {
469 generateAnnotatedList(cn, cn->members(), atom->string(), ItemizedList, sortOrder);
470 hasGeneratedSomething = true; // Approximation
471 }
472
473 // There must still be some content generated for the DocBook document
474 // to be valid (except if already in a paragraph).
475 if (!hasGeneratedSomething && !m_inPara) {
476 m_writer->writeEmptyElement(dbNamespace, "para");
477 newLine();
478 }
479 }
480 break;
481 case Atom::SinceList:
482 // Table of contents, should automatically be generated by the DocBook processor.
483 Q_FALLTHROUGH();
484 case Atom::LineBreak:
485 case Atom::BR:
486 case Atom::HR:
487 // Not supported in DocBook.
488 break;
489 case Atom::Image: // mediaobject
490 // An Image atom is always followed by an ImageText atom,
491 // containing the alternative text.
492 // If no caption is present, we just output a <db:mediaobject>,
493 // avoiding the wrapper as it is not required.
494 // For bordered images, there is another atom before the
495 // caption, DivRight (the corresponding DivLeft being just
496 // before the image).
497
500 // If there is a caption, there must be a <db:figure>
501 // wrapper starting with the caption.
502 Q_ASSERT(atom->next());
503 Q_ASSERT(atom->next()->next());
504 Q_ASSERT(atom->next()->next()->next());
507
508 m_writer->writeStartElement(dbNamespace, "figure");
509 newLine();
510
511 const Atom *current = atom->next()->next()->next();
512 skipAhead += 2;
513
514 Q_ASSERT(current->type() == Atom::CaptionLeft);
515 generateAtom(current, relative, nullptr);
516 current = current->next();
517 ++skipAhead;
518
519 while (current->type() != Atom::CaptionRight) { // The actual caption.
520 generateAtom(current, relative, nullptr);
521 current = current->next();
522 ++skipAhead;
523 }
524
525 Q_ASSERT(current->type() == Atom::CaptionRight);
526 generateAtom(current, relative, nullptr);
527 current = current->next();
528 ++skipAhead;
529
530 m_closeFigureWrapper = true;
531 }
532
534 // If there is a caption, there must be a <db:figure>
535 // wrapper starting with the caption.
536 Q_ASSERT(atom->next());
537 Q_ASSERT(atom->next()->next());
538 Q_ASSERT(atom->next()->next()->next());
540
541 m_writer->writeStartElement(dbNamespace, "figure");
542 newLine();
543
544 const Atom *current = atom->next()->next();
545 ++skipAhead;
546
547 Q_ASSERT(current->type() == Atom::CaptionLeft);
548 generateAtom(current, relative, nullptr);
549 current = current->next();
550 ++skipAhead;
551
552 while (current->type() != Atom::CaptionRight) { // The actual caption.
553 generateAtom(current, relative, nullptr);
554 current = current->next();
555 ++skipAhead;
556 }
557
558 Q_ASSERT(current->type() == Atom::CaptionRight);
559 generateAtom(current, relative, nullptr);
560 current = current->next();
561 ++skipAhead;
562
563 m_closeFigureWrapper = true;
564 }
565
566 Q_FALLTHROUGH();
567 case Atom::InlineImage: { // inlinemediaobject
568 // TODO: [generator-insufficient-structural-abstraction]
569 // The structure of the computations for this part of the
570 // docbook generation and the same parts in other format
571 // generators is the same.
572 //
573 // The difference, instead, lies in what the generated output
574 // is like. A correct abstraction for a generator would take
575 // this structural equivalence into account and encapsulate it
576 // into a driver for the format generators.
577 //
578 // This would avoid the replication of content, and the
579 // subsequent friction for changes and desynchronization
580 // between generators.
581 //
582 // Review all the generators routines and find the actual
583 // skeleton that is shared between them, then consider it when
584 // extracting the logic for the generation phase.
585 QString tag = atom->type() == Atom::Image ? "mediaobject" : "inlinemediaobject";
586 m_writer->writeStartElement(dbNamespace, tag);
587 newLine();
588
589 auto maybe_resolved_file{file_resolver.resolve(atom->string())};
590 if (!maybe_resolved_file) {
591 // TODO: [uncetnralized-admonition][failed-resolve-file]
592 relative->location().warning(QStringLiteral("Missing image: %1").arg(atom->string()));
593
594 m_writer->writeStartElement(dbNamespace, "textobject");
595 newLine();
596 m_writer->writeStartElement(dbNamespace, "para");
597 m_writer->writeTextElement(dbNamespace, "emphasis",
598 "[Missing image " + atom->string() + "]");
599 m_writer->writeEndElement(); // para
600 newLine();
601 m_writer->writeEndElement(); // textobject
602 newLine();
603 } else {
604 ResolvedFile file{*maybe_resolved_file};
605 QString file_name{QFileInfo{file.get_path()}.fileName()};
606
607 // TODO: [uncentralized-output-directory-structure]
608 Config::copyFile(relative->doc().location(), file.get_path(), file_name, outputDir() + QLatin1String("/images"));
609
610 if (atom->next() && !atom->next()->string().isEmpty()
612 m_writer->writeTextElement(dbNamespace, "alt", atom->next()->string());
613 newLine();
614 }
615
616 m_writer->writeStartElement(dbNamespace, "imageobject");
617 newLine();
618 m_writer->writeEmptyElement(dbNamespace, "imagedata");
619 // TODO: [uncentralized-output-directory-structure]
620 m_writer->writeAttribute("fileref", "images/" + file_name);
621 newLine();
622 m_writer->writeEndElement(); // imageobject
623 newLine();
624
625 // TODO: [uncentralized-output-directory-structure]
626 setImageFileName(relative, "images/" + file_name);
627 }
628
629 m_writer->writeEndElement(); // [inline]mediaobject
630 if (atom->type() == Atom::Image)
631 newLine();
632
633 if (m_closeFigureWrapper) {
634 m_writer->writeEndElement(); // figure
635 newLine();
636 m_closeFigureWrapper = false;
637 }
638 } break;
639 case Atom::ImageText:
640 break;
642 case Atom::NoteLeft:
643 case Atom::WarningLeft: {
644 QString admonType = atom->typeString().toLower();
645 // Remove 'Left' to get the admonition type
646 admonType.chop(4);
647 m_writer->writeStartElement(dbNamespace, admonType);
648 newLine();
649 m_writer->writeStartElement(dbNamespace, "para");
650 m_inPara = true;
651 } break;
652 case Atom::ImportantRight:
653 case Atom::NoteRight:
654 case Atom::WarningRight:
655 m_writer->writeEndElement(); // para
656 m_inPara = false;
657 newLine();
658 m_writer->writeEndElement(); // note/important
659 newLine();
660 break;
663 break;
664 case Atom::Link:
665 case Atom::NavLink: {
666 const Node *node = nullptr;
667 QString link = getLink(atom, relative, &node);
668 beginLink(link, node, relative); // Ended at Atom::FormattingRight
669 skipAhead = 1;
670 } break;
671 case Atom::LinkNode: {
672 const Node *node = CodeMarker::nodeForString(atom->string());
673 beginLink(linkForNode(node, relative), node, relative);
674 skipAhead = 1;
675 } break;
676 case Atom::ListLeft:
677 if (m_inPara) {
678 // The variable m_inPara is not set in a very smart way, because
679 // it ignores nesting. This might in theory create false positives
680 // here. A better solution would be to track the depth of
681 // paragraphs the generator is in, but determining the right check
682 // for this condition is far from trivial (think of nested lists).
683 m_writer->writeEndElement(); // para
684 newLine();
685 m_inPara = false;
686 }
687
688 if (atom->string() == ATOM_LIST_BULLET) {
689 m_writer->writeStartElement(dbNamespace, "itemizedlist");
690 newLine();
691 } else if (atom->string() == ATOM_LIST_TAG) {
692 m_writer->writeStartElement(dbNamespace, "variablelist");
693 newLine();
694 } else if (atom->string() == ATOM_LIST_VALUE) {
695 m_writer->writeStartElement(dbNamespace, "informaltable");
696 newLine();
697 m_writer->writeStartElement(dbNamespace, "thead");
698 newLine();
699 m_writer->writeStartElement(dbNamespace, "tr");
700 newLine();
701 m_writer->writeTextElement(dbNamespace, "th", "Constant");
702 newLine();
703
706 // With three columns, if not in \enum topic, skip the value column
707 m_writer->writeTextElement(dbNamespace, "th", "Value");
708 newLine();
709 }
710
711 if (!isOneColumnValueTable(atom)) {
712 m_writer->writeTextElement(dbNamespace, "th", "Description");
713 newLine();
714 }
715
716 m_writer->writeEndElement(); // tr
717 newLine();
718 m_writer->writeEndElement(); // thead
719 newLine();
720 } else { // No recognized list type.
721 m_writer->writeStartElement(dbNamespace, "orderedlist");
722
723 if (atom->next() != nullptr && atom->next()->string().toInt() > 1)
724 m_writer->writeAttribute("startingnumber", atom->next()->string());
725
726 if (atom->string() == ATOM_LIST_UPPERALPHA)
727 m_writer->writeAttribute("numeration", "upperalpha");
728 else if (atom->string() == ATOM_LIST_LOWERALPHA)
729 m_writer->writeAttribute("numeration", "loweralpha");
730 else if (atom->string() == ATOM_LIST_UPPERROMAN)
731 m_writer->writeAttribute("numeration", "upperroman");
732 else if (atom->string() == ATOM_LIST_LOWERROMAN)
733 m_writer->writeAttribute("numeration", "lowerroman");
734 else // (atom->string() == ATOM_LIST_NUMERIC)
735 m_writer->writeAttribute("numeration", "arabic");
736
737 newLine();
738 }
739 m_inList++;
740 break;
742 break;
744 if (atom->string() == ATOM_LIST_TAG) {
745 m_writer->writeStartElement(dbNamespace, "varlistentry");
746 newLine();
747 m_writer->writeStartElement(dbNamespace, "item");
748 } else { // (atom->string() == ATOM_LIST_VALUE)
749 std::pair<QString, int> pair = getAtomListValue(atom);
750 skipAhead = pair.second;
751
752 m_writer->writeStartElement(dbNamespace, "tr");
753 newLine();
754 m_writer->writeStartElement(dbNamespace, "td");
755 newLine();
756 m_writer->writeStartElement(dbNamespace, "para");
757 if (m_useITS)
758 m_writer->writeAttribute(itsNamespace, "translate", "no");
759 generateEnumValue(pair.first, relative);
760 m_writer->writeEndElement(); // para
761 newLine();
762 m_writer->writeEndElement(); // td
763 newLine();
764
765 if (relative->nodeType() == Node::Enum) {
766 const auto enume = static_cast<const EnumNode *>(relative);
767 QString itemValue = enume->itemValue(atom->next()->string());
768
769 m_writer->writeStartElement(dbNamespace, "td");
770 if (itemValue.isEmpty())
771 m_writer->writeCharacters("?");
772 else {
773 m_writer->writeStartElement(dbNamespace, "code");
774 if (m_useITS)
775 m_writer->writeAttribute(itsNamespace, "translate", "no");
776 m_writer->writeCharacters(itemValue);
777 m_writer->writeEndElement(); // code
778 }
779 m_writer->writeEndElement(); // td
780 newLine();
781 }
782 }
783 m_inList++;
784 break;
786 if (atom->string() == ATOM_LIST_TAG) {
787 m_writer->writeEndElement(); // item
788 newLine();
789 }
790 break;
792 if (m_inList > 0 && atom->string() == ATOM_LIST_TAG) {
793 m_writer->writeEndElement(); // item
794 newLine();
795 m_inList = false;
796 }
797 break;
799 if (m_inList > 0) {
800 m_inListItemLineOpen = false;
801 if (atom->string() == ATOM_LIST_TAG) {
802 m_writer->writeStartElement(dbNamespace, "listitem");
803 newLine();
804 m_writer->writeStartElement(dbNamespace, "para");
805 m_inPara = true;
806 } else if (atom->string() == ATOM_LIST_VALUE) {
809 m_writer->writeEmptyElement(dbNamespace, "td");
810 newLine();
811 m_inListItemLineOpen = false;
812 } else {
813 m_writer->writeStartElement(dbNamespace, "td");
814 newLine();
815 m_inListItemLineOpen = true;
816 }
817 }
818 } else {
819 m_writer->writeStartElement(dbNamespace, "listitem");
820 newLine();
821 }
822 // Don't skip a paragraph, DocBook requires them within list items.
823 }
824 break;
826 if (m_inList > 0) {
827 if (atom->string() == ATOM_LIST_TAG) {
828 m_writer->writeEndElement(); // para
829 m_inPara = false;
830 newLine();
831 m_writer->writeEndElement(); // listitem
832 newLine();
833 m_writer->writeEndElement(); // varlistentry
834 newLine();
835 } else if (atom->string() == ATOM_LIST_VALUE) {
836 if (m_inListItemLineOpen) {
837 m_writer->writeEndElement(); // td
838 newLine();
839 m_inListItemLineOpen = false;
840 }
841 m_writer->writeEndElement(); // tr
842 newLine();
843 } else {
844 m_writer->writeEndElement(); // listitem
845 newLine();
846 }
847 }
848 break;
849 case Atom::ListRight:
850 // Depending on atom->string(), closing a different item:
851 // - ATOM_LIST_BULLET: itemizedlist
852 // - ATOM_LIST_TAG: variablelist
853 // - ATOM_LIST_VALUE: informaltable
854 // - ATOM_LIST_NUMERIC: orderedlist
855 m_writer->writeEndElement();
856 newLine();
857 m_inList--;
858 break;
859 case Atom::Nop:
860 break;
861 case Atom::ParaLeft:
862 m_writer->writeStartElement(dbNamespace, "para");
863 m_inPara = true;
864 break;
865 case Atom::ParaRight:
866 endLink();
867 if (m_inPara) {
868 m_writer->writeEndElement(); // para
869 newLine();
870 m_inPara = false;
871 }
872 break;
873 case Atom::QuotationLeft:
874 m_writer->writeStartElement(dbNamespace, "blockquote");
875 m_inBlockquote = true;
876 break;
877 case Atom::QuotationRight:
878 m_writer->writeEndElement(); // blockquote
879 newLine();
880 m_inBlockquote = false;
881 break;
882 case Atom::RawString: {
883 m_writer->device()->write(atom->string().toUtf8());
884 }
885 break;
887 m_hasSection = true;
888
889 currentSectionLevel = atom->string().toInt() + hOffset(relative);
890 // Level 1 is dealt with at the header level (info tag).
891 if (currentSectionLevel > 1) {
892 // Unfortunately, SectionRight corresponds to the end of any section,
893 // i.e. going to a new section, even deeper.
894 while (!sectionLevels.empty() && sectionLevels.top() >= currentSectionLevel) {
895 sectionLevels.pop();
896 m_writer->writeEndElement(); // section
897 newLine();
898 }
899
900 sectionLevels.push(currentSectionLevel);
901
902 m_writer->writeStartElement(dbNamespace, "section");
903 writeXmlId(Tree::refForAtom(atom));
904 newLine();
905 // Unlike startSectionBegin, don't start a title here.
906 }
907
913 // A lonely section at the end of the document indicates that a
914 // generated list of some sort should be within this section.
915 // Close this section later on, in generateFooter().
916 generateAtom(atom->next(), relative, nullptr);
917 generateAtom(atom->next()->next(), relative, nullptr);
918 generateAtom(atom->next()->next()->next(), relative, nullptr);
919
920 m_closeSectionAfterGeneratedList = true;
921 skipAhead += 4;
922 sectionLevels.pop();
923 }
924
926 // No section title afterwards, make one up. This likely indicates a problem in the original documentation.
927 m_writer->writeTextElement(dbNamespace, "title", "");
928 }
929 break;
931 // All the logic about closing sections is done in the SectionLeft case
932 // and generateFooter() for the end of the page.
933 break;
935 // Level 1 is dealt with at the header level (info tag).
936 if (currentSectionLevel > 1) {
937 m_writer->writeStartElement(dbNamespace, "title");
938 m_inSectionHeading = true;
939 }
940 break;
942 // Level 1 is dealt with at the header level (info tag).
943 if (currentSectionLevel > 1) {
944 m_writer->writeEndElement(); // title
945 newLine();
946 m_inSectionHeading = false;
947 }
948 break;
949 case Atom::SidebarLeft:
950 m_writer->writeStartElement(dbNamespace, "sidebar");
951 break;
952 case Atom::SidebarRight:
953 m_writer->writeEndElement(); // sidebar
954 newLine();
955 break;
956 case Atom::String:
958 generateLink(atom);
959 else
960 m_writer->writeCharacters(atom->string());
961 break;
962 case Atom::TableLeft: {
963 std::pair<QString, QString> pair = getTableWidthAttr(atom);
964 QString attr = pair.second;
965 QString width = pair.first;
966
967 if (m_inPara) {
968 m_writer->writeEndElement(); // para or blockquote
969 newLine();
970 m_inPara = false;
971 }
972
973 m_tableHeaderAlreadyOutput = false;
974
975 m_writer->writeStartElement(dbNamespace, "informaltable");
976 m_writer->writeAttribute("style", attr);
977 if (!width.isEmpty())
978 m_writer->writeAttribute("width", width);
979 newLine();
980 } break;
981 case Atom::TableRight:
982 m_tableWidthAttr = {"", ""};
983 m_writer->writeEndElement(); // table
984 newLine();
985 break;
988 ++skipAhead;
989 break;
990 }
991
992 if (m_tableHeaderAlreadyOutput) {
993 // Headers are only allowed at the beginning of the table: close
994 // the table and reopen one.
995 m_writer->writeEndElement(); // table
996 newLine();
997
998 const QString &attr = m_tableWidthAttr.second;
999 const QString &width = m_tableWidthAttr.first;
1000
1001 m_writer->writeStartElement(dbNamespace, "informaltable");
1002 m_writer->writeAttribute("style", attr);
1003 if (!width.isEmpty())
1004 m_writer->writeAttribute("width", width);
1005 newLine();
1006 } else {
1007 m_tableHeaderAlreadyOutput = true;
1008 }
1009
1010 const Atom *next = atom->next();
1011 QString id{""};
1012 if (matchAhead(atom, Atom::Target)) {
1013 id = Utilities::asAsciiPrintable(next->string());
1014 next = next->next();
1015 ++skipAhead;
1016 }
1017
1018 m_writer->writeStartElement(dbNamespace, "thead");
1019 newLine();
1020 m_writer->writeStartElement(dbNamespace, "tr");
1021 writeXmlId(id);
1022 newLine();
1023 m_inTableHeader = true;
1024
1026 m_closeTableCell = true;
1027 m_writer->writeStartElement(dbNamespace, "td");
1028 newLine();
1029 }
1030 }
1031 break;
1033 if (m_closeTableCell) {
1034 m_closeTableCell = false;
1035 m_writer->writeEndElement(); // td
1036 newLine();
1037 }
1038
1039 m_writer->writeEndElement(); // tr
1040 newLine();
1042 skipAhead = 1;
1043 m_writer->writeStartElement(dbNamespace, "tr");
1044 newLine();
1045 } else {
1046 m_writer->writeEndElement(); // thead
1047 newLine();
1048 m_inTableHeader = false;
1049 }
1050 break;
1051 case Atom::TableRowLeft: {
1053 skipAhead = 1;
1054 break;
1055 }
1056
1057 QString id{""};
1058 bool hasTarget {false};
1059 if (matchAhead(atom, Atom::Target)) {
1060 id = Utilities::asAsciiPrintable(atom->next()->string());
1061 ++skipAhead;
1062 hasTarget = true;
1063 }
1064
1065 m_writer->writeStartElement(dbNamespace, "tr");
1066 writeXmlId(id);
1067
1068 if (atom->string().isEmpty()) {
1069 m_writer->writeAttribute("valign", "top");
1070 } else {
1071 // Basic parsing of attributes, should be enough. The input string (atom->string())
1072 // looks like:
1073 // arg1="val1" arg2="val2"
1074 QStringList args = atom->string().split("\"", Qt::SkipEmptyParts);
1075 // arg1=, val1, arg2=, val2,
1076 // \-- 1st --/ \-- 2nd --/ \-- remainder
1077 const int nArgs = args.size();
1078
1079 if (nArgs % 2) {
1080 // Problem...
1081 relative->doc().location().warning(
1082 QStringLiteral("Error when parsing attributes for the table: got \"%1\"")
1083 .arg(atom->string()));
1084 }
1085 for (int i = 0; i + 1 < nArgs; i += 2) {
1086 // args.at(i): name of the attribute being set.
1087 // args.at(i + 1): value of the said attribute.
1088 const QString &attr = args.at(i).chopped(1);
1089 if (attr == "id") { // Too bad if there is an anchor later on
1090 // (currently never happens).
1091 writeXmlId(args.at(i + 1));
1092 } else {
1093 m_writer->writeAttribute(attr, args.at(i + 1));
1094 }
1095 }
1096 }
1097 newLine();
1098
1099 // If there is nothing in this row, close it right now. There might be keywords before the row contents.
1100 bool isRowEmpty = hasTarget ? !matchAhead(atom->next(), Atom::TableItemLeft) : !matchAhead(atom, Atom::TableItemLeft);
1101 if (isRowEmpty && matchAhead(atom, Atom::Keyword)) {
1102 const Atom* next = atom->next();
1103 while (matchAhead(next, Atom::Keyword))
1104 next = next->next();
1105 isRowEmpty = !matchAhead(next, Atom::TableItemLeft);
1106 }
1107
1108 if (isRowEmpty) {
1109 m_closeTableRow = true;
1110 m_writer->writeEndElement(); // td
1111 newLine();
1112 }
1113 }
1114 break;
1116 if (m_closeTableRow) {
1117 m_closeTableRow = false;
1118 m_writer->writeEndElement(); // td
1119 newLine();
1120 }
1121
1122 m_writer->writeEndElement(); // tr
1123 newLine();
1124 break;
1125 case Atom::TableItemLeft:
1126 m_writer->writeStartElement(dbNamespace, m_inTableHeader ? "th" : "td");
1127
1128 for (int i = 0; i < atom->count(); ++i) {
1129 const QString &p = atom->string(i);
1130 if (p.contains('=')) {
1131 QStringList lp = p.split(QLatin1Char('='));
1132 m_writer->writeAttribute(lp.at(0), lp.at(1));
1133 } else {
1134 QStringList spans = p.split(QLatin1Char(','));
1135 if (spans.size() == 2) {
1136 if (spans.at(0) != "1")
1137 m_writer->writeAttribute("colspan", spans.at(0).trimmed());
1138 if (spans.at(1) != "1")
1139 m_writer->writeAttribute("rowspan", spans.at(1).trimmed());
1140 }
1141 }
1142 }
1143 newLine();
1144 // No skipahead, as opposed to HTML: in DocBook, the text must be wrapped in paragraphs.
1145 break;
1146 case Atom::TableItemRight:
1147 m_writer->writeEndElement(); // th if m_inTableHeader, otherwise td
1148 newLine();
1149 break;
1151 Q_FALLTHROUGH();
1152 case Atom::Keyword:
1153 break;
1154 case Atom::Target:
1155 // Sometimes, there is a \target just before a section title with the same ID. Only output one xml:id.
1157 QString nextId = Utilities::asAsciiPrintable(
1158 Text::sectionHeading(atom->next()->next()).toString());
1159 QString ownId = Utilities::asAsciiPrintable(atom->string());
1160 if (nextId == ownId)
1161 break;
1162 }
1163
1164 writeAnchor(Utilities::asAsciiPrintable(atom->string()));
1165 break;
1166 case Atom::UnhandledFormat:
1167 m_writer->writeStartElement(dbNamespace, "emphasis");
1168 m_writer->writeAttribute("role", "bold");
1169 m_writer->writeCharacters("<Missing DocBook>");
1170 m_writer->writeEndElement(); // emphasis
1171 break;
1172 case Atom::UnknownCommand:
1173 m_writer->writeStartElement(dbNamespace, "emphasis");
1174 m_writer->writeAttribute("role", "bold");
1175 if (m_useITS)
1176 m_writer->writeAttribute(itsNamespace, "translate", "no");
1177 m_writer->writeCharacters("<Unknown command>");
1178 m_writer->writeStartElement(dbNamespace, "code");
1179 m_writer->writeCharacters(atom->string());
1180 m_writer->writeEndElement(); // code
1181 m_writer->writeEndElement(); // emphasis
1182 break;
1185 case Atom::ComparesLeft:
1190 // No output (ignore).
1191 break;
1192 default:
1193 unknownAtom(atom);
1194 }
1195 return skipAhead;
1196}
1197
1198void DocBookGenerator::generateClassHierarchy(const Node *relative, NodeMultiMap &classMap)
1199{
1200 // From HtmlGenerator::generateClassHierarchy.
1201 if (classMap.isEmpty())
1202 return;
1203
1204 std::function<void(ClassNode *)> generateClassAndChildren
1205 = [this, &relative, &generateClassAndChildren](ClassNode * classe) {
1206 m_writer->writeStartElement(dbNamespace, "listitem");
1207 newLine();
1208
1209 // This class.
1210 m_writer->writeStartElement(dbNamespace, "para");
1211 generateFullName(classe, relative);
1212 m_writer->writeEndElement(); // para
1213 newLine();
1214
1215 // Children, if any.
1216 bool hasChild = false;
1217 for (const RelatedClass &relatedClass : classe->derivedClasses()) {
1218 if (relatedClass.m_node && relatedClass.m_node->isInAPI()) {
1219 hasChild = true;
1220 break;
1221 }
1222 }
1223
1224 if (hasChild) {
1225 m_writer->writeStartElement(dbNamespace, "itemizedlist");
1226 newLine();
1227
1228 for (const RelatedClass &relatedClass: classe->derivedClasses()) {
1229 if (relatedClass.m_node && relatedClass.m_node->isInAPI()) {
1230 generateClassAndChildren(relatedClass.m_node);
1231 }
1232 }
1233
1234 m_writer->writeEndElement(); // itemizedlist
1235 newLine();
1236 }
1237
1238 // End this class.
1239 m_writer->writeEndElement(); // listitem
1240 newLine();
1241 };
1242
1243 m_writer->writeStartElement(dbNamespace, "itemizedlist");
1244 newLine();
1245
1246 for (const auto &it : classMap) {
1247 auto *classe = static_cast<ClassNode *>(it);
1248 if (classe->baseClasses().isEmpty())
1249 generateClassAndChildren(classe);
1250 }
1251
1252 m_writer->writeEndElement(); // itemizedlist
1253 newLine();
1254}
1255
1256void DocBookGenerator::generateLink(const Atom *atom)
1257{
1258 Q_ASSERT(m_inLink);
1259
1260 // From HtmlGenerator::generateLink.
1262 auto match = XmlGenerator::m_funcLeftParen.match(atom->string());
1263 if (match.hasMatch()) {
1264 // C++: move () outside of link
1265 qsizetype leftParenLoc = match.capturedStart(1);
1266 m_writer->writeCharacters(atom->string().left(leftParenLoc));
1267 endLink();
1268 m_writer->writeCharacters(atom->string().mid(leftParenLoc));
1269 return;
1270 }
1271 }
1272 m_writer->writeCharacters(atom->string());
1273}
1274
1275/*!
1276 This version of the function is called when the \a link is known
1277 to be correct.
1278 */
1279void DocBookGenerator::beginLink(const QString &link, const Node *node, const Node *relative)
1280{
1281 // From HtmlGenerator::beginLink.
1282 m_writer->writeStartElement(dbNamespace, "link");
1283 m_writer->writeAttribute(xlinkNamespace, "href", link);
1284 if (node && !(relative && node->status() == relative->status())
1285 && node->isDeprecated())
1286 m_writer->writeAttribute("role", "deprecated");
1287 m_inLink = true;
1288 m_linkNode = node;
1289}
1290
1291void DocBookGenerator::endLink()
1292{
1293 // From HtmlGenerator::endLink.
1294 if (m_inLink)
1295 m_writer->writeEndElement(); // link
1296 m_inLink = false;
1297 m_linkNode = nullptr;
1298}
1299
1300void DocBookGenerator::generateList(const Node *relative, const QString &selector,
1301 Qt::SortOrder sortOrder)
1302{
1303 // From HtmlGenerator::generateList, without warnings, changing prototype.
1304 CNMap cnm;
1305 Node::NodeType type = Node::NoType;
1306 if (selector == QLatin1String("overviews"))
1307 type = Node::Group;
1308 else if (selector == QLatin1String("cpp-modules"))
1309 type = Node::Module;
1310 else if (selector == QLatin1String("qml-modules"))
1311 type = Node::QmlModule;
1312
1313 if (type != Node::NoType) {
1314 NodeList nodeList;
1315 m_qdb->mergeCollections(type, cnm, relative);
1316 const QList<CollectionNode *> collectionList = cnm.values();
1317 nodeList.reserve(collectionList.size());
1318 for (auto *collectionNode : collectionList)
1319 nodeList.append(collectionNode);
1320 generateAnnotatedList(relative, nodeList, selector, Auto, sortOrder);
1321 } else {
1322 /*
1323 \generatelist {selector} is only allowed in a comment where
1324 the topic is \group, \module, or \qmlmodule.
1325 */
1326 Node *n = const_cast<Node *>(relative);
1327 auto *cn = static_cast<CollectionNode *>(n);
1329 generateAnnotatedList(cn, cn->members(), selector, Auto, sortOrder);
1330 }
1331}
1332
1333/*!
1334 Outputs an annotated list of the nodes in \a nodeList.
1335 A two-column table is output.
1336 */
1337void DocBookGenerator::generateAnnotatedList(const Node *relative, const NodeList &nodeList,
1338 const QString &selector, GeneratedListType type,
1339 Qt::SortOrder sortOrder)
1340{
1341 if (nodeList.isEmpty())
1342 return;
1343
1344 // Do nothing if all items are internal or obsolete.
1345 if (std::all_of(nodeList.cbegin(), nodeList.cend(), [](const Node *n) {
1346 return n->isInternal() || n->isDeprecated(); })) {
1347 return;
1348 }
1349
1350 // Detect if there is a need for a variablelist (i.e. titles mapped to
1351 // descriptions) or a regular itemizedlist (only titles).
1352 bool noItemsHaveTitle =
1353 type == ItemizedList || std::all_of(nodeList.begin(), nodeList.end(),
1354 [](const Node* node) {
1355 return node->doc().briefText().toString().isEmpty();
1356 });
1357
1358 // Wrap the list in a section if needed.
1359 if (type == AutoSection && m_hasSection)
1360 startSection("", "Contents");
1361
1362 // From WebXMLGenerator::generateAnnotatedList.
1363 if (!nodeList.isEmpty()) {
1364 m_writer->writeStartElement(dbNamespace, noItemsHaveTitle ? "itemizedlist" : "variablelist");
1365 m_writer->writeAttribute("role", selector);
1366 newLine();
1367
1368 NodeList members{nodeList};
1369 if (sortOrder == Qt::DescendingOrder)
1370 std::sort(members.rbegin(), members.rend(), Node::nodeSortKeyOrNameLessThan);
1371 else
1372 std::sort(members.begin(), members.end(), Node::nodeSortKeyOrNameLessThan);
1373 for (const auto &node : std::as_const(members)) {
1374 if (node->isInternal() || node->isDeprecated())
1375 continue;
1376
1377 if (noItemsHaveTitle) {
1378 m_writer->writeStartElement(dbNamespace, "listitem");
1379 newLine();
1380 m_writer->writeStartElement(dbNamespace, "para");
1381 } else {
1382 m_writer->writeStartElement(dbNamespace, "varlistentry");
1383 newLine();
1384 m_writer->writeStartElement(dbNamespace, "term");
1385 }
1386 generateFullName(node, relative);
1387 if (noItemsHaveTitle) {
1388 m_writer->writeEndElement(); // para
1389 newLine();
1390 m_writer->writeEndElement(); // listitem
1391 } else {
1392 m_writer->writeEndElement(); // term
1393 newLine();
1394 m_writer->writeStartElement(dbNamespace, "listitem");
1395 newLine();
1396 m_writer->writeStartElement(dbNamespace, "para");
1397 m_writer->writeCharacters(node->doc().briefText().toString());
1398 m_writer->writeEndElement(); // para
1399 newLine();
1400 m_writer->writeEndElement(); // listitem
1401 newLine();
1402 m_writer->writeEndElement(); // varlistentry
1403 }
1404 newLine();
1405 }
1406
1407 m_writer->writeEndElement(); // itemizedlist or variablelist
1408 newLine();
1409 }
1410
1411 if (type == AutoSection && m_hasSection)
1412 endSection();
1413}
1414
1415/*!
1416 Outputs a series of annotated lists from the nodes in \a nmm,
1417 divided into sections based by the key names in the multimap.
1418 */
1419void DocBookGenerator::generateAnnotatedLists(const Node *relative, const NodeMultiMap &nmm,
1420 const QString &selector)
1421{
1422 // From HtmlGenerator::generateAnnotatedLists.
1423 for (const QString &name : nmm.uniqueKeys()) {
1424 if (!name.isEmpty())
1425 startSection(name.toLower(), name);
1426 generateAnnotatedList(relative, nmm.values(name), selector);
1427 if (!name.isEmpty())
1428 endSection();
1429 }
1430}
1431
1432/*!
1433 This function finds the common prefix of the names of all
1434 the classes in the class map \a nmm and then generates a
1435 compact list of the class names alphabetized on the part
1436 of the name not including the common prefix. You can tell
1437 the function to use \a comonPrefix as the common prefix,
1438 but normally you let it figure it out itself by looking at
1439 the name of the first and last classes in the class map
1440 \a nmm.
1441 */
1442void DocBookGenerator::generateCompactList(const Node *relative, const NodeMultiMap &nmm,
1443 bool includeAlphabet, const QString &commonPrefix,
1444 const QString &selector)
1445{
1446 // From HtmlGenerator::generateCompactList. No more "includeAlphabet", this should be handled by
1447 // the DocBook toolchain afterwards.
1448 // TODO: In DocBook, probably no need for this method: this is purely presentational, i.e. to be
1449 // fully handled by the DocBook toolchain.
1450
1451 if (nmm.isEmpty())
1452 return;
1453
1454 const int NumParagraphs = 37; // '0' to '9', 'A' to 'Z', '_'
1455 qsizetype commonPrefixLen = commonPrefix.size();
1456
1457 /*
1458 Divide the data into 37 paragraphs: 0, ..., 9, A, ..., Z,
1459 underscore (_). QAccel will fall in paragraph 10 (A) and
1460 QXtWidget in paragraph 33 (X). This is the only place where we
1461 assume that NumParagraphs is 37. Each paragraph is a NodeMultiMap.
1462 */
1463 NodeMultiMap paragraph[NumParagraphs + 1];
1464 QString paragraphName[NumParagraphs + 1];
1465 QSet<char> usedParagraphNames;
1466
1467 for (auto c = nmm.constBegin(); c != nmm.constEnd(); ++c) {
1468 QStringList pieces = c.key().split("::");
1469 int idx = commonPrefixLen;
1470 if (idx > 0 && !pieces.last().startsWith(commonPrefix, Qt::CaseInsensitive))
1471 idx = 0;
1472 QString last = pieces.last().toLower();
1473 QString key = last.mid(idx);
1474
1475 int paragraphNr = NumParagraphs - 1;
1476
1477 if (key[0].digitValue() != -1) {
1478 paragraphNr = key[0].digitValue();
1479 } else if (key[0] >= QLatin1Char('a') && key[0] <= QLatin1Char('z')) {
1480 paragraphNr = 10 + key[0].unicode() - 'a';
1481 }
1482
1483 paragraphName[paragraphNr] = key[0].toUpper();
1484 usedParagraphNames.insert(key[0].toLower().cell());
1485 paragraph[paragraphNr].insert(last, c.value());
1486 }
1487
1488 /*
1489 Each paragraph j has a size: paragraph[j].count(). In the
1490 discussion, we will assume paragraphs 0 to 5 will have sizes
1491 3, 1, 4, 1, 5, 9.
1492
1493 We now want to compute the paragraph offset. Paragraphs 0 to 6
1494 start at offsets 0, 3, 4, 8, 9, 14, 23.
1495 */
1496 int paragraphOffset[NumParagraphs + 1]; // 37 + 1
1497 paragraphOffset[0] = 0;
1498 for (int i = 0; i < NumParagraphs; i++) // i = 0..36
1499 paragraphOffset[i + 1] = paragraphOffset[i] + paragraph[i].size();
1500
1501 // Output the alphabet as a row of links.
1502 if (includeAlphabet && !usedParagraphNames.isEmpty()) {
1503 m_writer->writeStartElement(dbNamespace, "simplelist");
1504 newLine();
1505
1506 for (int i = 0; i < 26; i++) {
1507 QChar ch('a' + i);
1508 if (usedParagraphNames.contains(char('a' + i))) {
1509 m_writer->writeStartElement(dbNamespace, "member");
1510 generateSimpleLink(ch, ch.toUpper());
1511 m_writer->writeEndElement(); // member
1512 newLine();
1513 }
1514 }
1515
1516 m_writer->writeEndElement(); // simplelist
1517 newLine();
1518 }
1519
1520 // Actual output.
1521 int curParNr = 0;
1522 int curParOffset = 0;
1523 QString previousName;
1524 bool multipleOccurrences = false;
1525
1526 m_writer->writeStartElement(dbNamespace, "variablelist");
1527 m_writer->writeAttribute("role", selector);
1528 newLine();
1529
1530 for (int i = 0; i < nmm.size(); i++) {
1531 while ((curParNr < NumParagraphs) && (curParOffset == paragraph[curParNr].size())) {
1532
1533 ++curParNr;
1534 curParOffset = 0;
1535 }
1536
1537 // Starting a new paragraph means starting a new varlistentry.
1538 if (curParOffset == 0) {
1539 if (i > 0) {
1540 m_writer->writeEndElement(); // itemizedlist
1541 newLine();
1542 m_writer->writeEndElement(); // listitem
1543 newLine();
1544 m_writer->writeEndElement(); // varlistentry
1545 newLine();
1546 }
1547
1548 m_writer->writeStartElement(dbNamespace, "varlistentry");
1549 if (includeAlphabet)
1550 writeXmlId(paragraphName[curParNr][0].toLower());
1551 newLine();
1552
1553 m_writer->writeStartElement(dbNamespace, "term");
1554 m_writer->writeStartElement(dbNamespace, "emphasis");
1555 m_writer->writeAttribute("role", "bold");
1556 m_writer->writeCharacters(paragraphName[curParNr]);
1557 m_writer->writeEndElement(); // emphasis
1558 m_writer->writeEndElement(); // term
1559 newLine();
1560
1561 m_writer->writeStartElement(dbNamespace, "listitem");
1562 newLine();
1563 m_writer->writeStartElement(dbNamespace, "itemizedlist");
1564 newLine();
1565 }
1566
1567 // Output a listitem for the current offset in the current paragraph.
1568 m_writer->writeStartElement(dbNamespace, "listitem");
1569 newLine();
1570 m_writer->writeStartElement(dbNamespace, "para");
1571
1572 if ((curParNr < NumParagraphs) && !paragraphName[curParNr].isEmpty()) {
1573 NodeMultiMap::Iterator it;
1574 NodeMultiMap::Iterator next;
1575 it = paragraph[curParNr].begin();
1576 for (int j = 0; j < curParOffset; j++)
1577 ++it;
1578
1579 // Cut the name into pieces to determine whether it is simple (one piece) or complex
1580 // (more than one piece).
1581 QStringList pieces{it.value()->fullName(relative).split("::"_L1)};
1582 const auto &name{pieces.last()};
1583 next = it;
1584 ++next;
1585 if (name != previousName)
1586 multipleOccurrences = false;
1587 if ((next != paragraph[curParNr].end()) && (name == next.value()->name())) {
1588 multipleOccurrences = true;
1589 previousName = name;
1590 }
1591 if (multipleOccurrences && pieces.size() == 1)
1592 pieces.last().append(": "_L1.arg(it.value()->tree()->camelCaseModuleName()));
1593
1594 // Write the link to the element, which is identical if the element is obsolete or not.
1595 m_writer->writeStartElement(dbNamespace, "link");
1596 m_writer->writeAttribute(xlinkNamespace, "href", linkForNode(*it, relative));
1597 if (const QString type = targetType(it.value()); !type.isEmpty())
1598 m_writer->writeAttribute("role", type);
1599 m_writer->writeCharacters(pieces.last());
1600 m_writer->writeEndElement(); // link
1601
1602 // Outside the link, give the full name of the node if it is complex.
1603 if (pieces.size() > 1) {
1604 m_writer->writeCharacters(" (");
1605 generateFullName(it.value()->parent(), relative);
1606 m_writer->writeCharacters(")");
1607 }
1608 }
1609
1610 m_writer->writeEndElement(); // para
1611 newLine();
1612 m_writer->writeEndElement(); // listitem
1613 newLine();
1614
1615 curParOffset++;
1616 }
1617 m_writer->writeEndElement(); // itemizedlist
1618 newLine();
1619 m_writer->writeEndElement(); // listitem
1620 newLine();
1621 m_writer->writeEndElement(); // varlistentry
1622 newLine();
1623
1624 m_writer->writeEndElement(); // variablelist
1625 newLine();
1626}
1627
1628void DocBookGenerator::generateFunctionIndex(const Node *relative)
1629{
1630 // From HtmlGenerator::generateFunctionIndex.
1631
1632 // First list: links to parts of the second list, one item per letter.
1633 m_writer->writeStartElement(dbNamespace, "simplelist");
1634 m_writer->writeAttribute("role", "functionIndex");
1635 newLine();
1636 for (int i = 0; i < 26; i++) {
1637 QChar ch('a' + i);
1638 m_writer->writeStartElement(dbNamespace, "member");
1639 m_writer->writeAttribute(xlinkNamespace, "href", QString("#") + ch);
1640 m_writer->writeCharacters(ch.toUpper());
1641 m_writer->writeEndElement(); // member
1642 newLine();
1643 }
1644 m_writer->writeEndElement(); // simplelist
1645 newLine();
1646
1647 // Second list: the actual list of functions, sorted by alphabetical
1648 // order. One entry of the list per letter.
1649 if (m_qdb->getFunctionIndex().isEmpty())
1650 return;
1651 char nextLetter = 'a';
1652 char currentLetter;
1653
1654 m_writer->writeStartElement(dbNamespace, "itemizedlist");
1655 newLine();
1656
1658 QMap<QString, NodeMap>::ConstIterator f = funcIndex.constBegin();
1659 while (f != funcIndex.constEnd()) {
1660 m_writer->writeStartElement(dbNamespace, "listitem");
1661 newLine();
1662 m_writer->writeStartElement(dbNamespace, "para");
1663 m_writer->writeCharacters(f.key() + ": ");
1664
1665 currentLetter = f.key()[0].unicode();
1666 while (islower(currentLetter) && currentLetter >= nextLetter) {
1667 writeAnchor(QString(nextLetter));
1668 nextLetter++;
1669 }
1670
1671 NodeMap::ConstIterator s = (*f).constBegin();
1672 while (s != (*f).constEnd()) {
1673 m_writer->writeCharacters(" ");
1674 generateFullName((*s)->parent(), relative);
1675 ++s;
1676 }
1677
1678 m_writer->writeEndElement(); // para
1679 newLine();
1680 m_writer->writeEndElement(); // listitem
1681 newLine();
1682 ++f;
1683 }
1684 m_writer->writeEndElement(); // itemizedlist
1685 newLine();
1686}
1687
1688void DocBookGenerator::generateLegaleseList(const Node *relative)
1689{
1690 // From HtmlGenerator::generateLegaleseList.
1692 for (auto it = legaleseTexts.cbegin(), end = legaleseTexts.cend(); it != end; ++it) {
1693 Text text = it.key();
1694 generateText(text, relative);
1695 m_writer->writeStartElement(dbNamespace, "itemizedlist");
1696 newLine();
1697 do {
1698 m_writer->writeStartElement(dbNamespace, "listitem");
1699 newLine();
1700 m_writer->writeStartElement(dbNamespace, "para");
1701 generateFullName(it.value(), relative);
1702 m_writer->writeEndElement(); // para
1703 newLine();
1704 m_writer->writeEndElement(); // listitem
1705 newLine();
1706 ++it;
1707 } while (it != legaleseTexts.constEnd() && it.key() == text);
1708 m_writer->writeEndElement(); // itemizedlist
1709 newLine();
1710 }
1711}
1712
1713void DocBookGenerator::generateBrief(const Node *node)
1714{
1715 // From HtmlGenerator::generateBrief. Also see generateHeader, which is specifically dealing
1716 // with the DocBook header (and thus wraps the brief in an abstract).
1717 Text brief = node->doc().briefText();
1718
1719 if (!brief.isEmpty()) {
1720 if (!brief.lastAtom()->string().endsWith('.'))
1721 brief << Atom(Atom::String, ".");
1722
1723 m_writer->writeStartElement(dbNamespace, "para");
1724 generateText(brief, node);
1725 m_writer->writeEndElement(); // para
1726 newLine();
1727 }
1728}
1729
1731{
1732 // From Generator::generateSince.
1733 if (!node->since().isEmpty()) {
1734 m_writer->writeStartElement(dbNamespace, "para");
1735 m_writer->writeCharacters("This " + typeString(node) + " was introduced in ");
1736 m_writer->writeCharacters(formatSince(node) + ".");
1737 m_writer->writeEndElement(); // para
1738 newLine();
1739
1740 return true;
1741 }
1742
1743 return false;
1744}
1745
1746/*!
1747 Generate the DocBook header for the file, including the abstract.
1748 Equivalent to calling generateTitle and generateBrief in HTML.
1749*/
1750void DocBookGenerator::generateHeader(const QString &title, const QString &subTitle,
1751 const Node *node)
1752{
1753 refMap.clear();
1754
1755 // Output the DocBook header.
1756 m_writer->writeStartElement(dbNamespace, "info");
1757 newLine();
1758 m_writer->writeStartElement(dbNamespace, "title");
1759 if (node->genus() & Node::API && m_useITS)
1760 m_writer->writeAttribute(itsNamespace, "translate", "no");
1761 m_writer->writeCharacters(title);
1762 m_writer->writeEndElement(); // title
1763 newLine();
1764
1765 if (!subTitle.isEmpty()) {
1766 m_writer->writeStartElement(dbNamespace, "subtitle");
1767 if (node->genus() & Node::API && m_useITS)
1768 m_writer->writeAttribute(itsNamespace, "translate", "no");
1769 m_writer->writeCharacters(subTitle);
1770 m_writer->writeEndElement(); // subtitle
1771 newLine();
1772 }
1773
1774 if (!m_project.isEmpty()) {
1775 m_writer->writeTextElement(dbNamespace, "productname", m_project);
1776 newLine();
1777 }
1778
1779 if (!m_buildVersion.isEmpty()) {
1780 m_writer->writeTextElement(dbNamespace, "edition", m_buildVersion);
1781 newLine();
1782 }
1783
1784 if (!m_projectDescription.isEmpty()) {
1785 m_writer->writeTextElement(dbNamespace, "titleabbrev", m_projectDescription);
1786 newLine();
1787 }
1788
1789 // Deal with links.
1790 // Adapted from HtmlGenerator::generateHeader (output part: no need to update a navigationLinks
1791 // or useSeparator field, as this content is only output in the info tag, not in the main
1792 // content).
1793 if (node && !node->links().empty()) {
1794 std::pair<QString, QString> linkPair;
1795 std::pair<QString, QString> anchorPair;
1796 const Node *linkNode;
1797
1798 if (node->links().contains(Node::PreviousLink)) {
1799 linkPair = node->links()[Node::PreviousLink];
1800 linkNode = m_qdb->findNodeForTarget(linkPair.first, node);
1801 if (!linkNode || linkNode == node)
1802 anchorPair = linkPair;
1803 else
1804 anchorPair = anchorForNode(linkNode);
1805
1806 m_writer->writeStartElement(dbNamespace, "extendedlink");
1807 m_writer->writeAttribute(xlinkNamespace, "type", "extended");
1808 m_writer->writeEmptyElement(dbNamespace, "link");
1809 m_writer->writeAttribute(xlinkNamespace, "to", anchorPair.first);
1810 m_writer->writeAttribute(xlinkNamespace, "type", "arc");
1811 m_writer->writeAttribute(xlinkNamespace, "arcrole", "prev");
1812 if (linkPair.first == linkPair.second && !anchorPair.second.isEmpty())
1813 m_writer->writeAttribute(xlinkNamespace, "title", anchorPair.second);
1814 else
1815 m_writer->writeAttribute(xlinkNamespace, "title", linkPair.second);
1816 m_writer->writeEndElement(); // extendedlink
1817 newLine();
1818 }
1819 if (node->links().contains(Node::NextLink)) {
1820 linkPair = node->links()[Node::NextLink];
1821 linkNode = m_qdb->findNodeForTarget(linkPair.first, node);
1822 if (!linkNode || linkNode == node)
1823 anchorPair = linkPair;
1824 else
1825 anchorPair = anchorForNode(linkNode);
1826
1827 m_writer->writeStartElement(dbNamespace, "extendedlink");
1828 m_writer->writeAttribute(xlinkNamespace, "type", "extended");
1829 m_writer->writeEmptyElement(dbNamespace, "link");
1830 m_writer->writeAttribute(xlinkNamespace, "to", anchorPair.first);
1831 m_writer->writeAttribute(xlinkNamespace, "type", "arc");
1832 m_writer->writeAttribute(xlinkNamespace, "arcrole", "next");
1833 if (linkPair.first == linkPair.second && !anchorPair.second.isEmpty())
1834 m_writer->writeAttribute(xlinkNamespace, "title", anchorPair.second);
1835 else
1836 m_writer->writeAttribute(xlinkNamespace, "title", linkPair.second);
1837 m_writer->writeEndElement(); // extendedlink
1838 newLine();
1839 }
1840 if (node->links().contains(Node::StartLink)) {
1841 linkPair = node->links()[Node::StartLink];
1842 linkNode = m_qdb->findNodeForTarget(linkPair.first, node);
1843 if (!linkNode || linkNode == node)
1844 anchorPair = linkPair;
1845 else
1846 anchorPair = anchorForNode(linkNode);
1847
1848 m_writer->writeStartElement(dbNamespace, "extendedlink");
1849 m_writer->writeAttribute(xlinkNamespace, "type", "extended");
1850 m_writer->writeEmptyElement(dbNamespace, "link");
1851 m_writer->writeAttribute(xlinkNamespace, "to", anchorPair.first);
1852 m_writer->writeAttribute(xlinkNamespace, "type", "arc");
1853 m_writer->writeAttribute(xlinkNamespace, "arcrole", "start");
1854 if (linkPair.first == linkPair.second && !anchorPair.second.isEmpty())
1855 m_writer->writeAttribute(xlinkNamespace, "title", anchorPair.second);
1856 else
1857 m_writer->writeAttribute(xlinkNamespace, "title", linkPair.second);
1858 m_writer->writeEndElement(); // extendedlink
1859 newLine();
1860 }
1861 }
1862
1863 // Deal with the abstract (what qdoc calls brief).
1864 if (node) {
1865 // Adapted from HtmlGenerator::generateBrief, without extraction marks. The parameter
1866 // addLink is always false. Factoring this function out is not as easy as in HtmlGenerator:
1867 // abstracts only happen in the header (info tag), slightly different tags must be used at
1868 // other places. Also includes code from HtmlGenerator::generateCppReferencePage to handle
1869 // the name spaces.
1870 m_writer->writeStartElement(dbNamespace, "abstract");
1871 newLine();
1872
1873 bool generatedSomething = false;
1874
1875 Text brief;
1876 const NamespaceNode *ns =
1877 node->isNamespace() ? static_cast<const NamespaceNode *>(node) : nullptr;
1878 if (ns && !ns->hasDoc() && ns->docNode()) {
1879 NamespaceNode *NS = ns->docNode();
1880 brief << "The " << ns->name()
1881 << " namespace includes the following elements from module "
1882 << ns->tree()->camelCaseModuleName() << ". The full namespace is "
1883 << "documented in module " << NS->tree()->camelCaseModuleName()
1884 << Atom(Atom::LinkNode, fullDocumentLocation(NS))
1885 << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK)
1886 << Atom(Atom::String, " here.")
1887 << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK);
1888 } else {
1889 brief = node->doc().briefText();
1890 }
1891
1892 if (!brief.isEmpty()) {
1893 if (!brief.lastAtom()->string().endsWith('.'))
1894 brief << Atom(Atom::String, ".");
1895
1896 m_writer->writeStartElement(dbNamespace, "para");
1897 generateText(brief, node);
1898 m_writer->writeEndElement(); // para
1899 newLine();
1900
1901 generatedSomething = true;
1902 }
1903
1904 // Generate other paragraphs that should go into the abstract.
1905 generatedSomething |= generateStatus(node);
1906 generatedSomething |= generateSince(node);
1907 generatedSomething |= generateThreadSafeness(node);
1908 generatedSomething |= generateComparisonCategory(node);
1909 generatedSomething |= generateComparisonList(node);
1910
1911 // An abstract cannot be empty, hence use the project description.
1912 if (!generatedSomething)
1913 m_writer->writeTextElement(dbNamespace, "para", m_projectDescription + ".");
1914
1915 m_writer->writeEndElement(); // abstract
1916 newLine();
1917 }
1918
1919 // End of the DocBook header.
1920 m_writer->writeEndElement(); // info
1921 newLine();
1922}
1923
1925{
1926 while (!sectionLevels.isEmpty()) {
1927 sectionLevels.pop();
1928 endSection();
1929 }
1930}
1931
1933{
1934 if (m_closeSectionAfterGeneratedList) {
1935 m_closeSectionAfterGeneratedList = false;
1936 endSection();
1937 }
1938 if (m_closeSectionAfterRawTitle) {
1939 m_closeSectionAfterRawTitle = false;
1940 endSection();
1941 }
1942
1944 m_writer->writeEndElement(); // article
1945}
1946
1947void DocBookGenerator::generateSimpleLink(const QString &href, const QString &text)
1948{
1949 m_writer->writeStartElement(dbNamespace, "link");
1950 m_writer->writeAttribute(xlinkNamespace, "href", href);
1951 m_writer->writeCharacters(text);
1952 m_writer->writeEndElement(); // link
1953}
1954
1955void DocBookGenerator::generateObsoleteMembers(const Sections &sections)
1956{
1957 // From HtmlGenerator::generateObsoleteMembersFile.
1958 SectionPtrVector summary_spv; // Summaries are ignored in DocBook (table of contents).
1959 SectionPtrVector details_spv;
1960 if (!sections.hasObsoleteMembers(&summary_spv, &details_spv))
1961 return;
1962
1963 Aggregate *aggregate = sections.aggregate();
1964 startSection("obsolete", "Obsolete Members for " + aggregate->name());
1965
1966 m_writer->writeStartElement(dbNamespace, "para");
1967 m_writer->writeStartElement(dbNamespace, "emphasis");
1968 m_writer->writeAttribute("role", "bold");
1969 m_writer->writeCharacters("The following members of class ");
1970 generateSimpleLink(linkForNode(aggregate, nullptr), aggregate->name());
1971 m_writer->writeCharacters(" are deprecated.");
1972 m_writer->writeEndElement(); // emphasis bold
1973 m_writer->writeCharacters(" We strongly advise against using them in new code.");
1974 m_writer->writeEndElement(); // para
1975 newLine();
1976
1977 for (const Section *section : details_spv) {
1978 const QString &title = "Obsolete " + section->title();
1979 startSection(title.toLower(), title);
1980
1981 const NodeVector &members = section->obsoleteMembers();
1982 NodeVector::ConstIterator m = members.constBegin();
1983 while (m != members.constEnd()) {
1984 if ((*m)->access() != Access::Private)
1985 generateDetailedMember(*m, aggregate);
1986 ++m;
1987 }
1988
1989 endSection();
1990 }
1991
1992 endSection();
1993}
1994
1995/*!
1996 Generates a separate section where obsolete members of the QML
1997 type \a qcn are listed. The \a marker is used to generate
1998 the section lists, which are then traversed and output here.
1999
2000 Note that this function currently only handles correctly the
2001 case where \a status is \c {Section::Deprecated}.
2002 */
2003void DocBookGenerator::generateObsoleteQmlMembers(const Sections &sections)
2004{
2005 // From HtmlGenerator::generateObsoleteQmlMembersFile.
2006 SectionPtrVector summary_spv; // Summaries are not useful in DocBook.
2007 SectionPtrVector details_spv;
2008 if (!sections.hasObsoleteMembers(&summary_spv, &details_spv))
2009 return;
2010
2011 Aggregate *aggregate = sections.aggregate();
2012 startSection("obsolete", "Obsolete Members for " + aggregate->name());
2013
2014 m_writer->writeStartElement(dbNamespace, "para");
2015 m_writer->writeStartElement(dbNamespace, "emphasis");
2016 m_writer->writeAttribute("role", "bold");
2017 m_writer->writeCharacters("The following members of QML type ");
2018 generateSimpleLink(linkForNode(aggregate, nullptr), aggregate->name());
2019 m_writer->writeCharacters(" are deprecated.");
2020 m_writer->writeEndElement(); // emphasis bold
2021 m_writer->writeCharacters(" We strongly advise against using them in new code.");
2022 m_writer->writeEndElement(); // para
2023 newLine();
2024
2025 for (const auto *section : details_spv) {
2026 const QString &title = "Obsolete " + section->title();
2027 startSection(title.toLower(), title);
2028
2029 const NodeVector &members = section->obsoleteMembers();
2030 NodeVector::ConstIterator m = members.constBegin();
2031 while (m != members.constEnd()) {
2032 if ((*m)->access() != Access::Private)
2033 generateDetailedQmlMember(*m, aggregate);
2034 ++m;
2035 }
2036
2037 endSection();
2038 }
2039
2040 endSection();
2041}
2042
2043static QString nodeToSynopsisTag(const Node *node)
2044{
2045 // Order from Node::nodeTypeString.
2046 if (node->isClass() || node->isQmlType())
2047 return QStringLiteral("classsynopsis");
2048 if (node->isNamespace())
2049 return QStringLiteral("packagesynopsis");
2050 if (node->isPageNode()) {
2051 node->doc().location().warning("Unexpected document node in nodeToSynopsisTag");
2052 return QString();
2053 }
2054 if (node->isEnumType())
2055 return QStringLiteral("enumsynopsis");
2056 if (node->isTypedef())
2057 return QStringLiteral("typedefsynopsis");
2058 if (node->isFunction()) {
2059 // Signals are also encoded as functions (including QML ones).
2060 const auto fn = static_cast<const FunctionNode *>(node);
2061 if (fn->isCtor() || fn->isCCtor() || fn->isMCtor())
2062 return QStringLiteral("constructorsynopsis");
2063 if (fn->isDtor())
2064 return QStringLiteral("destructorsynopsis");
2065 return QStringLiteral("methodsynopsis");
2066 }
2067 if (node->isProperty() || node->isVariable() || node->isQmlProperty())
2068 return QStringLiteral("fieldsynopsis");
2069
2070 node->doc().location().warning(QString("Unknown node tag %1").arg(node->nodeTypeString()));
2071 return QStringLiteral("synopsis");
2072}
2073
2074void DocBookGenerator::generateStartRequisite(const QString &description)
2075{
2076 m_writer->writeStartElement(dbNamespace, "varlistentry");
2077 newLine();
2078 m_writer->writeTextElement(dbNamespace, "term", description);
2079 newLine();
2080 m_writer->writeStartElement(dbNamespace, "listitem");
2081 newLine();
2082 m_writer->writeStartElement(dbNamespace, "para");
2083 m_inPara = true;
2084}
2085
2086void DocBookGenerator::generateEndRequisite()
2087{
2088 m_writer->writeEndElement(); // para
2089 m_inPara = false;
2090 newLine();
2091 m_writer->writeEndElement(); // listitem
2092 newLine();
2093 m_writer->writeEndElement(); // varlistentry
2094 newLine();
2095}
2096
2097void DocBookGenerator::generateRequisite(const QString &description, const QString &value)
2098{
2099 generateStartRequisite(description);
2100 m_writer->writeCharacters(value);
2101 generateEndRequisite();
2102}
2103
2104/*!
2105 * \internal
2106 * Generates the CMake (\a description) requisites
2107 */
2108void DocBookGenerator::generateCMakeRequisite(const QString &findPackage, const QString &linkLibraries)
2109{
2110 const QString description("CMake");
2111 generateStartRequisite(description);
2112 m_writer->writeCharacters(findPackage);
2113 m_writer->writeEndElement(); // para
2114 newLine();
2115
2116 m_writer->writeStartElement(dbNamespace, "para");
2117 m_writer->writeCharacters(linkLibraries);
2118 generateEndRequisite();
2119}
2120
2121void DocBookGenerator::generateSortedNames(const ClassNode *cn, const QList<RelatedClass> &rc)
2122{
2123 // From Generator::appendSortedNames.
2124 QMap<QString, ClassNode *> classMap;
2125 QList<RelatedClass>::ConstIterator r = rc.constBegin();
2126 while (r != rc.constEnd()) {
2127 ClassNode *rcn = (*r).m_node;
2128 if (rcn && rcn->access() == Access::Public && rcn->status() != Node::Internal
2129 && !rcn->doc().isEmpty()) {
2130 classMap[rcn->plainFullName(cn).toLower()] = rcn;
2131 }
2132 ++r;
2133 }
2134
2135 QStringList classNames = classMap.keys();
2136 classNames.sort();
2137
2138 int index = 0;
2139 for (const QString &className : classNames) {
2140 generateFullName(classMap.value(className), cn);
2141 m_writer->writeCharacters(Utilities::comma(index++, classNames.size()));
2142 }
2143}
2144
2146{
2147 // From Generator::appendSortedQmlNames.
2148 QMap<QString, Node *> classMap;
2149
2150 for (auto sub : subs)
2151 classMap[sub->plainFullName(base).toLower()] = sub;
2152
2153 QStringList names = classMap.keys();
2154 names.sort();
2155
2156 int index = 0;
2157 for (const QString &name : names) {
2158 generateFullName(classMap.value(name), base);
2159 m_writer->writeCharacters(Utilities::comma(index++, names.size()));
2160 }
2161}
2162
2163/*!
2164 Lists the required imports and includes.
2165*/
2167{
2168 // Adapted from HtmlGenerator::generateRequisites, but simplified: no need to store all the
2169 // elements, they can be produced one by one.
2170
2171 // Generate the requisites first separately: if some of them are generated, output them in a wrapper.
2172 // This complexity is required to ensure the DocBook file is valid: an empty list is not valid. It is not easy
2173 // to write a truly comprehensive condition.
2174 QXmlStreamWriter* oldWriter = m_writer;
2175 QString output;
2176 m_writer = new QXmlStreamWriter(&output);
2177
2178 // Includes.
2179 if (aggregate->includeFile()) generateRequisite("Header", *aggregate->includeFile());
2180
2181 // Since and project.
2182 if (!aggregate->since().isEmpty())
2183 generateRequisite("Since", formatSince(aggregate));
2184
2185 if (aggregate->isClassNode() || aggregate->isNamespace()) {
2186 // CMake and QT variable.
2187 const CollectionNode *cn =
2188 m_qdb->getCollectionNode(aggregate->physicalModuleName(), Node::Module);
2189
2190 if (const auto result = cmakeRequisite(cn)) {
2191 generateCMakeRequisite(result->first, result->second);
2192 }
2193
2194 if (cn && !cn->qtVariable().isEmpty())
2195 generateRequisite("qmake", "QT += " + cn->qtVariable());
2196 }
2197
2198 if (aggregate->nodeType() == Node::Class) {
2199 // Native type information.
2200 auto *classe = const_cast<ClassNode *>(static_cast<const ClassNode *>(aggregate));
2201 if (classe && classe->isQmlNativeType() && classe->status() != Node::Internal) {
2202 generateStartRequisite("In QML");
2203
2204 qsizetype idx{0};
2205 QList<QmlTypeNode *> nativeTypes { classe->qmlNativeTypes().cbegin(), classe->qmlNativeTypes().cend()};
2206 std::sort(nativeTypes.begin(), nativeTypes.end(), Node::nodeNameLessThan);
2207
2208 for (const auto &item : std::as_const(nativeTypes)) {
2209 generateFullName(item, classe);
2210 m_writer->writeCharacters(
2211 Utilities::comma(idx++, nativeTypes.size()));
2212 }
2213 generateEndRequisite();
2214 }
2215
2216 // Inherits.
2217 QList<RelatedClass>::ConstIterator r;
2218 if (!classe->baseClasses().isEmpty()) {
2219 generateStartRequisite("Inherits");
2220
2221 r = classe->baseClasses().constBegin();
2222 int index = 0;
2223 while (r != classe->baseClasses().constEnd()) {
2224 if ((*r).m_node) {
2225 generateFullName((*r).m_node, classe);
2226
2227 if ((*r).m_access == Access::Protected)
2228 m_writer->writeCharacters(" (protected)");
2229 else if ((*r).m_access == Access::Private)
2230 m_writer->writeCharacters(" (private)");
2231 m_writer->writeCharacters(
2232 Utilities::comma(index++, classe->baseClasses().size()));
2233 }
2234 ++r;
2235 }
2236
2237 generateEndRequisite();
2238 }
2239
2240 // Inherited by.
2241 if (!classe->derivedClasses().isEmpty()) {
2242 generateStartRequisite("Inherited By");
2243 generateSortedNames(classe, classe->derivedClasses());
2244 generateEndRequisite();
2245 }
2246 }
2247
2248 // Group.
2249 if (!aggregate->groupNames().empty()) {
2250 generateStartRequisite("Group");
2251 generateGroupReferenceText(aggregate);
2252 generateEndRequisite();
2253 }
2254
2255 // Status.
2256 if (auto status = formatStatus(aggregate, m_qdb); status)
2257 generateRequisite("Status", status.value());
2258
2259 // Write the elements as a list if not empty.
2260 delete m_writer;
2261 m_writer = oldWriter;
2262
2263 if (!output.isEmpty()) {
2264 // Namespaces are mangled in this output, because QXmlStreamWriter doesn't know about them. (Letting it know
2265 // would imply generating the xmlns declaration one more time.)
2266 static const QRegularExpression xmlTag(R"(<(/?)n\d+:)"); // Only for DocBook tags.
2267 static const QRegularExpression xmlnsDocBookDefinition(R"( xmlns:n\d+=")" + QString{dbNamespace} + "\"");
2268 static const QRegularExpression xmlnsXLinkDefinition(R"( xmlns:n\d+=")" + QString{xlinkNamespace} + "\"");
2269 static const QRegularExpression xmlAttr(R"( n\d+:)"); // Only for XLink attributes.
2270 // Space at the beginning!
2271 const QString cleanOutput = output.replace(xmlTag, R"(<\1db:)")
2272 .replace(xmlnsDocBookDefinition, "")
2273 .replace(xmlnsXLinkDefinition, "")
2274 .replace(xmlAttr, " xlink:");
2275
2276 m_writer->writeStartElement(dbNamespace, "variablelist");
2277 if (m_useITS)
2278 m_writer->writeAttribute(itsNamespace, "translate", "no");
2279 newLine();
2280
2281 m_writer->device()->write(cleanOutput.toUtf8());
2282
2283 m_writer->writeEndElement(); // variablelist
2284 newLine();
2285 }
2286}
2287
2288/*!
2289 Lists the required imports and includes.
2290*/
2292{
2293 // From HtmlGenerator::generateQmlRequisites, but simplified: no need to store all the elements,
2294 // they can be produced one by one.
2295 if (!qcn)
2296 return;
2297
2298 const CollectionNode *collection = qcn->logicalModule();
2299
2300 NodeList subs;
2301 QmlTypeNode::subclasses(qcn, subs);
2302
2303 QmlTypeNode *base = qcn->qmlBaseNode();
2304 while (base && base->isInternal()) {
2305 base = base->qmlBaseNode();
2306 }
2307
2308 // Skip import statement for \internal collections
2309 const bool generate_import_statement = !qcn->logicalModuleName().isEmpty() && (!collection || !collection->isInternal() || m_showInternal);
2310 // Detect if anything is generated in this method. If not, exit early to avoid having an empty list.
2311 const bool generates_something = generate_import_statement || !qcn->since().isEmpty() || !subs.isEmpty() || base;
2312
2313 if (!generates_something)
2314 return;
2315
2316 // Start writing the elements as a list.
2317 m_writer->writeStartElement(dbNamespace, "variablelist");
2318 if (m_useITS)
2319 m_writer->writeAttribute(itsNamespace, "translate", "no");
2320 newLine();
2321
2322 if (generate_import_statement) {
2323 QStringList parts = QStringList() << "import" << qcn->logicalModuleName() << qcn->logicalModuleVersion();
2324 generateRequisite("Import Statement", parts.join(' ').trimmed());
2325 }
2326
2327 // Since and project.
2328 if (!qcn->since().isEmpty())
2329 generateRequisite("Since:", formatSince(qcn));
2330
2331 // Inherited by.
2332 if (!subs.isEmpty()) {
2333 generateStartRequisite("Inherited By:");
2334 generateSortedQmlNames(qcn, subs);
2335 generateEndRequisite();
2336 }
2337
2338 // Inherits.
2339 if (base) {
2340 const Node *otherNode = nullptr;
2341 Atom a = Atom(Atom::LinkNode, CodeMarker::stringForNode(base));
2342 QString link = getAutoLink(&a, qcn, &otherNode);
2343
2344 generateStartRequisite("Inherits:");
2345 generateSimpleLink(link, base->name());
2346 generateEndRequisite();
2347 }
2348
2349 // Native type information.
2350 ClassNode *cn = (const_cast<QmlTypeNode *>(qcn))->classNode();
2351 if (cn && cn->isQmlNativeType() && cn->status() != Node::Internal) {
2352 generateStartRequisite("In C++:");
2353 generateSimpleLink(fullDocumentLocation(cn), cn->name());
2354 generateEndRequisite();
2355 }
2356
2357 // Group.
2358 if (!qcn->groupNames().empty()) {
2359 generateStartRequisite("Group");
2361 generateEndRequisite();
2362 }
2363
2364 // Status.
2365 if (auto status = formatStatus(qcn, m_qdb); status)
2366 generateRequisite("Status:", status.value());
2367
2368 m_writer->writeEndElement(); // variablelist
2369 newLine();
2370}
2371
2373{
2374 // From Generator::generateStatus.
2375 switch (node->status()) {
2376 case Node::Active:
2377 // Output the module 'state' description if set.
2378 if (node->isModule() || node->isQmlModule()) {
2379 const QString &state = static_cast<const CollectionNode*>(node)->state();
2380 if (!state.isEmpty()) {
2381 m_writer->writeStartElement(dbNamespace, "para");
2382 m_writer->writeCharacters("This " + typeString(node) + " is in ");
2383 m_writer->writeStartElement(dbNamespace, "emphasis");
2384 m_writer->writeCharacters(state);
2385 m_writer->writeEndElement(); // emphasis
2386 m_writer->writeCharacters(" state.");
2387 m_writer->writeEndElement(); // para
2388 newLine();
2389 return true;
2390 }
2391 }
2392 if (const auto version = node->deprecatedSince(); !version.isEmpty()) {
2393 m_writer->writeStartElement(dbNamespace, "para");
2394 m_writer->writeCharacters("This " + typeString(node)
2395 + " is scheduled for deprecation in version "
2396 + version + ".");
2397 m_writer->writeEndElement(); // para
2398 newLine();
2399 return true;
2400 }
2401 return false;
2402 case Node::Preliminary:
2403 m_writer->writeStartElement(dbNamespace, "para");
2404 m_writer->writeStartElement(dbNamespace, "emphasis");
2405 m_writer->writeAttribute("role", "bold");
2406 m_writer->writeCharacters("This " + typeString(node)
2407 + " is under development and is subject to change.");
2408 m_writer->writeEndElement(); // emphasis
2409 m_writer->writeEndElement(); // para
2410 newLine();
2411 return true;
2412 case Node::Deprecated:
2413 m_writer->writeStartElement(dbNamespace, "para");
2414 if (node->isAggregate()) {
2415 m_writer->writeStartElement(dbNamespace, "emphasis");
2416 m_writer->writeAttribute("role", "bold");
2417 }
2418 m_writer->writeCharacters("This " + typeString(node) + " is deprecated");
2419 if (const QString &version = node->deprecatedSince(); !version.isEmpty()) {
2420 m_writer->writeCharacters(" since ");
2421 if (node->isQmlNode() && !node->logicalModuleName().isEmpty())
2422 m_writer->writeCharacters(node->logicalModuleName() + " ");
2423 m_writer->writeCharacters(version);
2424 }
2425 m_writer->writeCharacters(". We strongly advise against using it in new code.");
2426 if (node->isAggregate())
2427 m_writer->writeEndElement(); // emphasis
2428 m_writer->writeEndElement(); // para
2429 newLine();
2430 return true;
2431 case Node::Internal:
2432 default:
2433 return false;
2434 }
2435}
2436
2437/*!
2438 Generate a list of function signatures. The function nodes
2439 are in \a nodes.
2440 */
2441void DocBookGenerator::generateSignatureList(const NodeList &nodes)
2442{
2443 // From Generator::signatureList and Generator::appendSignature.
2444 m_writer->writeStartElement(dbNamespace, "itemizedlist");
2445 newLine();
2446
2447 NodeList::ConstIterator n = nodes.constBegin();
2448 while (n != nodes.constEnd()) {
2449 m_writer->writeStartElement(dbNamespace, "listitem");
2450 newLine();
2451 m_writer->writeStartElement(dbNamespace, "para");
2452
2453 generateSimpleLink(currentGenerator()->fullDocumentLocation(*n),
2454 (*n)->signature(Node::SignaturePlain));
2455
2456 m_writer->writeEndElement(); // para
2457 newLine();
2458 m_writer->writeEndElement(); // itemizedlist
2459 newLine();
2460 ++n;
2461 }
2462
2463 m_writer->writeEndElement(); // itemizedlist
2464 newLine();
2465}
2466
2467/*!
2468 * Return a string representing a text that exposes information about
2469 * the groups that the \a node is part of.
2470 */
2472{
2473 // From HtmlGenerator::groupReferenceText
2474
2475 if (!node->isAggregate())
2476 return;
2477 const auto aggregate = static_cast<const Aggregate *>(node);
2478
2479 const QStringList &groups_names{aggregate->groupNames()};
2480 if (!groups_names.empty()) {
2481 m_writer->writeCharacters(aggregate->name() + " is part of ");
2482 m_writer->writeStartElement(dbNamespace, "simplelist");
2483
2484 for (qsizetype index{0}; index < groups_names.size(); ++index) {
2485 CollectionNode* group{m_qdb->groups()[groups_names[index]]};
2487
2488 m_writer->writeStartElement(dbNamespace, "member");
2489 if (QString target{linkForNode(group, nullptr)}; !target.isEmpty())
2490 generateSimpleLink(target, group->fullTitle());
2491 else
2492 m_writer->writeCharacters(group->name());
2493 m_writer->writeEndElement(); // member
2494 }
2495
2496 m_writer->writeEndElement(); // simplelist
2497 newLine();
2498 }
2499}
2500
2501/*!
2502 Generates text that explains how threadsafe and/or reentrant
2503 \a node is.
2504 */
2506{
2507 // From Generator::generateThreadSafeness
2509
2510 const Node *reentrantNode;
2511 Atom reentrantAtom = Atom(Atom::Link, "reentrant");
2512 QString linkReentrant = getAutoLink(&reentrantAtom, node, &reentrantNode);
2513 const Node *threadSafeNode;
2514 Atom threadSafeAtom = Atom(Atom::Link, "thread-safe");
2515 QString linkThreadSafe = getAutoLink(&threadSafeAtom, node, &threadSafeNode);
2516
2517 if (ts == Node::NonReentrant) {
2518 m_writer->writeStartElement(dbNamespace, "warning");
2519 newLine();
2520 m_writer->writeStartElement(dbNamespace, "para");
2521 m_writer->writeCharacters("This " + typeString(node) + " is not ");
2522 generateSimpleLink(linkReentrant, "reentrant");
2523 m_writer->writeCharacters(".");
2524 m_writer->writeEndElement(); // para
2525 newLine();
2526 m_writer->writeEndElement(); // warning
2527
2528 return true;
2529 } else if (ts == Node::Reentrant || ts == Node::ThreadSafe) {
2530 m_writer->writeStartElement(dbNamespace, "note");
2531 newLine();
2532 m_writer->writeStartElement(dbNamespace, "para");
2533
2534 if (node->isAggregate()) {
2535 m_writer->writeCharacters("All functions in this " + typeString(node) + " are ");
2536 if (ts == Node::ThreadSafe)
2537 generateSimpleLink(linkThreadSafe, "thread-safe");
2538 else
2539 generateSimpleLink(linkReentrant, "reentrant");
2540
2541 NodeList reentrant;
2542 NodeList threadsafe;
2543 NodeList nonreentrant;
2544 bool exceptions = hasExceptions(node, reentrant, threadsafe, nonreentrant);
2545 if (!exceptions || (ts == Node::Reentrant && !threadsafe.isEmpty())) {
2546 m_writer->writeCharacters(".");
2547 m_writer->writeEndElement(); // para
2548 newLine();
2549 } else {
2550 m_writer->writeCharacters(" with the following exceptions:");
2551 m_writer->writeEndElement(); // para
2552 newLine();
2553 m_writer->writeStartElement(dbNamespace, "para");
2554
2555 if (ts == Node::Reentrant) {
2556 if (!nonreentrant.isEmpty()) {
2557 m_writer->writeCharacters("These functions are not ");
2558 generateSimpleLink(linkReentrant, "reentrant");
2559 m_writer->writeCharacters(":");
2560 m_writer->writeEndElement(); // para
2561 newLine();
2562 generateSignatureList(nonreentrant);
2563 }
2564 if (!threadsafe.isEmpty()) {
2565 m_writer->writeCharacters("These functions are also ");
2566 generateSimpleLink(linkThreadSafe, "thread-safe");
2567 m_writer->writeCharacters(":");
2568 m_writer->writeEndElement(); // para
2569 newLine();
2570 generateSignatureList(threadsafe);
2571 }
2572 } else { // thread-safe
2573 if (!reentrant.isEmpty()) {
2574 m_writer->writeCharacters("These functions are only ");
2575 generateSimpleLink(linkReentrant, "reentrant");
2576 m_writer->writeCharacters(":");
2577 m_writer->writeEndElement(); // para
2578 newLine();
2579 generateSignatureList(reentrant);
2580 }
2581 if (!nonreentrant.isEmpty()) {
2582 m_writer->writeCharacters("These functions are not ");
2583 generateSimpleLink(linkReentrant, "reentrant");
2584 m_writer->writeCharacters(":");
2585 m_writer->writeEndElement(); // para
2586 newLine();
2587 generateSignatureList(nonreentrant);
2588 }
2589 }
2590 }
2591 } else {
2592 m_writer->writeCharacters("This " + typeString(node) + " is ");
2593 if (ts == Node::ThreadSafe)
2594 generateSimpleLink(linkThreadSafe, "thread-safe");
2595 else
2596 generateSimpleLink(linkReentrant, "reentrant");
2597 m_writer->writeCharacters(".");
2598 m_writer->writeEndElement(); // para
2599 newLine();
2600 }
2601 m_writer->writeEndElement(); // note
2602 newLine();
2603
2604 return true;
2605 }
2606
2607 return false;
2608}
2609
2610/*!
2611 Generate the body of the documentation from the qdoc comment
2612 found with the entity represented by the \a node.
2613 */
2615{
2616 // From Generator::generateBody, without warnings.
2617 const FunctionNode *fn = node->isFunction() ? static_cast<const FunctionNode *>(node) : nullptr;
2618
2619 if (!node->hasDoc()) {
2620 /*
2621 Test for special function, like a destructor or copy constructor,
2622 that has no documentation.
2623 */
2624 if (fn) {
2625 QString t;
2626 if (fn->isDtor()) {
2627 t = "Destroys the instance of " + fn->parent()->name() + ".";
2628 if (fn->isVirtual())
2629 t += " The destructor is virtual.";
2630 } else if (fn->isCtor()) {
2631 t = "Default constructs an instance of " + fn->parent()->name() + ".";
2632 } else if (fn->isCCtor()) {
2633 t = "Copy constructor.";
2634 } else if (fn->isMCtor()) {
2635 t = "Move-copy constructor.";
2636 } else if (fn->isCAssign()) {
2637 t = "Copy-assignment constructor.";
2638 } else if (fn->isMAssign()) {
2639 t = "Move-assignment constructor.";
2640 }
2641
2642 if (!t.isEmpty())
2643 m_writer->writeTextElement(dbNamespace, "para", t);
2644 }
2645 } else if (!node->isSharingComment()) {
2646 // Reimplements clause and type alias info precede body text
2647 if (fn && !fn->overridesThis().isEmpty())
2648 generateReimplementsClause(fn);
2649 else if (node->isProperty()) {
2651 generateAddendum(node, BindableProperty, nullptr, false);
2652 }
2653
2654 // Generate the body.
2655 if (!generateText(node->doc().body(), node)) {
2656 if (node->isMarkedReimp())
2657 return;
2658 }
2659
2660 // Output what is after the main body.
2661 if (fn) {
2662 if (fn->isQmlSignal())
2663 generateAddendum(node, QmlSignalHandler, nullptr, true);
2664 if (fn->isPrivateSignal())
2665 generateAddendum(node, PrivateSignal, nullptr, true);
2666 if (fn->isInvokable())
2667 generateAddendum(node, Invokable, nullptr, true);
2670 }
2671
2672 // Warning generation skipped with respect to Generator::generateBody.
2673 }
2674
2675 generateEnumValuesForQmlProperty(node, nullptr);
2676 generateRequiredLinks(node);
2677}
2678
2679/*!
2680 Generates either a link to the project folder for example \a node, or a list
2681 of links files/images if 'url.examples config' variable is not defined.
2682
2683 Does nothing for non-example nodes.
2684*/
2685void DocBookGenerator::generateRequiredLinks(const Node *node)
2686{
2687 // From Generator::generateRequiredLinks.
2688 if (!node->isExample())
2689 return;
2690
2691 const auto en = static_cast<const ExampleNode *>(node);
2692 QString exampleUrl{Config::instance().get(CONFIG_URL + Config::dot + CONFIG_EXAMPLES).asString()};
2693
2694 if (exampleUrl.isEmpty()) {
2695 if (!en->noAutoList()) {
2696 generateFileList(en, false); // files
2697 generateFileList(en, true); // images
2698 }
2699 } else {
2700 generateLinkToExample(en, exampleUrl);
2701 }
2702}
2703
2704/*!
2705 The path to the example replaces a placeholder '\1' character if
2706 one is found in the \a baseUrl string. If no such placeholder is found,
2707 the path is appended to \a baseUrl, after a '/' character if \a baseUrl did
2708 not already end in one.
2709*/
2710void DocBookGenerator::generateLinkToExample(const ExampleNode *en, const QString &baseUrl)
2711{
2712 // From Generator::generateLinkToExample.
2713 QString exampleUrl(baseUrl);
2714 QString link;
2715#ifndef QT_BOOTSTRAPPED
2716 link = QUrl(exampleUrl).host();
2717#endif
2718 if (!link.isEmpty())
2719 link.prepend(" @ ");
2720 link.prepend("Example project");
2721
2722 const QLatin1Char separator('/');
2723 const QLatin1Char placeholder('\1');
2724 if (!exampleUrl.contains(placeholder)) {
2725 if (!exampleUrl.endsWith(separator))
2726 exampleUrl += separator;
2727 exampleUrl += placeholder;
2728 }
2729
2730 // Construct a path to the example; <install path>/<example name>
2731 QStringList path = QStringList()
2732 << Config::instance().get(CONFIG_EXAMPLESINSTALLPATH).asString() << en->name();
2733 path.removeAll(QString());
2734
2735 // Write the link to the example. Typically, this link comes after sections, hence
2736 // wrap it in a section too.
2737 startSection("Example project");
2738
2739 m_writer->writeStartElement(dbNamespace, "para");
2740 generateSimpleLink(exampleUrl.replace(placeholder, path.join(separator)), link);
2741 m_writer->writeEndElement(); // para
2742 newLine();
2743
2744 endSection();
2745}
2746
2747// TODO: [multi-purpose-function-with-flag][generate-file-list]
2748
2749/*!
2750 This function is called when the documentation for an example is
2751 being formatted. It outputs a list of files for the example, which
2752 can be the example's source files or the list of images used by the
2753 example. The images are copied into a subtree of
2754 \c{...doc/html/images/used-in-examples/...}
2755*/
2756void DocBookGenerator::generateFileList(const ExampleNode *en, bool images)
2757{
2758 // TODO: [possibly-stale-duplicate-code][generator-insufficient-structural-abstraction]
2759 // Review and compare this code with
2760 // Generator::generateFileList.
2761 // Some subtle changes that might be semantically equivalent are
2762 // present between the two.
2763 // Supposedly, this version is to be considered stale compared to
2764 // Generator's one and it might be possible to remove it in favor
2765 // of that as long as the difference in output are taken into consideration.
2766
2767 // From Generator::generateFileList
2768 QString tag;
2769 QStringList paths;
2770 if (images) {
2771 paths = en->images();
2772 tag = "Images:";
2773 } else { // files
2774 paths = en->files();
2775 tag = "Files:";
2776 }
2777 std::sort(paths.begin(), paths.end(), Generator::comparePaths);
2778
2779 if (paths.isEmpty())
2780 return;
2781
2782 startSection("", "List of Files");
2783
2784 m_writer->writeStartElement(dbNamespace, "para");
2785 m_writer->writeCharacters(tag);
2786 m_writer->writeEndElement(); // para
2787 newLine();
2788
2789 startSection("List of Files");
2790
2791 m_writer->writeStartElement(dbNamespace, "itemizedlist");
2792 newLine();
2793
2794 for (const auto &path : std::as_const(paths)) {
2795 auto maybe_resolved_file{file_resolver.resolve(path)};
2796 if (!maybe_resolved_file) {
2797 // TODO: [uncentralized-admonition][failed-resolve-file]
2798 QString details = std::transform_reduce(
2799 file_resolver.get_search_directories().cbegin(),
2800 file_resolver.get_search_directories().cend(),
2801 u"Searched directories:"_s,
2802 std::plus(),
2803 [](const DirectoryPath &directory_path) -> QString { return u' ' + directory_path.value(); }
2804 );
2805
2806 en->location().warning(u"Cannot find file to quote from: %1"_s.arg(path), details);
2807
2808 continue;
2809 }
2810
2811 auto file{*maybe_resolved_file};
2812 if (images) addImageToCopy(en, file);
2813 else generateExampleFilePage(en, file);
2814
2815 m_writer->writeStartElement(dbNamespace, "listitem");
2816 newLine();
2817 m_writer->writeStartElement(dbNamespace, "para");
2818 generateSimpleLink(file.get_query(), file.get_query());
2819 m_writer->writeEndElement(); // para
2820 m_writer->writeEndElement(); // listitem
2821 newLine();
2822 }
2823
2824 m_writer->writeEndElement(); // itemizedlist
2825 newLine();
2826
2827 endSection();
2828}
2829
2830/*!
2831 Generate a file with the contents of a C++ or QML source file.
2832 */
2834{
2835 // TODO: [generator-insufficient-structural-abstraction]
2836
2837 // From HtmlGenerator::generateExampleFilePage.
2838 if (!node->isExample())
2839 return;
2840
2841 // TODO: Understand if this is safe.
2842 const auto en = static_cast<const ExampleNode *>(node);
2843
2844 // Store current (active) writer
2845 QXmlStreamWriter *currentWriter = m_writer;
2846 m_writer = startDocument(en, resolved_file.get_query());
2847 generateHeader(en->fullTitle(), en->subtitle(), en);
2848
2849 Text text;
2850 Quoter quoter;
2851 Doc::quoteFromFile(en->doc().location(), quoter, resolved_file);
2852 QString code = quoter.quoteTo(en->location(), QString(), QString());
2853 CodeMarker *codeMarker = CodeMarker::markerForFileName(resolved_file.get_path());
2854 text << Atom(codeMarker->atomType(), code);
2855 Atom a(codeMarker->atomType(), code);
2856 generateText(text, en);
2857
2858 endDocument(); // Delete m_writer.
2859 m_writer = currentWriter; // Restore writer.
2860}
2861
2862void DocBookGenerator::generateReimplementsClause(const FunctionNode *fn)
2863{
2864 // From Generator::generateReimplementsClause, without warning generation.
2865 if (fn->overridesThis().isEmpty() || !fn->parent()->isClassNode())
2866 return;
2867
2868 auto cn = static_cast<ClassNode *>(fn->parent());
2869
2870 if (const FunctionNode *overrides = cn->findOverriddenFunction(fn);
2871 overrides && !overrides->isPrivate() && !overrides->parent()->isPrivate()) {
2872 if (overrides->hasDoc()) {
2873 m_writer->writeStartElement(dbNamespace, "para");
2874 m_writer->writeCharacters("Reimplements: ");
2875 QString fullName =
2876 overrides->parent()->name() + "::" + overrides->signature(Node::SignaturePlain);
2877 generateFullName(overrides->parent(), fullName, overrides);
2878 m_writer->writeCharacters(".");
2879 m_writer->writeEndElement(); // para
2880 newLine();
2881 return;
2882 }
2883 }
2884
2885 if (const PropertyNode *sameName = cn->findOverriddenProperty(fn); sameName && sameName->hasDoc()) {
2886 m_writer->writeStartElement(dbNamespace, "para");
2887 m_writer->writeCharacters("Reimplements an access function for property: ");
2888 QString fullName = sameName->parent()->name() + "::" + sameName->name();
2889 generateFullName(sameName->parent(), fullName, sameName);
2890 m_writer->writeCharacters(".");
2891 m_writer->writeEndElement(); // para
2892 newLine();
2893 return;
2894 }
2895}
2896
2898{
2899 // From Generator::generateAlsoList.
2900 QList<Text> alsoList = node->doc().alsoList();
2901 supplementAlsoList(node, alsoList);
2902
2903 if (!alsoList.isEmpty()) {
2904 startSection("See Also");
2905
2906 m_writer->writeStartElement(dbNamespace, "para");
2907 m_writer->writeStartElement(dbNamespace, "emphasis");
2908 m_writer->writeCharacters("See also ");
2909 m_writer->writeEndElement(); // emphasis
2910 newLine();
2911
2912 m_writer->writeStartElement(dbNamespace, "simplelist");
2913 m_writer->writeAttribute("type", "vert");
2914 m_writer->writeAttribute("role", "see-also");
2915 newLine();
2916
2917 for (const Text &text : alsoList) {
2918 m_writer->writeStartElement(dbNamespace, "member");
2919 generateText(text, node);
2920 m_writer->writeEndElement(); // member
2921 newLine();
2922 }
2923
2924 m_writer->writeEndElement(); // simplelist
2925 newLine();
2926
2927 m_writer->writeEndElement(); // para
2928 newLine();
2929
2930 endSection();
2931 }
2932}
2933
2934/*!
2935 Open a new file to write XML contents, including the DocBook
2936 opening tag.
2937 */
2938QXmlStreamWriter *DocBookGenerator::startGenericDocument(const Node *node, const QString &fileName)
2939{
2940 Q_ASSERT(node->isPageNode());
2941 QFile *outFile = openSubPageFile(static_cast<const PageNode*>(node), fileName);
2942 m_writer = new QXmlStreamWriter(outFile);
2943 m_writer->setAutoFormatting(false); // We need a precise handling of line feeds.
2944
2945 m_writer->writeStartDocument();
2946 newLine();
2947 m_writer->writeNamespace(dbNamespace, "db");
2948 m_writer->writeNamespace(xlinkNamespace, "xlink");
2949 if (m_useITS)
2950 m_writer->writeNamespace(itsNamespace, "its");
2951 m_writer->writeStartElement(dbNamespace, "article");
2952 m_writer->writeAttribute("version", "5.2");
2953 if (!m_naturalLanguage.isEmpty())
2954 m_writer->writeAttribute("xml:lang", m_naturalLanguage);
2955 newLine();
2956
2957 // Reset the state for the new document.
2958 sectionLevels.resize(0);
2959 m_inPara = false;
2960 m_inList = 0;
2961
2962 return m_writer;
2963}
2964
2965QXmlStreamWriter *DocBookGenerator::startDocument(const Node *node)
2966{
2967 m_hasSection = false;
2968 refMap.clear();
2969
2970 QString fileName = Generator::fileName(node, fileExtension());
2971 return startGenericDocument(node, fileName);
2972}
2973
2974QXmlStreamWriter *DocBookGenerator::startDocument(const ExampleNode *en, const QString &file)
2975{
2976 m_hasSection = false;
2977
2978 QString fileName = linkForExampleFile(file);
2979 return startGenericDocument(en, fileName);
2980}
2981
2982void DocBookGenerator::endDocument()
2983{
2984 m_writer->writeEndElement(); // article
2985 m_writer->writeEndDocument();
2986
2987 m_writer->device()->close();
2988 delete m_writer->device();
2989 delete m_writer;
2990 m_writer = nullptr;
2991}
2992
2993/*!
2994 Generate a reference page for the C++ class, namespace, or
2995 header file documented in \a node.
2996 */
2998{
2999 // Based on HtmlGenerator::generateCppReferencePage.
3000 Q_ASSERT(node->isAggregate());
3001 const auto aggregate = static_cast<const Aggregate *>(node);
3002
3003 QString title;
3004 QString rawTitle;
3005 QString fullTitle;
3006 if (aggregate->isNamespace()) {
3007 rawTitle = aggregate->plainName();
3008 fullTitle = aggregate->plainFullName();
3009 title = rawTitle + " Namespace";
3010 } else if (aggregate->isClass()) {
3011 rawTitle = aggregate->plainName();
3012
3013 auto templateDecl = node->templateDecl();
3014 if (templateDecl)
3015 fullTitle = QString("%1 %2 ").arg((*templateDecl).to_qstring(), aggregate->typeWord(false));
3016
3017 fullTitle += aggregate->plainFullName();
3018 title = rawTitle + QLatin1Char(' ') + aggregate->typeWord(true);
3019 } else if (aggregate->isHeader()) {
3020 title = fullTitle = rawTitle = aggregate->fullTitle();
3021 }
3022
3023 QString subtitleText;
3024 if (rawTitle != fullTitle)
3025 subtitleText = fullTitle;
3026
3027 // Start producing the DocBook file.
3028 m_writer = startDocument(node);
3029
3030 // Info container.
3031 generateHeader(title, subtitleText, aggregate);
3032
3033 generateRequisites(aggregate);
3034 generateStatus(aggregate);
3035
3036 // Element synopsis.
3038
3039 // Actual content.
3040 if (!aggregate->doc().isEmpty()) {
3041 startSection("details", "Detailed Description");
3042
3043 generateBody(aggregate);
3044 generateAlsoList(aggregate);
3045
3046 endSection();
3047 }
3048
3049 Sections sections(const_cast<Aggregate *>(aggregate));
3050 SectionVector sectionVector =
3051 (aggregate->isNamespace() || aggregate->isHeader()) ?
3052 sections.stdDetailsSections() :
3054 for (const Section &section : sectionVector) {
3055 if (section.members().isEmpty())
3056 continue;
3057
3058 startSection(section.title().toLower(), section.title());
3059
3060 for (const Node *member : section.members()) {
3061 if (member->access() == Access::Private) // ### check necessary?
3062 continue;
3063
3064 if (member->nodeType() != Node::Class) {
3065 // This function starts its own section.
3066 generateDetailedMember(member, aggregate);
3067 } else {
3068 startSectionBegin();
3069 m_writer->writeCharacters("class ");
3070 generateFullName(member, aggregate);
3071 startSectionEnd();
3072
3073 generateBrief(member);
3074
3075 endSection();
3076 }
3077 }
3078
3079 endSection();
3080 }
3081
3082 generateObsoleteMembers(sections);
3083
3084 endDocument();
3085}
3086
3087void DocBookGenerator::generateSynopsisInfo(const QString &key, const QString &value)
3088{
3089 m_writer->writeStartElement(dbNamespace, "synopsisinfo");
3090 m_writer->writeAttribute("role", key);
3091 m_writer->writeCharacters(value);
3092 m_writer->writeEndElement(); // synopsisinfo
3093 newLine();
3094}
3095
3096void DocBookGenerator::generateModifier(const QString &value)
3097{
3098 m_writer->writeTextElement(dbNamespace, "modifier", value);
3099 newLine();
3100}
3101
3102/*!
3103 Generate the metadata for the given \a node in DocBook.
3104 */
3106{
3107 if (!node)
3108 return;
3109
3110 // From Generator::generateStatus, HtmlGenerator::generateRequisites,
3111 // Generator::generateThreadSafeness, QDocIndexFiles::generateIndexSection.
3112
3113 // This function is the major place where DocBook extensions are used.
3114 if (!m_useDocBook52)
3115 return;
3116
3117 // Nothing to export in some cases. Note that isSharedCommentNode() returns
3118 // true also for QML property groups.
3120 return;
3121
3122 // Cast the node to several subtypes (null pointer if the node is not of the required type).
3123 const Aggregate *aggregate =
3124 node->isAggregate() ? static_cast<const Aggregate *>(node) : nullptr;
3125 const ClassNode *classNode = node->isClass() ? static_cast<const ClassNode *>(node) : nullptr;
3126 const FunctionNode *functionNode =
3127 node->isFunction() ? static_cast<const FunctionNode *>(node) : nullptr;
3128 const PropertyNode *propertyNode =
3129 node->isProperty() ? static_cast<const PropertyNode *>(node) : nullptr;
3130 const VariableNode *variableNode =
3131 node->isVariable() ? static_cast<const VariableNode *>(node) : nullptr;
3132 const EnumNode *enumNode = node->isEnumType() ? static_cast<const EnumNode *>(node) : nullptr;
3133 const QmlPropertyNode *qpn =
3134 node->isQmlProperty() ? static_cast<const QmlPropertyNode *>(node) : nullptr;
3135 const QmlTypeNode *qcn = node->isQmlType() ? static_cast<const QmlTypeNode *>(node) : nullptr;
3136 // Typedefs are ignored, as they correspond to enums.
3137 // Groups and modules are ignored.
3138 // Documents are ignored, they have no interesting metadata.
3139
3140 // Start the synopsis tag.
3141 QString synopsisTag = nodeToSynopsisTag(node);
3142 m_writer->writeStartElement(dbNamespace, synopsisTag);
3143 newLine();
3144
3145 // Name and basic properties of each tag (like types and parameters).
3146 if (node->isClass()) {
3147 m_writer->writeStartElement(dbNamespace, "ooclass");
3148 m_writer->writeTextElement(dbNamespace, "classname", node->plainName());
3149 m_writer->writeEndElement(); // ooclass
3150 newLine();
3151 } else if (node->isNamespace()) {
3152 m_writer->writeTextElement(dbNamespace, "namespacename", node->plainName());
3153 newLine();
3154 } else if (node->isQmlType()) {
3155 m_writer->writeStartElement(dbNamespace, "ooclass");
3156 m_writer->writeTextElement(dbNamespace, "classname", node->plainName());
3157 m_writer->writeEndElement(); // ooclass
3158 newLine();
3159 if (!qcn->groupNames().isEmpty())
3160 m_writer->writeAttribute("groups", qcn->groupNames().join(QLatin1Char(',')));
3161 } else if (node->isProperty()) {
3162 m_writer->writeTextElement(dbNamespace, "modifier", "(Qt property)");
3163 newLine();
3164 m_writer->writeTextElement(dbNamespace, "type", propertyNode->dataType());
3165 newLine();
3166 m_writer->writeTextElement(dbNamespace, "varname", node->plainName());
3167 newLine();
3168 } else if (node->isVariable()) {
3169 if (variableNode->isStatic()) {
3170 m_writer->writeTextElement(dbNamespace, "modifier", "static");
3171 newLine();
3172 }
3173 m_writer->writeTextElement(dbNamespace, "type", variableNode->dataType());
3174 newLine();
3175 m_writer->writeTextElement(dbNamespace, "varname", node->plainName());
3176 newLine();
3177 } else if (node->isEnumType()) {
3178 m_writer->writeTextElement(dbNamespace, "enumname", node->plainName());
3179 newLine();
3180 } else if (node->isQmlProperty()) {
3181 QString name = node->name();
3182 if (qpn->isAttached())
3183 name.prepend(qpn->element() + QLatin1Char('.'));
3184
3185 m_writer->writeTextElement(dbNamespace, "type", qpn->dataType());
3186 newLine();
3187 m_writer->writeTextElement(dbNamespace, "varname", name);
3188 newLine();
3189
3190 if (qpn->isAttached()) {
3191 m_writer->writeTextElement(dbNamespace, "modifier", "attached");
3192 newLine();
3193 }
3194 if (!(const_cast<QmlPropertyNode *>(qpn))->isReadOnly()) {
3195 m_writer->writeTextElement(dbNamespace, "modifier", "writable");
3196 newLine();
3197 }
3198 if ((const_cast<QmlPropertyNode *>(qpn))->isRequired()) {
3199 m_writer->writeTextElement(dbNamespace, "modifier", "required");
3200 newLine();
3201 }
3202 if (qpn->isReadOnly()) {
3203 generateModifier("[read-only]");
3204 newLine();
3205 }
3206 if (qpn->isDefault()) {
3207 generateModifier("[default]");
3208 newLine();
3209 }
3210 } else if (node->isFunction()) {
3211 if (functionNode->virtualness() != "non")
3212 generateModifier("virtual");
3213 if (functionNode->isConst())
3214 generateModifier("const");
3215 if (functionNode->isStatic())
3216 generateModifier("static");
3217
3218 if (!functionNode->isMacro() && !functionNode->isCtor() &&
3219 !functionNode->isCCtor() && !functionNode->isMCtor()
3220 && !functionNode->isDtor()) {
3221 if (functionNode->returnType() == "void")
3222 m_writer->writeEmptyElement(dbNamespace, "void");
3223 else
3224 m_writer->writeTextElement(dbNamespace, "type", functionNode->returnTypeString());
3225 newLine();
3226 }
3227 // Remove two characters from the plain name to only get the name
3228 // of the method without parentheses (only for functions, not macros).
3229 QString name = node->plainName();
3230 if (name.endsWith("()"))
3231 name.chop(2);
3232 m_writer->writeTextElement(dbNamespace, "methodname", name);
3233 newLine();
3234
3235 if (functionNode->parameters().isEmpty()) {
3236 m_writer->writeEmptyElement(dbNamespace, "void");
3237 newLine();
3238 }
3239
3240 const Parameters &lp = functionNode->parameters();
3241 for (int i = 0; i < lp.count(); ++i) {
3242 const Parameter &parameter = lp.at(i);
3243 m_writer->writeStartElement(dbNamespace, "methodparam");
3244 newLine();
3245 m_writer->writeTextElement(dbNamespace, "type", parameter.type());
3246 newLine();
3247 m_writer->writeTextElement(dbNamespace, "parameter", parameter.name());
3248 newLine();
3249 if (!parameter.defaultValue().isEmpty()) {
3250 m_writer->writeTextElement(dbNamespace, "initializer", parameter.defaultValue());
3251 newLine();
3252 }
3253 m_writer->writeEndElement(); // methodparam
3254 newLine();
3255 }
3256
3257 if (functionNode->isDefault())
3258 generateModifier("default");
3259 if (functionNode->isFinal())
3260 generateModifier("final");
3261 if (functionNode->isOverride())
3262 generateModifier("override");
3263 } else if (node->isTypedef()) {
3264 m_writer->writeTextElement(dbNamespace, "typedefname", node->plainName());
3265 newLine();
3266 } else {
3267 node->doc().location().warning(
3268 QStringLiteral("Unexpected node type in generateDocBookSynopsis: %1")
3269 .arg(node->nodeTypeString()));
3270 newLine();
3271 }
3272
3273 // Enums and typedefs.
3274 if (enumNode) {
3275 for (const EnumItem &item : enumNode->items()) {
3276 m_writer->writeStartElement(dbNamespace, "enumitem");
3277 newLine();
3278 m_writer->writeTextElement(dbNamespace, "enumidentifier", item.name());
3279 newLine();
3280 m_writer->writeTextElement(dbNamespace, "enumvalue", item.value());
3281 newLine();
3282 m_writer->writeEndElement(); // enumitem
3283 newLine();
3284 }
3285
3286 if (enumNode->items().isEmpty()) {
3287 // If the enumeration is empty (really rare case), still produce
3288 // something for the DocBook document to be valid.
3289 m_writer->writeStartElement(dbNamespace, "enumitem");
3290 newLine();
3291 m_writer->writeEmptyElement(dbNamespace, "enumidentifier");
3292 newLine();
3293 m_writer->writeEndElement(); // enumitem
3294 newLine();
3295 }
3296 }
3297
3298 // Below: only synopsisinfo within synopsisTag. These elements must be at
3299 // the end of the tag, as per DocBook grammar.
3300
3301 // Information for functions that could not be output previously
3302 // (synopsisinfo).
3303 if (node->isFunction()) {
3304 generateSynopsisInfo("meta", functionNode->metanessString());
3305
3306 if (functionNode->isOverload()) {
3307 generateSynopsisInfo("overload", "overload");
3308 generateSynopsisInfo("overload-number",
3309 QString::number(functionNode->overloadNumber()));
3310 }
3311
3312 if (functionNode->isRef())
3313 generateSynopsisInfo("refness", QString::number(1));
3314 else if (functionNode->isRefRef())
3315 generateSynopsisInfo("refness", QString::number(2));
3316
3317 if (functionNode->hasAssociatedProperties()) {
3318 QStringList associatedProperties;
3319 const auto &nodes = functionNode->associatedProperties();
3320 for (const Node *n : nodes) {
3321 const auto pn = static_cast<const PropertyNode *>(n);
3322 associatedProperties << pn->name();
3323 }
3324 associatedProperties.sort();
3325 generateSynopsisInfo("associated-property",
3326 associatedProperties.join(QLatin1Char(',')));
3327 }
3328
3329 QString signature = functionNode->signature(Node::SignatureReturnType);
3330 // 'const' is already part of FunctionNode::signature()
3331 if (functionNode->isFinal())
3332 signature += " final";
3333 if (functionNode->isOverride())
3334 signature += " override";
3335 if (functionNode->isPureVirtual())
3336 signature += " = 0";
3337 else if (functionNode->isDefault())
3338 signature += " = default";
3339 generateSynopsisInfo("signature", signature);
3340 }
3341
3342 // Accessibility status.
3343 if (!node->isPageNode() && !node->isCollectionNode()) {
3344 switch (node->access()) {
3345 case Access::Public:
3346 generateSynopsisInfo("access", "public");
3347 break;
3348 case Access::Protected:
3349 generateSynopsisInfo("access", "protected");
3350 break;
3351 case Access::Private:
3352 generateSynopsisInfo("access", "private");
3353 break;
3354 default:
3355 break;
3356 }
3357 if (node->isAbstract())
3358 generateSynopsisInfo("abstract", "true");
3359 }
3360
3361 // Status.
3362 switch (node->status()) {
3363 case Node::Active:
3364 generateSynopsisInfo("status", "active");
3365 break;
3366 case Node::Preliminary:
3367 generateSynopsisInfo("status", "preliminary");
3368 break;
3369 case Node::Deprecated:
3370 generateSynopsisInfo("status", "deprecated");
3371 break;
3372 case Node::Internal:
3373 generateSynopsisInfo("status", "internal");
3374 break;
3375 default:
3376 generateSynopsisInfo("status", "main");
3377 break;
3378 }
3379
3380 // C++ classes and name spaces.
3381 if (aggregate) {
3382 // Includes.
3383 if (aggregate->includeFile()) generateSynopsisInfo("headers", *aggregate->includeFile());
3384
3385 // Since and project.
3386 if (!aggregate->since().isEmpty())
3387 generateSynopsisInfo("since", formatSince(aggregate));
3388
3389 if (aggregate->nodeType() == Node::Class || aggregate->nodeType() == Node::Namespace) {
3390 // CMake and QT variable.
3391 if (!aggregate->physicalModuleName().isEmpty()) {
3392 const CollectionNode *cn =
3393 m_qdb->getCollectionNode(aggregate->physicalModuleName(), Node::Module);
3394
3395 if (const auto result = cmakeRequisite(cn)) {
3396 generateSynopsisInfo("cmake-find-package", result->first);
3397 generateSynopsisInfo("cmake-target-link-libraries", result->second);
3398 }
3399
3400 if (cn && !cn->qtVariable().isEmpty())
3401 generateSynopsisInfo("qmake", "QT += " + cn->qtVariable());
3402 }
3403 }
3404
3405 if (aggregate->nodeType() == Node::Class) {
3406 // Native type
3407 auto *classe = const_cast<ClassNode *>(static_cast<const ClassNode *>(aggregate));
3408 if (classe && classe->isQmlNativeType() && classe->status() != Node::Internal) {
3409 m_writer->writeStartElement(dbNamespace, "synopsisinfo");
3410 m_writer->writeAttribute("role", "nativeTypeFor");
3411
3412 QList<QmlTypeNode *> nativeTypes { classe->qmlNativeTypes().cbegin(), classe->qmlNativeTypes().cend()};
3413 std::sort(nativeTypes.begin(), nativeTypes.end(), Node::nodeNameLessThan);
3414
3415 for (auto item : std::as_const(nativeTypes)) {
3416 const Node *otherNode{nullptr};
3417 Atom a = Atom(Atom::LinkNode, CodeMarker::stringForNode(item));
3418 const QString &link = getAutoLink(&a, aggregate, &otherNode);
3419 generateSimpleLink(link, item->name());
3420 }
3421
3422 m_writer->writeEndElement(); // synopsisinfo
3423 }
3424
3425 // Inherits.
3426 QList<RelatedClass>::ConstIterator r;
3427 if (!classe->baseClasses().isEmpty()) {
3428 m_writer->writeStartElement(dbNamespace, "synopsisinfo");
3429 m_writer->writeAttribute("role", "inherits");
3430
3431 r = classe->baseClasses().constBegin();
3432 int index = 0;
3433 while (r != classe->baseClasses().constEnd()) {
3434 if ((*r).m_node) {
3435 generateFullName((*r).m_node, classe);
3436
3437 if ((*r).m_access == Access::Protected) {
3438 m_writer->writeCharacters(" (protected)");
3439 } else if ((*r).m_access == Access::Private) {
3440 m_writer->writeCharacters(" (private)");
3441 }
3442 m_writer->writeCharacters(
3443 Utilities::comma(index++, classe->baseClasses().size()));
3444 }
3445 ++r;
3446 }
3447
3448 m_writer->writeEndElement(); // synopsisinfo
3449 newLine();
3450 }
3451
3452 // Inherited by.
3453 if (!classe->derivedClasses().isEmpty()) {
3454 m_writer->writeStartElement(dbNamespace, "synopsisinfo");
3455 m_writer->writeAttribute("role", "inheritedBy");
3456 generateSortedNames(classe, classe->derivedClasses());
3457 m_writer->writeEndElement(); // synopsisinfo
3458 newLine();
3459 }
3460 }
3461 }
3462
3463 // QML types.
3464 if (qcn) {
3465 // Module name and version (i.e. import).
3466 QString logicalModuleVersion;
3467 const CollectionNode *collection =
3468 m_qdb->getCollectionNode(qcn->logicalModuleName(), qcn->nodeType());
3469 if (collection)
3470 logicalModuleVersion = collection->logicalModuleVersion();
3471 else
3472 logicalModuleVersion = qcn->logicalModuleVersion();
3473
3474 QStringList importText;
3475 importText << "import " + qcn->logicalModuleName();
3476 if (!logicalModuleVersion.isEmpty())
3477 importText << logicalModuleVersion;
3478 generateSynopsisInfo("import", importText.join(' '));
3479
3480 // Since and project.
3481 if (!qcn->since().isEmpty())
3482 generateSynopsisInfo("since", formatSince(qcn));
3483
3484 // Inherited by.
3485 NodeList subs;
3486 QmlTypeNode::subclasses(qcn, subs);
3487 if (!subs.isEmpty()) {
3488 m_writer->writeTextElement(dbNamespace, "synopsisinfo");
3489 m_writer->writeAttribute("role", "inheritedBy");
3490 generateSortedQmlNames(qcn, subs);
3491 m_writer->writeEndElement(); // synopsisinfo
3492 newLine();
3493 }
3494
3495 // Inherits.
3496 QmlTypeNode *base = qcn->qmlBaseNode();
3497 while (base && base->isInternal())
3498 base = base->qmlBaseNode();
3499 if (base) {
3500 const Node *otherNode = nullptr;
3501 Atom a = Atom(Atom::LinkNode, CodeMarker::stringForNode(base));
3502 QString link = getAutoLink(&a, base, &otherNode);
3503
3504 m_writer->writeTextElement(dbNamespace, "synopsisinfo");
3505 m_writer->writeAttribute("role", "inherits");
3506 generateSimpleLink(link, base->name());
3507 m_writer->writeEndElement(); // synopsisinfo
3508 newLine();
3509 }
3510
3511 // Native type
3512 ClassNode *cn = (const_cast<QmlTypeNode *>(qcn))->classNode();
3513
3514 if (cn && cn->isQmlNativeType() && (cn->status() != Node::Internal)) {
3515 const Node *otherNode = nullptr;
3516 Atom a = Atom(Atom::LinkNode, CodeMarker::stringForNode(qcn));
3517 QString link = getAutoLink(&a, cn, &otherNode);
3518
3519 m_writer->writeTextElement(dbNamespace, "synopsisinfo");
3520 m_writer->writeAttribute("role", "nativeType");
3521 generateSimpleLink(link, cn->name());
3522 m_writer->writeEndElement(); // synopsisinfo
3523 newLine();
3524 }
3525 }
3526
3527 // Thread safeness.
3528 switch (node->threadSafeness()) {
3529 case Node::UnspecifiedSafeness:
3530 generateSynopsisInfo("threadsafeness", "unspecified");
3531 break;
3532 case Node::NonReentrant:
3533 generateSynopsisInfo("threadsafeness", "non-reentrant");
3534 break;
3535 case Node::Reentrant:
3536 generateSynopsisInfo("threadsafeness", "reentrant");
3537 break;
3538 case Node::ThreadSafe:
3539 generateSynopsisInfo("threadsafeness", "thread safe");
3540 break;
3541 default:
3542 generateSynopsisInfo("threadsafeness", "unspecified");
3543 break;
3544 }
3545
3546 // Module.
3547 if (!node->physicalModuleName().isEmpty())
3548 generateSynopsisInfo("module", node->physicalModuleName());
3549
3550 // Group.
3551 if (classNode && !classNode->groupNames().isEmpty()) {
3552 generateSynopsisInfo("groups", classNode->groupNames().join(QLatin1Char(',')));
3553 } else if (qcn && !qcn->groupNames().isEmpty()) {
3554 generateSynopsisInfo("groups", qcn->groupNames().join(QLatin1Char(',')));
3555 }
3556
3557 // Properties.
3558 if (propertyNode) {
3559 for (const Node *fnNode : propertyNode->getters()) {
3560 if (fnNode) {
3561 const auto funcNode = static_cast<const FunctionNode *>(fnNode);
3562 generateSynopsisInfo("getter", funcNode->name());
3563 }
3564 }
3565 for (const Node *fnNode : propertyNode->setters()) {
3566 if (fnNode) {
3567 const auto funcNode = static_cast<const FunctionNode *>(fnNode);
3568 generateSynopsisInfo("setter", funcNode->name());
3569 }
3570 }
3571 for (const Node *fnNode : propertyNode->resetters()) {
3572 if (fnNode) {
3573 const auto funcNode = static_cast<const FunctionNode *>(fnNode);
3574 generateSynopsisInfo("resetter", funcNode->name());
3575 }
3576 }
3577 for (const Node *fnNode : propertyNode->notifiers()) {
3578 if (fnNode) {
3579 const auto funcNode = static_cast<const FunctionNode *>(fnNode);
3580 generateSynopsisInfo("notifier", funcNode->name());
3581 }
3582 }
3583 }
3584
3585 m_writer->writeEndElement(); // nodeToSynopsisTag (like classsynopsis)
3586 newLine();
3587
3588 // The typedef associated to this enum. It is output *after* the main tag,
3589 // i.e. it must be after the synopsisinfo.
3590 if (enumNode && enumNode->flagsType()) {
3591 m_writer->writeStartElement(dbNamespace, "typedefsynopsis");
3592 newLine();
3593
3594 m_writer->writeTextElement(dbNamespace, "typedefname",
3595 enumNode->flagsType()->fullDocumentName());
3596 newLine();
3597
3598 m_writer->writeEndElement(); // typedefsynopsis
3599 newLine();
3600 }
3601}
3602
3604{
3605 // From CodeMarker::taggedNode, but without the tag part (i.e. only the QML specific case
3606 // remaining).
3607 // TODO: find a better name for this.
3608 if (node->nodeType() == Node::QmlType && node->name().startsWith(QLatin1String("QML:")))
3609 return node->name().mid(4);
3610 return node->name();
3611}
3612
3613/*!
3614 Parses a string with method/variable name and (return) type
3615 to include type tags.
3616 */
3617void DocBookGenerator::typified(const QString &string, const Node *relative, bool trailingSpace,
3618 bool generateType)
3619{
3620 // Adapted from CodeMarker::typified and HtmlGenerator::highlightedCode.
3621 QString result;
3622 QString pendingWord;
3623
3624 for (int i = 0; i <= string.size(); ++i) {
3625 QChar ch;
3626 if (i != string.size())
3627 ch = string.at(i);
3628
3629 QChar lower = ch.toLower();
3630 if ((lower >= QLatin1Char('a') && lower <= QLatin1Char('z')) || ch.digitValue() >= 0
3631 || ch == QLatin1Char('_') || ch == QLatin1Char(':')) {
3632 pendingWord += ch;
3633 } else {
3634 if (!pendingWord.isEmpty()) {
3635 bool isProbablyType = (pendingWord != QLatin1String("const"));
3636 if (generateType && isProbablyType) {
3637 // Flush the current buffer.
3638 m_writer->writeCharacters(result);
3639 result.truncate(0);
3640
3641 // Add the link, logic from HtmlGenerator::highlightedCode.
3642 const Node *n = m_qdb->findTypeNode(pendingWord, relative, Node::DontCare);
3643 QString href;
3644 if (!(n && n->isQmlBasicType())
3645 || (relative
3646 && (relative->genus() == n->genus() || Node::DontCare == n->genus()))) {
3647 href = linkForNode(n, relative);
3648 }
3649
3650 m_writer->writeStartElement(dbNamespace, "type");
3651 if (href.isEmpty())
3652 m_writer->writeCharacters(pendingWord);
3653 else
3654 generateSimpleLink(href, pendingWord);
3655 m_writer->writeEndElement(); // type
3656 } else {
3657 result += pendingWord;
3658 }
3659 }
3660 pendingWord.clear();
3661
3662 if (ch.unicode() != '\0')
3663 result += ch;
3664 }
3665 }
3666
3667 if (trailingSpace && string.size()) {
3668 if (!string.endsWith(QLatin1Char('*')) && !string.endsWith(QLatin1Char('&')))
3669 result += QLatin1Char(' ');
3670 }
3671
3672 m_writer->writeCharacters(result);
3673}
3674
3675void DocBookGenerator::generateSynopsisName(const Node *node, const Node *relative,
3676 bool generateNameLink)
3677{
3678 // Implements the rewriting of <@link> from HtmlGenerator::highlightedCode, only due to calls to
3679 // CodeMarker::linkTag in CppCodeMarker::markedUpSynopsis.
3680 QString name = taggedNode(node);
3681
3682 if (!generateNameLink) {
3683 m_writer->writeCharacters(name);
3684 return;
3685 }
3686
3687 m_writer->writeStartElement(dbNamespace, "emphasis");
3688 m_writer->writeAttribute("role", "bold");
3689 generateSimpleLink(linkForNode(node, relative), name);
3690 m_writer->writeEndElement(); // emphasis
3691}
3692
3693void DocBookGenerator::generateParameter(const Parameter &parameter, const Node *relative,
3694 bool generateExtra, bool generateType)
3695{
3696 const QString &pname = parameter.name();
3697 const QString &ptype = parameter.type();
3698 QString paramName;
3699 if (!pname.isEmpty()) {
3700 typified(ptype, relative, true, generateType);
3701 paramName = pname;
3702 } else {
3703 paramName = ptype;
3704 }
3705
3706 if (generateExtra || pname.isEmpty()) {
3707 m_writer->writeStartElement(dbNamespace, "emphasis");
3708 m_writer->writeCharacters(paramName);
3709 m_writer->writeEndElement(); // emphasis
3710 }
3711
3712 const QString &pvalue = parameter.defaultValue();
3713 if (generateExtra && !pvalue.isEmpty())
3714 m_writer->writeCharacters(" = " + pvalue);
3715}
3716
3717void DocBookGenerator::generateSynopsis(const Node *node, const Node *relative,
3718 Section::Style style)
3719{
3720 // From HtmlGenerator::generateSynopsis (conditions written as booleans).
3721 const bool generateExtra = style != Section::AllMembers;
3722 const bool generateType = style != Section::Details;
3723 const bool generateNameLink = style != Section::Details;
3724
3725 // From CppCodeMarker::markedUpSynopsis, reversed the generation of "extra" and "synopsis".
3726 const int MaxEnumValues = 6;
3727
3728 if (generateExtra) {
3729 if (auto extra = CodeMarker::extraSynopsis(node, style); !extra.isEmpty())
3730 m_writer->writeCharacters(extra + " ");
3731 }
3732
3733 // Then generate the synopsis.
3734 QString namePrefix {};
3735 if (style == Section::Details) {
3736 if (!node->isRelatedNonmember() && !node->isProxyNode() && !node->parent()->name().isEmpty()
3737 && !node->parent()->isHeader() && !node->isProperty() && !node->isQmlNode()) {
3738 namePrefix = taggedNode(node->parent()) + "::";
3739 }
3740 }
3741
3742 switch (node->nodeType()) {
3743 case Node::Namespace:
3744 m_writer->writeCharacters("namespace ");
3745 m_writer->writeCharacters(namePrefix);
3746 generateSynopsisName(node, relative, generateNameLink);
3747 break;
3748 case Node::Class:
3749 m_writer->writeCharacters("class ");
3750 m_writer->writeCharacters(namePrefix);
3751 generateSynopsisName(node, relative, generateNameLink);
3752 break;
3753 case Node::Function: {
3754 const auto func = (const FunctionNode *)node;
3755
3756 // First, the part coming before the name.
3757 if (style == Section::Summary || style == Section::Accessors) {
3758 if (!func->isNonvirtual())
3759 m_writer->writeCharacters(QStringLiteral("virtual "));
3760 }
3761
3762 // Name and parameters.
3763 if (style != Section::AllMembers && !func->returnType().isEmpty())
3764 typified(func->returnTypeString(), relative, true, generateType);
3765 m_writer->writeCharacters(namePrefix);
3766 generateSynopsisName(node, relative, generateNameLink);
3767
3768 if (!func->isMacroWithoutParams()) {
3769 m_writer->writeCharacters(QStringLiteral("("));
3770 if (!func->parameters().isEmpty()) {
3771 const Parameters &parameters = func->parameters();
3772 for (int i = 0; i < parameters.count(); i++) {
3773 if (i > 0)
3774 m_writer->writeCharacters(QStringLiteral(", "));
3775 generateParameter(parameters.at(i), relative, generateExtra, generateType);
3776 }
3777 }
3778 m_writer->writeCharacters(QStringLiteral(")"));
3779 }
3780
3781 if (func->isConst())
3782 m_writer->writeCharacters(QStringLiteral(" const"));
3783
3784 if (style == Section::Summary || style == Section::Accessors) {
3785 // virtual is prepended, if needed.
3786 QString synopsis;
3787 if (func->isFinal())
3788 synopsis += QStringLiteral(" final");
3789 if (func->isOverride())
3790 synopsis += QStringLiteral(" override");
3791 if (func->isPureVirtual())
3792 synopsis += QStringLiteral(" = 0");
3793 if (func->isRef())
3794 synopsis += QStringLiteral(" &");
3795 else if (func->isRefRef())
3796 synopsis += QStringLiteral(" &&");
3797 m_writer->writeCharacters(synopsis);
3798 } else if (style == Section::AllMembers) {
3799 if (!func->returnType().isEmpty() && func->returnType() != "void") {
3800 m_writer->writeCharacters(QStringLiteral(" : "));
3801 typified(func->returnTypeString(), relative, false, generateType);
3802 }
3803 } else {
3804 QString synopsis;
3805 if (func->isRef())
3806 synopsis += QStringLiteral(" &");
3807 else if (func->isRefRef())
3808 synopsis += QStringLiteral(" &&");
3809 m_writer->writeCharacters(synopsis);
3810 }
3811 } break;
3812 case Node::Enum: {
3813 const auto enume = static_cast<const EnumNode *>(node);
3814 m_writer->writeCharacters(QStringLiteral("enum "));
3815 m_writer->writeCharacters(namePrefix);
3816 generateSynopsisName(node, relative, generateNameLink);
3817
3818 QString synopsis;
3819 if (style == Section::Summary) {
3820 synopsis += " { ";
3821
3822 QStringList documentedItems = enume->doc().enumItemNames();
3823 if (documentedItems.isEmpty()) {
3824 const auto &enumItems = enume->items();
3825 for (const auto &item : enumItems)
3826 documentedItems << item.name();
3827 }
3828 const QStringList omitItems = enume->doc().omitEnumItemNames();
3829 for (const auto &item : omitItems)
3830 documentedItems.removeAll(item);
3831
3832 if (documentedItems.size() > MaxEnumValues) {
3833 // Take the last element and keep it safe, then elide the surplus.
3834 const QString last = documentedItems.last();
3835 documentedItems = documentedItems.mid(0, MaxEnumValues - 1);
3836 documentedItems += "&#x2026;"; // Ellipsis: in HTML, &hellip;.
3837 documentedItems += last;
3838 }
3839 synopsis += documentedItems.join(QLatin1String(", "));
3840
3841 if (!documentedItems.isEmpty())
3842 synopsis += QLatin1Char(' ');
3843 synopsis += QLatin1Char('}');
3844 }
3845 m_writer->writeCharacters(synopsis);
3846 } break;
3847 case Node::TypeAlias: {
3848 if (style == Section::Details) {
3849 auto templateDecl = node->templateDecl();
3850 if (templateDecl)
3851 m_writer->writeCharacters((*templateDecl).to_qstring() + QLatin1Char(' '));
3852 }
3853 m_writer->writeCharacters(namePrefix);
3854 generateSynopsisName(node, relative, generateNameLink);
3855 } break;
3856 case Node::Typedef: {
3857 if (static_cast<const TypedefNode *>(node)->associatedEnum())
3858 m_writer->writeCharacters("flags ");
3859 m_writer->writeCharacters(namePrefix);
3860 generateSynopsisName(node, relative, generateNameLink);
3861 } break;
3862 case Node::Property: {
3863 const auto property = static_cast<const PropertyNode *>(node);
3864 m_writer->writeCharacters(namePrefix);
3865 generateSynopsisName(node, relative, generateNameLink);
3866 m_writer->writeCharacters(" : ");
3867 typified(property->qualifiedDataType(), relative, false, generateType);
3868 } break;
3869 case Node::Variable: {
3870 const auto variable = static_cast<const VariableNode *>(node);
3871 if (style == Section::AllMembers) {
3872 generateSynopsisName(node, relative, generateNameLink);
3873 m_writer->writeCharacters(" : ");
3874 typified(variable->dataType(), relative, false, generateType);
3875 } else {
3876 typified(variable->leftType(), relative, false, generateType);
3877 m_writer->writeCharacters(" ");
3878 m_writer->writeCharacters(namePrefix);
3879 generateSynopsisName(node, relative, generateNameLink);
3880 m_writer->writeCharacters(variable->rightType());
3881 }
3882 } break;
3883 default:
3884 m_writer->writeCharacters(namePrefix);
3885 generateSynopsisName(node, relative, generateNameLink);
3886 }
3887}
3888
3889void DocBookGenerator::generateEnumValue(const QString &enumValue, const Node *relative)
3890{
3891 // From CppCodeMarker::markedUpEnumValue, simplifications from Generator::plainCode (removing
3892 // <@op>). With respect to CppCodeMarker::markedUpEnumValue, the order of generation of parents
3893 // must be reversed so that they are processed in the order
3894 const auto *node = relative->parent();
3895
3896 if (relative->isQmlProperty()) {
3897 const auto *qpn = static_cast<const QmlPropertyNode*>(relative);
3898 if (qpn->enumNode() && !enumValue.startsWith("%1."_L1.arg(qpn->enumPrefix()))) {
3899 m_writer->writeCharacters("%1.%2"_L1.arg(qpn->enumPrefix(), enumValue));
3900 return;
3901 }
3902 }
3903
3904 if (!relative->isEnumType()) {
3905 m_writer->writeCharacters(enumValue);
3906 return;
3907 }
3908
3909 QList<const Node *> parents;
3910 while (!node->isHeader() && node->parent()) {
3911 parents.prepend(node);
3912 if (node->parent() == relative || node->parent()->name().isEmpty())
3913 break;
3914 node = node->parent();
3915 }
3916 if (static_cast<const EnumNode *>(relative)->isScoped())
3917 parents << relative;
3918
3919 m_writer->writeStartElement(dbNamespace, "code");
3920 for (auto parent : parents) {
3921 generateSynopsisName(parent, relative, true);
3922 m_writer->writeCharacters("::");
3923 }
3924
3925 m_writer->writeCharacters(enumValue);
3926 m_writer->writeEndElement(); // code
3927}
3928
3929/*!
3930 If the node is an overloaded signal, and a node with an
3931 example on how to connect to it
3932
3933 Someone didn't finish writing this comment, and I don't know what this
3934 function is supposed to do, so I have not tried to complete the comment
3935 yet.
3936 */
3937void DocBookGenerator::generateOverloadedSignal(const Node *node)
3938{
3939 // From Generator::generateOverloadedSignal.
3940 QString code = getOverloadedSignalCode(node);
3941 if (code.isEmpty())
3942 return;
3943
3944 m_writer->writeStartElement(dbNamespace, "note");
3945 newLine();
3946 m_writer->writeStartElement(dbNamespace, "para");
3947 m_writer->writeCharacters("Signal ");
3948 m_writer->writeTextElement(dbNamespace, "emphasis", node->name());
3949 m_writer->writeCharacters(" is overloaded in this class. To connect to this "
3950 "signal by using the function pointer syntax, Qt "
3951 "provides a convenient helper for obtaining the "
3952 "function pointer as shown in this example:");
3953 m_writer->writeTextElement(dbNamespace, "code", code);
3954 m_writer->writeEndElement(); // para
3955 newLine();
3956 m_writer->writeEndElement(); // note
3957 newLine();
3958}
3959
3960/*!
3961 Generates an addendum note of type \a type for \a node. \a marker
3962 is unused in this generator.
3963*/
3965 bool generateNote)
3966{
3967 Q_UNUSED(marker)
3968 Q_ASSERT(node && !node->name().isEmpty());
3969 if (generateNote) {
3970 m_writer->writeStartElement(dbNamespace, "note");
3971 newLine();
3972 }
3973 switch (type) {
3974 case Invokable:
3975 m_writer->writeStartElement(dbNamespace, "para");
3976 m_writer->writeCharacters(
3977 "This function can be invoked via the meta-object system and from QML. See ");
3978 generateSimpleLink(node->url(), "Q_INVOKABLE");
3979 m_writer->writeCharacters(".");
3980 m_writer->writeEndElement(); // para
3981 newLine();
3982 break;
3983 case PrivateSignal:
3984 m_writer->writeTextElement(
3985 dbNamespace, "para",
3986 "This is a private signal. It can be used in signal connections but "
3987 "cannot be emitted by the user.");
3988 break;
3989 case QmlSignalHandler:
3990 {
3991 QString handler(node->name());
3992 int prefixLocation = handler.lastIndexOf('.', -2) + 1;
3993 handler[prefixLocation] = handler[prefixLocation].toTitleCase();
3994 handler.insert(prefixLocation, QLatin1String("on"));
3995 m_writer->writeStartElement(dbNamespace, "para");
3996 m_writer->writeCharacters("The corresponding handler is ");
3997 m_writer->writeTextElement(dbNamespace, "code", handler);
3998 m_writer->writeCharacters(".");
3999 m_writer->writeEndElement(); // para
4000 newLine();
4001 break;
4002 }
4004 {
4005 if (!node->isFunction())
4006 return;
4007 const auto *fn = static_cast<const FunctionNode *>(node);
4008 auto propertyNodes = fn->associatedProperties();
4009 if (propertyNodes.isEmpty())
4010 return;
4011 std::sort(propertyNodes.begin(), propertyNodes.end(), Node::nodeNameLessThan);
4012 for (const auto propertyNode : std::as_const(propertyNodes)) {
4013 QString msg;
4014 const auto pn = static_cast<const PropertyNode *>(propertyNode);
4015 switch (pn->role(fn)) {
4016 case PropertyNode::FunctionRole::Getter:
4017 msg = QStringLiteral("Getter function");
4018 break;
4019 case PropertyNode::FunctionRole::Setter:
4020 msg = QStringLiteral("Setter function");
4021 break;
4022 case PropertyNode::FunctionRole::Resetter:
4023 msg = QStringLiteral("Resetter function");
4024 break;
4025 case PropertyNode::FunctionRole::Notifier:
4026 msg = QStringLiteral("Notifier signal");
4027 break;
4028 default:
4029 continue;
4030 }
4031 m_writer->writeStartElement(dbNamespace, "para");
4032 m_writer->writeCharacters(msg + " for property ");
4033 generateSimpleLink(linkForNode(pn, nullptr), pn->name());
4034 m_writer->writeCharacters(". ");
4035 m_writer->writeEndElement(); // para
4036 newLine();
4037 }
4038 break;
4039 }
4040 case BindableProperty:
4041 {
4042 const Node *linkNode;
4043 Atom linkAtom = Atom(Atom::Link, "QProperty");
4044 QString link = getAutoLink(&linkAtom, node, &linkNode);
4045 m_writer->writeStartElement(dbNamespace, "para");
4046 m_writer->writeCharacters("This property supports ");
4047 generateSimpleLink(link, "QProperty");
4048 m_writer->writeCharacters(" bindings.");
4049 m_writer->writeEndElement(); // para
4050 newLine();
4051 break;
4052 }
4053 default:
4054 break;
4055 }
4056
4057 if (generateNote) {
4058 m_writer->writeEndElement(); // note
4059 newLine();
4060 }
4061}
4062
4063void DocBookGenerator::generateDetailedMember(const Node *node, const PageNode *relative)
4064{
4065 // From HtmlGenerator::generateDetailedMember.
4066 bool closeSupplementarySection = false;
4067
4068 if (node->isSharedCommentNode()) {
4069 const auto *scn = reinterpret_cast<const SharedCommentNode *>(node);
4070 const QList<Node *> &collective = scn->collective();
4071
4072 bool firstFunction = true;
4073 for (const auto *sharedNode : collective) {
4074 if (firstFunction) {
4075 startSectionBegin(sharedNode);
4076 } else {
4077 m_writer->writeStartElement(dbNamespace, "bridgehead");
4078 m_writer->writeAttribute("renderas", "sect2");
4079 writeXmlId(sharedNode);
4080 }
4081 if (m_useITS)
4082 m_writer->writeAttribute(itsNamespace, "translate", "no");
4083
4084 generateSynopsis(sharedNode, relative, Section::Details);
4085
4086 if (firstFunction) {
4087 startSectionEnd();
4088 firstFunction = false;
4089 } else {
4090 m_writer->writeEndElement(); // bridgehead
4091 newLine();
4092 }
4093 }
4094 } else {
4095 const EnumNode *etn;
4096 if (node->isEnumType() && (etn = static_cast<const EnumNode *>(node))->flagsType()) {
4097 startSectionBegin(node);
4098 if (m_useITS)
4099 m_writer->writeAttribute(itsNamespace, "translate", "no");
4100 generateSynopsis(etn, relative, Section::Details);
4101 startSectionEnd();
4102
4103 m_writer->writeStartElement(dbNamespace, "bridgehead");
4104 m_writer->writeAttribute("renderas", "sect2");
4105 generateSynopsis(etn->flagsType(), relative, Section::Details);
4106 m_writer->writeEndElement(); // bridgehead
4107 newLine();
4108 } else {
4109 startSectionBegin(node);
4110 if (m_useITS)
4111 m_writer->writeAttribute(itsNamespace, "translate", "no");
4112 generateSynopsis(node, relative, Section::Details);
4113 startSectionEnd();
4114 }
4115 }
4116 Q_ASSERT(m_hasSection);
4117
4119
4120 generateStatus(node);
4121 generateBody(node);
4122
4123 // If the body ends with a section, the rest of the description must be wrapped in a section too.
4125 closeSupplementarySection = true;
4126 startSection("", "Notes");
4127 }
4128
4129 generateOverloadedSignal(node);
4132 generateSince(node);
4133
4134 if (node->isProperty()) {
4135 const auto property = static_cast<const PropertyNode *>(node);
4137 Section section("", "", "", "", Section::Accessors);
4138
4139 section.appendMembers(property->getters().toVector());
4140 section.appendMembers(property->setters().toVector());
4141 section.appendMembers(property->resetters().toVector());
4142
4143 if (!section.members().isEmpty()) {
4144 m_writer->writeStartElement(dbNamespace, "para");
4145 newLine();
4146 m_writer->writeStartElement(dbNamespace, "emphasis");
4147 m_writer->writeAttribute("role", "bold");
4148 m_writer->writeCharacters("Access functions:");
4149 newLine();
4150 m_writer->writeEndElement(); // emphasis
4151 newLine();
4152 m_writer->writeEndElement(); // para
4153 newLine();
4154 generateSectionList(section, node);
4155 }
4156
4157 Section notifiers("", "", "", "", Section::Accessors);
4158 notifiers.appendMembers(property->notifiers().toVector());
4159
4160 if (!notifiers.members().isEmpty()) {
4161 m_writer->writeStartElement(dbNamespace, "para");
4162 newLine();
4163 m_writer->writeStartElement(dbNamespace, "emphasis");
4164 m_writer->writeAttribute("role", "bold");
4165 m_writer->writeCharacters("Notifier signal:");
4166 newLine();
4167 m_writer->writeEndElement(); // emphasis
4168 newLine();
4169 m_writer->writeEndElement(); // para
4170 newLine();
4171 generateSectionList(notifiers, node);
4172 }
4173 }
4174 } else if (node->isEnumType()) {
4175 const auto en = static_cast<const EnumNode *>(node);
4176
4177 if (m_qflagsHref.isEmpty()) {
4178 Node *qflags = m_qdb->findClassNode(QStringList("QFlags"));
4179 if (qflags)
4180 m_qflagsHref = linkForNode(qflags, nullptr);
4181 }
4182
4183 if (en->flagsType()) {
4184 m_writer->writeStartElement(dbNamespace, "para");
4185 m_writer->writeCharacters("The ");
4186 m_writer->writeStartElement(dbNamespace, "code");
4187 m_writer->writeCharacters(en->flagsType()->name());
4188 m_writer->writeEndElement(); // code
4189 m_writer->writeCharacters(" type is a typedef for ");
4190 m_writer->writeStartElement(dbNamespace, "code");
4191 generateSimpleLink(m_qflagsHref, "QFlags");
4192 m_writer->writeCharacters("<" + en->name() + ">. ");
4193 m_writer->writeEndElement(); // code
4194 m_writer->writeCharacters("It stores an OR combination of ");
4195 m_writer->writeStartElement(dbNamespace, "code");
4196 m_writer->writeCharacters(en->name());
4197 m_writer->writeEndElement(); // code
4198 m_writer->writeCharacters(" values.");
4199 m_writer->writeEndElement(); // para
4200 newLine();
4201 }
4202 }
4203
4204 if (closeSupplementarySection)
4205 endSection();
4206
4207 // The list of linked pages is always in its own section.
4209
4210 // Close the section for this member.
4211 endSection(); // section
4212}
4213
4214void DocBookGenerator::generateSectionList(const Section &section, const Node *relative,
4215 bool useObsoleteMembers)
4216{
4217 // From HtmlGenerator::generateSectionList, just generating a list (not tables).
4218 const NodeVector &members =
4219 (useObsoleteMembers ? section.obsoleteMembers() : section.members());
4220 if (!members.isEmpty()) {
4221 bool hasPrivateSignals = false;
4222 bool isInvokable = false;
4223
4224 m_writer->writeStartElement(dbNamespace, "itemizedlist");
4225 if (m_useITS)
4226 m_writer->writeAttribute(itsNamespace, "translate", "no");
4227 newLine();
4228
4229 NodeVector::ConstIterator m = members.constBegin();
4230 while (m != members.constEnd()) {
4231 if ((*m)->access() == Access::Private) {
4232 ++m;
4233 continue;
4234 }
4235
4236 m_writer->writeStartElement(dbNamespace, "listitem");
4237 newLine();
4238 m_writer->writeStartElement(dbNamespace, "para");
4239
4240 // prefix no more needed.
4241 generateSynopsis(*m, relative, section.style());
4242 if ((*m)->isFunction()) {
4243 const auto fn = static_cast<const FunctionNode *>(*m);
4244 if (fn->isPrivateSignal())
4245 hasPrivateSignals = true;
4246 else if (fn->isInvokable())
4247 isInvokable = true;
4248 }
4249
4250 m_writer->writeEndElement(); // para
4251 newLine();
4252 m_writer->writeEndElement(); // listitem
4253 newLine();
4254
4255 ++m;
4256 }
4257
4258 m_writer->writeEndElement(); // itemizedlist
4259 newLine();
4260
4261 if (hasPrivateSignals)
4263 if (isInvokable)
4264 generateAddendum(relative, Generator::Invokable, nullptr, true);
4265 }
4266
4267 if (!useObsoleteMembers && section.style() == Section::Summary
4268 && !section.inheritedMembers().isEmpty()) {
4269 m_writer->writeStartElement(dbNamespace, "itemizedlist");
4270 if (m_useITS)
4271 m_writer->writeAttribute(itsNamespace, "translate", "no");
4272 newLine();
4273
4274 generateSectionInheritedList(section, relative);
4275
4276 m_writer->writeEndElement(); // itemizedlist
4277 newLine();
4278 }
4279}
4280
4281void DocBookGenerator::generateSectionInheritedList(const Section &section, const Node *relative)
4282{
4283 // From HtmlGenerator::generateSectionInheritedList.
4284 QList<std::pair<Aggregate *, int>>::ConstIterator p = section.inheritedMembers().constBegin();
4285 while (p != section.inheritedMembers().constEnd()) {
4286 m_writer->writeStartElement(dbNamespace, "listitem");
4287 m_writer->writeCharacters(QString::number((*p).second) + u' ');
4288 if ((*p).second == 1)
4289 m_writer->writeCharacters(section.singular());
4290 else
4291 m_writer->writeCharacters(section.plural());
4292 m_writer->writeCharacters(" inherited from ");
4293 generateSimpleLink(fileName((*p).first) + '#'
4294 + Generator::cleanRef(section.title().toLower()),
4295 (*p).first->plainFullName(relative));
4296 ++p;
4297 }
4298}
4299
4300/*!
4301 Generate the DocBook page for an entity that doesn't map
4302 to any underlying parsable C++ or QML element.
4303 */
4305{
4306 // From HtmlGenerator::generatePageNode, remove anything related to TOCs.
4307 Q_ASSERT(m_writer == nullptr);
4308 m_writer = startDocument(pn);
4309
4310 generateHeader(pn->fullTitle(), pn->subtitle(), pn);
4311 generateBody(pn);
4312 generateAlsoList(pn);
4314
4315 endDocument();
4316}
4317
4318/*!
4319 Generate the DocBook page for a QML type. \qcn is the QML type.
4320 */
4322{
4323 // From HtmlGenerator::generateQmlTypePage.
4324 // Start producing the DocBook file.
4325 Q_ASSERT(m_writer == nullptr);
4326 m_writer = startDocument(qcn);
4327
4329 QString title = qcn->fullTitle();
4330 if (qcn->isQmlBasicType())
4331 title.append(" QML Value Type");
4332 else
4333 title.append(" QML Type");
4334 // TODO: for ITS attribute, only apply translate="no" on qcn->fullTitle(),
4335 // not its suffix (which should be translated). generateHeader doesn't
4336 // allow this kind of input, the title isn't supposed to be structured.
4337 // Ideally, do the same in HTML.
4338
4339 generateHeader(title, qcn->subtitle(), qcn);
4341 generateStatus(qcn);
4342
4343 startSection("details", "Detailed Description");
4344 generateBody(qcn);
4345
4346 generateAlsoList(qcn);
4347
4348 endSection();
4349
4350 Sections sections(qcn);
4351 for (const auto &section : sections.stdQmlTypeDetailsSections()) {
4352 if (!section.isEmpty()) {
4353 startSection(section.title().toLower(), section.title());
4354
4355 for (const auto &member : section.members())
4356 generateDetailedQmlMember(member, qcn);
4357
4358 endSection();
4359 }
4360 }
4361
4362 generateObsoleteQmlMembers(sections);
4363
4366
4367 endDocument();
4368}
4369
4370/*!
4371 Outputs the DocBook detailed documentation for a section
4372 on a QML element reference page.
4373 */
4374void DocBookGenerator::generateDetailedQmlMember(Node *node, const Aggregate *relative)
4375{
4376 // From HtmlGenerator::generateDetailedQmlMember, with elements from
4377 // CppCodeMarker::markedUpQmlItem and HtmlGenerator::generateQmlItem.
4378 auto getQmlPropertyTitle = [&](QmlPropertyNode *n) {
4379 QString title{CodeMarker::extraSynopsis(n, Section::Details)};
4380 if (!title.isEmpty())
4381 title += ' '_L1;
4382 // Finalise generation of name, as per CppCodeMarker::markedUpQmlItem.
4383 if (n->isAttached())
4384 title += n->element() + QLatin1Char('.');
4385 title += n->name() + " : " + n->dataType();
4386
4387 return title;
4388 };
4389
4390 auto generateQmlMethodTitle = [&](Node *node) {
4391 generateSynopsis(node, relative, Section::Details);
4392 };
4393
4394 if (node->isPropertyGroup()) {
4395 const auto *scn = static_cast<const SharedCommentNode *>(node);
4396
4397 QString heading;
4398 if (!scn->name().isEmpty())
4399 heading = scn->name() + " group";
4400 else
4401 heading = node->name();
4402 startSection(scn, heading);
4403 // This last call creates a title for this section. In other words,
4404 // titles are forbidden for the rest of the section, hence the use of
4405 // bridgehead.
4406
4407 const QList<Node *> sharedNodes = scn->collective();
4408 for (const auto &sharedNode : sharedNodes) {
4409 if (sharedNode->isQmlProperty()) {
4410 auto *qpn = static_cast<QmlPropertyNode *>(sharedNode);
4411
4412 m_writer->writeStartElement(dbNamespace, "bridgehead");
4413 m_writer->writeAttribute("renderas", "sect2");
4414 writeXmlId(qpn);
4415 m_writer->writeCharacters(getQmlPropertyTitle(qpn));
4416 m_writer->writeEndElement(); // bridgehead
4417 newLine();
4418
4419 generateDocBookSynopsis(qpn);
4420 }
4421 }
4422 } else if (node->isQmlProperty()) {
4423 auto qpn = static_cast<QmlPropertyNode *>(node);
4424 startSection(qpn, getQmlPropertyTitle(qpn));
4426 } else if (node->isSharedCommentNode()) {
4427 const auto scn = reinterpret_cast<const SharedCommentNode *>(node);
4428 const QList<Node *> &sharedNodes = scn->collective();
4429
4430 // In the section, generate a title for the first node, then bridgeheads for
4431 // the next ones.
4432 int i = 0;
4433 for (const auto &sharedNode : sharedNodes) {
4434 // Ignore this element if there is nothing to generate.
4435 if (!sharedNode->isFunction(Node::QML) && !sharedNode->isQmlProperty()) {
4436 continue;
4437 }
4438
4439 // Write the tag containing the title.
4440 if (i == 0) {
4441 startSectionBegin(sharedNode);
4442 } else {
4443 m_writer->writeStartElement(dbNamespace, "bridgehead");
4444 m_writer->writeAttribute("renderas", "sect2");
4445 }
4446
4447 // Write the title.
4448 if (sharedNode->isFunction(Node::QML))
4449 generateQmlMethodTitle(sharedNode);
4450 else if (sharedNode->isQmlProperty())
4451 m_writer->writeCharacters(
4452 getQmlPropertyTitle(static_cast<QmlPropertyNode *>(sharedNode)));
4453
4454 // Complete the title and the synopsis.
4455 if (i == 0)
4456 startSectionEnd();
4457 else
4458 m_writer->writeEndElement(); // bridgehead
4459 generateDocBookSynopsis(sharedNode);
4460 ++i;
4461 }
4462
4463 // If the list is empty, still generate a section.
4464 if (i == 0) {
4465 startSectionBegin(refForNode(node));
4466
4468 generateQmlMethodTitle(node);
4469 else if (node->isQmlProperty())
4470 m_writer->writeCharacters(
4471 getQmlPropertyTitle(static_cast<QmlPropertyNode *>(node)));
4472
4473 startSectionEnd();
4474 }
4475 } else { // assume the node is a method/signal handler
4476 startSectionBegin(node);
4477 generateQmlMethodTitle(node);
4478 startSectionEnd();
4479 }
4480
4481 generateStatus(node);
4482 generateBody(node);
4484 generateSince(node);
4486
4487 endSection();
4488}
4489
4490/*!
4491 Recursive writing of DocBook files from the root \a node.
4492 */
4494{
4495 // Mainly from Generator::generateDocumentation, with parts from
4496 // Generator::generateDocumentation and WebXMLGenerator::generateDocumentation.
4497 // Don't generate nodes that are already processed, or if they're not
4498 // supposed to generate output, ie. external, index or images nodes.
4499 if (!node->url().isNull())
4500 return;
4501 if (node->isIndexNode())
4502 return;
4503 if (node->isInternal() && !m_showInternal)
4504 return;
4505 if (node->isExternalPage())
4506 return;
4507
4508 if (node->parent()) {
4509 if (node->isCollectionNode()) {
4510 /*
4511 A collection node collects: groups, C++ modules, or QML
4512 modules. Testing for a CollectionNode must be done
4513 before testing for a TextPageNode because a
4514 CollectionNode is a PageNode at this point.
4515
4516 Don't output an HTML page for the collection node unless
4517 the \group, \module, or \qmlmodule command was actually
4518 seen by qdoc in the qdoc comment for the node.
4519
4520 A key prerequisite in this case is the call to
4521 mergeCollections(cn). We must determine whether this
4522 group, module, or QML module has members in other
4523 modules. We know at this point that cn's members list
4524 contains only members in the current module. Therefore,
4525 before outputting the page for cn, we must search for
4526 members of cn in the other modules and add them to the
4527 members list.
4528 */
4529 auto cn = static_cast<CollectionNode *>(node);
4530 if (cn->wasSeen()) {
4533 } else if (cn->isGenericCollection()) {
4534 // Currently used only for the module's related orphans page
4535 // but can be generalized for other kinds of collections if
4536 // other use cases pop up.
4538 }
4539 } else if (node->isTextPageNode()) { // Pages.
4540 generatePageNode(static_cast<PageNode *>(node));
4541 } else if (node->isAggregate()) { // Aggregates.
4542 if ((node->isClassNode() || node->isHeader() || node->isNamespace())
4543 && node->docMustBeGenerated()) {
4544 generateCppReferencePage(static_cast<Aggregate *>(node));
4545 } else if (node->isQmlType()) { // Includes QML value types
4546 generateQmlTypePage(static_cast<QmlTypeNode *>(node));
4547 } else if (node->isProxyNode()) {
4548 generateProxyPage(static_cast<Aggregate *>(node));
4549 }
4550 }
4551 }
4552
4553 if (node->isAggregate()) {
4554 auto *aggregate = static_cast<Aggregate *>(node);
4555 for (auto c : aggregate->childNodes()) {
4556 if (node->isPageNode() && !node->isPrivate())
4557 generateDocumentation(c);
4558 }
4559 }
4560}
4561
4563{
4564 // Adapted from HtmlGenerator::generateProxyPage.
4565 Q_ASSERT(aggregate->isProxyNode());
4566
4567 // Start producing the DocBook file.
4568 Q_ASSERT(m_writer == nullptr);
4569 m_writer = startDocument(aggregate);
4570
4571 // Info container.
4572 generateHeader(aggregate->plainFullName(), "", aggregate);
4573
4574 // No element synopsis.
4575
4576 // Actual content.
4577 if (!aggregate->doc().isEmpty()) {
4578 startSection("details", "Detailed Description");
4579
4580 generateBody(aggregate);
4581 generateAlsoList(aggregate);
4582
4583 endSection();
4584 }
4585
4586 Sections sections(aggregate);
4587 SectionVector *detailsSections = &sections.stdDetailsSections();
4588
4589 for (const auto &section : std::as_const(*detailsSections)) {
4590 if (section.isEmpty())
4591 continue;
4592
4593 startSection(section.title().toLower(), section.title());
4594
4595 const QList<Node *> &members = section.members();
4596 for (const auto &member : members) {
4597 if (!member->isPrivate()) { // ### check necessary?
4598 if (!member->isClassNode()) {
4599 generateDetailedMember(member, aggregate);
4600 } else {
4601 startSectionBegin();
4602 generateFullName(member, aggregate);
4603 startSectionEnd();
4604
4605 generateBrief(member);
4606 endSection();
4607 }
4608 }
4609 }
4610
4611 endSection();
4612 }
4613
4615
4616 endDocument();
4617}
4618
4619/*!
4620 Generate the HTML page for a group, module, or QML module.
4621 */
4623{
4624 // Adapted from HtmlGenerator::generateCollectionNode.
4625 // Start producing the DocBook file.
4626 Q_ASSERT(m_writer == nullptr);
4627 m_writer = startDocument(cn);
4628
4629 // Info container.
4630 generateHeader(cn->fullTitle(), cn->subtitle(), cn);
4631
4632 // Element synopsis.
4634
4635 // Generate brief for C++ modules, status for all modules.
4636 if (cn->genus() != Node::DOC && cn->genus() != Node::DontCare) {
4637 if (cn->isModule())
4638 generateBrief(cn);
4639 generateStatus(cn);
4640 generateSince(cn);
4641 }
4642
4643 // Actual content.
4644 if (cn->isModule()) {
4645 if (!cn->noAutoList()) {
4647 if (!nmm.isEmpty()) {
4648 startSection("namespaces", "Namespaces");
4649 generateAnnotatedList(cn, nmm.values(), "namespaces");
4650 endSection();
4651 }
4652 nmm = cn->getMembers([](const Node *n){ return n->isClassNode(); });
4653 if (!nmm.isEmpty()) {
4654 startSection("classes", "Classes");
4655 generateAnnotatedList(cn, nmm.values(), "classes");
4656 endSection();
4657 }
4658 }
4659 }
4660
4661 bool generatedTitle = false;
4662 if (cn->isModule() && !cn->doc().briefText().isEmpty()) {
4663 startSection("details", "Detailed Description");
4664 generatedTitle = true;
4665 }
4666 // The anchor is only needed if the node has a body.
4667 else if (
4668 // generateBody generates something.
4669 !cn->doc().body().isEmpty() ||
4670 // generateAlsoList generates something.
4671 !cn->doc().alsoList().empty() ||
4672 // generateAnnotatedList generates something.
4673 (!cn->noAutoList() && (cn->isGroup() || cn->isQmlModule()))) {
4674 writeAnchor("details");
4675 }
4676
4677 generateBody(cn);
4678 generateAlsoList(cn);
4679
4680 if (!cn->noAutoList() && (cn->isGroup() || cn->isQmlModule()))
4681 generateAnnotatedList(cn, cn->members(), "members", AutoSection);
4682
4683 if (generatedTitle)
4684 endSection();
4685
4687
4688 endDocument();
4689}
4690
4691/*!
4692 Generate the HTML page for a generic collection. This is usually
4693 a collection of C++ elements that are related to an element in
4694 a different module.
4695 */
4697{
4698 // Adapted from HtmlGenerator::generateGenericCollectionPage.
4699 // TODO: factor out this code to generate a file name.
4700 QString name = cn->name().toLower();
4701 name.replace(QChar(' '), QString("-"));
4702 QString filename = cn->tree()->physicalModuleName() + "-" + name + "." + fileExtension();
4703
4704 // Start producing the DocBook file.
4705 Q_ASSERT(m_writer == nullptr);
4706 m_writer = startGenericDocument(cn, filename);
4707
4708 // Info container.
4709 generateHeader(cn->fullTitle(), cn->subtitle(), cn);
4710
4711 // Element synopsis.
4713
4714 // Actual content.
4715 m_writer->writeStartElement(dbNamespace, "para");
4716 m_writer->writeCharacters("Each function or type documented here is related to a class or "
4717 "namespace that is documented in a different module. The reference "
4718 "page for that class or namespace will link to the function or type "
4719 "on this page.");
4720 m_writer->writeEndElement(); // para
4721
4722 const CollectionNode *cnc = cn;
4723 const QList<Node *> members = cn->members();
4724 for (const auto &member : members)
4725 generateDetailedMember(member, cnc);
4726
4728
4729 endDocument();
4730}
4731
4732void DocBookGenerator::generateFullName(const Node *node, const Node *relative)
4733{
4734 Q_ASSERT(node);
4735 Q_ASSERT(relative);
4736
4737 // From Generator::appendFullName.
4738 m_writer->writeStartElement(dbNamespace, "link");
4739 m_writer->writeAttribute(xlinkNamespace, "href", fullDocumentLocation(node));
4740 m_writer->writeAttribute(xlinkNamespace, "role", targetType(node));
4741 m_writer->writeCharacters(node->fullName(relative));
4742 m_writer->writeEndElement(); // link
4743}
4744
4745void DocBookGenerator::generateFullName(const Node *apparentNode, const QString &fullName,
4746 const Node *actualNode)
4747{
4748 Q_ASSERT(apparentNode);
4749 Q_ASSERT(actualNode);
4750
4751 // From Generator::appendFullName.
4752 m_writer->writeStartElement(dbNamespace, "link");
4753 m_writer->writeAttribute(xlinkNamespace, "href", fullDocumentLocation(actualNode));
4754 m_writer->writeAttribute("role", targetType(actualNode));
4755 m_writer->writeCharacters(fullName);
4756 m_writer->writeEndElement(); // link
4757}
4758
4759QT_END_NAMESPACE
#define ATOM_LIST_BULLET
Definition atom.h:212
#define ATOM_FORMATTING_TELETYPE
Definition atom.h:207
#define ATOM_LIST_LOWERALPHA
Definition atom.h:215
#define ATOM_FORMATTING_UNDERLINE
Definition atom.h:210
#define ATOM_LIST_UPPERALPHA
Definition atom.h:218
#define ATOM_LIST_TAG
Definition atom.h:213
#define ATOM_LIST_LOWERROMAN
Definition atom.h:216
#define ATOM_FORMATTING_SUBSCRIPT
Definition atom.h:205
#define ATOM_FORMATTING_BOLD
Definition atom.h:199
#define ATOM_FORMATTING_TRADEMARK
Definition atom.h:208
#define ATOM_LIST_VALUE
Definition atom.h:214
#define ATOM_FORMATTING_ITALIC
Definition atom.h:201
#define ATOM_LIST_UPPERROMAN
Definition atom.h:219
#define ATOM_FORMATTING_LINK
Definition atom.h:202
#define ATOM_FORMATTING_SUPERSCRIPT
Definition atom.h:206
#define ATOM_FORMATTING_UICONTROL
Definition atom.h:209
#define ATOM_FORMATTING_PARAMETER
Definition atom.h:203
The Atom class is the fundamental unit for representing documents internally.
Definition atom.h:18
AtomType type() const
Return the type of this atom.
Definition atom.h:149
@ CaptionLeft
Definition atom.h:28
@ ListTagLeft
Definition atom.h:64
@ DivRight
Definition atom.h:39
@ GeneratedList
Definition atom.h:49
@ BriefRight
Definition atom.h:26
@ CodeQuoteArgument
Definition atom.h:32
@ WarningLeft
Definition atom.h:104
@ SinceList
Definition atom.h:86
@ Keyword
Definition atom.h:56
@ TableHeaderRight
Definition atom.h:96
@ FormatElse
Definition atom.h:44
@ InlineImage
Definition atom.h:55
@ TableRowRight
Definition atom.h:98
@ LineBreak
Definition atom.h:59
@ SnippetCommand
Definition atom.h:89
@ TableRowLeft
Definition atom.h:97
@ Nop
Definition atom.h:71
@ LegaleseRight
Definition atom.h:58
@ ListTagRight
Definition atom.h:65
@ CaptionRight
Definition atom.h:29
@ NavLink
Definition atom.h:70
@ ListItemNumber
Definition atom.h:63
@ SinceTagRight
Definition atom.h:88
@ DetailsLeft
Definition atom.h:36
@ RawString
Definition atom.h:79
@ Target
Definition atom.h:102
@ AnnotatedList
Definition atom.h:21
@ SectionRight
Definition atom.h:81
@ SectionHeadingLeft
Definition atom.h:82
@ TableLeft
Definition atom.h:93
@ ListItemRight
Definition atom.h:67
@ Image
Definition atom.h:51
@ ListItemLeft
Definition atom.h:66
@ String
Definition atom.h:92
@ ListLeft
Definition atom.h:62
@ NavAutoLink
Definition atom.h:69
@ CodeQuoteCommand
Definition atom.h:33
@ BriefLeft
Definition atom.h:25
@ ImageText
Definition atom.h:52
@ LegaleseLeft
Definition atom.h:57
@ ParaRight
Definition atom.h:75
@ FormattingLeft
Definition atom.h:47
@ FormattingRight
Definition atom.h:48
@ SectionHeadingRight
Definition atom.h:83
@ Link
Definition atom.h:60
@ ImportantLeft
Definition atom.h:53
@ FormatEndif
Definition atom.h:45
@ BR
Definition atom.h:24
@ DetailsRight
Definition atom.h:37
@ AutoLink
Definition atom.h:22
@ SnippetLocation
Definition atom.h:91
@ TableHeaderLeft
Definition atom.h:95
@ ComparesRight
Definition atom.h:35
@ SectionLeft
Definition atom.h:80
@ LinkNode
Definition atom.h:61
@ HR
Definition atom.h:50
@ DivLeft
Definition atom.h:38
@ TableItemLeft
Definition atom.h:99
@ BaseName
Definition atom.h:23
@ TableOfContents
Definition atom.h:101
@ ComparesLeft
Definition atom.h:34
@ FormatIf
Definition atom.h:46
@ SnippetIdentifier
Definition atom.h:90
@ NoteLeft
Definition atom.h:72
const Atom * next() const
Return the next atom in the atom list.
Definition atom.h:146
The ClassNode represents a C++ class.
Definition classnode.h:21
bool isQmlNativeType()
Definition classnode.h:52
virtual Atom::AtomType atomType() const
Definition codemarker.h:23
A class for holding the members of a collection of doc pages.
const NodeList & members() const
NodeMap getMembers(Node::NodeType type) const
Returns a map containing this collection node's member nodes with a specified node type.
bool wasSeen() const override
Returns the seen flag data member of this node if it is a NamespaceNode or a CollectionNode.
void generatePageNode(PageNode *pn)
Generate the DocBook page for an entity that doesn't map to any underlying parsable C++ or QML elemen...
void generateQmlTypePage(QmlTypeNode *qcn)
Generate the DocBook page for a QML type.
void generateAlsoList(const Node *node) override
void generateQmlRequisites(const QmlTypeNode *qcn)
Lists the required imports and includes.
void generateSortedQmlNames(const Node *base, const NodeList &subs)
void initializeGenerator() override
Initializes the DocBook output generator's data structures from the configuration (Config).
void generateExampleFilePage(const Node *en, ResolvedFile resolved_file, CodeMarker *=nullptr) override
Generate a file with the contents of a C++ or QML source file.
bool generateText(const Text &text, const Node *relative) override
Generate the documentation for relative.
void generateCppReferencePage(Node *node)
Generate a reference page for the C++ class, namespace, or header file documented in node.
void generateHeader(const QString &title, const QString &subtitle, const Node *node)
Generate the DocBook header for the file, including the abstract.
DocBookGenerator(FileResolver &file_resolver)
bool generateThreadSafeness(const Node *node)
Generates text that explains how threadsafe and/or reentrant node is.
void generateCollectionNode(CollectionNode *cn)
Generate the HTML page for a group, module, or QML module.
QString format() override
void generateDocBookSynopsis(const Node *node)
Generate the metadata for the given node in DocBook.
void generateGenericCollectionPage(CollectionNode *cn)
Generate the HTML page for a generic collection.
void generateList(const Node *relative, const QString &selector, Qt::SortOrder sortOrder=Qt::AscendingOrder)
void generateBody(const Node *node)
Generate the body of the documentation from the qdoc comment found with the entity represented by the...
qsizetype generateAtom(const Atom *atom, const Node *relative, CodeMarker *) override
Generate DocBook from an instance of Atom.
void generateProxyPage(Aggregate *aggregate)
bool generateStatus(const Node *node)
void generateRequisites(const Aggregate *inner)
Lists the required imports and includes.
void generateSortedNames(const ClassNode *cn, const QList< RelatedClass > &rc)
void generateDocumentation(Node *node) override
Recursive writing of DocBook files from the root node.
void generateGroupReferenceText(const Node *node)
Return a string representing a text that exposes information about the groups that the node is part o...
void generateAddendum(const Node *node, Generator::Addendum type, CodeMarker *marker, bool generateNote) override
Generates an addendum note of type type for node.
QString fileExtension() const override
Returns "xml" for this subclass of Generator.
bool generateSince(const Node *node)
Definition doc.h:31
const Location & location() const
Returns the starting location of a qdoc comment.
Definition doc.cpp:90
static void quoteFromFile(const Location &location, Quoter &quoter, ResolvedFile resolved_file)
Definition doc.cpp:403
const Text & body() const
Definition doc.cpp:115
Text briefText(bool inclusive=false) const
Definition doc.cpp:121
const TypedefNode * flagsType() const
Definition enumnode.h:35
bool isScoped() const
Definition enumnode.h:31
Encapsulate the logic that QDoc uses to find files whose path is provided by the user and that are re...
This node is used to represent any kind of function being documented.
bool isMacroWithoutParams() const
bool isPrivateSignal() const
bool isOverride() const
const Parameters & parameters() const
bool isRef() const
bool isMAssign() const
bool isVirtual() const
bool isCAssign() const
bool isInvokable() const
bool isDefault() const override
Returns true if the QML property node is marked as default.
bool isPureVirtual() const
bool isDtor() const
bool isQmlSignal() const
bool isMacro() const override
returns true if either FunctionNode::isMacroWithParams() or FunctionNode::isMacroWithoutParams() retu...
bool isOverload() const
bool isRefRef() const
bool isCCtor() const
bool isMCtor() const
bool isFinal() const
bool isCtor() const
bool hasAssociatedProperties() const
bool generateComparisonCategory(const Node *node, CodeMarker *marker=nullptr)
static bool hasExceptions(const Node *node, NodeList &reentrant, NodeList &threadsafe, NodeList &nonreentrant)
void generateEnumValuesForQmlProperty(const Node *node, CodeMarker *marker)
FileResolver & file_resolver
Definition generator.h:194
static void setQmlTypeContext(QmlTypeNode *t)
Definition generator.h:69
const Atom * generateAtomList(const Atom *atom, const Node *relative, CodeMarker *marker, bool generate, int &numGeneratedAtoms)
QDocDatabase * m_qdb
Definition generator.h:196
bool m_inContents
Definition generator.h:198
bool generateComparisonList(const Node *node)
Generates a list of types that compare to node with the comparison category that applies for the rela...
void unknownAtom(const Atom *atom)
static bool matchAhead(const Atom *atom, Atom::AtomType expectedAtomType)
bool m_inLink
Definition generator.h:197
bool m_inTableHeader
Definition generator.h:200
bool m_inSectionHeading
Definition generator.h:199
virtual int skipAtoms(const Atom *atom, Atom::AtomType type) const
bool m_threeColumnEnumValueTable
Definition generator.h:201
@ AssociatedProperties
Definition generator.h:39
@ PrivateSignal
Definition generator.h:37
@ QmlSignalHandler
Definition generator.h:38
@ BindableProperty
Definition generator.h:40
bool m_showInternal
Definition generator.h:202
virtual void initializeGenerator()
Updates the generator's m_showInternal from the Config.
void initializeTextOutput()
Resets the variables used during text output.
This class represents a C++ namespace.
NamespaceNode * docNode() const
Returns a pointer to the NamespaceNode that represents where the namespace documentation is actually ...
bool isGenericCollection() const
Returns true if the node type is Collection.
Definition node.h:166
bool isExternalPage() const
Returns true if the node type is ExternalPage.
Definition node.h:134
const Doc & doc() const
Returns a reference to the node's Doc data member.
Definition node.h:271
bool isQmlNode() const
Returns true if this node's Genus value is QML.
Definition node.h:153
bool isGroup() const
Returns true if the node type is Group.
Definition node.h:139
virtual bool docMustBeGenerated() const
This function is called to perform a test to decide if the node must have documentation generated.
Definition node.h:228
bool isPrivate() const
Returns true if this node's access is Private.
Definition node.h:146
NodeType
An unsigned char value that identifies an object as a particular subclass of Node.
Definition node.h:54
@ Variable
Definition node.h:69
@ Module
Definition node.h:71
@ QmlModule
Definition node.h:73
@ Typedef
Definition node.h:66
@ QmlValueType
Definition node.h:75
@ Function
Definition node.h:65
@ TypeAlias
Definition node.h:67
@ Group
Definition node.h:70
@ Enum
Definition node.h:62
@ QmlType
Definition node.h:72
@ Namespace
Definition node.h:56
@ Property
Definition node.h:68
@ Class
Definition node.h:57
@ NoType
Definition node.h:55
bool isNamespace() const
Returns true if the node type is Namespace.
Definition node.h:143
bool isTypedef() const
Returns true if the node type is Typedef.
Definition node.h:160
bool isQmlBasicType() const
Returns true if the node type is QmlBasicType.
Definition node.h:151
bool isFunction(Genus g=DontCare) const
Returns true if this is a FunctionNode and its Genus is set to g.
Definition node.h:135
bool isQmlType() const
Returns true if the node type is QmlType or QmlValueType.
Definition node.h:155
bool isSharedCommentNode() const
Returns true if the node type is SharedComment.
Definition node.h:158
virtual bool isInternal() const
Returns true if the node's status is Internal, or if its parent is a class with Internal status.
Definition node.cpp:849
bool isHeader() const
Returns true if the node type is HeaderFile.
Definition node.h:140
virtual bool isPageNode() const
Returns true if this node represents something that generates a documentation page.
Definition node.h:182
bool isEnumType() const
Returns true if the node type is Enum.
Definition node.h:132
virtual bool isTextPageNode() const
Returns true if the node is a PageNode but not an Aggregate.
Definition node.h:187
Aggregate * parent() const
Returns the node's parent pointer.
Definition node.h:244
bool isVariable() const
Returns true if the node type is Variable.
Definition node.h:165
virtual bool isDeprecated() const
Returns true if this node's status is Deprecated.
Definition node.h:168
virtual bool isAggregate() const
Returns true if this node is an aggregate, which means it inherits Aggregate and can therefore have c...
Definition node.h:170
NodeType nodeType() const
Returns this node's type.
Definition node.h:121
static bool nodeNameLessThan(const Node *first, const Node *second)
Returns true if the node n1 is less than node n2.
Definition node.cpp:57
const Location & location() const
If this node's definition location is empty, this function returns this node's declaration location.
Definition node.h:267
bool isProxyNode() const
Returns true if the node type is Proxy.
Definition node.h:148
Access access() const
Returns the node's Access setting, which can be Public, Protected, or Private.
Definition node.h:264
ThreadSafeness threadSafeness() const
Returns the thread safeness value for whatever this node represents.
Definition node.cpp:826
Genus
An unsigned char value that specifies whether the Node represents a C++ element, a QML element,...
Definition node.h:81
@ DontCare
Definition node.h:82
@ API
Definition node.h:86
@ DOC
Definition node.h:85
@ QML
Definition node.h:84
virtual bool isMarkedReimp() const
Returns true if the FunctionNode is marked as a reimplemented function.
Definition node.h:184
bool isProperty() const
Returns true if the node type is Property.
Definition node.h:147
bool isModule() const
Returns true if the node type is Module.
Definition node.h:142
bool isClass() const
Returns true if the node type is Class.
Definition node.h:129
virtual bool isPropertyGroup() const
Returns true if the node is a SharedCommentNode for documenting multiple C++ properties or multiple Q...
Definition node.h:185
Genus genus() const
Returns this node's Genus.
Definition node.h:124
ThreadSafeness
An unsigned char that specifies the degree of thread-safeness of the element.
Definition node.h:97
@ ThreadSafe
Definition node.h:101
@ NonReentrant
Definition node.h:99
@ Reentrant
Definition node.h:100
bool isSharingComment() const
This function returns true if the node is sharing a comment with other nodes.
Definition node.h:285
bool hasDoc() const
Returns true if this node is documented, or it represents a documented node read from the index ('had...
Definition node.cpp:906
LinkType
An unsigned char value that probably should be moved out of the Node base class.
Definition node.h:112
bool isRelatedNonmember() const
Returns true if this is a related nonmember of something.
Definition node.h:156
virtual bool isClassNode() const
Returns true if this is an instance of ClassNode.
Definition node.h:177
virtual bool isCollectionNode() const
Returns true if this is an instance of CollectionNode.
Definition node.h:178
static bool nodeSortKeyOrNameLessThan(const Node *n1, const Node *n2)
Returns true if node n1 is less than node n2 when comparing the sort keys, defined with.
Definition node.cpp:99
@ Internal
Definition node.h:93
@ Active
Definition node.h:92
bool isQmlModule() const
Returns true if the node type is QmlModule.
Definition node.h:152
bool isExample() const
Returns true if the node type is Example.
Definition node.h:133
bool isIndexNode() const
Returns true if this node was created from something in an index file.
Definition node.h:141
Status status() const
Returns the node's status value.
Definition node.h:278
bool isQmlProperty() const
Returns true if the node type is QmlProperty.
Definition node.h:154
A PageNode is a Node that generates a documentation page.
Definition pagenode.h:18
bool noAutoList() const
Returns the value of the no auto-list flag.
Definition pagenode.h:41
The Parameter class describes one function parameter.
Definition parameters.h:20
A class for parsing and managing a function parameter list.
Definition parameters.h:57
bool isEmpty() const
Definition parameters.h:70
const Parameter & at(int i) const
Definition parameters.h:74
int count() const
Definition parameters.h:72
This class describes one instance of using the Q_PROPERTY macro.
const NodeList & getters() const
const NodeList & resetters() const
const NodeList & setters() const
PropertyType propertyType() const
const NodeList & notifiers() const
This class provides exclusive access to the qdoc database, which consists of a forrest of trees and a...
NodeMapMap & getFunctionIndex()
Returns the function index.
TextToNodeMap & getLegaleseTexts()
Returns a reference to the collection of legalese texts.
NodeMultiMap & getAttributions()
Returns a reference to the multimap of attribution nodes.
NodeMultiMap & getQmlTypesWithObsoleteMembers()
Returns a reference to the map of QML types with obsolete members.
NodeMultiMap & getObsoleteQmlTypes()
Returns a reference to the map of obsolete QML types.
static QDocDatabase * qdocDB()
Creates the singleton.
NodeMultiMap & getQmlTypes()
Returns a reference to the multimap of QML types.
NodeMultiMap & getClassesWithObsoleteMembers()
Returns a reference to the map of C++ classes with obsolete members.
NodeMultiMap & getQmlValueTypes()
Returns a reference to the map of QML basic types.
NodeMultiMap & getCppClasses()
Returns a reference to the map of all C++ classes.
NodeMultiMap & getNamespaces()
Returns a reference to the namespace map.
NodeMultiMap & getExamples()
Returns a reference to the multimap of example nodes.
NodeMultiMap & getObsoleteClasses()
Returns a reference to the map of obsolete C++ clases.
void mergeCollections(Node::NodeType type, CNMap &cnm, const Node *relative)
Finds all the collection nodes of the specified type and merges them into the collection node map cnm...
void mergeCollections(CollectionNode *c)
Finds all the collection nodes with the same name and type as c and merges their members into the mem...
const CNMap & groups()
Returns a const reference to the collection of all group nodes in the primary tree.
bool isDefault() const override
Returns true if the QML property node is marked as default.
bool isReadOnly() const
Returns true if this QML property node is marked as a read-only property.
bool isRequired()
Returns true if this QML property is marked with \required or the corresponding C++ property uses the...
bool isReadOnly()
Returns true if this QML property or attached property is read-only.
bool isAttached() const override
Returns true if the QML property or QML method node is marked as attached.
ClassNode * classNode() override
If this is a QmlTypeNode, this function returns the pointer to the C++ ClassNode that this QML type r...
Definition qmltypenode.h:26
bool isInternal() const override
Returns true if the node's status is Internal, or if its parent is a class with Internal status.
Definition qmltypenode.h:32
static void subclasses(const Node *base, NodeList &subs)
Loads the list subs with the nodes of all the subclasses of base.
QmlTypeNode * qmlBaseNode() const override
If this Aggregate is a QmlTypeNode, this function returns a pointer to the QmlTypeNode that is its ba...
Definition qmltypenode.h:43
CollectionNode * logicalModule() const override
If this is a QmlTypeNode, a pointer to its QML module is returned, which is a pointer to a Collection...
Definition qmltypenode.h:37
A class for containing the elements of one documentation section.
Definition sections.h:17
const NodeVector & obsoleteMembers() const
Definition sections.h:56
void appendMembers(const NodeVector &nv)
Definition sections.h:57
@ Summary
Definition sections.h:19
@ Details
Definition sections.h:19
@ Accessors
Definition sections.h:19
@ AllMembers
Definition sections.h:19
const NodeVector & members() const
Definition sections.h:49
Style style() const
Definition sections.h:44
A class for creating vectors of collections for documentation.
Definition sections.h:82
Aggregate * aggregate() const
Definition sections.h:181
Sections(Aggregate *aggregate)
This constructor builds the vectors of sections based on the type of the aggregate node.
Definition sections.cpp:344
SectionVector & stdDetailsSections()
Definition sections.h:156
SectionVector & stdCppClassDetailsSections()
Definition sections.h:158
bool hasObsoleteMembers(SectionPtrVector *summary_spv, SectionPtrVector *details_spv) const
Returns true if any sections in this object contain obsolete members.
Definition sections.cpp:955
Definition text.h:12
const Atom * firstAtom() const
Definition text.h:34
bool isEmpty() const
Definition text.h:31
Text & operator=(const Text &text)
Definition text.cpp:29
const Atom * lastAtom() const
Definition text.h:35
Atom * lastAtom()
Definition text.h:22
bool isStatic() const override
Returns true if the FunctionNode represents a static function.
static bool isThreeColumnEnumValueTable(const Atom *atom)
Determines whether the list atom should be shown with three columns (constant-value-description).
static Node::NodeType typeFromString(const Atom *atom)
Returns the type of this atom as an enumeration.
static bool isOneColumnValueTable(const Atom *atom)
Determines whether the list atom should be shown with just one column (value).
static void rewritePropertyBrief(const Atom *atom, const Node *relative)
Rewrites the brief of this node depending on its first word.
static int hOffset(const Node *node)
Header offset depending on the type of the node.
static bool hasBrief(const Node *node)
Do not display.
XmlGenerator(FileResolver &file_resolver)
const Node * m_linkNode
static const QRegularExpression m_funcLeftParen
#define CONFIG_DOCBOOKEXTENSIONS
Definition config.h:333
#define CONFIG_EXAMPLES
Definition config.h:336
#define CONFIG_URL
Definition config.h:391
#define CONFIG_DESCRIPTION
Definition config.h:332
#define CONFIG_PROJECT
Definition config.h:373
#define CONFIG_EXAMPLESINSTALLPATH
Definition config.h:337
#define CONFIG_NATURALLANGUAGE
Definition config.h:365
#define CONFIG_BUILDVERSION
Definition config.h:324
static const char xlinkNamespace[]
static QString nodeToSynopsisTag(const Node *node)
QString removeCodeMarkers(const QString &code)
static const char dbNamespace[]
QString taggedNode(const Node *node)
static const char itsNamespace[]
Combined button and popup list for selecting options.
QList< Node * > NodeList
Definition node.h:41
QList< Node * > NodeVector
Definition node.h:43
QMap< QString, Node * > NodeMap
Definition node.h:44
QMap< QString, NodeMap > NodeMapMap
Definition node.h:45
QMap< QString, CollectionNode * > CNMap
Definition node.h:48
QT_BEGIN_NAMESPACE typedef QMultiMap< Text, const Node * > TextToNodeMap
QT_BEGIN_NAMESPACE typedef QMultiMap< QString, Node * > NodeMultiMap
Definition generator.h:20
QList< const Section * > SectionPtrVector
Definition sections.h:79
QList< Section > SectionVector
Definition sections.h:78
Represents a file that is reachable by QDoc based on its current configuration.