qdoc: Remove the spectitle attribute
[profile/ivi/qtbase.git] / src / tools / qdoc / ditaxmlgenerator.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
5 **
6 ** This file is part of the tools applications of the Qt Toolkit.
7 **
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.
16 **
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.
20 **
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.
28 **
29 ** Other Usage
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.
32 **
33 **
34 **
35 **
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 /*
43   ditaxmlgenerator.cpp
44 */
45
46 #include <QDebug>
47 #include <QList>
48 #include <qiterator.h>
49 #include <QTextCodec>
50 #include <QUuid>
51 #include "codemarker.h"
52 #include "codeparser.h"
53 #include "ditaxmlgenerator.h"
54 #include "node.h"
55 #include "quoter.h"
56 #include "separator.h"
57 #include "tree.h"
58 #include <ctype.h>
59
60 QT_BEGIN_NAMESPACE
61
62 #define COMMAND_VERSION                         Doc::alias("version")
63 int DitaXmlGenerator::id = 0;
64
65 /*
66   The strings in this array must appear in the same order as
67   the values in enum DitaXmlGenerator::DitaTag.
68  */
69 QString DitaXmlGenerator::ditaTags[] =
70 {
71     "",
72     "alt",
73     "apiDesc",
74     "APIMap",
75     "apiName",
76     "apiRelation",
77     "audience",
78     "author",
79     "b",
80     "body",
81     "bodydiv",
82     "brand",
83     "category",
84     "codeblock",
85     "comment",
86     "component",
87     "copyrholder",
88     "copyright",
89     "copyryear",
90     "created",
91     "critdates",
92     "cxxAPIMap",
93     "cxxClass",
94     "cxxClassAbstract",
95     "cxxClassAccessSpecifier",
96     "cxxClassAPIItemLocation",
97     "cxxClassBaseClass",
98     "cxxClassDeclarationFile",
99     "cxxClassDeclarationFileLine",
100     "cxxClassDeclarationFileLineStart",
101     "cxxClassDeclarationFileLineEnd",
102     "cxxClassDefinition",
103     "cxxClassDerivation",
104     "cxxClassDerivationAccessSpecifier",
105     "cxxClassDerivations",
106     "cxxClassDetail",
107     "cxxClassNested",
108     "cxxClassNestedClass",
109     "cxxClassNestedDetail",
110     "cxxDefine",
111     "cxxDefineAccessSpecifier",
112     "cxxDefineAPIItemLocation",
113     "cxxDefineDeclarationFile",
114     "cxxDefineDeclarationFileLine",
115     "cxxDefineDefinition",
116     "cxxDefineDetail",
117     "cxxDefineNameLookup",
118     "cxxDefineParameter",
119     "cxxDefineParameterDeclarationName",
120     "cxxDefineParameters",
121     "cxxDefinePrototype",
122     "cxxDefineReimplemented",
123     "cxxEnumeration",
124     "cxxEnumerationAccessSpecifier",
125     "cxxEnumerationAPIItemLocation",
126     "cxxEnumerationDeclarationFile",
127     "cxxEnumerationDeclarationFileLine",
128     "cxxEnumerationDeclarationFileLineStart",
129     "cxxEnumerationDeclarationFileLineEnd",
130     "cxxEnumerationDefinition",
131     "cxxEnumerationDetail",
132     "cxxEnumerationNameLookup",
133     "cxxEnumerationPrototype",
134     "cxxEnumerationScopedName",
135     "cxxEnumerator",
136     "cxxEnumeratorInitialiser",
137     "cxxEnumeratorNameLookup",
138     "cxxEnumeratorPrototype",
139     "cxxEnumerators",
140     "cxxEnumeratorScopedName",
141     "cxxFunction",
142     "cxxFunctionAccessSpecifier",
143     "cxxFunctionAPIItemLocation",
144     "cxxFunctionConst",
145     "cxxFunctionConstructor",
146     "cxxFunctionDeclarationFile",
147     "cxxFunctionDeclarationFileLine",
148     "cxxFunctionDeclaredType",
149     "cxxFunctionDefinition",
150     "cxxFunctionDestructor",
151     "cxxFunctionDetail",
152     "cxxFunctionNameLookup",
153     "cxxFunctionParameter",
154     "cxxFunctionParameterDeclarationName",
155     "cxxFunctionParameterDeclaredType",
156     "cxxFunctionParameterDefaultValue",
157     "cxxFunctionParameters",
158     "cxxFunctionPrototype",
159     "cxxFunctionPureVirtual",
160     "cxxFunctionReimplemented",
161     "cxxFunctionScopedName",
162     "cxxFunctionStorageClassSpecifierStatic",
163     "cxxFunctionVirtual",
164     "cxxTypedef",
165     "cxxTypedefAccessSpecifier",
166     "cxxTypedefAPIItemLocation",
167     "cxxTypedefDeclarationFile",
168     "cxxTypedefDeclarationFileLine",
169     "cxxTypedefDefinition",
170     "cxxTypedefDetail",
171     "cxxTypedefNameLookup",
172     "cxxTypedefScopedName",
173     "cxxVariable",
174     "cxxVariableAccessSpecifier",
175     "cxxVariableAPIItemLocation",
176     "cxxVariableDeclarationFile",
177     "cxxVariableDeclarationFileLine",
178     "cxxVariableDeclaredType",
179     "cxxVariableDefinition",
180     "cxxVariableDetail",
181     "cxxVariableNameLookup",
182     "cxxVariablePrototype",
183     "cxxVariableReimplemented",
184     "cxxVariableScopedName",
185     "cxxVariableStorageClassSpecifierStatic",
186     "data",
187     "data-about",
188     "dd",
189     "dl",
190     "dlentry",
191     "dt",
192     "entry",
193     "fig",
194     "i",
195     "image",
196     "keyword",
197     "keywords",
198     "li",
199     "link",
200     "linktext",
201     "lq",
202     "map",
203     "mapref",
204     "metadata",
205     "note",
206     "ol",
207     "othermeta",
208     "p",
209     "parameter",
210     "permissions",
211     "ph",
212     "platform",
213     "pre",
214     "prodinfo",
215     "prodname",
216     "prolog",
217     "publisher",
218     "related-links",
219     "resourceid",
220     "revised",
221     "row",
222     "section",
223     "sectiondiv",
224     "shortdesc",
225     "simpletable",
226     "source",
227     "stentry",
228     "sthead",
229     "strow",
230     "sub",
231     "sup",
232     "table",
233     "tbody",
234     "tgroup",
235     "thead",
236     "title",
237     "tm",
238     "topic",
239     "topicmeta",
240     "topicref",
241     "tt",
242     "u",
243     "ul",
244     "unknown",
245     "vrm",
246     "vrmlist",
247     "xref",
248     ""
249 };
250
251 static bool showBrokenLinks = false;
252
253 /*!
254   Quick, dirty, and very ugly. Unescape \a text
255   so QXmlStreamWriter::writeCharacters() can put
256   the escapes back in again!
257  */
258 void DitaXmlGenerator::writeCharacters(const QString& text)
259 {
260     QString t = text;
261     t = t.replace("&lt;","<");
262     t = t.replace("&gt;",">");
263     t = t.replace("&amp;","&");
264     t = t.replace("&quot;","\"");
265     xmlWriter().writeCharacters(t);
266 }
267
268 /*!
269   Appends an <xref> element to the current XML stream
270   with the \a href attribute and the \a text.
271  */
272 void DitaXmlGenerator::addLink(const QString& href,
273                                const QStringRef& text,
274                                DitaTag t)
275 {
276     if (!href.isEmpty()) {
277         writeStartTag(t);
278         // formathtml
279         writeHrefAttribute(href);
280         writeCharacters(text.toString());
281         writeEndTag(); // </t>
282     }
283     else {
284         writeCharacters(text.toString());
285     }
286 }
287
288 /*!
289   Push \a t onto the dita tag stack and write the appropriate
290   start tag to the DITA XML file.
291  */
292 void DitaXmlGenerator::writeStartTag(DitaTag t)
293 {
294     xmlWriter().writeStartElement(ditaTags[t]);
295     tagStack.push(t);
296 }
297
298 /*!
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.
303
304   If the stack is empty, no end tag is written and false
305   is returned. Otherwise, an end tag is written and true
306   is returned.
307  */
308 bool DitaXmlGenerator::writeEndTag(DitaTag t)
309 {
310     if (tagStack.isEmpty())
311         return false;
312     DitaTag top = tagStack.pop();
313     if (t > DT_NONE && top != t)
314         qDebug() << "Expected:" << t << "ACTUAL:" << top;
315     xmlWriter().writeEndElement();
316     return true;
317 }
318
319 /*!
320   Return the current DITA element tag, the one
321   on top of the stack.
322  */
323 DitaXmlGenerator::DitaTag DitaXmlGenerator::currentTag()
324 {
325     return tagStack.top();
326 }
327
328 /*!
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.
332
333   Then if \a outputclass is not empty, write it as the value
334   of the \a outputclass attribute.
335
336   Fiunally, set the section nesting level to 1 and return 1.
337  */
338 int DitaXmlGenerator::enterApiDesc(const QString& outputclass, const QString& title)
339 {
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);
345     }
346     if (!outputclass.isEmpty())
347         xmlWriter().writeAttribute("outputclass",outputclass);
348     sectionNestingLevel = 1;
349     return sectionNestingLevel;
350 }
351
352 /*!
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
358   is output.
359
360   Finally, increment the section nesting level and return
361   the new value.
362  */
363 int DitaXmlGenerator::enterSection(const QString& outputclass, const QString& title)
364 {
365     if (sectionNestingLevel == 0) {
366         writeStartTag(DT_section);
367         if (!title.isEmpty())
368             writeGuidAttribute(title);
369         if (!outputclass.isEmpty())
370             xmlWriter().writeAttribute("outputclass",outputclass);
371     }
372     else if (!title.isEmpty()) {
373         writeStartTag(DT_p);
374         writeGuidAttribute(title);
375         if (!outputclass.isEmpty())
376             xmlWriter().writeAttribute("outputclass",outputclass);
377         writeCharacters(title);
378         writeEndTag(); // </p>
379     }
380     return ++sectionNestingLevel;
381 }
382
383 /*!
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.
387  */
388 int DitaXmlGenerator::leaveSection()
389 {
390     if (sectionNestingLevel > 0) {
391         --sectionNestingLevel;
392         if (sectionNestingLevel == 0)
393             writeEndTag(); // </section> or </apiDesc>
394     }
395     return sectionNestingLevel;
396 }
397
398 /*!
399   The default constructor.
400  */
401 DitaXmlGenerator::DitaXmlGenerator()
402     : inContents(false),
403       inDetailedDescription(false),
404       inLegaleseText(false),
405       inLink(false),
406       inObsoleteLink(false),
407       inSectionHeading(false),
408       inTableHeader(false),
409       inTableBody(false),
410       noLinks(false),
411       obsoleteLinks(false),
412       offlineDocs(true),
413       threeColumnEnumValueTable(true),
414       codeIndent(0),
415       numTableRows(0),
416       divNestingLevel(0),
417       sectionNestingLevel(0),
418       tableColumnCount(0),
419       funcLeftParen("\\S(\\()"),
420       tree_(0),
421       nodeTypeMaps(Node::LastType,0),
422       nodeSubtypeMaps(Node::LastSubtype,0),
423       pageTypeMaps(Node::OnBeyondZebra,0)
424 {
425     // nothing yet.
426 }
427
428 /*!
429   The destructor has nothing to do.
430  */
431 DitaXmlGenerator::~DitaXmlGenerator()
432 {
433     GuidMaps::iterator i = guidMaps.begin();
434     while (i != guidMaps.end()) {
435         delete i.value();
436         ++i;
437     }
438 }
439
440 /*!
441   A lot of internal structures are initialized.
442  */
443 void DitaXmlGenerator::initializeGenerator(const Config &config)
444 {
445     Generator::initializeGenerator(config);
446     obsoleteLinks = config.getBool(QLatin1String(CONFIG_OBSOLETELINKS));
447     setImageFileExtensions(QStringList() << "png" << "jpg" << "jpeg" << "gif");
448
449     style = config.getString(DitaXmlGenerator::format() +
450                              Config::dot +
451                              DITAXMLGENERATOR_STYLE);
452     postHeader = config.getString(DitaXmlGenerator::format() +
453                                   Config::dot +
454                                   DITAXMLGENERATOR_POSTHEADER);
455     postPostHeader = config.getString(DitaXmlGenerator::format() +
456                                       Config::dot +
457                                       DITAXMLGENERATOR_POSTPOSTHEADER);
458     footer = config.getString(DitaXmlGenerator::format() +
459                               Config::dot +
460                               DITAXMLGENERATOR_FOOTER);
461     address = config.getString(DitaXmlGenerator::format() +
462                                Config::dot +
463                                DITAXMLGENERATOR_ADDRESS);
464     pleaseGenerateMacRef = config.getBool(DitaXmlGenerator::format() +
465                                           Config::dot +
466                                           DITAXMLGENERATOR_GENERATEMACREFS);
467
468     project = config.getString(CONFIG_PROJECT);
469     projectDescription = config.getString(CONFIG_DESCRIPTION);
470     if (projectDescription.isEmpty() && !project.isEmpty())
471         projectDescription = project + " Reference Documentation";
472
473     projectUrl = config.getString(CONFIG_URL);
474
475     outputEncoding = config.getString(CONFIG_OUTPUTENCODING);
476     if (outputEncoding.isEmpty())
477         outputEncoding = QLatin1String("ISO-8859-1");
478     outputCodec = QTextCodec::codecForName(outputEncoding.toLocal8Bit());
479
480     naturalLanguage = config.getString(CONFIG_NATURALLANGUAGE);
481     if (naturalLanguage.isEmpty())
482         naturalLanguage = QLatin1String("en");
483
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 +
490                                                           Config::dot +
491                                                           editionName +
492                                                           Config::dot +
493                                                           "modules");
494         QStringList editionGroups = config.getStringList(CONFIG_EDITION +
495                                                          Config::dot +
496                                                          editionName +
497                                                          Config::dot +
498                                                          "groups");
499
500         if (!editionModules.isEmpty())
501             editionModuleMap[editionName] = editionModules;
502         if (!editionGroups.isEmpty())
503             editionGroupMap[editionName] = editionGroups;
504
505         ++edition;
506     }
507
508     stylesheets = config.getStringList(DitaXmlGenerator::format() +
509                                        Config::dot +
510                                        DITAXMLGENERATOR_STYLESHEETS);
511     customHeadElements = config.getStringList(DitaXmlGenerator::format() +
512                                               Config::dot +
513                                               DITAXMLGENERATOR_CUSTOMHEADELEMENTS);
514     codeIndent = config.getInt(CONFIG_CODEINDENT);
515     version = config.getString(CONFIG_VERSION);
516     vrm = version.split(".");
517 }
518
519 /*!
520   All this does is call the same function in the base class.
521  */
522 void DitaXmlGenerator::terminateGenerator()
523 {
524     Generator::terminateGenerator();
525 }
526
527 /*!
528   Returns "DITAXML".
529  */
530 QString DitaXmlGenerator::format()
531 {
532     return "DITAXML";
533 }
534
535 /*!
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.
538  */
539 QString DitaXmlGenerator::writeGuidAttribute(QString text)
540 {
541     QString guid = lookupGuid(outFileName(),text);
542     xmlWriter().writeAttribute("id",guid);
543     return guid;
544 }
545
546
547 /*!
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,
550   one is generated.
551  */
552 void DitaXmlGenerator::writeGuidAttribute(Node* node)
553 {
554     xmlWriter().writeAttribute("id",node->guid());
555 }
556
557 /*!
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
561   the new GUID.
562  */
563 QString DitaXmlGenerator::lookupGuid(QString text)
564 {
565     QMap<QString, QString>::const_iterator i = name2guidMap.find(text);
566     if (i != name2guidMap.end())
567         return i.value();
568 #if 0
569     QString t = QUuid::createUuid().toString();
570     QString guid = "id-" + t.mid(1,t.length()-2);
571 #endif
572     QString guid = Node::cleanId(text);
573     name2guidMap.insert(text,guid);
574     return guid;
575 }
576
577 /*!
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
583   the new GUID.
584  */
585 QString DitaXmlGenerator::lookupGuid(const QString& fileName, const QString& text)
586 {
587     GuidMap* gm = lookupGuidMap(fileName);
588     GuidMap::const_iterator i = gm->find(text);
589     if (i != gm->end())
590         return i.value();
591 #if 0
592     QString t = QUuid::createUuid().toString();
593     QString guid = "id-" + t.mid(1,t.length()-2);
594 #endif
595     QString guid = Node::cleanId(text);
596     gm->insert(text,guid);
597     return guid;
598 }
599
600 /*!
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.
605  */
606 GuidMap* DitaXmlGenerator::lookupGuidMap(const QString& fileName)
607 {
608     GuidMaps::const_iterator i = guidMaps.find(fileName);
609     if (i != guidMaps.end())
610         return i.value();
611     GuidMap* gm = new GuidMap;
612     guidMaps.insert(fileName,gm);
613     return gm;
614 }
615
616 /*!
617   This is where the DITA XML files are written.
618   \note The file is created in PageGenerator::generateTree().
619  */
620 void DitaXmlGenerator::generateTree(const Tree *tree)
621 {
622     tree_ = tree;
623     nonCompatClasses.clear();
624     mainClasses.clear();
625     compatClasses.clear();
626     obsoleteClasses.clear();
627     moduleClassMap.clear();
628     moduleNamespaceMap.clear();
629     funcIndex.clear();
630     legaleseTexts.clear();
631     serviceClasses.clear();
632     qmlClasses.clear();
633     findAllClasses(tree->root());
634     findAllFunctions(tree->root());
635     findAllLegaleseTexts(tree->root());
636     findAllNamespaces(tree->root());
637     findAllSince(tree->root());
638
639     Generator::generateTree(tree);
640     writeDitaMap(tree);
641 }
642
643 void DitaXmlGenerator::startText(const Node* /* relative */,
644                                  CodeMarker* /* marker */)
645 {
646     inLink = false;
647     inContents = false;
648     inSectionHeading = false;
649     inTableHeader = false;
650     numTableRows = 0;
651     threeColumnEnumValueTable = true;
652     link.clear();
653     sectionNumber.clear();
654 }
655
656 static int countTableColumns(const Atom* t)
657 {
658     int result = 0;
659     if (t->type() == Atom::TableHeaderLeft) {
660         while (t->type() == Atom::TableHeaderLeft) {
661             int count = 0;
662             t = t->next();
663             while (t->type() != Atom::TableHeaderRight) {
664                 if (t->type() == Atom::TableItemLeft)
665                     ++count;
666                 t = t->next();
667             }
668             if (count > result)
669                 result = count;
670             t = t->next();
671         }
672     }
673     else if (t->type() == Atom::TableRowLeft) {
674         while (t->type() != Atom::TableRowRight) {
675             if (t->type() == Atom::TableItemLeft)
676                 ++result;
677             t = t->next();
678         }
679     }
680     return result;
681 }
682
683 /*!
684   Generate html from an instance of Atom.
685  */
686 int DitaXmlGenerator::generateAtom(const Atom *atom,
687                                    const Node *relative,
688                                    CodeMarker *marker)
689 {
690     int skipAhead = 0;
691     QString hx, str;
692     static bool in_para = false;
693     QString guid, hc, attr;
694
695     switch (atom->type()) {
696     case Atom::AbstractLeft:
697         break;
698     case Atom::AbstractRight:
699         break;
700     case Atom::AutoLink:
701         if (!noLinks && !inLink && !inContents && !inSectionHeading) {
702             const Node* node = 0;
703             QString link = getLink(atom, relative, marker, &node);
704             if (!link.isEmpty()) {
705                 beginLink(link);
706                 generateLink(atom, relative, marker);
707                 endLink();
708             }
709             else {
710                 writeCharacters(protectEnc(atom->string()));
711             }
712         }
713         else {
714             writeCharacters(protectEnc(atom->string()));
715         }
716         break;
717     case Atom::BaseName:
718         break;
719     case Atom::BriefLeft:
720         //if (relative->type() == Node::Fake) {
721         //skipAhead = skipAtoms(atom, Atom::BriefRight);
722         //break;
723         //}
724         if (inSection()) {
725             writeStartTag(DT_p);
726             xmlWriter().writeAttribute("outputclass","brief");
727         }
728         else {
729             noLinks = true;
730             writeStartTag(DT_shortdesc);
731         }
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 ");
740         }
741         if (noLinks) {
742             atom = atom->next();
743             while (atom != 0 && atom->type() != Atom::BriefRight) {
744                 if (atom->type() == Atom::String ||
745                         atom->type() == Atom::AutoLink)
746                     str += atom->string();
747                 skipAhead++;
748                 atom = atom->next();
749             }
750             str[0] = str[0].toLower();
751             if (str.endsWith(QLatin1Char('.')))
752                 str.truncate(str.length() - 1);
753             writeCharacters(str + QLatin1Char('.'));
754         }
755         break;
756     case Atom::BriefRight:
757         //        if (relative->type() != Node::Fake)
758         writeEndTag(); // </shortdesc> or </p>
759         noLinks = false;
760         break;
761     case Atom::C:
762         writeStartTag(DT_tt);
763         if (inLink) {
764             writeCharacters(protectEnc(plainCode(atom->string())));
765         }
766         else {
767             writeText(atom->string(), marker, relative);
768         }
769         writeEndTag(); // see writeStartElement() above
770         break;
771     case Atom::Code:
772     {
773         writeStartTag(DT_codeblock);
774         xmlWriter().writeAttribute("outputclass","cpp");
775         QString chars = trimmedTrailing(atom->string());
776         writeText(chars, marker, relative);
777         writeEndTag(); // </codeblock>
778     }
779         break;
780     case Atom::Qml:
781         writeStartTag(DT_codeblock);
782         xmlWriter().writeAttribute("outputclass","qml");
783         writeText(trimmedTrailing(atom->string()), marker, relative);
784         writeEndTag(); // </codeblock>
785         break;
786     case Atom::CodeNew:
787         writeStartTag(DT_p);
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>
793         break;
794     case Atom::CodeOld:
795         writeStartTag(DT_p);
796         xmlWriter().writeCharacters("For example, if you have code like");
797         writeEndTag(); // </p>
798         // fallthrough
799     case Atom::CodeBad:
800         writeStartTag(DT_codeblock);
801         writeCharacters(trimmedTrailing(plainCode(atom->string())));
802         writeEndTag(); // </codeblock>
803         break;
804     case Atom::DivLeft:
805     {
806         bool inStartElement = false;
807         attr = atom->string();
808         DitaTag t = currentTag();
809         if ((t == DT_section) || (t == DT_sectiondiv)) {
810             writeStartTag(DT_sectiondiv);
811             divNestingLevel++;
812             inStartElement = true;
813         }
814         else if ((t == DT_body) || (t == DT_bodydiv)) {
815             writeStartTag(DT_bodydiv);
816             divNestingLevel++;
817             inStartElement = true;
818         }
819         if (!attr.isEmpty()) {
820             if (attr.contains('=')) {
821                 int index = 0;
822                 int from = 0;
823                 QString values;
824                 while (index >= 0) {
825                     index = attr.indexOf('"',from);
826                     if (index >= 0) {
827                         ++index;
828                         from = index;
829                         index = attr.indexOf('"',from);
830                         if (index > from) {
831                             if (!values.isEmpty())
832                                 values.append(' ');
833                             values += attr.mid(from,index-from);
834                             from = index+1;
835                         }
836                     }
837                 }
838                 attr = values;
839             }
840         }
841         if (inStartElement)
842             xmlWriter().writeAttribute("outputclass", attr);
843     }
844         break;
845     case Atom::DivRight:
846         if ((currentTag() == DT_sectiondiv) || (currentTag() == DT_bodydiv)) {
847             writeEndTag(); // </sectiondiv>, </bodydiv>, or </p>
848             if (divNestingLevel > 0)
849                 --divNestingLevel;
850         }
851         break;
852     case Atom::FootnoteLeft:
853         // ### For now
854         if (in_para) {
855             writeEndTag(); // </p>
856             in_para = false;
857         }
858         xmlWriter().writeCharacters("<!-- ");
859         break;
860     case Atom::FootnoteRight:
861         // ### For now
862         xmlWriter().writeCharacters("-->");
863         break;
864     case Atom::FormatElse:
865     case Atom::FormatEndif:
866     case Atom::FormatIf:
867         break;
868     case Atom::FormattingLeft:
869     {
870         DitaTag t = DT_LAST;
871         if (atom->string() == ATOM_FORMATTING_BOLD)
872             t = DT_b;
873         else if (atom->string() == ATOM_FORMATTING_PARAMETER)
874             t = DT_i;
875         else if (atom->string() == ATOM_FORMATTING_ITALIC)
876             t = DT_i;
877         else if (atom->string() == ATOM_FORMATTING_TELETYPE)
878             t = DT_tt;
879         else if (atom->string().startsWith("span ")) {
880             t = DT_keyword;
881         }
882         else if (atom->string() == ATOM_FORMATTING_UNDERLINE)
883             t = DT_u;
884         else if (atom->string() == ATOM_FORMATTING_INDEX)
885             t = DT_comment;
886         else if (atom->string() == ATOM_FORMATTING_SUBSCRIPT)
887             t = DT_sub;
888         else if (atom->string() == ATOM_FORMATTING_SUPERSCRIPT)
889             t = DT_sup;
890         else
891             qDebug() << "DT_LAST";
892         writeStartTag(t);
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>
901                     skipAhead = 1;
902                 }
903             }
904         }
905         else if (t == DT_keyword) {
906             QString attr = atom->string().mid(5);
907             if (!attr.isEmpty()) {
908                 if (attr.contains('=')) {
909                     int index = 0;
910                     int from = 0;
911                     QString values;
912                     while (index >= 0) {
913                         index = attr.indexOf('"',from);
914                         if (index >= 0) {
915                             ++index;
916                             from = index;
917                             index = attr.indexOf('"',from);
918                             if (index > from) {
919                                 if (!values.isEmpty())
920                                     values.append(' ');
921                                 values += attr.mid(from,index-from);
922                                 from = index+1;
923                             }
924                         }
925                     }
926                     attr = values;
927                 }
928             }
929             xmlWriter().writeAttribute("outputclass", attr);
930         }
931     }
932         break;
933     case Atom::FormattingRight:
934         if (atom->string() == ATOM_FORMATTING_LINK) {
935             endLink();
936         }
937         else {
938             writeEndTag(); // ?
939         }
940         break;
941     case Atom::AnnotatedList:
942     {
943         QList<Node*> values = tree_->groups().values(atom->string());
944         NodeMap nodeMap;
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);
949             }
950         }
951         generateAnnotatedList(relative, marker, nodeMap);
952     }
953         break;
954     case Atom::GeneratedList:
955         if (atom->string() == "annotatedclasses") {
956             generateAnnotatedList(relative, marker, nonCompatClasses);
957         }
958         else if (atom->string() == "classes") {
959             generateCompactList(relative, marker, nonCompatClasses, true);
960         }
961         else if (atom->string() == "qmlclasses") {
962             generateCompactList(relative, marker, qmlClasses, true);
963         }
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]);
970         }
971         else if (atom->string().contains("classesbyedition")) {
972
973             QString arg = atom->string().trimmed();
974             QString editionName = atom->string().mid(atom->string().indexOf(
975                                                          "classesbyedition") + 16).trimmed();
976
977             if (editionModuleMap.contains(editionName)) {
978
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]);
984                 }
985
986                 // Add additional groups and remove groups of classes that
987                 // should be excluded from the edition.
988
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());
996                     }
997                     else {
998                         groupClasses = groups.values(groupName);
999                         foreach (const Node *node, groupClasses)
1000                             editionClasses.insert(node->name(), node);
1001                     }
1002                 }
1003                 generateAnnotatedList(relative, marker, editionClasses);
1004             }
1005         }
1006         else if (atom->string() == "classhierarchy") {
1007             generateClassHierarchy(relative, marker, nonCompatClasses);
1008         }
1009         else if (atom->string() == "compatclasses") {
1010             generateCompactList(relative, marker, compatClasses, false);
1011         }
1012         else if (atom->string() == "obsoleteclasses") {
1013             generateCompactList(relative, marker, obsoleteClasses, false);
1014         }
1015         else if (atom->string() == "functionindex") {
1016             generateFunctionIndex(relative, marker);
1017         }
1018         else if (atom->string() == "legalese") {
1019             generateLegaleseList(relative, marker);
1020         }
1021         else if (atom->string() == "mainclasses") {
1022             generateCompactList(relative, marker, mainClasses, true);
1023         }
1024         else if (atom->string() == "services") {
1025             generateCompactList(relative, marker, serviceClasses, false);
1026         }
1027         else if (atom->string() == "overviews") {
1028             generateOverviewList(relative, marker);
1029         }
1030         else if (atom->string() == "namespaces") {
1031             generateAnnotatedList(relative, marker, namespaceIndex);
1032         }
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;
1040                 }
1041                 generateAnnotatedList(fake, marker, groupMembersMap);
1042             }
1043         }
1044         break;
1045     case Atom::SinceList:
1046     {
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()));
1058
1059             NodeMultiMap::const_iterator n = nsmap.value().constBegin();
1060             while (n != nsmap.value().constEnd()) {
1061                 const Node* node = n.value();
1062                 switch (node->type()) {
1063                 case Node::Fake:
1064                     if (node->subType() == Node::QmlClass) {
1065                         sections[QmlClass].appendMember((Node*)node);
1066                     }
1067                     break;
1068                 case Node::Namespace:
1069                     sections[Namespace].appendMember((Node*)node);
1070                     break;
1071                 case Node::Class:
1072                     sections[Class].appendMember((Node*)node);
1073                     break;
1074                 case Node::Enum:
1075                     sections[Enum].appendMember((Node*)node);
1076                     break;
1077                 case Node::Typedef:
1078                     sections[Typedef].appendMember((Node*)node);
1079                     break;
1080                 case Node::Function: {
1081                     const FunctionNode* fn = static_cast<const FunctionNode*>(node);
1082                     if (fn->isMacro())
1083                         sections[Macro].appendMember((Node*)node);
1084                     else {
1085                         Node* p = fn->parent();
1086                         if (p) {
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);
1092                                 else
1093                                     sections[NamespaceFunction].appendMember((Node*)node);
1094                             }
1095                             else
1096                                 sections[GlobalFunction].appendMember((Node*)node);
1097                         }
1098                         else
1099                             sections[GlobalFunction].appendMember((Node*)node);
1100                     }
1101                     break;
1102                 }
1103                 case Node::Property:
1104                     sections[Property].appendMember((Node*)node);
1105                     break;
1106                 case Node::Variable:
1107                     sections[Variable].appendMember((Node*)node);
1108                     break;
1109                 case Node::QmlProperty:
1110                     sections[QmlProperty].appendMember((Node*)node);
1111                     break;
1112                 case Node::QmlSignal:
1113                     sections[QmlSignal].appendMember((Node*)node);
1114                     break;
1115                 case Node::QmlSignalHandler:
1116                     sections[QmlSignalHandler].appendMember((Node*)node);
1117                     break;
1118                 case Node::QmlMethod:
1119                     sections[QmlMethod].appendMember((Node*)node);
1120                     break;
1121                 default:
1122                     break;
1123                 }
1124                 ++n;
1125             }
1126
1127             /*
1128                   First generate the table of contents.
1129                  */
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);
1136                 }
1137                 ++s;
1138             }
1139             writeEndTag(); // </ul>
1140
1141             int idx = 0;
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>
1150                     if (idx == Class)
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));
1164                             ++i;
1165                         }
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);
1172                             // formathtml
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>
1179
1180                             generateSection(nlist, 0, marker, CodeMarker::Summary);
1181                             ++pmap;
1182                         }
1183                     }
1184                     else {
1185                         generateSection(s->members, 0, marker, CodeMarker::Summary);
1186                     }
1187                 }
1188                 ++idx;
1189                 ++s;
1190             }
1191         }
1192     }
1193         break;
1194     case Atom::Image:
1195     case Atom::InlineImage:
1196     {
1197         QString fileName = imageFileName(relative, atom->string());
1198         QString text;
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();
1208         }
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);
1215             }
1216         }
1217
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");
1224         else {
1225             xmlWriter().writeAttribute("placement","break");
1226             xmlWriter().writeAttribute("align","center");
1227         }
1228         if (!text.isEmpty()) {
1229             writeStartTag(DT_alt);
1230             writeCharacters(protectEnc(text));
1231             writeEndTag(); // </alt>
1232         }
1233         writeEndTag(); // </image>
1234         if (currentTag() != DT_xref)
1235             writeEndTag(); // </fig>
1236     }
1237         break;
1238     case Atom::ImageText:
1239         // nothing
1240         break;
1241     case Atom::ImportantLeft:
1242         writeStartTag(DT_note);
1243         xmlWriter().writeAttribute("type","important");
1244         break;
1245     case Atom::ImportantRight:
1246         writeEndTag(); // </note>
1247         break;
1248     case Atom::NoteLeft:
1249         writeStartTag(DT_note);
1250         xmlWriter().writeAttribute("type","note");
1251         break;
1252     case Atom::NoteRight:
1253         writeEndTag(); // </note>
1254         break;
1255     case Atom::LegaleseLeft:
1256         inLegaleseText = true;
1257         break;
1258     case Atom::LegaleseRight:
1259         inLegaleseText = false;
1260         break;
1261     case Atom::LineBreak:
1262         //xmlWriter().writeEmptyElement("br");
1263         break;
1264     case Atom::Link:
1265     {
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)));
1272         }
1273         else if (!inSectionHeading) {
1274             beginLink(myLink);
1275         }
1276 #if 0
1277         else {
1278             //xmlWriter().writeCharacters(atom->string());
1279             //qDebug() << "MYLINK:" << myLink << outFileName() << atom->string();
1280         }
1281 #endif
1282         skipAhead = 1;
1283     }
1284         break;
1285     case Atom::GuidLink:
1286     {
1287         beginLink(atom->string());
1288         skipAhead = 1;
1289     }
1290         break;
1291     case Atom::LinkNode:
1292     {
1293         const Node* node = CodeMarker::nodeForString(atom->string());
1294         beginLink(linkForNode(node, relative));
1295         skipAhead = 1;
1296     }
1297         break;
1298     case Atom::ListLeft:
1299         if (in_para) {
1300             writeEndTag(); // </p>
1301             in_para = false;
1302         }
1303         if (atom->string() == ATOM_LIST_BULLET) {
1304             writeStartTag(DT_ul);
1305         }
1306         else if (atom->string() == ATOM_LIST_TAG) {
1307             writeStartTag(DT_dl);
1308         }
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>
1325             }
1326             else {
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>
1337             }
1338         }
1339         else {
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());
1354             }
1355         }
1356         break;
1357     case Atom::ListItemNumber:
1358         // nothing
1359         break;
1360     case Atom::ListTagLeft:
1361         if (atom->string() == ATOM_LIST_TAG) {
1362             writeStartTag(DT_dt);
1363         }
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(),
1369                                                                            relative))));
1370             writeEndTag(); // </tt>
1371             writeEndTag(); // </stentry>
1372             writeStartTag(DT_stentry);
1373
1374             QString itemValue;
1375             if (relative->type() == Node::Enum) {
1376                 const EnumNode *enume = static_cast<const EnumNode *>(relative);
1377                 itemValue = enume->itemValue(atom->next()->string());
1378             }
1379
1380             if (itemValue.isEmpty())
1381                 xmlWriter().writeCharacters("?");
1382             else {
1383                 writeStartTag(DT_tt);
1384                 writeCharacters(protectEnc(itemValue));
1385                 writeEndTag(); // </tt>
1386             }
1387             skipAhead = 1;
1388         }
1389         break;
1390     case Atom::ListTagRight:
1391         if (atom->string() == ATOM_LIST_TAG)
1392             writeEndTag(); // </dt>
1393         break;
1394     case Atom::ListItemLeft:
1395         if (atom->string() == ATOM_LIST_TAG) {
1396             writeStartTag(DT_dd);
1397         }
1398         else if (atom->string() == ATOM_LIST_VALUE) {
1399             if (threeColumnEnumValueTable) {
1400                 writeEndTag(); // </stentry>
1401                 writeStartTag(DT_stentry);
1402             }
1403         }
1404         else {
1405             writeStartTag(DT_li);
1406         }
1407         if (matchAhead(atom, Atom::ParaLeft))
1408             skipAhead = 1;
1409         break;
1410     case Atom::ListItemRight:
1411         if (atom->string() == ATOM_LIST_TAG) {
1412             writeEndTag(); // </dd>
1413         }
1414         else if (atom->string() == ATOM_LIST_VALUE) {
1415             writeEndTag(); // </stentry>
1416             writeEndTag(); // </strow>
1417         }
1418         else {
1419             writeEndTag(); // </li>
1420         }
1421         break;
1422     case Atom::ListRight:
1423         if (atom->string() == ATOM_LIST_BULLET) {
1424             writeEndTag(); // </ul>
1425         }
1426         else if (atom->string() == ATOM_LIST_TAG) {
1427             writeEndTag(); // </dl>
1428         }
1429         else if (atom->string() == ATOM_LIST_VALUE) {
1430             writeEndTag(); // </simpletable>
1431         }
1432         else {
1433             writeEndTag(); // </ol>
1434         }
1435         break;
1436     case Atom::Nop:
1437         // nothing
1438         break;
1439     case Atom::ParaLeft:
1440         writeStartTag(DT_p);
1441         if (inLegaleseText)
1442             xmlWriter().writeAttribute("outputclass","legalese");
1443         in_para = true;
1444         break;
1445     case Atom::ParaRight:
1446         endLink();
1447         if (in_para) {
1448             writeEndTag(); // </p>
1449             in_para = false;
1450         }
1451         break;
1452     case Atom::QuotationLeft:
1453         writeStartTag(DT_lq);
1454         break;
1455     case Atom::QuotationRight:
1456         writeEndTag(); // </lq>
1457         break;
1458     case Atom::RawString:
1459         if (atom->string() == " ")
1460             break;
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>
1467         }
1468         else if (atom->string() == "<sup>&reg;</sup>") {
1469             writeStartTag(DT_tm);
1470             xmlWriter().writeAttribute("tmtype","reg");
1471             writeEndTag(); // </tm>
1472         }
1473         else {
1474             writeStartTag(DT_pre);
1475             xmlWriter().writeAttribute("outputclass","raw-html");
1476             writeCharacters(atom->string());
1477             writeEndTag(); // </pre>
1478         }
1479         break;
1480     case Atom::SectionLeft:
1481 #if 0
1482         if (inApiDesc) {
1483             writeEndTag(); // </apiDesc>
1484             inApiDesc = false;
1485         }
1486 #endif
1487         enterSection("details",QString());
1488         //writeGuidAttribute(Doc::canonicalTitle(Text::sectionHeading(atom).toString()));
1489         break;
1490     case Atom::SectionRight:
1491         leaveSection();
1492         break;
1493     case Atom::SectionHeadingLeft:
1494     {
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;
1503     }
1504         break;
1505     case Atom::SectionHeadingRight:
1506         writeEndTag(); // </title> (see case Atom::SectionHeadingLeft)
1507         inSectionHeading = false;
1508         break;
1509     case Atom::SidebarLeft:
1510         // nothing
1511         break;
1512     case Atom::SidebarRight:
1513         // nothing
1514         break;
1515     case Atom::String:
1516         if (inLink && !inContents && !inSectionHeading) {
1517             generateLink(atom, relative, marker);
1518         }
1519         else {
1520             writeCharacters(atom->string());
1521         }
1522         break;
1523     case Atom::TableLeft:
1524     {
1525         QString attr;
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";
1530         if (in_para) {
1531             writeEndTag(); // </p>
1532             in_para = false;
1533         }
1534         writeStartTag(DT_table);
1535         if (!attr.isEmpty())
1536             xmlWriter().writeAttribute("outputclass",attr);
1537         numTableRows = 0;
1538         if (tableColumnCount != 0) {
1539             qDebug() << "ERROR: Nested tables!";
1540             tableColumnCount = 0;
1541         }
1542         tableColumnCount = countTableColumns(atom->next());
1543         writeStartTag(DT_tgroup);
1544         xmlWriter().writeAttribute("cols",QString::number(tableColumnCount));
1545         inTableHeader = false;
1546         inTableBody = false;
1547     }
1548         break;
1549     case Atom::TableRight:
1550         writeEndTag(); // </tbody>
1551         writeEndTag(); // </tgroup>
1552         writeEndTag(); // </table>
1553         inTableHeader = false;
1554         inTableBody = false;
1555         tableColumnCount = 0;
1556         break;
1557     case Atom::TableHeaderLeft:
1558         if (inTableBody) {
1559             writeEndTag(); // </tbody>
1560             writeEndTag(); // </tgroup>
1561             writeEndTag(); // </table>
1562             inTableHeader = false;
1563             inTableBody = false;
1564             tableColumnCount = 0;
1565             writeStartTag(DT_table);
1566             numTableRows = 0;
1567             tableColumnCount = countTableColumns(atom);
1568             writeStartTag(DT_tgroup);
1569             xmlWriter().writeAttribute("cols",QString::number(tableColumnCount));
1570         }
1571         writeStartTag(DT_thead);
1572         xmlWriter().writeAttribute("valign","top");
1573         writeStartTag(DT_row);
1574         xmlWriter().writeAttribute("valign","top");
1575         inTableHeader = true;
1576         inTableBody = false;
1577         break;
1578     case Atom::TableHeaderRight:
1579         writeEndTag(); // </row>
1580         if (matchAhead(atom, Atom::TableHeaderLeft)) {
1581             skipAhead = 1;
1582             writeStartTag(DT_row);
1583             xmlWriter().writeAttribute("valign","top");
1584         }
1585         else {
1586             writeEndTag(); // </thead>
1587             inTableHeader = false;
1588             inTableBody = true;
1589             writeStartTag(DT_tbody);
1590         }
1591         break;
1592     case Atom::TableRowLeft:
1593         if (!inTableHeader && !inTableBody) {
1594             inTableBody = true;
1595             writeStartTag(DT_tbody);
1596         }
1597         writeStartTag(DT_row);
1598         attr = atom->string();
1599         if (!attr.isEmpty()) {
1600             if (attr.contains('=')) {
1601                 int index = 0;
1602                 int from = 0;
1603                 QString values;
1604                 while (index >= 0) {
1605                     index = attr.indexOf('"',from);
1606                     if (index >= 0) {
1607                         ++index;
1608                         from = index;
1609                         index = attr.indexOf('"',from);
1610                         if (index > from) {
1611                             if (!values.isEmpty())
1612                                 values.append(' ');
1613                             values += attr.mid(from,index-from);
1614                             from = index+1;
1615                         }
1616                     }
1617                 }
1618                 attr = values;
1619             }
1620             xmlWriter().writeAttribute("outputclass", attr);
1621         }
1622         xmlWriter().writeAttribute("valign","top");
1623         break;
1624     case Atom::TableRowRight:
1625         writeEndTag(); // </row>
1626         break;
1627     case Atom::TableItemLeft:
1628     {
1629         QString values;
1630         writeStartTag(DT_entry);
1631         for (int i=0; i<atom->count(); ++i) {
1632             attr = atom->string(i);
1633             if (attr.contains('=')) {
1634                 int index = 0;
1635                 int from = 0;
1636                 while (index >= 0) {
1637                     index = attr.indexOf('"',from);
1638                     if (index >= 0) {
1639                         ++index;
1640                         from = index;
1641                         index = attr.indexOf('"',from);
1642                         if (index > from) {
1643                             if (!values.isEmpty())
1644                                 values.append(' ');
1645                             values += attr.mid(from,index-from);
1646                             from = index+1;
1647                         }
1648                     }
1649                 }
1650             }
1651             else {
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(')');
1656                     }
1657                 }
1658             }
1659         }
1660         if (!values.isEmpty())
1661             xmlWriter().writeAttribute("outputclass",values);
1662         if (matchAhead(atom, Atom::ParaLeft))
1663             skipAhead = 1;
1664     }
1665         break;
1666     case Atom::TableItemRight:
1667         if (inTableHeader)
1668             writeEndTag(); // </entry>
1669         else {
1670             writeEndTag(); // </entry>
1671         }
1672         if (matchAhead(atom, Atom::ParaLeft))
1673             skipAhead = 1;
1674         break;
1675     case Atom::TableOfContents:
1676     {
1677         int numColumns = 1;
1678         const Node* node = relative;
1679
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);
1686             pieces.pop_front();
1687             QString path = pieces.join(" ").trimmed();
1688             node = findNodeForTarget(path, relative, marker, atom);
1689         }
1690
1691         if (params.size() == 2) {
1692             numColumns = qMax(columnText.toInt(), numColumns);
1693             sectionUnit = (Doc::Sections)params.at(1).toInt();
1694         }
1695
1696         if (node)
1697             generateTableOfContents(node,
1698                                     marker,
1699                                     sectionUnit,
1700                                     numColumns,
1701                                     relative);
1702     }
1703         break;
1704     case Atom::Target:
1705         if (in_para) {
1706             writeEndTag(); // </p>
1707             in_para = false;
1708         }
1709         writeStartTag(DT_p);
1710         writeGuidAttribute(Doc::canonicalTitle(atom->string()));
1711         xmlWriter().writeAttribute("outputclass","target");
1712         //xmlWriter().writeCharacters(protectEnc(atom->string()));
1713         writeEndTag(); // </p>
1714         break;
1715     case Atom::UnhandledFormat:
1716         writeStartTag(DT_b);
1717         xmlWriter().writeAttribute("outputclass","error");
1718         xmlWriter().writeCharacters("<Missing DITAXML>");
1719         writeEndTag(); // </b>
1720         break;
1721     case Atom::UnknownCommand:
1722         writeStartTag(DT_b);
1723         xmlWriter().writeAttribute("outputclass","error unknown-command");
1724         writeCharacters(protectEnc(atom->string()));
1725         writeEndTag(); // </b>
1726         break;
1727     case Atom::QmlText:
1728     case Atom::EndQmlText:
1729         // don't do anything with these. They are just tags.
1730         break;
1731     default:
1732         //        unknownAtom(atom);
1733         break;
1734     }
1735     return skipAhead;
1736 }
1737
1738 /*!
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.
1742  */
1743 void
1744 DitaXmlGenerator::generateClassLikeNode(const InnerNode* inner, CodeMarker* marker)
1745 {
1746     QList<Section>::ConstIterator s;
1747
1748     QString title;
1749     QString rawTitle;
1750     QString fullTitle;
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";
1756
1757         /*
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" .
1762          */
1763         generateHeader(inner, fullTitle);
1764         generateBrief(inner, marker); // <shortdesc>
1765         writeProlog(inner);
1766
1767         writeStartTag(DT_cxxClassDetail);
1768         writeStartTag(DT_cxxClassDefinition);
1769         writeLocation(nsn);
1770         writeEndTag(); // <cxxClassDefinition>
1771
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>
1778         }
1779         generateStatus(nsn, marker);
1780         generateThreadSafeness(nsn, marker);
1781         generateSince(nsn, marker);
1782
1783         enterSection("h2","Detailed Description");
1784         generateBody(nsn, marker);
1785         generateAlsoList(nsn, marker);
1786         leaveSection();
1787         leaveSection(); // </apiDesc>
1788
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;
1799                 }
1800                 else {
1801                     QString attr;
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);
1810                     }
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);
1820                     }
1821                 }
1822                 ++s;
1823             }
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);
1833                     ++s;
1834                 }
1835             }
1836             leaveSection();
1837         }
1838
1839         writeEndTag(); // </cxxClassDetail>
1840
1841         // not included: <related-links>
1842         // not included: <cxxClassNested>
1843
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);
1850                 break;
1851             }
1852             ++s;
1853         }
1854
1855         s = detailSections.begin();
1856         while (s != detailSections.end()) {
1857             if ((*s).name == "Function Documentation") {
1858                 writeFunctions((*s),nsn,marker);
1859             }
1860             else if ((*s).name == "Type Documentation") {
1861                 writeEnumerations((*s),marker);
1862                 writeTypedefs((*s),marker);
1863             }
1864             else if ((*s).name == "Namespaces") {
1865                 qDebug() << "Nested namespaces" << outFileName();
1866             }
1867             else if ((*s).name == "Macro Documentation") {
1868                 //writeMacros((*s),marker);
1869             }
1870             ++s;
1871         }
1872
1873         generateLowStatusMembers(inner,marker,CodeMarker::Obsolete);
1874         generateLowStatusMembers(inner,marker,CodeMarker::Compat);
1875         writeEndTag(); // </cxxClass>
1876     }
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";
1882
1883         generateHeader(inner, fullTitle);
1884         generateBrief(inner, marker); // <shortdesc>
1885         writeProlog(inner);
1886
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>
1897         }
1898         writeDerivations(cn, marker); // <cxxClassDerivations>
1899
1900         // not included: <cxxClassTemplateParameters>
1901
1902         writeLocation(cn);
1903         writeEndTag(); // <cxxClassDefinition>
1904
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>
1911         }
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);
1920         leaveSection();
1921         leaveSection(); // </apiDesc>
1922
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;
1933                 }
1934                 else {
1935                     QString attr;
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);
1944                     }
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);
1954                     }
1955                 }
1956                 ++s;
1957             }
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);
1967                     ++s;
1968                 }
1969             }
1970             leaveSection();
1971         }
1972
1973         // not included: <example> or <apiImpl>
1974
1975         writeEndTag(); // </cxxClassDetail>
1976
1977         // not included: <related-links>
1978         // not included: <cxxClassNested>
1979
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);
1986             }
1987             else if ((*s).name == "Member Type Documentation") {
1988                 writeEnumerations((*s),marker);
1989                 writeTypedefs((*s),marker);
1990             }
1991             else if ((*s).name == "Member Variable Documentation") {
1992                 writeDataMembers((*s),marker);
1993             }
1994             else if ((*s).name == "Property Documentation") {
1995                 writeProperties((*s),marker);
1996             }
1997             else if ((*s).name == "Macro Documentation") {
1998                 //writeMacros((*s),marker);
1999             }
2000             else if ((*s).name == "Related Non-Members") {
2001                 QString attribute("related-non-member");
2002                 writeFunctions((*s),cn,marker,attribute);
2003             }
2004             ++s;
2005         }
2006
2007         generateLowStatusMembers(inner,marker,CodeMarker::Obsolete);
2008         generateLowStatusMembers(inner,marker,CodeMarker::Compat);
2009         writeEndTag(); // </cxxClass>
2010     }
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);
2015         title = rawTitle;
2016
2017         /*
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" .
2022          */
2023         generateHeader(inner, fullTitle);
2024         generateBrief(inner, marker); // <shortdesc>
2025         writeProlog(inner);
2026
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>
2034         }
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);
2042         leaveSection();
2043         leaveSection(); // </apiDesc>
2044
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;
2055                 }
2056                 else {
2057                     QString attr;
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);
2066                     }
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);
2076                     }
2077                 }
2078                 ++s;
2079             }
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);
2090                     ++s;
2091                 }
2092             }
2093             leaveSection();
2094         }
2095
2096         writeEndTag(); // </cxxClassDetail>
2097
2098         // not included: <related-links>
2099         // not included: <cxxClassNested>
2100
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);
2107                 break;
2108             }
2109             ++s;
2110         }
2111
2112         s = detailSections.begin();
2113         while (s != detailSections.end()) {
2114             if ((*s).name == "Function Documentation") {
2115                 writeFunctions((*s),fn,marker);
2116             }
2117             else if ((*s).name == "Type Documentation") {
2118                 writeEnumerations((*s),marker);
2119                 writeTypedefs((*s),marker);
2120             }
2121             else if ((*s).name == "Namespaces") {
2122                 qDebug() << "Nested namespaces" << outFileName();
2123             }
2124             else if ((*s).name == "Macro Documentation") {
2125                 //writeMacros((*s),marker);
2126             }
2127             ++s;
2128         }
2129         generateLowStatusMembers(inner,marker,CodeMarker::Obsolete);
2130         generateLowStatusMembers(inner,marker,CodeMarker::Compat);
2131         writeEndTag(); // </cxxClass>
2132     }
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;
2141
2142         generateHeader(inner, fullTitle);
2143         generateBrief(inner, marker); // <shortdesc>
2144         writeProlog(inner);
2145
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>
2153         }
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);
2160         if (cn) {
2161             generateQmlText(cn->doc().body(), cn, marker, qcn->name());
2162             generateAlsoList(cn, marker);
2163         }
2164         leaveSection();
2165         leaveSection(); // </apiDesc>
2166
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()) {
2173                 QString attr;
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);
2183                 }
2184                 ++s;
2185             }
2186             leaveSection();
2187         }
2188
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()) {
2196                     QString attr;
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);
2205                         ++m;
2206                     }
2207                 }
2208                 ++s;
2209             }
2210             leaveSection();
2211         }
2212         writeEndTag(); // </cxxClassDetail>
2213         writeEndTag(); // </cxxClass>
2214     }
2215 }
2216
2217
2218 /*!
2219   Write a list item for a \a link with the given \a text.
2220  */
2221 void DitaXmlGenerator::writeXrefListItem(const QString& link, const QString& text)
2222 {
2223     writeStartTag(DT_li);
2224     writeStartTag(DT_xref);
2225     // formathtml
2226     writeHrefAttribute(link);
2227     writeCharacters(text);
2228     writeEndTag(); // </xref>
2229     writeEndTag(); // </li>
2230 }
2231
2232 /*!
2233   Generate the DITA page for a qdoc file that doesn't map
2234   to an underlying c++ file.
2235  */
2236 void DitaXmlGenerator::generateFakeNode(const FakeNode* fake, CodeMarker* marker)
2237 {
2238     /*
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.
2242      */
2243     if (fake->subType() == Node::Page && fake->pageType() == Node::DitaMapPage) {
2244         const DitaMapNode* dmn = static_cast<const DitaMapNode*>(fake);
2245         writeDitaMap(dmn);
2246         return;
2247     }
2248
2249     QList<Section> sections;
2250     QList<Section>::const_iterator s;
2251     QString fullTitle = fake->fullTitle();
2252
2253     if (fake->subType() == Node::QmlBasicType) {
2254         fullTitle = "QML Basic Type: " + fullTitle;
2255     }
2256     else if (fake->subType() == Node::Collision) {
2257         fullTitle = "Name Collision: " + fullTitle;
2258     }
2259
2260     generateHeader(fake, fullTitle);
2261     generateBrief(fake, marker); // <shortdesc>
2262     writeProlog(fake);
2263
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()]);
2271             leaveSection();
2272         }
2273         if (moduleClassMap.contains(fake->name())) {
2274             enterSection("h2","Classes");
2275             generateAnnotatedList(fake, marker, moduleClassMap[fake->name()]);
2276             leaveSection();
2277         }
2278     }
2279
2280     if (fake->doc().isEmpty()) {
2281         if (fake->subType() == Node::File) {
2282             Text text;
2283             Quoter quoter;
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(), "", "");
2291             text.clear();
2292             text << Atom(Atom::Code, code);
2293             generateText(text, fake, marker);
2294         }
2295     }
2296     else {
2297         if (fake->subType() == Node::Module) {
2298             enterSection("h2","Detailed Description");
2299             generateBody(fake, marker);
2300             leaveSection();
2301         }
2302         else {
2303             generateBody(fake, marker);
2304         }
2305         generateAlsoList(fake, marker);
2306
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;
2312             }
2313             generateAnnotatedList(fake, marker, qmlModuleMembersMap);
2314         }
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;
2320             }
2321             generateAnnotatedList(fake, marker, groupMembersMap);
2322         }
2323     }
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()));
2327         return;
2328     }
2329     writeRelatedLinks(fake, marker);
2330     writeEndTag(); // </topic>
2331 }
2332
2333 /*!
2334   This function writes a \e{<link>} element inside a
2335   \e{<related-links>} element.
2336
2337   \sa writeRelatedLinks()
2338  */
2339 void DitaXmlGenerator::writeLink(const Node* node,
2340                                  const QString& text,
2341                                  const QString& role)
2342 {
2343     if (node) {
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>
2354     }
2355 }
2356
2357 /*!
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
2362   \c{start} link.
2363  */
2364 void DitaXmlGenerator::writeRelatedLinks(const FakeNode* node, CodeMarker* marker)
2365 {
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");
2374         }
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");
2379         }
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");
2384         }
2385         writeEndTag(); // </related-links>
2386     }
2387 }
2388
2389 /*!
2390   Returns "dita" for this subclass of class Generator.
2391  */
2392 QString DitaXmlGenerator::fileExtension(const Node * /* node */) const
2393 {
2394     return "dita";
2395 }
2396
2397 /*!
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.
2403  */
2404 void DitaXmlGenerator::generateHeader(const Node* node,
2405                                       const QString& name,
2406                                       bool subpage)
2407 {
2408     if (!node)
2409         return;
2410
2411     DitaTag mainTag = DT_cxxClass;
2412     DitaTag nameTag = DT_apiName;
2413     QString doctype;
2414     QString dtd;
2415     QString base;
2416     QString version;
2417     QString outputclass;
2418
2419     if (node->type() == Node::Class) {
2420         mainTag = DT_cxxClass;
2421         nameTag = DT_apiName;
2422         dtd = "dtd/cxxClass.dtd";
2423         version = "0.7.0";
2424         doctype = "<!DOCTYPE " + ditaTags[mainTag] +
2425                 " PUBLIC \"-//NOKIA//DTD DITA C++ API Class Reference Type v" +
2426                 version + "//EN\" \"" + dtd + "\">";
2427     }
2428     else if (node->type() == Node::Namespace) {
2429         mainTag = DT_cxxClass;
2430         nameTag = DT_apiName;
2431         dtd = "dtd/cxxClass.dtd";
2432         version = "0.7.0";
2433         doctype = "<!DOCTYPE " + ditaTags[mainTag] +
2434                 " PUBLIC \"-//NOKIA//DTD DITA C++ API Class Reference Type v" +
2435                 version + "//EN\" \"" + dtd + "\">";
2436         outputclass = "namespace";
2437     }
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";
2443             version = "0.7.0";
2444             doctype = "<!DOCTYPE " + ditaTags[mainTag] +
2445                     " PUBLIC \"-//NOKIA//DTD DITA C++ API Class Reference Type v" +
2446                     version + "//EN\" \"" + dtd + "\">";
2447             outputclass = "headerfile";
2448         }
2449         else if (node->subType() == Node::QmlClass) {
2450             mainTag = DT_cxxClass;
2451             nameTag = DT_apiName;
2452             dtd = "dtd/cxxClass.dtd";
2453             version = "0.7.0";
2454             doctype = "<!DOCTYPE " + ditaTags[mainTag] +
2455                     " PUBLIC \"-//NOKIA//DTD DITA C++ API Class Reference Type v" +
2456                     version + "//EN\" \"" + dtd + "\">";
2457             outputclass = "QML-class";
2458         }
2459         else {
2460             mainTag = DT_topic;
2461             nameTag = DT_title;
2462             dtd = "dtd/topic.dtd";
2463             doctype = "<!DOCTYPE " + ditaTags[mainTag] +
2464                     " PUBLIC \"-//OASIS//DTD DITA Topic//EN\" \"" + dtd + "\">";
2465             switch (node->subType()) {
2466             case Node::Page:
2467                 outputclass = node->pageTypeString();
2468                 break;
2469             case Node::Group:
2470                 outputclass = "group";
2471                 break;
2472             case Node::Example:
2473                 outputclass = "example";
2474                 break;
2475             case Node::File:
2476                 outputclass = "file";
2477                 break;
2478             case Node::Image:  // not used
2479                 outputclass = "image";
2480                 break;
2481             case Node::Module:
2482                 outputclass = "module";
2483                 break;
2484             case Node::ExternalPage: // not used
2485                 outputclass = "externalpage";
2486                 break;
2487             default:
2488                 outputclass = "page";
2489             }
2490         }
2491     }
2492
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>
2503 }
2504
2505 /*!
2506   Outputs the \e brief command as a <shortdesc> element.
2507  */
2508 void DitaXmlGenerator::generateBrief(const Node* node, CodeMarker* marker)
2509 {
2510     Text brief = node->doc().briefText(true); // zzz
2511     if (!brief.isEmpty()) {
2512         generateText(brief, node, marker);
2513     }
2514 }
2515
2516 /*!
2517   zzz
2518   Generates a table of contents beginning at \a node.
2519   Currently just returns without writing anything.
2520  */
2521 void DitaXmlGenerator::generateTableOfContents(const Node* node,
2522                                                CodeMarker* marker,
2523                                                Doc::Sections sectionUnit,
2524                                                int numColumns,
2525                                                const Node* relative)
2526
2527 {
2528     return;
2529     if (!node->doc().hasTableOfContents())
2530         return;
2531     QList<Atom *> toc = node->doc().tableOfContents();
2532     if (toc.isEmpty())
2533         return;
2534
2535     QString nodeName = "";
2536     if (node != relative)
2537         nodeName = node->name();
2538
2539     QStringList sectionNumber;
2540     int columnSize = 0;
2541
2542     QString tdTag;
2543     if (numColumns > 1) {
2544         tdTag = "<td>"; /* width=\"" + QString::number((100 + numColumns - 1) / numColumns) + "%\">";*/
2545         out() << "<table class=\"toc\">\n<tr class=\"topAlign\">"
2546               << tdTag << '\n';
2547     }
2548
2549     // disable nested links in table of contents
2550     inContents = true;
2551     inLink = true;
2552
2553     for (int i = 0; i < toc.size(); ++i) {
2554         Atom *atom = toc.at(i);
2555
2556         int nextLevel = atom->string().toInt();
2557         if (nextLevel > (int)sectionUnit)
2558             continue;
2559
2560         if (sectionNumber.size() < nextLevel) {
2561             do {
2562                 out() << "<ul>";
2563                 sectionNumber.append("1");
2564             } while (sectionNumber.size() < nextLevel);
2565         }
2566         else {
2567             while (sectionNumber.size() > nextLevel) {
2568                 out() << "</ul>\n";
2569                 sectionNumber.removeLast();
2570             }
2571             sectionNumber.last() = QString::number(sectionNumber.last().toInt() + 1);
2572         }
2573         int numAtoms;
2574         Text headingText = Text::sectionHeading(atom);
2575
2576         if (sectionNumber.size() == 1 && columnSize > toc.size() / numColumns) {
2577             out() << "</ul></td>" << tdTag << "<ul>\n";
2578             columnSize = 0;
2579         }
2580         out() << "<li>";
2581         out() << "<xref href=\""
2582               << nodeName
2583               << "#"
2584               << Doc::canonicalTitle(headingText.toString())
2585               << "\">";
2586         generateAtomList(headingText.firstAtom(), node, marker, true, numAtoms);
2587         out() << "</xref></li>\n";
2588
2589         ++columnSize;
2590     }
2591     while (!sectionNumber.isEmpty()) {
2592         out() << "</ul>\n";
2593         sectionNumber.removeLast();
2594     }
2595
2596     if (numColumns > 1)
2597         out() << "</td></tr></table>\n";
2598
2599     inContents = false;
2600     inLink = false;
2601 }
2602
2603 /*!
2604   zzz
2605   Revised for the new doc format.
2606   Generates a table of contents beginning at \a node.
2607  */
2608 void DitaXmlGenerator::generateTableOfContents(const Node* node,
2609                                                CodeMarker* marker,
2610                                                QList<Section>* sections)
2611 {
2612     QList<Atom*> toc;
2613     if (node->doc().hasTableOfContents())
2614         toc = node->doc().tableOfContents();
2615     if (toc.isEmpty() && !sections && (node->subType() != Node::Module))
2616         return;
2617
2618     QStringList sectionNumber;
2619     int detailsBase = 0;
2620
2621     // disable nested links in table of contents
2622     inContents = true;
2623     inLink = true;
2624
2625     out() << "<div class=\"toc\">\n";
2626     out() << "<h3>Contents</h3>\n";
2627     sectionNumber.append("1");
2628     out() << "<ul>\n";
2629
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";
2637         }
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";
2644         }
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) {
2652                 detailsBase = 1;
2653                 break;
2654             }
2655         }
2656     }
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";
2667             }
2668             ++s;
2669         }
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) {
2677                 detailsBase = 1;
2678                 break;
2679             }
2680         }
2681     }
2682
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) {
2687             do {
2688                 sectionNumber.append("1");
2689             } while (sectionNumber.size() < nextLevel);
2690         }
2691         else {
2692             while (sectionNumber.size() > nextLevel) {
2693                 sectionNumber.removeLast();
2694             }
2695             sectionNumber.last() = QString::number(sectionNumber.last().toInt() + 1);
2696         }
2697         int numAtoms;
2698         Text headingText = Text::sectionHeading(atom);
2699         QString s = headingText.toString();
2700         out() << "<li class=\"level"
2701               << sectionNumber.size()
2702               << "\">";
2703         out() << "<xref href=\""
2704               << "#"
2705               << Doc::canonicalTitle(s)
2706               << "\">";
2707         generateAtomList(headingText.firstAtom(), node, marker, true, numAtoms);
2708         out() << "</xref></li>\n";
2709     }
2710     while (!sectionNumber.isEmpty()) {
2711         sectionNumber.removeLast();
2712     }
2713     out() << "</ul>\n";
2714     out() << "</div>\n";
2715     inContents = false;
2716     inLink = false;
2717 }
2718
2719 void DitaXmlGenerator::generateLowStatusMembers(const InnerNode* inner,
2720                                                 CodeMarker* marker,
2721                                                 CodeMarker::Status status)
2722 {
2723     QString attribute;
2724     if (status == CodeMarker::Compat)
2725         attribute = "Qt3-support";
2726     else if (status == CodeMarker::Obsolete)
2727         attribute = "obsolete";
2728     else
2729         return;
2730
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)
2735             j.remove();
2736     }
2737     if (sections.isEmpty())
2738         return;
2739
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);
2744         }
2745         else if ((*s).name == "Member Type Documentation") {
2746             writeEnumerations((*s),marker,attribute);
2747             writeTypedefs((*s),marker,attribute);
2748         }
2749         else if ((*s).name == "Member Variable Documentation") {
2750             writeDataMembers((*s),marker,attribute);
2751         }
2752         else if ((*s).name == "Property Documentation") {
2753             writeProperties((*s),marker,attribute);
2754         }
2755         else if ((*s).name == "Macro Documentation") {
2756             //writeMacros((*s),marker,attribute);
2757         }
2758         ++s;
2759     }
2760 }
2761
2762 /*!
2763   Write the XML for the class hierarchy to the current XML stream.
2764  */
2765 void DitaXmlGenerator::generateClassHierarchy(const Node* relative,
2766                                               CodeMarker* marker,
2767                                               const QMap<QString,const Node*>& classMap)
2768 {
2769     if (classMap.isEmpty())
2770         return;
2771
2772     NodeMap topLevel;
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);
2778         ++c;
2779     }
2780
2781     QStack<NodeMap > stack;
2782     stack.push(topLevel);
2783
2784     writeStartTag(DT_ul);
2785     while (!stack.isEmpty()) {
2786         if (stack.top().isEmpty()) {
2787             stack.pop();
2788             writeEndTag(); // </ul>
2789             if (!stack.isEmpty())
2790                 writeEndTag(); // </li>
2791         }
2792         else {
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());
2799
2800             NodeMap newTop;
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);
2804             }
2805             if (!newTop.isEmpty()) {
2806                 stack.push(newTop);
2807                 writeStartTag(DT_li);
2808                 writeStartTag(DT_ul);
2809             }
2810         }
2811     }
2812 }
2813
2814 /*!
2815   Write XML for the contents of the \a nodeMap to the current
2816   XML stream.
2817  */
2818 void DitaXmlGenerator::generateAnnotatedList(const Node* relative,
2819                                              CodeMarker* marker,
2820                                              const NodeMap& nodeMap)
2821 {
2822     if (nodeMap.isEmpty())
2823         return;
2824     writeStartTag(DT_table);
2825     xmlWriter().writeAttribute("outputclass","annotated");
2826     writeStartTag(DT_tgroup);
2827     xmlWriter().writeAttribute("cols","2");
2828     writeStartTag(DT_tbody);
2829
2830     foreach (const QString& name, nodeMap.keys()) {
2831         const Node* node = nodeMap[name];
2832
2833         if (node->status() == Node::Obsolete)
2834             continue;
2835
2836         writeStartTag(DT_row);
2837         writeStartTag(DT_entry);
2838         writeStartTag(DT_p);
2839         generateFullName(node, relative, marker);
2840         writeEndTag(); // </p>
2841         writeEndTag(); // <entry>
2842
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>
2851             }
2852         }
2853         else {
2854             writeStartTag(DT_entry);
2855             writeStartTag(DT_p);
2856             writeCharacters(protectEnc(node->doc().briefText().toString())); // zzz
2857             writeEndTag(); // </p>
2858             writeEndTag(); // <entry>
2859         }
2860         writeEndTag(); // </row>
2861     }
2862     writeEndTag(); // </tbody>
2863     writeEndTag(); // </tgroup>
2864     writeEndTag(); // </table>
2865 }
2866
2867 /*!
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.
2875  */
2876 void DitaXmlGenerator::generateCompactList(const Node* relative,
2877                                            CodeMarker* marker,
2878                                            const NodeMap& classMap,
2879                                            bool includeAlphabet,
2880                                            QString commonPrefix)
2881 {
2882     const int NumParagraphs = 37; // '0' to '9', 'A' to 'Z', '_'
2883
2884     if (classMap.isEmpty())
2885         return;
2886
2887     /*
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.
2894      */
2895     int commonPrefixLen = commonPrefix.length();
2896     if (commonPrefixLen == 0) {
2897         QVector<int> count(26);
2898         for (int i=0; i<26; ++i)
2899             count[i] = 0;
2900
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();
2907                     ++count[idx];
2908                 }
2909             }
2910             ++iter;
2911         }
2912         int highest = 0;
2913         int idx = -1;
2914         for (int i=0; i<26; ++i) {
2915             if (count[i] > highest) {
2916                 highest = count[i];
2917                 idx = i;
2918             }
2919         }
2920         idx += QChar('A').unicode();
2921         QChar common(idx);
2922         commonPrefix = common;
2923         commonPrefixLen = 1;
2924
2925 #if 0
2926         /*
2927           The algorithm below eventually failed, so it was replaced
2928           with the simple (perhaps too simple) algorithm above.
2929
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".
2934
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
2938           begin with Q.
2939         */
2940         QString first;
2941         QString last;
2942         NodeMap::const_iterator iter = classMap.begin();
2943         while (iter != classMap.end()) {
2944             if (!iter.key().contains("::")) {
2945                 first = iter.key();
2946                 break;
2947             }
2948             ++iter;
2949         }
2950
2951         if (first.isEmpty())
2952             first = classMap.begin().key();
2953
2954         iter = classMap.end();
2955         while (iter != classMap.begin()) {
2956             --iter;
2957             if (!iter.key().contains("::")) {
2958                 last = iter.key();
2959                 break;
2960             }
2961         }
2962
2963         if (last.isEmpty())
2964             last = classMap.begin().key();
2965
2966         if (classMap.size() > 1) {
2967             while (commonPrefixLen < first.length() + 1 &&
2968                    commonPrefixLen < last.length() + 1 &&
2969                    first[commonPrefixLen] == last[commonPrefixLen])
2970                 ++commonPrefixLen;
2971         }
2972
2973         commonPrefix = first.left(commonPrefixLen);
2974 #endif
2975     }
2976
2977     /*
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.
2982     */
2983     NodeMap paragraph[NumParagraphs+1];
2984     QString paragraphName[NumParagraphs+1];
2985     QSet<char> usedParagraphNames;
2986
2987     NodeMap::ConstIterator c = classMap.begin();
2988     while (c != classMap.end()) {
2989         QStringList pieces = c.key().split("::");
2990         QString key;
2991         int idx = commonPrefixLen;
2992         if (!pieces.last().startsWith(commonPrefix))
2993             idx = 0;
2994         if (pieces.size() == 1)
2995             key = pieces.last().mid(idx).toLower();
2996         else
2997             key = pieces.last().toLower();
2998
2999         int paragraphNr = NumParagraphs - 1;
3000
3001         if (key[0].digitValue() != -1) {
3002             paragraphNr = key[0].digitValue();
3003         }
3004         else if (key[0] >= QLatin1Char('a') && key[0] <= QLatin1Char('z')) {
3005             paragraphNr = 10 + key[0].unicode() - 'a';
3006         }
3007
3008         paragraphName[paragraphNr] = key[0].toUpper();
3009         usedParagraphNames.insert(key[0].toLower().cell());
3010         paragraph[paragraphNr].insert(key, c.value());
3011         ++c;
3012     }
3013
3014     /*
3015       Each paragraph j has a size: paragraph[j].count(). In the
3016       discussion, we will assume paragraphs 0 to 5 will have sizes
3017       3, 1, 4, 1, 5, 9.
3018
3019       We now want to compute the paragraph offset. Paragraphs 0 to 6
3020       start at offsets 0, 3, 4, 8, 9, 14, 23.
3021     */
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();
3026
3027     int curParNr = 0;
3028     int curParOffset = 0;
3029     QMap<QChar,QString> cmap;
3030
3031     /*
3032       Output the alphabet as a row of links.
3033      */
3034     if (includeAlphabet) {
3035         writeStartTag(DT_p);
3036         xmlWriter().writeAttribute("outputclass","alphabet");
3037         for (int i = 0; i < 26; i++) {
3038             QChar ch('a' + i);
3039             if (usedParagraphNames.contains(char('a' + i))) {
3040                 writeStartTag(DT_xref);
3041                 // formathtml
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>
3047             }
3048         }
3049         writeEndTag(); // </p>
3050     }
3051
3052     /*
3053       Output a <p> element to contain all the <dl> elements.
3054      */
3055     writeStartTag(DT_p);
3056     xmlWriter().writeAttribute("outputclass","compactlist");
3057
3058     for (int i=0; i<classMap.count()-1; i++) {
3059         while ((curParNr < NumParagraphs) &&
3060                (curParOffset == paragraph[curParNr].count())) {
3061             ++curParNr;
3062             curParOffset = 0;
3063         }
3064
3065         /*
3066           Starting a new paragraph means starting a new <dl>.
3067         */
3068         if (curParOffset == 0) {
3069             if (i > 0) {
3070                 writeEndTag(); // </dlentry>
3071                 writeEndTag(); // </dl>
3072             }
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));
3079             }
3080             xmlWriter().writeAttribute("outputclass","sublist-header");
3081             xmlWriter().writeCharacters(paragraphName[curParNr]);
3082             writeEndTag(); // </dt>
3083         }
3084
3085         /*
3086           Output a <dd> for the current offset in the current paragraph.
3087          */
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++)
3094                 ++it;
3095
3096             /*
3097               Previously, we used generateFullName() for this, but we
3098               require some special formatting.
3099             */
3100             writeStartTag(DT_xref);
3101             // formathtml
3102             writeHrefAttribute(linkForNode(it.value(), relative));
3103
3104             QStringList pieces;
3105             if (it.value()->subType() == Node::QmlClass)
3106                 pieces << it.value()->name();
3107             else
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(")");
3115             }
3116         }
3117         writeEndTag(); // </dd>
3118         curParOffset++;
3119     }
3120     writeEndTag(); // </dlentry>
3121     writeEndTag(); // </dl>
3122     writeEndTag(); // </p>
3123 }
3124
3125 /*!
3126   Write XML for a function index to the current XML stream.
3127  */
3128 void DitaXmlGenerator::generateFunctionIndex(const Node* relative,
3129                                              CodeMarker* marker)
3130 {
3131     writeStartTag(DT_p);
3132     xmlWriter().writeAttribute("outputclass","alphabet");
3133     for (int i = 0; i < 26; i++) {
3134         QChar ch('a' + i);
3135         writeStartTag(DT_xref);
3136         // formathtml
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>
3142
3143     }
3144     writeEndTag(); // </p>
3145
3146     char nextLetter = 'a';
3147     char currentLetter;
3148
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>
3160             nextLetter++;
3161         }
3162         xmlWriter().writeCharacters(protectEnc(f.key()));
3163         xmlWriter().writeCharacters(":");
3164
3165         NodeMap::ConstIterator s = (*f).begin();
3166         while (s != (*f).end()) {
3167             generateFullName((*s)->parent(), relative, marker, *s);
3168             ++s;
3169         }
3170         writeEndTag(); // </li>
3171         ++f;
3172     }
3173     writeEndTag(); // </ul>
3174 }
3175
3176 /*!
3177   Write the legalese texts as XML to the current XML stream.
3178  */
3179 void DitaXmlGenerator::generateLegaleseList(const Node* relative,
3180                                             CodeMarker* marker)
3181 {
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);
3187         do {
3188             writeStartTag(DT_li);
3189             generateFullName(it.value(), relative, marker);
3190             writeEndTag(); // </li>
3191             ++it;
3192         } while (it != legaleseTexts.end() && it.key() == text);
3193         writeEndTag(); //</ul>
3194     }
3195 }
3196
3197 /*!
3198   Generate the text for the QML item described by \a node
3199   and write it to the current XML stream.
3200  */
3201 void DitaXmlGenerator::generateQmlItem(const Node* node,
3202                                        const Node* relative,
3203                                        CodeMarker* marker,
3204                                        bool summary)
3205 {
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);
3211     }
3212     marked.replace(QRegExp("<@param>([a-z]+)_([1-9n])</@param>"),
3213                    "<i>\\1<sub>\\2</sub></i>");
3214 #if 0
3215     marked.replace("<@param>", "<i>");
3216     marked.replace("</@param>", "</i>");
3217
3218     marked.replace("<@extra>", "<tt>");
3219     marked.replace("</@extra>", "</tt>");
3220 #endif
3221     if (summary) {
3222         marked.remove("<@type>");
3223         marked.remove("</@type>");
3224     }
3225     writeText(marked, marker, relative);
3226 }
3227
3228 /*!
3229   Write the XML for the overview list to the current XML stream.
3230  */
3231 void DitaXmlGenerator::generateOverviewList(const Node* relative, CodeMarker* /* marker */)
3232 {
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");
3237
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);
3242
3243             // Check whether the page is part of a group or is the group
3244             // definition page.
3245             QString group;
3246             bool isGroupPage = false;
3247             if (fakeNode->doc().metaCommandsUsed().contains("group")) {
3248                 group = fakeNode->doc().metaCommandArgs("group")[0];
3249                 isGroupPage = true;
3250             }
3251
3252             // there are too many examples; they would clutter the list
3253             if (fakeNode->subType() == Node::Example)
3254                 continue;
3255
3256             // not interested either in individual (Qt Designer etc.) manual chapters
3257             if (fakeNode->links().contains(Node::ContentsLink))
3258                 continue;
3259
3260             // Discard external nodes.
3261             if (fakeNode->subType() == Node::ExternalPage)
3262                 continue;
3263
3264             QString sortKey = fakeNode->fullTitle().toLower();
3265             if (sortKey.startsWith("the "))
3266                 sortKey.remove(0, 4);
3267             sortKey.replace(singleDigit, "0\\1");
3268
3269             if (!group.isEmpty()) {
3270                 if (isGroupPage) {
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)
3275                             continue;
3276                         FakeNode* page = static_cast<FakeNode*>(member);
3277                         if (page) {
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);
3284                         }
3285                     }
3286                 }
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));
3292                     if (groupNode)
3293                         fakeNodeMap[groupNode].insert(sortKey, fakeNode);
3294                     //else
3295                     //    uncategorizedNodeMap.insert(sortKey, fakeNode);
3296                 }// else
3297                 //    uncategorizedNodeMap.insert(sortKey, fakeNode);
3298             }// else
3299             //    uncategorizedNodeMap.insert(sortKey, fakeNode);
3300         }
3301     }
3302
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.
3308
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);
3315             // formathtml
3316             xmlWriter().writeAttribute("href",linkForNode(groupNode, relative));
3317             writeCharacters(protectEnc(groupNode->fullTitle()));
3318             writeEndTag(); // </xref>
3319             writeEndTag(); // </p>
3320             if (fakeNodeMap[groupNode].count() == 0)
3321                 continue;
3322
3323             writeStartTag(DT_ul);
3324             foreach (const FakeNode* fakeNode, fakeNodeMap[groupNode]) {
3325                 QString title = fakeNode->fullTitle();
3326                 if (title.startsWith("The "))
3327                     title.remove(0, 4);
3328                 writeStartTag(DT_li);
3329                 writeStartTag(DT_xref);
3330                 // formathtml
3331                 xmlWriter().writeAttribute("href",linkForNode(fakeNode, relative));
3332                 writeCharacters(protectEnc(title));
3333                 writeEndTag(); // </xref>
3334                 writeEndTag(); // </li>
3335             }
3336             writeEndTag(); // </ul>
3337         }
3338     }
3339
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 "))
3349                 title.remove(0, 4);
3350             writeStartTag(DT_li);
3351             writeStartTag(DT_xref);
3352             // formathtml
3353             xmlWriter().writeAttribute("href",linkForNode(fakeNode, relative));
3354             writeCharacters(protectEnc(title));
3355             writeEndTag(); // </xref>
3356             writeEndTag(); // </li>
3357         }
3358         writeEndTag(); // </ul>
3359     }
3360 }
3361
3362 /*!
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.
3366  */
3367 void DitaXmlGenerator::generateSection(const NodeList& nl,
3368                                        const Node* relative,
3369                                        CodeMarker* marker,
3370                                        CodeMarker::SynopsisStyle style)
3371 {
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>
3381             }
3382             ++m;
3383         }
3384         writeEndTag(); // </ul>
3385     }
3386 }
3387
3388 /*!
3389   Writes the "inherited from" list to the current XML stream.
3390  */
3391 void DitaXmlGenerator::generateSectionInheritedList(const Section& section,
3392                                                     const Node* relative,
3393                                                     CodeMarker* marker)
3394 {
3395     if (section.inherited.isEmpty())
3396         return;
3397     writeStartTag(DT_ul);
3398     QList<QPair<InnerNode*,int> >::ConstIterator p = section.inherited.begin();
3399     while (p != section.inherited.end()) {
3400         writeStartTag(DT_li);
3401         QString text;
3402         text.setNum((*p).second);
3403         text += QLatin1Char(' ');
3404         if ((*p).second == 1)
3405             text += section.singularMember;
3406         else
3407             text += section.pluralMember;
3408         text += " inherited from ";
3409         writeCharacters(text);
3410         writeStartTag(DT_xref);
3411         // formathtml
3412         // zzz
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>
3420         ++p;
3421     }
3422     writeEndTag(); // </ul>
3423 }
3424
3425 /*!
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
3430   string.
3431  */
3432 QString DitaXmlGenerator::getMarkedUpSynopsis(const Node* node,
3433                                               const Node* relative,
3434                                               CodeMarker* marker,
3435                                               CodeMarker::SynopsisStyle style)
3436 {
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);
3442     }
3443     marked.replace(QRegExp("<@param>([a-z]+)_([1-9n])</@param>"),
3444                    "<i> \\1<sub>\\2</sub></i>");
3445 #if 0
3446     marked.replace("<@param>","<i>");
3447     marked.replace("</@param>","</i>");
3448 #endif
3449     if (style == CodeMarker::Summary) {
3450         marked.remove("<@name>");   // was "<b>"
3451         marked.remove("</@name>");  // was "</b>"
3452     }
3453
3454     if (style == CodeMarker::Subpage) {
3455         QRegExp extraRegExp("<@extra>.*</@extra>");
3456         extraRegExp.setMinimal(true);
3457         marked.remove(extraRegExp);
3458     }
3459 #if 0
3460     else {
3461         marked.replace("<@extra>","<tt>");
3462         marked.replace("</@extra>","</tt>");
3463     }
3464 #endif
3465
3466     if (style != CodeMarker::Detailed) {
3467         marked.remove("<@type>");
3468         marked.remove("</@type>");
3469     }
3470     return marked;
3471 }
3472
3473 /*!
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
3476   stream.
3477  */
3478 void DitaXmlGenerator::writeText(const QString& markedCode,
3479                                  CodeMarker* marker,
3480                                  const Node* relative)
3481 {
3482     QString src = markedCode;
3483     QString text;
3484     QStringRef arg;
3485     QStringRef par1;
3486
3487     const QChar charLangle = '<';
3488     const QChar charAt = '@';
3489
3490     /*
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.
3494      */
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>"
3509     };
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];
3517                     i += tag.length();
3518                     handled = true;
3519                     break;
3520                 }
3521             }
3522             if (!handled) {
3523                 ++i;
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('>'))
3528                         ++i;
3529                     ++i;
3530                 }
3531                 else {
3532                     // retain all others
3533                     text += charLangle;
3534                 }
3535             }
3536         }
3537         else {
3538             text += src.at(i);
3539             ++i;
3540         }
3541     }
3542
3543     // replace all <@link> tags: "(<@link node=\"([^\"]+)\">).*(</@link>)"
3544     // replace all "(<@(type|headerfile|func)(?: +[^>]*)?>)(.*)(</@\\2>)" tags
3545     src = text;
3546     text = QString();
3547     static const QString markTags[] = {
3548         // 0       1         2           3       4        5
3549         "link", "type", "headerfile", "func", "param", "extra"
3550     };
3551
3552     for (int i = 0, n = src.size(); i < n;) {
3553         if (src.at(i) == charLangle && src.at(i + 1) == charAt) {
3554             i += 2;
3555             for (int k = 0; k != 6; ++k) {
3556                 if (parseArg(src, markTags[k], &i, n, &arg, &par1)) {
3557                     const Node* n = 0;
3558                     if (k == 0) { // <@link>
3559                         if (!text.isEmpty()) {
3560                             writeCharacters(text);
3561                             text.clear();
3562                         }
3563                         n = CodeMarker::nodeForString(par1.toString());
3564                         QString link = linkForNode(n, relative);
3565                         addLink(link, arg);
3566                     }
3567                     else if (k == 4) { // <@param>
3568                         if (!text.isEmpty()) {
3569                             writeCharacters(text);
3570                             text.clear();
3571                         }
3572                         writeStartTag(DT_i);
3573                         //writeCharacters(" " + arg.toString());
3574                         writeCharacters(arg.toString());
3575                         writeEndTag(); // </i>
3576                     }
3577                     else if (k == 5) { // <@extra>
3578                         if (!text.isEmpty()) {
3579                             writeCharacters(text);
3580                             text.clear();
3581                         }
3582                         writeStartTag(DT_tt);
3583                         writeCharacters(arg.toString());
3584                         writeEndTag(); // </tt>
3585                     }
3586                     else {
3587                         if (!text.isEmpty()) {
3588                             writeCharacters(text);
3589                             text.clear();
3590                         }
3591                         par1 = QStringRef();
3592                         QString link;
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);
3597                                 addLink(link, arg);
3598                             }
3599                             else {
3600                                 writeCharacters(arg.toString());
3601                             }
3602                         }
3603                         else {
3604                             // (zzz) Is this correct for all cases?
3605                             link = linkForNode(n,relative);
3606                             addLink(link, arg);
3607                         }
3608                     }
3609                     break;
3610                 }
3611             }
3612         }
3613         else {
3614             text += src.at(i++);
3615         }
3616     }
3617     if (!text.isEmpty()) {
3618         writeCharacters(text);
3619     }
3620 }
3621
3622 void DitaXmlGenerator::generateLink(const Atom* atom,
3623                                     const Node* /* relative */,
3624                                     CodeMarker* marker)
3625 {
3626     static QRegExp camelCase("[A-Z][A-Z][a-z]|[a-z][A-Z0-9]|_");
3627
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>
3635         }
3636         else
3637             writeEndTag(); // </xref>
3638         inLink = false;
3639         writeCharacters(protectEnc(atom->string().mid(k)));
3640     }
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));
3645         if (tt)
3646             writeStartTag(DT_tt);
3647         if (func)
3648             writeCharacters(protectEnc(atom->string().left(atom->string().length() - 2)));
3649         else
3650             writeCharacters(protectEnc(atom->string()));
3651         writeEndTag(); // </tt>
3652     }
3653     else
3654         writeCharacters(protectEnc(atom->string()));
3655 }
3656
3657 QString DitaXmlGenerator::cleanRef(const QString& ref)
3658 {
3659     QString clean;
3660
3661     if (ref.isEmpty())
3662         return clean;
3663
3664     clean.reserve(ref.size() + 20);
3665     const QChar c = ref[0];
3666     const uint u = c.unicode();
3667
3668     if ((u >= 'a' && u <= 'z') ||
3669             (u >= 'A' && u <= 'Z') ||
3670             (u >= '0' && u <= '9')) {
3671         clean += c;
3672     }
3673     else if (u == '~') {
3674         clean += "dtor.";
3675     }
3676     else if (u == '_') {
3677         clean += "underscore.";
3678     }
3679     else {
3680         clean += QLatin1Char('A');
3681     }
3682
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 == '.') {
3690             clean += c;
3691         }
3692         else if (c.isSpace()) {
3693             clean += QLatin1Char('-');
3694         }
3695         else if (u == '!') {
3696             clean += "-not";
3697         }
3698         else if (u == '&') {
3699             clean += "-and";
3700         }
3701         else if (u == '<') {
3702             clean += "-lt";
3703         }
3704         else if (u == '=') {
3705             clean += "-eq";
3706         }
3707         else if (u == '>') {
3708             clean += "-gt";
3709         }
3710         else if (u == '#') {
3711             clean += QLatin1Char('#');
3712         }
3713         else {
3714             clean += QLatin1Char('-');
3715             clean += QString::number((int)u, 16);
3716         }
3717     }
3718     return clean;
3719 }
3720
3721 QString DitaXmlGenerator::registerRef(const QString& ref)
3722 {
3723     QString clean = DitaXmlGenerator::cleanRef(ref);
3724
3725     for (;;) {
3726         QString& prevRef = refMap[clean.toLower()];
3727         if (prevRef.isEmpty()) {
3728             prevRef = ref;
3729             break;
3730         }
3731         else if (prevRef == ref)
3732             break;
3733         clean += QLatin1Char('x');
3734     }
3735     return clean;
3736 }
3737
3738 /*!
3739   Calls protect() with the \a string. Returns the result.
3740  */
3741 QString DitaXmlGenerator::protectEnc(const QString& string)
3742 {
3743     return protect(string, outputEncoding);
3744 }
3745
3746 QString DitaXmlGenerator::protect(const QString& string, const QString& ) //outputEncoding)
3747 {
3748 #define APPEND(x) \
3749     if (xml.isEmpty()) { \
3750     xml = string; \
3751     xml.truncate(i); \
3752 } \
3753     xml += (x);
3754
3755     QString xml;
3756     int n = string.length();
3757
3758     for (int i = 0; i < n; ++i) {
3759         QChar ch = string.at(i);
3760
3761         if (ch == QLatin1Char('&')) {
3762             APPEND("&amp;");
3763         }
3764         else if (ch == QLatin1Char('<')) {
3765             APPEND("&lt;");
3766         }
3767         else if (ch == QLatin1Char('>')) {
3768             APPEND("&gt;");
3769         }
3770         else if (ch == QLatin1Char('"')) {
3771             APPEND("&quot;");
3772         }
3773 #if 0
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
3778             APPEND("&#x");
3779             xml += QString::number(ch.unicode(), 16);
3780             xml += QLatin1Char(';');
3781         }
3782 #endif
3783         else {
3784             if (!xml.isEmpty())
3785                 xml += ch;
3786         }
3787     }
3788
3789     if (!xml.isEmpty())
3790         return xml;
3791     return string;
3792
3793 #undef APPEND
3794 }
3795
3796 /*!
3797   Constructs a file name appropriate for the \a node
3798   and returns the file name.
3799  */
3800 QString DitaXmlGenerator::fileBase(const Node* node) const
3801 {
3802     QString result;
3803     result = Generator::fileBase(node);
3804 #if 0
3805     if (!node->isInnerNode()) {
3806         switch (node->status()) {
3807         case Node::Compat:
3808             result += "-qt3";
3809             break;
3810         case Node::Obsolete:
3811             result += "-obsolete";
3812             break;
3813         default:
3814             ;
3815         }
3816     }
3817 #endif
3818     return result;
3819 }
3820
3821 QString DitaXmlGenerator::guidForNode(const Node* node)
3822 {
3823     switch (node->type()) {
3824     case Node::Namespace:
3825     case Node::Class:
3826     default:
3827         break;
3828     case Node::Enum:
3829         return node->guid();
3830     case Node::Typedef:
3831     {
3832         const TypedefNode* tdn = static_cast<const TypedefNode*>(node);
3833         if (tdn->associatedEnum())
3834             return guidForNode(tdn->associatedEnum());
3835     }
3836         return node->guid();
3837     case Node::Function:
3838     {
3839         const FunctionNode* fn = static_cast<const FunctionNode*>(node);
3840         if (fn->associatedProperty()) {
3841             return guidForNode(fn->associatedProperty());
3842         }
3843         else {
3844             QString ref = fn->name();
3845             if (fn->overloadNumber() != 1) {
3846                 ref += QLatin1Char('-') + QString::number(fn->overloadNumber());
3847             }
3848         }
3849         return fn->guid();
3850     }
3851     case Node::Fake:
3852         if (node->subType() != Node::QmlPropertyGroup)
3853             break;
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();
3865     case Node::Target:
3866         return node->guid();
3867     }
3868     return QString();
3869 }
3870
3871 /*!
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.
3876  */
3877 QString DitaXmlGenerator::fileName(const Node* node)
3878 {
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();
3884     }
3885     return Generator::fileName(node);
3886 }
3887
3888 QString DitaXmlGenerator::linkForNode(const Node* node, const Node* relative)
3889 {
3890     if (node == 0 || node == relative)
3891         return QString();
3892     if (!node->url().isEmpty())
3893         return node->url();
3894     if (fileBase(node).isEmpty())
3895         return QString();
3896     if (node->access() == Node::Private)
3897         return QString();
3898
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()) {
3903                 /*
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.
3911                  */
3912                 fn = fileName(relative);
3913 #if DEBUG_ABSTRACT
3914                 qDebug() << "ABSTRACT:" << node->parent()->name()
3915                          << node->name() << relative->name()
3916                          << node->parent()->type() << node->parent()->subType()
3917                          << relative->type() << relative->subType() << outFileName();
3918 #endif
3919             }
3920         }
3921     }
3922     QString link = fn;
3923
3924     if (!node->isInnerNode() || node->subType() == Node::QmlPropertyGroup) {
3925         QString guid = guidForNode(node);
3926         if (relative && fn == fileName(relative) && guid == guidForNode(relative)) {
3927             return QString();
3928         }
3929         link += QLatin1Char('#');
3930         link += guid;
3931     }
3932     /*
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.
3937      */
3938     if (node && relative && (node != relative)) {
3939         if (node->outputSubdirectory() != relative->outputSubdirectory())
3940             link.prepend(QString("../" + node->outputSubdirectory() + QLatin1Char('/')));
3941     }
3942     return link;
3943 }
3944
3945 QString DitaXmlGenerator::refForAtom(Atom* atom, const Node* /* node */)
3946 {
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());
3951     return QString();
3952 }
3953
3954 void DitaXmlGenerator::generateFullName(const Node* apparentNode,
3955                                         const Node* relative,
3956                                         CodeMarker* marker,
3957                                         const Node* actualNode)
3958 {
3959     if (actualNode == 0)
3960         actualNode = apparentNode;
3961     writeStartTag(DT_xref);
3962     // formathtml
3963     QString href = linkForNode(actualNode, relative);
3964     writeHrefAttribute(href);
3965     writeCharacters(protectEnc(fullName(apparentNode, relative, marker)));
3966     writeEndTag(); // </xref>
3967 }
3968
3969 void DitaXmlGenerator::findAllClasses(const InnerNode* node)
3970 {
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;
3980
3981                 if (!(static_cast<const ClassNode *>(*c))->hideFromMainList()) {
3982                     if ((*c)->status() == Node::Compat) {
3983                         compatClasses.insert(className, *c);
3984                     }
3985                     else if ((*c)->status() == Node::Obsolete) {
3986                         obsoleteClasses.insert(className, *c);
3987                     }
3988                     else {
3989                         nonCompatClasses.insert(className, *c);
3990                         if ((*c)->status() == Node::Main)
3991                             mainClasses.insert(className, *c);
3992                     }
3993                 }
3994
3995                 QString moduleName = (*c)->moduleName();
3996                 if (moduleName == "Qt3SupportLight") {
3997                     moduleClassMap[moduleName].insert((*c)->name(), *c);
3998                     moduleName = "Qt3Support";
3999                 }
4000                 if (!moduleName.isEmpty())
4001                     moduleClassMap[moduleName].insert((*c)->name(), *c);
4002
4003                 QString serviceName =
4004                         (static_cast<const ClassNode *>(*c))->serviceName();
4005                 if (!serviceName.isEmpty())
4006                     serviceClasses.insert(serviceName, *c);
4007             }
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);
4013             }
4014             else if ((*c)->isInnerNode()) {
4015                 findAllClasses(static_cast<InnerNode *>(*c));
4016             }
4017         }
4018         ++c;
4019     }
4020 }
4021
4022 void DitaXmlGenerator::findAllFunctions(const InnerNode* node)
4023 {
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));
4029             }
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);
4037                 }
4038             }
4039         }
4040         ++c;
4041     }
4042 }
4043
4044 void DitaXmlGenerator::findAllLegaleseTexts(const InnerNode* node)
4045 {
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));
4053         }
4054         ++c;
4055     }
4056 }
4057
4058 void DitaXmlGenerator::findAllNamespaces(const InnerNode* node)
4059 {
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";
4075                         }
4076                         if (!moduleName.isEmpty())
4077                             moduleNamespaceMap[moduleName].insert((*c)->name(), *c);
4078                     }
4079                 }
4080             }
4081         }
4082         ++c;
4083     }
4084 }
4085
4086 /*!
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.
4090  */
4091 int DitaXmlGenerator::hOffset(const Node* node)
4092 {
4093     switch (node->type()) {
4094     case Node::Namespace:
4095     case Node::Class:
4096         return 2;
4097     case Node::Fake:
4098         return 1;
4099     case Node::Enum:
4100     case Node::Typedef:
4101     case Node::Function:
4102     case Node::Property:
4103     default:
4104         return 3;
4105     }
4106 }
4107
4108 bool DitaXmlGenerator::isThreeColumnEnumValueTable(const Atom* atom)
4109 {
4110     while (atom != 0 && !(atom->type() == Atom::ListRight && atom->string() == ATOM_LIST_VALUE)) {
4111         if (atom->type() == Atom::ListItemLeft && !matchAhead(atom, Atom::ListItemRight))
4112             return true;
4113         atom = atom->next();
4114     }
4115     return false;
4116 }
4117
4118 const Node* DitaXmlGenerator::findNodeForTarget(const QString& target,
4119                                                 const Node* relative,
4120                                                 CodeMarker* marker,
4121                                                 const Atom* atom)
4122 {
4123     const Node* node = 0;
4124
4125     if (target.isEmpty()) {
4126         node = relative;
4127     }
4128     else if (target.endsWith(".html")) {
4129         node = tree_->root()->findNode(target, Node::Fake);
4130     }
4131     else if (marker) {
4132         node = marker->resolveTarget(target, tree_, relative);
4133         if (!node)
4134             node = tree_->findFakeNodeByTitle(target, relative);
4135         if (!node && atom) {
4136             node = tree_->findUnambiguousTarget(target, *const_cast<Atom**>(&atom), relative);
4137         }
4138     }
4139
4140     if (!node)
4141         relative->doc().location().warning(tr("Cannot link to '%1'").arg(target));
4142
4143     return node;
4144 }
4145
4146 const QPair<QString,QString> DitaXmlGenerator::anchorForNode(const Node* node)
4147 {
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();
4153     }
4154
4155     return anchorPair;
4156 }
4157
4158 QString DitaXmlGenerator::getLink(const Atom* atom,
4159                                   const Node* relative,
4160                                   CodeMarker* marker,
4161                                   const Node** node)
4162 {
4163     QString link;
4164     *node = 0;
4165     inObsoleteLink = false;
4166
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:"))) {
4173
4174         link = atom->string();
4175     }
4176     else {
4177         QStringList path;
4178         if (atom->string().contains('#'))
4179             path = atom->string().split('#');
4180         else
4181             path.append(atom->string());
4182
4183         Atom* targetAtom = 0;
4184         QString first = path.first().trimmed();
4185
4186         if (first.isEmpty()) {
4187             *node = relative;
4188         }
4189         else if (first.endsWith(".html")) {
4190             *node = tree_->root()->findNode(first, Node::Fake);
4191         }
4192         else {
4193             *node = marker->resolveTarget(first, tree_, relative);
4194             if (!*node) {
4195                 *node = tree_->findFakeNodeByTitle(first, relative);
4196             }
4197             if (!*node) {
4198                 *node = tree_->findUnambiguousTarget(first, targetAtom, relative);
4199             }
4200         }
4201
4202         if (*node) {
4203             if (!(*node)->url().isEmpty()) {
4204                 return (*node)->url();
4205             }
4206             else {
4207                 path.removeFirst();
4208             }
4209         }
4210         else {
4211             *node = relative;
4212         }
4213
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"))
4221                         porting = true;
4222                 }
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())
4228                                                            .arg(name));
4229                     }
4230                     inObsoleteLink = true;
4231                 }
4232             }
4233         }
4234
4235         while (!path.isEmpty()) {
4236             targetAtom = tree_->findTarget(path.first(), *node);
4237             if (targetAtom == 0)
4238                 break;
4239             path.removeFirst();
4240         }
4241
4242         if (path.isEmpty()) {
4243             link = linkForNode(*node, relative);
4244             if (*node && (*node)->subType() == Node::Image)
4245                 link = "images/used-in-examples/" + link;
4246             if (targetAtom) {
4247                 if (link.isEmpty())
4248                     link = outFileName();
4249                 QString guid = lookupGuid(link,refForAtom(targetAtom,*node));
4250                 link += QLatin1Char('#') + guid;
4251             }
4252             else if (!link.isEmpty() && *node &&
4253                      (link.endsWith(".xml") || link.endsWith(".dita"))) {
4254                 link += QLatin1Char('#') + (*node)->guid();
4255             }
4256         }
4257         /*
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.
4262         */
4263         if (link.startsWith("images/")) {
4264             link.prepend(QString("../"));
4265         }
4266         else if (*node && relative && (*node != relative)) {
4267             if ((*node)->outputSubdirectory() != relative->outputSubdirectory()) {
4268                 link.prepend(QString("../" + (*node)->outputSubdirectory() + QLatin1Char('/')));
4269             }
4270         }
4271     }
4272     if (!link.isEmpty() && link[0] == '#') {
4273         link.prepend(outFileName());
4274         qDebug() << "LOCAL LINK:" << link;
4275     }
4276     return link;
4277 }
4278
4279 /*!
4280   This function can be called if getLink() returns an empty
4281   string.
4282  */
4283 QString DitaXmlGenerator::getDisambiguationLink(const Atom *, CodeMarker *)
4284 {
4285     qDebug() << "Unimplemented function called: "
4286              << "QString DitaXmlGenerator::getDisambiguationLink()";
4287     return QString();
4288 }
4289
4290 void DitaXmlGenerator::generateIndex(const QString& fileBase,
4291                                      const QString& url,
4292                                      const QString& title)
4293 {
4294     tree_->generateIndex(outputDir() + QLatin1Char('/') + fileBase + ".index", url, title);
4295 }
4296
4297 void DitaXmlGenerator::generateStatus(const Node* node, CodeMarker* marker)
4298 {
4299     Text text;
4300
4301     switch (node->status()) {
4302     case Node::Obsolete:
4303         if (node->isInnerNode())
4304             Generator::generateStatus(node, marker);
4305         break;
4306     case Node::Compat:
4307         if (node->isInnerNode()) {
4308             text << Atom::ParaLeft
4309                  << Atom(Atom::FormattingLeft,ATOM_FORMATTING_BOLD)
4310                  << "This "
4311                  << typeString(node)
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 ";
4317
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);
4324             }
4325
4326             if (targetAtom) {
4327                 QString fn = fileName(fakeNode);
4328                 QString guid = lookupGuid(fn,refForAtom(targetAtom,fakeNode));
4329                 text << Atom(Atom::GuidLink, fn + QLatin1Char('#') + guid);
4330             }
4331             else
4332                 text << Atom(Atom::Link, "Porting to Qt 4");
4333
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."
4338                  << Atom::ParaRight;
4339         }
4340         generateText(text, node, marker);
4341         break;
4342     default:
4343         Generator::generateStatus(node, marker);
4344     }
4345 }
4346
4347 void DitaXmlGenerator::beginLink(const QString& link)
4348 {
4349     this->link = link;
4350     if (link.isEmpty())
4351         return;
4352     writeStartTag(DT_xref);
4353     // formathtml
4354     writeHrefAttribute(link);
4355     inLink = true;
4356 }
4357
4358 void DitaXmlGenerator::endLink()
4359 {
4360     if (inLink) {
4361         if (link.isEmpty()) {
4362             if (showBrokenLinks)
4363                 writeEndTag(); // </i>
4364         }
4365         else {
4366             if (inObsoleteLink) {
4367                 writeStartTag(DT_sup);
4368                 xmlWriter().writeCharacters("(obsolete)");
4369                 writeEndTag(); // </sup>
4370             }
4371             writeEndTag(); // </xref>
4372         }
4373     }
4374     inLink = false;
4375     inObsoleteLink = false;
4376 }
4377
4378 /*!
4379   Generates the summary for the \a section. Only used for
4380   sections of QML element documentation.
4381
4382   Currently handles only the QML property group.
4383  */
4384 void DitaXmlGenerator::generateQmlSummary(const Section& section,
4385                                           const Node* relative,
4386                                           CodeMarker* marker)
4387 {
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>
4396             ++m;
4397         }
4398         writeEndTag(); // </ul>
4399     }
4400 }
4401
4402 /*!
4403   Outputs the DITA detailed documentation for a section
4404   on a QML element reference page.
4405  */
4406 void DitaXmlGenerator::generateDetailedQmlMember(const Node* node,
4407                                                  const InnerNode* relative,
4408                                                  CodeMarker* marker)
4409 {
4410     QString marked;
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);
4421                 QString attr;
4422                 int ro = qpn->getReadOnly();
4423                 if (ro < 0) {
4424                     if (!qpn->isWritable(tree_))
4425                         attr = "read-only";
4426                 }
4427                 else if (ro > 0)
4428                     attr = "read-only";
4429                 if (qpgn->isDefault()) {
4430                     if (!attr.isEmpty())
4431                         attr += QLatin1Char(' ');
4432                     attr += "default";
4433                 }
4434                 if (!attr.isEmpty())
4435                     xmlWriter().writeAttribute("outputclass",attr);
4436                 generateQmlItem(qpn, relative, marker, false);
4437                 writeEndTag(); // </li>
4438             }
4439             ++p;
4440         }
4441         writeEndTag(); // </ul>
4442     }
4443     else if (node->type() == Node::QmlProperty) {
4444         qpn = static_cast<const QmlPropertyNode*>(node);
4445         /*
4446           If the QML property node has a single subproperty,
4447           override, replace qpn with that override node and
4448           proceed as normal.
4449          */
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);
4454         }
4455         /*
4456           Now qpn either has no overrides, or it has more
4457           than 1. If it has none, proceed to output as nortmal.
4458          */
4459         if (qpn->qmlPropNodes().isEmpty()) {
4460             writeStartTag(DT_ul);
4461             writeStartTag(DT_li);
4462             writeGuidAttribute((Node*)qpn);
4463             QString attr;
4464             int ro = qpn->getReadOnly();
4465             if (ro < 0) {
4466                 const ClassNode* cn = qpn->declarativeCppNode();
4467                 if (cn && !qpn->isWritable(tree_))
4468                     attr = "read-only";
4469             }
4470             else if (ro > 0)
4471                 attr = "read-only";
4472             if (qpn->isDefault()) {
4473                 if (!attr.isEmpty())
4474                     attr += QLatin1Char(' ');
4475                 attr += "default";
4476             }
4477             if (!attr.isEmpty())
4478                 xmlWriter().writeAttribute("outputclass",attr);
4479             generateQmlItem(qpn, relative, marker, false);
4480             writeEndTag(); // </li>
4481             writeEndTag(); // </ul>
4482         }
4483         else {
4484             /*
4485               The QML property node has multiple override nodes.
4486               Process the whole list as we would for a QML property
4487               group.
4488              */
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);
4496                     QString attr;
4497                     int ro = qpn->getReadOnly();
4498                     if (ro < 0) {
4499                         if (!qpn->isWritable(tree_))
4500                             attr = "read-only";
4501                     }
4502                     else if (ro > 0)
4503                         attr = "read-only";
4504                     if (qpn->isDefault()) {
4505                         if (!attr.isEmpty())
4506                             attr += QLatin1Char(' ');
4507                         attr += "default";
4508                     }
4509                     if (!attr.isEmpty())
4510                         xmlWriter().writeAttribute("outputclass",attr);
4511                     generateQmlItem(q, relative, marker, false);
4512                     writeEndTag(); // </li>
4513                 }
4514                 ++p;
4515             }
4516             writeEndTag(); // </ul>
4517         }
4518     }
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>
4528     }
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>
4538     }
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>
4548     }
4549     generateStatus(node, marker);
4550     generateBody(node, marker);
4551     generateThreadSafeness(node, marker);
4552     generateSince(node, marker);
4553     generateAlsoList(node, marker);
4554 }
4555
4556 /*!
4557   Output the "Inherits" line for the QML element,
4558   if there should be one.
4559  */
4560 void DitaXmlGenerator::generateQmlInherits(const QmlClassNode* qcn, CodeMarker* marker)
4561 {
4562     if (!qcn)
4563         return;
4564     const FakeNode* base = qcn->qmlBase();
4565     if (base) {
4566         writeStartTag(DT_p);
4567         xmlWriter().writeAttribute("outputclass","inherits");
4568         Text text;
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);
4574         text << "]";
4575         generateText(text, qcn, marker);
4576         writeEndTag(); // </p>
4577     }
4578 }
4579
4580 /*!
4581   Output the "[Xxx instantiates the C++ class QmlGraphicsXxx]"
4582   line for the QML element, if there should be one.
4583
4584   If there is no class node, or if the class node status
4585   is set to Node::Internal, do nothing.
4586  */
4587 void DitaXmlGenerator::generateQmlInstantiates(const QmlClassNode* qcn,
4588                                                CodeMarker* marker)
4589 {
4590     const ClassNode* cn = qcn->classNode();
4591     if (cn && (cn->status() != Node::Internal)) {
4592         writeStartTag(DT_p);
4593         xmlWriter().writeAttribute("outputclass","instantiates");
4594         Text text;
4595         text << "[";
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);
4605         text << "]";
4606         generateText(text, qcn, marker);
4607         writeEndTag(); // </p>
4608     }
4609 }
4610
4611 /*!
4612   Output the "[QmlGraphicsXxx is instantiated by QML element Xxx]"
4613   line for the class, if there should be one.
4614
4615   If there is no QML element, or if the class node status
4616   is set to Node::Internal, do nothing.
4617  */
4618 void DitaXmlGenerator::generateInstantiatedBy(const ClassNode* cn,
4619                                               CodeMarker* marker)
4620 {
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");
4625         Text text;
4626         text << "[";
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);
4636         text << "]";
4637         generateText(text, cn, marker);
4638         writeEndTag(); // </p>
4639     }
4640 }
4641
4642 /*!
4643   Return the full qualification of the node \a n, but without
4644   the name of \a n itself. e.g. A::B::C
4645  */
4646 QString DitaXmlGenerator::fullQualification(const Node* n)
4647 {
4648     QString fq;
4649     InnerNode* in = n->parent();
4650     while (in) {
4651         if ((in->type() == Node::Class) ||
4652                 (in->type() == Node::Namespace)) {
4653             if (in->name().isEmpty())
4654                 break;
4655             if (fq.isEmpty())
4656                 fq = in->name();
4657             else
4658                 fq = in->name() + "::" + fq;
4659         }
4660         else
4661             break;
4662         in = in->parent();
4663     }
4664     return fq;
4665 }
4666
4667 /*!
4668   Outputs the <cxxClassDerivations> element.
4669   \code
4670  <cxxClassDerivations>
4671   <cxxClassDerivation>
4672    ...
4673   </cxxClassDerivation>
4674   ...
4675  </cxxClassDerivations>
4676   \endcode
4677
4678   The <cxxClassDerivation> element is:
4679
4680   \code
4681  <cxxClassDerivation>
4682   <cxxClassDerivationAccessSpecifier value="public"/>
4683   <cxxClassBaseClass href="class_base">Base</cxxClassBaseClass>
4684  </cxxClassDerivation>
4685   \endcode
4686  */
4687 void DitaXmlGenerator::writeDerivations(const ClassNode* cn, CodeMarker* marker)
4688 {
4689     QList<RelatedClass>::ConstIterator r;
4690
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>
4699
4700             // not included: <cxxClassDerivationVirtual>
4701
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>
4707
4708             // not included: <ClassBaseStruct> or <cxxClassBaseUnion>
4709
4710             writeEndTag(); // </cxxClassDerivation>
4711
4712             // not included: <cxxStructDerivation>
4713
4714             ++r;
4715         }
4716         writeEndTag(); // </cxxClassDerivations>
4717     }
4718 }
4719
4720 /*!
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.
4724  */
4725 void DitaXmlGenerator::writeLocation(const Node* n)
4726 {
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;
4737     }
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;
4744             s3b = DT_NONE;
4745         }
4746         else {
4747             s1 = DT_cxxFunctionAPIItemLocation;
4748             s2 = DT_cxxFunctionDeclarationFile;
4749             s3a = DT_cxxFunctionDeclarationFileLine;
4750             s3b = DT_NONE;
4751         }
4752     }
4753     else if (n->type() == Node::Enum) {
4754         s1 = DT_cxxEnumerationAPIItemLocation;
4755         s2 = DT_cxxEnumerationDeclarationFile;
4756         s3a = DT_cxxEnumerationDeclarationFileLineStart;
4757         s3b = DT_cxxEnumerationDeclarationFileLineEnd;
4758     }
4759     else if (n->type() == Node::Typedef) {
4760         s1 = DT_cxxTypedefAPIItemLocation;
4761         s2 = DT_cxxTypedefDeclarationFile;
4762         s3a = DT_cxxTypedefDeclarationFileLine;
4763         s3b = DT_NONE;
4764     }
4765     else if ((n->type() == Node::Property) ||
4766              (n->type() == Node::Variable)) {
4767         s1 = DT_cxxVariableAPIItemLocation;
4768         s2 = DT_cxxVariableDeclarationFile;
4769         s3a = DT_cxxVariableDeclarationFileLine;
4770         s3b = DT_NONE;
4771     }
4772     writeStartTag(s1);
4773     writeStartTag(s2);
4774     xmlWriter().writeAttribute("name","filePath");
4775     xmlWriter().writeAttribute("value",n->location().filePath());
4776     writeEndTag(); // <s2>
4777     writeStartTag(s3a);
4778     xmlWriter().writeAttribute("name","lineNumber");
4779     QString lineNr;
4780     xmlWriter().writeAttribute("value",lineNr.setNum(n->location().lineNo()));
4781     writeEndTag(); // </s3a>
4782     if (s3b != DT_NONE) {
4783         writeStartTag(s3b);
4784         xmlWriter().writeAttribute("name","lineNumber");
4785         QString lineNr;
4786         xmlWriter().writeAttribute("value",lineNr.setNum(n->location().lineNo()));
4787         writeEndTag(); // </s3b>
4788     }
4789     writeEndTag(); // </cxx<s1>ApiItemLocation>
4790 }
4791
4792 /*!
4793   Write the <cxxFunction> elements.
4794  */
4795 void DitaXmlGenerator::writeFunctions(const Section& s,
4796                                       const InnerNode* parent,
4797                                       CodeMarker* marker,
4798                                       const QString& attribute)
4799 {
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);
4816
4817             // not included: <prolog>
4818
4819             writeStartTag(DT_cxxFunctionDetail);
4820             writeStartTag(DT_cxxFunctionDefinition);
4821             writeStartTag(DT_cxxFunctionAccessSpecifier);
4822             xmlWriter().writeAttribute("value",fn->accessString());
4823             writeEndTag(); // <cxxFunctionAccessSpecifier>
4824
4825             // not included: <cxxFunctionStorageClassSpecifierExtern>
4826
4827             if (fn->isStatic()) {
4828                 writeStartTag(DT_cxxFunctionStorageClassSpecifierStatic);
4829                 xmlWriter().writeAttribute("name","static");
4830                 xmlWriter().writeAttribute("value","static");
4831                 writeEndTag(); // <cxxFunctionStorageClassSpecifierStatic>
4832             }
4833
4834             // not included: <cxxFunctionStorageClassSpecifierMutable>,
4835
4836             if (fn->isConst()) {
4837                 writeStartTag(DT_cxxFunctionConst);
4838                 xmlWriter().writeAttribute("name","const");
4839                 xmlWriter().writeAttribute("value","const");
4840                 writeEndTag(); // <cxxFunctionConst>
4841             }
4842
4843             // not included: <cxxFunctionExplicit>
4844             //               <cxxFunctionInline
4845
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>
4856                 }
4857             }
4858
4859             if (fn->name() == parent->name()) {
4860                 writeStartTag(DT_cxxFunctionConstructor);
4861                 xmlWriter().writeAttribute("name","constructor");
4862                 xmlWriter().writeAttribute("value","constructor");
4863                 writeEndTag(); // <cxxFunctionConstructor>
4864             }
4865             else if (fn->name()[0] == QChar('~')) {
4866                 writeStartTag(DT_cxxFunctionDestructor);
4867                 xmlWriter().writeAttribute("name","destructor");
4868                 xmlWriter().writeAttribute("value","destructor");
4869                 writeEndTag(); // <cxxFunctionDestructor>
4870             }
4871             else {
4872                 writeStartTag(DT_cxxFunctionDeclaredType);
4873                 QString src = marker->typified(fn->returnType());
4874                 replaceTypesWithLinks(fn,parent,marker,src);
4875                 writeEndTag(); // <cxxFunctionDeclaredType>
4876             }
4877
4878             // not included: <cxxFunctionReturnType>
4879
4880             QString fq = fullQualification(fn);
4881             if (!fq.isEmpty()) {
4882                 writeStartTag(DT_cxxFunctionScopedName);
4883                 writeCharacters(fq);
4884                 writeEndTag(); // <cxxFunctionScopedName>
4885             }
4886             writeStartTag(DT_cxxFunctionPrototype);
4887             writeCharacters(fn->signature(true));
4888             writeEndTag(); // <cxxFunctionPrototype>
4889
4890             QString fnl = fn->signature(false);
4891             int idx = fnl.indexOf(' ');
4892             if (idx < 0)
4893                 idx = 0;
4894             else
4895                 ++idx;
4896             fnl = fn->parent()->name() + "::" + fnl.mid(idx);
4897             writeStartTag(DT_cxxFunctionNameLookup);
4898             writeCharacters(fnl);
4899             writeEndTag(); // <cxxFunctionNameLookup>
4900
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>
4908                 }
4909             }
4910             writeParameters(fn,parent,marker);
4911             writeLocation(fn);
4912             writeEndTag(); // <cxxFunctionDefinition>
4913
4914             writeApiDesc(fn, marker, QString());
4915             // generateAlsoList(inner, marker);
4916
4917             // not included: <example> or <apiImpl>
4918
4919             writeEndTag(); // </cxxFunctionDetail>
4920             writeEndTag(); // </cxxFunction>
4921
4922             if (fn->metaness() == FunctionNode::Ctor ||
4923                     fn->metaness() == FunctionNode::Dtor ||
4924                     fn->overloadNumber() != 1) {
4925             }
4926         }
4927         ++m;
4928     }
4929 }
4930
4931 static const QString typeTag("type");
4932 static const QChar charLangle = '<';
4933 static const QChar charAt = '@';
4934
4935 /*!
4936   This function replaces class and enum names with <apiRelation>
4937   elements, i.e. links.
4938  */
4939 void DitaXmlGenerator::replaceTypesWithLinks(const Node* n,
4940                                              const InnerNode* parent,
4941                                              CodeMarker* marker,
4942                                              QString& src)
4943 {
4944     QStringRef arg;
4945     QStringRef par1;
4946     int srcSize = src.size();
4947     QString text;
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);
4952                 text.clear();
4953             }
4954             i += 2;
4955             if (parseArg(src, typeTag, &i, srcSize, &arg, &par1)) {
4956                 const Node* tn = marker->resolveTarget(arg.toString(), tree_, parent, n);
4957                 if (tn) {
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());
4961                     else
4962                         addLink(linkForNode(tn,parent),arg,DT_apiRelation);
4963                 }
4964             }
4965         }
4966         else {
4967             text += src.at(i++);
4968         }
4969     }
4970     if (!text.isEmpty()) {
4971         writeCharacters(text);
4972         text.clear();
4973     }
4974 }
4975
4976 /*!
4977   This function writes the <cxxFunctionParameters> element.
4978  */
4979 void DitaXmlGenerator::writeParameters(const FunctionNode* fn,
4980                                        const InnerNode* parent,
4981                                        CodeMarker* marker)
4982 {
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>
4999
5000             // not included: <cxxFunctionParameterDefinitionName>
5001
5002             if (!(*p).defaultValue().isEmpty()) {
5003                 writeStartTag(DT_cxxFunctionParameterDefaultValue);
5004                 writeCharacters((*p).defaultValue());
5005                 writeEndTag(); // <cxxFunctionParameterDefaultValue>
5006             }
5007
5008             // not included: <apiDefNote>
5009
5010             writeEndTag(); // <cxxFunctionParameter>
5011             ++p;
5012         }
5013         writeEndTag(); // <cxxFunctionParameters>
5014     }
5015 }
5016
5017 /*!
5018   This function writes the enum types.
5019  */
5020 void DitaXmlGenerator::writeEnumerations(const Section& s,
5021                                          CodeMarker* marker,
5022                                          const QString& attribute)
5023 {
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);
5036
5037             // not included <prolog>
5038
5039             writeStartTag(DT_cxxEnumerationDetail);
5040             writeStartTag(DT_cxxEnumerationDefinition);
5041             writeStartTag(DT_cxxEnumerationAccessSpecifier);
5042             xmlWriter().writeAttribute("value",en->accessString());
5043             writeEndTag(); // <cxxEnumerationAccessSpecifier>
5044
5045             QString fq = fullQualification(en);
5046             if (!fq.isEmpty()) {
5047                 writeStartTag(DT_cxxEnumerationScopedName);
5048                 writeCharacters(fq);
5049                 writeEndTag(); // <cxxEnumerationScopedName>
5050             }
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());
5062                     }
5063                     ++i;
5064                     if (i != items.end())
5065                         xmlWriter().writeCharacters(", ");
5066                 }
5067                 xmlWriter().writeCharacters(" }");
5068                 writeEndTag(); // <cxxEnumerationPrototype>
5069             }
5070
5071             writeStartTag(DT_cxxEnumerationNameLookup);
5072             writeCharacters(en->parent()->name() + "::" + en->name());
5073             writeEndTag(); // <cxxEnumerationNameLookup>
5074
5075             // not included: <cxxEnumerationReimplemented>
5076
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>
5085
5086                     QString fq = fullQualification(en->parent());
5087                     if (!fq.isEmpty()) {
5088                         writeStartTag(DT_cxxEnumeratorScopedName);
5089                         writeCharacters(fq + "::" + (*i).name());
5090                         writeEndTag(); // <cxxEnumeratorScopedName>
5091                     }
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>
5098
5099                     if (!(*i).value().isEmpty()) {
5100                         writeStartTag(DT_cxxEnumeratorInitialiser);
5101                         if ((*i).value().toInt(0,16) == 0)
5102                             xmlWriter().writeAttribute("value", "0");
5103                         else
5104                             xmlWriter().writeAttribute("value", (*i).value());
5105                         writeEndTag(); // <cxxEnumeratorInitialiser>
5106                     }
5107
5108                     // not included: <cxxEnumeratorAPIItemLocation>
5109
5110                     if (!(*i).text().isEmpty()) {
5111                         writeStartTag(DT_apiDesc);
5112                         generateText((*i).text(), en, marker);
5113                         writeEndTag(); // </apiDesc>
5114                     }
5115                     writeEndTag(); // <cxxEnumerator>
5116                     ++i;
5117                 }
5118                 writeEndTag(); // <cxxEnumerators>
5119             }
5120
5121             writeLocation(en);
5122             writeEndTag(); // <cxxEnumerationDefinition>
5123
5124             writeApiDesc(en, marker, QString());
5125
5126             // not included: <example> or <apiImpl>
5127
5128             writeEndTag(); // </cxxEnumerationDetail>
5129
5130             // not included: <related-links>
5131
5132             writeEndTag(); // </cxxEnumeration>
5133         }
5134         ++m;
5135     }
5136 }
5137
5138 /*!
5139   This function writes the output for the \typedef commands.
5140  */
5141 void DitaXmlGenerator::writeTypedefs(const Section& s,
5142                                      CodeMarker* marker,
5143                                      const QString& attribute)
5144
5145 {
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);
5158
5159             // not included: <prolog>
5160
5161             writeStartTag(DT_cxxTypedefDetail);
5162             writeStartTag(DT_cxxTypedefDefinition);
5163             writeStartTag(DT_cxxTypedefAccessSpecifier);
5164             xmlWriter().writeAttribute("value",tn->accessString());
5165             writeEndTag(); // <cxxTypedefAccessSpecifier>
5166
5167             // not included: <cxxTypedefDeclaredType>
5168
5169             QString fq = fullQualification(tn);
5170             if (!fq.isEmpty()) {
5171                 writeStartTag(DT_cxxTypedefScopedName);
5172                 writeCharacters(fq);
5173                 writeEndTag(); // <cxxTypedefScopedName>
5174             }
5175
5176             // not included: <cxxTypedefPrototype>
5177
5178             writeStartTag(DT_cxxTypedefNameLookup);
5179             writeCharacters(tn->parent()->name() + "::" + tn->name());
5180             writeEndTag(); // <cxxTypedefNameLookup>
5181
5182             // not included: <cxxTypedefReimplemented>
5183
5184             writeLocation(tn);
5185             writeEndTag(); // <cxxTypedefDefinition>
5186
5187             writeApiDesc(tn, marker, QString());
5188
5189             // not included: <example> or <apiImpl>
5190
5191             writeEndTag(); // </cxxTypedefDetail>
5192
5193             // not included: <related-links>
5194
5195             writeEndTag(); // </cxxTypedef>
5196         }
5197         ++m;
5198     }
5199 }
5200
5201 /*!
5202   This function writes the output for the \property commands.
5203   This is the Q_PROPERTYs.
5204  */
5205 void DitaXmlGenerator::writeProperties(const Section& s,
5206                                        CodeMarker* marker,
5207                                        const QString& attribute)
5208 {
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);
5221
5222             // not included: <prolog>
5223
5224             writeStartTag(DT_cxxVariableDetail);
5225             writeStartTag(DT_cxxVariableDefinition);
5226             writeStartTag(DT_cxxVariableAccessSpecifier);
5227             xmlWriter().writeAttribute("value",pn->accessString());
5228             writeEndTag(); // <cxxVariableAccessSpecifier>
5229
5230             // not included: <cxxVariableStorageClassSpecifierExtern>,
5231             //               <cxxVariableStorageClassSpecifierStatic>,
5232             //               <cxxVariableStorageClassSpecifierMutable>,
5233             //               <cxxVariableConst>, <cxxVariableVolatile>
5234
5235             if (!pn->qualifiedDataType().isEmpty()) {
5236                 writeStartTag(DT_cxxVariableDeclaredType);
5237                 writeCharacters(pn->qualifiedDataType());
5238                 writeEndTag(); // <cxxVariableDeclaredType>
5239             }
5240             QString fq = fullQualification(pn);
5241             if (!fq.isEmpty()) {
5242                 writeStartTag(DT_cxxVariableScopedName);
5243                 writeCharacters(fq);
5244                 writeEndTag(); // <cxxVariableScopedName>
5245             }
5246
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());
5260                 else
5261                     xmlWriter().writeCharacters(pn->isDesignable() ? "true" : "false");
5262             }
5263             if (pn->isScriptable() != pn->scriptableDefault()) {
5264                 xmlWriter().writeCharacters(" SCRIPTABLE ");
5265                 if (!pn->runtimeScriptabilityFunction().isEmpty())
5266                     writeCharacters(pn->runtimeScriptabilityFunction());
5267                 else
5268                     xmlWriter().writeCharacters(pn->isScriptable() ? "true" : "false");
5269             }
5270             if (pn->isWritable() != pn->writableDefault()) {
5271                 xmlWriter().writeCharacters(" STORED ");
5272                 xmlWriter().writeCharacters(pn->isStored() ? "true" : "false");
5273             }
5274             if (pn->isUser() != pn->userDefault()) {
5275                 xmlWriter().writeCharacters(" USER ");
5276                 xmlWriter().writeCharacters(pn->isUser() ? "true" : "false");
5277             }
5278             if (pn->isConstant())
5279                 xmlWriter().writeCharacters(" CONSTANT");
5280             if (pn->isFinal())
5281                 xmlWriter().writeCharacters(" FINAL");
5282             xmlWriter().writeCharacters(")");
5283             writeEndTag(); // <cxxVariablePrototype>
5284
5285             writeStartTag(DT_cxxVariableNameLookup);
5286             writeCharacters(pn->parent()->name() + "::" + pn->name());
5287             writeEndTag(); // <cxxVariableNameLookup>
5288
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>
5295             }
5296
5297             writeLocation(pn);
5298             writeEndTag(); // <cxxVariableDefinition>
5299
5300             writeApiDesc(pn, marker, QString());
5301
5302             // not included: <example> or <apiImpl>
5303
5304             writeEndTag(); // </cxxVariableDetail>
5305
5306             // not included: <related-links>
5307
5308             writeEndTag(); // </cxxVariable>
5309         }
5310         ++m;
5311     }
5312 }
5313
5314 /*!
5315   This function outputs the nodes resulting from \variable commands.
5316  */
5317 void DitaXmlGenerator::writeDataMembers(const Section& s,
5318                                         CodeMarker* marker,
5319                                         const QString& attribute)
5320 {
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);
5333
5334             // not included: <prolog>
5335
5336             writeStartTag(DT_cxxVariableDetail);
5337             writeStartTag(DT_cxxVariableDefinition);
5338             writeStartTag(DT_cxxVariableAccessSpecifier);
5339             xmlWriter().writeAttribute("value",vn->accessString());
5340             writeEndTag(); // <cxxVariableAccessSpecifier>
5341
5342             // not included: <cxxVAriableStorageClassSpecifierExtern>
5343
5344             if (vn->isStatic()) {
5345                 writeStartTag(DT_cxxVariableStorageClassSpecifierStatic);
5346                 xmlWriter().writeAttribute("name","static");
5347                 xmlWriter().writeAttribute("value","static");
5348                 writeEndTag(); // <cxxVariableStorageClassSpecifierStatic>
5349             }
5350
5351             // not included: <cxxVAriableStorageClassSpecifierMutable>,
5352             //               <cxxVariableConst>, <cxxVariableVolatile>
5353
5354             writeStartTag(DT_cxxVariableDeclaredType);
5355             writeCharacters(vn->leftType());
5356             if (!vn->rightType().isEmpty())
5357                 writeCharacters(vn->rightType());
5358             writeEndTag(); // <cxxVariableDeclaredType>
5359
5360             QString fq = fullQualification(vn);
5361             if (!fq.isEmpty()) {
5362                 writeStartTag(DT_cxxVariableScopedName);
5363                 writeCharacters(fq);
5364                 writeEndTag(); // <cxxVariableScopedName>
5365             }
5366
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>
5374
5375             writeStartTag(DT_cxxVariableNameLookup);
5376             writeCharacters(vn->parent()->name() + "::" + vn->name());
5377             writeEndTag(); // <cxxVariableNameLookup>
5378
5379             // not included: <cxxVariableReimplemented>
5380
5381             writeLocation(vn);
5382             writeEndTag(); // <cxxVariableDefinition>
5383
5384             writeApiDesc(vn, marker, QString());
5385
5386             // not included: <example> or <apiImpl>
5387
5388             writeEndTag(); // </cxxVariableDetail>
5389
5390             // not included: <related-links>
5391
5392             writeEndTag(); // </cxxVariable>
5393         }
5394         ++m;
5395     }
5396 }
5397
5398 /*!
5399   This function writes a \macro as a <cxxDefine>.
5400  */
5401 void DitaXmlGenerator::writeMacros(const Section& s,
5402                                    CodeMarker* marker,
5403                                    const QString& attribute)
5404 {
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);
5418
5419                 // not included: <prolog>
5420
5421                 writeStartTag(DT_cxxDefineDetail);
5422                 writeStartTag(DT_cxxDefineDefinition);
5423                 writeStartTag(DT_cxxDefineAccessSpecifier);
5424                 xmlWriter().writeAttribute("value",fn->accessString());
5425                 writeEndTag(); // <cxxDefineAccessSpecifier>
5426
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("...");
5437                             else
5438                                 writeCharacters(params[i]);
5439                             if ((i+1) < params.size())
5440                                 xmlWriter().writeCharacters(", ");
5441                         }
5442                         xmlWriter().writeCharacters(")");
5443                     }
5444                 }
5445                 writeEndTag(); // <cxxDefinePrototype>
5446
5447                 writeStartTag(DT_cxxDefineNameLookup);
5448                 writeCharacters(fn->name());
5449                 writeEndTag(); // <cxxDefineNameLookup>
5450
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>
5457                 }
5458
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>
5468
5469                             // not included: <apiDefNote>
5470
5471                             writeEndTag(); // <cxxDefineParameter>
5472                         }
5473                         writeEndTag(); // <cxxDefineParameters>
5474                     }
5475                 }
5476
5477                 writeLocation(fn);
5478                 writeEndTag(); // <cxxDefineDefinition>
5479
5480                 writeApiDesc(fn, marker, QString());
5481
5482                 // not included: <example> or <apiImpl>
5483
5484                 writeEndTag(); // </cxxDefineDetail>
5485
5486                 // not included: <related-links>
5487
5488                 writeEndTag(); // </cxxDefine>
5489             }
5490         }
5491         ++m;
5492     }
5493 }
5494
5495 /*!
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.
5499  */
5500 void DitaXmlGenerator::writePropertyParameter(const QString& tag, const NodeList& nlist)
5501 {
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());
5508         ++n;
5509     }
5510 }
5511
5512 /*!
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().
5520
5521   It also ensures that a GUID map is created for the output file.
5522  */
5523 void DitaXmlGenerator::beginSubPage(const InnerNode* node,
5524                                     const QString& fileName)
5525 {
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();
5534 }
5535
5536 /*!
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.
5540  */
5541 void DitaXmlGenerator::endSubPage()
5542 {
5543     if (inSection())
5544         qDebug() << "Missing </section> in" << outFileName() << sectionNestingLevel;
5545     xmlWriter().writeEndDocument();
5546     delete xmlWriterStack.pop();
5547     Generator::endSubPage();
5548 }
5549
5550 /*!
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.
5555  */
5556 QXmlStreamWriter& DitaXmlGenerator::xmlWriter()
5557 {
5558     return *xmlWriterStack.top();
5559 }
5560
5561 /*!
5562   Writes the \e {<apiDesc>} element for \a node to the current XML
5563   stream using the code \a marker and the \a title.
5564  */
5565 void DitaXmlGenerator::writeApiDesc(const Node* node,
5566                                     CodeMarker* marker,
5567                                     const QString& title)
5568 {
5569     if (!node->doc().isEmpty()) {
5570         inDetailedDescription = true;
5571         enterApiDesc(QString(),title);
5572         generateBody(node, marker);
5573         generateAlsoList(node, marker);
5574         leaveSection();
5575     }
5576     inDetailedDescription = false;
5577 }
5578
5579 /*!
5580   Write the nested class elements.
5581  */
5582 void DitaXmlGenerator::writeNestedClasses(const Section& s,
5583                                           const Node* n)
5584 {
5585     if (s.members.isEmpty())
5586         return;
5587     writeStartTag(DT_cxxClassNested);
5588     writeStartTag(DT_cxxClassNestedDetail);
5589
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>
5599         }
5600         ++m;
5601     }
5602     writeEndTag(); // <cxxClassNestedDetail>
5603     writeEndTag(); // <cxxClassNested>
5604 }
5605
5606 /*!
5607   Recursive writing of DITA XML files from the root \a node.
5608  */
5609 void
5610 DitaXmlGenerator::generateInnerNode(const InnerNode* node)
5611 {
5612     if (!node->url().isNull())
5613         return;
5614
5615     if (node->type() == Node::Fake) {
5616         const FakeNode *fakeNode = static_cast<const FakeNode *>(node);
5617         if (fakeNode->subType() == Node::ExternalPage)
5618             return;
5619         if (fakeNode->subType() == Node::Image)
5620             return;
5621         if (fakeNode->subType() == Node::QmlPropertyGroup)
5622             return;
5623         if (fakeNode->subType() == Node::Page) {
5624             if (node->count() > 0)
5625                 qDebug("PAGE %s HAS CHILDREN", qPrintable(fakeNode->title()));
5626         }
5627     }
5628
5629     /*
5630       Obtain a code marker for the source file.
5631      */
5632     CodeMarker *marker = CodeMarker::markerForFileName(node->location().filePath());
5633
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);
5639         }
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);
5645             else
5646                 generateFakeNode(static_cast<const FakeNode*>(node), marker);
5647         }
5648         if (!node->name().endsWith(".ditamap"))
5649             endSubPage();
5650     }
5651
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);
5656         ++c;
5657     }
5658 }
5659
5660 /*!
5661   Returns true if \a format is "XML" or "HTML" .
5662  */
5663 bool DitaXmlGenerator::canHandleFormat(const QString& format)
5664 {
5665     return (format == "HTML") || (format == this->format());
5666 }
5667
5668 /*!
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.
5672  */
5673 bool DitaXmlGenerator::isDuplicate(NodeMultiMap* nmm, const QString& key, Node* node)
5674 {
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)
5679                 return true;
5680             if (fileName(node) == fileName(matches[i]))
5681                 return true;
5682         }
5683     }
5684     return false;
5685 }
5686 /*!
5687   Collect all the nodes in the tree according to their type or subtype.
5688
5689   If a node is found that is named index.html, return that node as the
5690   root page node.
5691
5692   type: Class
5693   type: Namespace
5694
5695   subtype: Example
5696   subtype: External page
5697   subtype: Group
5698   subtype: Header file
5699   subtype: Module
5700   subtype: Page
5701   subtype: QML basic type
5702   subtype: QML class
5703   subtype: QML module
5704  */
5705 Node* DitaXmlGenerator::collectNodesByTypeAndSubtype(const InnerNode* parent)
5706 {
5707     Node* rootPageNode = 0;
5708     const NodeList& children = parent->childNodes();
5709     if (children.size() == 0)
5710         return rootPageNode;
5711
5712     QString message;
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);
5718             if (n)
5719                 rootPageNode = n;
5720             continue;
5721         }
5722         if (!child || child->isInternal() || child->doc().isEmpty())
5723             continue;
5724
5725         if (child->name() == "index.html") {
5726             rootPageNode = child;
5727         }
5728
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);
5733             break;
5734         case Node::Class:
5735             if (!isDuplicate(nodeTypeMaps[Node::Class],child->name(),child))
5736                 nodeTypeMaps[Node::Class]->insert(child->name(),child);
5737             break;
5738         case Node::Fake:
5739             switch (child->subType()) {
5740             case Node::Example:
5741                 if (!isDuplicate(nodeSubtypeMaps[Node::Example],child->title(),child))
5742                     nodeSubtypeMaps[Node::Example]->insert(child->title(),child);
5743                 break;
5744             case Node::HeaderFile:
5745                 if (!isDuplicate(nodeSubtypeMaps[Node::HeaderFile],child->title(),child))
5746                     nodeSubtypeMaps[Node::HeaderFile]->insert(child->title(),child);
5747                 break;
5748             case Node::File:
5749                 break;
5750             case Node::Image:
5751                 break;
5752             case Node::Group:
5753                 if (!isDuplicate(nodeSubtypeMaps[Node::Group],child->title(),child))
5754                     nodeSubtypeMaps[Node::Group]->insert(child->title(),child);
5755                 break;
5756             case Node::Module:
5757                 if (!isDuplicate(nodeSubtypeMaps[Node::Module],child->title(),child))
5758                     nodeSubtypeMaps[Node::Module]->insert(child->title(),child);
5759                 break;
5760             case Node::Page:
5761                 if (!isDuplicate(pageTypeMaps[child->pageType()],child->title(),child))
5762                     pageTypeMaps[child->pageType()]->insert(child->title(),child);
5763                 break;
5764             case Node::ExternalPage:
5765                 if (!isDuplicate(nodeSubtypeMaps[Node::ExternalPage],child->title(),child))
5766                     nodeSubtypeMaps[Node::ExternalPage]->insert(child->title(),child);
5767                 break;
5768             case Node::QmlClass:
5769                 if (!isDuplicate(nodeSubtypeMaps[Node::QmlClass],child->title(),child))
5770                     nodeSubtypeMaps[Node::QmlClass]->insert(child->title(),child);
5771                 break;
5772             case Node::QmlPropertyGroup:
5773                 break;
5774             case Node::QmlBasicType:
5775                 if (!isDuplicate(nodeSubtypeMaps[Node::QmlBasicType],child->title(),child))
5776                     nodeSubtypeMaps[Node::QmlBasicType]->insert(child->title(),child);
5777                 break;
5778             case Node::QmlModule:
5779                 if (!isDuplicate(nodeSubtypeMaps[Node::QmlModule],child->title(),child))
5780                     nodeSubtypeMaps[Node::QmlModule]->insert(child->title(),child);
5781                 break;
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);
5786                 break;
5787             default:
5788                 break;
5789             }
5790             break;
5791         case Node::Enum:
5792             break;
5793         case Node::Typedef:
5794             break;
5795         case Node::Function:
5796             break;
5797         case Node::Property:
5798             break;
5799         case Node::Variable:
5800             break;
5801         case Node::Target:
5802             break;
5803         case Node::QmlProperty:
5804             break;
5805         case Node::QmlSignal:
5806             break;
5807         case Node::QmlSignalHandler:
5808             break;
5809         case Node::QmlMethod:
5810             break;
5811         default:
5812             break;
5813         }
5814     }
5815     return rootPageNode;
5816 }
5817
5818 /*!
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.
5821  */
5822 void DitaXmlGenerator::writeDitaMap(const Tree *tree)
5823 {
5824     QString doctype;
5825
5826 #if 0
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>
5842         ++i;
5843     }
5844     endSubPage();
5845 #endif
5846
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());
5854
5855     beginSubPage(tree->root(),"qt.ditamap");
5856
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>
5865
5866     writeStartTag(DT_topicref);
5867     if (rootPageNode) {
5868         if (!rootPageNode->title().isEmpty())
5869             xmlWriter().writeAttribute("navtitle",rootPageNode->title());
5870         else
5871             xmlWriter().writeAttribute("navtitle",project);
5872         xmlWriter().writeAttribute("href",fileName(rootPageNode));
5873     }
5874     else
5875         xmlWriter().writeAttribute("navtitle",project);
5876
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");
5891
5892     writeEndTag(); // </topicref>
5893     endSubPage();
5894
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];
5901 }
5902
5903 /*!
5904   Creates the DITA map from the topicrefs in \a node,
5905   which is a DitaMapNode.
5906  */
5907 void DitaXmlGenerator::writeDitaMap(const DitaMapNode* node)
5908 {
5909     beginSubPage(node,node->name());
5910
5911     QString doctype;
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();
5921     writeDitaRefs(map);
5922     endSubPage();
5923 }
5924
5925 /*!
5926   Write the \a ditarefs to the current output file.
5927  */
5928 void DitaXmlGenerator::writeDitaRefs(const DitaRefList& ditarefs)
5929 {
5930     foreach (DitaRef* t, ditarefs) {
5931         if (t->isMapRef())
5932             writeStartTag(DT_mapref);
5933         else
5934             writeStartTag(DT_topicref);
5935         xmlWriter().writeAttribute("navtitle",t->navtitle());
5936         if (t->href().isEmpty()) {
5937             const FakeNode* fn = tree_->findFakeNodeByTitle(t->navtitle());
5938             if (fn)
5939                 xmlWriter().writeAttribute("href",fileName(fn));
5940         }
5941         else
5942             xmlWriter().writeAttribute("href",t->href());
5943         if (t->subrefs() && !t->subrefs()->isEmpty())
5944             writeDitaRefs(*(t->subrefs()));
5945         writeEndTag(); // </topicref> or </mapref>
5946     }
5947 }
5948
5949 void DitaXmlGenerator::writeTopicrefs(NodeMultiMap* nmm, const QString& navtitle)
5950 {
5951     if (!nmm || nmm->isEmpty())
5952         return;
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()) {
5961         case Node::Fake: {
5962             const FakeNode* fn = static_cast<const FakeNode*>(i.value());
5963             switch (fn->subType()) {
5964             case Node::Group: {
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>
5971                 }
5972                 break;
5973             }
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>
5981                 }
5982                 break;
5983             }
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>
5991                 }
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>
5997                 }
5998                 break;
5999             }
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())
6009                             continue;
6010                         writeStartTag(DT_topicref);
6011                         xmlWriter().writeAttribute("navtitle",node->name());
6012                         xmlWriter().writeAttribute("href",fileName(node));
6013                         writeEndTag(); // </topicref>
6014                     }
6015                 }
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())
6024                             continue;
6025                         writeStartTag(DT_topicref);
6026                         xmlWriter().writeAttribute("navtitle",node->name());
6027                         xmlWriter().writeAttribute("href",fileName(node));
6028                         writeEndTag(); // </topicref>
6029                     }
6030                 }
6031                 break;
6032             }
6033             default:
6034                 break;
6035             }
6036             break;
6037         }
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())
6043                     continue;
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>
6049                 }
6050             }
6051             break;
6052         }
6053         case Node::Class: {
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())
6058                     continue;
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>
6064                 }
6065             }
6066             break;
6067         }
6068         default:
6069             break;
6070         }
6071         writeEndTag(); // </topicref>
6072         ++i;
6073     }
6074     writeEndTag(); // </topicref>
6075 }
6076
6077
6078 /*!
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.
6084
6085   Returns true or false depending on whether it writes
6086   an element using the tag \a t.
6087
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,
6090   you consume \a t.
6091  */
6092 bool DitaXmlGenerator::writeMetadataElement(const InnerNode* inner,
6093                                             DitaXmlGenerator::DitaTag t,
6094                                             bool force)
6095 {
6096     QString s = getMetadataElement(inner,t);
6097     if (s.isEmpty() && !force)
6098         return false;
6099     writeStartTag(t);
6100     if (!s.isEmpty())
6101         xmlWriter().writeCharacters(s);
6102     writeEndTag();
6103     return true;
6104 }
6105
6106
6107 /*!
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.
6112
6113   Returns true or false depending on whether it writes
6114   at least one element using the tag \a t.
6115
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,
6118   you consume \a t.
6119  */
6120 bool DitaXmlGenerator::writeMetadataElements(const InnerNode* inner,
6121                                              DitaXmlGenerator::DitaTag t)
6122 {
6123     QStringList s = getMetadataElements(inner,t);
6124     if (s.isEmpty())
6125         return false;
6126     for (int i=0; i<s.size(); ++i) {
6127         writeStartTag(t);
6128         xmlWriter().writeCharacters(s[i]);
6129         writeEndTag();
6130     }
6131     return true;
6132 }
6133
6134 /*!
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.
6138
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,
6141   you consume \a t.
6142  */
6143 QString DitaXmlGenerator::getMetadataElement(const InnerNode* inner, DitaXmlGenerator::DitaTag t)
6144 {
6145     QString s = Generator::getMetadataElement(inner, ditaTags[t]);
6146     if (s.isEmpty())
6147         s = metadataDefault(t);
6148     return s;
6149 }
6150
6151 /*!
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
6155   list.
6156
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
6160   \a t.
6161  */
6162 QStringList DitaXmlGenerator::getMetadataElements(const InnerNode* inner,
6163                                                   DitaXmlGenerator::DitaTag t)
6164 {
6165     QStringList s = Generator::getMetadataElements(inner,ditaTags[t]);
6166     if (s.isEmpty())
6167         s.append(metadataDefault(t));
6168     return s;
6169 }
6170
6171 /*!
6172   Returns the value of key \a t or an empty string
6173   if \a t is not found in the map.
6174  */
6175 QString DitaXmlGenerator::metadataDefault(DitaTag t) const
6176 {
6177     return metadataDefaults.value(ditaTags[t]);
6178 }
6179
6180 /*!
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:
6185
6186   \list
6187     \o <audience> *
6188     \o <author> *
6189     \o <brand> not used
6190     \o <category> *
6191     \o <compomnent> *
6192     \o <copyrholder> *
6193     \o <copyright> *
6194     \o <created> not used
6195     \o <copyryear> *
6196     \o <critdates> not used
6197     \o <keyword> not used
6198     \o <keywords> not used
6199     \o <metadata> *
6200     \o <othermeta> *
6201     \o <permissions> *
6202     \o <platform> not used
6203     \o <prodinfo> *
6204     \o <prodname> *
6205     \o <prolog> *
6206     \o <publisher> *
6207     \o <resourceid> not used
6208     \o <revised> not used
6209     \o <source> not used
6210     \o <tm> not used
6211     \o <unknown> not used
6212     \o <vrm> *
6213     \o <vrmlist> *
6214   \endlist
6215
6216   \node * means the tag has been used.
6217
6218  */
6219 void
6220 DitaXmlGenerator::writeProlog(const InnerNode* inner)
6221 {
6222     if (!inner)
6223         return;
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);
6231     if (!s.isEmpty())
6232         xmlWriter().writeAttribute("year",s);
6233     writeEndTag(); // </copyryear>
6234     writeStartTag(DT_copyrholder);
6235     if (!s.isEmpty())
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>
6250         }
6251     }
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)
6273                 category = "Image";
6274             else if (inner->subType() == Node::Group)
6275                 category = "Group";
6276             else if (inner->subType() == Node::Page)
6277                 category = "Page";
6278             else if (inner->subType() == Node::ExternalPage)
6279                 category = "External Page"; // Is this necessary?
6280         }
6281         xmlWriter().writeCharacters(category);
6282         writeEndTag(); // </category>
6283     }
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>
6290         }
6291         writeStartTag(DT_vrmlist);
6292         writeStartTag(DT_vrm);
6293         if (vrm.size() > 0)
6294             xmlWriter().writeAttribute("version",vrm[0]);
6295         if (vrm.size() > 1)
6296             xmlWriter().writeAttribute("release",vrm[1]);
6297         if (vrm.size() > 2)
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>
6307             }
6308         }
6309         writeEndTag(); // </prodinfo>
6310     }
6311     const QStringMultiMap& metaTagMap = inner->doc().metaTagMap();
6312     QMapIterator<QString, QString> i(metaTagMap);
6313     while (i.hasNext()) {
6314         i.next();
6315         writeStartTag(DT_othermeta);
6316         xmlWriter().writeAttribute("name",i.key());
6317         xmlWriter().writeAttribute("content",i.value());
6318         writeEndTag(); // </othermeta>
6319     }
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");
6324         QString text;
6325         QStringList::ConstIterator i = inner->includes().begin();
6326         while (i != inner->includes().end()) {
6327             if ((*i).startsWith("<") && (*i).endsWith(">"))
6328                 text += *i;
6329             else
6330                 text += "<" + *i + ">";
6331             ++i;
6332             if (i != inner->includes().end())
6333                 text += "\n";
6334         }
6335         xmlWriter().writeAttribute("content",text);
6336         writeEndTag(); // </othermeta>
6337     }
6338     writeEndTag(); // </metadata>
6339     writeEndTag(); // </prolog>
6340 }
6341
6342 /*!
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
6346   value \e external.
6347  */
6348 void DitaXmlGenerator::writeHrefAttribute(const QString& href)
6349 {
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");
6354 }
6355
6356 /*!
6357   Strips the markup tags from \a src, when we are trying to
6358   create an \e{id} attribute. Returns the stripped text.
6359  */
6360 QString DitaXmlGenerator::stripMarkup(const QString& src) const
6361 {
6362     QString text;
6363     const QChar charAt = '@';
6364     const QChar charSlash = '/';
6365     const QChar charLangle = '<';
6366     const QChar charRangle = '>';
6367
6368     int n = src.size();
6369     int i = 0;
6370     while (i < n) {
6371         if (src.at(i) == charLangle) {
6372             ++i;
6373             if (src.at(i) == charAt || (src.at(i) == charSlash && src.at(i+1) == charAt)) {
6374                 while (i < n && src.at(i) != charRangle)
6375                     ++i;
6376                 ++i;
6377             }
6378             else {
6379                 text += charLangle;
6380             }
6381         }
6382         else
6383             text += src.at(i++);
6384     }
6385     return text;
6386 }
6387
6388
6389 QT_END_NAMESPACE