1 /****************************************************************************
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
6 ** This file is part of the tools applications of the Qt Toolkit.
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** GNU Lesser General Public License Usage
10 ** This file may be used under the terms of the GNU Lesser General Public
11 ** License version 2.1 as published by the Free Software Foundation and
12 ** appearing in the file LICENSE.LGPL included in the packaging of this
13 ** file. Please review the following information to ensure the GNU Lesser
14 ** General Public License version 2.1 requirements will be met:
15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
17 ** In addition, as a special exception, Nokia gives you certain additional
18 ** rights. These rights are described in the Nokia Qt LGPL Exception
19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
21 ** GNU General Public License Usage
22 ** Alternatively, this file may be used under the terms of the GNU General
23 ** Public License version 3.0 as published by the Free Software Foundation
24 ** and appearing in the file LICENSE.GPL included in the packaging of this
25 ** file. Please review the following information to ensure the GNU General
26 ** Public License version 3.0 requirements will be met:
27 ** http://www.gnu.org/copyleft/gpl.html.
30 ** Alternatively, this file may be used in accordance with the terms and
31 ** conditions contained in a signed written agreement between you and Nokia.
40 ****************************************************************************/
48 #include <qiterator.h>
51 #include "codemarker.h"
52 #include "codeparser.h"
53 #include "ditaxmlgenerator.h"
56 #include "separator.h"
62 #define COMMAND_VERSION Doc::alias("version")
63 int DitaXmlGenerator::id = 0;
66 The strings in this array must appear in the same order as
67 the values in enum DitaXmlGenerator::DitaTag.
69 QString DitaXmlGenerator::ditaTags[] =
102 "cxxClassAccessSpecifier",
103 "cxxClassAPIItemLocation",
105 "cxxClassDeclarationFile",
106 "cxxClassDeclarationFileLine",
107 "cxxClassDeclarationFileLineStart",
108 "cxxClassDeclarationFileLineEnd",
109 "cxxClassDefinition",
110 "cxxClassDerivation",
111 "cxxClassDerivationAccessSpecifier",
112 "cxxClassDerivations",
115 "cxxClassNestedClass",
116 "cxxClassNestedDetail",
118 "cxxDefineAccessSpecifier",
119 "cxxDefineAPIItemLocation",
120 "cxxDefineDeclarationFile",
121 "cxxDefineDeclarationFileLine",
122 "cxxDefineDefinition",
124 "cxxDefineNameLookup",
125 "cxxDefineParameter",
126 "cxxDefineParameterDeclarationName",
127 "cxxDefineParameters",
128 "cxxDefinePrototype",
129 "cxxDefineReimplemented",
131 "cxxEnumerationAccessSpecifier",
132 "cxxEnumerationAPIItemLocation",
133 "cxxEnumerationDeclarationFile",
134 "cxxEnumerationDeclarationFileLine",
135 "cxxEnumerationDeclarationFileLineStart",
136 "cxxEnumerationDeclarationFileLineEnd",
137 "cxxEnumerationDefinition",
138 "cxxEnumerationDetail",
139 "cxxEnumerationNameLookup",
140 "cxxEnumerationPrototype",
141 "cxxEnumerationScopedName",
143 "cxxEnumeratorInitialiser",
144 "cxxEnumeratorNameLookup",
145 "cxxEnumeratorPrototype",
147 "cxxEnumeratorScopedName",
149 "cxxFunctionAccessSpecifier",
150 "cxxFunctionAPIItemLocation",
152 "cxxFunctionConstructor",
153 "cxxFunctionDeclarationFile",
154 "cxxFunctionDeclarationFileLine",
155 "cxxFunctionDeclaredType",
156 "cxxFunctionDefinition",
157 "cxxFunctionDestructor",
159 "cxxFunctionNameLookup",
160 "cxxFunctionParameter",
161 "cxxFunctionParameterDeclarationName",
162 "cxxFunctionParameterDeclaredType",
163 "cxxFunctionParameterDefaultValue",
164 "cxxFunctionParameters",
165 "cxxFunctionPrototype",
166 "cxxFunctionPureVirtual",
167 "cxxFunctionReimplemented",
168 "cxxFunctionScopedName",
169 "cxxFunctionStorageClassSpecifierStatic",
170 "cxxFunctionVirtual",
172 "cxxTypedefAccessSpecifier",
173 "cxxTypedefAPIItemLocation",
174 "cxxTypedefDeclarationFile",
175 "cxxTypedefDeclarationFileLine",
176 "cxxTypedefDefinition",
178 "cxxTypedefNameLookup",
179 "cxxTypedefScopedName",
181 "cxxVariableAccessSpecifier",
182 "cxxVariableAPIItemLocation",
183 "cxxVariableDeclarationFile",
184 "cxxVariableDeclarationFileLine",
185 "cxxVariableDeclaredType",
186 "cxxVariableDefinition",
188 "cxxVariableNameLookup",
189 "cxxVariablePrototype",
190 "cxxVariableReimplemented",
191 "cxxVariableScopedName",
192 "cxxVariableStorageClassSpecifierStatic",
239 "qmlPropertyGroupDef",
240 "qmlPropertyGroupDetail",
246 "qmlSignalHandlerDef",
247 "qmlSignalHandlerDetail",
287 void DitaXmlGenerator::debugPara(const QString& t)
290 xmlWriter().writeAttribute("outputclass",t);
291 xmlWriter().writeCharacters(t);
292 writeEndTag(); // </p>
295 static bool showBrokenLinks = false;
298 Quick, dirty, and very ugly. Unescape \a text
299 so QXmlStreamWriter::writeCharacters() can put
300 the escapes back in again!
302 void DitaXmlGenerator::writeCharacters(const QString& text)
305 t = t.replace("<","<");
306 t = t.replace(">",">");
307 t = t.replace("&","&");
308 t = t.replace(""","\"");
309 xmlWriter().writeCharacters(t);
313 Appends an <xref> element to the current XML stream
314 with the \a href attribute and the \a text.
316 void DitaXmlGenerator::addLink(const QString& href,
317 const QStringRef& text,
320 if (!href.isEmpty()) {
323 writeHrefAttribute(href);
324 writeCharacters(text.toString());
325 writeEndTag(); // </t>
328 writeCharacters(text.toString());
333 Push \a t onto the dita tag stack and write the appropriate
334 start tag to the DITA XML file.
336 void DitaXmlGenerator::writeStartTag(DitaTag t)
338 xmlWriter().writeStartElement(ditaTags[t]);
343 Pop the current DITA tag off the stack, and write the
344 appropriate end tag to the DITA XML file. If \a t is
345 not \e DT_NONE (default), then \a t contains the enum
346 value of the tag that should be on top of the stack.
348 If the stack is empty, no end tag is written and false
349 is returned. Otherwise, an end tag is written and true
352 bool DitaXmlGenerator::writeEndTag(DitaTag t)
354 if (tagStack.isEmpty())
356 DitaTag top = tagStack.pop();
357 if (t > DT_NONE && top != t)
358 qDebug() << "Expected:" << t << "ACTUAL:" << top;
359 xmlWriter().writeEndElement();
364 Return the current DITA element tag, the one
367 DitaXmlGenerator::DitaTag DitaXmlGenerator::currentTag()
369 return tagStack.top();
373 Write the start \a tag. if \a title is not empty, generate
374 a GUID from it and write the GUID as the value of the \e{id}
377 Then if \a outputclass is not empty, write it as the value
378 of the \a outputclass attribute.
380 Fiunally, set the section nesting level to 1 and return 1.
382 int DitaXmlGenerator::enterDesc(DitaTag tag, const QString& outputclass, const QString& title)
385 if (!title.isEmpty()) {
386 writeGuidAttribute(title);
387 //Are there cases where the spectitle is required?
388 //xmlWriter().writeAttribute("spectitle",title);
390 if (!outputclass.isEmpty())
391 xmlWriter().writeAttribute("outputclass",outputclass);
392 sectionNestingLevel = 1;
393 return sectionNestingLevel;
397 If the section nesting level is 0, output a \c{<section>}
398 element with an \e id attribute generated from \a title and
399 an \e outputclass attribute set to \a outputclass.
400 If \a title is null, no \e id attribute is output.
401 If \a outputclass is empty, no \e outputclass attribute
404 Finally, increment the section nesting level and return
407 int DitaXmlGenerator::enterSection(const QString& outputclass, const QString& title)
409 if (sectionNestingLevel == 0) {
410 writeStartTag(DT_section);
411 if (!title.isEmpty())
412 writeGuidAttribute(title);
413 if (!outputclass.isEmpty())
414 xmlWriter().writeAttribute("outputclass",outputclass);
416 else if (!title.isEmpty()) {
418 writeGuidAttribute(title);
419 if (!outputclass.isEmpty())
420 xmlWriter().writeAttribute("outputclass",outputclass);
421 writeCharacters(title);
422 writeEndTag(); // </p>
424 return ++sectionNestingLevel;
428 If the section nesting level is greater than 0, decrement
429 it. If it becomes 0, output a \c {</section>}. Return the
430 decremented section nesting level.
432 int DitaXmlGenerator::leaveSection()
434 if (sectionNestingLevel > 0) {
435 --sectionNestingLevel;
436 if (sectionNestingLevel == 0)
437 writeEndTag(); // </section> or </apiDesc>
439 return sectionNestingLevel;
443 The default constructor.
445 DitaXmlGenerator::DitaXmlGenerator()
447 inDetailedDescription(false),
448 inLegaleseText(false),
450 inObsoleteLink(false),
451 inSectionHeading(false),
452 inTableHeader(false),
455 obsoleteLinks(false),
457 threeColumnEnumValueTable(true),
461 sectionNestingLevel(0),
463 funcLeftParen("\\S(\\()"),
464 nodeTypeMaps(Node::LastType,0),
465 nodeSubtypeMaps(Node::LastSubtype,0),
466 pageTypeMaps(Node::OnBeyondZebra,0)
472 The destructor has nothing to do.
474 DitaXmlGenerator::~DitaXmlGenerator()
476 GuidMaps::iterator i = guidMaps.begin();
477 while (i != guidMaps.end()) {
484 A lot of internal structures are initialized.
486 void DitaXmlGenerator::initializeGenerator(const Config &config)
488 Generator::initializeGenerator(config);
489 obsoleteLinks = config.getBool(QLatin1String(CONFIG_OBSOLETELINKS));
490 setImageFileExtensions(QStringList() << "png" << "jpg" << "jpeg" << "gif");
492 style = config.getString(DitaXmlGenerator::format() +
494 DITAXMLGENERATOR_STYLE);
495 postHeader = config.getString(DitaXmlGenerator::format() +
497 DITAXMLGENERATOR_POSTHEADER);
498 postPostHeader = config.getString(DitaXmlGenerator::format() +
500 DITAXMLGENERATOR_POSTPOSTHEADER);
501 footer = config.getString(DitaXmlGenerator::format() +
503 DITAXMLGENERATOR_FOOTER);
504 address = config.getString(DitaXmlGenerator::format() +
506 DITAXMLGENERATOR_ADDRESS);
507 pleaseGenerateMacRef = config.getBool(DitaXmlGenerator::format() +
509 DITAXMLGENERATOR_GENERATEMACREFS);
511 project = config.getString(CONFIG_PROJECT);
512 projectDescription = config.getString(CONFIG_DESCRIPTION);
513 if (projectDescription.isEmpty() && !project.isEmpty())
514 projectDescription = project + " Reference Documentation";
516 projectUrl = config.getString(CONFIG_URL);
518 outputEncoding = config.getString(CONFIG_OUTPUTENCODING);
519 if (outputEncoding.isEmpty())
520 outputEncoding = QLatin1String("ISO-8859-1");
521 outputCodec = QTextCodec::codecForName(outputEncoding.toLocal8Bit());
523 naturalLanguage = config.getString(CONFIG_NATURALLANGUAGE);
524 if (naturalLanguage.isEmpty())
525 naturalLanguage = QLatin1String("en");
527 config.subVarsAndValues("dita.metadata.default",metadataDefaults);
528 QSet<QString> editionNames = config.subVars(CONFIG_EDITION);
529 QSet<QString>::ConstIterator edition = editionNames.constBegin();
530 while (edition != editionNames.constEnd()) {
531 QString editionName = *edition;
532 QStringList editionModules = config.getStringList(CONFIG_EDITION +
537 QStringList editionGroups = config.getStringList(CONFIG_EDITION +
543 if (!editionModules.isEmpty())
544 editionModuleMap[editionName] = editionModules;
545 if (!editionGroups.isEmpty())
546 editionGroupMap[editionName] = editionGroups;
551 stylesheets = config.getStringList(DitaXmlGenerator::format() +
553 DITAXMLGENERATOR_STYLESHEETS);
554 customHeadElements = config.getStringList(DitaXmlGenerator::format() +
556 DITAXMLGENERATOR_CUSTOMHEADELEMENTS);
557 codeIndent = config.getInt(CONFIG_CODEINDENT);
558 version = config.getString(CONFIG_VERSION);
559 vrm = version.split(QLatin1Char('.'));
563 All this does is call the same function in the base class.
565 void DitaXmlGenerator::terminateGenerator()
567 Generator::terminateGenerator();
573 QString DitaXmlGenerator::format()
579 Calls lookupGuid() to get a GUID for \a text, then writes
580 it to the XML stream as an "id" attribute, and returns it.
582 QString DitaXmlGenerator::writeGuidAttribute(QString text)
584 QString guid = lookupGuid(outFileName(),text);
585 xmlWriter().writeAttribute("id",guid);
591 Write's the GUID for the \a node to the current XML stream
592 as an "id" attribute. If the \a node doesn't yet have a GUID,
595 void DitaXmlGenerator::writeGuidAttribute(Node* node)
597 xmlWriter().writeAttribute("id",node->guid());
601 Looks up \a text in the GUID map. If it finds \a text,
602 it returns the associated GUID. Otherwise it inserts
603 \a text into the map with a new GUID, and it returns
606 QString DitaXmlGenerator::lookupGuid(QString text)
608 QMap<QString, QString>::const_iterator i = name2guidMap.constFind(text);
609 if (i != name2guidMap.constEnd())
611 QString guid = Node::cleanId(text);
612 name2guidMap.insert(text,guid);
617 First, look up the GUID map for \a fileName. If there isn't
618 a GUID map for \a fileName, create one and insert it into
619 the map of GUID maps. Then look up \a text in that GUID map.
620 If \a text is found, return the associated GUID. Otherwise,
621 insert \a text into the GUID map with a new GUID, and return
624 QString DitaXmlGenerator::lookupGuid(const QString& fileName, const QString& text)
626 GuidMap* gm = lookupGuidMap(fileName);
627 GuidMap::const_iterator i = gm->constFind(text);
628 if (i != gm->constEnd())
630 QString guid = Node::cleanId(text);
631 gm->insert(text,guid);
636 Looks up \a fileName in the map of GUID maps. If it finds
637 \a fileName, it returns a pointer to the associated GUID
638 map. Otherwise it creates a new GUID map and inserts it
639 into the map of GUID maps with \a fileName as its key.
641 GuidMap* DitaXmlGenerator::lookupGuidMap(const QString& fileName)
643 GuidMaps::const_iterator i = guidMaps.constFind(fileName);
644 if (i != guidMaps.constEnd())
646 GuidMap* gm = new GuidMap;
647 guidMaps.insert(fileName,gm);
652 This is where the DITA XML files are written.
653 \note The file is created in PageGenerator::generateTree().
655 void DitaXmlGenerator::generateTree(Tree *tree)
658 nonCompatClasses.clear();
660 compatClasses.clear();
661 obsoleteClasses.clear();
662 moduleClassMap.clear();
663 moduleNamespaceMap.clear();
665 legaleseTexts.clear();
666 serviceClasses.clear();
668 findAllClasses(tree->root());
669 findAllFunctions(tree->root());
670 findAllLegaleseTexts(tree->root());
671 findAllNamespaces(tree->root());
672 findAllSince(tree->root());
674 Generator::generateTree(tree);
675 generateCollisionPages();
677 QString fileBase = project.toLower().simplified().replace(QLatin1Char(' '), QLatin1Char('-'));
678 generateIndex(fileBase, projectUrl, projectDescription);
683 void DitaXmlGenerator::startText(const Node* /* relative */,
684 CodeMarker* /* marker */)
688 inSectionHeading = false;
689 inTableHeader = false;
691 threeColumnEnumValueTable = true;
693 sectionNumber.clear();
696 static int countTableColumns(const Atom* t)
699 if (t->type() == Atom::TableHeaderLeft) {
700 while (t->type() == Atom::TableHeaderLeft) {
703 while (t->type() != Atom::TableHeaderRight) {
704 if (t->type() == Atom::TableItemLeft) {
705 for (int i=0; i<t->count(); ++i) {
706 QString attr = t->string(i);
707 if (!attr.contains('=')) {
708 QStringList spans = attr.split(QLatin1Char(','));
709 if (spans.size() == 2) {
710 count += spans[0].toInt();
725 else if (t->type() == Atom::TableRowLeft) {
726 while (t->type() != Atom::TableRowRight) {
727 if (t->type() == Atom::TableItemLeft) {
728 for (int i=0; i<t->count(); ++i) {
729 QString attr = t->string(i);
730 if (!attr.contains('=')) {
731 QStringList spans = attr.split(QLatin1Char(','));
732 if (spans.size() == 2) {
733 result += spans[0].toInt();
748 Generate html from an instance of Atom.
750 int DitaXmlGenerator::generateAtom(const Atom *atom,
751 const Node *relative,
756 static bool in_para = false;
757 QString guid, hc, attr;
759 switch (atom->type()) {
760 case Atom::AbstractLeft:
762 case Atom::AbstractRight:
765 if (!noLinks && !inLink && !inContents && !inSectionHeading) {
766 const Node* node = 0;
767 QString link = getLink(atom, relative, marker, &node);
768 if (!link.isEmpty()) {
770 generateLink(atom, relative, marker);
774 writeCharacters(protectEnc(atom->string()));
778 writeCharacters(protectEnc(atom->string()));
783 case Atom::BriefLeft:
785 Node::Type t = relative->type();
789 xmlWriter().writeAttribute("outputclass","brief");
793 writeStartTag(DT_shortdesc);
795 if (t == Node::Property || t == Node::Variable) {
796 xmlWriter().writeCharacters("This ");
797 if (relative->type() == Node::Property)
798 xmlWriter().writeCharacters("property");
799 else if (relative->type() == Node::Variable)
800 xmlWriter().writeCharacters("variable");
801 xmlWriter().writeCharacters(" holds ");
805 while (atom != 0 && atom->type() != Atom::BriefRight) {
806 if (atom->type() == Atom::String ||
807 atom->type() == Atom::AutoLink)
808 str += atom->string();
812 if (t == Node::Property || t == Node::Variable)
813 str[0] = str[0].toLower();
814 if (str.endsWith(QLatin1Char('.')))
815 str.truncate(str.length() - 1);
816 writeCharacters(str + QLatin1Char('.'));
820 case Atom::BriefRight:
821 // if (relative->type() != Node::Fake)
822 writeEndTag(); // </shortdesc> or </p>
828 writeStartTag(DT_tt);
830 writeCharacters(protectEnc(plainCode(atom->string())));
833 writeText(atom->string(), marker, relative);
835 writeEndTag(); // see writeStartElement() above
839 writeStartTag(DT_codeblock);
840 xmlWriter().writeAttribute("outputclass","cpp");
841 writeCharacters("\n");
842 writeText(trimmedTrailing(atom->string()), marker, relative);
843 writeEndTag(); // </codeblock>
847 writeStartTag(DT_codeblock);
848 xmlWriter().writeAttribute("outputclass","qml");
849 writeCharacters("\n");
850 writeText(trimmedTrailing(atom->string()), marker, relative);
851 writeEndTag(); // </codeblock>
855 xmlWriter().writeCharacters("you can rewrite it as");
856 writeEndTag(); // </p>
857 writeStartTag(DT_codeblock);
858 writeCharacters("\n");
859 writeText(trimmedTrailing(atom->string()), marker, relative);
860 writeEndTag(); // </codeblock>
864 xmlWriter().writeCharacters("For example, if you have code like");
865 writeEndTag(); // </p>
868 writeStartTag(DT_codeblock);
869 writeCharacters("\n");
870 writeCharacters(trimmedTrailing(plainCode(atom->string())));
871 writeEndTag(); // </codeblock>
875 bool inStartElement = false;
876 attr = atom->string();
877 DitaTag t = currentTag();
878 if ((t == DT_section) || (t == DT_sectiondiv)) {
879 writeStartTag(DT_sectiondiv);
881 inStartElement = true;
883 else if ((t == DT_body) || (t == DT_bodydiv)) {
884 writeStartTag(DT_bodydiv);
886 inStartElement = true;
888 if (!attr.isEmpty()) {
889 if (attr.contains('=')) {
894 index = attr.indexOf('"',from);
898 index = attr.indexOf('"',from);
900 if (!values.isEmpty())
902 values += attr.mid(from,index-from);
911 xmlWriter().writeAttribute("outputclass", attr);
915 if ((currentTag() == DT_sectiondiv) || (currentTag() == DT_bodydiv)) {
916 writeEndTag(); // </sectiondiv>, </bodydiv>, or </p>
917 if (divNestingLevel > 0)
921 case Atom::FootnoteLeft:
924 writeEndTag(); // </p>
927 xmlWriter().writeCharacters("<!-- ");
929 case Atom::FootnoteRight:
931 xmlWriter().writeCharacters("-->");
933 case Atom::FormatElse:
934 case Atom::FormatEndif:
937 case Atom::FormattingLeft:
940 if (atom->string() == ATOM_FORMATTING_BOLD)
942 else if (atom->string() == ATOM_FORMATTING_PARAMETER)
944 else if (atom->string() == ATOM_FORMATTING_ITALIC)
946 else if (atom->string() == ATOM_FORMATTING_TELETYPE)
948 else if (atom->string().startsWith("span ")) {
951 else if (atom->string() == ATOM_FORMATTING_UICONTROL)
953 else if (atom->string() == ATOM_FORMATTING_UNDERLINE)
955 else if (atom->string() == ATOM_FORMATTING_INDEX)
957 else if (atom->string() == ATOM_FORMATTING_SUBSCRIPT)
959 else if (atom->string() == ATOM_FORMATTING_SUPERSCRIPT)
962 qDebug() << "DT_LAST";
964 if (atom->string() == ATOM_FORMATTING_PARAMETER) {
965 if (atom->next() != 0 && atom->next()->type() == Atom::String) {
966 QRegExp subscriptRegExp("([a-z]+)_([0-9n])");
967 if (subscriptRegExp.exactMatch(atom->next()->string())) {
968 xmlWriter().writeCharacters(subscriptRegExp.cap(1));
969 writeStartTag(DT_sub);
970 xmlWriter().writeCharacters(subscriptRegExp.cap(2));
971 writeEndTag(); // </sub>
976 else if (t == DT_keyword) {
977 QString attr = atom->string().mid(5);
978 if (!attr.isEmpty()) {
979 if (attr.contains('=')) {
984 index = attr.indexOf('"',from);
988 index = attr.indexOf('"',from);
990 if (!values.isEmpty())
992 values += attr.mid(from,index-from);
1000 xmlWriter().writeAttribute("outputclass", attr);
1004 case Atom::FormattingRight:
1005 if (atom->string() == ATOM_FORMATTING_LINK) {
1012 case Atom::AnnotatedList:
1014 QList<Node*> values = tree_->groups().values(atom->string());
1016 for (int i = 0; i < values.size(); ++i) {
1017 const Node* n = values.at(i);
1018 if ((n->status() != Node::Internal) && (n->access() != Node::Private)) {
1019 nodeMap.insert(n->nameForLists(),n);
1022 generateAnnotatedList(relative, marker, nodeMap);
1025 case Atom::GeneratedList:
1026 if (atom->string() == "annotatedclasses") {
1027 generateAnnotatedList(relative, marker, nonCompatClasses);
1029 else if (atom->string() == "classes") {
1030 generateCompactList(relative, marker, nonCompatClasses, true);
1032 else if (atom->string() == "qmlclasses") {
1033 generateCompactList(relative, marker, qmlClasses, true);
1035 else if (atom->string().contains("classesbymodule")) {
1036 QString arg = atom->string().trimmed();
1037 QString moduleName = atom->string().mid(atom->string().indexOf(
1038 "classesbymodule") + 15).trimmed();
1039 if (moduleClassMap.contains(moduleName))
1040 generateAnnotatedList(relative, marker, moduleClassMap[moduleName]);
1042 else if (atom->string().contains("classesbyedition")) {
1044 QString arg = atom->string().trimmed();
1045 QString editionName = atom->string().mid(atom->string().indexOf(
1046 "classesbyedition") + 16).trimmed();
1048 if (editionModuleMap.contains(editionName)) {
1050 // Add all classes in the modules listed for that edition.
1051 NodeMap editionClasses;
1052 foreach (const QString &moduleName, editionModuleMap[editionName]) {
1053 if (moduleClassMap.contains(moduleName))
1054 editionClasses.unite(moduleClassMap[moduleName]);
1057 // Add additional groups and remove groups of classes that
1058 // should be excluded from the edition.
1060 QMultiMap <QString, Node *> groups = tree_->groups();
1061 foreach (const QString &groupName, editionGroupMap[editionName]) {
1062 QList<Node *> groupClasses;
1063 if (groupName.startsWith(QLatin1Char('-'))) {
1064 groupClasses = groups.values(groupName.mid(1));
1065 foreach (const Node *node, groupClasses)
1066 editionClasses.remove(node->name());
1069 groupClasses = groups.values(groupName);
1070 foreach (const Node *node, groupClasses)
1071 editionClasses.insert(node->name(), node);
1074 generateAnnotatedList(relative, marker, editionClasses);
1077 else if (atom->string() == "classhierarchy") {
1078 generateClassHierarchy(relative, marker, nonCompatClasses);
1080 else if (atom->string() == "compatclasses") {
1081 generateCompactList(relative, marker, compatClasses, false);
1083 else if (atom->string() == "obsoleteclasses") {
1084 generateCompactList(relative, marker, obsoleteClasses, false);
1086 else if (atom->string() == "functionindex") {
1087 generateFunctionIndex(relative, marker);
1089 else if (atom->string() == "legalese") {
1090 generateLegaleseList(relative, marker);
1092 else if (atom->string() == "mainclasses") {
1093 generateCompactList(relative, marker, mainClasses, true);
1095 else if (atom->string() == "services") {
1096 generateCompactList(relative, marker, serviceClasses, false);
1098 else if (atom->string() == "overviews") {
1099 generateOverviewList(relative, marker);
1101 else if (atom->string() == "namespaces") {
1102 generateAnnotatedList(relative, marker, namespaceIndex);
1104 else if (atom->string() == "related") {
1105 const FakeNode *fake = static_cast<const FakeNode *>(relative);
1106 if (fake && !fake->groupMembers().isEmpty()) {
1107 NodeMap groupMembersMap;
1108 foreach (const Node *node, fake->groupMembers()) {
1109 if (node->type() == Node::Fake)
1110 groupMembersMap[fullName(node, relative, marker)] = node;
1112 generateAnnotatedList(fake, marker, groupMembersMap);
1116 case Atom::SinceList:
1118 NewSinceMaps::const_iterator nsmap;
1119 nsmap = newSinceMaps.constFind(atom->string());
1120 NewClassMaps::const_iterator ncmap;
1121 ncmap = newClassMaps.constFind(atom->string());
1122 NewClassMaps::const_iterator nqcmap;
1123 nqcmap = newQmlClassMaps.constFind(atom->string());
1124 if ((nsmap != newSinceMaps.constEnd()) && !nsmap.value().isEmpty()) {
1125 QList<Section> sections;
1126 QList<Section>::ConstIterator s;
1127 for (int i=0; i<LastSinceType; ++i)
1128 sections.append(Section(sinceTitle(i),QString(),QString(),QString()));
1130 NodeMultiMap::const_iterator n = nsmap.value().constBegin();
1131 while (n != nsmap.value().constEnd()) {
1132 const Node* node = n.value();
1133 switch (node->type()) {
1135 if (node->subType() == Node::QmlClass) {
1136 sections[QmlClass].appendMember((Node*)node);
1139 case Node::Namespace:
1140 sections[Namespace].appendMember((Node*)node);
1143 sections[Class].appendMember((Node*)node);
1146 sections[Enum].appendMember((Node*)node);
1149 sections[Typedef].appendMember((Node*)node);
1151 case Node::Function: {
1152 const FunctionNode* fn = static_cast<const FunctionNode*>(node);
1154 sections[Macro].appendMember((Node*)node);
1156 Node* p = fn->parent();
1158 if (p->type() == Node::Class)
1159 sections[MemberFunction].appendMember((Node*)node);
1160 else if (p->type() == Node::Namespace) {
1161 if (p->name().isEmpty())
1162 sections[GlobalFunction].appendMember((Node*)node);
1164 sections[NamespaceFunction].appendMember((Node*)node);
1167 sections[GlobalFunction].appendMember((Node*)node);
1170 sections[GlobalFunction].appendMember((Node*)node);
1174 case Node::Property:
1175 sections[Property].appendMember((Node*)node);
1177 case Node::Variable:
1178 sections[Variable].appendMember((Node*)node);
1180 case Node::QmlProperty:
1181 sections[QmlProperty].appendMember((Node*)node);
1183 case Node::QmlSignal:
1184 sections[QmlSignal].appendMember((Node*)node);
1186 case Node::QmlSignalHandler:
1187 sections[QmlSignalHandler].appendMember((Node*)node);
1189 case Node::QmlMethod:
1190 sections[QmlMethod].appendMember((Node*)node);
1199 First generate the table of contents.
1201 writeStartTag(DT_ul);
1202 s = sections.constBegin();
1203 while (s != sections.constEnd()) {
1204 if (!(*s).members.isEmpty()) {
1205 QString li = outFileName() + QLatin1Char('#') + Doc::canonicalTitle((*s).name);
1206 writeXrefListItem(li, (*s).name);
1210 writeEndTag(); // </ul>
1213 s = sections.constBegin();
1214 while (s != sections.constEnd()) {
1215 if (!(*s).members.isEmpty()) {
1216 writeStartTag(DT_p);
1217 writeGuidAttribute(Doc::canonicalTitle((*s).name));
1218 xmlWriter().writeAttribute("outputclass","h3");
1219 writeCharacters(protectEnc((*s).name));
1220 writeEndTag(); // </p>
1222 generateCompactList(0, marker, ncmap.value(), false, QString("Q"));
1223 else if (idx == QmlClass)
1224 generateCompactList(0, marker, nqcmap.value(), false, QString("Q"));
1225 else if (idx == MemberFunction) {
1226 ParentMaps parentmaps;
1227 ParentMaps::iterator pmap;
1228 NodeList::const_iterator i = s->members.constBegin();
1229 while (i != s->members.constEnd()) {
1230 Node* p = (*i)->parent();
1231 pmap = parentmaps.find(p);
1232 if (pmap == parentmaps.end())
1233 pmap = parentmaps.insert(p,NodeMultiMap());
1234 pmap->insert((*i)->name(),(*i));
1237 pmap = parentmaps.begin();
1238 while (pmap != parentmaps.end()) {
1239 NodeList nlist = pmap->values();
1240 writeStartTag(DT_p);
1241 xmlWriter().writeCharacters("Class ");
1242 writeStartTag(DT_xref);
1244 xmlWriter().writeAttribute("href",linkForNode(pmap.key(), 0));
1245 QStringList pieces = fullName(pmap.key(), 0, marker).split("::");
1246 writeCharacters(protectEnc(pieces.last()));
1247 writeEndTag(); // </xref>
1248 xmlWriter().writeCharacters(":");
1249 writeEndTag(); // </p>
1251 generateSection(nlist, 0, marker, CodeMarker::Summary);
1256 generateSection(s->members, 0, marker, CodeMarker::Summary);
1266 // DITA XML can't do <br>
1268 case Atom::HR: //<p outputclass="horizontal-rule" />
1269 writeStartTag(DT_p);
1270 xmlWriter().writeAttribute("outputclass","horizontal-rule");
1271 writeEndTag(); // </p>
1274 case Atom::InlineImage:
1276 QString fileName = imageFileName(relative, atom->string());
1278 if (atom->next() != 0)
1279 text = atom->next()->string();
1280 if (fileName.isEmpty()) {
1281 relative->location().warning(tr("Missing image: %1").arg(protectEnc(atom->string())));
1282 QString images = "images";
1283 if (!baseDir().isEmpty())
1284 images.prepend("../");
1285 if (!atom->string().isEmpty() && atom->string()[0] != '/')
1286 images.append(QLatin1Char('/'));
1287 fileName = images + atom->string();
1289 if (relative && (relative->type() == Node::Fake) &&
1290 (relative->subType() == Node::Example)) {
1291 const ExampleNode* cen = static_cast<const ExampleNode*>(relative);
1292 if (cen->imageFileName().isEmpty()) {
1293 ExampleNode* en = const_cast<ExampleNode*>(cen);
1294 en->setImageFileName(fileName);
1298 if (currentTag() != DT_xref && atom->type() != Atom::InlineImage)
1299 writeStartTag(DT_fig);
1300 writeStartTag(DT_image);
1301 writeHrefAttribute(protectEnc(fileName));
1302 if (atom->type() == Atom::Image) {
1303 xmlWriter().writeAttribute("placement","break");
1304 xmlWriter().writeAttribute("align","center");
1306 if (!text.isEmpty()) {
1307 writeStartTag(DT_alt);
1308 writeCharacters(protectEnc(text));
1309 writeEndTag(); // </alt>
1311 writeEndTag(); // </image>
1312 if (currentTag() != DT_xref && atom->type() != Atom::InlineImage)
1313 writeEndTag(); // </fig>
1316 case Atom::ImageText:
1319 case Atom::ImportantLeft:
1320 writeStartTag(DT_note);
1321 xmlWriter().writeAttribute("type","important");
1323 case Atom::ImportantRight:
1324 writeEndTag(); // </note>
1326 case Atom::NoteLeft:
1327 writeStartTag(DT_note);
1328 xmlWriter().writeAttribute("type","note");
1330 case Atom::NoteRight:
1331 writeEndTag(); // </note>
1333 case Atom::LegaleseLeft:
1334 inLegaleseText = true;
1336 case Atom::LegaleseRight:
1337 inLegaleseText = false;
1339 case Atom::LineBreak:
1340 //xmlWriter().writeEmptyElement("br");
1344 const Node *node = 0;
1345 QString myLink = getLink(atom, relative, marker, &node);
1346 if (myLink.isEmpty())
1347 myLink = getCollisionLink(atom);
1348 if (myLink.isEmpty())
1349 relative->doc().location().warning(tr("Can't link to '%1'").arg(atom->string()));
1350 else if (!inSectionHeading)
1355 case Atom::GuidLink:
1357 beginLink(atom->string());
1361 case Atom::LinkNode:
1363 const Node* node = CodeMarker::nodeForString(atom->string());
1364 beginLink(linkForNode(node, relative));
1368 case Atom::ListLeft:
1370 writeEndTag(); // </p>
1373 if (atom->string() == ATOM_LIST_BULLET) {
1374 writeStartTag(DT_ul);
1376 else if (atom->string() == ATOM_LIST_TAG) {
1377 writeStartTag(DT_dl);
1379 else if (atom->string() == ATOM_LIST_VALUE) {
1380 threeColumnEnumValueTable = isThreeColumnEnumValueTable(atom);
1381 if (threeColumnEnumValueTable) {
1382 writeStartTag(DT_simpletable);
1383 xmlWriter().writeAttribute("outputclass","valuelist");
1384 writeStartTag(DT_sthead);
1385 writeStartTag(DT_stentry);
1386 xmlWriter().writeCharacters("Constant");
1387 writeEndTag(); // </stentry>
1388 writeStartTag(DT_stentry);
1389 xmlWriter().writeCharacters("Value");
1390 writeEndTag(); // </stentry>
1391 writeStartTag(DT_stentry);
1392 xmlWriter().writeCharacters("Description");
1393 writeEndTag(); // </stentry>
1394 writeEndTag(); // </sthead>
1397 writeStartTag(DT_simpletable);
1398 xmlWriter().writeAttribute("outputclass","valuelist");
1399 writeStartTag(DT_sthead);
1400 writeStartTag(DT_stentry);
1401 xmlWriter().writeCharacters("Constant");
1402 writeEndTag(); // </stentry>
1403 writeStartTag(DT_stentry);
1404 xmlWriter().writeCharacters("Value");
1405 writeEndTag(); // </stentry>
1406 writeEndTag(); // </sthead>
1410 writeStartTag(DT_ol);
1411 if (atom->string() == ATOM_LIST_UPPERALPHA)
1412 xmlWriter().writeAttribute("outputclass","upperalpha");
1413 else if (atom->string() == ATOM_LIST_LOWERALPHA)
1414 xmlWriter().writeAttribute("outputclass","loweralpha");
1415 else if (atom->string() == ATOM_LIST_UPPERROMAN)
1416 xmlWriter().writeAttribute("outputclass","upperroman");
1417 else if (atom->string() == ATOM_LIST_LOWERROMAN)
1418 xmlWriter().writeAttribute("outputclass","lowerroman");
1419 else // (atom->string() == ATOM_LIST_NUMERIC)
1420 xmlWriter().writeAttribute("outputclass","numeric");
1421 if (atom->next() != 0 && atom->next()->string().toInt() != 1) {
1423 This attribute is not supported in DITA, and at the
1424 moment, including it is causing a validation error
1425 wherever it is used. I think it is only used in the
1428 //xmlWriter().writeAttribute("start",atom->next()->string());
1432 case Atom::ListItemNumber:
1435 case Atom::ListTagLeft:
1436 if (atom->string() == ATOM_LIST_TAG) {
1437 writeStartTag(DT_dt);
1439 else { // (atom->string() == ATOM_LIST_VALUE)
1440 writeStartTag(DT_strow);
1441 writeStartTag(DT_stentry);
1442 writeStartTag(DT_tt);
1443 writeCharacters(protectEnc(plainCode(marker->markedUpEnumValue(atom->next()->string(),
1445 writeEndTag(); // </tt>
1446 writeEndTag(); // </stentry>
1447 writeStartTag(DT_stentry);
1450 if (relative->type() == Node::Enum) {
1451 const EnumNode *enume = static_cast<const EnumNode *>(relative);
1452 itemValue = enume->itemValue(atom->next()->string());
1455 if (itemValue.isEmpty())
1456 xmlWriter().writeCharacters("?");
1458 writeStartTag(DT_tt);
1459 writeCharacters(protectEnc(itemValue));
1460 writeEndTag(); // </tt>
1465 case Atom::ListTagRight:
1466 if (atom->string() == ATOM_LIST_TAG)
1467 writeEndTag(); // </dt>
1469 case Atom::ListItemLeft:
1470 if (atom->string() == ATOM_LIST_TAG) {
1471 writeStartTag(DT_dd);
1473 else if (atom->string() == ATOM_LIST_VALUE) {
1474 if (threeColumnEnumValueTable) {
1475 writeEndTag(); // </stentry>
1476 writeStartTag(DT_stentry);
1480 writeStartTag(DT_li);
1482 if (matchAhead(atom, Atom::ParaLeft))
1485 case Atom::ListItemRight:
1486 if (atom->string() == ATOM_LIST_TAG) {
1487 writeEndTag(); // </dd>
1489 else if (atom->string() == ATOM_LIST_VALUE) {
1490 writeEndTag(); // </stentry>
1491 writeEndTag(); // </strow>
1494 writeEndTag(); // </li>
1497 case Atom::ListRight:
1498 if (atom->string() == ATOM_LIST_BULLET) {
1499 writeEndTag(); // </ul>
1501 else if (atom->string() == ATOM_LIST_TAG) {
1502 writeEndTag(); // </dl>
1504 else if (atom->string() == ATOM_LIST_VALUE) {
1505 writeEndTag(); // </simpletable>
1508 writeEndTag(); // </ol>
1514 case Atom::ParaLeft:
1515 writeStartTag(DT_p);
1517 xmlWriter().writeAttribute("outputclass","legalese");
1520 case Atom::ParaRight:
1523 writeEndTag(); // </p>
1527 case Atom::QuotationLeft:
1528 writeStartTag(DT_lq);
1530 case Atom::QuotationRight:
1531 writeEndTag(); // </lq>
1533 case Atom::RawString:
1534 if (atom->string() == " ")
1536 if (atom->string().startsWith(QLatin1Char('&')))
1537 writeCharacters(atom->string());
1538 else if (atom->string() == "<sup>*</sup>") {
1539 writeStartTag(DT_sup);
1540 writeCharacters("*");
1541 writeEndTag(); // </sup>
1543 else if (atom->string() == "<sup>®</sup>") {
1544 writeStartTag(DT_tm);
1545 xmlWriter().writeAttribute("tmtype","reg");
1546 writeEndTag(); // </tm>
1549 writeStartTag(DT_pre);
1550 xmlWriter().writeAttribute("outputclass","raw-html");
1551 writeCharacters(atom->string());
1552 writeEndTag(); // </pre>
1555 case Atom::SectionLeft:
1556 enterSection("details",QString());
1558 case Atom::SectionRight:
1561 case Atom::SectionHeadingLeft:
1563 writeStartTag(DT_p);
1564 QString id = Text::sectionHeading(atom).toString();
1565 id = stripMarkup(id);
1566 id = Doc::canonicalTitle(id);
1567 writeGuidAttribute(id);
1568 hx = QLatin1Char('h') + QString::number(atom->string().toInt() + hOffset(relative));
1569 xmlWriter().writeAttribute("outputclass",hx);
1570 inSectionHeading = true;
1573 case Atom::SectionHeadingRight:
1574 writeEndTag(); // </title> (see case Atom::SectionHeadingLeft)
1575 inSectionHeading = false;
1577 case Atom::SidebarLeft:
1580 case Atom::SidebarRight:
1584 if (inLink && !inContents && !inSectionHeading) {
1585 generateLink(atom, relative, marker);
1588 writeCharacters(atom->string());
1591 case Atom::TableLeft:
1594 if ((atom->count() > 0) && (atom->string(0) == "borderless"))
1595 attr = "borderless";
1596 else if ((atom->count() > 1) && (atom->string(1) == "borderless"))
1597 attr = "borderless";
1599 writeEndTag(); // </p>
1602 writeStartTag(DT_table);
1603 if (!attr.isEmpty())
1604 xmlWriter().writeAttribute("outputclass",attr);
1606 if (tableColumnCount != 0) {
1607 qDebug() << "ERROR: Nested tables!";
1608 tableColumnCount = 0;
1610 tableColumnCount = countTableColumns(atom->next());
1611 writeStartTag(DT_tgroup);
1612 xmlWriter().writeAttribute("cols",QString::number(tableColumnCount));
1613 for (int i = 0; i < tableColumnCount; i++) {
1614 writeStartTag(DT_colspec);
1615 xmlWriter().writeAttribute("colname", QStringLiteral("col%1").arg(i));
1616 xmlWriter().writeAttribute("colnum", QString::number(i));
1617 xmlWriter().writeAttribute("colwidth", QStringLiteral("1*"));
1618 writeEndTag(); // DT_colspec
1620 inTableHeader = false;
1621 inTableBody = false;
1624 case Atom::TableRight:
1625 writeEndTag(); // </tbody>
1626 writeEndTag(); // </tgroup>
1627 writeEndTag(); // </table>
1628 inTableHeader = false;
1629 inTableBody = false;
1630 tableColumnCount = 0;
1633 case Atom::TableHeaderLeft:
1635 writeEndTag(); // </tbody>
1636 writeEndTag(); // </tgroup>
1637 writeEndTag(); // </table>
1638 inTableHeader = false;
1639 inTableBody = false;
1640 tableColumnCount = 0;
1641 writeStartTag(DT_table);
1643 tableColumnCount = countTableColumns(atom);
1644 writeStartTag(DT_tgroup);
1645 xmlWriter().writeAttribute("cols",QString::number(tableColumnCount));
1648 writeStartTag(DT_thead);
1649 xmlWriter().writeAttribute("valign","top");
1650 writeStartTag(DT_row);
1651 xmlWriter().writeAttribute("valign","top");
1652 inTableHeader = true;
1653 inTableBody = false;
1655 case Atom::TableHeaderRight:
1656 writeEndTag(); // </row>
1657 if (matchAhead(atom, Atom::TableHeaderLeft)) {
1659 writeStartTag(DT_row);
1660 xmlWriter().writeAttribute("valign","top");
1663 writeEndTag(); // </thead>
1664 inTableHeader = false;
1666 writeStartTag(DT_tbody);
1669 case Atom::TableRowLeft:
1670 if (!inTableHeader && !inTableBody) {
1672 writeStartTag(DT_tbody);
1675 writeStartTag(DT_row);
1676 attr = atom->string();
1677 if (!attr.isEmpty()) {
1678 if (attr.contains('=')) {
1682 while (index >= 0) {
1683 index = attr.indexOf('"',from);
1687 index = attr.indexOf('"',from);
1689 if (!values.isEmpty())
1691 values += attr.mid(from,index-from);
1698 xmlWriter().writeAttribute("outputclass", attr);
1700 xmlWriter().writeAttribute("valign","top");
1702 case Atom::TableRowRight:
1703 writeEndTag(); // </row>
1705 case Atom::TableItemLeft:
1708 writeStartTag(DT_entry);
1709 for (int i=0; i<atom->count(); ++i) {
1710 attr = atom->string(i);
1711 if (attr.contains('=')) {
1714 while (index >= 0) {
1715 index = attr.indexOf('"',from);
1719 index = attr.indexOf('"',from);
1721 if (!values.isEmpty())
1723 values += attr.mid(from,index-from);
1730 QStringList spans = attr.split(QLatin1Char(','));
1731 if (spans.size() == 2) {
1732 if (spans[0].toInt()>1) {
1733 xmlWriter().writeAttribute("namest",QStringLiteral("col%1").arg(currentColumn));
1734 xmlWriter().writeAttribute("nameend",QStringLiteral("col%1")
1735 .arg(currentColumn + (spans[0].toInt() - 1)));
1737 if (spans[1].toInt()>1)
1738 xmlWriter().writeAttribute("morerows",spans[1].simplified());
1739 currentColumn += spans[0].toInt();
1743 if (!values.isEmpty())
1744 xmlWriter().writeAttribute("outputclass",values);
1745 if (matchAhead(atom, Atom::ParaLeft))
1749 case Atom::TableItemRight:
1750 if (inTableHeader) {
1751 writeEndTag(); // </entry>
1754 writeEndTag(); // </entry>
1756 if (matchAhead(atom, Atom::ParaLeft))
1759 case Atom::TableOfContents:
1762 const Node* node = relative;
1764 Doc::Sections sectionUnit = Doc::Section4;
1765 QStringList params = atom->string().split(QLatin1Char(','));
1766 QString columnText = params.at(0);
1767 QStringList pieces = columnText.split(QLatin1Char(' '), QString::SkipEmptyParts);
1768 if (pieces.size() >= 2) {
1769 columnText = pieces.at(0);
1771 QString path = pieces.join(" ").trimmed();
1772 node = findNodeForTarget(path, relative, marker, atom);
1775 if (params.size() == 2) {
1776 numColumns = qMax(columnText.toInt(), numColumns);
1777 sectionUnit = (Doc::Sections)params.at(1).toInt();
1781 generateTableOfContents(node,
1790 writeEndTag(); // </p>
1793 writeStartTag(DT_p);
1794 writeGuidAttribute(Doc::canonicalTitle(atom->string()));
1795 xmlWriter().writeAttribute("outputclass","target");
1796 //xmlWriter().writeCharacters(protectEnc(atom->string()));
1797 writeEndTag(); // </p>
1799 case Atom::UnhandledFormat:
1800 writeStartTag(DT_b);
1801 xmlWriter().writeAttribute("outputclass","error");
1802 xmlWriter().writeCharacters("<Missing DITAXML>");
1803 writeEndTag(); // </b>
1805 case Atom::UnknownCommand:
1806 writeStartTag(DT_b);
1807 xmlWriter().writeAttribute("outputclass","error unknown-command");
1808 writeCharacters(protectEnc(atom->string()));
1809 writeEndTag(); // </b>
1812 case Atom::EndQmlText:
1813 // don't do anything with these. They are just tags.
1816 // unknownAtom(atom);
1823 Generate a <cxxClass> element (and all the stuff inside it)
1824 for the C++ class represented by \a innerNode. \a marker is
1825 for marking up the code. I don't know what that means exactly.
1828 DitaXmlGenerator::generateClassLikeNode(InnerNode* inner, CodeMarker* marker)
1830 QList<Section>::ConstIterator s;
1835 if (inner->type() == Node::Namespace) {
1836 const NamespaceNode* nsn = const_cast<NamespaceNode*>(static_cast<const NamespaceNode*>(inner));
1837 rawTitle = marker->plainName(inner);
1838 fullTitle = marker->plainFullName(inner);
1839 title = rawTitle + " Namespace";
1842 Note: Because the C++ specialization we are using
1843 has no <cxxNamespace> element, we are using the
1844 <cxxClass> element with an outputclass attribute
1845 set to "namespace" .
1847 generateHeader(inner, fullTitle);
1848 generateBrief(inner, marker); // <shortdesc>
1851 writeStartTag(DT_cxxClassDetail);
1852 writeStartTag(DT_cxxClassDefinition);
1854 writeEndTag(); // <cxxClassDefinition>
1856 enterDesc(DT_apiDesc,QString(),title);
1857 generateStatus(nsn, marker);
1858 generateThreadSafeness(nsn, marker);
1859 generateSince(nsn, marker);
1861 enterSection(QString(), QString());
1862 generateBody(nsn, marker);
1863 generateAlsoList(nsn, marker);
1865 leaveSection(); // </apiDesc>
1867 bool needOtherSection = false;
1868 QList<Section> summarySections;
1869 summarySections = marker->sections(inner, CodeMarker::Summary, CodeMarker::Okay);
1870 if (!summarySections.isEmpty()) {
1871 enterSection("redundant",QString());
1872 s = summarySections.constBegin();
1873 while (s != summarySections.constEnd()) {
1874 if (s->members.isEmpty() && s->reimpMembers.isEmpty()) {
1875 if (!s->inherited.isEmpty())
1876 needOtherSection = true;
1880 if (!s->members.isEmpty()) {
1881 writeStartTag(DT_p);
1882 attr = cleanRef((*s).name).toLower() + " h2";
1883 xmlWriter().writeAttribute("outputclass",attr);
1884 writeCharacters(protectEnc((*s).name));
1885 writeEndTag(); // </title>
1886 generateSection(s->members, inner, marker, CodeMarker::Summary);
1887 generateSectionInheritedList(*s, inner, marker);
1889 if (!s->reimpMembers.isEmpty()) {
1890 QString name = QString("Reimplemented ") + (*s).name;
1891 attr = cleanRef(name).toLower() + " h2";
1892 writeStartTag(DT_p);
1893 xmlWriter().writeAttribute("outputclass",attr);
1894 writeCharacters(protectEnc(name));
1895 writeEndTag(); // </title>
1896 generateSection(s->reimpMembers, inner, marker, CodeMarker::Summary);
1897 generateSectionInheritedList(*s, inner, marker);
1902 if (needOtherSection) {
1903 writeStartTag(DT_p);
1904 xmlWriter().writeAttribute("outputclass","h3");
1905 xmlWriter().writeCharacters("Additional Inherited Members");
1906 writeEndTag(); // </title>
1907 s = summarySections.constBegin();
1908 while (s != summarySections.constEnd()) {
1909 if (s->members.isEmpty())
1910 generateSectionInheritedList(*s, inner, marker);
1917 writeEndTag(); // </cxxClassDetail>
1919 // not included: <related-links>
1920 // not included: <cxxClassNested>
1922 QList<Section> detailSections;
1923 detailSections = marker->sections(inner, CodeMarker::Detailed, CodeMarker::Okay);
1924 s = detailSections.constBegin();
1925 while (s != detailSections.constEnd()) {
1926 if ((*s).name == "Classes") {
1927 writeNestedClasses((*s),nsn);
1933 s = detailSections.constBegin();
1934 while (s != detailSections.constEnd()) {
1935 if ((*s).name == "Function Documentation") {
1936 writeFunctions((*s),nsn,marker);
1938 else if ((*s).name == "Type Documentation") {
1939 writeEnumerations((*s),marker);
1940 writeTypedefs((*s),marker);
1942 else if ((*s).name == "Namespaces") {
1943 qDebug() << "Nested namespaces" << outFileName();
1945 else if ((*s).name == "Macro Documentation") {
1946 //writeMacros((*s),marker);
1951 generateLowStatusMembers(inner,marker,CodeMarker::Obsolete);
1952 generateLowStatusMembers(inner,marker,CodeMarker::Compat);
1953 writeEndTag(); // </cxxClass>
1955 else if (inner->type() == Node::Class) {
1956 const ClassNode* cn = const_cast<ClassNode*>(static_cast<const ClassNode*>(inner));
1957 rawTitle = marker->plainName(inner);
1958 fullTitle = marker->plainFullName(inner);
1959 title = rawTitle + " Class";
1961 generateHeader(inner, fullTitle);
1962 generateBrief(inner, marker); // <shortdesc>
1965 writeStartTag(DT_cxxClassDetail);
1966 writeStartTag(DT_cxxClassDefinition);
1967 writeStartTag(DT_cxxClassAccessSpecifier);
1968 xmlWriter().writeAttribute("value",inner->accessString());
1969 writeEndTag(); // <cxxClassAccessSpecifier>
1970 if (cn->isAbstract()) {
1971 writeStartTag(DT_cxxClassAbstract);
1972 xmlWriter().writeAttribute("name","abstract");
1973 xmlWriter().writeAttribute("value","abstract");
1974 writeEndTag(); // </cxxClassAbstract>
1976 writeDerivations(cn, marker); // <cxxClassDerivations>
1978 // not included: <cxxClassTemplateParameters>
1981 writeEndTag(); // <cxxClassDefinition>
1983 enterDesc(DT_apiDesc,QString(),title);
1984 generateStatus(cn, marker);
1985 generateInherits(cn, marker);
1986 generateInheritedBy(cn, marker);
1987 generateThreadSafeness(cn, marker);
1988 generateSince(cn, marker);
1989 enterSection(QString(), QString());
1990 generateBody(cn, marker);
1991 generateAlsoList(cn, marker);
1993 leaveSection(); // </apiDesc>
1995 bool needOtherSection = false;
1996 QList<Section> summarySections;
1997 summarySections = marker->sections(inner, CodeMarker::Summary, CodeMarker::Okay);
1998 if (!summarySections.isEmpty()) {
1999 enterSection("redundant",QString());
2000 s = summarySections.constBegin();
2001 while (s != summarySections.constEnd()) {
2002 if (s->members.isEmpty() && s->reimpMembers.isEmpty()) {
2003 if (!s->inherited.isEmpty())
2004 needOtherSection = true;
2008 if (!s->members.isEmpty()) {
2009 writeStartTag(DT_p);
2010 attr = cleanRef((*s).name).toLower() + " h2";
2011 xmlWriter().writeAttribute("outputclass",attr);
2012 writeCharacters(protectEnc((*s).name));
2013 writeEndTag(); // </p>
2014 generateSection(s->members, inner, marker, CodeMarker::Summary);
2015 generateSectionInheritedList(*s, inner, marker);
2017 if (!s->reimpMembers.isEmpty()) {
2018 QString name = QString("Reimplemented ") + (*s).name;
2019 attr = cleanRef(name).toLower() + " h2";
2020 writeStartTag(DT_p);
2021 xmlWriter().writeAttribute("outputclass",attr);
2022 writeCharacters(protectEnc(name));
2023 writeEndTag(); // </p>
2024 generateSection(s->reimpMembers, inner, marker, CodeMarker::Summary);
2025 generateSectionInheritedList(*s, inner, marker);
2030 if (needOtherSection) {
2031 writeStartTag(DT_p);
2032 xmlWriter().writeAttribute("outputclass","h3");
2033 xmlWriter().writeCharacters("Additional Inherited Members");
2034 writeEndTag(); // </p>
2035 s = summarySections.constBegin();
2036 while (s != summarySections.constEnd()) {
2037 if (s->members.isEmpty())
2038 generateSectionInheritedList(*s, inner, marker);
2045 // not included: <example> or <apiImpl>
2047 writeEndTag(); // </cxxClassDetail>
2049 // not included: <related-links>
2050 // not included: <cxxClassNested>
2052 QList<Section> detailSections;
2053 detailSections = marker->sections(inner, CodeMarker::Detailed, CodeMarker::Okay);
2054 s = detailSections.constBegin();
2055 while (s != detailSections.constEnd()) {
2056 if ((*s).name == "Member Function Documentation") {
2057 writeFunctions((*s),cn,marker);
2059 else if ((*s).name == "Member Type Documentation") {
2060 writeEnumerations((*s),marker);
2061 writeTypedefs((*s),marker);
2063 else if ((*s).name == "Member Variable Documentation") {
2064 writeDataMembers((*s),marker);
2066 else if ((*s).name == "Property Documentation") {
2067 writeProperties((*s),marker);
2069 else if ((*s).name == "Macro Documentation") {
2070 //writeMacros((*s),marker);
2072 else if ((*s).name == "Related Non-Members") {
2073 QString attribute("related-non-member");
2074 writeFunctions((*s),cn,marker,attribute);
2079 generateLowStatusMembers(inner,marker,CodeMarker::Obsolete);
2080 generateLowStatusMembers(inner,marker,CodeMarker::Compat);
2081 writeEndTag(); // </cxxClass>
2083 else if ((inner->type() == Node::Fake) && (inner->subType() == Node::HeaderFile)) {
2084 const FakeNode* fn = const_cast<FakeNode*>(static_cast<const FakeNode*>(inner));
2085 rawTitle = marker->plainName(inner);
2086 fullTitle = marker->plainFullName(inner);
2090 Note: Because the C++ specialization we are using
2091 has no <cxxHeaderFile> element, we are using the
2092 <cxxClass> element with an outputclass attribute
2093 set to "headerfile" .
2095 generateHeader(inner, fullTitle);
2096 generateBrief(inner, marker); // <shortdesc>
2099 writeStartTag(DT_cxxClassDetail);
2100 enterDesc(DT_apiDesc,QString(),title);
2101 generateStatus(fn, marker);
2102 generateThreadSafeness(fn, marker);
2103 generateSince(fn, marker);
2104 generateSince(fn, marker);
2105 enterSection(QString(), QString());
2106 generateBody(fn, marker);
2107 generateAlsoList(fn, marker);
2109 leaveSection(); // </apiDesc>
2111 bool needOtherSection = false;
2112 QList<Section> summarySections;
2113 summarySections = marker->sections(inner, CodeMarker::Summary, CodeMarker::Okay);
2114 if (!summarySections.isEmpty()) {
2115 enterSection("redundant",QString());
2116 s = summarySections.constBegin();
2117 while (s != summarySections.constEnd()) {
2118 if (s->members.isEmpty() && s->reimpMembers.isEmpty()) {
2119 if (!s->inherited.isEmpty())
2120 needOtherSection = true;
2124 if (!s->members.isEmpty()) {
2125 writeStartTag(DT_p);
2126 attr = cleanRef((*s).name).toLower() + " h2";
2127 xmlWriter().writeAttribute("outputclass",attr);
2128 writeCharacters(protectEnc((*s).name));
2129 writeEndTag(); // </p>
2130 generateSection(s->members, inner, marker, CodeMarker::Summary);
2131 generateSectionInheritedList(*s, inner, marker);
2133 if (!s->reimpMembers.isEmpty()) {
2134 QString name = QString("Reimplemented ") + (*s).name;
2135 attr = cleanRef(name).toLower() + " h2";
2136 writeStartTag(DT_p);
2137 xmlWriter().writeAttribute("outputclass",attr);
2138 writeCharacters(protectEnc(name));
2139 writeEndTag(); // </p>
2140 generateSection(s->reimpMembers, inner, marker, CodeMarker::Summary);
2141 generateSectionInheritedList(*s, inner, marker);
2146 if (needOtherSection) {
2147 enterSection("additional-inherited-members redundant",QString());
2148 writeStartTag(DT_p);
2149 xmlWriter().writeAttribute("outputclass","h3");
2150 xmlWriter().writeCharacters("Additional Inherited Members");
2151 writeEndTag(); // </p>
2152 s = summarySections.constBegin();
2153 while (s != summarySections.constEnd()) {
2154 if (s->members.isEmpty())
2155 generateSectionInheritedList(*s, inner, marker);
2162 writeEndTag(); // </cxxClassDetail>
2164 // not included: <related-links>
2165 // not included: <cxxClassNested>
2167 QList<Section> detailSections;
2168 detailSections = marker->sections(inner, CodeMarker::Detailed, CodeMarker::Okay);
2169 s = detailSections.constBegin();
2170 while (s != detailSections.constEnd()) {
2171 if ((*s).name == "Classes") {
2172 writeNestedClasses((*s),fn);
2178 s = detailSections.constBegin();
2179 while (s != detailSections.constEnd()) {
2180 if ((*s).name == "Function Documentation") {
2181 writeFunctions((*s),fn,marker);
2183 else if ((*s).name == "Type Documentation") {
2184 writeEnumerations((*s),marker);
2185 writeTypedefs((*s),marker);
2187 else if ((*s).name == "Namespaces") {
2188 qDebug() << "Nested namespaces" << outFileName();
2190 else if ((*s).name == "Macro Documentation") {
2191 //writeMacros((*s),marker);
2195 generateLowStatusMembers(inner,marker,CodeMarker::Obsolete);
2196 generateLowStatusMembers(inner,marker,CodeMarker::Compat);
2197 writeEndTag(); // </cxxClass>
2199 else if ((inner->type() == Node::Fake) && (inner->subType() == Node::QmlClass)) {
2200 QmlClassNode* qcn = const_cast<QmlClassNode*>(static_cast<const QmlClassNode*>(inner));
2201 ClassNode* cn = qcn->classNode();
2202 rawTitle = marker->plainName(inner);
2203 fullTitle = marker->plainFullName(inner);
2204 title = rawTitle + " Type";
2205 Node::clearPropertyGroupCount();
2207 generateHeader(inner, fullTitle);
2208 generateBrief(inner, marker); // <shortdesc>
2211 writeStartTag(DT_qmlTypeDetail);
2212 generateQmlModuleDef(qcn);
2213 generateQmlInherits(qcn,marker);
2214 generateQmlInheritedBy(qcn, marker);
2215 generateQmlInstantiates(qcn,marker);
2216 generateQmlSince(qcn);
2218 enterDesc(DT_apiDesc,QString(),title);
2219 enterSection(QString(), QString());
2220 generateBody(qcn, marker);
2222 generateQmlText(cn->doc().body(), cn, marker, qcn->name());
2223 generateAlsoList(cn, marker);
2226 leaveSection(); // </apiDesc>
2227 writeEndTag(); // </qmlTypeDetail>
2229 QList<Section> members = marker->qmlSections(qcn,CodeMarker::Detailed);
2230 if (!members.isEmpty()) {
2231 s = members.constBegin();
2232 while (s != members.constEnd()) {
2233 if (!s->members.isEmpty()) {
2234 NodeList::ConstIterator m = (*s).members.constBegin();
2235 while (m != (*s).members.constEnd()) {
2236 generateDetailedQmlMember(*m, qcn, marker);
2243 writeEndTag(); // </apiRef>
2248 Write a list item for a \a link with the given \a text.
2250 void DitaXmlGenerator::writeXrefListItem(const QString& link, const QString& text)
2252 writeStartTag(DT_li);
2253 writeStartTag(DT_xref);
2255 writeHrefAttribute(link);
2256 writeCharacters(text);
2257 writeEndTag(); // </xref>
2258 writeEndTag(); // </li>
2262 Generate the DITA page for a qdoc file that doesn't map
2263 to an underlying c++ file.
2265 void DitaXmlGenerator::generateFakeNode(FakeNode* fake, CodeMarker* marker)
2268 If the fake node is a page node, and if the page type
2269 is DITA map page, write the node's contents as a dita
2270 map and return without doing anything else.
2272 if (fake->subType() == Node::Page && fake->pageType() == Node::DitaMapPage) {
2273 const DitaMapNode* dmn = static_cast<const DitaMapNode*>(fake);
2278 QList<Section> sections;
2279 QList<Section>::const_iterator s;
2280 QString fullTitle = fake->fullTitle();
2282 if (fake->subType() == Node::QmlBasicType) {
2283 fullTitle = "QML Basic Type: " + fullTitle;
2285 else if (fake->subType() == Node::Collision) {
2286 fullTitle = "Name Collision: " + fullTitle;
2289 generateHeader(fake, fullTitle);
2290 generateBrief(fake, marker); // <shortdesc>
2293 writeStartTag(DT_body);
2294 enterSection(QString(), QString());
2295 if (fake->subType() == Node::Module) {
2296 generateStatus(fake, marker);
2297 if (moduleNamespaceMap.contains(fake->name())) {
2298 enterSection("h2","Namespaces");
2299 generateAnnotatedList(fake, marker, moduleNamespaceMap[fake->name()]);
2302 if (moduleClassMap.contains(fake->name())) {
2303 enterSection("h2","Classes");
2304 generateAnnotatedList(fake, marker, moduleClassMap[fake->name()]);
2309 if (fake->doc().isEmpty()) {
2310 if (fake->subType() == Node::File) {
2313 writeStartTag(DT_p);
2314 xmlWriter().writeAttribute("outputclass", "small-subtitle");
2315 text << fake->subTitle();
2316 generateText(text, fake, marker);
2317 writeEndTag(); // </p>
2318 Doc::quoteFromFile(fake->doc().location(), quoter, fake->name());
2319 QString code = quoter.quoteTo(fake->location(), "", "");
2321 text << Atom(Atom::Code, code);
2322 generateText(text, fake, marker);
2326 if (fake->subType() == Node::Module) {
2327 enterSection(QString(), QString());
2328 generateBody(fake, marker);
2332 generateBody(fake, marker);
2334 generateAlsoList(fake, marker);
2336 if ((fake->subType() == Node::QmlModule) && !fake->qmlModuleMembers().isEmpty()) {
2337 NodeMap qmlModuleMembersMap;
2338 foreach (const Node* node, fake->qmlModuleMembers()) {
2339 if (node->type() == Node::Fake && node->subType() == Node::QmlClass)
2340 qmlModuleMembersMap[node->name()] = node;
2342 generateAnnotatedList(fake, marker, qmlModuleMembersMap);
2344 else if (!fake->groupMembers().isEmpty()) {
2345 NodeMap groupMembersMap;
2346 foreach (const Node *node, fake->groupMembers()) {
2347 if (node->type() == Node::Class || node->type() == Node::Namespace)
2348 groupMembersMap[node->name()] = node;
2350 generateAnnotatedList(fake, marker, groupMembersMap);
2353 leaveSection(); // </section>
2354 if (!writeEndTag()) { // </body>
2355 fake->doc().location().warning(tr("Pop of empty XML tag stack; generating DITA for '%1'").arg(fake->name()));
2358 writeRelatedLinks(fake, marker);
2359 writeEndTag(); // </topic>
2363 This function writes a \e{<link>} element inside a
2364 \e{<related-links>} element.
2366 \sa writeRelatedLinks()
2368 void DitaXmlGenerator::writeLink(const Node* node,
2369 const QString& text,
2370 const QString& role)
2373 QString link = fileName(node) + QLatin1Char('#') + node->guid();
2374 if (link.endsWith(QLatin1Char('#')))
2375 qDebug() << "LINK ENDS WITH #:" << link << outFileName();
2376 writeStartTag(DT_link);
2377 writeHrefAttribute(link);
2378 xmlWriter().writeAttribute("role", role);
2379 writeStartTag(DT_linktext);
2380 writeCharacters(text);
2381 writeEndTag(); // </linktext>
2382 writeEndTag(); // </link>
2387 This function writes a \e{<related-links>} element, which
2388 contains the \c{next}, \c{previous}, and \c{start}
2389 links for topic pages that have them. Note that the
2390 value of the \e role attribute is \c{parent} for the
2393 void DitaXmlGenerator::writeRelatedLinks(const FakeNode* node, CodeMarker* marker)
2395 const Node* linkNode = 0;
2396 QPair<QString,QString> linkPair;
2397 if (node && !node->links().empty()) {
2398 writeStartTag(DT_relatedLinks);
2399 if (node->links().contains(Node::PreviousLink)) {
2400 linkPair = node->links()[Node::PreviousLink];
2401 linkNode = findNodeForTarget(linkPair.first, node, marker);
2402 if (linkNode && linkNode->type() == Node::Fake) {
2403 const FakeNode *fakeNode = static_cast<const FakeNode*>(linkNode);
2404 linkPair.second = fakeNode->title();
2406 writeLink(linkNode, linkPair.second, "previous");
2408 if (node->links().contains(Node::NextLink)) {
2409 linkPair = node->links()[Node::NextLink];
2410 linkNode = findNodeForTarget(linkPair.first, node, marker);
2411 if (linkNode && linkNode->type() == Node::Fake) {
2412 const FakeNode *fakeNode = static_cast<const FakeNode*>(linkNode);
2413 linkPair.second = fakeNode->title();
2415 writeLink(linkNode, linkPair.second, "next");
2417 if (node->links().contains(Node::StartLink)) {
2418 linkPair = node->links()[Node::StartLink];
2419 linkNode = findNodeForTarget(linkPair.first, node, marker);
2420 if (linkNode && linkNode->type() == Node::Fake) {
2421 const FakeNode *fakeNode = static_cast<const FakeNode*>(linkNode);
2422 linkPair.second = fakeNode->title();
2424 writeLink(linkNode, linkPair.second, "parent");
2426 writeEndTag(); // </related-links>
2431 Returns "dita" for this subclass of class Generator.
2433 QString DitaXmlGenerator::fileExtension() const
2439 Writes an XML file header to the current XML stream. This
2440 depends on which kind of DITA XML file is being generated,
2441 which is determined by the \a node type and subtype and the
2442 \a subpage flag. If the \subpage flag is true, a \c{<topic>}
2443 header is written, regardless of the type of \a node.
2445 void DitaXmlGenerator::generateHeader(const Node* node,
2446 const QString& name,
2452 DitaTag mainTag = DT_cxxClass;
2453 DitaTag nameTag = DT_apiName;
2458 QString outputclass;
2460 if (node->type() == Node::Class) {
2461 mainTag = DT_cxxClass;
2462 nameTag = DT_apiName;
2463 dtd = "dtd/cxxClass.dtd";
2465 doctype = "<!DOCTYPE " + ditaTags[mainTag] +
2466 " PUBLIC \"-//NOKIA//DTD DITA C++ API Class Reference Type v" +
2467 version + "//EN\" \"" + dtd + "\">";
2469 else if (node->type() == Node::Namespace) {
2470 mainTag = DT_cxxClass;
2471 nameTag = DT_apiName;
2472 dtd = "dtd/cxxClass.dtd";
2474 doctype = "<!DOCTYPE " + ditaTags[mainTag] +
2475 " PUBLIC \"-//NOKIA//DTD DITA C++ API Class Reference Type v" +
2476 version + "//EN\" \"" + dtd + "\">";
2477 outputclass = "namespace";
2479 else if (node->type() == Node::Fake || subpage) {
2480 if (node->subType() == Node::HeaderFile) {
2481 mainTag = DT_cxxClass;
2482 nameTag = DT_apiName;
2483 dtd = "dtd/cxxClass.dtd";
2485 doctype = "<!DOCTYPE " + ditaTags[mainTag] +
2486 " PUBLIC \"-//NOKIA//DTD DITA C++ API Class Reference Type v" +
2487 version + "//EN\" \"" + dtd + "\">";
2488 outputclass = "headerfile";
2490 else if (node->subType() == Node::QmlClass) {
2491 mainTag = DT_qmlType;
2492 nameTag = DT_apiName;
2493 dtd = "dtd/qmlType.dtd";
2495 doctype = "<!DOCTYPE " + ditaTags[mainTag] +
2496 " PUBLIC \"-//NOKIA//DTD DITA QML Type" +
2497 "//EN\" \"" + dtd + "\">";
2498 outputclass = "QML-type";
2503 dtd = "dtd/topic.dtd";
2504 doctype = "<!DOCTYPE " + ditaTags[mainTag] +
2505 " PUBLIC \"-//OASIS//DTD DITA Topic//EN\" \"" + dtd + "\">";
2506 switch (node->subType()) {
2508 outputclass = node->pageTypeString();
2511 outputclass = "group";
2514 outputclass = "example";
2517 outputclass = "file";
2519 case Node::Image: // not used
2520 outputclass = "image";
2523 outputclass = "module";
2525 case Node::ExternalPage: // not used
2526 outputclass = "externalpage";
2528 case Node::Collision:
2529 outputclass = "collision";
2532 outputclass = "page";
2537 xmlWriter().writeDTD(doctype);
2538 xmlWriter().writeComment(node->doc().location().fileName());
2539 writeStartTag(mainTag);
2540 QString id = node->guid();
2541 xmlWriter().writeAttribute("id",id);
2542 if (!outputclass.isEmpty())
2543 xmlWriter().writeAttribute("outputclass",outputclass);
2544 writeStartTag(nameTag); // <title> or <apiName>
2545 if (!name.isEmpty())
2546 writeCharacters(name);
2548 writeCharacters(node->name());
2549 writeEndTag(); // </title> or </apiName>
2553 Outputs the \e brief command as a <shortdesc> element.
2555 void DitaXmlGenerator::generateBrief(const Node* node, CodeMarker* marker)
2557 Text brief = node->doc().briefText(true); // zzz
2558 if (!brief.isEmpty()) {
2559 generateText(brief, node, marker);
2565 Generates a table of contents beginning at \a node.
2566 Currently just returns without writing anything.
2568 void DitaXmlGenerator::generateTableOfContents(const Node* node,
2570 Doc::Sections sectionUnit,
2572 const Node* relative)
2576 if (!node->doc().hasTableOfContents())
2578 QList<Atom *> toc = node->doc().tableOfContents();
2582 QString nodeName = "";
2583 if (node != relative)
2584 nodeName = node->name();
2586 QStringList sectionNumber;
2590 if (numColumns > 1) {
2591 tdTag = "<td>"; /* width=\"" + QString::number((100 + numColumns - 1) / numColumns) + "%\">";*/
2592 out() << "<table class=\"toc\">\n<tr class=\"topAlign\">"
2596 // disable nested links in table of contents
2600 for (int i = 0; i < toc.size(); ++i) {
2601 Atom *atom = toc.at(i);
2603 int nextLevel = atom->string().toInt();
2604 if (nextLevel > (int)sectionUnit)
2607 if (sectionNumber.size() < nextLevel) {
2610 sectionNumber.append("1");
2611 } while (sectionNumber.size() < nextLevel);
2614 while (sectionNumber.size() > nextLevel) {
2616 sectionNumber.removeLast();
2618 sectionNumber.last() = QString::number(sectionNumber.last().toInt() + 1);
2621 Text headingText = Text::sectionHeading(atom);
2623 if (sectionNumber.size() == 1 && columnSize > toc.size() / numColumns) {
2624 out() << "</ul></td>" << tdTag << "<ul>\n";
2628 out() << "<xref href=\""
2631 << Doc::canonicalTitle(headingText.toString())
2633 generateAtomList(headingText.firstAtom(), node, marker, true, numAtoms);
2634 out() << "</xref></li>\n";
2638 while (!sectionNumber.isEmpty()) {
2640 sectionNumber.removeLast();
2644 out() << "</td></tr></table>\n";
2652 Revised for the new doc format.
2653 Generates a table of contents beginning at \a node.
2655 void DitaXmlGenerator::generateTableOfContents(const Node* node,
2657 QList<Section>* sections)
2660 if (node->doc().hasTableOfContents())
2661 toc = node->doc().tableOfContents();
2662 if (toc.isEmpty() && !sections && (node->subType() != Node::Module))
2665 QStringList sectionNumber;
2666 int detailsBase = 0;
2668 // disable nested links in table of contents
2672 out() << "<div class=\"toc\">\n";
2673 out() << "<h3>Contents</h3>\n";
2674 sectionNumber.append("1");
2677 if (node->subType() == Node::Module) {
2678 if (moduleNamespaceMap.contains(node->name())) {
2679 out() << "<li class=\"level"
2680 << sectionNumber.size()
2681 << "\"><xref href=\"#"
2682 << registerRef("namespaces")
2683 << "\">Namespaces</xref></li>\n";
2685 if (moduleClassMap.contains(node->name())) {
2686 out() << "<li class=\"level"
2687 << sectionNumber.size()
2688 << "\"><xref href=\"#"
2689 << registerRef("classes")
2690 << "\">Classes</xref></li>\n";
2692 out() << "<li class=\"level"
2693 << sectionNumber.size()
2694 << "\"><xref href=\"#"
2695 << registerRef("details")
2696 << "\">Detailed Description</xref></li>\n";
2697 for (int i = 0; i < toc.size(); ++i) {
2698 if (toc.at(i)->string().toInt() == 1) {
2704 else if (sections && (node->type() == Node::Class)) {
2705 QList<Section>::ConstIterator s = sections->constBegin();
2706 while (s != sections->constEnd()) {
2707 if (!s->members.isEmpty() || !s->reimpMembers.isEmpty()) {
2708 out() << "<li class=\"level"
2709 << sectionNumber.size()
2710 << "\"><xref href=\"#"
2711 << registerRef((*s).pluralMember)
2712 << "\">" << (*s).name
2713 << "</xref></li>\n";
2717 out() << "<li class=\"level"
2718 << sectionNumber.size()
2719 << "\"><xref href=\"#"
2720 << registerRef("details")
2721 << "\">Detailed Description</xref></li>\n";
2722 for (int i = 0; i < toc.size(); ++i) {
2723 if (toc.at(i)->string().toInt() == 1) {
2730 for (int i = 0; i < toc.size(); ++i) {
2731 Atom *atom = toc.at(i);
2732 int nextLevel = atom->string().toInt() + detailsBase;
2733 if (sectionNumber.size() < nextLevel) {
2735 sectionNumber.append("1");
2736 } while (sectionNumber.size() < nextLevel);
2739 while (sectionNumber.size() > nextLevel) {
2740 sectionNumber.removeLast();
2742 sectionNumber.last() = QString::number(sectionNumber.last().toInt() + 1);
2745 Text headingText = Text::sectionHeading(atom);
2746 QString s = headingText.toString();
2747 out() << "<li class=\"level"
2748 << sectionNumber.size()
2750 out() << "<xref href=\""
2752 << Doc::canonicalTitle(s)
2754 generateAtomList(headingText.firstAtom(), node, marker, true, numAtoms);
2755 out() << "</xref></li>\n";
2757 while (!sectionNumber.isEmpty()) {
2758 sectionNumber.removeLast();
2761 out() << "</div>\n";
2766 void DitaXmlGenerator::generateLowStatusMembers(const InnerNode* inner,
2768 CodeMarker::Status status)
2771 if (status == CodeMarker::Compat)
2772 attribute = "Qt3-support";
2773 else if (status == CodeMarker::Obsolete)
2774 attribute = "obsolete";
2778 QList<Section> sections = marker->sections(inner, CodeMarker::Detailed, status);
2779 QMutableListIterator<Section> j(sections);
2780 while (j.hasNext()) {
2781 if (j.next().members.size() == 0)
2784 if (sections.isEmpty())
2787 QList<Section>::ConstIterator s = sections.constBegin();
2788 while (s != sections.constEnd()) {
2789 if ((*s).name == "Member Function Documentation") {
2790 writeFunctions((*s),inner,marker,attribute);
2792 else if ((*s).name == "Member Type Documentation") {
2793 writeEnumerations((*s),marker,attribute);
2794 writeTypedefs((*s),marker,attribute);
2796 else if ((*s).name == "Member Variable Documentation") {
2797 writeDataMembers((*s),marker,attribute);
2799 else if ((*s).name == "Property Documentation") {
2800 writeProperties((*s),marker,attribute);
2802 else if ((*s).name == "Macro Documentation") {
2803 //writeMacros((*s),marker,attribute);
2810 Write the XML for the class hierarchy to the current XML stream.
2812 void DitaXmlGenerator::generateClassHierarchy(const Node* relative,
2814 const QMap<QString,const Node*>& classMap)
2816 if (classMap.isEmpty())
2820 NodeMap::ConstIterator c = classMap.constBegin();
2821 while (c != classMap.constEnd()) {
2822 const ClassNode* classe = static_cast<const ClassNode*>(*c);
2823 if (classe->baseClasses().isEmpty())
2824 topLevel.insert(classe->name(), classe);
2828 QStack<NodeMap > stack;
2829 stack.push(topLevel);
2831 writeStartTag(DT_ul);
2832 while (!stack.isEmpty()) {
2833 if (stack.top().isEmpty()) {
2835 writeEndTag(); // </ul>
2836 if (!stack.isEmpty())
2837 writeEndTag(); // </li>
2840 const ClassNode *child =
2841 static_cast<const ClassNode *>(*stack.top().constBegin());
2842 writeStartTag(DT_li);
2843 generateFullName(child, relative, marker);
2844 writeEndTag(); // </li>
2845 stack.top().erase(stack.top().begin());
2848 foreach (const RelatedClass &d, child->derivedClasses()) {
2849 if (d.access != Node::Private && !d.node->doc().isEmpty())
2850 newTop.insert(d.node->name(), d.node);
2852 if (!newTop.isEmpty()) {
2854 writeStartTag(DT_li);
2855 writeStartTag(DT_ul);
2862 Write XML for the contents of the \a nodeMap to the current
2865 void DitaXmlGenerator::generateAnnotatedList(const Node* relative,
2867 const NodeMap& nodeMap)
2869 if (nodeMap.isEmpty())
2871 writeStartTag(DT_table);
2872 xmlWriter().writeAttribute("outputclass","annotated");
2873 writeStartTag(DT_tgroup);
2874 xmlWriter().writeAttribute("cols","2");
2875 writeStartTag(DT_tbody);
2877 foreach (const QString& name, nodeMap.keys()) {
2878 const Node* node = nodeMap[name];
2880 if (node->status() == Node::Obsolete)
2883 writeStartTag(DT_row);
2884 writeStartTag(DT_entry);
2885 writeStartTag(DT_p);
2886 generateFullName(node, relative, marker);
2887 writeEndTag(); // </p>
2888 writeEndTag(); // <entry>
2890 if (!(node->type() == Node::Fake)) {
2891 Text brief = node->doc().trimmedBriefText(name);
2892 if (!brief.isEmpty()) {
2893 writeStartTag(DT_entry);
2894 writeStartTag(DT_p);
2895 generateText(brief, node, marker);
2896 writeEndTag(); // </p>
2897 writeEndTag(); // <entry>
2901 writeStartTag(DT_entry);
2902 writeStartTag(DT_p);
2903 writeCharacters(protectEnc(node->doc().briefText().toString())); // zzz
2904 writeEndTag(); // </p>
2905 writeEndTag(); // <entry>
2907 writeEndTag(); // </row>
2909 writeEndTag(); // </tbody>
2910 writeEndTag(); // </tgroup>
2911 writeEndTag(); // </table>
2915 This function finds the common prefix of the names of all
2916 the classes in \a classMap and then generates a compact
2917 list of the class names alphabetized on the part of the
2918 name not including the common prefix. You can tell the
2919 function to use \a comonPrefix as the common prefix, but
2920 normally you let it figure it out itself by looking at
2921 the name of the first and last classes in \a classMap.
2923 void DitaXmlGenerator::generateCompactList(const Node* relative,
2925 const NodeMap& classMap,
2926 bool includeAlphabet,
2927 QString commonPrefix)
2929 const int NumParagraphs = 37; // '0' to '9', 'A' to 'Z', '_'
2931 if (classMap.isEmpty())
2935 If commonPrefix is not empty, then the caller knows what
2936 the common prefix is and has passed it in, so just use that
2937 one. But if the commonPrefix is empty (it normally is), then
2938 compute a common prefix using this simple algorithm. Note we
2939 assume the prefix length is 1, i.e. we will have a single
2940 character as the common prefix.
2942 int commonPrefixLen = commonPrefix.length();
2943 if (commonPrefixLen == 0) {
2944 QVector<int> count(26);
2945 for (int i=0; i<26; ++i)
2948 NodeMap::const_iterator iter = classMap.constBegin();
2949 while (iter != classMap.constEnd()) {
2950 if (!iter.key().contains("::")) {
2951 QChar c = iter.key()[0];
2952 if ((c >= 'A') && (c <= 'Z')) {
2953 int idx = c.unicode() - QChar('A').unicode();
2961 for (int i=0; i<26; ++i) {
2962 if (count[i] > highest) {
2967 idx += QChar('A').unicode();
2969 commonPrefix = common;
2970 commonPrefixLen = 1;
2974 Divide the data into 37 paragraphs: 0, ..., 9, A, ..., Z,
2975 underscore (_). QAccel will fall in paragraph 10 (A) and
2976 QXtWidget in paragraph 33 (X). This is the only place where we
2977 assume that NumParagraphs is 37. Each paragraph is a NodeMap.
2979 NodeMap paragraph[NumParagraphs+1];
2980 QString paragraphName[NumParagraphs+1];
2981 QSet<char> usedParagraphNames;
2983 NodeMap::ConstIterator c = classMap.constBegin();
2984 while (c != classMap.constEnd()) {
2985 QStringList pieces = c.key().split("::");
2987 int idx = commonPrefixLen;
2988 if (!pieces.last().startsWith(commonPrefix))
2990 if (pieces.size() == 1)
2991 key = pieces.last().mid(idx).toLower();
2993 key = pieces.last().toLower();
2995 int paragraphNr = NumParagraphs - 1;
2997 if (key[0].digitValue() != -1) {
2998 paragraphNr = key[0].digitValue();
3000 else if (key[0] >= QLatin1Char('a') && key[0] <= QLatin1Char('z')) {
3001 paragraphNr = 10 + key[0].unicode() - 'a';
3004 paragraphName[paragraphNr] = key[0].toUpper();
3005 usedParagraphNames.insert(key[0].toLower().cell());
3006 paragraph[paragraphNr].insert(key, c.value());
3011 Each paragraph j has a size: paragraph[j].count(). In the
3012 discussion, we will assume paragraphs 0 to 5 will have sizes
3015 We now want to compute the paragraph offset. Paragraphs 0 to 6
3016 start at offsets 0, 3, 4, 8, 9, 14, 23.
3018 int paragraphOffset[NumParagraphs + 1]; // 37 + 1
3019 paragraphOffset[0] = 0;
3020 for (int i=0; i<NumParagraphs; i++) // i = 0..36
3021 paragraphOffset[i+1] = paragraphOffset[i] + paragraph[i].count();
3024 int curParOffset = 0;
3025 QMap<QChar,QString> cmap;
3028 Output the alphabet as a row of links.
3030 if (includeAlphabet) {
3031 writeStartTag(DT_p);
3032 xmlWriter().writeAttribute("outputclass","alphabet");
3033 for (int i = 0; i < 26; i++) {
3035 if (usedParagraphNames.contains(char('a' + i))) {
3036 writeStartTag(DT_xref);
3038 QString guid = lookupGuid(outFileName(),QString(ch));
3039 QString attr = outFileName() + QString("#%1").arg(guid);
3040 xmlWriter().writeAttribute("href", attr);
3041 xmlWriter().writeCharacters(QString(ch.toUpper()));
3042 writeEndTag(); // </xref>
3045 writeEndTag(); // </p>
3049 Output a <p> element to contain all the <dl> elements.
3051 writeStartTag(DT_p);
3052 xmlWriter().writeAttribute("outputclass","compactlist");
3054 for (int i=0; i<classMap.count()-1; i++) {
3055 while ((curParNr < NumParagraphs) &&
3056 (curParOffset == paragraph[curParNr].count())) {
3062 Starting a new paragraph means starting a new <dl>.
3064 if (curParOffset == 0) {
3066 writeEndTag(); // </dlentry>
3067 writeEndTag(); // </dl>
3069 writeStartTag(DT_dl);
3070 writeStartTag(DT_dlentry);
3071 writeStartTag(DT_dt);
3072 if (includeAlphabet) {
3073 QChar c = paragraphName[curParNr][0].toLower();
3074 writeGuidAttribute(QString(c));
3076 xmlWriter().writeAttribute("outputclass","sublist-header");
3077 xmlWriter().writeCharacters(paragraphName[curParNr]);
3078 writeEndTag(); // </dt>
3082 Output a <dd> for the current offset in the current paragraph.
3084 writeStartTag(DT_dd);
3085 if ((curParNr < NumParagraphs) &&
3086 !paragraphName[curParNr].isEmpty()) {
3087 NodeMap::Iterator it;
3088 it = paragraph[curParNr].begin();
3089 for (int i=0; i<curParOffset; i++)
3093 Previously, we used generateFullName() for this, but we
3094 require some special formatting.
3096 writeStartTag(DT_xref);
3098 writeHrefAttribute(linkForNode(it.value(), relative));
3101 if (it.value()->subType() == Node::QmlClass)
3102 pieces << it.value()->name();
3104 pieces = fullName(it.value(), relative, marker).split("::");
3105 xmlWriter().writeCharacters(protectEnc(pieces.last()));
3106 writeEndTag(); // </xref>
3107 if (pieces.size() > 1) {
3108 xmlWriter().writeCharacters(" (");
3109 generateFullName(it.value()->parent(),relative,marker);
3110 xmlWriter().writeCharacters(")");
3113 writeEndTag(); // </dd>
3116 writeEndTag(); // </dlentry>
3117 writeEndTag(); // </dl>
3118 writeEndTag(); // </p>
3122 Write XML for a function index to the current XML stream.
3124 void DitaXmlGenerator::generateFunctionIndex(const Node* relative,
3127 writeStartTag(DT_p);
3128 xmlWriter().writeAttribute("outputclass","alphabet");
3129 for (int i = 0; i < 26; i++) {
3131 writeStartTag(DT_xref);
3133 QString guid = lookupGuid(outFileName(),QString(ch));
3134 QString attr = outFileName() + QString("#%1").arg(guid);
3135 xmlWriter().writeAttribute("href", attr);
3136 xmlWriter().writeCharacters(QString(ch.toUpper()));
3137 writeEndTag(); // </xref>
3140 writeEndTag(); // </p>
3142 char nextLetter = 'a';
3145 writeStartTag(DT_ul);
3146 QMap<QString, NodeMap >::ConstIterator f = funcIndex.constBegin();
3147 while (f != funcIndex.constEnd()) {
3148 writeStartTag(DT_li);
3149 currentLetter = f.key()[0].unicode();
3150 while (islower(currentLetter) && currentLetter >= nextLetter) {
3151 writeStartTag(DT_p);
3152 writeGuidAttribute(QString(nextLetter));
3153 xmlWriter().writeAttribute("outputclass","target");
3154 xmlWriter().writeCharacters(QString(nextLetter));
3155 writeEndTag(); // </p>
3158 xmlWriter().writeCharacters(protectEnc(f.key()));
3159 xmlWriter().writeCharacters(":");
3161 NodeMap::ConstIterator s = (*f).constBegin();
3162 while (s != (*f).constEnd()) {
3163 generateFullName((*s)->parent(), relative, marker, *s);
3166 writeEndTag(); // </li>
3169 writeEndTag(); // </ul>
3173 Write the legalese texts as XML to the current XML stream.
3175 void DitaXmlGenerator::generateLegaleseList(const Node* relative,
3178 QMap<Text, const Node*>::ConstIterator it = legaleseTexts.constBegin();
3179 while (it != legaleseTexts.constEnd()) {
3180 Text text = it.key();
3181 generateText(text, relative, marker);
3182 writeStartTag(DT_ul);
3184 writeStartTag(DT_li);
3185 generateFullName(it.value(), relative, marker);
3186 writeEndTag(); // </li>
3188 } while (it != legaleseTexts.constEnd() && it.key() == text);
3189 writeEndTag(); //</ul>
3194 Generate the text for the QML item described by \a node
3195 and write it to the current XML stream.
3197 void DitaXmlGenerator::generateQmlItem(const Node* node,
3198 const Node* relative,
3202 QString marked = marker->markedUpQmlItem(node,summary);
3203 QRegExp tag("(<[^@>]*>)");
3204 if (marked.indexOf(tag) != -1) {
3205 QString tmp = protectEnc(marked.mid(tag.pos(1), tag.cap(1).length()));
3206 marked.replace(tag.pos(1), tag.cap(1).length(), tmp);
3208 marked.replace(QRegExp("<@param>([a-z]+)_([1-9n])</@param>"),
3209 "<i>\\1<sub>\\2</sub></i>");
3211 marked.remove("<@type>");
3212 marked.remove("</@type>");
3214 writeText(marked, marker, relative);
3218 Write the XML for the overview list to the current XML stream.
3220 void DitaXmlGenerator::generateOverviewList(const Node* relative, CodeMarker* /* marker */)
3222 QMap<const FakeNode*, QMap<QString, FakeNode*> > fakeNodeMap;
3223 QMap<QString, const FakeNode*> groupTitlesMap;
3224 QMap<QString, FakeNode*> uncategorizedNodeMap;
3225 QRegExp singleDigit("\\b([0-9])\\b");
3227 const NodeList children = tree_->root()->childNodes();
3228 foreach (Node* child, children) {
3229 if (child->type() == Node::Fake && child != relative) {
3230 FakeNode* fakeNode = static_cast<FakeNode*>(child);
3232 // Check whether the page is part of a group or is the group
3235 bool isGroupPage = false;
3236 if (fakeNode->doc().metaCommandsUsed().contains("group")) {
3237 group = fakeNode->doc().metaCommandArgs("group")[0].first;
3241 // there are too many examples; they would clutter the list
3242 if (fakeNode->subType() == Node::Example)
3245 // not interested either in individual (Qt Designer etc.) manual chapters
3246 if (fakeNode->links().contains(Node::ContentsLink))
3249 // Discard external nodes.
3250 if (fakeNode->subType() == Node::ExternalPage)
3253 QString sortKey = fakeNode->fullTitle().toLower();
3254 if (sortKey.startsWith("the "))
3255 sortKey.remove(0, 4);
3256 sortKey.replace(singleDigit, "0\\1");
3258 if (!group.isEmpty()) {
3260 // If we encounter a group definition page, we add all
3261 // the pages in that group to the list for that group.
3262 foreach (Node* member, fakeNode->groupMembers()) {
3263 if (member->type() != Node::Fake)
3265 FakeNode* page = static_cast<FakeNode*>(member);
3267 QString sortKey = page->fullTitle().toLower();
3268 if (sortKey.startsWith("the "))
3269 sortKey.remove(0, 4);
3270 sortKey.replace(singleDigit, "0\\1");
3271 fakeNodeMap[const_cast<const FakeNode*>(fakeNode)].insert(sortKey, page);
3272 groupTitlesMap[fakeNode->fullTitle()] = const_cast<const FakeNode*>(fakeNode);
3276 else if (!isGroupPage) {
3277 // If we encounter a page that belongs to a group then
3278 // we add that page to the list for that group.
3279 const FakeNode* gn = tree_->findGroupNode(QStringList(group));
3281 fakeNodeMap[gn].insert(sortKey, fakeNode);
3287 // We now list all the pages found that belong to groups.
3288 // If only certain pages were found for a group, but the definition page
3289 // for that group wasn't listed, the list of pages will be intentionally
3290 // incomplete. However, if the group definition page was listed, all the
3291 // pages in that group are listed for completeness.
3293 if (!fakeNodeMap.isEmpty()) {
3294 foreach (const QString& groupTitle, groupTitlesMap.keys()) {
3295 const FakeNode* groupNode = groupTitlesMap[groupTitle];
3296 writeStartTag(DT_p);
3297 xmlWriter().writeAttribute("outputclass","h3");
3298 writeStartTag(DT_xref);
3300 xmlWriter().writeAttribute("href",linkForNode(groupNode, relative));
3301 writeCharacters(protectEnc(groupNode->fullTitle()));
3302 writeEndTag(); // </xref>
3303 writeEndTag(); // </p>
3304 if (fakeNodeMap[groupNode].count() == 0)
3307 writeStartTag(DT_ul);
3308 foreach (const FakeNode* fakeNode, fakeNodeMap[groupNode]) {
3309 QString title = fakeNode->fullTitle();
3310 if (title.startsWith("The "))
3312 writeStartTag(DT_li);
3313 writeStartTag(DT_xref);
3315 xmlWriter().writeAttribute("href",linkForNode(fakeNode, relative));
3316 writeCharacters(protectEnc(title));
3317 writeEndTag(); // </xref>
3318 writeEndTag(); // </li>
3320 writeEndTag(); // </ul>
3324 if (!uncategorizedNodeMap.isEmpty()) {
3325 writeStartTag(DT_p);
3326 xmlWriter().writeAttribute("outputclass","h3");
3327 xmlWriter().writeCharacters("Miscellaneous");
3328 writeEndTag(); // </p>
3329 writeStartTag(DT_ul);
3330 foreach (const FakeNode *fakeNode, uncategorizedNodeMap) {
3331 QString title = fakeNode->fullTitle();
3332 if (title.startsWith("The "))
3334 writeStartTag(DT_li);
3335 writeStartTag(DT_xref);
3337 xmlWriter().writeAttribute("href",linkForNode(fakeNode, relative));
3338 writeCharacters(protectEnc(title));
3339 writeEndTag(); // </xref>
3340 writeEndTag(); // </li>
3342 writeEndTag(); // </ul>
3347 Write the XML for a standard section of a page, e.g.
3348 "Public Functions" or "Protected Slots." The section
3349 is written too the current XML stream as a table.
3351 void DitaXmlGenerator::generateSection(const NodeList& nl,
3352 const Node* relative,
3354 CodeMarker::SynopsisStyle style)
3356 if (!nl.isEmpty()) {
3357 writeStartTag(DT_ul);
3358 NodeList::ConstIterator m = nl.constBegin();
3359 while (m != nl.constEnd()) {
3360 if ((*m)->access() != Node::Private) {
3361 writeStartTag(DT_li);
3362 QString marked = getMarkedUpSynopsis(*m, relative, marker, style);
3363 writeText(marked, marker, relative);
3364 writeEndTag(); // </li>
3368 writeEndTag(); // </ul>
3373 Writes the "inherited from" list to the current XML stream.
3375 void DitaXmlGenerator::generateSectionInheritedList(const Section& section,
3376 const Node* relative,
3379 if (section.inherited.isEmpty())
3381 writeStartTag(DT_ul);
3382 QList<QPair<InnerNode*,int> >::ConstIterator p = section.inherited.constBegin();
3383 while (p != section.inherited.constEnd()) {
3384 writeStartTag(DT_li);
3386 text.setNum((*p).second);
3387 text += QLatin1Char(' ');
3388 if ((*p).second == 1)
3389 text += section.singularMember;
3391 text += section.pluralMember;
3392 text += " inherited from ";
3393 writeCharacters(text);
3394 writeStartTag(DT_xref);
3397 text = fileName((*p).first) + QLatin1Char('#');
3398 text += DitaXmlGenerator::cleanRef(section.name.toLower());
3399 xmlWriter().writeAttribute("href",text);
3400 text = protectEnc(marker->plainFullName((*p).first, relative));
3401 writeCharacters(text);
3402 writeEndTag(); // </xref>
3403 writeEndTag(); // </li>
3406 writeEndTag(); // </ul>
3410 Get the synopsis from the \a node using the \a relative
3411 node if needed, and mark up the synopsis using \a marker.
3412 Use the style to decide which kind of sysnopsis to build,
3413 normally \c Summary or \c Detailed. Return the marked up
3416 QString DitaXmlGenerator::getMarkedUpSynopsis(const Node* node,
3417 const Node* relative,
3419 CodeMarker::SynopsisStyle style)
3421 QString marked = marker->markedUpSynopsis(node, relative, style);
3422 QRegExp tag("(<[^@>]*>)");
3423 if (marked.indexOf(tag) != -1) {
3424 QString tmp = protectEnc(marked.mid(tag.pos(1), tag.cap(1).length()));
3425 marked.replace(tag.pos(1), tag.cap(1).length(), tmp);
3427 marked.replace(QRegExp("<@param>([a-z]+)_([1-9n])</@param>"),
3428 "<i> \\1<sub>\\2</sub></i>");
3429 if (style == CodeMarker::Summary) {
3430 marked.remove("<@name>"); // was "<b>"
3431 marked.remove("</@name>"); // was "</b>"
3434 if (style == CodeMarker::Subpage) {
3435 QRegExp extraRegExp("<@extra>.*</@extra>");
3436 extraRegExp.setMinimal(true);
3437 marked.remove(extraRegExp);
3440 if (style != CodeMarker::Detailed) {
3441 marked.remove("<@type>");
3442 marked.remove("</@type>");
3448 Renamed from highlightedCode() in the html generator. Gets the text
3449 from \a markedCode , and then the text is written to the current XML
3452 void DitaXmlGenerator::writeText(const QString& markedCode,
3454 const Node* relative)
3456 QString src = markedCode;
3461 const QChar charLangle = '<';
3462 const QChar charAt = '@';
3465 First strip out all the extraneous markup. The table
3466 below contains the markup we want to keep. Everything
3467 else that begins with "<@" or "</@" is stripped out.
3469 static const QString spanTags[] = {
3470 "<@link ", "<@link ",
3471 "<@type>", "<@type>",
3472 "<@headerfile>", "<@headerfile>",
3473 "<@func>", "<@func>",
3474 "<@func ", "<@func ",
3475 "<@param>", "<@param>",
3476 "<@extra>", "<@extra>",
3477 "</@link>", "</@link>",
3478 "</@type>", "</@type>",
3479 "</@headerfile>", "</@headerfile>",
3480 "</@func>", "</@func>",
3481 "</@param>", "</@param>",
3482 "</@extra>", "</@extra>"
3484 for (int i = 0, n = src.size(); i < n;) {
3485 if (src.at(i) == charLangle) {
3486 bool handled = false;
3487 for (int k = 0; k != 13; ++k) {
3488 const QString & tag = spanTags[2 * k];
3489 if (tag == QStringRef(&src, i, tag.length())) {
3490 text += spanTags[2 * k + 1];
3498 if (src.at(i) == charAt ||
3499 (src.at(i) == QLatin1Char('/') && src.at(i + 1) == charAt)) {
3500 // drop 'our' unknown tags (the ones still containing '@')
3501 while (i < n && src.at(i) != QLatin1Char('>'))
3506 // retain all others
3517 // replace all <@link> tags: "(<@link node=\"([^\"]+)\">).*(</@link>)"
3518 // replace all "(<@(type|headerfile|func)(?: +[^>]*)?>)(.*)(</@\\2>)" tags
3521 static const QString markTags[] = {
3523 "link", "type", "headerfile", "func", "param", "extra"
3526 for (int i = 0, n = src.size(); i < n;) {
3527 if (src.at(i) == charLangle && src.at(i + 1) == charAt) {
3529 for (int k = 0; k != 6; ++k) {
3530 if (parseArg(src, markTags[k], &i, n, &arg, &par1)) {
3532 if (k == 0) { // <@link>
3533 if (!text.isEmpty()) {
3534 writeCharacters(text);
3537 n = CodeMarker::nodeForString(par1.toString());
3538 QString link = linkForNode(n, relative);
3541 else if (k == 4) { // <@param>
3542 if (!text.isEmpty()) {
3543 writeCharacters(text);
3546 writeStartTag(DT_i);
3547 //writeCharacters(" " + arg.toString());
3548 writeCharacters(arg.toString());
3549 writeEndTag(); // </i>
3551 else if (k == 5) { // <@extra>
3552 if (!text.isEmpty()) {
3553 writeCharacters(text);
3556 writeStartTag(DT_tt);
3557 writeCharacters(arg.toString());
3558 writeEndTag(); // </tt>
3561 if (!text.isEmpty()) {
3562 writeCharacters(text);
3565 par1 = QStringRef();
3567 n = marker->resolveTarget(arg.toString(), tree_, relative);
3568 if (n && n->subType() == Node::QmlBasicType) {
3569 if (relative && relative->subType() == Node::QmlClass) {
3570 link = linkForNode(n,relative);
3574 writeCharacters(arg.toString());
3578 // (zzz) Is this correct for all cases?
3579 link = linkForNode(n,relative);
3588 text += src.at(i++);
3591 if (!text.isEmpty()) {
3592 writeCharacters(text);
3596 void DitaXmlGenerator::generateLink(const Atom* atom,
3597 const Node* /* relative */,
3600 static QRegExp camelCase("[A-Z][A-Z][a-z]|[a-z][A-Z0-9]|_");
3602 if (funcLeftParen.indexIn(atom->string()) != -1 && marker->recognizeLanguage("Cpp")) {
3603 // hack for C++: move () outside of link
3604 int k = funcLeftParen.pos(1);
3605 writeCharacters(protectEnc(atom->string().left(k)));
3606 if (link.isEmpty()) {
3607 if (showBrokenLinks)
3608 writeEndTag(); // </i>
3611 writeEndTag(); // </xref>
3613 writeCharacters(protectEnc(atom->string().mid(k)));
3615 else if (marker->recognizeLanguage("Java")) {
3616 // hack for Java: remove () and use <tt> when appropriate
3617 bool func = atom->string().endsWith("()");
3618 bool tt = (func || atom->string().contains(camelCase));
3620 writeStartTag(DT_tt);
3622 writeCharacters(protectEnc(atom->string().left(atom->string().length() - 2)));
3624 writeCharacters(protectEnc(atom->string()));
3625 writeEndTag(); // </tt>
3628 writeCharacters(protectEnc(atom->string()));
3631 QString DitaXmlGenerator::cleanRef(const QString& ref)
3638 clean.reserve(ref.size() + 20);
3639 const QChar c = ref[0];
3640 const uint u = c.unicode();
3642 if ((u >= 'a' && u <= 'z') ||
3643 (u >= 'A' && u <= 'Z') ||
3644 (u >= '0' && u <= '9')) {
3647 else if (u == '~') {
3650 else if (u == '_') {
3651 clean += "underscore.";
3654 clean += QLatin1Char('A');
3657 for (int i = 1; i < (int) ref.length(); i++) {
3658 const QChar c = ref[i];
3659 const uint u = c.unicode();
3660 if ((u >= 'a' && u <= 'z') ||
3661 (u >= 'A' && u <= 'Z') ||
3662 (u >= '0' && u <= '9') || u == '-' ||
3663 u == '_' || u == ':' || u == '.') {
3666 else if (c.isSpace()) {
3667 clean += QLatin1Char('-');
3669 else if (u == '!') {
3672 else if (u == '&') {
3675 else if (u == '<') {
3678 else if (u == '=') {
3681 else if (u == '>') {
3684 else if (u == '#') {
3685 clean += QLatin1Char('#');
3688 clean += QLatin1Char('-');
3689 clean += QString::number((int)u, 16);
3695 QString DitaXmlGenerator::registerRef(const QString& ref)
3697 QString clean = DitaXmlGenerator::cleanRef(ref);
3700 QString& prevRef = refMap[clean.toLower()];
3701 if (prevRef.isEmpty()) {
3705 else if (prevRef == ref)
3707 clean += QLatin1Char('x');
3713 Calls protect() with the \a string. Returns the result.
3715 QString DitaXmlGenerator::protectEnc(const QString& string)
3717 return protect(string, outputEncoding);
3720 QString DitaXmlGenerator::protect(const QString& string, const QString& ) //outputEncoding)
3723 if (xml.isEmpty()) { \
3730 int n = string.length();
3732 for (int i = 0; i < n; ++i) {
3733 QChar ch = string.at(i);
3735 if (ch == QLatin1Char('&')) {
3738 else if (ch == QLatin1Char('<')) {
3741 else if (ch == QLatin1Char('>')) {
3744 else if (ch == QLatin1Char('"')) {
3761 Constructs a file name appropriate for the \a node
3762 and returns the file name.
3764 QString DitaXmlGenerator::fileBase(const Node* node) const
3767 result = Generator::fileBase(node);
3771 QString DitaXmlGenerator::guidForNode(const Node* node)
3773 switch (node->type()) {
3774 case Node::Namespace:
3779 return node->guid();
3782 const TypedefNode* tdn = static_cast<const TypedefNode*>(node);
3783 if (tdn->associatedEnum())
3784 return guidForNode(tdn->associatedEnum());
3786 return node->guid();
3787 case Node::Function:
3789 const FunctionNode* fn = static_cast<const FunctionNode*>(node);
3790 if (fn->associatedProperty()) {
3791 return guidForNode(fn->associatedProperty());
3794 QString ref = fn->name();
3795 if (fn->overloadNumber() != 1) {
3796 ref += QLatin1Char('-') + QString::number(fn->overloadNumber());
3802 if (node->subType() != Node::QmlPropertyGroup)
3804 case Node::QmlProperty:
3805 case Node::Property:
3806 return node->guid();
3807 case Node::QmlSignal:
3808 return node->guid();
3809 case Node::QmlSignalHandler:
3810 return node->guid();
3811 case Node::QmlMethod:
3812 return node->guid();
3813 case Node::Variable:
3814 return node->guid();
3820 Constructs a file name appropriate for the \a node and returns
3821 it. If the \a node is not a fake node, or if it is a fake node but
3822 it is neither an external page node nor an image node or a ditamap,
3823 call the PageGenerator::fileName() function.
3825 QString DitaXmlGenerator::fileName(const Node* node)
3827 if (node->type() == Node::Fake) {
3828 if (static_cast<const FakeNode*>(node)->pageType() == Node::DitaMapPage)
3829 return node->name();
3830 if (static_cast<const FakeNode*>(node)->subType() == Node::ExternalPage)
3831 return node->name();
3832 if (static_cast<const FakeNode*>(node)->subType() == Node::Image)
3833 return node->name();
3835 return Generator::fileName(node);
3838 QString DitaXmlGenerator::linkForNode(const Node* node, const Node* relative)
3840 if (node == 0 || node == relative)
3842 if (!node->url().isEmpty())
3844 if (fileBase(node).isEmpty())
3846 if (node->access() == Node::Private)
3849 QString fn = fileName(node);
3850 if (node && relative && node->parent() != relative) {
3851 if (node->parent()->subType() == Node::QmlClass && relative->subType() == Node::QmlClass) {
3852 if (node->parent()->isAbstract()) {
3854 This is a bit of a hack. What we discover with
3855 the three 'if' statements immediately above,
3856 is that node's parent is marked \qmlabstract
3857 but the link appears in a qdoc comment for a
3858 subclass of the node's parent. This means the
3859 link should refer to the file for the relative
3860 node, not the file for node.
3862 fn = fileName(relative);
3864 qDebug() << "ABSTRACT:" << node->parent()->name()
3865 << node->name() << relative->name()
3866 << node->parent()->type() << node->parent()->subType()
3867 << relative->type() << relative->subType() << outFileName();
3874 if (!node->isInnerNode() || node->subType() == Node::QmlPropertyGroup) {
3875 QString guid = guidForNode(node);
3876 if (relative && fn == fileName(relative) && guid == guidForNode(relative)) {
3879 link += QLatin1Char('#');
3883 If the output is going to subdirectories, then if the
3884 two nodes will be output to different directories, then
3885 the link must go up to the parent directory and then
3886 back down into the other subdirectory.
3888 if (node && relative && (node != relative)) {
3889 if (node->outputSubdirectory() != relative->outputSubdirectory())
3890 link.prepend(QString("../" + node->outputSubdirectory() + QLatin1Char('/')));
3895 QString DitaXmlGenerator::refForAtom(Atom* atom, const Node* /* node */)
3897 if (atom->type() == Atom::SectionLeft)
3898 return Doc::canonicalTitle(Text::sectionHeading(atom).toString());
3899 if (atom->type() == Atom::Target)
3900 return Doc::canonicalTitle(atom->string());
3904 void DitaXmlGenerator::generateFullName(const Node* apparentNode,
3905 const Node* relative,
3907 const Node* actualNode)
3909 if (actualNode == 0)
3910 actualNode = apparentNode;
3911 writeStartTag(DT_xref);
3913 QString href = linkForNode(actualNode, relative);
3914 writeHrefAttribute(href);
3915 writeCharacters(protectEnc(fullName(apparentNode, relative, marker)));
3916 writeEndTag(); // </xref>
3919 void DitaXmlGenerator::findAllClasses(const InnerNode* node)
3921 NodeList::const_iterator c = node->childNodes().constBegin();
3922 while (c != node->childNodes().constEnd()) {
3923 if ((*c)->access() != Node::Private && (*c)->url().isEmpty()) {
3924 if ((*c)->type() == Node::Class && !(*c)->doc().isEmpty()) {
3925 QString className = (*c)->name();
3926 if ((*c)->parent() &&
3927 (*c)->parent()->type() == Node::Namespace &&
3928 !(*c)->parent()->name().isEmpty())
3929 className = (*c)->parent()->name()+"::"+className;
3931 if (!(static_cast<const ClassNode *>(*c))->hideFromMainList()) {
3932 if ((*c)->status() == Node::Compat) {
3933 compatClasses.insert(className, *c);
3935 else if ((*c)->status() == Node::Obsolete) {
3936 obsoleteClasses.insert(className, *c);
3939 nonCompatClasses.insert(className, *c);
3940 if ((*c)->status() == Node::Main)
3941 mainClasses.insert(className, *c);
3945 QString moduleName = (*c)->moduleName();
3946 if (moduleName == "Qt3SupportLight") {
3947 moduleClassMap[moduleName].insert((*c)->name(), *c);
3948 moduleName = "Qt3Support";
3950 if (!moduleName.isEmpty())
3951 moduleClassMap[moduleName].insert((*c)->name(), *c);
3953 QString serviceName =
3954 (static_cast<const ClassNode *>(*c))->serviceName();
3955 if (!serviceName.isEmpty())
3956 serviceClasses.insert(serviceName, *c);
3958 else if ((*c)->type() == Node::Fake &&
3959 (*c)->subType() == Node::QmlClass &&
3960 !(*c)->doc().isEmpty()) {
3961 QString qmlClassName = (*c)->name();
3962 qmlClasses.insert(qmlClassName,*c);
3964 else if ((*c)->isInnerNode()) {
3965 findAllClasses(static_cast<InnerNode *>(*c));
3972 void DitaXmlGenerator::findAllFunctions(const InnerNode* node)
3974 NodeList::ConstIterator c = node->childNodes().constBegin();
3975 while (c != node->childNodes().constEnd()) {
3976 if ((*c)->access() != Node::Private) {
3977 if ((*c)->isInnerNode() && (*c)->url().isEmpty()) {
3978 findAllFunctions(static_cast<const InnerNode*>(*c));
3980 else if ((*c)->type() == Node::Function) {
3981 const FunctionNode* func = static_cast<const FunctionNode*>(*c);
3982 if ((func->status() > Node::Obsolete) &&
3983 !func->isInternal() &&
3984 (func->metaness() != FunctionNode::Ctor) &&
3985 (func->metaness() != FunctionNode::Dtor)) {
3986 funcIndex[(*c)->name()].insert((*c)->parent()->fullDocumentName(), *c);
3994 void DitaXmlGenerator::findAllLegaleseTexts(const InnerNode* node)
3996 NodeList::ConstIterator c = node->childNodes().constBegin();
3997 while (c != node->childNodes().constEnd()) {
3998 if ((*c)->access() != Node::Private) {
3999 if (!(*c)->doc().legaleseText().isEmpty())
4000 legaleseTexts.insertMulti((*c)->doc().legaleseText(), *c);
4001 if ((*c)->isInnerNode())
4002 findAllLegaleseTexts(static_cast<const InnerNode *>(*c));
4008 void DitaXmlGenerator::findAllNamespaces(const InnerNode* node)
4010 NodeList::ConstIterator c = node->childNodes().constBegin();
4011 while (c != node->childNodes().constEnd()) {
4012 if ((*c)->access() != Node::Private) {
4013 if ((*c)->isInnerNode() && (*c)->url().isEmpty()) {
4014 findAllNamespaces(static_cast<const InnerNode *>(*c));
4015 if ((*c)->type() == Node::Namespace) {
4016 const NamespaceNode *nspace = static_cast<const NamespaceNode *>(*c);
4017 // Ensure that the namespace's name is not empty (the root
4018 // namespace has no name).
4019 if (!nspace->name().isEmpty()) {
4020 namespaceIndex.insert(nspace->name(), *c);
4021 QString moduleName = (*c)->moduleName();
4022 if (moduleName == "Qt3SupportLight") {
4023 moduleNamespaceMap[moduleName].insert((*c)->name(), *c);
4024 moduleName = "Qt3Support";
4026 if (!moduleName.isEmpty())
4027 moduleNamespaceMap[moduleName].insert((*c)->name(), *c);
4037 We're writing an attribute that indicates that the text
4038 data is a heading, hence, h1, h2, h3... etc, and we must
4039 decide which number to use.
4041 int DitaXmlGenerator::hOffset(const Node* node)
4043 switch (node->type()) {
4044 case Node::Namespace:
4051 case Node::Function:
4052 case Node::Property:
4058 bool DitaXmlGenerator::isThreeColumnEnumValueTable(const Atom* atom)
4060 while (atom != 0 && !(atom->type() == Atom::ListRight && atom->string() == ATOM_LIST_VALUE)) {
4061 if (atom->type() == Atom::ListItemLeft && !matchAhead(atom, Atom::ListItemRight))
4063 atom = atom->next();
4068 const Node* DitaXmlGenerator::findNodeForTarget(const QString& target,
4069 const Node* relative,
4073 const Node* node = 0;
4075 if (target.isEmpty()) {
4078 else if (target.endsWith(".html")) {
4079 node = tree_->root()->findChildNodeByNameAndType(target, Node::Fake);
4082 node = marker->resolveTarget(target, tree_, relative);
4084 node = tree_->findFakeNodeByTitle(target, relative);
4085 if (!node && atom) {
4086 node = tree_->findUnambiguousTarget(target, *const_cast<Atom**>(&atom), relative);
4091 relative->doc().location().warning(tr("Cannot link to '%1'").arg(target));
4096 const QPair<QString,QString> DitaXmlGenerator::anchorForNode(const Node* node)
4098 QPair<QString,QString> anchorPair;
4099 anchorPair.first = Generator::fileName(node);
4100 if (node->type() == Node::Fake) {
4101 const FakeNode *fakeNode = static_cast<const FakeNode*>(node);
4102 anchorPair.second = fakeNode->title();
4108 QString DitaXmlGenerator::getLink(const Atom* atom,
4109 const Node* relative,
4115 inObsoleteLink = false;
4117 if (atom->string().contains(QLatin1Char(':')) &&
4118 (atom->string().startsWith("file:")
4119 || atom->string().startsWith("http:")
4120 || atom->string().startsWith("https:")
4121 || atom->string().startsWith("ftp:")
4122 || atom->string().startsWith("mailto:"))) {
4124 link = atom->string();
4128 if (atom->string().contains('#'))
4129 path = atom->string().split('#');
4131 path.append(atom->string());
4133 Atom* targetAtom = 0;
4134 QString first = path.first().trimmed();
4136 if (first.isEmpty()) {
4139 else if (first.endsWith(".html")) {
4140 *node = tree_->root()->findChildNodeByNameAndType(first, Node::Fake);
4143 *node = marker->resolveTarget(first, tree_, relative);
4145 *node = tree_->findFakeNodeByTitle(first, relative);
4147 *node = tree_->findUnambiguousTarget(first, targetAtom, relative);
4151 if (!(*node)->url().isEmpty())
4152 return (*node)->url();
4159 if (*node && (*node)->status() == Node::Obsolete) {
4160 if (relative && (relative->parent() != *node) &&
4161 (relative->status() != Node::Obsolete)) {
4162 bool porting = false;
4163 if (relative->type() == Node::Fake) {
4164 const FakeNode* fake = static_cast<const FakeNode*>(relative);
4165 if (fake->title().startsWith("Porting"))
4168 QString name = marker->plainFullName(relative);
4169 if (!porting && !name.startsWith("Q3")) {
4170 if (obsoleteLinks) {
4171 relative->doc().location().warning(tr("Link to obsolete item '%1' in %2")
4172 .arg(atom->string())
4175 inObsoleteLink = true;
4180 while (!path.isEmpty()) {
4181 targetAtom = tree_->findTarget(path.first(), *node);
4182 if (targetAtom == 0)
4187 if (path.isEmpty()) {
4188 link = linkForNode(*node, relative);
4189 if (*node && (*node)->subType() == Node::Image)
4190 link = "images/used-in-examples/" + link;
4193 link = outFileName();
4194 QString guid = lookupGuid(link,refForAtom(targetAtom,*node));
4195 link += QLatin1Char('#') + guid;
4197 else if (!link.isEmpty() && *node &&
4198 (link.endsWith(".xml") || link.endsWith(".dita"))) {
4199 link += QLatin1Char('#') + (*node)->guid();
4203 If the output is going to subdirectories, then if the
4204 two nodes will be output to different directories, then
4205 the link must go up to the parent directory and then
4206 back down into the other subdirectory.
4208 if (link.startsWith("images/")) {
4209 link.prepend(QString("../"));
4211 else if (*node && relative && (*node != relative)) {
4212 if ((*node)->outputSubdirectory() != relative->outputSubdirectory()) {
4213 link.prepend(QString("../" + (*node)->outputSubdirectory() + QLatin1Char('/')));
4217 if (!link.isEmpty() && link[0] == '#') {
4218 link.prepend(outFileName());
4223 void DitaXmlGenerator::generateIndex(const QString& fileBase,
4225 const QString& title)
4227 tree_->generateIndex(outputDir() + QLatin1Char('/') + fileBase + ".index", url, title);
4230 void DitaXmlGenerator::generateStatus(const Node* node, CodeMarker* marker)
4234 switch (node->status()) {
4235 case Node::Obsolete:
4236 if (node->isInnerNode())
4237 Generator::generateStatus(node, marker);
4240 if (node->isInnerNode()) {
4241 text << Atom::ParaLeft
4242 << Atom(Atom::FormattingLeft,ATOM_FORMATTING_BOLD)
4245 << " is part of the Qt 3 support library."
4246 << Atom(Atom::FormattingRight, ATOM_FORMATTING_BOLD)
4247 << " It is provided to keep old source code working. "
4248 << "We strongly advise against "
4249 << "using it in new code. See ";
4251 const FakeNode *fakeNode = tree_->findFakeNodeByTitle("Porting To Qt 4");
4252 Atom *targetAtom = 0;
4253 if (fakeNode && node->type() == Node::Class) {
4254 QString oldName(node->name());
4255 oldName.remove(QLatin1Char('3'));
4256 targetAtom = tree_->findTarget(oldName,fakeNode);
4260 QString fn = fileName(fakeNode);
4261 QString guid = lookupGuid(fn,refForAtom(targetAtom,fakeNode));
4262 text << Atom(Atom::GuidLink, fn + QLatin1Char('#') + guid);
4265 text << Atom(Atom::Link, "Porting to Qt 4");
4267 text << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK)
4268 << Atom(Atom::String, "Porting to Qt 4")
4269 << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK)
4270 << " for more information."
4273 generateText(text, node, marker);
4276 Generator::generateStatus(node, marker);
4280 void DitaXmlGenerator::beginLink(const QString& link)
4285 writeStartTag(DT_xref);
4287 writeHrefAttribute(link);
4291 void DitaXmlGenerator::endLink()
4294 if (link.isEmpty()) {
4295 if (showBrokenLinks)
4296 writeEndTag(); // </i>
4299 if (inObsoleteLink) {
4300 writeStartTag(DT_sup);
4301 xmlWriter().writeCharacters("(obsolete)");
4302 writeEndTag(); // </sup>
4304 writeEndTag(); // </xref>
4308 inObsoleteLink = false;
4312 Generates the summary for the \a section. Only used for
4313 sections of QML element documentation.
4315 Currently handles only the QML property group.
4317 void DitaXmlGenerator::generateQmlSummary(const Section& section,
4318 const Node* relative,
4321 if (!section.members.isEmpty()) {
4322 writeStartTag(DT_ul);
4323 NodeList::ConstIterator m;
4324 m = section.members.constBegin();
4325 while (m != section.members.constEnd()) {
4326 writeStartTag(DT_li);
4327 generateQmlItem(*m,relative,marker,true);
4328 writeEndTag(); // </li>
4331 writeEndTag(); // </ul>
4336 Writes the QML property \a qpn to the current DITA XML file.
4337 Assumes that the correct start tag has already been written,
4338 but nothing has been written inside that tag. This function
4339 begins by writing the GUID id attribute for the property.
4341 void DitaXmlGenerator::startQmlProperty(QmlPropertyNode* qpn,
4342 const InnerNode* relative,
4345 writeStartTag(DT_qmlProperty);
4346 writeGuidAttribute((Node*)qpn);
4347 writeStartTag(DT_apiName);
4348 writeCharacters(qpn->name());
4349 writeEndTag(); // </apiName>
4350 generateBrief(qpn, marker); // <shortdesc>
4351 writeStartTag(DT_qmlPropertyDetail);
4352 writeStartTag(DT_qmlPropertyDef);
4353 if (!qpn->isReadOnlySet())
4354 qpn->setReadOnly(!qpn->isWritable(tree_));
4355 if (qpn->isReadOnly()) {
4356 writeStartTag(DT_qmlQualifier);
4357 xmlWriter().writeAttribute("name","read-only");
4358 xmlWriter().writeAttribute("value","read-only");
4359 writeEndTag(); // </qmlQualifier>
4361 if (qpn->isDefault()) {
4362 writeStartTag(DT_qmlQualifier);
4363 xmlWriter().writeAttribute("name","default");
4364 xmlWriter().writeAttribute("value","default");
4365 writeEndTag(); // </qmlQualifier>
4367 if (qpn->isAttached()) {
4368 writeStartTag(DT_qmlAttached);
4369 xmlWriter().writeAttribute("name","attached");
4370 xmlWriter().writeAttribute("value","yes");
4371 writeEndTag(); // </qmlAttached>
4373 writeStartTag(DT_apiData);
4374 generateQmlItem(qpn, relative, marker, false);
4375 writeEndTag(); // </apiData>
4376 writeEndTag(); // </qmlPropertyDef>
4380 Outputs the DITA detailed documentation for a section
4381 on a QML element reference page.
4383 void DitaXmlGenerator::generateDetailedQmlMember(Node* node,
4384 const InnerNode* relative,
4388 QmlPropertyNode* qpn = 0;
4390 if (node->subType() == Node::QmlPropertyGroup) {
4391 const QmlPropGroupNode* qpgn = static_cast<const QmlPropGroupNode*>(node);
4392 NodeList::ConstIterator p = qpgn->childNodes().constBegin();
4393 if (qpgn->childNodes().size() == 1) {
4394 qpn = static_cast<QmlPropertyNode*>(*p);
4395 startQmlProperty(qpn,relative,marker);
4396 writeApiDesc(node, marker, node->title());
4397 writeEndTag(); // </qmlPropertyDetail>
4398 writeEndTag(); // </qmlProperty>
4401 writeStartTag(DT_qmlPropertyGroup);
4402 QString id = "id-qml-propertygroup-" + node->name();
4403 id.replace('.','-');
4404 xmlWriter().writeAttribute("id",id);
4405 writeStartTag(DT_apiName);
4406 //writeCharacters("...");
4407 writeEndTag(); // </apiName>
4408 writeStartTag(DT_qmlPropertyGroupDetail);
4409 writeApiDesc(node, marker, node->title());
4410 writeEndTag(); // </qmlPropertyGroupDetail>
4411 while (p != qpgn->childNodes().constEnd()) {
4412 if ((*p)->type() == Node::QmlProperty) {
4413 qpn = static_cast<QmlPropertyNode*>(*p);
4414 startQmlProperty(qpn,relative,marker);
4415 writeEndTag(); // </qmlPropertyDetail>
4416 writeEndTag(); // </qmlProperty>
4420 writeEndTag(); // </qmlPropertyGroup
4423 else if (node->type() == Node::QmlProperty) {
4424 qpn = static_cast<QmlPropertyNode*>(node);
4425 if (qpn->qmlPropNodes().isEmpty()) {
4426 startQmlProperty(qpn,relative,marker);
4427 writeApiDesc(node, marker, node->title());
4428 writeEndTag(); // </qmlPropertyDetail>
4429 writeEndTag(); // </qmlProperty>
4431 else if (qpn->qmlPropNodes().size() == 1) {
4432 Node* n = qpn->qmlPropNodes().at(0);
4433 if (n->type() == Node::QmlProperty) {
4434 qpn = static_cast<QmlPropertyNode*>(n);
4435 startQmlProperty(qpn,relative,marker);
4436 writeApiDesc(node, marker, node->title());
4437 writeEndTag(); // </qmlPropertyDetail>
4438 writeEndTag(); // </qmlProperty>
4443 The QML property node has multiple override nodes.
4444 Process the whole list as we would for a QML property
4447 writeStartTag(DT_qmlPropertyGroup);
4448 QString id = "id-qml-propertygroup-" + node->name();
4449 id.replace('.','-');
4450 xmlWriter().writeAttribute("id",id);
4451 writeStartTag(DT_apiName);
4452 //writeCharacters("...");
4453 writeEndTag(); // </apiName>
4454 writeStartTag(DT_qmlPropertyGroupDetail);
4455 writeApiDesc(node, marker, node->title());
4456 writeEndTag(); // </qmlPropertyGroupDetail>
4457 NodeList::ConstIterator p = qpn->qmlPropNodes().constBegin();
4458 while (p != qpn->qmlPropNodes().constEnd()) {
4459 if ((*p)->type() == Node::QmlProperty) {
4460 QmlPropertyNode* q = static_cast<QmlPropertyNode*>(*p);
4461 startQmlProperty(q,relative,marker);
4462 writeEndTag(); // </qmlPropertyDetail>
4463 writeEndTag(); // </qmlProperty>
4467 writeEndTag(); // </qmlPropertyGroup
4470 else if (node->type() == Node::QmlSignal)
4471 writeQmlRef(DT_qmlSignal,node,relative,marker);
4472 else if (node->type() == Node::QmlSignalHandler)
4473 writeQmlRef(DT_qmlSignalHandler,node,relative,marker);
4474 else if (node->type() == Node::QmlMethod)
4475 writeQmlRef(DT_qmlMethod,node,relative,marker);
4479 Outputs the DITA detailed documentation for a section
4480 on a QML element reference page.
4482 void DitaXmlGenerator::writeQmlRef(DitaTag tag,
4484 const InnerNode* relative,
4488 Node* n = const_cast<Node*>(node);
4489 writeGuidAttribute(n);
4490 writeStartTag(DT_apiName);
4491 writeCharacters(n->name());
4492 writeEndTag(); // </apiName>
4493 writeStartTag((DitaTag)((int)tag+2));
4494 writeStartTag((DitaTag)((int)tag+1));
4495 writeStartTag(DT_apiData);
4496 QString marked = getMarkedUpSynopsis(n, relative, marker, CodeMarker::Detailed);
4497 writeText(marked, marker, relative);
4498 writeEndTag(); // </apiData>
4499 if (node->isAttached()) {
4500 writeStartTag(DT_qmlAttached);
4501 xmlWriter().writeAttribute("name","attached");
4502 xmlWriter().writeAttribute("value","yes");
4503 writeEndTag(); // </qmlAttached>
4505 writeEndTag(); // </qmlXxxDef>
4506 writeApiDesc(node, marker, node->title());
4507 writeEndTag(); // </qmlXxxDetail>
4508 writeEndTag(); // tag
4512 This generates a <qmlTypeDef> in which the
4513 QML module name and version number are specified.
4515 void DitaXmlGenerator::generateQmlModuleDef(QmlClassNode* qcn)
4517 writeStartTag(DT_qmlImportModule);
4518 writeStartTag(DT_apiItemName);
4519 writeCharacters(qcn->qmlModuleName());
4520 writeEndTag(); // </apiItemName>
4521 writeStartTag(DT_apiData);
4522 writeCharacters(qcn->qmlModuleVersion());
4523 writeEndTag(); // </apiData>
4524 writeEndTag(); // </qmlImportModule>
4528 Output the "Inherits" line for the QML element,
4529 if there should be one.
4531 void DitaXmlGenerator::generateQmlInherits(const QmlClassNode* qcn, CodeMarker* marker)
4535 const FakeNode* base = qcn->qmlBase();
4537 writeStartTag(DT_qmlInherits);
4538 //writeStartTag(DT_qmlTypeDef);
4539 //xmlWriter().writeAttribute("outputclass","inherits");
4540 writeStartTag(DT_apiData);
4542 text << Atom(Atom::LinkNode,CodeMarker::stringForNode(base));
4543 text << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK);
4544 text << Atom(Atom::String, base->name());
4545 text << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK);
4546 generateText(text, qcn, marker);
4547 writeEndTag(); // </apiData>
4548 writeEndTag(); // </qmlInherits>
4553 Output the "Inherit by" list for the QML element,
4554 if it is inherited by any other elements.
4556 void DitaXmlGenerator::generateQmlInheritedBy(const QmlClassNode* qcn, CodeMarker* marker)
4560 QmlClassNode::subclasses(qcn->name(),subs);
4561 if (!subs.isEmpty()) {
4562 writeStartTag(DT_qmlInheritedBy);
4563 //writeStartTag(DT_qmlTypeDef);
4564 //xmlWriter().writeAttribute("outputclass","inherited-by");
4565 writeStartTag(DT_apiData);
4567 appendSortedQmlNames(text,qcn,subs,marker);
4568 text << Atom::ParaRight;
4569 generateText(text, qcn, marker);
4570 writeEndTag(); // </apiData>
4571 writeEndTag(); // </qmlIneritedBy>
4577 Output the "[Xxx instantiates the C++ class QmlGraphicsXxx]"
4578 line for the QML element, if there should be one.
4580 If there is no class node, or if the class node status
4581 is set to Node::Internal, do nothing.
4583 void DitaXmlGenerator::generateQmlInstantiates(QmlClassNode* qcn, CodeMarker* marker)
4585 ClassNode* cn = qcn->classNode();
4586 if (cn && (cn->status() != Node::Internal)) {
4587 writeStartTag(DT_qmlInstantiates);
4588 //writeStartTag(DT_qmlTypeDef);
4589 //xmlWriter().writeAttribute("outputclass","instantiates");
4590 writeStartTag(DT_apiData);
4592 text << Atom(Atom::LinkNode,CodeMarker::stringForNode(cn));
4593 text << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK);
4594 text << Atom(Atom::String, cn->name());
4595 text << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK);
4596 generateText(text, qcn, marker);
4597 writeEndTag(); // </apiData>
4598 writeEndTag(); // </qmlInstantiates>
4603 Generate a <qmlXxxDef> for the "since" version string, if there is one.
4605 void DitaXmlGenerator::generateQmlSince(const Node* node)
4607 if (!node->since().isEmpty()) {
4608 writeStartTag(DT_qmlSince);
4609 //writeStartTag(DT_qmlTypeDef);
4610 //xmlWriter().writeAttribute("outputclass","since");
4611 writeStartTag(DT_apiItemName);
4612 QStringList pieces = node->since().split(QLatin1Char(' '));
4613 writeCharacters(pieces[0]);
4614 writeEndTag(); // </apiItemName>
4615 writeStartTag(DT_apiData);
4616 if (pieces.size() > 1)
4617 writeCharacters(pieces[1]);
4618 writeEndTag(); // </apiData>
4619 writeEndTag(); // </qmlSince>
4624 Output the "[QmlGraphicsXxx is instantiated by QML Type Xxx]"
4625 line for the class, if there should be one.
4627 If there is no QML element, or if the class node status
4628 is set to Node::Internal, do nothing.
4630 void DitaXmlGenerator::generateInstantiatedBy(ClassNode* cn, CodeMarker* marker)
4632 if (cn && cn->status() != Node::Internal && cn->qmlElement() != 0) {
4633 const QmlClassNode* qcn = cn->qmlElement();
4634 writeStartTag(DT_p);
4635 xmlWriter().writeAttribute("outputclass","instantiated-by");
4638 text << Atom(Atom::LinkNode,CodeMarker::stringForNode(cn));
4639 text << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK);
4640 text << Atom(Atom::String, cn->name());
4641 text << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK);
4642 text << " is instantiated by QML Type ";
4643 text << Atom(Atom::LinkNode,CodeMarker::stringForNode(qcn));
4644 text << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK);
4645 text << Atom(Atom::String, qcn->name());
4646 text << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK);
4648 generateText(text, cn, marker);
4649 writeEndTag(); // </p>
4654 Return the full qualification of the node \a n, but without
4655 the name of \a n itself. e.g. A::B::C
4657 QString DitaXmlGenerator::fullQualification(const Node* n)
4660 InnerNode* in = n->parent();
4662 if ((in->type() == Node::Class) ||
4663 (in->type() == Node::Namespace)) {
4664 if (in->name().isEmpty())
4669 fq = in->name() + "::" + fq;
4679 Outputs the <cxxClassDerivations> element.
4681 <cxxClassDerivations>
4682 <cxxClassDerivation>
4684 </cxxClassDerivation>
4686 </cxxClassDerivations>
4689 The <cxxClassDerivation> element is:
4692 <cxxClassDerivation>
4693 <cxxClassDerivationAccessSpecifier value="public"/>
4694 <cxxClassBaseClass href="class_base">Base</cxxClassBaseClass>
4695 </cxxClassDerivation>
4698 void DitaXmlGenerator::writeDerivations(const ClassNode* cn, CodeMarker* marker)
4700 QList<RelatedClass>::ConstIterator r;
4702 if (!cn->baseClasses().isEmpty()) {
4703 writeStartTag(DT_cxxClassDerivations);
4704 r = cn->baseClasses().constBegin();
4705 while (r != cn->baseClasses().constEnd()) {
4706 writeStartTag(DT_cxxClassDerivation);
4707 writeStartTag(DT_cxxClassDerivationAccessSpecifier);
4708 xmlWriter().writeAttribute("value",(*r).accessString());
4709 writeEndTag(); // </cxxClassDerivationAccessSpecifier>
4711 // not included: <cxxClassDerivationVirtual>
4713 writeStartTag(DT_cxxClassBaseClass);
4714 QString attr = fileName((*r).node) + QLatin1Char('#') + (*r).node->guid();
4715 xmlWriter().writeAttribute("href",attr);
4716 writeCharacters(marker->plainFullName((*r).node));
4717 writeEndTag(); // </cxxClassBaseClass>
4719 // not included: <ClassBaseStruct> or <cxxClassBaseUnion>
4721 writeEndTag(); // </cxxClassDerivation>
4723 // not included: <cxxStructDerivation>
4727 writeEndTag(); // </cxxClassDerivations>
4732 Writes a <cxxXXXAPIItemLocation> element, depending on the
4733 type of the node \a n, which can be a class, function, enum,
4734 typedef, or property.
4736 void DitaXmlGenerator::writeLocation(const Node* n)
4738 DitaTag s1, s2, s3a, s3b;
4739 s1 = DT_cxxClassAPIItemLocation;
4740 s2 = DT_cxxClassDeclarationFile;
4741 s3a = DT_cxxClassDeclarationFileLineStart;
4742 s3b = DT_cxxClassDeclarationFileLineEnd;
4743 if (n->type() == Node::Class || n->type() == Node::Namespace) {
4744 s1 = DT_cxxClassAPIItemLocation;
4745 s2 = DT_cxxClassDeclarationFile;
4746 s3a = DT_cxxClassDeclarationFileLineStart;
4747 s3b = DT_cxxClassDeclarationFileLineEnd;
4749 else if (n->type() == Node::Function) {
4750 FunctionNode* fn = const_cast<FunctionNode*>(static_cast<const FunctionNode*>(n));
4751 if (fn->isMacro()) {
4752 s1 = DT_cxxDefineAPIItemLocation;
4753 s2 = DT_cxxDefineDeclarationFile;
4754 s3a = DT_cxxDefineDeclarationFileLine;
4758 s1 = DT_cxxFunctionAPIItemLocation;
4759 s2 = DT_cxxFunctionDeclarationFile;
4760 s3a = DT_cxxFunctionDeclarationFileLine;
4764 else if (n->type() == Node::Enum) {
4765 s1 = DT_cxxEnumerationAPIItemLocation;
4766 s2 = DT_cxxEnumerationDeclarationFile;
4767 s3a = DT_cxxEnumerationDeclarationFileLineStart;
4768 s3b = DT_cxxEnumerationDeclarationFileLineEnd;
4770 else if (n->type() == Node::Typedef) {
4771 s1 = DT_cxxTypedefAPIItemLocation;
4772 s2 = DT_cxxTypedefDeclarationFile;
4773 s3a = DT_cxxTypedefDeclarationFileLine;
4776 else if ((n->type() == Node::Property) ||
4777 (n->type() == Node::Variable)) {
4778 s1 = DT_cxxVariableAPIItemLocation;
4779 s2 = DT_cxxVariableDeclarationFile;
4780 s3a = DT_cxxVariableDeclarationFileLine;
4785 xmlWriter().writeAttribute("name","filePath");
4786 xmlWriter().writeAttribute("value",n->location().filePath());
4787 writeEndTag(); // <s2>
4789 xmlWriter().writeAttribute("name","lineNumber");
4791 xmlWriter().writeAttribute("value",lineNr.setNum(n->location().lineNo()));
4792 writeEndTag(); // </s3a>
4793 if (s3b != DT_NONE) {
4795 xmlWriter().writeAttribute("name","lineNumber");
4797 xmlWriter().writeAttribute("value",lineNr.setNum(n->location().lineNo()));
4798 writeEndTag(); // </s3b>
4800 writeEndTag(); // </cxx<s1>ApiItemLocation>
4804 Write the <cxxFunction> elements.
4806 void DitaXmlGenerator::writeFunctions(const Section& s,
4807 const InnerNode* parent,
4809 const QString& attribute)
4811 NodeList::ConstIterator m = s.members.constBegin();
4812 while (m != s.members.constEnd()) {
4813 if ((*m)->type() == Node::Function) {
4814 FunctionNode* fn = const_cast<FunctionNode*>(static_cast<const FunctionNode*>(*m));
4815 writeStartTag(DT_cxxFunction);
4816 xmlWriter().writeAttribute("id",fn->guid());
4817 if (fn->metaness() == FunctionNode::Signal)
4818 xmlWriter().writeAttribute("otherprops","signal");
4819 else if (fn->metaness() == FunctionNode::Slot)
4820 xmlWriter().writeAttribute("otherprops","slot");
4821 if (!attribute.isEmpty())
4822 xmlWriter().writeAttribute("outputclass",attribute);
4823 writeStartTag(DT_apiName);
4824 writeCharacters(fn->name());
4825 writeEndTag(); // </apiName>
4826 generateBrief(fn,marker);
4828 // not included: <prolog>
4830 writeStartTag(DT_cxxFunctionDetail);
4831 writeStartTag(DT_cxxFunctionDefinition);
4832 writeStartTag(DT_cxxFunctionAccessSpecifier);
4833 xmlWriter().writeAttribute("value",fn->accessString());
4834 writeEndTag(); // <cxxFunctionAccessSpecifier>
4836 // not included: <cxxFunctionStorageClassSpecifierExtern>
4838 if (fn->isStatic()) {
4839 writeStartTag(DT_cxxFunctionStorageClassSpecifierStatic);
4840 xmlWriter().writeAttribute("name","static");
4841 xmlWriter().writeAttribute("value","static");
4842 writeEndTag(); // <cxxFunctionStorageClassSpecifierStatic>
4845 // not included: <cxxFunctionStorageClassSpecifierMutable>,
4847 if (fn->isConst()) {
4848 writeStartTag(DT_cxxFunctionConst);
4849 xmlWriter().writeAttribute("name","const");
4850 xmlWriter().writeAttribute("value","const");
4851 writeEndTag(); // <cxxFunctionConst>
4854 // not included: <cxxFunctionExplicit>
4855 // <cxxFunctionInline
4857 if (fn->virtualness() != FunctionNode::NonVirtual) {
4858 writeStartTag(DT_cxxFunctionVirtual);
4859 xmlWriter().writeAttribute("name","virtual");
4860 xmlWriter().writeAttribute("value","virtual");
4861 writeEndTag(); // <cxxFunctionVirtual>
4862 if (fn->virtualness() == FunctionNode::PureVirtual) {
4863 writeStartTag(DT_cxxFunctionPureVirtual);
4864 xmlWriter().writeAttribute("name","pure virtual");
4865 xmlWriter().writeAttribute("value","pure virtual");
4866 writeEndTag(); // <cxxFunctionPureVirtual>
4870 if (fn->name() == parent->name()) {
4871 writeStartTag(DT_cxxFunctionConstructor);
4872 xmlWriter().writeAttribute("name","constructor");
4873 xmlWriter().writeAttribute("value","constructor");
4874 writeEndTag(); // <cxxFunctionConstructor>
4876 else if (fn->name()[0] == QChar('~')) {
4877 writeStartTag(DT_cxxFunctionDestructor);
4878 xmlWriter().writeAttribute("name","destructor");
4879 xmlWriter().writeAttribute("value","destructor");
4880 writeEndTag(); // <cxxFunctionDestructor>
4883 writeStartTag(DT_cxxFunctionDeclaredType);
4884 QString src = marker->typified(fn->returnType());
4885 replaceTypesWithLinks(fn,parent,marker,src);
4886 writeEndTag(); // <cxxFunctionDeclaredType>
4889 // not included: <cxxFunctionReturnType>
4891 QString fq = fullQualification(fn);
4892 if (!fq.isEmpty()) {
4893 writeStartTag(DT_cxxFunctionScopedName);
4894 writeCharacters(fq);
4895 writeEndTag(); // <cxxFunctionScopedName>
4897 writeStartTag(DT_cxxFunctionPrototype);
4898 writeCharacters(fn->signature(true));
4899 writeEndTag(); // <cxxFunctionPrototype>
4901 QString fnl = fn->signature(false);
4902 int idx = fnl.indexOf(' ');
4907 fnl = fn->parent()->name() + "::" + fnl.mid(idx);
4908 writeStartTag(DT_cxxFunctionNameLookup);
4909 writeCharacters(fnl);
4910 writeEndTag(); // <cxxFunctionNameLookup>
4912 if (!fn->isInternal() && fn->isReimp() && fn->reimplementedFrom() != 0) {
4913 FunctionNode* rfn = (FunctionNode*)fn->reimplementedFrom();
4914 if (rfn && !rfn->isInternal()) {
4915 writeStartTag(DT_cxxFunctionReimplemented);
4916 xmlWriter().writeAttribute("href",rfn->ditaXmlHref());
4917 writeCharacters(marker->plainFullName(rfn));
4918 writeEndTag(); // </cxxFunctionReimplemented>
4921 writeParameters(fn,parent,marker);
4923 writeEndTag(); // <cxxFunctionDefinition>
4925 writeApiDesc(fn, marker, QString());
4926 // generateAlsoList(inner, marker);
4928 // not included: <example> or <apiImpl>
4930 writeEndTag(); // </cxxFunctionDetail>
4931 writeEndTag(); // </cxxFunction>
4933 if (fn->metaness() == FunctionNode::Ctor ||
4934 fn->metaness() == FunctionNode::Dtor ||
4935 fn->overloadNumber() != 1) {
4942 static const QString typeTag("type");
4943 static const QChar charLangle = '<';
4944 static const QChar charAt = '@';
4947 This function replaces class and enum names with <apiRelation>
4948 elements, i.e. links.
4950 void DitaXmlGenerator::replaceTypesWithLinks(const Node* n,
4951 const InnerNode* parent,
4957 int srcSize = src.size();
4959 for (int i=0; i<srcSize;) {
4960 if (src.at(i) == charLangle && src.at(i+1) == charAt) {
4961 if (!text.isEmpty()) {
4962 writeCharacters(text);
4966 if (parseArg(src, typeTag, &i, srcSize, &arg, &par1)) {
4967 const Node* tn = marker->resolveTarget(arg.toString(), tree_, parent, n);
4969 //Do not generate a link from a C++ function to a QML Basic Type (such as int)
4970 if (n->type() == Node::Function && tn->subType() == Node::QmlBasicType)
4971 writeCharacters(arg.toString());
4973 addLink(linkForNode(tn,parent),arg,DT_apiRelation);
4976 // Write simple arguments, like void and bool,
4977 // which do not have a Qt defined target.
4978 writeCharacters(arg.toString());
4983 text += src.at(i++);
4986 if (!text.isEmpty()) {
4987 writeCharacters(text);
4993 This function writes the <cxxFunctionParameters> element.
4995 void DitaXmlGenerator::writeParameters(const FunctionNode* fn,
4996 const InnerNode* parent,
4999 const QList<Parameter>& parameters = fn->parameters();
5000 if (!parameters.isEmpty()) {
5001 writeStartTag(DT_cxxFunctionParameters);
5002 QList<Parameter>::ConstIterator p = parameters.constBegin();
5003 while (p != parameters.constEnd()) {
5004 writeStartTag(DT_cxxFunctionParameter);
5005 writeStartTag(DT_cxxFunctionParameterDeclaredType);
5006 QString src = marker->typified((*p).leftType());
5007 replaceTypesWithLinks(fn,parent,marker,src);
5008 //writeCharacters((*p).leftType());
5009 if (!(*p).rightType().isEmpty())
5010 writeCharacters((*p).rightType());
5011 writeEndTag(); // <cxxFunctionParameterDeclaredType>
5012 writeStartTag(DT_cxxFunctionParameterDeclarationName);
5013 writeCharacters((*p).name());
5014 writeEndTag(); // <cxxFunctionParameterDeclarationName>
5016 // not included: <cxxFunctionParameterDefinitionName>
5018 if (!(*p).defaultValue().isEmpty()) {
5019 writeStartTag(DT_cxxFunctionParameterDefaultValue);
5020 writeCharacters((*p).defaultValue());
5021 writeEndTag(); // <cxxFunctionParameterDefaultValue>
5024 // not included: <apiDefNote>
5026 writeEndTag(); // <cxxFunctionParameter>
5029 writeEndTag(); // <cxxFunctionParameters>
5034 This function writes the enum types.
5036 void DitaXmlGenerator::writeEnumerations(const Section& s,
5038 const QString& attribute)
5040 NodeList::ConstIterator m = s.members.constBegin();
5041 while (m != s.members.constEnd()) {
5042 if ((*m)->type() == Node::Enum) {
5043 const EnumNode* en = static_cast<const EnumNode*>(*m);
5044 writeStartTag(DT_cxxEnumeration);
5045 xmlWriter().writeAttribute("id",en->guid());
5046 if (!attribute.isEmpty())
5047 xmlWriter().writeAttribute("outputclass",attribute);
5048 writeStartTag(DT_apiName);
5049 writeCharacters(en->name());
5050 writeEndTag(); // </apiName>
5051 generateBrief(en,marker);
5053 // not included <prolog>
5055 writeStartTag(DT_cxxEnumerationDetail);
5056 writeStartTag(DT_cxxEnumerationDefinition);
5057 writeStartTag(DT_cxxEnumerationAccessSpecifier);
5058 xmlWriter().writeAttribute("value",en->accessString());
5059 writeEndTag(); // <cxxEnumerationAccessSpecifier>
5061 QString fq = fullQualification(en);
5062 if (!fq.isEmpty()) {
5063 writeStartTag(DT_cxxEnumerationScopedName);
5064 writeCharacters(fq);
5065 writeEndTag(); // <cxxEnumerationScopedName>
5067 const QList<EnumItem>& items = en->items();
5068 if (!items.isEmpty()) {
5069 writeStartTag(DT_cxxEnumerationPrototype);
5070 writeCharacters(en->name());
5071 xmlWriter().writeCharacters(" = { ");
5072 QList<EnumItem>::ConstIterator i = items.constBegin();
5073 while (i != items.constEnd()) {
5074 writeCharacters((*i).name());
5075 if (!(*i).value().isEmpty()) {
5076 xmlWriter().writeCharacters(" = ");
5077 writeCharacters((*i).value());
5080 if (i != items.constEnd())
5081 xmlWriter().writeCharacters(", ");
5083 xmlWriter().writeCharacters(" }");
5084 writeEndTag(); // <cxxEnumerationPrototype>
5087 writeStartTag(DT_cxxEnumerationNameLookup);
5088 writeCharacters(en->parent()->name() + "::" + en->name());
5089 writeEndTag(); // <cxxEnumerationNameLookup>
5091 // not included: <cxxEnumerationReimplemented>
5093 if (!items.isEmpty()) {
5094 writeStartTag(DT_cxxEnumerators);
5095 QList<EnumItem>::ConstIterator i = items.constBegin();
5096 while (i != items.constEnd()) {
5097 writeStartTag(DT_cxxEnumerator);
5098 writeStartTag(DT_apiName);
5099 writeCharacters((*i).name());
5100 writeEndTag(); // </apiName>
5102 QString fq = fullQualification(en->parent());
5103 if (!fq.isEmpty()) {
5104 writeStartTag(DT_cxxEnumeratorScopedName);
5105 writeCharacters(fq + "::" + (*i).name());
5106 writeEndTag(); // <cxxEnumeratorScopedName>
5108 writeStartTag(DT_cxxEnumeratorPrototype);
5109 writeCharacters((*i).name());
5110 writeEndTag(); // <cxxEnumeratorPrototype>
5111 writeStartTag(DT_cxxEnumeratorNameLookup);
5112 writeCharacters(en->parent()->name() + "::" + (*i).name());
5113 writeEndTag(); // <cxxEnumeratorNameLookup>
5115 if (!(*i).value().isEmpty()) {
5116 writeStartTag(DT_cxxEnumeratorInitialiser);
5117 if ((*i).value().toInt(0,16) == 0)
5118 xmlWriter().writeAttribute("value", "0");
5120 xmlWriter().writeAttribute("value", (*i).value());
5121 writeEndTag(); // <cxxEnumeratorInitialiser>
5124 // not included: <cxxEnumeratorAPIItemLocation>
5126 if (!(*i).text().isEmpty()) {
5127 writeStartTag(DT_apiDesc);
5128 generateText((*i).text(), en, marker);
5129 writeEndTag(); // </apiDesc>
5131 writeEndTag(); // <cxxEnumerator>
5134 writeEndTag(); // <cxxEnumerators>
5138 writeEndTag(); // <cxxEnumerationDefinition>
5140 writeApiDesc(en, marker, QString());
5142 // not included: <example> or <apiImpl>
5144 writeEndTag(); // </cxxEnumerationDetail>
5146 // not included: <related-links>
5148 writeEndTag(); // </cxxEnumeration>
5155 This function writes the output for the \typedef commands.
5157 void DitaXmlGenerator::writeTypedefs(const Section& s,
5159 const QString& attribute)
5162 NodeList::ConstIterator m = s.members.constBegin();
5163 while (m != s.members.constEnd()) {
5164 if ((*m)->type() == Node::Typedef) {
5165 const TypedefNode* tn = static_cast<const TypedefNode*>(*m);
5166 writeStartTag(DT_cxxTypedef);
5167 xmlWriter().writeAttribute("id",tn->guid());
5168 if (!attribute.isEmpty())
5169 xmlWriter().writeAttribute("outputclass",attribute);
5170 writeStartTag(DT_apiName);
5171 writeCharacters(tn->name());
5172 writeEndTag(); // </apiName>
5173 generateBrief(tn,marker);
5175 // not included: <prolog>
5177 writeStartTag(DT_cxxTypedefDetail);
5178 writeStartTag(DT_cxxTypedefDefinition);
5179 writeStartTag(DT_cxxTypedefAccessSpecifier);
5180 xmlWriter().writeAttribute("value",tn->accessString());
5181 writeEndTag(); // <cxxTypedefAccessSpecifier>
5183 // not included: <cxxTypedefDeclaredType>
5185 QString fq = fullQualification(tn);
5186 if (!fq.isEmpty()) {
5187 writeStartTag(DT_cxxTypedefScopedName);
5188 writeCharacters(fq);
5189 writeEndTag(); // <cxxTypedefScopedName>
5192 // not included: <cxxTypedefPrototype>
5194 writeStartTag(DT_cxxTypedefNameLookup);
5195 writeCharacters(tn->parent()->name() + "::" + tn->name());
5196 writeEndTag(); // <cxxTypedefNameLookup>
5198 // not included: <cxxTypedefReimplemented>
5201 writeEndTag(); // <cxxTypedefDefinition>
5203 writeApiDesc(tn, marker, QString());
5205 // not included: <example> or <apiImpl>
5207 writeEndTag(); // </cxxTypedefDetail>
5209 // not included: <related-links>
5211 writeEndTag(); // </cxxTypedef>
5218 This function writes the output for the \property commands.
5219 This is the Q_PROPERTYs.
5221 void DitaXmlGenerator::writeProperties(const Section& s,
5223 const QString& attribute)
5225 NodeList::ConstIterator m = s.members.constBegin();
5226 while (m != s.members.constEnd()) {
5227 if ((*m)->type() == Node::Property) {
5228 const PropertyNode* pn = static_cast<const PropertyNode*>(*m);
5229 writeStartTag(DT_cxxVariable);
5230 xmlWriter().writeAttribute("id",pn->guid());
5231 if (!attribute.isEmpty())
5232 xmlWriter().writeAttribute("outputclass",attribute);
5233 writeStartTag(DT_apiName);
5234 writeCharacters(pn->name());
5235 writeEndTag(); // </apiName>
5236 generateBrief(pn,marker);
5238 // not included: <prolog>
5240 writeStartTag(DT_cxxVariableDetail);
5241 writeStartTag(DT_cxxVariableDefinition);
5242 writeStartTag(DT_cxxVariableAccessSpecifier);
5243 xmlWriter().writeAttribute("value",pn->accessString());
5244 writeEndTag(); // <cxxVariableAccessSpecifier>
5246 // not included: <cxxVariableStorageClassSpecifierExtern>,
5247 // <cxxVariableStorageClassSpecifierStatic>,
5248 // <cxxVariableStorageClassSpecifierMutable>,
5249 // <cxxVariableConst>, <cxxVariableVolatile>
5251 if (!pn->qualifiedDataType().isEmpty()) {
5252 writeStartTag(DT_cxxVariableDeclaredType);
5253 writeCharacters(pn->qualifiedDataType());
5254 writeEndTag(); // <cxxVariableDeclaredType>
5256 QString fq = fullQualification(pn);
5257 if (!fq.isEmpty()) {
5258 writeStartTag(DT_cxxVariableScopedName);
5259 writeCharacters(fq);
5260 writeEndTag(); // <cxxVariableScopedName>
5263 writeStartTag(DT_cxxVariablePrototype);
5264 xmlWriter().writeCharacters("Q_PROPERTY(");
5265 writeCharacters(pn->qualifiedDataType());
5266 xmlWriter().writeCharacters(" ");
5267 writeCharacters(pn->name());
5268 writePropertyParameter("READ",pn->getters());
5269 writePropertyParameter("WRITE",pn->setters());
5270 writePropertyParameter("RESET",pn->resetters());
5271 writePropertyParameter("NOTIFY",pn->notifiers());
5272 if (pn->isDesignable() != pn->designableDefault()) {
5273 xmlWriter().writeCharacters(" DESIGNABLE ");
5274 if (!pn->runtimeDesignabilityFunction().isEmpty())
5275 writeCharacters(pn->runtimeDesignabilityFunction());
5277 xmlWriter().writeCharacters(pn->isDesignable() ? "true" : "false");
5279 if (pn->isScriptable() != pn->scriptableDefault()) {
5280 xmlWriter().writeCharacters(" SCRIPTABLE ");
5281 if (!pn->runtimeScriptabilityFunction().isEmpty())
5282 writeCharacters(pn->runtimeScriptabilityFunction());
5284 xmlWriter().writeCharacters(pn->isScriptable() ? "true" : "false");
5286 if (pn->isWritable() != pn->writableDefault()) {
5287 xmlWriter().writeCharacters(" STORED ");
5288 xmlWriter().writeCharacters(pn->isStored() ? "true" : "false");
5290 if (pn->isUser() != pn->userDefault()) {
5291 xmlWriter().writeCharacters(" USER ");
5292 xmlWriter().writeCharacters(pn->isUser() ? "true" : "false");
5294 if (pn->isConstant())
5295 xmlWriter().writeCharacters(" CONSTANT");
5297 xmlWriter().writeCharacters(" FINAL");
5298 xmlWriter().writeCharacters(")");
5299 writeEndTag(); // <cxxVariablePrototype>
5301 writeStartTag(DT_cxxVariableNameLookup);
5302 writeCharacters(pn->parent()->name() + "::" + pn->name());
5303 writeEndTag(); // <cxxVariableNameLookup>
5305 if (pn->overriddenFrom() != 0) {
5306 PropertyNode* opn = (PropertyNode*)pn->overriddenFrom();
5307 writeStartTag(DT_cxxVariableReimplemented);
5308 xmlWriter().writeAttribute("href",opn->ditaXmlHref());
5309 writeCharacters(marker->plainFullName(opn));
5310 writeEndTag(); // </cxxVariableReimplemented>
5314 writeEndTag(); // <cxxVariableDefinition>
5316 writeApiDesc(pn, marker, QString());
5318 // not included: <example> or <apiImpl>
5320 writeEndTag(); // </cxxVariableDetail>
5322 // not included: <related-links>
5324 writeEndTag(); // </cxxVariable>
5331 This function outputs the nodes resulting from \variable commands.
5333 void DitaXmlGenerator::writeDataMembers(const Section& s,
5335 const QString& attribute)
5337 NodeList::ConstIterator m = s.members.constBegin();
5338 while (m != s.members.constEnd()) {
5339 if ((*m)->type() == Node::Variable) {
5340 const VariableNode* vn = static_cast<const VariableNode*>(*m);
5341 writeStartTag(DT_cxxVariable);
5342 xmlWriter().writeAttribute("id",vn->guid());
5343 if (!attribute.isEmpty())
5344 xmlWriter().writeAttribute("outputclass",attribute);
5345 writeStartTag(DT_apiName);
5346 writeCharacters(vn->name());
5347 writeEndTag(); // </apiName>
5348 generateBrief(vn,marker);
5350 // not included: <prolog>
5352 writeStartTag(DT_cxxVariableDetail);
5353 writeStartTag(DT_cxxVariableDefinition);
5354 writeStartTag(DT_cxxVariableAccessSpecifier);
5355 xmlWriter().writeAttribute("value",vn->accessString());
5356 writeEndTag(); // <cxxVariableAccessSpecifier>
5358 // not included: <cxxVAriableStorageClassSpecifierExtern>
5360 if (vn->isStatic()) {
5361 writeStartTag(DT_cxxVariableStorageClassSpecifierStatic);
5362 xmlWriter().writeAttribute("name","static");
5363 xmlWriter().writeAttribute("value","static");
5364 writeEndTag(); // <cxxVariableStorageClassSpecifierStatic>
5367 // not included: <cxxVAriableStorageClassSpecifierMutable>,
5368 // <cxxVariableConst>, <cxxVariableVolatile>
5370 writeStartTag(DT_cxxVariableDeclaredType);
5371 writeCharacters(vn->leftType());
5372 if (!vn->rightType().isEmpty())
5373 writeCharacters(vn->rightType());
5374 writeEndTag(); // <cxxVariableDeclaredType>
5376 QString fq = fullQualification(vn);
5377 if (!fq.isEmpty()) {
5378 writeStartTag(DT_cxxVariableScopedName);
5379 writeCharacters(fq);
5380 writeEndTag(); // <cxxVariableScopedName>
5383 writeStartTag(DT_cxxVariablePrototype);
5384 writeCharacters(vn->leftType() + QLatin1Char(' '));
5385 //writeCharacters(vn->parent()->name() + "::" + vn->name());
5386 writeCharacters(vn->name());
5387 if (!vn->rightType().isEmpty())
5388 writeCharacters(vn->rightType());
5389 writeEndTag(); // <cxxVariablePrototype>
5391 writeStartTag(DT_cxxVariableNameLookup);
5392 writeCharacters(vn->parent()->name() + "::" + vn->name());
5393 writeEndTag(); // <cxxVariableNameLookup>
5395 // not included: <cxxVariableReimplemented>
5398 writeEndTag(); // <cxxVariableDefinition>
5400 writeApiDesc(vn, marker, QString());
5402 // not included: <example> or <apiImpl>
5404 writeEndTag(); // </cxxVariableDetail>
5406 // not included: <related-links>
5408 writeEndTag(); // </cxxVariable>
5415 This function writes a \macro as a <cxxDefine>.
5417 void DitaXmlGenerator::writeMacros(const Section& s,
5419 const QString& attribute)
5421 NodeList::ConstIterator m = s.members.constBegin();
5422 while (m != s.members.constEnd()) {
5423 if ((*m)->type() == Node::Function) {
5424 const FunctionNode* fn = static_cast<const FunctionNode*>(*m);
5425 if (fn->isMacro()) {
5426 writeStartTag(DT_cxxDefine);
5427 xmlWriter().writeAttribute("id",fn->guid());
5428 if (!attribute.isEmpty())
5429 xmlWriter().writeAttribute("outputclass",attribute);
5430 writeStartTag(DT_apiName);
5431 writeCharacters(fn->name());
5432 writeEndTag(); // </apiName>
5433 generateBrief(fn,marker);
5435 // not included: <prolog>
5437 writeStartTag(DT_cxxDefineDetail);
5438 writeStartTag(DT_cxxDefineDefinition);
5439 writeStartTag(DT_cxxDefineAccessSpecifier);
5440 xmlWriter().writeAttribute("value",fn->accessString());
5441 writeEndTag(); // <cxxDefineAccessSpecifier>
5443 writeStartTag(DT_cxxDefinePrototype);
5444 xmlWriter().writeCharacters("#define ");
5445 writeCharacters(fn->name());
5446 if (fn->metaness() == FunctionNode::MacroWithParams) {
5447 QStringList params = fn->parameterNames();
5448 if (!params.isEmpty()) {
5449 xmlWriter().writeCharacters("(");
5450 for (int i = 0; i < params.size(); ++i) {
5451 if (params[i].isEmpty())
5452 xmlWriter().writeCharacters("...");
5454 writeCharacters(params[i]);
5455 if ((i+1) < params.size())
5456 xmlWriter().writeCharacters(", ");
5458 xmlWriter().writeCharacters(")");
5461 writeEndTag(); // <cxxDefinePrototype>
5463 writeStartTag(DT_cxxDefineNameLookup);
5464 writeCharacters(fn->name());
5465 writeEndTag(); // <cxxDefineNameLookup>
5467 if (fn->reimplementedFrom() != 0) {
5468 FunctionNode* rfn = (FunctionNode*)fn->reimplementedFrom();
5469 writeStartTag(DT_cxxDefineReimplemented);
5470 xmlWriter().writeAttribute("href",rfn->ditaXmlHref());
5471 writeCharacters(marker->plainFullName(rfn));
5472 writeEndTag(); // </cxxDefineReimplemented>
5475 if (fn->metaness() == FunctionNode::MacroWithParams) {
5476 QStringList params = fn->parameterNames();
5477 if (!params.isEmpty()) {
5478 writeStartTag(DT_cxxDefineParameters);
5479 for (int i = 0; i < params.size(); ++i) {
5480 writeStartTag(DT_cxxDefineParameter);
5481 writeStartTag(DT_cxxDefineParameterDeclarationName);
5482 writeCharacters(params[i]);
5483 writeEndTag(); // <cxxDefineParameterDeclarationName>
5485 // not included: <apiDefNote>
5487 writeEndTag(); // <cxxDefineParameter>
5489 writeEndTag(); // <cxxDefineParameters>
5494 writeEndTag(); // <cxxDefineDefinition>
5496 writeApiDesc(fn, marker, QString());
5498 // not included: <example> or <apiImpl>
5500 writeEndTag(); // </cxxDefineDetail>
5502 // not included: <related-links>
5504 writeEndTag(); // </cxxDefine>
5512 This function writes one parameter of a Q_PROPERTY macro.
5513 The property is identified by \a tag ("READ" "WRIE" etc),
5514 and it is found in the 'a nlist.
5516 void DitaXmlGenerator::writePropertyParameter(const QString& tag, const NodeList& nlist)
5518 NodeList::const_iterator n = nlist.constBegin();
5519 while (n != nlist.constEnd()) {
5520 xmlWriter().writeCharacters(" ");
5521 writeCharacters(tag);
5522 xmlWriter().writeCharacters(" ");
5523 writeCharacters((*n)->name());
5529 Calls beginSubPage() in the base class to open the file.
5530 Then creates a new XML stream writer using the IO device
5531 from opened file and pushes the XML writer onto a stackj.
5532 Creates the file named \a fileName in the output directory.
5533 Attaches a QTextStream to the created file, which is written
5534 to all over the place using out(). Finally, it sets some
5535 parameters in the XML writer and calls writeStartDocument().
5537 It also ensures that a GUID map is created for the output file.
5539 void DitaXmlGenerator::beginSubPage(const InnerNode* node,
5540 const QString& fileName)
5542 Generator::beginSubPage(node,fileName);
5543 (void) lookupGuidMap(fileName);
5544 QXmlStreamWriter* writer = new QXmlStreamWriter(out().device());
5545 xmlWriterStack.push(writer);
5546 writer->setAutoFormatting(true);
5547 writer->setAutoFormattingIndent(4);
5548 writer->writeStartDocument();
5549 clearSectionNesting();
5553 Calls writeEndDocument() and then pops the XML stream writer
5554 off the stack and deletes it. Then it calls endSubPage() in
5555 the base class to close the device.
5557 void DitaXmlGenerator::endSubPage()
5560 qDebug() << "Missing </section> in" << outFileName() << sectionNestingLevel;
5561 xmlWriter().writeEndDocument();
5562 delete xmlWriterStack.pop();
5563 Generator::endSubPage();
5567 Returns a reference to the XML stream writer currently in use.
5568 There is one XML stream writer open for each XML file being
5569 written, and they are kept on a stack. The one on top of the
5570 stack is the one being written to at the moment.
5572 QXmlStreamWriter& DitaXmlGenerator::xmlWriter()
5574 return *xmlWriterStack.top();
5578 Writes the \e {<apiDesc>} element for \a node to the current XML
5579 stream using the code \a marker and the \a title.
5581 void DitaXmlGenerator::writeApiDesc(const Node* node,
5583 const QString& title)
5585 if (!node->doc().isEmpty()) {
5586 inDetailedDescription = true;
5587 enterDesc(DT_apiDesc,QString(),title);
5588 generateBody(node, marker);
5589 generateAlsoList(node, marker);
5592 inDetailedDescription = false;
5596 Write the nested class elements.
5598 void DitaXmlGenerator::writeNestedClasses(const Section& s,
5601 if (s.members.isEmpty())
5603 writeStartTag(DT_cxxClassNested);
5604 writeStartTag(DT_cxxClassNestedDetail);
5606 NodeList::ConstIterator m = s.members.constBegin();
5607 while (m != s.members.constEnd()) {
5608 if ((*m)->type() == Node::Class) {
5609 writeStartTag(DT_cxxClassNestedClass);
5610 QString link = linkForNode((*m), n);
5611 xmlWriter().writeAttribute("href", link);
5612 QString name = n->name() + "::" + (*m)->name();
5613 writeCharacters(name);
5614 writeEndTag(); // <cxxClassNestedClass>
5618 writeEndTag(); // <cxxClassNestedDetail>
5619 writeEndTag(); // <cxxClassNested>
5623 Recursive writing of DITA XML files from the root \a node.
5626 DitaXmlGenerator::generateInnerNode(InnerNode* node)
5628 if (!node->url().isNull())
5631 if (node->type() == Node::Fake) {
5632 FakeNode* fakeNode = static_cast<FakeNode*>(node);
5633 if (fakeNode->subType() == Node::ExternalPage)
5635 if (fakeNode->subType() == Node::Image)
5637 if (fakeNode->subType() == Node::QmlPropertyGroup)
5639 if (fakeNode->subType() == Node::Page) {
5640 if (node->count() > 0)
5641 qDebug("PAGE %s HAS CHILDREN", qPrintable(fakeNode->title()));
5646 Obtain a code marker for the source file.
5648 CodeMarker *marker = CodeMarker::markerForFileName(node->location().filePath());
5649 if (node->parent() != 0) {
5651 Skip name collision nodes here and process them
5652 later in generateCollisionPages(). Each one is
5653 appended to a list for later.
5655 if ((node->type() == Node::Fake) && (node->subType() == Node::Collision)) {
5656 NameCollisionNode* ncn = static_cast<NameCollisionNode*>(node);
5657 collisionNodes.append(const_cast<NameCollisionNode*>(ncn));
5660 if (!node->name().endsWith(".ditamap"))
5661 beginSubPage(node, fileName(node));
5662 if (node->type() == Node::Namespace || node->type() == Node::Class) {
5663 generateClassLikeNode(node, marker);
5665 else if (node->type() == Node::Fake) {
5666 if (node->subType() == Node::HeaderFile)
5667 generateClassLikeNode(node, marker);
5668 else if (node->subType() == Node::QmlClass)
5669 generateClassLikeNode(node, marker);
5671 generateFakeNode(static_cast<FakeNode*>(node), marker);
5673 if (!node->name().endsWith(".ditamap"))
5678 NodeList::ConstIterator c = node->childNodes().constBegin();
5679 while (c != node->childNodes().constEnd()) {
5680 if ((*c)->isInnerNode() && (*c)->access() != Node::Private)
5681 generateInnerNode((InnerNode*)*c);
5687 Returns true if \a format is "DITAXML" or "HTML" .
5689 bool DitaXmlGenerator::canHandleFormat(const QString& format)
5691 return (format == "HTML") || (format == this->format());
5695 If the node multimap \a nmm contains nodes mapped to \a key,
5696 if any of the nodes mapped to \a key has the same href as the
5697 \a node, return true. Otherwise, return false.
5699 bool DitaXmlGenerator::isDuplicate(NodeMultiMap* nmm, const QString& key, Node* node)
5701 QList<Node*> matches = nmm->values(key);
5702 if (!matches.isEmpty()) {
5703 for (int i=0; i<matches.size(); ++i) {
5704 if (matches[i] == node)
5706 if (fileName(node) == fileName(matches[i]))
5713 Collect all the nodes in the tree according to their type or subtype.
5715 If a node is found that is named index.html, return that node as the
5722 subtype: External page
5724 subtype: Header file
5727 subtype: QML basic type
5731 Node* DitaXmlGenerator::collectNodesByTypeAndSubtype(const InnerNode* parent)
5733 Node* rootPageNode = 0;
5734 const NodeList& children = parent->childNodes();
5735 if (children.size() == 0)
5736 return rootPageNode;
5739 for (int i=0; i<children.size(); ++i) {
5740 Node* child = children[i];
5741 if ((child->type() == Node::Fake) && (child->subType() == Node::Collision)) {
5742 const FakeNode* fake = static_cast<const FakeNode*>(child);
5743 Node* n = collectNodesByTypeAndSubtype(fake);
5748 if (!child || child->isInternal() || child->doc().isEmpty() || child->isIndexNode())
5751 if (child->name() == "index.html" || child->name() == "index") {
5752 rootPageNode = child;
5755 switch (child->type()) {
5756 case Node::Namespace:
5757 if (!isDuplicate(nodeTypeMaps[Node::Namespace],child->name(),child))
5758 nodeTypeMaps[Node::Namespace]->insert(child->name(),child);
5761 if (!isDuplicate(nodeTypeMaps[Node::Class],child->name(),child))
5762 nodeTypeMaps[Node::Class]->insert(child->name(),child);
5765 switch (child->subType()) {
5767 if (!isDuplicate(nodeSubtypeMaps[Node::Example],child->title(),child))
5768 nodeSubtypeMaps[Node::Example]->insert(child->title(),child);
5770 case Node::HeaderFile:
5771 if (!isDuplicate(nodeSubtypeMaps[Node::HeaderFile],child->title(),child))
5772 nodeSubtypeMaps[Node::HeaderFile]->insert(child->title(),child);
5779 if (!isDuplicate(nodeSubtypeMaps[Node::Group],child->title(),child))
5780 nodeSubtypeMaps[Node::Group]->insert(child->title(),child);
5783 if (!isDuplicate(nodeSubtypeMaps[Node::Module],child->title(),child))
5784 nodeSubtypeMaps[Node::Module]->insert(child->title(),child);
5787 if (!isDuplicate(pageTypeMaps[child->pageType()],child->title(),child))
5788 pageTypeMaps[child->pageType()]->insert(child->title(),child);
5790 case Node::ExternalPage:
5791 if (!isDuplicate(nodeSubtypeMaps[Node::ExternalPage],child->title(),child))
5792 nodeSubtypeMaps[Node::ExternalPage]->insert(child->title(),child);
5794 case Node::QmlClass:
5795 if (!isDuplicate(nodeSubtypeMaps[Node::QmlClass],child->title(),child))
5796 nodeSubtypeMaps[Node::QmlClass]->insert(child->title(),child);
5798 case Node::QmlPropertyGroup:
5800 case Node::QmlBasicType:
5801 if (!isDuplicate(nodeSubtypeMaps[Node::QmlBasicType],child->title(),child))
5802 nodeSubtypeMaps[Node::QmlBasicType]->insert(child->title(),child);
5804 case Node::QmlModule:
5805 if (!isDuplicate(nodeSubtypeMaps[Node::QmlModule],child->title(),child))
5806 nodeSubtypeMaps[Node::QmlModule]->insert(child->title(),child);
5808 case Node::Collision:
5809 if (!isDuplicate(nodeSubtypeMaps[Node::Collision],child->title(),child))
5810 nodeSubtypeMaps[Node::Collision]->insert(child->title(),child);
5820 case Node::Function:
5822 case Node::Property:
5824 case Node::Variable:
5826 case Node::QmlProperty:
5828 case Node::QmlSignal:
5830 case Node::QmlSignalHandler:
5832 case Node::QmlMethod:
5838 return rootPageNode;
5842 Creates the DITA map for the qdoc run. The map is written
5843 to the file \e{qt.ditamap" in the DITA XML output directory.
5845 void DitaXmlGenerator::writeDitaMap(Tree *tree)
5850 Remove #if 0 to get a flat ditamap.
5853 beginSubPage(tree->root(),"qt.ditamap");
5854 doctype = "<!DOCTYPE map PUBLIC \"-//OASIS//DTD DITA Map//EN\" \"map.dtd\">";
5855 xmlWriter().writeDTD(doctype);
5856 writeStartTag(DT_map);
5857 writeStartTag(DT_topicmeta);
5858 writeStartTag(DT_shortdesc);
5859 xmlWriter().writeCharacters("The top level map for the Qt documentation");
5860 writeEndTag(); // </shortdesc>
5861 writeEndTag(); // </topicmeta>
5862 GuidMaps::iterator i = guidMaps.constBegin();
5863 while (i != guidMaps.constEnd()) {
5864 writeStartTag(DT_topicref);
5865 if (i.key() != "qt.ditamap")
5866 xmlWriter().writeAttribute("href",i.key());
5867 writeEndTag(); // </topicref>
5873 for (unsigned i=0; i<Node::LastType; ++i)
5874 nodeTypeMaps[i] = new NodeMultiMap;
5875 for (unsigned i=0; i<Node::LastSubtype; ++i)
5876 nodeSubtypeMaps[i] = new NodeMultiMap;
5877 for (unsigned i=0; i<Node::OnBeyondZebra; ++i)
5878 pageTypeMaps[i] = new NodeMultiMap;
5879 Node* rootPageNode = collectNodesByTypeAndSubtype(tree->root());
5881 beginSubPage(tree->root(),"qt.ditamap");
5883 doctype = "<!DOCTYPE map PUBLIC \"-//OASIS//DTD DITA Map//EN\" \"map.dtd\">";
5884 xmlWriter().writeDTD(doctype);
5885 writeStartTag(DT_map);
5886 writeStartTag(DT_topicmeta);
5887 writeStartTag(DT_shortdesc);
5888 xmlWriter().writeCharacters("The top level map for the Qt documentation");
5889 writeEndTag(); // </shortdesc>
5890 writeEndTag(); // </topicmeta>
5892 writeStartTag(DT_topicref);
5894 if (!rootPageNode->title().isEmpty())
5895 xmlWriter().writeAttribute("navtitle",rootPageNode->title());
5897 xmlWriter().writeAttribute("navtitle",project);
5898 xmlWriter().writeAttribute("href",fileName(rootPageNode));
5901 xmlWriter().writeAttribute("navtitle",project);
5903 writeTopicrefs(pageTypeMaps[Node::OverviewPage], "Overviews");
5904 writeTopicrefs(pageTypeMaps[Node::HowToPage], "Howtos");
5905 writeTopicrefs(pageTypeMaps[Node::TutorialPage], "Tutorials");
5906 writeTopicrefs(pageTypeMaps[Node::FAQPage], "FAQs");
5907 writeTopicrefs(pageTypeMaps[Node::ArticlePage], "Articles");
5908 writeTopicrefs(nodeSubtypeMaps[Node::Example], "Examples");
5909 if (nodeSubtypeMaps[Node::QmlModule]->size() > 1)
5910 writeTopicrefs(nodeSubtypeMaps[Node::QmlModule], "QML modules");
5911 if (nodeSubtypeMaps[Node::QmlModule]->size() == 1)
5912 writeTopicrefs(nodeSubtypeMaps[Node::QmlClass], "QML types", nodeSubtypeMaps[Node::QmlModule]->values()[0]);
5914 writeTopicrefs(nodeSubtypeMaps[Node::QmlClass], "QML types");
5915 writeTopicrefs(nodeSubtypeMaps[Node::QmlBasicType], "QML basic types");
5916 if (nodeSubtypeMaps[Node::Module]->size() > 1)
5917 writeTopicrefs(nodeSubtypeMaps[Node::Module], "Modules");
5918 if (nodeSubtypeMaps[Node::Module]->size() == 1)
5919 writeTopicrefs(nodeTypeMaps[Node::Class], "C++ classes", nodeSubtypeMaps[Node::Module]->values()[0]);
5921 writeTopicrefs(nodeTypeMaps[Node::Class], "C++ classes");
5922 writeTopicrefs(nodeTypeMaps[Node::Namespace], "C++ namespaces");
5923 writeTopicrefs(nodeSubtypeMaps[Node::HeaderFile], "Header files");
5924 writeTopicrefs(nodeSubtypeMaps[Node::Group], "Groups");
5926 writeEndTag(); // </topicref>
5929 for (unsigned i=0; i<Node::LastType; ++i)
5930 delete nodeTypeMaps[i];
5931 for (unsigned i=0; i<Node::LastSubtype; ++i)
5932 delete nodeSubtypeMaps[i];
5933 for (unsigned i=0; i<Node::OnBeyondZebra; ++i)
5934 delete pageTypeMaps[i];
5938 Creates the DITA map from the topicrefs in \a node,
5939 which is a DitaMapNode.
5941 void DitaXmlGenerator::writeDitaMap(const DitaMapNode* node)
5943 beginSubPage(node,node->name());
5946 doctype = "<!DOCTYPE map PUBLIC \"-//OASIS//DTD DITA Map//EN\" \"map.dtd\">";
5947 xmlWriter().writeDTD(doctype);
5948 writeStartTag(DT_map);
5949 writeStartTag(DT_topicmeta);
5950 writeStartTag(DT_shortdesc);
5951 xmlWriter().writeCharacters(node->title());
5952 writeEndTag(); // </shortdesc>
5953 writeEndTag(); // </topicmeta>
5954 const DitaRefList map = node->map();
5960 Write the \a ditarefs to the current output file.
5962 void DitaXmlGenerator::writeDitaRefs(const DitaRefList& ditarefs)
5964 foreach (DitaRef* t, ditarefs) {
5966 writeStartTag(DT_mapref);
5968 writeStartTag(DT_topicref);
5969 xmlWriter().writeAttribute("navtitle",t->navtitle());
5970 if (t->href().isEmpty()) {
5971 const FakeNode* fn = tree_->findFakeNodeByTitle(t->navtitle());
5973 xmlWriter().writeAttribute("href",fileName(fn));
5976 xmlWriter().writeAttribute("href",t->href());
5977 if (t->subrefs() && !t->subrefs()->isEmpty())
5978 writeDitaRefs(*(t->subrefs()));
5979 writeEndTag(); // </topicref> or </mapref>
5983 void DitaXmlGenerator::writeTopicrefs(NodeMultiMap* nmm, const QString& navtitle, Node* headingnode)
5985 if (!nmm || nmm->isEmpty())
5987 writeStartTag(DT_topicref);
5988 xmlWriter().writeAttribute("navtitle",navtitle);
5990 xmlWriter().writeAttribute("href",fileName(headingnode));
5991 NodeMultiMap::iterator i;
5992 NodeMultiMap *ditaMaps = pageTypeMaps[Node::DitaMapPage];
5995 Put all pages that are already in a hand-written ditamap not in
5996 the qt.ditamap separately. It loops through all ditamaps recursively
5997 before deciding to write an article to qt.ditamap.
5999 if ((navtitle == "Articles" && ditaMaps && ditaMaps->size() > 0)) {
6000 NodeMultiMap::iterator mapIterator = ditaMaps->begin();
6001 while (mapIterator != ditaMaps->end()) {
6002 writeStartTag(DT_mapref);
6003 xmlWriter().writeAttribute("navtitle",mapIterator.key());
6004 xmlWriter().writeAttribute("href",fileName(mapIterator.value()));
6009 while (i != nmm->end()) {
6010 // Hardcode not writing index.dita multiple times in the tree.
6011 // index.dita should only appear at the top of the ditamap.
6012 if (fileName(i.value()) == "index.dita") {
6016 bool foundInDitaMap = false;
6017 mapIterator = ditaMaps->begin();
6018 while (mapIterator != ditaMaps->end()) {
6019 const DitaMapNode *dmNode = static_cast<const DitaMapNode *>(mapIterator.value());
6020 for (int count = 0; count < dmNode->map().count(); count++) {
6021 if (dmNode->map().at(count)->navtitle() == i.key()) {
6022 foundInDitaMap = true;
6028 if (!foundInDitaMap) {
6029 writeStartTag(DT_topicref);
6030 xmlWriter().writeAttribute("navtitle",i.key());
6031 xmlWriter().writeAttribute("href",fileName(i.value()));
6032 writeEndTag(); // </topicref>
6038 Shortcut when there are no hand-written ditamaps or when we are
6039 not generating the articles list.
6043 while (i != nmm->end()) {
6044 // Hardcode not writing index.dita multiple times in the tree.
6045 // index.dita should only appear at the top of the ditamap.
6046 if (fileName(i.value()) == "index.dita") {
6050 writeStartTag(DT_topicref);
6051 xmlWriter().writeAttribute("navtitle",i.key());
6052 xmlWriter().writeAttribute("href",fileName(i.value()));
6053 switch (i.value()->type()) {
6055 case Node::Namespace: {
6056 const NamespaceNode* nn = static_cast<const NamespaceNode*>(i.value());
6057 const NodeList& c = nn->childNodes();
6058 QMap<QString, const Node*> tempMap;
6059 for (int j=0; j<c.size(); ++j) {
6060 if (c[j]->isInternal() || c[j]->access() == Node::Private || c[j]->doc().isEmpty())
6062 if (c[j]->type() == Node::Class) {
6063 tempMap.insert(c[j]->name(), c[j]);
6066 QMap<QString, const Node*>::iterator classIterator = tempMap.begin();
6067 while (classIterator != tempMap.end()) {
6068 const Node* currentNode = static_cast<const Node*>(classIterator.value());
6069 writeStartTag(DT_topicref);
6070 xmlWriter().writeAttribute("navtitle",currentNode->name());
6071 xmlWriter().writeAttribute("href",fileName(currentNode));
6072 writeEndTag(); // </topicref>
6081 writeEndTag(); // </topicref>
6085 writeEndTag(); // </topicref>
6090 Looks up the tag name for \a t in the map of metadata
6091 values for the current topic in \a inner. If a value
6092 for the tag is found, the element is written with the
6093 found value. Otherwise if \a force is set, an empty
6094 element is written using the tag.
6096 Returns true or false depending on whether it writes
6097 an element using the tag \a t.
6099 \note If \a t is found in the metadata map, it is erased.
6100 i.e. Once you call this function for a particular \a t,
6103 bool DitaXmlGenerator::writeMetadataElement(const InnerNode* inner,
6104 DitaXmlGenerator::DitaTag t,
6107 QString s = getMetadataElement(inner,t);
6108 if (s.isEmpty() && !force)
6112 xmlWriter().writeCharacters(s);
6119 Looks up the tag name for \a t in the map of metadata
6120 values for the current topic in \a inner. If one or more
6121 value sfor the tag are found, the elements are written.
6122 Otherwise nothing is written.
6124 Returns true or false depending on whether it writes
6125 at least one element using the tag \a t.
6127 \note If \a t is found in the metadata map, it is erased.
6128 i.e. Once you call this function for a particular \a t,
6131 bool DitaXmlGenerator::writeMetadataElements(const InnerNode* inner,
6132 DitaXmlGenerator::DitaTag t)
6134 QStringList s = getMetadataElements(inner,t);
6137 for (int i=0; i<s.size(); ++i) {
6139 xmlWriter().writeCharacters(s[i]);
6146 Looks up the tag name for \a t in the map of metadata
6147 values for the current topic in \a inner. If a value
6148 for the tag is found, the value is returned.
6150 \note If \a t is found in the metadata map, it is erased.
6151 i.e. Once you call this function for a particular \a t,
6154 QString DitaXmlGenerator::getMetadataElement(const InnerNode* inner, DitaXmlGenerator::DitaTag t)
6156 QString s = Generator::getMetadataElement(inner, ditaTags[t]);
6158 s = metadataDefault(t);
6163 Looks up the tag name for \a t in the map of metadata
6164 values for the current topic in \a inner. If values
6165 for the tag are found, they are returned in a string
6168 \note If \a t is found in the metadata map, all the
6169 pairs having the key \a t are erased. i.e. Once you
6170 all this function for a particular \a t, you consume
6173 QStringList DitaXmlGenerator::getMetadataElements(const InnerNode* inner,
6174 DitaXmlGenerator::DitaTag t)
6176 QStringList s = Generator::getMetadataElements(inner,ditaTags[t]);
6178 s.append(metadataDefault(t));
6183 Returns the value of key \a t or an empty string
6184 if \a t is not found in the map.
6186 QString DitaXmlGenerator::metadataDefault(DitaTag t) const
6188 return metadataDefaults.value(ditaTags[t]);
6192 Writes the <prolog> element for the \a inner node
6193 using the \a marker. The <prolog> element contains
6194 the <metadata> element, plus some others. This
6195 function writes one or more of these elements:
6205 \o <created> not used
6207 \o <critdates> not used
6208 \o <keyword> not used
6209 \o <keywords> not used
6213 \o <platform> not used
6218 \o <resourceid> not used
6219 \o <revised> not used
6220 \o <source> not used
6222 \o <unknown> not used
6227 \node * means the tag has been used.
6231 DitaXmlGenerator::writeProlog(const InnerNode* inner)
6235 writeStartTag(DT_prolog);
6236 writeMetadataElements(inner,DT_author);
6237 writeMetadataElement(inner,DT_publisher);
6238 QString s = getMetadataElement(inner,DT_copyryear);
6239 QString t = getMetadataElement(inner,DT_copyrholder);
6240 writeStartTag(DT_copyright);
6241 writeStartTag(DT_copyryear);
6243 xmlWriter().writeAttribute("year",s);
6244 writeEndTag(); // </copyryear>
6245 writeStartTag(DT_copyrholder);
6247 xmlWriter().writeCharacters(t);
6248 writeEndTag(); // </copyrholder>
6249 writeEndTag(); // </copyright>
6250 s = getMetadataElement(inner,DT_permissions);
6251 writeStartTag(DT_permissions);
6252 xmlWriter().writeAttribute("view",s);
6253 writeEndTag(); // </permissions>
6254 writeStartTag(DT_metadata);
6255 QStringList sl = getMetadataElements(inner,DT_audience);
6256 if (!sl.isEmpty()) {
6257 for (int i=0; i<sl.size(); ++i) {
6258 writeStartTag(DT_audience);
6259 xmlWriter().writeAttribute("type",sl[i]);
6260 writeEndTag(); // </audience>
6263 if (!writeMetadataElement(inner,DT_category,false)) {
6264 writeStartTag(DT_category);
6265 QString category = "Page";
6266 if (inner->type() == Node::Class)
6267 category = "Class reference";
6268 else if (inner->type() == Node::Namespace)
6269 category = "Namespace";
6270 else if (inner->type() == Node::Fake) {
6271 if (inner->subType() == Node::QmlClass)
6272 category = "QML Reference";
6273 else if (inner->subType() == Node::QmlBasicType)
6274 category = "QML Basic Type";
6275 else if (inner->subType() == Node::HeaderFile)
6276 category = "Header File";
6277 else if (inner->subType() == Node::Module)
6278 category = "Module";
6279 else if (inner->subType() == Node::File)
6280 category = "Example Source File";
6281 else if (inner->subType() == Node::Example)
6282 category = "Example";
6283 else if (inner->subType() == Node::Image)
6285 else if (inner->subType() == Node::Group)
6287 else if (inner->subType() == Node::Page)
6289 else if (inner->subType() == Node::ExternalPage)
6290 category = "External Page"; // Is this necessary?
6292 xmlWriter().writeCharacters(category);
6293 writeEndTag(); // </category>
6295 if (vrm.size() > 0) {
6296 writeStartTag(DT_prodinfo);
6297 if (!writeMetadataElement(inner,DT_prodname,false)) {
6298 writeStartTag(DT_prodname);
6299 xmlWriter().writeCharacters(projectDescription);
6300 writeEndTag(); // </prodname>
6302 writeStartTag(DT_vrmlist);
6303 writeStartTag(DT_vrm);
6305 xmlWriter().writeAttribute("version",vrm[0]);
6307 xmlWriter().writeAttribute("release",vrm[1]);
6309 xmlWriter().writeAttribute("modification",vrm[2]);
6310 writeEndTag(); // <vrm>
6311 writeEndTag(); // <vrmlist>
6312 if (!writeMetadataElement(inner,DT_component,false)) {
6313 QString component = inner->moduleName();
6314 if (!component.isEmpty()) {
6315 writeStartTag(DT_component);
6316 xmlWriter().writeCharacters(component);
6317 writeEndTag(); // </component>
6320 writeEndTag(); // </prodinfo>
6322 const QStringMultiMap& metaTagMap = inner->doc().metaTagMap();
6323 QMapIterator<QString, QString> i(metaTagMap);
6324 while (i.hasNext()) {
6326 writeStartTag(DT_othermeta);
6327 xmlWriter().writeAttribute("name",i.key());
6328 xmlWriter().writeAttribute("content",i.value());
6329 writeEndTag(); // </othermeta>
6331 if ((tagStack.first() == DT_cxxClass && !inner->includes().isEmpty()) ||
6332 (inner->type() == Node::Fake && inner->subType() == Node::HeaderFile)) {
6333 writeStartTag(DT_othermeta);
6334 xmlWriter().writeAttribute("name","includeFile");
6336 QStringList::ConstIterator i = inner->includes().constBegin();
6337 while (i != inner->includes().constEnd()) {
6338 if ((*i).startsWith(QLatin1Char('<')) && (*i).endsWith(QLatin1Char('>')))
6341 text += QLatin1Char('<') + *i + QLatin1Char('>');
6343 if (i != inner->includes().constEnd())
6346 xmlWriter().writeAttribute("content",text);
6347 writeEndTag(); // </othermeta>
6349 writeEndTag(); // </metadata>
6350 writeEndTag(); // </prolog>
6354 This function should be called to write the \a href attribute
6355 if the href could be an \e http or \e ftp link. If \a href is
6356 one or the other, a \e scope attribute is also writen, with
6359 void DitaXmlGenerator::writeHrefAttribute(const QString& href)
6361 xmlWriter().writeAttribute("href", href);
6362 if (href.startsWith("http:") || href.startsWith("ftp:") ||
6363 href.startsWith("https:") || href.startsWith("mailto:"))
6364 xmlWriter().writeAttribute("scope", "external");
6368 Strips the markup tags from \a src, when we are trying to
6369 create an \e{id} attribute. Returns the stripped text.
6371 QString DitaXmlGenerator::stripMarkup(const QString& src) const
6374 const QChar charAt = '@';
6375 const QChar charSlash = '/';
6376 const QChar charLangle = '<';
6377 const QChar charRangle = '>';
6382 if (src.at(i) == charLangle) {
6384 if (src.at(i) == charAt || (src.at(i) == charSlash && src.at(i+1) == charAt)) {
6385 while (i < n && src.at(i) != charRangle)
6394 text += src.at(i++);
6400 We delayed generation of the collision pages until now, after
6401 all the other pages have been generated. We do this because we might
6402 encounter a link command that tries to link to a target on a QML
6403 type page, but the link doesn't specify the module identifer
6404 for the QML type, and the QML type name without a module
6405 identifier is ambiguous. When such a link is found, qdoc can't find
6406 the target, so it appends the target to the NameCollisionNode. After
6407 the tree has been traversed and all these ambiguous links have been
6408 added to the name collision nodes, this function is called. The list
6409 of collision nodes is traversed here, and the collision page for
6410 each collision is generated. The collision page will not only
6411 disambiguate links to the QML type pages, but it will also disambiguate
6412 links to properties, section headers, etc.
6414 void DitaXmlGenerator::generateCollisionPages()
6416 if (collisionNodes.isEmpty())
6419 for (int i=0; i<collisionNodes.size(); ++i) {
6420 NameCollisionNode* ncn = collisionNodes.at(i);
6424 NodeList collisions;
6425 const NodeList& nl = ncn->childNodes();
6426 if (!nl.isEmpty()) {
6427 NodeList::ConstIterator it = nl.constBegin();
6428 while (it != nl.constEnd()) {
6429 if (!(*it)->isInternal())
6430 collisions.append(*it);
6434 if (collisions.size() <= 1)
6437 ncn->clearCurrentChild();
6438 beginSubPage(ncn, Generator::fileName(ncn));
6439 QString fullTitle = ncn->fullTitle();
6440 QString ditaTitle = fullTitle;
6441 CodeMarker* marker = CodeMarker::markerForFileName(ncn->location().filePath());
6442 if (ncn->isQmlNode()) {
6443 // Replace the marker with a QML code marker.
6444 if (ncn->isQmlNode())
6445 marker = CodeMarker::markerForLanguage(QLatin1String("QML"));
6448 generateHeader(ncn, ditaTitle);
6450 writeStartTag(DT_body);
6451 enterSection(QString(), QString());
6454 for (int i=0; i<collisions.size(); ++i) {
6455 Node* n = collisions.at(i);
6457 if (!n->qmlModuleIdentifier().isEmpty())
6458 t = n->qmlModuleIdentifier() + QLatin1Char(' ');
6459 t += protectEnc(fullTitle);
6460 nm.insertMulti(t,n);
6462 generateAnnotatedList(ncn, marker, nm);
6464 QList<QString> targets;
6465 if (!ncn->linkTargets().isEmpty()) {
6466 QMap<QString,QString>::ConstIterator t = ncn->linkTargets().constBegin();
6467 while (t != ncn->linkTargets().constEnd()) {
6469 for (int i=0; i<collisions.size(); ++i) {
6470 InnerNode* n = static_cast<InnerNode*>(collisions.at(i));
6471 if (n->findChildNodeByName(t.key())) {
6474 targets.append(t.key());
6482 if (!targets.isEmpty()) {
6483 QList<QString>::ConstIterator t = targets.constBegin();
6484 while (t != targets.constEnd()) {
6485 writeStartTag(DT_p);
6486 writeGuidAttribute(Doc::canonicalTitle(*t));
6487 xmlWriter().writeAttribute("outputclass","h2");
6488 writeCharacters(protectEnc(*t));
6489 writeEndTag(); // </p>
6490 writeStartTag(DT_ul);
6491 for (int i=0; i<collisions.size(); ++i) {
6492 InnerNode* n = static_cast<InnerNode*>(collisions.at(i));
6493 Node* p = n->findChildNodeByName(*t);
6495 QString link = linkForNode(p,0);
6497 if (!n->qmlModuleIdentifier().isEmpty())
6498 label = n->qmlModuleIdentifier() + "::";
6499 label += n->name() + "::" + p->name();
6500 writeStartTag(DT_li);
6501 writeStartTag(DT_xref);
6502 xmlWriter().writeAttribute("href", link);
6503 writeCharacters(protectEnc(label));
6504 writeEndTag(); // </xref>
6505 writeEndTag(); // </li>
6508 writeEndTag(); // </ul>
6512 leaveSection(); // </section>
6513 writeEndTag(); // </body>