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