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(
1159 Text::sectionHeading(atom->next()->next()).toString());
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
2148{
2149 // From Generator::appendSortedQmlNames.
2150 QMap<QString, Node *> classMap;
2151
2152 for (auto sub : subs)
2153 classMap[sub->plainFullName(base).toLower()] = sub;
2154
2155 QStringList names = classMap.keys();
2156 names.sort();
2157
2158 int index = 0;
2159 for (const QString &name : names) {
2160 generateFullName(classMap.value(name), base);
2161 m_writer->writeCharacters(Utilities::comma(index++, names.size()));
2162 }
2163}
2164
2165/*!
2166 Lists the required imports and includes.
2167*/
2169{
2170 // Adapted from HtmlGenerator::generateRequisites, but simplified: no need to store all the
2171 // elements, they can be produced one by one.
2172
2173 // Generate the requisites first separately: if some of them are generated, output them in a wrapper.
2174 // This complexity is required to ensure the DocBook file is valid: an empty list is not valid. It is not easy
2175 // to write a truly comprehensive condition.
2176 QXmlStreamWriter* oldWriter = m_writer;
2177 QString output;
2178 m_writer = new QXmlStreamWriter(&output);
2179
2180 // Includes.
2181 if (aggregate->includeFile()) generateRequisite("Header", *aggregate->includeFile());
2182
2183 // Since and project.
2184 if (!aggregate->since().isEmpty())
2185 generateRequisite("Since", formatSince(aggregate));
2186
2187 if (aggregate->isClassNode() || aggregate->isNamespace()) {
2188 // CMake and QT variable.
2189 const CollectionNode *cn =
2190 m_qdb->getCollectionNode(aggregate->physicalModuleName(), Node::Module);
2191
2192 if (const auto result = cmakeRequisite(cn)) {
2193 generateCMakeRequisite(result->first, result->second);
2194 }
2195
2196 if (cn && !cn->qtVariable().isEmpty())
2197 generateRequisite("qmake", "QT += " + cn->qtVariable());
2198 }
2199
2200 if (aggregate->nodeType() == Node::Class) {
2201 // Native type information.
2202 auto *classe = const_cast<ClassNode *>(static_cast<const ClassNode *>(aggregate));
2203 if (classe && classe->isQmlNativeType() && classe->status() != Node::Internal) {
2204 generateStartRequisite("In QML");
2205
2206 qsizetype idx{0};
2207 QList<QmlTypeNode *> nativeTypes { classe->qmlNativeTypes().cbegin(), classe->qmlNativeTypes().cend()};
2208 std::sort(nativeTypes.begin(), nativeTypes.end(), Node::nodeNameLessThan);
2209
2210 for (const auto &item : std::as_const(nativeTypes)) {
2211 generateFullName(item, classe);
2212 m_writer->writeCharacters(
2213 Utilities::comma(idx++, nativeTypes.size()));
2214 }
2215 generateEndRequisite();
2216 }
2217
2218 // Inherits.
2219 QList<RelatedClass>::ConstIterator r;
2220 if (!classe->baseClasses().isEmpty()) {
2221 generateStartRequisite("Inherits");
2222
2223 r = classe->baseClasses().constBegin();
2224 int index = 0;
2225 while (r != classe->baseClasses().constEnd()) {
2226 if ((*r).m_node) {
2227 generateFullName((*r).m_node, classe);
2228
2229 if ((*r).m_access == Access::Protected)
2230 m_writer->writeCharacters(" (protected)");
2231 else if ((*r).m_access == Access::Private)
2232 m_writer->writeCharacters(" (private)");
2233 m_writer->writeCharacters(
2234 Utilities::comma(index++, classe->baseClasses().size()));
2235 }
2236 ++r;
2237 }
2238
2239 generateEndRequisite();
2240 }
2241
2242 // Inherited by.
2243 if (!classe->derivedClasses().isEmpty()) {
2244 generateStartRequisite("Inherited By");
2245 generateSortedNames(classe, classe->derivedClasses());
2246 generateEndRequisite();
2247 }
2248 }
2249
2250 // Group.
2251 if (!aggregate->groupNames().empty()) {
2252 generateStartRequisite("Group");
2253 generateGroupReferenceText(aggregate);
2254 generateEndRequisite();
2255 }
2256
2257 // Status.
2258 if (auto status = formatStatus(aggregate, m_qdb); status)
2259 generateRequisite("Status", status.value());
2260
2261 // Write the elements as a list if not empty.
2262 delete m_writer;
2263 m_writer = oldWriter;
2264
2265 if (!output.isEmpty()) {
2266 // Namespaces are mangled in this output, because QXmlStreamWriter doesn't know about them. (Letting it know
2267 // would imply generating the xmlns declaration one more time.)
2268 static const QRegularExpression xmlTag(R"(<(/?)n\d+:)"); // Only for DocBook tags.
2269 static const QRegularExpression xmlnsDocBookDefinition(R"( xmlns:n\d+=")" + QString{dbNamespace} + "\"");
2270 static const QRegularExpression xmlnsXLinkDefinition(R"( xmlns:n\d+=")" + QString{xlinkNamespace} + "\"");
2271 static const QRegularExpression xmlAttr(R"( n\d+:)"); // Only for XLink attributes.
2272 // Space at the beginning!
2273 const QString cleanOutput = output.replace(xmlTag, R"(<\1db:)")
2274 .replace(xmlnsDocBookDefinition, "")
2275 .replace(xmlnsXLinkDefinition, "")
2276 .replace(xmlAttr, " xlink:");
2277
2278 m_writer->writeStartElement(dbNamespace, "variablelist");
2279 if (m_useITS)
2280 m_writer->writeAttribute(itsNamespace, "translate", "no");
2281 newLine();
2282
2283 m_writer->device()->write(cleanOutput.toUtf8());
2284
2285 m_writer->writeEndElement(); // variablelist
2286 newLine();
2287 }
2288}
2289
2290/*!
2291 Lists the required imports and includes.
2292*/
2294{
2295 // From HtmlGenerator::generateQmlRequisites, but simplified: no need to store all the elements,
2296 // they can be produced one by one.
2297 if (!qcn)
2298 return;
2299
2300 const CollectionNode *collection = qcn->logicalModule();
2301
2302 NodeList subs;
2303 QmlTypeNode::subclasses(qcn, subs);
2304
2305 QmlTypeNode *base = qcn->qmlBaseNode();
2306 while (base && base->isInternal()) {
2307 base = base->qmlBaseNode();
2308 }
2309
2310 // Skip import statement for \internal collections
2311 const bool generate_import_statement = !qcn->logicalModuleName().isEmpty() && (!collection || !collection->isInternal() || m_showInternal);
2312 // Detect if anything is generated in this method. If not, exit early to avoid having an empty list.
2313 const bool generates_something = generate_import_statement || !qcn->since().isEmpty() || !subs.isEmpty() || base;
2314
2315 if (!generates_something)
2316 return;
2317
2318 // Start writing the elements as a list.
2319 m_writer->writeStartElement(dbNamespace, "variablelist");
2320 if (m_useITS)
2321 m_writer->writeAttribute(itsNamespace, "translate", "no");
2322 newLine();
2323
2324 if (generate_import_statement) {
2325 QStringList parts = QStringList() << "import" << qcn->logicalModuleName() << qcn->logicalModuleVersion();
2326 generateRequisite("Import Statement", parts.join(' ').trimmed());
2327 }
2328
2329 // Since and project.
2330 if (!qcn->since().isEmpty())
2331 generateRequisite("Since:", formatSince(qcn));
2332
2333 // Inherited by.
2334 if (!subs.isEmpty()) {
2335 generateStartRequisite("Inherited By:");
2336 generateSortedQmlNames(qcn, subs);
2337 generateEndRequisite();
2338 }
2339
2340 // Inherits.
2341 if (base) {
2342 const Node *otherNode = nullptr;
2343 Atom a = Atom(Atom::LinkNode, CodeMarker::stringForNode(base));
2344 QString link = getAutoLink(&a, qcn, &otherNode);
2345
2346 generateStartRequisite("Inherits:");
2347 generateSimpleLink(link, base->name());
2348 generateEndRequisite();
2349 }
2350
2351 // Native type information.
2352 ClassNode *cn = (const_cast<QmlTypeNode *>(qcn))->classNode();
2353 if (cn && cn->isQmlNativeType() && cn->status() != Node::Internal) {
2354 generateStartRequisite("In C++:");
2355 generateSimpleLink(fullDocumentLocation(cn), cn->name());
2356 generateEndRequisite();
2357 }
2358
2359 // Group.
2360 if (!qcn->groupNames().empty()) {
2361 generateStartRequisite("Group");
2363 generateEndRequisite();
2364 }
2365
2366 // Status.
2367 if (auto status = formatStatus(qcn, m_qdb); status)
2368 generateRequisite("Status:", status.value());
2369
2370 m_writer->writeEndElement(); // variablelist
2371 newLine();
2372}
2373
2375{
2376 // From Generator::generateStatus.
2377 switch (node->status()) {
2378 case Node::Active:
2379 // Output the module 'state' description if set.
2380 if (node->isModule() || node->isQmlModule()) {
2381 const QString &state = static_cast<const CollectionNode*>(node)->state();
2382 if (!state.isEmpty()) {
2383 m_writer->writeStartElement(dbNamespace, "para");
2384 m_writer->writeCharacters("This " + typeString(node) + " is in ");
2385 m_writer->writeStartElement(dbNamespace, "emphasis");
2386 m_writer->writeCharacters(state);
2387 m_writer->writeEndElement(); // emphasis
2388 m_writer->writeCharacters(" state.");
2389 m_writer->writeEndElement(); // para
2390 newLine();
2391 return true;
2392 }
2393 }
2394 if (const auto version = node->deprecatedSince(); !version.isEmpty()) {
2395 m_writer->writeStartElement(dbNamespace, "para");
2396 m_writer->writeCharacters("This " + typeString(node)
2397 + " is scheduled for deprecation in version "
2398 + version + ".");
2399 m_writer->writeEndElement(); // para
2400 newLine();
2401 return true;
2402 }
2403 return false;
2404 case Node::Preliminary:
2405 m_writer->writeStartElement(dbNamespace, "para");
2406 m_writer->writeStartElement(dbNamespace, "emphasis");
2407 m_writer->writeAttribute("role", "bold");
2408 m_writer->writeCharacters("This " + typeString(node)
2409 + " is under development and is subject to change.");
2410 m_writer->writeEndElement(); // emphasis
2411 m_writer->writeEndElement(); // para
2412 newLine();
2413 return true;
2414 case Node::Deprecated:
2415 m_writer->writeStartElement(dbNamespace, "para");
2416 if (node->isAggregate()) {
2417 m_writer->writeStartElement(dbNamespace, "emphasis");
2418 m_writer->writeAttribute("role", "bold");
2419 }
2420 m_writer->writeCharacters("This " + typeString(node) + " is deprecated");
2421 if (const QString &version = node->deprecatedSince(); !version.isEmpty()) {
2422 m_writer->writeCharacters(" since ");
2423 if (node->isQmlNode() && !node->logicalModuleName().isEmpty())
2424 m_writer->writeCharacters(node->logicalModuleName() + " ");
2425 m_writer->writeCharacters(version);
2426 }
2427 m_writer->writeCharacters(". We strongly advise against using it in new code.");
2428 if (node->isAggregate())
2429 m_writer->writeEndElement(); // emphasis
2430 m_writer->writeEndElement(); // para
2431 newLine();
2432 return true;
2433 case Node::Internal:
2434 default:
2435 return false;
2436 }
2437}
2438
2439/*!
2440 Generate a list of function signatures. The function nodes
2441 are in \a nodes.
2442 */
2443void DocBookGenerator::generateSignatureList(const NodeList &nodes)
2444{
2445 // From Generator::signatureList and Generator::appendSignature.
2446 m_writer->writeStartElement(dbNamespace, "itemizedlist");
2447 newLine();
2448
2449 NodeList::ConstIterator n = nodes.constBegin();
2450 while (n != nodes.constEnd()) {
2451 m_writer->writeStartElement(dbNamespace, "listitem");
2452 newLine();
2453 m_writer->writeStartElement(dbNamespace, "para");
2454
2455 generateSimpleLink(currentGenerator()->fullDocumentLocation(*n),
2456 (*n)->signature(Node::SignaturePlain));
2457
2458 m_writer->writeEndElement(); // para
2459 newLine();
2460 m_writer->writeEndElement(); // itemizedlist
2461 newLine();
2462 ++n;
2463 }
2464
2465 m_writer->writeEndElement(); // itemizedlist
2466 newLine();
2467}
2468
2469/*!
2470 * Return a string representing a text that exposes information about
2471 * the groups that the \a node is part of.
2472 */
2474{
2475 // From HtmlGenerator::groupReferenceText
2476
2477 if (!node->isAggregate())
2478 return;
2479 const auto aggregate = static_cast<const Aggregate *>(node);
2480
2481 const QStringList &groups_names{aggregate->groupNames()};
2482 if (!groups_names.empty()) {
2483 m_writer->writeCharacters(aggregate->name() + " is part of ");
2484 m_writer->writeStartElement(dbNamespace, "simplelist");
2485
2486 for (qsizetype index{0}; index < groups_names.size(); ++index) {
2487 CollectionNode* group{m_qdb->groups()[groups_names[index]]};
2489
2490 m_writer->writeStartElement(dbNamespace, "member");
2491 if (QString target{linkForNode(group, nullptr)}; !target.isEmpty())
2492 generateSimpleLink(target, group->fullTitle());
2493 else
2494 m_writer->writeCharacters(group->name());
2495 m_writer->writeEndElement(); // member
2496 }
2497
2498 m_writer->writeEndElement(); // simplelist
2499 newLine();
2500 }
2501}
2502
2503/*!
2504 Generates text that explains how threadsafe and/or reentrant
2505 \a node is.
2506 */
2508{
2509 // From Generator::generateThreadSafeness
2511
2512 const Node *reentrantNode;
2513 Atom reentrantAtom = Atom(Atom::Link, "reentrant");
2514 QString linkReentrant = getAutoLink(&reentrantAtom, node, &reentrantNode);
2515 const Node *threadSafeNode;
2516 Atom threadSafeAtom = Atom(Atom::Link, "thread-safe");
2517 QString linkThreadSafe = getAutoLink(&threadSafeAtom, node, &threadSafeNode);
2518
2519 if (ts == Node::NonReentrant) {
2520 m_writer->writeStartElement(dbNamespace, "warning");
2521 newLine();
2522 m_writer->writeStartElement(dbNamespace, "para");
2523 m_writer->writeCharacters("This " + typeString(node) + " is not ");
2524 generateSimpleLink(linkReentrant, "reentrant");
2525 m_writer->writeCharacters(".");
2526 m_writer->writeEndElement(); // para
2527 newLine();
2528 m_writer->writeEndElement(); // warning
2529
2530 return true;
2531 } else if (ts == Node::Reentrant || ts == Node::ThreadSafe) {
2532 m_writer->writeStartElement(dbNamespace, "note");
2533 newLine();
2534 m_writer->writeStartElement(dbNamespace, "para");
2535
2536 if (node->isAggregate()) {
2537 m_writer->writeCharacters("All functions in this " + typeString(node) + " are ");
2538 if (ts == Node::ThreadSafe)
2539 generateSimpleLink(linkThreadSafe, "thread-safe");
2540 else
2541 generateSimpleLink(linkReentrant, "reentrant");
2542
2543 NodeList reentrant;
2544 NodeList threadsafe;
2545 NodeList nonreentrant;
2546 bool exceptions = hasExceptions(node, reentrant, threadsafe, nonreentrant);
2547 if (!exceptions || (ts == Node::Reentrant && !threadsafe.isEmpty())) {
2548 m_writer->writeCharacters(".");
2549 m_writer->writeEndElement(); // para
2550 newLine();
2551 } else {
2552 m_writer->writeCharacters(" with the following exceptions:");
2553 m_writer->writeEndElement(); // para
2554 newLine();
2555 m_writer->writeStartElement(dbNamespace, "para");
2556
2557 if (ts == Node::Reentrant) {
2558 if (!nonreentrant.isEmpty()) {
2559 m_writer->writeCharacters("These functions are not ");
2560 generateSimpleLink(linkReentrant, "reentrant");
2561 m_writer->writeCharacters(":");
2562 m_writer->writeEndElement(); // para
2563 newLine();
2564 generateSignatureList(nonreentrant);
2565 }
2566 if (!threadsafe.isEmpty()) {
2567 m_writer->writeCharacters("These functions are also ");
2568 generateSimpleLink(linkThreadSafe, "thread-safe");
2569 m_writer->writeCharacters(":");
2570 m_writer->writeEndElement(); // para
2571 newLine();
2572 generateSignatureList(threadsafe);
2573 }
2574 } else { // thread-safe
2575 if (!reentrant.isEmpty()) {
2576 m_writer->writeCharacters("These functions are only ");
2577 generateSimpleLink(linkReentrant, "reentrant");
2578 m_writer->writeCharacters(":");
2579 m_writer->writeEndElement(); // para
2580 newLine();
2581 generateSignatureList(reentrant);
2582 }
2583 if (!nonreentrant.isEmpty()) {
2584 m_writer->writeCharacters("These functions are not ");
2585 generateSimpleLink(linkReentrant, "reentrant");
2586 m_writer->writeCharacters(":");
2587 m_writer->writeEndElement(); // para
2588 newLine();
2589 generateSignatureList(nonreentrant);
2590 }
2591 }
2592 }
2593 } else {
2594 m_writer->writeCharacters("This " + typeString(node) + " is ");
2595 if (ts == Node::ThreadSafe)
2596 generateSimpleLink(linkThreadSafe, "thread-safe");
2597 else
2598 generateSimpleLink(linkReentrant, "reentrant");
2599 m_writer->writeCharacters(".");
2600 m_writer->writeEndElement(); // para
2601 newLine();
2602 }
2603 m_writer->writeEndElement(); // note
2604 newLine();
2605
2606 return true;
2607 }
2608
2609 return false;
2610}
2611
2612/*!
2613 Generate the body of the documentation from the qdoc comment
2614 found with the entity represented by the \a node.
2615 */
2617{
2618 // From Generator::generateBody, without warnings.
2619 const FunctionNode *fn = node->isFunction() ? static_cast<const FunctionNode *>(node) : nullptr;
2620
2621 if (!node->hasDoc()) {
2622 /*
2623 Test for special function, like a destructor or copy constructor,
2624 that has no documentation.
2625 */
2626 if (fn) {
2627 QString t;
2628 if (fn->isDtor()) {
2629 t = "Destroys the instance of " + fn->parent()->name() + ".";
2630 if (fn->isVirtual())
2631 t += " The destructor is virtual.";
2632 } else if (fn->isCtor()) {
2633 t = "Default constructs an instance of " + fn->parent()->name() + ".";
2634 } else if (fn->isCCtor()) {
2635 t = "Copy constructor.";
2636 } else if (fn->isMCtor()) {
2637 t = "Move-copy constructor.";
2638 } else if (fn->isCAssign()) {
2639 t = "Copy-assignment constructor.";
2640 } else if (fn->isMAssign()) {
2641 t = "Move-assignment constructor.";
2642 }
2643
2644 if (!t.isEmpty())
2645 m_writer->writeTextElement(dbNamespace, "para", t);
2646 }
2647 } else if (!node->isSharingComment()) {
2648 // Reimplements clause and type alias info precede body text
2649 if (fn && !fn->overridesThis().isEmpty())
2650 generateReimplementsClause(fn);
2651 else if (node->isProperty()) {
2653 generateAddendum(node, BindableProperty, nullptr, false);
2654 }
2655
2656 // Generate the body.
2657 if (!generateText(node->doc().body(), node)) {
2658 if (node->isMarkedReimp())
2659 return;
2660 }
2661
2662 // Output what is after the main body.
2663 if (fn) {
2664 if (fn->isQmlSignal())
2665 generateAddendum(node, QmlSignalHandler, nullptr, true);
2666 if (fn->isPrivateSignal())
2667 generateAddendum(node, PrivateSignal, nullptr, true);
2668 if (fn->isInvokable())
2669 generateAddendum(node, Invokable, nullptr, true);
2672 }
2673
2674 // Warning generation skipped with respect to Generator::generateBody.
2675 }
2676
2677 generateEnumValuesForQmlProperty(node, nullptr);
2678 generateRequiredLinks(node);
2679}
2680
2681/*!
2682 Generates either a link to the project folder for example \a node, or a list
2683 of links files/images if 'url.examples config' variable is not defined.
2684
2685 Does nothing for non-example nodes.
2686*/
2687void DocBookGenerator::generateRequiredLinks(const Node *node)
2688{
2689 // From Generator::generateRequiredLinks.
2690 if (!node->isExample())
2691 return;
2692
2693 const auto en = static_cast<const ExampleNode *>(node);
2694 QString exampleUrl{Config::instance().get(CONFIG_URL + Config::dot + CONFIG_EXAMPLES).asString()};
2695
2696 if (exampleUrl.isEmpty()) {
2697 if (!en->noAutoList()) {
2698 generateFileList(en, false); // files
2699 generateFileList(en, true); // images
2700 }
2701 } else {
2702 generateLinkToExample(en, exampleUrl);
2703 }
2704}
2705
2706/*!
2707 The path to the example replaces a placeholder '\1' character if
2708 one is found in the \a baseUrl string. If no such placeholder is found,
2709 the path is appended to \a baseUrl, after a '/' character if \a baseUrl did
2710 not already end in one.
2711*/
2712void DocBookGenerator::generateLinkToExample(const ExampleNode *en, const QString &baseUrl)
2713{
2714 // From Generator::generateLinkToExample.
2715 QString exampleUrl(baseUrl);
2716 QString link;
2717#ifndef QT_BOOTSTRAPPED
2718 link = QUrl(exampleUrl).host();
2719#endif
2720 if (!link.isEmpty())
2721 link.prepend(" @ ");
2722 link.prepend("Example project");
2723
2724 const QLatin1Char separator('/');
2725 const QLatin1Char placeholder('\1');
2726 if (!exampleUrl.contains(placeholder)) {
2727 if (!exampleUrl.endsWith(separator))
2728 exampleUrl += separator;
2729 exampleUrl += placeholder;
2730 }
2731
2732 // Construct a path to the example; <install path>/<example name>
2733 QStringList path = QStringList()
2734 << Config::instance().get(CONFIG_EXAMPLESINSTALLPATH).asString() << en->name();
2735 path.removeAll(QString());
2736
2737 // Write the link to the example. Typically, this link comes after sections, hence
2738 // wrap it in a section too.
2739 startSection("Example project");
2740
2741 m_writer->writeStartElement(dbNamespace, "para");
2742 generateSimpleLink(exampleUrl.replace(placeholder, path.join(separator)), link);
2743 m_writer->writeEndElement(); // para
2744 newLine();
2745
2746 endSection();
2747}
2748
2749// TODO: [multi-purpose-function-with-flag][generate-file-list]
2750
2751/*!
2752 This function is called when the documentation for an example is
2753 being formatted. It outputs a list of files for the example, which
2754 can be the example's source files or the list of images used by the
2755 example. The images are copied into a subtree of
2756 \c{...doc/html/images/used-in-examples/...}
2757*/
2758void DocBookGenerator::generateFileList(const ExampleNode *en, bool images)
2759{
2760 // TODO: [possibly-stale-duplicate-code][generator-insufficient-structural-abstraction]
2761 // Review and compare this code with
2762 // Generator::generateFileList.
2763 // Some subtle changes that might be semantically equivalent are
2764 // present between the two.
2765 // Supposedly, this version is to be considered stale compared to
2766 // Generator's one and it might be possible to remove it in favor
2767 // of that as long as the difference in output are taken into consideration.
2768
2769 // From Generator::generateFileList
2770 QString tag;
2771 QStringList paths;
2772 if (images) {
2773 paths = en->images();
2774 tag = "Images:";
2775 } else { // files
2776 paths = en->files();
2777 tag = "Files:";
2778 }
2779 std::sort(paths.begin(), paths.end(), Generator::comparePaths);
2780
2781 if (paths.isEmpty())
2782 return;
2783
2784 startSection("", "List of Files");
2785
2786 m_writer->writeStartElement(dbNamespace, "para");
2787 m_writer->writeCharacters(tag);
2788 m_writer->writeEndElement(); // para
2789 newLine();
2790
2791 startSection("List of Files");
2792
2793 m_writer->writeStartElement(dbNamespace, "itemizedlist");
2794 newLine();
2795
2796 for (const auto &path : std::as_const(paths)) {
2797 auto maybe_resolved_file{file_resolver.resolve(path)};
2798 if (!maybe_resolved_file) {
2799 // TODO: [uncentralized-admonition][failed-resolve-file]
2800 QString details = std::transform_reduce(
2801 file_resolver.get_search_directories().cbegin(),
2802 file_resolver.get_search_directories().cend(),
2803 u"Searched directories:"_s,
2804 std::plus(),
2805 [](const DirectoryPath &directory_path) -> QString { return u' ' + directory_path.value(); }
2806 );
2807
2808 en->location().warning(u"Cannot find file to quote from: %1"_s.arg(path), details);
2809
2810 continue;
2811 }
2812
2813 auto file{*maybe_resolved_file};
2814 if (images) addImageToCopy(en, file);
2815 else generateExampleFilePage(en, file);
2816
2817 m_writer->writeStartElement(dbNamespace, "listitem");
2818 newLine();
2819 m_writer->writeStartElement(dbNamespace, "para");
2820 generateSimpleLink(file.get_query(), file.get_query());
2821 m_writer->writeEndElement(); // para
2822 m_writer->writeEndElement(); // listitem
2823 newLine();
2824 }
2825
2826 m_writer->writeEndElement(); // itemizedlist
2827 newLine();
2828
2829 endSection();
2830}
2831
2832/*!
2833 Generate a file with the contents of a C++ or QML source file.
2834 */
2836{
2837 // TODO: [generator-insufficient-structural-abstraction]
2838
2839 // From HtmlGenerator::generateExampleFilePage.
2840 if (!node->isExample())
2841 return;
2842
2843 // TODO: Understand if this is safe.
2844 const auto en = static_cast<const ExampleNode *>(node);
2845
2846 // Store current (active) writer
2847 QXmlStreamWriter *currentWriter = m_writer;
2848 m_writer = startDocument(en, resolved_file.get_query());
2849 generateHeader(en->fullTitle(), en->subtitle(), en);
2850
2851 Text text;
2852 Quoter quoter;
2853 Doc::quoteFromFile(en->doc().location(), quoter, resolved_file);
2854 QString code = quoter.quoteTo(en->location(), QString(), QString());
2855 CodeMarker *codeMarker = CodeMarker::markerForFileName(resolved_file.get_path());
2856 text << Atom(codeMarker->atomType(), code);
2857 Atom a(codeMarker->atomType(), code);
2858 generateText(text, en);
2859
2860 endDocument(); // Delete m_writer.
2861 m_writer = currentWriter; // Restore writer.
2862}
2863
2864void DocBookGenerator::generateReimplementsClause(const FunctionNode *fn)
2865{
2866 // From Generator::generateReimplementsClause, without warning generation.
2867 if (fn->overridesThis().isEmpty() || !fn->parent()->isClassNode())
2868 return;
2869
2870 auto cn = static_cast<ClassNode *>(fn->parent());
2871
2872 if (const FunctionNode *overrides = cn->findOverriddenFunction(fn);
2873 overrides && !overrides->isPrivate() && !overrides->parent()->isPrivate()) {
2874 if (overrides->hasDoc()) {
2875 m_writer->writeStartElement(dbNamespace, "para");
2876 m_writer->writeCharacters("Reimplements: ");
2877 QString fullName =
2878 overrides->parent()->name() + "::" + overrides->signature(Node::SignaturePlain);
2879 generateFullName(overrides->parent(), fullName, overrides);
2880 m_writer->writeCharacters(".");
2881 m_writer->writeEndElement(); // para
2882 newLine();
2883 return;
2884 }
2885 }
2886
2887 if (const PropertyNode *sameName = cn->findOverriddenProperty(fn); sameName && sameName->hasDoc()) {
2888 m_writer->writeStartElement(dbNamespace, "para");
2889 m_writer->writeCharacters("Reimplements an access function for property: ");
2890 QString fullName = sameName->parent()->name() + "::" + sameName->name();
2891 generateFullName(sameName->parent(), fullName, sameName);
2892 m_writer->writeCharacters(".");
2893 m_writer->writeEndElement(); // para
2894 newLine();
2895 return;
2896 }
2897}
2898
2900{
2901 // From Generator::generateAlsoList.
2902 QList<Text> alsoList = node->doc().alsoList();
2903 supplementAlsoList(node, alsoList);
2904
2905 if (!alsoList.isEmpty()) {
2906 startSection("See Also");
2907
2908 m_writer->writeStartElement(dbNamespace, "para");
2909 m_writer->writeStartElement(dbNamespace, "emphasis");
2910 m_writer->writeCharacters("See also ");
2911 m_writer->writeEndElement(); // emphasis
2912 newLine();
2913
2914 m_writer->writeStartElement(dbNamespace, "simplelist");
2915 m_writer->writeAttribute("type", "vert");
2916 m_writer->writeAttribute("role", "see-also");
2917 newLine();
2918
2919 for (const Text &text : alsoList) {
2920 m_writer->writeStartElement(dbNamespace, "member");
2921 generateText(text, node);
2922 m_writer->writeEndElement(); // member
2923 newLine();
2924 }
2925
2926 m_writer->writeEndElement(); // simplelist
2927 newLine();
2928
2929 m_writer->writeEndElement(); // para
2930 newLine();
2931
2932 endSection();
2933 }
2934}
2935
2936/*!
2937 Open a new file to write XML contents, including the DocBook
2938 opening tag.
2939 */
2940QXmlStreamWriter *DocBookGenerator::startGenericDocument(const Node *node, const QString &fileName)
2941{
2942 Q_ASSERT(node->isPageNode());
2943 QFile *outFile = openSubPageFile(static_cast<const PageNode*>(node), fileName);
2944 m_writer = new QXmlStreamWriter(outFile);
2945 m_writer->setAutoFormatting(false); // We need a precise handling of line feeds.
2946
2947 m_writer->writeStartDocument();
2948 newLine();
2949 m_writer->writeNamespace(dbNamespace, "db");
2950 m_writer->writeNamespace(xlinkNamespace, "xlink");
2951 if (m_useITS)
2952 m_writer->writeNamespace(itsNamespace, "its");
2953 m_writer->writeStartElement(dbNamespace, "article");
2954 m_writer->writeAttribute("version", "5.2");
2955 if (!m_naturalLanguage.isEmpty())
2956 m_writer->writeAttribute("xml:lang", m_naturalLanguage);
2957 newLine();
2958
2959 // Reset the state for the new document.
2960 sectionLevels.resize(0);
2961 m_inPara = false;
2962 m_inList = 0;
2963
2964 return m_writer;
2965}
2966
2967QXmlStreamWriter *DocBookGenerator::startDocument(const Node *node)
2968{
2969 m_hasSection = false;
2970 refMap.clear();
2971
2972 QString fileName = Generator::fileName(node, fileExtension());
2973 return startGenericDocument(node, fileName);
2974}
2975
2976QXmlStreamWriter *DocBookGenerator::startDocument(const ExampleNode *en, const QString &file)
2977{
2978 m_hasSection = false;
2979
2980 QString fileName = linkForExampleFile(file);
2981 return startGenericDocument(en, fileName);
2982}
2983
2984void DocBookGenerator::endDocument()
2985{
2986 m_writer->writeEndElement(); // article
2987 m_writer->writeEndDocument();
2988
2989 m_writer->device()->close();
2990 delete m_writer->device();
2991 delete m_writer;
2992 m_writer = nullptr;
2993}
2994
2995/*!
2996 Generate a reference page for the C++ class, namespace, or
2997 header file documented in \a node.
2998 */
3000{
3001 // Based on HtmlGenerator::generateCppReferencePage.
3002 Q_ASSERT(node->isAggregate());
3003 const auto aggregate = static_cast<const Aggregate *>(node);
3004
3005 QString title;
3006 QString subtitleText;
3007 const QString typeWord{aggregate->typeWord(true)};
3008 if (aggregate->isNamespace()) {
3009 title = "%1 %2"_L1.arg(aggregate->plainFullName(), typeWord);
3010 } else if (aggregate->isClass()) {
3011 auto templateDecl = node->templateDecl();
3012 if (templateDecl)
3013 subtitleText = "%1 %2 %3"_L1.arg((*templateDecl).to_qstring(),
3014 aggregate->typeWord(false),
3015 aggregate->plainFullName());
3016 title = "%1 %2"_L1.arg(aggregate->plainFullName(), typeWord);
3017 } else if (aggregate->isHeader()) {
3018 title = aggregate->fullTitle();
3019 }
3020
3021 // Start producing the DocBook file.
3022 m_writer = startDocument(node);
3023
3024 // Info container.
3025 generateHeader(title, subtitleText, aggregate);
3026
3027 generateRequisites(aggregate);
3028 generateStatus(aggregate);
3029
3030 // Element synopsis.
3032
3033 // Actual content.
3034 if (!aggregate->doc().isEmpty()) {
3035 startSection("details", "Detailed Description");
3036
3037 generateBody(aggregate);
3038 generateAlsoList(aggregate);
3039
3040 endSection();
3041 }
3042
3043 Sections sections(const_cast<Aggregate *>(aggregate));
3044 SectionVector sectionVector =
3045 (aggregate->isNamespace() || aggregate->isHeader()) ?
3046 sections.stdDetailsSections() :
3048 for (const Section &section : sectionVector) {
3049 if (section.members().isEmpty())
3050 continue;
3051
3052 startSection(section.title().toLower(), section.title());
3053
3054 for (const Node *member : section.members()) {
3055 if (member->access() == Access::Private) // ### check necessary?
3056 continue;
3057
3058 if (member->nodeType() != Node::Class) {
3059 // This function starts its own section.
3060 generateDetailedMember(member, aggregate);
3061 } else {
3062 startSectionBegin();
3063 m_writer->writeCharacters("class ");
3064 generateFullName(member, aggregate);
3065 startSectionEnd();
3066
3067 generateBrief(member);
3068
3069 endSection();
3070 }
3071 }
3072
3073 endSection();
3074 }
3075
3076 generateObsoleteMembers(sections);
3077
3078 endDocument();
3079}
3080
3081void DocBookGenerator::generateSynopsisInfo(const QString &key, const QString &value)
3082{
3083 m_writer->writeStartElement(dbNamespace, "synopsisinfo");
3084 m_writer->writeAttribute("role", key);
3085 m_writer->writeCharacters(value);
3086 m_writer->writeEndElement(); // synopsisinfo
3087 newLine();
3088}
3089
3090void DocBookGenerator::generateModifier(const QString &value)
3091{
3092 m_writer->writeTextElement(dbNamespace, "modifier", value);
3093 newLine();
3094}
3095
3096/*!
3097 Generate the metadata for the given \a node in DocBook.
3098 */
3100{
3101 if (!node)
3102 return;
3103
3104 // From Generator::generateStatus, HtmlGenerator::generateRequisites,
3105 // Generator::generateThreadSafeness, QDocIndexFiles::generateIndexSection.
3106
3107 // This function is the major place where DocBook extensions are used.
3108 if (!m_useDocBook52)
3109 return;
3110
3111 // Nothing to export in some cases. Note that isSharedCommentNode() returns
3112 // true also for QML property groups.
3114 return;
3115
3116 // Cast the node to several subtypes (null pointer if the node is not of the required type).
3117 const Aggregate *aggregate =
3118 node->isAggregate() ? static_cast<const Aggregate *>(node) : nullptr;
3119 const ClassNode *classNode = node->isClass() ? static_cast<const ClassNode *>(node) : nullptr;
3120 const FunctionNode *functionNode =
3121 node->isFunction() ? static_cast<const FunctionNode *>(node) : nullptr;
3122 const PropertyNode *propertyNode =
3123 node->isProperty() ? static_cast<const PropertyNode *>(node) : nullptr;
3124 const VariableNode *variableNode =
3125 node->isVariable() ? static_cast<const VariableNode *>(node) : nullptr;
3126 const EnumNode *enumNode = node->isEnumType() ? static_cast<const EnumNode *>(node) : nullptr;
3127 const QmlPropertyNode *qpn =
3128 node->isQmlProperty() ? static_cast<const QmlPropertyNode *>(node) : nullptr;
3129 const QmlTypeNode *qcn = node->isQmlType() ? static_cast<const QmlTypeNode *>(node) : nullptr;
3130 // Typedefs are ignored, as they correspond to enums.
3131 // Groups and modules are ignored.
3132 // Documents are ignored, they have no interesting metadata.
3133
3134 // Start the synopsis tag.
3135 QString synopsisTag = nodeToSynopsisTag(node);
3136 m_writer->writeStartElement(dbNamespace, synopsisTag);
3137 newLine();
3138
3139 // Name and basic properties of each tag (like types and parameters).
3140 if (node->isClass()) {
3141 m_writer->writeStartElement(dbNamespace, "ooclass");
3142 m_writer->writeTextElement(dbNamespace, "classname", node->plainName());
3143 m_writer->writeEndElement(); // ooclass
3144 newLine();
3145 } else if (node->isNamespace()) {
3146 m_writer->writeTextElement(dbNamespace, "namespacename", node->plainName());
3147 newLine();
3148 } else if (node->isQmlType()) {
3149 m_writer->writeStartElement(dbNamespace, "ooclass");
3150 m_writer->writeTextElement(dbNamespace, "classname", node->plainName());
3151 m_writer->writeEndElement(); // ooclass
3152 newLine();
3153 if (!qcn->groupNames().isEmpty())
3154 m_writer->writeAttribute("groups", qcn->groupNames().join(QLatin1Char(',')));
3155 } else if (node->isProperty()) {
3156 m_writer->writeTextElement(dbNamespace, "modifier", "(Qt property)");
3157 newLine();
3158 m_writer->writeTextElement(dbNamespace, "type", propertyNode->dataType());
3159 newLine();
3160 m_writer->writeTextElement(dbNamespace, "varname", node->plainName());
3161 newLine();
3162 } else if (node->isVariable()) {
3163 if (variableNode->isStatic()) {
3164 m_writer->writeTextElement(dbNamespace, "modifier", "static");
3165 newLine();
3166 }
3167 m_writer->writeTextElement(dbNamespace, "type", variableNode->dataType());
3168 newLine();
3169 m_writer->writeTextElement(dbNamespace, "varname", node->plainName());
3170 newLine();
3171 } else if (node->isEnumType()) {
3172 m_writer->writeTextElement(dbNamespace, "enumname", node->plainName());
3173 newLine();
3174 } else if (node->isQmlProperty()) {
3175 QString name = node->name();
3176 if (qpn->isAttached())
3177 name.prepend(qpn->element() + QLatin1Char('.'));
3178
3179 m_writer->writeTextElement(dbNamespace, "type", qpn->dataType());
3180 newLine();
3181 m_writer->writeTextElement(dbNamespace, "varname", name);
3182 newLine();
3183
3184 if (qpn->isAttached()) {
3185 m_writer->writeTextElement(dbNamespace, "modifier", "attached");
3186 newLine();
3187 }
3188 if (!(const_cast<QmlPropertyNode *>(qpn))->isReadOnly()) {
3189 m_writer->writeTextElement(dbNamespace, "modifier", "writable");
3190 newLine();
3191 }
3192 if ((const_cast<QmlPropertyNode *>(qpn))->isRequired()) {
3193 m_writer->writeTextElement(dbNamespace, "modifier", "required");
3194 newLine();
3195 }
3196 if (qpn->isReadOnly()) {
3197 generateModifier("[read-only]");
3198 newLine();
3199 }
3200 if (qpn->isDefault()) {
3201 generateModifier("[default]");
3202 newLine();
3203 }
3204 } else if (node->isFunction()) {
3205 if (functionNode->virtualness() != "non")
3206 generateModifier("virtual");
3207 if (functionNode->isConst())
3208 generateModifier("const");
3209 if (functionNode->isStatic())
3210 generateModifier("static");
3211
3212 if (!functionNode->isMacro() && !functionNode->isCtor() &&
3213 !functionNode->isCCtor() && !functionNode->isMCtor()
3214 && !functionNode->isDtor()) {
3215 if (functionNode->returnType() == "void")
3216 m_writer->writeEmptyElement(dbNamespace, "void");
3217 else
3218 m_writer->writeTextElement(dbNamespace, "type", functionNode->returnTypeString());
3219 newLine();
3220 }
3221 // Remove two characters from the plain name to only get the name
3222 // of the method without parentheses (only for functions, not macros).
3223 QString name = node->plainName();
3224 if (name.endsWith("()"))
3225 name.chop(2);
3226 m_writer->writeTextElement(dbNamespace, "methodname", name);
3227 newLine();
3228
3229 if (functionNode->parameters().isEmpty()) {
3230 m_writer->writeEmptyElement(dbNamespace, "void");
3231 newLine();
3232 }
3233
3234 const Parameters &lp = functionNode->parameters();
3235 for (int i = 0; i < lp.count(); ++i) {
3236 const Parameter &parameter = lp.at(i);
3237 m_writer->writeStartElement(dbNamespace, "methodparam");
3238 newLine();
3239 m_writer->writeTextElement(dbNamespace, "type", parameter.type());
3240 newLine();
3241 m_writer->writeTextElement(dbNamespace, "parameter", parameter.name());
3242 newLine();
3243 if (!parameter.defaultValue().isEmpty()) {
3244 m_writer->writeTextElement(dbNamespace, "initializer", parameter.defaultValue());
3245 newLine();
3246 }
3247 m_writer->writeEndElement(); // methodparam
3248 newLine();
3249 }
3250
3251 if (functionNode->isDefault())
3252 generateModifier("default");
3253 if (functionNode->isFinal())
3254 generateModifier("final");
3255 if (functionNode->isOverride())
3256 generateModifier("override");
3257 } else if (node->isTypedef()) {
3258 m_writer->writeTextElement(dbNamespace, "typedefname", node->plainName());
3259 newLine();
3260 } else {
3261 node->doc().location().warning(
3262 QStringLiteral("Unexpected node type in generateDocBookSynopsis: %1")
3263 .arg(node->nodeTypeString()));
3264 newLine();
3265 }
3266
3267 // Enums and typedefs.
3268 if (enumNode) {
3269 for (const EnumItem &item : enumNode->items()) {
3270 m_writer->writeStartElement(dbNamespace, "enumitem");
3271 newLine();
3272 m_writer->writeTextElement(dbNamespace, "enumidentifier", item.name());
3273 newLine();
3274 m_writer->writeTextElement(dbNamespace, "enumvalue", item.value());
3275 newLine();
3276 m_writer->writeEndElement(); // enumitem
3277 newLine();
3278 }
3279
3280 if (enumNode->items().isEmpty()) {
3281 // If the enumeration is empty (really rare case), still produce
3282 // something for the DocBook document to be valid.
3283 m_writer->writeStartElement(dbNamespace, "enumitem");
3284 newLine();
3285 m_writer->writeEmptyElement(dbNamespace, "enumidentifier");
3286 newLine();
3287 m_writer->writeEndElement(); // enumitem
3288 newLine();
3289 }
3290 }
3291
3292 // Below: only synopsisinfo within synopsisTag. These elements must be at
3293 // the end of the tag, as per DocBook grammar.
3294
3295 // Information for functions that could not be output previously
3296 // (synopsisinfo).
3297 if (node->isFunction()) {
3298 generateSynopsisInfo("meta", functionNode->metanessString());
3299
3300 if (functionNode->isOverload()) {
3301 generateSynopsisInfo("overload", "overload");
3302 generateSynopsisInfo("overload-number",
3303 QString::number(functionNode->overloadNumber()));
3304 }
3305
3306 if (functionNode->isRef())
3307 generateSynopsisInfo("refness", QString::number(1));
3308 else if (functionNode->isRefRef())
3309 generateSynopsisInfo("refness", QString::number(2));
3310
3311 if (functionNode->hasAssociatedProperties()) {
3312 QStringList associatedProperties;
3313 const auto &nodes = functionNode->associatedProperties();
3314 for (const Node *n : nodes) {
3315 const auto pn = static_cast<const PropertyNode *>(n);
3316 associatedProperties << pn->name();
3317 }
3318 associatedProperties.sort();
3319 generateSynopsisInfo("associated-property",
3320 associatedProperties.join(QLatin1Char(',')));
3321 }
3322
3323 QString signature = functionNode->signature(Node::SignatureReturnType);
3324 // 'const' is already part of FunctionNode::signature()
3325 if (functionNode->isFinal())
3326 signature += " final";
3327 if (functionNode->isOverride())
3328 signature += " override";
3329 if (functionNode->isPureVirtual())
3330 signature += " = 0";
3331 else if (functionNode->isDefault())
3332 signature += " = default";
3333 generateSynopsisInfo("signature", signature);
3334 }
3335
3336 // Accessibility status.
3337 if (!node->isPageNode() && !node->isCollectionNode()) {
3338 switch (node->access()) {
3339 case Access::Public:
3340 generateSynopsisInfo("access", "public");
3341 break;
3342 case Access::Protected:
3343 generateSynopsisInfo("access", "protected");
3344 break;
3345 case Access::Private:
3346 generateSynopsisInfo("access", "private");
3347 break;
3348 default:
3349 break;
3350 }
3351 if (node->isAbstract())
3352 generateSynopsisInfo("abstract", "true");
3353 }
3354
3355 // Status.
3356 switch (node->status()) {
3357 case Node::Active:
3358 generateSynopsisInfo("status", "active");
3359 break;
3360 case Node::Preliminary:
3361 generateSynopsisInfo("status", "preliminary");
3362 break;
3363 case Node::Deprecated:
3364 generateSynopsisInfo("status", "deprecated");
3365 break;
3366 case Node::Internal:
3367 generateSynopsisInfo("status", "internal");
3368 break;
3369 default:
3370 generateSynopsisInfo("status", "main");
3371 break;
3372 }
3373
3374 // C++ classes and name spaces.
3375 if (aggregate) {
3376 // Includes.
3377 if (aggregate->includeFile()) generateSynopsisInfo("headers", *aggregate->includeFile());
3378
3379 // Since and project.
3380 if (!aggregate->since().isEmpty())
3381 generateSynopsisInfo("since", formatSince(aggregate));
3382
3383 if (aggregate->nodeType() == Node::Class || aggregate->nodeType() == Node::Namespace) {
3384 // CMake and QT variable.
3385 if (!aggregate->physicalModuleName().isEmpty()) {
3386 const CollectionNode *cn =
3387 m_qdb->getCollectionNode(aggregate->physicalModuleName(), Node::Module);
3388
3389 if (const auto result = cmakeRequisite(cn)) {
3390 generateSynopsisInfo("cmake-find-package", result->first);
3391 generateSynopsisInfo("cmake-target-link-libraries", result->second);
3392 }
3393
3394 if (cn && !cn->qtVariable().isEmpty())
3395 generateSynopsisInfo("qmake", "QT += " + cn->qtVariable());
3396 }
3397 }
3398
3399 if (aggregate->nodeType() == Node::Class) {
3400 // Native type
3401 auto *classe = const_cast<ClassNode *>(static_cast<const ClassNode *>(aggregate));
3402 if (classe && classe->isQmlNativeType() && classe->status() != Node::Internal) {
3403 m_writer->writeStartElement(dbNamespace, "synopsisinfo");
3404 m_writer->writeAttribute("role", "nativeTypeFor");
3405
3406 QList<QmlTypeNode *> nativeTypes { classe->qmlNativeTypes().cbegin(), classe->qmlNativeTypes().cend()};
3407 std::sort(nativeTypes.begin(), nativeTypes.end(), Node::nodeNameLessThan);
3408
3409 for (auto item : std::as_const(nativeTypes)) {
3410 const Node *otherNode{nullptr};
3411 Atom a = Atom(Atom::LinkNode, CodeMarker::stringForNode(item));
3412 const QString &link = getAutoLink(&a, aggregate, &otherNode);
3413 generateSimpleLink(link, item->name());
3414 }
3415
3416 m_writer->writeEndElement(); // synopsisinfo
3417 }
3418
3419 // Inherits.
3420 QList<RelatedClass>::ConstIterator r;
3421 if (!classe->baseClasses().isEmpty()) {
3422 m_writer->writeStartElement(dbNamespace, "synopsisinfo");
3423 m_writer->writeAttribute("role", "inherits");
3424
3425 r = classe->baseClasses().constBegin();
3426 int index = 0;
3427 while (r != classe->baseClasses().constEnd()) {
3428 if ((*r).m_node) {
3429 generateFullName((*r).m_node, classe);
3430
3431 if ((*r).m_access == Access::Protected) {
3432 m_writer->writeCharacters(" (protected)");
3433 } else if ((*r).m_access == Access::Private) {
3434 m_writer->writeCharacters(" (private)");
3435 }
3436 m_writer->writeCharacters(
3437 Utilities::comma(index++, classe->baseClasses().size()));
3438 }
3439 ++r;
3440 }
3441
3442 m_writer->writeEndElement(); // synopsisinfo
3443 newLine();
3444 }
3445
3446 // Inherited by.
3447 if (!classe->derivedClasses().isEmpty()) {
3448 m_writer->writeStartElement(dbNamespace, "synopsisinfo");
3449 m_writer->writeAttribute("role", "inheritedBy");
3450 generateSortedNames(classe, classe->derivedClasses());
3451 m_writer->writeEndElement(); // synopsisinfo
3452 newLine();
3453 }
3454 }
3455 }
3456
3457 // QML types.
3458 if (qcn) {
3459 // Module name and version (i.e. import).
3460 QString logicalModuleVersion;
3461 const CollectionNode *collection =
3462 m_qdb->getCollectionNode(qcn->logicalModuleName(), qcn->nodeType());
3463 if (collection)
3464 logicalModuleVersion = collection->logicalModuleVersion();
3465 else
3466 logicalModuleVersion = qcn->logicalModuleVersion();
3467
3468 QStringList importText;
3469 importText << "import " + qcn->logicalModuleName();
3470 if (!logicalModuleVersion.isEmpty())
3471 importText << logicalModuleVersion;
3472 generateSynopsisInfo("import", importText.join(' '));
3473
3474 // Since and project.
3475 if (!qcn->since().isEmpty())
3476 generateSynopsisInfo("since", formatSince(qcn));
3477
3478 // Inherited by.
3479 NodeList subs;
3480 QmlTypeNode::subclasses(qcn, subs);
3481 if (!subs.isEmpty()) {
3482 m_writer->writeTextElement(dbNamespace, "synopsisinfo");
3483 m_writer->writeAttribute("role", "inheritedBy");
3484 generateSortedQmlNames(qcn, subs);
3485 m_writer->writeEndElement(); // synopsisinfo
3486 newLine();
3487 }
3488
3489 // Inherits.
3490 QmlTypeNode *base = qcn->qmlBaseNode();
3491 while (base && base->isInternal())
3492 base = base->qmlBaseNode();
3493 if (base) {
3494 const Node *otherNode = nullptr;
3495 Atom a = Atom(Atom::LinkNode, CodeMarker::stringForNode(base));
3496 QString link = getAutoLink(&a, base, &otherNode);
3497
3498 m_writer->writeTextElement(dbNamespace, "synopsisinfo");
3499 m_writer->writeAttribute("role", "inherits");
3500 generateSimpleLink(link, base->name());
3501 m_writer->writeEndElement(); // synopsisinfo
3502 newLine();
3503 }
3504
3505 // Native type
3506 ClassNode *cn = (const_cast<QmlTypeNode *>(qcn))->classNode();
3507
3508 if (cn && cn->isQmlNativeType() && (cn->status() != Node::Internal)) {
3509 const Node *otherNode = nullptr;
3510 Atom a = Atom(Atom::LinkNode, CodeMarker::stringForNode(qcn));
3511 QString link = getAutoLink(&a, cn, &otherNode);
3512
3513 m_writer->writeTextElement(dbNamespace, "synopsisinfo");
3514 m_writer->writeAttribute("role", "nativeType");
3515 generateSimpleLink(link, cn->name());
3516 m_writer->writeEndElement(); // synopsisinfo
3517 newLine();
3518 }
3519 }
3520
3521 // Thread safeness.
3522 switch (node->threadSafeness()) {
3523 case Node::UnspecifiedSafeness:
3524 generateSynopsisInfo("threadsafeness", "unspecified");
3525 break;
3526 case Node::NonReentrant:
3527 generateSynopsisInfo("threadsafeness", "non-reentrant");
3528 break;
3529 case Node::Reentrant:
3530 generateSynopsisInfo("threadsafeness", "reentrant");
3531 break;
3532 case Node::ThreadSafe:
3533 generateSynopsisInfo("threadsafeness", "thread safe");
3534 break;
3535 default:
3536 generateSynopsisInfo("threadsafeness", "unspecified");
3537 break;
3538 }
3539
3540 // Module.
3541 if (!node->physicalModuleName().isEmpty())
3542 generateSynopsisInfo("module", node->physicalModuleName());
3543
3544 // Group.
3545 if (classNode && !classNode->groupNames().isEmpty()) {
3546 generateSynopsisInfo("groups", classNode->groupNames().join(QLatin1Char(',')));
3547 } else if (qcn && !qcn->groupNames().isEmpty()) {
3548 generateSynopsisInfo("groups", qcn->groupNames().join(QLatin1Char(',')));
3549 }
3550
3551 // Properties.
3552 if (propertyNode) {
3553 for (const Node *fnNode : propertyNode->getters()) {
3554 if (fnNode) {
3555 const auto funcNode = static_cast<const FunctionNode *>(fnNode);
3556 generateSynopsisInfo("getter", funcNode->name());
3557 }
3558 }
3559 for (const Node *fnNode : propertyNode->setters()) {
3560 if (fnNode) {
3561 const auto funcNode = static_cast<const FunctionNode *>(fnNode);
3562 generateSynopsisInfo("setter", funcNode->name());
3563 }
3564 }
3565 for (const Node *fnNode : propertyNode->resetters()) {
3566 if (fnNode) {
3567 const auto funcNode = static_cast<const FunctionNode *>(fnNode);
3568 generateSynopsisInfo("resetter", funcNode->name());
3569 }
3570 }
3571 for (const Node *fnNode : propertyNode->notifiers()) {
3572 if (fnNode) {
3573 const auto funcNode = static_cast<const FunctionNode *>(fnNode);
3574 generateSynopsisInfo("notifier", funcNode->name());
3575 }
3576 }
3577 }
3578
3579 m_writer->writeEndElement(); // nodeToSynopsisTag (like classsynopsis)
3580 newLine();
3581
3582 // The typedef associated to this enum. It is output *after* the main tag,
3583 // i.e. it must be after the synopsisinfo.
3584 if (enumNode && enumNode->flagsType()) {
3585 m_writer->writeStartElement(dbNamespace, "typedefsynopsis");
3586 newLine();
3587
3588 m_writer->writeTextElement(dbNamespace, "typedefname",
3589 enumNode->flagsType()->fullDocumentName());
3590 newLine();
3591
3592 m_writer->writeEndElement(); // typedefsynopsis
3593 newLine();
3594 }
3595}
3596
3598{
3599 // From CodeMarker::taggedNode, but without the tag part (i.e. only the QML specific case
3600 // remaining).
3601 // TODO: find a better name for this.
3602 if (node->nodeType() == Node::QmlType && node->name().startsWith(QLatin1String("QML:")))
3603 return node->name().mid(4);
3604 return node->name();
3605}
3606
3607/*!
3608 Parses a string with method/variable name and (return) type
3609 to include type tags.
3610 */
3611void DocBookGenerator::typified(const QString &string, const Node *relative, bool trailingSpace,
3612 bool generateType)
3613{
3614 // Adapted from CodeMarker::typified and HtmlGenerator::highlightedCode.
3615 QString result;
3616 QString pendingWord;
3617
3618 for (int i = 0; i <= string.size(); ++i) {
3619 QChar ch;
3620 if (i != string.size())
3621 ch = string.at(i);
3622
3623 QChar lower = ch.toLower();
3624 if ((lower >= QLatin1Char('a') && lower <= QLatin1Char('z')) || ch.digitValue() >= 0
3625 || ch == QLatin1Char('_') || ch == QLatin1Char(':')) {
3626 pendingWord += ch;
3627 } else {
3628 if (!pendingWord.isEmpty()) {
3629 bool isProbablyType = (pendingWord != QLatin1String("const"));
3630 if (generateType && isProbablyType) {
3631 // Flush the current buffer.
3632 m_writer->writeCharacters(result);
3633 result.truncate(0);
3634
3635 // Add the link, logic from HtmlGenerator::highlightedCode.
3636 const Node *n = m_qdb->findTypeNode(pendingWord, relative, Node::DontCare);
3637 QString href;
3638 if (!(n && n->isQmlBasicType())
3639 || (relative
3640 && (relative->genus() == n->genus() || Node::DontCare == n->genus()))) {
3641 href = linkForNode(n, relative);
3642 }
3643
3644 m_writer->writeStartElement(dbNamespace, "type");
3645 if (href.isEmpty())
3646 m_writer->writeCharacters(pendingWord);
3647 else
3648 generateSimpleLink(href, pendingWord);
3649 m_writer->writeEndElement(); // type
3650 } else {
3651 result += pendingWord;
3652 }
3653 }
3654 pendingWord.clear();
3655
3656 if (ch.unicode() != '\0')
3657 result += ch;
3658 }
3659 }
3660
3661 if (trailingSpace && string.size()) {
3662 if (!string.endsWith(QLatin1Char('*')) && !string.endsWith(QLatin1Char('&')))
3663 result += QLatin1Char(' ');
3664 }
3665
3666 m_writer->writeCharacters(result);
3667}
3668
3669void DocBookGenerator::generateSynopsisName(const Node *node, const Node *relative,
3670 bool generateNameLink)
3671{
3672 // Implements the rewriting of <@link> from HtmlGenerator::highlightedCode, only due to calls to
3673 // CodeMarker::linkTag in CppCodeMarker::markedUpSynopsis.
3674 QString name = taggedNode(node);
3675
3676 if (!generateNameLink) {
3677 m_writer->writeCharacters(name);
3678 return;
3679 }
3680
3681 m_writer->writeStartElement(dbNamespace, "emphasis");
3682 m_writer->writeAttribute("role", "bold");
3683 generateSimpleLink(linkForNode(node, relative), name);
3684 m_writer->writeEndElement(); // emphasis
3685}
3686
3687void DocBookGenerator::generateParameter(const Parameter &parameter, const Node *relative,
3688 bool generateExtra, bool generateType)
3689{
3690 const QString &pname = parameter.name();
3691 const QString &ptype = parameter.type();
3692 QString paramName;
3693 if (!pname.isEmpty()) {
3694 typified(ptype, relative, true, generateType);
3695 paramName = pname;
3696 } else {
3697 paramName = ptype;
3698 }
3699
3700 if (generateExtra || pname.isEmpty()) {
3701 m_writer->writeStartElement(dbNamespace, "emphasis");
3702 m_writer->writeCharacters(paramName);
3703 m_writer->writeEndElement(); // emphasis
3704 }
3705
3706 const QString &pvalue = parameter.defaultValue();
3707 if (generateExtra && !pvalue.isEmpty())
3708 m_writer->writeCharacters(" = " + pvalue);
3709}
3710
3711void DocBookGenerator::generateSynopsis(const Node *node, const Node *relative,
3712 Section::Style style)
3713{
3714 // From HtmlGenerator::generateSynopsis (conditions written as booleans).
3715 const bool generateExtra = style != Section::AllMembers;
3716 const bool generateType = style != Section::Details;
3717 const bool generateNameLink = style != Section::Details;
3718
3719 // From CppCodeMarker::markedUpSynopsis, reversed the generation of "extra" and "synopsis".
3720 const int MaxEnumValues = 6;
3721
3722 if (generateExtra) {
3723 if (auto extra = CodeMarker::extraSynopsis(node, style); !extra.isEmpty())
3724 m_writer->writeCharacters(extra + " ");
3725 }
3726
3727 // Then generate the synopsis.
3728 QString namePrefix {};
3729 if (style == Section::Details) {
3730 if (!node->isRelatedNonmember() && !node->isProxyNode() && !node->parent()->name().isEmpty()
3731 && !node->parent()->isHeader() && !node->isProperty() && !node->isQmlNode()) {
3732 namePrefix = taggedNode(node->parent()) + "::";
3733 }
3734 }
3735
3736 switch (node->nodeType()) {
3737 case Node::Namespace:
3738 m_writer->writeCharacters("namespace ");
3739 m_writer->writeCharacters(namePrefix);
3740 generateSynopsisName(node, relative, generateNameLink);
3741 break;
3742 case Node::Class:
3743 m_writer->writeCharacters("class ");
3744 m_writer->writeCharacters(namePrefix);
3745 generateSynopsisName(node, relative, generateNameLink);
3746 break;
3747 case Node::Function: {
3748 const auto func = (const FunctionNode *)node;
3749
3750 // First, the part coming before the name.
3751 if (style == Section::Summary || style == Section::Accessors) {
3752 if (!func->isNonvirtual())
3753 m_writer->writeCharacters(QStringLiteral("virtual "));
3754 }
3755
3756 // Name and parameters.
3757 if (style != Section::AllMembers && !func->returnType().isEmpty())
3758 typified(func->returnTypeString(), relative, true, generateType);
3759 m_writer->writeCharacters(namePrefix);
3760 generateSynopsisName(node, relative, generateNameLink);
3761
3762 if (!func->isMacroWithoutParams()) {
3763 m_writer->writeCharacters(QStringLiteral("("));
3764 if (!func->parameters().isEmpty()) {
3765 const Parameters &parameters = func->parameters();
3766 for (int i = 0; i < parameters.count(); i++) {
3767 if (i > 0)
3768 m_writer->writeCharacters(QStringLiteral(", "));
3769 generateParameter(parameters.at(i), relative, generateExtra, generateType);
3770 }
3771 }
3772 m_writer->writeCharacters(QStringLiteral(")"));
3773 }
3774
3775 if (func->isConst())
3776 m_writer->writeCharacters(QStringLiteral(" const"));
3777
3778 if (style == Section::Summary || style == Section::Accessors) {
3779 // virtual is prepended, if needed.
3780 QString synopsis;
3781 if (func->isFinal())
3782 synopsis += QStringLiteral(" final");
3783 if (func->isOverride())
3784 synopsis += QStringLiteral(" override");
3785 if (func->isPureVirtual())
3786 synopsis += QStringLiteral(" = 0");
3787 if (func->isRef())
3788 synopsis += QStringLiteral(" &");
3789 else if (func->isRefRef())
3790 synopsis += QStringLiteral(" &&");
3791 m_writer->writeCharacters(synopsis);
3792 } else if (style == Section::AllMembers) {
3793 if (!func->returnType().isEmpty() && func->returnType() != "void") {
3794 m_writer->writeCharacters(QStringLiteral(" : "));
3795 typified(func->returnTypeString(), relative, false, generateType);
3796 }
3797 } else {
3798 QString synopsis;
3799 if (func->isRef())
3800 synopsis += QStringLiteral(" &");
3801 else if (func->isRefRef())
3802 synopsis += QStringLiteral(" &&");
3803 m_writer->writeCharacters(synopsis);
3804 }
3805 } break;
3806 case Node::Enum: {
3807 const auto enume = static_cast<const EnumNode *>(node);
3808 m_writer->writeCharacters(QStringLiteral("enum "));
3809 m_writer->writeCharacters(namePrefix);
3810 generateSynopsisName(node, relative, generateNameLink);
3811
3812 QString synopsis;
3813 if (style == Section::Summary) {
3814 synopsis += " { ";
3815
3816 QStringList documentedItems = enume->doc().enumItemNames();
3817 if (documentedItems.isEmpty()) {
3818 const auto &enumItems = enume->items();
3819 for (const auto &item : enumItems)
3820 documentedItems << item.name();
3821 }
3822 const QStringList omitItems = enume->doc().omitEnumItemNames();
3823 for (const auto &item : omitItems)
3824 documentedItems.removeAll(item);
3825
3826 if (documentedItems.size() > MaxEnumValues) {
3827 // Take the last element and keep it safe, then elide the surplus.
3828 const QString last = documentedItems.last();
3829 documentedItems = documentedItems.mid(0, MaxEnumValues - 1);
3830 documentedItems += "&#x2026;"; // Ellipsis: in HTML, &hellip;.
3831 documentedItems += last;
3832 }
3833 synopsis += documentedItems.join(QLatin1String(", "));
3834
3835 if (!documentedItems.isEmpty())
3836 synopsis += QLatin1Char(' ');
3837 synopsis += QLatin1Char('}');
3838 }
3839 m_writer->writeCharacters(synopsis);
3840 } break;
3841 case Node::TypeAlias: {
3842 if (style == Section::Details) {
3843 auto templateDecl = node->templateDecl();
3844 if (templateDecl)
3845 m_writer->writeCharacters((*templateDecl).to_qstring() + QLatin1Char(' '));
3846 }
3847 m_writer->writeCharacters(namePrefix);
3848 generateSynopsisName(node, relative, generateNameLink);
3849 } break;
3850 case Node::Typedef: {
3851 if (static_cast<const TypedefNode *>(node)->associatedEnum())
3852 m_writer->writeCharacters("flags ");
3853 m_writer->writeCharacters(namePrefix);
3854 generateSynopsisName(node, relative, generateNameLink);
3855 } break;
3856 case Node::Property: {
3857 const auto property = static_cast<const PropertyNode *>(node);
3858 m_writer->writeCharacters(namePrefix);
3859 generateSynopsisName(node, relative, generateNameLink);
3860 m_writer->writeCharacters(" : ");
3861 typified(property->qualifiedDataType(), relative, false, generateType);
3862 } break;
3863 case Node::Variable: {
3864 const auto variable = static_cast<const VariableNode *>(node);
3865 if (style == Section::AllMembers) {
3866 generateSynopsisName(node, relative, generateNameLink);
3867 m_writer->writeCharacters(" : ");
3868 typified(variable->dataType(), relative, false, generateType);
3869 } else {
3870 typified(variable->leftType(), relative, false, generateType);
3871 m_writer->writeCharacters(" ");
3872 m_writer->writeCharacters(namePrefix);
3873 generateSynopsisName(node, relative, generateNameLink);
3874 m_writer->writeCharacters(variable->rightType());
3875 }
3876 } break;
3877 default:
3878 m_writer->writeCharacters(namePrefix);
3879 generateSynopsisName(node, relative, generateNameLink);
3880 }
3881}
3882
3883void DocBookGenerator::generateEnumValue(const QString &enumValue, const Node *relative)
3884{
3885 // From CppCodeMarker::markedUpEnumValue, simplifications from Generator::plainCode (removing
3886 // <@op>). With respect to CppCodeMarker::markedUpEnumValue, the order of generation of parents
3887 // must be reversed so that they are processed in the order
3888 const auto *node = relative->parent();
3889
3890 if (relative->isQmlProperty()) {
3891 const auto *qpn = static_cast<const QmlPropertyNode*>(relative);
3892 if (qpn->enumNode() && !enumValue.startsWith("%1."_L1.arg(qpn->enumPrefix()))) {
3893 m_writer->writeCharacters("%1.%2"_L1.arg(qpn->enumPrefix(), enumValue));
3894 return;
3895 }
3896 }
3897
3898 if (!relative->isEnumType()) {
3899 m_writer->writeCharacters(enumValue);
3900 return;
3901 }
3902
3903 QList<const Node *> parents;
3904 while (!node->isHeader() && node->parent()) {
3905 parents.prepend(node);
3906 if (node->parent() == relative || node->parent()->name().isEmpty())
3907 break;
3908 node = node->parent();
3909 }
3910 if (static_cast<const EnumNode *>(relative)->isScoped())
3911 parents << relative;
3912
3913 m_writer->writeStartElement(dbNamespace, "code");
3914 for (auto parent : parents) {
3915 generateSynopsisName(parent, relative, true);
3916 m_writer->writeCharacters("::");
3917 }
3918
3919 m_writer->writeCharacters(enumValue);
3920 m_writer->writeEndElement(); // code
3921}
3922
3923/*!
3924 If the node is an overloaded signal, and a node with an
3925 example on how to connect to it
3926
3927 Someone didn't finish writing this comment, and I don't know what this
3928 function is supposed to do, so I have not tried to complete the comment
3929 yet.
3930 */
3931void DocBookGenerator::generateOverloadedSignal(const Node *node)
3932{
3933 // From Generator::generateOverloadedSignal.
3934 QString code = getOverloadedSignalCode(node);
3935 if (code.isEmpty())
3936 return;
3937
3938 m_writer->writeStartElement(dbNamespace, "note");
3939 newLine();
3940 m_writer->writeStartElement(dbNamespace, "para");
3941 m_writer->writeCharacters("Signal ");
3942 m_writer->writeTextElement(dbNamespace, "emphasis", node->name());
3943 m_writer->writeCharacters(" is overloaded in this class. To connect to this "
3944 "signal by using the function pointer syntax, Qt "
3945 "provides a convenient helper for obtaining the "
3946 "function pointer as shown in this example:");
3947 m_writer->writeTextElement(dbNamespace, "code", code);
3948 m_writer->writeEndElement(); // para
3949 newLine();
3950 m_writer->writeEndElement(); // note
3951 newLine();
3952}
3953
3954/*!
3955 Generates an addendum note of type \a type for \a node. \a marker
3956 is unused in this generator.
3957*/
3959 bool generateNote)
3960{
3961 Q_UNUSED(marker)
3962 Q_ASSERT(node && !node->name().isEmpty());
3963 if (generateNote) {
3964 m_writer->writeStartElement(dbNamespace, "note");
3965 newLine();
3966 }
3967 switch (type) {
3968 case Invokable:
3969 m_writer->writeStartElement(dbNamespace, "para");
3970 m_writer->writeCharacters(
3971 "This function can be invoked via the meta-object system and from QML. See ");
3972 generateSimpleLink(node->url(), "Q_INVOKABLE");
3973 m_writer->writeCharacters(".");
3974 m_writer->writeEndElement(); // para
3975 newLine();
3976 break;
3977 case PrivateSignal:
3978 m_writer->writeTextElement(
3979 dbNamespace, "para",
3980 "This is a private signal. It can be used in signal connections but "
3981 "cannot be emitted by the user.");
3982 break;
3983 case QmlSignalHandler:
3984 {
3985 QString handler(node->name());
3986 int prefixLocation = handler.lastIndexOf('.', -2) + 1;
3987 handler[prefixLocation] = handler[prefixLocation].toTitleCase();
3988 handler.insert(prefixLocation, QLatin1String("on"));
3989 m_writer->writeStartElement(dbNamespace, "para");
3990 m_writer->writeCharacters("The corresponding handler is ");
3991 m_writer->writeTextElement(dbNamespace, "code", handler);
3992 m_writer->writeCharacters(".");
3993 m_writer->writeEndElement(); // para
3994 newLine();
3995 break;
3996 }
3998 {
3999 if (!node->isFunction())
4000 return;
4001 const auto *fn = static_cast<const FunctionNode *>(node);
4002 auto propertyNodes = fn->associatedProperties();
4003 if (propertyNodes.isEmpty())
4004 return;
4005 std::sort(propertyNodes.begin(), propertyNodes.end(), Node::nodeNameLessThan);
4006 for (const auto propertyNode : std::as_const(propertyNodes)) {
4007 QString msg;
4008 const auto pn = static_cast<const PropertyNode *>(propertyNode);
4009 switch (pn->role(fn)) {
4010 case PropertyNode::FunctionRole::Getter:
4011 msg = QStringLiteral("Getter function");
4012 break;
4013 case PropertyNode::FunctionRole::Setter:
4014 msg = QStringLiteral("Setter function");
4015 break;
4016 case PropertyNode::FunctionRole::Resetter:
4017 msg = QStringLiteral("Resetter function");
4018 break;
4019 case PropertyNode::FunctionRole::Notifier:
4020 msg = QStringLiteral("Notifier signal");
4021 break;
4022 default:
4023 continue;
4024 }
4025 m_writer->writeStartElement(dbNamespace, "para");
4026 m_writer->writeCharacters(msg + " for property ");
4027 generateSimpleLink(linkForNode(pn, nullptr), pn->name());
4028 m_writer->writeCharacters(". ");
4029 m_writer->writeEndElement(); // para
4030 newLine();
4031 }
4032 break;
4033 }
4034 case BindableProperty:
4035 {
4036 const Node *linkNode;
4037 Atom linkAtom = Atom(Atom::Link, "QProperty");
4038 QString link = getAutoLink(&linkAtom, node, &linkNode);
4039 m_writer->writeStartElement(dbNamespace, "para");
4040 m_writer->writeCharacters("This property supports ");
4041 generateSimpleLink(link, "QProperty");
4042 m_writer->writeCharacters(" bindings.");
4043 m_writer->writeEndElement(); // para
4044 newLine();
4045 break;
4046 }
4047 default:
4048 break;
4049 }
4050
4051 if (generateNote) {
4052 m_writer->writeEndElement(); // note
4053 newLine();
4054 }
4055}
4056
4057void DocBookGenerator::generateDetailedMember(const Node *node, const PageNode *relative)
4058{
4059 // From HtmlGenerator::generateDetailedMember.
4060 bool closeSupplementarySection = false;
4061
4062 if (node->isSharedCommentNode()) {
4063 const auto *scn = reinterpret_cast<const SharedCommentNode *>(node);
4064 const QList<Node *> &collective = scn->collective();
4065
4066 bool firstFunction = true;
4067 for (const auto *sharedNode : collective) {
4068 if (firstFunction) {
4069 startSectionBegin(sharedNode);
4070 } else {
4071 m_writer->writeStartElement(dbNamespace, "bridgehead");
4072 m_writer->writeAttribute("renderas", "sect2");
4073 writeXmlId(sharedNode);
4074 }
4075 if (m_useITS)
4076 m_writer->writeAttribute(itsNamespace, "translate", "no");
4077
4078 generateSynopsis(sharedNode, relative, Section::Details);
4079
4080 if (firstFunction) {
4081 startSectionEnd();
4082 firstFunction = false;
4083 } else {
4084 m_writer->writeEndElement(); // bridgehead
4085 newLine();
4086 }
4087 }
4088 } else {
4089 const EnumNode *etn;
4090 if (node->isEnumType() && (etn = static_cast<const EnumNode *>(node))->flagsType()) {
4091 startSectionBegin(node);
4092 if (m_useITS)
4093 m_writer->writeAttribute(itsNamespace, "translate", "no");
4094 generateSynopsis(etn, relative, Section::Details);
4095 startSectionEnd();
4096
4097 m_writer->writeStartElement(dbNamespace, "bridgehead");
4098 m_writer->writeAttribute("renderas", "sect2");
4099 generateSynopsis(etn->flagsType(), relative, Section::Details);
4100 m_writer->writeEndElement(); // bridgehead
4101 newLine();
4102 } else {
4103 startSectionBegin(node);
4104 if (m_useITS)
4105 m_writer->writeAttribute(itsNamespace, "translate", "no");
4106 generateSynopsis(node, relative, Section::Details);
4107 startSectionEnd();
4108 }
4109 }
4110 Q_ASSERT(m_hasSection);
4111
4113
4114 generateStatus(node);
4115 generateBody(node);
4116
4117 // If the body ends with a section, the rest of the description must be wrapped in a section too.
4119 closeSupplementarySection = true;
4120 startSection("", "Notes");
4121 }
4122
4123 generateOverloadedSignal(node);
4126 generateSince(node);
4127
4128 if (node->isProperty()) {
4129 const auto property = static_cast<const PropertyNode *>(node);
4131 Section section("", "", "", "", Section::Accessors);
4132
4133 section.appendMembers(property->getters().toVector());
4134 section.appendMembers(property->setters().toVector());
4135 section.appendMembers(property->resetters().toVector());
4136
4137 if (!section.members().isEmpty()) {
4138 m_writer->writeStartElement(dbNamespace, "para");
4139 newLine();
4140 m_writer->writeStartElement(dbNamespace, "emphasis");
4141 m_writer->writeAttribute("role", "bold");
4142 m_writer->writeCharacters("Access functions:");
4143 newLine();
4144 m_writer->writeEndElement(); // emphasis
4145 newLine();
4146 m_writer->writeEndElement(); // para
4147 newLine();
4148 generateSectionList(section, node);
4149 }
4150
4151 Section notifiers("", "", "", "", Section::Accessors);
4152 notifiers.appendMembers(property->notifiers().toVector());
4153
4154 if (!notifiers.members().isEmpty()) {
4155 m_writer->writeStartElement(dbNamespace, "para");
4156 newLine();
4157 m_writer->writeStartElement(dbNamespace, "emphasis");
4158 m_writer->writeAttribute("role", "bold");
4159 m_writer->writeCharacters("Notifier signal:");
4160 newLine();
4161 m_writer->writeEndElement(); // emphasis
4162 newLine();
4163 m_writer->writeEndElement(); // para
4164 newLine();
4165 generateSectionList(notifiers, node);
4166 }
4167 }
4168 } else if (node->isEnumType()) {
4169 const auto en = static_cast<const EnumNode *>(node);
4170
4171 if (m_qflagsHref.isEmpty()) {
4172 Node *qflags = m_qdb->findClassNode(QStringList("QFlags"));
4173 if (qflags)
4174 m_qflagsHref = linkForNode(qflags, nullptr);
4175 }
4176
4177 if (en->flagsType()) {
4178 m_writer->writeStartElement(dbNamespace, "para");
4179 m_writer->writeCharacters("The ");
4180 m_writer->writeStartElement(dbNamespace, "code");
4181 m_writer->writeCharacters(en->flagsType()->name());
4182 m_writer->writeEndElement(); // code
4183 m_writer->writeCharacters(" type is a typedef for ");
4184 m_writer->writeStartElement(dbNamespace, "code");
4185 generateSimpleLink(m_qflagsHref, "QFlags");
4186 m_writer->writeCharacters("<" + en->name() + ">. ");
4187 m_writer->writeEndElement(); // code
4188 m_writer->writeCharacters("It stores an OR combination of ");
4189 m_writer->writeStartElement(dbNamespace, "code");
4190 m_writer->writeCharacters(en->name());
4191 m_writer->writeEndElement(); // code
4192 m_writer->writeCharacters(" values.");
4193 m_writer->writeEndElement(); // para
4194 newLine();
4195 }
4196 }
4197
4198 if (closeSupplementarySection)
4199 endSection();
4200
4201 // The list of linked pages is always in its own section.
4203
4204 // Close the section for this member.
4205 endSection(); // section
4206}
4207
4208void DocBookGenerator::generateSectionList(const Section &section, const Node *relative,
4209 bool useObsoleteMembers)
4210{
4211 // From HtmlGenerator::generateSectionList, just generating a list (not tables).
4212 const NodeVector &members =
4213 (useObsoleteMembers ? section.obsoleteMembers() : section.members());
4214 if (!members.isEmpty()) {
4215 bool hasPrivateSignals = false;
4216 bool isInvokable = false;
4217
4218 m_writer->writeStartElement(dbNamespace, "itemizedlist");
4219 if (m_useITS)
4220 m_writer->writeAttribute(itsNamespace, "translate", "no");
4221 newLine();
4222
4223 NodeVector::ConstIterator m = members.constBegin();
4224 while (m != members.constEnd()) {
4225 if ((*m)->access() == Access::Private) {
4226 ++m;
4227 continue;
4228 }
4229
4230 m_writer->writeStartElement(dbNamespace, "listitem");
4231 newLine();
4232 m_writer->writeStartElement(dbNamespace, "para");
4233
4234 // prefix no more needed.
4235 generateSynopsis(*m, relative, section.style());
4236 if ((*m)->isFunction()) {
4237 const auto fn = static_cast<const FunctionNode *>(*m);
4238 if (fn->isPrivateSignal())
4239 hasPrivateSignals = true;
4240 else if (fn->isInvokable())
4241 isInvokable = true;
4242 }
4243
4244 m_writer->writeEndElement(); // para
4245 newLine();
4246 m_writer->writeEndElement(); // listitem
4247 newLine();
4248
4249 ++m;
4250 }
4251
4252 m_writer->writeEndElement(); // itemizedlist
4253 newLine();
4254
4255 if (hasPrivateSignals)
4257 if (isInvokable)
4258 generateAddendum(relative, Generator::Invokable, nullptr, true);
4259 }
4260
4261 if (!useObsoleteMembers && section.style() == Section::Summary
4262 && !section.inheritedMembers().isEmpty()) {
4263 m_writer->writeStartElement(dbNamespace, "itemizedlist");
4264 if (m_useITS)
4265 m_writer->writeAttribute(itsNamespace, "translate", "no");
4266 newLine();
4267
4268 generateSectionInheritedList(section, relative);
4269
4270 m_writer->writeEndElement(); // itemizedlist
4271 newLine();
4272 }
4273}
4274
4275void DocBookGenerator::generateSectionInheritedList(const Section &section, const Node *relative)
4276{
4277 // From HtmlGenerator::generateSectionInheritedList.
4278 QList<std::pair<Aggregate *, int>>::ConstIterator p = section.inheritedMembers().constBegin();
4279 while (p != section.inheritedMembers().constEnd()) {
4280 m_writer->writeStartElement(dbNamespace, "listitem");
4281 m_writer->writeCharacters(QString::number((*p).second) + u' ');
4282 if ((*p).second == 1)
4283 m_writer->writeCharacters(section.singular());
4284 else
4285 m_writer->writeCharacters(section.plural());
4286 m_writer->writeCharacters(" inherited from ");
4287 generateSimpleLink(fileName((*p).first) + '#'
4288 + Generator::cleanRef(section.title().toLower()),
4289 (*p).first->plainFullName(relative));
4290 ++p;
4291 }
4292}
4293
4294/*!
4295 Generate the DocBook page for an entity that doesn't map
4296 to any underlying parsable C++ or QML element.
4297 */
4299{
4300 // From HtmlGenerator::generatePageNode, remove anything related to TOCs.
4301 Q_ASSERT(m_writer == nullptr);
4302 m_writer = startDocument(pn);
4303
4304 generateHeader(pn->fullTitle(), pn->subtitle(), pn);
4305 generateBody(pn);
4306 generateAlsoList(pn);
4308
4309 endDocument();
4310}
4311
4312/*!
4313 Generate the DocBook page for a QML type. \qcn is the QML type.
4314 */
4316{
4317 // From HtmlGenerator::generateQmlTypePage.
4318 // Start producing the DocBook file.
4319 Q_ASSERT(m_writer == nullptr);
4320 m_writer = startDocument(qcn);
4321
4323 QString title = qcn->fullTitle();
4324 if (qcn->isQmlBasicType())
4325 title.append(" QML Value Type");
4326 else
4327 title.append(" QML Type");
4328 // TODO: for ITS attribute, only apply translate="no" on qcn->fullTitle(),
4329 // not its suffix (which should be translated). generateHeader doesn't
4330 // allow this kind of input, the title isn't supposed to be structured.
4331 // Ideally, do the same in HTML.
4332
4333 generateHeader(title, qcn->subtitle(), qcn);
4335 generateStatus(qcn);
4336
4337 startSection("details", "Detailed Description");
4338 generateBody(qcn);
4339
4340 generateAlsoList(qcn);
4341
4342 endSection();
4343
4344 Sections sections(qcn);
4345 for (const auto &section : sections.stdQmlTypeDetailsSections()) {
4346 if (!section.isEmpty()) {
4347 startSection(section.title().toLower(), section.title());
4348
4349 for (const auto &member : section.members())
4350 generateDetailedQmlMember(member, qcn);
4351
4352 endSection();
4353 }
4354 }
4355
4356 generateObsoleteQmlMembers(sections);
4357
4360
4361 endDocument();
4362}
4363
4364/*!
4365 Outputs the DocBook detailed documentation for a section
4366 on a QML element reference page.
4367 */
4368void DocBookGenerator::generateDetailedQmlMember(Node *node, const Aggregate *relative)
4369{
4370 // From HtmlGenerator::generateDetailedQmlMember, with elements from
4371 // CppCodeMarker::markedUpQmlItem and HtmlGenerator::generateQmlItem.
4372 auto getQmlPropertyTitle = [&](QmlPropertyNode *n) {
4373 QString title{CodeMarker::extraSynopsis(n, Section::Details)};
4374 if (!title.isEmpty())
4375 title += ' '_L1;
4376 // Finalise generation of name, as per CppCodeMarker::markedUpQmlItem.
4377 if (n->isAttached())
4378 title += n->element() + QLatin1Char('.');
4379 title += n->name() + " : " + n->dataType();
4380
4381 return title;
4382 };
4383
4384 auto generateQmlMethodTitle = [&](Node *node) {
4385 generateSynopsis(node, relative, Section::Details);
4386 };
4387
4388 if (node->isPropertyGroup()) {
4389 const auto *scn = static_cast<const SharedCommentNode *>(node);
4390
4391 QString heading;
4392 if (!scn->name().isEmpty())
4393 heading = scn->name() + " group";
4394 else
4395 heading = node->name();
4396 startSection(scn, heading);
4397 // This last call creates a title for this section. In other words,
4398 // titles are forbidden for the rest of the section, hence the use of
4399 // bridgehead.
4400
4401 const QList<Node *> sharedNodes = scn->collective();
4402 for (const auto &sharedNode : sharedNodes) {
4403 if (sharedNode->isQmlProperty()) {
4404 auto *qpn = static_cast<QmlPropertyNode *>(sharedNode);
4405
4406 m_writer->writeStartElement(dbNamespace, "bridgehead");
4407 m_writer->writeAttribute("renderas", "sect2");
4408 writeXmlId(qpn);
4409 m_writer->writeCharacters(getQmlPropertyTitle(qpn));
4410 m_writer->writeEndElement(); // bridgehead
4411 newLine();
4412
4413 generateDocBookSynopsis(qpn);
4414 }
4415 }
4416 } else if (node->isQmlProperty()) {
4417 auto qpn = static_cast<QmlPropertyNode *>(node);
4418 startSection(qpn, getQmlPropertyTitle(qpn));
4420 } else if (node->isSharedCommentNode()) {
4421 const auto scn = reinterpret_cast<const SharedCommentNode *>(node);
4422 const QList<Node *> &sharedNodes = scn->collective();
4423
4424 // In the section, generate a title for the first node, then bridgeheads for
4425 // the next ones.
4426 int i = 0;
4427 for (const auto &sharedNode : sharedNodes) {
4428 // Ignore this element if there is nothing to generate.
4429 if (!sharedNode->isFunction(Node::QML) && !sharedNode->isQmlProperty()) {
4430 continue;
4431 }
4432
4433 // Write the tag containing the title.
4434 if (i == 0) {
4435 startSectionBegin(sharedNode);
4436 } else {
4437 m_writer->writeStartElement(dbNamespace, "bridgehead");
4438 m_writer->writeAttribute("renderas", "sect2");
4439 }
4440
4441 // Write the title.
4442 if (sharedNode->isFunction(Node::QML))
4443 generateQmlMethodTitle(sharedNode);
4444 else if (sharedNode->isQmlProperty())
4445 m_writer->writeCharacters(
4446 getQmlPropertyTitle(static_cast<QmlPropertyNode *>(sharedNode)));
4447
4448 // Complete the title and the synopsis.
4449 if (i == 0)
4450 startSectionEnd();
4451 else
4452 m_writer->writeEndElement(); // bridgehead
4453 generateDocBookSynopsis(sharedNode);
4454 ++i;
4455 }
4456
4457 // If the list is empty, still generate a section.
4458 if (i == 0) {
4459 startSectionBegin(refForNode(node));
4460
4462 generateQmlMethodTitle(node);
4463 else if (node->isQmlProperty())
4464 m_writer->writeCharacters(
4465 getQmlPropertyTitle(static_cast<QmlPropertyNode *>(node)));
4466
4467 startSectionEnd();
4468 }
4469 } else { // assume the node is a method/signal handler
4470 startSectionBegin(node);
4471 generateQmlMethodTitle(node);
4472 startSectionEnd();
4473 }
4474
4475 generateStatus(node);
4476 generateBody(node);
4478 generateSince(node);
4480
4481 endSection();
4482}
4483
4484/*!
4485 Recursive writing of DocBook files from the root \a node.
4486 */
4488{
4489 // Mainly from Generator::generateDocumentation, with parts from
4490 // Generator::generateDocumentation and WebXMLGenerator::generateDocumentation.
4491 // Don't generate nodes that are already processed, or if they're not
4492 // supposed to generate output, ie. external, index or images nodes.
4493 if (!node->url().isNull())
4494 return;
4495 if (node->isIndexNode())
4496 return;
4497 if (node->isInternal() && !m_showInternal)
4498 return;
4499 if (node->isExternalPage())
4500 return;
4501
4502 if (node->parent()) {
4503 if (node->isCollectionNode()) {
4504 /*
4505 A collection node collects: groups, C++ modules, or QML
4506 modules. Testing for a CollectionNode must be done
4507 before testing for a TextPageNode because a
4508 CollectionNode is a PageNode at this point.
4509
4510 Don't output an HTML page for the collection node unless
4511 the \group, \module, or \qmlmodule command was actually
4512 seen by qdoc in the qdoc comment for the node.
4513
4514 A key prerequisite in this case is the call to
4515 mergeCollections(cn). We must determine whether this
4516 group, module, or QML module has members in other
4517 modules. We know at this point that cn's members list
4518 contains only members in the current module. Therefore,
4519 before outputting the page for cn, we must search for
4520 members of cn in the other modules and add them to the
4521 members list.
4522 */
4523 auto cn = static_cast<CollectionNode *>(node);
4524 if (cn->wasSeen()) {
4527 } else if (cn->isGenericCollection()) {
4528 // Currently used only for the module's related orphans page
4529 // but can be generalized for other kinds of collections if
4530 // other use cases pop up.
4532 }
4533 } else if (node->isTextPageNode()) { // Pages.
4534 generatePageNode(static_cast<PageNode *>(node));
4535 } else if (node->isAggregate()) { // Aggregates.
4536 if ((node->isClassNode() || node->isHeader() || node->isNamespace())
4537 && node->docMustBeGenerated()) {
4538 generateCppReferencePage(static_cast<Aggregate *>(node));
4539 } else if (node->isQmlType()) { // Includes QML value types
4540 generateQmlTypePage(static_cast<QmlTypeNode *>(node));
4541 } else if (node->isProxyNode()) {
4542 generateProxyPage(static_cast<Aggregate *>(node));
4543 }
4544 }
4545 }
4546
4547 if (node->isAggregate()) {
4548 auto *aggregate = static_cast<Aggregate *>(node);
4549 for (auto c : aggregate->childNodes()) {
4550 if (node->isPageNode() && !node->isPrivate())
4551 generateDocumentation(c);
4552 }
4553 }
4554}
4555
4557{
4558 // Adapted from HtmlGenerator::generateProxyPage.
4559 Q_ASSERT(aggregate->isProxyNode());
4560
4561 // Start producing the DocBook file.
4562 Q_ASSERT(m_writer == nullptr);
4563 m_writer = startDocument(aggregate);
4564
4565 // Info container.
4566 generateHeader(aggregate->plainFullName(), "", aggregate);
4567
4568 // No element synopsis.
4569
4570 // Actual content.
4571 if (!aggregate->doc().isEmpty()) {
4572 startSection("details", "Detailed Description");
4573
4574 generateBody(aggregate);
4575 generateAlsoList(aggregate);
4576
4577 endSection();
4578 }
4579
4580 Sections sections(aggregate);
4581 SectionVector *detailsSections = &sections.stdDetailsSections();
4582
4583 for (const auto &section : std::as_const(*detailsSections)) {
4584 if (section.isEmpty())
4585 continue;
4586
4587 startSection(section.title().toLower(), section.title());
4588
4589 const QList<Node *> &members = section.members();
4590 for (const auto &member : members) {
4591 if (!member->isPrivate()) { // ### check necessary?
4592 if (!member->isClassNode()) {
4593 generateDetailedMember(member, aggregate);
4594 } else {
4595 startSectionBegin();
4596 generateFullName(member, aggregate);
4597 startSectionEnd();
4598
4599 generateBrief(member);
4600 endSection();
4601 }
4602 }
4603 }
4604
4605 endSection();
4606 }
4607
4609
4610 endDocument();
4611}
4612
4613/*!
4614 Generate the HTML page for a group, module, or QML module.
4615 */
4617{
4618 // Adapted from HtmlGenerator::generateCollectionNode.
4619 // Start producing the DocBook file.
4620 Q_ASSERT(m_writer == nullptr);
4621 m_writer = startDocument(cn);
4622
4623 // Info container.
4624 generateHeader(cn->fullTitle(), cn->subtitle(), cn);
4625
4626 // Element synopsis.
4628
4629 // Generate brief for C++ modules, status for all modules.
4630 if (cn->genus() != Node::DOC && cn->genus() != Node::DontCare) {
4631 if (cn->isModule())
4632 generateBrief(cn);
4633 generateStatus(cn);
4634 generateSince(cn);
4635 }
4636
4637 // Actual content.
4638 if (cn->isModule()) {
4639 if (!cn->noAutoList()) {
4641 if (!nmm.isEmpty()) {
4642 startSection("namespaces", "Namespaces");
4643 generateAnnotatedList(cn, nmm.values(), "namespaces");
4644 endSection();
4645 }
4646 nmm = cn->getMembers([](const Node *n){ return n->isClassNode(); });
4647 if (!nmm.isEmpty()) {
4648 startSection("classes", "Classes");
4649 generateAnnotatedList(cn, nmm.values(), "classes");
4650 endSection();
4651 }
4652 }
4653 }
4654
4655 bool generatedTitle = false;
4656 if (cn->isModule() && !cn->doc().briefText().isEmpty()) {
4657 startSection("details", "Detailed Description");
4658 generatedTitle = true;
4659 }
4660 // The anchor is only needed if the node has a body.
4661 else if (
4662 // generateBody generates something.
4663 !cn->doc().body().isEmpty() ||
4664 // generateAlsoList generates something.
4665 !cn->doc().alsoList().empty() ||
4666 // generateAnnotatedList generates something.
4667 (!cn->noAutoList() && (cn->isGroup() || cn->isQmlModule()))) {
4668 writeAnchor("details");
4669 }
4670
4671 generateBody(cn);
4672 generateAlsoList(cn);
4673
4674 if (!cn->noAutoList() && (cn->isGroup() || cn->isQmlModule()))
4675 generateAnnotatedList(cn, cn->members(), "members", AutoSection);
4676
4677 if (generatedTitle)
4678 endSection();
4679
4681
4682 endDocument();
4683}
4684
4685/*!
4686 Generate the HTML page for a generic collection. This is usually
4687 a collection of C++ elements that are related to an element in
4688 a different module.
4689 */
4691{
4692 // Adapted from HtmlGenerator::generateGenericCollectionPage.
4693 // TODO: factor out this code to generate a file name.
4694 QString name = cn->name().toLower();
4695 name.replace(QChar(' '), QString("-"));
4696 QString filename = cn->tree()->physicalModuleName() + "-" + name + "." + fileExtension();
4697
4698 // Start producing the DocBook file.
4699 Q_ASSERT(m_writer == nullptr);
4700 m_writer = startGenericDocument(cn, filename);
4701
4702 // Info container.
4703 generateHeader(cn->fullTitle(), cn->subtitle(), cn);
4704
4705 // Element synopsis.
4707
4708 // Actual content.
4709 m_writer->writeStartElement(dbNamespace, "para");
4710 m_writer->writeCharacters("Each function or type documented here is related to a class or "
4711 "namespace that is documented in a different module. The reference "
4712 "page for that class or namespace will link to the function or type "
4713 "on this page.");
4714 m_writer->writeEndElement(); // para
4715
4716 const CollectionNode *cnc = cn;
4717 const QList<Node *> members = cn->members();
4718 for (const auto &member : members)
4719 generateDetailedMember(member, cnc);
4720
4722
4723 endDocument();
4724}
4725
4726void DocBookGenerator::generateFullName(const Node *node, const Node *relative)
4727{
4728 Q_ASSERT(node);
4729 Q_ASSERT(relative);
4730
4731 // From Generator::appendFullName.
4732 m_writer->writeStartElement(dbNamespace, "link");
4733 m_writer->writeAttribute(xlinkNamespace, "href", fullDocumentLocation(node));
4734 m_writer->writeAttribute(xlinkNamespace, "role", targetType(node));
4735 m_writer->writeCharacters(node->fullName(relative));
4736 m_writer->writeEndElement(); // link
4737}
4738
4739void DocBookGenerator::generateFullName(const Node *apparentNode, const QString &fullName,
4740 const Node *actualNode)
4741{
4742 Q_ASSERT(apparentNode);
4743 Q_ASSERT(actualNode);
4744
4745 // From Generator::appendFullName.
4746 m_writer->writeStartElement(dbNamespace, "link");
4747 m_writer->writeAttribute(xlinkNamespace, "href", fullDocumentLocation(actualNode));
4748 m_writer->writeAttribute("role", targetType(actualNode));
4749 m_writer->writeCharacters(fullName);
4750 m_writer->writeEndElement(); // link
4751}
4752
4753QT_END_NAMESPACE
#define ATOM_LIST_BULLET
Definition atom.h:212
#define ATOM_FORMATTING_TELETYPE
Definition atom.h:207
#define ATOM_LIST_LOWERALPHA
Definition atom.h:215
#define ATOM_FORMATTING_UNDERLINE
Definition atom.h:210
#define ATOM_LIST_UPPERALPHA
Definition atom.h:218
#define ATOM_LIST_TAG
Definition atom.h:213
#define ATOM_LIST_LOWERROMAN
Definition atom.h:216
#define ATOM_FORMATTING_SUBSCRIPT
Definition atom.h:205
#define ATOM_FORMATTING_BOLD
Definition atom.h:199
#define ATOM_FORMATTING_TRADEMARK
Definition atom.h:208
#define ATOM_LIST_VALUE
Definition atom.h:214
#define ATOM_FORMATTING_ITALIC
Definition atom.h:201
#define ATOM_LIST_UPPERROMAN
Definition atom.h:219
#define ATOM_FORMATTING_LINK
Definition atom.h:202
#define ATOM_FORMATTING_SUPERSCRIPT
Definition atom.h:206
#define ATOM_FORMATTING_UICONTROL
Definition atom.h:209
#define ATOM_FORMATTING_PARAMETER
Definition atom.h:203
The Atom class is the fundamental unit for representing documents internally.
Definition atom.h:18
AtomType type() const
Return the type of this atom.
Definition atom.h:149
@ CaptionLeft
Definition atom.h:28
@ ListTagLeft
Definition atom.h:64
@ DivRight
Definition atom.h:39
@ GeneratedList
Definition atom.h:49
@ BriefRight
Definition atom.h:26
@ CodeQuoteArgument
Definition atom.h:32
@ WarningLeft
Definition atom.h:104
@ SinceList
Definition atom.h:86
@ Keyword
Definition atom.h:56
@ TableHeaderRight
Definition atom.h:96
@ FormatElse
Definition atom.h:44
@ InlineImage
Definition atom.h:55
@ TableRowRight
Definition atom.h:98
@ LineBreak
Definition atom.h:59
@ SnippetCommand
Definition atom.h:89
@ TableRowLeft
Definition atom.h:97
@ Nop
Definition atom.h:71
@ LegaleseRight
Definition atom.h:58
@ ListTagRight
Definition atom.h:65
@ CaptionRight
Definition atom.h:29
@ NavLink
Definition atom.h:70
@ ListItemNumber
Definition atom.h:63
@ SinceTagRight
Definition atom.h:88
@ DetailsLeft
Definition atom.h:36
@ RawString
Definition atom.h:79
@ Target
Definition atom.h:102
@ AnnotatedList
Definition atom.h:21
@ SectionRight
Definition atom.h:81
@ SectionHeadingLeft
Definition atom.h:82
@ TableLeft
Definition atom.h:93
@ ListItemRight
Definition atom.h:67
@ Image
Definition atom.h:51
@ ListItemLeft
Definition atom.h:66
@ String
Definition atom.h:92
@ ListLeft
Definition atom.h:62
@ NavAutoLink
Definition atom.h:69
@ CodeQuoteCommand
Definition atom.h:33
@ BriefLeft
Definition atom.h:25
@ ImageText
Definition atom.h:52
@ LegaleseLeft
Definition atom.h:57
@ ParaRight
Definition atom.h:75
@ FormattingLeft
Definition atom.h:47
@ FormattingRight
Definition atom.h:48
@ SectionHeadingRight
Definition atom.h:83
@ Link
Definition atom.h:60
@ ImportantLeft
Definition atom.h:53
@ FormatEndif
Definition atom.h:45
@ BR
Definition atom.h:24
@ DetailsRight
Definition atom.h:37
@ AutoLink
Definition atom.h:22
@ SnippetLocation
Definition atom.h:91
@ TableHeaderLeft
Definition atom.h:95
@ ComparesRight
Definition atom.h:35
@ SectionLeft
Definition atom.h:80
@ LinkNode
Definition atom.h:61
@ HR
Definition atom.h:50
@ DivLeft
Definition atom.h:38
@ TableItemLeft
Definition atom.h:99
@ BaseName
Definition atom.h:23
@ TableOfContents
Definition atom.h:101
@ ComparesLeft
Definition atom.h:34
@ FormatIf
Definition atom.h:46
@ SnippetIdentifier
Definition atom.h:90
@ NoteLeft
Definition atom.h:72
const Atom * next() const
Return the next atom in the atom list.
Definition atom.h:146
The ClassNode represents a C++ class.
Definition classnode.h:21
bool isQmlNativeType()
Definition classnode.h:52
virtual Atom::AtomType atomType() const
Definition codemarker.h:23
A class for holding the members of a collection of doc pages.
const NodeList & members() const
NodeMap getMembers(Node::NodeType type) const
Returns a map containing this collection node's member nodes with a specified node type.
bool wasSeen() const override
Returns the seen flag data member of this node if it is a NamespaceNode or a CollectionNode.
void generatePageNode(PageNode *pn)
Generate the DocBook page for an entity that doesn't map to any underlying parsable C++ or QML elemen...
void generateQmlTypePage(QmlTypeNode *qcn)
Generate the DocBook page for a QML type.
void generateAlsoList(const Node *node) override
void generateQmlRequisites(const QmlTypeNode *qcn)
Lists the required imports and includes.
void generateSortedQmlNames(const Node *base, const NodeList &subs)
void initializeGenerator() override
Initializes the DocBook output generator's data structures from the configuration (Config).
void generateExampleFilePage(const Node *en, ResolvedFile resolved_file, CodeMarker *=nullptr) override
Generate a file with the contents of a C++ or QML source file.
bool generateText(const Text &text, const Node *relative) override
Generate the documentation for relative.
void generateCppReferencePage(Node *node)
Generate a reference page for the C++ class, namespace, or header file documented in node.
void generateHeader(const QString &title, const QString &subtitle, const Node *node)
Generate the DocBook header for the file, including the abstract.
DocBookGenerator(FileResolver &file_resolver)
bool generateThreadSafeness(const Node *node)
Generates text that explains how threadsafe and/or reentrant node is.
void generateCollectionNode(CollectionNode *cn)
Generate the HTML page for a group, module, or QML module.
QString format() override
void generateDocBookSynopsis(const Node *node)
Generate the metadata for the given node in DocBook.
void generateGenericCollectionPage(CollectionNode *cn)
Generate the HTML page for a generic collection.
void generateList(const Node *relative, const QString &selector, Qt::SortOrder sortOrder=Qt::AscendingOrder)
void generateBody(const Node *node)
Generate the body of the documentation from the qdoc comment found with the entity represented by the...
qsizetype generateAtom(const Atom *atom, const Node *relative, CodeMarker *) override
Generate DocBook from an instance of Atom.
void generateProxyPage(Aggregate *aggregate)
bool generateStatus(const Node *node)
void generateRequisites(const Aggregate *inner)
Lists the required imports and includes.
void generateSortedNames(const ClassNode *cn, const QList< RelatedClass > &rc)
void generateDocumentation(Node *node) override
Recursive writing of DocBook files from the root node.
void generateGroupReferenceText(const Node *node)
Return a string representing a text that exposes information about the groups that the node is part o...
void generateAddendum(const Node *node, Generator::Addendum type, CodeMarker *marker, bool generateNote) override
Generates an addendum note of type type for node.
QString fileExtension() const override
Returns "xml" for this subclass of Generator.
bool generateSince(const Node *node)
Definition doc.h:31
const Location & location() const
Returns the starting location of a qdoc comment.
Definition doc.cpp:90
static void quoteFromFile(const Location &location, Quoter &quoter, ResolvedFile resolved_file)
Definition doc.cpp:403
const Text & body() const
Definition doc.cpp:115
Text briefText(bool inclusive=false) const
Definition doc.cpp:121
const TypedefNode * flagsType() const
Definition enumnode.h:35
bool isScoped() const
Definition enumnode.h:31
Encapsulate the logic that QDoc uses to find files whose path is provided by the user and that are re...
This node is used to represent any kind of function being documented.
bool isMacroWithoutParams() const
bool isPrivateSignal() const
bool isOverride() const
const Parameters & parameters() const
bool isRef() const
bool isMAssign() const
bool isVirtual() const
bool isCAssign() const
bool isInvokable() const
bool isDefault() const override
Returns true if the QML property node is marked as default.
bool isPureVirtual() const
bool isDtor() const
bool isQmlSignal() const
bool isMacro() const override
returns true if either FunctionNode::isMacroWithParams() or FunctionNode::isMacroWithoutParams() retu...
bool isOverload() const
bool isRefRef() const
bool isCCtor() const
bool isMCtor() const
bool isFinal() const
bool isCtor() const
bool hasAssociatedProperties() const
bool generateComparisonCategory(const Node *node, CodeMarker *marker=nullptr)
static bool hasExceptions(const Node *node, NodeList &reentrant, NodeList &threadsafe, NodeList &nonreentrant)
void generateEnumValuesForQmlProperty(const Node *node, CodeMarker *marker)
FileResolver & file_resolver
Definition generator.h:194
static void setQmlTypeContext(QmlTypeNode *t)
Definition generator.h:69
const Atom * generateAtomList(const Atom *atom, const Node *relative, CodeMarker *marker, bool generate, int &numGeneratedAtoms)
QDocDatabase * m_qdb
Definition generator.h:196
bool m_inContents
Definition generator.h:198
bool generateComparisonList(const Node *node)
Generates a list of types that compare to node with the comparison category that applies for the rela...
void unknownAtom(const Atom *atom)
static bool matchAhead(const Atom *atom, Atom::AtomType expectedAtomType)
bool m_inLink
Definition generator.h:197
bool m_inTableHeader
Definition generator.h:200
bool m_inSectionHeading
Definition generator.h:199
virtual int skipAtoms(const Atom *atom, Atom::AtomType type) const
bool m_threeColumnEnumValueTable
Definition generator.h:201
@ AssociatedProperties
Definition generator.h:39
@ PrivateSignal
Definition generator.h:37
@ QmlSignalHandler
Definition generator.h:38
@ BindableProperty
Definition generator.h:40
bool m_showInternal
Definition generator.h:202
virtual void initializeGenerator()
Updates the generator's m_showInternal from the Config.
void initializeTextOutput()
Resets the variables used during text output.
This class represents a C++ namespace.
NamespaceNode * docNode() const
Returns a pointer to the NamespaceNode that represents where the namespace documentation is actually ...
bool isGenericCollection() const
Returns true if the node type is Collection.
Definition node.h:166
bool isExternalPage() const
Returns true if the node type is ExternalPage.
Definition node.h:134
const Doc & doc() const
Returns a reference to the node's Doc data member.
Definition node.h:271
bool isQmlNode() const
Returns true if this node's Genus value is QML.
Definition node.h:153
bool isGroup() const
Returns true if the node type is Group.
Definition node.h:139
virtual bool docMustBeGenerated() const
This function is called to perform a test to decide if the node must have documentation generated.
Definition node.h:228
bool isPrivate() const
Returns true if this node's access is Private.
Definition node.h:146
NodeType
An unsigned char value that identifies an object as a particular subclass of Node.
Definition node.h:54
@ Variable
Definition node.h:69
@ Module
Definition node.h:71
@ QmlModule
Definition node.h:73
@ Typedef
Definition node.h:66
@ QmlValueType
Definition node.h:75
@ Function
Definition node.h:65
@ TypeAlias
Definition node.h:67
@ Group
Definition node.h:70
@ Enum
Definition node.h:62
@ QmlType
Definition node.h:72
@ Namespace
Definition node.h:56
@ Property
Definition node.h:68
@ Class
Definition node.h:57
@ NoType
Definition node.h:55
bool isNamespace() const
Returns true if the node type is Namespace.
Definition node.h:143
bool isTypedef() const
Returns true if the node type is Typedef.
Definition node.h:160
bool isQmlBasicType() const
Returns true if the node type is QmlBasicType.
Definition node.h:151
bool isFunction(Genus g=DontCare) const
Returns true if this is a FunctionNode and its Genus is set to g.
Definition node.h:135
bool isQmlType() const
Returns true if the node type is QmlType or QmlValueType.
Definition node.h:155
bool isSharedCommentNode() const
Returns true if the node type is SharedComment.
Definition node.h:158
virtual bool isInternal() const
Returns true if the node's status is Internal, or if its parent is a class with Internal status.
Definition node.cpp:849
bool isHeader() const
Returns true if the node type is HeaderFile.
Definition node.h:140
virtual bool isPageNode() const
Returns true if this node represents something that generates a documentation page.
Definition node.h:182
bool isEnumType() const
Returns true if the node type is Enum.
Definition node.h:132
virtual bool isTextPageNode() const
Returns true if the node is a PageNode but not an Aggregate.
Definition node.h:187
Aggregate * parent() const
Returns the node's parent pointer.
Definition node.h:244
bool isVariable() const
Returns true if the node type is Variable.
Definition node.h:165
virtual bool isDeprecated() const
Returns true if this node's status is Deprecated.
Definition node.h:168
virtual bool isAggregate() const
Returns true if this node is an aggregate, which means it inherits Aggregate and can therefore have c...
Definition node.h:170
NodeType nodeType() const
Returns this node's type.
Definition node.h:121
static bool nodeNameLessThan(const Node *first, const Node *second)
Returns true if the node n1 is less than node n2.
Definition node.cpp:57
const Location & location() const
If this node's definition location is empty, this function returns this node's declaration location.
Definition node.h:267
bool isProxyNode() const
Returns true if the node type is Proxy.
Definition node.h:148
Access access() const
Returns the node's Access setting, which can be Public, Protected, or Private.
Definition node.h:264
ThreadSafeness threadSafeness() const
Returns the thread safeness value for whatever this node represents.
Definition node.cpp:826
Genus
An unsigned char value that specifies whether the Node represents a C++ element, a QML element,...
Definition node.h:81
@ DontCare
Definition node.h:82
@ API
Definition node.h:86
@ DOC
Definition node.h:85
@ QML
Definition node.h:84
virtual bool isMarkedReimp() const
Returns true if the FunctionNode is marked as a reimplemented function.
Definition node.h:184
bool isProperty() const
Returns true if the node type is Property.
Definition node.h:147
bool isModule() const
Returns true if the node type is Module.
Definition node.h:142
bool isClass() const
Returns true if the node type is Class.
Definition node.h:129
virtual bool isPropertyGroup() const
Returns true if the node is a SharedCommentNode for documenting multiple C++ properties or multiple Q...
Definition node.h:185
Genus genus() const
Returns this node's Genus.
Definition node.h:124
ThreadSafeness
An unsigned char that specifies the degree of thread-safeness of the element.
Definition node.h:97
@ ThreadSafe
Definition node.h:101
@ NonReentrant
Definition node.h:99
@ Reentrant
Definition node.h:100
bool isSharingComment() const
This function returns true if the node is sharing a comment with other nodes.
Definition node.h:285
bool hasDoc() const
Returns true if this node is documented, or it represents a documented node read from the index ('had...
Definition node.cpp:906
LinkType
An unsigned char value that probably should be moved out of the Node base class.
Definition node.h:112
bool isRelatedNonmember() const
Returns true if this is a related nonmember of something.
Definition node.h:156
virtual bool isClassNode() const
Returns true if this is an instance of ClassNode.
Definition node.h:177
virtual bool isCollectionNode() const
Returns true if this is an instance of CollectionNode.
Definition node.h:178
static bool nodeSortKeyOrNameLessThan(const Node *n1, const Node *n2)
Returns true if node n1 is less than node n2 when comparing the sort keys, defined with.
Definition node.cpp:99
@ Internal
Definition node.h:93
@ Active
Definition node.h:92
bool isQmlModule() const
Returns true if the node type is QmlModule.
Definition node.h:152
bool isExample() const
Returns true if the node type is Example.
Definition node.h:133
bool isIndexNode() const
Returns true if this node was created from something in an index file.
Definition node.h:141
Status status() const
Returns the node's status value.
Definition node.h:278
bool isQmlProperty() const
Returns true if the node type is QmlProperty.
Definition node.h:154
A PageNode is a Node that generates a documentation page.
Definition pagenode.h:18
bool noAutoList() const
Returns the value of the no auto-list flag.
Definition pagenode.h:41
The Parameter class describes one function parameter.
Definition parameters.h:20
A class for parsing and managing a function parameter list.
Definition parameters.h:57
bool isEmpty() const
Definition parameters.h:70
const Parameter & at(int i) const
Definition parameters.h:74
int count() const
Definition parameters.h:72
This class describes one instance of using the Q_PROPERTY macro.
const NodeList & getters() const
const NodeList & resetters() const
const NodeList & setters() const
PropertyType propertyType() const
const NodeList & notifiers() const
This class provides exclusive access to the qdoc database, which consists of a forrest of trees and a...
NodeMapMap & getFunctionIndex()
Returns the function index.
TextToNodeMap & getLegaleseTexts()
Returns a reference to the collection of legalese texts.
NodeMultiMap & getAttributions()
Returns a reference to the multimap of attribution nodes.
NodeMultiMap & getQmlTypesWithObsoleteMembers()
Returns a reference to the map of QML types with obsolete members.
NodeMultiMap & getObsoleteQmlTypes()
Returns a reference to the map of obsolete QML types.
static QDocDatabase * qdocDB()
Creates the singleton.
NodeMultiMap & getQmlTypes()
Returns a reference to the multimap of QML types.
NodeMultiMap & getClassesWithObsoleteMembers()
Returns a reference to the map of C++ classes with obsolete members.
NodeMultiMap & getQmlValueTypes()
Returns a reference to the map of QML basic types.
NodeMultiMap & getCppClasses()
Returns a reference to the map of all C++ classes.
NodeMultiMap & getNamespaces()
Returns a reference to the namespace map.
NodeMultiMap & getExamples()
Returns a reference to the multimap of example nodes.
NodeMultiMap & getObsoleteClasses()
Returns a reference to the map of obsolete C++ clases.
void mergeCollections(Node::NodeType type, CNMap &cnm, const Node *relative)
Finds all the collection nodes of the specified type and merges them into the collection node map cnm...
void mergeCollections(CollectionNode *c)
Finds all the collection nodes with the same name and type as c and merges their members into the mem...
const CNMap & groups()
Returns a const reference to the collection of all group nodes in the primary tree.
bool isDefault() const override
Returns true if the QML property node is marked as default.
bool isReadOnly() const
Returns true if this QML property node is marked as a read-only property.
bool isRequired()
Returns true if this QML property is marked with \required or the corresponding C++ property uses the...
bool isReadOnly()
Returns true if this QML property or attached property is read-only.
bool isAttached() const override
Returns true if the QML property or QML method node is marked as attached.
ClassNode * classNode() override
If this is a QmlTypeNode, this function returns the pointer to the C++ ClassNode that this QML type r...
Definition qmltypenode.h:26
bool isInternal() const override
Returns true if the node's status is Internal, or if its parent is a class with Internal status.
Definition qmltypenode.h:32
static void subclasses(const Node *base, NodeList &subs)
Loads the list subs with the nodes of all the subclasses of base.
QmlTypeNode * qmlBaseNode() const override
If this Aggregate is a QmlTypeNode, this function returns a pointer to the QmlTypeNode that is its ba...
Definition qmltypenode.h:43
CollectionNode * logicalModule() const override
If this is a QmlTypeNode, a pointer to its QML module is returned, which is a pointer to a Collection...
Definition qmltypenode.h:37
A class for containing the elements of one documentation section.
Definition sections.h:17
const NodeVector & obsoleteMembers() const
Definition sections.h:56
void appendMembers(const NodeVector &nv)
Definition sections.h:57
@ Summary
Definition sections.h:19
@ Details
Definition sections.h:19
@ Accessors
Definition sections.h:19
@ AllMembers
Definition sections.h:19
const NodeVector & members() const
Definition sections.h:49
Style style() const
Definition sections.h:44
A class for creating vectors of collections for documentation.
Definition sections.h:82
Aggregate * aggregate() const
Definition sections.h:181
Sections(Aggregate *aggregate)
This constructor builds the vectors of sections based on the type of the aggregate node.
Definition sections.cpp:344
SectionVector & stdDetailsSections()
Definition sections.h:156
SectionVector & stdCppClassDetailsSections()
Definition sections.h:158
bool hasObsoleteMembers(SectionPtrVector *summary_spv, SectionPtrVector *details_spv) const
Returns true if any sections in this object contain obsolete members.
Definition sections.cpp:955
Definition text.h:12
const Atom * firstAtom() const
Definition text.h:34
bool isEmpty() const
Definition text.h:31
Text & operator=(const Text &text)
Definition text.cpp:29
const Atom * lastAtom() const
Definition text.h:35
Atom * lastAtom()
Definition text.h:22
bool isStatic() const override
Returns true if the FunctionNode represents a static function.
static bool isThreeColumnEnumValueTable(const Atom *atom)
Determines whether the list atom should be shown with three columns (constant-value-description).
static Node::NodeType typeFromString(const Atom *atom)
Returns the type of this atom as an enumeration.
static bool isOneColumnValueTable(const Atom *atom)
Determines whether the list atom should be shown with just one column (value).
static void rewritePropertyBrief(const Atom *atom, const Node *relative)
Rewrites the brief of this node depending on its first word.
static int hOffset(const Node *node)
Header offset depending on the type of the node.
static bool hasBrief(const Node *node)
Do not display.
XmlGenerator(FileResolver &file_resolver)
const Node * m_linkNode
static const QRegularExpression m_funcLeftParen
#define CONFIG_DOCBOOKEXTENSIONS
Definition config.h:334
#define CONFIG_EXAMPLES
Definition config.h:338
#define CONFIG_URL
Definition config.h:393
#define CONFIG_DESCRIPTION
Definition config.h:333
#define CONFIG_PROJECT
Definition config.h:375
#define CONFIG_EXAMPLESINSTALLPATH
Definition config.h:339
#define CONFIG_NATURALLANGUAGE
Definition config.h:367
#define CONFIG_PRODUCTNAME
Definition config.h:374
#define CONFIG_BUILDVERSION
Definition config.h:325
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.