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[] =
95 "cxxClassAccessSpecifier",
96 "cxxClassAPIItemLocation",
98 "cxxClassDeclarationFile",
99 "cxxClassDeclarationFileLine",
100 "cxxClassDeclarationFileLineStart",
101 "cxxClassDeclarationFileLineEnd",
102 "cxxClassDefinition",
103 "cxxClassDerivation",
104 "cxxClassDerivationAccessSpecifier",
105 "cxxClassDerivations",
108 "cxxClassNestedClass",
109 "cxxClassNestedDetail",
111 "cxxDefineAccessSpecifier",
112 "cxxDefineAPIItemLocation",
113 "cxxDefineDeclarationFile",
114 "cxxDefineDeclarationFileLine",
115 "cxxDefineDefinition",
117 "cxxDefineNameLookup",
118 "cxxDefineParameter",
119 "cxxDefineParameterDeclarationName",
120 "cxxDefineParameters",
121 "cxxDefinePrototype",
122 "cxxDefineReimplemented",
124 "cxxEnumerationAccessSpecifier",
125 "cxxEnumerationAPIItemLocation",
126 "cxxEnumerationDeclarationFile",
127 "cxxEnumerationDeclarationFileLine",
128 "cxxEnumerationDeclarationFileLineStart",
129 "cxxEnumerationDeclarationFileLineEnd",
130 "cxxEnumerationDefinition",
131 "cxxEnumerationDetail",
132 "cxxEnumerationNameLookup",
133 "cxxEnumerationPrototype",
134 "cxxEnumerationScopedName",
136 "cxxEnumeratorInitialiser",
137 "cxxEnumeratorNameLookup",
138 "cxxEnumeratorPrototype",
140 "cxxEnumeratorScopedName",
142 "cxxFunctionAccessSpecifier",
143 "cxxFunctionAPIItemLocation",
145 "cxxFunctionConstructor",
146 "cxxFunctionDeclarationFile",
147 "cxxFunctionDeclarationFileLine",
148 "cxxFunctionDeclaredType",
149 "cxxFunctionDefinition",
150 "cxxFunctionDestructor",
152 "cxxFunctionNameLookup",
153 "cxxFunctionParameter",
154 "cxxFunctionParameterDeclarationName",
155 "cxxFunctionParameterDeclaredType",
156 "cxxFunctionParameterDefaultValue",
157 "cxxFunctionParameters",
158 "cxxFunctionPrototype",
159 "cxxFunctionPureVirtual",
160 "cxxFunctionReimplemented",
161 "cxxFunctionScopedName",
162 "cxxFunctionStorageClassSpecifierStatic",
163 "cxxFunctionVirtual",
165 "cxxTypedefAccessSpecifier",
166 "cxxTypedefAPIItemLocation",
167 "cxxTypedefDeclarationFile",
168 "cxxTypedefDeclarationFileLine",
169 "cxxTypedefDefinition",
171 "cxxTypedefNameLookup",
172 "cxxTypedefScopedName",
174 "cxxVariableAccessSpecifier",
175 "cxxVariableAPIItemLocation",
176 "cxxVariableDeclarationFile",
177 "cxxVariableDeclarationFileLine",
178 "cxxVariableDeclaredType",
179 "cxxVariableDefinition",
181 "cxxVariableNameLookup",
182 "cxxVariablePrototype",
183 "cxxVariableReimplemented",
184 "cxxVariableScopedName",
185 "cxxVariableStorageClassSpecifierStatic",
251 static bool showBrokenLinks = false;
254 Quick, dirty, and very ugly. Unescape \a text
255 so QXmlStreamWriter::writeCharacters() can put
256 the escapes back in again!
258 void DitaXmlGenerator::writeCharacters(const QString& text)
261 t = t.replace("<","<");
262 t = t.replace(">",">");
263 t = t.replace("&","&");
264 t = t.replace(""","\"");
265 xmlWriter().writeCharacters(t);
269 Appends an <xref> element to the current XML stream
270 with the \a href attribute and the \a text.
272 void DitaXmlGenerator::addLink(const QString& href,
273 const QStringRef& text,
276 if (!href.isEmpty()) {
279 writeHrefAttribute(href);
280 writeCharacters(text.toString());
281 writeEndTag(); // </t>
284 writeCharacters(text.toString());
289 Push \a t onto the dita tag stack and write the appropriate
290 start tag to the DITA XML file.
292 void DitaXmlGenerator::writeStartTag(DitaTag t)
294 xmlWriter().writeStartElement(ditaTags[t]);
299 Pop the current DITA tag off the stack, and write the
300 appropriate end tag to the DITA XML file. If \a t is
301 not \e DT_NONE (default), then \a t contains the enum
302 value of the tag that should be on top of the stack.
304 If the stack is empty, no end tag is written and false
305 is returned. Otherwise, an end tag is written and true
308 bool DitaXmlGenerator::writeEndTag(DitaTag t)
310 if (tagStack.isEmpty())
312 DitaTag top = tagStack.pop();
313 if (t > DT_NONE && top != t)
314 qDebug() << "Expected:" << t << "ACTUAL:" << top;
315 xmlWriter().writeEndElement();
320 Return the current DITA element tag, the one
323 DitaXmlGenerator::DitaTag DitaXmlGenerator::currentTag()
325 return tagStack.top();
329 Write the start tag \c{<apiDesc>}. if \a title is not
330 empty, generate a GUID from it and write the GUID as the
331 value of the \e{id} attribute.
333 Then if \a outputclass is not empty, write it as the value
334 of the \a outputclass attribute.
336 Fiunally, set the section nesting level to 1 and return 1.
338 int DitaXmlGenerator::enterApiDesc(const QString& outputclass, const QString& title)
340 writeStartTag(DT_apiDesc);
341 if (!title.isEmpty()) {
342 writeGuidAttribute(title);
343 //Are there cases where the spectitle is required?
344 //xmlWriter().writeAttribute("spectitle",title);
346 if (!outputclass.isEmpty())
347 xmlWriter().writeAttribute("outputclass",outputclass);
348 sectionNestingLevel = 1;
349 return sectionNestingLevel;
353 If the section nesting level is 0, output a \c{<section>}
354 element with an \e id attribute generated from \a title and
355 an \e outputclass attribute set to \a outputclass.
356 If \a title is null, no \e id attribute is output.
357 If \a outputclass is empty, no \e outputclass attribute
360 Finally, increment the section nesting level and return
363 int DitaXmlGenerator::enterSection(const QString& outputclass, const QString& title)
365 if (sectionNestingLevel == 0) {
366 writeStartTag(DT_section);
367 if (!title.isEmpty())
368 writeGuidAttribute(title);
369 if (!outputclass.isEmpty())
370 xmlWriter().writeAttribute("outputclass",outputclass);
372 else if (!title.isEmpty()) {
374 writeGuidAttribute(title);
375 if (!outputclass.isEmpty())
376 xmlWriter().writeAttribute("outputclass",outputclass);
377 writeCharacters(title);
378 writeEndTag(); // </p>
380 return ++sectionNestingLevel;
384 If the section nesting level is greater than 0, decrement
385 it. If it becomes 0, output a \c {</section>}. Return the
386 decremented section nesting level.
388 int DitaXmlGenerator::leaveSection()
390 if (sectionNestingLevel > 0) {
391 --sectionNestingLevel;
392 if (sectionNestingLevel == 0)
393 writeEndTag(); // </section> or </apiDesc>
395 return sectionNestingLevel;
399 The default constructor.
401 DitaXmlGenerator::DitaXmlGenerator()
403 inDetailedDescription(false),
404 inLegaleseText(false),
406 inObsoleteLink(false),
407 inSectionHeading(false),
408 inTableHeader(false),
411 obsoleteLinks(false),
413 threeColumnEnumValueTable(true),
417 sectionNestingLevel(0),
419 funcLeftParen("\\S(\\()"),
421 nodeTypeMaps(Node::LastType,0),
422 nodeSubtypeMaps(Node::LastSubtype,0),
423 pageTypeMaps(Node::OnBeyondZebra,0)
429 The destructor has nothing to do.
431 DitaXmlGenerator::~DitaXmlGenerator()
433 GuidMaps::iterator i = guidMaps.begin();
434 while (i != guidMaps.end()) {
441 A lot of internal structures are initialized.
443 void DitaXmlGenerator::initializeGenerator(const Config &config)
445 Generator::initializeGenerator(config);
446 obsoleteLinks = config.getBool(QLatin1String(CONFIG_OBSOLETELINKS));
447 setImageFileExtensions(QStringList() << "png" << "jpg" << "jpeg" << "gif");
449 style = config.getString(DitaXmlGenerator::format() +
451 DITAXMLGENERATOR_STYLE);
452 postHeader = config.getString(DitaXmlGenerator::format() +
454 DITAXMLGENERATOR_POSTHEADER);
455 postPostHeader = config.getString(DitaXmlGenerator::format() +
457 DITAXMLGENERATOR_POSTPOSTHEADER);
458 footer = config.getString(DitaXmlGenerator::format() +
460 DITAXMLGENERATOR_FOOTER);
461 address = config.getString(DitaXmlGenerator::format() +
463 DITAXMLGENERATOR_ADDRESS);
464 pleaseGenerateMacRef = config.getBool(DitaXmlGenerator::format() +
466 DITAXMLGENERATOR_GENERATEMACREFS);
468 project = config.getString(CONFIG_PROJECT);
469 projectDescription = config.getString(CONFIG_DESCRIPTION);
470 if (projectDescription.isEmpty() && !project.isEmpty())
471 projectDescription = project + " Reference Documentation";
473 projectUrl = config.getString(CONFIG_URL);
475 outputEncoding = config.getString(CONFIG_OUTPUTENCODING);
476 if (outputEncoding.isEmpty())
477 outputEncoding = QLatin1String("ISO-8859-1");
478 outputCodec = QTextCodec::codecForName(outputEncoding.toLocal8Bit());
480 naturalLanguage = config.getString(CONFIG_NATURALLANGUAGE);
481 if (naturalLanguage.isEmpty())
482 naturalLanguage = QLatin1String("en");
484 config.subVarsAndValues("dita.metadata.default",metadataDefaults);
485 QSet<QString> editionNames = config.subVars(CONFIG_EDITION);
486 QSet<QString>::ConstIterator edition = editionNames.begin();
487 while (edition != editionNames.end()) {
488 QString editionName = *edition;
489 QStringList editionModules = config.getStringList(CONFIG_EDITION +
494 QStringList editionGroups = config.getStringList(CONFIG_EDITION +
500 if (!editionModules.isEmpty())
501 editionModuleMap[editionName] = editionModules;
502 if (!editionGroups.isEmpty())
503 editionGroupMap[editionName] = editionGroups;
508 stylesheets = config.getStringList(DitaXmlGenerator::format() +
510 DITAXMLGENERATOR_STYLESHEETS);
511 customHeadElements = config.getStringList(DitaXmlGenerator::format() +
513 DITAXMLGENERATOR_CUSTOMHEADELEMENTS);
514 codeIndent = config.getInt(CONFIG_CODEINDENT);
515 version = config.getString(CONFIG_VERSION);
516 vrm = version.split(".");
520 All this does is call the same function in the base class.
522 void DitaXmlGenerator::terminateGenerator()
524 Generator::terminateGenerator();
530 QString DitaXmlGenerator::format()
536 Calls lookupGuid() to get a GUID for \a text, then writes
537 it to the XML stream as an "id" attribute, and returns it.
539 QString DitaXmlGenerator::writeGuidAttribute(QString text)
541 QString guid = lookupGuid(outFileName(),text);
542 xmlWriter().writeAttribute("id",guid);
548 Write's the GUID for the \a node to the current XML stream
549 as an "id" attribute. If the \a node doesn't yet have a GUID,
552 void DitaXmlGenerator::writeGuidAttribute(Node* node)
554 xmlWriter().writeAttribute("id",node->guid());
558 Looks up \a text in the GUID map. If it finds \a text,
559 it returns the associated GUID. Otherwise it inserts
560 \a text into the map with a new GUID, and it returns
563 QString DitaXmlGenerator::lookupGuid(QString text)
565 QMap<QString, QString>::const_iterator i = name2guidMap.find(text);
566 if (i != name2guidMap.end())
569 QString t = QUuid::createUuid().toString();
570 QString guid = "id-" + t.mid(1,t.length()-2);
572 QString guid = Node::cleanId(text);
573 name2guidMap.insert(text,guid);
578 First, look up the GUID map for \a fileName. If there isn't
579 a GUID map for \a fileName, create one and insert it into
580 the map of GUID maps. Then look up \a text in that GUID map.
581 If \a text is found, return the associated GUID. Otherwise,
582 insert \a text into the GUID map with a new GUID, and return
585 QString DitaXmlGenerator::lookupGuid(const QString& fileName, const QString& text)
587 GuidMap* gm = lookupGuidMap(fileName);
588 GuidMap::const_iterator i = gm->find(text);
592 QString t = QUuid::createUuid().toString();
593 QString guid = "id-" + t.mid(1,t.length()-2);
595 QString guid = Node::cleanId(text);
596 gm->insert(text,guid);
601 Looks up \a fileName in the map of GUID maps. If it finds
602 \a fileName, it returns a pointer to the associated GUID
603 map. Otherwise it creates a new GUID map and inserts it
604 into the map of GUID maps with \a fileName as its key.
606 GuidMap* DitaXmlGenerator::lookupGuidMap(const QString& fileName)
608 GuidMaps::const_iterator i = guidMaps.find(fileName);
609 if (i != guidMaps.end())
611 GuidMap* gm = new GuidMap;
612 guidMaps.insert(fileName,gm);
617 This is where the DITA XML files are written.
618 \note The file is created in PageGenerator::generateTree().
620 void DitaXmlGenerator::generateTree(const Tree *tree)
623 nonCompatClasses.clear();
625 compatClasses.clear();
626 obsoleteClasses.clear();
627 moduleClassMap.clear();
628 moduleNamespaceMap.clear();
630 legaleseTexts.clear();
631 serviceClasses.clear();
633 findAllClasses(tree->root());
634 findAllFunctions(tree->root());
635 findAllLegaleseTexts(tree->root());
636 findAllNamespaces(tree->root());
637 findAllSince(tree->root());
639 Generator::generateTree(tree);
643 void DitaXmlGenerator::startText(const Node* /* relative */,
644 CodeMarker* /* marker */)
648 inSectionHeading = false;
649 inTableHeader = false;
651 threeColumnEnumValueTable = true;
653 sectionNumber.clear();
656 static int countTableColumns(const Atom* t)
659 if (t->type() == Atom::TableHeaderLeft) {
660 while (t->type() == Atom::TableHeaderLeft) {
663 while (t->type() != Atom::TableHeaderRight) {
664 if (t->type() == Atom::TableItemLeft)
673 else if (t->type() == Atom::TableRowLeft) {
674 while (t->type() != Atom::TableRowRight) {
675 if (t->type() == Atom::TableItemLeft)
684 Generate html from an instance of Atom.
686 int DitaXmlGenerator::generateAtom(const Atom *atom,
687 const Node *relative,
692 static bool in_para = false;
693 QString guid, hc, attr;
695 switch (atom->type()) {
696 case Atom::AbstractLeft:
698 case Atom::AbstractRight:
701 if (!noLinks && !inLink && !inContents && !inSectionHeading) {
702 const Node* node = 0;
703 QString link = getLink(atom, relative, marker, &node);
704 if (!link.isEmpty()) {
706 generateLink(atom, relative, marker);
710 writeCharacters(protectEnc(atom->string()));
714 writeCharacters(protectEnc(atom->string()));
719 case Atom::BriefLeft:
720 //if (relative->type() == Node::Fake) {
721 //skipAhead = skipAtoms(atom, Atom::BriefRight);
726 xmlWriter().writeAttribute("outputclass","brief");
730 writeStartTag(DT_shortdesc);
732 if (relative->type() == Node::Property ||
733 relative->type() == Node::Variable) {
734 xmlWriter().writeCharacters("This ");
735 if (relative->type() == Node::Property)
736 xmlWriter().writeCharacters("property");
737 else if (relative->type() == Node::Variable)
738 xmlWriter().writeCharacters("variable");
739 xmlWriter().writeCharacters(" holds ");
743 while (atom != 0 && atom->type() != Atom::BriefRight) {
744 if (atom->type() == Atom::String ||
745 atom->type() == Atom::AutoLink)
746 str += atom->string();
750 str[0] = str[0].toLower();
751 if (str.endsWith(QLatin1Char('.')))
752 str.truncate(str.length() - 1);
753 writeCharacters(str + QLatin1Char('.'));
756 case Atom::BriefRight:
757 // if (relative->type() != Node::Fake)
758 writeEndTag(); // </shortdesc> or </p>
762 writeStartTag(DT_tt);
764 writeCharacters(protectEnc(plainCode(atom->string())));
767 writeText(atom->string(), marker, relative);
769 writeEndTag(); // see writeStartElement() above
773 writeStartTag(DT_codeblock);
774 xmlWriter().writeAttribute("outputclass","cpp");
775 QString chars = trimmedTrailing(atom->string());
776 writeText(chars, marker, relative);
777 writeEndTag(); // </codeblock>
781 writeStartTag(DT_codeblock);
782 xmlWriter().writeAttribute("outputclass","qml");
783 writeText(trimmedTrailing(atom->string()), marker, relative);
784 writeEndTag(); // </codeblock>
788 xmlWriter().writeCharacters("you can rewrite it as");
789 writeEndTag(); // </p>
790 writeStartTag(DT_codeblock);
791 writeText(trimmedTrailing(atom->string()), marker, relative);
792 writeEndTag(); // </codeblock>
796 xmlWriter().writeCharacters("For example, if you have code like");
797 writeEndTag(); // </p>
800 writeStartTag(DT_codeblock);
801 writeCharacters(trimmedTrailing(plainCode(atom->string())));
802 writeEndTag(); // </codeblock>
806 bool inStartElement = false;
807 attr = atom->string();
808 DitaTag t = currentTag();
809 if ((t == DT_section) || (t == DT_sectiondiv)) {
810 writeStartTag(DT_sectiondiv);
812 inStartElement = true;
814 else if ((t == DT_body) || (t == DT_bodydiv)) {
815 writeStartTag(DT_bodydiv);
817 inStartElement = true;
819 if (!attr.isEmpty()) {
820 if (attr.contains('=')) {
825 index = attr.indexOf('"',from);
829 index = attr.indexOf('"',from);
831 if (!values.isEmpty())
833 values += attr.mid(from,index-from);
842 xmlWriter().writeAttribute("outputclass", attr);
846 if ((currentTag() == DT_sectiondiv) || (currentTag() == DT_bodydiv)) {
847 writeEndTag(); // </sectiondiv>, </bodydiv>, or </p>
848 if (divNestingLevel > 0)
852 case Atom::FootnoteLeft:
855 writeEndTag(); // </p>
858 xmlWriter().writeCharacters("<!-- ");
860 case Atom::FootnoteRight:
862 xmlWriter().writeCharacters("-->");
864 case Atom::FormatElse:
865 case Atom::FormatEndif:
868 case Atom::FormattingLeft:
871 if (atom->string() == ATOM_FORMATTING_BOLD)
873 else if (atom->string() == ATOM_FORMATTING_PARAMETER)
875 else if (atom->string() == ATOM_FORMATTING_ITALIC)
877 else if (atom->string() == ATOM_FORMATTING_TELETYPE)
879 else if (atom->string().startsWith("span ")) {
882 else if (atom->string() == ATOM_FORMATTING_UNDERLINE)
884 else if (atom->string() == ATOM_FORMATTING_INDEX)
886 else if (atom->string() == ATOM_FORMATTING_SUBSCRIPT)
888 else if (atom->string() == ATOM_FORMATTING_SUPERSCRIPT)
891 qDebug() << "DT_LAST";
893 if (atom->string() == ATOM_FORMATTING_PARAMETER) {
894 if (atom->next() != 0 && atom->next()->type() == Atom::String) {
895 QRegExp subscriptRegExp("([a-z]+)_([0-9n])");
896 if (subscriptRegExp.exactMatch(atom->next()->string())) {
897 xmlWriter().writeCharacters(subscriptRegExp.cap(1));
898 writeStartTag(DT_sub);
899 xmlWriter().writeCharacters(subscriptRegExp.cap(2));
900 writeEndTag(); // </sub>
905 else if (t == DT_keyword) {
906 QString attr = atom->string().mid(5);
907 if (!attr.isEmpty()) {
908 if (attr.contains('=')) {
913 index = attr.indexOf('"',from);
917 index = attr.indexOf('"',from);
919 if (!values.isEmpty())
921 values += attr.mid(from,index-from);
929 xmlWriter().writeAttribute("outputclass", attr);
933 case Atom::FormattingRight:
934 if (atom->string() == ATOM_FORMATTING_LINK) {
941 case Atom::AnnotatedList:
943 QList<Node*> values = tree_->groups().values(atom->string());
945 for (int i = 0; i < values.size(); ++i) {
946 const Node* n = values.at(i);
947 if ((n->status() != Node::Internal) && (n->access() != Node::Private)) {
948 nodeMap.insert(n->nameForLists(),n);
951 generateAnnotatedList(relative, marker, nodeMap);
954 case Atom::GeneratedList:
955 if (atom->string() == "annotatedclasses") {
956 generateAnnotatedList(relative, marker, nonCompatClasses);
958 else if (atom->string() == "classes") {
959 generateCompactList(relative, marker, nonCompatClasses, true);
961 else if (atom->string() == "qmlclasses") {
962 generateCompactList(relative, marker, qmlClasses, true);
964 else if (atom->string().contains("classesbymodule")) {
965 QString arg = atom->string().trimmed();
966 QString moduleName = atom->string().mid(atom->string().indexOf(
967 "classesbymodule") + 15).trimmed();
968 if (moduleClassMap.contains(moduleName))
969 generateAnnotatedList(relative, marker, moduleClassMap[moduleName]);
971 else if (atom->string().contains("classesbyedition")) {
973 QString arg = atom->string().trimmed();
974 QString editionName = atom->string().mid(atom->string().indexOf(
975 "classesbyedition") + 16).trimmed();
977 if (editionModuleMap.contains(editionName)) {
979 // Add all classes in the modules listed for that edition.
980 NodeMap editionClasses;
981 foreach (const QString &moduleName, editionModuleMap[editionName]) {
982 if (moduleClassMap.contains(moduleName))
983 editionClasses.unite(moduleClassMap[moduleName]);
986 // Add additional groups and remove groups of classes that
987 // should be excluded from the edition.
989 QMultiMap <QString, Node *> groups = tree_->groups();
990 foreach (const QString &groupName, editionGroupMap[editionName]) {
991 QList<Node *> groupClasses;
992 if (groupName.startsWith(QLatin1Char('-'))) {
993 groupClasses = groups.values(groupName.mid(1));
994 foreach (const Node *node, groupClasses)
995 editionClasses.remove(node->name());
998 groupClasses = groups.values(groupName);
999 foreach (const Node *node, groupClasses)
1000 editionClasses.insert(node->name(), node);
1003 generateAnnotatedList(relative, marker, editionClasses);
1006 else if (atom->string() == "classhierarchy") {
1007 generateClassHierarchy(relative, marker, nonCompatClasses);
1009 else if (atom->string() == "compatclasses") {
1010 generateCompactList(relative, marker, compatClasses, false);
1012 else if (atom->string() == "obsoleteclasses") {
1013 generateCompactList(relative, marker, obsoleteClasses, false);
1015 else if (atom->string() == "functionindex") {
1016 generateFunctionIndex(relative, marker);
1018 else if (atom->string() == "legalese") {
1019 generateLegaleseList(relative, marker);
1021 else if (atom->string() == "mainclasses") {
1022 generateCompactList(relative, marker, mainClasses, true);
1024 else if (atom->string() == "services") {
1025 generateCompactList(relative, marker, serviceClasses, false);
1027 else if (atom->string() == "overviews") {
1028 generateOverviewList(relative, marker);
1030 else if (atom->string() == "namespaces") {
1031 generateAnnotatedList(relative, marker, namespaceIndex);
1033 else if (atom->string() == "related") {
1034 const FakeNode *fake = static_cast<const FakeNode *>(relative);
1035 if (fake && !fake->groupMembers().isEmpty()) {
1036 NodeMap groupMembersMap;
1037 foreach (const Node *node, fake->groupMembers()) {
1038 if (node->type() == Node::Fake)
1039 groupMembersMap[fullName(node, relative, marker)] = node;
1041 generateAnnotatedList(fake, marker, groupMembersMap);
1045 case Atom::SinceList:
1047 NewSinceMaps::const_iterator nsmap;
1048 nsmap = newSinceMaps.find(atom->string());
1049 NewClassMaps::const_iterator ncmap;
1050 ncmap = newClassMaps.find(atom->string());
1051 NewClassMaps::const_iterator nqcmap;
1052 nqcmap = newQmlClassMaps.find(atom->string());
1053 if ((nsmap != newSinceMaps.constEnd()) && !nsmap.value().isEmpty()) {
1054 QList<Section> sections;
1055 QList<Section>::ConstIterator s;
1056 for (int i=0; i<LastSinceType; ++i)
1057 sections.append(Section(sinceTitle(i),QString(),QString(),QString()));
1059 NodeMultiMap::const_iterator n = nsmap.value().constBegin();
1060 while (n != nsmap.value().constEnd()) {
1061 const Node* node = n.value();
1062 switch (node->type()) {
1064 if (node->subType() == Node::QmlClass) {
1065 sections[QmlClass].appendMember((Node*)node);
1068 case Node::Namespace:
1069 sections[Namespace].appendMember((Node*)node);
1072 sections[Class].appendMember((Node*)node);
1075 sections[Enum].appendMember((Node*)node);
1078 sections[Typedef].appendMember((Node*)node);
1080 case Node::Function: {
1081 const FunctionNode* fn = static_cast<const FunctionNode*>(node);
1083 sections[Macro].appendMember((Node*)node);
1085 Node* p = fn->parent();
1087 if (p->type() == Node::Class)
1088 sections[MemberFunction].appendMember((Node*)node);
1089 else if (p->type() == Node::Namespace) {
1090 if (p->name().isEmpty())
1091 sections[GlobalFunction].appendMember((Node*)node);
1093 sections[NamespaceFunction].appendMember((Node*)node);
1096 sections[GlobalFunction].appendMember((Node*)node);
1099 sections[GlobalFunction].appendMember((Node*)node);
1103 case Node::Property:
1104 sections[Property].appendMember((Node*)node);
1106 case Node::Variable:
1107 sections[Variable].appendMember((Node*)node);
1109 case Node::QmlProperty:
1110 sections[QmlProperty].appendMember((Node*)node);
1112 case Node::QmlSignal:
1113 sections[QmlSignal].appendMember((Node*)node);
1115 case Node::QmlSignalHandler:
1116 sections[QmlSignalHandler].appendMember((Node*)node);
1118 case Node::QmlMethod:
1119 sections[QmlMethod].appendMember((Node*)node);
1128 First generate the table of contents.
1130 writeStartTag(DT_ul);
1131 s = sections.constBegin();
1132 while (s != sections.constEnd()) {
1133 if (!(*s).members.isEmpty()) {
1134 QString li = outFileName() + QLatin1Char('#') + Doc::canonicalTitle((*s).name);
1135 writeXrefListItem(li, (*s).name);
1139 writeEndTag(); // </ul>
1142 s = sections.constBegin();
1143 while (s != sections.constEnd()) {
1144 if (!(*s).members.isEmpty()) {
1145 writeStartTag(DT_p);
1146 writeGuidAttribute(Doc::canonicalTitle((*s).name));
1147 xmlWriter().writeAttribute("outputclass","h3");
1148 writeCharacters(protectEnc((*s).name));
1149 writeEndTag(); // </p>
1151 generateCompactList(0, marker, ncmap.value(), false, QString("Q"));
1152 else if (idx == QmlClass)
1153 generateCompactList(0, marker, nqcmap.value(), false, QString("Q"));
1154 else if (idx == MemberFunction) {
1155 ParentMaps parentmaps;
1156 ParentMaps::iterator pmap;
1157 NodeList::const_iterator i = s->members.constBegin();
1158 while (i != s->members.constEnd()) {
1159 Node* p = (*i)->parent();
1160 pmap = parentmaps.find(p);
1161 if (pmap == parentmaps.end())
1162 pmap = parentmaps.insert(p,NodeMultiMap());
1163 pmap->insert((*i)->name(),(*i));
1166 pmap = parentmaps.begin();
1167 while (pmap != parentmaps.end()) {
1168 NodeList nlist = pmap->values();
1169 writeStartTag(DT_p);
1170 xmlWriter().writeCharacters("Class ");
1171 writeStartTag(DT_xref);
1173 xmlWriter().writeAttribute("href",linkForNode(pmap.key(), 0));
1174 QStringList pieces = fullName(pmap.key(), 0, marker).split("::");
1175 writeCharacters(protectEnc(pieces.last()));
1176 writeEndTag(); // </xref>
1177 xmlWriter().writeCharacters(":");
1178 writeEndTag(); // </p>
1180 generateSection(nlist, 0, marker, CodeMarker::Summary);
1185 generateSection(s->members, 0, marker, CodeMarker::Summary);
1195 case Atom::InlineImage:
1197 QString fileName = imageFileName(relative, atom->string());
1199 if (atom->next() != 0)
1200 text = atom->next()->string();
1201 if (fileName.isEmpty()) {
1202 QString images = "images";
1203 if (!baseDir().isEmpty())
1204 images.prepend("../");
1205 if (atom->string()[0] != '/')
1206 images.append(QLatin1Char('/'));
1207 fileName = images + atom->string();
1209 if (relative && (relative->type() == Node::Fake) &&
1210 (relative->subType() == Node::Example)) {
1211 const ExampleNode* cen = static_cast<const ExampleNode*>(relative);
1212 if (cen->imageFileName().isEmpty()) {
1213 ExampleNode* en = const_cast<ExampleNode*>(cen);
1214 en->setImageFileName(fileName);
1218 if (currentTag() != DT_xref)
1219 writeStartTag(DT_fig);
1220 writeStartTag(DT_image);
1221 writeHrefAttribute(protectEnc(fileName));
1222 if (atom->type() == Atom::InlineImage)
1223 xmlWriter().writeAttribute("placement","inline");
1225 xmlWriter().writeAttribute("placement","break");
1226 xmlWriter().writeAttribute("align","center");
1228 if (!text.isEmpty()) {
1229 writeStartTag(DT_alt);
1230 writeCharacters(protectEnc(text));
1231 writeEndTag(); // </alt>
1233 writeEndTag(); // </image>
1234 if (currentTag() != DT_xref)
1235 writeEndTag(); // </fig>
1238 case Atom::ImageText:
1241 case Atom::ImportantLeft:
1242 writeStartTag(DT_note);
1243 xmlWriter().writeAttribute("type","important");
1245 case Atom::ImportantRight:
1246 writeEndTag(); // </note>
1248 case Atom::NoteLeft:
1249 writeStartTag(DT_note);
1250 xmlWriter().writeAttribute("type","note");
1252 case Atom::NoteRight:
1253 writeEndTag(); // </note>
1255 case Atom::LegaleseLeft:
1256 inLegaleseText = true;
1258 case Atom::LegaleseRight:
1259 inLegaleseText = false;
1261 case Atom::LineBreak:
1262 //xmlWriter().writeEmptyElement("br");
1266 const Node *node = 0;
1267 QString myLink = getLink(atom, relative, marker, &node);
1268 if (myLink.isEmpty()) {
1269 relative->doc().location().warning(tr("Can't link to '%1' in %2")
1270 .arg(atom->string())
1271 .arg(marker->plainFullName(relative)));
1273 else if (!inSectionHeading) {
1278 //xmlWriter().writeCharacters(atom->string());
1279 //qDebug() << "MYLINK:" << myLink << outFileName() << atom->string();
1285 case Atom::GuidLink:
1287 beginLink(atom->string());
1291 case Atom::LinkNode:
1293 const Node* node = CodeMarker::nodeForString(atom->string());
1294 beginLink(linkForNode(node, relative));
1298 case Atom::ListLeft:
1300 writeEndTag(); // </p>
1303 if (atom->string() == ATOM_LIST_BULLET) {
1304 writeStartTag(DT_ul);
1306 else if (atom->string() == ATOM_LIST_TAG) {
1307 writeStartTag(DT_dl);
1309 else if (atom->string() == ATOM_LIST_VALUE) {
1310 threeColumnEnumValueTable = isThreeColumnEnumValueTable(atom);
1311 if (threeColumnEnumValueTable) {
1312 writeStartTag(DT_simpletable);
1313 xmlWriter().writeAttribute("outputclass","valuelist");
1314 writeStartTag(DT_sthead);
1315 writeStartTag(DT_stentry);
1316 xmlWriter().writeCharacters("Constant");
1317 writeEndTag(); // </stentry>
1318 writeStartTag(DT_stentry);
1319 xmlWriter().writeCharacters("Value");
1320 writeEndTag(); // </stentry>
1321 writeStartTag(DT_stentry);
1322 xmlWriter().writeCharacters("Description");
1323 writeEndTag(); // </stentry>
1324 writeEndTag(); // </sthead>
1327 writeStartTag(DT_simpletable);
1328 xmlWriter().writeAttribute("outputclass","valuelist");
1329 writeStartTag(DT_sthead);
1330 writeStartTag(DT_stentry);
1331 xmlWriter().writeCharacters("Constant");
1332 writeEndTag(); // </stentry>
1333 writeStartTag(DT_stentry);
1334 xmlWriter().writeCharacters("Value");
1335 writeEndTag(); // </stentry>
1336 writeEndTag(); // </sthead>
1340 writeStartTag(DT_ol);
1341 if (atom->string() == ATOM_LIST_UPPERALPHA)
1342 xmlWriter().writeAttribute("outputclass","upperalpha");
1343 else if (atom->string() == ATOM_LIST_LOWERALPHA)
1344 xmlWriter().writeAttribute("outputclass","loweralpha");
1345 else if (atom->string() == ATOM_LIST_UPPERROMAN)
1346 xmlWriter().writeAttribute("outputclass","upperroman");
1347 else if (atom->string() == ATOM_LIST_LOWERROMAN)
1348 xmlWriter().writeAttribute("outputclass","lowerroman");
1349 else // (atom->string() == ATOM_LIST_NUMERIC)
1350 xmlWriter().writeAttribute("outputclass","numeric");
1351 if (atom->next() != 0 && atom->next()->string().toInt() != 1) {
1352 // I don't think this attribute is supported.
1353 xmlWriter().writeAttribute("start",atom->next()->string());
1357 case Atom::ListItemNumber:
1360 case Atom::ListTagLeft:
1361 if (atom->string() == ATOM_LIST_TAG) {
1362 writeStartTag(DT_dt);
1364 else { // (atom->string() == ATOM_LIST_VALUE)
1365 writeStartTag(DT_strow);
1366 writeStartTag(DT_stentry);
1367 writeStartTag(DT_tt);
1368 writeCharacters(protectEnc(plainCode(marker->markedUpEnumValue(atom->next()->string(),
1370 writeEndTag(); // </tt>
1371 writeEndTag(); // </stentry>
1372 writeStartTag(DT_stentry);
1375 if (relative->type() == Node::Enum) {
1376 const EnumNode *enume = static_cast<const EnumNode *>(relative);
1377 itemValue = enume->itemValue(atom->next()->string());
1380 if (itemValue.isEmpty())
1381 xmlWriter().writeCharacters("?");
1383 writeStartTag(DT_tt);
1384 writeCharacters(protectEnc(itemValue));
1385 writeEndTag(); // </tt>
1390 case Atom::ListTagRight:
1391 if (atom->string() == ATOM_LIST_TAG)
1392 writeEndTag(); // </dt>
1394 case Atom::ListItemLeft:
1395 if (atom->string() == ATOM_LIST_TAG) {
1396 writeStartTag(DT_dd);
1398 else if (atom->string() == ATOM_LIST_VALUE) {
1399 if (threeColumnEnumValueTable) {
1400 writeEndTag(); // </stentry>
1401 writeStartTag(DT_stentry);
1405 writeStartTag(DT_li);
1407 if (matchAhead(atom, Atom::ParaLeft))
1410 case Atom::ListItemRight:
1411 if (atom->string() == ATOM_LIST_TAG) {
1412 writeEndTag(); // </dd>
1414 else if (atom->string() == ATOM_LIST_VALUE) {
1415 writeEndTag(); // </stentry>
1416 writeEndTag(); // </strow>
1419 writeEndTag(); // </li>
1422 case Atom::ListRight:
1423 if (atom->string() == ATOM_LIST_BULLET) {
1424 writeEndTag(); // </ul>
1426 else if (atom->string() == ATOM_LIST_TAG) {
1427 writeEndTag(); // </dl>
1429 else if (atom->string() == ATOM_LIST_VALUE) {
1430 writeEndTag(); // </simpletable>
1433 writeEndTag(); // </ol>
1439 case Atom::ParaLeft:
1440 writeStartTag(DT_p);
1442 xmlWriter().writeAttribute("outputclass","legalese");
1445 case Atom::ParaRight:
1448 writeEndTag(); // </p>
1452 case Atom::QuotationLeft:
1453 writeStartTag(DT_lq);
1455 case Atom::QuotationRight:
1456 writeEndTag(); // </lq>
1458 case Atom::RawString:
1459 if (atom->string() == " ")
1461 if (atom->string().startsWith(QLatin1Char('&')))
1462 writeCharacters(atom->string());
1463 else if (atom->string() == "<sup>*</sup>") {
1464 writeStartTag(DT_sup);
1465 writeCharacters("*");
1466 writeEndTag(); // </sup>
1468 else if (atom->string() == "<sup>®</sup>") {
1469 writeStartTag(DT_tm);
1470 xmlWriter().writeAttribute("tmtype","reg");
1471 writeEndTag(); // </tm>
1474 writeStartTag(DT_pre);
1475 xmlWriter().writeAttribute("outputclass","raw-html");
1476 writeCharacters(atom->string());
1477 writeEndTag(); // </pre>
1480 case Atom::SectionLeft:
1483 writeEndTag(); // </apiDesc>
1487 enterSection("details",QString());
1488 //writeGuidAttribute(Doc::canonicalTitle(Text::sectionHeading(atom).toString()));
1490 case Atom::SectionRight:
1493 case Atom::SectionHeadingLeft:
1495 writeStartTag(DT_p);
1496 QString id = Text::sectionHeading(atom).toString();
1497 id = stripMarkup(id);
1498 id = Doc::canonicalTitle(id);
1499 writeGuidAttribute(id);
1500 hx = QLatin1Char('h') + QString::number(atom->string().toInt() + hOffset(relative));
1501 xmlWriter().writeAttribute("outputclass",hx);
1502 inSectionHeading = true;
1505 case Atom::SectionHeadingRight:
1506 writeEndTag(); // </title> (see case Atom::SectionHeadingLeft)
1507 inSectionHeading = false;
1509 case Atom::SidebarLeft:
1512 case Atom::SidebarRight:
1516 if (inLink && !inContents && !inSectionHeading) {
1517 generateLink(atom, relative, marker);
1520 writeCharacters(atom->string());
1523 case Atom::TableLeft:
1526 if ((atom->count() > 0) && (atom->string(0) == "borderless"))
1527 attr = "borderless";
1528 else if ((atom->count() > 1) && (atom->string(1) == "borderless"))
1529 attr = "borderless";
1531 writeEndTag(); // </p>
1534 writeStartTag(DT_table);
1535 if (!attr.isEmpty())
1536 xmlWriter().writeAttribute("outputclass",attr);
1538 if (tableColumnCount != 0) {
1539 qDebug() << "ERROR: Nested tables!";
1540 tableColumnCount = 0;
1542 tableColumnCount = countTableColumns(atom->next());
1543 writeStartTag(DT_tgroup);
1544 xmlWriter().writeAttribute("cols",QString::number(tableColumnCount));
1545 inTableHeader = false;
1546 inTableBody = false;
1549 case Atom::TableRight:
1550 writeEndTag(); // </tbody>
1551 writeEndTag(); // </tgroup>
1552 writeEndTag(); // </table>
1553 inTableHeader = false;
1554 inTableBody = false;
1555 tableColumnCount = 0;
1557 case Atom::TableHeaderLeft:
1559 writeEndTag(); // </tbody>
1560 writeEndTag(); // </tgroup>
1561 writeEndTag(); // </table>
1562 inTableHeader = false;
1563 inTableBody = false;
1564 tableColumnCount = 0;
1565 writeStartTag(DT_table);
1567 tableColumnCount = countTableColumns(atom);
1568 writeStartTag(DT_tgroup);
1569 xmlWriter().writeAttribute("cols",QString::number(tableColumnCount));
1571 writeStartTag(DT_thead);
1572 xmlWriter().writeAttribute("valign","top");
1573 writeStartTag(DT_row);
1574 xmlWriter().writeAttribute("valign","top");
1575 inTableHeader = true;
1576 inTableBody = false;
1578 case Atom::TableHeaderRight:
1579 writeEndTag(); // </row>
1580 if (matchAhead(atom, Atom::TableHeaderLeft)) {
1582 writeStartTag(DT_row);
1583 xmlWriter().writeAttribute("valign","top");
1586 writeEndTag(); // </thead>
1587 inTableHeader = false;
1589 writeStartTag(DT_tbody);
1592 case Atom::TableRowLeft:
1593 if (!inTableHeader && !inTableBody) {
1595 writeStartTag(DT_tbody);
1597 writeStartTag(DT_row);
1598 attr = atom->string();
1599 if (!attr.isEmpty()) {
1600 if (attr.contains('=')) {
1604 while (index >= 0) {
1605 index = attr.indexOf('"',from);
1609 index = attr.indexOf('"',from);
1611 if (!values.isEmpty())
1613 values += attr.mid(from,index-from);
1620 xmlWriter().writeAttribute("outputclass", attr);
1622 xmlWriter().writeAttribute("valign","top");
1624 case Atom::TableRowRight:
1625 writeEndTag(); // </row>
1627 case Atom::TableItemLeft:
1630 writeStartTag(DT_entry);
1631 for (int i=0; i<atom->count(); ++i) {
1632 attr = atom->string(i);
1633 if (attr.contains('=')) {
1636 while (index >= 0) {
1637 index = attr.indexOf('"',from);
1641 index = attr.indexOf('"',from);
1643 if (!values.isEmpty())
1645 values += attr.mid(from,index-from);
1652 QStringList spans = attr.split(QLatin1Char(','));
1653 if (spans.size() == 2) {
1654 if ((spans[0].toInt()>1) || (spans[1].toInt()>1)) {
1655 values += "span(" + spans[0] + QLatin1Char(',') + spans[1] + QLatin1Char(')');
1660 if (!values.isEmpty())
1661 xmlWriter().writeAttribute("outputclass",values);
1662 if (matchAhead(atom, Atom::ParaLeft))
1666 case Atom::TableItemRight:
1668 writeEndTag(); // </entry>
1670 writeEndTag(); // </entry>
1672 if (matchAhead(atom, Atom::ParaLeft))
1675 case Atom::TableOfContents:
1678 const Node* node = relative;
1680 Doc::Sections sectionUnit = Doc::Section4;
1681 QStringList params = atom->string().split(QLatin1Char(','));
1682 QString columnText = params.at(0);
1683 QStringList pieces = columnText.split(QLatin1Char(' '), QString::SkipEmptyParts);
1684 if (pieces.size() >= 2) {
1685 columnText = pieces.at(0);
1687 QString path = pieces.join(" ").trimmed();
1688 node = findNodeForTarget(path, relative, marker, atom);
1691 if (params.size() == 2) {
1692 numColumns = qMax(columnText.toInt(), numColumns);
1693 sectionUnit = (Doc::Sections)params.at(1).toInt();
1697 generateTableOfContents(node,
1706 writeEndTag(); // </p>
1709 writeStartTag(DT_p);
1710 writeGuidAttribute(Doc::canonicalTitle(atom->string()));
1711 xmlWriter().writeAttribute("outputclass","target");
1712 //xmlWriter().writeCharacters(protectEnc(atom->string()));
1713 writeEndTag(); // </p>
1715 case Atom::UnhandledFormat:
1716 writeStartTag(DT_b);
1717 xmlWriter().writeAttribute("outputclass","error");
1718 xmlWriter().writeCharacters("<Missing DITAXML>");
1719 writeEndTag(); // </b>
1721 case Atom::UnknownCommand:
1722 writeStartTag(DT_b);
1723 xmlWriter().writeAttribute("outputclass","error unknown-command");
1724 writeCharacters(protectEnc(atom->string()));
1725 writeEndTag(); // </b>
1728 case Atom::EndQmlText:
1729 // don't do anything with these. They are just tags.
1732 // unknownAtom(atom);
1739 Generate a <cxxClass> element (and all the stuff inside it)
1740 for the C++ class represented by \a innerNode. \a marker is
1741 for marking up the code. I don't know what that means exactly.
1744 DitaXmlGenerator::generateClassLikeNode(const InnerNode* inner, CodeMarker* marker)
1746 QList<Section>::ConstIterator s;
1751 if (inner->type() == Node::Namespace) {
1752 const NamespaceNode* nsn = const_cast<NamespaceNode*>(static_cast<const NamespaceNode*>(inner));
1753 rawTitle = marker->plainName(inner);
1754 fullTitle = marker->plainFullName(inner);
1755 title = rawTitle + " Namespace";
1758 Note: Because the C++ specialization we are using
1759 has no <cxxNamespace> element, we are using the
1760 <cxxClass> element with an outputclass attribute
1761 set to "namespace" .
1763 generateHeader(inner, fullTitle);
1764 generateBrief(inner, marker); // <shortdesc>
1767 writeStartTag(DT_cxxClassDetail);
1768 writeStartTag(DT_cxxClassDefinition);
1770 writeEndTag(); // <cxxClassDefinition>
1772 enterApiDesc(QString(),title);
1773 Text brief = nsn->doc().briefText(); // zzz
1774 if (!brief.isEmpty()) {
1775 writeStartTag(DT_p);
1776 generateText(brief, nsn, marker);
1777 writeEndTag(); // </p>
1779 generateStatus(nsn, marker);
1780 generateThreadSafeness(nsn, marker);
1781 generateSince(nsn, marker);
1783 enterSection("h2","Detailed Description");
1784 generateBody(nsn, marker);
1785 generateAlsoList(nsn, marker);
1787 leaveSection(); // </apiDesc>
1789 bool needOtherSection = false;
1790 QList<Section> summarySections;
1791 summarySections = marker->sections(inner, CodeMarker::Summary, CodeMarker::Okay);
1792 if (!summarySections.isEmpty()) {
1793 enterSection("redundant",QString());
1794 s = summarySections.begin();
1795 while (s != summarySections.end()) {
1796 if (s->members.isEmpty() && s->reimpMembers.isEmpty()) {
1797 if (!s->inherited.isEmpty())
1798 needOtherSection = true;
1802 if (!s->members.isEmpty()) {
1803 writeStartTag(DT_p);
1804 attr = cleanRef((*s).name).toLower() + " h2";
1805 xmlWriter().writeAttribute("outputclass",attr);
1806 writeCharacters(protectEnc((*s).name));
1807 writeEndTag(); // </title>
1808 generateSection(s->members, inner, marker, CodeMarker::Summary);
1809 generateSectionInheritedList(*s, inner, marker);
1811 if (!s->reimpMembers.isEmpty()) {
1812 QString name = QString("Reimplemented ") + (*s).name;
1813 attr = cleanRef(name).toLower() + " h2";
1814 writeStartTag(DT_p);
1815 xmlWriter().writeAttribute("outputclass",attr);
1816 writeCharacters(protectEnc(name));
1817 writeEndTag(); // </title>
1818 generateSection(s->reimpMembers, inner, marker, CodeMarker::Summary);
1819 generateSectionInheritedList(*s, inner, marker);
1824 if (needOtherSection) {
1825 writeStartTag(DT_p);
1826 xmlWriter().writeAttribute("outputclass","h3");
1827 xmlWriter().writeCharacters("Additional Inherited Members");
1828 writeEndTag(); // </title>
1829 s = summarySections.begin();
1830 while (s != summarySections.end()) {
1831 if (s->members.isEmpty())
1832 generateSectionInheritedList(*s, inner, marker);
1839 writeEndTag(); // </cxxClassDetail>
1841 // not included: <related-links>
1842 // not included: <cxxClassNested>
1844 QList<Section> detailSections;
1845 detailSections = marker->sections(inner, CodeMarker::Detailed, CodeMarker::Okay);
1846 s = detailSections.begin();
1847 while (s != detailSections.end()) {
1848 if ((*s).name == "Classes") {
1849 writeNestedClasses((*s),nsn);
1855 s = detailSections.begin();
1856 while (s != detailSections.end()) {
1857 if ((*s).name == "Function Documentation") {
1858 writeFunctions((*s),nsn,marker);
1860 else if ((*s).name == "Type Documentation") {
1861 writeEnumerations((*s),marker);
1862 writeTypedefs((*s),marker);
1864 else if ((*s).name == "Namespaces") {
1865 qDebug() << "Nested namespaces" << outFileName();
1867 else if ((*s).name == "Macro Documentation") {
1868 //writeMacros((*s),marker);
1873 generateLowStatusMembers(inner,marker,CodeMarker::Obsolete);
1874 generateLowStatusMembers(inner,marker,CodeMarker::Compat);
1875 writeEndTag(); // </cxxClass>
1877 else if (inner->type() == Node::Class) {
1878 const ClassNode* cn = const_cast<ClassNode*>(static_cast<const ClassNode*>(inner));
1879 rawTitle = marker->plainName(inner);
1880 fullTitle = marker->plainFullName(inner);
1881 title = rawTitle + " Class";
1883 generateHeader(inner, fullTitle);
1884 generateBrief(inner, marker); // <shortdesc>
1887 writeStartTag(DT_cxxClassDetail);
1888 writeStartTag(DT_cxxClassDefinition);
1889 writeStartTag(DT_cxxClassAccessSpecifier);
1890 xmlWriter().writeAttribute("value",inner->accessString());
1891 writeEndTag(); // <cxxClassAccessSpecifier>
1892 if (cn->isAbstract()) {
1893 writeStartTag(DT_cxxClassAbstract);
1894 xmlWriter().writeAttribute("name","abstract");
1895 xmlWriter().writeAttribute("value","abstract");
1896 writeEndTag(); // </cxxClassAbstract>
1898 writeDerivations(cn, marker); // <cxxClassDerivations>
1900 // not included: <cxxClassTemplateParameters>
1903 writeEndTag(); // <cxxClassDefinition>
1905 enterApiDesc(QString(),title);
1906 Text brief = cn->doc().briefText(); // zzz
1907 if (!brief.isEmpty()) {
1908 writeStartTag(DT_p);
1909 generateText(brief, cn, marker);
1910 writeEndTag(); // </p>
1912 generateStatus(cn, marker);
1913 generateInherits(cn, marker);
1914 generateInheritedBy(cn, marker);
1915 generateThreadSafeness(cn, marker);
1916 generateSince(cn, marker);
1917 enterSection("h2","Detailed Description");
1918 generateBody(cn, marker);
1919 generateAlsoList(cn, marker);
1921 leaveSection(); // </apiDesc>
1923 bool needOtherSection = false;
1924 QList<Section> summarySections;
1925 summarySections = marker->sections(inner, CodeMarker::Summary, CodeMarker::Okay);
1926 if (!summarySections.isEmpty()) {
1927 enterSection("redundant",QString());
1928 s = summarySections.begin();
1929 while (s != summarySections.end()) {
1930 if (s->members.isEmpty() && s->reimpMembers.isEmpty()) {
1931 if (!s->inherited.isEmpty())
1932 needOtherSection = true;
1936 if (!s->members.isEmpty()) {
1937 writeStartTag(DT_p);
1938 attr = cleanRef((*s).name).toLower() + " h2";
1939 xmlWriter().writeAttribute("outputclass",attr);
1940 writeCharacters(protectEnc((*s).name));
1941 writeEndTag(); // </p>
1942 generateSection(s->members, inner, marker, CodeMarker::Summary);
1943 generateSectionInheritedList(*s, inner, marker);
1945 if (!s->reimpMembers.isEmpty()) {
1946 QString name = QString("Reimplemented ") + (*s).name;
1947 attr = cleanRef(name).toLower() + " h2";
1948 writeStartTag(DT_p);
1949 xmlWriter().writeAttribute("outputclass",attr);
1950 writeCharacters(protectEnc(name));
1951 writeEndTag(); // </p>
1952 generateSection(s->reimpMembers, inner, marker, CodeMarker::Summary);
1953 generateSectionInheritedList(*s, inner, marker);
1958 if (needOtherSection) {
1959 writeStartTag(DT_p);
1960 xmlWriter().writeAttribute("outputclass","h3");
1961 xmlWriter().writeCharacters("Additional Inherited Members");
1962 writeEndTag(); // </p>
1963 s = summarySections.begin();
1964 while (s != summarySections.end()) {
1965 if (s->members.isEmpty())
1966 generateSectionInheritedList(*s, inner, marker);
1973 // not included: <example> or <apiImpl>
1975 writeEndTag(); // </cxxClassDetail>
1977 // not included: <related-links>
1978 // not included: <cxxClassNested>
1980 QList<Section> detailSections;
1981 detailSections = marker->sections(inner, CodeMarker::Detailed, CodeMarker::Okay);
1982 s = detailSections.begin();
1983 while (s != detailSections.end()) {
1984 if ((*s).name == "Member Function Documentation") {
1985 writeFunctions((*s),cn,marker);
1987 else if ((*s).name == "Member Type Documentation") {
1988 writeEnumerations((*s),marker);
1989 writeTypedefs((*s),marker);
1991 else if ((*s).name == "Member Variable Documentation") {
1992 writeDataMembers((*s),marker);
1994 else if ((*s).name == "Property Documentation") {
1995 writeProperties((*s),marker);
1997 else if ((*s).name == "Macro Documentation") {
1998 //writeMacros((*s),marker);
2000 else if ((*s).name == "Related Non-Members") {
2001 QString attribute("related-non-member");
2002 writeFunctions((*s),cn,marker,attribute);
2007 generateLowStatusMembers(inner,marker,CodeMarker::Obsolete);
2008 generateLowStatusMembers(inner,marker,CodeMarker::Compat);
2009 writeEndTag(); // </cxxClass>
2011 else if ((inner->type() == Node::Fake) && (inner->subType() == Node::HeaderFile)) {
2012 const FakeNode* fn = const_cast<FakeNode*>(static_cast<const FakeNode*>(inner));
2013 rawTitle = marker->plainName(inner);
2014 fullTitle = marker->plainFullName(inner);
2018 Note: Because the C++ specialization we are using
2019 has no <cxxHeaderFile> element, we are using the
2020 <cxxClass> element with an outputclass attribute
2021 set to "headerfile" .
2023 generateHeader(inner, fullTitle);
2024 generateBrief(inner, marker); // <shortdesc>
2027 writeStartTag(DT_cxxClassDetail);
2028 enterApiDesc(QString(),title);
2029 Text brief = fn->doc().briefText(); // zzz
2030 if (!brief.isEmpty()) {
2031 writeStartTag(DT_p);
2032 generateText(brief, fn, marker);
2033 writeEndTag(); // </p>
2035 generateStatus(fn, marker);
2036 generateThreadSafeness(fn, marker);
2037 generateSince(fn, marker);
2038 generateSince(fn, marker);
2039 enterSection("h2","Detailed Description");
2040 generateBody(fn, marker);
2041 generateAlsoList(fn, marker);
2043 leaveSection(); // </apiDesc>
2045 bool needOtherSection = false;
2046 QList<Section> summarySections;
2047 summarySections = marker->sections(inner, CodeMarker::Summary, CodeMarker::Okay);
2048 if (!summarySections.isEmpty()) {
2049 enterSection("redundant",QString());
2050 s = summarySections.begin();
2051 while (s != summarySections.end()) {
2052 if (s->members.isEmpty() && s->reimpMembers.isEmpty()) {
2053 if (!s->inherited.isEmpty())
2054 needOtherSection = true;
2058 if (!s->members.isEmpty()) {
2059 writeStartTag(DT_p);
2060 attr = cleanRef((*s).name).toLower() + " h2";
2061 xmlWriter().writeAttribute("outputclass",attr);
2062 writeCharacters(protectEnc((*s).name));
2063 writeEndTag(); // </p>
2064 generateSection(s->members, inner, marker, CodeMarker::Summary);
2065 generateSectionInheritedList(*s, inner, marker);
2067 if (!s->reimpMembers.isEmpty()) {
2068 QString name = QString("Reimplemented ") + (*s).name;
2069 attr = cleanRef(name).toLower() + " h2";
2070 writeStartTag(DT_p);
2071 xmlWriter().writeAttribute("outputclass",attr);
2072 writeCharacters(protectEnc(name));
2073 writeEndTag(); // </p>
2074 generateSection(s->reimpMembers, inner, marker, CodeMarker::Summary);
2075 generateSectionInheritedList(*s, inner, marker);
2080 if (needOtherSection) {
2081 enterSection("additional-inherited-members redundant",QString());
2082 writeStartTag(DT_p);
2083 xmlWriter().writeAttribute("outputclass","h3");
2084 xmlWriter().writeCharacters("Additional Inherited Members");
2085 writeEndTag(); // </p>
2086 s = summarySections.begin();
2087 while (s != summarySections.end()) {
2088 if (s->members.isEmpty())
2089 generateSectionInheritedList(*s, inner, marker);
2096 writeEndTag(); // </cxxClassDetail>
2098 // not included: <related-links>
2099 // not included: <cxxClassNested>
2101 QList<Section> detailSections;
2102 detailSections = marker->sections(inner, CodeMarker::Detailed, CodeMarker::Okay);
2103 s = detailSections.begin();
2104 while (s != detailSections.end()) {
2105 if ((*s).name == "Classes") {
2106 writeNestedClasses((*s),fn);
2112 s = detailSections.begin();
2113 while (s != detailSections.end()) {
2114 if ((*s).name == "Function Documentation") {
2115 writeFunctions((*s),fn,marker);
2117 else if ((*s).name == "Type Documentation") {
2118 writeEnumerations((*s),marker);
2119 writeTypedefs((*s),marker);
2121 else if ((*s).name == "Namespaces") {
2122 qDebug() << "Nested namespaces" << outFileName();
2124 else if ((*s).name == "Macro Documentation") {
2125 //writeMacros((*s),marker);
2129 generateLowStatusMembers(inner,marker,CodeMarker::Obsolete);
2130 generateLowStatusMembers(inner,marker,CodeMarker::Compat);
2131 writeEndTag(); // </cxxClass>
2133 else if ((inner->type() == Node::Fake) && (inner->subType() == Node::QmlClass)) {
2134 const QmlClassNode* qcn = const_cast<QmlClassNode*>(static_cast<const QmlClassNode*>(inner));
2135 const ClassNode* cn = qcn->classNode();
2136 rawTitle = marker->plainName(inner);
2137 fullTitle = marker->plainFullName(inner);
2138 title = rawTitle + " Element";
2139 //QString fullTitle = fake->fullTitle();
2140 //QString htmlTitle = fullTitle;
2142 generateHeader(inner, fullTitle);
2143 generateBrief(inner, marker); // <shortdesc>
2146 writeStartTag(DT_cxxClassDetail);
2147 enterApiDesc(QString(),title);
2148 Text brief = qcn->doc().briefText(); // zzz
2149 if (!brief.isEmpty()) {
2150 writeStartTag(DT_p);
2151 generateText(brief, qcn, marker);
2152 writeEndTag(); // </p>
2154 generateQmlInstantiates(qcn, marker);
2155 generateQmlInherits(qcn, marker);
2156 generateQmlInheritedBy(qcn, marker);
2157 generateSince(qcn, marker);
2158 enterSection("h2","Detailed Description");
2159 generateBody(qcn, marker);
2161 generateQmlText(cn->doc().body(), cn, marker, qcn->name());
2162 generateAlsoList(cn, marker);
2165 leaveSection(); // </apiDesc>
2167 QList<Section> summarySections;
2168 summarySections = marker->qmlSections(qcn,CodeMarker::Summary);
2169 if (!summarySections.isEmpty()) {
2170 enterSection("redundant",QString());
2171 s = summarySections.begin();
2172 while (s != summarySections.end()) {
2174 if (!s->members.isEmpty()) {
2175 writeStartTag(DT_p);
2176 attr = cleanRef((*s).name).toLower() + " h2";
2177 xmlWriter().writeAttribute("outputclass",attr);
2178 writeCharacters(protectEnc((*s).name));
2179 writeEndTag(); // </p>
2180 generateQmlSummary(*s,qcn,marker);
2181 //generateSection(s->members, inner, marker, CodeMarker::Summary);
2182 //generateSectionInheritedList(*s, inner, marker);
2189 QList<Section> detailSections;
2190 detailSections = marker->qmlSections(qcn,CodeMarker::Detailed);
2191 if (!detailSections.isEmpty()) {
2192 enterSection("details",QString());
2193 s = detailSections.begin();
2194 while (s != detailSections.end()) {
2195 if (!s->members.isEmpty()) {
2197 writeStartTag(DT_p);
2198 attr = cleanRef((*s).name).toLower() + " h2";
2199 xmlWriter().writeAttribute("outputclass",attr);
2200 writeCharacters(protectEnc((*s).name));
2201 writeEndTag(); // </p>
2202 NodeList::ConstIterator m = (*s).members.begin();
2203 while (m != (*s).members.end()) {
2204 generateDetailedQmlMember(*m, qcn, marker);
2212 writeEndTag(); // </cxxClassDetail>
2213 writeEndTag(); // </cxxClass>
2219 Write a list item for a \a link with the given \a text.
2221 void DitaXmlGenerator::writeXrefListItem(const QString& link, const QString& text)
2223 writeStartTag(DT_li);
2224 writeStartTag(DT_xref);
2226 writeHrefAttribute(link);
2227 writeCharacters(text);
2228 writeEndTag(); // </xref>
2229 writeEndTag(); // </li>
2233 Generate the DITA page for a qdoc file that doesn't map
2234 to an underlying c++ file.
2236 void DitaXmlGenerator::generateFakeNode(const FakeNode* fake, CodeMarker* marker)
2239 If the fake node is a page node, and if the page type
2240 is DITA map page, write the node's contents as a dita
2241 map and return without doing anything else.
2243 if (fake->subType() == Node::Page && fake->pageType() == Node::DitaMapPage) {
2244 const DitaMapNode* dmn = static_cast<const DitaMapNode*>(fake);
2249 QList<Section> sections;
2250 QList<Section>::const_iterator s;
2251 QString fullTitle = fake->fullTitle();
2253 if (fake->subType() == Node::QmlBasicType) {
2254 fullTitle = "QML Basic Type: " + fullTitle;
2256 else if (fake->subType() == Node::Collision) {
2257 fullTitle = "Name Collision: " + fullTitle;
2260 generateHeader(fake, fullTitle);
2261 generateBrief(fake, marker); // <shortdesc>
2264 writeStartTag(DT_body);
2265 enterSection(QString(),QString());
2266 if (fake->subType() == Node::Module) {
2267 generateStatus(fake, marker);
2268 if (moduleNamespaceMap.contains(fake->name())) {
2269 enterSection("h2","Namespaces");
2270 generateAnnotatedList(fake, marker, moduleNamespaceMap[fake->name()]);
2273 if (moduleClassMap.contains(fake->name())) {
2274 enterSection("h2","Classes");
2275 generateAnnotatedList(fake, marker, moduleClassMap[fake->name()]);
2280 if (fake->doc().isEmpty()) {
2281 if (fake->subType() == Node::File) {
2284 writeStartTag(DT_p);
2285 xmlWriter().writeAttribute("outputclass", "small-subtitle");
2286 text << fake->subTitle();
2287 generateText(text, fake, marker);
2288 writeEndTag(); // </p>
2289 Doc::quoteFromFile(fake->doc().location(), quoter, fake->name());
2290 QString code = quoter.quoteTo(fake->location(), "", "");
2292 text << Atom(Atom::Code, code);
2293 generateText(text, fake, marker);
2297 if (fake->subType() == Node::Module) {
2298 enterSection("h2","Detailed Description");
2299 generateBody(fake, marker);
2303 generateBody(fake, marker);
2305 generateAlsoList(fake, marker);
2307 if ((fake->subType() == Node::QmlModule) && !fake->qmlModuleMembers().isEmpty()) {
2308 NodeMap qmlModuleMembersMap;
2309 foreach (const Node* node, fake->qmlModuleMembers()) {
2310 if (node->type() == Node::Fake && node->subType() == Node::QmlClass)
2311 qmlModuleMembersMap[node->name()] = node;
2313 generateAnnotatedList(fake, marker, qmlModuleMembersMap);
2315 else if (!fake->groupMembers().isEmpty()) {
2316 NodeMap groupMembersMap;
2317 foreach (const Node *node, fake->groupMembers()) {
2318 if (node->type() == Node::Class || node->type() == Node::Namespace)
2319 groupMembersMap[node->name()] = node;
2321 generateAnnotatedList(fake, marker, groupMembersMap);
2324 leaveSection(); // </section>
2325 if (!writeEndTag()) { // </body>
2326 fake->doc().location().warning(tr("Pop of empty XML tag stack; generating DITA for '%1'").arg(fake->name()));
2329 writeRelatedLinks(fake, marker);
2330 writeEndTag(); // </topic>
2334 This function writes a \e{<link>} element inside a
2335 \e{<related-links>} element.
2337 \sa writeRelatedLinks()
2339 void DitaXmlGenerator::writeLink(const Node* node,
2340 const QString& text,
2341 const QString& role)
2344 QString link = fileName(node) + QLatin1Char('#') + node->guid();
2345 if (link.endsWith("#"))
2346 qDebug() << "LINK ENDS WITH #:" << link << outFileName();
2347 writeStartTag(DT_link);
2348 writeHrefAttribute(link);
2349 xmlWriter().writeAttribute("role", role);
2350 writeStartTag(DT_linktext);
2351 writeCharacters(text);
2352 writeEndTag(); // </linktext>
2353 writeEndTag(); // </link>
2358 This function writes a \e{<related-links>} element, which
2359 contains the \c{next}, \c{previous}, and \c{start}
2360 links for topic pages that have them. Note that the
2361 value of the \e role attribute is \c{parent} for the
2364 void DitaXmlGenerator::writeRelatedLinks(const FakeNode* node, CodeMarker* marker)
2366 const Node* linkNode = 0;
2367 QPair<QString,QString> linkPair;
2368 if (node && !node->links().empty()) {
2369 writeStartTag(DT_relatedLinks);
2370 if (node->links().contains(Node::PreviousLink)) {
2371 linkPair = node->links()[Node::PreviousLink];
2372 linkNode = findNodeForTarget(linkPair.first, node, marker);
2373 writeLink(linkNode, linkPair.second, "previous");
2375 if (node->links().contains(Node::NextLink)) {
2376 linkPair = node->links()[Node::NextLink];
2377 linkNode = findNodeForTarget(linkPair.first, node, marker);
2378 writeLink(linkNode, linkPair.second, "next");
2380 if (node->links().contains(Node::StartLink)) {
2381 linkPair = node->links()[Node::StartLink];
2382 linkNode = findNodeForTarget(linkPair.first, node, marker);
2383 writeLink(linkNode, linkPair.second, "parent");
2385 writeEndTag(); // </related-links>
2390 Returns "dita" for this subclass of class Generator.
2392 QString DitaXmlGenerator::fileExtension(const Node * /* node */) const
2398 Writes an XML file header to the current XML stream. This
2399 depends on which kind of DITA XML file is being generated,
2400 which is determined by the \a node type and subtype and the
2401 \a subpage flag. If the \subpage flag is true, a \c{<topic>}
2402 header is written, regardless of the type of \a node.
2404 void DitaXmlGenerator::generateHeader(const Node* node,
2405 const QString& name,
2411 DitaTag mainTag = DT_cxxClass;
2412 DitaTag nameTag = DT_apiName;
2417 QString outputclass;
2419 if (node->type() == Node::Class) {
2420 mainTag = DT_cxxClass;
2421 nameTag = DT_apiName;
2422 dtd = "dtd/cxxClass.dtd";
2424 doctype = "<!DOCTYPE " + ditaTags[mainTag] +
2425 " PUBLIC \"-//NOKIA//DTD DITA C++ API Class Reference Type v" +
2426 version + "//EN\" \"" + dtd + "\">";
2428 else if (node->type() == Node::Namespace) {
2429 mainTag = DT_cxxClass;
2430 nameTag = DT_apiName;
2431 dtd = "dtd/cxxClass.dtd";
2433 doctype = "<!DOCTYPE " + ditaTags[mainTag] +
2434 " PUBLIC \"-//NOKIA//DTD DITA C++ API Class Reference Type v" +
2435 version + "//EN\" \"" + dtd + "\">";
2436 outputclass = "namespace";
2438 else if (node->type() == Node::Fake || subpage) {
2439 if (node->subType() == Node::HeaderFile) {
2440 mainTag = DT_cxxClass;
2441 nameTag = DT_apiName;
2442 dtd = "dtd/cxxClass.dtd";
2444 doctype = "<!DOCTYPE " + ditaTags[mainTag] +
2445 " PUBLIC \"-//NOKIA//DTD DITA C++ API Class Reference Type v" +
2446 version + "//EN\" \"" + dtd + "\">";
2447 outputclass = "headerfile";
2449 else if (node->subType() == Node::QmlClass) {
2450 mainTag = DT_cxxClass;
2451 nameTag = DT_apiName;
2452 dtd = "dtd/cxxClass.dtd";
2454 doctype = "<!DOCTYPE " + ditaTags[mainTag] +
2455 " PUBLIC \"-//NOKIA//DTD DITA C++ API Class Reference Type v" +
2456 version + "//EN\" \"" + dtd + "\">";
2457 outputclass = "QML-class";
2462 dtd = "dtd/topic.dtd";
2463 doctype = "<!DOCTYPE " + ditaTags[mainTag] +
2464 " PUBLIC \"-//OASIS//DTD DITA Topic//EN\" \"" + dtd + "\">";
2465 switch (node->subType()) {
2467 outputclass = node->pageTypeString();
2470 outputclass = "group";
2473 outputclass = "example";
2476 outputclass = "file";
2478 case Node::Image: // not used
2479 outputclass = "image";
2482 outputclass = "module";
2484 case Node::ExternalPage: // not used
2485 outputclass = "externalpage";
2488 outputclass = "page";
2493 xmlWriter().writeDTD(doctype);
2494 xmlWriter().writeComment(node->doc().location().fileName());
2495 writeStartTag(mainTag);
2496 QString id = node->guid();
2497 xmlWriter().writeAttribute("id",id);
2498 if (!outputclass.isEmpty())
2499 xmlWriter().writeAttribute("outputclass",outputclass);
2500 writeStartTag(nameTag); // <title> or <apiName>
2501 writeCharacters(name);
2502 writeEndTag(); // </title> or </apiName>
2506 Outputs the \e brief command as a <shortdesc> element.
2508 void DitaXmlGenerator::generateBrief(const Node* node, CodeMarker* marker)
2510 Text brief = node->doc().briefText(true); // zzz
2511 if (!brief.isEmpty()) {
2512 generateText(brief, node, marker);
2518 Generates a table of contents beginning at \a node.
2519 Currently just returns without writing anything.
2521 void DitaXmlGenerator::generateTableOfContents(const Node* node,
2523 Doc::Sections sectionUnit,
2525 const Node* relative)
2529 if (!node->doc().hasTableOfContents())
2531 QList<Atom *> toc = node->doc().tableOfContents();
2535 QString nodeName = "";
2536 if (node != relative)
2537 nodeName = node->name();
2539 QStringList sectionNumber;
2543 if (numColumns > 1) {
2544 tdTag = "<td>"; /* width=\"" + QString::number((100 + numColumns - 1) / numColumns) + "%\">";*/
2545 out() << "<table class=\"toc\">\n<tr class=\"topAlign\">"
2549 // disable nested links in table of contents
2553 for (int i = 0; i < toc.size(); ++i) {
2554 Atom *atom = toc.at(i);
2556 int nextLevel = atom->string().toInt();
2557 if (nextLevel > (int)sectionUnit)
2560 if (sectionNumber.size() < nextLevel) {
2563 sectionNumber.append("1");
2564 } while (sectionNumber.size() < nextLevel);
2567 while (sectionNumber.size() > nextLevel) {
2569 sectionNumber.removeLast();
2571 sectionNumber.last() = QString::number(sectionNumber.last().toInt() + 1);
2574 Text headingText = Text::sectionHeading(atom);
2576 if (sectionNumber.size() == 1 && columnSize > toc.size() / numColumns) {
2577 out() << "</ul></td>" << tdTag << "<ul>\n";
2581 out() << "<xref href=\""
2584 << Doc::canonicalTitle(headingText.toString())
2586 generateAtomList(headingText.firstAtom(), node, marker, true, numAtoms);
2587 out() << "</xref></li>\n";
2591 while (!sectionNumber.isEmpty()) {
2593 sectionNumber.removeLast();
2597 out() << "</td></tr></table>\n";
2605 Revised for the new doc format.
2606 Generates a table of contents beginning at \a node.
2608 void DitaXmlGenerator::generateTableOfContents(const Node* node,
2610 QList<Section>* sections)
2613 if (node->doc().hasTableOfContents())
2614 toc = node->doc().tableOfContents();
2615 if (toc.isEmpty() && !sections && (node->subType() != Node::Module))
2618 QStringList sectionNumber;
2619 int detailsBase = 0;
2621 // disable nested links in table of contents
2625 out() << "<div class=\"toc\">\n";
2626 out() << "<h3>Contents</h3>\n";
2627 sectionNumber.append("1");
2630 if (node->subType() == Node::Module) {
2631 if (moduleNamespaceMap.contains(node->name())) {
2632 out() << "<li class=\"level"
2633 << sectionNumber.size()
2634 << "\"><xref href=\"#"
2635 << registerRef("namespaces")
2636 << "\">Namespaces</xref></li>\n";
2638 if (moduleClassMap.contains(node->name())) {
2639 out() << "<li class=\"level"
2640 << sectionNumber.size()
2641 << "\"><xref href=\"#"
2642 << registerRef("classes")
2643 << "\">Classes</xref></li>\n";
2645 out() << "<li class=\"level"
2646 << sectionNumber.size()
2647 << "\"><xref href=\"#"
2648 << registerRef("details")
2649 << "\">Detailed Description</xref></li>\n";
2650 for (int i = 0; i < toc.size(); ++i) {
2651 if (toc.at(i)->string().toInt() == 1) {
2657 else if (sections && (node->type() == Node::Class)) {
2658 QList<Section>::ConstIterator s = sections->begin();
2659 while (s != sections->end()) {
2660 if (!s->members.isEmpty() || !s->reimpMembers.isEmpty()) {
2661 out() << "<li class=\"level"
2662 << sectionNumber.size()
2663 << "\"><xref href=\"#"
2664 << registerRef((*s).pluralMember)
2665 << "\">" << (*s).name
2666 << "</xref></li>\n";
2670 out() << "<li class=\"level"
2671 << sectionNumber.size()
2672 << "\"><xref href=\"#"
2673 << registerRef("details")
2674 << "\">Detailed Description</xref></li>\n";
2675 for (int i = 0; i < toc.size(); ++i) {
2676 if (toc.at(i)->string().toInt() == 1) {
2683 for (int i = 0; i < toc.size(); ++i) {
2684 Atom *atom = toc.at(i);
2685 int nextLevel = atom->string().toInt() + detailsBase;
2686 if (sectionNumber.size() < nextLevel) {
2688 sectionNumber.append("1");
2689 } while (sectionNumber.size() < nextLevel);
2692 while (sectionNumber.size() > nextLevel) {
2693 sectionNumber.removeLast();
2695 sectionNumber.last() = QString::number(sectionNumber.last().toInt() + 1);
2698 Text headingText = Text::sectionHeading(atom);
2699 QString s = headingText.toString();
2700 out() << "<li class=\"level"
2701 << sectionNumber.size()
2703 out() << "<xref href=\""
2705 << Doc::canonicalTitle(s)
2707 generateAtomList(headingText.firstAtom(), node, marker, true, numAtoms);
2708 out() << "</xref></li>\n";
2710 while (!sectionNumber.isEmpty()) {
2711 sectionNumber.removeLast();
2714 out() << "</div>\n";
2719 void DitaXmlGenerator::generateLowStatusMembers(const InnerNode* inner,
2721 CodeMarker::Status status)
2724 if (status == CodeMarker::Compat)
2725 attribute = "Qt3-support";
2726 else if (status == CodeMarker::Obsolete)
2727 attribute = "obsolete";
2731 QList<Section> sections = marker->sections(inner, CodeMarker::Detailed, status);
2732 QMutableListIterator<Section> j(sections);
2733 while (j.hasNext()) {
2734 if (j.next().members.size() == 0)
2737 if (sections.isEmpty())
2740 QList<Section>::ConstIterator s = sections.begin();
2741 while (s != sections.end()) {
2742 if ((*s).name == "Member Function Documentation") {
2743 writeFunctions((*s),inner,marker,attribute);
2745 else if ((*s).name == "Member Type Documentation") {
2746 writeEnumerations((*s),marker,attribute);
2747 writeTypedefs((*s),marker,attribute);
2749 else if ((*s).name == "Member Variable Documentation") {
2750 writeDataMembers((*s),marker,attribute);
2752 else if ((*s).name == "Property Documentation") {
2753 writeProperties((*s),marker,attribute);
2755 else if ((*s).name == "Macro Documentation") {
2756 //writeMacros((*s),marker,attribute);
2763 Write the XML for the class hierarchy to the current XML stream.
2765 void DitaXmlGenerator::generateClassHierarchy(const Node* relative,
2767 const QMap<QString,const Node*>& classMap)
2769 if (classMap.isEmpty())
2773 NodeMap::ConstIterator c = classMap.begin();
2774 while (c != classMap.end()) {
2775 const ClassNode* classe = static_cast<const ClassNode*>(*c);
2776 if (classe->baseClasses().isEmpty())
2777 topLevel.insert(classe->name(), classe);
2781 QStack<NodeMap > stack;
2782 stack.push(topLevel);
2784 writeStartTag(DT_ul);
2785 while (!stack.isEmpty()) {
2786 if (stack.top().isEmpty()) {
2788 writeEndTag(); // </ul>
2789 if (!stack.isEmpty())
2790 writeEndTag(); // </li>
2793 const ClassNode *child =
2794 static_cast<const ClassNode *>(*stack.top().begin());
2795 writeStartTag(DT_li);
2796 generateFullName(child, relative, marker);
2797 writeEndTag(); // </li>
2798 stack.top().erase(stack.top().begin());
2801 foreach (const RelatedClass &d, child->derivedClasses()) {
2802 if (d.access != Node::Private && !d.node->doc().isEmpty())
2803 newTop.insert(d.node->name(), d.node);
2805 if (!newTop.isEmpty()) {
2807 writeStartTag(DT_li);
2808 writeStartTag(DT_ul);
2815 Write XML for the contents of the \a nodeMap to the current
2818 void DitaXmlGenerator::generateAnnotatedList(const Node* relative,
2820 const NodeMap& nodeMap)
2822 if (nodeMap.isEmpty())
2824 writeStartTag(DT_table);
2825 xmlWriter().writeAttribute("outputclass","annotated");
2826 writeStartTag(DT_tgroup);
2827 xmlWriter().writeAttribute("cols","2");
2828 writeStartTag(DT_tbody);
2830 foreach (const QString& name, nodeMap.keys()) {
2831 const Node* node = nodeMap[name];
2833 if (node->status() == Node::Obsolete)
2836 writeStartTag(DT_row);
2837 writeStartTag(DT_entry);
2838 writeStartTag(DT_p);
2839 generateFullName(node, relative, marker);
2840 writeEndTag(); // </p>
2841 writeEndTag(); // <entry>
2843 if (!(node->type() == Node::Fake)) {
2844 Text brief = node->doc().trimmedBriefText(name);
2845 if (!brief.isEmpty()) {
2846 writeStartTag(DT_entry);
2847 writeStartTag(DT_p);
2848 generateText(brief, node, marker);
2849 writeEndTag(); // </p>
2850 writeEndTag(); // <entry>
2854 writeStartTag(DT_entry);
2855 writeStartTag(DT_p);
2856 writeCharacters(protectEnc(node->doc().briefText().toString())); // zzz
2857 writeEndTag(); // </p>
2858 writeEndTag(); // <entry>
2860 writeEndTag(); // </row>
2862 writeEndTag(); // </tbody>
2863 writeEndTag(); // </tgroup>
2864 writeEndTag(); // </table>
2868 This function finds the common prefix of the names of all
2869 the classes in \a classMap and then generates a compact
2870 list of the class names alphabetized on the part of the
2871 name not including the common prefix. You can tell the
2872 function to use \a comonPrefix as the common prefix, but
2873 normally you let it figure it out itself by looking at
2874 the name of the first and last classes in \a classMap.
2876 void DitaXmlGenerator::generateCompactList(const Node* relative,
2878 const NodeMap& classMap,
2879 bool includeAlphabet,
2880 QString commonPrefix)
2882 const int NumParagraphs = 37; // '0' to '9', 'A' to 'Z', '_'
2884 if (classMap.isEmpty())
2888 If commonPrefix is not empty, then the caller knows what
2889 the common prefix is and has passed it in, so just use that
2890 one. But if the commonPrefix is empty (it normally is), then
2891 compute a common prefix using this simple algorithm. Note we
2892 assume the prefix length is 1, i.e. we will have a single
2893 character as the common prefix.
2895 int commonPrefixLen = commonPrefix.length();
2896 if (commonPrefixLen == 0) {
2897 QVector<int> count(26);
2898 for (int i=0; i<26; ++i)
2901 NodeMap::const_iterator iter = classMap.begin();
2902 while (iter != classMap.end()) {
2903 if (!iter.key().contains("::")) {
2904 QChar c = iter.key()[0];
2905 if ((c >= 'A') && (c <= 'Z')) {
2906 int idx = c.unicode() - QChar('A').unicode();
2914 for (int i=0; i<26; ++i) {
2915 if (count[i] > highest) {
2920 idx += QChar('A').unicode();
2922 commonPrefix = common;
2923 commonPrefixLen = 1;
2927 The algorithm below eventually failed, so it was replaced
2928 with the simple (perhaps too simple) algorithm above.
2930 The caller didn't pass in a common prefix, so get the common
2931 prefix by looking at the class names of the first and last
2932 classes in the class map. Discard any namespace names and
2933 just use the bare class names. For Qt, the prefix is "Q".
2935 Note that the algorithm used here to derive the common prefix
2936 from the first and last classes in alphabetical order (QAccel
2937 and QXtWidget in Qt 2.1), fails if either class name does not
2942 NodeMap::const_iterator iter = classMap.begin();
2943 while (iter != classMap.end()) {
2944 if (!iter.key().contains("::")) {
2951 if (first.isEmpty())
2952 first = classMap.begin().key();
2954 iter = classMap.end();
2955 while (iter != classMap.begin()) {
2957 if (!iter.key().contains("::")) {
2964 last = classMap.begin().key();
2966 if (classMap.size() > 1) {
2967 while (commonPrefixLen < first.length() + 1 &&
2968 commonPrefixLen < last.length() + 1 &&
2969 first[commonPrefixLen] == last[commonPrefixLen])
2973 commonPrefix = first.left(commonPrefixLen);
2978 Divide the data into 37 paragraphs: 0, ..., 9, A, ..., Z,
2979 underscore (_). QAccel will fall in paragraph 10 (A) and
2980 QXtWidget in paragraph 33 (X). This is the only place where we
2981 assume that NumParagraphs is 37. Each paragraph is a NodeMap.
2983 NodeMap paragraph[NumParagraphs+1];
2984 QString paragraphName[NumParagraphs+1];
2985 QSet<char> usedParagraphNames;
2987 NodeMap::ConstIterator c = classMap.begin();
2988 while (c != classMap.end()) {
2989 QStringList pieces = c.key().split("::");
2991 int idx = commonPrefixLen;
2992 if (!pieces.last().startsWith(commonPrefix))
2994 if (pieces.size() == 1)
2995 key = pieces.last().mid(idx).toLower();
2997 key = pieces.last().toLower();
2999 int paragraphNr = NumParagraphs - 1;
3001 if (key[0].digitValue() != -1) {
3002 paragraphNr = key[0].digitValue();
3004 else if (key[0] >= QLatin1Char('a') && key[0] <= QLatin1Char('z')) {
3005 paragraphNr = 10 + key[0].unicode() - 'a';
3008 paragraphName[paragraphNr] = key[0].toUpper();
3009 usedParagraphNames.insert(key[0].toLower().cell());
3010 paragraph[paragraphNr].insert(key, c.value());
3015 Each paragraph j has a size: paragraph[j].count(). In the
3016 discussion, we will assume paragraphs 0 to 5 will have sizes
3019 We now want to compute the paragraph offset. Paragraphs 0 to 6
3020 start at offsets 0, 3, 4, 8, 9, 14, 23.
3022 int paragraphOffset[NumParagraphs + 1]; // 37 + 1
3023 paragraphOffset[0] = 0;
3024 for (int i=0; i<NumParagraphs; i++) // i = 0..36
3025 paragraphOffset[i+1] = paragraphOffset[i] + paragraph[i].count();
3028 int curParOffset = 0;
3029 QMap<QChar,QString> cmap;
3032 Output the alphabet as a row of links.
3034 if (includeAlphabet) {
3035 writeStartTag(DT_p);
3036 xmlWriter().writeAttribute("outputclass","alphabet");
3037 for (int i = 0; i < 26; i++) {
3039 if (usedParagraphNames.contains(char('a' + i))) {
3040 writeStartTag(DT_xref);
3042 QString guid = lookupGuid(outFileName(),QString(ch));
3043 QString attr = outFileName() + QString("#%1").arg(guid);
3044 xmlWriter().writeAttribute("href", attr);
3045 xmlWriter().writeCharacters(QString(ch.toUpper()));
3046 writeEndTag(); // </xref>
3049 writeEndTag(); // </p>
3053 Output a <p> element to contain all the <dl> elements.
3055 writeStartTag(DT_p);
3056 xmlWriter().writeAttribute("outputclass","compactlist");
3058 for (int i=0; i<classMap.count()-1; i++) {
3059 while ((curParNr < NumParagraphs) &&
3060 (curParOffset == paragraph[curParNr].count())) {
3066 Starting a new paragraph means starting a new <dl>.
3068 if (curParOffset == 0) {
3070 writeEndTag(); // </dlentry>
3071 writeEndTag(); // </dl>
3073 writeStartTag(DT_dl);
3074 writeStartTag(DT_dlentry);
3075 writeStartTag(DT_dt);
3076 if (includeAlphabet) {
3077 QChar c = paragraphName[curParNr][0].toLower();
3078 writeGuidAttribute(QString(c));
3080 xmlWriter().writeAttribute("outputclass","sublist-header");
3081 xmlWriter().writeCharacters(paragraphName[curParNr]);
3082 writeEndTag(); // </dt>
3086 Output a <dd> for the current offset in the current paragraph.
3088 writeStartTag(DT_dd);
3089 if ((curParNr < NumParagraphs) &&
3090 !paragraphName[curParNr].isEmpty()) {
3091 NodeMap::Iterator it;
3092 it = paragraph[curParNr].begin();
3093 for (int i=0; i<curParOffset; i++)
3097 Previously, we used generateFullName() for this, but we
3098 require some special formatting.
3100 writeStartTag(DT_xref);
3102 writeHrefAttribute(linkForNode(it.value(), relative));
3105 if (it.value()->subType() == Node::QmlClass)
3106 pieces << it.value()->name();
3108 pieces = fullName(it.value(), relative, marker).split("::");
3109 xmlWriter().writeCharacters(protectEnc(pieces.last()));
3110 writeEndTag(); // </xref>
3111 if (pieces.size() > 1) {
3112 xmlWriter().writeCharacters(" (");
3113 generateFullName(it.value()->parent(),relative,marker);
3114 xmlWriter().writeCharacters(")");
3117 writeEndTag(); // </dd>
3120 writeEndTag(); // </dlentry>
3121 writeEndTag(); // </dl>
3122 writeEndTag(); // </p>
3126 Write XML for a function index to the current XML stream.
3128 void DitaXmlGenerator::generateFunctionIndex(const Node* relative,
3131 writeStartTag(DT_p);
3132 xmlWriter().writeAttribute("outputclass","alphabet");
3133 for (int i = 0; i < 26; i++) {
3135 writeStartTag(DT_xref);
3137 QString guid = lookupGuid(outFileName(),QString(ch));
3138 QString attr = outFileName() + QString("#%1").arg(guid);
3139 xmlWriter().writeAttribute("href", attr);
3140 xmlWriter().writeCharacters(QString(ch.toUpper()));
3141 writeEndTag(); // </xref>
3144 writeEndTag(); // </p>
3146 char nextLetter = 'a';
3149 writeStartTag(DT_ul);
3150 QMap<QString, NodeMap >::ConstIterator f = funcIndex.begin();
3151 while (f != funcIndex.end()) {
3152 writeStartTag(DT_li);
3153 currentLetter = f.key()[0].unicode();
3154 while (islower(currentLetter) && currentLetter >= nextLetter) {
3155 writeStartTag(DT_p);
3156 writeGuidAttribute(QString(nextLetter));
3157 xmlWriter().writeAttribute("outputclass","target");
3158 xmlWriter().writeCharacters(QString(nextLetter));
3159 writeEndTag(); // </p>
3162 xmlWriter().writeCharacters(protectEnc(f.key()));
3163 xmlWriter().writeCharacters(":");
3165 NodeMap::ConstIterator s = (*f).begin();
3166 while (s != (*f).end()) {
3167 generateFullName((*s)->parent(), relative, marker, *s);
3170 writeEndTag(); // </li>
3173 writeEndTag(); // </ul>
3177 Write the legalese texts as XML to the current XML stream.
3179 void DitaXmlGenerator::generateLegaleseList(const Node* relative,
3182 QMap<Text, const Node*>::ConstIterator it = legaleseTexts.begin();
3183 while (it != legaleseTexts.end()) {
3184 Text text = it.key();
3185 generateText(text, relative, marker);
3186 writeStartTag(DT_ul);
3188 writeStartTag(DT_li);
3189 generateFullName(it.value(), relative, marker);
3190 writeEndTag(); // </li>
3192 } while (it != legaleseTexts.end() && it.key() == text);
3193 writeEndTag(); //</ul>
3198 Generate the text for the QML item described by \a node
3199 and write it to the current XML stream.
3201 void DitaXmlGenerator::generateQmlItem(const Node* node,
3202 const Node* relative,
3206 QString marked = marker->markedUpQmlItem(node,summary);
3207 QRegExp tag("(<[^@>]*>)");
3208 if (marked.indexOf(tag) != -1) {
3209 QString tmp = protectEnc(marked.mid(tag.pos(1), tag.cap(1).length()));
3210 marked.replace(tag.pos(1), tag.cap(1).length(), tmp);
3212 marked.replace(QRegExp("<@param>([a-z]+)_([1-9n])</@param>"),
3213 "<i>\\1<sub>\\2</sub></i>");
3215 marked.replace("<@param>", "<i>");
3216 marked.replace("</@param>", "</i>");
3218 marked.replace("<@extra>", "<tt>");
3219 marked.replace("</@extra>", "</tt>");
3222 marked.remove("<@type>");
3223 marked.remove("</@type>");
3225 writeText(marked, marker, relative);
3229 Write the XML for the overview list to the current XML stream.
3231 void DitaXmlGenerator::generateOverviewList(const Node* relative, CodeMarker* /* marker */)
3233 QMap<const FakeNode*, QMap<QString, FakeNode*> > fakeNodeMap;
3234 QMap<QString, const FakeNode*> groupTitlesMap;
3235 QMap<QString, FakeNode*> uncategorizedNodeMap;
3236 QRegExp singleDigit("\\b([0-9])\\b");
3238 const NodeList children = tree_->root()->childNodes();
3239 foreach (Node* child, children) {
3240 if (child->type() == Node::Fake && child != relative) {
3241 FakeNode* fakeNode = static_cast<FakeNode*>(child);
3243 // Check whether the page is part of a group or is the group
3246 bool isGroupPage = false;
3247 if (fakeNode->doc().metaCommandsUsed().contains("group")) {
3248 group = fakeNode->doc().metaCommandArgs("group")[0];
3252 // there are too many examples; they would clutter the list
3253 if (fakeNode->subType() == Node::Example)
3256 // not interested either in individual (Qt Designer etc.) manual chapters
3257 if (fakeNode->links().contains(Node::ContentsLink))
3260 // Discard external nodes.
3261 if (fakeNode->subType() == Node::ExternalPage)
3264 QString sortKey = fakeNode->fullTitle().toLower();
3265 if (sortKey.startsWith("the "))
3266 sortKey.remove(0, 4);
3267 sortKey.replace(singleDigit, "0\\1");
3269 if (!group.isEmpty()) {
3271 // If we encounter a group definition page, we add all
3272 // the pages in that group to the list for that group.
3273 foreach (Node* member, fakeNode->groupMembers()) {
3274 if (member->type() != Node::Fake)
3276 FakeNode* page = static_cast<FakeNode*>(member);
3278 QString sortKey = page->fullTitle().toLower();
3279 if (sortKey.startsWith("the "))
3280 sortKey.remove(0, 4);
3281 sortKey.replace(singleDigit, "0\\1");
3282 fakeNodeMap[const_cast<const FakeNode*>(fakeNode)].insert(sortKey, page);
3283 groupTitlesMap[fakeNode->fullTitle()] = const_cast<const FakeNode*>(fakeNode);
3287 else if (!isGroupPage) {
3288 // If we encounter a page that belongs to a group then
3289 // we add that page to the list for that group.
3290 const FakeNode* groupNode =
3291 static_cast<const FakeNode*>(tree_->root()->findNode(group, Node::Fake));
3293 fakeNodeMap[groupNode].insert(sortKey, fakeNode);
3295 // uncategorizedNodeMap.insert(sortKey, fakeNode);
3297 // uncategorizedNodeMap.insert(sortKey, fakeNode);
3299 // uncategorizedNodeMap.insert(sortKey, fakeNode);
3303 // We now list all the pages found that belong to groups.
3304 // If only certain pages were found for a group, but the definition page
3305 // for that group wasn't listed, the list of pages will be intentionally
3306 // incomplete. However, if the group definition page was listed, all the
3307 // pages in that group are listed for completeness.
3309 if (!fakeNodeMap.isEmpty()) {
3310 foreach (const QString& groupTitle, groupTitlesMap.keys()) {
3311 const FakeNode* groupNode = groupTitlesMap[groupTitle];
3312 writeStartTag(DT_p);
3313 xmlWriter().writeAttribute("outputclass","h3");
3314 writeStartTag(DT_xref);
3316 xmlWriter().writeAttribute("href",linkForNode(groupNode, relative));
3317 writeCharacters(protectEnc(groupNode->fullTitle()));
3318 writeEndTag(); // </xref>
3319 writeEndTag(); // </p>
3320 if (fakeNodeMap[groupNode].count() == 0)
3323 writeStartTag(DT_ul);
3324 foreach (const FakeNode* fakeNode, fakeNodeMap[groupNode]) {
3325 QString title = fakeNode->fullTitle();
3326 if (title.startsWith("The "))
3328 writeStartTag(DT_li);
3329 writeStartTag(DT_xref);
3331 xmlWriter().writeAttribute("href",linkForNode(fakeNode, relative));
3332 writeCharacters(protectEnc(title));
3333 writeEndTag(); // </xref>
3334 writeEndTag(); // </li>
3336 writeEndTag(); // </ul>
3340 if (!uncategorizedNodeMap.isEmpty()) {
3341 writeStartTag(DT_p);
3342 xmlWriter().writeAttribute("outputclass","h3");
3343 xmlWriter().writeCharacters("Miscellaneous");
3344 writeEndTag(); // </p>
3345 writeStartTag(DT_ul);
3346 foreach (const FakeNode *fakeNode, uncategorizedNodeMap) {
3347 QString title = fakeNode->fullTitle();
3348 if (title.startsWith("The "))
3350 writeStartTag(DT_li);
3351 writeStartTag(DT_xref);
3353 xmlWriter().writeAttribute("href",linkForNode(fakeNode, relative));
3354 writeCharacters(protectEnc(title));
3355 writeEndTag(); // </xref>
3356 writeEndTag(); // </li>
3358 writeEndTag(); // </ul>
3363 Write the XML for a standard section of a page, e.g.
3364 "Public Functions" or "Protected Slots." The section
3365 is written too the current XML stream as a table.
3367 void DitaXmlGenerator::generateSection(const NodeList& nl,
3368 const Node* relative,
3370 CodeMarker::SynopsisStyle style)
3372 if (!nl.isEmpty()) {
3373 writeStartTag(DT_ul);
3374 NodeList::ConstIterator m = nl.begin();
3375 while (m != nl.end()) {
3376 if ((*m)->access() != Node::Private) {
3377 writeStartTag(DT_li);
3378 QString marked = getMarkedUpSynopsis(*m, relative, marker, style);
3379 writeText(marked, marker, relative);
3380 writeEndTag(); // </li>
3384 writeEndTag(); // </ul>
3389 Writes the "inherited from" list to the current XML stream.
3391 void DitaXmlGenerator::generateSectionInheritedList(const Section& section,
3392 const Node* relative,
3395 if (section.inherited.isEmpty())
3397 writeStartTag(DT_ul);
3398 QList<QPair<InnerNode*,int> >::ConstIterator p = section.inherited.begin();
3399 while (p != section.inherited.end()) {
3400 writeStartTag(DT_li);
3402 text.setNum((*p).second);
3403 text += QLatin1Char(' ');
3404 if ((*p).second == 1)
3405 text += section.singularMember;
3407 text += section.pluralMember;
3408 text += " inherited from ";
3409 writeCharacters(text);
3410 writeStartTag(DT_xref);
3413 text = fileName((*p).first) + QLatin1Char('#');
3414 text += DitaXmlGenerator::cleanRef(section.name.toLower());
3415 xmlWriter().writeAttribute("href",text);
3416 text = protectEnc(marker->plainFullName((*p).first, relative));
3417 writeCharacters(text);
3418 writeEndTag(); // </xref>
3419 writeEndTag(); // </li>
3422 writeEndTag(); // </ul>
3426 Get the synopsis from the \a node using the \a relative
3427 node if needed, and mark up the synopsis using \a marker.
3428 Use the style to decide which kind of sysnopsis to build,
3429 normally \c Summary or \c Detailed. Return the marked up
3432 QString DitaXmlGenerator::getMarkedUpSynopsis(const Node* node,
3433 const Node* relative,
3435 CodeMarker::SynopsisStyle style)
3437 QString marked = marker->markedUpSynopsis(node, relative, style);
3438 QRegExp tag("(<[^@>]*>)");
3439 if (marked.indexOf(tag) != -1) {
3440 QString tmp = protectEnc(marked.mid(tag.pos(1), tag.cap(1).length()));
3441 marked.replace(tag.pos(1), tag.cap(1).length(), tmp);
3443 marked.replace(QRegExp("<@param>([a-z]+)_([1-9n])</@param>"),
3444 "<i> \\1<sub>\\2</sub></i>");
3446 marked.replace("<@param>","<i>");
3447 marked.replace("</@param>","</i>");
3449 if (style == CodeMarker::Summary) {
3450 marked.remove("<@name>"); // was "<b>"
3451 marked.remove("</@name>"); // was "</b>"
3454 if (style == CodeMarker::Subpage) {
3455 QRegExp extraRegExp("<@extra>.*</@extra>");
3456 extraRegExp.setMinimal(true);
3457 marked.remove(extraRegExp);
3461 marked.replace("<@extra>","<tt>");
3462 marked.replace("</@extra>","</tt>");
3466 if (style != CodeMarker::Detailed) {
3467 marked.remove("<@type>");
3468 marked.remove("</@type>");
3474 Renamed from highlightedCode() in the html generator. Gets the text
3475 from \a markedCode , and then the text is written to the current XML
3478 void DitaXmlGenerator::writeText(const QString& markedCode,
3480 const Node* relative)
3482 QString src = markedCode;
3487 const QChar charLangle = '<';
3488 const QChar charAt = '@';
3491 First strip out all the extraneous markup. The table
3492 below contains the markup we want to keep. Everything
3493 else that begins with "<@" or "</@" is stripped out.
3495 static const QString spanTags[] = {
3496 "<@link ", "<@link ",
3497 "<@type>", "<@type>",
3498 "<@headerfile>", "<@headerfile>",
3499 "<@func>", "<@func>",
3500 "<@func ", "<@func ",
3501 "<@param>", "<@param>",
3502 "<@extra>", "<@extra>",
3503 "</@link>", "</@link>",
3504 "</@type>", "</@type>",
3505 "</@headerfile>", "</@headerfile>",
3506 "</@func>", "</@func>",
3507 "</@param>", "</@param>",
3508 "</@extra>", "</@extra>"
3510 for (int i = 0, n = src.size(); i < n;) {
3511 if (src.at(i) == charLangle) {
3512 bool handled = false;
3513 for (int k = 0; k != 13; ++k) {
3514 const QString & tag = spanTags[2 * k];
3515 if (tag == QStringRef(&src, i, tag.length())) {
3516 text += spanTags[2 * k + 1];
3524 if (src.at(i) == charAt ||
3525 (src.at(i) == QLatin1Char('/') && src.at(i + 1) == charAt)) {
3526 // drop 'our' unknown tags (the ones still containing '@')
3527 while (i < n && src.at(i) != QLatin1Char('>'))
3532 // retain all others
3543 // replace all <@link> tags: "(<@link node=\"([^\"]+)\">).*(</@link>)"
3544 // replace all "(<@(type|headerfile|func)(?: +[^>]*)?>)(.*)(</@\\2>)" tags
3547 static const QString markTags[] = {
3549 "link", "type", "headerfile", "func", "param", "extra"
3552 for (int i = 0, n = src.size(); i < n;) {
3553 if (src.at(i) == charLangle && src.at(i + 1) == charAt) {
3555 for (int k = 0; k != 6; ++k) {
3556 if (parseArg(src, markTags[k], &i, n, &arg, &par1)) {
3558 if (k == 0) { // <@link>
3559 if (!text.isEmpty()) {
3560 writeCharacters(text);
3563 n = CodeMarker::nodeForString(par1.toString());
3564 QString link = linkForNode(n, relative);
3567 else if (k == 4) { // <@param>
3568 if (!text.isEmpty()) {
3569 writeCharacters(text);
3572 writeStartTag(DT_i);
3573 //writeCharacters(" " + arg.toString());
3574 writeCharacters(arg.toString());
3575 writeEndTag(); // </i>
3577 else if (k == 5) { // <@extra>
3578 if (!text.isEmpty()) {
3579 writeCharacters(text);
3582 writeStartTag(DT_tt);
3583 writeCharacters(arg.toString());
3584 writeEndTag(); // </tt>
3587 if (!text.isEmpty()) {
3588 writeCharacters(text);
3591 par1 = QStringRef();
3593 n = marker->resolveTarget(arg.toString(), tree_, relative);
3594 if (n && n->subType() == Node::QmlBasicType) {
3595 if (relative && relative->subType() == Node::QmlClass) {
3596 link = linkForNode(n,relative);
3600 writeCharacters(arg.toString());
3604 // (zzz) Is this correct for all cases?
3605 link = linkForNode(n,relative);
3614 text += src.at(i++);
3617 if (!text.isEmpty()) {
3618 writeCharacters(text);
3622 void DitaXmlGenerator::generateLink(const Atom* atom,
3623 const Node* /* relative */,
3626 static QRegExp camelCase("[A-Z][A-Z][a-z]|[a-z][A-Z0-9]|_");
3628 if (funcLeftParen.indexIn(atom->string()) != -1 && marker->recognizeLanguage("Cpp")) {
3629 // hack for C++: move () outside of link
3630 int k = funcLeftParen.pos(1);
3631 writeCharacters(protectEnc(atom->string().left(k)));
3632 if (link.isEmpty()) {
3633 if (showBrokenLinks)
3634 writeEndTag(); // </i>
3637 writeEndTag(); // </xref>
3639 writeCharacters(protectEnc(atom->string().mid(k)));
3641 else if (marker->recognizeLanguage("Java")) {
3642 // hack for Java: remove () and use <tt> when appropriate
3643 bool func = atom->string().endsWith("()");
3644 bool tt = (func || atom->string().contains(camelCase));
3646 writeStartTag(DT_tt);
3648 writeCharacters(protectEnc(atom->string().left(atom->string().length() - 2)));
3650 writeCharacters(protectEnc(atom->string()));
3651 writeEndTag(); // </tt>
3654 writeCharacters(protectEnc(atom->string()));
3657 QString DitaXmlGenerator::cleanRef(const QString& ref)
3664 clean.reserve(ref.size() + 20);
3665 const QChar c = ref[0];
3666 const uint u = c.unicode();
3668 if ((u >= 'a' && u <= 'z') ||
3669 (u >= 'A' && u <= 'Z') ||
3670 (u >= '0' && u <= '9')) {
3673 else if (u == '~') {
3676 else if (u == '_') {
3677 clean += "underscore.";
3680 clean += QLatin1Char('A');
3683 for (int i = 1; i < (int) ref.length(); i++) {
3684 const QChar c = ref[i];
3685 const uint u = c.unicode();
3686 if ((u >= 'a' && u <= 'z') ||
3687 (u >= 'A' && u <= 'Z') ||
3688 (u >= '0' && u <= '9') || u == '-' ||
3689 u == '_' || u == ':' || u == '.') {
3692 else if (c.isSpace()) {
3693 clean += QLatin1Char('-');
3695 else if (u == '!') {
3698 else if (u == '&') {
3701 else if (u == '<') {
3704 else if (u == '=') {
3707 else if (u == '>') {
3710 else if (u == '#') {
3711 clean += QLatin1Char('#');
3714 clean += QLatin1Char('-');
3715 clean += QString::number((int)u, 16);
3721 QString DitaXmlGenerator::registerRef(const QString& ref)
3723 QString clean = DitaXmlGenerator::cleanRef(ref);
3726 QString& prevRef = refMap[clean.toLower()];
3727 if (prevRef.isEmpty()) {
3731 else if (prevRef == ref)
3733 clean += QLatin1Char('x');
3739 Calls protect() with the \a string. Returns the result.
3741 QString DitaXmlGenerator::protectEnc(const QString& string)
3743 return protect(string, outputEncoding);
3746 QString DitaXmlGenerator::protect(const QString& string, const QString& ) //outputEncoding)
3749 if (xml.isEmpty()) { \
3756 int n = string.length();
3758 for (int i = 0; i < n; ++i) {
3759 QChar ch = string.at(i);
3761 if (ch == QLatin1Char('&')) {
3764 else if (ch == QLatin1Char('<')) {
3767 else if (ch == QLatin1Char('>')) {
3770 else if (ch == QLatin1Char('"')) {
3774 else if ((outputEncoding == "ISO-8859-1" && ch.unicode() > 0x007F) ||
3775 (ch == QLatin1Char('*') && i + 1 < n && string.at(i) == QLatin1Char('/')) ||
3776 (ch == QLatin1Char('.') && i > 2 && string.at(i - 2) == QLatin1Char('.'))) {
3777 // we escape '*/' and the last dot in 'e.g.' and 'i.e.' for the Javadoc generator
3779 xml += QString::number(ch.unicode(), 16);
3780 xml += QLatin1Char(';');
3797 Constructs a file name appropriate for the \a node
3798 and returns the file name.
3800 QString DitaXmlGenerator::fileBase(const Node* node) const
3803 result = Generator::fileBase(node);
3805 if (!node->isInnerNode()) {
3806 switch (node->status()) {
3810 case Node::Obsolete:
3811 result += "-obsolete";
3821 QString DitaXmlGenerator::guidForNode(const Node* node)
3823 switch (node->type()) {
3824 case Node::Namespace:
3829 return node->guid();
3832 const TypedefNode* tdn = static_cast<const TypedefNode*>(node);
3833 if (tdn->associatedEnum())
3834 return guidForNode(tdn->associatedEnum());
3836 return node->guid();
3837 case Node::Function:
3839 const FunctionNode* fn = static_cast<const FunctionNode*>(node);
3840 if (fn->associatedProperty()) {
3841 return guidForNode(fn->associatedProperty());
3844 QString ref = fn->name();
3845 if (fn->overloadNumber() != 1) {
3846 ref += QLatin1Char('-') + QString::number(fn->overloadNumber());
3852 if (node->subType() != Node::QmlPropertyGroup)
3854 case Node::QmlProperty:
3855 case Node::Property:
3856 return node->guid();
3857 case Node::QmlSignal:
3858 return node->guid();
3859 case Node::QmlSignalHandler:
3860 return node->guid();
3861 case Node::QmlMethod:
3862 return node->guid();
3863 case Node::Variable:
3864 return node->guid();
3866 return node->guid();
3872 Constructs a file name appropriate for the \a node and returns
3873 it. If the \a node is not a fake node, or if it is a fake node but
3874 it is neither an external page node nor an image node, call the
3875 PageGenerator::fileName() function.
3877 QString DitaXmlGenerator::fileName(const Node* node)
3879 if (node->type() == Node::Fake) {
3880 if (static_cast<const FakeNode*>(node)->subType() == Node::ExternalPage)
3881 return node->name();
3882 if (static_cast<const FakeNode*>(node)->subType() == Node::Image)
3883 return node->name();
3885 return Generator::fileName(node);
3888 QString DitaXmlGenerator::linkForNode(const Node* node, const Node* relative)
3890 if (node == 0 || node == relative)
3892 if (!node->url().isEmpty())
3894 if (fileBase(node).isEmpty())
3896 if (node->access() == Node::Private)
3899 QString fn = fileName(node);
3900 if (node && relative && node->parent() != relative) {
3901 if (node->parent()->subType() == Node::QmlClass && relative->subType() == Node::QmlClass) {
3902 if (node->parent()->isAbstract()) {
3904 This is a bit of a hack. What we discover with
3905 the three 'if' statements immediately above,
3906 is that node's parent is marked \qmlabstract
3907 but the link appears in a qdoc comment for a
3908 subclass of the node's parent. This means the
3909 link should refer to the file for the relative
3910 node, not the file for node.
3912 fn = fileName(relative);
3914 qDebug() << "ABSTRACT:" << node->parent()->name()
3915 << node->name() << relative->name()
3916 << node->parent()->type() << node->parent()->subType()
3917 << relative->type() << relative->subType() << outFileName();
3924 if (!node->isInnerNode() || node->subType() == Node::QmlPropertyGroup) {
3925 QString guid = guidForNode(node);
3926 if (relative && fn == fileName(relative) && guid == guidForNode(relative)) {
3929 link += QLatin1Char('#');
3933 If the output is going to subdirectories, then if the
3934 two nodes will be output to different directories, then
3935 the link must go up to the parent directory and then
3936 back down into the other subdirectory.
3938 if (node && relative && (node != relative)) {
3939 if (node->outputSubdirectory() != relative->outputSubdirectory())
3940 link.prepend(QString("../" + node->outputSubdirectory() + QLatin1Char('/')));
3945 QString DitaXmlGenerator::refForAtom(Atom* atom, const Node* /* node */)
3947 if (atom->type() == Atom::SectionLeft)
3948 return Doc::canonicalTitle(Text::sectionHeading(atom).toString());
3949 if (atom->type() == Atom::Target)
3950 return Doc::canonicalTitle(atom->string());
3954 void DitaXmlGenerator::generateFullName(const Node* apparentNode,
3955 const Node* relative,
3957 const Node* actualNode)
3959 if (actualNode == 0)
3960 actualNode = apparentNode;
3961 writeStartTag(DT_xref);
3963 QString href = linkForNode(actualNode, relative);
3964 writeHrefAttribute(href);
3965 writeCharacters(protectEnc(fullName(apparentNode, relative, marker)));
3966 writeEndTag(); // </xref>
3969 void DitaXmlGenerator::findAllClasses(const InnerNode* node)
3971 NodeList::const_iterator c = node->childNodes().constBegin();
3972 while (c != node->childNodes().constEnd()) {
3973 if ((*c)->access() != Node::Private && (*c)->url().isEmpty()) {
3974 if ((*c)->type() == Node::Class && !(*c)->doc().isEmpty()) {
3975 QString className = (*c)->name();
3976 if ((*c)->parent() &&
3977 (*c)->parent()->type() == Node::Namespace &&
3978 !(*c)->parent()->name().isEmpty())
3979 className = (*c)->parent()->name()+"::"+className;
3981 if (!(static_cast<const ClassNode *>(*c))->hideFromMainList()) {
3982 if ((*c)->status() == Node::Compat) {
3983 compatClasses.insert(className, *c);
3985 else if ((*c)->status() == Node::Obsolete) {
3986 obsoleteClasses.insert(className, *c);
3989 nonCompatClasses.insert(className, *c);
3990 if ((*c)->status() == Node::Main)
3991 mainClasses.insert(className, *c);
3995 QString moduleName = (*c)->moduleName();
3996 if (moduleName == "Qt3SupportLight") {
3997 moduleClassMap[moduleName].insert((*c)->name(), *c);
3998 moduleName = "Qt3Support";
4000 if (!moduleName.isEmpty())
4001 moduleClassMap[moduleName].insert((*c)->name(), *c);
4003 QString serviceName =
4004 (static_cast<const ClassNode *>(*c))->serviceName();
4005 if (!serviceName.isEmpty())
4006 serviceClasses.insert(serviceName, *c);
4008 else if ((*c)->type() == Node::Fake &&
4009 (*c)->subType() == Node::QmlClass &&
4010 !(*c)->doc().isEmpty()) {
4011 QString qmlClassName = (*c)->name();
4012 qmlClasses.insert(qmlClassName,*c);
4014 else if ((*c)->isInnerNode()) {
4015 findAllClasses(static_cast<InnerNode *>(*c));
4022 void DitaXmlGenerator::findAllFunctions(const InnerNode* node)
4024 NodeList::ConstIterator c = node->childNodes().begin();
4025 while (c != node->childNodes().end()) {
4026 if ((*c)->access() != Node::Private) {
4027 if ((*c)->isInnerNode() && (*c)->url().isEmpty()) {
4028 findAllFunctions(static_cast<const InnerNode*>(*c));
4030 else if ((*c)->type() == Node::Function) {
4031 const FunctionNode* func = static_cast<const FunctionNode*>(*c);
4032 if ((func->status() > Node::Obsolete) &&
4033 !func->isInternal() &&
4034 (func->metaness() != FunctionNode::Ctor) &&
4035 (func->metaness() != FunctionNode::Dtor)) {
4036 funcIndex[(*c)->name()].insert((*c)->parent()->fullDocumentName(), *c);
4044 void DitaXmlGenerator::findAllLegaleseTexts(const InnerNode* node)
4046 NodeList::ConstIterator c = node->childNodes().begin();
4047 while (c != node->childNodes().end()) {
4048 if ((*c)->access() != Node::Private) {
4049 if (!(*c)->doc().legaleseText().isEmpty())
4050 legaleseTexts.insertMulti((*c)->doc().legaleseText(), *c);
4051 if ((*c)->isInnerNode())
4052 findAllLegaleseTexts(static_cast<const InnerNode *>(*c));
4058 void DitaXmlGenerator::findAllNamespaces(const InnerNode* node)
4060 NodeList::ConstIterator c = node->childNodes().begin();
4061 while (c != node->childNodes().end()) {
4062 if ((*c)->access() != Node::Private) {
4063 if ((*c)->isInnerNode() && (*c)->url().isEmpty()) {
4064 findAllNamespaces(static_cast<const InnerNode *>(*c));
4065 if ((*c)->type() == Node::Namespace) {
4066 const NamespaceNode *nspace = static_cast<const NamespaceNode *>(*c);
4067 // Ensure that the namespace's name is not empty (the root
4068 // namespace has no name).
4069 if (!nspace->name().isEmpty()) {
4070 namespaceIndex.insert(nspace->name(), *c);
4071 QString moduleName = (*c)->moduleName();
4072 if (moduleName == "Qt3SupportLight") {
4073 moduleNamespaceMap[moduleName].insert((*c)->name(), *c);
4074 moduleName = "Qt3Support";
4076 if (!moduleName.isEmpty())
4077 moduleNamespaceMap[moduleName].insert((*c)->name(), *c);
4087 We're writing an attribute that indicates that the text
4088 data is a heading, hence, h1, h2, h3... etc, and we must
4089 decide which number to use.
4091 int DitaXmlGenerator::hOffset(const Node* node)
4093 switch (node->type()) {
4094 case Node::Namespace:
4101 case Node::Function:
4102 case Node::Property:
4108 bool DitaXmlGenerator::isThreeColumnEnumValueTable(const Atom* atom)
4110 while (atom != 0 && !(atom->type() == Atom::ListRight && atom->string() == ATOM_LIST_VALUE)) {
4111 if (atom->type() == Atom::ListItemLeft && !matchAhead(atom, Atom::ListItemRight))
4113 atom = atom->next();
4118 const Node* DitaXmlGenerator::findNodeForTarget(const QString& target,
4119 const Node* relative,
4123 const Node* node = 0;
4125 if (target.isEmpty()) {
4128 else if (target.endsWith(".html")) {
4129 node = tree_->root()->findNode(target, Node::Fake);
4132 node = marker->resolveTarget(target, tree_, relative);
4134 node = tree_->findFakeNodeByTitle(target, relative);
4135 if (!node && atom) {
4136 node = tree_->findUnambiguousTarget(target, *const_cast<Atom**>(&atom), relative);
4141 relative->doc().location().warning(tr("Cannot link to '%1'").arg(target));
4146 const QPair<QString,QString> DitaXmlGenerator::anchorForNode(const Node* node)
4148 QPair<QString,QString> anchorPair;
4149 anchorPair.first = Generator::fileName(node);
4150 if (node->type() == Node::Fake) {
4151 const FakeNode *fakeNode = static_cast<const FakeNode*>(node);
4152 anchorPair.second = fakeNode->title();
4158 QString DitaXmlGenerator::getLink(const Atom* atom,
4159 const Node* relative,
4165 inObsoleteLink = false;
4167 if (atom->string().contains(QLatin1Char(':')) &&
4168 (atom->string().startsWith("file:")
4169 || atom->string().startsWith("http:")
4170 || atom->string().startsWith("https:")
4171 || atom->string().startsWith("ftp:")
4172 || atom->string().startsWith("mailto:"))) {
4174 link = atom->string();
4178 if (atom->string().contains('#'))
4179 path = atom->string().split('#');
4181 path.append(atom->string());
4183 Atom* targetAtom = 0;
4184 QString first = path.first().trimmed();
4186 if (first.isEmpty()) {
4189 else if (first.endsWith(".html")) {
4190 *node = tree_->root()->findNode(first, Node::Fake);
4193 *node = marker->resolveTarget(first, tree_, relative);
4195 *node = tree_->findFakeNodeByTitle(first, relative);
4198 *node = tree_->findUnambiguousTarget(first, targetAtom, relative);
4203 if (!(*node)->url().isEmpty()) {
4204 return (*node)->url();
4214 if (*node && (*node)->status() == Node::Obsolete) {
4215 if (relative && (relative->parent() != *node) &&
4216 (relative->status() != Node::Obsolete)) {
4217 bool porting = false;
4218 if (relative->type() == Node::Fake) {
4219 const FakeNode* fake = static_cast<const FakeNode*>(relative);
4220 if (fake->title().startsWith("Porting"))
4223 QString name = marker->plainFullName(relative);
4224 if (!porting && !name.startsWith("Q3")) {
4225 if (obsoleteLinks) {
4226 relative->doc().location().warning(tr("Link to obsolete item '%1' in %2")
4227 .arg(atom->string())
4230 inObsoleteLink = true;
4235 while (!path.isEmpty()) {
4236 targetAtom = tree_->findTarget(path.first(), *node);
4237 if (targetAtom == 0)
4242 if (path.isEmpty()) {
4243 link = linkForNode(*node, relative);
4244 if (*node && (*node)->subType() == Node::Image)
4245 link = "images/used-in-examples/" + link;
4248 link = outFileName();
4249 QString guid = lookupGuid(link,refForAtom(targetAtom,*node));
4250 link += QLatin1Char('#') + guid;
4252 else if (!link.isEmpty() && *node &&
4253 (link.endsWith(".xml") || link.endsWith(".dita"))) {
4254 link += QLatin1Char('#') + (*node)->guid();
4258 If the output is going to subdirectories, then if the
4259 two nodes will be output to different directories, then
4260 the link must go up to the parent directory and then
4261 back down into the other subdirectory.
4263 if (link.startsWith("images/")) {
4264 link.prepend(QString("../"));
4266 else if (*node && relative && (*node != relative)) {
4267 if ((*node)->outputSubdirectory() != relative->outputSubdirectory()) {
4268 link.prepend(QString("../" + (*node)->outputSubdirectory() + QLatin1Char('/')));
4272 if (!link.isEmpty() && link[0] == '#') {
4273 link.prepend(outFileName());
4274 qDebug() << "LOCAL LINK:" << link;
4280 This function can be called if getLink() returns an empty
4283 QString DitaXmlGenerator::getDisambiguationLink(const Atom *, CodeMarker *)
4285 qDebug() << "Unimplemented function called: "
4286 << "QString DitaXmlGenerator::getDisambiguationLink()";
4290 void DitaXmlGenerator::generateIndex(const QString& fileBase,
4292 const QString& title)
4294 tree_->generateIndex(outputDir() + QLatin1Char('/') + fileBase + ".index", url, title);
4297 void DitaXmlGenerator::generateStatus(const Node* node, CodeMarker* marker)
4301 switch (node->status()) {
4302 case Node::Obsolete:
4303 if (node->isInnerNode())
4304 Generator::generateStatus(node, marker);
4307 if (node->isInnerNode()) {
4308 text << Atom::ParaLeft
4309 << Atom(Atom::FormattingLeft,ATOM_FORMATTING_BOLD)
4312 << " is part of the Qt 3 support library."
4313 << Atom(Atom::FormattingRight, ATOM_FORMATTING_BOLD)
4314 << " It is provided to keep old source code working. "
4315 << "We strongly advise against "
4316 << "using it in new code. See ";
4318 const FakeNode *fakeNode = tree_->findFakeNodeByTitle("Porting To Qt 4");
4319 Atom *targetAtom = 0;
4320 if (fakeNode && node->type() == Node::Class) {
4321 QString oldName(node->name());
4322 oldName.remove(QLatin1Char('3'));
4323 targetAtom = tree_->findTarget(oldName,fakeNode);
4327 QString fn = fileName(fakeNode);
4328 QString guid = lookupGuid(fn,refForAtom(targetAtom,fakeNode));
4329 text << Atom(Atom::GuidLink, fn + QLatin1Char('#') + guid);
4332 text << Atom(Atom::Link, "Porting to Qt 4");
4334 text << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK)
4335 << Atom(Atom::String, "Porting to Qt 4")
4336 << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK)
4337 << " for more information."
4340 generateText(text, node, marker);
4343 Generator::generateStatus(node, marker);
4347 void DitaXmlGenerator::beginLink(const QString& link)
4352 writeStartTag(DT_xref);
4354 writeHrefAttribute(link);
4358 void DitaXmlGenerator::endLink()
4361 if (link.isEmpty()) {
4362 if (showBrokenLinks)
4363 writeEndTag(); // </i>
4366 if (inObsoleteLink) {
4367 writeStartTag(DT_sup);
4368 xmlWriter().writeCharacters("(obsolete)");
4369 writeEndTag(); // </sup>
4371 writeEndTag(); // </xref>
4375 inObsoleteLink = false;
4379 Generates the summary for the \a section. Only used for
4380 sections of QML element documentation.
4382 Currently handles only the QML property group.
4384 void DitaXmlGenerator::generateQmlSummary(const Section& section,
4385 const Node* relative,
4388 if (!section.members.isEmpty()) {
4389 writeStartTag(DT_ul);
4390 NodeList::ConstIterator m;
4391 m = section.members.begin();
4392 while (m != section.members.end()) {
4393 writeStartTag(DT_li);
4394 generateQmlItem(*m,relative,marker,true);
4395 writeEndTag(); // </li>
4398 writeEndTag(); // </ul>
4403 Outputs the DITA detailed documentation for a section
4404 on a QML element reference page.
4406 void DitaXmlGenerator::generateDetailedQmlMember(const Node* node,
4407 const InnerNode* relative,
4411 const QmlPropertyNode* qpn = 0;
4412 if (node->subType() == Node::QmlPropertyGroup) {
4413 const QmlPropGroupNode* qpgn = static_cast<const QmlPropGroupNode*>(node);
4414 NodeList::ConstIterator p = qpgn->childNodes().begin();
4415 writeStartTag(DT_ul);
4416 while (p != qpgn->childNodes().end()) {
4417 if ((*p)->type() == Node::QmlProperty) {
4418 qpn = static_cast<const QmlPropertyNode*>(*p);
4419 writeStartTag(DT_li);
4420 writeGuidAttribute((Node*)qpn);
4422 int ro = qpn->getReadOnly();
4424 if (!qpn->isWritable(tree_))
4429 if (qpgn->isDefault()) {
4430 if (!attr.isEmpty())
4431 attr += QLatin1Char(' ');
4434 if (!attr.isEmpty())
4435 xmlWriter().writeAttribute("outputclass",attr);
4436 generateQmlItem(qpn, relative, marker, false);
4437 writeEndTag(); // </li>
4441 writeEndTag(); // </ul>
4443 else if (node->type() == Node::QmlProperty) {
4444 qpn = static_cast<const QmlPropertyNode*>(node);
4446 If the QML property node has a single subproperty,
4447 override, replace qpn with that override node and
4450 if (qpn->qmlPropNodes().size() == 1) {
4451 Node* n = qpn->qmlPropNodes().at(0);
4452 if (n->type() == Node::QmlProperty)
4453 qpn = static_cast<const QmlPropertyNode*>(n);
4456 Now qpn either has no overrides, or it has more
4457 than 1. If it has none, proceed to output as nortmal.
4459 if (qpn->qmlPropNodes().isEmpty()) {
4460 writeStartTag(DT_ul);
4461 writeStartTag(DT_li);
4462 writeGuidAttribute((Node*)qpn);
4464 int ro = qpn->getReadOnly();
4466 const ClassNode* cn = qpn->declarativeCppNode();
4467 if (cn && !qpn->isWritable(tree_))
4472 if (qpn->isDefault()) {
4473 if (!attr.isEmpty())
4474 attr += QLatin1Char(' ');
4477 if (!attr.isEmpty())
4478 xmlWriter().writeAttribute("outputclass",attr);
4479 generateQmlItem(qpn, relative, marker, false);
4480 writeEndTag(); // </li>
4481 writeEndTag(); // </ul>
4485 The QML property node has multiple override nodes.
4486 Process the whole list as we would for a QML property
4489 NodeList::ConstIterator p = qpn->qmlPropNodes().begin();
4490 writeStartTag(DT_ul);
4491 while (p != qpn->qmlPropNodes().end()) {
4492 if ((*p)->type() == Node::QmlProperty) {
4493 QmlPropertyNode* q = static_cast<QmlPropertyNode*>(*p);
4494 writeStartTag(DT_li);
4495 writeGuidAttribute((Node*)q);
4497 int ro = qpn->getReadOnly();
4499 if (!qpn->isWritable(tree_))
4504 if (qpn->isDefault()) {
4505 if (!attr.isEmpty())
4506 attr += QLatin1Char(' ');
4509 if (!attr.isEmpty())
4510 xmlWriter().writeAttribute("outputclass",attr);
4511 generateQmlItem(q, relative, marker, false);
4512 writeEndTag(); // </li>
4516 writeEndTag(); // </ul>
4519 else if (node->type() == Node::QmlSignal) {
4520 Node* n = const_cast<Node*>(node);
4521 writeStartTag(DT_ul);
4522 writeStartTag(DT_li);
4523 writeGuidAttribute(n);
4524 marked = getMarkedUpSynopsis(n, relative, marker, CodeMarker::Detailed);
4525 writeText(marked, marker, relative);
4526 writeEndTag(); // </li>
4527 writeEndTag(); // </ul>
4529 else if (node->type() == Node::QmlSignalHandler) {
4530 Node* n = const_cast<Node*>(node);
4531 writeStartTag(DT_ul);
4532 writeStartTag(DT_li);
4533 writeGuidAttribute(n);
4534 marked = getMarkedUpSynopsis(n, relative, marker, CodeMarker::Detailed);
4535 writeText(marked, marker, relative);
4536 writeEndTag(); // </li>
4537 writeEndTag(); // </ul>
4539 else if (node->type() == Node::QmlMethod) {
4540 Node* n = const_cast<Node*>(node);
4541 writeStartTag(DT_ul);
4542 writeStartTag(DT_li);
4543 writeGuidAttribute(n);
4544 marked = getMarkedUpSynopsis(n, relative, marker, CodeMarker::Detailed);
4545 writeText(marked, marker, relative);
4546 writeEndTag(); // </li>
4547 writeEndTag(); // </ul>
4549 generateStatus(node, marker);
4550 generateBody(node, marker);
4551 generateThreadSafeness(node, marker);
4552 generateSince(node, marker);
4553 generateAlsoList(node, marker);
4557 Output the "Inherits" line for the QML element,
4558 if there should be one.
4560 void DitaXmlGenerator::generateQmlInherits(const QmlClassNode* qcn, CodeMarker* marker)
4564 const FakeNode* base = qcn->qmlBase();
4566 writeStartTag(DT_p);
4567 xmlWriter().writeAttribute("outputclass","inherits");
4569 text << "[Inherits ";
4570 text << Atom(Atom::LinkNode,CodeMarker::stringForNode(base));
4571 text << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK);
4572 text << Atom(Atom::String, base->name());
4573 text << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK);
4575 generateText(text, qcn, marker);
4576 writeEndTag(); // </p>
4581 Output the "[Xxx instantiates the C++ class QmlGraphicsXxx]"
4582 line for the QML element, if there should be one.
4584 If there is no class node, or if the class node status
4585 is set to Node::Internal, do nothing.
4587 void DitaXmlGenerator::generateQmlInstantiates(const QmlClassNode* qcn,
4590 const ClassNode* cn = qcn->classNode();
4591 if (cn && (cn->status() != Node::Internal)) {
4592 writeStartTag(DT_p);
4593 xmlWriter().writeAttribute("outputclass","instantiates");
4596 text << Atom(Atom::LinkNode,CodeMarker::stringForNode(qcn));
4597 text << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK);
4598 text << Atom(Atom::String, qcn->name());
4599 text << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK);
4600 text << " instantiates the C++ class ";
4601 text << Atom(Atom::LinkNode,CodeMarker::stringForNode(cn));
4602 text << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK);
4603 text << Atom(Atom::String, cn->name());
4604 text << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK);
4606 generateText(text, qcn, marker);
4607 writeEndTag(); // </p>
4612 Output the "[QmlGraphicsXxx is instantiated by QML element Xxx]"
4613 line for the class, if there should be one.
4615 If there is no QML element, or if the class node status
4616 is set to Node::Internal, do nothing.
4618 void DitaXmlGenerator::generateInstantiatedBy(const ClassNode* cn,
4621 if (cn && cn->status() != Node::Internal && cn->qmlElement() != 0) {
4622 const QmlClassNode* qcn = cn->qmlElement();
4623 writeStartTag(DT_p);
4624 xmlWriter().writeAttribute("outputclass","instantiated-by");
4627 text << Atom(Atom::LinkNode,CodeMarker::stringForNode(cn));
4628 text << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK);
4629 text << Atom(Atom::String, cn->name());
4630 text << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK);
4631 text << " is instantiated by QML element ";
4632 text << Atom(Atom::LinkNode,CodeMarker::stringForNode(qcn));
4633 text << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK);
4634 text << Atom(Atom::String, qcn->name());
4635 text << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK);
4637 generateText(text, cn, marker);
4638 writeEndTag(); // </p>
4643 Return the full qualification of the node \a n, but without
4644 the name of \a n itself. e.g. A::B::C
4646 QString DitaXmlGenerator::fullQualification(const Node* n)
4649 InnerNode* in = n->parent();
4651 if ((in->type() == Node::Class) ||
4652 (in->type() == Node::Namespace)) {
4653 if (in->name().isEmpty())
4658 fq = in->name() + "::" + fq;
4668 Outputs the <cxxClassDerivations> element.
4670 <cxxClassDerivations>
4671 <cxxClassDerivation>
4673 </cxxClassDerivation>
4675 </cxxClassDerivations>
4678 The <cxxClassDerivation> element is:
4681 <cxxClassDerivation>
4682 <cxxClassDerivationAccessSpecifier value="public"/>
4683 <cxxClassBaseClass href="class_base">Base</cxxClassBaseClass>
4684 </cxxClassDerivation>
4687 void DitaXmlGenerator::writeDerivations(const ClassNode* cn, CodeMarker* marker)
4689 QList<RelatedClass>::ConstIterator r;
4691 if (!cn->baseClasses().isEmpty()) {
4692 writeStartTag(DT_cxxClassDerivations);
4693 r = cn->baseClasses().begin();
4694 while (r != cn->baseClasses().end()) {
4695 writeStartTag(DT_cxxClassDerivation);
4696 writeStartTag(DT_cxxClassDerivationAccessSpecifier);
4697 xmlWriter().writeAttribute("value",(*r).accessString());
4698 writeEndTag(); // </cxxClassDerivationAccessSpecifier>
4700 // not included: <cxxClassDerivationVirtual>
4702 writeStartTag(DT_cxxClassBaseClass);
4703 QString attr = fileName((*r).node) + QLatin1Char('#') + (*r).node->guid();
4704 xmlWriter().writeAttribute("href",attr);
4705 writeCharacters(marker->plainFullName((*r).node));
4706 writeEndTag(); // </cxxClassBaseClass>
4708 // not included: <ClassBaseStruct> or <cxxClassBaseUnion>
4710 writeEndTag(); // </cxxClassDerivation>
4712 // not included: <cxxStructDerivation>
4716 writeEndTag(); // </cxxClassDerivations>
4721 Writes a <cxxXXXAPIItemLocation> element, depending on the
4722 type of the node \a n, which can be a class, function, enum,
4723 typedef, or property.
4725 void DitaXmlGenerator::writeLocation(const Node* n)
4727 DitaTag s1, s2, s3a, s3b;
4728 s1 = DT_cxxClassAPIItemLocation;
4729 s2 = DT_cxxClassDeclarationFile;
4730 s3a = DT_cxxClassDeclarationFileLineStart;
4731 s3b = DT_cxxClassDeclarationFileLineEnd;
4732 if (n->type() == Node::Class || n->type() == Node::Namespace) {
4733 s1 = DT_cxxClassAPIItemLocation;
4734 s2 = DT_cxxClassDeclarationFile;
4735 s3a = DT_cxxClassDeclarationFileLineStart;
4736 s3b = DT_cxxClassDeclarationFileLineEnd;
4738 else if (n->type() == Node::Function) {
4739 FunctionNode* fn = const_cast<FunctionNode*>(static_cast<const FunctionNode*>(n));
4740 if (fn->isMacro()) {
4741 s1 = DT_cxxDefineAPIItemLocation;
4742 s2 = DT_cxxDefineDeclarationFile;
4743 s3a = DT_cxxDefineDeclarationFileLine;
4747 s1 = DT_cxxFunctionAPIItemLocation;
4748 s2 = DT_cxxFunctionDeclarationFile;
4749 s3a = DT_cxxFunctionDeclarationFileLine;
4753 else if (n->type() == Node::Enum) {
4754 s1 = DT_cxxEnumerationAPIItemLocation;
4755 s2 = DT_cxxEnumerationDeclarationFile;
4756 s3a = DT_cxxEnumerationDeclarationFileLineStart;
4757 s3b = DT_cxxEnumerationDeclarationFileLineEnd;
4759 else if (n->type() == Node::Typedef) {
4760 s1 = DT_cxxTypedefAPIItemLocation;
4761 s2 = DT_cxxTypedefDeclarationFile;
4762 s3a = DT_cxxTypedefDeclarationFileLine;
4765 else if ((n->type() == Node::Property) ||
4766 (n->type() == Node::Variable)) {
4767 s1 = DT_cxxVariableAPIItemLocation;
4768 s2 = DT_cxxVariableDeclarationFile;
4769 s3a = DT_cxxVariableDeclarationFileLine;
4774 xmlWriter().writeAttribute("name","filePath");
4775 xmlWriter().writeAttribute("value",n->location().filePath());
4776 writeEndTag(); // <s2>
4778 xmlWriter().writeAttribute("name","lineNumber");
4780 xmlWriter().writeAttribute("value",lineNr.setNum(n->location().lineNo()));
4781 writeEndTag(); // </s3a>
4782 if (s3b != DT_NONE) {
4784 xmlWriter().writeAttribute("name","lineNumber");
4786 xmlWriter().writeAttribute("value",lineNr.setNum(n->location().lineNo()));
4787 writeEndTag(); // </s3b>
4789 writeEndTag(); // </cxx<s1>ApiItemLocation>
4793 Write the <cxxFunction> elements.
4795 void DitaXmlGenerator::writeFunctions(const Section& s,
4796 const InnerNode* parent,
4798 const QString& attribute)
4800 NodeList::ConstIterator m = s.members.begin();
4801 while (m != s.members.end()) {
4802 if ((*m)->type() == Node::Function) {
4803 FunctionNode* fn = const_cast<FunctionNode*>(static_cast<const FunctionNode*>(*m));
4804 writeStartTag(DT_cxxFunction);
4805 xmlWriter().writeAttribute("id",fn->guid());
4806 if (!attribute.isEmpty())
4807 xmlWriter().writeAttribute("outputclass",attribute);
4808 writeStartTag(DT_apiName);
4809 if (fn->metaness() == FunctionNode::Signal)
4810 xmlWriter().writeAttribute("outputclass","signal");
4811 else if (fn->metaness() == FunctionNode::Slot)
4812 xmlWriter().writeAttribute("outputclass","slot");
4813 writeCharacters(fn->name());
4814 writeEndTag(); // </apiName>
4815 generateBrief(fn,marker);
4817 // not included: <prolog>
4819 writeStartTag(DT_cxxFunctionDetail);
4820 writeStartTag(DT_cxxFunctionDefinition);
4821 writeStartTag(DT_cxxFunctionAccessSpecifier);
4822 xmlWriter().writeAttribute("value",fn->accessString());
4823 writeEndTag(); // <cxxFunctionAccessSpecifier>
4825 // not included: <cxxFunctionStorageClassSpecifierExtern>
4827 if (fn->isStatic()) {
4828 writeStartTag(DT_cxxFunctionStorageClassSpecifierStatic);
4829 xmlWriter().writeAttribute("name","static");
4830 xmlWriter().writeAttribute("value","static");
4831 writeEndTag(); // <cxxFunctionStorageClassSpecifierStatic>
4834 // not included: <cxxFunctionStorageClassSpecifierMutable>,
4836 if (fn->isConst()) {
4837 writeStartTag(DT_cxxFunctionConst);
4838 xmlWriter().writeAttribute("name","const");
4839 xmlWriter().writeAttribute("value","const");
4840 writeEndTag(); // <cxxFunctionConst>
4843 // not included: <cxxFunctionExplicit>
4844 // <cxxFunctionInline
4846 if (fn->virtualness() != FunctionNode::NonVirtual) {
4847 writeStartTag(DT_cxxFunctionVirtual);
4848 xmlWriter().writeAttribute("name","virtual");
4849 xmlWriter().writeAttribute("value","virtual");
4850 writeEndTag(); // <cxxFunctionVirtual>
4851 if (fn->virtualness() == FunctionNode::PureVirtual) {
4852 writeStartTag(DT_cxxFunctionPureVirtual);
4853 xmlWriter().writeAttribute("name","pure virtual");
4854 xmlWriter().writeAttribute("value","pure virtual");
4855 writeEndTag(); // <cxxFunctionPureVirtual>
4859 if (fn->name() == parent->name()) {
4860 writeStartTag(DT_cxxFunctionConstructor);
4861 xmlWriter().writeAttribute("name","constructor");
4862 xmlWriter().writeAttribute("value","constructor");
4863 writeEndTag(); // <cxxFunctionConstructor>
4865 else if (fn->name()[0] == QChar('~')) {
4866 writeStartTag(DT_cxxFunctionDestructor);
4867 xmlWriter().writeAttribute("name","destructor");
4868 xmlWriter().writeAttribute("value","destructor");
4869 writeEndTag(); // <cxxFunctionDestructor>
4872 writeStartTag(DT_cxxFunctionDeclaredType);
4873 QString src = marker->typified(fn->returnType());
4874 replaceTypesWithLinks(fn,parent,marker,src);
4875 writeEndTag(); // <cxxFunctionDeclaredType>
4878 // not included: <cxxFunctionReturnType>
4880 QString fq = fullQualification(fn);
4881 if (!fq.isEmpty()) {
4882 writeStartTag(DT_cxxFunctionScopedName);
4883 writeCharacters(fq);
4884 writeEndTag(); // <cxxFunctionScopedName>
4886 writeStartTag(DT_cxxFunctionPrototype);
4887 writeCharacters(fn->signature(true));
4888 writeEndTag(); // <cxxFunctionPrototype>
4890 QString fnl = fn->signature(false);
4891 int idx = fnl.indexOf(' ');
4896 fnl = fn->parent()->name() + "::" + fnl.mid(idx);
4897 writeStartTag(DT_cxxFunctionNameLookup);
4898 writeCharacters(fnl);
4899 writeEndTag(); // <cxxFunctionNameLookup>
4901 if (!fn->isInternal() && fn->isReimp() && fn->reimplementedFrom() != 0) {
4902 FunctionNode* rfn = (FunctionNode*)fn->reimplementedFrom();
4903 if (rfn && !rfn->isInternal()) {
4904 writeStartTag(DT_cxxFunctionReimplemented);
4905 xmlWriter().writeAttribute("href",rfn->ditaXmlHref());
4906 writeCharacters(marker->plainFullName(rfn));
4907 writeEndTag(); // </cxxFunctionReimplemented>
4910 writeParameters(fn,parent,marker);
4912 writeEndTag(); // <cxxFunctionDefinition>
4914 writeApiDesc(fn, marker, QString());
4915 // generateAlsoList(inner, marker);
4917 // not included: <example> or <apiImpl>
4919 writeEndTag(); // </cxxFunctionDetail>
4920 writeEndTag(); // </cxxFunction>
4922 if (fn->metaness() == FunctionNode::Ctor ||
4923 fn->metaness() == FunctionNode::Dtor ||
4924 fn->overloadNumber() != 1) {
4931 static const QString typeTag("type");
4932 static const QChar charLangle = '<';
4933 static const QChar charAt = '@';
4936 This function replaces class and enum names with <apiRelation>
4937 elements, i.e. links.
4939 void DitaXmlGenerator::replaceTypesWithLinks(const Node* n,
4940 const InnerNode* parent,
4946 int srcSize = src.size();
4948 for (int i=0; i<srcSize;) {
4949 if (src.at(i) == charLangle && src.at(i+1) == charAt) {
4950 if (!text.isEmpty()) {
4951 writeCharacters(text);
4955 if (parseArg(src, typeTag, &i, srcSize, &arg, &par1)) {
4956 const Node* tn = marker->resolveTarget(arg.toString(), tree_, parent, n);
4958 //Do not generate a link from a C++ function to a QML Basic Type (such as int)
4959 if (n->type() == Node::Function && tn->subType() == Node::QmlBasicType)
4960 writeCharacters(arg.toString());
4962 addLink(linkForNode(tn,parent),arg,DT_apiRelation);
4967 text += src.at(i++);
4970 if (!text.isEmpty()) {
4971 writeCharacters(text);
4977 This function writes the <cxxFunctionParameters> element.
4979 void DitaXmlGenerator::writeParameters(const FunctionNode* fn,
4980 const InnerNode* parent,
4983 const QList<Parameter>& parameters = fn->parameters();
4984 if (!parameters.isEmpty()) {
4985 writeStartTag(DT_cxxFunctionParameters);
4986 QList<Parameter>::ConstIterator p = parameters.begin();
4987 while (p != parameters.end()) {
4988 writeStartTag(DT_cxxFunctionParameter);
4989 writeStartTag(DT_cxxFunctionParameterDeclaredType);
4990 QString src = marker->typified((*p).leftType());
4991 replaceTypesWithLinks(fn,parent,marker,src);
4992 //writeCharacters((*p).leftType());
4993 if (!(*p).rightType().isEmpty())
4994 writeCharacters((*p).rightType());
4995 writeEndTag(); // <cxxFunctionParameterDeclaredType>
4996 writeStartTag(DT_cxxFunctionParameterDeclarationName);
4997 writeCharacters((*p).name());
4998 writeEndTag(); // <cxxFunctionParameterDeclarationName>
5000 // not included: <cxxFunctionParameterDefinitionName>
5002 if (!(*p).defaultValue().isEmpty()) {
5003 writeStartTag(DT_cxxFunctionParameterDefaultValue);
5004 writeCharacters((*p).defaultValue());
5005 writeEndTag(); // <cxxFunctionParameterDefaultValue>
5008 // not included: <apiDefNote>
5010 writeEndTag(); // <cxxFunctionParameter>
5013 writeEndTag(); // <cxxFunctionParameters>
5018 This function writes the enum types.
5020 void DitaXmlGenerator::writeEnumerations(const Section& s,
5022 const QString& attribute)
5024 NodeList::ConstIterator m = s.members.begin();
5025 while (m != s.members.end()) {
5026 if ((*m)->type() == Node::Enum) {
5027 const EnumNode* en = static_cast<const EnumNode*>(*m);
5028 writeStartTag(DT_cxxEnumeration);
5029 xmlWriter().writeAttribute("id",en->guid());
5030 if (!attribute.isEmpty())
5031 xmlWriter().writeAttribute("outputclass",attribute);
5032 writeStartTag(DT_apiName);
5033 writeCharacters(en->name());
5034 writeEndTag(); // </apiName>
5035 generateBrief(en,marker);
5037 // not included <prolog>
5039 writeStartTag(DT_cxxEnumerationDetail);
5040 writeStartTag(DT_cxxEnumerationDefinition);
5041 writeStartTag(DT_cxxEnumerationAccessSpecifier);
5042 xmlWriter().writeAttribute("value",en->accessString());
5043 writeEndTag(); // <cxxEnumerationAccessSpecifier>
5045 QString fq = fullQualification(en);
5046 if (!fq.isEmpty()) {
5047 writeStartTag(DT_cxxEnumerationScopedName);
5048 writeCharacters(fq);
5049 writeEndTag(); // <cxxEnumerationScopedName>
5051 const QList<EnumItem>& items = en->items();
5052 if (!items.isEmpty()) {
5053 writeStartTag(DT_cxxEnumerationPrototype);
5054 writeCharacters(en->name());
5055 xmlWriter().writeCharacters(" = { ");
5056 QList<EnumItem>::ConstIterator i = items.begin();
5057 while (i != items.end()) {
5058 writeCharacters((*i).name());
5059 if (!(*i).value().isEmpty()) {
5060 xmlWriter().writeCharacters(" = ");
5061 writeCharacters((*i).value());
5064 if (i != items.end())
5065 xmlWriter().writeCharacters(", ");
5067 xmlWriter().writeCharacters(" }");
5068 writeEndTag(); // <cxxEnumerationPrototype>
5071 writeStartTag(DT_cxxEnumerationNameLookup);
5072 writeCharacters(en->parent()->name() + "::" + en->name());
5073 writeEndTag(); // <cxxEnumerationNameLookup>
5075 // not included: <cxxEnumerationReimplemented>
5077 if (!items.isEmpty()) {
5078 writeStartTag(DT_cxxEnumerators);
5079 QList<EnumItem>::ConstIterator i = items.begin();
5080 while (i != items.end()) {
5081 writeStartTag(DT_cxxEnumerator);
5082 writeStartTag(DT_apiName);
5083 writeCharacters((*i).name());
5084 writeEndTag(); // </apiName>
5086 QString fq = fullQualification(en->parent());
5087 if (!fq.isEmpty()) {
5088 writeStartTag(DT_cxxEnumeratorScopedName);
5089 writeCharacters(fq + "::" + (*i).name());
5090 writeEndTag(); // <cxxEnumeratorScopedName>
5092 writeStartTag(DT_cxxEnumeratorPrototype);
5093 writeCharacters((*i).name());
5094 writeEndTag(); // <cxxEnumeratorPrototype>
5095 writeStartTag(DT_cxxEnumeratorNameLookup);
5096 writeCharacters(en->parent()->name() + "::" + (*i).name());
5097 writeEndTag(); // <cxxEnumeratorNameLookup>
5099 if (!(*i).value().isEmpty()) {
5100 writeStartTag(DT_cxxEnumeratorInitialiser);
5101 if ((*i).value().toInt(0,16) == 0)
5102 xmlWriter().writeAttribute("value", "0");
5104 xmlWriter().writeAttribute("value", (*i).value());
5105 writeEndTag(); // <cxxEnumeratorInitialiser>
5108 // not included: <cxxEnumeratorAPIItemLocation>
5110 if (!(*i).text().isEmpty()) {
5111 writeStartTag(DT_apiDesc);
5112 generateText((*i).text(), en, marker);
5113 writeEndTag(); // </apiDesc>
5115 writeEndTag(); // <cxxEnumerator>
5118 writeEndTag(); // <cxxEnumerators>
5122 writeEndTag(); // <cxxEnumerationDefinition>
5124 writeApiDesc(en, marker, QString());
5126 // not included: <example> or <apiImpl>
5128 writeEndTag(); // </cxxEnumerationDetail>
5130 // not included: <related-links>
5132 writeEndTag(); // </cxxEnumeration>
5139 This function writes the output for the \typedef commands.
5141 void DitaXmlGenerator::writeTypedefs(const Section& s,
5143 const QString& attribute)
5146 NodeList::ConstIterator m = s.members.begin();
5147 while (m != s.members.end()) {
5148 if ((*m)->type() == Node::Typedef) {
5149 const TypedefNode* tn = static_cast<const TypedefNode*>(*m);
5150 writeStartTag(DT_cxxTypedef);
5151 xmlWriter().writeAttribute("id",tn->guid());
5152 if (!attribute.isEmpty())
5153 xmlWriter().writeAttribute("outputclass",attribute);
5154 writeStartTag(DT_apiName);
5155 writeCharacters(tn->name());
5156 writeEndTag(); // </apiName>
5157 generateBrief(tn,marker);
5159 // not included: <prolog>
5161 writeStartTag(DT_cxxTypedefDetail);
5162 writeStartTag(DT_cxxTypedefDefinition);
5163 writeStartTag(DT_cxxTypedefAccessSpecifier);
5164 xmlWriter().writeAttribute("value",tn->accessString());
5165 writeEndTag(); // <cxxTypedefAccessSpecifier>
5167 // not included: <cxxTypedefDeclaredType>
5169 QString fq = fullQualification(tn);
5170 if (!fq.isEmpty()) {
5171 writeStartTag(DT_cxxTypedefScopedName);
5172 writeCharacters(fq);
5173 writeEndTag(); // <cxxTypedefScopedName>
5176 // not included: <cxxTypedefPrototype>
5178 writeStartTag(DT_cxxTypedefNameLookup);
5179 writeCharacters(tn->parent()->name() + "::" + tn->name());
5180 writeEndTag(); // <cxxTypedefNameLookup>
5182 // not included: <cxxTypedefReimplemented>
5185 writeEndTag(); // <cxxTypedefDefinition>
5187 writeApiDesc(tn, marker, QString());
5189 // not included: <example> or <apiImpl>
5191 writeEndTag(); // </cxxTypedefDetail>
5193 // not included: <related-links>
5195 writeEndTag(); // </cxxTypedef>
5202 This function writes the output for the \property commands.
5203 This is the Q_PROPERTYs.
5205 void DitaXmlGenerator::writeProperties(const Section& s,
5207 const QString& attribute)
5209 NodeList::ConstIterator m = s.members.begin();
5210 while (m != s.members.end()) {
5211 if ((*m)->type() == Node::Property) {
5212 const PropertyNode* pn = static_cast<const PropertyNode*>(*m);
5213 writeStartTag(DT_cxxVariable);
5214 xmlWriter().writeAttribute("id",pn->guid());
5215 if (!attribute.isEmpty())
5216 xmlWriter().writeAttribute("outputclass",attribute);
5217 writeStartTag(DT_apiName);
5218 writeCharacters(pn->name());
5219 writeEndTag(); // </apiName>
5220 generateBrief(pn,marker);
5222 // not included: <prolog>
5224 writeStartTag(DT_cxxVariableDetail);
5225 writeStartTag(DT_cxxVariableDefinition);
5226 writeStartTag(DT_cxxVariableAccessSpecifier);
5227 xmlWriter().writeAttribute("value",pn->accessString());
5228 writeEndTag(); // <cxxVariableAccessSpecifier>
5230 // not included: <cxxVariableStorageClassSpecifierExtern>,
5231 // <cxxVariableStorageClassSpecifierStatic>,
5232 // <cxxVariableStorageClassSpecifierMutable>,
5233 // <cxxVariableConst>, <cxxVariableVolatile>
5235 if (!pn->qualifiedDataType().isEmpty()) {
5236 writeStartTag(DT_cxxVariableDeclaredType);
5237 writeCharacters(pn->qualifiedDataType());
5238 writeEndTag(); // <cxxVariableDeclaredType>
5240 QString fq = fullQualification(pn);
5241 if (!fq.isEmpty()) {
5242 writeStartTag(DT_cxxVariableScopedName);
5243 writeCharacters(fq);
5244 writeEndTag(); // <cxxVariableScopedName>
5247 writeStartTag(DT_cxxVariablePrototype);
5248 xmlWriter().writeCharacters("Q_PROPERTY(");
5249 writeCharacters(pn->qualifiedDataType());
5250 xmlWriter().writeCharacters(" ");
5251 writeCharacters(pn->name());
5252 writePropertyParameter("READ",pn->getters());
5253 writePropertyParameter("WRITE",pn->setters());
5254 writePropertyParameter("RESET",pn->resetters());
5255 writePropertyParameter("NOTIFY",pn->notifiers());
5256 if (pn->isDesignable() != pn->designableDefault()) {
5257 xmlWriter().writeCharacters(" DESIGNABLE ");
5258 if (!pn->runtimeDesignabilityFunction().isEmpty())
5259 writeCharacters(pn->runtimeDesignabilityFunction());
5261 xmlWriter().writeCharacters(pn->isDesignable() ? "true" : "false");
5263 if (pn->isScriptable() != pn->scriptableDefault()) {
5264 xmlWriter().writeCharacters(" SCRIPTABLE ");
5265 if (!pn->runtimeScriptabilityFunction().isEmpty())
5266 writeCharacters(pn->runtimeScriptabilityFunction());
5268 xmlWriter().writeCharacters(pn->isScriptable() ? "true" : "false");
5270 if (pn->isWritable() != pn->writableDefault()) {
5271 xmlWriter().writeCharacters(" STORED ");
5272 xmlWriter().writeCharacters(pn->isStored() ? "true" : "false");
5274 if (pn->isUser() != pn->userDefault()) {
5275 xmlWriter().writeCharacters(" USER ");
5276 xmlWriter().writeCharacters(pn->isUser() ? "true" : "false");
5278 if (pn->isConstant())
5279 xmlWriter().writeCharacters(" CONSTANT");
5281 xmlWriter().writeCharacters(" FINAL");
5282 xmlWriter().writeCharacters(")");
5283 writeEndTag(); // <cxxVariablePrototype>
5285 writeStartTag(DT_cxxVariableNameLookup);
5286 writeCharacters(pn->parent()->name() + "::" + pn->name());
5287 writeEndTag(); // <cxxVariableNameLookup>
5289 if (pn->overriddenFrom() != 0) {
5290 PropertyNode* opn = (PropertyNode*)pn->overriddenFrom();
5291 writeStartTag(DT_cxxVariableReimplemented);
5292 xmlWriter().writeAttribute("href",opn->ditaXmlHref());
5293 writeCharacters(marker->plainFullName(opn));
5294 writeEndTag(); // </cxxVariableReimplemented>
5298 writeEndTag(); // <cxxVariableDefinition>
5300 writeApiDesc(pn, marker, QString());
5302 // not included: <example> or <apiImpl>
5304 writeEndTag(); // </cxxVariableDetail>
5306 // not included: <related-links>
5308 writeEndTag(); // </cxxVariable>
5315 This function outputs the nodes resulting from \variable commands.
5317 void DitaXmlGenerator::writeDataMembers(const Section& s,
5319 const QString& attribute)
5321 NodeList::ConstIterator m = s.members.begin();
5322 while (m != s.members.end()) {
5323 if ((*m)->type() == Node::Variable) {
5324 const VariableNode* vn = static_cast<const VariableNode*>(*m);
5325 writeStartTag(DT_cxxVariable);
5326 xmlWriter().writeAttribute("id",vn->guid());
5327 if (!attribute.isEmpty())
5328 xmlWriter().writeAttribute("outputclass",attribute);
5329 writeStartTag(DT_apiName);
5330 writeCharacters(vn->name());
5331 writeEndTag(); // </apiName>
5332 generateBrief(vn,marker);
5334 // not included: <prolog>
5336 writeStartTag(DT_cxxVariableDetail);
5337 writeStartTag(DT_cxxVariableDefinition);
5338 writeStartTag(DT_cxxVariableAccessSpecifier);
5339 xmlWriter().writeAttribute("value",vn->accessString());
5340 writeEndTag(); // <cxxVariableAccessSpecifier>
5342 // not included: <cxxVAriableStorageClassSpecifierExtern>
5344 if (vn->isStatic()) {
5345 writeStartTag(DT_cxxVariableStorageClassSpecifierStatic);
5346 xmlWriter().writeAttribute("name","static");
5347 xmlWriter().writeAttribute("value","static");
5348 writeEndTag(); // <cxxVariableStorageClassSpecifierStatic>
5351 // not included: <cxxVAriableStorageClassSpecifierMutable>,
5352 // <cxxVariableConst>, <cxxVariableVolatile>
5354 writeStartTag(DT_cxxVariableDeclaredType);
5355 writeCharacters(vn->leftType());
5356 if (!vn->rightType().isEmpty())
5357 writeCharacters(vn->rightType());
5358 writeEndTag(); // <cxxVariableDeclaredType>
5360 QString fq = fullQualification(vn);
5361 if (!fq.isEmpty()) {
5362 writeStartTag(DT_cxxVariableScopedName);
5363 writeCharacters(fq);
5364 writeEndTag(); // <cxxVariableScopedName>
5367 writeStartTag(DT_cxxVariablePrototype);
5368 writeCharacters(vn->leftType() + QLatin1Char(' '));
5369 //writeCharacters(vn->parent()->name() + "::" + vn->name());
5370 writeCharacters(vn->name());
5371 if (!vn->rightType().isEmpty())
5372 writeCharacters(vn->rightType());
5373 writeEndTag(); // <cxxVariablePrototype>
5375 writeStartTag(DT_cxxVariableNameLookup);
5376 writeCharacters(vn->parent()->name() + "::" + vn->name());
5377 writeEndTag(); // <cxxVariableNameLookup>
5379 // not included: <cxxVariableReimplemented>
5382 writeEndTag(); // <cxxVariableDefinition>
5384 writeApiDesc(vn, marker, QString());
5386 // not included: <example> or <apiImpl>
5388 writeEndTag(); // </cxxVariableDetail>
5390 // not included: <related-links>
5392 writeEndTag(); // </cxxVariable>
5399 This function writes a \macro as a <cxxDefine>.
5401 void DitaXmlGenerator::writeMacros(const Section& s,
5403 const QString& attribute)
5405 NodeList::ConstIterator m = s.members.begin();
5406 while (m != s.members.end()) {
5407 if ((*m)->type() == Node::Function) {
5408 const FunctionNode* fn = static_cast<const FunctionNode*>(*m);
5409 if (fn->isMacro()) {
5410 writeStartTag(DT_cxxDefine);
5411 xmlWriter().writeAttribute("id",fn->guid());
5412 if (!attribute.isEmpty())
5413 xmlWriter().writeAttribute("outputclass",attribute);
5414 writeStartTag(DT_apiName);
5415 writeCharacters(fn->name());
5416 writeEndTag(); // </apiName>
5417 generateBrief(fn,marker);
5419 // not included: <prolog>
5421 writeStartTag(DT_cxxDefineDetail);
5422 writeStartTag(DT_cxxDefineDefinition);
5423 writeStartTag(DT_cxxDefineAccessSpecifier);
5424 xmlWriter().writeAttribute("value",fn->accessString());
5425 writeEndTag(); // <cxxDefineAccessSpecifier>
5427 writeStartTag(DT_cxxDefinePrototype);
5428 xmlWriter().writeCharacters("#define ");
5429 writeCharacters(fn->name());
5430 if (fn->metaness() == FunctionNode::MacroWithParams) {
5431 QStringList params = fn->parameterNames();
5432 if (!params.isEmpty()) {
5433 xmlWriter().writeCharacters("(");
5434 for (int i = 0; i < params.size(); ++i) {
5435 if (params[i].isEmpty())
5436 xmlWriter().writeCharacters("...");
5438 writeCharacters(params[i]);
5439 if ((i+1) < params.size())
5440 xmlWriter().writeCharacters(", ");
5442 xmlWriter().writeCharacters(")");
5445 writeEndTag(); // <cxxDefinePrototype>
5447 writeStartTag(DT_cxxDefineNameLookup);
5448 writeCharacters(fn->name());
5449 writeEndTag(); // <cxxDefineNameLookup>
5451 if (fn->reimplementedFrom() != 0) {
5452 FunctionNode* rfn = (FunctionNode*)fn->reimplementedFrom();
5453 writeStartTag(DT_cxxDefineReimplemented);
5454 xmlWriter().writeAttribute("href",rfn->ditaXmlHref());
5455 writeCharacters(marker->plainFullName(rfn));
5456 writeEndTag(); // </cxxDefineReimplemented>
5459 if (fn->metaness() == FunctionNode::MacroWithParams) {
5460 QStringList params = fn->parameterNames();
5461 if (!params.isEmpty()) {
5462 writeStartTag(DT_cxxDefineParameters);
5463 for (int i = 0; i < params.size(); ++i) {
5464 writeStartTag(DT_cxxDefineParameter);
5465 writeStartTag(DT_cxxDefineParameterDeclarationName);
5466 writeCharacters(params[i]);
5467 writeEndTag(); // <cxxDefineParameterDeclarationName>
5469 // not included: <apiDefNote>
5471 writeEndTag(); // <cxxDefineParameter>
5473 writeEndTag(); // <cxxDefineParameters>
5478 writeEndTag(); // <cxxDefineDefinition>
5480 writeApiDesc(fn, marker, QString());
5482 // not included: <example> or <apiImpl>
5484 writeEndTag(); // </cxxDefineDetail>
5486 // not included: <related-links>
5488 writeEndTag(); // </cxxDefine>
5496 This function writes one parameter of a Q_PROPERTY macro.
5497 The property is identified by \a tag ("READ" "WRIE" etc),
5498 and it is found in the 'a nlist.
5500 void DitaXmlGenerator::writePropertyParameter(const QString& tag, const NodeList& nlist)
5502 NodeList::const_iterator n = nlist.begin();
5503 while (n != nlist.end()) {
5504 xmlWriter().writeCharacters(" ");
5505 writeCharacters(tag);
5506 xmlWriter().writeCharacters(" ");
5507 writeCharacters((*n)->name());
5513 Calls beginSubPage() in the base class to open the file.
5514 Then creates a new XML stream writer using the IO device
5515 from opened file and pushes the XML writer onto a stackj.
5516 Creates the file named \a fileName in the output directory.
5517 Attaches a QTextStream to the created file, which is written
5518 to all over the place using out(). Finally, it sets some
5519 parameters in the XML writer and calls writeStartDocument().
5521 It also ensures that a GUID map is created for the output file.
5523 void DitaXmlGenerator::beginSubPage(const InnerNode* node,
5524 const QString& fileName)
5526 Generator::beginSubPage(node,fileName);
5527 (void) lookupGuidMap(fileName);
5528 QXmlStreamWriter* writer = new QXmlStreamWriter(out().device());
5529 xmlWriterStack.push(writer);
5530 writer->setAutoFormatting(true);
5531 writer->setAutoFormattingIndent(4);
5532 writer->writeStartDocument();
5533 clearSectionNesting();
5537 Calls writeEndDocument() and then pops the XML stream writer
5538 off the stack and deletes it. Then it calls endSubPage() in
5539 the base class to close the device.
5541 void DitaXmlGenerator::endSubPage()
5544 qDebug() << "Missing </section> in" << outFileName() << sectionNestingLevel;
5545 xmlWriter().writeEndDocument();
5546 delete xmlWriterStack.pop();
5547 Generator::endSubPage();
5551 Returns a reference to the XML stream writer currently in use.
5552 There is one XML stream writer open for each XML file being
5553 written, and they are kept on a stack. The one on top of the
5554 stack is the one being written to at the moment.
5556 QXmlStreamWriter& DitaXmlGenerator::xmlWriter()
5558 return *xmlWriterStack.top();
5562 Writes the \e {<apiDesc>} element for \a node to the current XML
5563 stream using the code \a marker and the \a title.
5565 void DitaXmlGenerator::writeApiDesc(const Node* node,
5567 const QString& title)
5569 if (!node->doc().isEmpty()) {
5570 inDetailedDescription = true;
5571 enterApiDesc(QString(),title);
5572 generateBody(node, marker);
5573 generateAlsoList(node, marker);
5576 inDetailedDescription = false;
5580 Write the nested class elements.
5582 void DitaXmlGenerator::writeNestedClasses(const Section& s,
5585 if (s.members.isEmpty())
5587 writeStartTag(DT_cxxClassNested);
5588 writeStartTag(DT_cxxClassNestedDetail);
5590 NodeList::ConstIterator m = s.members.begin();
5591 while (m != s.members.end()) {
5592 if ((*m)->type() == Node::Class) {
5593 writeStartTag(DT_cxxClassNestedClass);
5594 QString link = linkForNode((*m), n);
5595 xmlWriter().writeAttribute("href", link);
5596 QString name = n->name() + "::" + (*m)->name();
5597 writeCharacters(name);
5598 writeEndTag(); // <cxxClassNestedClass>
5602 writeEndTag(); // <cxxClassNestedDetail>
5603 writeEndTag(); // <cxxClassNested>
5607 Recursive writing of DITA XML files from the root \a node.
5610 DitaXmlGenerator::generateInnerNode(const InnerNode* node)
5612 if (!node->url().isNull())
5615 if (node->type() == Node::Fake) {
5616 const FakeNode *fakeNode = static_cast<const FakeNode *>(node);
5617 if (fakeNode->subType() == Node::ExternalPage)
5619 if (fakeNode->subType() == Node::Image)
5621 if (fakeNode->subType() == Node::QmlPropertyGroup)
5623 if (fakeNode->subType() == Node::Page) {
5624 if (node->count() > 0)
5625 qDebug("PAGE %s HAS CHILDREN", qPrintable(fakeNode->title()));
5630 Obtain a code marker for the source file.
5632 CodeMarker *marker = CodeMarker::markerForFileName(node->location().filePath());
5634 if (node->parent() != 0) {
5635 if (!node->name().endsWith(".ditamap"))
5636 beginSubPage(node, fileName(node));
5637 if (node->type() == Node::Namespace || node->type() == Node::Class) {
5638 generateClassLikeNode(node, marker);
5640 else if (node->type() == Node::Fake) {
5641 if (node->subType() == Node::HeaderFile)
5642 generateClassLikeNode(node, marker);
5643 else if (node->subType() == Node::QmlClass)
5644 generateClassLikeNode(node, marker);
5646 generateFakeNode(static_cast<const FakeNode*>(node), marker);
5648 if (!node->name().endsWith(".ditamap"))
5652 NodeList::ConstIterator c = node->childNodes().begin();
5653 while (c != node->childNodes().end()) {
5654 if ((*c)->isInnerNode() && (*c)->access() != Node::Private)
5655 generateInnerNode((const InnerNode*) *c);
5661 Returns true if \a format is "XML" or "HTML" .
5663 bool DitaXmlGenerator::canHandleFormat(const QString& format)
5665 return (format == "HTML") || (format == this->format());
5669 If the node multimap \a nmm contains nodes mapped to \a key,
5670 if any of the nodes mapped to \a key has the same href as the
5671 \a node, return true. Otherwise, return false.
5673 bool DitaXmlGenerator::isDuplicate(NodeMultiMap* nmm, const QString& key, Node* node)
5675 QList<Node*> matches = nmm->values(key);
5676 if (!matches.isEmpty()) {
5677 for (int i=0; i<matches.size(); ++i) {
5678 if (matches[i] == node)
5680 if (fileName(node) == fileName(matches[i]))
5687 Collect all the nodes in the tree according to their type or subtype.
5689 If a node is found that is named index.html, return that node as the
5696 subtype: External page
5698 subtype: Header file
5701 subtype: QML basic type
5705 Node* DitaXmlGenerator::collectNodesByTypeAndSubtype(const InnerNode* parent)
5707 Node* rootPageNode = 0;
5708 const NodeList& children = parent->childNodes();
5709 if (children.size() == 0)
5710 return rootPageNode;
5713 for (int i=0; i<children.size(); ++i) {
5714 Node* child = children[i];
5715 if ((child->type() == Node::Fake) && (child->subType() == Node::Collision)) {
5716 const FakeNode* fake = static_cast<const FakeNode*>(child);
5717 Node* n = collectNodesByTypeAndSubtype(fake);
5722 if (!child || child->isInternal() || child->doc().isEmpty())
5725 if (child->name() == "index.html") {
5726 rootPageNode = child;
5729 switch (child->type()) {
5730 case Node::Namespace:
5731 if (!isDuplicate(nodeTypeMaps[Node::Namespace],child->name(),child))
5732 nodeTypeMaps[Node::Namespace]->insert(child->name(),child);
5735 if (!isDuplicate(nodeTypeMaps[Node::Class],child->name(),child))
5736 nodeTypeMaps[Node::Class]->insert(child->name(),child);
5739 switch (child->subType()) {
5741 if (!isDuplicate(nodeSubtypeMaps[Node::Example],child->title(),child))
5742 nodeSubtypeMaps[Node::Example]->insert(child->title(),child);
5744 case Node::HeaderFile:
5745 if (!isDuplicate(nodeSubtypeMaps[Node::HeaderFile],child->title(),child))
5746 nodeSubtypeMaps[Node::HeaderFile]->insert(child->title(),child);
5753 if (!isDuplicate(nodeSubtypeMaps[Node::Group],child->title(),child))
5754 nodeSubtypeMaps[Node::Group]->insert(child->title(),child);
5757 if (!isDuplicate(nodeSubtypeMaps[Node::Module],child->title(),child))
5758 nodeSubtypeMaps[Node::Module]->insert(child->title(),child);
5761 if (!isDuplicate(pageTypeMaps[child->pageType()],child->title(),child))
5762 pageTypeMaps[child->pageType()]->insert(child->title(),child);
5764 case Node::ExternalPage:
5765 if (!isDuplicate(nodeSubtypeMaps[Node::ExternalPage],child->title(),child))
5766 nodeSubtypeMaps[Node::ExternalPage]->insert(child->title(),child);
5768 case Node::QmlClass:
5769 if (!isDuplicate(nodeSubtypeMaps[Node::QmlClass],child->title(),child))
5770 nodeSubtypeMaps[Node::QmlClass]->insert(child->title(),child);
5772 case Node::QmlPropertyGroup:
5774 case Node::QmlBasicType:
5775 if (!isDuplicate(nodeSubtypeMaps[Node::QmlBasicType],child->title(),child))
5776 nodeSubtypeMaps[Node::QmlBasicType]->insert(child->title(),child);
5778 case Node::QmlModule:
5779 if (!isDuplicate(nodeSubtypeMaps[Node::QmlModule],child->title(),child))
5780 nodeSubtypeMaps[Node::QmlModule]->insert(child->title(),child);
5782 case Node::Collision:
5783 qDebug() << "FAKE NODE: Collision";
5784 if (!isDuplicate(nodeSubtypeMaps[Node::Collision],child->title(),child))
5785 nodeSubtypeMaps[Node::Collision]->insert(child->title(),child);
5795 case Node::Function:
5797 case Node::Property:
5799 case Node::Variable:
5803 case Node::QmlProperty:
5805 case Node::QmlSignal:
5807 case Node::QmlSignalHandler:
5809 case Node::QmlMethod:
5815 return rootPageNode;
5819 Creates the DITA map for the qdoc run. The map is written
5820 to the file \e{qt.ditamap" in the DITA XML output directory.
5822 void DitaXmlGenerator::writeDitaMap(const Tree *tree)
5827 beginSubPage(tree->root(),"qt.ditamap");
5828 doctype = "<!DOCTYPE map PUBLIC \"-//OASIS//DTD DITA Map//EN\" \"map.dtd\">";
5829 xmlWriter().writeDTD(doctype);
5830 writeStartTag(DT_map);
5831 writeStartTag(DT_topicmeta);
5832 writeStartTag(DT_shortdesc);
5833 xmlWriter().writeCharacters("The top level map for the Qt documentation");
5834 writeEndTag(); // </shortdesc>
5835 writeEndTag(); // </topicmeta>
5836 GuidMaps::iterator i = guidMaps.begin();
5837 while (i != guidMaps.end()) {
5838 writeStartTag(DT_topicref);
5839 if (i.key() != "qt.ditamap")
5840 xmlWriter().writeAttribute("href",i.key());
5841 writeEndTag(); // </topicref>
5847 for (unsigned i=0; i<Node::LastType; ++i)
5848 nodeTypeMaps[i] = new NodeMultiMap;
5849 for (unsigned i=0; i<Node::LastSubtype; ++i)
5850 nodeSubtypeMaps[i] = new NodeMultiMap;
5851 for (unsigned i=0; i<Node::OnBeyondZebra; ++i)
5852 pageTypeMaps[i] = new NodeMultiMap;
5853 Node* rootPageNode = collectNodesByTypeAndSubtype(tree->root());
5855 beginSubPage(tree->root(),"qt.ditamap");
5857 doctype = "<!DOCTYPE map PUBLIC \"-//OASIS//DTD DITA Map//EN\" \"map.dtd\">";
5858 xmlWriter().writeDTD(doctype);
5859 writeStartTag(DT_map);
5860 writeStartTag(DT_topicmeta);
5861 writeStartTag(DT_shortdesc);
5862 xmlWriter().writeCharacters("The top level map for the Qt documentation");
5863 writeEndTag(); // </shortdesc>
5864 writeEndTag(); // </topicmeta>
5866 writeStartTag(DT_topicref);
5868 if (!rootPageNode->title().isEmpty())
5869 xmlWriter().writeAttribute("navtitle",rootPageNode->title());
5871 xmlWriter().writeAttribute("navtitle",project);
5872 xmlWriter().writeAttribute("href",fileName(rootPageNode));
5875 xmlWriter().writeAttribute("navtitle",project);
5877 writeTopicrefs(pageTypeMaps[Node::OverviewPage], "overviews");
5878 writeTopicrefs(pageTypeMaps[Node::HowToPage], "howtos");
5879 writeTopicrefs(pageTypeMaps[Node::TutorialPage], "tutorials");
5880 writeTopicrefs(pageTypeMaps[Node::FAQPage], "faqs");
5881 writeTopicrefs(pageTypeMaps[Node::ArticlePage], "articles");
5882 writeTopicrefs(nodeSubtypeMaps[Node::Example], "examples");
5883 writeTopicrefs(nodeSubtypeMaps[Node::QmlClass], "QML classes");
5884 writeTopicrefs(nodeTypeMaps[Node::Class], "C++ classes");
5885 writeTopicrefs(nodeTypeMaps[Node::Namespace], "C++ namespaces");
5886 writeTopicrefs(nodeSubtypeMaps[Node::HeaderFile], "header files");
5887 writeTopicrefs(nodeSubtypeMaps[Node::Module], "modules");
5888 writeTopicrefs(nodeSubtypeMaps[Node::Group], "groups");
5889 writeTopicrefs(nodeSubtypeMaps[Node::QmlModule], "QML modules");
5890 writeTopicrefs(nodeSubtypeMaps[Node::QmlBasicType], "QML basic types");
5892 writeEndTag(); // </topicref>
5895 for (unsigned i=0; i<Node::LastType; ++i)
5896 delete nodeTypeMaps[i];
5897 for (unsigned i=0; i<Node::LastSubtype; ++i)
5898 delete nodeSubtypeMaps[i];
5899 for (unsigned i=0; i<Node::OnBeyondZebra; ++i)
5900 delete pageTypeMaps[i];
5904 Creates the DITA map from the topicrefs in \a node,
5905 which is a DitaMapNode.
5907 void DitaXmlGenerator::writeDitaMap(const DitaMapNode* node)
5909 beginSubPage(node,node->name());
5912 doctype = "<!DOCTYPE map PUBLIC \"-//OASIS//DTD DITA Map//EN\" \"map.dtd\">";
5913 xmlWriter().writeDTD(doctype);
5914 writeStartTag(DT_map);
5915 writeStartTag(DT_topicmeta);
5916 writeStartTag(DT_shortdesc);
5917 xmlWriter().writeCharacters(node->title());
5918 writeEndTag(); // </shortdesc>
5919 writeEndTag(); // </topicmeta>
5920 const DitaRefList map = node->map();
5926 Write the \a ditarefs to the current output file.
5928 void DitaXmlGenerator::writeDitaRefs(const DitaRefList& ditarefs)
5930 foreach (DitaRef* t, ditarefs) {
5932 writeStartTag(DT_mapref);
5934 writeStartTag(DT_topicref);
5935 xmlWriter().writeAttribute("navtitle",t->navtitle());
5936 if (t->href().isEmpty()) {
5937 const FakeNode* fn = tree_->findFakeNodeByTitle(t->navtitle());
5939 xmlWriter().writeAttribute("href",fileName(fn));
5942 xmlWriter().writeAttribute("href",t->href());
5943 if (t->subrefs() && !t->subrefs()->isEmpty())
5944 writeDitaRefs(*(t->subrefs()));
5945 writeEndTag(); // </topicref> or </mapref>
5949 void DitaXmlGenerator::writeTopicrefs(NodeMultiMap* nmm, const QString& navtitle)
5951 if (!nmm || nmm->isEmpty())
5953 writeStartTag(DT_topicref);
5954 xmlWriter().writeAttribute("navtitle",navtitle);
5955 NodeMultiMap::iterator i = nmm->begin();
5956 while (i != nmm->end()) {
5957 writeStartTag(DT_topicref);
5958 xmlWriter().writeAttribute("navtitle",i.key());
5959 xmlWriter().writeAttribute("href",fileName(i.value()));
5960 switch (i.value()->type()) {
5962 const FakeNode* fn = static_cast<const FakeNode*>(i.value());
5963 switch (fn->subType()) {
5965 const NodeList& members = fn->groupMembers();
5966 for (int j=0; j<members.size(); ++j) {
5967 writeStartTag(DT_topicref);
5968 xmlWriter().writeAttribute("navtitle",members[j]->name());
5969 xmlWriter().writeAttribute("href",fileName(members[j]));
5970 writeEndTag(); // </topicref>
5974 case Node::QmlModule: {
5975 const NodeList& members = fn->qmlModuleMembers();
5976 for (int j=0; j<members.size(); ++j) {
5977 writeStartTag(DT_topicref);
5978 xmlWriter().writeAttribute("navtitle",members[j]->name());
5979 xmlWriter().writeAttribute("href",fileName(members[j]));
5980 writeEndTag(); // </topicref>
5984 case Node::Example: {
5985 const ExampleNode* en = static_cast<const ExampleNode*>(fn);
5986 if (!en->imageFileName().isEmpty()) {
5987 writeStartTag(DT_topicref);
5988 xmlWriter().writeAttribute("navtitle","image");
5989 xmlWriter().writeAttribute("href",en->imageFileName());
5990 writeEndTag(); // </topicref>
5992 const NodeList& files = en->childNodes();
5993 for (int j=0; j<files.size(); ++j) {
5994 writeStartTag(DT_topicref);
5995 xmlWriter().writeAttribute("href",files[j]->name());
5996 writeEndTag(); // </topicref>
6000 case Node::Module: {
6001 if (moduleNamespaceMap.contains(fn->name())) {
6002 const NodeMap& nodeMap = moduleNamespaceMap[fn->name()];
6003 foreach (const QString& name, nodeMap.keys()) {
6004 const Node* node = nodeMap[name];
6005 if (node->status() == Node::Obsolete ||
6006 node->isInternal() ||
6007 node->access() == Node::Private ||
6008 node->doc().isEmpty())
6010 writeStartTag(DT_topicref);
6011 xmlWriter().writeAttribute("navtitle",node->name());
6012 xmlWriter().writeAttribute("href",fileName(node));
6013 writeEndTag(); // </topicref>
6016 if (moduleClassMap.contains(fn->name())) {
6017 const NodeMap& nodeMap = moduleClassMap[fn->name()];
6018 foreach (const QString& name, nodeMap.keys()) {
6019 const Node* node = nodeMap[name];
6020 if (node->status() == Node::Obsolete ||
6021 node->isInternal() ||
6022 node->access() == Node::Private ||
6023 node->doc().isEmpty())
6025 writeStartTag(DT_topicref);
6026 xmlWriter().writeAttribute("navtitle",node->name());
6027 xmlWriter().writeAttribute("href",fileName(node));
6028 writeEndTag(); // </topicref>
6038 case Node::Namespace: {
6039 const NamespaceNode* nn = static_cast<const NamespaceNode*>(i.value());
6040 const NodeList& c = nn->childNodes();
6041 for (int j=0; j<c.size(); ++j) {
6042 if (c[j]->isInternal() || c[j]->access() == Node::Private || c[j]->doc().isEmpty())
6044 if (c[j]->type() == Node::Class) {
6045 writeStartTag(DT_topicref);
6046 xmlWriter().writeAttribute("navtitle",c[j]->name());
6047 xmlWriter().writeAttribute("href",fileName(c[j]));
6048 writeEndTag(); // </topicref>
6054 const NamespaceNode* nn = static_cast<const NamespaceNode*>(i.value());
6055 const NodeList& c = nn->childNodes();
6056 for (int j=0; j<c.size(); ++j) {
6057 if (c[j]->isInternal() || c[j]->access() == Node::Private || c[j]->doc().isEmpty())
6059 if (c[j]->type() == Node::Class) {
6060 writeStartTag(DT_topicref);
6061 xmlWriter().writeAttribute("navtitle",c[j]->name());
6062 xmlWriter().writeAttribute("href",fileName(c[j]));
6063 writeEndTag(); // </topicref>
6071 writeEndTag(); // </topicref>
6074 writeEndTag(); // </topicref>
6079 Looks up the tag name for \a t in the map of metadata
6080 values for the current topic in \a inner. If a value
6081 for the tag is found, the element is written with the
6082 found value. Otherwise if \a force is set, an empty
6083 element is written using the tag.
6085 Returns true or false depending on whether it writes
6086 an element using the tag \a t.
6088 \note If \a t is found in the metadata map, it is erased.
6089 i.e. Once you call this function for a particular \a t,
6092 bool DitaXmlGenerator::writeMetadataElement(const InnerNode* inner,
6093 DitaXmlGenerator::DitaTag t,
6096 QString s = getMetadataElement(inner,t);
6097 if (s.isEmpty() && !force)
6101 xmlWriter().writeCharacters(s);
6108 Looks up the tag name for \a t in the map of metadata
6109 values for the current topic in \a inner. If one or more
6110 value sfor the tag are found, the elements are written.
6111 Otherwise nothing is written.
6113 Returns true or false depending on whether it writes
6114 at least one element using the tag \a t.
6116 \note If \a t is found in the metadata map, it is erased.
6117 i.e. Once you call this function for a particular \a t,
6120 bool DitaXmlGenerator::writeMetadataElements(const InnerNode* inner,
6121 DitaXmlGenerator::DitaTag t)
6123 QStringList s = getMetadataElements(inner,t);
6126 for (int i=0; i<s.size(); ++i) {
6128 xmlWriter().writeCharacters(s[i]);
6135 Looks up the tag name for \a t in the map of metadata
6136 values for the current topic in \a inner. If a value
6137 for the tag is found, the value is returned.
6139 \note If \a t is found in the metadata map, it is erased.
6140 i.e. Once you call this function for a particular \a t,
6143 QString DitaXmlGenerator::getMetadataElement(const InnerNode* inner, DitaXmlGenerator::DitaTag t)
6145 QString s = Generator::getMetadataElement(inner, ditaTags[t]);
6147 s = metadataDefault(t);
6152 Looks up the tag name for \a t in the map of metadata
6153 values for the current topic in \a inner. If values
6154 for the tag are found, they are returned in a string
6157 \note If \a t is found in the metadata map, all the
6158 pairs having the key \a t are erased. i.e. Once you
6159 all this function for a particular \a t, you consume
6162 QStringList DitaXmlGenerator::getMetadataElements(const InnerNode* inner,
6163 DitaXmlGenerator::DitaTag t)
6165 QStringList s = Generator::getMetadataElements(inner,ditaTags[t]);
6167 s.append(metadataDefault(t));
6172 Returns the value of key \a t or an empty string
6173 if \a t is not found in the map.
6175 QString DitaXmlGenerator::metadataDefault(DitaTag t) const
6177 return metadataDefaults.value(ditaTags[t]);
6181 Writes the <prolog> element for the \a inner node
6182 using the \a marker. The <prolog> element contains
6183 the <metadata> element, plus some others. This
6184 function writes one or more of these elements:
6194 \o <created> not used
6196 \o <critdates> not used
6197 \o <keyword> not used
6198 \o <keywords> not used
6202 \o <platform> not used
6207 \o <resourceid> not used
6208 \o <revised> not used
6209 \o <source> not used
6211 \o <unknown> not used
6216 \node * means the tag has been used.
6220 DitaXmlGenerator::writeProlog(const InnerNode* inner)
6224 writeStartTag(DT_prolog);
6225 writeMetadataElements(inner,DT_author);
6226 writeMetadataElement(inner,DT_publisher);
6227 QString s = getMetadataElement(inner,DT_copyryear);
6228 QString t = getMetadataElement(inner,DT_copyrholder);
6229 writeStartTag(DT_copyright);
6230 writeStartTag(DT_copyryear);
6232 xmlWriter().writeAttribute("year",s);
6233 writeEndTag(); // </copyryear>
6234 writeStartTag(DT_copyrholder);
6236 xmlWriter().writeCharacters(t);
6237 writeEndTag(); // </copyrholder>
6238 writeEndTag(); // </copyright>
6239 s = getMetadataElement(inner,DT_permissions);
6240 writeStartTag(DT_permissions);
6241 xmlWriter().writeAttribute("view",s);
6242 writeEndTag(); // </permissions>
6243 writeStartTag(DT_metadata);
6244 QStringList sl = getMetadataElements(inner,DT_audience);
6245 if (!sl.isEmpty()) {
6246 for (int i=0; i<sl.size(); ++i) {
6247 writeStartTag(DT_audience);
6248 xmlWriter().writeAttribute("type",sl[i]);
6249 writeEndTag(); // </audience>
6252 if (!writeMetadataElement(inner,DT_category,false)) {
6253 writeStartTag(DT_category);
6254 QString category = "Page";
6255 if (inner->type() == Node::Class)
6256 category = "Class reference";
6257 else if (inner->type() == Node::Namespace)
6258 category = "Namespace";
6259 else if (inner->type() == Node::Fake) {
6260 if (inner->subType() == Node::QmlClass)
6261 category = "QML Reference";
6262 else if (inner->subType() == Node::QmlBasicType)
6263 category = "QML Basic Type";
6264 else if (inner->subType() == Node::HeaderFile)
6265 category = "Header File";
6266 else if (inner->subType() == Node::Module)
6267 category = "Module";
6268 else if (inner->subType() == Node::File)
6269 category = "Example Source File";
6270 else if (inner->subType() == Node::Example)
6271 category = "Example";
6272 else if (inner->subType() == Node::Image)
6274 else if (inner->subType() == Node::Group)
6276 else if (inner->subType() == Node::Page)
6278 else if (inner->subType() == Node::ExternalPage)
6279 category = "External Page"; // Is this necessary?
6281 xmlWriter().writeCharacters(category);
6282 writeEndTag(); // </category>
6284 if (vrm.size() > 0) {
6285 writeStartTag(DT_prodinfo);
6286 if (!writeMetadataElement(inner,DT_prodname,false)) {
6287 writeStartTag(DT_prodname);
6288 xmlWriter().writeCharacters(projectDescription);
6289 writeEndTag(); // </prodname>
6291 writeStartTag(DT_vrmlist);
6292 writeStartTag(DT_vrm);
6294 xmlWriter().writeAttribute("version",vrm[0]);
6296 xmlWriter().writeAttribute("release",vrm[1]);
6298 xmlWriter().writeAttribute("modification",vrm[2]);
6299 writeEndTag(); // <vrm>
6300 writeEndTag(); // <vrmlist>
6301 if (!writeMetadataElement(inner,DT_component,false)) {
6302 QString component = inner->moduleName();
6303 if (!component.isEmpty()) {
6304 writeStartTag(DT_component);
6305 xmlWriter().writeCharacters(component);
6306 writeEndTag(); // </component>
6309 writeEndTag(); // </prodinfo>
6311 const QStringMultiMap& metaTagMap = inner->doc().metaTagMap();
6312 QMapIterator<QString, QString> i(metaTagMap);
6313 while (i.hasNext()) {
6315 writeStartTag(DT_othermeta);
6316 xmlWriter().writeAttribute("name",i.key());
6317 xmlWriter().writeAttribute("content",i.value());
6318 writeEndTag(); // </othermeta>
6320 if ((tagStack.first() == DT_cxxClass && !inner->includes().isEmpty()) ||
6321 (inner->type() == Node::Fake && inner->subType() == Node::HeaderFile)) {
6322 writeStartTag(DT_othermeta);
6323 xmlWriter().writeAttribute("name","includeFile");
6325 QStringList::ConstIterator i = inner->includes().begin();
6326 while (i != inner->includes().end()) {
6327 if ((*i).startsWith("<") && (*i).endsWith(">"))
6330 text += "<" + *i + ">";
6332 if (i != inner->includes().end())
6335 xmlWriter().writeAttribute("content",text);
6336 writeEndTag(); // </othermeta>
6338 writeEndTag(); // </metadata>
6339 writeEndTag(); // </prolog>
6343 This function should be called to write the \a href attribute
6344 if the href could be an \e http or \e ftp link. If \a href is
6345 one or the other, a \e scope attribute is also writen, with
6348 void DitaXmlGenerator::writeHrefAttribute(const QString& href)
6350 xmlWriter().writeAttribute("href", href);
6351 if (href.startsWith("http:") || href.startsWith("ftp:") ||
6352 href.startsWith("https:") || href.startsWith("mailto:"))
6353 xmlWriter().writeAttribute("scope", "external");
6357 Strips the markup tags from \a src, when we are trying to
6358 create an \e{id} attribute. Returns the stripped text.
6360 QString DitaXmlGenerator::stripMarkup(const QString& src) const
6363 const QChar charAt = '@';
6364 const QChar charSlash = '/';
6365 const QChar charLangle = '<';
6366 const QChar charRangle = '>';
6371 if (src.at(i) == charLangle) {
6373 if (src.at(i) == charAt || (src.at(i) == charSlash && src.at(i+1) == charAt)) {
6374 while (i < n && src.at(i) != charRangle)
6383 text += src.at(i++);