qdoc: Removed several #if 0 blocks.
[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     "apiData",
74     "apiDef",
75     "apiDefItem",
76     "apiDesc",
77     "apiDetail",
78     "apiItemName",
79     "APIMap",
80     "apiName",
81     "apiRef",
82     "apiRelation",
83     "audience",
84     "author",
85     "b",
86     "body",
87     "bodydiv",
88     "brand",
89     "category",
90     "codeblock",
91     "colspec",
92     "comment",
93     "component",
94     "copyrholder",
95     "copyright",
96     "copyryear",
97     "created",
98     "critdates",
99     "cxxAPIMap",
100     "cxxClass",
101     "cxxClassAbstract",
102     "cxxClassAccessSpecifier",
103     "cxxClassAPIItemLocation",
104     "cxxClassBaseClass",
105     "cxxClassDeclarationFile",
106     "cxxClassDeclarationFileLine",
107     "cxxClassDeclarationFileLineStart",
108     "cxxClassDeclarationFileLineEnd",
109     "cxxClassDefinition",
110     "cxxClassDerivation",
111     "cxxClassDerivationAccessSpecifier",
112     "cxxClassDerivations",
113     "cxxClassDetail",
114     "cxxClassNested",
115     "cxxClassNestedClass",
116     "cxxClassNestedDetail",
117     "cxxDefine",
118     "cxxDefineAccessSpecifier",
119     "cxxDefineAPIItemLocation",
120     "cxxDefineDeclarationFile",
121     "cxxDefineDeclarationFileLine",
122     "cxxDefineDefinition",
123     "cxxDefineDetail",
124     "cxxDefineNameLookup",
125     "cxxDefineParameter",
126     "cxxDefineParameterDeclarationName",
127     "cxxDefineParameters",
128     "cxxDefinePrototype",
129     "cxxDefineReimplemented",
130     "cxxEnumeration",
131     "cxxEnumerationAccessSpecifier",
132     "cxxEnumerationAPIItemLocation",
133     "cxxEnumerationDeclarationFile",
134     "cxxEnumerationDeclarationFileLine",
135     "cxxEnumerationDeclarationFileLineStart",
136     "cxxEnumerationDeclarationFileLineEnd",
137     "cxxEnumerationDefinition",
138     "cxxEnumerationDetail",
139     "cxxEnumerationNameLookup",
140     "cxxEnumerationPrototype",
141     "cxxEnumerationScopedName",
142     "cxxEnumerator",
143     "cxxEnumeratorInitialiser",
144     "cxxEnumeratorNameLookup",
145     "cxxEnumeratorPrototype",
146     "cxxEnumerators",
147     "cxxEnumeratorScopedName",
148     "cxxFunction",
149     "cxxFunctionAccessSpecifier",
150     "cxxFunctionAPIItemLocation",
151     "cxxFunctionConst",
152     "cxxFunctionConstructor",
153     "cxxFunctionDeclarationFile",
154     "cxxFunctionDeclarationFileLine",
155     "cxxFunctionDeclaredType",
156     "cxxFunctionDefinition",
157     "cxxFunctionDestructor",
158     "cxxFunctionDetail",
159     "cxxFunctionNameLookup",
160     "cxxFunctionParameter",
161     "cxxFunctionParameterDeclarationName",
162     "cxxFunctionParameterDeclaredType",
163     "cxxFunctionParameterDefaultValue",
164     "cxxFunctionParameters",
165     "cxxFunctionPrototype",
166     "cxxFunctionPureVirtual",
167     "cxxFunctionReimplemented",
168     "cxxFunctionScopedName",
169     "cxxFunctionStorageClassSpecifierStatic",
170     "cxxFunctionVirtual",
171     "cxxTypedef",
172     "cxxTypedefAccessSpecifier",
173     "cxxTypedefAPIItemLocation",
174     "cxxTypedefDeclarationFile",
175     "cxxTypedefDeclarationFileLine",
176     "cxxTypedefDefinition",
177     "cxxTypedefDetail",
178     "cxxTypedefNameLookup",
179     "cxxTypedefScopedName",
180     "cxxVariable",
181     "cxxVariableAccessSpecifier",
182     "cxxVariableAPIItemLocation",
183     "cxxVariableDeclarationFile",
184     "cxxVariableDeclarationFileLine",
185     "cxxVariableDeclaredType",
186     "cxxVariableDefinition",
187     "cxxVariableDetail",
188     "cxxVariableNameLookup",
189     "cxxVariablePrototype",
190     "cxxVariableReimplemented",
191     "cxxVariableScopedName",
192     "cxxVariableStorageClassSpecifierStatic",
193     "data",
194     "data-about",
195     "dd",
196     "dl",
197     "dlentry",
198     "dt",
199     "entry",
200     "fig",
201     "i",
202     "image",
203     "keyword",
204     "keywords",
205     "li",
206     "link",
207     "linktext",
208     "lq",
209     "map",
210     "mapref",
211     "metadata",
212     "note",
213     "ol",
214     "othermeta",
215     "p",
216     "parameter",
217     "permissions",
218     "ph",
219     "platform",
220     "pre",
221     "prodinfo",
222     "prodname",
223     "prolog",
224     "publisher",
225     "qmlAttached",
226     "qmlDetail",
227     "qmlImportModule",
228     "qmlInheritedBy",
229     "qmlInherits",
230     "qmlInstantiates",
231     "qmlMethod",
232     "qmlMethodDef",
233     "qmlMethodDetail",
234     "qmlName",
235     "qmlProperty",
236     "qmlPropertyDef",
237     "qmlPropertyDetail",
238     "qmlPropertyGroup",
239     "qmlPropertyGroupDef",
240     "qmlPropertyGroupDetail",
241     "qmlQualifier",
242     "qmlSignal",
243     "qmlSignalDef",
244     "qmlSignalDetail",
245     "qmlSignalHandler",
246     "qmlSignalHandlerDef",
247     "qmlSignalHandlerDetail",
248     "qmlSignature",
249     "qmlSince",
250     "qmlType",
251     "qmlTypeDef",
252     "qmlTypeDetail",
253     "related-links",
254     "resourceid",
255     "revised",
256     "row",
257     "section",
258     "sectiondiv",
259     "shortdesc",
260     "simpletable",
261     "source",
262     "stentry",
263     "sthead",
264     "strow",
265     "sub",
266     "sup",
267     "table",
268     "tbody",
269     "tgroup",
270     "thead",
271     "title",
272     "tm",
273     "topic",
274     "topicmeta",
275     "topicref",
276     "tt",
277     "u",
278     "uicontrol",
279     "ul",
280     "unknown",
281     "vrm",
282     "vrmlist",
283     "xref",
284     ""
285 };
286
287 void DitaXmlGenerator::debugPara(const QString& t)
288 {
289     writeStartTag(DT_p);
290     xmlWriter().writeAttribute("outputclass",t);
291     xmlWriter().writeCharacters(t);
292     writeEndTag(); // </p>
293 }
294
295 static bool showBrokenLinks = false;
296
297 /*!
298   Quick, dirty, and very ugly. Unescape \a text
299   so QXmlStreamWriter::writeCharacters() can put
300   the escapes back in again!
301  */
302 void DitaXmlGenerator::writeCharacters(const QString& text)
303 {
304     QString t = text;
305     t = t.replace("&lt;","<");
306     t = t.replace("&gt;",">");
307     t = t.replace("&amp;","&");
308     t = t.replace("&quot;","\"");
309     xmlWriter().writeCharacters(t);
310 }
311
312 /*!
313   Appends an <xref> element to the current XML stream
314   with the \a href attribute and the \a text.
315  */
316 void DitaXmlGenerator::addLink(const QString& href,
317                                const QStringRef& text,
318                                DitaTag t)
319 {
320     if (!href.isEmpty()) {
321         writeStartTag(t);
322         // formathtml
323         writeHrefAttribute(href);
324         writeCharacters(text.toString());
325         writeEndTag(); // </t>
326     }
327     else {
328         writeCharacters(text.toString());
329     }
330 }
331
332 /*!
333   Push \a t onto the dita tag stack and write the appropriate
334   start tag to the DITA XML file.
335  */
336 void DitaXmlGenerator::writeStartTag(DitaTag t)
337 {
338     xmlWriter().writeStartElement(ditaTags[t]);
339     tagStack.push(t);
340 }
341
342 /*!
343   Pop the current DITA tag off the stack, and write the
344   appropriate end tag to the DITA XML file. If \a t is
345   not \e DT_NONE (default), then \a t contains the enum
346   value of the tag that should be on top of the stack.
347
348   If the stack is empty, no end tag is written and false
349   is returned. Otherwise, an end tag is written and true
350   is returned.
351  */
352 bool DitaXmlGenerator::writeEndTag(DitaTag t)
353 {
354     if (tagStack.isEmpty())
355         return false;
356     DitaTag top = tagStack.pop();
357     if (t > DT_NONE && top != t)
358         qDebug() << "Expected:" << t << "ACTUAL:" << top;
359     xmlWriter().writeEndElement();
360     return true;
361 }
362
363 /*!
364   Return the current DITA element tag, the one
365   on top of the stack.
366  */
367 DitaXmlGenerator::DitaTag DitaXmlGenerator::currentTag()
368 {
369     return tagStack.top();
370 }
371
372 /*!
373   Write the start \a tag. if \a title is not empty, generate
374   a GUID from it and write the GUID as the value of the \e{id}
375   attribute.
376
377   Then if \a outputclass is not empty, write it as the value
378   of the \a outputclass attribute.
379
380   Fiunally, set the section nesting level to 1 and return 1.
381  */
382 int DitaXmlGenerator::enterDesc(DitaTag tag, const QString& outputclass, const QString& title)
383 {
384     writeStartTag(tag);
385     if (!title.isEmpty()) {
386         writeGuidAttribute(title);
387         //Are there cases where the spectitle is required?
388         //xmlWriter().writeAttribute("spectitle",title);
389     }
390     if (!outputclass.isEmpty())
391         xmlWriter().writeAttribute("outputclass",outputclass);
392     sectionNestingLevel = 1;
393     return sectionNestingLevel;
394 }
395
396 /*!
397   If the section nesting level is 0, output a \c{<section>}
398   element with an \e id attribute generated from \a title and
399   an \e outputclass attribute set to \a outputclass.
400   If \a title is null, no \e id attribute is output.
401   If \a outputclass is empty, no \e outputclass attribute
402   is output.
403
404   Finally, increment the section nesting level and return
405   the new value.
406  */
407 int DitaXmlGenerator::enterSection(const QString& outputclass, const QString& title)
408 {
409     if (sectionNestingLevel == 0) {
410         writeStartTag(DT_section);
411         if (!title.isEmpty())
412             writeGuidAttribute(title);
413         if (!outputclass.isEmpty())
414             xmlWriter().writeAttribute("outputclass",outputclass);
415     }
416     else if (!title.isEmpty()) {
417         writeStartTag(DT_p);
418         writeGuidAttribute(title);
419         if (!outputclass.isEmpty())
420             xmlWriter().writeAttribute("outputclass",outputclass);
421         writeCharacters(title);
422         writeEndTag(); // </p>
423     }
424     return ++sectionNestingLevel;
425 }
426
427 /*!
428   If the section nesting level is greater than 0, decrement
429   it. If it becomes 0, output a \c {</section>}. Return the
430   decremented section nesting level.
431  */
432 int DitaXmlGenerator::leaveSection()
433 {
434     if (sectionNestingLevel > 0) {
435         --sectionNestingLevel;
436         if (sectionNestingLevel == 0)
437             writeEndTag(); // </section> or </apiDesc>
438     }
439     return sectionNestingLevel;
440 }
441
442 /*!
443   The default constructor.
444  */
445 DitaXmlGenerator::DitaXmlGenerator()
446     : inContents(false),
447       inDetailedDescription(false),
448       inLegaleseText(false),
449       inLink(false),
450       inObsoleteLink(false),
451       inSectionHeading(false),
452       inTableHeader(false),
453       inTableBody(false),
454       noLinks(false),
455       obsoleteLinks(false),
456       offlineDocs(true),
457       threeColumnEnumValueTable(true),
458       codeIndent(0),
459       numTableRows(0),
460       divNestingLevel(0),
461       sectionNestingLevel(0),
462       tableColumnCount(0),
463       funcLeftParen("\\S(\\()"),
464       nodeTypeMaps(Node::LastType,0),
465       nodeSubtypeMaps(Node::LastSubtype,0),
466       pageTypeMaps(Node::OnBeyondZebra,0)
467 {
468     // nothing yet.
469 }
470
471 /*!
472   The destructor has nothing to do.
473  */
474 DitaXmlGenerator::~DitaXmlGenerator()
475 {
476     GuidMaps::iterator i = guidMaps.begin();
477     while (i != guidMaps.end()) {
478         delete i.value();
479         ++i;
480     }
481 }
482
483 /*!
484   A lot of internal structures are initialized.
485  */
486 void DitaXmlGenerator::initializeGenerator(const Config &config)
487 {
488     Generator::initializeGenerator(config);
489     obsoleteLinks = config.getBool(QLatin1String(CONFIG_OBSOLETELINKS));
490     setImageFileExtensions(QStringList() << "png" << "jpg" << "jpeg" << "gif");
491
492     style = config.getString(DitaXmlGenerator::format() +
493                              Config::dot +
494                              DITAXMLGENERATOR_STYLE);
495     postHeader = config.getString(DitaXmlGenerator::format() +
496                                   Config::dot +
497                                   DITAXMLGENERATOR_POSTHEADER);
498     postPostHeader = config.getString(DitaXmlGenerator::format() +
499                                       Config::dot +
500                                       DITAXMLGENERATOR_POSTPOSTHEADER);
501     footer = config.getString(DitaXmlGenerator::format() +
502                               Config::dot +
503                               DITAXMLGENERATOR_FOOTER);
504     address = config.getString(DitaXmlGenerator::format() +
505                                Config::dot +
506                                DITAXMLGENERATOR_ADDRESS);
507     pleaseGenerateMacRef = config.getBool(DitaXmlGenerator::format() +
508                                           Config::dot +
509                                           DITAXMLGENERATOR_GENERATEMACREFS);
510
511     project = config.getString(CONFIG_PROJECT);
512     projectDescription = config.getString(CONFIG_DESCRIPTION);
513     if (projectDescription.isEmpty() && !project.isEmpty())
514         projectDescription = project + " Reference Documentation";
515
516     projectUrl = config.getString(CONFIG_URL);
517
518     outputEncoding = config.getString(CONFIG_OUTPUTENCODING);
519     if (outputEncoding.isEmpty())
520         outputEncoding = QLatin1String("ISO-8859-1");
521     outputCodec = QTextCodec::codecForName(outputEncoding.toLocal8Bit());
522
523     naturalLanguage = config.getString(CONFIG_NATURALLANGUAGE);
524     if (naturalLanguage.isEmpty())
525         naturalLanguage = QLatin1String("en");
526
527     config.subVarsAndValues("dita.metadata.default",metadataDefaults);
528     QSet<QString> editionNames = config.subVars(CONFIG_EDITION);
529     QSet<QString>::ConstIterator edition = editionNames.constBegin();
530     while (edition != editionNames.constEnd()) {
531         QString editionName = *edition;
532         QStringList editionModules = config.getStringList(CONFIG_EDITION +
533                                                           Config::dot +
534                                                           editionName +
535                                                           Config::dot +
536                                                           "modules");
537         QStringList editionGroups = config.getStringList(CONFIG_EDITION +
538                                                          Config::dot +
539                                                          editionName +
540                                                          Config::dot +
541                                                          "groups");
542
543         if (!editionModules.isEmpty())
544             editionModuleMap[editionName] = editionModules;
545         if (!editionGroups.isEmpty())
546             editionGroupMap[editionName] = editionGroups;
547
548         ++edition;
549     }
550
551     stylesheets = config.getStringList(DitaXmlGenerator::format() +
552                                        Config::dot +
553                                        DITAXMLGENERATOR_STYLESHEETS);
554     customHeadElements = config.getStringList(DitaXmlGenerator::format() +
555                                               Config::dot +
556                                               DITAXMLGENERATOR_CUSTOMHEADELEMENTS);
557     codeIndent = config.getInt(CONFIG_CODEINDENT);
558     version = config.getString(CONFIG_VERSION);
559     vrm = version.split(QLatin1Char('.'));
560 }
561
562 /*!
563   All this does is call the same function in the base class.
564  */
565 void DitaXmlGenerator::terminateGenerator()
566 {
567     Generator::terminateGenerator();
568 }
569
570 /*!
571   Returns "DITAXML".
572  */
573 QString DitaXmlGenerator::format()
574 {
575     return "DITAXML";
576 }
577
578 /*!
579   Calls lookupGuid() to get a GUID for \a text, then writes
580   it to the XML stream as an "id" attribute, and returns it.
581  */
582 QString DitaXmlGenerator::writeGuidAttribute(QString text)
583 {
584     QString guid = lookupGuid(outFileName(),text);
585     xmlWriter().writeAttribute("id",guid);
586     return guid;
587 }
588
589
590 /*!
591   Write's the GUID for the \a node to the current XML stream
592   as an "id" attribute. If the \a node doesn't yet have a GUID,
593   one is generated.
594  */
595 void DitaXmlGenerator::writeGuidAttribute(Node* node)
596 {
597     xmlWriter().writeAttribute("id",node->guid());
598 }
599
600 /*!
601   Looks up \a text in the GUID map. If it finds \a text,
602   it returns the associated GUID. Otherwise it inserts
603   \a text into the map with a new GUID, and it returns
604   the new GUID.
605  */
606 QString DitaXmlGenerator::lookupGuid(QString text)
607 {
608     QMap<QString, QString>::const_iterator i = name2guidMap.constFind(text);
609     if (i != name2guidMap.constEnd())
610         return i.value();
611     QString guid = Node::cleanId(text);
612     name2guidMap.insert(text,guid);
613     return guid;
614 }
615
616 /*!
617   First, look up the GUID map for \a fileName. If there isn't
618   a GUID map for \a fileName, create one and insert it into
619   the map of GUID maps. Then look up \a text in that GUID map.
620   If \a text is found, return the associated GUID. Otherwise,
621   insert \a text into the GUID map with a new GUID, and return
622   the new GUID.
623  */
624 QString DitaXmlGenerator::lookupGuid(const QString& fileName, const QString& text)
625 {
626     GuidMap* gm = lookupGuidMap(fileName);
627     GuidMap::const_iterator i = gm->constFind(text);
628     if (i != gm->constEnd())
629         return i.value();
630     QString guid = Node::cleanId(text);
631     gm->insert(text,guid);
632     return guid;
633 }
634
635 /*!
636   Looks up \a fileName in the map of GUID maps. If it finds
637   \a fileName, it returns a pointer to the associated GUID
638   map. Otherwise it creates a new GUID map and inserts it
639   into the map of GUID maps with \a fileName as its key.
640  */
641 GuidMap* DitaXmlGenerator::lookupGuidMap(const QString& fileName)
642 {
643     GuidMaps::const_iterator i = guidMaps.constFind(fileName);
644     if (i != guidMaps.constEnd())
645         return i.value();
646     GuidMap* gm = new GuidMap;
647     guidMaps.insert(fileName,gm);
648     return gm;
649 }
650
651 /*!
652   This is where the DITA XML files are written.
653   \note The file is created in PageGenerator::generateTree().
654  */
655 void DitaXmlGenerator::generateTree(Tree *tree)
656 {
657     tree_ = tree;
658     nonCompatClasses.clear();
659     mainClasses.clear();
660     compatClasses.clear();
661     obsoleteClasses.clear();
662     moduleClassMap.clear();
663     moduleNamespaceMap.clear();
664     funcIndex.clear();
665     legaleseTexts.clear();
666     serviceClasses.clear();
667     qmlClasses.clear();
668     findAllClasses(tree->root());
669     findAllFunctions(tree->root());
670     findAllLegaleseTexts(tree->root());
671     findAllNamespaces(tree->root());
672     findAllSince(tree->root());
673
674     Generator::generateTree(tree);
675     generateCollisionPages();
676
677     QString fileBase = project.toLower().simplified().replace(QLatin1Char(' '), QLatin1Char('-'));
678     generateIndex(fileBase, projectUrl, projectDescription);
679
680     writeDitaMap(tree);
681 }
682
683 void DitaXmlGenerator::startText(const Node* /* relative */,
684                                  CodeMarker* /* marker */)
685 {
686     inLink = false;
687     inContents = false;
688     inSectionHeading = false;
689     inTableHeader = false;
690     numTableRows = 0;
691     threeColumnEnumValueTable = true;
692     link.clear();
693     sectionNumber.clear();
694 }
695
696 static int countTableColumns(const Atom* t)
697 {
698     int result = 0;
699     if (t->type() == Atom::TableHeaderLeft) {
700         while (t->type() == Atom::TableHeaderLeft) {
701             int count = 0;
702             t = t->next();
703             while (t->type() != Atom::TableHeaderRight) {
704                 if (t->type() == Atom::TableItemLeft) {
705                     for (int i=0; i<t->count(); ++i) {
706                         QString attr = t->string(i);
707                         if (!attr.contains('=')) {
708                             QStringList spans = attr.split(QLatin1Char(','));
709                             if (spans.size() == 2) {
710                                 count += spans[0].toInt();
711                             }
712                             else {
713                                 ++count;
714                             }
715                         }
716                     }
717                 }
718                 t = t->next();
719             }
720             if (count > result)
721                 result = count;
722             t = t->next();
723         }
724     }
725     else if (t->type() == Atom::TableRowLeft) {
726         while (t->type() != Atom::TableRowRight) {
727             if (t->type() == Atom::TableItemLeft) {
728                 for (int i=0; i<t->count(); ++i) {
729                     QString attr = t->string(i);
730                     if (!attr.contains('=')) {
731                         QStringList spans = attr.split(QLatin1Char(','));
732                         if (spans.size() == 2) {
733                             result += spans[0].toInt();
734                         }
735                         else {
736                             ++result;
737                         }
738                     }
739                 }
740             }
741             t = t->next();
742         }
743     }
744     return result;
745 }
746
747 /*!
748   Generate html from an instance of Atom.
749  */
750 int DitaXmlGenerator::generateAtom(const Atom *atom,
751                                    const Node *relative,
752                                    CodeMarker *marker)
753 {
754     int skipAhead = 0;
755     QString hx, str;
756     static bool in_para = false;
757     QString guid, hc, attr;
758
759     switch (atom->type()) {
760     case Atom::AbstractLeft:
761         break;
762     case Atom::AbstractRight:
763         break;
764     case Atom::AutoLink:
765         if (!noLinks && !inLink && !inContents && !inSectionHeading) {
766             const Node* node = 0;
767             QString link = getLink(atom, relative, marker, &node);
768             if (!link.isEmpty()) {
769                 beginLink(link);
770                 generateLink(atom, relative, marker);
771                 endLink();
772             }
773             else {
774                 writeCharacters(protectEnc(atom->string()));
775             }
776         }
777         else {
778             writeCharacters(protectEnc(atom->string()));
779         }
780         break;
781     case Atom::BaseName:
782         break;
783     case Atom::BriefLeft:
784         {
785             Node::Type t = relative->type();
786             if (inSection()) {
787                 in_para = true;
788                 writeStartTag(DT_p);
789                 xmlWriter().writeAttribute("outputclass","brief");
790             }
791             else {
792                 noLinks = true;
793                 writeStartTag(DT_shortdesc);
794             }
795             if (t == Node::Property || t == Node::Variable) {
796                 xmlWriter().writeCharacters("This ");
797                 if (relative->type() == Node::Property)
798                     xmlWriter().writeCharacters("property");
799                 else if (relative->type() == Node::Variable)
800                     xmlWriter().writeCharacters("variable");
801                 xmlWriter().writeCharacters(" holds ");
802             }
803             if (noLinks) {
804                 atom = atom->next();
805                 while (atom != 0 && atom->type() != Atom::BriefRight) {
806                     if (atom->type() == Atom::String ||
807                         atom->type() == Atom::AutoLink)
808                         str += atom->string();
809                     skipAhead++;
810                     atom = atom->next();
811                 }
812                 if (t == Node::Property || t == Node::Variable)
813                     str[0] = str[0].toLower();
814                 if (str.endsWith(QLatin1Char('.')))
815                     str.truncate(str.length() - 1);
816                 writeCharacters(str + QLatin1Char('.'));
817             }
818         }
819         break;
820     case Atom::BriefRight:
821         //        if (relative->type() != Node::Fake)
822         writeEndTag(); // </shortdesc> or </p>
823         if (in_para)
824             in_para = false;
825         noLinks = false;
826         break;
827     case Atom::C:
828         writeStartTag(DT_tt);
829         if (inLink) {
830             writeCharacters(protectEnc(plainCode(atom->string())));
831         }
832         else {
833             writeText(atom->string(), marker, relative);
834         }
835         writeEndTag(); // see writeStartElement() above
836         break;
837     case Atom::Code:
838         {
839             writeStartTag(DT_codeblock);
840             xmlWriter().writeAttribute("outputclass","cpp");
841             writeCharacters("\n");
842             writeText(trimmedTrailing(atom->string()), marker, relative);
843             writeEndTag(); // </codeblock>
844         }
845         break;
846     case Atom::Qml:
847         writeStartTag(DT_codeblock);
848         xmlWriter().writeAttribute("outputclass","qml");
849         writeCharacters("\n");
850         writeText(trimmedTrailing(atom->string()), marker, relative);
851         writeEndTag(); // </codeblock>
852         break;
853     case Atom::CodeNew:
854         writeStartTag(DT_p);
855         xmlWriter().writeCharacters("you can rewrite it as");
856         writeEndTag(); // </p>
857         writeStartTag(DT_codeblock);
858         writeCharacters("\n");
859         writeText(trimmedTrailing(atom->string()), marker, relative);
860         writeEndTag(); // </codeblock>
861         break;
862     case Atom::CodeOld:
863         writeStartTag(DT_p);
864         xmlWriter().writeCharacters("For example, if you have code like");
865         writeEndTag(); // </p>
866         // fallthrough
867     case Atom::CodeBad:
868         writeStartTag(DT_codeblock);
869         writeCharacters("\n");
870         writeCharacters(trimmedTrailing(plainCode(atom->string())));
871         writeEndTag(); // </codeblock>
872         break;
873     case Atom::DivLeft:
874     {
875         bool inStartElement = false;
876         attr = atom->string();
877         DitaTag t = currentTag();
878         if ((t == DT_section) || (t == DT_sectiondiv)) {
879             writeStartTag(DT_sectiondiv);
880             divNestingLevel++;
881             inStartElement = true;
882         }
883         else if ((t == DT_body) || (t == DT_bodydiv)) {
884             writeStartTag(DT_bodydiv);
885             divNestingLevel++;
886             inStartElement = true;
887         }
888         if (!attr.isEmpty()) {
889             if (attr.contains('=')) {
890                 int index = 0;
891                 int from = 0;
892                 QString values;
893                 while (index >= 0) {
894                     index = attr.indexOf('"',from);
895                     if (index >= 0) {
896                         ++index;
897                         from = index;
898                         index = attr.indexOf('"',from);
899                         if (index > from) {
900                             if (!values.isEmpty())
901                                 values.append(' ');
902                             values += attr.mid(from,index-from);
903                             from = index+1;
904                         }
905                     }
906                 }
907                 attr = values;
908             }
909         }
910         if (inStartElement)
911             xmlWriter().writeAttribute("outputclass", attr);
912     }
913         break;
914     case Atom::DivRight:
915         if ((currentTag() == DT_sectiondiv) || (currentTag() == DT_bodydiv)) {
916             writeEndTag(); // </sectiondiv>, </bodydiv>, or </p>
917             if (divNestingLevel > 0)
918                 --divNestingLevel;
919         }
920         break;
921     case Atom::FootnoteLeft:
922         // ### For now
923         if (in_para) {
924             writeEndTag(); // </p>
925             in_para = false;
926         }
927         xmlWriter().writeCharacters("<!-- ");
928         break;
929     case Atom::FootnoteRight:
930         // ### For now
931         xmlWriter().writeCharacters("-->");
932         break;
933     case Atom::FormatElse:
934     case Atom::FormatEndif:
935     case Atom::FormatIf:
936         break;
937     case Atom::FormattingLeft:
938         {
939             DitaTag t = DT_LAST;
940             if (atom->string() == ATOM_FORMATTING_BOLD)
941                 t = DT_b;
942             else if (atom->string() == ATOM_FORMATTING_PARAMETER)
943                 t = DT_i;
944             else if (atom->string() == ATOM_FORMATTING_ITALIC)
945                 t = DT_i;
946             else if (atom->string() == ATOM_FORMATTING_TELETYPE)
947                 t = DT_tt;
948             else if (atom->string().startsWith("span ")) {
949                 t = DT_keyword;
950             }
951             else if (atom->string() == ATOM_FORMATTING_UICONTROL)
952                 t = DT_uicontrol;
953             else if (atom->string() == ATOM_FORMATTING_UNDERLINE)
954                 t = DT_u;
955             else if (atom->string() == ATOM_FORMATTING_INDEX)
956                 t = DT_comment;
957             else if (atom->string() == ATOM_FORMATTING_SUBSCRIPT)
958                 t = DT_sub;
959             else if (atom->string() == ATOM_FORMATTING_SUPERSCRIPT)
960                 t = DT_sup;
961             else
962                 qDebug() << "DT_LAST";
963             writeStartTag(t);
964             if (atom->string() == ATOM_FORMATTING_PARAMETER) {
965                 if (atom->next() != 0 && atom->next()->type() == Atom::String) {
966                     QRegExp subscriptRegExp("([a-z]+)_([0-9n])");
967                     if (subscriptRegExp.exactMatch(atom->next()->string())) {
968                         xmlWriter().writeCharacters(subscriptRegExp.cap(1));
969                         writeStartTag(DT_sub);
970                         xmlWriter().writeCharacters(subscriptRegExp.cap(2));
971                         writeEndTag(); // </sub>
972                         skipAhead = 1;
973                     }
974                 }
975             }
976             else if (t == DT_keyword) {
977                 QString attr = atom->string().mid(5);
978                 if (!attr.isEmpty()) {
979                     if (attr.contains('=')) {
980                         int index = 0;
981                         int from = 0;
982                         QString values;
983                         while (index >= 0) {
984                             index = attr.indexOf('"',from);
985                             if (index >= 0) {
986                                 ++index;
987                                 from = index;
988                                 index = attr.indexOf('"',from);
989                                 if (index > from) {
990                                     if (!values.isEmpty())
991                                         values.append(' ');
992                                     values += attr.mid(from,index-from);
993                                     from = index+1;
994                                 }
995                             }
996                         }
997                         attr = values;
998                     }
999                 }
1000                 xmlWriter().writeAttribute("outputclass", attr);
1001             }
1002         }
1003         break;
1004     case Atom::FormattingRight:
1005         if (atom->string() == ATOM_FORMATTING_LINK) {
1006             endLink();
1007         }
1008         else {
1009             writeEndTag(); // ?
1010         }
1011         break;
1012     case Atom::AnnotatedList:
1013         {
1014             QList<Node*> values = tree_->groups().values(atom->string());
1015             NodeMap nodeMap;
1016             for (int i = 0; i < values.size(); ++i) {
1017                 const Node* n = values.at(i);
1018                 if ((n->status() != Node::Internal) && (n->access() != Node::Private)) {
1019                     nodeMap.insert(n->nameForLists(),n);
1020                 }
1021             }
1022             generateAnnotatedList(relative, marker, nodeMap);
1023         }
1024         break;
1025     case Atom::GeneratedList:
1026         if (atom->string() == "annotatedclasses") {
1027             generateAnnotatedList(relative, marker, nonCompatClasses);
1028         }
1029         else if (atom->string() == "classes") {
1030             generateCompactList(relative, marker, nonCompatClasses, true);
1031         }
1032         else if (atom->string() == "qmlclasses") {
1033             generateCompactList(relative, marker, qmlClasses, true);
1034         }
1035         else if (atom->string().contains("classesbymodule")) {
1036             QString arg = atom->string().trimmed();
1037             QString moduleName = atom->string().mid(atom->string().indexOf(
1038                                                         "classesbymodule") + 15).trimmed();
1039             if (moduleClassMap.contains(moduleName))
1040                 generateAnnotatedList(relative, marker, moduleClassMap[moduleName]);
1041         }
1042         else if (atom->string().contains("classesbyedition")) {
1043
1044             QString arg = atom->string().trimmed();
1045             QString editionName = atom->string().mid(atom->string().indexOf(
1046                                                          "classesbyedition") + 16).trimmed();
1047
1048             if (editionModuleMap.contains(editionName)) {
1049
1050                 // Add all classes in the modules listed for that edition.
1051                 NodeMap editionClasses;
1052                 foreach (const QString &moduleName, editionModuleMap[editionName]) {
1053                     if (moduleClassMap.contains(moduleName))
1054                         editionClasses.unite(moduleClassMap[moduleName]);
1055                 }
1056
1057                 // Add additional groups and remove groups of classes that
1058                 // should be excluded from the edition.
1059
1060                 QMultiMap <QString, Node *> groups = tree_->groups();
1061                 foreach (const QString &groupName, editionGroupMap[editionName]) {
1062                     QList<Node *> groupClasses;
1063                     if (groupName.startsWith(QLatin1Char('-'))) {
1064                         groupClasses = groups.values(groupName.mid(1));
1065                         foreach (const Node *node, groupClasses)
1066                             editionClasses.remove(node->name());
1067                     }
1068                     else {
1069                         groupClasses = groups.values(groupName);
1070                         foreach (const Node *node, groupClasses)
1071                             editionClasses.insert(node->name(), node);
1072                     }
1073                 }
1074                 generateAnnotatedList(relative, marker, editionClasses);
1075             }
1076         }
1077         else if (atom->string() == "classhierarchy") {
1078             generateClassHierarchy(relative, marker, nonCompatClasses);
1079         }
1080         else if (atom->string() == "compatclasses") {
1081             generateCompactList(relative, marker, compatClasses, false);
1082         }
1083         else if (atom->string() == "obsoleteclasses") {
1084             generateCompactList(relative, marker, obsoleteClasses, false);
1085         }
1086         else if (atom->string() == "functionindex") {
1087             generateFunctionIndex(relative, marker);
1088         }
1089         else if (atom->string() == "legalese") {
1090             generateLegaleseList(relative, marker);
1091         }
1092         else if (atom->string() == "mainclasses") {
1093             generateCompactList(relative, marker, mainClasses, true);
1094         }
1095         else if (atom->string() == "services") {
1096             generateCompactList(relative, marker, serviceClasses, false);
1097         }
1098         else if (atom->string() == "overviews") {
1099             generateOverviewList(relative, marker);
1100         }
1101         else if (atom->string() == "namespaces") {
1102             generateAnnotatedList(relative, marker, namespaceIndex);
1103         }
1104         else if (atom->string() == "related") {
1105             const FakeNode *fake = static_cast<const FakeNode *>(relative);
1106             if (fake && !fake->groupMembers().isEmpty()) {
1107                 NodeMap groupMembersMap;
1108                 foreach (const Node *node, fake->groupMembers()) {
1109                     if (node->type() == Node::Fake)
1110                         groupMembersMap[fullName(node, relative, marker)] = node;
1111                 }
1112                 generateAnnotatedList(fake, marker, groupMembersMap);
1113             }
1114         }
1115         break;
1116     case Atom::SinceList:
1117     {
1118         NewSinceMaps::const_iterator nsmap;
1119         nsmap = newSinceMaps.constFind(atom->string());
1120         NewClassMaps::const_iterator ncmap;
1121         ncmap = newClassMaps.constFind(atom->string());
1122         NewClassMaps::const_iterator nqcmap;
1123         nqcmap = newQmlClassMaps.constFind(atom->string());
1124         if ((nsmap != newSinceMaps.constEnd()) && !nsmap.value().isEmpty()) {
1125             QList<Section> sections;
1126             QList<Section>::ConstIterator s;
1127             for (int i=0; i<LastSinceType; ++i)
1128                 sections.append(Section(sinceTitle(i),QString(),QString(),QString()));
1129
1130             NodeMultiMap::const_iterator n = nsmap.value().constBegin();
1131             while (n != nsmap.value().constEnd()) {
1132                 const Node* node = n.value();
1133                 switch (node->type()) {
1134                 case Node::Fake:
1135                     if (node->subType() == Node::QmlClass) {
1136                         sections[QmlClass].appendMember((Node*)node);
1137                     }
1138                     break;
1139                 case Node::Namespace:
1140                     sections[Namespace].appendMember((Node*)node);
1141                     break;
1142                 case Node::Class:
1143                     sections[Class].appendMember((Node*)node);
1144                     break;
1145                 case Node::Enum:
1146                     sections[Enum].appendMember((Node*)node);
1147                     break;
1148                 case Node::Typedef:
1149                     sections[Typedef].appendMember((Node*)node);
1150                     break;
1151                 case Node::Function: {
1152                     const FunctionNode* fn = static_cast<const FunctionNode*>(node);
1153                     if (fn->isMacro())
1154                         sections[Macro].appendMember((Node*)node);
1155                     else {
1156                         Node* p = fn->parent();
1157                         if (p) {
1158                             if (p->type() == Node::Class)
1159                                 sections[MemberFunction].appendMember((Node*)node);
1160                             else if (p->type() == Node::Namespace) {
1161                                 if (p->name().isEmpty())
1162                                     sections[GlobalFunction].appendMember((Node*)node);
1163                                 else
1164                                     sections[NamespaceFunction].appendMember((Node*)node);
1165                             }
1166                             else
1167                                 sections[GlobalFunction].appendMember((Node*)node);
1168                         }
1169                         else
1170                             sections[GlobalFunction].appendMember((Node*)node);
1171                     }
1172                     break;
1173                 }
1174                 case Node::Property:
1175                     sections[Property].appendMember((Node*)node);
1176                     break;
1177                 case Node::Variable:
1178                     sections[Variable].appendMember((Node*)node);
1179                     break;
1180                 case Node::QmlProperty:
1181                     sections[QmlProperty].appendMember((Node*)node);
1182                     break;
1183                 case Node::QmlSignal:
1184                     sections[QmlSignal].appendMember((Node*)node);
1185                     break;
1186                 case Node::QmlSignalHandler:
1187                     sections[QmlSignalHandler].appendMember((Node*)node);
1188                     break;
1189                 case Node::QmlMethod:
1190                     sections[QmlMethod].appendMember((Node*)node);
1191                     break;
1192                 default:
1193                     break;
1194                 }
1195                 ++n;
1196             }
1197
1198             /*
1199                   First generate the table of contents.
1200                  */
1201             writeStartTag(DT_ul);
1202             s = sections.constBegin();
1203             while (s != sections.constEnd()) {
1204                 if (!(*s).members.isEmpty()) {
1205                     QString li = outFileName() + QLatin1Char('#') + Doc::canonicalTitle((*s).name);
1206                     writeXrefListItem(li, (*s).name);
1207                 }
1208                 ++s;
1209             }
1210             writeEndTag(); // </ul>
1211
1212             int idx = 0;
1213             s = sections.constBegin();
1214             while (s != sections.constEnd()) {
1215                 if (!(*s).members.isEmpty()) {
1216                     writeStartTag(DT_p);
1217                     writeGuidAttribute(Doc::canonicalTitle((*s).name));
1218                     xmlWriter().writeAttribute("outputclass","h3");
1219                     writeCharacters(protectEnc((*s).name));
1220                     writeEndTag(); // </p>
1221                     if (idx == Class)
1222                         generateCompactList(0, marker, ncmap.value(), false, QString("Q"));
1223                     else if (idx == QmlClass)
1224                         generateCompactList(0, marker, nqcmap.value(), false, QString("Q"));
1225                     else if (idx == MemberFunction) {
1226                         ParentMaps parentmaps;
1227                         ParentMaps::iterator pmap;
1228                         NodeList::const_iterator i = s->members.constBegin();
1229                         while (i != s->members.constEnd()) {
1230                             Node* p = (*i)->parent();
1231                             pmap = parentmaps.find(p);
1232                             if (pmap == parentmaps.end())
1233                                 pmap = parentmaps.insert(p,NodeMultiMap());
1234                             pmap->insert((*i)->name(),(*i));
1235                             ++i;
1236                         }
1237                         pmap = parentmaps.begin();
1238                         while (pmap != parentmaps.end()) {
1239                             NodeList nlist = pmap->values();
1240                             writeStartTag(DT_p);
1241                             xmlWriter().writeCharacters("Class ");
1242                             writeStartTag(DT_xref);
1243                             // formathtml
1244                             xmlWriter().writeAttribute("href",linkForNode(pmap.key(), 0));
1245                             QStringList pieces = fullName(pmap.key(), 0, marker).split("::");
1246                             writeCharacters(protectEnc(pieces.last()));
1247                             writeEndTag(); // </xref>
1248                             xmlWriter().writeCharacters(":");
1249                             writeEndTag(); // </p>
1250
1251                             generateSection(nlist, 0, marker, CodeMarker::Summary);
1252                             ++pmap;
1253                         }
1254                     }
1255                     else {
1256                         generateSection(s->members, 0, marker, CodeMarker::Summary);
1257                     }
1258                 }
1259                 ++idx;
1260                 ++s;
1261             }
1262         }
1263     }
1264         break;
1265     case Atom::BR:
1266         // DITA XML can't do <br>
1267         break;
1268     case Atom::HR: //<p outputclass="horizontal-rule" />
1269         writeStartTag(DT_p);
1270         xmlWriter().writeAttribute("outputclass","horizontal-rule");
1271         writeEndTag(); // </p>
1272         break;
1273     case Atom::Image:
1274     case Atom::InlineImage:
1275     {
1276         QString fileName = imageFileName(relative, atom->string());
1277         QString text;
1278         if (atom->next() != 0)
1279             text = atom->next()->string();
1280         if (fileName.isEmpty()) {
1281             relative->location().warning(tr("Missing image: %1").arg(protectEnc(atom->string())));
1282             QString images = "images";
1283             if (!baseDir().isEmpty())
1284                 images.prepend("../");
1285             if (!atom->string().isEmpty() && atom->string()[0] != '/')
1286                 images.append(QLatin1Char('/'));
1287             fileName = images + atom->string();
1288         }
1289         if (relative && (relative->type() == Node::Fake) &&
1290                 (relative->subType() == Node::Example)) {
1291             const ExampleNode* cen = static_cast<const ExampleNode*>(relative);
1292             if (cen->imageFileName().isEmpty()) {
1293                 ExampleNode* en = const_cast<ExampleNode*>(cen);
1294                 en->setImageFileName(fileName);
1295             }
1296         }
1297
1298         if (currentTag() != DT_xref && atom->type() != Atom::InlineImage)
1299             writeStartTag(DT_fig);
1300         writeStartTag(DT_image);
1301         writeHrefAttribute(protectEnc(fileName));
1302         if (atom->type() == Atom::Image) {
1303             xmlWriter().writeAttribute("placement","break");
1304             xmlWriter().writeAttribute("align","center");
1305         }
1306         if (!text.isEmpty()) {
1307             writeStartTag(DT_alt);
1308             writeCharacters(protectEnc(text));
1309             writeEndTag(); // </alt>
1310         }
1311         writeEndTag(); // </image>
1312         if (currentTag() != DT_xref && atom->type() != Atom::InlineImage)
1313             writeEndTag(); // </fig>
1314     }
1315         break;
1316     case Atom::ImageText:
1317         // nothing
1318         break;
1319     case Atom::ImportantLeft:
1320         writeStartTag(DT_note);
1321         xmlWriter().writeAttribute("type","important");
1322         break;
1323     case Atom::ImportantRight:
1324         writeEndTag(); // </note>
1325         break;
1326     case Atom::NoteLeft:
1327         writeStartTag(DT_note);
1328         xmlWriter().writeAttribute("type","note");
1329         break;
1330     case Atom::NoteRight:
1331         writeEndTag(); // </note>
1332         break;
1333     case Atom::LegaleseLeft:
1334         inLegaleseText = true;
1335         break;
1336     case Atom::LegaleseRight:
1337         inLegaleseText = false;
1338         break;
1339     case Atom::LineBreak:
1340         //xmlWriter().writeEmptyElement("br");
1341         break;
1342     case Atom::Link:
1343         {
1344             const Node *node = 0;
1345             QString myLink = getLink(atom, relative, marker, &node);
1346             if (myLink.isEmpty())
1347                 myLink = getCollisionLink(atom);
1348             if (myLink.isEmpty())
1349                 relative->doc().location().warning(tr("Can't link to '%1'").arg(atom->string()));
1350             else if (!inSectionHeading)
1351                 beginLink(myLink);
1352             skipAhead = 1;
1353         }
1354         break;
1355     case Atom::GuidLink:
1356         {
1357             beginLink(atom->string());
1358             skipAhead = 1;
1359         }
1360         break;
1361     case Atom::LinkNode:
1362         {
1363             const Node* node = CodeMarker::nodeForString(atom->string());
1364             beginLink(linkForNode(node, relative));
1365             skipAhead = 1;
1366         }
1367         break;
1368     case Atom::ListLeft:
1369         if (in_para) {
1370             writeEndTag(); // </p>
1371             in_para = false;
1372         }
1373         if (atom->string() == ATOM_LIST_BULLET) {
1374             writeStartTag(DT_ul);
1375         }
1376         else if (atom->string() == ATOM_LIST_TAG) {
1377             writeStartTag(DT_dl);
1378         }
1379         else if (atom->string() == ATOM_LIST_VALUE) {
1380             threeColumnEnumValueTable = isThreeColumnEnumValueTable(atom);
1381             if (threeColumnEnumValueTable) {
1382                 writeStartTag(DT_simpletable);
1383                 xmlWriter().writeAttribute("outputclass","valuelist");
1384                 writeStartTag(DT_sthead);
1385                 writeStartTag(DT_stentry);
1386                 xmlWriter().writeCharacters("Constant");
1387                 writeEndTag(); // </stentry>
1388                 writeStartTag(DT_stentry);
1389                 xmlWriter().writeCharacters("Value");
1390                 writeEndTag(); // </stentry>
1391                 writeStartTag(DT_stentry);
1392                 xmlWriter().writeCharacters("Description");
1393                 writeEndTag(); // </stentry>
1394                 writeEndTag(); // </sthead>
1395             }
1396             else {
1397                 writeStartTag(DT_simpletable);
1398                 xmlWriter().writeAttribute("outputclass","valuelist");
1399                 writeStartTag(DT_sthead);
1400                 writeStartTag(DT_stentry);
1401                 xmlWriter().writeCharacters("Constant");
1402                 writeEndTag(); // </stentry>
1403                 writeStartTag(DT_stentry);
1404                 xmlWriter().writeCharacters("Value");
1405                 writeEndTag(); // </stentry>
1406                 writeEndTag(); // </sthead>
1407             }
1408         }
1409         else {
1410             writeStartTag(DT_ol);
1411             if (atom->string() == ATOM_LIST_UPPERALPHA)
1412                 xmlWriter().writeAttribute("outputclass","upperalpha");
1413             else if (atom->string() == ATOM_LIST_LOWERALPHA)
1414                 xmlWriter().writeAttribute("outputclass","loweralpha");
1415             else if (atom->string() == ATOM_LIST_UPPERROMAN)
1416                 xmlWriter().writeAttribute("outputclass","upperroman");
1417             else if (atom->string() == ATOM_LIST_LOWERROMAN)
1418                 xmlWriter().writeAttribute("outputclass","lowerroman");
1419             else // (atom->string() == ATOM_LIST_NUMERIC)
1420                 xmlWriter().writeAttribute("outputclass","numeric");
1421             if (atom->next() != 0 && atom->next()->string().toInt() != 1) {
1422                 /*
1423                   This attribute is not supported in DITA, and at the
1424                   moment, including it is causing a validation error
1425                   wherever it is used. I think it is only used in the
1426                   qdoc manual.
1427                  */
1428                 //xmlWriter().writeAttribute("start",atom->next()->string());
1429             }
1430         }
1431         break;
1432     case Atom::ListItemNumber:
1433         // nothing
1434         break;
1435     case Atom::ListTagLeft:
1436         if (atom->string() == ATOM_LIST_TAG) {
1437             writeStartTag(DT_dt);
1438         }
1439         else { // (atom->string() == ATOM_LIST_VALUE)
1440             writeStartTag(DT_strow);
1441             writeStartTag(DT_stentry);
1442             writeStartTag(DT_tt);
1443             writeCharacters(protectEnc(plainCode(marker->markedUpEnumValue(atom->next()->string(),
1444                                                                            relative))));
1445             writeEndTag(); // </tt>
1446             writeEndTag(); // </stentry>
1447             writeStartTag(DT_stentry);
1448
1449             QString itemValue;
1450             if (relative->type() == Node::Enum) {
1451                 const EnumNode *enume = static_cast<const EnumNode *>(relative);
1452                 itemValue = enume->itemValue(atom->next()->string());
1453             }
1454
1455             if (itemValue.isEmpty())
1456                 xmlWriter().writeCharacters("?");
1457             else {
1458                 writeStartTag(DT_tt);
1459                 writeCharacters(protectEnc(itemValue));
1460                 writeEndTag(); // </tt>
1461             }
1462             skipAhead = 1;
1463         }
1464         break;
1465     case Atom::ListTagRight:
1466         if (atom->string() == ATOM_LIST_TAG)
1467             writeEndTag(); // </dt>
1468         break;
1469     case Atom::ListItemLeft:
1470         if (atom->string() == ATOM_LIST_TAG) {
1471             writeStartTag(DT_dd);
1472         }
1473         else if (atom->string() == ATOM_LIST_VALUE) {
1474             if (threeColumnEnumValueTable) {
1475                 writeEndTag(); // </stentry>
1476                 writeStartTag(DT_stentry);
1477             }
1478         }
1479         else {
1480             writeStartTag(DT_li);
1481         }
1482         if (matchAhead(atom, Atom::ParaLeft))
1483             skipAhead = 1;
1484         break;
1485     case Atom::ListItemRight:
1486         if (atom->string() == ATOM_LIST_TAG) {
1487             writeEndTag(); // </dd>
1488         }
1489         else if (atom->string() == ATOM_LIST_VALUE) {
1490             writeEndTag(); // </stentry>
1491             writeEndTag(); // </strow>
1492         }
1493         else {
1494             writeEndTag(); // </li>
1495         }
1496         break;
1497     case Atom::ListRight:
1498         if (atom->string() == ATOM_LIST_BULLET) {
1499             writeEndTag(); // </ul>
1500         }
1501         else if (atom->string() == ATOM_LIST_TAG) {
1502             writeEndTag(); // </dl>
1503         }
1504         else if (atom->string() == ATOM_LIST_VALUE) {
1505             writeEndTag(); // </simpletable>
1506         }
1507         else {
1508             writeEndTag(); // </ol>
1509         }
1510         break;
1511     case Atom::Nop:
1512         // nothing
1513         break;
1514     case Atom::ParaLeft:
1515         writeStartTag(DT_p);
1516         if (inLegaleseText)
1517             xmlWriter().writeAttribute("outputclass","legalese");
1518         in_para = true;
1519         break;
1520     case Atom::ParaRight:
1521         endLink();
1522         if (in_para) {
1523             writeEndTag(); // </p>
1524             in_para = false;
1525         }
1526         break;
1527     case Atom::QuotationLeft:
1528         writeStartTag(DT_lq);
1529         break;
1530     case Atom::QuotationRight:
1531         writeEndTag(); // </lq>
1532         break;
1533     case Atom::RawString:
1534         if (atom->string() == " ")
1535             break;
1536         if (atom->string().startsWith(QLatin1Char('&')))
1537             writeCharacters(atom->string());
1538         else if (atom->string() == "<sup>*</sup>") {
1539             writeStartTag(DT_sup);
1540             writeCharacters("*");
1541             writeEndTag(); // </sup>
1542         }
1543         else if (atom->string() == "<sup>&reg;</sup>") {
1544             writeStartTag(DT_tm);
1545             xmlWriter().writeAttribute("tmtype","reg");
1546             writeEndTag(); // </tm>
1547         }
1548         else {
1549             writeStartTag(DT_pre);
1550             xmlWriter().writeAttribute("outputclass","raw-html");
1551             writeCharacters(atom->string());
1552             writeEndTag(); // </pre>
1553         }
1554         break;
1555     case Atom::SectionLeft:
1556         enterSection("details",QString());
1557         break;
1558     case Atom::SectionRight:
1559         leaveSection();
1560         break;
1561     case Atom::SectionHeadingLeft:
1562     {
1563         writeStartTag(DT_p);
1564         QString id = Text::sectionHeading(atom).toString();
1565         id = stripMarkup(id);
1566         id = Doc::canonicalTitle(id);
1567         writeGuidAttribute(id);
1568         hx = QLatin1Char('h') + QString::number(atom->string().toInt() + hOffset(relative));
1569         xmlWriter().writeAttribute("outputclass",hx);
1570         inSectionHeading = true;
1571     }
1572         break;
1573     case Atom::SectionHeadingRight:
1574         writeEndTag(); // </title> (see case Atom::SectionHeadingLeft)
1575         inSectionHeading = false;
1576         break;
1577     case Atom::SidebarLeft:
1578         // nothing
1579         break;
1580     case Atom::SidebarRight:
1581         // nothing
1582         break;
1583     case Atom::String:
1584         if (inLink && !inContents && !inSectionHeading) {
1585             generateLink(atom, relative, marker);
1586         }
1587         else {
1588             writeCharacters(atom->string());
1589         }
1590         break;
1591     case Atom::TableLeft:
1592     {
1593         QString attr;
1594         if ((atom->count() > 0) && (atom->string(0) == "borderless"))
1595             attr = "borderless";
1596         else if ((atom->count() > 1) && (atom->string(1) == "borderless"))
1597             attr = "borderless";
1598         if (in_para) {
1599             writeEndTag(); // </p>
1600             in_para = false;
1601         }
1602         writeStartTag(DT_table);
1603         if (!attr.isEmpty())
1604             xmlWriter().writeAttribute("outputclass",attr);
1605         numTableRows = 0;
1606         if (tableColumnCount != 0) {
1607             qDebug() << "ERROR: Nested tables!";
1608             tableColumnCount = 0;
1609         }
1610         tableColumnCount = countTableColumns(atom->next());
1611         writeStartTag(DT_tgroup);
1612         xmlWriter().writeAttribute("cols",QString::number(tableColumnCount));
1613         for (int i = 0; i < tableColumnCount; i++) {
1614             writeStartTag(DT_colspec);
1615             xmlWriter().writeAttribute("colname", QStringLiteral("col%1").arg(i));
1616             xmlWriter().writeAttribute("colnum", QString::number(i));
1617             xmlWriter().writeAttribute("colwidth", QStringLiteral("1*"));
1618             writeEndTag(); // DT_colspec
1619         }
1620         inTableHeader = false;
1621         inTableBody = false;
1622     }
1623         break;
1624     case Atom::TableRight:
1625         writeEndTag(); // </tbody>
1626         writeEndTag(); // </tgroup>
1627         writeEndTag(); // </table>
1628         inTableHeader = false;
1629         inTableBody = false;
1630         tableColumnCount = 0;
1631         currentColumn = 0;
1632         break;
1633     case Atom::TableHeaderLeft:
1634         if (inTableBody) {
1635             writeEndTag(); // </tbody>
1636             writeEndTag(); // </tgroup>
1637             writeEndTag(); // </table>
1638             inTableHeader = false;
1639             inTableBody = false;
1640             tableColumnCount = 0;
1641             writeStartTag(DT_table);
1642             numTableRows = 0;
1643             tableColumnCount = countTableColumns(atom);
1644             writeStartTag(DT_tgroup);
1645             xmlWriter().writeAttribute("cols",QString::number(tableColumnCount));
1646         }
1647         currentColumn = 0;
1648         writeStartTag(DT_thead);
1649         xmlWriter().writeAttribute("valign","top");
1650         writeStartTag(DT_row);
1651         xmlWriter().writeAttribute("valign","top");
1652         inTableHeader = true;
1653         inTableBody = false;
1654         break;
1655     case Atom::TableHeaderRight:
1656         writeEndTag(); // </row>
1657         if (matchAhead(atom, Atom::TableHeaderLeft)) {
1658             skipAhead = 1;
1659             writeStartTag(DT_row);
1660             xmlWriter().writeAttribute("valign","top");
1661         }
1662         else {
1663             writeEndTag(); // </thead>
1664             inTableHeader = false;
1665             inTableBody = true;
1666             writeStartTag(DT_tbody);
1667         }
1668         break;
1669     case Atom::TableRowLeft:
1670         if (!inTableHeader && !inTableBody) {
1671             inTableBody = true;
1672             writeStartTag(DT_tbody);
1673         }
1674         currentColumn = 0;
1675         writeStartTag(DT_row);
1676         attr = atom->string();
1677         if (!attr.isEmpty()) {
1678             if (attr.contains('=')) {
1679                 int index = 0;
1680                 int from = 0;
1681                 QString values;
1682                 while (index >= 0) {
1683                     index = attr.indexOf('"',from);
1684                     if (index >= 0) {
1685                         ++index;
1686                         from = index;
1687                         index = attr.indexOf('"',from);
1688                         if (index > from) {
1689                             if (!values.isEmpty())
1690                                 values.append(' ');
1691                             values += attr.mid(from,index-from);
1692                             from = index+1;
1693                         }
1694                     }
1695                 }
1696                 attr = values;
1697             }
1698             xmlWriter().writeAttribute("outputclass", attr);
1699         }
1700         xmlWriter().writeAttribute("valign","top");
1701         break;
1702     case Atom::TableRowRight:
1703         writeEndTag(); // </row>
1704         break;
1705     case Atom::TableItemLeft:
1706         {
1707             QString values;
1708             writeStartTag(DT_entry);
1709             for (int i=0; i<atom->count(); ++i) {
1710                 attr = atom->string(i);
1711                 if (attr.contains('=')) {
1712                     int index = 0;
1713                     int from = 0;
1714                     while (index >= 0) {
1715                         index = attr.indexOf('"',from);
1716                         if (index >= 0) {
1717                             ++index;
1718                             from = index;
1719                             index = attr.indexOf('"',from);
1720                             if (index > from) {
1721                                 if (!values.isEmpty())
1722                                     values.append(' ');
1723                                 values += attr.mid(from,index-from);
1724                                 from = index+1;
1725                             }
1726                         }
1727                     }
1728                 }
1729                 else {
1730                     QStringList spans = attr.split(QLatin1Char(','));
1731                     if (spans.size() == 2) {
1732                         if (spans[0].toInt()>1) {
1733                             xmlWriter().writeAttribute("namest",QStringLiteral("col%1").arg(currentColumn));
1734                             xmlWriter().writeAttribute("nameend",QStringLiteral("col%1")
1735                                                        .arg(currentColumn + (spans[0].toInt() - 1)));
1736                         }
1737                         if (spans[1].toInt()>1)
1738                             xmlWriter().writeAttribute("morerows",spans[1].simplified());
1739                         currentColumn += spans[0].toInt();
1740                     }
1741                 }
1742             }
1743             if (!values.isEmpty())
1744                 xmlWriter().writeAttribute("outputclass",values);
1745             if (matchAhead(atom, Atom::ParaLeft))
1746                 skipAhead = 1;
1747         }
1748         break;
1749     case Atom::TableItemRight:
1750         if (inTableHeader) {
1751             writeEndTag(); // </entry>
1752         }
1753         else {
1754             writeEndTag(); // </entry>
1755         }
1756         if (matchAhead(atom, Atom::ParaLeft))
1757             skipAhead = 1;
1758         break;
1759     case Atom::TableOfContents:
1760         {
1761             int numColumns = 1;
1762             const Node* node = relative;
1763
1764             Doc::Sections sectionUnit = Doc::Section4;
1765             QStringList params = atom->string().split(QLatin1Char(','));
1766             QString columnText = params.at(0);
1767             QStringList pieces = columnText.split(QLatin1Char(' '), QString::SkipEmptyParts);
1768             if (pieces.size() >= 2) {
1769                 columnText = pieces.at(0);
1770                 pieces.pop_front();
1771                 QString path = pieces.join(" ").trimmed();
1772                 node = findNodeForTarget(path, relative, marker, atom);
1773             }
1774
1775             if (params.size() == 2) {
1776                 numColumns = qMax(columnText.toInt(), numColumns);
1777                 sectionUnit = (Doc::Sections)params.at(1).toInt();
1778             }
1779
1780             if (node)
1781                 generateTableOfContents(node,
1782                                         marker,
1783                                         sectionUnit,
1784                                         numColumns,
1785                                         relative);
1786         }
1787         break;
1788     case Atom::Target:
1789         if (in_para) {
1790             writeEndTag(); // </p>
1791             in_para = false;
1792         }
1793         writeStartTag(DT_p);
1794         writeGuidAttribute(Doc::canonicalTitle(atom->string()));
1795         xmlWriter().writeAttribute("outputclass","target");
1796         //xmlWriter().writeCharacters(protectEnc(atom->string()));
1797         writeEndTag(); // </p>
1798         break;
1799     case Atom::UnhandledFormat:
1800         writeStartTag(DT_b);
1801         xmlWriter().writeAttribute("outputclass","error");
1802         xmlWriter().writeCharacters("<Missing DITAXML>");
1803         writeEndTag(); // </b>
1804         break;
1805     case Atom::UnknownCommand:
1806         writeStartTag(DT_b);
1807         xmlWriter().writeAttribute("outputclass","error unknown-command");
1808         writeCharacters(protectEnc(atom->string()));
1809         writeEndTag(); // </b>
1810         break;
1811     case Atom::QmlText:
1812     case Atom::EndQmlText:
1813         // don't do anything with these. They are just tags.
1814         break;
1815     default:
1816         //        unknownAtom(atom);
1817         break;
1818     }
1819     return skipAhead;
1820 }
1821
1822 /*!
1823   Generate a <cxxClass> element (and all the stuff inside it)
1824   for the C++ class represented by \a innerNode. \a marker is
1825   for marking up the code. I don't know what that means exactly.
1826  */
1827 void
1828 DitaXmlGenerator::generateClassLikeNode(InnerNode* inner, CodeMarker* marker)
1829 {
1830     QList<Section>::ConstIterator s;
1831
1832     QString title;
1833     QString rawTitle;
1834     QString fullTitle;
1835     if (inner->type() == Node::Namespace) {
1836         const NamespaceNode* nsn = const_cast<NamespaceNode*>(static_cast<const NamespaceNode*>(inner));
1837         rawTitle = marker->plainName(inner);
1838         fullTitle = marker->plainFullName(inner);
1839         title = rawTitle + " Namespace";
1840
1841         /*
1842           Note: Because the C++ specialization we are using
1843           has no <cxxNamespace> element, we are using the
1844           <cxxClass> element with an outputclass attribute
1845           set to "namespace" .
1846          */
1847         generateHeader(inner, fullTitle);
1848         generateBrief(inner, marker); // <shortdesc>
1849         writeProlog(inner);
1850
1851         writeStartTag(DT_cxxClassDetail);
1852         writeStartTag(DT_cxxClassDefinition);
1853         writeLocation(nsn);
1854         writeEndTag(); // <cxxClassDefinition>
1855
1856         enterDesc(DT_apiDesc,QString(),title);
1857         generateStatus(nsn, marker);
1858         generateThreadSafeness(nsn, marker);
1859         generateSince(nsn, marker);
1860
1861         enterSection(QString(), QString());
1862         generateBody(nsn, marker);
1863         generateAlsoList(nsn, marker);
1864         leaveSection();
1865         leaveSection(); // </apiDesc>
1866
1867         bool needOtherSection = false;
1868         QList<Section> summarySections;
1869         summarySections = marker->sections(inner, CodeMarker::Summary, CodeMarker::Okay);
1870         if (!summarySections.isEmpty()) {
1871             enterSection("redundant",QString());
1872             s = summarySections.constBegin();
1873             while (s != summarySections.constEnd()) {
1874                 if (s->members.isEmpty() && s->reimpMembers.isEmpty()) {
1875                     if (!s->inherited.isEmpty())
1876                         needOtherSection = true;
1877                 }
1878                 else {
1879                     QString attr;
1880                     if (!s->members.isEmpty()) {
1881                         writeStartTag(DT_p);
1882                         attr  = cleanRef((*s).name).toLower() + " h2";
1883                         xmlWriter().writeAttribute("outputclass",attr);
1884                         writeCharacters(protectEnc((*s).name));
1885                         writeEndTag(); // </title>
1886                         generateSection(s->members, inner, marker, CodeMarker::Summary);
1887                         generateSectionInheritedList(*s, inner, marker);
1888                     }
1889                     if (!s->reimpMembers.isEmpty()) {
1890                         QString name = QString("Reimplemented ") + (*s).name;
1891                         attr = cleanRef(name).toLower() + " h2";
1892                         writeStartTag(DT_p);
1893                         xmlWriter().writeAttribute("outputclass",attr);
1894                         writeCharacters(protectEnc(name));
1895                         writeEndTag(); // </title>
1896                         generateSection(s->reimpMembers, inner, marker, CodeMarker::Summary);
1897                         generateSectionInheritedList(*s, inner, marker);
1898                     }
1899                 }
1900                 ++s;
1901             }
1902             if (needOtherSection) {
1903                 writeStartTag(DT_p);
1904                 xmlWriter().writeAttribute("outputclass","h3");
1905                 xmlWriter().writeCharacters("Additional Inherited Members");
1906                 writeEndTag(); // </title>
1907                 s = summarySections.constBegin();
1908                 while (s != summarySections.constEnd()) {
1909                     if (s->members.isEmpty())
1910                         generateSectionInheritedList(*s, inner, marker);
1911                     ++s;
1912                 }
1913             }
1914             leaveSection();
1915         }
1916
1917         writeEndTag(); // </cxxClassDetail>
1918
1919         // not included: <related-links>
1920         // not included: <cxxClassNested>
1921
1922         QList<Section> detailSections;
1923         detailSections = marker->sections(inner, CodeMarker::Detailed, CodeMarker::Okay);
1924         s = detailSections.constBegin();
1925         while (s != detailSections.constEnd()) {
1926             if ((*s).name == "Classes") {
1927                 writeNestedClasses((*s),nsn);
1928                 break;
1929             }
1930             ++s;
1931         }
1932
1933         s = detailSections.constBegin();
1934         while (s != detailSections.constEnd()) {
1935             if ((*s).name == "Function Documentation") {
1936                 writeFunctions((*s),nsn,marker);
1937             }
1938             else if ((*s).name == "Type Documentation") {
1939                 writeEnumerations((*s),marker);
1940                 writeTypedefs((*s),marker);
1941             }
1942             else if ((*s).name == "Namespaces") {
1943                 qDebug() << "Nested namespaces" << outFileName();
1944             }
1945             else if ((*s).name == "Macro Documentation") {
1946                 //writeMacros((*s),marker);
1947             }
1948             ++s;
1949         }
1950
1951         generateLowStatusMembers(inner,marker,CodeMarker::Obsolete);
1952         generateLowStatusMembers(inner,marker,CodeMarker::Compat);
1953         writeEndTag(); // </cxxClass>
1954     }
1955     else if (inner->type() == Node::Class) {
1956         const ClassNode* cn = const_cast<ClassNode*>(static_cast<const ClassNode*>(inner));
1957         rawTitle = marker->plainName(inner);
1958         fullTitle = marker->plainFullName(inner);
1959         title = rawTitle + " Class";
1960
1961         generateHeader(inner, fullTitle);
1962         generateBrief(inner, marker); // <shortdesc>
1963         writeProlog(inner);
1964
1965         writeStartTag(DT_cxxClassDetail);
1966         writeStartTag(DT_cxxClassDefinition);
1967         writeStartTag(DT_cxxClassAccessSpecifier);
1968         xmlWriter().writeAttribute("value",inner->accessString());
1969         writeEndTag(); // <cxxClassAccessSpecifier>
1970         if (cn->isAbstract()) {
1971             writeStartTag(DT_cxxClassAbstract);
1972             xmlWriter().writeAttribute("name","abstract");
1973             xmlWriter().writeAttribute("value","abstract");
1974             writeEndTag(); // </cxxClassAbstract>
1975         }
1976         writeDerivations(cn, marker); // <cxxClassDerivations>
1977
1978         // not included: <cxxClassTemplateParameters>
1979
1980         writeLocation(cn);
1981         writeEndTag(); // <cxxClassDefinition>
1982
1983         enterDesc(DT_apiDesc,QString(),title);
1984         generateStatus(cn, marker);
1985         generateInherits(cn, marker);
1986         generateInheritedBy(cn, marker);
1987         generateThreadSafeness(cn, marker);
1988         generateSince(cn, marker);
1989         enterSection(QString(), QString());
1990         generateBody(cn, marker);
1991         generateAlsoList(cn, marker);
1992         leaveSection();
1993         leaveSection(); // </apiDesc>
1994
1995         bool needOtherSection = false;
1996         QList<Section> summarySections;
1997         summarySections = marker->sections(inner, CodeMarker::Summary, CodeMarker::Okay);
1998         if (!summarySections.isEmpty()) {
1999             enterSection("redundant",QString());
2000             s = summarySections.constBegin();
2001             while (s != summarySections.constEnd()) {
2002                 if (s->members.isEmpty() && s->reimpMembers.isEmpty()) {
2003                     if (!s->inherited.isEmpty())
2004                         needOtherSection = true;
2005                 }
2006                 else {
2007                     QString attr;
2008                     if (!s->members.isEmpty()) {
2009                         writeStartTag(DT_p);
2010                         attr = cleanRef((*s).name).toLower() + " h2";
2011                         xmlWriter().writeAttribute("outputclass",attr);
2012                         writeCharacters(protectEnc((*s).name));
2013                         writeEndTag(); // </p>
2014                         generateSection(s->members, inner, marker, CodeMarker::Summary);
2015                         generateSectionInheritedList(*s, inner, marker);
2016                     }
2017                     if (!s->reimpMembers.isEmpty()) {
2018                         QString name = QString("Reimplemented ") + (*s).name;
2019                         attr = cleanRef(name).toLower() + " h2";
2020                         writeStartTag(DT_p);
2021                         xmlWriter().writeAttribute("outputclass",attr);
2022                         writeCharacters(protectEnc(name));
2023                         writeEndTag(); // </p>
2024                         generateSection(s->reimpMembers, inner, marker, CodeMarker::Summary);
2025                         generateSectionInheritedList(*s, inner, marker);
2026                     }
2027                 }
2028                 ++s;
2029             }
2030             if (needOtherSection) {
2031                 writeStartTag(DT_p);
2032                 xmlWriter().writeAttribute("outputclass","h3");
2033                 xmlWriter().writeCharacters("Additional Inherited Members");
2034                 writeEndTag(); // </p>
2035                 s = summarySections.constBegin();
2036                 while (s != summarySections.constEnd()) {
2037                     if (s->members.isEmpty())
2038                         generateSectionInheritedList(*s, inner, marker);
2039                     ++s;
2040                 }
2041             }
2042             leaveSection();
2043         }
2044
2045         // not included: <example> or <apiImpl>
2046
2047         writeEndTag(); // </cxxClassDetail>
2048
2049         // not included: <related-links>
2050         // not included: <cxxClassNested>
2051
2052         QList<Section> detailSections;
2053         detailSections = marker->sections(inner, CodeMarker::Detailed, CodeMarker::Okay);
2054         s = detailSections.constBegin();
2055         while (s != detailSections.constEnd()) {
2056             if ((*s).name == "Member Function Documentation") {
2057                 writeFunctions((*s),cn,marker);
2058             }
2059             else if ((*s).name == "Member Type Documentation") {
2060                 writeEnumerations((*s),marker);
2061                 writeTypedefs((*s),marker);
2062             }
2063             else if ((*s).name == "Member Variable Documentation") {
2064                 writeDataMembers((*s),marker);
2065             }
2066             else if ((*s).name == "Property Documentation") {
2067                 writeProperties((*s),marker);
2068             }
2069             else if ((*s).name == "Macro Documentation") {
2070                 //writeMacros((*s),marker);
2071             }
2072             else if ((*s).name == "Related Non-Members") {
2073                 QString attribute("related-non-member");
2074                 writeFunctions((*s),cn,marker,attribute);
2075             }
2076             ++s;
2077         }
2078
2079         generateLowStatusMembers(inner,marker,CodeMarker::Obsolete);
2080         generateLowStatusMembers(inner,marker,CodeMarker::Compat);
2081         writeEndTag(); // </cxxClass>
2082     }
2083     else if ((inner->type() == Node::Fake) && (inner->subType() == Node::HeaderFile)) {
2084         const FakeNode* fn = const_cast<FakeNode*>(static_cast<const FakeNode*>(inner));
2085         rawTitle = marker->plainName(inner);
2086         fullTitle = marker->plainFullName(inner);
2087         title = rawTitle;
2088
2089         /*
2090           Note: Because the C++ specialization we are using
2091           has no <cxxHeaderFile> element, we are using the
2092           <cxxClass> element with an outputclass attribute
2093           set to "headerfile" .
2094          */
2095         generateHeader(inner, fullTitle);
2096         generateBrief(inner, marker); // <shortdesc>
2097         writeProlog(inner);
2098
2099         writeStartTag(DT_cxxClassDetail);
2100         enterDesc(DT_apiDesc,QString(),title);
2101         generateStatus(fn, marker);
2102         generateThreadSafeness(fn, marker);
2103         generateSince(fn, marker);
2104         generateSince(fn, marker);
2105         enterSection(QString(), QString());
2106         generateBody(fn, marker);
2107         generateAlsoList(fn, marker);
2108         leaveSection();
2109         leaveSection(); // </apiDesc>
2110
2111         bool needOtherSection = false;
2112         QList<Section> summarySections;
2113         summarySections = marker->sections(inner, CodeMarker::Summary, CodeMarker::Okay);
2114         if (!summarySections.isEmpty()) {
2115             enterSection("redundant",QString());
2116             s = summarySections.constBegin();
2117             while (s != summarySections.constEnd()) {
2118                 if (s->members.isEmpty() && s->reimpMembers.isEmpty()) {
2119                     if (!s->inherited.isEmpty())
2120                         needOtherSection = true;
2121                 }
2122                 else {
2123                     QString attr;
2124                     if (!s->members.isEmpty()) {
2125                         writeStartTag(DT_p);
2126                         attr = cleanRef((*s).name).toLower() + " h2";
2127                         xmlWriter().writeAttribute("outputclass",attr);
2128                         writeCharacters(protectEnc((*s).name));
2129                         writeEndTag(); // </p>
2130                         generateSection(s->members, inner, marker, CodeMarker::Summary);
2131                         generateSectionInheritedList(*s, inner, marker);
2132                     }
2133                     if (!s->reimpMembers.isEmpty()) {
2134                         QString name = QString("Reimplemented ") + (*s).name;
2135                         attr = cleanRef(name).toLower() + " h2";
2136                         writeStartTag(DT_p);
2137                         xmlWriter().writeAttribute("outputclass",attr);
2138                         writeCharacters(protectEnc(name));
2139                         writeEndTag(); // </p>
2140                         generateSection(s->reimpMembers, inner, marker, CodeMarker::Summary);
2141                         generateSectionInheritedList(*s, inner, marker);
2142                     }
2143                 }
2144                 ++s;
2145             }
2146             if (needOtherSection) {
2147                 enterSection("additional-inherited-members redundant",QString());
2148                 writeStartTag(DT_p);
2149                 xmlWriter().writeAttribute("outputclass","h3");
2150                 xmlWriter().writeCharacters("Additional Inherited Members");
2151                 writeEndTag(); // </p>
2152                 s = summarySections.constBegin();
2153                 while (s != summarySections.constEnd()) {
2154                     if (s->members.isEmpty())
2155                         generateSectionInheritedList(*s, inner, marker);
2156                     ++s;
2157                 }
2158             }
2159             leaveSection();
2160         }
2161
2162         writeEndTag(); // </cxxClassDetail>
2163
2164         // not included: <related-links>
2165         // not included: <cxxClassNested>
2166
2167         QList<Section> detailSections;
2168         detailSections = marker->sections(inner, CodeMarker::Detailed, CodeMarker::Okay);
2169         s = detailSections.constBegin();
2170         while (s != detailSections.constEnd()) {
2171             if ((*s).name == "Classes") {
2172                 writeNestedClasses((*s),fn);
2173                 break;
2174             }
2175             ++s;
2176         }
2177
2178         s = detailSections.constBegin();
2179         while (s != detailSections.constEnd()) {
2180             if ((*s).name == "Function Documentation") {
2181                 writeFunctions((*s),fn,marker);
2182             }
2183             else if ((*s).name == "Type Documentation") {
2184                 writeEnumerations((*s),marker);
2185                 writeTypedefs((*s),marker);
2186             }
2187             else if ((*s).name == "Namespaces") {
2188                 qDebug() << "Nested namespaces" << outFileName();
2189             }
2190             else if ((*s).name == "Macro Documentation") {
2191                 //writeMacros((*s),marker);
2192             }
2193             ++s;
2194         }
2195         generateLowStatusMembers(inner,marker,CodeMarker::Obsolete);
2196         generateLowStatusMembers(inner,marker,CodeMarker::Compat);
2197         writeEndTag(); // </cxxClass>
2198     }
2199     else if ((inner->type() == Node::Fake) && (inner->subType() == Node::QmlClass)) {
2200         QmlClassNode* qcn = const_cast<QmlClassNode*>(static_cast<const QmlClassNode*>(inner));
2201         ClassNode* cn = qcn->classNode();
2202         rawTitle = marker->plainName(inner);
2203         fullTitle = marker->plainFullName(inner);
2204         title = rawTitle + " Type";
2205         Node::clearPropertyGroupCount();
2206
2207         generateHeader(inner, fullTitle);
2208         generateBrief(inner, marker); // <shortdesc>
2209         writeProlog(inner);
2210
2211         writeStartTag(DT_qmlTypeDetail);
2212         generateQmlModuleDef(qcn);
2213         generateQmlInherits(qcn,marker);
2214         generateQmlInheritedBy(qcn, marker);
2215         generateQmlInstantiates(qcn,marker);
2216         generateQmlSince(qcn);
2217
2218         enterDesc(DT_apiDesc,QString(),title);
2219         enterSection(QString(), QString());
2220         generateBody(qcn, marker);
2221         if (cn) {
2222             generateQmlText(cn->doc().body(), cn, marker, qcn->name());
2223             generateAlsoList(cn, marker);
2224         }
2225         leaveSection();
2226         leaveSection(); // </apiDesc>
2227         writeEndTag(); // </qmlTypeDetail>
2228
2229         QList<Section> members = marker->qmlSections(qcn,CodeMarker::Detailed);
2230         if (!members.isEmpty()) {
2231             s = members.constBegin();
2232             while (s != members.constEnd()) {
2233                 if (!s->members.isEmpty()) {
2234                     NodeList::ConstIterator m = (*s).members.constBegin();
2235                     while (m != (*s).members.constEnd()) {
2236                         generateDetailedQmlMember(*m, qcn, marker);
2237                         ++m;
2238                     }
2239                 }
2240                 ++s;
2241             }
2242         }
2243         writeEndTag(); // </apiRef>
2244     }
2245 }
2246
2247 /*!
2248   Write a list item for a \a link with the given \a text.
2249  */
2250 void DitaXmlGenerator::writeXrefListItem(const QString& link, const QString& text)
2251 {
2252     writeStartTag(DT_li);
2253     writeStartTag(DT_xref);
2254     // formathtml
2255     writeHrefAttribute(link);
2256     writeCharacters(text);
2257     writeEndTag(); // </xref>
2258     writeEndTag(); // </li>
2259 }
2260
2261 /*!
2262   Generate the DITA page for a qdoc file that doesn't map
2263   to an underlying c++ file.
2264  */
2265 void DitaXmlGenerator::generateFakeNode(FakeNode* fake, CodeMarker* marker)
2266 {
2267     /*
2268       If the fake node is a page node, and if the page type
2269       is DITA map page, write the node's contents as a dita
2270       map and return without doing anything else.
2271      */
2272     if (fake->subType() == Node::Page && fake->pageType() == Node::DitaMapPage) {
2273         const DitaMapNode* dmn = static_cast<const DitaMapNode*>(fake);
2274         writeDitaMap(dmn);
2275         return;
2276     }
2277
2278     QList<Section> sections;
2279     QList<Section>::const_iterator s;
2280     QString fullTitle = fake->fullTitle();
2281
2282     if (fake->subType() == Node::QmlBasicType) {
2283         fullTitle = "QML Basic Type: " + fullTitle;
2284     }
2285     else if (fake->subType() == Node::Collision) {
2286         fullTitle = "Name Collision: " + fullTitle;
2287     }
2288
2289     generateHeader(fake, fullTitle);
2290     generateBrief(fake, marker); // <shortdesc>
2291     writeProlog(fake);
2292
2293     writeStartTag(DT_body);
2294     enterSection(QString(), QString());
2295     if (fake->subType() == Node::Module) {
2296         generateStatus(fake, marker);
2297         if (moduleNamespaceMap.contains(fake->name())) {
2298             enterSection("h2","Namespaces");
2299             generateAnnotatedList(fake, marker, moduleNamespaceMap[fake->name()]);
2300             leaveSection();
2301         }
2302         if (moduleClassMap.contains(fake->name())) {
2303             enterSection("h2","Classes");
2304             generateAnnotatedList(fake, marker, moduleClassMap[fake->name()]);
2305             leaveSection();
2306         }
2307     }
2308
2309     if (fake->doc().isEmpty()) {
2310         if (fake->subType() == Node::File) {
2311             Text text;
2312             Quoter quoter;
2313             writeStartTag(DT_p);
2314             xmlWriter().writeAttribute("outputclass", "small-subtitle");
2315             text << fake->subTitle();
2316             generateText(text, fake, marker);
2317             writeEndTag(); // </p>
2318             Doc::quoteFromFile(fake->doc().location(), quoter, fake->name());
2319             QString code = quoter.quoteTo(fake->location(), "", "");
2320             text.clear();
2321             text << Atom(Atom::Code, code);
2322             generateText(text, fake, marker);
2323         }
2324     }
2325     else {
2326         if (fake->subType() == Node::Module) {
2327             enterSection(QString(), QString());
2328             generateBody(fake, marker);
2329             leaveSection();
2330         }
2331         else {
2332             generateBody(fake, marker);
2333         }
2334         generateAlsoList(fake, marker);
2335
2336         if ((fake->subType() == Node::QmlModule) && !fake->qmlModuleMembers().isEmpty()) {
2337             NodeMap qmlModuleMembersMap;
2338             foreach (const Node* node, fake->qmlModuleMembers()) {
2339                 if (node->type() == Node::Fake && node->subType() == Node::QmlClass)
2340                     qmlModuleMembersMap[node->name()] = node;
2341             }
2342             generateAnnotatedList(fake, marker, qmlModuleMembersMap);
2343         }
2344         else if (!fake->groupMembers().isEmpty()) {
2345             NodeMap groupMembersMap;
2346             foreach (const Node *node, fake->groupMembers()) {
2347                 if (node->type() == Node::Class || node->type() == Node::Namespace)
2348                     groupMembersMap[node->name()] = node;
2349             }
2350             generateAnnotatedList(fake, marker, groupMembersMap);
2351         }
2352     }
2353     leaveSection(); // </section>
2354     if (!writeEndTag()) { // </body>
2355         fake->doc().location().warning(tr("Pop of empty XML tag stack; generating DITA for '%1'").arg(fake->name()));
2356         return;
2357     }
2358     writeRelatedLinks(fake, marker);
2359     writeEndTag(); // </topic>
2360 }
2361
2362 /*!
2363   This function writes a \e{<link>} element inside a
2364   \e{<related-links>} element.
2365
2366   \sa writeRelatedLinks()
2367  */
2368 void DitaXmlGenerator::writeLink(const Node* node,
2369                                  const QString& text,
2370                                  const QString& role)
2371 {
2372     if (node) {
2373         QString link = fileName(node) + QLatin1Char('#') + node->guid();
2374         if (link.endsWith(QLatin1Char('#')))
2375             qDebug() << "LINK ENDS WITH #:" << link << outFileName();
2376         writeStartTag(DT_link);
2377         writeHrefAttribute(link);
2378         xmlWriter().writeAttribute("role", role);
2379         writeStartTag(DT_linktext);
2380         writeCharacters(text);
2381         writeEndTag(); // </linktext>
2382         writeEndTag(); // </link>
2383     }
2384 }
2385
2386 /*!
2387   This function writes a \e{<related-links>} element, which
2388   contains the \c{next}, \c{previous}, and \c{start}
2389   links for topic pages that have them. Note that the
2390   value of the \e role attribute is \c{parent} for the
2391   \c{start} link.
2392  */
2393 void DitaXmlGenerator::writeRelatedLinks(const FakeNode* node, CodeMarker* marker)
2394 {
2395     const Node* linkNode = 0;
2396     QPair<QString,QString> linkPair;
2397     if (node && !node->links().empty()) {
2398         writeStartTag(DT_relatedLinks);
2399         if (node->links().contains(Node::PreviousLink)) {
2400             linkPair = node->links()[Node::PreviousLink];
2401             linkNode = findNodeForTarget(linkPair.first, node, marker);
2402             if (linkNode && linkNode->type() == Node::Fake) {
2403                 const FakeNode *fakeNode = static_cast<const FakeNode*>(linkNode);
2404                 linkPair.second = fakeNode->title();
2405             }
2406             writeLink(linkNode, linkPair.second, "previous");
2407         }
2408         if (node->links().contains(Node::NextLink)) {
2409             linkPair = node->links()[Node::NextLink];
2410             linkNode = findNodeForTarget(linkPair.first, node, marker);
2411             if (linkNode && linkNode->type() == Node::Fake) {
2412                 const FakeNode *fakeNode = static_cast<const FakeNode*>(linkNode);
2413                 linkPair.second = fakeNode->title();
2414             }
2415             writeLink(linkNode, linkPair.second, "next");
2416         }
2417         if (node->links().contains(Node::StartLink)) {
2418             linkPair = node->links()[Node::StartLink];
2419             linkNode = findNodeForTarget(linkPair.first, node, marker);
2420             if (linkNode && linkNode->type() == Node::Fake) {
2421                 const FakeNode *fakeNode = static_cast<const FakeNode*>(linkNode);
2422                 linkPair.second = fakeNode->title();
2423             }
2424             writeLink(linkNode, linkPair.second, "parent");
2425         }
2426         writeEndTag(); // </related-links>
2427     }
2428 }
2429
2430 /*!
2431   Returns "dita" for this subclass of class Generator.
2432  */
2433 QString DitaXmlGenerator::fileExtension() const
2434 {
2435     return "dita";
2436 }
2437
2438 /*!
2439   Writes an XML file header to the current XML stream. This
2440   depends on which kind of DITA XML file is being generated,
2441   which is determined by the \a node type and subtype and the
2442   \a subpage flag. If the \subpage flag is true, a \c{<topic>}
2443   header is written, regardless of the type of \a node.
2444  */
2445 void DitaXmlGenerator::generateHeader(const Node* node,
2446                                       const QString& name,
2447                                       bool subpage)
2448 {
2449     if (!node)
2450         return;
2451
2452     DitaTag mainTag = DT_cxxClass;
2453     DitaTag nameTag = DT_apiName;
2454     QString doctype;
2455     QString dtd;
2456     QString base;
2457     QString version;
2458     QString outputclass;
2459
2460     if (node->type() == Node::Class) {
2461         mainTag = DT_cxxClass;
2462         nameTag = DT_apiName;
2463         dtd = "dtd/cxxClass.dtd";
2464         version = "0.7.0";
2465         doctype = "<!DOCTYPE " + ditaTags[mainTag] +
2466                 " PUBLIC \"-//NOKIA//DTD DITA C++ API Class Reference Type v" +
2467                 version + "//EN\" \"" + dtd + "\">";
2468     }
2469     else if (node->type() == Node::Namespace) {
2470         mainTag = DT_cxxClass;
2471         nameTag = DT_apiName;
2472         dtd = "dtd/cxxClass.dtd";
2473         version = "0.7.0";
2474         doctype = "<!DOCTYPE " + ditaTags[mainTag] +
2475                 " PUBLIC \"-//NOKIA//DTD DITA C++ API Class Reference Type v" +
2476                 version + "//EN\" \"" + dtd + "\">";
2477         outputclass = "namespace";
2478     }
2479     else if (node->type() == Node::Fake || subpage) {
2480         if (node->subType() == Node::HeaderFile) {
2481             mainTag = DT_cxxClass;
2482             nameTag = DT_apiName;
2483             dtd = "dtd/cxxClass.dtd";
2484             version = "0.7.0";
2485             doctype = "<!DOCTYPE " + ditaTags[mainTag] +
2486                     " PUBLIC \"-//NOKIA//DTD DITA C++ API Class Reference Type v" +
2487                     version + "//EN\" \"" + dtd + "\">";
2488             outputclass = "headerfile";
2489         }
2490         else if (node->subType() == Node::QmlClass) {
2491             mainTag = DT_qmlType;
2492             nameTag = DT_apiName;
2493             dtd = "dtd/qmlType.dtd";
2494             version = "0.1.0";
2495             doctype = "<!DOCTYPE " + ditaTags[mainTag] +
2496                     " PUBLIC \"-//NOKIA//DTD DITA QML Type" +
2497                     "//EN\" \"" + dtd + "\">";
2498             outputclass = "QML-type";
2499         }
2500         else {
2501             mainTag = DT_topic;
2502             nameTag = DT_title;
2503             dtd = "dtd/topic.dtd";
2504             doctype = "<!DOCTYPE " + ditaTags[mainTag] +
2505                     " PUBLIC \"-//OASIS//DTD DITA Topic//EN\" \"" + dtd + "\">";
2506             switch (node->subType()) {
2507             case Node::Page:
2508                 outputclass = node->pageTypeString();
2509                 break;
2510             case Node::Group:
2511                 outputclass = "group";
2512                 break;
2513             case Node::Example:
2514                 outputclass = "example";
2515                 break;
2516             case Node::File:
2517                 outputclass = "file";
2518                 break;
2519             case Node::Image:  // not used
2520                 outputclass = "image";
2521                 break;
2522             case Node::Module:
2523                 outputclass = "module";
2524                 break;
2525             case Node::ExternalPage: // not used
2526                 outputclass = "externalpage";
2527                 break;
2528             case Node::Collision:
2529                 outputclass = "collision";
2530                 break;
2531             default:
2532                 outputclass = "page";
2533             }
2534         }
2535     }
2536
2537     xmlWriter().writeDTD(doctype);
2538     xmlWriter().writeComment(node->doc().location().fileName());
2539     writeStartTag(mainTag);
2540     QString id = node->guid();
2541     xmlWriter().writeAttribute("id",id);
2542     if (!outputclass.isEmpty())
2543         xmlWriter().writeAttribute("outputclass",outputclass);
2544     writeStartTag(nameTag); // <title> or <apiName>
2545     if (!name.isEmpty())
2546         writeCharacters(name);
2547     else
2548         writeCharacters(node->name());
2549     writeEndTag(); // </title> or </apiName>
2550 }
2551
2552 /*!
2553   Outputs the \e brief command as a <shortdesc> element.
2554  */
2555 void DitaXmlGenerator::generateBrief(const Node* node, CodeMarker* marker)
2556 {
2557     Text brief = node->doc().briefText(true); // zzz
2558     if (!brief.isEmpty()) {
2559         generateText(brief, node, marker);
2560     }
2561 }
2562
2563 /*!
2564   zzz
2565   Generates a table of contents beginning at \a node.
2566   Currently just returns without writing anything.
2567  */
2568 void DitaXmlGenerator::generateTableOfContents(const Node* node,
2569                                                CodeMarker* marker,
2570                                                Doc::Sections sectionUnit,
2571                                                int numColumns,
2572                                                const Node* relative)
2573
2574 {
2575     return;
2576     if (!node->doc().hasTableOfContents())
2577         return;
2578     QList<Atom *> toc = node->doc().tableOfContents();
2579     if (toc.isEmpty())
2580         return;
2581
2582     QString nodeName = "";
2583     if (node != relative)
2584         nodeName = node->name();
2585
2586     QStringList sectionNumber;
2587     int columnSize = 0;
2588
2589     QString tdTag;
2590     if (numColumns > 1) {
2591         tdTag = "<td>"; /* width=\"" + QString::number((100 + numColumns - 1) / numColumns) + "%\">";*/
2592         out() << "<table class=\"toc\">\n<tr class=\"topAlign\">"
2593               << tdTag << '\n';
2594     }
2595
2596     // disable nested links in table of contents
2597     inContents = true;
2598     inLink = true;
2599
2600     for (int i = 0; i < toc.size(); ++i) {
2601         Atom *atom = toc.at(i);
2602
2603         int nextLevel = atom->string().toInt();
2604         if (nextLevel > (int)sectionUnit)
2605             continue;
2606
2607         if (sectionNumber.size() < nextLevel) {
2608             do {
2609                 out() << "<ul>";
2610                 sectionNumber.append("1");
2611             } while (sectionNumber.size() < nextLevel);
2612         }
2613         else {
2614             while (sectionNumber.size() > nextLevel) {
2615                 out() << "</ul>\n";
2616                 sectionNumber.removeLast();
2617             }
2618             sectionNumber.last() = QString::number(sectionNumber.last().toInt() + 1);
2619         }
2620         int numAtoms;
2621         Text headingText = Text::sectionHeading(atom);
2622
2623         if (sectionNumber.size() == 1 && columnSize > toc.size() / numColumns) {
2624             out() << "</ul></td>" << tdTag << "<ul>\n";
2625             columnSize = 0;
2626         }
2627         out() << "<li>";
2628         out() << "<xref href=\""
2629               << nodeName
2630               << '#'
2631               << Doc::canonicalTitle(headingText.toString())
2632               << "\">";
2633         generateAtomList(headingText.firstAtom(), node, marker, true, numAtoms);
2634         out() << "</xref></li>\n";
2635
2636         ++columnSize;
2637     }
2638     while (!sectionNumber.isEmpty()) {
2639         out() << "</ul>\n";
2640         sectionNumber.removeLast();
2641     }
2642
2643     if (numColumns > 1)
2644         out() << "</td></tr></table>\n";
2645
2646     inContents = false;
2647     inLink = false;
2648 }
2649
2650 /*!
2651   zzz
2652   Revised for the new doc format.
2653   Generates a table of contents beginning at \a node.
2654  */
2655 void DitaXmlGenerator::generateTableOfContents(const Node* node,
2656                                                CodeMarker* marker,
2657                                                QList<Section>* sections)
2658 {
2659     QList<Atom*> toc;
2660     if (node->doc().hasTableOfContents())
2661         toc = node->doc().tableOfContents();
2662     if (toc.isEmpty() && !sections && (node->subType() != Node::Module))
2663         return;
2664
2665     QStringList sectionNumber;
2666     int detailsBase = 0;
2667
2668     // disable nested links in table of contents
2669     inContents = true;
2670     inLink = true;
2671
2672     out() << "<div class=\"toc\">\n";
2673     out() << "<h3>Contents</h3>\n";
2674     sectionNumber.append("1");
2675     out() << "<ul>\n";
2676
2677     if (node->subType() == Node::Module) {
2678         if (moduleNamespaceMap.contains(node->name())) {
2679             out() << "<li class=\"level"
2680                   << sectionNumber.size()
2681                   << "\"><xref href=\"#"
2682                   << registerRef("namespaces")
2683                   << "\">Namespaces</xref></li>\n";
2684         }
2685         if (moduleClassMap.contains(node->name())) {
2686             out() << "<li class=\"level"
2687                   << sectionNumber.size()
2688                   << "\"><xref href=\"#"
2689                   << registerRef("classes")
2690                   << "\">Classes</xref></li>\n";
2691         }
2692         out() << "<li class=\"level"
2693               << sectionNumber.size()
2694               << "\"><xref href=\"#"
2695               << registerRef("details")
2696               << "\">Detailed Description</xref></li>\n";
2697         for (int i = 0; i < toc.size(); ++i) {
2698             if (toc.at(i)->string().toInt() == 1) {
2699                 detailsBase = 1;
2700                 break;
2701             }
2702         }
2703     }
2704     else if (sections && (node->type() == Node::Class)) {
2705         QList<Section>::ConstIterator s = sections->constBegin();
2706         while (s != sections->constEnd()) {
2707             if (!s->members.isEmpty() || !s->reimpMembers.isEmpty()) {
2708                 out() << "<li class=\"level"
2709                       << sectionNumber.size()
2710                       << "\"><xref href=\"#"
2711                       << registerRef((*s).pluralMember)
2712                       << "\">" << (*s).name
2713                       << "</xref></li>\n";
2714             }
2715             ++s;
2716         }
2717         out() << "<li class=\"level"
2718               << sectionNumber.size()
2719               << "\"><xref href=\"#"
2720               << registerRef("details")
2721               << "\">Detailed Description</xref></li>\n";
2722         for (int i = 0; i < toc.size(); ++i) {
2723             if (toc.at(i)->string().toInt() == 1) {
2724                 detailsBase = 1;
2725                 break;
2726             }
2727         }
2728     }
2729
2730     for (int i = 0; i < toc.size(); ++i) {
2731         Atom *atom = toc.at(i);
2732         int nextLevel = atom->string().toInt() + detailsBase;
2733         if (sectionNumber.size() < nextLevel) {
2734             do {
2735                 sectionNumber.append("1");
2736             } while (sectionNumber.size() < nextLevel);
2737         }
2738         else {
2739             while (sectionNumber.size() > nextLevel) {
2740                 sectionNumber.removeLast();
2741             }
2742             sectionNumber.last() = QString::number(sectionNumber.last().toInt() + 1);
2743         }
2744         int numAtoms;
2745         Text headingText = Text::sectionHeading(atom);
2746         QString s = headingText.toString();
2747         out() << "<li class=\"level"
2748               << sectionNumber.size()
2749               << "\">";
2750         out() << "<xref href=\""
2751               << '#'
2752               << Doc::canonicalTitle(s)
2753               << "\">";
2754         generateAtomList(headingText.firstAtom(), node, marker, true, numAtoms);
2755         out() << "</xref></li>\n";
2756     }
2757     while (!sectionNumber.isEmpty()) {
2758         sectionNumber.removeLast();
2759     }
2760     out() << "</ul>\n";
2761     out() << "</div>\n";
2762     inContents = false;
2763     inLink = false;
2764 }
2765
2766 void DitaXmlGenerator::generateLowStatusMembers(const InnerNode* inner,
2767                                                 CodeMarker* marker,
2768                                                 CodeMarker::Status status)
2769 {
2770     QString attribute;
2771     if (status == CodeMarker::Compat)
2772         attribute = "Qt3-support";
2773     else if (status == CodeMarker::Obsolete)
2774         attribute = "obsolete";
2775     else
2776         return;
2777
2778     QList<Section> sections = marker->sections(inner, CodeMarker::Detailed, status);
2779     QMutableListIterator<Section> j(sections);
2780     while (j.hasNext()) {
2781         if (j.next().members.size() == 0)
2782             j.remove();
2783     }
2784     if (sections.isEmpty())
2785         return;
2786
2787     QList<Section>::ConstIterator s = sections.constBegin();
2788     while (s != sections.constEnd()) {
2789         if ((*s).name == "Member Function Documentation") {
2790             writeFunctions((*s),inner,marker,attribute);
2791         }
2792         else if ((*s).name == "Member Type Documentation") {
2793             writeEnumerations((*s),marker,attribute);
2794             writeTypedefs((*s),marker,attribute);
2795         }
2796         else if ((*s).name == "Member Variable Documentation") {
2797             writeDataMembers((*s),marker,attribute);
2798         }
2799         else if ((*s).name == "Property Documentation") {
2800             writeProperties((*s),marker,attribute);
2801         }
2802         else if ((*s).name == "Macro Documentation") {
2803             //writeMacros((*s),marker,attribute);
2804         }
2805         ++s;
2806     }
2807 }
2808
2809 /*!
2810   Write the XML for the class hierarchy to the current XML stream.
2811  */
2812 void DitaXmlGenerator::generateClassHierarchy(const Node* relative,
2813                                               CodeMarker* marker,
2814                                               const QMap<QString,const Node*>& classMap)
2815 {
2816     if (classMap.isEmpty())
2817         return;
2818
2819     NodeMap topLevel;
2820     NodeMap::ConstIterator c = classMap.constBegin();
2821     while (c != classMap.constEnd()) {
2822         const ClassNode* classe = static_cast<const ClassNode*>(*c);
2823         if (classe->baseClasses().isEmpty())
2824             topLevel.insert(classe->name(), classe);
2825         ++c;
2826     }
2827
2828     QStack<NodeMap > stack;
2829     stack.push(topLevel);
2830
2831     writeStartTag(DT_ul);
2832     while (!stack.isEmpty()) {
2833         if (stack.top().isEmpty()) {
2834             stack.pop();
2835             writeEndTag(); // </ul>
2836             if (!stack.isEmpty())
2837                 writeEndTag(); // </li>
2838         }
2839         else {
2840             const ClassNode *child =
2841                     static_cast<const ClassNode *>(*stack.top().constBegin());
2842             writeStartTag(DT_li);
2843             generateFullName(child, relative, marker);
2844             writeEndTag(); // </li>
2845             stack.top().erase(stack.top().begin());
2846
2847             NodeMap newTop;
2848             foreach (const RelatedClass &d, child->derivedClasses()) {
2849                 if (d.access != Node::Private && !d.node->doc().isEmpty())
2850                     newTop.insert(d.node->name(), d.node);
2851             }
2852             if (!newTop.isEmpty()) {
2853                 stack.push(newTop);
2854                 writeStartTag(DT_li);
2855                 writeStartTag(DT_ul);
2856             }
2857         }
2858     }
2859 }
2860
2861 /*!
2862   Write XML for the contents of the \a nodeMap to the current
2863   XML stream.
2864  */
2865 void DitaXmlGenerator::generateAnnotatedList(const Node* relative,
2866                                              CodeMarker* marker,
2867                                              const NodeMap& nodeMap)
2868 {
2869     if (nodeMap.isEmpty())
2870         return;
2871     writeStartTag(DT_table);
2872     xmlWriter().writeAttribute("outputclass","annotated");
2873     writeStartTag(DT_tgroup);
2874     xmlWriter().writeAttribute("cols","2");
2875     writeStartTag(DT_tbody);
2876
2877     foreach (const QString& name, nodeMap.keys()) {
2878         const Node* node = nodeMap[name];
2879
2880         if (node->status() == Node::Obsolete)
2881             continue;
2882
2883         writeStartTag(DT_row);
2884         writeStartTag(DT_entry);
2885         writeStartTag(DT_p);
2886         generateFullName(node, relative, marker);
2887         writeEndTag(); // </p>
2888         writeEndTag(); // <entry>
2889
2890         if (!(node->type() == Node::Fake)) {
2891             Text brief = node->doc().trimmedBriefText(name);
2892             if (!brief.isEmpty()) {
2893                 writeStartTag(DT_entry);
2894                 writeStartTag(DT_p);
2895                 generateText(brief, node, marker);
2896                 writeEndTag(); // </p>
2897                 writeEndTag(); // <entry>
2898             }
2899         }
2900         else {
2901             writeStartTag(DT_entry);
2902             writeStartTag(DT_p);
2903             writeCharacters(protectEnc(node->doc().briefText().toString())); // zzz
2904             writeEndTag(); // </p>
2905             writeEndTag(); // <entry>
2906         }
2907         writeEndTag(); // </row>
2908     }
2909     writeEndTag(); // </tbody>
2910     writeEndTag(); // </tgroup>
2911     writeEndTag(); // </table>
2912 }
2913
2914 /*!
2915   This function finds the common prefix of the names of all
2916   the classes in \a classMap and then generates a compact
2917   list of the class names alphabetized on the part of the
2918   name not including the common prefix. You can tell the
2919   function to use \a comonPrefix as the common prefix, but
2920   normally you let it figure it out itself by looking at
2921   the name of the first and last classes in \a classMap.
2922  */
2923 void DitaXmlGenerator::generateCompactList(const Node* relative,
2924                                            CodeMarker* marker,
2925                                            const NodeMap& classMap,
2926                                            bool includeAlphabet,
2927                                            QString commonPrefix)
2928 {
2929     const int NumParagraphs = 37; // '0' to '9', 'A' to 'Z', '_'
2930
2931     if (classMap.isEmpty())
2932         return;
2933
2934     /*
2935       If commonPrefix is not empty, then the caller knows what
2936       the common prefix is and has passed it in, so just use that
2937       one. But if the commonPrefix is empty (it normally is), then
2938       compute a common prefix using this simple algorithm. Note we
2939       assume the prefix length is 1, i.e. we will have a single
2940       character as the common prefix.
2941      */
2942     int commonPrefixLen = commonPrefix.length();
2943     if (commonPrefixLen == 0) {
2944         QVector<int> count(26);
2945         for (int i=0; i<26; ++i)
2946             count[i] = 0;
2947
2948         NodeMap::const_iterator iter = classMap.constBegin();
2949         while (iter != classMap.constEnd()) {
2950             if (!iter.key().contains("::")) {
2951                 QChar c = iter.key()[0];
2952                 if ((c >= 'A') && (c <= 'Z')) {
2953                     int idx = c.unicode() - QChar('A').unicode();
2954                     ++count[idx];
2955                 }
2956             }
2957             ++iter;
2958         }
2959         int highest = 0;
2960         int idx = -1;
2961         for (int i=0; i<26; ++i) {
2962             if (count[i] > highest) {
2963                 highest = count[i];
2964                 idx = i;
2965             }
2966         }
2967         idx += QChar('A').unicode();
2968         QChar common(idx);
2969         commonPrefix = common;
2970         commonPrefixLen = 1;
2971     }
2972
2973     /*
2974       Divide the data into 37 paragraphs: 0, ..., 9, A, ..., Z,
2975       underscore (_). QAccel will fall in paragraph 10 (A) and
2976       QXtWidget in paragraph 33 (X). This is the only place where we
2977       assume that NumParagraphs is 37. Each paragraph is a NodeMap.
2978     */
2979     NodeMap paragraph[NumParagraphs+1];
2980     QString paragraphName[NumParagraphs+1];
2981     QSet<char> usedParagraphNames;
2982
2983     NodeMap::ConstIterator c = classMap.constBegin();
2984     while (c != classMap.constEnd()) {
2985         QStringList pieces = c.key().split("::");
2986         QString key;
2987         int idx = commonPrefixLen;
2988         if (!pieces.last().startsWith(commonPrefix))
2989             idx = 0;
2990         if (pieces.size() == 1)
2991             key = pieces.last().mid(idx).toLower();
2992         else
2993             key = pieces.last().toLower();
2994
2995         int paragraphNr = NumParagraphs - 1;
2996
2997         if (key[0].digitValue() != -1) {
2998             paragraphNr = key[0].digitValue();
2999         }
3000         else if (key[0] >= QLatin1Char('a') && key[0] <= QLatin1Char('z')) {
3001             paragraphNr = 10 + key[0].unicode() - 'a';
3002         }
3003
3004         paragraphName[paragraphNr] = key[0].toUpper();
3005         usedParagraphNames.insert(key[0].toLower().cell());
3006         paragraph[paragraphNr].insert(key, c.value());
3007         ++c;
3008     }
3009
3010     /*
3011       Each paragraph j has a size: paragraph[j].count(). In the
3012       discussion, we will assume paragraphs 0 to 5 will have sizes
3013       3, 1, 4, 1, 5, 9.
3014
3015       We now want to compute the paragraph offset. Paragraphs 0 to 6
3016       start at offsets 0, 3, 4, 8, 9, 14, 23.
3017     */
3018     int paragraphOffset[NumParagraphs + 1];     // 37 + 1
3019     paragraphOffset[0] = 0;
3020     for (int i=0; i<NumParagraphs; i++)         // i = 0..36
3021         paragraphOffset[i+1] = paragraphOffset[i] + paragraph[i].count();
3022
3023     int curParNr = 0;
3024     int curParOffset = 0;
3025     QMap<QChar,QString> cmap;
3026
3027     /*
3028       Output the alphabet as a row of links.
3029      */
3030     if (includeAlphabet) {
3031         writeStartTag(DT_p);
3032         xmlWriter().writeAttribute("outputclass","alphabet");
3033         for (int i = 0; i < 26; i++) {
3034             QChar ch('a' + i);
3035             if (usedParagraphNames.contains(char('a' + i))) {
3036                 writeStartTag(DT_xref);
3037                 // formathtml
3038                 QString guid = lookupGuid(outFileName(),QString(ch));
3039                 QString attr = outFileName() + QString("#%1").arg(guid);
3040                 xmlWriter().writeAttribute("href", attr);
3041                 xmlWriter().writeCharacters(QString(ch.toUpper()));
3042                 writeEndTag(); // </xref>
3043             }
3044         }
3045         writeEndTag(); // </p>
3046     }
3047
3048     /*
3049       Output a <p> element to contain all the <dl> elements.
3050      */
3051     writeStartTag(DT_p);
3052     xmlWriter().writeAttribute("outputclass","compactlist");
3053
3054     for (int i=0; i<classMap.count()-1; i++) {
3055         while ((curParNr < NumParagraphs) &&
3056                (curParOffset == paragraph[curParNr].count())) {
3057             ++curParNr;
3058             curParOffset = 0;
3059         }
3060
3061         /*
3062           Starting a new paragraph means starting a new <dl>.
3063         */
3064         if (curParOffset == 0) {
3065             if (i > 0) {
3066                 writeEndTag(); // </dlentry>
3067                 writeEndTag(); // </dl>
3068             }
3069             writeStartTag(DT_dl);
3070             writeStartTag(DT_dlentry);
3071             writeStartTag(DT_dt);
3072             if (includeAlphabet) {
3073                 QChar c = paragraphName[curParNr][0].toLower();
3074                 writeGuidAttribute(QString(c));
3075             }
3076             xmlWriter().writeAttribute("outputclass","sublist-header");
3077             xmlWriter().writeCharacters(paragraphName[curParNr]);
3078             writeEndTag(); // </dt>
3079         }
3080
3081         /*
3082           Output a <dd> for the current offset in the current paragraph.
3083          */
3084         writeStartTag(DT_dd);
3085         if ((curParNr < NumParagraphs) &&
3086                 !paragraphName[curParNr].isEmpty()) {
3087             NodeMap::Iterator it;
3088             it = paragraph[curParNr].begin();
3089             for (int i=0; i<curParOffset; i++)
3090                 ++it;
3091
3092             /*
3093               Previously, we used generateFullName() for this, but we
3094               require some special formatting.
3095             */
3096             writeStartTag(DT_xref);
3097             // formathtml
3098             writeHrefAttribute(linkForNode(it.value(), relative));
3099
3100             QStringList pieces;
3101             if (it.value()->subType() == Node::QmlClass)
3102                 pieces << it.value()->name();
3103             else
3104                 pieces = fullName(it.value(), relative, marker).split("::");
3105             xmlWriter().writeCharacters(protectEnc(pieces.last()));
3106             writeEndTag(); // </xref>
3107             if (pieces.size() > 1) {
3108                 xmlWriter().writeCharacters(" (");
3109                 generateFullName(it.value()->parent(),relative,marker);
3110                 xmlWriter().writeCharacters(")");
3111             }
3112         }
3113         writeEndTag(); // </dd>
3114         curParOffset++;
3115     }
3116     writeEndTag(); // </dlentry>
3117     writeEndTag(); // </dl>
3118     writeEndTag(); // </p>
3119 }
3120
3121 /*!
3122   Write XML for a function index to the current XML stream.
3123  */
3124 void DitaXmlGenerator::generateFunctionIndex(const Node* relative,
3125                                              CodeMarker* marker)
3126 {
3127     writeStartTag(DT_p);
3128     xmlWriter().writeAttribute("outputclass","alphabet");
3129     for (int i = 0; i < 26; i++) {
3130         QChar ch('a' + i);
3131         writeStartTag(DT_xref);
3132         // formathtml
3133         QString guid = lookupGuid(outFileName(),QString(ch));
3134         QString attr = outFileName() + QString("#%1").arg(guid);
3135         xmlWriter().writeAttribute("href", attr);
3136         xmlWriter().writeCharacters(QString(ch.toUpper()));
3137         writeEndTag(); // </xref>
3138
3139     }
3140     writeEndTag(); // </p>
3141
3142     char nextLetter = 'a';
3143     char currentLetter;
3144
3145     writeStartTag(DT_ul);
3146     QMap<QString, NodeMap >::ConstIterator f = funcIndex.constBegin();
3147     while (f != funcIndex.constEnd()) {
3148         writeStartTag(DT_li);
3149         currentLetter = f.key()[0].unicode();
3150         while (islower(currentLetter) && currentLetter >= nextLetter) {
3151             writeStartTag(DT_p);
3152             writeGuidAttribute(QString(nextLetter));
3153             xmlWriter().writeAttribute("outputclass","target");
3154             xmlWriter().writeCharacters(QString(nextLetter));
3155             writeEndTag(); // </p>
3156             nextLetter++;
3157         }
3158         xmlWriter().writeCharacters(protectEnc(f.key()));
3159         xmlWriter().writeCharacters(":");
3160
3161         NodeMap::ConstIterator s = (*f).constBegin();
3162         while (s != (*f).constEnd()) {
3163             generateFullName((*s)->parent(), relative, marker, *s);
3164             ++s;
3165         }
3166         writeEndTag(); // </li>
3167         ++f;
3168     }
3169     writeEndTag(); // </ul>
3170 }
3171
3172 /*!
3173   Write the legalese texts as XML to the current XML stream.
3174  */
3175 void DitaXmlGenerator::generateLegaleseList(const Node* relative,
3176                                             CodeMarker* marker)
3177 {
3178     QMap<Text, const Node*>::ConstIterator it = legaleseTexts.constBegin();
3179     while (it != legaleseTexts.constEnd()) {
3180         Text text = it.key();
3181         generateText(text, relative, marker);
3182         writeStartTag(DT_ul);
3183         do {
3184             writeStartTag(DT_li);
3185             generateFullName(it.value(), relative, marker);
3186             writeEndTag(); // </li>
3187             ++it;
3188         } while (it != legaleseTexts.constEnd() && it.key() == text);
3189         writeEndTag(); //</ul>
3190     }
3191 }
3192
3193 /*!
3194   Generate the text for the QML item described by \a node
3195   and write it to the current XML stream.
3196  */
3197 void DitaXmlGenerator::generateQmlItem(const Node* node,
3198                                        const Node* relative,
3199                                        CodeMarker* marker,
3200                                        bool summary)
3201 {
3202     QString marked = marker->markedUpQmlItem(node,summary);
3203     QRegExp tag("(<[^@>]*>)");
3204     if (marked.indexOf(tag) != -1) {
3205         QString tmp = protectEnc(marked.mid(tag.pos(1), tag.cap(1).length()));
3206         marked.replace(tag.pos(1), tag.cap(1).length(), tmp);
3207     }
3208     marked.replace(QRegExp("<@param>([a-z]+)_([1-9n])</@param>"),
3209                    "<i>\\1<sub>\\2</sub></i>");
3210     if (summary) {
3211         marked.remove("<@type>");
3212         marked.remove("</@type>");
3213     }
3214     writeText(marked, marker, relative);
3215 }
3216
3217 /*!
3218   Write the XML for the overview list to the current XML stream.
3219  */
3220 void DitaXmlGenerator::generateOverviewList(const Node* relative, CodeMarker* /* marker */)
3221 {
3222     QMap<const FakeNode*, QMap<QString, FakeNode*> > fakeNodeMap;
3223     QMap<QString, const FakeNode*> groupTitlesMap;
3224     QMap<QString, FakeNode*> uncategorizedNodeMap;
3225     QRegExp singleDigit("\\b([0-9])\\b");
3226
3227     const NodeList children = tree_->root()->childNodes();
3228     foreach (Node* child, children) {
3229         if (child->type() == Node::Fake && child != relative) {
3230             FakeNode* fakeNode = static_cast<FakeNode*>(child);
3231
3232             // Check whether the page is part of a group or is the group
3233             // definition page.
3234             QString group;
3235             bool isGroupPage = false;
3236             if (fakeNode->doc().metaCommandsUsed().contains("group")) {
3237                 group = fakeNode->doc().metaCommandArgs("group")[0].first;
3238                 isGroupPage = true;
3239             }
3240
3241             // there are too many examples; they would clutter the list
3242             if (fakeNode->subType() == Node::Example)
3243                 continue;
3244
3245             // not interested either in individual (Qt Designer etc.) manual chapters
3246             if (fakeNode->links().contains(Node::ContentsLink))
3247                 continue;
3248
3249             // Discard external nodes.
3250             if (fakeNode->subType() == Node::ExternalPage)
3251                 continue;
3252
3253             QString sortKey = fakeNode->fullTitle().toLower();
3254             if (sortKey.startsWith("the "))
3255                 sortKey.remove(0, 4);
3256             sortKey.replace(singleDigit, "0\\1");
3257
3258             if (!group.isEmpty()) {
3259                 if (isGroupPage) {
3260                     // If we encounter a group definition page, we add all
3261                     // the pages in that group to the list for that group.
3262                     foreach (Node* member, fakeNode->groupMembers()) {
3263                         if (member->type() != Node::Fake)
3264                             continue;
3265                         FakeNode* page = static_cast<FakeNode*>(member);
3266                         if (page) {
3267                             QString sortKey = page->fullTitle().toLower();
3268                             if (sortKey.startsWith("the "))
3269                                 sortKey.remove(0, 4);
3270                             sortKey.replace(singleDigit, "0\\1");
3271                             fakeNodeMap[const_cast<const FakeNode*>(fakeNode)].insert(sortKey, page);
3272                             groupTitlesMap[fakeNode->fullTitle()] = const_cast<const FakeNode*>(fakeNode);
3273                         }
3274                     }
3275                 }
3276                 else if (!isGroupPage) {
3277                     // If we encounter a page that belongs to a group then
3278                     // we add that page to the list for that group.
3279                     const FakeNode* gn = tree_->findGroupNode(QStringList(group));
3280                     if (gn)
3281                         fakeNodeMap[gn].insert(sortKey, fakeNode);
3282                 }
3283             }
3284         }
3285     }
3286
3287     // We now list all the pages found that belong to groups.
3288     // If only certain pages were found for a group, but the definition page
3289     // for that group wasn't listed, the list of pages will be intentionally
3290     // incomplete. However, if the group definition page was listed, all the
3291     // pages in that group are listed for completeness.
3292
3293     if (!fakeNodeMap.isEmpty()) {
3294         foreach (const QString& groupTitle, groupTitlesMap.keys()) {
3295             const FakeNode* groupNode = groupTitlesMap[groupTitle];
3296             writeStartTag(DT_p);
3297             xmlWriter().writeAttribute("outputclass","h3");
3298             writeStartTag(DT_xref);
3299             // formathtml
3300             xmlWriter().writeAttribute("href",linkForNode(groupNode, relative));
3301             writeCharacters(protectEnc(groupNode->fullTitle()));
3302             writeEndTag(); // </xref>
3303             writeEndTag(); // </p>
3304             if (fakeNodeMap[groupNode].count() == 0)
3305                 continue;
3306
3307             writeStartTag(DT_ul);
3308             foreach (const FakeNode* fakeNode, fakeNodeMap[groupNode]) {
3309                 QString title = fakeNode->fullTitle();
3310                 if (title.startsWith("The "))
3311                     title.remove(0, 4);
3312                 writeStartTag(DT_li);
3313                 writeStartTag(DT_xref);
3314                 // formathtml
3315                 xmlWriter().writeAttribute("href",linkForNode(fakeNode, relative));
3316                 writeCharacters(protectEnc(title));
3317                 writeEndTag(); // </xref>
3318                 writeEndTag(); // </li>
3319             }
3320             writeEndTag(); // </ul>
3321         }
3322     }
3323
3324     if (!uncategorizedNodeMap.isEmpty()) {
3325         writeStartTag(DT_p);
3326         xmlWriter().writeAttribute("outputclass","h3");
3327         xmlWriter().writeCharacters("Miscellaneous");
3328         writeEndTag(); // </p>
3329         writeStartTag(DT_ul);
3330         foreach (const FakeNode *fakeNode, uncategorizedNodeMap) {
3331             QString title = fakeNode->fullTitle();
3332             if (title.startsWith("The "))
3333                 title.remove(0, 4);
3334             writeStartTag(DT_li);
3335             writeStartTag(DT_xref);
3336             // formathtml
3337             xmlWriter().writeAttribute("href",linkForNode(fakeNode, relative));
3338             writeCharacters(protectEnc(title));
3339             writeEndTag(); // </xref>
3340             writeEndTag(); // </li>
3341         }
3342         writeEndTag(); // </ul>
3343     }
3344 }
3345
3346 /*!
3347   Write the XML for a standard section of a page, e.g.
3348   "Public Functions" or "Protected Slots." The section
3349   is written too the current XML stream as a table.
3350  */
3351 void DitaXmlGenerator::generateSection(const NodeList& nl,
3352                                        const Node* relative,
3353                                        CodeMarker* marker,
3354                                        CodeMarker::SynopsisStyle style)
3355 {
3356     if (!nl.isEmpty()) {
3357         writeStartTag(DT_ul);
3358         NodeList::ConstIterator m = nl.constBegin();
3359         while (m != nl.constEnd()) {
3360             if ((*m)->access() != Node::Private) {
3361                 writeStartTag(DT_li);
3362                 QString marked = getMarkedUpSynopsis(*m, relative, marker, style);
3363                 writeText(marked, marker, relative);
3364                 writeEndTag(); // </li>
3365             }
3366             ++m;
3367         }
3368         writeEndTag(); // </ul>
3369     }
3370 }
3371
3372 /*!
3373   Writes the "inherited from" list to the current XML stream.
3374  */
3375 void DitaXmlGenerator::generateSectionInheritedList(const Section& section,
3376                                                     const Node* relative,
3377                                                     CodeMarker* marker)
3378 {
3379     if (section.inherited.isEmpty())
3380         return;
3381     writeStartTag(DT_ul);
3382     QList<QPair<InnerNode*,int> >::ConstIterator p = section.inherited.constBegin();
3383     while (p != section.inherited.constEnd()) {
3384         writeStartTag(DT_li);
3385         QString text;
3386         text.setNum((*p).second);
3387         text += QLatin1Char(' ');
3388         if ((*p).second == 1)
3389             text += section.singularMember;
3390         else
3391             text += section.pluralMember;
3392         text += " inherited from ";
3393         writeCharacters(text);
3394         writeStartTag(DT_xref);
3395         // formathtml
3396         // zzz
3397         text = fileName((*p).first) + QLatin1Char('#');
3398         text += DitaXmlGenerator::cleanRef(section.name.toLower());
3399         xmlWriter().writeAttribute("href",text);
3400         text = protectEnc(marker->plainFullName((*p).first, relative));
3401         writeCharacters(text);
3402         writeEndTag(); // </xref>
3403         writeEndTag(); // </li>
3404         ++p;
3405     }
3406     writeEndTag(); // </ul>
3407 }
3408
3409 /*!
3410   Get the synopsis from the \a node using the \a relative
3411   node if needed, and mark up the synopsis using \a marker.
3412   Use the style to decide which kind of sysnopsis to build,
3413   normally \c Summary or \c Detailed. Return the marked up
3414   string.
3415  */
3416 QString DitaXmlGenerator::getMarkedUpSynopsis(const Node* node,
3417                                               const Node* relative,
3418                                               CodeMarker* marker,
3419                                               CodeMarker::SynopsisStyle style)
3420 {
3421     QString marked = marker->markedUpSynopsis(node, relative, style);
3422     QRegExp tag("(<[^@>]*>)");
3423     if (marked.indexOf(tag) != -1) {
3424         QString tmp = protectEnc(marked.mid(tag.pos(1), tag.cap(1).length()));
3425         marked.replace(tag.pos(1), tag.cap(1).length(), tmp);
3426     }
3427     marked.replace(QRegExp("<@param>([a-z]+)_([1-9n])</@param>"),
3428                    "<i> \\1<sub>\\2</sub></i>");
3429     if (style == CodeMarker::Summary) {
3430         marked.remove("<@name>");   // was "<b>"
3431         marked.remove("</@name>");  // was "</b>"
3432     }
3433
3434     if (style == CodeMarker::Subpage) {
3435         QRegExp extraRegExp("<@extra>.*</@extra>");
3436         extraRegExp.setMinimal(true);
3437         marked.remove(extraRegExp);
3438     }
3439
3440     if (style != CodeMarker::Detailed) {
3441         marked.remove("<@type>");
3442         marked.remove("</@type>");
3443     }
3444     return marked;
3445 }
3446
3447 /*!
3448   Renamed from highlightedCode() in the html generator. Gets the text
3449   from \a markedCode , and then the text is written to the current XML
3450   stream.
3451  */
3452 void DitaXmlGenerator::writeText(const QString& markedCode,
3453                                  CodeMarker* marker,
3454                                  const Node* relative)
3455 {
3456     QString src = markedCode;
3457     QString text;
3458     QStringRef arg;
3459     QStringRef par1;
3460
3461     const QChar charLangle = '<';
3462     const QChar charAt = '@';
3463
3464     /*
3465       First strip out all the extraneous markup. The table
3466       below contains the markup we want to keep. Everything
3467       else that begins with "<@" or "</@" is stripped out.
3468      */
3469     static const QString spanTags[] = {
3470         "<@link ",         "<@link ",
3471         "<@type>",         "<@type>",
3472         "<@headerfile>",   "<@headerfile>",
3473         "<@func>",         "<@func>",
3474         "<@func ",         "<@func ",
3475         "<@param>",        "<@param>",
3476         "<@extra>",        "<@extra>",
3477         "</@link>",        "</@link>",
3478         "</@type>",        "</@type>",
3479         "</@headerfile>",  "</@headerfile>",
3480         "</@func>",        "</@func>",
3481         "</@param>",        "</@param>",
3482         "</@extra>",        "</@extra>"
3483     };
3484     for (int i = 0, n = src.size(); i < n;) {
3485         if (src.at(i) == charLangle) {
3486             bool handled = false;
3487             for (int k = 0; k != 13; ++k) {
3488                 const QString & tag = spanTags[2 * k];
3489                 if (tag == QStringRef(&src, i, tag.length())) {
3490                     text += spanTags[2 * k + 1];
3491                     i += tag.length();
3492                     handled = true;
3493                     break;
3494                 }
3495             }
3496             if (!handled) {
3497                 ++i;
3498                 if (src.at(i) == charAt ||
3499                         (src.at(i) == QLatin1Char('/') && src.at(i + 1) == charAt)) {
3500                     // drop 'our' unknown tags (the ones still containing '@')
3501                     while (i < n && src.at(i) != QLatin1Char('>'))
3502                         ++i;
3503                     ++i;
3504                 }
3505                 else {
3506                     // retain all others
3507                     text += charLangle;
3508                 }
3509             }
3510         }
3511         else {
3512             text += src.at(i);
3513             ++i;
3514         }
3515     }
3516
3517     // replace all <@link> tags: "(<@link node=\"([^\"]+)\">).*(</@link>)"
3518     // replace all "(<@(type|headerfile|func)(?: +[^>]*)?>)(.*)(</@\\2>)" tags
3519     src = text;
3520     text = QString();
3521     static const QString markTags[] = {
3522         // 0       1         2           3       4        5
3523         "link", "type", "headerfile", "func", "param", "extra"
3524     };
3525
3526     for (int i = 0, n = src.size(); i < n;) {
3527         if (src.at(i) == charLangle && src.at(i + 1) == charAt) {
3528             i += 2;
3529             for (int k = 0; k != 6; ++k) {
3530                 if (parseArg(src, markTags[k], &i, n, &arg, &par1)) {
3531                     const Node* n = 0;
3532                     if (k == 0) { // <@link>
3533                         if (!text.isEmpty()) {
3534                             writeCharacters(text);
3535                             text.clear();
3536                         }
3537                         n = CodeMarker::nodeForString(par1.toString());
3538                         QString link = linkForNode(n, relative);
3539                         addLink(link, arg);
3540                     }
3541                     else if (k == 4) { // <@param>
3542                         if (!text.isEmpty()) {
3543                             writeCharacters(text);
3544                             text.clear();
3545                         }
3546                         writeStartTag(DT_i);
3547                         //writeCharacters(" " + arg.toString());
3548                         writeCharacters(arg.toString());
3549                         writeEndTag(); // </i>
3550                     }
3551                     else if (k == 5) { // <@extra>
3552                         if (!text.isEmpty()) {
3553                             writeCharacters(text);
3554                             text.clear();
3555                         }
3556                         writeStartTag(DT_tt);
3557                         writeCharacters(arg.toString());
3558                         writeEndTag(); // </tt>
3559                     }
3560                     else {
3561                         if (!text.isEmpty()) {
3562                             writeCharacters(text);
3563                             text.clear();
3564                         }
3565                         par1 = QStringRef();
3566                         QString link;
3567                         n = marker->resolveTarget(arg.toString(), tree_, relative);
3568                         if (n && n->subType() == Node::QmlBasicType) {
3569                             if (relative && relative->subType() == Node::QmlClass) {
3570                                 link = linkForNode(n,relative);
3571                                 addLink(link, arg);
3572                             }
3573                             else {
3574                                 writeCharacters(arg.toString());
3575                             }
3576                         }
3577                         else {
3578                             // (zzz) Is this correct for all cases?
3579                             link = linkForNode(n,relative);
3580                             addLink(link, arg);
3581                         }
3582                     }
3583                     break;
3584                 }
3585             }
3586         }
3587         else {
3588             text += src.at(i++);
3589         }
3590     }
3591     if (!text.isEmpty()) {
3592         writeCharacters(text);
3593     }
3594 }
3595
3596 void DitaXmlGenerator::generateLink(const Atom* atom,
3597                                     const Node* /* relative */,
3598                                     CodeMarker* marker)
3599 {
3600     static QRegExp camelCase("[A-Z][A-Z][a-z]|[a-z][A-Z0-9]|_");
3601
3602     if (funcLeftParen.indexIn(atom->string()) != -1 && marker->recognizeLanguage("Cpp")) {
3603         // hack for C++: move () outside of link
3604         int k = funcLeftParen.pos(1);
3605         writeCharacters(protectEnc(atom->string().left(k)));
3606         if (link.isEmpty()) {
3607             if (showBrokenLinks)
3608                 writeEndTag(); // </i>
3609         }
3610         else
3611             writeEndTag(); // </xref>
3612         inLink = false;
3613         writeCharacters(protectEnc(atom->string().mid(k)));
3614     }
3615     else if (marker->recognizeLanguage("Java")) {
3616         // hack for Java: remove () and use <tt> when appropriate
3617         bool func = atom->string().endsWith("()");
3618         bool tt = (func || atom->string().contains(camelCase));
3619         if (tt)
3620             writeStartTag(DT_tt);
3621         if (func)
3622             writeCharacters(protectEnc(atom->string().left(atom->string().length() - 2)));
3623         else
3624             writeCharacters(protectEnc(atom->string()));
3625         writeEndTag(); // </tt>
3626     }
3627     else
3628         writeCharacters(protectEnc(atom->string()));
3629 }
3630
3631 QString DitaXmlGenerator::cleanRef(const QString& ref)
3632 {
3633     QString clean;
3634
3635     if (ref.isEmpty())
3636         return clean;
3637
3638     clean.reserve(ref.size() + 20);
3639     const QChar c = ref[0];
3640     const uint u = c.unicode();
3641
3642     if ((u >= 'a' && u <= 'z') ||
3643             (u >= 'A' && u <= 'Z') ||
3644             (u >= '0' && u <= '9')) {
3645         clean += c;
3646     }
3647     else if (u == '~') {
3648         clean += "dtor.";
3649     }
3650     else if (u == '_') {
3651         clean += "underscore.";
3652     }
3653     else {
3654         clean += QLatin1Char('A');
3655     }
3656
3657     for (int i = 1; i < (int) ref.length(); i++) {
3658         const QChar c = ref[i];
3659         const uint u = c.unicode();
3660         if ((u >= 'a' && u <= 'z') ||
3661                 (u >= 'A' && u <= 'Z') ||
3662                 (u >= '0' && u <= '9') || u == '-' ||
3663                 u == '_' || u == ':' || u == '.') {
3664             clean += c;
3665         }
3666         else if (c.isSpace()) {
3667             clean += QLatin1Char('-');
3668         }
3669         else if (u == '!') {
3670             clean += "-not";
3671         }
3672         else if (u == '&') {
3673             clean += "-and";
3674         }
3675         else if (u == '<') {
3676             clean += "-lt";
3677         }
3678         else if (u == '=') {
3679             clean += "-eq";
3680         }
3681         else if (u == '>') {
3682             clean += "-gt";
3683         }
3684         else if (u == '#') {
3685             clean += QLatin1Char('#');
3686         }
3687         else {
3688             clean += QLatin1Char('-');
3689             clean += QString::number((int)u, 16);
3690         }
3691     }
3692     return clean;
3693 }
3694
3695 QString DitaXmlGenerator::registerRef(const QString& ref)
3696 {
3697     QString clean = DitaXmlGenerator::cleanRef(ref);
3698
3699     for (;;) {
3700         QString& prevRef = refMap[clean.toLower()];
3701         if (prevRef.isEmpty()) {
3702             prevRef = ref;
3703             break;
3704         }
3705         else if (prevRef == ref)
3706             break;
3707         clean += QLatin1Char('x');
3708     }
3709     return clean;
3710 }
3711
3712 /*!
3713   Calls protect() with the \a string. Returns the result.
3714  */
3715 QString DitaXmlGenerator::protectEnc(const QString& string)
3716 {
3717     return protect(string, outputEncoding);
3718 }
3719
3720 QString DitaXmlGenerator::protect(const QString& string, const QString& ) //outputEncoding)
3721 {
3722 #define APPEND(x) \
3723     if (xml.isEmpty()) { \
3724     xml = string; \
3725     xml.truncate(i); \
3726 } \
3727     xml += (x);
3728
3729     QString xml;
3730     int n = string.length();
3731
3732     for (int i = 0; i < n; ++i) {
3733         QChar ch = string.at(i);
3734
3735         if (ch == QLatin1Char('&')) {
3736             APPEND("&amp;");
3737         }
3738         else if (ch == QLatin1Char('<')) {
3739             APPEND("&lt;");
3740         }
3741         else if (ch == QLatin1Char('>')) {
3742             APPEND("&gt;");
3743         }
3744         else if (ch == QLatin1Char('"')) {
3745             APPEND("&quot;");
3746         }
3747         else {
3748             if (!xml.isEmpty())
3749                 xml += ch;
3750         }
3751     }
3752
3753     if (!xml.isEmpty())
3754         return xml;
3755     return string;
3756
3757 #undef APPEND
3758 }
3759
3760 /*!
3761   Constructs a file name appropriate for the \a node
3762   and returns the file name.
3763  */
3764 QString DitaXmlGenerator::fileBase(const Node* node) const
3765 {
3766     QString result;
3767     result = Generator::fileBase(node);
3768     return result;
3769 }
3770
3771 QString DitaXmlGenerator::guidForNode(const Node* node)
3772 {
3773     switch (node->type()) {
3774     case Node::Namespace:
3775     case Node::Class:
3776     default:
3777         break;
3778     case Node::Enum:
3779         return node->guid();
3780     case Node::Typedef:
3781     {
3782         const TypedefNode* tdn = static_cast<const TypedefNode*>(node);
3783         if (tdn->associatedEnum())
3784             return guidForNode(tdn->associatedEnum());
3785     }
3786         return node->guid();
3787     case Node::Function:
3788     {
3789         const FunctionNode* fn = static_cast<const FunctionNode*>(node);
3790         if (fn->associatedProperty()) {
3791             return guidForNode(fn->associatedProperty());
3792         }
3793         else {
3794             QString ref = fn->name();
3795             if (fn->overloadNumber() != 1) {
3796                 ref += QLatin1Char('-') + QString::number(fn->overloadNumber());
3797             }
3798         }
3799         return fn->guid();
3800     }
3801     case Node::Fake:
3802         if (node->subType() != Node::QmlPropertyGroup)
3803             break;
3804     case Node::QmlProperty:
3805     case Node::Property:
3806         return node->guid();
3807     case Node::QmlSignal:
3808         return node->guid();
3809     case Node::QmlSignalHandler:
3810         return node->guid();
3811     case Node::QmlMethod:
3812         return node->guid();
3813     case Node::Variable:
3814         return node->guid();
3815     }
3816     return QString();
3817 }
3818
3819 /*!
3820   Constructs a file name appropriate for the \a node and returns
3821   it. If the \a node is not a fake node, or if it is a fake node but
3822   it is neither an external page node nor an image node or a ditamap,
3823   call the PageGenerator::fileName() function.
3824  */
3825 QString DitaXmlGenerator::fileName(const Node* node)
3826 {
3827     if (node->type() == Node::Fake) {
3828         if (static_cast<const FakeNode*>(node)->pageType() == Node::DitaMapPage)
3829             return node->name();
3830         if (static_cast<const FakeNode*>(node)->subType() == Node::ExternalPage)
3831             return node->name();
3832         if (static_cast<const FakeNode*>(node)->subType() == Node::Image)
3833             return node->name();
3834     }
3835     return Generator::fileName(node);
3836 }
3837
3838 QString DitaXmlGenerator::linkForNode(const Node* node, const Node* relative)
3839 {
3840     if (node == 0 || node == relative)
3841         return QString();
3842     if (!node->url().isEmpty())
3843         return node->url();
3844     if (fileBase(node).isEmpty())
3845         return QString();
3846     if (node->access() == Node::Private)
3847         return QString();
3848
3849     QString fn = fileName(node);
3850     if (node && relative && node->parent() != relative) {
3851         if (node->parent()->subType() == Node::QmlClass && relative->subType() == Node::QmlClass) {
3852             if (node->parent()->isAbstract()) {
3853                 /*
3854                   This is a bit of a hack. What we discover with
3855                   the three 'if' statements immediately above,
3856                   is that node's parent is marked \qmlabstract
3857                   but the link appears in a qdoc comment for a
3858                   subclass of the node's parent. This means the
3859                   link should refer to the file for the relative
3860                   node, not the file for node.
3861                  */
3862                 fn = fileName(relative);
3863 #if DEBUG_ABSTRACT
3864                 qDebug() << "ABSTRACT:" << node->parent()->name()
3865                          << node->name() << relative->name()
3866                          << node->parent()->type() << node->parent()->subType()
3867                          << relative->type() << relative->subType() << outFileName();
3868 #endif
3869             }
3870         }
3871     }
3872     QString link = fn;
3873
3874     if (!node->isInnerNode() || node->subType() == Node::QmlPropertyGroup) {
3875         QString guid = guidForNode(node);
3876         if (relative && fn == fileName(relative) && guid == guidForNode(relative)) {
3877             return QString();
3878         }
3879         link += QLatin1Char('#');
3880         link += guid;
3881     }
3882     /*
3883       If the output is going to subdirectories, then if the
3884       two nodes will be output to different directories, then
3885       the link must go up to the parent directory and then
3886       back down into the other subdirectory.
3887      */
3888     if (node && relative && (node != relative)) {
3889         if (node->outputSubdirectory() != relative->outputSubdirectory())
3890             link.prepend(QString("../" + node->outputSubdirectory() + QLatin1Char('/')));
3891     }
3892     return link;
3893 }
3894
3895 QString DitaXmlGenerator::refForAtom(Atom* atom, const Node* /* node */)
3896 {
3897     if (atom->type() == Atom::SectionLeft)
3898         return Doc::canonicalTitle(Text::sectionHeading(atom).toString());
3899     if (atom->type() == Atom::Target)
3900         return Doc::canonicalTitle(atom->string());
3901     return QString();
3902 }
3903
3904 void DitaXmlGenerator::generateFullName(const Node* apparentNode,
3905                                         const Node* relative,
3906                                         CodeMarker* marker,
3907                                         const Node* actualNode)
3908 {
3909     if (actualNode == 0)
3910         actualNode = apparentNode;
3911     writeStartTag(DT_xref);
3912     // formathtml
3913     QString href = linkForNode(actualNode, relative);
3914     writeHrefAttribute(href);
3915     writeCharacters(protectEnc(fullName(apparentNode, relative, marker)));
3916     writeEndTag(); // </xref>
3917 }
3918
3919 void DitaXmlGenerator::findAllClasses(const InnerNode* node)
3920 {
3921     NodeList::const_iterator c = node->childNodes().constBegin();
3922     while (c != node->childNodes().constEnd()) {
3923         if ((*c)->access() != Node::Private && (*c)->url().isEmpty()) {
3924             if ((*c)->type() == Node::Class && !(*c)->doc().isEmpty()) {
3925                 QString className = (*c)->name();
3926                 if ((*c)->parent() &&
3927                         (*c)->parent()->type() == Node::Namespace &&
3928                         !(*c)->parent()->name().isEmpty())
3929                     className = (*c)->parent()->name()+"::"+className;
3930
3931                 if (!(static_cast<const ClassNode *>(*c))->hideFromMainList()) {
3932                     if ((*c)->status() == Node::Compat) {
3933                         compatClasses.insert(className, *c);
3934                     }
3935                     else if ((*c)->status() == Node::Obsolete) {
3936                         obsoleteClasses.insert(className, *c);
3937                     }
3938                     else {
3939                         nonCompatClasses.insert(className, *c);
3940                         if ((*c)->status() == Node::Main)
3941                             mainClasses.insert(className, *c);
3942                     }
3943                 }
3944
3945                 QString moduleName = (*c)->moduleName();
3946                 if (moduleName == "Qt3SupportLight") {
3947                     moduleClassMap[moduleName].insert((*c)->name(), *c);
3948                     moduleName = "Qt3Support";
3949                 }
3950                 if (!moduleName.isEmpty())
3951                     moduleClassMap[moduleName].insert((*c)->name(), *c);
3952
3953                 QString serviceName =
3954                         (static_cast<const ClassNode *>(*c))->serviceName();
3955                 if (!serviceName.isEmpty())
3956                     serviceClasses.insert(serviceName, *c);
3957             }
3958             else if ((*c)->type() == Node::Fake &&
3959                      (*c)->subType() == Node::QmlClass &&
3960                      !(*c)->doc().isEmpty()) {
3961                 QString qmlClassName = (*c)->name();
3962                 qmlClasses.insert(qmlClassName,*c);
3963             }
3964             else if ((*c)->isInnerNode()) {
3965                 findAllClasses(static_cast<InnerNode *>(*c));
3966             }
3967         }
3968         ++c;
3969     }
3970 }
3971
3972 void DitaXmlGenerator::findAllFunctions(const InnerNode* node)
3973 {
3974     NodeList::ConstIterator c = node->childNodes().constBegin();
3975     while (c != node->childNodes().constEnd()) {
3976         if ((*c)->access() != Node::Private) {
3977             if ((*c)->isInnerNode() && (*c)->url().isEmpty()) {
3978                 findAllFunctions(static_cast<const InnerNode*>(*c));
3979             }
3980             else if ((*c)->type() == Node::Function) {
3981                 const FunctionNode* func = static_cast<const FunctionNode*>(*c);
3982                 if ((func->status() > Node::Obsolete) &&
3983                         !func->isInternal() &&
3984                         (func->metaness() != FunctionNode::Ctor) &&
3985                         (func->metaness() != FunctionNode::Dtor)) {
3986                     funcIndex[(*c)->name()].insert((*c)->parent()->fullDocumentName(), *c);
3987                 }
3988             }
3989         }
3990         ++c;
3991     }
3992 }
3993
3994 void DitaXmlGenerator::findAllLegaleseTexts(const InnerNode* node)
3995 {
3996     NodeList::ConstIterator c = node->childNodes().constBegin();
3997     while (c != node->childNodes().constEnd()) {
3998         if ((*c)->access() != Node::Private) {
3999             if (!(*c)->doc().legaleseText().isEmpty())
4000                 legaleseTexts.insertMulti((*c)->doc().legaleseText(), *c);
4001             if ((*c)->isInnerNode())
4002                 findAllLegaleseTexts(static_cast<const InnerNode *>(*c));
4003         }
4004         ++c;
4005     }
4006 }
4007
4008 void DitaXmlGenerator::findAllNamespaces(const InnerNode* node)
4009 {
4010     NodeList::ConstIterator c = node->childNodes().constBegin();
4011     while (c != node->childNodes().constEnd()) {
4012         if ((*c)->access() != Node::Private) {
4013             if ((*c)->isInnerNode() && (*c)->url().isEmpty()) {
4014                 findAllNamespaces(static_cast<const InnerNode *>(*c));
4015                 if ((*c)->type() == Node::Namespace) {
4016                     const NamespaceNode *nspace = static_cast<const NamespaceNode *>(*c);
4017                     // Ensure that the namespace's name is not empty (the root
4018                     // namespace has no name).
4019                     if (!nspace->name().isEmpty()) {
4020                         namespaceIndex.insert(nspace->name(), *c);
4021                         QString moduleName = (*c)->moduleName();
4022                         if (moduleName == "Qt3SupportLight") {
4023                             moduleNamespaceMap[moduleName].insert((*c)->name(), *c);
4024                             moduleName = "Qt3Support";
4025                         }
4026                         if (!moduleName.isEmpty())
4027                             moduleNamespaceMap[moduleName].insert((*c)->name(), *c);
4028                     }
4029                 }
4030             }
4031         }
4032         ++c;
4033     }
4034 }
4035
4036 /*!
4037   We're writing an attribute that indicates that the text
4038   data is a heading, hence, h1, h2, h3... etc, and we must
4039   decide which number to use.
4040  */
4041 int DitaXmlGenerator::hOffset(const Node* node)
4042 {
4043     switch (node->type()) {
4044     case Node::Namespace:
4045     case Node::Class:
4046         return 2;
4047     case Node::Fake:
4048         return 1;
4049     case Node::Enum:
4050     case Node::Typedef:
4051     case Node::Function:
4052     case Node::Property:
4053     default:
4054         return 3;
4055     }
4056 }
4057
4058 bool DitaXmlGenerator::isThreeColumnEnumValueTable(const Atom* atom)
4059 {
4060     while (atom != 0 && !(atom->type() == Atom::ListRight && atom->string() == ATOM_LIST_VALUE)) {
4061         if (atom->type() == Atom::ListItemLeft && !matchAhead(atom, Atom::ListItemRight))
4062             return true;
4063         atom = atom->next();
4064     }
4065     return false;
4066 }
4067
4068 const Node* DitaXmlGenerator::findNodeForTarget(const QString& target,
4069                                                 const Node* relative,
4070                                                 CodeMarker* marker,
4071                                                 const Atom* atom)
4072 {
4073     const Node* node = 0;
4074
4075     if (target.isEmpty()) {
4076         node = relative;
4077     }
4078     else if (target.endsWith(".html")) {
4079         node = tree_->root()->findChildNodeByNameAndType(target, Node::Fake);
4080     }
4081     else if (marker) {
4082         node = marker->resolveTarget(target, tree_, relative);
4083         if (!node)
4084             node = tree_->findFakeNodeByTitle(target, relative);
4085         if (!node && atom) {
4086             node = tree_->findUnambiguousTarget(target, *const_cast<Atom**>(&atom), relative);
4087         }
4088     }
4089
4090     if (!node)
4091         relative->doc().location().warning(tr("Cannot link to '%1'").arg(target));
4092
4093     return node;
4094 }
4095
4096 const QPair<QString,QString> DitaXmlGenerator::anchorForNode(const Node* node)
4097 {
4098     QPair<QString,QString> anchorPair;
4099     anchorPair.first = Generator::fileName(node);
4100     if (node->type() == Node::Fake) {
4101         const FakeNode *fakeNode = static_cast<const FakeNode*>(node);
4102         anchorPair.second = fakeNode->title();
4103     }
4104
4105     return anchorPair;
4106 }
4107
4108 QString DitaXmlGenerator::getLink(const Atom* atom,
4109                                   const Node* relative,
4110                                   CodeMarker* marker,
4111                                   const Node** node)
4112 {
4113     QString link;
4114     *node = 0;
4115     inObsoleteLink = false;
4116
4117     if (atom->string().contains(QLatin1Char(':')) &&
4118             (atom->string().startsWith("file:")
4119              || atom->string().startsWith("http:")
4120              || atom->string().startsWith("https:")
4121              || atom->string().startsWith("ftp:")
4122              || atom->string().startsWith("mailto:"))) {
4123
4124         link = atom->string();
4125     }
4126     else {
4127         QStringList path;
4128         if (atom->string().contains('#'))
4129             path = atom->string().split('#');
4130         else
4131             path.append(atom->string());
4132
4133         Atom* targetAtom = 0;
4134         QString first = path.first().trimmed();
4135
4136         if (first.isEmpty()) {
4137             *node = relative;
4138         }
4139         else if (first.endsWith(".html")) {
4140             *node = tree_->root()->findChildNodeByNameAndType(first, Node::Fake);
4141         }
4142         else {
4143             *node = marker->resolveTarget(first, tree_, relative);
4144             if (!*node)
4145                 *node = tree_->findFakeNodeByTitle(first, relative);
4146             if (!*node)
4147                 *node = tree_->findUnambiguousTarget(first, targetAtom, relative);
4148         }
4149
4150         if (*node) {
4151             if (!(*node)->url().isEmpty())
4152                 return (*node)->url();
4153             else
4154                 path.removeFirst();
4155         }
4156         else
4157             *node = relative;
4158
4159         if (*node && (*node)->status() == Node::Obsolete) {
4160             if (relative && (relative->parent() != *node) &&
4161                     (relative->status() != Node::Obsolete)) {
4162                 bool porting = false;
4163                 if (relative->type() == Node::Fake) {
4164                     const FakeNode* fake = static_cast<const FakeNode*>(relative);
4165                     if (fake->title().startsWith("Porting"))
4166                         porting = true;
4167                 }
4168                 QString name = marker->plainFullName(relative);
4169                 if (!porting && !name.startsWith("Q3")) {
4170                     if (obsoleteLinks) {
4171                         relative->doc().location().warning(tr("Link to obsolete item '%1' in %2")
4172                                                            .arg(atom->string())
4173                                                            .arg(name));
4174                     }
4175                     inObsoleteLink = true;
4176                 }
4177             }
4178         }
4179
4180         while (!path.isEmpty()) {
4181             targetAtom = tree_->findTarget(path.first(), *node);
4182             if (targetAtom == 0)
4183                 break;
4184             path.removeFirst();
4185         }
4186
4187         if (path.isEmpty()) {
4188             link = linkForNode(*node, relative);
4189             if (*node && (*node)->subType() == Node::Image)
4190                 link = "images/used-in-examples/" + link;
4191             if (targetAtom) {
4192                 if (link.isEmpty())
4193                     link = outFileName();
4194                 QString guid = lookupGuid(link,refForAtom(targetAtom,*node));
4195                 link += QLatin1Char('#') + guid;
4196             }
4197             else if (!link.isEmpty() && *node &&
4198                      (link.endsWith(".xml") || link.endsWith(".dita"))) {
4199                 link += QLatin1Char('#') + (*node)->guid();
4200             }
4201         }
4202         /*
4203           If the output is going to subdirectories, then if the
4204           two nodes will be output to different directories, then
4205           the link must go up to the parent directory and then
4206           back down into the other subdirectory.
4207         */
4208         if (link.startsWith("images/")) {
4209             link.prepend(QString("../"));
4210         }
4211         else if (*node && relative && (*node != relative)) {
4212             if ((*node)->outputSubdirectory() != relative->outputSubdirectory()) {
4213                 link.prepend(QString("../" + (*node)->outputSubdirectory() + QLatin1Char('/')));
4214             }
4215         }
4216     }
4217     if (!link.isEmpty() && link[0] == '#') {
4218         link.prepend(outFileName());
4219     }
4220     return link;
4221 }
4222
4223 void DitaXmlGenerator::generateIndex(const QString& fileBase,
4224                                      const QString& url,
4225                                      const QString& title)
4226 {
4227     tree_->generateIndex(outputDir() + QLatin1Char('/') + fileBase + ".index", url, title);
4228 }
4229
4230 void DitaXmlGenerator::generateStatus(const Node* node, CodeMarker* marker)
4231 {
4232     Text text;
4233
4234     switch (node->status()) {
4235     case Node::Obsolete:
4236         if (node->isInnerNode())
4237             Generator::generateStatus(node, marker);
4238         break;
4239     case Node::Compat:
4240         if (node->isInnerNode()) {
4241             text << Atom::ParaLeft
4242                  << Atom(Atom::FormattingLeft,ATOM_FORMATTING_BOLD)
4243                  << "This "
4244                  << typeString(node)
4245                  << " is part of the Qt 3 support library."
4246                  << Atom(Atom::FormattingRight, ATOM_FORMATTING_BOLD)
4247                  << " It is provided to keep old source code working. "
4248                  << "We strongly advise against "
4249                  << "using it in new code. See ";
4250
4251             const FakeNode *fakeNode = tree_->findFakeNodeByTitle("Porting To Qt 4");
4252             Atom *targetAtom = 0;
4253             if (fakeNode && node->type() == Node::Class) {
4254                 QString oldName(node->name());
4255                 oldName.remove(QLatin1Char('3'));
4256                 targetAtom = tree_->findTarget(oldName,fakeNode);
4257             }
4258
4259             if (targetAtom) {
4260                 QString fn = fileName(fakeNode);
4261                 QString guid = lookupGuid(fn,refForAtom(targetAtom,fakeNode));
4262                 text << Atom(Atom::GuidLink, fn + QLatin1Char('#') + guid);
4263             }
4264             else
4265                 text << Atom(Atom::Link, "Porting to Qt 4");
4266
4267             text << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK)
4268                  << Atom(Atom::String, "Porting to Qt 4")
4269                  << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK)
4270                  << " for more information."
4271                  << Atom::ParaRight;
4272         }
4273         generateText(text, node, marker);
4274         break;
4275     default:
4276         Generator::generateStatus(node, marker);
4277     }
4278 }
4279
4280 void DitaXmlGenerator::beginLink(const QString& link)
4281 {
4282     this->link = link;
4283     if (link.isEmpty())
4284         return;
4285     writeStartTag(DT_xref);
4286     // formathtml
4287     writeHrefAttribute(link);
4288     inLink = true;
4289 }
4290
4291 void DitaXmlGenerator::endLink()
4292 {
4293     if (inLink) {
4294         if (link.isEmpty()) {
4295             if (showBrokenLinks)
4296                 writeEndTag(); // </i>
4297         }
4298         else {
4299             if (inObsoleteLink) {
4300                 writeStartTag(DT_sup);
4301                 xmlWriter().writeCharacters("(obsolete)");
4302                 writeEndTag(); // </sup>
4303             }
4304             writeEndTag(); // </xref>
4305         }
4306     }
4307     inLink = false;
4308     inObsoleteLink = false;
4309 }
4310
4311 /*!
4312   Generates the summary for the \a section. Only used for
4313   sections of QML element documentation.
4314
4315   Currently handles only the QML property group.
4316  */
4317 void DitaXmlGenerator::generateQmlSummary(const Section& section,
4318                                           const Node* relative,
4319                                           CodeMarker* marker)
4320 {
4321     if (!section.members.isEmpty()) {
4322         writeStartTag(DT_ul);
4323         NodeList::ConstIterator m;
4324         m = section.members.constBegin();
4325         while (m != section.members.constEnd()) {
4326             writeStartTag(DT_li);
4327             generateQmlItem(*m,relative,marker,true);
4328             writeEndTag(); // </li>
4329             ++m;
4330         }
4331         writeEndTag(); // </ul>
4332     }
4333 }
4334
4335 /*!
4336   Writes the QML property \a qpn to the current DITA XML file.
4337   Assumes that the correct start tag has already been written,
4338   but nothing has been written inside that tag. This function
4339   begins by writing the GUID id attribute for the property.
4340  */
4341 void DitaXmlGenerator::startQmlProperty(QmlPropertyNode* qpn,
4342                                         const InnerNode* relative,
4343                                         CodeMarker* marker)
4344 {
4345     writeStartTag(DT_qmlProperty);
4346     writeGuidAttribute((Node*)qpn);
4347     writeStartTag(DT_apiName);
4348     writeCharacters(qpn->name());
4349     writeEndTag(); // </apiName>
4350     generateBrief(qpn, marker); // <shortdesc>
4351     writeStartTag(DT_qmlPropertyDetail);
4352     writeStartTag(DT_qmlPropertyDef);
4353     if (!qpn->isReadOnlySet())
4354         qpn->setReadOnly(!qpn->isWritable(tree_));
4355     if (qpn->isReadOnly()) {
4356         writeStartTag(DT_qmlQualifier);
4357         xmlWriter().writeAttribute("name","read-only");
4358         xmlWriter().writeAttribute("value","read-only");
4359         writeEndTag(); // </qmlQualifier>
4360     }
4361     if (qpn->isDefault()) {
4362         writeStartTag(DT_qmlQualifier);
4363         xmlWriter().writeAttribute("name","default");
4364         xmlWriter().writeAttribute("value","default");
4365         writeEndTag(); // </qmlQualifier>
4366     }
4367     if (qpn->isAttached()) {
4368         writeStartTag(DT_qmlAttached);
4369         xmlWriter().writeAttribute("name","attached");
4370         xmlWriter().writeAttribute("value","yes");
4371         writeEndTag(); // </qmlAttached>
4372     }
4373     writeStartTag(DT_apiData);
4374     generateQmlItem(qpn, relative, marker, false);
4375     writeEndTag(); // </apiData>
4376     writeEndTag(); // </qmlPropertyDef>
4377 }
4378
4379 /*!
4380   Outputs the DITA detailed documentation for a section
4381   on a QML element reference page.
4382  */
4383 void DitaXmlGenerator::generateDetailedQmlMember(Node* node,
4384                                                  const InnerNode* relative,
4385                                                  CodeMarker* marker)
4386 {
4387     QString marked;
4388     QmlPropertyNode* qpn = 0;
4389
4390     if (node->subType() == Node::QmlPropertyGroup) {
4391         const QmlPropGroupNode* qpgn = static_cast<const QmlPropGroupNode*>(node);
4392         NodeList::ConstIterator p = qpgn->childNodes().constBegin();
4393         if (qpgn->childNodes().size() == 1) {
4394             qpn = static_cast<QmlPropertyNode*>(*p);
4395             startQmlProperty(qpn,relative,marker);
4396             writeApiDesc(node, marker, node->title());
4397             writeEndTag(); // </qmlPropertyDetail>
4398             writeEndTag(); // </qmlProperty>
4399         }
4400         else {
4401             writeStartTag(DT_qmlPropertyGroup);
4402             QString id = "id-qml-propertygroup-" + node->name();
4403             id.replace('.','-');
4404             xmlWriter().writeAttribute("id",id);
4405             writeStartTag(DT_apiName);
4406             //writeCharacters("...");
4407             writeEndTag(); // </apiName>
4408             writeStartTag(DT_qmlPropertyGroupDetail);
4409             writeApiDesc(node, marker, node->title());
4410             writeEndTag(); // </qmlPropertyGroupDetail>
4411             while (p != qpgn->childNodes().constEnd()) {
4412                 if ((*p)->type() == Node::QmlProperty) {
4413                     qpn = static_cast<QmlPropertyNode*>(*p);
4414                     startQmlProperty(qpn,relative,marker);
4415                     writeEndTag(); // </qmlPropertyDetail>
4416                     writeEndTag(); // </qmlProperty>
4417                 }
4418                 ++p;
4419             }
4420             writeEndTag(); // </qmlPropertyGroup
4421         }
4422     }
4423     else if (node->type() == Node::QmlProperty) {
4424         qpn = static_cast<QmlPropertyNode*>(node);
4425         if (qpn->qmlPropNodes().isEmpty()) {
4426             startQmlProperty(qpn,relative,marker);
4427             writeApiDesc(node, marker, node->title());
4428             writeEndTag(); // </qmlPropertyDetail>
4429             writeEndTag(); // </qmlProperty>
4430         }
4431         else if (qpn->qmlPropNodes().size() == 1) {
4432             Node* n = qpn->qmlPropNodes().at(0);
4433             if (n->type() == Node::QmlProperty) {
4434                 qpn = static_cast<QmlPropertyNode*>(n);
4435                 startQmlProperty(qpn,relative,marker);
4436                 writeApiDesc(node, marker, node->title());
4437                 writeEndTag(); // </qmlPropertyDetail>
4438                 writeEndTag(); // </qmlProperty>
4439             }
4440         }
4441         else {
4442             /*
4443               The QML property node has multiple override nodes.
4444               Process the whole list as we would for a QML property
4445               group.
4446              */
4447             writeStartTag(DT_qmlPropertyGroup);
4448             QString id = "id-qml-propertygroup-" + node->name();
4449             id.replace('.','-');
4450             xmlWriter().writeAttribute("id",id);
4451             writeStartTag(DT_apiName);
4452             //writeCharacters("...");
4453             writeEndTag(); // </apiName>
4454             writeStartTag(DT_qmlPropertyGroupDetail);
4455             writeApiDesc(node, marker, node->title());
4456             writeEndTag(); // </qmlPropertyGroupDetail>
4457             NodeList::ConstIterator p = qpn->qmlPropNodes().constBegin();
4458             while (p != qpn->qmlPropNodes().constEnd()) {
4459                 if ((*p)->type() == Node::QmlProperty) {
4460                     QmlPropertyNode* q = static_cast<QmlPropertyNode*>(*p);
4461                     startQmlProperty(q,relative,marker);
4462                     writeEndTag(); // </qmlPropertyDetail>
4463                     writeEndTag(); // </qmlProperty>
4464                 }
4465                 ++p;
4466             }
4467             writeEndTag(); // </qmlPropertyGroup
4468         }
4469     }
4470     else if (node->type() == Node::QmlSignal)
4471         writeQmlRef(DT_qmlSignal,node,relative,marker);
4472     else if (node->type() == Node::QmlSignalHandler)
4473         writeQmlRef(DT_qmlSignalHandler,node,relative,marker);
4474     else if (node->type() == Node::QmlMethod)
4475         writeQmlRef(DT_qmlMethod,node,relative,marker);
4476 }
4477
4478 /*!
4479   Outputs the DITA detailed documentation for a section
4480   on a QML element reference page.
4481  */
4482 void DitaXmlGenerator::writeQmlRef(DitaTag tag,
4483                                    Node* node,
4484                                    const InnerNode* relative,
4485                                    CodeMarker* marker)
4486 {
4487     writeStartTag(tag);
4488     Node* n = const_cast<Node*>(node);
4489     writeGuidAttribute(n);
4490     writeStartTag(DT_apiName);
4491     writeCharacters(n->name());
4492     writeEndTag(); // </apiName>
4493     writeStartTag((DitaTag)((int)tag+2));
4494     writeStartTag((DitaTag)((int)tag+1));
4495     writeStartTag(DT_apiData);
4496     QString marked = getMarkedUpSynopsis(n, relative, marker, CodeMarker::Detailed);
4497     writeText(marked, marker, relative);
4498     writeEndTag(); // </apiData>
4499     if (node->isAttached()) {
4500         writeStartTag(DT_qmlAttached);
4501         xmlWriter().writeAttribute("name","attached");
4502         xmlWriter().writeAttribute("value","yes");
4503         writeEndTag(); // </qmlAttached>
4504     }
4505     writeEndTag(); // </qmlXxxDef>
4506     writeApiDesc(node, marker, node->title());
4507     writeEndTag(); // </qmlXxxDetail>
4508     writeEndTag(); // tag
4509 }
4510
4511 /*!
4512   This generates a <qmlTypeDef> in which the
4513   QML module name and version number are specified.
4514  */
4515 void DitaXmlGenerator::generateQmlModuleDef(QmlClassNode* qcn)
4516 {
4517     writeStartTag(DT_qmlImportModule);
4518     writeStartTag(DT_apiItemName);
4519     writeCharacters(qcn->qmlModuleName());
4520     writeEndTag(); // </apiItemName>
4521     writeStartTag(DT_apiData);
4522     writeCharacters(qcn->qmlModuleVersion());
4523     writeEndTag(); // </apiData>
4524     writeEndTag(); // </qmlImportModule>
4525 }
4526
4527 /*!
4528   Output the "Inherits" line for the QML element,
4529   if there should be one.
4530  */
4531 void DitaXmlGenerator::generateQmlInherits(const QmlClassNode* qcn, CodeMarker* marker)
4532 {
4533     if (!qcn)
4534         return;
4535     const FakeNode* base = qcn->qmlBase();
4536     if (base) {
4537         writeStartTag(DT_qmlInherits);
4538         //writeStartTag(DT_qmlTypeDef);
4539         //xmlWriter().writeAttribute("outputclass","inherits");
4540         writeStartTag(DT_apiData);
4541         Text text;
4542         text << Atom(Atom::LinkNode,CodeMarker::stringForNode(base));
4543         text << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK);
4544         text << Atom(Atom::String, base->name());
4545         text << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK);
4546         generateText(text, qcn, marker);
4547         writeEndTag(); // </apiData>
4548         writeEndTag(); // </qmlInherits>
4549     }
4550 }
4551
4552 /*!
4553   Output the "Inherit by" list for the QML element,
4554   if it is inherited by any other elements.
4555  */
4556 void DitaXmlGenerator::generateQmlInheritedBy(const QmlClassNode* qcn, CodeMarker* marker)
4557 {
4558     if (qcn) {
4559         NodeList subs;
4560         QmlClassNode::subclasses(qcn->name(),subs);
4561         if (!subs.isEmpty()) {
4562             writeStartTag(DT_qmlInheritedBy);
4563             //writeStartTag(DT_qmlTypeDef);
4564             //xmlWriter().writeAttribute("outputclass","inherited-by");
4565             writeStartTag(DT_apiData);
4566             Text text;
4567             appendSortedQmlNames(text,qcn,subs,marker);
4568             text << Atom::ParaRight;
4569             generateText(text, qcn, marker);
4570             writeEndTag(); // </apiData>
4571             writeEndTag(); // </qmlIneritedBy>
4572         }
4573     }
4574 }
4575
4576 /*!
4577   Output the "[Xxx instantiates the C++ class QmlGraphicsXxx]"
4578   line for the QML element, if there should be one.
4579
4580   If there is no class node, or if the class node status
4581   is set to Node::Internal, do nothing.
4582  */
4583 void DitaXmlGenerator::generateQmlInstantiates(QmlClassNode* qcn, CodeMarker* marker)
4584 {
4585     ClassNode* cn = qcn->classNode();
4586     if (cn && (cn->status() != Node::Internal)) {
4587         writeStartTag(DT_qmlInstantiates);
4588         //writeStartTag(DT_qmlTypeDef);
4589         //xmlWriter().writeAttribute("outputclass","instantiates");
4590         writeStartTag(DT_apiData);
4591         Text text;
4592         text << Atom(Atom::LinkNode,CodeMarker::stringForNode(cn));
4593         text << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK);
4594         text << Atom(Atom::String, cn->name());
4595         text << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK);
4596         generateText(text, qcn, marker);
4597         writeEndTag(); // </apiData>
4598         writeEndTag(); // </qmlInstantiates>
4599     }
4600 }
4601
4602 /*!
4603   Generate a <qmlXxxDef> for the "since" version string, if there is one.
4604  */
4605 void DitaXmlGenerator::generateQmlSince(const Node* node)
4606 {
4607     if (!node->since().isEmpty()) {
4608         writeStartTag(DT_qmlSince);
4609         //writeStartTag(DT_qmlTypeDef);
4610         //xmlWriter().writeAttribute("outputclass","since");
4611         writeStartTag(DT_apiItemName);
4612         QStringList pieces = node->since().split(QLatin1Char(' '));
4613         writeCharacters(pieces[0]);
4614         writeEndTag(); // </apiItemName>
4615         writeStartTag(DT_apiData);
4616         if (pieces.size() > 1)
4617             writeCharacters(pieces[1]);
4618         writeEndTag(); // </apiData>
4619         writeEndTag(); // </qmlSince>
4620     }
4621 }
4622
4623 /*!
4624   Output the "[QmlGraphicsXxx is instantiated by QML Type Xxx]"
4625   line for the class, if there should be one.
4626
4627   If there is no QML element, or if the class node status
4628   is set to Node::Internal, do nothing.
4629  */
4630 void DitaXmlGenerator::generateInstantiatedBy(ClassNode* cn, CodeMarker* marker)
4631 {
4632     if (cn &&  cn->status() != Node::Internal && cn->qmlElement() != 0) {
4633         const QmlClassNode* qcn = cn->qmlElement();
4634         writeStartTag(DT_p);
4635         xmlWriter().writeAttribute("outputclass","instantiated-by");
4636         Text text;
4637         text << "[";
4638         text << Atom(Atom::LinkNode,CodeMarker::stringForNode(cn));
4639         text << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK);
4640         text << Atom(Atom::String, cn->name());
4641         text << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK);
4642         text << " is instantiated by QML Type ";
4643         text << Atom(Atom::LinkNode,CodeMarker::stringForNode(qcn));
4644         text << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK);
4645         text << Atom(Atom::String, qcn->name());
4646         text << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK);
4647         text << "]";
4648         generateText(text, cn, marker);
4649         writeEndTag(); // </p>
4650     }
4651 }
4652
4653 /*!
4654   Return the full qualification of the node \a n, but without
4655   the name of \a n itself. e.g. A::B::C
4656  */
4657 QString DitaXmlGenerator::fullQualification(const Node* n)
4658 {
4659     QString fq;
4660     InnerNode* in = n->parent();
4661     while (in) {
4662         if ((in->type() == Node::Class) ||
4663                 (in->type() == Node::Namespace)) {
4664             if (in->name().isEmpty())
4665                 break;
4666             if (fq.isEmpty())
4667                 fq = in->name();
4668             else
4669                 fq = in->name() + "::" + fq;
4670         }
4671         else
4672             break;
4673         in = in->parent();
4674     }
4675     return fq;
4676 }
4677
4678 /*!
4679   Outputs the <cxxClassDerivations> element.
4680   \code
4681  <cxxClassDerivations>
4682   <cxxClassDerivation>
4683    ...
4684   </cxxClassDerivation>
4685   ...
4686  </cxxClassDerivations>
4687   \endcode
4688
4689   The <cxxClassDerivation> element is:
4690
4691   \code
4692  <cxxClassDerivation>
4693   <cxxClassDerivationAccessSpecifier value="public"/>
4694   <cxxClassBaseClass href="class_base">Base</cxxClassBaseClass>
4695  </cxxClassDerivation>
4696   \endcode
4697  */
4698 void DitaXmlGenerator::writeDerivations(const ClassNode* cn, CodeMarker* marker)
4699 {
4700     QList<RelatedClass>::ConstIterator r;
4701
4702     if (!cn->baseClasses().isEmpty()) {
4703         writeStartTag(DT_cxxClassDerivations);
4704         r = cn->baseClasses().constBegin();
4705         while (r != cn->baseClasses().constEnd()) {
4706             writeStartTag(DT_cxxClassDerivation);
4707             writeStartTag(DT_cxxClassDerivationAccessSpecifier);
4708             xmlWriter().writeAttribute("value",(*r).accessString());
4709             writeEndTag(); // </cxxClassDerivationAccessSpecifier>
4710
4711             // not included: <cxxClassDerivationVirtual>
4712
4713             writeStartTag(DT_cxxClassBaseClass);
4714             QString attr = fileName((*r).node) + QLatin1Char('#') + (*r).node->guid();
4715             xmlWriter().writeAttribute("href",attr);
4716             writeCharacters(marker->plainFullName((*r).node));
4717             writeEndTag(); // </cxxClassBaseClass>
4718
4719             // not included: <ClassBaseStruct> or <cxxClassBaseUnion>
4720
4721             writeEndTag(); // </cxxClassDerivation>
4722
4723             // not included: <cxxStructDerivation>
4724
4725             ++r;
4726         }
4727         writeEndTag(); // </cxxClassDerivations>
4728     }
4729 }
4730
4731 /*!
4732   Writes a <cxxXXXAPIItemLocation> element, depending on the
4733   type of the node \a n, which can be a class, function, enum,
4734   typedef, or property.
4735  */
4736 void DitaXmlGenerator::writeLocation(const Node* n)
4737 {
4738     DitaTag s1, s2, s3a, s3b;
4739     s1 = DT_cxxClassAPIItemLocation;
4740     s2 = DT_cxxClassDeclarationFile;
4741     s3a = DT_cxxClassDeclarationFileLineStart;
4742     s3b = DT_cxxClassDeclarationFileLineEnd;
4743     if (n->type() == Node::Class || n->type() == Node::Namespace) {
4744         s1 = DT_cxxClassAPIItemLocation;
4745         s2 = DT_cxxClassDeclarationFile;
4746         s3a = DT_cxxClassDeclarationFileLineStart;
4747         s3b = DT_cxxClassDeclarationFileLineEnd;
4748     }
4749     else if (n->type() == Node::Function) {
4750         FunctionNode* fn = const_cast<FunctionNode*>(static_cast<const FunctionNode*>(n));
4751         if (fn->isMacro()) {
4752             s1 = DT_cxxDefineAPIItemLocation;
4753             s2 = DT_cxxDefineDeclarationFile;
4754             s3a = DT_cxxDefineDeclarationFileLine;
4755             s3b = DT_NONE;
4756         }
4757         else {
4758             s1 = DT_cxxFunctionAPIItemLocation;
4759             s2 = DT_cxxFunctionDeclarationFile;
4760             s3a = DT_cxxFunctionDeclarationFileLine;
4761             s3b = DT_NONE;
4762         }
4763     }
4764     else if (n->type() == Node::Enum) {
4765         s1 = DT_cxxEnumerationAPIItemLocation;
4766         s2 = DT_cxxEnumerationDeclarationFile;
4767         s3a = DT_cxxEnumerationDeclarationFileLineStart;
4768         s3b = DT_cxxEnumerationDeclarationFileLineEnd;
4769     }
4770     else if (n->type() == Node::Typedef) {
4771         s1 = DT_cxxTypedefAPIItemLocation;
4772         s2 = DT_cxxTypedefDeclarationFile;
4773         s3a = DT_cxxTypedefDeclarationFileLine;
4774         s3b = DT_NONE;
4775     }
4776     else if ((n->type() == Node::Property) ||
4777              (n->type() == Node::Variable)) {
4778         s1 = DT_cxxVariableAPIItemLocation;
4779         s2 = DT_cxxVariableDeclarationFile;
4780         s3a = DT_cxxVariableDeclarationFileLine;
4781         s3b = DT_NONE;
4782     }
4783     writeStartTag(s1);
4784     writeStartTag(s2);
4785     xmlWriter().writeAttribute("name","filePath");
4786     xmlWriter().writeAttribute("value",n->location().filePath());
4787     writeEndTag(); // <s2>
4788     writeStartTag(s3a);
4789     xmlWriter().writeAttribute("name","lineNumber");
4790     QString lineNr;
4791     xmlWriter().writeAttribute("value",lineNr.setNum(n->location().lineNo()));
4792     writeEndTag(); // </s3a>
4793     if (s3b != DT_NONE) {
4794         writeStartTag(s3b);
4795         xmlWriter().writeAttribute("name","lineNumber");
4796         QString lineNr;
4797         xmlWriter().writeAttribute("value",lineNr.setNum(n->location().lineNo()));
4798         writeEndTag(); // </s3b>
4799     }
4800     writeEndTag(); // </cxx<s1>ApiItemLocation>
4801 }
4802
4803 /*!
4804   Write the <cxxFunction> elements.
4805  */
4806 void DitaXmlGenerator::writeFunctions(const Section& s,
4807                                       const InnerNode* parent,
4808                                       CodeMarker* marker,
4809                                       const QString& attribute)
4810 {
4811     NodeList::ConstIterator m = s.members.constBegin();
4812     while (m != s.members.constEnd()) {
4813         if ((*m)->type() == Node::Function) {
4814             FunctionNode* fn = const_cast<FunctionNode*>(static_cast<const FunctionNode*>(*m));
4815             writeStartTag(DT_cxxFunction);
4816             xmlWriter().writeAttribute("id",fn->guid());
4817             if (fn->metaness() == FunctionNode::Signal)
4818                 xmlWriter().writeAttribute("otherprops","signal");
4819             else if (fn->metaness() == FunctionNode::Slot)
4820                 xmlWriter().writeAttribute("otherprops","slot");
4821             if (!attribute.isEmpty())
4822                 xmlWriter().writeAttribute("outputclass",attribute);
4823             writeStartTag(DT_apiName);
4824             writeCharacters(fn->name());
4825             writeEndTag(); // </apiName>
4826             generateBrief(fn,marker);
4827
4828             // not included: <prolog>
4829
4830             writeStartTag(DT_cxxFunctionDetail);
4831             writeStartTag(DT_cxxFunctionDefinition);
4832             writeStartTag(DT_cxxFunctionAccessSpecifier);
4833             xmlWriter().writeAttribute("value",fn->accessString());
4834             writeEndTag(); // <cxxFunctionAccessSpecifier>
4835
4836             // not included: <cxxFunctionStorageClassSpecifierExtern>
4837
4838             if (fn->isStatic()) {
4839                 writeStartTag(DT_cxxFunctionStorageClassSpecifierStatic);
4840                 xmlWriter().writeAttribute("name","static");
4841                 xmlWriter().writeAttribute("value","static");
4842                 writeEndTag(); // <cxxFunctionStorageClassSpecifierStatic>
4843             }
4844
4845             // not included: <cxxFunctionStorageClassSpecifierMutable>,
4846
4847             if (fn->isConst()) {
4848                 writeStartTag(DT_cxxFunctionConst);
4849                 xmlWriter().writeAttribute("name","const");
4850                 xmlWriter().writeAttribute("value","const");
4851                 writeEndTag(); // <cxxFunctionConst>
4852             }
4853
4854             // not included: <cxxFunctionExplicit>
4855             //               <cxxFunctionInline
4856
4857             if (fn->virtualness() != FunctionNode::NonVirtual) {
4858                 writeStartTag(DT_cxxFunctionVirtual);
4859                 xmlWriter().writeAttribute("name","virtual");
4860                 xmlWriter().writeAttribute("value","virtual");
4861                 writeEndTag(); // <cxxFunctionVirtual>
4862                 if (fn->virtualness() == FunctionNode::PureVirtual) {
4863                     writeStartTag(DT_cxxFunctionPureVirtual);
4864                     xmlWriter().writeAttribute("name","pure virtual");
4865                     xmlWriter().writeAttribute("value","pure virtual");
4866                     writeEndTag(); // <cxxFunctionPureVirtual>
4867                 }
4868             }
4869
4870             if (fn->name() == parent->name()) {
4871                 writeStartTag(DT_cxxFunctionConstructor);
4872                 xmlWriter().writeAttribute("name","constructor");
4873                 xmlWriter().writeAttribute("value","constructor");
4874                 writeEndTag(); // <cxxFunctionConstructor>
4875             }
4876             else if (fn->name()[0] == QChar('~')) {
4877                 writeStartTag(DT_cxxFunctionDestructor);
4878                 xmlWriter().writeAttribute("name","destructor");
4879                 xmlWriter().writeAttribute("value","destructor");
4880                 writeEndTag(); // <cxxFunctionDestructor>
4881             }
4882             else {
4883                 writeStartTag(DT_cxxFunctionDeclaredType);
4884                 QString src = marker->typified(fn->returnType());
4885                 replaceTypesWithLinks(fn,parent,marker,src);
4886                 writeEndTag(); // <cxxFunctionDeclaredType>
4887             }
4888
4889             // not included: <cxxFunctionReturnType>
4890
4891             QString fq = fullQualification(fn);
4892             if (!fq.isEmpty()) {
4893                 writeStartTag(DT_cxxFunctionScopedName);
4894                 writeCharacters(fq);
4895                 writeEndTag(); // <cxxFunctionScopedName>
4896             }
4897             writeStartTag(DT_cxxFunctionPrototype);
4898             writeCharacters(fn->signature(true));
4899             writeEndTag(); // <cxxFunctionPrototype>
4900
4901             QString fnl = fn->signature(false);
4902             int idx = fnl.indexOf(' ');
4903             if (idx < 0)
4904                 idx = 0;
4905             else
4906                 ++idx;
4907             fnl = fn->parent()->name() + "::" + fnl.mid(idx);
4908             writeStartTag(DT_cxxFunctionNameLookup);
4909             writeCharacters(fnl);
4910             writeEndTag(); // <cxxFunctionNameLookup>
4911
4912             if (!fn->isInternal() && fn->isReimp() && fn->reimplementedFrom() != 0) {
4913                 FunctionNode* rfn = (FunctionNode*)fn->reimplementedFrom();
4914                 if (rfn && !rfn->isInternal()) {
4915                     writeStartTag(DT_cxxFunctionReimplemented);
4916                     xmlWriter().writeAttribute("href",rfn->ditaXmlHref());
4917                     writeCharacters(marker->plainFullName(rfn));
4918                     writeEndTag(); // </cxxFunctionReimplemented>
4919                 }
4920             }
4921             writeParameters(fn,parent,marker);
4922             writeLocation(fn);
4923             writeEndTag(); // <cxxFunctionDefinition>
4924
4925             writeApiDesc(fn, marker, QString());
4926             // generateAlsoList(inner, marker);
4927
4928             // not included: <example> or <apiImpl>
4929
4930             writeEndTag(); // </cxxFunctionDetail>
4931             writeEndTag(); // </cxxFunction>
4932
4933             if (fn->metaness() == FunctionNode::Ctor ||
4934                     fn->metaness() == FunctionNode::Dtor ||
4935                     fn->overloadNumber() != 1) {
4936             }
4937         }
4938         ++m;
4939     }
4940 }
4941
4942 static const QString typeTag("type");
4943 static const QChar charLangle = '<';
4944 static const QChar charAt = '@';
4945
4946 /*!
4947   This function replaces class and enum names with <apiRelation>
4948   elements, i.e. links.
4949  */
4950 void DitaXmlGenerator::replaceTypesWithLinks(const Node* n,
4951                                              const InnerNode* parent,
4952                                              CodeMarker* marker,
4953                                              QString& src)
4954 {
4955     QStringRef arg;
4956     QStringRef par1;
4957     int srcSize = src.size();
4958     QString text;
4959     for (int i=0; i<srcSize;) {
4960         if (src.at(i) == charLangle && src.at(i+1) == charAt) {
4961             if (!text.isEmpty()) {
4962                 writeCharacters(text);
4963                 text.clear();
4964             }
4965             i += 2;
4966             if (parseArg(src, typeTag, &i, srcSize, &arg, &par1)) {
4967                 const Node* tn = marker->resolveTarget(arg.toString(), tree_, parent, n);
4968                 if (tn) {
4969                     //Do not generate a link from a C++ function to a QML Basic Type (such as int)
4970                     if (n->type() == Node::Function && tn->subType() == Node::QmlBasicType)
4971                         writeCharacters(arg.toString());
4972                     else
4973                         addLink(linkForNode(tn,parent),arg,DT_apiRelation);
4974                 }
4975                 else {
4976                     // Write simple arguments, like void and bool,
4977                     // which do not have a Qt defined target.
4978                     writeCharacters(arg.toString());
4979                 }
4980             }
4981         }
4982         else {
4983             text += src.at(i++);
4984         }
4985     }
4986     if (!text.isEmpty()) {
4987         writeCharacters(text);
4988         text.clear();
4989     }
4990 }
4991
4992 /*!
4993   This function writes the <cxxFunctionParameters> element.
4994  */
4995 void DitaXmlGenerator::writeParameters(const FunctionNode* fn,
4996                                        const InnerNode* parent,
4997                                        CodeMarker* marker)
4998 {
4999     const QList<Parameter>& parameters = fn->parameters();
5000     if (!parameters.isEmpty()) {
5001         writeStartTag(DT_cxxFunctionParameters);
5002         QList<Parameter>::ConstIterator p = parameters.constBegin();
5003         while (p != parameters.constEnd()) {
5004             writeStartTag(DT_cxxFunctionParameter);
5005             writeStartTag(DT_cxxFunctionParameterDeclaredType);
5006             QString src = marker->typified((*p).leftType());
5007             replaceTypesWithLinks(fn,parent,marker,src);
5008             //writeCharacters((*p).leftType());
5009             if (!(*p).rightType().isEmpty())
5010                 writeCharacters((*p).rightType());
5011             writeEndTag(); // <cxxFunctionParameterDeclaredType>
5012             writeStartTag(DT_cxxFunctionParameterDeclarationName);
5013             writeCharacters((*p).name());
5014             writeEndTag(); // <cxxFunctionParameterDeclarationName>
5015
5016             // not included: <cxxFunctionParameterDefinitionName>
5017
5018             if (!(*p).defaultValue().isEmpty()) {
5019                 writeStartTag(DT_cxxFunctionParameterDefaultValue);
5020                 writeCharacters((*p).defaultValue());
5021                 writeEndTag(); // <cxxFunctionParameterDefaultValue>
5022             }
5023
5024             // not included: <apiDefNote>
5025
5026             writeEndTag(); // <cxxFunctionParameter>
5027             ++p;
5028         }
5029         writeEndTag(); // <cxxFunctionParameters>
5030     }
5031 }
5032
5033 /*!
5034   This function writes the enum types.
5035  */
5036 void DitaXmlGenerator::writeEnumerations(const Section& s,
5037                                          CodeMarker* marker,
5038                                          const QString& attribute)
5039 {
5040     NodeList::ConstIterator m = s.members.constBegin();
5041     while (m != s.members.constEnd()) {
5042         if ((*m)->type() == Node::Enum) {
5043             const EnumNode* en = static_cast<const EnumNode*>(*m);
5044             writeStartTag(DT_cxxEnumeration);
5045             xmlWriter().writeAttribute("id",en->guid());
5046             if (!attribute.isEmpty())
5047                 xmlWriter().writeAttribute("outputclass",attribute);
5048             writeStartTag(DT_apiName);
5049             writeCharacters(en->name());
5050             writeEndTag(); // </apiName>
5051             generateBrief(en,marker);
5052
5053             // not included <prolog>
5054
5055             writeStartTag(DT_cxxEnumerationDetail);
5056             writeStartTag(DT_cxxEnumerationDefinition);
5057             writeStartTag(DT_cxxEnumerationAccessSpecifier);
5058             xmlWriter().writeAttribute("value",en->accessString());
5059             writeEndTag(); // <cxxEnumerationAccessSpecifier>
5060
5061             QString fq = fullQualification(en);
5062             if (!fq.isEmpty()) {
5063                 writeStartTag(DT_cxxEnumerationScopedName);
5064                 writeCharacters(fq);
5065                 writeEndTag(); // <cxxEnumerationScopedName>
5066             }
5067             const QList<EnumItem>& items = en->items();
5068             if (!items.isEmpty()) {
5069                 writeStartTag(DT_cxxEnumerationPrototype);
5070                 writeCharacters(en->name());
5071                 xmlWriter().writeCharacters(" = { ");
5072                 QList<EnumItem>::ConstIterator i = items.constBegin();
5073                 while (i != items.constEnd()) {
5074                     writeCharacters((*i).name());
5075                     if (!(*i).value().isEmpty()) {
5076                         xmlWriter().writeCharacters(" = ");
5077                         writeCharacters((*i).value());
5078                     }
5079                     ++i;
5080                     if (i != items.constEnd())
5081                         xmlWriter().writeCharacters(", ");
5082                 }
5083                 xmlWriter().writeCharacters(" }");
5084                 writeEndTag(); // <cxxEnumerationPrototype>
5085             }
5086
5087             writeStartTag(DT_cxxEnumerationNameLookup);
5088             writeCharacters(en->parent()->name() + "::" + en->name());
5089             writeEndTag(); // <cxxEnumerationNameLookup>
5090
5091             // not included: <cxxEnumerationReimplemented>
5092
5093             if (!items.isEmpty()) {
5094                 writeStartTag(DT_cxxEnumerators);
5095                 QList<EnumItem>::ConstIterator i = items.constBegin();
5096                 while (i != items.constEnd()) {
5097                     writeStartTag(DT_cxxEnumerator);
5098                     writeStartTag(DT_apiName);
5099                     writeCharacters((*i).name());
5100                     writeEndTag(); // </apiName>
5101
5102                     QString fq = fullQualification(en->parent());
5103                     if (!fq.isEmpty()) {
5104                         writeStartTag(DT_cxxEnumeratorScopedName);
5105                         writeCharacters(fq + "::" + (*i).name());
5106                         writeEndTag(); // <cxxEnumeratorScopedName>
5107                     }
5108                     writeStartTag(DT_cxxEnumeratorPrototype);
5109                     writeCharacters((*i).name());
5110                     writeEndTag(); // <cxxEnumeratorPrototype>
5111                     writeStartTag(DT_cxxEnumeratorNameLookup);
5112                     writeCharacters(en->parent()->name() + "::" + (*i).name());
5113                     writeEndTag(); // <cxxEnumeratorNameLookup>
5114
5115                     if (!(*i).value().isEmpty()) {
5116                         writeStartTag(DT_cxxEnumeratorInitialiser);
5117                         if ((*i).value().toInt(0,16) == 0)
5118                             xmlWriter().writeAttribute("value", "0");
5119                         else
5120                             xmlWriter().writeAttribute("value", (*i).value());
5121                         writeEndTag(); // <cxxEnumeratorInitialiser>
5122                     }
5123
5124                     // not included: <cxxEnumeratorAPIItemLocation>
5125
5126                     if (!(*i).text().isEmpty()) {
5127                         writeStartTag(DT_apiDesc);
5128                         generateText((*i).text(), en, marker);
5129                         writeEndTag(); // </apiDesc>
5130                     }
5131                     writeEndTag(); // <cxxEnumerator>
5132                     ++i;
5133                 }
5134                 writeEndTag(); // <cxxEnumerators>
5135             }
5136
5137             writeLocation(en);
5138             writeEndTag(); // <cxxEnumerationDefinition>
5139
5140             writeApiDesc(en, marker, QString());
5141
5142             // not included: <example> or <apiImpl>
5143
5144             writeEndTag(); // </cxxEnumerationDetail>
5145
5146             // not included: <related-links>
5147
5148             writeEndTag(); // </cxxEnumeration>
5149         }
5150         ++m;
5151     }
5152 }
5153
5154 /*!
5155   This function writes the output for the \typedef commands.
5156  */
5157 void DitaXmlGenerator::writeTypedefs(const Section& s,
5158                                      CodeMarker* marker,
5159                                      const QString& attribute)
5160
5161 {
5162     NodeList::ConstIterator m = s.members.constBegin();
5163     while (m != s.members.constEnd()) {
5164         if ((*m)->type() == Node::Typedef) {
5165             const TypedefNode* tn = static_cast<const TypedefNode*>(*m);
5166             writeStartTag(DT_cxxTypedef);
5167             xmlWriter().writeAttribute("id",tn->guid());
5168             if (!attribute.isEmpty())
5169                 xmlWriter().writeAttribute("outputclass",attribute);
5170             writeStartTag(DT_apiName);
5171             writeCharacters(tn->name());
5172             writeEndTag(); // </apiName>
5173             generateBrief(tn,marker);
5174
5175             // not included: <prolog>
5176
5177             writeStartTag(DT_cxxTypedefDetail);
5178             writeStartTag(DT_cxxTypedefDefinition);
5179             writeStartTag(DT_cxxTypedefAccessSpecifier);
5180             xmlWriter().writeAttribute("value",tn->accessString());
5181             writeEndTag(); // <cxxTypedefAccessSpecifier>
5182
5183             // not included: <cxxTypedefDeclaredType>
5184
5185             QString fq = fullQualification(tn);
5186             if (!fq.isEmpty()) {
5187                 writeStartTag(DT_cxxTypedefScopedName);
5188                 writeCharacters(fq);
5189                 writeEndTag(); // <cxxTypedefScopedName>
5190             }
5191
5192             // not included: <cxxTypedefPrototype>
5193
5194             writeStartTag(DT_cxxTypedefNameLookup);
5195             writeCharacters(tn->parent()->name() + "::" + tn->name());
5196             writeEndTag(); // <cxxTypedefNameLookup>
5197
5198             // not included: <cxxTypedefReimplemented>
5199
5200             writeLocation(tn);
5201             writeEndTag(); // <cxxTypedefDefinition>
5202
5203             writeApiDesc(tn, marker, QString());
5204
5205             // not included: <example> or <apiImpl>
5206
5207             writeEndTag(); // </cxxTypedefDetail>
5208
5209             // not included: <related-links>
5210
5211             writeEndTag(); // </cxxTypedef>
5212         }
5213         ++m;
5214     }
5215 }
5216
5217 /*!
5218   This function writes the output for the \property commands.
5219   This is the Q_PROPERTYs.
5220  */
5221 void DitaXmlGenerator::writeProperties(const Section& s,
5222                                        CodeMarker* marker,
5223                                        const QString& attribute)
5224 {
5225     NodeList::ConstIterator m = s.members.constBegin();
5226     while (m != s.members.constEnd()) {
5227         if ((*m)->type() == Node::Property) {
5228             const PropertyNode* pn = static_cast<const PropertyNode*>(*m);
5229             writeStartTag(DT_cxxVariable);
5230             xmlWriter().writeAttribute("id",pn->guid());
5231             if (!attribute.isEmpty())
5232                 xmlWriter().writeAttribute("outputclass",attribute);
5233             writeStartTag(DT_apiName);
5234             writeCharacters(pn->name());
5235             writeEndTag(); // </apiName>
5236             generateBrief(pn,marker);
5237
5238             // not included: <prolog>
5239
5240             writeStartTag(DT_cxxVariableDetail);
5241             writeStartTag(DT_cxxVariableDefinition);
5242             writeStartTag(DT_cxxVariableAccessSpecifier);
5243             xmlWriter().writeAttribute("value",pn->accessString());
5244             writeEndTag(); // <cxxVariableAccessSpecifier>
5245
5246             // not included: <cxxVariableStorageClassSpecifierExtern>,
5247             //               <cxxVariableStorageClassSpecifierStatic>,
5248             //               <cxxVariableStorageClassSpecifierMutable>,
5249             //               <cxxVariableConst>, <cxxVariableVolatile>
5250
5251             if (!pn->qualifiedDataType().isEmpty()) {
5252                 writeStartTag(DT_cxxVariableDeclaredType);
5253                 writeCharacters(pn->qualifiedDataType());
5254                 writeEndTag(); // <cxxVariableDeclaredType>
5255             }
5256             QString fq = fullQualification(pn);
5257             if (!fq.isEmpty()) {
5258                 writeStartTag(DT_cxxVariableScopedName);
5259                 writeCharacters(fq);
5260                 writeEndTag(); // <cxxVariableScopedName>
5261             }
5262
5263             writeStartTag(DT_cxxVariablePrototype);
5264             xmlWriter().writeCharacters("Q_PROPERTY(");
5265             writeCharacters(pn->qualifiedDataType());
5266             xmlWriter().writeCharacters(" ");
5267             writeCharacters(pn->name());
5268             writePropertyParameter("READ",pn->getters());
5269             writePropertyParameter("WRITE",pn->setters());
5270             writePropertyParameter("RESET",pn->resetters());
5271             writePropertyParameter("NOTIFY",pn->notifiers());
5272             if (pn->isDesignable() != pn->designableDefault()) {
5273                 xmlWriter().writeCharacters(" DESIGNABLE ");
5274                 if (!pn->runtimeDesignabilityFunction().isEmpty())
5275                     writeCharacters(pn->runtimeDesignabilityFunction());
5276                 else
5277                     xmlWriter().writeCharacters(pn->isDesignable() ? "true" : "false");
5278             }
5279             if (pn->isScriptable() != pn->scriptableDefault()) {
5280                 xmlWriter().writeCharacters(" SCRIPTABLE ");
5281                 if (!pn->runtimeScriptabilityFunction().isEmpty())
5282                     writeCharacters(pn->runtimeScriptabilityFunction());
5283                 else
5284                     xmlWriter().writeCharacters(pn->isScriptable() ? "true" : "false");
5285             }
5286             if (pn->isWritable() != pn->writableDefault()) {
5287                 xmlWriter().writeCharacters(" STORED ");
5288                 xmlWriter().writeCharacters(pn->isStored() ? "true" : "false");
5289             }
5290             if (pn->isUser() != pn->userDefault()) {
5291                 xmlWriter().writeCharacters(" USER ");
5292                 xmlWriter().writeCharacters(pn->isUser() ? "true" : "false");
5293             }
5294             if (pn->isConstant())
5295                 xmlWriter().writeCharacters(" CONSTANT");
5296             if (pn->isFinal())
5297                 xmlWriter().writeCharacters(" FINAL");
5298             xmlWriter().writeCharacters(")");
5299             writeEndTag(); // <cxxVariablePrototype>
5300
5301             writeStartTag(DT_cxxVariableNameLookup);
5302             writeCharacters(pn->parent()->name() + "::" + pn->name());
5303             writeEndTag(); // <cxxVariableNameLookup>
5304
5305             if (pn->overriddenFrom() != 0) {
5306                 PropertyNode* opn = (PropertyNode*)pn->overriddenFrom();
5307                 writeStartTag(DT_cxxVariableReimplemented);
5308                 xmlWriter().writeAttribute("href",opn->ditaXmlHref());
5309                 writeCharacters(marker->plainFullName(opn));
5310                 writeEndTag(); // </cxxVariableReimplemented>
5311             }
5312
5313             writeLocation(pn);
5314             writeEndTag(); // <cxxVariableDefinition>
5315
5316             writeApiDesc(pn, marker, QString());
5317
5318             // not included: <example> or <apiImpl>
5319
5320             writeEndTag(); // </cxxVariableDetail>
5321
5322             // not included: <related-links>
5323
5324             writeEndTag(); // </cxxVariable>
5325         }
5326         ++m;
5327     }
5328 }
5329
5330 /*!
5331   This function outputs the nodes resulting from \variable commands.
5332  */
5333 void DitaXmlGenerator::writeDataMembers(const Section& s,
5334                                         CodeMarker* marker,
5335                                         const QString& attribute)
5336 {
5337     NodeList::ConstIterator m = s.members.constBegin();
5338     while (m != s.members.constEnd()) {
5339         if ((*m)->type() == Node::Variable) {
5340             const VariableNode* vn = static_cast<const VariableNode*>(*m);
5341             writeStartTag(DT_cxxVariable);
5342             xmlWriter().writeAttribute("id",vn->guid());
5343             if (!attribute.isEmpty())
5344                 xmlWriter().writeAttribute("outputclass",attribute);
5345             writeStartTag(DT_apiName);
5346             writeCharacters(vn->name());
5347             writeEndTag(); // </apiName>
5348             generateBrief(vn,marker);
5349
5350             // not included: <prolog>
5351
5352             writeStartTag(DT_cxxVariableDetail);
5353             writeStartTag(DT_cxxVariableDefinition);
5354             writeStartTag(DT_cxxVariableAccessSpecifier);
5355             xmlWriter().writeAttribute("value",vn->accessString());
5356             writeEndTag(); // <cxxVariableAccessSpecifier>
5357
5358             // not included: <cxxVAriableStorageClassSpecifierExtern>
5359
5360             if (vn->isStatic()) {
5361                 writeStartTag(DT_cxxVariableStorageClassSpecifierStatic);
5362                 xmlWriter().writeAttribute("name","static");
5363                 xmlWriter().writeAttribute("value","static");
5364                 writeEndTag(); // <cxxVariableStorageClassSpecifierStatic>
5365             }
5366
5367             // not included: <cxxVAriableStorageClassSpecifierMutable>,
5368             //               <cxxVariableConst>, <cxxVariableVolatile>
5369
5370             writeStartTag(DT_cxxVariableDeclaredType);
5371             writeCharacters(vn->leftType());
5372             if (!vn->rightType().isEmpty())
5373                 writeCharacters(vn->rightType());
5374             writeEndTag(); // <cxxVariableDeclaredType>
5375
5376             QString fq = fullQualification(vn);
5377             if (!fq.isEmpty()) {
5378                 writeStartTag(DT_cxxVariableScopedName);
5379                 writeCharacters(fq);
5380                 writeEndTag(); // <cxxVariableScopedName>
5381             }
5382
5383             writeStartTag(DT_cxxVariablePrototype);
5384             writeCharacters(vn->leftType() + QLatin1Char(' '));
5385             //writeCharacters(vn->parent()->name() + "::" + vn->name());
5386             writeCharacters(vn->name());
5387             if (!vn->rightType().isEmpty())
5388                 writeCharacters(vn->rightType());
5389             writeEndTag(); // <cxxVariablePrototype>
5390
5391             writeStartTag(DT_cxxVariableNameLookup);
5392             writeCharacters(vn->parent()->name() + "::" + vn->name());
5393             writeEndTag(); // <cxxVariableNameLookup>
5394
5395             // not included: <cxxVariableReimplemented>
5396
5397             writeLocation(vn);
5398             writeEndTag(); // <cxxVariableDefinition>
5399
5400             writeApiDesc(vn, marker, QString());
5401
5402             // not included: <example> or <apiImpl>
5403
5404             writeEndTag(); // </cxxVariableDetail>
5405
5406             // not included: <related-links>
5407
5408             writeEndTag(); // </cxxVariable>
5409         }
5410         ++m;
5411     }
5412 }
5413
5414 /*!
5415   This function writes a \macro as a <cxxDefine>.
5416  */
5417 void DitaXmlGenerator::writeMacros(const Section& s,
5418                                    CodeMarker* marker,
5419                                    const QString& attribute)
5420 {
5421     NodeList::ConstIterator m = s.members.constBegin();
5422     while (m != s.members.constEnd()) {
5423         if ((*m)->type() == Node::Function) {
5424             const FunctionNode* fn = static_cast<const FunctionNode*>(*m);
5425             if (fn->isMacro()) {
5426                 writeStartTag(DT_cxxDefine);
5427                 xmlWriter().writeAttribute("id",fn->guid());
5428                 if (!attribute.isEmpty())
5429                     xmlWriter().writeAttribute("outputclass",attribute);
5430                 writeStartTag(DT_apiName);
5431                 writeCharacters(fn->name());
5432                 writeEndTag(); // </apiName>
5433                 generateBrief(fn,marker);
5434
5435                 // not included: <prolog>
5436
5437                 writeStartTag(DT_cxxDefineDetail);
5438                 writeStartTag(DT_cxxDefineDefinition);
5439                 writeStartTag(DT_cxxDefineAccessSpecifier);
5440                 xmlWriter().writeAttribute("value",fn->accessString());
5441                 writeEndTag(); // <cxxDefineAccessSpecifier>
5442
5443                 writeStartTag(DT_cxxDefinePrototype);
5444                 xmlWriter().writeCharacters("#define ");
5445                 writeCharacters(fn->name());
5446                 if (fn->metaness() == FunctionNode::MacroWithParams) {
5447                     QStringList params = fn->parameterNames();
5448                     if (!params.isEmpty()) {
5449                         xmlWriter().writeCharacters("(");
5450                         for (int i = 0; i < params.size(); ++i) {
5451                             if (params[i].isEmpty())
5452                                 xmlWriter().writeCharacters("...");
5453                             else
5454                                 writeCharacters(params[i]);
5455                             if ((i+1) < params.size())
5456                                 xmlWriter().writeCharacters(", ");
5457                         }
5458                         xmlWriter().writeCharacters(")");
5459                     }
5460                 }
5461                 writeEndTag(); // <cxxDefinePrototype>
5462
5463                 writeStartTag(DT_cxxDefineNameLookup);
5464                 writeCharacters(fn->name());
5465                 writeEndTag(); // <cxxDefineNameLookup>
5466
5467                 if (fn->reimplementedFrom() != 0) {
5468                     FunctionNode* rfn = (FunctionNode*)fn->reimplementedFrom();
5469                     writeStartTag(DT_cxxDefineReimplemented);
5470                     xmlWriter().writeAttribute("href",rfn->ditaXmlHref());
5471                     writeCharacters(marker->plainFullName(rfn));
5472                     writeEndTag(); // </cxxDefineReimplemented>
5473                 }
5474
5475                 if (fn->metaness() == FunctionNode::MacroWithParams) {
5476                     QStringList params = fn->parameterNames();
5477                     if (!params.isEmpty()) {
5478                         writeStartTag(DT_cxxDefineParameters);
5479                         for (int i = 0; i < params.size(); ++i) {
5480                             writeStartTag(DT_cxxDefineParameter);
5481                             writeStartTag(DT_cxxDefineParameterDeclarationName);
5482                             writeCharacters(params[i]);
5483                             writeEndTag(); // <cxxDefineParameterDeclarationName>
5484
5485                             // not included: <apiDefNote>
5486
5487                             writeEndTag(); // <cxxDefineParameter>
5488                         }
5489                         writeEndTag(); // <cxxDefineParameters>
5490                     }
5491                 }
5492
5493                 writeLocation(fn);
5494                 writeEndTag(); // <cxxDefineDefinition>
5495
5496                 writeApiDesc(fn, marker, QString());
5497
5498                 // not included: <example> or <apiImpl>
5499
5500                 writeEndTag(); // </cxxDefineDetail>
5501
5502                 // not included: <related-links>
5503
5504                 writeEndTag(); // </cxxDefine>
5505             }
5506         }
5507         ++m;
5508     }
5509 }
5510
5511 /*!
5512   This function writes one parameter of a Q_PROPERTY macro.
5513   The property is identified by \a tag ("READ" "WRIE" etc),
5514   and it is found in the 'a nlist.
5515  */
5516 void DitaXmlGenerator::writePropertyParameter(const QString& tag, const NodeList& nlist)
5517 {
5518     NodeList::const_iterator n = nlist.constBegin();
5519     while (n != nlist.constEnd()) {
5520         xmlWriter().writeCharacters(" ");
5521         writeCharacters(tag);
5522         xmlWriter().writeCharacters(" ");
5523         writeCharacters((*n)->name());
5524         ++n;
5525     }
5526 }
5527
5528 /*!
5529   Calls beginSubPage() in the base class to open the file.
5530   Then creates a new XML stream writer using the IO device
5531   from opened file and pushes the XML writer onto a stackj.
5532   Creates the file named \a fileName in the output directory.
5533   Attaches a QTextStream to the created file, which is written
5534   to all over the place using out(). Finally, it sets some
5535   parameters in the XML writer and calls writeStartDocument().
5536
5537   It also ensures that a GUID map is created for the output file.
5538  */
5539 void DitaXmlGenerator::beginSubPage(const InnerNode* node,
5540                                     const QString& fileName)
5541 {
5542     Generator::beginSubPage(node,fileName);
5543     (void) lookupGuidMap(fileName);
5544     QXmlStreamWriter* writer = new QXmlStreamWriter(out().device());
5545     xmlWriterStack.push(writer);
5546     writer->setAutoFormatting(true);
5547     writer->setAutoFormattingIndent(4);
5548     writer->writeStartDocument();
5549     clearSectionNesting();
5550 }
5551
5552 /*!
5553   Calls writeEndDocument() and then pops the XML stream writer
5554   off the stack and deletes it. Then it calls endSubPage() in
5555   the base class to close the device.
5556  */
5557 void DitaXmlGenerator::endSubPage()
5558 {
5559     if (inSection())
5560         qDebug() << "Missing </section> in" << outFileName() << sectionNestingLevel;
5561     xmlWriter().writeEndDocument();
5562     delete xmlWriterStack.pop();
5563     Generator::endSubPage();
5564 }
5565
5566 /*!
5567   Returns a reference to the XML stream writer currently in use.
5568   There is one XML stream writer open for each XML file being
5569   written, and they are kept on a stack. The one on top of the
5570   stack is the one being written to at the moment.
5571  */
5572 QXmlStreamWriter& DitaXmlGenerator::xmlWriter()
5573 {
5574     return *xmlWriterStack.top();
5575 }
5576
5577 /*!
5578   Writes the \e {<apiDesc>} element for \a node to the current XML
5579   stream using the code \a marker and the \a title.
5580  */
5581 void DitaXmlGenerator::writeApiDesc(const Node* node,
5582                                     CodeMarker* marker,
5583                                     const QString& title)
5584 {
5585     if (!node->doc().isEmpty()) {
5586         inDetailedDescription = true;
5587         enterDesc(DT_apiDesc,QString(),title);
5588         generateBody(node, marker);
5589         generateAlsoList(node, marker);
5590         leaveSection();
5591     }
5592     inDetailedDescription = false;
5593 }
5594
5595 /*!
5596   Write the nested class elements.
5597  */
5598 void DitaXmlGenerator::writeNestedClasses(const Section& s,
5599                                           const Node* n)
5600 {
5601     if (s.members.isEmpty())
5602         return;
5603     writeStartTag(DT_cxxClassNested);
5604     writeStartTag(DT_cxxClassNestedDetail);
5605
5606     NodeList::ConstIterator m = s.members.constBegin();
5607     while (m != s.members.constEnd()) {
5608         if ((*m)->type() == Node::Class) {
5609             writeStartTag(DT_cxxClassNestedClass);
5610             QString link = linkForNode((*m), n);
5611             xmlWriter().writeAttribute("href", link);
5612             QString name = n->name() + "::" + (*m)->name();
5613             writeCharacters(name);
5614             writeEndTag(); // <cxxClassNestedClass>
5615         }
5616         ++m;
5617     }
5618     writeEndTag(); // <cxxClassNestedDetail>
5619     writeEndTag(); // <cxxClassNested>
5620 }
5621
5622 /*!
5623   Recursive writing of DITA XML files from the root \a node.
5624  */
5625 void
5626 DitaXmlGenerator::generateInnerNode(InnerNode* node)
5627 {
5628     if (!node->url().isNull())
5629         return;
5630
5631     if (node->type() == Node::Fake) {
5632         FakeNode* fakeNode = static_cast<FakeNode*>(node);
5633         if (fakeNode->subType() == Node::ExternalPage)
5634             return;
5635         if (fakeNode->subType() == Node::Image)
5636             return;
5637         if (fakeNode->subType() == Node::QmlPropertyGroup)
5638             return;
5639         if (fakeNode->subType() == Node::Page) {
5640             if (node->count() > 0)
5641                 qDebug("PAGE %s HAS CHILDREN", qPrintable(fakeNode->title()));
5642         }
5643     }
5644
5645     /*
5646       Obtain a code marker for the source file.
5647      */
5648     CodeMarker *marker = CodeMarker::markerForFileName(node->location().filePath());
5649     if (node->parent() != 0) {
5650         /*
5651           Skip name collision nodes here and process them
5652           later in generateCollisionPages(). Each one is
5653           appended to a list for later.
5654          */
5655         if ((node->type() == Node::Fake) && (node->subType() == Node::Collision)) {
5656             NameCollisionNode* ncn = static_cast<NameCollisionNode*>(node);
5657             collisionNodes.append(const_cast<NameCollisionNode*>(ncn));
5658         }
5659         else {
5660             if (!node->name().endsWith(".ditamap"))
5661                 beginSubPage(node, fileName(node));
5662             if (node->type() == Node::Namespace || node->type() == Node::Class) {
5663                 generateClassLikeNode(node, marker);
5664             }
5665             else if (node->type() == Node::Fake) {
5666                 if (node->subType() == Node::HeaderFile)
5667                     generateClassLikeNode(node, marker);
5668                 else if (node->subType() == Node::QmlClass)
5669                     generateClassLikeNode(node, marker);
5670                 else
5671                     generateFakeNode(static_cast<FakeNode*>(node), marker);
5672             }
5673             if (!node->name().endsWith(".ditamap"))
5674                 endSubPage();
5675         }
5676     }
5677
5678     NodeList::ConstIterator c = node->childNodes().constBegin();
5679     while (c != node->childNodes().constEnd()) {
5680         if ((*c)->isInnerNode() && (*c)->access() != Node::Private)
5681             generateInnerNode((InnerNode*)*c);
5682         ++c;
5683     }
5684 }
5685
5686 /*!
5687   Returns true if \a format is "DITAXML" or "HTML" .
5688  */
5689 bool DitaXmlGenerator::canHandleFormat(const QString& format)
5690 {
5691     return (format == "HTML") || (format == this->format());
5692 }
5693
5694 /*!
5695   If the node multimap \a nmm contains nodes mapped to \a key,
5696   if any of the nodes mapped to \a key has the same href as the
5697   \a node, return true. Otherwise, return false.
5698  */
5699 bool DitaXmlGenerator::isDuplicate(NodeMultiMap* nmm, const QString& key, Node* node)
5700 {
5701     QList<Node*> matches = nmm->values(key);
5702     if (!matches.isEmpty()) {
5703         for (int i=0; i<matches.size(); ++i) {
5704             if (matches[i] == node)
5705                 return true;
5706             if (fileName(node) == fileName(matches[i]))
5707                 return true;
5708         }
5709     }
5710     return false;
5711 }
5712 /*!
5713   Collect all the nodes in the tree according to their type or subtype.
5714
5715   If a node is found that is named index.html, return that node as the
5716   root page node.
5717
5718   type: Class
5719   type: Namespace
5720
5721   subtype: Example
5722   subtype: External page
5723   subtype: Group
5724   subtype: Header file
5725   subtype: Module
5726   subtype: Page
5727   subtype: QML basic type
5728   subtype: QML class
5729   subtype: QML module
5730  */
5731 Node* DitaXmlGenerator::collectNodesByTypeAndSubtype(const InnerNode* parent)
5732 {
5733     Node* rootPageNode = 0;
5734     const NodeList& children = parent->childNodes();
5735     if (children.size() == 0)
5736         return rootPageNode;
5737
5738     QString message;
5739     for (int i=0; i<children.size(); ++i) {
5740         Node* child = children[i];
5741         if ((child->type() == Node::Fake) && (child->subType() == Node::Collision)) {
5742             const FakeNode* fake = static_cast<const FakeNode*>(child);
5743             Node* n = collectNodesByTypeAndSubtype(fake);
5744             if (n)
5745                 rootPageNode = n;
5746             continue;
5747         }
5748         if (!child || child->isInternal() || child->doc().isEmpty() || child->isIndexNode())
5749             continue;
5750
5751         if (child->name() == "index.html" || child->name() == "index") {
5752             rootPageNode = child;
5753         }
5754
5755         switch (child->type()) {
5756         case Node::Namespace:
5757             if (!isDuplicate(nodeTypeMaps[Node::Namespace],child->name(),child))
5758                 nodeTypeMaps[Node::Namespace]->insert(child->name(),child);
5759             break;
5760         case Node::Class:
5761             if (!isDuplicate(nodeTypeMaps[Node::Class],child->name(),child))
5762                 nodeTypeMaps[Node::Class]->insert(child->name(),child);
5763             break;
5764         case Node::Fake:
5765             switch (child->subType()) {
5766             case Node::Example:
5767                 if (!isDuplicate(nodeSubtypeMaps[Node::Example],child->title(),child))
5768                     nodeSubtypeMaps[Node::Example]->insert(child->title(),child);
5769                 break;
5770             case Node::HeaderFile:
5771                 if (!isDuplicate(nodeSubtypeMaps[Node::HeaderFile],child->title(),child))
5772                     nodeSubtypeMaps[Node::HeaderFile]->insert(child->title(),child);
5773                 break;
5774             case Node::File:
5775                 break;
5776             case Node::Image:
5777                 break;
5778             case Node::Group:
5779                 if (!isDuplicate(nodeSubtypeMaps[Node::Group],child->title(),child))
5780                     nodeSubtypeMaps[Node::Group]->insert(child->title(),child);
5781                 break;
5782             case Node::Module:
5783                 if (!isDuplicate(nodeSubtypeMaps[Node::Module],child->title(),child))
5784                     nodeSubtypeMaps[Node::Module]->insert(child->title(),child);
5785                 break;
5786             case Node::Page:
5787                 if (!isDuplicate(pageTypeMaps[child->pageType()],child->title(),child))
5788                     pageTypeMaps[child->pageType()]->insert(child->title(),child);
5789                 break;
5790             case Node::ExternalPage:
5791                 if (!isDuplicate(nodeSubtypeMaps[Node::ExternalPage],child->title(),child))
5792                     nodeSubtypeMaps[Node::ExternalPage]->insert(child->title(),child);
5793                 break;
5794             case Node::QmlClass:
5795                 if (!isDuplicate(nodeSubtypeMaps[Node::QmlClass],child->title(),child))
5796                     nodeSubtypeMaps[Node::QmlClass]->insert(child->title(),child);
5797                 break;
5798             case Node::QmlPropertyGroup:
5799                 break;
5800             case Node::QmlBasicType:
5801                 if (!isDuplicate(nodeSubtypeMaps[Node::QmlBasicType],child->title(),child))
5802                     nodeSubtypeMaps[Node::QmlBasicType]->insert(child->title(),child);
5803                 break;
5804             case Node::QmlModule:
5805                 if (!isDuplicate(nodeSubtypeMaps[Node::QmlModule],child->title(),child))
5806                     nodeSubtypeMaps[Node::QmlModule]->insert(child->title(),child);
5807                 break;
5808             case Node::Collision:
5809                 if (!isDuplicate(nodeSubtypeMaps[Node::Collision],child->title(),child))
5810                     nodeSubtypeMaps[Node::Collision]->insert(child->title(),child);
5811                 break;
5812             default:
5813                 break;
5814             }
5815             break;
5816         case Node::Enum:
5817             break;
5818         case Node::Typedef:
5819             break;
5820         case Node::Function:
5821             break;
5822         case Node::Property:
5823             break;
5824         case Node::Variable:
5825             break;
5826         case Node::QmlProperty:
5827             break;
5828         case Node::QmlSignal:
5829             break;
5830         case Node::QmlSignalHandler:
5831             break;
5832         case Node::QmlMethod:
5833             break;
5834         default:
5835             break;
5836         }
5837     }
5838     return rootPageNode;
5839 }
5840
5841 /*!
5842   Creates the DITA map for the qdoc run. The map is written
5843   to the file \e{qt.ditamap" in the DITA XML output directory.
5844  */
5845 void DitaXmlGenerator::writeDitaMap(Tree *tree)
5846 {
5847     QString doctype;
5848
5849 /*
5850     Remove #if 0 to get a flat ditamap.
5851 */
5852 #if 0
5853     beginSubPage(tree->root(),"qt.ditamap");
5854     doctype = "<!DOCTYPE map PUBLIC \"-//OASIS//DTD DITA Map//EN\" \"map.dtd\">";
5855     xmlWriter().writeDTD(doctype);
5856     writeStartTag(DT_map);
5857     writeStartTag(DT_topicmeta);
5858     writeStartTag(DT_shortdesc);
5859     xmlWriter().writeCharacters("The top level map for the Qt documentation");
5860     writeEndTag(); // </shortdesc>
5861     writeEndTag(); // </topicmeta>
5862     GuidMaps::iterator i = guidMaps.constBegin();
5863     while (i != guidMaps.constEnd()) {
5864         writeStartTag(DT_topicref);
5865         if (i.key() != "qt.ditamap")
5866             xmlWriter().writeAttribute("href",i.key());
5867         writeEndTag(); // </topicref>
5868         ++i;
5869     }
5870     endSubPage();
5871 #endif
5872
5873     for (unsigned i=0; i<Node::LastType; ++i)
5874         nodeTypeMaps[i] = new NodeMultiMap;
5875     for (unsigned i=0; i<Node::LastSubtype; ++i)
5876         nodeSubtypeMaps[i] = new NodeMultiMap;
5877     for (unsigned i=0; i<Node::OnBeyondZebra; ++i)
5878         pageTypeMaps[i] = new NodeMultiMap;
5879     Node* rootPageNode = collectNodesByTypeAndSubtype(tree->root());
5880
5881     beginSubPage(tree->root(),"qt.ditamap");
5882
5883     doctype = "<!DOCTYPE map PUBLIC \"-//OASIS//DTD DITA Map//EN\" \"map.dtd\">";
5884     xmlWriter().writeDTD(doctype);
5885     writeStartTag(DT_map);
5886     writeStartTag(DT_topicmeta);
5887     writeStartTag(DT_shortdesc);
5888     xmlWriter().writeCharacters("The top level map for the Qt documentation");
5889     writeEndTag(); // </shortdesc>
5890     writeEndTag(); // </topicmeta>
5891
5892     writeStartTag(DT_topicref);
5893     if (rootPageNode) {
5894         if (!rootPageNode->title().isEmpty())
5895             xmlWriter().writeAttribute("navtitle",rootPageNode->title());
5896         else
5897             xmlWriter().writeAttribute("navtitle",project);
5898         xmlWriter().writeAttribute("href",fileName(rootPageNode));
5899     }
5900     else
5901         xmlWriter().writeAttribute("navtitle",project);
5902
5903     writeTopicrefs(pageTypeMaps[Node::OverviewPage], "Overviews");
5904     writeTopicrefs(pageTypeMaps[Node::HowToPage], "Howtos");
5905     writeTopicrefs(pageTypeMaps[Node::TutorialPage], "Tutorials");
5906     writeTopicrefs(pageTypeMaps[Node::FAQPage], "FAQs");
5907     writeTopicrefs(pageTypeMaps[Node::ArticlePage], "Articles");
5908     writeTopicrefs(nodeSubtypeMaps[Node::Example], "Examples");
5909     if (nodeSubtypeMaps[Node::QmlModule]->size() > 1)
5910         writeTopicrefs(nodeSubtypeMaps[Node::QmlModule], "QML modules");
5911     if (nodeSubtypeMaps[Node::QmlModule]->size() == 1)
5912         writeTopicrefs(nodeSubtypeMaps[Node::QmlClass], "QML types", nodeSubtypeMaps[Node::QmlModule]->values()[0]);
5913     else
5914         writeTopicrefs(nodeSubtypeMaps[Node::QmlClass], "QML types");
5915     writeTopicrefs(nodeSubtypeMaps[Node::QmlBasicType], "QML basic types");
5916     if (nodeSubtypeMaps[Node::Module]->size() > 1)
5917         writeTopicrefs(nodeSubtypeMaps[Node::Module], "Modules");
5918     if (nodeSubtypeMaps[Node::Module]->size() == 1)
5919         writeTopicrefs(nodeTypeMaps[Node::Class], "C++ classes", nodeSubtypeMaps[Node::Module]->values()[0]);
5920     else
5921         writeTopicrefs(nodeTypeMaps[Node::Class], "C++ classes");
5922     writeTopicrefs(nodeTypeMaps[Node::Namespace], "C++ namespaces");
5923     writeTopicrefs(nodeSubtypeMaps[Node::HeaderFile], "Header files");
5924     writeTopicrefs(nodeSubtypeMaps[Node::Group], "Groups");
5925
5926     writeEndTag(); // </topicref>
5927     endSubPage();
5928
5929     for (unsigned i=0; i<Node::LastType; ++i)
5930         delete nodeTypeMaps[i];
5931     for (unsigned i=0; i<Node::LastSubtype; ++i)
5932         delete nodeSubtypeMaps[i];
5933     for (unsigned i=0; i<Node::OnBeyondZebra; ++i)
5934         delete pageTypeMaps[i];
5935 }
5936
5937 /*!
5938   Creates the DITA map from the topicrefs in \a node,
5939   which is a DitaMapNode.
5940  */
5941 void DitaXmlGenerator::writeDitaMap(const DitaMapNode* node)
5942 {
5943     beginSubPage(node,node->name());
5944
5945     QString doctype;
5946     doctype = "<!DOCTYPE map PUBLIC \"-//OASIS//DTD DITA Map//EN\" \"map.dtd\">";
5947     xmlWriter().writeDTD(doctype);
5948     writeStartTag(DT_map);
5949     writeStartTag(DT_topicmeta);
5950     writeStartTag(DT_shortdesc);
5951     xmlWriter().writeCharacters(node->title());
5952     writeEndTag(); // </shortdesc>
5953     writeEndTag(); // </topicmeta>
5954     const DitaRefList map = node->map();
5955     writeDitaRefs(map);
5956     endSubPage();
5957 }
5958
5959 /*!
5960   Write the \a ditarefs to the current output file.
5961  */
5962 void DitaXmlGenerator::writeDitaRefs(const DitaRefList& ditarefs)
5963 {
5964     foreach (DitaRef* t, ditarefs) {
5965         if (t->isMapRef())
5966             writeStartTag(DT_mapref);
5967         else
5968             writeStartTag(DT_topicref);
5969         xmlWriter().writeAttribute("navtitle",t->navtitle());
5970         if (t->href().isEmpty()) {
5971             const FakeNode* fn = tree_->findFakeNodeByTitle(t->navtitle());
5972             if (fn)
5973                 xmlWriter().writeAttribute("href",fileName(fn));
5974         }
5975         else
5976             xmlWriter().writeAttribute("href",t->href());
5977         if (t->subrefs() && !t->subrefs()->isEmpty())
5978             writeDitaRefs(*(t->subrefs()));
5979         writeEndTag(); // </topicref> or </mapref>
5980     }
5981 }
5982
5983 void DitaXmlGenerator::writeTopicrefs(NodeMultiMap* nmm, const QString& navtitle, Node* headingnode)
5984 {
5985     if (!nmm || nmm->isEmpty())
5986         return;
5987     writeStartTag(DT_topicref);
5988     xmlWriter().writeAttribute("navtitle",navtitle);
5989     if (headingnode)
5990         xmlWriter().writeAttribute("href",fileName(headingnode));
5991     NodeMultiMap::iterator i;
5992     NodeMultiMap *ditaMaps = pageTypeMaps[Node::DitaMapPage];
5993
5994     /*!
5995        Put all pages that are already in a hand-written ditamap not in
5996        the qt.ditamap separately. It loops through all ditamaps recursively
5997        before deciding to write an article to qt.ditamap.
5998      */
5999     if ((navtitle == "Articles" && ditaMaps && ditaMaps->size() > 0)) {
6000         NodeMultiMap::iterator mapIterator = ditaMaps->begin();
6001         while (mapIterator != ditaMaps->end()) {
6002             writeStartTag(DT_mapref);
6003             xmlWriter().writeAttribute("navtitle",mapIterator.key());
6004             xmlWriter().writeAttribute("href",fileName(mapIterator.value()));
6005             writeEndTag();
6006             ++mapIterator;
6007         }
6008         i = nmm->begin();
6009         while (i != nmm->end()) {
6010             // Hardcode not writing index.dita multiple times in the tree.
6011             // index.dita should only appear at the top of the ditamap.
6012             if (fileName(i.value()) == "index.dita") {
6013                 i++;
6014                 continue;
6015             }
6016             bool foundInDitaMap = false;
6017             mapIterator = ditaMaps->begin();
6018             while (mapIterator != ditaMaps->end()) {
6019                 const DitaMapNode *dmNode = static_cast<const DitaMapNode *>(mapIterator.value());
6020                 for (int count = 0; count < dmNode->map().count(); count++) {
6021                     if (dmNode->map().at(count)->navtitle() == i.key()) {
6022                         foundInDitaMap = true;
6023                         break;
6024                     }
6025                 }
6026                 ++mapIterator;
6027             }
6028             if (!foundInDitaMap) {
6029                 writeStartTag(DT_topicref);
6030                 xmlWriter().writeAttribute("navtitle",i.key());
6031                 xmlWriter().writeAttribute("href",fileName(i.value()));
6032                 writeEndTag(); // </topicref>
6033             }
6034             ++i;
6035         }
6036     }
6037     /*!
6038        Shortcut when there are no hand-written ditamaps or when we are
6039        not generating the articles list.
6040      */
6041     else {
6042         i = nmm->begin();
6043         while (i != nmm->end()) {
6044             // Hardcode not writing index.dita multiple times in the tree.
6045             // index.dita should only appear at the top of the ditamap.
6046             if (fileName(i.value()) == "index.dita") {
6047                 i++;
6048                 continue;
6049             }
6050             writeStartTag(DT_topicref);
6051             xmlWriter().writeAttribute("navtitle",i.key());
6052             xmlWriter().writeAttribute("href",fileName(i.value()));
6053             switch (i.value()->type()) {
6054                 case Node::Class:
6055                 case Node::Namespace: {
6056                     const NamespaceNode* nn = static_cast<const NamespaceNode*>(i.value());
6057                     const NodeList& c = nn->childNodes();
6058                     QMap<QString, const Node*> tempMap;
6059                     for (int j=0; j<c.size(); ++j) {
6060                         if (c[j]->isInternal() || c[j]->access() == Node::Private || c[j]->doc().isEmpty())
6061                             continue;
6062                         if (c[j]->type() == Node::Class) {
6063                             tempMap.insert(c[j]->name(), c[j]);
6064                         }
6065                     }
6066                     QMap<QString, const Node*>::iterator classIterator = tempMap.begin();
6067                     while (classIterator != tempMap.end()) {
6068                         const Node* currentNode = static_cast<const Node*>(classIterator.value());
6069                         writeStartTag(DT_topicref);
6070                         xmlWriter().writeAttribute("navtitle",currentNode->name());
6071                         xmlWriter().writeAttribute("href",fileName(currentNode));
6072                         writeEndTag(); // </topicref>
6073                         ++classIterator;
6074                     }
6075
6076                     break;
6077                 }
6078                 default:
6079                     break;
6080             }
6081             writeEndTag(); // </topicref>
6082             ++i;
6083         }
6084     }
6085     writeEndTag(); // </topicref>
6086 }
6087
6088
6089 /*!
6090   Looks up the tag name for \a t in the map of metadata
6091   values for the current topic in \a inner. If a value
6092   for the tag is found, the element is written with the
6093   found value. Otherwise if \a force is set, an empty
6094   element is written using the tag.
6095
6096   Returns true or false depending on whether it writes
6097   an element using the tag \a t.
6098
6099   \note If \a t is found in the metadata map, it is erased.
6100   i.e. Once you call this function for a particular \a t,
6101   you consume \a t.
6102  */
6103 bool DitaXmlGenerator::writeMetadataElement(const InnerNode* inner,
6104                                             DitaXmlGenerator::DitaTag t,
6105                                             bool force)
6106 {
6107     QString s = getMetadataElement(inner,t);
6108     if (s.isEmpty() && !force)
6109         return false;
6110     writeStartTag(t);
6111     if (!s.isEmpty())
6112         xmlWriter().writeCharacters(s);
6113     writeEndTag();
6114     return true;
6115 }
6116
6117
6118 /*!
6119   Looks up the tag name for \a t in the map of metadata
6120   values for the current topic in \a inner. If one or more
6121   value sfor the tag are found, the elements are written.
6122   Otherwise nothing is written.
6123
6124   Returns true or false depending on whether it writes
6125   at least one element using the tag \a t.
6126
6127   \note If \a t is found in the metadata map, it is erased.
6128   i.e. Once you call this function for a particular \a t,
6129   you consume \a t.
6130  */
6131 bool DitaXmlGenerator::writeMetadataElements(const InnerNode* inner,
6132                                              DitaXmlGenerator::DitaTag t)
6133 {
6134     QStringList s = getMetadataElements(inner,t);
6135     if (s.isEmpty())
6136         return false;
6137     for (int i=0; i<s.size(); ++i) {
6138         writeStartTag(t);
6139         xmlWriter().writeCharacters(s[i]);
6140         writeEndTag();
6141     }
6142     return true;
6143 }
6144
6145 /*!
6146   Looks up the tag name for \a t in the map of metadata
6147   values for the current topic in \a inner. If a value
6148   for the tag is found, the value is returned.
6149
6150   \note If \a t is found in the metadata map, it is erased.
6151   i.e. Once you call this function for a particular \a t,
6152   you consume \a t.
6153  */
6154 QString DitaXmlGenerator::getMetadataElement(const InnerNode* inner, DitaXmlGenerator::DitaTag t)
6155 {
6156     QString s = Generator::getMetadataElement(inner, ditaTags[t]);
6157     if (s.isEmpty())
6158         s = metadataDefault(t);
6159     return s;
6160 }
6161
6162 /*!
6163   Looks up the tag name for \a t in the map of metadata
6164   values for the current topic in \a inner. If values
6165   for the tag are found, they are returned in a string
6166   list.
6167
6168   \note If \a t is found in the metadata map, all the
6169   pairs having the key \a t are erased. i.e. Once you
6170   all this function for a particular \a t, you consume
6171   \a t.
6172  */
6173 QStringList DitaXmlGenerator::getMetadataElements(const InnerNode* inner,
6174                                                   DitaXmlGenerator::DitaTag t)
6175 {
6176     QStringList s = Generator::getMetadataElements(inner,ditaTags[t]);
6177     if (s.isEmpty())
6178         s.append(metadataDefault(t));
6179     return s;
6180 }
6181
6182 /*!
6183   Returns the value of key \a t or an empty string
6184   if \a t is not found in the map.
6185  */
6186 QString DitaXmlGenerator::metadataDefault(DitaTag t) const
6187 {
6188     return metadataDefaults.value(ditaTags[t]);
6189 }
6190
6191 /*!
6192   Writes the <prolog> element for the \a inner node
6193   using the \a marker. The <prolog> element contains
6194   the <metadata> element, plus some others. This
6195   function writes one or more of these elements:
6196
6197   \list
6198     \o <audience> *
6199     \o <author> *
6200     \o <brand> not used
6201     \o <category> *
6202     \o <compomnent> *
6203     \o <copyrholder> *
6204     \o <copyright> *
6205     \o <created> not used
6206     \o <copyryear> *
6207     \o <critdates> not used
6208     \o <keyword> not used
6209     \o <keywords> not used
6210     \o <metadata> *
6211     \o <othermeta> *
6212     \o <permissions> *
6213     \o <platform> not used
6214     \o <prodinfo> *
6215     \o <prodname> *
6216     \o <prolog> *
6217     \o <publisher> *
6218     \o <resourceid> not used
6219     \o <revised> not used
6220     \o <source> not used
6221     \o <tm> not used
6222     \o <unknown> not used
6223     \o <vrm> *
6224     \o <vrmlist> *
6225   \endlist
6226
6227   \node * means the tag has been used.
6228
6229  */
6230 void
6231 DitaXmlGenerator::writeProlog(const InnerNode* inner)
6232 {
6233     if (!inner)
6234         return;
6235     writeStartTag(DT_prolog);
6236     writeMetadataElements(inner,DT_author);
6237     writeMetadataElement(inner,DT_publisher);
6238     QString s = getMetadataElement(inner,DT_copyryear);
6239     QString t = getMetadataElement(inner,DT_copyrholder);
6240     writeStartTag(DT_copyright);
6241     writeStartTag(DT_copyryear);
6242     if (!s.isEmpty())
6243         xmlWriter().writeAttribute("year",s);
6244     writeEndTag(); // </copyryear>
6245     writeStartTag(DT_copyrholder);
6246     if (!s.isEmpty())
6247         xmlWriter().writeCharacters(t);
6248     writeEndTag(); // </copyrholder>
6249     writeEndTag(); // </copyright>
6250     s = getMetadataElement(inner,DT_permissions);
6251     writeStartTag(DT_permissions);
6252     xmlWriter().writeAttribute("view",s);
6253     writeEndTag(); // </permissions>
6254     writeStartTag(DT_metadata);
6255     QStringList sl = getMetadataElements(inner,DT_audience);
6256     if (!sl.isEmpty()) {
6257         for (int i=0; i<sl.size(); ++i) {
6258             writeStartTag(DT_audience);
6259             xmlWriter().writeAttribute("type",sl[i]);
6260             writeEndTag(); // </audience>
6261         }
6262     }
6263     if (!writeMetadataElement(inner,DT_category,false)) {
6264         writeStartTag(DT_category);
6265         QString category = "Page";
6266         if (inner->type() == Node::Class)
6267             category = "Class reference";
6268         else if (inner->type() == Node::Namespace)
6269             category = "Namespace";
6270         else if (inner->type() == Node::Fake) {
6271             if (inner->subType() == Node::QmlClass)
6272                 category = "QML Reference";
6273             else if (inner->subType() == Node::QmlBasicType)
6274                 category = "QML Basic Type";
6275             else if (inner->subType() == Node::HeaderFile)
6276                 category = "Header File";
6277             else if (inner->subType() == Node::Module)
6278                 category = "Module";
6279             else if (inner->subType() == Node::File)
6280                 category = "Example Source File";
6281             else if (inner->subType() == Node::Example)
6282                 category = "Example";
6283             else if (inner->subType() == Node::Image)
6284                 category = "Image";
6285             else if (inner->subType() == Node::Group)
6286                 category = "Group";
6287             else if (inner->subType() == Node::Page)
6288                 category = "Page";
6289             else if (inner->subType() == Node::ExternalPage)
6290                 category = "External Page"; // Is this necessary?
6291         }
6292         xmlWriter().writeCharacters(category);
6293         writeEndTag(); // </category>
6294     }
6295     if (vrm.size() > 0) {
6296         writeStartTag(DT_prodinfo);
6297         if (!writeMetadataElement(inner,DT_prodname,false)) {
6298             writeStartTag(DT_prodname);
6299             xmlWriter().writeCharacters(projectDescription);
6300             writeEndTag(); // </prodname>
6301         }
6302         writeStartTag(DT_vrmlist);
6303         writeStartTag(DT_vrm);
6304         if (vrm.size() > 0)
6305             xmlWriter().writeAttribute("version",vrm[0]);
6306         if (vrm.size() > 1)
6307             xmlWriter().writeAttribute("release",vrm[1]);
6308         if (vrm.size() > 2)
6309             xmlWriter().writeAttribute("modification",vrm[2]);
6310         writeEndTag(); // <vrm>
6311         writeEndTag(); // <vrmlist>
6312         if (!writeMetadataElement(inner,DT_component,false)) {
6313             QString component = inner->moduleName();
6314             if (!component.isEmpty()) {
6315                 writeStartTag(DT_component);
6316                 xmlWriter().writeCharacters(component);
6317                 writeEndTag(); // </component>
6318             }
6319         }
6320         writeEndTag(); // </prodinfo>
6321     }
6322     const QStringMultiMap& metaTagMap = inner->doc().metaTagMap();
6323     QMapIterator<QString, QString> i(metaTagMap);
6324     while (i.hasNext()) {
6325         i.next();
6326         writeStartTag(DT_othermeta);
6327         xmlWriter().writeAttribute("name",i.key());
6328         xmlWriter().writeAttribute("content",i.value());
6329         writeEndTag(); // </othermeta>
6330     }
6331     if ((tagStack.first() == DT_cxxClass && !inner->includes().isEmpty()) ||
6332         (inner->type() == Node::Fake && inner->subType() == Node::HeaderFile)) {
6333         writeStartTag(DT_othermeta);
6334         xmlWriter().writeAttribute("name","includeFile");
6335         QString text;
6336         QStringList::ConstIterator i = inner->includes().constBegin();
6337         while (i != inner->includes().constEnd()) {
6338             if ((*i).startsWith(QLatin1Char('<')) && (*i).endsWith(QLatin1Char('>')))
6339                 text += *i;
6340             else
6341                 text += QLatin1Char('<') + *i + QLatin1Char('>');
6342             ++i;
6343             if (i != inner->includes().constEnd())
6344                 text += "\n";
6345         }
6346         xmlWriter().writeAttribute("content",text);
6347         writeEndTag(); // </othermeta>
6348     }
6349     writeEndTag(); // </metadata>
6350     writeEndTag(); // </prolog>
6351 }
6352
6353 /*!
6354   This function should be called to write the \a href attribute
6355   if the href could be an \e http or \e ftp link. If \a href is
6356   one or the other, a \e scope attribute is also writen, with
6357   value \e external.
6358  */
6359 void DitaXmlGenerator::writeHrefAttribute(const QString& href)
6360 {
6361     xmlWriter().writeAttribute("href", href);
6362     if (href.startsWith("http:") || href.startsWith("ftp:") ||
6363             href.startsWith("https:") || href.startsWith("mailto:"))
6364         xmlWriter().writeAttribute("scope", "external");
6365 }
6366
6367 /*!
6368   Strips the markup tags from \a src, when we are trying to
6369   create an \e{id} attribute. Returns the stripped text.
6370  */
6371 QString DitaXmlGenerator::stripMarkup(const QString& src) const
6372 {
6373     QString text;
6374     const QChar charAt = '@';
6375     const QChar charSlash = '/';
6376     const QChar charLangle = '<';
6377     const QChar charRangle = '>';
6378
6379     int n = src.size();
6380     int i = 0;
6381     while (i < n) {
6382         if (src.at(i) == charLangle) {
6383             ++i;
6384             if (src.at(i) == charAt || (src.at(i) == charSlash && src.at(i+1) == charAt)) {
6385                 while (i < n && src.at(i) != charRangle)
6386                     ++i;
6387                 ++i;
6388             }
6389             else {
6390                 text += charLangle;
6391             }
6392         }
6393         else
6394             text += src.at(i++);
6395     }
6396     return text;
6397 }
6398
6399 /*!
6400   We delayed generation of the collision pages until now, after
6401   all the other pages have been generated. We do this because we might
6402   encounter a link command that tries to link to a target on a QML
6403   type page, but the link doesn't specify the module identifer
6404   for the QML type, and the QML type name without a module
6405   identifier is ambiguous. When such a link is found, qdoc can't find
6406   the target, so it appends the target to the NameCollisionNode. After
6407   the tree has been traversed and all these ambiguous links have been
6408   added to the name collision nodes, this function is called. The list
6409   of collision nodes is traversed here, and the collision page for
6410   each collision is generated. The collision page will not only
6411   disambiguate links to the QML type pages, but it will also disambiguate
6412   links to properties, section headers, etc.
6413  */
6414 void DitaXmlGenerator::generateCollisionPages()
6415 {
6416     if (collisionNodes.isEmpty())
6417         return;
6418
6419     for (int i=0; i<collisionNodes.size(); ++i) {
6420         NameCollisionNode* ncn = collisionNodes.at(i);
6421         if (!ncn)
6422             continue;
6423
6424         NodeList collisions;
6425         const NodeList& nl = ncn->childNodes();
6426         if (!nl.isEmpty()) {
6427             NodeList::ConstIterator it = nl.constBegin();
6428             while (it != nl.constEnd()) {
6429                 if (!(*it)->isInternal())
6430                     collisions.append(*it);
6431                 ++it;
6432             }
6433         }
6434         if (collisions.size() <= 1)
6435             continue;
6436
6437         ncn->clearCurrentChild();
6438         beginSubPage(ncn, Generator::fileName(ncn));
6439         QString fullTitle = ncn->fullTitle();
6440         QString ditaTitle = fullTitle;
6441         CodeMarker* marker = CodeMarker::markerForFileName(ncn->location().filePath());
6442         if (ncn->isQmlNode()) {
6443             // Replace the marker with a QML code marker.
6444             if (ncn->isQmlNode())
6445                 marker = CodeMarker::markerForLanguage(QLatin1String("QML"));
6446         }
6447
6448         generateHeader(ncn, ditaTitle);
6449         writeProlog(ncn);
6450         writeStartTag(DT_body);
6451         enterSection(QString(), QString());
6452
6453         NodeMap nm;
6454         for (int i=0; i<collisions.size(); ++i) {
6455             Node* n = collisions.at(i);
6456             QString t;
6457             if (!n->qmlModuleIdentifier().isEmpty())
6458                 t = n->qmlModuleIdentifier() + QLatin1Char(' ');
6459             t += protectEnc(fullTitle);
6460             nm.insertMulti(t,n);
6461         }
6462         generateAnnotatedList(ncn, marker, nm);
6463
6464         QList<QString> targets;
6465         if (!ncn->linkTargets().isEmpty()) {
6466             QMap<QString,QString>::ConstIterator t = ncn->linkTargets().constBegin();
6467             while (t != ncn->linkTargets().constEnd()) {
6468                 int count = 0;
6469                 for (int i=0; i<collisions.size(); ++i) {
6470                     InnerNode* n = static_cast<InnerNode*>(collisions.at(i));
6471                     if (n->findChildNodeByName(t.key())) {
6472                         ++count;
6473                         if (count > 1) {
6474                             targets.append(t.key());
6475                             break;
6476                         }
6477                     }
6478                 }
6479                 ++t;
6480             }
6481         }
6482         if (!targets.isEmpty()) {
6483             QList<QString>::ConstIterator t = targets.constBegin();
6484             while (t != targets.constEnd()) {
6485                 writeStartTag(DT_p);
6486                 writeGuidAttribute(Doc::canonicalTitle(*t));
6487                 xmlWriter().writeAttribute("outputclass","h2");
6488                 writeCharacters(protectEnc(*t));
6489                 writeEndTag(); // </p>
6490                 writeStartTag(DT_ul);
6491                 for (int i=0; i<collisions.size(); ++i) {
6492                     InnerNode* n = static_cast<InnerNode*>(collisions.at(i));
6493                     Node* p = n->findChildNodeByName(*t);
6494                     if (p) {
6495                         QString link = linkForNode(p,0);
6496                         QString label;
6497                         if (!n->qmlModuleIdentifier().isEmpty())
6498                             label = n->qmlModuleIdentifier() + "::";
6499                         label += n->name() + "::" + p->name();
6500                         writeStartTag(DT_li);
6501                         writeStartTag(DT_xref);
6502                         xmlWriter().writeAttribute("href", link);
6503                         writeCharacters(protectEnc(label));
6504                         writeEndTag(); // </xref>
6505                         writeEndTag(); // </li>
6506                     }
6507                 }
6508                 writeEndTag(); // </ul>
6509                 ++t;
6510             }
6511         }
6512         leaveSection(); // </section>
6513         writeEndTag(); // </body>
6514         endSubPage();
6515     }
6516 }
6517
6518
6519
6520 QT_END_NAMESPACE