Warning: That file was not part of the compilation database. It may have many parsing errors.

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

Warning: That file was not part of the compilation database. It may have many parsing errors.