summaryrefslogtreecommitdiffstats
path: root/tools/qdoc3
diff options
context:
space:
mode:
Diffstat (limited to 'tools/qdoc3')
-rw-r--r--tools/qdoc3/JAVATODO.txt28
-rw-r--r--tools/qdoc3/README.TXT6
-rw-r--r--tools/qdoc3/TODO.txt96
-rw-r--r--tools/qdoc3/apigenerator.cpp150
-rw-r--r--tools/qdoc3/apigenerator.h65
-rw-r--r--tools/qdoc3/archiveextractor.cpp108
-rw-r--r--tools/qdoc3/archiveextractor.h78
-rw-r--r--tools/qdoc3/atom.cpp357
-rw-r--r--tools/qdoc3/atom.h200
-rw-r--r--tools/qdoc3/bookgenerator.cpp64
-rw-r--r--tools/qdoc3/bookgenerator.h64
-rw-r--r--tools/qdoc3/ccodeparser.cpp73
-rw-r--r--tools/qdoc3/ccodeparser.h66
-rw-r--r--tools/qdoc3/codechunk.cpp150
-rw-r--r--tools/qdoc3/codechunk.h123
-rw-r--r--tools/qdoc3/codemarker.cpp538
-rw-r--r--tools/qdoc3/codemarker.h166
-rw-r--r--tools/qdoc3/codeparser.cpp263
-rw-r--r--tools/qdoc3/codeparser.h94
-rw-r--r--tools/qdoc3/command.cpp92
-rw-r--r--tools/qdoc3/command.h60
-rw-r--r--tools/qdoc3/config.cpp892
-rw-r--r--tools/qdoc3/config.h165
-rw-r--r--tools/qdoc3/cppcodemarker.cpp1009
-rw-r--r--tools/qdoc3/cppcodemarker.h91
-rw-r--r--tools/qdoc3/cppcodeparser.cpp2014
-rw-r--r--tools/qdoc3/cppcodeparser.h167
-rw-r--r--tools/qdoc3/cpptoqsconverter.cpp415
-rw-r--r--tools/qdoc3/cpptoqsconverter.h88
-rw-r--r--tools/qdoc3/dcfsection.cpp111
-rw-r--r--tools/qdoc3/dcfsection.h94
-rw-r--r--tools/qdoc3/doc.cpp5036
-rw-r--r--tools/qdoc3/doc.h315
-rw-r--r--tools/qdoc3/documentation.pri5
-rw-r--r--tools/qdoc3/editdistance.cpp111
-rw-r--r--tools/qdoc3/editdistance.h59
-rw-r--r--tools/qdoc3/generator.cpp995
-rw-r--r--tools/qdoc3/generator.h177
-rw-r--r--tools/qdoc3/helpprojectwriter.cpp653
-rw-r--r--tools/qdoc3/helpprojectwriter.h110
-rw-r--r--tools/qdoc3/htmlgenerator.cpp3195
-rw-r--r--tools/qdoc3/htmlgenerator.h253
-rw-r--r--tools/qdoc3/jambiapiparser.cpp547
-rw-r--r--tools/qdoc3/jambiapiparser.h99
-rw-r--r--tools/qdoc3/javacodemarker.cpp201
-rw-r--r--tools/qdoc3/javacodemarker.h80
-rw-r--r--tools/qdoc3/javadocgenerator.cpp453
-rw-r--r--tools/qdoc3/javadocgenerator.h95
-rw-r--r--tools/qdoc3/linguistgenerator.cpp245
-rw-r--r--tools/qdoc3/linguistgenerator.h85
-rw-r--r--tools/qdoc3/location.cpp401
-rw-r--r--tools/qdoc3/location.h131
-rw-r--r--tools/qdoc3/loutgenerator.cpp63
-rw-r--r--tools/qdoc3/loutgenerator.h67
-rw-r--r--tools/qdoc3/main.cpp496
-rw-r--r--tools/qdoc3/mangenerator.cpp228
-rw-r--r--tools/qdoc3/mangenerator.h79
-rw-r--r--tools/qdoc3/node.cpp1024
-rw-r--r--tools/qdoc3/node.h587
-rw-r--r--tools/qdoc3/openedlist.cpp228
-rw-r--r--tools/qdoc3/openedlist.h91
-rw-r--r--tools/qdoc3/pagegenerator.cpp219
-rw-r--r--tools/qdoc3/pagegenerator.h85
-rw-r--r--tools/qdoc3/plaincodemarker.cpp139
-rw-r--r--tools/qdoc3/plaincodemarker.h79
-rw-r--r--tools/qdoc3/polyarchiveextractor.cpp94
-rw-r--r--tools/qdoc3/polyarchiveextractor.h70
-rw-r--r--tools/qdoc3/polyuncompressor.cpp109
-rw-r--r--tools/qdoc3/polyuncompressor.h71
-rw-r--r--tools/qdoc3/qdoc3.pro108
-rw-r--r--tools/qdoc3/qsakernelparser.cpp186
-rw-r--r--tools/qdoc3/qsakernelparser.h77
-rw-r--r--tools/qdoc3/qscodemarker.cpp385
-rw-r--r--tools/qdoc3/qscodemarker.h80
-rw-r--r--tools/qdoc3/qscodeparser.cpp944
-rw-r--r--tools/qdoc3/qscodeparser.h128
-rw-r--r--tools/qdoc3/quoter.cpp369
-rw-r--r--tools/qdoc3/quoter.h89
-rw-r--r--tools/qdoc3/separator.cpp69
-rw-r--r--tools/qdoc3/separator.h57
-rw-r--r--tools/qdoc3/sgmlgenerator.cpp63
-rw-r--r--tools/qdoc3/sgmlgenerator.h67
-rw-r--r--tools/qdoc3/test/arthurtext.qdocconf6
-rw-r--r--tools/qdoc3/test/assistant.qdocconf45
-rw-r--r--tools/qdoc3/test/carbide-eclipse-integration.qdocconf12
-rw-r--r--tools/qdoc3/test/classic.css131
-rw-r--r--tools/qdoc3/test/compat.qdocconf31
-rw-r--r--tools/qdoc3/test/designer.qdocconf51
-rw-r--r--tools/qdoc3/test/eclipse-integration.qdocconf13
-rw-r--r--tools/qdoc3/test/jambi.qdocconf47
-rw-r--r--tools/qdoc3/test/linguist.qdocconf47
-rw-r--r--tools/qdoc3/test/macros.qdocconf27
-rw-r--r--tools/qdoc3/test/qmake.qdocconf40
-rw-r--r--tools/qdoc3/test/qt-api-only-with-xcode.qdocconf29
-rw-r--r--tools/qdoc3/test/qt-api-only.qdocconf30
-rw-r--r--tools/qdoc3/test/qt-build-docs-with-xcode.qdocconf3
-rw-r--r--tools/qdoc3/test/qt-build-docs.qdocconf109
-rw-r--r--tools/qdoc3/test/qt-cpp-ignore.qdocconf87
-rw-r--r--tools/qdoc3/test/qt-defines.qdocconf26
-rw-r--r--tools/qdoc3/test/qt-for-jambi.qdocconf12
-rw-r--r--tools/qdoc3/test/qt-html-templates.qdocconf32
-rw-r--r--tools/qdoc3/test/qt-inc.qdocconf146
-rw-r--r--tools/qdoc3/test/qt-linguist.qdocconf4
-rw-r--r--tools/qdoc3/test/qt-webxml.qdocconf11
-rw-r--r--tools/qdoc3/test/qt-with-extensions.qdocconf8
-rw-r--r--tools/qdoc3/test/qt-with-xcode.qdocconf3
-rw-r--r--tools/qdoc3/test/qt.qdocconf115
-rw-r--r--tools/qdoc3/test/standalone-eclipse-integration.qdocconf11
-rw-r--r--tools/qdoc3/text.cpp270
-rw-r--r--tools/qdoc3/text.h106
-rw-r--r--tools/qdoc3/tokenizer.cpp753
-rw-r--r--tools/qdoc3/tokenizer.h183
-rw-r--r--tools/qdoc3/tr.h60
-rw-r--r--tools/qdoc3/tree.cpp2012
-rw-r--r--tools/qdoc3/tree.h157
-rw-r--r--tools/qdoc3/uncompressor.cpp108
-rw-r--r--tools/qdoc3/uncompressor.h79
-rw-r--r--tools/qdoc3/webxmlgenerator.cpp1195
-rw-r--r--tools/qdoc3/webxmlgenerator.h122
-rw-r--r--tools/qdoc3/yyindent.cpp1190
120 files changed, 34795 insertions, 0 deletions
diff --git a/tools/qdoc3/JAVATODO.txt b/tools/qdoc3/JAVATODO.txt
new file mode 100644
index 0000000..96df4c1
--- /dev/null
+++ b/tools/qdoc3/JAVATODO.txt
@@ -0,0 +1,28 @@
+ * index page on "O" and downwards?
+ * flag types, e.g. QTextStream::NumberFlags
+ * example references doc.trolltech.com etc.
+
+ * fix enum value table (e.g. QCoreApplication.Encoding.html)
+ * check reimplementation of interface functions (e.g. QWidget::widthMM())
+ * handle '::' in doc, e.g. in QAbstractEventDispatcher's detailed desc
+ * make sure default constructor logic doesn't interfere with default params (e.g. QAbstractEventDispatcher ctors)
+ * document default constructor provided for Java (e.g.?)
+ * document "protected QAbstractEventDispatcher(QtObject.QPrivateConstructor p)" and the like
+ * memory-managed, type
+
+ * replace QString with String, etc.
+ * nested classes
+ * enum_1
+
+ * fix stylesheet
+ * no link to class itself
+ * support \ifjava
+ * add // C++
+ * support Java-only /*! ... */ comments
+
+ * links to property names have to be fixed
+ * example: version 1 in QStyleOptionFrame
+ * example: foo -> isFoo()
+
+ * lowercase, and remove final stop, in \brief texts for variables and properties
+ * omit "see alsos" that don't work instead of showing them as broken links
diff --git a/tools/qdoc3/README.TXT b/tools/qdoc3/README.TXT
new file mode 100644
index 0000000..4f9f924
--- /dev/null
+++ b/tools/qdoc3/README.TXT
@@ -0,0 +1,6 @@
+qdoc3 is the tool used to generate the Qt reference documentation.
+The source code is included as part of this package primarily to
+fulfill our GPL obligations. We highly recommend using
+Doxygen (http://www.stack.nl/~dimitri/doxygen/) for generating
+documentation for your Qt-based application.
+
diff --git a/tools/qdoc3/TODO.txt b/tools/qdoc3/TODO.txt
new file mode 100644
index 0000000..6deeb06
--- /dev/null
+++ b/tools/qdoc3/TODO.txt
@@ -0,0 +1,96 @@
+ * fix QWSPointerCalibrationData::devPoints and qwsServer
+ * Fix QMenu::addAction(QAction *) overload, "using" etc.
+ * fix space between two tables using <p></p>
+ * qpixmap-qt3.html; remove 8 public functions inherited from QPaintDevice
+ * \variable array
+ * Added support for parameterless macros (e.g. \macro Q_OBJECT).
+ * Made qdoc stricter regarding the data types (e.g. can't use \enum to document a typedef).
+ * Parse QT_MODULE() macro and generate proper warnings for the various editions.
+ * Fix parsing of \image following \value (e.g. qt.html).
+ * Don't turn X11 and similar names into links.
+ * Added automatic links from getters to setters and vice versa.
+ * Support \module and show which module each class is from.
+ * Use bullet list for the list of all functions, now that
+ Assistant handles these gracefully.
+ * Fix occasional crash caused by misuse of const_cast().
+ * Provide clearer error messages when resolves fail.
+
+
+
+
+CHECK:
+
+ * Identify editions
+ * Automatic \sa getter setter
+ * \macro Q_OBJECT
+
+MUST HAVES:
+
+ * resolve [gs]etters for \sa using base class
+
+ * fix \overload when one is a signal and the other a normal function
+ * use "project" for .dcf files
+ * functions.html: include the types from QtGlobal as well as the functions (whatever that means)
+
+ * nice template function/class syntax
+ * spellchecker: built-in vs. builtin
+
+ * verbose mode for functions that don't exist
+ * No links to Porting Guide sections (e.g. QStringList)
+ * link toggled(bool)
+ * autolink foo(1)
+ * handle using correctly
+ * QObject "reentrant" list: duplicates
+ * operator<< \overload
+ * \compat \overload
+ * qWarning() link
+ * operator<<() autolink
+
+ * get rid of spurious 'global' functions
+ * Make automatic links in code work
+
+ * Fix encoding bug (see Important email from Simon Hausmann)
+ * Make links to QFoo::bar().baz() work
+ * Fix automatic links in \sectionX (e.g. qt4-getting-started.html)
+ * Provide a "List of all properties" page.
+
+ * expand QObjectList -> QList<QObject *>
+ * make \macro work (?)
+ * warning for unnamed parameters in property access functions
+ * \center...\endcenter
+
+ * warning for undocumented enum values
+
+LINKS:
+
+ * explanation following nonstandard wording warning
+
+ * omit \overload in operator<< and operator>>
+ * make operator-() unary and binary independent functions (no \overload)
+ * fix \overload
+ * fix \legalese
+ * remove warning for undocumented enum item like QLocale::LastLanguage
+ * improve the \a warnings for overloads; if one overload documents a para, fine
+
+ * implement \sidebar
+
+ * implement qbook
+
+ * implement \legalesefile
+
+ * show in which module each class is
+ * list namespaces, list header files
+
+
+NICE FEATURES:
+ * implement inheritance tree for each class (as a PNG)
+ * avoid <p>...</p> in table/item cells without relying on horrible kludge
+ * prevent macros from having same name as commands
+ * be smart about enum types Foo::Bar vs. Bar when comparing functions
+ * be smart about const & non-const when comparing functions
+
+OTHER:
+ * make qdoc run faster
+ * make sure \headerfile works even if specified after \relates
+
+ * use qtstyle.css instead of inline style for each page
diff --git a/tools/qdoc3/apigenerator.cpp b/tools/qdoc3/apigenerator.cpp
new file mode 100644
index 0000000..d998839
--- /dev/null
+++ b/tools/qdoc3/apigenerator.cpp
@@ -0,0 +1,150 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QFile>
+
+#include "apigenerator.h"
+#include "codemarker.h"
+#include "tree.h"
+
+QT_BEGIN_NAMESPACE
+
+static QString indentStr(int indent)
+{
+ QString str;
+ str.fill(QLatin1Char(' '), indent * 4);
+ return str;
+}
+
+static bool lessThanName(Node *node1, Node *node2)
+{
+ return node1->name() < node2->name();
+}
+
+QString ApiGenerator::format()
+{
+ return QLatin1String("API");
+}
+
+void ApiGenerator::generateTree(const Tree *tree, CodeMarker *marker)
+{
+ QFile outFile(QLatin1String("api"));
+ outFile.open(QIODevice::WriteOnly);
+
+ out.setDevice(&outFile);
+ generateNode(tree->root(), marker);
+ out.flush();
+}
+
+void ApiGenerator::generateNode(const Node *node, CodeMarker *marker, int indent)
+{
+ if (node->access() == Node::Private)
+ return;
+
+ switch (node->type()) {
+ case Node::Namespace:
+ if (!node->name().isEmpty()) {
+ out << indentStr(indent) << "Namespace: " << node->name() << "\n";
+ ++indent;
+ }
+ break;
+ case Node::Class:
+ {
+ const ClassNode *classe = static_cast<const ClassNode *>(node);
+ out << indentStr(indent) << "Class: " << node->name();
+ foreach (const RelatedClass &baseClass, classe->baseClasses()) {
+ if (baseClass.access == Node::Public)
+ out << " inherits " << baseClass.dataTypeWithTemplateArgs;
+ }
+ out << "\n";
+ ++indent;
+ }
+ break;
+ case Node::Enum:
+ {
+ const EnumNode *enume = static_cast<const EnumNode *>(node);
+ out << indentStr(indent) << "Enum: " << node->name() << "\n";
+ ++indent;
+
+ QStringList enumNames;
+ foreach (const EnumItem &item, enume->items())
+ enumNames << item.name();
+ qSort(enumNames);
+
+ foreach (const QString &name, enumNames)
+ out << indentStr(indent) << "Enum value: " << name << "\n";
+ }
+ break;
+ case Node::Typedef:
+ out << indentStr(indent) << "Typedef: " << node->name() << "\n";
+ ++indent;
+ break;
+ case Node::Function:
+ {
+ out << indentStr(indent) << "Function: "
+ << plainCode(marker->markedUpSynopsis(node, 0, CodeMarker::Detailed)) << "\n";
+ ++indent;
+ }
+ break;
+ case Node::Property:
+ {
+ const PropertyNode *property = static_cast<const PropertyNode *>(node);
+ out << indentStr(indent) << "Property: " << property->name()
+ << " type " << property->dataType() << "\n";
+ ++indent;
+ }
+ break;
+ default:
+ ;
+ }
+
+ if (node->isInnerNode()) {
+ const InnerNode *inner = static_cast<const InnerNode *>(node);
+ NodeList nodes = inner->childNodes();
+ qSort(nodes.begin(), nodes.end(), lessThanName);
+ foreach (const Node *child, nodes)
+ generateNode(child, marker, indent);
+ }
+
+ out.flush();
+}
+
+QT_END_NAMESPACE
diff --git a/tools/qdoc3/apigenerator.h b/tools/qdoc3/apigenerator.h
new file mode 100644
index 0000000..9545fae
--- /dev/null
+++ b/tools/qdoc3/apigenerator.h
@@ -0,0 +1,65 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef APIGENERATOR_H
+#define APIGENERATOR_H
+
+#include <QTextStream>
+
+#include "generator.h"
+
+QT_BEGIN_NAMESPACE
+
+class ApiGenerator : public Generator
+{
+public:
+ QString format();
+ void generateTree(const Tree *tree, CodeMarker *marker);
+
+private:
+ void generateNode(const Node *node, CodeMarker *marker, int indent = 0);
+
+ QTextStream out;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/tools/qdoc3/archiveextractor.cpp b/tools/qdoc3/archiveextractor.cpp
new file mode 100644
index 0000000..b72d27a
--- /dev/null
+++ b/tools/qdoc3/archiveextractor.cpp
@@ -0,0 +1,108 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*
+ archiveextractor.cpp
+*/
+
+#include "archiveextractor.h"
+
+QT_BEGIN_NAMESPACE
+
+QList<ArchiveExtractor *> ArchiveExtractor::extractors;
+
+/*!
+ \class ArchiveExtractor
+
+ \brief The ArchiveExtractor class is a base class for classes that
+ know how to unpack a certain kind of archive file.
+
+ The archive extractor contains a list of the filename extensions
+ of the files that the archive extractor knows how to unpack.
+
+ It maintains a static list of all the instances of ArchiveExtractor
+ that have been created. It also has a static function for searching
+ that list to find the archive extracter for a file with a certain
+ extension.
+ */
+
+/*!
+ The constructor takes a list of filename extensions, which it
+ copies and saves internally. This archive extractor is prepended
+ to the static list.
+ */
+ArchiveExtractor::ArchiveExtractor( const QStringList& extensions )
+ : fileExts( extensions )
+{
+ extractors.prepend( this );
+}
+
+/*!
+ The destructor deletes all the filename extensions.
+ */
+ArchiveExtractor::~ArchiveExtractor()
+{
+ extractors.removeAll( this );
+}
+
+/*!
+ This function searches the static list of archive extractors
+ to find the first one that can handle \a fileName. If it finds
+ an acceptable extractor, it returns a pointer to it. Otherwise
+ it returns null.
+ */
+ArchiveExtractor*
+ArchiveExtractor::extractorForFileName( const QString& fileName )
+{
+ int dot = -1;
+ while ( (dot = fileName.indexOf(QLatin1Char('.'), dot + 1)) != -1 ) {
+ QString ext = fileName.mid( dot + 1 );
+ QList<ArchiveExtractor *>::ConstIterator e = extractors.begin();
+ while ( e != extractors.end() ) {
+ if ( (*e)->fileExtensions().contains(ext) )
+ return *e;
+ ++e;
+ }
+ }
+ return 0;
+}
+
+QT_END_NAMESPACE
diff --git a/tools/qdoc3/archiveextractor.h b/tools/qdoc3/archiveextractor.h
new file mode 100644
index 0000000..cfa45bd
--- /dev/null
+++ b/tools/qdoc3/archiveextractor.h
@@ -0,0 +1,78 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*
+ archiveextractor.h
+*/
+
+#ifndef ARCHIVEEXTRACTOR_H
+#define ARCHIVEEXTRACTOR_H
+
+#include <qstringlist.h>
+
+#include "location.h"
+
+QT_BEGIN_NAMESPACE
+
+class ArchiveExtractor
+{
+public:
+ ArchiveExtractor( const QStringList& extensions );
+ virtual ~ArchiveExtractor();
+
+ virtual void extractArchive( const Location& location,
+ const QString& filePath,
+ const QString& outputDir ) = 0;
+
+ static ArchiveExtractor *extractorForFileName( const QString& fileName );
+
+protected:
+ const QStringList& fileExtensions() { return fileExts; }
+
+private:
+ QStringList fileExts;
+
+ static QList<ArchiveExtractor *> extractors;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/tools/qdoc3/atom.cpp b/tools/qdoc3/atom.cpp
new file mode 100644
index 0000000..f341862
--- /dev/null
+++ b/tools/qdoc3/atom.cpp
@@ -0,0 +1,357 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <qregexp.h>
+#include "atom.h"
+#include "location.h"
+#include <stdio.h>
+
+QT_BEGIN_NAMESPACE
+
+QString Atom::BOLD_ ("bold");
+QString Atom::INDEX_ ("index");
+QString Atom::ITALIC_ ("italic");
+QString Atom::LINK_ ("link");
+QString Atom::PARAMETER_ ("parameter");
+QString Atom::SUBSCRIPT_ ("subscript");
+QString Atom::SUPERSCRIPT_ ("superscript");
+QString Atom::TELETYPE_ ("teletype");
+QString Atom::UNDERLINE_ ("underline");
+
+QString Atom::BULLET_ ("bullet");
+QString Atom::TAG_ ("tag");
+QString Atom::VALUE_ ("value");
+QString Atom::LOWERALPHA_ ("loweralpha");
+QString Atom::LOWERROMAN_ ("lowerroman");
+QString Atom::NUMERIC_ ("numeric");
+QString Atom::UPPERALPHA_ ("upperalpha");
+QString Atom::UPPERROMAN_ ("upperroman");
+
+/*! \class Atom
+ \brief The Atom class is the fundamental unit for representing
+ documents internally.
+
+ Atoms have a \i type and are completed by a \i string whose
+ meaning depends on the \i type. For example, the string
+ \quotation
+ \i italic text looks nicer than \bold bold text
+ \endquotation
+ is represented by the following atoms:
+ \quotation
+ (FormattingLeft, ATOM_FORMATTING_ITALIC)
+ (String, "italic")
+ (FormattingRight, ATOM_FORMATTING_ITALIC)
+ (String, " text is more attractive than ")
+ (FormattingLeft, ATOM_FORMATTING_BOLD)
+ (String, "bold")
+ (FormattingRight, ATOM_FORMATTING_BOLD)
+ (String, " text")
+ \endquotation
+
+ \also Text
+*/
+
+/*! \enum Atom::Type
+
+ \value AbstractLeft
+ \value AbstractRight
+ \value AutoLink
+ \value BaseName
+ \value BriefLeft
+ \value BriefRight
+ \value C
+ \value CaptionLeft
+ \value CaptionRight
+ \value Code
+ \value CodeBad
+ \value CodeNew
+ \value CodeOld
+ \value CodeQuoteArgument
+ \value CodeQuoteCommand
+ \value EndQmlText
+ \value FormatElse
+ \value FormatEndif
+ \value FormatIf
+ \value FootnoteLeft
+ \value FootnoteRight
+ \value FormattingLeft
+ \value FormattingRight
+ \value GeneratedList
+ \value Image
+ \value ImageText
+ \value InlineImage
+ \value LineBreak
+ \value Link
+ \value LinkNode
+ \value ListLeft
+ \value ListItemNumber
+ \value ListTagLeft
+ \value ListTagRight
+ \value ListItemLeft
+ \value ListItemRight
+ \value ListRight
+ \value Nop
+ \value ParaLeft
+ \value ParaRight
+ \value Qml
+ \value QmlText
+ \value QuotationLeft
+ \value QuotationRight
+ \value RawString
+ \value SectionLeft
+ \value SectionRight
+ \value SectionHeadingLeft
+ \value SectionHeadingRight
+ \value SidebarLeft
+ \value SidebarRight
+ \value String
+ \value TableLeft
+ \value TableRight
+ \value TableHeaderLeft
+ \value TableHeaderRight
+ \value TableRowLeft
+ \value TableRowRight
+ \value TableItemLeft
+ \value TableItemRight
+ \value TableOfContents
+ \value Target
+ \value UnhandledFormat
+ \value UnknownCommand
+*/
+
+static const struct {
+ const char *english;
+ int no;
+} atms[] = {
+ { "AbstractLeft", Atom::AbstractLeft },
+ { "AbstractRight", Atom::AbstractRight },
+ { "AutoLink", Atom::AutoLink },
+ { "BaseName", Atom::BaseName },
+ { "BriefLeft", Atom::BriefLeft },
+ { "BriefRight", Atom::BriefRight },
+ { "C", Atom::C },
+ { "CaptionLeft", Atom::CaptionLeft },
+ { "CaptionRight", Atom::CaptionRight },
+ { "Code", Atom::Code },
+ { "CodeBad", Atom::CodeBad },
+ { "CodeNew", Atom::CodeNew },
+ { "CodeOld", Atom::CodeOld },
+ { "CodeQuoteArgument", Atom::CodeQuoteArgument },
+ { "CodeQuoteCommand", Atom::CodeQuoteCommand },
+#ifdef QDOC_QML
+ { "EndQmlText", Atom::EndQmlText },
+#endif
+ { "FootnoteLeft", Atom::FootnoteLeft },
+ { "FootnoteRight", Atom::FootnoteRight },
+ { "FormatElse", Atom::FormatElse },
+ { "FormatEndif", Atom::FormatEndif },
+ { "FormatIf", Atom::FormatIf },
+ { "FormattingLeft", Atom::FormattingLeft },
+ { "FormattingRight", Atom::FormattingRight },
+ { "GeneratedList", Atom::GeneratedList },
+ { "Image", Atom::Image },
+ { "ImageText", Atom::ImageText },
+ { "InlineImage", Atom::InlineImage },
+ { "LegaleseLeft", Atom::LegaleseLeft },
+ { "LegaleseRight", Atom::LegaleseRight },
+ { "LineBreak", Atom::LineBreak },
+ { "Link", Atom::Link },
+ { "LinkNode", Atom::LinkNode },
+ { "ListLeft", Atom::ListLeft },
+ { "ListItemNumber", Atom::ListItemNumber },
+ { "ListTagLeft", Atom::ListTagLeft },
+ { "ListTagRight", Atom::ListTagRight },
+ { "ListItemLeft", Atom::ListItemLeft },
+ { "ListItemRight", Atom::ListItemRight },
+ { "ListRight", Atom::ListRight },
+ { "Nop", Atom::Nop },
+ { "ParaLeft", Atom::ParaLeft },
+ { "ParaRight", Atom::ParaRight },
+#ifdef QDOC_QML
+ { "Qml", Atom::Qml},
+ { "QmlText", Atom::QmlText },
+#endif
+ { "QuotationLeft", Atom::QuotationLeft },
+ { "QuotationRight", Atom::QuotationRight },
+ { "RawString", Atom::RawString },
+ { "SectionLeft", Atom::SectionLeft },
+ { "SectionRight", Atom::SectionRight },
+ { "SectionHeadingLeft", Atom::SectionHeadingLeft },
+ { "SectionHeadingRight", Atom::SectionHeadingRight },
+ { "SidebarLeft", Atom::SidebarLeft },
+ { "SidebarRight", Atom::SidebarRight },
+ { "SnippetCommand", Atom::SnippetCommand },
+ { "SnippetIdentifier", Atom::SnippetIdentifier },
+ { "SnippetLocation", Atom::SnippetLocation },
+ { "String", Atom::String },
+ { "TableLeft", Atom::TableLeft },
+ { "TableRight", Atom::TableRight },
+ { "TableHeaderLeft", Atom::TableHeaderLeft },
+ { "TableHeaderRight", Atom::TableHeaderRight },
+ { "TableRowLeft", Atom::TableRowLeft },
+ { "TableRowRight", Atom::TableRowRight },
+ { "TableItemLeft", Atom::TableItemLeft },
+ { "TableItemRight", Atom::TableItemRight },
+ { "TableOfContents", Atom::TableOfContents },
+ { "Target", Atom::Target },
+ { "UnhandledFormat", Atom::UnhandledFormat },
+ { "UnknownCommand", Atom::UnknownCommand },
+ { 0, 0 }
+};
+
+/*! \fn Atom::Atom( Type type, const QString& string )
+
+ Constructs an atom (\a type, \a string) outside of any atom list.
+*/
+
+/*! \fn Atom( Atom *prev, Type type, const QString& string )
+
+ Constructs an atom (\a type, \a string) that follows \a prev in \a
+ prev's atom list.
+*/
+
+/*! \fn void Atom::appendChar( QChar ch )
+
+ Appends \a ch to the string parameter of this atom.
+
+ \also string()
+*/
+
+/*! \fn void Atom::appendString( const QString& string )
+
+ Appends \a string to the string parameter of this atom.
+
+ \also string()
+*/
+
+/*! \fn void Atom::chopString()
+
+ \also string()
+*/
+
+/*! \fn Atom *Atom::next()
+ Return the next atom in the atom list.
+ \also type(), string()
+*/
+
+/*!
+ Return the next Atom in the list if it is of Type \a t.
+ Otherwise return 0.
+ */
+const Atom* Atom::next(Type t) const
+{
+ return (nxt && (nxt->type() == t)) ? nxt : 0;
+}
+
+/*!
+ Return the next Atom in the list if it is of Type \a t
+ and its string part is \a s. Otherwise return 0.
+ */
+const Atom* Atom::next(Type t, const QString& s) const
+{
+ return (nxt && (nxt->type() == t) && (nxt->string() == s)) ? nxt : 0;
+}
+
+/*! \fn const Atom *Atom::next() const
+ Return the next atom in the atom list.
+ \also type(), string()
+*/
+
+/*! \fn Type Atom::type() const
+ Return the type of this atom.
+ \also string(), next()
+*/
+
+/*!
+ Return the type of this atom as a string. Return "Invalid" if
+ type() returns an impossible value.
+
+ This is only useful for debugging.
+
+ \also type()
+*/
+QString Atom::typeString() const
+{
+ static bool deja = false;
+
+ if ( !deja ) {
+ int i = 0;
+ while ( atms[i].english != 0 ) {
+ if ( atms[i].no != i )
+ Location::internalError( tr("atom %1 missing").arg(i) );
+ i++;
+ }
+ deja = true;
+ }
+
+ int i = (int) type();
+ if ( i < 0 || i > (int) Last )
+ return QLatin1String("Invalid");
+ return QLatin1String(atms[i].english);
+}
+
+/*! \fn const QString& Atom::string() const
+
+ Returns the string parameter that together with the type
+ characterizes this atom.
+
+ \also type(), next()
+*/
+
+/*!
+ Dumps this Atom to stderr in printer friendly form.
+ */
+void Atom::dump() const
+{
+ QString str = string();
+ str.replace( "\\", "\\\\" );
+ str.replace( "\"", "\\\"" );
+ str.replace( "\n", "\\n" );
+ str.replace( QRegExp("[^\x20-\x7e]"), "?" );
+ if (!str.isEmpty())
+ str = " \"" + str + "\"";
+ fprintf(stderr,
+ " %-15s%s\n",
+ typeString().toLatin1().data(),
+ str.toLatin1().data());
+}
+
+QT_END_NAMESPACE
diff --git a/tools/qdoc3/atom.h b/tools/qdoc3/atom.h
new file mode 100644
index 0000000..178d9ed
--- /dev/null
+++ b/tools/qdoc3/atom.h
@@ -0,0 +1,200 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*
+ atom.h
+*/
+
+#ifndef ATOM_H
+#define ATOM_H
+
+#include <qstring.h>
+
+#define QDOC_QML
+
+QT_BEGIN_NAMESPACE
+
+class Atom
+{
+ public:
+ enum Type {
+ AbstractLeft,
+ AbstractRight,
+ AutoLink,
+ BaseName,
+ BriefLeft,
+ BriefRight,
+ C,
+ CaptionLeft,
+ CaptionRight,
+ Code,
+ CodeBad,
+ CodeNew,
+ CodeOld,
+ CodeQuoteArgument,
+ CodeQuoteCommand,
+#ifdef QDOC_QML
+ EndQmlText,
+#endif
+ FootnoteLeft,
+ FootnoteRight,
+ FormatElse,
+ FormatEndif,
+ FormatIf,
+ FormattingLeft,
+ FormattingRight,
+ GeneratedList,
+ Image,
+ ImageText,
+ InlineImage,
+ LegaleseLeft,
+ LegaleseRight,
+ LineBreak,
+ Link,
+ LinkNode,
+ ListLeft,
+ ListItemNumber,
+ ListTagLeft,
+ ListTagRight,
+ ListItemLeft,
+ ListItemRight,
+ ListRight,
+ Nop,
+ ParaLeft,
+ ParaRight,
+#ifdef QDOC_QML
+ Qml,
+ QmlText,
+#endif
+ QuotationLeft,
+ QuotationRight,
+ RawString,
+ SectionLeft,
+ SectionRight,
+ SectionHeadingLeft,
+ SectionHeadingRight,
+ SidebarLeft, SidebarRight,
+ SnippetCommand,
+ SnippetIdentifier,
+ SnippetLocation,
+ String,
+ TableLeft,
+ TableRight,
+ TableHeaderLeft,
+ TableHeaderRight,
+ TableRowLeft,
+ TableRowRight,
+ TableItemLeft,
+ TableItemRight,
+ TableOfContents,
+ Target,
+ UnhandledFormat,
+ UnknownCommand,
+ Last = UnknownCommand
+ };
+
+ Atom(Type type, const QString &string = "")
+ : nxt(0), typ(type), str(string) { }
+ Atom(Atom *prev, Type type, const QString &string = "")
+ : nxt(prev->nxt), typ(type), str(string) { prev->nxt = this; }
+
+ void appendChar(QChar ch) { str += ch; }
+ void appendString(const QString& string) { str += string; }
+ void chopString() { str.chop(1); }
+ void setString(const QString &string) { str = string; }
+ Atom *next() { return nxt; }
+ void setNext(Atom *newNext) { nxt = newNext; }
+
+ const Atom *next() const { return nxt; }
+ const Atom *next(Type t) const;
+ const Atom *next(Type t, const QString& s) const;
+ Type type() const { return typ; }
+ QString typeString() const;
+ const QString& string() const { return str; }
+ void dump() const;
+
+ static QString BOLD_;
+ static QString INDEX_;
+ static QString ITALIC_;
+ static QString LINK_;
+ static QString PARAMETER_;
+ static QString SUBSCRIPT_;
+ static QString SUPERSCRIPT_;
+ static QString TELETYPE_;
+ static QString UNDERLINE_;
+
+ static QString BULLET_;
+ static QString TAG_;
+ static QString VALUE_;
+ static QString LOWERALPHA_;
+ static QString LOWERROMAN_;
+ static QString NUMERIC_;
+ static QString UPPERALPHA_;
+ static QString UPPERROMAN_;
+
+ private:
+ Atom *nxt;
+ Type typ;
+ QString str;
+};
+
+#define ATOM_FORMATTING_BOLD "bold"
+#define ATOM_FORMATTING_INDEX "index"
+#define ATOM_FORMATTING_ITALIC "italic"
+#define ATOM_FORMATTING_LINK "link"
+#define ATOM_FORMATTING_PARAMETER "parameter"
+#define ATOM_FORMATTING_SUBSCRIPT "subscript"
+#define ATOM_FORMATTING_SUPERSCRIPT "superscript"
+#define ATOM_FORMATTING_TELETYPE "teletype"
+#define ATOM_FORMATTING_UNDERLINE "underline"
+
+#define ATOM_LIST_BULLET "bullet"
+#define ATOM_LIST_TAG "tag"
+#define ATOM_LIST_VALUE "value"
+#define ATOM_LIST_LOWERALPHA "loweralpha"
+#define ATOM_LIST_LOWERROMAN "lowerroman"
+#define ATOM_LIST_NUMERIC "numeric"
+#define ATOM_LIST_UPPERALPHA "upperalpha"
+#define ATOM_LIST_UPPERROMAN "upperroman"
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/tools/qdoc3/bookgenerator.cpp b/tools/qdoc3/bookgenerator.cpp
new file mode 100644
index 0000000..4e49491
--- /dev/null
+++ b/tools/qdoc3/bookgenerator.cpp
@@ -0,0 +1,64 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*
+ bookgenerator.cpp
+*/
+
+#include "bookgenerator.h"
+
+QT_BEGIN_NAMESPACE
+
+BookGenerator::BookGenerator()
+{
+}
+
+BookGenerator::~BookGenerator()
+{
+}
+
+void BookGenerator::generateTree( const Tree *tree, CodeMarker *marker )
+{
+ Q_UNUSED( tree )
+ Q_UNUSED( marker )
+}
+
+QT_END_NAMESPACE
diff --git a/tools/qdoc3/bookgenerator.h b/tools/qdoc3/bookgenerator.h
new file mode 100644
index 0000000..15b830e
--- /dev/null
+++ b/tools/qdoc3/bookgenerator.h
@@ -0,0 +1,64 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*
+ bookgenerator.h
+*/
+
+#ifndef BOOKGENERATOR_H
+#define BOOKGENERATOR_H
+
+#include "generator.h"
+
+QT_BEGIN_NAMESPACE
+
+class BookGenerator : public Generator
+{
+public:
+ BookGenerator();
+ ~BookGenerator();
+
+ virtual void generateTree( const Tree *tree, CodeMarker *marker );
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/tools/qdoc3/ccodeparser.cpp b/tools/qdoc3/ccodeparser.cpp
new file mode 100644
index 0000000..d6318f5
--- /dev/null
+++ b/tools/qdoc3/ccodeparser.cpp
@@ -0,0 +1,73 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*
+ ccodeparser.cpp
+*/
+
+#include "ccodeparser.h"
+
+QT_BEGIN_NAMESPACE
+
+CCodeParser::CCodeParser()
+{
+}
+
+CCodeParser::~CCodeParser()
+{
+}
+
+QString CCodeParser::language()
+{
+ return QLatin1String("C");
+}
+
+QString CCodeParser::headerFileNameFilter()
+{
+ return QLatin1String("*.ch *.h");
+}
+
+QString CCodeParser::sourceFileNameFilter()
+{
+ return QLatin1String("*.c");
+}
+
+QT_END_NAMESPACE
diff --git a/tools/qdoc3/ccodeparser.h b/tools/qdoc3/ccodeparser.h
new file mode 100644
index 0000000..4e1877a
--- /dev/null
+++ b/tools/qdoc3/ccodeparser.h
@@ -0,0 +1,66 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*
+ ccodeparser.h
+*/
+
+#ifndef CCODEPARSER_H
+#define CCODEPARSER_H
+
+#include "cppcodeparser.h"
+
+QT_BEGIN_NAMESPACE
+
+class CCodeParser : public CppCodeParser
+{
+public:
+ CCodeParser();
+ ~CCodeParser();
+
+ virtual QString language();
+ virtual QString headerFileNameFilter();
+ virtual QString sourceFileNameFilter();
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/tools/qdoc3/codechunk.cpp b/tools/qdoc3/codechunk.cpp
new file mode 100644
index 0000000..77c2a3e
--- /dev/null
+++ b/tools/qdoc3/codechunk.cpp
@@ -0,0 +1,150 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*
+ codechunk.cpp
+*/
+
+#include <qregexp.h>
+#include <qstringlist.h>
+
+#include "codechunk.h"
+
+QT_BEGIN_NAMESPACE
+
+enum { Other, Alnum, Gizmo, Comma, LParen, RParen, RAngle, Colon };
+
+// entries 128 and above are Other
+static const int charCategory[256] = {
+ Other, Other, Other, Other, Other, Other, Other, Other,
+ Other, Other, Other, Other, Other, Other, Other, Other,
+ Other, Other, Other, Other, Other, Other, Other, Other,
+ Other, Other, Other, Other, Other, Other, Other, Other,
+// ! " # $ % & '
+ Other, Other, Other, Other, Other, Gizmo, Gizmo, Other,
+// ( ) * + , - . /
+ LParen, RParen, Gizmo, Gizmo, Comma, Other, Other, Gizmo,
+// 0 1 2 3 4 5 6 7
+ Alnum, Alnum, Alnum, Alnum, Alnum, Alnum, Alnum, Alnum,
+// 8 9 : ; < = > ?
+ Alnum, Alnum, Colon, Other, Other, Gizmo, RAngle, Gizmo,
+// @ A B C D E F G
+ Other, Alnum, Alnum, Alnum, Alnum, Alnum, Alnum, Alnum,
+// H I J K L M N O
+ Alnum, Alnum, Alnum, Alnum, Alnum, Alnum, Alnum, Alnum,
+// P Q R S T U V W
+ Alnum, Alnum, Alnum, Alnum, Alnum, Alnum, Alnum, Alnum,
+// X Y Z [ \ ] ^ _
+ Alnum, Alnum, Alnum, Other, Other, Other, Gizmo, Alnum,
+// ` a b c d e f g
+ Other, Alnum, Alnum, Alnum, Alnum, Alnum, Alnum, Alnum,
+// h i j k l m n o
+ Alnum, Alnum, Alnum, Alnum, Alnum, Alnum, Alnum, Alnum,
+// p q r s t u v w
+ Alnum, Alnum, Alnum, Alnum, Alnum, Alnum, Alnum, Alnum,
+// x y z { | } ~
+ Alnum, Alnum, Alnum, LParen, Gizmo, RParen, Other, Other
+};
+
+static const bool needSpace[8][8] = {
+ /* [ a + , ( ) > : */
+ /* [ */ { false, false, false, false, false, true, false, false },
+ /* a */ { false, true, true, false, false, true, false, false },
+ /* + */ { false, true, false, false, false, true, false, true },
+ /* , */ { true, true, true, true, true, true, true, true },
+ /* ( */ { true, true, true, false, true, false, true, true },
+ /* ) */ { true, true, true, false, true, true, true, true },
+ /* > */ { true, true, true, false, true, true, true, false },
+ /* : */ { false, false, true, true, true, true, true, false }
+};
+
+static int category( QChar ch )
+{
+ return charCategory[(int)ch.toLatin1()];
+}
+
+CodeChunk::CodeChunk()
+ : hotspot( -1 )
+{
+}
+
+CodeChunk::CodeChunk( const QString& str )
+ : s( str ), hotspot( -1 )
+{
+}
+
+void CodeChunk::append( const QString& lexeme )
+{
+ if ( !s.isEmpty() && !lexeme.isEmpty() ) {
+ /*
+ Should there be a space or not between the code chunk so far and the
+ new lexeme?
+ */
+ int cat1 = category(s.at(s.size() - 1));
+ int cat2 = category(lexeme[0]);
+ if ( needSpace[cat1][cat2] )
+ s += QLatin1Char( ' ' );
+ }
+ s += lexeme;
+}
+
+void CodeChunk::appendHotspot()
+{
+ /*
+ The first hotspot is the right one.
+ */
+ if ( hotspot == -1 )
+ hotspot = s.length();
+}
+
+QString CodeChunk::toString() const
+{
+ return s;
+}
+
+QStringList CodeChunk::toPath() const
+{
+ QString t = s;
+ t.remove(QRegExp(QLatin1String("<([^<>]|<([^<>]|<[^<>]*>)*>)*>")));
+ return t.split(QLatin1String("::"));
+}
+
+QT_END_NAMESPACE
diff --git a/tools/qdoc3/codechunk.h b/tools/qdoc3/codechunk.h
new file mode 100644
index 0000000..f7ac863
--- /dev/null
+++ b/tools/qdoc3/codechunk.h
@@ -0,0 +1,123 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*
+ codechunk.h
+*/
+
+#ifndef CODECHUNK_H
+#define CODECHUNK_H
+
+#include <qstring.h>
+
+QT_BEGIN_NAMESPACE
+
+// ### get rid of that class
+
+/*
+ The CodeChunk class represents a tiny piece of C++ code.
+
+ The class provides convertion between a list of lexemes and a string. It adds
+ spaces at the right place for consistent style. The tiny pieces of code it
+ represents are data types, enum values, and default parameter values.
+
+ Apart from the piece of code itself, there are two bits of metainformation
+ stored in CodeChunk: the base and the hotspot. The base is the part of the
+ piece that may be a hypertext link. The base of
+
+ QMap<QString, QString>
+
+ is QMap.
+
+ The hotspot is the place the variable name should be inserted in the case of a
+ variable (or parameter) declaration. The base of
+
+ char * []
+
+ is between '*' and '[]'.
+*/
+class CodeChunk
+{
+public:
+ CodeChunk();
+ CodeChunk( const QString& str );
+
+ void append( const QString& lexeme );
+ void appendHotspot();
+
+ bool isEmpty() const { return s.isEmpty(); }
+ QString toString() const;
+ QStringList toPath() const;
+ QString left() const { return s.left(hotspot == -1 ? s.length() : hotspot); }
+ QString right() const { return s.mid(hotspot == -1 ? s.length() : hotspot); }
+
+private:
+ QString s;
+ int hotspot;
+};
+
+inline bool operator==( const CodeChunk& c, const CodeChunk& d ) {
+ return c.toString() == d.toString();
+}
+
+inline bool operator!=( const CodeChunk& c, const CodeChunk& d ) {
+ return !( c == d );
+}
+
+inline bool operator<( const CodeChunk& c, const CodeChunk& d ) {
+ return c.toString() < d.toString();
+}
+
+inline bool operator>( const CodeChunk& c, const CodeChunk& d ) {
+ return d < c;
+}
+
+inline bool operator<=( const CodeChunk& c, const CodeChunk& d ) {
+ return !( c > d );
+}
+
+inline bool operator>=( const CodeChunk& c, const CodeChunk& d ) {
+ return !( c < d );
+}
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/tools/qdoc3/codemarker.cpp b/tools/qdoc3/codemarker.cpp
new file mode 100644
index 0000000..a803281
--- /dev/null
+++ b/tools/qdoc3/codemarker.cpp
@@ -0,0 +1,538 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QMetaObject>
+#include <QDebug>
+#include "codemarker.h"
+#include "config.h"
+#include "node.h"
+
+#include <stdio.h>
+
+QT_BEGIN_NAMESPACE
+
+QString CodeMarker::defaultLang;
+QList<CodeMarker *> CodeMarker::markers;
+
+/*!
+ When a code marker constructs itself, it puts itself into
+ the static list of code markers. All the code markers in
+ the static list get initialized in initialize(), which is
+ not called until after the qdoc configuration file has
+ been read.
+ */
+CodeMarker::CodeMarker()
+ : slow(false)
+{
+ markers.prepend(this);
+}
+
+/*!
+ When a code marker destroys itself, it removes itself from
+ the static list of code markers.
+ */
+CodeMarker::~CodeMarker()
+{
+ markers.removeAll(this);
+}
+
+/*!
+ The only thing a code market initializes is its \e{slow}
+ flag. The \e{slow} flag indicates whether the operations
+ that slow down qdoc are to be performed or not. It is
+ turned off by default.
+ */
+void CodeMarker::initializeMarker(const Config &config)
+{
+ slow = config.getBool(QLatin1String(CONFIG_SLOW));
+}
+
+/*!
+ Terminating a code marker is trivial.
+ */
+void CodeMarker::terminateMarker()
+{
+ // nothing.
+}
+
+/*!
+ All the code markers in the static list are initialized
+ here, after the qdoc configuration file has been loaded.
+ */
+void CodeMarker::initialize(const Config& config)
+{
+ defaultLang = config.getString(QLatin1String(CONFIG_LANGUAGE));
+ QList<CodeMarker *>::ConstIterator m = markers.begin();
+ while (m != markers.end()) {
+ (*m)->initializeMarker(config);
+ ++m;
+ }
+}
+
+/*!
+ All the code markers in the static list are terminated here.
+ */
+void CodeMarker::terminate()
+{
+ QList<CodeMarker *>::ConstIterator m = markers.begin();
+ while (m != markers.end()) {
+ (*m)->terminateMarker();
+ ++m;
+ }
+}
+
+CodeMarker *CodeMarker::markerForCode(const QString& code)
+{
+ CodeMarker *defaultMarker = markerForLanguage(defaultLang);
+ if (defaultMarker != 0 && defaultMarker->recognizeCode(code))
+ return defaultMarker;
+
+ QList<CodeMarker *>::ConstIterator m = markers.begin();
+ while (m != markers.end()) {
+ if ((*m)->recognizeCode(code))
+ return *m;
+ ++m;
+ }
+ return defaultMarker;
+}
+
+CodeMarker *CodeMarker::markerForFileName(const QString& fileName)
+{
+ CodeMarker *defaultMarker = markerForLanguage(defaultLang);
+ int dot = -1;
+ while ((dot = fileName.lastIndexOf(QLatin1Char('.'), dot)) != -1) {
+ QString ext = fileName.mid(dot + 1);
+ if (defaultMarker != 0 && defaultMarker->recognizeExtension(ext))
+ return defaultMarker;
+ QList<CodeMarker *>::ConstIterator m = markers.begin();
+ while (m != markers.end()) {
+ if ((*m)->recognizeExtension(ext))
+ return *m;
+ ++m;
+ }
+ --dot;
+ }
+ return defaultMarker;
+}
+
+CodeMarker *CodeMarker::markerForLanguage(const QString& lang)
+{
+ QList<CodeMarker *>::ConstIterator m = markers.begin();
+ while (m != markers.end()) {
+ if ((*m)->recognizeLanguage(lang))
+ return *m;
+ ++m;
+ }
+ return 0;
+}
+
+const Node *CodeMarker::nodeForString(const QString& string)
+{
+ if (sizeof(const Node *) == sizeof(uint)) {
+ return reinterpret_cast<const Node *>(string.toUInt());
+ } else {
+ return reinterpret_cast<const Node *>(string.toULongLong());
+ }
+}
+
+QString CodeMarker::stringForNode(const Node *node)
+{
+ if (sizeof(const Node *) == sizeof(ulong)) {
+ return QString::number(reinterpret_cast<ulong>(node));
+ } else {
+ return QString::number(reinterpret_cast<qulonglong>(node));
+ }
+}
+
+static const QString samp = QLatin1String("&amp;");
+static const QString slt = QLatin1String("&lt;");
+static const QString sgt = QLatin1String("&gt;");
+static const QString squot = QLatin1String("&quot;");
+
+QString CodeMarker::protect(const QString& str)
+{
+ int n = str.length();
+ QString marked;
+ marked.reserve(n * 2 + 30);
+ const QChar *data = str.constData();
+ for (int i = 0; i != n; ++i) {
+ switch (data[i].unicode()) {
+ case '&': marked += samp; break;
+ case '<': marked += slt; break;
+ case '>': marked += sgt; break;
+ case '"': marked += squot; break;
+ default : marked += data[i];
+ }
+ }
+ return marked;
+}
+
+QString CodeMarker::typified(const QString &string)
+{
+ QString result;
+ QString pendingWord;
+
+ for (int i = 0; i <= string.size(); ++i) {
+ QChar ch;
+ if (i != string.size())
+ ch = string.at(i);
+
+ QChar lower = ch.toLower();
+ if ((lower >= QLatin1Char('a') && lower <= QLatin1Char('z'))
+ || ch.digitValue() >= 0 || ch == QLatin1Char('_')
+ || ch == QLatin1Char(':')) {
+ pendingWord += ch;
+ } else {
+ if (!pendingWord.isEmpty()) {
+ bool isProbablyType = (pendingWord != QLatin1String("const"));
+ if (isProbablyType)
+ result += QLatin1String("<@type>");
+ result += pendingWord;
+ if (isProbablyType)
+ result += QLatin1String("</@type>");
+ }
+ pendingWord.clear();
+
+ switch (ch.unicode()) {
+ case '\0':
+ break;
+ case '&':
+ result += QLatin1String("&amp;");
+ break;
+ case '<':
+ result += QLatin1String("&lt;");
+ break;
+ case '>':
+ result += QLatin1String("&gt;");
+ break;
+ default:
+ result += ch;
+ }
+ }
+ }
+ return result;
+}
+
+QString CodeMarker::taggedNode(const Node *node)
+{
+ QString tag;
+
+ switch (node->type()) {
+ case Node::Namespace:
+ tag = QLatin1String("@namespace");
+ break;
+ case Node::Class:
+ tag = QLatin1String("@class");
+ break;
+ case Node::Enum:
+ tag = QLatin1String("@enum");
+ break;
+ case Node::Typedef:
+ tag = QLatin1String("@typedef");
+ break;
+ case Node::Function:
+ tag = QLatin1String("@function");
+ break;
+ case Node::Property:
+ tag = QLatin1String("@property");
+ break;
+ default:
+ tag = QLatin1String("@unknown");
+ }
+ return QLatin1Char('<') + tag + QLatin1Char('>') + protect(node->name())
+ + QLatin1String("</") + tag + QLatin1Char('>');
+}
+
+QString CodeMarker::linkTag(const Node *node, const QString& body)
+{
+ return QLatin1String("<@link node=\"") + stringForNode(node)
+ + QLatin1String("\">") + body + QLatin1String("</@link>");
+}
+
+QString CodeMarker::sortName(const Node *node)
+{
+ QString nodeName = node->name();
+ int numDigits = 0;
+ for (int i = nodeName.size() - 1; i > 0; --i) {
+ if (nodeName.at(i).digitValue() == -1)
+ break;
+ ++numDigits;
+ }
+
+ // we want 'qint8' to appear before 'qint16'
+ if (numDigits > 0) {
+ for (int i = 0; i < 4 - numDigits; ++i)
+ nodeName.insert(nodeName.size()-numDigits-1, QLatin1String("0"));
+ }
+
+ if (node->type() == Node::Function) {
+ const FunctionNode *func = static_cast<const FunctionNode *>(node);
+ QString sortNo;
+ if (func->metaness() == FunctionNode::Ctor) {
+ sortNo = QLatin1String("C");
+ } else if (func->metaness() == FunctionNode::Dtor) {
+ sortNo = QLatin1String("D");
+ } else {
+ if (nodeName.startsWith(QLatin1String("operator"))
+ && nodeName.length() > 8
+ && !nodeName[8].isLetterOrNumber())
+ sortNo = QLatin1String("F");
+ else
+ sortNo = QLatin1String("E");
+ }
+ return sortNo + nodeName + QLatin1Char(' ')
+ + QString::number(func->overloadNumber(), 36);
+ }
+
+ if (node->type() == Node::Class)
+ return QLatin1Char('A') + nodeName;
+
+ if (node->type() == Node::Property || node->type() == Node::Variable)
+ return QLatin1Char('E') + nodeName;
+
+ return QLatin1Char('B') + nodeName;
+}
+
+void CodeMarker::insert(FastSection &fastSection, Node *node, SynopsisStyle style, Status status)
+{
+ bool inheritedMember = (!node->relates() &&
+ (node->parent() != (const InnerNode *)fastSection.innerNode));
+ bool irrelevant = false;
+
+ if (node->access() == Node::Private) {
+ irrelevant = true;
+ } else if (node->type() == Node::Function) {
+ FunctionNode *func = (FunctionNode *) node;
+ irrelevant = (inheritedMember
+ && (func->metaness() == FunctionNode::Ctor ||
+ func->metaness() == FunctionNode::Dtor));
+ } else if (node->type() == Node::Class || node->type() == Node::Enum
+ || node->type() == Node::Typedef) {
+ irrelevant = (inheritedMember && style != SeparateList);
+ if (!irrelevant && style == Detailed && node->type() == Node::Typedef) {
+ const TypedefNode* typedeffe = static_cast<const TypedefNode*>(node);
+ if (typedeffe->associatedEnum())
+ irrelevant = true;
+ }
+ }
+
+ if (!irrelevant) {
+ if (status == Compat) {
+ irrelevant = (node->status() != Node::Compat);
+ } else if (status == Obsolete) {
+ irrelevant = (node->status() != Node::Obsolete);
+ } else {
+ irrelevant = (node->status() == Node::Compat ||
+ node->status() == Node::Obsolete);
+ }
+ }
+
+ if (!irrelevant) {
+ if (!inheritedMember || style == SeparateList) {
+ QString key = sortName(node);
+ if (!fastSection.memberMap.contains(key))
+ fastSection.memberMap.insert(key, node);
+ } else {
+ if (node->parent()->type() == Node::Class) {
+ if (fastSection.inherited.isEmpty()
+ || fastSection.inherited.last().first != node->parent()) {
+ QPair<ClassNode *, int> p((ClassNode *)node->parent(), 0);
+ fastSection.inherited.append(p);
+ }
+ fastSection.inherited.last().second++;
+ }
+ }
+ }
+}
+
+void CodeMarker::append(QList<Section>& sectionList,
+ const FastSection& fastSection)
+{
+ if (!fastSection.memberMap.isEmpty() ||
+ !fastSection.inherited.isEmpty()) {
+ Section section(fastSection.name,
+ fastSection.singularMember,
+ fastSection.pluralMember);
+ section.members = fastSection.memberMap.values();
+ section.inherited = fastSection.inherited;
+ sectionList.append(section);
+ }
+}
+
+static QString encode(const QString &string)
+{
+#if 0
+ QString result = string;
+
+ for (int i = string.size() - 1; i >= 0; --i) {
+ uint ch = string.at(i).unicode();
+ if (ch > 0xFF)
+ ch = '?';
+ if ((ch - '0') >= 10 && (ch - 'a') >= 26 && (ch - 'A') >= 26
+ && ch != '/' && ch != '(' && ch != ')' && ch != ',' && ch != '*'
+ && ch != '&' && ch != '_' && ch != '<' && ch != '>' && ch != ':'
+ && ch != '~')
+ result.replace(i, 1, QString("%") + QString("%1").arg(ch, 2, 16));
+ }
+ return result;
+#else
+ return string;
+#endif
+}
+
+QStringList CodeMarker::macRefsForNode(const Node *node)
+{
+ QString result = QLatin1String("cpp/");
+ switch (node->type()) {
+ case Node::Class:
+ {
+ const ClassNode *classe = static_cast<const ClassNode *>(node);
+#if 0
+ if (!classe->templateStuff().isEmpty()) {
+ result += QLatin1String("tmplt/");
+ } else
+#endif
+ {
+ result += QLatin1String("cl/");
+ }
+ result += macName(classe); // ### Maybe plainName?
+ }
+ break;
+ case Node::Enum:
+ {
+ QStringList stringList;
+ stringList << encode(result + QLatin1String("tag/") +
+ macName(node));
+ foreach (const QString &enumName, node->doc().enumItemNames()) {
+ // ### Write a plainEnumValue() and use it here
+ stringList << encode(result + QLatin1String("econst/") +
+ macName(node->parent(), enumName));
+ }
+ return stringList;
+ }
+ case Node::Typedef:
+ result += QLatin1String("tdef/") + macName(node);
+ break;
+ case Node::Function:
+ {
+ bool isMacro = false;
+ const FunctionNode *func = static_cast<const FunctionNode *>(node);
+
+ // overloads are too clever for the Xcode documentation browser
+ if (func->isOverload())
+ return QStringList();
+
+ if (func->metaness() == FunctionNode::MacroWithParams
+ || func->metaness() == FunctionNode::MacroWithoutParams) {
+ result += QLatin1String("macro/");
+ isMacro = true;
+#if 0
+ } else if (!func->templateStuff().isEmpty()) {
+ result += QLatin1String("ftmplt/");
+#endif
+ } else if (func->isStatic()) {
+ result += QLatin1String("clm/");
+ } else if (!func->parent()->name().isEmpty()) {
+ result += QLatin1String("instm/");
+ } else {
+ result += QLatin1String("func/");
+ }
+
+ result += macName(func);
+ if (result.endsWith(QLatin1String("()")))
+ result.chop(2);
+#if 0
+ // this code is too clever for the Xcode documentation
+ // browser and/or pbhelpindexer
+ if (!isMacro) {
+ result += "/" + QLatin1String(QMetaObject::normalizedSignature(func->returnType().toLatin1().constData())) + "/(";
+ const QList<Parameter> &params = func->parameters();
+ for (int i = 0; i < params.count(); ++i) {
+ QString type = params.at(i).leftType() + params.at(i).rightType();
+ type = QLatin1String(QMetaObject::normalizedSignature(type.toLatin1().constData()));
+ if (i != 0)
+ result += ",";
+ result += type;
+ }
+ result += ")";
+ }
+#endif
+ }
+ break;
+ case Node::Variable:
+ result += QLatin1String("data/") + macName(node);
+ break;
+ case Node::Property:
+ {
+ NodeList list = static_cast<const PropertyNode*>(node)->functions();
+ QStringList stringList;
+ foreach (const Node *node, list) {
+ stringList += macRefsForNode(node);
+ }
+ return stringList;
+ }
+ case Node::Namespace:
+ case Node::Fake:
+ case Node::Target:
+ default:
+ return QStringList();
+ }
+
+ return QStringList(encode(result));
+}
+
+QString CodeMarker::macName(const Node *node, const QString &name)
+{
+ QString myName = name;
+ if (myName.isEmpty()) {
+ myName = node->name();
+ node = node->parent();
+ }
+
+ if (node->name().isEmpty()) {
+ return QLatin1Char('/') + myName;
+ } else {
+ return plainFullName(node) + QLatin1Char('/') + myName;
+ }
+}
+
+
+QT_END_NAMESPACE
diff --git a/tools/qdoc3/codemarker.h b/tools/qdoc3/codemarker.h
new file mode 100644
index 0000000..2bb1f2b
--- /dev/null
+++ b/tools/qdoc3/codemarker.h
@@ -0,0 +1,166 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*
+ codemarker.h
+*/
+
+#ifndef CODEMARKER_H
+#define CODEMARKER_H
+
+#include <qpair.h>
+
+#include "node.h"
+
+QT_BEGIN_NAMESPACE
+
+class Config;
+class Tree;
+
+struct Section
+{
+ QString name;
+ QString singularMember;
+ QString pluralMember;
+ NodeList members;
+ QList<QPair<ClassNode *, int> > inherited;
+
+ Section() { }
+ Section(const QString& name0,
+ const QString& singularMember0,
+ const QString& pluralMember0)
+ : name(name0),
+ singularMember(singularMember0),
+ pluralMember(pluralMember0) { }
+};
+
+struct FastSection
+{
+ const InnerNode *innerNode;
+ QString name;
+ QString singularMember;
+ QString pluralMember;
+ QMap<QString, Node *> memberMap;
+ QList<QPair<ClassNode *, int> > inherited;
+
+ FastSection(const InnerNode *innerNode0,
+ const QString& name0 = "",
+ const QString& singularMember0 = "member",
+ const QString& pluralMember0 = "members")
+ : innerNode(innerNode0),
+ name(name0),
+ singularMember(singularMember0),
+ pluralMember(pluralMember0) { }
+};
+
+class CodeMarker
+{
+ public:
+ enum SynopsisStyle { Summary, Detailed, SeparateList, Accessors };
+ enum Status { Compat, Obsolete, Okay };
+
+ CodeMarker();
+ virtual ~CodeMarker();
+
+ virtual void initializeMarker(const Config& config);
+ virtual void terminateMarker();
+ virtual bool recognizeCode(const QString& code) = 0;
+ virtual bool recognizeExtension(const QString& ext) = 0;
+ virtual bool recognizeLanguage(const QString& lang) = 0;
+ virtual QString plainName(const Node *node) = 0;
+ virtual QString plainFullName(const Node *node,
+ const Node *relative = 0) = 0;
+ virtual QString markedUpCode(const QString& code,
+ const Node *relative,
+ const QString& dirPath) = 0;
+ virtual QString markedUpSynopsis(const Node *node,
+ const Node *relative,
+ SynopsisStyle style) = 0;
+ virtual QString markedUpName(const Node *node) = 0;
+ virtual QString markedUpFullName(const Node *node,
+ const Node *relative = 0) = 0;
+ virtual QString markedUpEnumValue(const QString &enumValue,
+ const Node *relative) = 0;
+ virtual QString markedUpIncludes(const QStringList& includes) = 0;
+ virtual QString functionBeginRegExp(const QString& funcName) = 0;
+ virtual QString functionEndRegExp(const QString& funcName) = 0;
+ virtual QList<Section> sections(const InnerNode *inner,
+ SynopsisStyle style,
+ Status status) = 0;
+ virtual const Node *resolveTarget(const QString& target,
+ const Tree *tree,
+ const Node *relative) = 0;
+ virtual QStringList macRefsForNode(const Node *node);
+
+ static void initialize(const Config& config);
+ static void terminate();
+ static CodeMarker *markerForCode(const QString& code);
+ static CodeMarker *markerForFileName(const QString& fileName);
+ static CodeMarker *markerForLanguage(const QString& lang);
+ static const Node *nodeForString(const QString& string);
+ static QString stringForNode(const Node *node);
+
+ protected:
+ bool hurryUp() const { return !slow; }
+
+ virtual QString sortName(const Node *node);
+ QString protect(const QString &string);
+ QString typified(const QString &string);
+ QString taggedNode(const Node *node);
+ QString linkTag(const Node *node, const QString& body);
+ void insert(FastSection &fastSection,
+ Node *node,
+ SynopsisStyle style,
+ Status status);
+ void append(QList<Section>& sectionList, const FastSection& fastSection);
+
+ private:
+ QString macName(const Node *parent, const QString &name = QString());
+
+ bool slow;
+
+ static QString defaultLang;
+ static QList<CodeMarker *> markers;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/tools/qdoc3/codeparser.cpp b/tools/qdoc3/codeparser.cpp
new file mode 100644
index 0000000..9a58bc6
--- /dev/null
+++ b/tools/qdoc3/codeparser.cpp
@@ -0,0 +1,263 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*
+ codeparser.cpp
+*/
+
+#include <QtCore>
+#include "codeparser.h"
+#include "node.h"
+#include "tree.h"
+
+QT_BEGIN_NAMESPACE
+
+#define COMMAND_COMPAT Doc::alias(QLatin1String("compat"))
+#define COMMAND_DEPRECATED Doc::alias(QLatin1String("deprecated")) // ### don't document
+#define COMMAND_INGROUP Doc::alias(QLatin1String("ingroup"))
+#define COMMAND_INMODULE Doc::alias(QLatin1String("inmodule")) // ### don't document
+#define COMMAND_INTERNAL Doc::alias(QLatin1String("internal"))
+#define COMMAND_MAINCLASS Doc::alias(QLatin1String("mainclass"))
+#define COMMAND_NONREENTRANT Doc::alias(QLatin1String("nonreentrant"))
+#define COMMAND_OBSOLETE Doc::alias(QLatin1String("obsolete"))
+#define COMMAND_PRELIMINARY Doc::alias(QLatin1String("preliminary"))
+#define COMMAND_INPUBLICGROUP Doc::alias(QLatin1String("inpublicgroup"))
+#define COMMAND_REENTRANT Doc::alias(QLatin1String("reentrant"))
+#define COMMAND_SINCE Doc::alias(QLatin1String("since"))
+#define COMMAND_SUBTITLE Doc::alias(QLatin1String("subtitle"))
+#define COMMAND_THREADSAFE Doc::alias(QLatin1String("threadsafe"))
+#define COMMAND_TITLE Doc::alias(QLatin1String("title"))
+
+QList<CodeParser *> CodeParser::parsers;
+
+/*!
+ The constructor adds this code parser to the static
+ list of code parsers.
+ */
+CodeParser::CodeParser()
+{
+ parsers.prepend(this);
+}
+
+/*!
+ The destructor removes this code parser from the static
+ list of code parsers.
+ */
+CodeParser::~CodeParser()
+{
+ parsers.removeAll(this);
+}
+
+/*!
+ Initializing a code parser is trivial.
+ */
+void CodeParser::initializeParser(const Config & /* config */)
+{
+ // nothing.
+}
+
+/*!
+ Teerminating a code parser is trivial.
+ */
+void CodeParser::terminateParser()
+{
+ // nothing.
+}
+
+QString CodeParser::headerFileNameFilter()
+{
+ return sourceFileNameFilter();
+}
+
+void CodeParser::parseHeaderFile(const Location& location,
+ const QString& filePath,
+ Tree *tree)
+{
+ parseSourceFile(location, filePath, tree);
+}
+
+void CodeParser::doneParsingHeaderFiles(Tree *tree)
+{
+ doneParsingSourceFiles(tree);
+}
+
+/*!
+ All the code parsers in the static list are initialized here,
+ after the qdoc configuration variables have been set.
+ */
+void CodeParser::initialize(const Config& config)
+{
+ QList<CodeParser *>::ConstIterator p = parsers.begin();
+ while (p != parsers.end()) {
+ (*p)->initializeParser(config);
+ ++p;
+ }
+}
+
+/*!
+ All the code parsers in the static list are terminated here.
+ */
+void CodeParser::terminate()
+{
+ QList<CodeParser *>::ConstIterator p = parsers.begin();
+ while (p != parsers.end()) {
+ (*p)->terminateParser();
+ ++p;
+ }
+}
+
+CodeParser *CodeParser::parserForLanguage(const QString& language)
+{
+ QList<CodeParser *>::ConstIterator p = parsers.begin();
+ while (p != parsers.end()) {
+ if ((*p)->language() == language)
+ return *p;
+ ++p;
+ }
+ return 0;
+}
+
+/*!
+ Returns the set of strings representing the common metacommands.
+ */
+QSet<QString> CodeParser::commonMetaCommands()
+{
+ return QSet<QString>() << COMMAND_COMPAT
+ << COMMAND_DEPRECATED
+ << COMMAND_INGROUP
+ << COMMAND_INMODULE
+ << COMMAND_INTERNAL
+ << COMMAND_MAINCLASS
+ << COMMAND_NONREENTRANT
+ << COMMAND_OBSOLETE
+ << COMMAND_PRELIMINARY
+ << COMMAND_INPUBLICGROUP
+ << COMMAND_REENTRANT
+ << COMMAND_SINCE
+ << COMMAND_SUBTITLE
+ << COMMAND_THREADSAFE
+ << COMMAND_TITLE;
+}
+
+/*!
+ The topic command has been processed. Now process the other
+ metacommands that were found. These are not the text markup
+ commands.
+ */
+void CodeParser::processCommonMetaCommand(const Location &location,
+ const QString &command,
+ const QString &arg,
+ Node *node,
+ Tree *tree)
+{
+ if (command == COMMAND_COMPAT) {
+ node->setStatus(Node::Compat);
+ }
+ else if (command == COMMAND_DEPRECATED) {
+ node->setStatus(Node::Deprecated);
+ }
+ else if (command == COMMAND_INGROUP) {
+ tree->addToGroup(node, arg);
+ }
+ else if (command == COMMAND_INPUBLICGROUP) {
+ tree->addToPublicGroup(node, arg);
+ }
+ else if (command == COMMAND_INMODULE) {
+ node->setModuleName(arg);
+ }
+ else if (command == COMMAND_MAINCLASS) {
+ node->setStatus(Node::Main);
+ }
+ else if (command == COMMAND_OBSOLETE) {
+ if (node->status() != Node::Compat)
+ node->setStatus(Node::Obsolete);
+ }
+ else if (command == COMMAND_NONREENTRANT) {
+ node->setThreadSafeness(Node::NonReentrant);
+ }
+ else if (command == COMMAND_PRELIMINARY) {
+ node->setStatus(Node::Preliminary);
+ }
+ else if (command == COMMAND_INTERNAL) {
+ node->setAccess(Node::Private);
+ node->setStatus(Node::Internal);
+ }
+ else if (command == COMMAND_REENTRANT) {
+ node->setThreadSafeness(Node::Reentrant);
+ }
+ else if (command == COMMAND_SINCE) {
+ node->setSince(arg);
+ }
+ else if (command == COMMAND_SUBTITLE) {
+ if (node->type() == Node::Fake) {
+ FakeNode *fake = static_cast<FakeNode *>(node);
+ fake->setSubTitle(arg);
+ }
+ else
+ location.warning(tr("Ignored '\\%1'").arg(COMMAND_SUBTITLE));
+ }
+ else if (command == COMMAND_THREADSAFE) {
+ node->setThreadSafeness(Node::ThreadSafe);
+ }
+ else if (command == COMMAND_TITLE) {
+ if (node->type() == Node::Fake) {
+ FakeNode *fake = static_cast<FakeNode *>(node);
+ fake->setTitle(arg);
+#ifdef QDOC2DOX
+ /* qdoc -> doxygen.
+ I think this must be done here, because there can be multiple
+ "\externalpage" and "\title" metacommands in a single qdoc
+ comment, which means, among other things, that the "\title"
+ commands are not inserted into the metacommand map used by
+ the Doc class. I'm sure there4 is a better way to do this in
+ the DoxWriter class using the information in the FakeNode,
+ but I don't have time to figure it out right now.
+ */
+ if (DoxWriter::isDoxPass(1))
+ DoxWriter::insertTitle(fake,arg);
+#endif
+ }
+ else
+ location.warning(tr("Ignored '\\%1'").arg(COMMAND_TITLE));
+ }
+}
+
+QT_END_NAMESPACE
diff --git a/tools/qdoc3/codeparser.h b/tools/qdoc3/codeparser.h
new file mode 100644
index 0000000..019e806
--- /dev/null
+++ b/tools/qdoc3/codeparser.h
@@ -0,0 +1,94 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*
+ codeparser.h
+*/
+
+#ifndef CODEPARSER_H
+#define CODEPARSER_H
+
+#include <QSet>
+
+#include "location.h"
+
+QT_BEGIN_NAMESPACE
+
+class Config;
+class Node;
+class QString;
+class Tree;
+
+class CodeParser
+{
+ public:
+ CodeParser();
+ virtual ~CodeParser();
+
+ virtual void initializeParser(const Config& config);
+ virtual void terminateParser();
+ virtual QString language() = 0;
+ virtual QString headerFileNameFilter();
+ virtual QString sourceFileNameFilter() = 0;
+ virtual void parseHeaderFile(const Location& location,
+ const QString& filePath, Tree *tree);
+ virtual void parseSourceFile(const Location& location,
+ const QString& filePath, Tree *tree) = 0;
+ virtual void doneParsingHeaderFiles(Tree *tree);
+ virtual void doneParsingSourceFiles(Tree *tree) = 0;
+
+ static void initialize(const Config& config);
+ static void terminate();
+ static CodeParser *parserForLanguage(const QString& language);
+
+ protected:
+ QSet<QString> commonMetaCommands();
+ void processCommonMetaCommand(const Location& location,
+ const QString& command, const QString& arg,
+ Node *node, Tree *tree);
+
+ private:
+ static QList<CodeParser *> parsers;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/tools/qdoc3/command.cpp b/tools/qdoc3/command.cpp
new file mode 100644
index 0000000..a82494d
--- /dev/null
+++ b/tools/qdoc3/command.cpp
@@ -0,0 +1,92 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*
+ command.cpp
+*/
+
+#include <QProcess>
+
+#include "command.h"
+
+QT_BEGIN_NAMESPACE
+
+void executeCommand( const Location& location, const QString& format,
+ const QStringList& args )
+{
+ QString actualCommand;
+ for ( int i = 0; i < (int) format.length(); i++ ) {
+ int ch = format[i].unicode();
+ if ( ch > 0 && ch < 8 ) {
+ actualCommand += args[ch - 1];
+ } else {
+ actualCommand += format[i];
+ }
+ }
+
+ QString toolName = actualCommand;
+ int space = toolName.indexOf( QLatin1Char(' ') );
+ if ( space != -1 )
+ toolName.truncate( space );
+
+ QProcess process;
+ process.start(QLatin1String("sh"),
+ QStringList() << QLatin1String("-c") << actualCommand );
+ process.waitForFinished();
+
+ if (process.exitCode() == 127)
+ location.fatal( tr("Couldn't launch the '%1' tool")
+ .arg(toolName),
+ tr("Make sure the tool is installed and in the"
+ " path.") );
+
+ QString errors = QString::fromLocal8Bit(process.readAllStandardError());
+ while ( errors.endsWith(QLatin1Char('\n')) )
+ errors.truncate( errors.length() - 1 );
+ if ( !errors.isEmpty() )
+ location.fatal( tr("The '%1' tool encountered some problems")
+ .arg(toolName),
+ tr("The tool was invoked like this:\n%1\n"
+ "It emitted these errors:\n%2")
+ .arg(actualCommand).arg(errors) );
+}
+
+QT_END_NAMESPACE
diff --git a/tools/qdoc3/command.h b/tools/qdoc3/command.h
new file mode 100644
index 0000000..4b86c38
--- /dev/null
+++ b/tools/qdoc3/command.h
@@ -0,0 +1,60 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*
+ command.h
+*/
+
+#ifndef COMMAND_H
+#define COMMAND_H
+
+#include <qstringlist.h>
+
+#include "location.h"
+
+QT_BEGIN_NAMESPACE
+
+void executeCommand( const Location& location, const QString& commandFormat,
+ const QStringList& args );
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/tools/qdoc3/config.cpp b/tools/qdoc3/config.cpp
new file mode 100644
index 0000000..c8488f3
--- /dev/null
+++ b/tools/qdoc3/config.cpp
@@ -0,0 +1,892 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*
+ config.cpp
+*/
+
+#include <QtCore>
+
+#include "archiveextractor.h"
+#include "config.h"
+#include "uncompressor.h"
+#include <stdlib.h>
+
+QT_BEGIN_NAMESPACE
+
+/*
+ An entry on the MetaStack.
+ */
+class MetaStackEntry
+{
+public:
+ void open();
+ void close();
+
+ QStringList accum;
+ QStringList next;
+};
+
+/*
+ */
+void MetaStackEntry::open()
+{
+ next.append(QString());
+}
+
+/*
+ */
+void MetaStackEntry::close()
+{
+ accum += next;
+ next.clear();
+}
+
+/*
+ ###
+*/
+class MetaStack : private QStack<MetaStackEntry>
+{
+public:
+ MetaStack();
+
+ void process(QChar ch, const Location& location);
+ QStringList getExpanded(const Location& location);
+};
+
+MetaStack::MetaStack()
+{
+ push(MetaStackEntry());
+ top().open();
+}
+
+void MetaStack::process(QChar ch, const Location& location)
+{
+ if (ch == QLatin1Char('{')) {
+ push(MetaStackEntry());
+ top().open();
+ }
+ else if (ch == QLatin1Char('}')) {
+ if (count() == 1)
+ location.fatal(tr("Unexpected '}'"));
+
+ top().close();
+ QStringList suffixes = pop().accum;
+ QStringList prefixes = top().next;
+
+ top().next.clear();
+ QStringList::ConstIterator pre = prefixes.begin();
+ while (pre != prefixes.end()) {
+ QStringList::ConstIterator suf = suffixes.begin();
+ while (suf != suffixes.end()) {
+ top().next << (*pre + *suf);
+ ++suf;
+ }
+ ++pre;
+ }
+ }
+ else if (ch == QLatin1Char(',') && count() > 1) {
+ top().close();
+ top().open();
+ }
+ else {
+ QStringList::Iterator pre = top().next.begin();
+ while (pre != top().next.end()) {
+ *pre += ch;
+ ++pre;
+ }
+ }
+}
+
+QStringList MetaStack::getExpanded(const Location& location)
+{
+ if (count() > 1)
+ location.fatal(tr("Missing '}'"));
+
+ top().close();
+ return top().accum;
+}
+
+QT_STATIC_CONST_IMPL QString Config::dot = QLatin1String(".");
+QMap<QString, QString> Config::uncompressedFiles;
+QMap<QString, QString> Config::extractedDirs;
+int Config::numInstances;
+
+/*!
+ \class Config
+ \brief The Config class contains the configuration variables
+ for controlling how qdoc produces documentation.
+
+ Its load() function, reads, parses, and processes a qdocconf file.
+ */
+
+/*!
+ The constructor sets the \a programName and initializes all
+ internal state variables to empty values.
+ */
+Config::Config(const QString& programName)
+ : prog(programName)
+{
+ loc = Location::null;
+ lastLoc = Location::null;
+ locMap.clear();
+ stringValueMap.clear();
+ stringListValueMap.clear();
+ numInstances++;
+}
+
+/*!
+ The destructor deletes all the temporary files and
+ directories it built.
+ */
+Config::~Config()
+{
+ if (--numInstances == 0) {
+ QMap<QString, QString>::ConstIterator f = uncompressedFiles.begin();
+ while (f != uncompressedFiles.end()) {
+ QDir().remove(*f);
+ ++f;
+ }
+ uncompressedFiles.clear();
+
+ QMap<QString, QString>::ConstIterator d = extractedDirs.begin();
+ while (d != extractedDirs.end()) {
+ removeDirContents(*d);
+ QDir dir(*d);
+ QString name = dir.dirName();
+ dir.cdUp();
+ dir.rmdir(name);
+ ++d;
+ }
+ extractedDirs.clear();
+ }
+}
+
+/*!
+ Loads and parses the qdoc configuration file \a fileName.
+ This function calls the other load() function, which does
+ the loading, parsing, and processing of the configuration
+ file.
+
+ Intializes the location variables returned by location()
+ and lastLocation().
+ */
+void Config::load(const QString& fileName)
+{
+ load(Location::null, fileName);
+ if (loc.isEmpty()) {
+ loc = Location(fileName);
+ }
+ else {
+ loc.setEtc(true);
+ }
+ lastLoc = Location::null;
+}
+
+/*!
+ Joins all the strings in \a values into a single string with the
+ individual \a values separated by ' '. Then it inserts the result
+ into the string list map with \a var as the key.
+
+ It also inserts the \a values string list into a separate map,
+ also with \a var as the key.
+ */
+void Config::setStringList(const QString& var, const QStringList& values)
+{
+ stringValueMap[var] = values.join(QLatin1String(" "));
+ stringListValueMap[var] = values;
+}
+
+/*!
+ Looks up the configuarion variable \a var in the string
+ map and returns the boolean value.
+ */
+bool Config::getBool(const QString& var) const
+{
+ return QVariant(getString(var)).toBool();
+}
+
+/*!
+ Looks up the configuration variable \a var in the string list
+ map. Iterates through the string list found, interpreting each
+ string in the list as an integer and adding it to a total sum.
+ Returns the sum.
+ */
+int Config::getInt(const QString& var) const
+{
+ QStringList strs = getStringList(var);
+ QStringList::ConstIterator s = strs.begin();
+ int sum = 0;
+
+ while (s != strs.end()) {
+ sum += (*s).toInt();
+ ++s;
+ }
+ return sum;
+}
+
+/*!
+ First, this function looks up the configuration variable \a var
+ in the location map and, if found, sets the internal variable
+ \c{lastLoc} to the Location that \a var maps to.
+
+ Then it looks up the configuration variable \a var in the string
+ map, and returns the string that \a var maps to.
+ */
+QString Config::getString(const QString& var) const
+{
+ if (!locMap[var].isEmpty())
+ (Location&) lastLoc = locMap[var];
+ return stringValueMap[var];
+}
+
+/*!
+ Looks up the configuration variable \a var in the string
+ list map, converts the string list it maps to into a set
+ of strings, and returns the set.
+ */
+QSet<QString> Config::getStringSet(const QString& var) const
+{
+ return QSet<QString>::fromList(getStringList(var));
+}
+
+/*!
+ First, this function looks up the configuration variable \a var
+ in the location map and, if found, sets the internal variable
+ \c{lastLoc} the Location that \a var maps to.
+
+ Then it looks up the configuration variable \a var in the string
+ list map, and returns the string list that \a var maps to.
+ */
+QStringList Config::getStringList(const QString& var) const
+{
+ if (!locMap[var].isEmpty())
+ (Location&) lastLoc = locMap[var];
+ return stringListValueMap[var];
+}
+
+/*!
+ Calls getRegExpList() with the control variable \a var and
+ iterates through the resulting list of regular expressions,
+ concatening them with some extras characters to form a single
+ QRegExp, which is returned/
+
+ \sa getRegExpList()
+ */
+QRegExp Config::getRegExp(const QString& var) const
+{
+ QString pattern;
+ QList<QRegExp> subRegExps = getRegExpList(var);
+ QList<QRegExp>::ConstIterator s = subRegExps.begin();
+
+ while (s != subRegExps.end()) {
+ if (!(*s).isValid())
+ return *s;
+ if (!pattern.isEmpty())
+ pattern += QLatin1Char('|');
+ pattern += QLatin1String("(?:") + (*s).pattern() + QLatin1Char(')');
+ ++s;
+ }
+ if (pattern.isEmpty())
+ pattern = QLatin1String("$x"); // cannot match
+ return QRegExp(pattern);
+}
+
+/*!
+ Looks up the configuration variable \a var in the string list
+ map, converts the string list to a list of regular expressions,
+ and returns it.
+ */
+QList<QRegExp> Config::getRegExpList(const QString& var) const
+{
+ QStringList strs = getStringList(var);
+ QStringList::ConstIterator s = strs.begin();
+ QList<QRegExp> regExps;
+
+ while (s != strs.end()) {
+ regExps += QRegExp(*s);
+ ++s;
+ }
+ return regExps;
+}
+
+/*!
+ This function is slower than it could be.
+ */
+QSet<QString> Config::subVars(const QString& var) const
+{
+ QSet<QString> result;
+ QString varDot = var + QLatin1Char('.');
+ QMap<QString, QString>::ConstIterator v = stringValueMap.begin();
+ while (v != stringValueMap.end()) {
+ if (v.key().startsWith(varDot)) {
+ QString subVar = v.key().mid(varDot.length());
+ int dot = subVar.indexOf(QLatin1Char('.'));
+ if (dot != -1)
+ subVar.truncate(dot);
+ result.insert(subVar);
+ }
+ ++v;
+ }
+ return result;
+}
+
+/*!
+ Builds and returns a list of file pathnames for the file
+ type specified by \a filesVar (e.g. "headers" or "sources").
+ The files are found in the directories specified by
+ \a dirsVar, and they are filtered by \a defaultNameFilter
+ if a better filter can't be constructed from \a filesVar.
+ The directories in \a excludedDirs are avoided.
+ */
+QStringList Config::getAllFiles(const QString &filesVar,
+ const QString &dirsVar,
+ const QString &defaultNameFilter,
+ const QSet<QString> &excludedDirs)
+{
+ QStringList result = getStringList(filesVar);
+ QStringList dirs = getStringList(dirsVar);
+
+ QString nameFilter = getString(filesVar + dot +
+ QLatin1String(CONFIG_FILEEXTENSIONS));
+ if (nameFilter.isEmpty())
+ nameFilter = defaultNameFilter;
+
+ QStringList::ConstIterator d = dirs.begin();
+ while (d != dirs.end()) {
+ result += getFilesHere(*d, nameFilter, excludedDirs);
+ ++d;
+ }
+ return result;
+}
+
+/*!
+ */
+QString Config::findFile(const Location& location,
+ const QStringList& files,
+ const QStringList& dirs,
+ const QString& fileName,
+ QString& userFriendlyFilePath)
+{
+ if (fileName.isEmpty() || fileName.startsWith(QLatin1Char('/'))) {
+ userFriendlyFilePath = fileName;
+ return fileName;
+ }
+
+ QFileInfo fileInfo;
+ QStringList components = fileName.split(QLatin1Char('?'));
+ QString firstComponent = components.first();
+
+ QStringList::ConstIterator f = files.begin();
+ while (f != files.end()) {
+ if (*f == firstComponent ||
+ (*f).endsWith(QLatin1Char('/') + firstComponent)) {
+ fileInfo.setFile(*f);
+ if (!fileInfo.exists())
+ location.fatal(tr("File '%1' does not exist").arg(*f));
+ break;
+ }
+ ++f;
+ }
+
+ if (fileInfo.fileName().isEmpty()) {
+ QStringList::ConstIterator d = dirs.begin();
+ while (d != dirs.end()) {
+ fileInfo.setFile(QDir(*d), firstComponent);
+ if (fileInfo.exists()) {
+ break;
+ }
+ ++d;
+ }
+ }
+
+ userFriendlyFilePath = QString();
+ if (!fileInfo.exists())
+ return QString();
+
+ QStringList::ConstIterator c = components.begin();
+ for (;;) {
+ bool isArchive = (c != components.end() - 1);
+ ArchiveExtractor *extractor = 0;
+ QString userFriendly = *c;
+
+ if (isArchive) {
+ extractor = ArchiveExtractor::extractorForFileName(userFriendly);
+ }
+
+ if (extractor == 0) {
+ Uncompressor *uncompressor =
+ Uncompressor::uncompressorForFileName(userFriendly);
+ if (uncompressor != 0) {
+ QString fileNameWithCorrectExtension =
+ uncompressor->uncompressedFilePath(
+ fileInfo.filePath());
+ QString uncompressed = uncompressedFiles[fileInfo.filePath()];
+ if (uncompressed.isEmpty()) {
+ uncompressed =
+ QTemporaryFile(fileInfo.filePath()).fileName();
+ uncompressor->uncompressFile(location,
+ fileInfo.filePath(),
+ uncompressed);
+ uncompressedFiles[fileInfo.filePath()] = uncompressed;
+ }
+ fileInfo.setFile(uncompressed);
+
+ if (isArchive) {
+ extractor = ArchiveExtractor::extractorForFileName(
+ fileNameWithCorrectExtension);
+ }
+ else {
+ userFriendly = fileNameWithCorrectExtension;
+ }
+ }
+ }
+ userFriendlyFilePath += userFriendly;
+
+ if (isArchive) {
+ if (extractor == 0)
+ location.fatal(tr("Unknown archive type '%1'")
+ .arg(userFriendlyFilePath));
+ QString extracted = extractedDirs[fileInfo.filePath()];
+ if (extracted.isEmpty()) {
+ extracted = QTemporaryFile(fileInfo.filePath()).fileName();
+ if (!QDir().mkdir(extracted))
+ location.fatal(tr("Cannot create temporary directory '%1'")
+ .arg(extracted));
+ extractor->extractArchive(location, fileInfo.filePath(),
+ extracted);
+ extractedDirs[fileInfo.filePath()] = extracted;
+ }
+ ++c;
+ fileInfo.setFile(QDir(extracted), *c);
+ }
+ else {
+ break;
+ }
+ userFriendlyFilePath += "?";
+ }
+ return fileInfo.filePath();
+}
+
+/*!
+ */
+QString Config::findFile(const Location& location,
+ const QStringList& files,
+ const QStringList& dirs,
+ const QString& fileBase,
+ const QStringList& fileExtensions,
+ QString& userFriendlyFilePath)
+{
+ QStringList::ConstIterator e = fileExtensions.begin();
+ while (e != fileExtensions.end()) {
+ QString filePath = findFile(location, files, dirs, fileBase + "." + *e,
+ userFriendlyFilePath);
+ if (!filePath.isEmpty())
+ return filePath;
+ ++e;
+ }
+ return findFile(location, files, dirs, fileBase, userFriendlyFilePath);
+}
+
+/*!
+ */
+QString Config::copyFile(const Location& location,
+ const QString& sourceFilePath,
+ const QString& userFriendlySourceFilePath,
+ const QString& targetDirPath)
+{
+ QFile inFile(sourceFilePath);
+ if (!inFile.open(QFile::ReadOnly)) {
+ location.fatal(tr("Cannot open input file '%1': %2")
+ .arg(inFile.fileName()).arg(inFile.errorString()));
+ return "";
+ }
+
+ QString outFileName = userFriendlySourceFilePath;
+ int slash = outFileName.lastIndexOf("/");
+ if (slash != -1)
+ outFileName = outFileName.mid(slash);
+
+ QFile outFile(targetDirPath + "/" + outFileName);
+ if (!outFile.open(QFile::WriteOnly)) {
+ location.fatal(tr("Cannot open output file '%1': %2")
+ .arg(outFile.fileName()).arg(outFile.errorString()));
+ return "";
+ }
+
+ char buffer[1024];
+ int len;
+ while ((len = inFile.read(buffer, sizeof(buffer))) > 0) {
+ outFile.write(buffer, len);
+ }
+ return outFileName;
+}
+
+/*!
+ Finds the largest unicode digit in \a value in the range
+ 1..7 and returns it.
+ */
+int Config::numParams(const QString& value)
+{
+ int max = 0;
+ for (int i = 0; i != value.length(); i++) {
+ uint c = value[i].unicode();
+ if (c > 0 && c < 8)
+ max = qMax(max, (int)c);
+ }
+ return max;
+}
+
+/*!
+ Removes everything from \a dir. This function is recursive.
+ It doesn't remove \a dir itself, but if it was called
+ recursively, then the caller will remove \a dir.
+ */
+bool Config::removeDirContents(const QString& dir)
+{
+ QDir dirInfo(dir);
+ QFileInfoList entries = dirInfo.entryInfoList();
+
+ bool ok = true;
+
+ QFileInfoList::Iterator it = entries.begin();
+ while (it != entries.end()) {
+ if ((*it).isFile()) {
+ if (!dirInfo.remove((*it).fileName()))
+ ok = false;
+ }
+ else if ((*it).isDir()) {
+ if ((*it).fileName() != "." && (*it).fileName() != "..") {
+ if (removeDirContents((*it).absoluteFilePath())) {
+ if (!dirInfo.rmdir((*it).fileName()))
+ ok = false;
+ }
+ else {
+ ok = false;
+ }
+ }
+ }
+ ++it;
+ }
+ return ok;
+}
+
+/*!
+ Returns true if \a ch is a letter, number, '_', '.',
+ '{', '}', or ','.
+ */
+bool Config::isMetaKeyChar(QChar ch)
+{
+ return ch.isLetterOrNumber()
+ || ch == QLatin1Char('_')
+ || ch == QLatin1Char('.')
+ || ch == QLatin1Char('{')
+ || ch == QLatin1Char('}')
+ || ch == QLatin1Char(',');
+}
+
+/*!
+ Load, parse, and process a qdoc configuration file. This
+ function is only called by the other load() function, but
+ this one is recursive, i.e., it calls itself when it sees
+ an \c{include} statement in the qdog configuration file.
+ */
+void Config::load(Location location, const QString& fileName)
+{
+ QRegExp keySyntax("\\w+(?:\\.\\w+)*");
+
+#define SKIP_CHAR() \
+ do { \
+ location.advance(c); \
+ ++i; \
+ c = text.at(i); \
+ cc = c.unicode(); \
+ } while (0)
+
+#define SKIP_SPACES() \
+ while (c.isSpace() && cc != '\n') \
+ SKIP_CHAR()
+
+#define PUT_CHAR() \
+ word += c; \
+ SKIP_CHAR();
+
+ if (location.depth() > 16)
+ location.fatal(tr("Too many nested includes"));
+
+ QFile fin(fileName);
+ if (!fin.open(QFile::ReadOnly | QFile::Text)) {
+ fin.setFileName(fileName + ".qdoc");
+ if (!fin.open(QFile::ReadOnly | QFile::Text))
+ location.fatal(tr("Cannot open file '%1': %2").arg(fileName).arg(fin.errorString()));
+ }
+
+ QString text = fin.readAll();
+ text += QLatin1String("\n\n");
+ text += QChar('\0');
+ fin.close();
+
+ location.push(fileName);
+ location.start();
+
+ int i = 0;
+ QChar c = text.at(0);
+ uint cc = c.unicode();
+ while (i < (int) text.length()) {
+ if (cc == 0)
+ ++i;
+ else if (c.isSpace()) {
+ SKIP_CHAR();
+ }
+ else if (cc == '#') {
+ do {
+ SKIP_CHAR();
+ } while (cc != '\n');
+ }
+ else if (isMetaKeyChar(c)) {
+ Location keyLoc = location;
+ bool plus = false;
+ QString stringValue;
+ QStringList stringListValue;
+ QString word;
+ bool inQuote = false;
+ bool prevWordQuoted = true;
+ bool metWord = false;
+
+ MetaStack stack;
+ do {
+ stack.process(c, location);
+ SKIP_CHAR();
+ } while (isMetaKeyChar(c));
+
+ QStringList keys = stack.getExpanded(location);
+ SKIP_SPACES();
+
+ if (keys.count() == 1 && keys.first() == "include") {
+ QString includeFile;
+
+ if (cc != '(')
+ location.fatal(tr("Bad include syntax"));
+ SKIP_CHAR();
+ SKIP_SPACES();
+ while (!c.isSpace() && cc != '#' && cc != ')') {
+ includeFile += c;
+ SKIP_CHAR();
+ }
+ SKIP_SPACES();
+ if (cc != ')')
+ location.fatal(tr("Bad include syntax"));
+ SKIP_CHAR();
+ SKIP_SPACES();
+ if (cc != '#' && cc != '\n')
+ location.fatal(tr("Trailing garbage"));
+
+ /*
+ Here is the recursive call.
+ */
+ load(location,
+ QFileInfo(QFileInfo(fileName).dir(), includeFile)
+ .filePath());
+ }
+ else {
+ /*
+ It wasn't an include statement, so it;s something else.
+ */
+ if (cc == '+') {
+ plus = true;
+ SKIP_CHAR();
+ }
+ if (cc != '=')
+ location.fatal(tr("Expected '=' or '+=' after key"));
+ SKIP_CHAR();
+ SKIP_SPACES();
+
+ for (;;) {
+ if (cc == '\\') {
+ int metaCharPos;
+
+ SKIP_CHAR();
+ if (cc == '\n') {
+ SKIP_CHAR();
+ }
+ else if (cc > '0' && cc < '8') {
+ word += QChar(c.digitValue());
+ SKIP_CHAR();
+ }
+ else if ((metaCharPos = QString(QLatin1String("abfnrtv")).indexOf(c)) != -1) {
+ word += "\a\b\f\n\r\t\v"[metaCharPos];
+ SKIP_CHAR();
+ }
+ else {
+ PUT_CHAR();
+ }
+ }
+ else if (c.isSpace() || cc == '#') {
+ if (inQuote) {
+ if (cc == '\n')
+ location.fatal(tr("Unterminated string"));
+ PUT_CHAR();
+ }
+ else {
+ if (!word.isEmpty()) {
+ if (metWord)
+ stringValue += QLatin1Char(' ');
+ stringValue += word;
+ stringListValue << word;
+ metWord = true;
+ word.clear();
+ prevWordQuoted = false;
+ }
+ if (cc == '\n' || cc == '#')
+ break;
+ SKIP_SPACES();
+ }
+ }
+ else if (cc == '"') {
+ if (inQuote) {
+ if (!prevWordQuoted)
+ stringValue += QLatin1Char(' ');
+ stringValue += word;
+ if (!word.isEmpty())
+ stringListValue << word;
+ metWord = true;
+ word.clear();
+ prevWordQuoted = true;
+ }
+ inQuote = !inQuote;
+ SKIP_CHAR();
+ }
+ else if (cc == '$') {
+ QString var;
+ SKIP_CHAR();
+ while (c.isLetterOrNumber() || cc == '_') {
+ var += c;
+ SKIP_CHAR();
+ }
+ if (!var.isEmpty()) {
+ char *val = getenv(var.toLatin1().data());
+ if (val == 0) {
+ location.fatal(tr("Environment variable '%1' undefined").arg(var));
+ }
+ else {
+ word += QString(val);
+ }
+ }
+ }
+ else {
+ if (!inQuote && cc == '=')
+ location.fatal(tr("Unexpected '='"));
+ PUT_CHAR();
+ }
+ }
+
+ QStringList::ConstIterator key = keys.begin();
+ while (key != keys.end()) {
+ if (!keySyntax.exactMatch(*key))
+ keyLoc.fatal(tr("Invalid key '%1'").arg(*key));
+
+ if (plus) {
+ if (locMap[*key].isEmpty()) {
+ locMap[*key] = keyLoc;
+ }
+ else {
+ locMap[*key].setEtc(true);
+ }
+ if (stringValueMap[*key].isEmpty()) {
+ stringValueMap[*key] = stringValue;
+ }
+ else {
+ stringValueMap[*key] +=
+ QLatin1Char(' ') + stringValue;
+ }
+ stringListValueMap[*key] += stringListValue;
+ }
+ else {
+ locMap[*key] = keyLoc;
+ stringValueMap[*key] = stringValue;
+ stringListValueMap[*key] = stringListValue;
+ }
+ ++key;
+ }
+ }
+ }
+ else {
+ location.fatal(tr("Unexpected character '%1' at beginning of line")
+ .arg(c));
+ }
+ }
+}
+
+QStringList Config::getFilesHere(const QString& dir,
+ const QString& nameFilter,
+ const QSet<QString> &excludedDirs)
+{
+ QStringList result;
+ if (excludedDirs.contains(dir))
+ return result;
+
+ QDir dirInfo(dir);
+ QStringList fileNames;
+ QStringList::const_iterator fn;
+
+ dirInfo.setNameFilters(nameFilter.split(' '));
+ dirInfo.setSorting(QDir::Name);
+ dirInfo.setFilter(QDir::Files);
+ fileNames = dirInfo.entryList();
+ fn = fileNames.constBegin();
+ while (fn != fileNames.constEnd()) {
+ if (!fn->startsWith(QLatin1Char('~')))
+ result.append(dirInfo.filePath(*fn));
+ ++fn;
+ }
+
+ dirInfo.setNameFilters(QStringList("*"));
+ dirInfo.setFilter(QDir::Dirs|QDir::NoDotAndDotDot);
+ fileNames = dirInfo.entryList();
+ fn = fileNames.constBegin();
+ while (fn != fileNames.constEnd()) {
+ result += getFilesHere(dirInfo.filePath(*fn), nameFilter, excludedDirs);
+ ++fn;
+ }
+ return result;
+}
+
+QT_END_NAMESPACE
diff --git a/tools/qdoc3/config.h b/tools/qdoc3/config.h
new file mode 100644
index 0000000..9443f0d
--- /dev/null
+++ b/tools/qdoc3/config.h
@@ -0,0 +1,165 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*
+ config.h
+*/
+
+#ifndef CONFIG_H
+#define CONFIG_H
+
+#include <QMap>
+#include <QSet>
+#include <QStringList>
+
+#include "location.h"
+
+QT_BEGIN_NAMESPACE
+
+class Config
+{
+ public:
+ Config(const QString& programName);
+ ~Config();
+
+ void load(const QString& fileName);
+ void setStringList(const QString& var, const QStringList& values);
+
+ const QString& programName() const { return prog; }
+ const Location& location() const { return loc; }
+ const Location& lastLocation() const { return lastLoc; }
+ bool getBool(const QString& var) const;
+ int getInt(const QString& var) const;
+ QString getString(const QString& var) const;
+ QSet<QString> getStringSet(const QString& var) const;
+ QStringList getStringList(const QString& var) const;
+ QRegExp getRegExp(const QString& var) const;
+ QList<QRegExp> getRegExpList(const QString& var) const;
+ QSet<QString> subVars(const QString& var) const;
+ QStringList getAllFiles(const QString& filesVar,
+ const QString& dirsVar,
+ const QString& defaultNameFilter,
+ const QSet<QString> &excludedDirs = QSet<QString>());
+
+ static QStringList getFilesHere(const QString& dir,
+ const QString& nameFilter,
+ const QSet<QString> &excludedDirs = QSet<QString>());
+ static QString findFile(const Location& location,
+ const QStringList &files,
+ const QStringList& dirs,
+ const QString& fileName,
+ QString& userFriendlyFilePath);
+ static QString findFile(const Location &location,
+ const QStringList &files,
+ const QStringList &dirs,
+ const QString &fileBase,
+ const QStringList &fileExtensions,
+ QString &userFriendlyFilePath);
+ static QString copyFile(const Location& location,
+ const QString& sourceFilePath,
+ const QString& userFriendlySourceFilePath,
+ const QString& targetDirPath);
+ static int numParams(const QString& value);
+ static bool removeDirContents(const QString& dir);
+
+ QT_STATIC_CONST QString dot;
+
+ private:
+ static bool isMetaKeyChar(QChar ch);
+ void load(Location location, const QString& fileName);
+
+ QString prog;
+ Location loc;
+ Location lastLoc;
+ QMap<QString, Location> locMap;
+ QMap<QString, QStringList> stringListValueMap;
+ QMap<QString, QString> stringValueMap;
+
+ static QMap<QString, QString> uncompressedFiles;
+ static QMap<QString, QString> extractedDirs;
+ static int numInstances;
+};
+
+#define CONFIG_ALIAS "alias"
+#define CONFIG_BASE "base" // ### don't document for now
+#define CONFIG_CODEINDENT "codeindent"
+#define CONFIG_DEFINES "defines"
+#define CONFIG_DESCRIPTION "description"
+#define CONFIG_EDITION "edition"
+#define CONFIG_EXAMPLEDIRS "exampledirs"
+#define CONFIG_EXAMPLES "examples"
+#define CONFIG_EXCLUDEDIRS "excludedirs"
+#define CONFIG_EXTRAIMAGES "extraimages"
+#define CONFIG_FALSEHOODS "falsehoods"
+#define CONFIG_FORMATTING "formatting"
+#define CONFIG_GENERATEINDEX "generateindex"
+#define CONFIG_HEADERDIRS "headerdirs"
+#define CONFIG_HEADERS "headers"
+#define CONFIG_IGNOREDIRECTIVES "ignoredirectives"
+#define CONFIG_IGNORETOKENS "ignoretokens"
+#define CONFIG_IMAGEDIRS "imagedirs"
+#define CONFIG_IMAGES "images"
+#define CONFIG_INDEXES "indexes"
+#define CONFIG_LANGUAGE "language"
+#define CONFIG_MACRO "macro"
+#define CONFIG_OUTPUTDIR "outputdir"
+#define CONFIG_OUTPUTLANGUAGE "outputlanguage"
+#define CONFIG_OUTPUTFORMATS "outputformats"
+#define CONFIG_PROJECT "project"
+#define CONFIG_QHP "qhp"
+#define CONFIG_QUOTINGINFORMATION "quotinginformation"
+#define CONFIG_SLOW "slow"
+#define CONFIG_SOURCEDIRS "sourcedirs"
+#define CONFIG_SOURCES "sources"
+#define CONFIG_SPURIOUS "spurious"
+#define CONFIG_STYLESHEETS "stylesheets"
+#define CONFIG_TABSIZE "tabsize"
+#define CONFIG_TAGFILE "tagfile"
+#define CONFIG_TRANSLATORS "translators" // ### don't document for now
+#define CONFIG_URL "url"
+#define CONFIG_VERSION "version"
+#define CONFIG_VERSIONSYM "versionsym"
+
+#define CONFIG_FILEEXTENSIONS "fileextensions"
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/tools/qdoc3/cppcodemarker.cpp b/tools/qdoc3/cppcodemarker.cpp
new file mode 100644
index 0000000..59b967b
--- /dev/null
+++ b/tools/qdoc3/cppcodemarker.cpp
@@ -0,0 +1,1009 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*
+ cppcodemarker.cpp
+*/
+
+#include "atom.h"
+#include "cppcodemarker.h"
+#include "node.h"
+#include "text.h"
+#include "tree.h"
+
+QT_BEGIN_NAMESPACE
+
+static int insertTagAround(QString &result, int pos, int len, const QString &tagName,
+ const QString &attributes = QString())
+{
+ QString s;
+ //s.reserve(result.size() + tagName.size() * 2 + attributes.size() + 20);
+ s += result.midRef(0, pos);
+ s += QLatin1Char('<');
+ s += tagName;
+ if (!attributes.isEmpty()) {
+ s += QLatin1Char(' ');
+ s += attributes;
+ }
+ s += QLatin1Char('>');
+ s += result.midRef(pos, len);
+ s += QLatin1String("</");
+ s += tagName;
+ s += QLatin1Char('>');
+ s += result.midRef(pos + len);
+ int diff = s.length() - result.length();
+ result = s;
+ return diff;
+}
+
+/*!
+ The constructor does nothing.
+ */
+CppCodeMarker::CppCodeMarker()
+{
+ // nothing.
+}
+
+/*!
+ The destructor does nothing.
+ */
+CppCodeMarker::~CppCodeMarker()
+{
+ // nothing.
+}
+
+/*!
+ Returns true.
+ */
+bool CppCodeMarker::recognizeCode(const QString & /* code */)
+{
+ return true;
+}
+
+/*!
+ Returns true if \a ext is any of a list of file extensions
+ for the C++ language.
+ */
+bool CppCodeMarker::recognizeExtension(const QString& ext)
+{
+ return ext == "c" ||
+ ext == "c++" ||
+ ext == "cc" ||
+ ext == "cpp" ||
+ ext == "cxx" ||
+ ext == "ch" ||
+ ext == "h" ||
+ ext == "h++" ||
+ ext == "hh" ||
+ ext == "hpp" ||
+ ext == "hxx";
+}
+
+/*!
+ Returns true if \a lang is either "C" or "Cpp".
+ */
+bool CppCodeMarker::recognizeLanguage(const QString &lang)
+{
+ return lang == "C" || lang == "Cpp";
+}
+
+/*!
+ Returns the \a node name, or "()" if \a node is a
+ Node::Function node.
+ */
+QString CppCodeMarker::plainName(const Node *node)
+{
+ QString name = node->name();
+ if (node->type() == Node::Function)
+ name += "()";
+ return name;
+}
+
+QString CppCodeMarker::plainFullName(const Node *node, const Node *relative)
+{
+ if (node->name().isEmpty()) {
+ return "global";
+ }
+ else {
+ QString fullName;
+ for (;;) {
+ fullName.prepend(plainName(node));
+ if (node->parent() == relative || node->parent()->name().isEmpty())
+ break;
+ fullName.prepend("::");
+ node = node->parent();
+ }
+ return fullName;
+ }
+}
+
+QString CppCodeMarker::markedUpCode(const QString &code,
+ const Node *relative,
+ const QString &dirPath)
+{
+ return addMarkUp(protect(code), relative, dirPath);
+}
+
+QString CppCodeMarker::markedUpSynopsis(const Node *node,
+ const Node * /* relative */,
+ SynopsisStyle style)
+{
+ const int MaxEnumValues = 6;
+ const FunctionNode *func;
+ const PropertyNode *property;
+ const VariableNode *variable;
+ const EnumNode *enume;
+ const TypedefNode *typedeff;
+ QString synopsis;
+ QString extra;
+ QString name;
+
+ name = taggedNode(node);
+ if (style != Detailed)
+ name = linkTag(node, name);
+ name = "<@name>" + name + "</@name>";
+
+ if (style == Detailed && !node->parent()->name().isEmpty() &&
+ node->type() != Node::Property)
+ name.prepend(taggedNode(node->parent()) + "::");
+
+ switch (node->type()) {
+ case Node::Namespace:
+ synopsis = "namespace " + name;
+ break;
+ case Node::Class:
+ synopsis = "class " + name;
+ break;
+ case Node::Function:
+ func = (const FunctionNode *) node;
+ if (style != SeparateList && !func->returnType().isEmpty())
+ synopsis = typified(func->returnType()) + " ";
+ synopsis += name;
+ if (func->metaness() != FunctionNode::MacroWithoutParams) {
+ synopsis += " (";
+ if (!func->parameters().isEmpty()) {
+ synopsis += " ";
+ QList<Parameter>::ConstIterator p = func->parameters().begin();
+ while (p != func->parameters().end()) {
+ if (p != func->parameters().begin())
+ synopsis += ", ";
+ synopsis += typified((*p).leftType());
+ if (style != SeparateList && !(*p).name().isEmpty())
+ synopsis +=
+ " <@param>" + protect((*p).name()) + "</@param>";
+ synopsis += protect((*p).rightType());
+ if (style != SeparateList && !(*p).defaultValue().isEmpty())
+ synopsis += " = " + protect((*p).defaultValue());
+ ++p;
+ }
+ synopsis += " ";
+ }
+ synopsis += ")";
+ }
+ if (func->isConst())
+ synopsis += " const";
+
+ if (style == Summary || style == Accessors) {
+ if (func->virtualness() != FunctionNode::NonVirtual)
+ synopsis.prepend("virtual ");
+ if (func->virtualness() == FunctionNode::PureVirtual)
+ synopsis.append(" = 0");
+ }
+ else if (style == SeparateList) {
+ if (!func->returnType().isEmpty() && func->returnType() != "void")
+ synopsis += " : " + typified(func->returnType());
+ }
+ else {
+ QStringList bracketed;
+ if (func->isStatic()) {
+ bracketed += "static";
+ }
+ else if (func->virtualness() != FunctionNode::NonVirtual) {
+ if (func->virtualness() == FunctionNode::PureVirtual)
+ bracketed += "pure";
+ bracketed += "virtual";
+ }
+
+ if (func->access() == Node::Protected) {
+ bracketed += "protected";
+ }
+ else if (func->access() == Node::Private) {
+ bracketed += "private";
+ }
+
+ if (func->metaness() == FunctionNode::Signal) {
+ bracketed += "signal";
+ }
+ else if (func->metaness() == FunctionNode::Slot) {
+ bracketed += "slot";
+ }
+ if (!bracketed.isEmpty())
+ extra += " [" + bracketed.join(" ") + "]";
+ }
+ break;
+ case Node::Enum:
+ enume = static_cast<const EnumNode *>(node);
+ synopsis = "enum " + name;
+ if (style == Summary) {
+ synopsis += " { ";
+
+ QStringList documentedItems = enume->doc().enumItemNames();
+ if (documentedItems.isEmpty()) {
+ foreach (const EnumItem &item, enume->items())
+ documentedItems << item.name();
+ }
+ QStringList omitItems = enume->doc().omitEnumItemNames();
+ foreach (const QString &item, omitItems)
+ documentedItems.removeAll(item);
+
+ if (documentedItems.size() <= MaxEnumValues) {
+ for (int i = 0; i < documentedItems.size(); ++i) {
+ if (i != 0)
+ synopsis += ", ";
+ synopsis += documentedItems.at(i);
+ }
+ }
+ else {
+ for (int i = 0; i < documentedItems.size(); ++i) {
+ if (i < MaxEnumValues - 2 || i == documentedItems.size() - 1) {
+ if (i != 0)
+ synopsis += ", ";
+ synopsis += documentedItems.at(i);
+ }
+ else if (i == MaxEnumValues - 1) {
+ synopsis += ", ...";
+ }
+ }
+ }
+ if (!documentedItems.isEmpty())
+ synopsis += " ";
+ synopsis += "}";
+ }
+ break;
+ case Node::Typedef:
+ typedeff = static_cast<const TypedefNode *>(node);
+ if (typedeff->associatedEnum()) {
+ synopsis = "flags " + name;
+ }
+ else {
+ synopsis = "typedef " + name;
+ }
+ break;
+ case Node::Property:
+ property = static_cast<const PropertyNode *>(node);
+ synopsis = name + " : " + typified(property->qualifiedDataType());
+ break;
+ case Node::Variable:
+ variable = static_cast<const VariableNode *>(node);
+ if (style == SeparateList) {
+ synopsis = name + " : " + typified(variable->dataType());
+ }
+ else {
+ synopsis = typified(variable->leftType()) + " " +
+ name + protect(variable->rightType());
+ }
+ break;
+ default:
+ synopsis = name;
+ }
+
+ if (style == Summary) {
+ if (node->status() == Node::Preliminary) {
+ extra += " (preliminary)";
+ }
+ else if (node->status() == Node::Deprecated) {
+ extra += " (deprecated)";
+ }
+ else if (node->status() == Node::Obsolete) {
+ extra += " (obsolete)";
+ }
+ }
+
+ if (!extra.isEmpty()) {
+ extra.prepend("<@extra>");
+ extra.append("</@extra>");
+ }
+ return synopsis + extra;
+}
+
+QString CppCodeMarker::markedUpName(const Node *node)
+{
+ QString name = linkTag(node, taggedNode(node));
+ if (node->type() == Node::Function)
+ name += "()";
+ return name;
+}
+
+QString CppCodeMarker::markedUpFullName(const Node *node, const Node *relative)
+{
+ if (node->name().isEmpty()) {
+ return "global";
+ }
+ else {
+ QString fullName;
+ for (;;) {
+ fullName.prepend(markedUpName(node));
+ if (node->parent() == relative || node->parent()->name().isEmpty())
+ break;
+ fullName.prepend("<@op>::</@op>");
+ node = node->parent();
+ }
+ return fullName;
+ }
+}
+
+QString CppCodeMarker::markedUpEnumValue(const QString &enumValue,
+ const Node *relative)
+{
+ const Node *node = relative->parent();
+ QString fullName;
+ while (node->parent()) {
+ fullName.prepend(markedUpName(node));
+ if (node->parent() == relative || node->parent()->name().isEmpty())
+ break;
+ fullName.prepend("<@op>::</@op>");
+ node = node->parent();
+ }
+ if (!fullName.isEmpty())
+ fullName.append("<@op>::</@op>");
+ fullName.append(enumValue);
+ return fullName;
+}
+
+QString CppCodeMarker::markedUpIncludes(const QStringList& includes)
+{
+ QString code;
+
+ QStringList::ConstIterator inc = includes.begin();
+ while (inc != includes.end()) {
+ code += "#include &lt;<@headerfile>" + *inc + "</@headerfile>&gt;\n";
+ ++inc;
+ }
+ return addMarkUp(code, 0, "");
+}
+
+QString CppCodeMarker::functionBeginRegExp(const QString& funcName)
+{
+ return "^" + QRegExp::escape(funcName) + "$";
+
+}
+
+QString CppCodeMarker::functionEndRegExp(const QString& /* funcName */)
+{
+ return "^\\}$";
+}
+
+QList<Section> CppCodeMarker::sections(const InnerNode *inner,
+ SynopsisStyle style,
+ Status status)
+{
+ QList<Section> sections;
+
+ if (inner->type() == Node::Class) {
+ const ClassNode *classe = static_cast<const ClassNode *>(inner);
+
+ if (style == Summary) {
+ FastSection privateFunctions(classe, "Private Functions", "private function",
+ "private functions");
+ FastSection privateSlots(classe, "Private Slots", "private slot", "private slots");
+ FastSection privateTypes(classe, "Private Types", "private type", "private types");
+ FastSection protectedFunctions(classe, "Protected Functions", "protected function",
+ "protected functions");
+ FastSection protectedSlots(classe, "Protected Slots", "protected slot", "protected slots");
+ FastSection protectedTypes(classe, "Protected Types", "protected type", "protected types");
+ FastSection protectedVariables(classe, "Protected Variables", "protected type", "protected variables");
+ FastSection publicFunctions(classe, "Public Functions", "public function",
+ "public functions");
+ FastSection publicSignals(classe, "Signals", "signal", "signals");
+ FastSection publicSlots(classe, "Public Slots", "public slot", "public slots");
+ FastSection publicTypes(classe, "Public Types", "public type", "public types");
+ FastSection publicVariables(classe, "Public Variables", "public type", "public variables");
+ FastSection properties(classe, "Properties", "property", "properties");
+ FastSection relatedNonMembers(classe, "Related Non-Members", "related non-member",
+ "related non-members");
+ FastSection staticPrivateMembers(classe, "Static Private Members", "static private member",
+ "static private members");
+ FastSection staticProtectedMembers(classe, "Static Protected Members",
+ "static protected member", "static protected members");
+ FastSection staticPublicMembers(classe, "Static Public Members", "static public member",
+ "static public members");
+ FastSection macros(inner, "Macros", "macro", "macros");
+
+ NodeList::ConstIterator r = classe->relatedNodes().begin();
+ while (r != classe->relatedNodes().end()) {
+ if ((*r)->type() == Node::Function) {
+ FunctionNode *func = static_cast<FunctionNode *>(*r);
+ if (func->isMacro())
+ insert(macros, *r, style, status);
+ else
+ insert(relatedNonMembers, *r, style, status);
+ }
+ else {
+ insert(relatedNonMembers, *r, style, status);
+ }
+ ++r;
+ }
+
+ QStack<const ClassNode *> stack;
+ stack.push(classe);
+
+ while (!stack.isEmpty()) {
+ const ClassNode *ancestorClass = stack.pop();
+
+ NodeList::ConstIterator c = ancestorClass->childNodes().begin();
+ while (c != ancestorClass->childNodes().end()) {
+ bool isSlot = false;
+ bool isSignal = false;
+ bool isStatic = false;
+ if ((*c)->type() == Node::Function) {
+ const FunctionNode *func = (const FunctionNode *) *c;
+ isSlot = (func->metaness() == FunctionNode::Slot);
+ isSignal = (func->metaness() == FunctionNode::Signal);
+ isStatic = func->isStatic();
+ }
+ else if ((*c)->type() == Node::Variable) {
+ const VariableNode *var = static_cast<const VariableNode *>(*c);
+ isStatic = var->isStatic();
+ }
+
+ switch ((*c)->access()) {
+ case Node::Public:
+ if (isSlot) {
+ insert(publicSlots, *c, style, status);
+ }
+ else if (isSignal) {
+ insert(publicSignals, *c, style, status);
+ }
+ else if (isStatic) {
+ if ((*c)->type() != Node::Variable
+ || !(*c)->doc().isEmpty())
+ insert(staticPublicMembers, *c, style, status);
+ }
+ else if ((*c)->type() == Node::Property) {
+ insert(properties, *c, style, status);
+ }
+ else if ((*c)->type() == Node::Variable) {
+ if (!(*c)->doc().isEmpty())
+ insert(publicVariables, *c, style, status);
+ }
+ else if ((*c)->type() == Node::Function) {
+ insert(publicFunctions, *c, style, status);
+ }
+ else {
+ insert(publicTypes, *c, style, status);
+ }
+ break;
+ case Node::Protected:
+ if (isSlot) {
+ insert(protectedSlots, *c, style, status);
+ }
+ else if (isStatic) {
+ if ((*c)->type() != Node::Variable
+ || !(*c)->doc().isEmpty())
+ insert(staticProtectedMembers, *c, style, status);
+ }
+ else if ((*c)->type() == Node::Variable) {
+ if (!(*c)->doc().isEmpty())
+ insert(protectedVariables, *c, style, status);
+ }
+ else if ((*c)->type() == Node::Function) {
+ insert(protectedFunctions, *c, style, status);
+ }
+ else {
+ insert(protectedTypes, *c, style, status);
+ }
+ break;
+ case Node::Private:
+ if (isSlot) {
+ insert(privateSlots, *c, style, status);
+ }
+ else if (isStatic) {
+ if ((*c)->type() != Node::Variable
+ || !(*c)->doc().isEmpty())
+ insert(staticPrivateMembers, *c, style, status);
+ }
+ else if ((*c)->type() == Node::Function) {
+ insert(privateFunctions, *c, style, status);
+ }
+ else {
+ insert(privateTypes, *c, style, status);
+ }
+ }
+ ++c;
+ }
+
+ QList<RelatedClass>::ConstIterator r =
+ ancestorClass->baseClasses().begin();
+ while (r != ancestorClass->baseClasses().end()) {
+ stack.prepend((*r).node);
+ ++r;
+ }
+ }
+
+ append(sections, publicTypes);
+ append(sections, properties);
+ append(sections, publicFunctions);
+ append(sections, publicSlots);
+ append(sections, publicSignals);
+ append(sections, publicVariables);
+ append(sections, staticPublicMembers);
+ append(sections, protectedTypes);
+ append(sections, protectedFunctions);
+ append(sections, protectedSlots);
+ append(sections, protectedVariables);
+ append(sections, staticProtectedMembers);
+ append(sections, privateTypes);
+ append(sections, privateFunctions);
+ append(sections, privateSlots);
+ append(sections, staticPrivateMembers);
+ append(sections, relatedNonMembers);
+ append(sections, macros);
+ }
+ else if (style == Detailed) {
+ FastSection memberFunctions(classe,"Member Function Documentation");
+ FastSection memberTypes(classe,"Member Type Documentation");
+ FastSection memberVariables(classe,"Member Variable Documentation");
+ FastSection properties(classe,"Property Documentation");
+ FastSection relatedNonMembers(classe,"Related Non-Members");
+ FastSection macros(classe,"Macro Documentation");
+
+ NodeList::ConstIterator r = classe->relatedNodes().begin();
+ while (r != classe->relatedNodes().end()) {
+ if ((*r)->type() == Node::Function) {
+ FunctionNode *func = static_cast<FunctionNode *>(*r);
+ if (func->isMacro())
+ insert(macros, *r, style, status);
+ else
+ insert(relatedNonMembers, *r, style, status);
+ }
+ else {
+ insert(relatedNonMembers, *r, style, status);
+ }
+ ++r;
+ }
+
+ NodeList::ConstIterator c = classe->childNodes().begin();
+ while (c != classe->childNodes().end()) {
+ if ((*c)->type() == Node::Enum ||
+ (*c)->type() == Node::Typedef) {
+ insert(memberTypes, *c, style, status);
+ }
+ else if ((*c)->type() == Node::Property) {
+ insert(properties, *c, style, status);
+ }
+ else if ((*c)->type() == Node::Variable) {
+ if (!(*c)->doc().isEmpty())
+ insert(memberVariables, *c, style, status);
+ }
+ else if ((*c)->type() == Node::Function) {
+ FunctionNode *function = static_cast<FunctionNode *>(*c);
+ if (!function->associatedProperty())
+ insert(memberFunctions, function, style, status);
+ }
+ ++c;
+ }
+
+ append(sections, memberTypes);
+ append(sections, properties);
+ append(sections, memberFunctions);
+ append(sections, memberVariables);
+ append(sections, relatedNonMembers);
+ append(sections, macros);
+ }
+ else {
+ FastSection all(classe);
+
+ QStack<const ClassNode *> stack;
+ stack.push(classe);
+
+ while (!stack.isEmpty()) {
+ const ClassNode *ancestorClass = stack.pop();
+
+ NodeList::ConstIterator c = ancestorClass->childNodes().begin();
+ while (c != ancestorClass->childNodes().end()) {
+ if ((*c)->access() != Node::Private &&
+ (*c)->type() != Node::Property)
+ insert(all, *c, style, status);
+ ++c;
+ }
+
+ QList<RelatedClass>::ConstIterator r =
+ ancestorClass->baseClasses().begin();
+ while (r != ancestorClass->baseClasses().end()) {
+ stack.prepend((*r).node);
+ ++r;
+ }
+ }
+ append(sections, all);
+ }
+ }
+ else {
+ if (style == Summary || style == Detailed) {
+ FastSection namespaces(inner,
+ "Namespaces",
+ "namespace",
+ "namespaces");
+ FastSection classes(inner, "Classes", "class", "classes");
+ FastSection types(inner,
+ style == Summary ? "Types" : "Type Documentation",
+ "type",
+ "types");
+ FastSection functions(inner,
+ style == Summary ? "Functions" : "Function Documentation",
+ "function",
+ "functions");
+ FastSection macros(inner,
+ style == Summary ? "Macros" : "Macro Documentation",
+ "macro",
+ "macros");
+
+ NodeList nodeList = inner->childNodes();
+ nodeList += inner->relatedNodes();
+
+ NodeList::ConstIterator n = nodeList.begin();
+ while (n != nodeList.end()) {
+ switch ((*n)->type()) {
+ case Node::Namespace:
+ insert(namespaces, *n, style, status);
+ break;
+ case Node::Class:
+ insert(classes, *n, style, status);
+ break;
+ case Node::Enum:
+ case Node::Typedef:
+ insert(types, *n, style, status);
+ break;
+ case Node::Function:
+ {
+ FunctionNode *func = static_cast<FunctionNode *>(*n);
+ if (func->isMacro())
+ insert(macros, *n, style, status);
+ else
+ insert(functions, *n, style, status);
+ }
+ break;
+ default:
+ ;
+ }
+ ++n;
+ }
+ append(sections, namespaces);
+ append(sections, classes);
+ append(sections, types);
+ append(sections, functions);
+ append(sections, macros);
+ }
+ }
+
+ return sections;
+}
+
+const Node *CppCodeMarker::resolveTarget(const QString &target,
+ const Tree *tree,
+ const Node *relative)
+{
+ if (target.endsWith("()")) {
+ const FunctionNode *func;
+ QString funcName = target;
+ funcName.chop(2);
+
+ QStringList path = funcName.split("::");
+ if ((func = tree->findFunctionNode(path,
+ relative,
+ Tree::SearchBaseClasses))
+ && func->metaness() != FunctionNode::MacroWithoutParams)
+ return func;
+ }
+ else if (target.contains("#")) {
+ // ### this doesn't belong here; get rid of TargetNode hack
+ int hashAt = target.indexOf("#");
+ QString link = target.left(hashAt);
+ QString ref = target.mid(hashAt + 1);
+ const Node *node;
+ if (link.isEmpty()) {
+ node = relative;
+ }
+ else {
+ QStringList path(link);
+ node = tree->findNode(path, tree->root(), Tree::SearchBaseClasses);
+ }
+ if (node && node->isInnerNode()) {
+ const Atom *atom = node->doc().body().firstAtom();
+ while (atom) {
+ if (atom->type() == Atom::Target && atom->string() == ref) {
+ Node *parentNode = const_cast<Node *>(node);
+ return new TargetNode(static_cast<InnerNode*>(parentNode),
+ ref);
+ }
+ atom = atom->next();
+ }
+ }
+ }
+ else {
+ QStringList path = target.split("::");
+ const Node *node;
+ if ((node = tree->findNode(path,
+ relative,
+ Tree::SearchBaseClasses |
+ Tree::SearchEnumValues |
+ Tree::NonFunction)))
+ return node;
+ }
+ return 0;
+}
+
+QString CppCodeMarker::addMarkUp(const QString& protectedCode,
+ const Node * /* relative */,
+ const QString& /* dirPath */)
+{
+ static QRegExp globalInclude("#include +&lt;([^<>&]+)&gt;");
+ static QRegExp yHasTypeX("(?:^|\n *)([a-zA-Z_][a-zA-Z_0-9]*)"
+ "(?:&lt;[^;{}]+&gt;)?(?: *(?:\\*|&amp;) *| +)"
+ "([a-zA-Z_][a-zA-Z_0-9]*)? *[,;()=]");
+ static QRegExp xNewY("([a-zA-Z_][a-zA-Z_0-9]*) *= *new +([a-zA-Z_0-9]+)");
+ static QRegExp xDotY("\\b([a-zA-Z_][a-zA-Z_0-9]*) *(?:\\.|-&gt;|,[ \n]*S(?:IGNAL|LOT)\\() *"
+ "([a-zA-Z_][a-zA-Z_0-9]*)(?= *\\()");
+ static QRegExp xIsStaticZOfY("[\n:;{(=] *(([a-zA-Z_0-9]+)::([a-zA-Z_0-9]+))(?= *\\()");
+ static QRegExp classX("[:,][ \n]*(?:p(?:ublic|r(?:otected|ivate))[ \n]+)?"
+ "([a-zA-Z_][a-zA-Z_0-9]*)");
+ static QRegExp globalX("[\n{()=] *([a-zA-Z_][a-zA-Z_0-9]*)[ \n]*\\(");
+ static QRegExp multiLineComment("/(?:( )?\\*(?:[^*]+|\\*(?! /))*\\*\\1/)");
+ multiLineComment.setMinimal(true);
+ static QRegExp singleLineComment("//(?!!)[^!\n]*");
+ static QRegExp preprocessor("(?:^|\n)(#[ \t]*(?:include|if|elif|endif|error|pragma|define"
+ "|warning)(?:(?:\\\\\n|\\n#)[^\n]*)*)");
+ static QRegExp literals("&quot;(?:[^\\\\&]|\\\\[^\n]|&(?!quot;))*&quot;"
+ "|'(?:[^\\\\]|\\\\(?:[^x0-9']|x[0-9a-f]{1,4}|[0-9]{1,3}))'");
+
+ QString result = protectedCode;
+ int pos;
+
+ if (!hurryUp()) {
+ /*
+ Mark global includes. For example:
+
+ #include &lt;<@headerfile>QString</@headerfile>
+ */
+ pos = 0;
+ while ((pos = result.indexOf(globalInclude, pos)) != -1)
+ pos += globalInclude.matchedLength()
+ + insertTagAround(result,
+ globalInclude.pos(1),
+ globalInclude.cap(1).length(),
+ "@headerfile");
+
+ /*
+ Look for variable definitions and similar constructs, mark
+ the data type, and remember the type of the variable.
+ */
+ QMap<QString, QSet<QString> > typesForVariable;
+ pos = 0;
+ while ((pos = yHasTypeX.indexIn(result, pos)) != -1) {
+ QString x = yHasTypeX.cap(1);
+ QString y = yHasTypeX.cap(2);
+
+ if (!y.isEmpty())
+ typesForVariable[y].insert(x);
+
+ /*
+ Without the minus one at the end, 'void member(Class
+ var)' would give 'member' as a variable of type 'void',
+ but would ignore 'Class var'. (### Is that true?)
+ */
+ pos += yHasTypeX.matchedLength()
+ + insertTagAround(result,
+ yHasTypeX.pos(1),
+ x.length(),
+ "@type") - 1;
+ }
+
+ /*
+ Do syntax highlighting of preprocessor directives.
+ */
+ pos = 0;
+ while ((pos = preprocessor.indexIn(result, pos)) != -1)
+ pos += preprocessor.matchedLength()
+ + insertTagAround(result,
+ preprocessor.pos(1),
+ preprocessor.cap(1).length(),
+ "@preprocessor");
+
+ /*
+ Deal with string and character literals.
+ */
+ pos = 0;
+ while ((pos = literals.indexIn(result, pos)) != -1)
+ pos += literals.matchedLength()
+ + insertTagAround(result,
+ pos,
+ literals.matchedLength(),
+ result.at(pos) ==
+ QLatin1Char(' ') ? "@string" : "@char");
+
+ /*
+ Look for 'var = new Class'.
+ */
+ pos = 0;
+ while ((pos = xNewY.indexIn(result, pos)) != -1) {
+ QString x = xNewY.cap(1);
+ QString y = xNewY.cap(2);
+ typesForVariable[x].insert(y);
+
+ pos += xNewY.matchedLength() + insertTagAround(result,
+ xNewY.pos(2),
+ y.length(),
+ "@type");
+ }
+
+ /*
+ Insert some stuff that cannot harm.
+ */
+ typesForVariable["qApp"].insert("QApplication");
+
+ /*
+ Add link to ': Class'.
+ */
+ pos = 0;
+ while ((pos = classX.indexIn(result, pos)) != -1)
+ pos += classX.matchedLength()
+ + insertTagAround(result,
+ classX.pos(1),
+ classX.cap(1).length(),
+ "@type") - 1;
+
+ /*
+ Find use of any of
+
+ var.method()
+ var->method()
+ var, SIGNAL(method())
+ var, SLOT(method()).
+ */
+ pos = 0;
+ while ((pos = xDotY.indexIn(result, pos)) != -1) {
+ QString x = xDotY.cap(1);
+ QString y = xDotY.cap(2);
+
+ QSet<QString> types = typesForVariable.value(x);
+ pos += xDotY.matchedLength()
+ + insertTagAround(result,
+ xDotY.pos(2),
+ xDotY.cap(2).length(),
+ "@func",
+ (types.count() == 1) ? "target=\""
+ + protect(*types.begin() + "::" + y)
+ + "()\"" : QString());
+ }
+
+ /*
+ Add link to 'Class::method()'.
+ */
+ pos = 0;
+ while ((pos = xIsStaticZOfY.indexIn(result, pos)) != -1) {
+ QString x = xIsStaticZOfY.cap(1);
+ QString z = xIsStaticZOfY.cap(3);
+
+ pos += insertTagAround(result,
+ xIsStaticZOfY.pos(3),
+ z.length(),
+ "@func",
+ "target=\"" + protect(x) + "()\"");
+ pos += insertTagAround(result,
+ xIsStaticZOfY.pos(2),
+ xIsStaticZOfY.cap(2).length(),
+ "@type");
+ pos += xIsStaticZOfY.matchedLength() - 1;
+ }
+
+ /*
+ Add link to 'globalFunction()'.
+ */
+ pos = 0;
+ while ((pos = globalX.indexIn(result, pos)) != -1) {
+ QString x = globalX.cap(1);
+ if (x != "QT_FORWARD_DECLARE_CLASS") {
+ pos += globalX.matchedLength()
+ + insertTagAround(result,
+ globalX.pos(1),
+ x.length(),
+ "@func",
+ "target=\"" + protect(x) + "()\"") - 1;
+ }
+ else
+ pos += globalX.matchedLength();
+ }
+ }
+
+ /*
+ Do syntax highlighting of comments. Also alter the code in a
+ minor way, so that we can include comments in documentation
+ comments.
+ */
+ pos = 0;
+ while (pos != -1) {
+ int mlpos;
+ int slpos;
+ int len;
+ slpos = singleLineComment.indexIn(result, pos);
+ mlpos = multiLineComment.indexIn(result, pos);
+
+ if (slpos == -1 && mlpos == -1)
+ break;
+
+ if (slpos == -1) {
+ pos = mlpos;
+ len = multiLineComment.matchedLength();
+ }
+ else if (mlpos == -1) {
+ pos = slpos;
+ len = singleLineComment.matchedLength();
+ }
+ else {
+ if (slpos < mlpos) {
+ pos = slpos;
+ len = singleLineComment.matchedLength();
+ }
+ else {
+ pos = mlpos;
+ len = multiLineComment.matchedLength();
+ }
+ }
+
+ if (result.at(pos + 1) == QLatin1Char(' ')) {
+ result.remove(pos + len - 2, 1);
+ result.remove(pos + 1, 1);
+ len -= 2;
+
+ forever {
+ int endcodePos = result.indexOf("\\ endcode", pos);
+ if (endcodePos == -1 || endcodePos >= pos + len)
+ break;
+ result.remove(endcodePos + 1, 1);
+ len -= 1;
+ }
+ }
+ pos += len + insertTagAround(result, pos, len, "@comment");
+ }
+
+ return result;
+}
+
+QT_END_NAMESPACE
diff --git a/tools/qdoc3/cppcodemarker.h b/tools/qdoc3/cppcodemarker.h
new file mode 100644
index 0000000..8b68bc0
--- /dev/null
+++ b/tools/qdoc3/cppcodemarker.h
@@ -0,0 +1,91 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*
+ cppcodemarker.h
+*/
+
+#ifndef CPPCODEMARKER_H
+#define CPPCODEMARKER_H
+
+#include "codemarker.h"
+
+QT_BEGIN_NAMESPACE
+
+class CppCodeMarker : public CodeMarker
+{
+ public:
+ CppCodeMarker();
+ ~CppCodeMarker();
+
+ bool recognizeCode(const QString& code);
+ bool recognizeExtension(const QString& ext);
+ bool recognizeLanguage(const QString& lang);
+ QString plainName(const Node *node);
+ QString plainFullName(const Node *node, const Node *relative);
+ QString markedUpCode(const QString& code,
+ const Node *relative,
+ const QString& dirPath);
+ QString markedUpSynopsis(const Node *node,
+ const Node *relative,
+ SynopsisStyle style);
+ QString markedUpName(const Node *node);
+ QString markedUpFullName(const Node *node, const Node *relative);
+ QString markedUpEnumValue(const QString &enumValue, const Node *relative);
+ QString markedUpIncludes(const QStringList& includes);
+ QString functionBeginRegExp(const QString& funcName);
+ QString functionEndRegExp(const QString& funcName);
+ QList<Section> sections(const InnerNode *innerNode,
+ SynopsisStyle style,
+ Status status);
+ const Node *resolveTarget(const QString& target,
+ const Tree *tree,
+ const Node *relative);
+
+private:
+ QString addMarkUp(const QString& protectedCode,
+ const Node *relative,
+ const QString& dirPath);
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/tools/qdoc3/cppcodeparser.cpp b/tools/qdoc3/cppcodeparser.cpp
new file mode 100644
index 0000000..1ad5843
--- /dev/null
+++ b/tools/qdoc3/cppcodeparser.cpp
@@ -0,0 +1,2014 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*
+ cppcodeparser.cpp
+*/
+
+#include <QtCore>
+#include <qfile.h>
+
+#include <stdio.h>
+
+#include "codechunk.h"
+#include "config.h"
+#include "cppcodeparser.h"
+#include "tokenizer.h"
+#include "tree.h"
+
+QT_BEGIN_NAMESPACE
+
+/* qmake ignore Q_OBJECT */
+
+#define COMMAND_CLASS Doc::alias("class")
+#define COMMAND_CONTENTSPAGE Doc::alias("contentspage")
+#define COMMAND_ENUM Doc::alias("enum")
+#define COMMAND_EXAMPLE Doc::alias("example")
+#define COMMAND_EXTERNALPAGE Doc::alias("externalpage")
+#define COMMAND_FILE Doc::alias("file") // ### don't document
+#define COMMAND_FN Doc::alias("fn")
+#define COMMAND_GROUP Doc::alias("group")
+#define COMMAND_HEADERFILE Doc::alias("headerfile")
+#define COMMAND_INDEXPAGE Doc::alias("indexpage")
+#define COMMAND_INHEADERFILE Doc::alias("inheaderfile") // ### don't document
+#define COMMAND_MACRO Doc::alias("macro")
+#define COMMAND_MODULE Doc::alias("module") // ### don't document
+#define COMMAND_NAMESPACE Doc::alias("namespace")
+#define COMMAND_OVERLOAD Doc::alias("overload")
+#define COMMAND_NEXTPAGE Doc::alias("nextpage")
+#define COMMAND_PAGE Doc::alias("page")
+#define COMMAND_PREVIOUSPAGE Doc::alias("previouspage")
+#define COMMAND_PROPERTY Doc::alias("property")
+#define COMMAND_REIMP Doc::alias("reimp")
+#define COMMAND_RELATES Doc::alias("relates")
+#define COMMAND_SERVICE Doc::alias("service")
+#define COMMAND_STARTPAGE Doc::alias("startpage")
+#define COMMAND_TYPEDEF Doc::alias("typedef")
+#define COMMAND_VARIABLE Doc::alias("variable")
+
+#ifdef QDOC_QML
+#define COMMAND_QMLCLASS Doc::alias("qmlclass")
+#define COMMAND_QMLPROPERTY Doc::alias("qmlproperty")
+#endif
+
+QStringList CppCodeParser::exampleFiles;
+QStringList CppCodeParser::exampleDirs;
+
+static void extractPageLinkAndDesc(const QString &arg,
+ QString *link,
+ QString *desc)
+{
+ QRegExp bracedRegExp("\\{([^{}]*)\\}(?:\\{([^{}]*)\\})?");
+
+ if (bracedRegExp.exactMatch(arg)) {
+ *link = bracedRegExp.cap(1);
+ *desc = bracedRegExp.cap(2);
+ if (desc->isEmpty())
+ *desc = *link;
+ }
+ else {
+ int spaceAt = arg.indexOf(" ");
+ if (arg.contains(".html") && spaceAt != -1) {
+ *link = arg.left(spaceAt).trimmed();
+ *desc = arg.mid(spaceAt).trimmed();
+ } else {
+ *link = arg;
+ *desc = arg;
+ }
+ }
+}
+
+static void setLink(Node *node, Node::LinkType linkType, const QString &arg)
+{
+ QString link;
+ QString desc;
+ extractPageLinkAndDesc(arg, &link, &desc);
+ node->setLink(linkType, link, desc);
+}
+
+/*
+ This is used for fuzzy matching only, which in turn is only used
+ for Qt Jambi.
+*/
+static QString cleanType(const QString &type, const Tree *tree)
+{
+ QString result = type;
+ result.replace("qlonglong", "long long");
+ result.replace("qulonglong", "unsigned long long");
+ result.replace("qreal", "double");
+ result.replace(QRegExp("\\bu(int|short|char|long)\\b"), "unsigned \\1");
+ result.replace("QRgb", "unsigned int");
+ result.replace(" >", ">");
+ result.remove(" const[]");
+ result.replace("QStringList<QString>", "QStringList");
+ result.replace("qint8", "char");
+ result.replace("qint16", "short");
+ result.replace("qint32", "int");
+ result.replace("qint64", "long long");
+ result.replace("quint8", "unsigned char");
+ result.replace("quint16", "unsigned short");
+ result.replace("quint32", "unsigned int");
+ result.replace("quint64", "unsigned long long");
+
+ if (result.contains("QFlags")) {
+ QRegExp regExp("QFlags<(((?:[^<>]+::)*)([^<>:]+))>");
+ int pos = 0;
+ while ((pos = result.indexOf(regExp, pos)) != -1) {
+ // we assume that the path for the associated enum
+ // is the same as for the flag typedef
+ QStringList path = regExp.cap(2).split("::",
+ QString::SkipEmptyParts);
+ const EnumNode *enume = static_cast<const EnumNode *>(
+ tree->findNode(QStringList(path) << regExp.cap(3),
+ Node::Enum));
+ if (enume && enume->flagsType())
+ result.replace(pos, regExp.matchedLength(),
+ (QStringList(path) << enume->flagsType()->name()).join("::"));
+ ++pos;
+ }
+ }
+ if (result.contains("::")) {
+ // remove needless (and needful) class prefixes
+ QRegExp regExp("[A-Za-z0-9_]+::");
+ result.replace(regExp, "");
+ }
+ return result;
+}
+
+/*!
+ The constructor initializes some regular expressions
+ and calls reset().
+ */
+CppCodeParser::CppCodeParser()
+ : varComment("/\\*\\s*([a-zA-Z_0-9]+)\\s*\\*/"), sep("(?:<[^>]+>)?::")
+{
+ reset(0);
+}
+
+/*!
+ The destructor is trivial.
+ */
+CppCodeParser::~CppCodeParser()
+{
+}
+
+void CppCodeParser::initializeParser(const Config &config)
+{
+ CodeParser::initializeParser(config);
+
+ nodeTypeMap.insert(COMMAND_NAMESPACE, Node::Namespace);
+ nodeTypeMap.insert(COMMAND_CLASS, Node::Class);
+ nodeTypeMap.insert(COMMAND_SERVICE, Node::Class);
+ nodeTypeMap.insert(COMMAND_ENUM, Node::Enum);
+ nodeTypeMap.insert(COMMAND_TYPEDEF, Node::Typedef);
+ nodeTypeMap.insert(COMMAND_PROPERTY, Node::Property);
+ nodeTypeMap.insert(COMMAND_VARIABLE, Node::Variable);
+
+#ifdef QDOC_QML
+ // nodeTypeMap.insert(COMMAND_QMLCLASS, Node::Class);
+ nodeTypeMap.insert(COMMAND_QMLPROPERTY, Node::Property);
+#endif
+
+ exampleFiles = config.getStringList(CONFIG_EXAMPLES);
+ exampleDirs = config.getStringList(CONFIG_EXAMPLEDIRS);
+ QStringList exampleFilePatterns = config.getStringList(
+ CONFIG_EXAMPLES + Config::dot + CONFIG_FILEEXTENSIONS);
+
+ if (!exampleFilePatterns.isEmpty())
+ exampleNameFilter = exampleFilePatterns.join(" ");
+ else
+ exampleNameFilter = "*.cpp *.h *.js *.xq *.svg *.xml *.ui";
+}
+
+void CppCodeParser::terminateParser()
+{
+ nodeTypeMap.clear();
+ CodeParser::terminateParser();
+}
+
+QString CppCodeParser::language()
+{
+ return "Cpp";
+}
+
+QString CppCodeParser::headerFileNameFilter()
+{
+ return "*.ch *.h *.h++ *.hh *.hpp *.hxx";
+}
+
+QString CppCodeParser::sourceFileNameFilter()
+{
+ return "*.c++ *.cc *.cpp *.cxx";
+}
+
+/*!
+ Parse the C++ header file identified by \a filePath
+ and add the parsed contents to the big \a tree. The
+ \a location is used for reporting errors.
+ */
+void CppCodeParser::parseHeaderFile(const Location& location,
+ const QString& filePath,
+ Tree *tree)
+{
+ FILE *in = fopen(QFile::encodeName(filePath), "r");
+ if (!in) {
+ location.error(tr("Cannot open C++ header file '%1'").arg(filePath));
+ return;
+ }
+
+ reset(tree);
+ Location fileLocation(filePath);
+ Tokenizer fileTokenizer(fileLocation, in);
+ tokenizer = &fileTokenizer;
+ readToken();
+ matchDeclList(tree->root());
+ if (!fileTokenizer.version().isEmpty())
+ tree->setVersion(fileTokenizer.version());
+ fclose(in);
+
+ if (fileLocation.fileName() == "qiterator.h")
+ parseQiteratorDotH(location, filePath);
+}
+
+/*!
+ Get ready to parse the C++ cpp file identified by \a filePath
+ and add its parsed contents to the big \a tree. \a location is
+ used for reporting errors.
+
+ Call matchDocsAndStuff() to do all the parsing and tree building.
+ */
+void CppCodeParser::parseSourceFile(const Location& location,
+ const QString& filePath,
+ Tree *tree)
+{
+ FILE *in = fopen(QFile::encodeName(filePath), "r");
+ if (!in) {
+ location.error(tr("Cannot open C++ source file '%1'").arg(filePath));
+ return;
+ }
+
+ reset(tree);
+ Location fileLocation(filePath);
+ Tokenizer fileTokenizer(fileLocation, in);
+ tokenizer = &fileTokenizer;
+ readToken();
+ usedNamespaces.clear();
+ matchDocsAndStuff();
+ fclose(in);
+}
+
+void CppCodeParser::doneParsingHeaderFiles(Tree *tree)
+{
+ tree->resolveInheritance();
+
+ QMapIterator<QString, QString> i(sequentialIteratorClasses);
+ while (i.hasNext()) {
+ i.next();
+ instantiateIteratorMacro(i.key(),
+ i.value(),
+ sequentialIteratorDefinition,
+ tree);
+ }
+ i = mutableSequentialIteratorClasses;
+ while (i.hasNext()) {
+ i.next();
+ instantiateIteratorMacro(i.key(),
+ i.value(),
+ mutableSequentialIteratorDefinition,
+ tree);
+ }
+ i = associativeIteratorClasses;
+ while (i.hasNext()) {
+ i.next();
+ instantiateIteratorMacro(i.key(),
+ i.value(),
+ associativeIteratorDefinition,
+ tree);
+ }
+ i = mutableAssociativeIteratorClasses;
+ while (i.hasNext()) {
+ i.next();
+ instantiateIteratorMacro(i.key(),
+ i.value(),
+ mutableAssociativeIteratorDefinition,
+ tree);
+ }
+ sequentialIteratorDefinition.clear();
+ mutableSequentialIteratorDefinition.clear();
+ associativeIteratorDefinition.clear();
+ mutableAssociativeIteratorDefinition.clear();
+ sequentialIteratorClasses.clear();
+ mutableSequentialIteratorClasses.clear();
+ associativeIteratorClasses.clear();
+ mutableAssociativeIteratorClasses.clear();
+}
+
+void CppCodeParser::doneParsingSourceFiles(Tree *tree)
+{
+ tree->root()->makeUndocumentedChildrenInternal();
+ tree->root()->normalizeOverloads();
+ tree->fixInheritance();
+ tree->resolveProperties();
+}
+
+const FunctionNode *CppCodeParser::findFunctionNode(const QString& synopsis,
+ Tree *tree,
+ Node *relative,
+ bool fuzzy)
+{
+ QStringList parentPath;
+ FunctionNode *clone;
+ FunctionNode *func = 0;
+ int flags = fuzzy ? int(Tree::SearchBaseClasses) : 0;
+
+ reset(tree);
+ if (makeFunctionNode(synopsis, &parentPath, &clone)) {
+ func = tree->findFunctionNode(parentPath, clone, relative, flags);
+
+ /*
+ This is necessary because Roberto's parser resolves typedefs.
+ */
+ if (!func && fuzzy) {
+ func = tre->findFunctionNode(parentPath +
+ QStringList(clone->name()),
+ relative,
+ flags);
+ if (!func && clone->name().contains('_')) {
+ QStringList path = parentPath;
+ path << clone->name().split('_');
+ func = tre->findFunctionNode(path, relative, flags);
+ }
+
+ if (func) {
+ NodeList overloads = func->parent()->overloads(func->name());
+ NodeList candidates;
+ for (int i = 0; i < overloads.count(); ++i) {
+ FunctionNode *overload = static_cast<FunctionNode *>(overloads.at(i));
+ if (overload->status() != Node::Compat
+ && overload->parameters().count() == clone->parameters().count()
+ && !overload->isConst() == !clone->isConst())
+ candidates << overload;
+ }
+ if (candidates.count() == 0)
+ return 0;
+
+ /*
+ There's only one function with the correct number
+ of parameters. That must be the one.
+ */
+ if (candidates.count() == 1)
+ return static_cast<FunctionNode *>(candidates.first());
+
+ overloads = candidates;
+ candidates.clear();
+ for (int i = 0; i < overloads.count(); ++i) {
+ FunctionNode *overload = static_cast<FunctionNode *>(overloads.at(i));
+ QList<Parameter> params1 = overload->parameters();
+ QList<Parameter> params2 = clone->parameters();
+
+ int j;
+ for (j = 0; j < params1.count(); ++j) {
+ if (!params2.at(j).name().startsWith(params1.at(j).name()))
+ break;
+ }
+ if (j == params1.count())
+ candidates << overload;
+ }
+
+ /*
+ There are several functions with the correct
+ parameter count, but only one has the correct
+ parameter names.
+ */
+ if (candidates.count() == 1)
+ return static_cast<FunctionNode *>(candidates.first());
+
+ candidates.clear();
+ for (int i = 0; i < overloads.count(); ++i) {
+ FunctionNode *overload = static_cast<FunctionNode *>(overloads.at(i));
+ QList<Parameter> params1 = overload->parameters();
+ QList<Parameter> params2 = clone->parameters();
+
+ int j;
+ for (j = 0; j < params1.count(); ++j) {
+ if (params1.at(j).rightType() != params2.at(j).rightType())
+ break;
+
+ if (cleanType(params1.at(j).leftType(), tree)
+ != cleanType(params2.at(j).leftType(), tree))
+ break;
+ }
+ if (j == params1.count())
+ candidates << overload;
+ }
+
+
+ /*
+ There are several functions with the correct
+ parameter count, but only one has the correct
+ types, loosely compared.
+ */
+ if (candidates.count() == 1)
+ return static_cast<FunctionNode *>(candidates.first());
+
+ return 0;
+ }
+ }
+ delete clone;
+ }
+ return func;
+}
+
+/*!
+ Returns the set of strings reopresenting the topic commands.
+ */
+QSet<QString> CppCodeParser::topicCommands()
+{
+ return QSet<QString>() << COMMAND_CLASS
+ << COMMAND_ENUM
+ << COMMAND_EXAMPLE
+ << COMMAND_EXTERNALPAGE
+ << COMMAND_FILE
+ << COMMAND_FN
+ << COMMAND_GROUP
+ << COMMAND_HEADERFILE
+ << COMMAND_MACRO
+ << COMMAND_MODULE
+ << COMMAND_NAMESPACE
+ << COMMAND_PAGE
+ << COMMAND_PROPERTY
+ << COMMAND_SERVICE
+ << COMMAND_TYPEDEF
+#ifdef QDOC_QML
+ << COMMAND_VARIABLE
+ << COMMAND_QMLCLASS
+ << COMMAND_QMLPROPERTY;
+#else
+ << COMMAND_VARIABLE;
+#endif
+}
+
+/*!
+ Process the topic \a command in context \a doc with argument \a arg.
+ */
+Node *CppCodeParser::processTopicCommand(const Doc& doc,
+ const QString& command,
+ const QString& arg)
+{
+ if (command == COMMAND_FN) {
+ QStringList parentPath;
+ FunctionNode *func = 0;
+ FunctionNode *clone = 0;
+
+ if (!makeFunctionNode(arg, &parentPath, &clone) &&
+ !makeFunctionNode("void " + arg, &parentPath, &clone)) {
+ doc.location().warning(tr("Invalid syntax in '\\%1'")
+ .arg(COMMAND_FN));
+ }
+ else {
+ if (!usedNamespaces.isEmpty()) {
+ foreach (const QString &usedNamespace, usedNamespaces) {
+ QStringList newPath = usedNamespace.split("::") + parentPath;
+ func = tre->findFunctionNode(newPath, clone);
+ if (func)
+ break;
+ }
+ }
+ // Search the root namespace if no match was found.
+ if (func == 0)
+ func = tre->findFunctionNode(parentPath, clone);
+
+ if (func == 0) {
+ if (parentPath.isEmpty() && !lastPath.isEmpty())
+ func = tre->findFunctionNode(lastPath, clone);
+ if (func == 0) {
+ doc.location().warning(tr("Cannot find '%1' in '\\%2'")
+ .arg(clone->name() + "(...)")
+ .arg(COMMAND_FN),
+ tr("I cannot find any function of that name with the "
+ "specified signature. Make sure that the signature "
+ "is identical to the declaration, including 'const' "
+ "qualifiers."));
+ }
+ else {
+ doc.location().warning(tr("Missing '%1::' for '%2' in '\\%3'")
+ .arg(lastPath.join("::"))
+ .arg(clone->name() + "()")
+ .arg(COMMAND_FN));
+ }
+ }
+ else {
+ lastPath = parentPath;
+ }
+
+ if (func)
+ func->borrowParameterNames(clone);
+ delete clone;
+ }
+ return func;
+ }
+ else if (command == COMMAND_MACRO) {
+ QStringList parentPath;
+ FunctionNode *func = 0;
+
+ if (makeFunctionNode(arg, &parentPath, &func, tre->root())) {
+ if (!parentPath.isEmpty()) {
+ doc.location().warning(tr("Invalid syntax in '\\%1'")
+ .arg(COMMAND_MACRO));
+ delete func;
+ func = 0;
+ }
+ else {
+ func->setMetaness(FunctionNode::MacroWithParams);
+ QList<Parameter> params = func->parameters();
+ for (int i = 0; i < params.size(); ++i) {
+ Parameter &param = params[i];
+ if (param.name().isEmpty() && !param.leftType().isEmpty()
+ && param.leftType() != "...")
+ param = Parameter("", "", param.leftType());
+ }
+ func->setParameters(params);
+ }
+ return func;
+ }
+ else if (QRegExp("[A-Za-z_][A-Za-z0-9_]+").exactMatch(arg)) {
+ func = new FunctionNode(tre->root(), arg);
+ func->setAccess(Node::Public);
+ func->setLocation(doc.location());
+ func->setMetaness(FunctionNode::MacroWithoutParams);
+ }
+ else {
+ doc.location().warning(tr("Invalid syntax in '\\%1'")
+ .arg(COMMAND_MACRO));
+
+ }
+ return func;
+ }
+ else if (nodeTypeMap.contains(command)) {
+ /*
+ The command was neither "fn" nor "macro" .
+ */
+ // ### split(" ") hack is there to support header file syntax
+ QStringList paths = arg.split(" ");
+ QStringList path = paths[0].split("::");
+
+#if QDOC2DOX
+ // qdoc -> doxygen.
+ if (Doc::isDoxPass(1)) {
+ if (command == COMMAND_PROPERTY) {
+ Doc::insertProperty(path);
+ }
+ else if (command == COMMAND_VARIABLE) {
+ Doc::insertVariable(path);
+ }
+ else if (command == COMMAND_ENUM) {
+ // zzz
+ }
+ }
+#endif
+
+ Node *node = 0;
+ if (!usedNamespaces.isEmpty()) {
+ foreach (const QString &usedNamespace, usedNamespaces) {
+ QStringList newPath = usedNamespace.split("::") + path;
+ node = tre->findNode(newPath, nodeTypeMap[command]);
+ if (node) {
+ path = newPath;
+ break;
+ }
+ }
+ }
+ // Search the root namespace if no match was found.
+ if (node == 0)
+ node = tre->findNode(path, nodeTypeMap[command]);
+
+ if (node == 0) {
+ doc.location().warning(tr("Cannot find '%1' specified with '\\%2' in any header file")
+ .arg(arg).arg(command));
+ lastPath = path;
+
+ }
+ else if (command == COMMAND_SERVICE) {
+ // If the command is "\service", then we need to tag the
+ // class with the actual service name.
+ QStringList args = arg.split(" ");
+ if (args.size() > 1) {
+ ClassNode *cnode = static_cast<ClassNode *>(node);
+ cnode->setServiceName(args[1]);
+ cnode->setHideFromMainList(true);
+ }
+ }
+ else if (node->isInnerNode()) {
+ if (path.size() > 1) {
+ path.pop_back();
+ usedNamespaces.insert(path.join("::"));
+ }
+ }
+
+ return node;
+ }
+ else if (command == COMMAND_EXAMPLE) {
+ FakeNode *fake = new FakeNode(tre->root(), arg, FakeNode::Example);
+ createExampleFileNodes(fake);
+ return fake;
+ }
+ else if (command == COMMAND_EXTERNALPAGE) {
+ return new FakeNode(tre->root(), arg, FakeNode::ExternalPage);
+ }
+ else if (command == COMMAND_FILE) {
+ return new FakeNode(tre->root(), arg, FakeNode::File);
+ }
+ else if (command == COMMAND_GROUP) {
+ return new FakeNode(tre->root(), arg, FakeNode::Group);
+ }
+ else if (command == COMMAND_HEADERFILE) {
+ return new FakeNode(tre->root(), arg, FakeNode::HeaderFile);
+ }
+ else if (command == COMMAND_MODULE) {
+ return new FakeNode(tre->root(), arg, FakeNode::Module);
+ }
+ else if (command == COMMAND_PAGE) {
+ return new FakeNode(tre->root(), arg, FakeNode::Page);
+ }
+#ifdef QDOC_QML
+ else if (command == COMMAND_QMLCLASS) {
+ const ClassNode* classNode = 0;
+ QStringList names = arg.split(" ");
+ //qDebug() << "QMLCLASS" << names;
+ if (names.size() > 1) {
+ Node* n = tre->findNode(names[1].split("::"),Node::Class);
+ if (n) {
+ classNode = static_cast<const ClassNode*>(n);
+ //qDebug() << "FOUND IT!" << classNode->name();
+ }
+ }
+ return new QmlNode(tre->root(), names[0], classNode);
+ }
+#endif
+ return 0;
+}
+
+/*!
+ Returns the set of strings representing the common metacommands
+ plus some other metacommands.
+ */
+QSet<QString> CppCodeParser::otherMetaCommands()
+{
+ return commonMetaCommands() << COMMAND_INHEADERFILE
+ << COMMAND_OVERLOAD
+ << COMMAND_REIMP
+ << COMMAND_RELATES
+ << COMMAND_CONTENTSPAGE
+ << COMMAND_NEXTPAGE
+ << COMMAND_PREVIOUSPAGE
+ << COMMAND_INDEXPAGE
+ << COMMAND_STARTPAGE;
+}
+
+/*!
+ Process the metacommand \a command in the context of the
+ \a node associated with the topic command and the \a doc.
+ \a arg is the argument to the metacommand.
+ */
+void CppCodeParser::processOtherMetaCommand(const Doc& doc,
+ const QString& command,
+ const QString& arg,
+ Node *node)
+{
+ if (command == COMMAND_INHEADERFILE) {
+ if (node != 0 && node->isInnerNode()) {
+ ((InnerNode *) node)->addInclude(arg);
+ }
+ else {
+ doc.location().warning(tr("Ignored '\\%1'")
+ .arg(COMMAND_INHEADERFILE));
+ }
+ }
+ else if (command == COMMAND_OVERLOAD) {
+ if (node != 0 && node->type() == Node::Function) {
+ ((FunctionNode *) node)->setOverload(true);
+ }
+ else {
+ doc.location().warning(tr("Ignored '\\%1'")
+ .arg(COMMAND_OVERLOAD));
+ }
+ }
+ else if (command == COMMAND_REIMP) {
+ if (node != 0 && node->type() == Node::Function) {
+ FunctionNode *func = (FunctionNode *) node;
+ const FunctionNode *from = func->reimplementedFrom();
+ if (from == 0) {
+ doc.location().warning(
+ tr("Cannot find base function for '\\%1' in %2()")
+ .arg(COMMAND_REIMP).arg(node->name()),
+ tr("The function either doesn't exist in any base class "
+ "with the same signature or it exists but isn't virtual."));
+ }
+#if 0 // Ideally, we would enable this check to warn whenever \reimp is used
+ // incorrectly, and only make the node internal if the function is a
+ // reimplementation of another function in a base class.
+ else if (from->access() == Node::Private
+ || from->parent()->access() == Node::Private) {
+ doc.location().warning(
+ tr("Base function for '\\%1' in %2() is private or internal")
+ .arg(COMMAND_REIMP).arg(node->name()));
+ }
+#endif
+ // Note: Setting the access to Private hides the documentation,
+ // but setting the status to Internal makes the node available
+ // in the XML output when the WebXMLGenerator is used.
+ func->setAccess(Node::Private);
+ func->setStatus(Node::Internal);
+ }
+ else {
+ doc.location().warning(tr("Ignored '\\%1' in %2")
+ .arg(COMMAND_REIMP)
+ .arg(node->name()));
+ }
+ }
+ else if (command == COMMAND_RELATES) {
+ InnerNode *pseudoParent;
+ if (arg.startsWith("<") || arg.startsWith("\"")) {
+ pseudoParent = static_cast<InnerNode *>(tre->findNode(QStringList(arg), Node::Fake));
+ }
+ else {
+ QStringList newPath = arg.split("::");
+ pseudoParent = static_cast<InnerNode *>(tre->findNode(QStringList(newPath), Node::Class));
+ if (!pseudoParent)
+ pseudoParent = static_cast<InnerNode *>(tre->findNode(QStringList(newPath),
+ Node::Namespace));
+ }
+ if (!pseudoParent) {
+ doc.location().warning(tr("Cannot find '%1' in '\\%2'")
+ .arg(arg).arg(COMMAND_RELATES));
+ }
+ else {
+ node->setRelates(pseudoParent);
+ }
+ }
+ else if (command == COMMAND_CONTENTSPAGE) {
+ setLink(node, Node::ContentsLink, arg);
+ }
+ else if (command == COMMAND_NEXTPAGE) {
+ setLink(node, Node::NextLink, arg);
+ }
+ else if (command == COMMAND_PREVIOUSPAGE) {
+ setLink(node, Node::PreviousLink, arg);
+ }
+ else if (command == COMMAND_INDEXPAGE) {
+ setLink(node, Node::IndexLink, arg);
+ }
+ else if (command == COMMAND_STARTPAGE) {
+ setLink(node, Node::StartLink, arg);
+ }
+ else {
+ processCommonMetaCommand(doc.location(),command,arg,node,tre);
+ }
+}
+
+/*!
+ The topic command has been processed resulting in the \a doc
+ and \a node passed in here. Process the other meta commands,
+ which are found in \a doc, in the context of the topic \a node.
+ */
+void CppCodeParser::processOtherMetaCommands(const Doc& doc, Node *node)
+{
+ const QSet<QString> metaCommands = doc.metaCommandsUsed();
+ QSet<QString>::ConstIterator cmd = metaCommands.begin();
+ while (cmd != metaCommands.end()) {
+ QStringList args = doc.metaCommandArgs(*cmd);
+ QStringList::ConstIterator arg = args.begin();
+ while (arg != args.end()) {
+ processOtherMetaCommand(doc, *cmd, *arg, node);
+ ++arg;
+ }
+ ++cmd;
+ }
+}
+
+/*!
+ Resets the C++ code parser to its default initialized state.
+ */
+void CppCodeParser::reset(Tree *tree)
+{
+ tre = tree;
+ tokenizer = 0;
+ tok = 0;
+ access = Node::Public;
+ metaness = FunctionNode::Plain;
+ lastPath.clear();
+ moduleName = "";
+}
+
+/*!
+ Get the next token from the file being parsed and store it
+ in the token variable.
+ */
+void CppCodeParser::readToken()
+{
+ tok = tokenizer->getToken();
+}
+
+/*!
+ Return the current location in the file being parsed,
+ i.e. the file name, line number, and column number.
+ */
+const Location& CppCodeParser::location()
+{
+ return tokenizer->location();
+}
+
+/*!
+ Return the previous string read from the file being parsed.
+ */
+QString CppCodeParser::previousLexeme()
+{
+ return tokenizer->previousLexeme();
+}
+
+/*!
+ Return the current string string from the file being parsed.
+ */
+QString CppCodeParser::lexeme()
+{
+ return tokenizer->lexeme();
+}
+
+bool CppCodeParser::match(int target)
+{
+ if (tok == target) {
+ readToken();
+ return true;
+ }
+ else {
+ return false;
+ }
+}
+
+/*!
+ If the current token is one of the keyword thingees that
+ are used in Qt, skip over it to the next token and return
+ true. Otherwise just return false without reading the
+ next token.
+ */
+bool CppCodeParser::matchCompat()
+{
+ switch (tok) {
+ case Tok_QT_COMPAT:
+ case Tok_QT_COMPAT_CONSTRUCTOR:
+ case Tok_QT_DEPRECATED:
+ case Tok_QT_MOC_COMPAT:
+ case Tok_QT3_SUPPORT:
+ case Tok_QT3_SUPPORT_CONSTRUCTOR:
+ case Tok_QT3_MOC_SUPPORT:
+ readToken();
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool CppCodeParser::matchTemplateAngles(CodeChunk *dataType)
+{
+ bool matches = (tok == Tok_LeftAngle);
+ if (matches) {
+ int leftAngleDepth = 0;
+ int parenAndBraceDepth = 0;
+ do {
+ if (tok == Tok_LeftAngle) {
+ leftAngleDepth++;
+ } else if (tok == Tok_RightAngle) {
+ leftAngleDepth--;
+ } else if (tok == Tok_LeftParen || tok == Tok_LeftBrace) {
+ ++parenAndBraceDepth;
+ } else if (tok == Tok_RightParen || tok == Tok_RightBrace) {
+ if (--parenAndBraceDepth < 0)
+ return false;
+ }
+
+ if (dataType != 0)
+ dataType->append(lexeme());
+ readToken();
+ } while (leftAngleDepth > 0 && tok != Tok_Eoi);
+ }
+ return matches;
+}
+
+bool CppCodeParser::matchTemplateHeader()
+{
+ readToken();
+ return matchTemplateAngles();
+}
+
+bool CppCodeParser::matchDataType(CodeChunk *dataType, QString *var)
+{
+ /*
+ This code is really hard to follow... sorry. The loop is there to match
+ Alpha::Beta::Gamma::...::Omega.
+ */
+ for (;;) {
+ bool virgin = true;
+
+ if (tok != Tok_Ident) {
+ /*
+ There is special processing for 'Foo::operator int()'
+ and such elsewhere. This is the only case where we
+ return something with a trailing gulbrandsen ('Foo::').
+ */
+ if (tok == Tok_operator)
+ return true;
+
+ /*
+ People may write 'const unsigned short' or
+ 'short unsigned const' or any other permutation.
+ */
+ while (match(Tok_const) || match(Tok_volatile))
+ dataType->append(previousLexeme());
+ while (match(Tok_signed) || match(Tok_unsigned) ||
+ match(Tok_short) || match(Tok_long) || match(Tok_int64)) {
+ dataType->append(previousLexeme());
+ virgin = false;
+ }
+ while (match(Tok_const) || match(Tok_volatile))
+ dataType->append(previousLexeme());
+
+ if (match(Tok_Tilde))
+ dataType->append(previousLexeme());
+ }
+
+ if (virgin) {
+ if (match(Tok_Ident))
+ dataType->append(previousLexeme());
+ else if (match(Tok_void) || match(Tok_int) || match(Tok_char) ||
+ match(Tok_double) || match(Tok_Ellipsis))
+ dataType->append(previousLexeme());
+ else
+ return false;
+ } else if (match(Tok_int) || match(Tok_char) || match(Tok_double)) {
+ dataType->append(previousLexeme());
+ }
+
+ matchTemplateAngles(dataType);
+
+ while (match(Tok_const) || match(Tok_volatile))
+ dataType->append(previousLexeme());
+
+ if (match(Tok_Gulbrandsen))
+ dataType->append(previousLexeme());
+ else
+ break;
+ }
+
+ while (match(Tok_Ampersand) || match(Tok_Aster) || match(Tok_const) ||
+ match(Tok_Caret))
+ dataType->append(previousLexeme());
+
+ if (match(Tok_LeftParenAster)) {
+ /*
+ A function pointer. This would be rather hard to handle without a
+ tokenizer hack, because a type can be followed with a left parenthesis
+ in some cases (e.g., 'operator int()'). The tokenizer recognizes '(*'
+ as a single token.
+ */
+ dataType->append(previousLexeme());
+ dataType->appendHotspot();
+ if (var != 0 && match(Tok_Ident))
+ *var = previousLexeme();
+ if (!match(Tok_RightParen) || tok != Tok_LeftParen)
+ return false;
+ dataType->append(previousLexeme());
+
+ int parenDepth0 = tokenizer->parenDepth();
+ while (tokenizer->parenDepth() >= parenDepth0 && tok != Tok_Eoi) {
+ dataType->append(lexeme());
+ readToken();
+ }
+ if (match(Tok_RightParen))
+ dataType->append(previousLexeme());
+ }
+ else {
+ /*
+ The common case: Look for an optional identifier, then for
+ some array brackets.
+ */
+ dataType->appendHotspot();
+
+ if (var != 0) {
+ if (match(Tok_Ident)) {
+ *var = previousLexeme();
+ }
+ else if (match(Tok_Comment)) {
+ /*
+ A neat hack: Commented-out parameter names are
+ recognized by qdoc. It's impossible to illustrate
+ here inside a C-style comment, because it requires
+ an asterslash. It's also impossible to illustrate
+ inside a C++-style comment, because the explanation
+ does not fit on one line.
+ */
+ if (varComment.exactMatch(previousLexeme()))
+ *var = varComment.cap(1);
+ }
+ }
+
+ if (tok == Tok_LeftBracket) {
+ int bracketDepth0 = tokenizer->bracketDepth();
+ while ((tokenizer->bracketDepth() >= bracketDepth0 &&
+ tok != Tok_Eoi) ||
+ tok == Tok_RightBracket) {
+ dataType->append(lexeme());
+ readToken();
+ }
+ }
+ }
+ return true;
+}
+
+bool CppCodeParser::matchParameter(FunctionNode *func)
+{
+ CodeChunk dataType;
+ QString name;
+ CodeChunk defaultValue;
+
+ if (!matchDataType(&dataType, &name))
+ return false;
+ match(Tok_Comment);
+ if (match(Tok_Equal)) {
+ int parenDepth0 = tokenizer->parenDepth();
+
+ while (tokenizer->parenDepth() >= parenDepth0 &&
+ (tok != Tok_Comma ||
+ tokenizer->parenDepth() > parenDepth0) &&
+ tok != Tok_Eoi) {
+ defaultValue.append(lexeme());
+ readToken();
+ }
+ }
+ func->addParameter(Parameter(dataType.toString(), "", name,
+ defaultValue.toString())); // ###
+ return true;
+}
+
+bool CppCodeParser::matchFunctionDecl(InnerNode *parent,
+ QStringList *parentPathPtr,
+ FunctionNode **funcPtr,
+ const QString &templateStuff)
+{
+ CodeChunk returnType;
+ QStringList parentPath;
+ QString name;
+
+ bool compat = false;
+
+ if (match(Tok_friend))
+ return false;
+ match(Tok_explicit);
+ if (matchCompat())
+ compat = true;
+ bool sta = false;
+ if (match(Tok_static)) {
+ sta = true;
+ if (matchCompat())
+ compat = true;
+ }
+ FunctionNode::Virtualness vir = FunctionNode::NonVirtual;
+ if (match(Tok_virtual)) {
+ vir = FunctionNode::ImpureVirtual;
+ if (matchCompat())
+ compat = true;
+ }
+
+ if (!matchDataType(&returnType)) {
+ if (tokenizer->parsingFnOrMacro()
+ && (match(Tok_Q_DECLARE_FLAGS) || match(Tok_Q_PROPERTY)))
+ returnType = CodeChunk(previousLexeme());
+ else
+ return false;
+ }
+
+ if (returnType.toString() == "QBool")
+ returnType = CodeChunk("bool");
+
+ if (matchCompat())
+ compat = true;
+
+ if (tok == Tok_operator &&
+ (returnType.toString().isEmpty() || returnType.toString().endsWith("::"))) {
+ // 'QString::operator const char *()'
+ parentPath = returnType.toString().split(sep);
+ parentPath.removeAll(QString());
+ returnType = CodeChunk();
+ readToken();
+
+ CodeChunk restOfName;
+ if (tok != Tok_Tilde && matchDataType(&restOfName)) {
+ name = "operator " + restOfName.toString();
+ }
+ else {
+ name = previousLexeme() + lexeme();
+ readToken();
+ while (tok != Tok_LeftParen && tok != Tok_Eoi) {
+ name += lexeme();
+ readToken();
+ }
+ }
+ if (tok != Tok_LeftParen)
+ return false;
+ }
+ else if (tok == Tok_LeftParen) {
+ // constructor or destructor
+ parentPath = returnType.toString().split(sep);
+ if (!parentPath.isEmpty()) {
+ name = parentPath.last();
+ parentPath.erase(parentPath.end() - 1);
+ }
+ returnType = CodeChunk();
+ }
+ else {
+ while (match(Tok_Ident)) {
+ name = previousLexeme();
+ matchTemplateAngles();
+
+ if (match(Tok_Gulbrandsen)) {
+ parentPath.append(name);
+ } else {
+ break;
+ }
+ }
+
+ if (tok == Tok_operator) {
+ name = lexeme();
+ readToken();
+ while (tok != Tok_Eoi) {
+ name += lexeme();
+ readToken();
+ if (tok == Tok_LeftParen)
+ break;
+ }
+ }
+ if (parent && (tok == Tok_Semicolon || tok == Tok_LeftBracket || tok == Tok_Colon)
+ && access != Node::Private) {
+ if (tok == Tok_LeftBracket) {
+ returnType.appendHotspot();
+
+ int bracketDepth0 = tokenizer->bracketDepth();
+ while ((tokenizer->bracketDepth() >= bracketDepth0 &&
+ tok != Tok_Eoi) ||
+ tok == Tok_RightBracket) {
+ returnType.append(lexeme());
+ readToken();
+ }
+ if (tok != Tok_Semicolon)
+ return false;
+ } else if (tok == Tok_Colon) {
+ returnType.appendHotspot();
+
+ while (tok != Tok_Semicolon && tok != Tok_Eoi) {
+ returnType.append(lexeme());
+ readToken();
+ }
+ if (tok != Tok_Semicolon)
+ return false;
+ }
+
+ VariableNode *var = new VariableNode(parent, name);
+ var->setAccess(access);
+ var->setLocation(location());
+ var->setLeftType(returnType.left());
+ var->setRightType(returnType.right());
+ if (compat)
+ var->setStatus(Node::Compat);
+ var->setStatic(sta);
+ return false;
+ }
+ if (tok != Tok_LeftParen)
+ return false;
+ }
+ readToken();
+
+ FunctionNode *func = new FunctionNode(parent, name);
+ func->setAccess(access);
+ func->setLocation(location());
+ func->setReturnType(returnType.toString());
+ func->setTemplateStuff(templateStuff);
+ if (compat)
+ func->setStatus(Node::Compat);
+
+ func->setMetaness(metaness);
+ if (parent) {
+ if (name == parent->name()) {
+ func->setMetaness(FunctionNode::Ctor);
+ } else if (name.startsWith("~")) {
+ func->setMetaness(FunctionNode::Dtor);
+ }
+ }
+ func->setStatic(sta);
+
+ if (tok != Tok_RightParen) {
+ do {
+ if (!matchParameter(func))
+ return false;
+ } while (match(Tok_Comma));
+ }
+ if (!match(Tok_RightParen))
+ return false;
+
+ func->setConst(match(Tok_const));
+
+ if (match(Tok_Equal) && match(Tok_Number))
+ vir = FunctionNode::PureVirtual;
+ func->setVirtualness(vir);
+
+ if (match(Tok_Colon)) {
+ while (tok != Tok_LeftBrace && tok != Tok_Eoi)
+ readToken();
+ }
+
+ if (!match(Tok_Semicolon) && tok != Tok_Eoi) {
+ int braceDepth0 = tokenizer->braceDepth();
+
+ if (!match(Tok_LeftBrace))
+ return false;
+ while (tokenizer->braceDepth() >= braceDepth0 && tok != Tok_Eoi)
+ readToken();
+ match(Tok_RightBrace);
+ }
+ if (parentPathPtr != 0)
+ *parentPathPtr = parentPath;
+ if (funcPtr != 0)
+ *funcPtr = func;
+ return true;
+}
+
+bool CppCodeParser::matchBaseSpecifier(ClassNode *classe, bool isClass)
+{
+ Node::Access access;
+
+ switch (tok) {
+ case Tok_public:
+ access = Node::Public;
+ readToken();
+ break;
+ case Tok_protected:
+ access = Node::Protected;
+ readToken();
+ break;
+ case Tok_private:
+ access = Node::Private;
+ readToken();
+ break;
+ default:
+ access = isClass ? Node::Private : Node::Public;
+ }
+
+ if (tok == Tok_virtual)
+ readToken();
+
+ CodeChunk baseClass;
+ if (!matchDataType(&baseClass))
+ return false;
+
+ tre->addBaseClass(classe,
+ access,
+ baseClass.toPath(),
+ baseClass.toString(),
+ classe->parent());
+ return true;
+}
+
+bool CppCodeParser::matchBaseList(ClassNode *classe, bool isClass)
+{
+ for (;;) {
+ if (!matchBaseSpecifier(classe, isClass))
+ return false;
+ if (tok == Tok_LeftBrace)
+ return true;
+ if (!match(Tok_Comma))
+ return false;
+ }
+}
+
+/*!
+ Parse a C++ class, union, or struct declarion.
+ */
+bool CppCodeParser::matchClassDecl(InnerNode *parent,
+ const QString &templateStuff)
+{
+ bool isClass = (tok == Tok_class);
+ readToken();
+
+ bool compat = matchCompat();
+
+ if (tok != Tok_Ident)
+ return false;
+ while (tok == Tok_Ident)
+ readToken();
+ if (tok != Tok_Colon && tok != Tok_LeftBrace)
+ return false;
+
+ /*
+ So far, so good. We have 'class Foo {' or 'class Foo :'.
+ This is enough to recognize a class definition.
+ */
+ ClassNode *classe = new ClassNode(parent, previousLexeme());
+ classe->setAccess(access);
+ classe->setLocation(location());
+ if (compat)
+ classe->setStatus(Node::Compat);
+ if (!moduleName.isEmpty())
+ classe->setModuleName(moduleName);
+ classe->setTemplateStuff(templateStuff);
+
+ if (match(Tok_Colon) && !matchBaseList(classe, isClass))
+ return false;
+ if (!match(Tok_LeftBrace))
+ return false;
+
+ Node::Access outerAccess = access;
+ access = isClass ? Node::Private : Node::Public;
+ FunctionNode::Metaness outerMetaness = metaness;
+ metaness = FunctionNode::Plain;
+
+ bool matches = (matchDeclList(classe) && match(Tok_RightBrace) &&
+ match(Tok_Semicolon));
+ access = outerAccess;
+ metaness = outerMetaness;
+ return matches;
+}
+
+bool CppCodeParser::matchNamespaceDecl(InnerNode *parent)
+{
+ readToken(); // skip 'namespace'
+ if (tok != Tok_Ident)
+ return false;
+ while (tok == Tok_Ident)
+ readToken();
+ if (tok != Tok_LeftBrace)
+ return false;
+
+ /*
+ So far, so good. We have 'namespace Foo {'.
+ */
+ QString namespaceName = previousLexeme();
+ NamespaceNode *namespasse = 0;
+ if (parent)
+ namespasse = static_cast<NamespaceNode *>(parent->findNode(namespaceName, Node::Namespace));
+ if (!namespasse) {
+ namespasse = new NamespaceNode(parent, namespaceName);
+ namespasse->setAccess(access);
+ namespasse->setLocation(location());
+ }
+
+ readToken(); // skip '{'
+ bool matched = matchDeclList(namespasse);
+
+ return matched && match(Tok_RightBrace);
+}
+
+bool CppCodeParser::matchUsingDecl()
+{
+ readToken(); // skip 'using'
+
+ // 'namespace'
+ if (tok != Tok_namespace)
+ return false;
+
+ readToken();
+ // identifier
+ if (tok != Tok_Ident)
+ return false;
+
+ QString name;
+ while (tok == Tok_Ident) {
+ name += lexeme();
+ readToken();
+ if (tok == Tok_Semicolon)
+ break;
+ else if (tok != Tok_Gulbrandsen)
+ return false;
+ name += "::";
+ readToken();
+ }
+
+ /*
+ So far, so good. We have 'using namespace Foo;'.
+ */
+ usedNamespaces.insert(name);
+ return true;
+}
+
+bool CppCodeParser::matchEnumItem(InnerNode *parent, EnumNode *enume)
+{
+ if (!match(Tok_Ident))
+ return false;
+
+ QString name = previousLexeme();
+ CodeChunk val;
+
+ if (match(Tok_Equal)) {
+ while (tok != Tok_Comma && tok != Tok_RightBrace &&
+ tok != Tok_Eoi) {
+ val.append(lexeme());
+ readToken();
+ }
+ }
+
+ if (enume) {
+ QString strVal = val.toString();
+ if (strVal.isEmpty()) {
+ if (enume->items().isEmpty()) {
+ strVal = "0";
+ } else {
+ QString last = enume->items().last().value();
+ bool ok;
+ int n = last.toInt(&ok);
+ if (ok) {
+ if (last.startsWith("0") && last.size() > 1) {
+ if (last.startsWith("0x") || last.startsWith("0X"))
+ strVal = last.left(2) + QString::number(n + 1, 16);
+ else
+ strVal = "0" + QString::number(n + 1, 8);
+ } else {
+ strVal = QString::number(n + 1);
+ }
+ }
+ }
+ }
+
+ enume->addItem(EnumItem(name, strVal));
+ } else {
+ VariableNode *var = new VariableNode(parent, name);
+ var->setAccess(access);
+ var->setLocation(location());
+ var->setLeftType("const int");
+ var->setStatic(true);
+ }
+ return true;
+}
+
+bool CppCodeParser::matchEnumDecl(InnerNode *parent)
+{
+ QString name;
+
+ if (!match(Tok_enum))
+ return false;
+ if (match(Tok_Ident))
+ name = previousLexeme();
+ if (tok != Tok_LeftBrace)
+ return false;
+
+ EnumNode *enume = 0;
+
+ if (!name.isEmpty()) {
+ enume = new EnumNode(parent, name);
+ enume->setAccess(access);
+ enume->setLocation(location());
+ }
+
+ readToken();
+
+ if (!matchEnumItem(parent, enume))
+ return false;
+
+ while (match(Tok_Comma)) {
+ if (!matchEnumItem(parent, enume))
+ return false;
+ }
+ return match(Tok_RightBrace) && match(Tok_Semicolon);
+}
+
+bool CppCodeParser::matchTypedefDecl(InnerNode *parent)
+{
+ CodeChunk dataType;
+ QString name;
+
+ if (!match(Tok_typedef))
+ return false;
+ if (!matchDataType(&dataType, &name))
+ return false;
+ if (!match(Tok_Semicolon))
+ return false;
+
+ if (parent && !parent->findNode(name, Node::Typedef)) {
+ TypedefNode *typedeffe = new TypedefNode(parent, name);
+ typedeffe->setAccess(access);
+ typedeffe->setLocation(location());
+ }
+ return true;
+}
+
+bool CppCodeParser::matchProperty(InnerNode *parent)
+{
+ if (!match(Tok_Q_PROPERTY) &&
+ !match(Tok_Q_OVERRIDE) &&
+ !match(Tok_QDOC_PROPERTY))
+ return false;
+ if (!match(Tok_LeftParen))
+ return false;
+
+ QString name;
+ CodeChunk dataType;
+ if (!matchDataType(&dataType, &name))
+ return false;
+
+ PropertyNode *property = new PropertyNode(parent, name);
+ property->setAccess(Node::Public);
+ property->setLocation(location());
+ property->setDataType(dataType.toString());
+
+ while (tok != Tok_RightParen && tok != Tok_Eoi) {
+ if (!match(Tok_Ident))
+ return false;
+ QString key = previousLexeme();
+ QString value;
+
+ if (match(Tok_Ident)) {
+ value = previousLexeme();
+ } else if (match(Tok_LeftParen)) {
+ int depth = 1;
+ while (tok != Tok_Eoi) {
+ if (tok == Tok_LeftParen) {
+ readToken();
+ ++depth;
+ } else if (tok == Tok_RightParen) {
+ readToken();
+ if (--depth == 0)
+ break;
+ } else {
+ readToken();
+ }
+ }
+ value = "?";
+ }
+
+ if (key == "READ")
+ tre->addPropertyFunction(property, value, PropertyNode::Getter);
+ else if (key == "WRITE")
+ tre->addPropertyFunction(property, value, PropertyNode::Setter);
+ else if (key == "STORED")
+ property->setStored(value.toLower() == "true");
+ else if (key == "DESIGNABLE")
+ property->setDesignable(value.toLower() == "true");
+ else if (key == "RESET")
+ tre->addPropertyFunction(property, value, PropertyNode::Resetter);
+ }
+ match(Tok_RightParen);
+ return true;
+}
+
+/*!
+ Parse a C++ declaration.
+ */
+bool CppCodeParser::matchDeclList(InnerNode *parent)
+{
+ QString templateStuff;
+ int braceDepth0 = tokenizer->braceDepth();
+ if (tok == Tok_RightBrace) // prevents failure on empty body
+ braceDepth0++;
+
+ while (tokenizer->braceDepth() >= braceDepth0 && tok != Tok_Eoi) {
+ switch (tok) {
+ case Tok_Colon:
+ readToken();
+ break;
+ case Tok_class:
+ case Tok_struct:
+ case Tok_union:
+ matchClassDecl(parent, templateStuff);
+ break;
+ case Tok_namespace:
+ matchNamespaceDecl(parent);
+ break;
+ case Tok_using:
+ matchUsingDecl();
+ break;
+ case Tok_template:
+ templateStuff = matchTemplateHeader();
+ continue;
+ case Tok_enum:
+ matchEnumDecl(parent);
+ break;
+ case Tok_typedef:
+ matchTypedefDecl(parent);
+ break;
+ case Tok_private:
+ readToken();
+ access = Node::Private;
+ metaness = FunctionNode::Plain;
+ break;
+ case Tok_protected:
+ readToken();
+ access = Node::Protected;
+ metaness = FunctionNode::Plain;
+ break;
+ case Tok_public:
+ readToken();
+ access = Node::Public;
+ metaness = FunctionNode::Plain;
+ break;
+ case Tok_signals:
+ case Tok_Q_SIGNALS:
+ readToken();
+ access = Node::Public;
+ metaness = FunctionNode::Signal;
+ break;
+ case Tok_slots:
+ case Tok_Q_SLOTS:
+ readToken();
+ metaness = FunctionNode::Slot;
+ break;
+ case Tok_Q_OBJECT:
+ readToken();
+ break;
+ case Tok_Q_OVERRIDE:
+ case Tok_Q_PROPERTY:
+ case Tok_QDOC_PROPERTY:
+ matchProperty(parent);
+ break;
+ case Tok_Q_DECLARE_SEQUENTIAL_ITERATOR:
+ readToken();
+ if (match(Tok_LeftParen) && match(Tok_Ident))
+ sequentialIteratorClasses.insert(previousLexeme(),
+ location().fileName());
+ match(Tok_RightParen);
+ break;
+ case Tok_Q_DECLARE_MUTABLE_SEQUENTIAL_ITERATOR:
+ readToken();
+ if (match(Tok_LeftParen) && match(Tok_Ident))
+ mutableSequentialIteratorClasses.insert(previousLexeme(),
+ location().fileName());
+ match(Tok_RightParen);
+ break;
+ case Tok_Q_DECLARE_ASSOCIATIVE_ITERATOR:
+ readToken();
+ if (match(Tok_LeftParen) && match(Tok_Ident))
+ associativeIteratorClasses.insert(previousLexeme(),
+ location().fileName());
+ match(Tok_RightParen);
+ break;
+ case Tok_Q_DECLARE_MUTABLE_ASSOCIATIVE_ITERATOR:
+ readToken();
+ if (match(Tok_LeftParen) && match(Tok_Ident))
+ mutableAssociativeIteratorClasses.insert(previousLexeme(),
+ location().fileName());
+ match(Tok_RightParen);
+ break;
+ case Tok_Q_DECLARE_FLAGS:
+ readToken();
+ if (match(Tok_LeftParen) && match(Tok_Ident)) {
+ QString flagsType = previousLexeme();
+ if (match(Tok_Comma) && match(Tok_Ident)) {
+ QString enumType = previousLexeme();
+ TypedefNode *flagsNode = new TypedefNode(parent, flagsType);
+ flagsNode->setAccess(access);
+ flagsNode->setLocation(location());
+ EnumNode *enumNode =
+ static_cast<EnumNode*>(parent->findNode(enumType,
+ Node::Enum));
+ if (enumNode)
+ enumNode->setFlagsType(flagsNode);
+ }
+ }
+ match(Tok_RightParen);
+ break;
+ case Tok_QT_MODULE:
+ readToken();
+ if (match(Tok_LeftParen) && match(Tok_Ident))
+ moduleName = previousLexeme();
+ if (!moduleName.startsWith("Qt"))
+ moduleName.prepend("Qt");
+ match(Tok_RightParen);
+ break;
+ default:
+ if (!matchFunctionDecl(parent, 0, 0, templateStuff)) {
+ while (tok != Tok_Eoi &&
+ (tokenizer->braceDepth() > braceDepth0 ||
+ (!match(Tok_Semicolon) &&
+ tok != Tok_public && tok != Tok_protected &&
+ tok != Tok_private)))
+ readToken();
+ }
+ }
+ templateStuff.clear();
+ }
+ return true;
+}
+
+/*!
+ This is called by parseSourceFile() to do the actual parsing
+ and tree building.
+ */
+bool CppCodeParser::matchDocsAndStuff()
+{
+ QSet<QString> topicCommandsAllowed = topicCommands();
+ QSet<QString> otherMetacommandsAllowed = otherMetaCommands();
+ QSet<QString> metacommandsAllowed = topicCommandsAllowed +
+ otherMetacommandsAllowed;
+
+ while (tok != Tok_Eoi) {
+ if (tok == Tok_Doc) {
+ /*
+ lexeme() returns an entire qdoc comment.
+ */
+ QString comment = lexeme();
+ Location start_loc(location());
+ readToken();
+
+ Doc::trimCStyleComment(start_loc,comment);
+ /*
+ qdoc --> doxygen
+ We must also remember the location of the end
+ of the comment, so we can construct a diff for
+ it.
+ */
+ Location end_loc(location());
+
+ /*
+ Doc parses the comment.
+ */
+ Doc doc(start_loc,end_loc,comment,metacommandsAllowed);
+
+ QString topic;
+ QStringList args;
+
+ QSet<QString> topicCommandsUsed = topicCommandsAllowed &
+ doc.metaCommandsUsed();
+
+ /*
+ There should be one topic command in the set,
+ or none. If the set is empty, then the comment
+ should be a function description.
+ */
+ if (topicCommandsUsed.count() > 0) {
+ topic = *topicCommandsUsed.begin();
+ args = doc.metaCommandArgs(topic);
+ }
+
+ NodeList nodes;
+ QList<Doc> docs;
+
+ if (topic.isEmpty()) {
+ QStringList parentPath;
+ FunctionNode *clone;
+ FunctionNode *func = 0;
+
+ if (matchFunctionDecl(0, &parentPath, &clone)) {
+ foreach (const QString &usedNamespace, usedNamespaces) {
+ QStringList newPath = usedNamespace.split("::") + parentPath;
+ func = tre->findFunctionNode(newPath, clone);
+ if (func)
+ break;
+ }
+ if (func == 0)
+ func = tre->findFunctionNode(parentPath, clone);
+
+ if (func) {
+ func->borrowParameterNames(clone);
+ nodes.append(func);
+ docs.append(doc);
+ }
+ delete clone;
+ }
+ else {
+ doc.location().warning(
+ tr("Cannot tie this documentation to anything"),
+ tr("I found a /*! ... */ comment, but there was no "
+ "topic command (e.g., '\\%1', '\\%2') in the "
+ "comment and no function definition following "
+ "the comment.")
+ .arg(COMMAND_FN).arg(COMMAND_PAGE));
+ }
+ }
+ else {
+ /*
+ There is a topic command. Process it.
+ */
+ QStringList::ConstIterator a = args.begin();
+ while (a != args.end()) {
+ Doc nodeDoc = doc;
+ Node *node = processTopicCommand(nodeDoc, topic, *a);
+ if (node != 0) {
+ nodes.append(node);
+ docs.append(nodeDoc);
+ }
+ ++a;
+ }
+ }
+
+ NodeList::Iterator n = nodes.begin();
+ QList<Doc>::Iterator d = docs.begin();
+ while (n != nodes.end()) {
+ processOtherMetaCommands(*d, *n);
+ (*n)->setDoc(*d);
+ if ((*n)->isInnerNode() && ((InnerNode *)*n)->includes().isEmpty()) {
+ InnerNode *m = static_cast<InnerNode *>(*n);
+ while (m->parent() != tre->root())
+ m = m->parent();
+ if (m == *n)
+ ((InnerNode *)*n)->addInclude((*n)->name());
+ else
+ ((InnerNode *)*n)->setIncludes(m->includes());
+ }
+ ++d;
+ ++n;
+ }
+ }
+ else if (tok == Tok_using) {
+ matchUsingDecl();
+ }
+ else {
+ QStringList parentPath;
+ FunctionNode *clone;
+ FunctionNode *node = 0;
+
+ if (matchFunctionDecl(0, &parentPath, &clone)) {
+ /*
+ The location of the definition is more interesting
+ than that of the declaration. People equipped with
+ a sophisticated text editor can respond to warnings
+ concerning undocumented functions very quickly.
+
+ Signals are implemented in uninteresting files
+ generated by moc.
+ */
+ node = tre->findFunctionNode(parentPath, clone);
+ if (node != 0 && node->metaness() != FunctionNode::Signal)
+ node->setLocation(clone->location());
+ delete clone;
+ }
+ else {
+ if (tok != Tok_Doc)
+ readToken();
+ }
+ }
+ }
+ return true;
+}
+
+bool CppCodeParser::makeFunctionNode(const QString& synopsis,
+ QStringList *parentPathPtr,
+ FunctionNode **funcPtr,
+ InnerNode *root)
+{
+ Tokenizer *outerTokenizer = tokenizer;
+ int outerTok = tok;
+
+ Location loc;
+ QByteArray latin1 = synopsis.toLatin1();
+ Tokenizer stringTokenizer(loc, latin1);
+ stringTokenizer.setParsingFnOrMacro(true);
+ tokenizer = &stringTokenizer;
+ readToken();
+
+ bool ok = matchFunctionDecl(root, parentPathPtr, funcPtr);
+ // potential memory leak with funcPtr
+
+ tokenizer = outerTokenizer;
+ tok = outerTok;
+
+ return ok;
+}
+
+void CppCodeParser::parseQiteratorDotH(const Location &location,
+ const QString &filePath)
+{
+ QFile file(filePath);
+ if (!file.open(QFile::ReadOnly))
+ return;
+
+ QString text = file.readAll();
+ text.remove("\r");
+ text.replace("\\\n", "");
+ QStringList lines = text.split("\n");
+ lines = lines.filter("Q_DECLARE");
+ lines.replaceInStrings(QRegExp("#define Q[A-Z_]*\\(C\\)"), "");
+
+ if (lines.size() == 4) {
+ sequentialIteratorDefinition = lines[0];
+ mutableSequentialIteratorDefinition = lines[1];
+ associativeIteratorDefinition = lines[2];
+ mutableAssociativeIteratorDefinition = lines[3];
+ } else {
+ location.warning(tr("The qiterator.h hack failed"));
+ }
+}
+
+void CppCodeParser::instantiateIteratorMacro(const QString &container,
+ const QString &includeFile,
+ const QString &macroDef,
+ Tree * /* tree */)
+{
+ QString resultingCode = macroDef;
+ resultingCode.replace(QRegExp("\\bC\\b"), container);
+ resultingCode.replace(QRegExp("\\s*##\\s*"), "");
+
+ Location loc(includeFile); // hack to get the include file for free
+ QByteArray latin1 = resultingCode.toLatin1();
+ Tokenizer stringTokenizer(loc, latin1);
+ tokenizer = &stringTokenizer;
+ readToken();
+ matchDeclList(tre->root());
+}
+
+void CppCodeParser::createExampleFileNodes(FakeNode *fake)
+{
+ QString examplePath = fake->name();
+
+ // we can assume that this file always exists
+ QString proFileName = examplePath + "/" +
+ examplePath.split("/").last() + ".pro";
+
+ QString userFriendlyFilePath;
+ QString fullPath = Config::findFile(fake->doc().location(),
+ exampleFiles,
+ exampleDirs,
+ proFileName,
+ userFriendlyFilePath);
+ if (fullPath.isEmpty()) {
+ QString tmp = proFileName;
+ proFileName = examplePath + "/" + "qbuild.pro";
+ userFriendlyFilePath.clear();
+ fullPath = Config::findFile(fake->doc().location(),
+ exampleFiles,
+ exampleDirs,
+ proFileName,
+ userFriendlyFilePath);
+ if (fullPath.isEmpty()) {
+ fake->doc().location().warning(
+ tr("Cannot find file '%1' or '%2'").arg(tmp).arg(proFileName));
+ return;
+ }
+ }
+
+ int sizeOfBoringPartOfName = fullPath.size() - proFileName.size();
+ fullPath.truncate(fullPath.lastIndexOf('/'));
+
+ QStringList exampleFiles = Config::getFilesHere(fullPath,
+ exampleNameFilter);
+ if (!exampleFiles.isEmpty()) {
+ // move main.cpp and to the end, if it exists
+ QString mainCpp;
+ QMutableStringListIterator i(exampleFiles);
+ i.toBack();
+ while (i.hasPrevious()) {
+ QString fileName = i.previous();
+ if (fileName.endsWith("/main.cpp")) {
+ mainCpp = fileName;
+ i.remove();
+ }
+ else if (fileName.contains("/qrc_") || fileName.contains("/moc_")
+ || fileName.contains("/ui_"))
+ i.remove();
+ }
+ if (!mainCpp.isEmpty())
+ exampleFiles.append(mainCpp);
+
+ // add any qmake Qt resource files and qmake project files
+ exampleFiles += Config::getFilesHere(fullPath, "*.qrc *.pro");
+ }
+
+ foreach (const QString &exampleFile, exampleFiles)
+ (void) new FakeNode(fake,
+ exampleFile.mid(sizeOfBoringPartOfName),
+ FakeNode::File);
+}
+
+QT_END_NAMESPACE
diff --git a/tools/qdoc3/cppcodeparser.h b/tools/qdoc3/cppcodeparser.h
new file mode 100644
index 0000000..e672654
--- /dev/null
+++ b/tools/qdoc3/cppcodeparser.h
@@ -0,0 +1,167 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*
+ cppcodeparser.h
+*/
+
+#ifndef CPPCODEPARSER_H
+#define CPPCODEPARSER_H
+
+#include <qregexp.h>
+
+#include "codeparser.h"
+#include "node.h"
+
+QT_BEGIN_NAMESPACE
+
+class ClassNode;
+class CodeChunk;
+class CppCodeParserPrivate;
+class FunctionNode;
+class InnerNode;
+class Tokenizer;
+
+class CppCodeParser : public CodeParser
+{
+ public:
+ CppCodeParser();
+ ~CppCodeParser();
+
+ virtual void initializeParser(const Config& config);
+ virtual void terminateParser();
+ virtual QString language();
+ virtual QString headerFileNameFilter();
+ virtual QString sourceFileNameFilter();
+ virtual void parseHeaderFile(const Location& location,
+ const QString& filePath,
+ Tree *tree);
+ virtual void parseSourceFile(const Location& location,
+ const QString& filePath,
+ Tree *tree);
+ virtual void doneParsingHeaderFiles(Tree *tree);
+ virtual void doneParsingSourceFiles(Tree *tree);
+
+ const FunctionNode *findFunctionNode(const QString& synopsis,
+ Tree *tree,
+ Node *relative = 0,
+ bool fuzzy = false);
+
+ protected:
+ virtual QSet<QString> topicCommands();
+ virtual Node *processTopicCommand(const Doc& doc,
+ const QString& command,
+ const QString& arg);
+ virtual QSet<QString> otherMetaCommands();
+ virtual void processOtherMetaCommand(const Doc& doc,
+ const QString& command,
+ const QString& arg,
+ Node *node);
+ void processOtherMetaCommands(const Doc& doc, Node *node);
+
+ private:
+ void reset(Tree *tree);
+ void readToken();
+ const Location& location();
+ QString previousLexeme();
+ QString lexeme();
+ bool match(int target);
+ bool matchCompat();
+ bool matchTemplateAngles(CodeChunk *type = 0);
+ bool matchTemplateHeader();
+ bool matchDataType(CodeChunk *type, QString *var = 0);
+ bool matchParameter(FunctionNode *func);
+ bool matchFunctionDecl(InnerNode *parent,
+ QStringList *parentPathPtr = 0,
+ FunctionNode **funcPtr = 0,
+ const QString &templateStuff = QString());
+ bool matchBaseSpecifier(ClassNode *classe, bool isClass);
+ bool matchBaseList(ClassNode *classe, bool isClass);
+ bool matchClassDecl(InnerNode *parent,
+ const QString &templateStuff = QString());
+ bool matchNamespaceDecl(InnerNode *parent);
+ bool matchUsingDecl();
+ bool matchEnumItem(InnerNode *parent, EnumNode *enume);
+ bool matchEnumDecl(InnerNode *parent);
+ bool matchTypedefDecl(InnerNode *parent);
+ bool matchProperty(InnerNode *parent);
+ bool matchDeclList(InnerNode *parent);
+ bool matchDocsAndStuff();
+ bool makeFunctionNode(const QString &synopsis,
+ QStringList *parentPathPtr,
+ FunctionNode **funcPtr,
+ InnerNode *root = 0);
+ void parseQiteratorDotH(const Location &location, const QString &filePath);
+ void instantiateIteratorMacro(const QString &container,
+ const QString &includeFile,
+ const QString &macroDef,
+ Tree *tree);
+ void createExampleFileNodes(FakeNode *fake);
+
+ QMap<QString, Node::Type> nodeTypeMap;
+ Tree *tre;
+ Tokenizer *tokenizer;
+ int tok;
+ Node::Access access;
+ FunctionNode::Metaness metaness;
+ QString moduleName;
+ QStringList lastPath;
+ QRegExp varComment;
+ QRegExp sep;
+
+ QString sequentialIteratorDefinition;
+ QString mutableSequentialIteratorDefinition;
+ QString associativeIteratorDefinition;
+ QString mutableAssociativeIteratorDefinition;
+ QSet<QString> usedNamespaces;
+ QMap<QString, QString> sequentialIteratorClasses;
+ QMap<QString, QString> mutableSequentialIteratorClasses;
+ QMap<QString, QString> associativeIteratorClasses;
+ QMap<QString, QString> mutableAssociativeIteratorClasses;
+
+ static QStringList exampleFiles;
+ static QStringList exampleDirs;
+ QString exampleNameFilter;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/tools/qdoc3/cpptoqsconverter.cpp b/tools/qdoc3/cpptoqsconverter.cpp
new file mode 100644
index 0000000..dfc81bc
--- /dev/null
+++ b/tools/qdoc3/cpptoqsconverter.cpp
@@ -0,0 +1,415 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*
+ cpptoqsconverter.cpp
+*/
+
+#include "config.h"
+#include "cpptoqsconverter.h"
+
+QT_BEGIN_NAMESPACE
+
+#define CONFIG_QUICK "quick"
+#define CONFIG_INDENTSIZE "indentsize"
+
+void setTabSize( int size );
+void setIndentSize( int size );
+int columnForIndex( const QString& t, int index );
+int indentForBottomLine( const QStringList& program, QChar typedIn );
+
+static QString balancedParens = "(?:[^()]+|\\([^()]*\\))*";
+
+QRegExp CppToQsConverter::qClassRegExp;
+QRegExp CppToQsConverter::addressOperatorRegExp;
+QRegExp CppToQsConverter::gulbrandsenRegExp;
+int CppToQsConverter::tabSize;
+
+ClassNode *CppToQsConverter::findClassNode( Tree *qsTree,
+ const QString& qtName )
+{
+ ClassNode *classe = (ClassNode *) qsTree->findNode( QStringList(qtName), Node::Class );
+ if ( classe == 0 )
+ classe = (ClassNode *) qsTree->findNode( QStringList(qtName.mid(1)), Node::Class );
+ return classe;
+}
+
+QString CppToQsConverter::convertedDataType( Tree *qsTree,
+ const QString& leftType,
+ const QString& /* rightType */ )
+{
+ QString s = leftType;
+
+ if ( s.startsWith("const ") )
+ s = s.mid( 6 );
+ while ( s.endsWith("*") || s.endsWith("&") || s.endsWith(" ") )
+ s.truncate( s.length() - 1 );
+
+ switch ( s[0].unicode() ) {
+ case 'Q':
+ if ( s == "QCString" ) {
+ return "String";
+ } else {
+ Node *node = findClassNode( qsTree, s );
+ if ( node == 0 ) {
+ return "";
+ } else {
+ return node->name();
+ }
+ }
+ break;
+ case 'b':
+ if ( s == "bool" )
+ return "Boolean";
+ break;
+ case 'c':
+ if ( s == "char" ) {
+ if ( leftType == "const char *" ) {
+ return "String";
+ } else {
+ return "Number";
+ }
+ }
+ break;
+ case 'd':
+ if ( s == "double" )
+ return "Number";
+ break;
+ case 'f':
+ if ( s == "float" )
+ return "Number";
+ case 'i':
+ if ( s == "int" )
+ return "Number";
+ break;
+ case 'l':
+ if ( s == "long" || s == "long int" || s == "long long" ||
+ s == "long long int" || s == "long double" )
+ return "Number";
+ break;
+ case 's':
+ if ( s == "short" || s == "short int" || s == "signed char" ||
+ s == "signed short" || s == "signed short int" || s == "signed" ||
+ s == "signed int" || s == "signed long" || s == "signed long int" )
+ return "Number";
+ break;
+ case 'u':
+ if ( s == "uchar" || s == "unsigned" || s == "unsigned char" ||
+ s == "ushort" || s == "unsigned short" ||
+ s == "unsigned short int" || s == "uint" || s == "unsigned int" ||
+ s == "ulong" || s == "unsigned long" || s == "unsigned long int" )
+ return "Number";
+ break;
+ case 'v':
+ if ( s == "void" )
+ return "";
+ }
+ return s;
+}
+
+QString CppToQsConverter::convertedCode( Tree *qsTree, const QString& code,
+ const QSet<QString>& classesWithNoQ )
+{
+ QString result;
+ QStringList program;
+ QStringList comments;
+ int programWidth = 0;
+
+ QStringList originalLines = code.split("\n");
+ QStringList::ConstIterator ol = originalLines.begin();
+ while ( ol != originalLines.end() ) {
+ QString code = (*ol).trimmed();
+ QString comment;
+
+ int slashSlash = code.indexOf( "//" );
+ if ( slashSlash != -1 ) {
+ comment = code.mid( slashSlash );
+ code.truncate( slashSlash );
+ code = code.trimmed();
+ }
+
+ code = convertCodeLine( qsTree, program, code, classesWithNoQ );
+ program.append( code );
+
+ comment = convertComment( qsTree, comment, classesWithNoQ );
+ comments.append( comment );
+
+ int n = indentForBottomLine( program, QChar::Null );
+ for ( int i = 0; i < n; i++ )
+ program.last().prepend( " " );
+
+ int width = columnForIndex( program.last(), program.last().length() );
+ if ( !comment.isEmpty() && width > programWidth )
+ programWidth = width;
+ ++ol;
+ }
+
+ programWidth = ( (programWidth + (tabSize - 1) + 2) / tabSize ) * tabSize;
+
+ QStringList::ConstIterator p = program.begin();
+ QStringList::ConstIterator c = comments.begin();
+ while ( c != comments.end() ) {
+ if ( c != comments.begin() )
+ result += "\n";
+
+ if ( (*p).trimmed().isEmpty() ) {
+ if ( !(*c).isEmpty() )
+ result += *p;
+ } else {
+ result += *p;
+ if ( !(*c).isEmpty() ) {
+ int i = columnForIndex( *p, (*p).length() );
+ while ( i++ < programWidth )
+ result += " ";
+ }
+ }
+ result += *c;
+ ++p;
+ ++c;
+ }
+ return result;
+}
+
+void CppToQsConverter::initialize( const Config& config )
+{
+ qClassRegExp.setPattern( "\\bQ([A-Z][A-Za-z]+)\\b" );
+ addressOperatorRegExp.setPattern( "([(\\s])[*&]([a-zA-Z])" );
+ gulbrandsenRegExp.setPattern( "\\b::\\b|->" );
+
+ tabSize = config.getInt( CONFIG_TABSIZE );
+ setTabSize( tabSize );
+
+ int size = config.getInt( CONFIG_QUICK + Config::dot + CONFIG_INDENTSIZE );
+ if ( size > 0 )
+ setIndentSize( size );
+}
+
+void CppToQsConverter::terminate()
+{
+}
+
+QString CppToQsConverter::convertCodeLine( Tree *qsTree,
+ const QStringList& program,
+ const QString& code,
+ const QSet<QString>& classesWithNoQ )
+{
+ static QString dataTypeFmt =
+ "(?!return)(?:const\\b\\s*)?[A-Za-z_]+(?:\\s*[*&])?";
+ static QRegExp funcPrototypeRegExp(
+ "(" + dataTypeFmt + ")\\s*\\b([A-Z][a-zA-Z_0-9]*::)?"
+ "([a-z][a-zA-Z_0-9]*)\\(([^);]*)(\\)?)(?:\\s*const)?" );
+ static QRegExp paramRegExp(
+ "^\\s*(" + dataTypeFmt + ")\\s*\\b([a-z][a-zA-Z_0-9]*)\\s*(,)?\\s*" );
+ static QRegExp uninitVarRegExp(
+ "(" + dataTypeFmt + ")\\s*\\b([a-z][a-zA-Z_0-9]*);" );
+ static QRegExp eqVarRegExp(
+ dataTypeFmt + "\\s*\\b([a-z][a-zA-Z_0-9]*)\\s*=(\\s*)(.*)" );
+ static QRegExp ctorVarRegExp(
+ "(" + dataTypeFmt + ")\\s*\\b([a-z][a-zA-Z_0-9]*)\\((.*)\\);" );
+ static QRegExp qdebugRegExp(
+ "q(?:Debug|Warning|Fatal)\\(\\s*(\"(?:\\\\.|[^\"])*\")\\s*"
+ "(?:,\\s*(\\S(?:[^,]*\\S)?))?\\s*\\);" );
+ static QRegExp coutRegExp( "c(?:out|err)\\b(.*);" );
+ static QRegExp lshiftRegExp( "\\s*<<\\s*" );
+ static QRegExp endlRegExp( "^endl$" );
+
+ if ( code.isEmpty() || code == "{" || code == "}" )
+ return code;
+
+ QString result;
+
+ if ( funcPrototypeRegExp.exactMatch(code) ) {
+ QString returnType = funcPrototypeRegExp.cap( 1 );
+ QString className = funcPrototypeRegExp.cap( 2 );
+ QString funcName = funcPrototypeRegExp.cap( 3 );
+ QString params = funcPrototypeRegExp.cap( 4 ).trimmed();
+ bool toBeContinued = funcPrototypeRegExp.cap( 5 ).isEmpty();
+ // ### unused
+ Q_UNUSED(toBeContinued);
+
+ className.replace( "::", "." );
+
+ result = "function " + className + funcName + "(";
+
+ if ( !params.isEmpty() && params != "void" ) {
+ result += " ";
+ int i = funcPrototypeRegExp.pos( 4 );
+ while ( (i = paramRegExp.indexIn(code, i,
+ QRegExp::CaretAtOffset)) != -1 ) {
+ QString dataType = paramRegExp.cap( 1 );
+ QString paramName = paramRegExp.cap( 2 );
+ QString comma = paramRegExp.cap( 3 );
+
+ result += paramName + " : " +
+ convertedDataType( qsTree, dataType );
+ if ( comma.isEmpty() )
+ break;
+ result += ", ";
+ i += paramRegExp.matchedLength();
+ }
+ result += " ";
+ }
+
+ result += ")";
+ returnType = convertedDataType( qsTree, returnType );
+ if ( !returnType.isEmpty() )
+ result += " : " + returnType;
+ } else if ( uninitVarRegExp.exactMatch(code) ) {
+ QString dataType = uninitVarRegExp.cap( 1 );
+ QString varName = uninitVarRegExp.cap( 2 );
+
+ result = "var " + varName;
+ dataType = convertedDataType( qsTree, dataType );
+ if ( !dataType.isEmpty() )
+ result += " : " + dataType;
+ result += ";";
+ } else if ( eqVarRegExp.exactMatch(code) ) {
+ QString varName = eqVarRegExp.cap( 1 );
+ QString value = eqVarRegExp.cap( 3 );
+
+ value = convertExpr( qsTree, value, classesWithNoQ );
+ result += "var " + varName + " = " + value;
+ } else if ( ctorVarRegExp.exactMatch(code) ) {
+ QString dataType = ctorVarRegExp.cap( 1 );
+ QString varName = ctorVarRegExp.cap( 2 );
+ QString value = ctorVarRegExp.cap( 3 ).trimmed();
+
+ result += "var " + varName + " = ";
+
+ dataType = convertedDataType( qsTree, dataType );
+ value = convertExpr( qsTree, value, classesWithNoQ );
+
+ if ( dataType.isEmpty() || dataType == "String" ) {
+ if ( value.contains(",") ) {
+ result += "...";
+ } else {
+ result += value;
+ }
+ } else {
+ result += "new " + dataType;
+ if ( !value.isEmpty() )
+ result += "( " + value + " )";
+ }
+ result += ";";
+ } else if ( qdebugRegExp.exactMatch(code) ) {
+ QString fmt = qdebugRegExp.cap( 1 );
+ QString arg1 = qdebugRegExp.cap( 2 );
+
+ result += "println ";
+ int i = 0;
+ while ( i < (int) fmt.length() ) {
+ if ( fmt[i] == '%' ) {
+ int percent = i;
+ i++;
+ while ( i < (int) fmt.length() &&
+ QString("diouxXeEfFgGaAcsCSpn%\"").indexOf(fmt[i]) == -1 )
+ i++;
+ if ( fmt[i] == '%' ) {
+ result += fmt[i++];
+ } else if ( fmt[i] != '"' ) {
+ if ( percent == 1 ) {
+ result.truncate( result.length() - 1 );
+ } else {
+ result += "\" + ";
+ }
+ i++;
+ if ( arg1.endsWith(".latin1()") )
+ arg1.truncate( arg1.length() - 9 );
+ result += arg1;
+ if ( i == (int) fmt.length() - 1 ) {
+ i++;
+ } else {
+ result += " + \"";
+ }
+ }
+ } else {
+ result += fmt[i++];
+ }
+ }
+ result += ";";
+ } else if ( coutRegExp.exactMatch(code) &&
+ program.filter("var cout").isEmpty() ) {
+ QStringList args = coutRegExp.cap(1).split(lshiftRegExp);
+ args.replaceInStrings( endlRegExp, "\"\\n\"" );
+ if ( args.last() == "\"\\n\"" ) {
+ args.erase( args.end() - 1 );
+ if ( args.isEmpty() )
+ args << "\"\"";
+ result += "println ";
+ } else {
+ result += "print ";
+ }
+ result += args.join( " + " ) + ";";
+ } else {
+ result = convertExpr( qsTree, code, classesWithNoQ );
+ }
+ return result;
+}
+
+QString CppToQsConverter::convertComment( Tree * /* qsTree */,
+ const QString& comment,
+ const QSet<QString>& classesWithNoQ )
+
+{
+ QString result = comment;
+
+ result.replace( "TRUE", "true" );
+ result.replace( "FALSE", "false" );
+ result.replace( addressOperatorRegExp, "\\1\\2" );
+ result.replace( gulbrandsenRegExp, "." );
+
+ int i = 0;
+ while ( (i = result.indexOf(qClassRegExp, i)) != -1 ) {
+ if ( classesWithNoQ.contains(qClassRegExp.cap(1)) )
+ result.remove( i, 1 );
+ i++;
+ }
+ return result;
+}
+
+QString CppToQsConverter::convertExpr( Tree *qsTree, const QString& expr,
+ const QSet<QString>& classesWithNoQ )
+{
+ // suboptimal
+ return convertComment( qsTree, expr, classesWithNoQ );
+}
+
+QT_END_NAMESPACE
diff --git a/tools/qdoc3/cpptoqsconverter.h b/tools/qdoc3/cpptoqsconverter.h
new file mode 100644
index 0000000..baf801c
--- /dev/null
+++ b/tools/qdoc3/cpptoqsconverter.h
@@ -0,0 +1,88 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*
+ cpptoqsconverter.h
+*/
+
+#ifndef CPPTOQSCONVERTER_H
+#define CPPTOQSCONVERTER_H
+
+#include <qregexp.h>
+
+#include "tree.h"
+
+QT_BEGIN_NAMESPACE
+
+class CppToQsConverter
+{
+public:
+ CppToQsConverter() { }
+
+ ClassNode *findClassNode( Tree *qsTree, const QString& qtName );
+ QString convertedDataType( Tree *qsTree, const QString& leftType,
+ const QString& rightType = "" );
+ QString convertedCode( Tree *qsTree, const QString& code,
+ const QSet<QString>& classesWithNoQ );
+
+ static void initialize( const Config& config );
+ static void terminate();
+
+private:
+ void clearState();
+ QString convertCodeLine( Tree *qsTree, const QStringList& program,
+ const QString& code,
+ const QSet<QString>& classesWithNoQ );
+ QString convertComment( Tree *qsTree, const QString& comment,
+ const QSet<QString>& classesWithNoQ );
+ QString convertExpr( Tree *qsTree, const QString& expr,
+ const QSet<QString>& classesWithNoQ );
+ void updateDelimDepths( const QString& code );
+
+ static QRegExp qClassRegExp;
+ static QRegExp addressOperatorRegExp;
+ static QRegExp gulbrandsenRegExp;
+ static int tabSize;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/tools/qdoc3/dcfsection.cpp b/tools/qdoc3/dcfsection.cpp
new file mode 100644
index 0000000..5bff070
--- /dev/null
+++ b/tools/qdoc3/dcfsection.cpp
@@ -0,0 +1,111 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <qfile.h>
+#include <qfileinfo.h>
+#include <qtextstream.h>
+
+#include "dcfsection.h"
+#include "htmlgenerator.h"
+
+QT_BEGIN_NAMESPACE
+
+void appendDcfSubSection( DcfSection *dcfSect, const DcfSection& sub )
+{
+ dcfSect->subsections.append( sub );
+}
+
+void appendDcfSubSections( DcfSection *dcfSect, const QList<DcfSection>& subs )
+{
+ dcfSect->subsections += subs;
+}
+
+void generateDcfSubSections( QString indent, QTextStream& out, const DcfSection& sect )
+{
+ QList<DcfSection>::const_iterator ss = sect.subsections.constBegin();
+ while ( ss != sect.subsections.constEnd() ) {
+ out << indent << "<section ref=\"" << HtmlGenerator::cleanRef(HtmlGenerator::protect((*ss).ref))
+ << "\" title=\"" << HtmlGenerator::protect((*ss).title) << "\"";
+ if ((*ss).keywords.isEmpty() && (*ss).subsections.isEmpty()) {
+ out << "/>\n";
+ } else {
+ out << ">\n";
+ QString indentIndent = indent + " ";
+ QList<QPair<QString, QString> >::const_iterator k = (*ss).keywords.constBegin();
+ while ( k != (*ss).keywords.constEnd() ) {
+ out << indentIndent << "<keyword ref=\"" << HtmlGenerator::cleanRef((*k).second) << "\">"
+ << HtmlGenerator::protect((*k).first) << "</keyword>\n";
+ ++k;
+ }
+
+ generateDcfSubSections( indentIndent, out, *ss );
+ out << indent << "</section>\n";
+ }
+ ++ss;
+ }
+ out.flush();
+}
+
+void generateDcfSections( const DcfSection& rootSect, const QString& fileName,
+ const QString& /* category */ )
+{
+ QFile file(fileName);
+ if (!file.open(QFile::WriteOnly | QFile::Text))
+ return ;
+
+ QTextStream out(&file);
+
+ QString icon = QFileInfo(fileName).baseName() + ".png";
+
+ out << "<!DOCTYPE DCF>\n";
+ out << "<DCF ref=\"" << HtmlGenerator::cleanRef(HtmlGenerator::protect(rootSect.ref));
+ if (icon != "qmake.png")
+ out << "\" icon=\"" << HtmlGenerator::protect(icon);
+ out << "\" imagedir=\"../../gif\" title=\"" << HtmlGenerator::protect(rootSect.title) +
+ "\">\n";
+
+ generateDcfSubSections( "", out, rootSect );
+
+ out << "</DCF>\n";
+ out.flush();
+}
+
+QT_END_NAMESPACE
diff --git a/tools/qdoc3/dcfsection.h b/tools/qdoc3/dcfsection.h
new file mode 100644
index 0000000..91d31ed
--- /dev/null
+++ b/tools/qdoc3/dcfsection.h
@@ -0,0 +1,94 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef DCFSECTION_H
+#define DCFSECTION_H
+
+#include <qlist.h>
+#include <qpair.h>
+#include <qstring.h>
+
+QT_BEGIN_NAMESPACE
+
+class QTextStream;
+
+struct DcfSection
+{
+ QString title;
+ QString ref;
+ QList<QPair<QString, QString> > keywords;
+ QList<DcfSection> subsections;
+};
+
+inline bool operator<( const DcfSection& s1, const DcfSection& s2 ) {
+ QString title1 = s1.title;
+ QString title2 = s2.title;
+
+ // cheat with Q3 classes
+ if (title1.startsWith("Q3"))
+ title1.insert(1, '~');
+ if (title2.startsWith("Q3"))
+ title2.insert(1, '~');
+
+ int delta = title1.toLower().compare( title2.toLower() );
+ if ( delta == 0 ) {
+ delta = title1.compare( title2 );
+ if ( delta == 0 )
+ delta = s1.ref.localeAwareCompare( s2.ref );
+ }
+ return delta < 0;
+}
+
+inline bool operator>( const DcfSection& s1, const DcfSection& s2 ) { return s2 < s1; }
+inline bool operator<=( const DcfSection& s1, const DcfSection& s2 ) { return !( s2 < s1 ); }
+inline bool operator>=( const DcfSection& s1, const DcfSection& s2 ) { return !( s1 < s2 ); }
+inline bool operator==( const DcfSection& s1, const DcfSection& s2 ) { return &s1 == &s2; }
+inline bool operator!=( const DcfSection& s1, const DcfSection& s2 ) { return !( s1 == s2 ); }
+
+void appendDcfSubSection(DcfSection *dcfSect, const DcfSection &sub);
+void appendDcfSubSections(DcfSection *dcfSect, const QList<DcfSection> &subs);
+void generateDcfSubSections(QString indent, QTextStream &out, const DcfSection &sect);
+void generateDcfSections(const DcfSection &rootSect, const QString& fileName,
+ const QString& category );
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/tools/qdoc3/doc.cpp b/tools/qdoc3/doc.cpp
new file mode 100644
index 0000000..61d0ed6
--- /dev/null
+++ b/tools/qdoc3/doc.cpp
@@ -0,0 +1,5036 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "config.h"
+#include "doc.h"
+#include "codemarker.h"
+#include "editdistance.h"
+#include "openedlist.h"
+#include "quoter.h"
+#include "text.h"
+#include "tokenizer.h"
+#include <qdatetime.h>
+#include <qdebug.h>
+#include <qfile.h>
+#include <qfileinfo.h>
+#include <qhash.h>
+#include <qtextstream.h>
+#include <qregexp.h>
+#include <ctype.h>
+#include <limits.h>
+
+QT_BEGIN_NAMESPACE
+
+Q_GLOBAL_STATIC(QSet<QString>, null_Set_QString)
+Q_GLOBAL_STATIC(QStringList, null_QStringList)
+Q_GLOBAL_STATIC(QList<Text>, null_QList_Text)
+Q_GLOBAL_STATIC(QStringMap, null_QStringMap)
+
+struct Macro
+{
+ QString defaultDef;
+ Location defaultDefLocation;
+ QStringMap otherDefs;
+ int numParams;
+};
+
+enum {
+ CMD_A, CMD_ABSTRACT, CMD_BADCODE, CMD_BASENAME, CMD_BOLD,
+ CMD_BRIEF, CMD_C, CMD_CAPTION, CMD_CHAPTER, CMD_CODE,
+ CMD_CODELINE, CMD_DOTS, CMD_ELSE, CMD_ENDABSTRACT,
+ CMD_ENDCHAPTER, CMD_ENDCODE, CMD_ENDFOOTNOTE, CMD_ENDIF,
+ CMD_ENDLEGALESE, CMD_ENDLINK, CMD_ENDLIST, CMD_ENDOMIT,
+ CMD_ENDPART, CMD_ENDQUOTATION, CMD_ENDRAW, CMD_ENDSECTION1,
+ CMD_ENDSECTION2, CMD_ENDSECTION3, CMD_ENDSECTION4,
+ CMD_ENDSIDEBAR, CMD_ENDTABLE, CMD_EXPIRE, CMD_FOOTNOTE,
+ CMD_GENERATELIST, CMD_GRANULARITY, CMD_HEADER, CMD_I,
+ CMD_IF, CMD_IMAGE, CMD_INCLUDE, CMD_INLINEIMAGE, CMD_INDEX,
+ CMD_KEYWORD, CMD_L, CMD_LEGALESE, CMD_LINK, CMD_LIST,
+ CMD_META, CMD_NEWCODE, CMD_O, CMD_OLDCODE, CMD_OMIT,
+ CMD_OMITVALUE, CMD_OVERLOAD,
+ CMD_PART, CMD_PRINTLINE, CMD_PRINTTO,
+ CMD_PRINTUNTIL, CMD_QUOTATION, CMD_QUOTEFILE,
+ CMD_QUOTEFROMFILE, CMD_QUOTEFUNCTION, CMD_RAW, CMD_ROW,
+ CMD_SA, CMD_SECTION1, CMD_SECTION2, CMD_SECTION3,
+ CMD_SECTION4, CMD_SIDEBAR, CMD_SKIPLINE, CMD_SKIPTO,
+ CMD_SKIPUNTIL, CMD_SNIPPET, CMD_SUB, CMD_SUP, CMD_TABLE,
+ CMD_TABLEOFCONTENTS, CMD_TARGET, CMD_TT, CMD_UNDERLINE,
+ CMD_UNICODE, CMD_VALUE, CMD_WARNING,
+#ifdef QDOC_QML
+ CMD_QML, CMD_ENDQML, CMD_CPP, CMD_ENDCPP, CMD_QMLTEXT,
+ CMD_ENDQMLTEXT, CMD_CPPTEXT, CMD_ENDCPPTEXT,
+#endif
+ NOT_A_CMD
+};
+
+static struct {
+ const char *english;
+ int no;
+ QString *alias;
+} cmds[] = {
+ { "a", CMD_A, 0 },
+ { "abstract", CMD_ABSTRACT, 0 },
+ { "badcode", CMD_BADCODE, 0 },
+ { "basename", CMD_BASENAME, 0 }, // ### don't document for now
+ { "bold", CMD_BOLD, 0 },
+ { "brief", CMD_BRIEF, 0 },
+ { "c", CMD_C, 0 },
+ { "caption", CMD_CAPTION, 0 },
+ { "chapter", CMD_CHAPTER, 0 },
+ { "code", CMD_CODE, 0 },
+ { "codeline", CMD_CODELINE, 0},
+ { "dots", CMD_DOTS, 0 },
+ { "else", CMD_ELSE, 0 },
+ { "endabstract", CMD_ENDABSTRACT, 0 },
+ { "endchapter", CMD_ENDCHAPTER, 0 },
+ { "endcode", CMD_ENDCODE, 0 },
+ { "endfootnote", CMD_ENDFOOTNOTE, 0 },
+ { "endif", CMD_ENDIF, 0 },
+ { "endlegalese", CMD_ENDLEGALESE, 0 },
+ { "endlink", CMD_ENDLINK, 0 },
+ { "endlist", CMD_ENDLIST, 0 },
+ { "endomit", CMD_ENDOMIT, 0 },
+ { "endpart", CMD_ENDPART, 0 },
+ { "endquotation", CMD_ENDQUOTATION, 0 },
+ { "endraw", CMD_ENDRAW, 0 },
+ { "endsection1", CMD_ENDSECTION1, 0 }, // ### don't document for now
+ { "endsection2", CMD_ENDSECTION2, 0 }, // ### don't document for now
+ { "endsection3", CMD_ENDSECTION3, 0 }, // ### don't document for now
+ { "endsection4", CMD_ENDSECTION4, 0 }, // ### don't document for now
+ { "endsidebar", CMD_ENDSIDEBAR, 0 },
+ { "endtable", CMD_ENDTABLE, 0 },
+ { "expire", CMD_EXPIRE, 0 },
+ { "footnote", CMD_FOOTNOTE, 0 },
+ { "generatelist", CMD_GENERATELIST, 0 },
+ { "granularity", CMD_GRANULARITY, 0 }, // ### don't document for now
+ { "header", CMD_HEADER, 0 },
+ { "i", CMD_I, 0 },
+ { "if", CMD_IF, 0 },
+ { "image", CMD_IMAGE, 0 },
+ { "include", CMD_INCLUDE, 0 },
+ { "inlineimage", CMD_INLINEIMAGE, 0 },
+ { "index", CMD_INDEX, 0 }, // ### don't document for now
+ { "keyword", CMD_KEYWORD, 0 },
+ { "l", CMD_L, 0 },
+ { "legalese", CMD_LEGALESE, 0 },
+ { "link", CMD_LINK, 0 },
+ { "list", CMD_LIST, 0 },
+ { "meta", CMD_META, 0 },
+ { "newcode", CMD_NEWCODE, 0 },
+ { "o", CMD_O, 0 },
+ { "oldcode", CMD_OLDCODE, 0 },
+ { "omit", CMD_OMIT, 0 },
+ { "omitvalue", CMD_OMITVALUE, 0 },
+ { "overload", CMD_OVERLOAD, 0 },
+ { "part", CMD_PART, 0 },
+ { "printline", CMD_PRINTLINE, 0 },
+ { "printto", CMD_PRINTTO, 0 },
+ { "printuntil", CMD_PRINTUNTIL, 0 },
+ { "quotation", CMD_QUOTATION, 0 },
+ { "quotefile", CMD_QUOTEFILE, 0 },
+ { "quotefromfile", CMD_QUOTEFROMFILE, 0 },
+ { "quotefunction", CMD_QUOTEFUNCTION, 0 }, // ### don't document for now
+ { "raw", CMD_RAW, 0 },
+ { "row", CMD_ROW, 0 },
+ { "sa", CMD_SA, 0 },
+ { "section1", CMD_SECTION1, 0 },
+ { "section2", CMD_SECTION2, 0 },
+ { "section3", CMD_SECTION3, 0 },
+ { "section4", CMD_SECTION4, 0 },
+ { "sidebar", CMD_SIDEBAR, 0 }, // ### don't document for now
+ { "skipline", CMD_SKIPLINE, 0 },
+ { "skipto", CMD_SKIPTO, 0 },
+ { "skipuntil", CMD_SKIPUNTIL, 0 },
+ { "snippet", CMD_SNIPPET, 0 },
+ { "sub", CMD_SUB, 0 },
+ { "sup", CMD_SUP, 0 },
+ { "table", CMD_TABLE, 0 },
+ { "tableofcontents", CMD_TABLEOFCONTENTS, 0 },
+ { "target", CMD_TARGET, 0 },
+ { "tt", CMD_TT, 0 },
+ { "underline", CMD_UNDERLINE, 0 },
+ { "unicode", CMD_UNICODE, 0 },
+ { "value", CMD_VALUE, 0 },
+ { "warning", CMD_WARNING, 0 },
+#ifdef QDOC_QML
+ { "qml", CMD_QML, 0 },
+ { "endqml", CMD_ENDQML, 0 },
+ { "cpp", CMD_CPP, 0 },
+ { "endcpp", CMD_ENDCPP, 0 },
+ { "qmltext", CMD_QMLTEXT, 0 },
+ { "endqmltext", CMD_ENDQMLTEXT, 0 },
+ { "cpptext", CMD_CPPTEXT, 0 },
+ { "endcpptext", CMD_ENDCPPTEXT, 0 },
+#endif
+ { 0, 0, 0 }
+};
+
+typedef QHash<QString, int> QHash_QString_int;
+typedef QHash<QString, Macro> QHash_QString_Macro;
+
+Q_GLOBAL_STATIC(QStringMap, aliasMap)
+Q_GLOBAL_STATIC(QHash_QString_int, cmdHash)
+Q_GLOBAL_STATIC(QHash_QString_Macro, macroHash)
+
+class DocPrivateExtra
+{
+ public:
+ QString baseName;
+ Doc::SectioningUnit granularity;
+ Doc::SectioningUnit sectioningUnit; // ###
+ QList<Atom*> tableOfContents;
+ QList<int> tableOfContentsLevels;
+ QList<Atom*> keywords;
+ QList<Atom*> targets;
+ QStringMap metaMap;
+
+ DocPrivateExtra()
+ : granularity(Doc::Part) { }
+};
+
+struct Shared // ### get rid of
+{
+ Shared()
+ : count(1) { }
+ void ref() { ++count; }
+ bool deref() { return (--count == 0); }
+
+ int count;
+};
+
+static QString cleanLink(const QString &link)
+{
+ int colonPos = link.indexOf(':');
+ if ((colonPos == -1) ||
+ (!link.startsWith("file:") && !link.startsWith("mailto:")))
+ return link;
+ return link.mid(colonPos + 1).simplified();
+}
+
+class DocPrivate : public Shared
+{
+ public:
+ DocPrivate(const Location& start = Location::null,
+ const Location& end = Location::null,
+ const QString& source = "");
+ ~DocPrivate();
+
+ void addAlso(const Text& also);
+ void constructExtra();
+ bool isEnumDocSimplifiable() const;
+
+ // ### move some of this in DocPrivateExtra
+ Location start_loc;
+ Location end_loc;
+ QString src;
+ Text text;
+ QSet<QString> params;
+ QList<Text> alsoList;
+ QStringList enumItemList;
+ QStringList omitEnumItemList;
+ QSet<QString> metacommandsUsed;
+ QCommandMap metaCommandMap;
+ bool hasLegalese : 1;
+ bool hasSectioningUnits : 1;
+ DocPrivateExtra *extra;
+};
+
+DocPrivate::DocPrivate(const Location& start,
+ const Location& end,
+ const QString& source)
+ : start_loc(start),
+ end_loc(end),
+ src(source),
+ hasLegalese(false),
+ hasSectioningUnits(false),
+ extra(0)
+{
+ // nothing.
+}
+
+DocPrivate::~DocPrivate()
+{
+ delete extra;
+}
+
+void DocPrivate::addAlso(const Text& also)
+{
+ alsoList.append(also);
+}
+
+void DocPrivate::constructExtra()
+{
+ if (extra == 0)
+ extra = new DocPrivateExtra;
+}
+
+bool DocPrivate::isEnumDocSimplifiable() const
+{
+ bool justMetColon = false;
+ int numValueTables = 0;
+
+ const Atom *atom = text.firstAtom();
+ while (atom) {
+ if (atom->type() == Atom::AutoLink || atom->type() == Atom::String) {
+ justMetColon = atom->string().endsWith(":");
+ }
+ else if ((atom->type() == Atom::ListLeft) &&
+ (atom->string() == ATOM_LIST_VALUE)) {
+ if (justMetColon || numValueTables > 0)
+ return false;
+ ++numValueTables;
+ }
+ atom = atom->next();
+ }
+ return true;
+}
+
+class DocParser
+{
+ public:
+ void parse(const QString &source,
+ DocPrivate *docPrivate,
+ const QSet<QString> &metaCommandSet);
+
+ static int endCmdFor(int cmd);
+ static QString cmdName(int cmd);
+ static QString endCmdName(int cmd);
+ static QString untabifyEtc(const QString& str);
+ static int indentLevel(const QString& str);
+ static QString unindent(int level, const QString& str);
+ static QString slashed(const QString& str);
+
+ static int tabSize;
+ static QStringList exampleFiles;
+ static QStringList exampleDirs;
+ static QStringList sourceFiles;
+ static QStringList sourceDirs;
+ static bool quoting;
+
+ private:
+ Location& location();
+ QString detailsUnknownCommand(const QSet<QString>& metaCommandSet,
+ const QString& str);
+ void checkExpiry(const QString& date);
+ void insertBaseName(const QString &baseName);
+ void insertTarget(const QString& target, bool keyword);
+ void include(const QString& fileName);
+ void startFormat(const QString& format, int cmd);
+ bool openCommand(int cmd);
+ bool closeCommand(int endCmd);
+ void startSection(Doc::SectioningUnit unit, int cmd);
+ void endSection(int unit, int endCmd);
+ void parseAlso();
+ void append(Atom::Type type, const QString& string = "");
+ void appendChar(QChar ch);
+ void appendWord(const QString &word);
+ void appendToCode(const QString &code);
+ void startNewPara();
+ void enterPara(Atom::Type leftType = Atom::ParaLeft,
+ Atom::Type rightType = Atom::ParaRight,
+ const QString& string = "");
+ void leavePara();
+ void leaveValue();
+ void leaveValueList();
+ void leaveTableRow();
+ CodeMarker *quoteFromFile();
+ void expandMacro(const QString& name, const QString& def, int numParams);
+ Doc::SectioningUnit getSectioningUnit();
+ QString getArgument(bool verbatim = false);
+ QString getOptionalArgument();
+ QString getRestOfLine();
+ QString getMetaCommandArgument(const QString &cmdStr);
+ QString getUntilEnd(int cmd);
+ QString getCode(int cmd, CodeMarker *marker);
+ QString getUnmarkedCode(int cmd);
+
+ bool isBlankLine();
+ bool isLeftBraceAhead();
+ void skipSpacesOnLine();
+ void skipSpacesOrOneEndl();
+ void skipAllSpaces();
+ void skipToNextPreprocessorCommand();
+
+ QStack<int> openedInputs;
+
+ QString in;
+ int pos;
+ int len;
+ Location cachedLoc;
+ int cachedPos;
+
+ DocPrivate *priv;
+ enum ParaState { OutsidePara, InsideSingleLinePara, InsideMultiLinePara };
+ ParaState paraState;
+ bool inTableHeader;
+ bool inTableRow;
+ bool inTableItem;
+ bool indexStartedPara; // ### rename
+ Atom::Type pendingParaLeftType;
+ Atom::Type pendingParaRightType;
+ QString pendingParaString;
+
+ int braceDepth;
+ int minIndent;
+ Doc::SectioningUnit currentSectioningUnit;
+ QMap<QString, Location> targetMap;
+ QMap<int, QString> pendingFormats;
+ QStack<int> openedCommands;
+ QStack<OpenedList> openedLists;
+ Quoter quoter;
+};
+
+int DocParser::tabSize;
+QStringList DocParser::exampleFiles;
+QStringList DocParser::exampleDirs;
+QStringList DocParser::sourceFiles;
+QStringList DocParser::sourceDirs;
+bool DocParser::quoting;
+
+/*!
+ Parse the \a source string to build a Text data structure
+ in \a docPrivate. The Text data structure is a linked list
+ of Atoms.
+
+ \a metaCommandSet is the set of metacommands that may be
+ found in \a source. These metacommands are not markup text
+ commands. They are topic commands and related metacommands.
+ */
+void DocParser::parse(const QString& source,
+ DocPrivate *docPrivate,
+ const QSet<QString>& metaCommandSet)
+{
+ in = source;
+ pos = 0;
+ len = in.length();
+ cachedLoc = docPrivate->start_loc;
+ cachedPos = 0;
+ priv = docPrivate;
+ priv->text << Atom::Nop;
+
+ paraState = OutsidePara;
+ inTableHeader = false;
+ inTableRow = false;
+ inTableItem = false;
+ indexStartedPara = false;
+ pendingParaLeftType = Atom::Nop;
+ pendingParaRightType = Atom::Nop;
+
+ braceDepth = 0;
+ minIndent = INT_MAX;
+ currentSectioningUnit = Doc::Book;
+ openedCommands.push(CMD_OMIT);
+ quoter.reset();
+
+ CodeMarker *marker = 0;
+ Atom *currentLinkAtom = 0;
+ QString x;
+ QStack<bool> preprocessorSkipping;
+ int numPreprocessorSkipping = 0;
+
+ while (pos < len) {
+ QChar ch = in.at(pos);
+
+ switch (ch.unicode()) {
+ case '\\':
+ {
+ QString cmdStr;
+ pos++;
+ while (pos < len) {
+ ch = in.at(pos);
+ if (ch.isLetterOrNumber()) {
+ cmdStr += ch;
+ pos++;
+ }
+ else {
+ break;
+ }
+ }
+ if (cmdStr.isEmpty()) {
+ if (pos < len) {
+ enterPara();
+ if (in.at(pos).isSpace()) {
+ skipAllSpaces();
+ appendChar(QLatin1Char(' '));
+ }
+ else {
+ appendChar(in.at(pos++));
+ }
+ }
+ }
+ else {
+ int cmd = cmdHash()->value(cmdStr,NOT_A_CMD);
+ switch (cmd) {
+ case CMD_A:
+ enterPara();
+ x = getArgument();
+ append(Atom::FormattingLeft,ATOM_FORMATTING_PARAMETER);
+ append(Atom::String, x);
+ append(Atom::FormattingRight,ATOM_FORMATTING_PARAMETER);
+ priv->params.insert(x);
+ break;
+ case CMD_ABSTRACT:
+ if (openCommand(cmd)) {
+ leavePara();
+ append(Atom::AbstractLeft);
+ }
+ break;
+ case CMD_BADCODE:
+ leavePara();
+#ifdef QDOC2DOX
+ if (DoxWriter::isDoxPass())
+ append(Atom::CodeBad,getUnmarkedCode(CMD_BADCODE));
+ else
+ append(Atom::CodeBad,getCode(CMD_BADCODE, marker));
+#else
+ append(Atom::CodeBad,getCode(CMD_BADCODE, marker));
+#endif
+ break;
+ case CMD_BASENAME:
+ leavePara();
+ insertBaseName(getArgument());
+ break;
+ case CMD_BOLD:
+ startFormat(ATOM_FORMATTING_BOLD, cmd);
+ break;
+ case CMD_BRIEF:
+ leavePara();
+ enterPara(Atom::BriefLeft, Atom::BriefRight);
+ break;
+ case CMD_C:
+ enterPara();
+ x = untabifyEtc(getArgument(true));
+#ifdef QDOC2DOX
+ if (DoxWriter::isDoxPass())
+ append(Atom::C, x);
+ else {
+ marker = CodeMarker::markerForCode(x);
+ append(Atom::C, marker->markedUpCode(x, 0, ""));
+ }
+#else
+ marker = CodeMarker::markerForCode(x);
+ append(Atom::C, marker->markedUpCode(x, 0, ""));
+#endif
+ break;
+ case CMD_CAPTION:
+ leavePara();
+ /* ... */
+ break;
+ case CMD_CHAPTER:
+ startSection(Doc::Chapter, cmd);
+ break;
+ case CMD_CODE:
+ leavePara();
+#ifdef QDOC2DOX
+ if (DoxWriter::isDoxPass())
+ append(Atom::Code, getUnmarkedCode(CMD_CODE));
+ else
+ append(Atom::Code, getCode(CMD_CODE, marker));
+#else
+ append(Atom::Code, getCode(CMD_CODE, marker));
+#endif
+ break;
+#ifdef QDOC_QML
+ case CMD_QML:
+ leavePara();
+ append(Atom::Qml, getCode(CMD_QML, marker));
+ break;
+ case CMD_QMLTEXT:
+ append(Atom::QmlText);
+ break;
+#endif
+ case CMD_CODELINE:
+ {
+#ifdef QDOC2DOX
+ if (!quoting && !DoxWriter::isDoxPass()) {
+ if (priv->text.lastAtom()->type() == Atom::Code
+ && priv->text.lastAtom()->string().endsWith("\n\n"))
+ priv->text.lastAtom()->chopString();
+ appendToCode("\n");
+ } lse {
+ append(Atom::CodeQuoteCommand, cmdStr);
+ append(Atom::CodeQuoteArgument, " ");
+ }
+#else
+ if (!quoting) {
+ if (priv->text.lastAtom()->type() == Atom::Code
+ && priv->text.lastAtom()->string().endsWith("\n\n"))
+ priv->text.lastAtom()->chopString();
+ appendToCode("\n");
+ }
+ else {
+ append(Atom::CodeQuoteCommand, cmdStr);
+ append(Atom::CodeQuoteArgument, " ");
+ }
+#endif
+ }
+ break;
+ case CMD_DOTS:
+ {
+#ifdef QDOC2DOX
+ if (DoxWriter::isDoxPass()) {
+ append(Atom::CodeQuoteCommand, cmdStr);
+ append(Atom::CodeQuoteArgument, " ...");
+ }
+ else if (!quoting) {
+ if (priv->text.lastAtom()->type() == Atom::Code
+ && priv->text.lastAtom()->string().endsWith("\n\n"))
+ priv->text.lastAtom()->chopString();
+
+ QString arg = getOptionalArgument();
+ int indent = 4;
+ if (!arg.isEmpty())
+ indent = arg.toInt();
+ for (int i = 0; i < indent; ++i)
+ appendToCode(" ");
+ appendToCode("...\n");
+ }
+ else {
+ append(Atom::CodeQuoteCommand, cmdStr);
+ QString arg = getOptionalArgument();
+ if (arg.isEmpty())
+ arg = "4";
+ append(Atom::CodeQuoteArgument, arg);
+ }
+#else
+ if (!quoting) {
+ if (priv->text.lastAtom()->type() == Atom::Code
+ && priv->text.lastAtom()->string().endsWith("\n\n"))
+ priv->text.lastAtom()->chopString();
+
+ QString arg = getOptionalArgument();
+ int indent = 4;
+ if (!arg.isEmpty())
+ indent = arg.toInt();
+ for (int i = 0; i < indent; ++i)
+ appendToCode(" ");
+ appendToCode("...\n");
+ }
+ else {
+ append(Atom::CodeQuoteCommand, cmdStr);
+ QString arg = getOptionalArgument();
+ if (arg.isEmpty())
+ arg = "4";
+ append(Atom::CodeQuoteArgument, arg);
+ }
+#endif
+ }
+ break;
+ case CMD_ELSE:
+ if (preprocessorSkipping.size() > 0) {
+ if (preprocessorSkipping.top()) {
+ --numPreprocessorSkipping;
+ }
+ else {
+ ++numPreprocessorSkipping;
+ }
+ preprocessorSkipping.top() = !preprocessorSkipping.top();
+ (void)getRestOfLine(); // ### should ensure that it's empty
+ if (numPreprocessorSkipping)
+ skipToNextPreprocessorCommand();
+ }
+ else {
+ location().warning(tr("Unexpected '\\%1'").arg(cmdName(CMD_ELSE)));
+ }
+ break;
+ case CMD_ENDABSTRACT:
+ if (closeCommand(cmd)) {
+ leavePara();
+ append(Atom::AbstractRight);
+ }
+ break;
+ case CMD_ENDCHAPTER:
+ endSection(0, cmd);
+ break;
+ case CMD_ENDCODE:
+ closeCommand(cmd);
+ break;
+#ifdef QDOC_QML
+ case CMD_ENDQML:
+ closeCommand(cmd);
+ break;
+ case CMD_ENDQMLTEXT:
+ append(Atom::EndQmlText);
+ break;
+#endif
+ case CMD_ENDFOOTNOTE:
+ if (closeCommand(cmd)) {
+ leavePara();
+ append(Atom::FootnoteRight);
+ paraState = InsideMultiLinePara; // ###
+ }
+ break;
+ case CMD_ENDIF:
+ if (preprocessorSkipping.count() > 0) {
+ if (preprocessorSkipping.pop())
+ --numPreprocessorSkipping;
+ (void)getRestOfLine(); // ### should ensure that it's empty
+ if (numPreprocessorSkipping)
+ skipToNextPreprocessorCommand();
+ }
+ else {
+ location().warning(tr("Unexpected '\\%1'").arg(cmdName(CMD_ENDIF)));
+ }
+ break;
+ case CMD_ENDLEGALESE:
+ if (closeCommand(cmd)) {
+ leavePara();
+ append(Atom::LegaleseRight);
+ }
+ break;
+ case CMD_ENDLINK:
+ if (closeCommand(cmd)) {
+ if (priv->text.lastAtom()->type() == Atom::String
+ && priv->text.lastAtom()->string().endsWith(" "))
+ priv->text.lastAtom()->chopString();
+ append(Atom::FormattingRight, ATOM_FORMATTING_LINK);
+ }
+ break;
+ case CMD_ENDLIST:
+ if (closeCommand(cmd)) {
+ leavePara();
+ if (openedLists.top().isStarted()) {
+ append(Atom::ListItemRight,
+ openedLists.top().styleString());
+ append(Atom::ListRight,
+ openedLists.top().styleString());
+ }
+ openedLists.pop();
+ }
+ break;
+ case CMD_ENDOMIT:
+ closeCommand(cmd);
+ break;
+ case CMD_ENDPART:
+ endSection(-1, cmd);
+ break;
+ case CMD_ENDQUOTATION:
+ if (closeCommand(cmd)) {
+ leavePara();
+ append(Atom::QuotationRight);
+ }
+ break;
+ case CMD_ENDRAW:
+ location().warning(tr("Unexpected '\\%1'").arg(cmdName(CMD_ENDRAW)));
+ break;
+ case CMD_ENDSECTION1:
+ endSection(1, cmd);
+ break;
+ case CMD_ENDSECTION2:
+ endSection(2, cmd);
+ break;
+ case CMD_ENDSECTION3:
+ endSection(3, cmd);
+ break;
+ case CMD_ENDSECTION4:
+ endSection(4, cmd);
+ break;
+ case CMD_ENDSIDEBAR:
+ if (closeCommand(cmd)) {
+ leavePara();
+ append(Atom::SidebarRight);
+ }
+ break;
+ case CMD_ENDTABLE:
+ if (closeCommand(cmd)) {
+ leaveTableRow();
+ append(Atom::TableRight);
+ }
+ break;
+ case CMD_EXPIRE:
+ checkExpiry(getArgument());
+ break;
+ case CMD_FOOTNOTE:
+ if (openCommand(cmd)) {
+ enterPara();
+ append(Atom::FootnoteLeft);
+ paraState = OutsidePara; // ###
+ }
+ break;
+ case CMD_GENERATELIST:
+ append(Atom::GeneratedList, getArgument());
+ break;
+ case CMD_GRANULARITY:
+ priv->constructExtra();
+ priv->extra->granularity = getSectioningUnit();
+ break;
+ case CMD_HEADER:
+ if (openedCommands.top() == CMD_TABLE) {
+ leaveTableRow();
+ append(Atom::TableHeaderLeft);
+ inTableHeader = true;
+ }
+ else {
+ if (openedCommands.contains(CMD_TABLE)) {
+ location().warning(tr("Cannot use '\\%1' within '\\%2'")
+ .arg(cmdName(CMD_HEADER))
+ .arg(cmdName(openedCommands.top())));
+ }
+ else {
+ location().warning(tr("Cannot use '\\%1' outside of '\\%2'")
+ .arg(cmdName(CMD_HEADER))
+ .arg(cmdName(CMD_TABLE)));
+ }
+ }
+ break;
+ case CMD_I:
+ startFormat(ATOM_FORMATTING_ITALIC, cmd);
+ break;
+ case CMD_IF:
+ preprocessorSkipping.push(!Tokenizer::isTrue(getRestOfLine()));
+ if (preprocessorSkipping.top())
+ ++numPreprocessorSkipping;
+ if (numPreprocessorSkipping)
+ skipToNextPreprocessorCommand();
+ break;
+ case CMD_IMAGE:
+ leaveValueList();
+ append(Atom::Image, getArgument());
+ append(Atom::ImageText, getRestOfLine());
+ break;
+ case CMD_INCLUDE:
+ include(getArgument());
+ break;
+ case CMD_INLINEIMAGE:
+ enterPara();
+ append(Atom::InlineImage, getArgument());
+ append(Atom::ImageText, getRestOfLine());
+ append(Atom::String, " ");
+ break;
+ case CMD_INDEX:
+ if (paraState == OutsidePara) {
+ enterPara();
+ indexStartedPara = true;
+ }
+ else {
+ const Atom *last = priv->text.lastAtom();
+ if (indexStartedPara &&
+ (last->type() != Atom::FormattingRight ||
+ last->string() != ATOM_FORMATTING_INDEX))
+ indexStartedPara = false;
+ }
+ startFormat(ATOM_FORMATTING_INDEX, cmd);
+ break;
+ case CMD_KEYWORD:
+ insertTarget(getRestOfLine(),true);
+ break;
+ case CMD_L:
+ enterPara();
+ if (isLeftBraceAhead()) {
+ x = getArgument();
+ append(Atom::Link, x);
+ if (isLeftBraceAhead()) {
+ currentLinkAtom = priv->text.lastAtom();
+ startFormat(ATOM_FORMATTING_LINK, cmd);
+ }
+ else {
+ append(Atom::FormattingLeft, ATOM_FORMATTING_LINK);
+ append(Atom::String, cleanLink(x));
+ append(Atom::FormattingRight, ATOM_FORMATTING_LINK);
+ }
+ }
+ else {
+ x = getArgument();
+ append(Atom::Link, x);
+ append(Atom::FormattingLeft, ATOM_FORMATTING_LINK);
+ append(Atom::String, cleanLink(x));
+ append(Atom::FormattingRight, ATOM_FORMATTING_LINK);
+ }
+ break;
+ case CMD_LEGALESE:
+ leavePara();
+ if (openCommand(cmd))
+ append(Atom::LegaleseLeft);
+ docPrivate->hasLegalese = true;
+ break;
+ case CMD_LINK:
+ if (openCommand(cmd)) {
+ enterPara();
+ x = getArgument();
+ append(Atom::Link, x);
+ append(Atom::FormattingLeft, ATOM_FORMATTING_LINK);
+ skipSpacesOrOneEndl();
+ }
+ break;
+ case CMD_LIST:
+ if (openCommand(cmd)) {
+ leavePara();
+ openedLists.push(OpenedList(location(),
+ getOptionalArgument()));
+ }
+ break;
+ case CMD_META:
+ priv->constructExtra();
+ x = getArgument();
+ priv->extra->metaMap.insert(x, getRestOfLine());
+ break;
+ case CMD_NEWCODE:
+ location().warning(tr("Unexpected '\\%1'").arg(cmdName(CMD_NEWCODE)));
+ break;
+ case CMD_O:
+ leavePara();
+ if (openedCommands.top() == CMD_LIST) {
+ if (openedLists.top().isStarted()) {
+ append(Atom::ListItemRight,
+ openedLists.top().styleString());
+ }
+ else {
+ append(Atom::ListLeft,
+ openedLists.top().styleString());
+ }
+ openedLists.top().next();
+ append(Atom::ListItemNumber,
+ openedLists.top().numberString());
+ append(Atom::ListItemLeft,
+ openedLists.top().styleString());
+ enterPara();
+ }
+ else if (openedCommands.top() == CMD_TABLE) {
+ x = "1,1";
+ if (isLeftBraceAhead())
+ x = getArgument();
+
+ if (!inTableHeader && !inTableRow) {
+ location().warning(tr("Missing '\\%1' or '\\%1' before '\\%3'")
+ .arg(cmdName(CMD_HEADER))
+ .arg(cmdName(CMD_ROW))
+ .arg(cmdName(CMD_O)));
+ append(Atom::TableRowLeft);
+ inTableRow = true;
+ }
+ else if (inTableItem) {
+ append(Atom::TableItemRight);
+ inTableItem = false;
+ }
+
+ append(Atom::TableItemLeft, x);
+ inTableItem = true;
+ }
+ else {
+ location().warning(tr("Command '\\%1' outside of '\\%2' and '\\%3'")
+ .arg(cmdName(cmd))
+ .arg(cmdName(CMD_LIST))
+ .arg(cmdName(CMD_TABLE)));
+ }
+ break;
+ case CMD_OLDCODE:
+ leavePara();
+#ifdef QDOC2DOX
+ if (DoxWriter::isDoxPass()) {
+ append(Atom::CodeOld, getUnmarkedCode(CMD_OLDCODE));
+ append(Atom::CodeNew, getUnmarkedCode(CMD_NEWCODE));
+ }
+ else {
+ append(Atom::CodeOld, getCode(CMD_OLDCODE, marker));
+ append(Atom::CodeNew, getCode(CMD_NEWCODE, marker));
+ }
+#else
+ append(Atom::CodeOld, getCode(CMD_OLDCODE, marker));
+ append(Atom::CodeNew, getCode(CMD_NEWCODE, marker));
+#endif
+ break;
+ case CMD_OMIT:
+ getUntilEnd(cmd);
+ break;
+ case CMD_OMITVALUE:
+ x = getArgument();
+ if (!priv->enumItemList.contains(x))
+ priv->enumItemList.append(x);
+ if (!priv->omitEnumItemList.contains(x))
+ priv->omitEnumItemList.append(x);
+ break;
+ case CMD_PART:
+ startSection(Doc::Part, cmd);
+ break;
+ case CMD_PRINTLINE:
+ leavePara();
+ if (!quoting)
+ appendToCode(quoter.quoteLine(location(), cmdStr,
+ getRestOfLine()));
+ else {
+ append(Atom::CodeQuoteCommand, cmdStr);
+ append(Atom::CodeQuoteArgument, getRestOfLine());
+ }
+ break;
+ case CMD_PRINTTO:
+ leavePara();
+ if (!quoting)
+ appendToCode(quoter.quoteTo(location(), cmdStr,
+ getRestOfLine()));
+ else {
+ append(Atom::CodeQuoteCommand, cmdStr);
+ append(Atom::CodeQuoteArgument, getRestOfLine());
+ }
+ break;
+ case CMD_PRINTUNTIL:
+ leavePara();
+ if (!quoting)
+ appendToCode(quoter.quoteUntil(location(), cmdStr,
+ getRestOfLine()));
+ else {
+ append(Atom::CodeQuoteCommand, cmdStr);
+ append(Atom::CodeQuoteArgument, getRestOfLine());
+ }
+ break;
+ case CMD_QUOTATION:
+ if (openCommand(cmd)) {
+ leavePara();
+ append(Atom::QuotationLeft);
+ }
+ break;
+ case CMD_QUOTEFILE:
+ {
+ leavePara();
+ QString fileName = getArgument();
+ Doc::quoteFromFile(location(), quoter, fileName);
+ if (!quoting) {
+ append(Atom::Code,
+ quoter.quoteTo(location(), cmdStr, ""));
+ quoter.reset();
+ }
+ else {
+ append(Atom::CodeQuoteCommand, cmdStr);
+ append(Atom::CodeQuoteArgument, fileName);
+ }
+ break;
+ }
+ case CMD_QUOTEFROMFILE:
+ leavePara();
+ if (!quoting)
+ quoteFromFile();
+ else {
+ append(Atom::CodeQuoteCommand, cmdStr);
+ append(Atom::CodeQuoteArgument, getArgument());
+ }
+ break;
+ case CMD_QUOTEFUNCTION:
+ leavePara();
+ marker = quoteFromFile();
+ x = getRestOfLine();
+ if (!quoting) {
+ quoter.quoteTo(location(), cmdStr,
+ slashed(marker->functionBeginRegExp(x)));
+ append(Atom::Code,
+ quoter.quoteUntil(location(), cmdStr,
+ slashed(marker->functionEndRegExp(x))));
+ quoter.reset();
+ }
+ else {
+ append(Atom::CodeQuoteCommand, cmdStr);
+ append(Atom::CodeQuoteArgument, slashed(marker->functionEndRegExp(x)));
+ }
+ break;
+ case CMD_RAW:
+ leavePara();
+ x = getRestOfLine();
+ if (x.isEmpty())
+ location().warning(tr("Missing format name after '\\%1")
+ .arg(cmdName(CMD_RAW)));
+ append(Atom::FormatIf, x);
+ append(Atom::RawString, untabifyEtc(getUntilEnd(cmd)));
+ append(Atom::FormatElse);
+ append(Atom::FormatEndif);
+ break;
+ case CMD_ROW:
+ if (openedCommands.top() == CMD_TABLE) {
+ leaveTableRow();
+ append(Atom::TableRowLeft);
+ inTableRow = true;
+ }
+ else {
+ if (openedCommands.contains(CMD_TABLE)) {
+ location().warning(tr("Cannot use '\\%1' within '\\%2'")
+ .arg(cmdName(CMD_ROW))
+ .arg(cmdName(openedCommands.top())));
+ }
+ else {
+ location().warning(tr("Cannot use '\\%1' outside of '\\%2'")
+ .arg(cmdName(CMD_ROW))
+ .arg(cmdName(CMD_TABLE)));
+ }
+ }
+ break;
+ case CMD_SA:
+ parseAlso();
+ break;
+ case CMD_SECTION1:
+ startSection(Doc::Section1, cmd);
+ break;
+ case CMD_SECTION2:
+ startSection(Doc::Section2, cmd);
+ break;
+ case CMD_SECTION3:
+ startSection(Doc::Section3, cmd);
+ break;
+ case CMD_SECTION4:
+ startSection(Doc::Section4, cmd);
+ break;
+ case CMD_SIDEBAR:
+ if (openCommand(cmd)) {
+ leavePara();
+ append(Atom::SidebarLeft);
+ }
+ break;
+ case CMD_SKIPLINE:
+ leavePara();
+ if (!quoting)
+ quoter.quoteLine(location(),
+ cmdStr,
+ getRestOfLine());
+ else {
+ append(Atom::CodeQuoteCommand, cmdStr);
+ append(Atom::CodeQuoteArgument, getRestOfLine());
+ }
+ break;
+ case CMD_SKIPTO:
+ leavePara();
+ if (!quoting)
+ quoter.quoteTo(location(),
+ cmdStr,
+ getRestOfLine());
+ else {
+ append(Atom::CodeQuoteCommand, cmdStr);
+ append(Atom::CodeQuoteArgument, getRestOfLine());
+ }
+ break;
+ case CMD_SKIPUNTIL:
+ leavePara();
+ if (!quoting)
+ quoter.quoteUntil(location(),
+ cmdStr,
+ getRestOfLine());
+ else {
+ append(Atom::CodeQuoteCommand, cmdStr);
+ append(Atom::CodeQuoteArgument, getRestOfLine());
+ }
+ break;
+ case CMD_SNIPPET:
+ leavePara();
+ {
+ QString snippet = getArgument();
+ QString identifier = getRestOfLine();
+#ifdef QDOC2DOX
+ if (quoting || DoxWriter::isDoxPass()) {
+ append(Atom::SnippetCommand, cmdStr);
+ append(Atom::SnippetLocation, snippet);
+ append(Atom::SnippetIdentifier, identifier);
+ }
+ else {
+ Doc::quoteFromFile(location(),quoter,snippet);
+ appendToCode(quoter.quoteSnippet(location(),
+ identifier));
+ }
+#else
+ if (quoting) {
+ append(Atom::SnippetCommand, cmdStr);
+ append(Atom::SnippetLocation, snippet);
+ append(Atom::SnippetIdentifier, identifier);
+ }
+ else {
+ Doc::quoteFromFile(location(),quoter,snippet);
+ appendToCode(quoter.quoteSnippet(location(),
+ identifier));
+ }
+#endif
+ }
+ break;
+ case CMD_SUB:
+ startFormat(ATOM_FORMATTING_SUBSCRIPT, cmd);
+ break;
+ case CMD_SUP:
+ startFormat(ATOM_FORMATTING_SUPERSCRIPT, cmd);
+ break;
+ case CMD_TABLE:
+ x = getRestOfLine();
+ if (openCommand(cmd)) {
+ leavePara();
+ append(Atom::TableLeft, x);
+ inTableHeader = false;
+ inTableRow = false;
+ inTableItem = false;
+ }
+ break;
+ case CMD_TABLEOFCONTENTS:
+ x = "1";
+ if (isLeftBraceAhead())
+ x = getArgument();
+ x += ",";
+ x += QString::number((int)getSectioningUnit());
+ append(Atom::TableOfContents, x);
+ break;
+ case CMD_TARGET:
+ insertTarget(getRestOfLine(),false);
+ break;
+ case CMD_TT:
+ startFormat(ATOM_FORMATTING_TELETYPE, cmd);
+ break;
+ case CMD_UNDERLINE:
+ startFormat(ATOM_FORMATTING_UNDERLINE, cmd);
+ break;
+ case CMD_UNICODE:
+ enterPara();
+ x = getArgument();
+ {
+ bool ok;
+ uint unicodeChar = x.toUInt(&ok, 0);
+ if (!ok ||
+ (unicodeChar == 0x0000) ||
+ (unicodeChar > 0xFFFE)) {
+ location().warning(tr("Invalid Unicode character '%1' specified "
+ "with '%2'")
+ .arg(x, cmdName(CMD_UNICODE)));
+ }
+ else {
+ append(Atom::String, QChar(unicodeChar));
+ }
+ }
+ break;
+ case CMD_VALUE:
+ leaveValue();
+ if (openedLists.top().style() == OpenedList::Value) {
+ x = getArgument();
+ if (!priv->enumItemList.contains(x))
+ priv->enumItemList.append(x);
+
+ openedLists.top().next();
+ append(Atom::ListTagLeft, ATOM_LIST_VALUE);
+ append(Atom::String, x);
+ append(Atom::ListTagRight, ATOM_LIST_VALUE);
+ append(Atom::ListItemLeft, ATOM_LIST_VALUE);
+
+ skipSpacesOrOneEndl();
+ if (isBlankLine())
+ append(Atom::Nop);
+ }
+ else {
+ // ### problems
+ }
+ break;
+ case CMD_WARNING:
+ startNewPara();
+ append(Atom::FormattingLeft, ATOM_FORMATTING_BOLD);
+ append(Atom::String, "Warning:");
+ append(Atom::FormattingRight, ATOM_FORMATTING_BOLD);
+ append(Atom::String, " ");
+ break;
+ case CMD_OVERLOAD: // qdoc --> doxygen
+ priv->metacommandsUsed.insert(cmdStr);
+ x.clear();
+ if (!isBlankLine())
+ x = getRestOfLine();
+ if (!x.isEmpty()) {
+ append(Atom::ParaLeft);
+ append(Atom::String, "This function overloads ");
+ append(Atom::AutoLink,x);
+ append(Atom::String, ".");
+ append(Atom::ParaRight);
+ }
+ else {
+ append(Atom::ParaLeft);
+ append(Atom::String,
+ "This is an overloaded member function, "
+ "provided for convenience.");
+ append(Atom::ParaRight);
+ x = getMetaCommandArgument(cmdStr);
+ }
+ priv->metaCommandMap[cmdStr].append(x);
+ break;
+ case NOT_A_CMD:
+ if (metaCommandSet.contains(cmdStr)) {
+ priv->metacommandsUsed.insert(cmdStr);
+ QString xxx = getMetaCommandArgument(cmdStr);
+ priv->metaCommandMap[cmdStr].append(xxx);
+ }
+ else if (macroHash()->contains(cmdStr)) {
+ const Macro &macro = macroHash()->value(cmdStr);
+ int numPendingFi = 0;
+ QStringMap::ConstIterator d;
+ d = macro.otherDefs.begin();
+ while (d != macro.otherDefs.end()) {
+ append(Atom::FormatIf, d.key());
+ expandMacro(cmdStr, *d, macro.numParams);
+ ++d;
+
+ if (d == macro.otherDefs.end()) {
+ append(Atom::FormatEndif);
+ }
+ else {
+ append(Atom::FormatElse);
+ numPendingFi++;
+ }
+ }
+ while (numPendingFi-- > 0)
+ append(Atom::FormatEndif);
+
+ if (!macro.defaultDef.isEmpty()) {
+ if (!macro.otherDefs.isEmpty()) {
+ macro.defaultDefLocation.warning(
+ tr("Macro cannot have both "
+ "format-specific and qdoc- "
+ "syntax definitions"));
+ }
+ else {
+ location().push(macro.defaultDefLocation.filePath());
+ in.insert(pos, macro.defaultDef);
+ len = in.length();
+ openedInputs.push(pos + macro.defaultDef.length());
+ }
+ }
+ }
+ else {
+ location().warning(
+ tr("Unknown command '\\%1'").arg(cmdStr),
+ detailsUnknownCommand(metaCommandSet,cmdStr));
+ enterPara();
+ append(Atom::UnknownCommand, cmdStr);
+ }
+ }
+ }
+ }
+ break;
+ case '{':
+ enterPara();
+ appendChar('{');
+ braceDepth++;
+ pos++;
+ break;
+ case '}':
+ {
+ braceDepth--;
+ pos++;
+
+ QMap<int, QString>::Iterator f =
+ pendingFormats.find(braceDepth);
+ if (f == pendingFormats.end()) {
+ enterPara();
+ appendChar('}');
+ }
+ else {
+ append(Atom::FormattingRight, *f);
+ if (*f == ATOM_FORMATTING_INDEX) {
+ if (indexStartedPara)
+ skipAllSpaces();
+ }
+ else if (*f == ATOM_FORMATTING_LINK) {
+ // hack for C++ to support links like
+ // \l{QString::}{count()}
+ if (currentLinkAtom &&
+ currentLinkAtom->string().endsWith("::")) {
+ QString suffix = Text::subText(currentLinkAtom,
+ priv->text.lastAtom()).toString();
+ currentLinkAtom->appendString(suffix);
+ }
+ currentLinkAtom = 0;
+ }
+ pendingFormats.erase(f);
+ }
+ }
+ break;
+ default:
+ {
+ bool newWord;
+ switch (priv->text.lastAtom()->type()) {
+ case Atom::ParaLeft:
+ newWord = true;
+ break;
+ default:
+ newWord = false;
+ }
+
+ if (paraState == OutsidePara) {
+ if (ch.isSpace()) {
+ ++pos;
+ newWord = false;
+ }
+ else {
+ enterPara();
+ newWord = true;
+ }
+ }
+ else {
+ if (ch.isSpace()) {
+ ++pos;
+ if ((ch == '\n') &&
+ (paraState == InsideSingleLinePara ||
+ isBlankLine())) {
+ leavePara();
+ newWord = false;
+ }
+ else {
+ appendChar(' ');
+ newWord = true;
+ }
+ }
+ else {
+ newWord = true;
+ }
+ }
+
+ if (newWord) {
+ int startPos = pos;
+ int numInternalUppercase = 0;
+ int numLowercase = 0;
+ int numStrangeSymbols = 0;
+
+ while (pos < len) {
+ unsigned char latin1Ch = in.at(pos).toLatin1();
+ if (islower(latin1Ch)) {
+ ++numLowercase;
+ ++pos;
+ }
+ else if (isupper(latin1Ch)) {
+ if (pos > startPos)
+ ++numInternalUppercase;
+ ++pos;
+ }
+ else if (isdigit(latin1Ch)) {
+ if (pos > startPos) {
+ ++pos;
+ }
+ else {
+ break;
+ }
+ }
+ else if (latin1Ch == '_' || latin1Ch == '@') {
+ ++numStrangeSymbols;
+ ++pos;
+ }
+ else if (latin1Ch == ':' && pos < len - 1
+ && in.at(pos + 1) == QLatin1Char(':')) {
+ ++numStrangeSymbols;
+ pos += 2;
+ }
+ else if (latin1Ch == '(') {
+ if (pos > startPos) {
+ if (pos < len - 1 &&
+ in.at(pos + 1) == QLatin1Char(')')) {
+ ++numStrangeSymbols;
+ pos += 2;
+ break;
+ }
+ else {
+ // ### handle functions with signatures
+ // and function calls
+ break;
+ }
+ }
+ else {
+ break;
+ }
+ }
+ else {
+ break;
+ }
+ }
+
+ if (pos == startPos) {
+ if (!ch.isSpace()) {
+ appendChar(ch);
+ ++pos;
+ }
+ }
+ else {
+ QString word = in.mid(startPos, pos - startPos);
+ // is word a C++ symbol or an English word?
+ if ((numInternalUppercase >= 1 && numLowercase >= 2)
+ || numStrangeSymbols >= 1) {
+ append(Atom::AutoLink, word);
+ }
+ else {
+ appendWord(word);
+ }
+ }
+ }
+ }
+ }
+ }
+ leaveValueList();
+
+ // for compatibility
+ if (openedCommands.top() == CMD_LEGALESE) {
+ append(Atom::LegaleseRight);
+ openedCommands.pop();
+ }
+
+ if (openedCommands.top() != CMD_OMIT) {
+ location().warning(tr("Missing '\\%1'").arg(endCmdName(openedCommands.top())));
+ }
+ else if (preprocessorSkipping.count() > 0) {
+ location().warning(tr("Missing '\\%1'").arg(cmdName(CMD_ENDIF)));
+ }
+
+ while (currentSectioningUnit > Doc::Chapter) {
+ int delta = currentSectioningUnit - priv->extra->sectioningUnit;
+ append(Atom::SectionRight, QString::number(delta));
+ currentSectioningUnit = Doc::SectioningUnit(int(currentSectioningUnit) - 1);
+ }
+
+ if (priv->extra && priv->extra->granularity < priv->extra->sectioningUnit)
+ priv->extra->granularity = priv->extra->sectioningUnit;
+ priv->text.stripFirstAtom();
+}
+
+Location &DocParser::location()
+{
+ while (!openedInputs.isEmpty() && openedInputs.top() <= pos) {
+ cachedLoc.pop();
+ cachedPos = openedInputs.pop();
+ }
+ while (cachedPos < pos)
+ cachedLoc.advance(in.at(cachedPos++));
+ return cachedLoc;
+}
+
+QString DocParser::detailsUnknownCommand(const QSet<QString> &metaCommandSet,
+ const QString &str)
+{
+ QSet<QString> commandSet = metaCommandSet;
+ int i = 0;
+ while (cmds[i].english != 0) {
+ commandSet.insert(*cmds[i].alias);
+ i++;
+ }
+
+ if (aliasMap()->contains(str))
+ return tr("The command '\\%1' was renamed '\\%2' by the configuration"
+ " file. Use the new name.")
+ .arg(str).arg((*aliasMap())[str]);
+
+ QString best = nearestName(str, commandSet);
+ if (best.isEmpty())
+ return QString();
+ return tr("Maybe you meant '\\%1'?").arg(best);
+}
+
+void DocParser::checkExpiry(const QString& date)
+{
+ QRegExp ymd("(\\d{4})(?:-(\\d{2})(?:-(\\d{2})))");
+
+ if (ymd.exactMatch(date)) {
+ int y = ymd.cap(1).toInt();
+ int m = ymd.cap(2).toInt();
+ int d = ymd.cap(3).toInt();
+
+ if (m == 0)
+ m = 1;
+ if (d == 0)
+ d = 1;
+ QDate expiryDate(y, m, d);
+ if (expiryDate.isValid()) {
+ int days = expiryDate.daysTo(QDate::currentDate());
+ if (days == 0) {
+ location().warning(tr("Documentation expires today"));
+ }
+ else if (days == 1) {
+ location().warning(tr("Documentation expired yesterday"));
+ }
+ else if (days >= 2) {
+ location().warning(tr("Documentation expired %1 days ago")
+ .arg(days));
+ }
+ }
+ else {
+ location().warning(tr("Date '%1' invalid").arg(date));
+ }
+ }
+ else {
+ location().warning(tr("Date '%1' not in YYYY-MM-DD format")
+ .arg(date));
+ }
+}
+
+void DocParser::insertBaseName(const QString &baseName)
+{
+ priv->constructExtra();
+ if (currentSectioningUnit == priv->extra->sectioningUnit) {
+ priv->extra->baseName = baseName;
+ }
+ else {
+ Atom *atom = priv->text.firstAtom();
+ Atom *sectionLeft = 0;
+
+ int delta = currentSectioningUnit - priv->extra->sectioningUnit;
+
+ while (atom != 0) {
+ if (atom->type() == Atom::SectionLeft &&
+ atom->string().toInt() == delta)
+ sectionLeft = atom;
+ atom = atom->next();
+ }
+ if (sectionLeft != 0)
+ (void) new Atom(sectionLeft, Atom::BaseName, baseName);
+ }
+}
+
+void DocParser::insertTarget(const QString &target, bool keyword)
+{
+ if (targetMap.contains(target)) {
+ location().warning(tr("Duplicate target name '%1'").arg(target));
+ targetMap[target].warning(tr("(The previous occurrence is here)"));
+ }
+ else {
+ targetMap.insert(target, location());
+ append(Atom::Target, target);
+ priv->constructExtra();
+ if (keyword)
+ priv->extra->keywords.append(priv->text.lastAtom());
+ else
+ priv->extra->targets.append(priv->text.lastAtom());
+ }
+}
+
+void DocParser::include(const QString& fileName)
+{
+ if (location().depth() > 16)
+ location().fatal(tr("Too many nested '\\%1's")
+ .arg(cmdName(CMD_INCLUDE)));
+
+ QString userFriendlyFilePath;
+ // ### use current directory?
+ QString filePath = Config::findFile(location(),
+ sourceFiles,
+ sourceDirs,
+ fileName,
+ userFriendlyFilePath);
+ if (filePath.isEmpty()) {
+ location().warning(tr("Cannot find leaf file '%1'").arg(fileName));
+ }
+ else {
+ QFile inFile(filePath);
+ if (!inFile.open(QFile::ReadOnly)) {
+ location().warning(tr("Cannot open leaf file '%1'")
+ .arg(userFriendlyFilePath));
+ }
+ else {
+ location().push(userFriendlyFilePath);
+
+ QTextStream inStream(&inFile);
+ QString includedStuff = inStream.readAll();
+ inFile.close();
+
+ in.insert(pos, includedStuff);
+ len = in.length();
+ openedInputs.push(pos + includedStuff.length());
+ }
+ }
+}
+
+void DocParser::startFormat(const QString& format, int cmd)
+{
+ enterPara();
+
+ QMap<int, QString>::ConstIterator f = pendingFormats.begin();
+ while (f != pendingFormats.end()) {
+ if (*f == format) {
+ location().warning(tr("Cannot nest '\\%1' commands")
+ .arg(cmdName(cmd)));
+ return;
+ }
+ ++f;
+ }
+
+ append(Atom::FormattingLeft, format);
+
+ if (isLeftBraceAhead()) {
+ skipSpacesOrOneEndl();
+ pendingFormats.insert(braceDepth, format);
+ ++braceDepth;
+ ++pos;
+ }
+ else {
+ append(Atom::String, getArgument());
+ append(Atom::FormattingRight, format);
+ if (format == ATOM_FORMATTING_INDEX && indexStartedPara) {
+ skipAllSpaces();
+ indexStartedPara = false;
+ }
+ }
+}
+
+bool DocParser::openCommand(int cmd)
+{
+ int outer = openedCommands.top();
+ bool ok = true;
+
+ if (cmd != CMD_LINK) {
+ if (outer == CMD_LIST) {
+ ok = (cmd == CMD_FOOTNOTE || cmd == CMD_LIST);
+ }
+ else if (outer == CMD_ABSTRACT) {
+ ok = (cmd == CMD_LIST ||
+ cmd == CMD_QUOTATION ||
+ cmd == CMD_TABLE);
+ }
+ else if (outer == CMD_SIDEBAR) {
+ ok = (cmd == CMD_LIST ||
+ cmd == CMD_QUOTATION ||
+ cmd == CMD_SIDEBAR);
+ }
+ else if (outer == CMD_QUOTATION) {
+ ok = (cmd == CMD_LIST);
+ }
+ else if (outer == CMD_TABLE) {
+ ok = (cmd == CMD_LIST ||
+ cmd == CMD_FOOTNOTE ||
+ cmd == CMD_QUOTATION);
+ }
+ else if (outer == CMD_FOOTNOTE || outer == CMD_LINK) {
+ ok = false;
+ }
+ }
+
+ if (ok) {
+ openedCommands.push(cmd);
+ }
+ else {
+ location().warning(tr("Cannot use '\\%1' within '\\%2'")
+ .arg(cmdName(cmd)).arg(cmdName(outer)));
+ }
+ return ok;
+}
+
+bool DocParser::closeCommand(int endCmd)
+{
+ if (endCmdFor(openedCommands.top()) == endCmd && openedCommands.size() > 1) {
+ openedCommands.pop();
+ return true;
+ }
+ else {
+ bool contains = false;
+ QStack<int> opened2 = openedCommands;
+ while (opened2.size() > 1) {
+ if (endCmdFor(opened2.top()) == endCmd) {
+ contains = true;
+ break;
+ }
+ opened2.pop();
+ }
+
+ if (contains) {
+ while (endCmdFor(openedCommands.top()) != endCmd && openedCommands.size() > 1) {
+ location().warning(tr("Missing '\\%1' before '\\%2'")
+ .arg(endCmdName(openedCommands.top()))
+ .arg(cmdName(endCmd)));
+ openedCommands.pop();
+ }
+ }
+ else {
+ location().warning(tr("Unexpected '\\%1'")
+ .arg(cmdName(endCmd)));
+ }
+ return false;
+ }
+}
+
+void DocParser::startSection(Doc::SectioningUnit unit, int cmd)
+{
+ leavePara();
+
+ if (currentSectioningUnit == Doc::Book) {
+ if (unit > Doc::Section1)
+ location().warning(tr("Unexpected '\\%1' without '\\%2'")
+ .arg(cmdName(cmd))
+ .arg(cmdName(CMD_SECTION1)));
+ currentSectioningUnit = (Doc::SectioningUnit) (unit - 1);
+ priv->constructExtra();
+ priv->extra->sectioningUnit = currentSectioningUnit;
+ }
+
+ if (unit <= priv->extra->sectioningUnit) {
+ location().warning(tr("Unexpected '\\%1' in this documentation")
+ .arg(cmdName(cmd)));
+ }
+ else if (unit - currentSectioningUnit > 1) {
+ location().warning(tr("Unexpected '\\%1' at this point")
+ .arg(cmdName(cmd)));
+ }
+ else {
+ if (currentSectioningUnit >= unit)
+ endSection(unit, cmd);
+
+ int delta = unit - priv->extra->sectioningUnit;
+ append(Atom::SectionLeft, QString::number(delta));
+ priv->constructExtra();
+ priv->extra->tableOfContents.append(priv->text.lastAtom());
+ priv->extra->tableOfContentsLevels.append(unit);
+ enterPara(Atom::SectionHeadingLeft,
+ Atom::SectionHeadingRight,
+ QString::number(delta));
+ currentSectioningUnit = unit;
+ }
+}
+
+void DocParser::endSection(int unit, int endCmd)
+{
+ leavePara();
+
+ if (unit < priv->extra->sectioningUnit) {
+ location().warning(tr("Unexpected '\\%1' in this documentation")
+ .arg(cmdName(endCmd)));
+ }
+ else if (unit > currentSectioningUnit) {
+ location().warning(tr("Unexpected '\\%1' at this point")
+ .arg(cmdName(endCmd)));
+ }
+ else {
+ while (currentSectioningUnit >= unit) {
+ int delta = currentSectioningUnit - priv->extra->sectioningUnit;
+ append(Atom::SectionRight, QString::number(delta));
+ currentSectioningUnit =
+ (Doc::SectioningUnit) (currentSectioningUnit - 1);
+ }
+ }
+}
+
+void DocParser::parseAlso()
+{
+ leavePara();
+ skipSpacesOnLine();
+ while (pos < len && in[pos] != '\n') {
+ QString target;
+ QString str;
+
+ if (in[pos] == '{') {
+ target = getArgument();
+ skipSpacesOnLine();
+ if (in[pos] == '{') {
+ str = getArgument();
+
+ // hack for C++ to support links like \l{QString::}{count()}
+ if (target.endsWith("::"))
+ target += str;
+ }
+ else {
+ str = target;
+ }
+#ifdef QDOC2_COMPAT
+ }
+ else if (in[pos] == '\\' && in.mid(pos, 5) == "\\link") {
+ pos += 6;
+ target = getArgument();
+ int endPos = in.indexOf("\\endlink", pos);
+ if (endPos != -1) {
+ str = in.mid(pos, endPos - pos).trimmed();
+ pos = endPos + 8;
+ }
+#endif
+ }
+ else {
+ target = getArgument();
+ str = cleanLink(target);
+ }
+
+ Text also;
+ also << Atom(Atom::Link, target)
+ << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK)
+ << str
+ << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK);
+ priv->addAlso(also);
+
+ skipSpacesOnLine();
+ if (pos < len && in[pos] == ',') {
+ pos++;
+ skipSpacesOrOneEndl();
+ }
+ else if (in[pos] != '\n') {
+ location().warning(tr("Missing comma in '\\%1'").arg(cmdName(CMD_SA)));
+ }
+ }
+}
+
+void DocParser::append(Atom::Type type, const QString &string)
+{
+ Atom::Type lastType = priv->text.lastAtom()->type();
+#ifdef QDOC_QML
+ if (((lastType == Atom::Code) || (lastType == Atom::Code)) &&
+#else
+ if ((lastType == Atom::Code) &&
+#endif
+ priv->text.lastAtom()->string().endsWith(QLatin1String("\n\n")))
+ priv->text.lastAtom()->chopString();
+ priv->text << Atom(type, string);
+}
+
+void DocParser::appendChar(QChar ch)
+{
+ if (priv->text.lastAtom()->type() != Atom::String)
+ append(Atom::String);
+ Atom *atom = priv->text.lastAtom();
+ if (ch == QLatin1Char(' ')) {
+ if (!atom->string().endsWith(QLatin1Char(' ')))
+ atom->appendChar(QLatin1Char(' '));
+ }
+ else
+ atom->appendChar(ch);
+}
+
+void DocParser::appendWord(const QString &word)
+{
+ if (priv->text.lastAtom()->type() != Atom::String) {
+ append(Atom::String, word);
+ }
+ else
+ priv->text.lastAtom()->appendString(word);
+}
+
+void DocParser::appendToCode(const QString& markedCode)
+{
+ Atom::Type lastType = priv->text.lastAtom()->type();
+#ifdef QDOC_QML
+ if (lastType != Atom::Qml)
+ append(Atom::Qml);
+#else
+ if (lastType != Atom::Code)
+ append(Atom::Code);
+#endif
+ priv->text.lastAtom()->appendString(markedCode);
+}
+
+void DocParser::startNewPara()
+{
+ leavePara();
+ enterPara();
+}
+
+void DocParser::enterPara(Atom::Type leftType,
+ Atom::Type rightType,
+ const QString& string)
+{
+ if (paraState == OutsidePara) {
+ if (priv->text.lastAtom()->type() != Atom::ListItemLeft)
+ leaveValueList();
+ append(leftType, string);
+ indexStartedPara = false;
+ pendingParaLeftType = leftType;
+ pendingParaRightType = rightType;
+ pendingParaString = string;
+ if (
+#if 0
+ leftType == Atom::BriefLeft ||
+#endif
+ leftType == Atom::SectionHeadingLeft) {
+ paraState = InsideSingleLinePara;
+ }
+ else {
+ paraState = InsideMultiLinePara;
+ }
+ skipSpacesOrOneEndl();
+ }
+}
+
+void DocParser::leavePara()
+{
+ if (paraState != OutsidePara) {
+ if (!pendingFormats.isEmpty()) {
+ location().warning(tr("Missing '}'"));
+ pendingFormats.clear();
+ }
+
+ if (priv->text.lastAtom()->type() == pendingParaLeftType) {
+ priv->text.stripLastAtom();
+ }
+ else {
+ if (priv->text.lastAtom()->type() == Atom::String &&
+ priv->text.lastAtom()->string().endsWith(" ")) {
+ priv->text.lastAtom()->chopString();
+ }
+ append(pendingParaRightType, pendingParaString);
+ }
+ paraState = OutsidePara;
+ indexStartedPara = false;
+ pendingParaRightType = Atom::Nop;
+ pendingParaString = "";
+ }
+}
+
+void DocParser::leaveValue()
+{
+ leavePara();
+ if (openedLists.isEmpty()) {
+ openedLists.push(OpenedList(OpenedList::Value));
+ append(Atom::ListLeft, ATOM_LIST_VALUE);
+ }
+ else {
+ if (priv->text.lastAtom()->type() == Atom::Nop)
+ priv->text.stripLastAtom();
+ append(Atom::ListItemRight, ATOM_LIST_VALUE);
+ }
+}
+
+void DocParser::leaveValueList()
+{
+ leavePara();
+ if (!openedLists.isEmpty() &&
+ (openedLists.top().style() == OpenedList::Value)) {
+ if (priv->text.lastAtom()->type() == Atom::Nop)
+ priv->text.stripLastAtom();
+ append(Atom::ListItemRight, ATOM_LIST_VALUE);
+ append(Atom::ListRight, ATOM_LIST_VALUE);
+ openedLists.pop();
+ }
+}
+
+void DocParser::leaveTableRow()
+{
+ if (inTableItem) {
+ leavePara();
+ append(Atom::TableItemRight);
+ inTableItem = false;
+ }
+ if (inTableHeader) {
+ append(Atom::TableHeaderRight);
+ inTableHeader = false;
+ }
+ if (inTableRow) {
+ append(Atom::TableRowRight);
+ inTableRow = false;
+ }
+}
+
+CodeMarker *DocParser::quoteFromFile()
+{
+ return Doc::quoteFromFile(location(), quoter, getArgument());
+}
+
+void DocParser::expandMacro(const QString &name,
+ const QString &def,
+ int numParams)
+{
+ if (numParams == 0) {
+ append(Atom::RawString, def);
+ }
+ else {
+ QStringList args;
+ QString rawString;
+
+ for (int i = 0; i < numParams; i++) {
+ if (numParams == 1 || isLeftBraceAhead()) {
+ args << getArgument(true);
+ }
+ else {
+ location().warning(tr("Macro '\\%1' invoked with too few"
+ " arguments (expected %2, got %3)")
+ .arg(name).arg(numParams).arg(i));
+ break;
+ }
+ }
+
+ int j = 0;
+ while (j < def.size()) {
+ int paramNo;
+ if ((def[j] == '\\') && (j < def.size() - 1) &&
+ ((paramNo = def[j + 1].digitValue()) >= 1) &&
+ (paramNo <= numParams)) {
+ if (!rawString.isEmpty()) {
+ append(Atom::RawString, rawString);
+ rawString = "";
+ }
+ append(Atom::String, args[paramNo - 1]);
+ j += 2;
+ }
+ else {
+ rawString += def[j++];
+ }
+ }
+ if (!rawString.isEmpty())
+ append(Atom::RawString, rawString);
+ }
+}
+
+Doc::SectioningUnit DocParser::getSectioningUnit()
+{
+ QString name = getOptionalArgument();
+
+ if (name == "part") {
+ return Doc::Part;
+ }
+ else if (name == "chapter") {
+ return Doc::Chapter;
+ }
+ else if (name == "section1") {
+ return Doc::Section1;
+ }
+ else if (name == "section2") {
+ return Doc::Section2;
+ }
+ else if (name == "section3") {
+ return Doc::Section3;
+ }
+ else if (name == "section4") {
+ return Doc::Section4;
+ }
+ else if (name.isEmpty()) {
+ return Doc::Section4;
+ }
+ else {
+ location().warning(tr("Invalid sectioning unit '%1'").arg(name));
+ return Doc::Book;
+ }
+}
+
+QString DocParser::getArgument(bool verbatim)
+{
+ QString arg;
+ int delimDepth = 0;
+
+ skipSpacesOrOneEndl();
+
+ int startPos = pos;
+
+ /*
+ Typically, an argument ends at the next white-space. However,
+ braces can be used to group words:
+
+ {a few words}
+
+ Also, opening and closing parentheses have to match. Thus,
+
+ printf("%d\n", x)
+
+ is an argument too, although it contains spaces. Finally,
+ trailing punctuation is not included in an argument, nor is 's.
+ */
+ if (pos < (int) in.length() && in[pos] == '{') {
+ pos++;
+ while (pos < (int) in.length() && delimDepth >= 0) {
+ switch (in[pos].unicode()) {
+ case '{':
+ delimDepth++;
+ arg += "{";
+ pos++;
+ break;
+ case '}':
+ delimDepth--;
+ if (delimDepth >= 0)
+ arg += "}";
+ pos++;
+ break;
+ case '\\':
+ if (verbatim) {
+ arg += in[pos];
+ pos++;
+ }
+ else {
+ pos++;
+ if (pos < (int) in.length()) {
+ if (in[pos].isLetterOrNumber())
+ break;
+ arg += in[pos];
+ if (in[pos].isSpace()) {
+ skipAllSpaces();
+ }
+ else {
+ pos++;
+ }
+ }
+ }
+ break;
+ default:
+ arg += in[pos];
+ pos++;
+ }
+ }
+ if (delimDepth > 0)
+ location().warning(tr("Missing '}'"));
+ }
+ else {
+ while (pos < in.length() &&
+ ((delimDepth > 0) ||
+ ((delimDepth == 0) &&
+ !in[pos].isSpace()))) {
+ switch (in[pos].unicode()) {
+ case '(':
+ case '[':
+ case '{':
+ delimDepth++;
+ arg += in[pos];
+ pos++;
+ break;
+ case ')':
+ case ']':
+ case '}':
+ delimDepth--;
+ if (pos == startPos || delimDepth >= 0) {
+ arg += in[pos];
+ pos++;
+ }
+ break;
+ case '\\':
+ if (verbatim) {
+ arg += in[pos];
+ pos++;
+ }
+ else {
+ pos++;
+ if (pos < (int) in.length()) {
+ if (in[pos].isLetterOrNumber())
+ break;
+ arg += in[pos];
+ if (in[pos].isSpace()) {
+ skipAllSpaces();
+ }
+ else {
+ pos++;
+ }
+ }
+ }
+ break;
+ default:
+ arg += in[pos];
+ pos++;
+ }
+ }
+ if ((arg.length() > 1) &&
+ (QString(".,:;!?").indexOf(in[pos - 1]) != -1) &&
+ !arg.endsWith("...")) {
+ arg.truncate(arg.length() - 1);
+ pos--;
+ }
+ if (arg.length() > 2 && in.mid(pos - 2, 2) == "'s") {
+ arg.truncate(arg.length() - 2);
+ pos -= 2;
+ }
+ }
+ return arg.simplified();
+}
+
+QString DocParser::getOptionalArgument()
+{
+ skipSpacesOrOneEndl();
+ if (pos + 1 < (int) in.length() && in[pos] == '\\' &&
+ in[pos + 1].isLetterOrNumber()) {
+ return "";
+ }
+ else {
+ return getArgument();
+ }
+}
+
+QString DocParser::getRestOfLine()
+{
+ QString t;
+
+ skipSpacesOnLine();
+
+ bool trailingSlash = false;
+
+ do {
+ int begin = pos;
+
+ while (pos < in.size() && in[pos] != '\n') {
+ if (in[pos] == '\\' && !trailingSlash) {
+ trailingSlash = true;
+ ++pos;
+ while ((pos < in.size()) &&
+ in[pos].isSpace() &&
+ (in[pos] != '\n'))
+ ++pos;
+ }
+ else {
+ trailingSlash = false;
+ ++pos;
+ }
+ }
+
+ if (!t.isEmpty())
+ t += " ";
+ t += in.mid(begin, pos - begin).simplified();
+
+ if (trailingSlash) {
+ t.chop(1);
+ t = t.simplified();
+ }
+ if (pos < in.size())
+ ++pos;
+ } while (pos < in.size() && trailingSlash);
+
+ return t;
+}
+
+/*!
+ The metacommand argument is normally the remaining text to
+ the right of the metacommand itself. The extra blanks are
+ stripped and the argument string is returned.
+ */
+QString DocParser::getMetaCommandArgument(const QString &cmdStr)
+{
+ skipSpacesOnLine();
+
+ int begin = pos;
+ int parenDepth = 0;
+
+ while (pos < in.size() && (in[pos] != '\n' || parenDepth > 0)) {
+ if (in.at(pos) == '(')
+ ++parenDepth;
+ else if (in.at(pos) == ')')
+ --parenDepth;
+
+ ++pos;
+ }
+ if (pos == in.size() && parenDepth > 0) {
+ pos = begin;
+ location().warning(tr("Unbalanced parentheses in '%1'").arg(cmdStr));
+ }
+
+ QString t = in.mid(begin, pos - begin).simplified();
+ skipSpacesOnLine();
+ return t;
+}
+
+QString DocParser::getUntilEnd(int cmd)
+{
+ int endCmd = endCmdFor(cmd);
+ QRegExp rx("\\\\" + cmdName(endCmd) + "\\b");
+ QString t;
+ int end = rx.indexIn(in, pos);
+
+ if (end == -1) {
+ location().warning(tr("Missing '\\%1'").arg(cmdName(endCmd)));
+ pos = in.length();
+ }
+ else {
+ t = in.mid(pos, end - pos);
+ pos = end + rx.matchedLength();
+ }
+ return t;
+}
+
+QString DocParser::getCode(int cmd, CodeMarker *marker)
+{
+ QString code = untabifyEtc(getUntilEnd(cmd));
+ int indent = indentLevel(code);
+ if (indent < minIndent)
+ minIndent = indent;
+ code = unindent(minIndent, code);
+ marker = CodeMarker::markerForCode(code);
+ return marker->markedUpCode(code, 0, "");
+}
+
+/*!
+ Used only for generating doxygen output.
+ */
+QString DocParser::getUnmarkedCode(int cmd)
+{
+ QString code = getUntilEnd(cmd);
+#if 0
+ int indent = indentLevel(code);
+ if (indent < minIndent)
+ minIndent = indent;
+ code = unindent(minIndent, code);
+#endif
+ return code;
+}
+
+bool DocParser::isBlankLine()
+{
+ int i = pos;
+
+ while (i < len && in[i].isSpace()) {
+ if (in[i] == '\n')
+ return true;
+ i++;
+ }
+ return false;
+}
+
+bool DocParser::isLeftBraceAhead()
+{
+ int numEndl = 0;
+ int i = pos;
+
+ while (i < len && in[i].isSpace() && numEndl < 2) {
+ // ### bug with '\\'
+ if (in[i] == '\n')
+ numEndl++;
+ i++;
+ }
+ return numEndl < 2 && i < len && in[i] == '{';
+}
+
+void DocParser::skipSpacesOnLine()
+{
+ while ((pos < in.length()) &&
+ in[pos].isSpace() &&
+ (in[pos].unicode() != '\n'))
+ ++pos;
+}
+
+void DocParser::skipSpacesOrOneEndl()
+{
+ int firstEndl = -1;
+ while (pos < (int) in.length() && in[pos].isSpace()) {
+ QChar ch = in[pos];
+ if (ch == '\n') {
+ if (firstEndl == -1) {
+ firstEndl = pos;
+ }
+ else {
+ pos = firstEndl;
+ break;
+ }
+ }
+ pos++;
+ }
+}
+
+void DocParser::skipAllSpaces()
+{
+ while (pos < len && in[pos].isSpace())
+ pos++;
+}
+
+void DocParser::skipToNextPreprocessorCommand()
+{
+ QRegExp rx("\\\\(?:" + cmdName(CMD_IF) + "|" +
+ cmdName(CMD_ELSE) + "|" +
+ cmdName(CMD_ENDIF) + ")\\b");
+ int end = rx.indexIn(in, pos + 1); // ### + 1 necessary?
+
+ if (end == -1)
+ pos = in.length();
+ else
+ pos = end;
+}
+
+int DocParser::endCmdFor(int cmd)
+{
+ switch (cmd) {
+ case CMD_ABSTRACT:
+ return CMD_ENDABSTRACT;
+ case CMD_BADCODE:
+ return CMD_ENDCODE;
+ case CMD_CHAPTER:
+ return CMD_ENDCHAPTER;
+ case CMD_CODE:
+ return CMD_ENDCODE;
+#ifdef QDOC_QML
+ case CMD_QML:
+ return CMD_ENDQML;
+ case CMD_QMLTEXT:
+ return CMD_ENDQMLTEXT;
+#endif
+ case CMD_FOOTNOTE:
+ return CMD_ENDFOOTNOTE;
+ case CMD_LEGALESE:
+ return CMD_ENDLEGALESE;
+ case CMD_LINK:
+ return CMD_ENDLINK;
+ case CMD_LIST:
+ return CMD_ENDLIST;
+ case CMD_NEWCODE:
+ return CMD_ENDCODE;
+ case CMD_OLDCODE:
+ return CMD_NEWCODE;
+ case CMD_OMIT:
+ return CMD_ENDOMIT;
+ case CMD_PART:
+ return CMD_ENDPART;
+ case CMD_QUOTATION:
+ return CMD_ENDQUOTATION;
+ case CMD_RAW:
+ return CMD_ENDRAW;
+ case CMD_SECTION1:
+ return CMD_ENDSECTION1;
+ case CMD_SECTION2:
+ return CMD_ENDSECTION2;
+ case CMD_SECTION3:
+ return CMD_ENDSECTION3;
+ case CMD_SECTION4:
+ return CMD_ENDSECTION4;
+ case CMD_SIDEBAR:
+ return CMD_ENDSIDEBAR;
+ case CMD_TABLE:
+ return CMD_ENDTABLE;
+ default:
+ return cmd;
+ }
+}
+
+QString DocParser::cmdName(int cmd)
+{
+ return *cmds[cmd].alias;
+}
+
+QString DocParser::endCmdName(int cmd)
+{
+ return cmdName(endCmdFor(cmd));
+}
+
+QString DocParser::untabifyEtc(const QString& str)
+{
+ QString result;
+ result.reserve(str.length());
+ int column = 0;
+
+ for (int i = 0; i < str.length(); i++) {
+ const QChar c = str.at(i);
+ if (c == QLatin1Char('\r'))
+ continue;
+ if (c == QLatin1Char('\t')) {
+ result += " " + (column % tabSize);
+ column = ((column / tabSize) + 1) * tabSize;
+ continue;
+ }
+ if (c == QLatin1Char('\n')) {
+ while (result.endsWith(QLatin1Char(' ')))
+ result.chop(1);
+ result += c;
+ column = 0;
+ continue;
+ }
+ result += c;
+ column++;
+ }
+
+ while (result.endsWith("\n\n"))
+ result.truncate(result.length() - 1);
+ while (result.startsWith("\n"))
+ result = result.mid(1);
+
+ return result;
+}
+
+int DocParser::indentLevel(const QString& str)
+{
+ int minIndent = INT_MAX;
+ int column = 0;
+
+ for (int i = 0; i < (int) str.length(); i++) {
+ if (str[i] == '\n') {
+ column = 0;
+ }
+ else {
+ if (str[i] != ' ' && column < minIndent)
+ minIndent = column;
+ column++;
+ }
+ }
+ return minIndent;
+}
+
+QString DocParser::unindent(int level, const QString& str)
+{
+ if (level == 0)
+ return str;
+
+ QString t;
+ int column = 0;
+
+ for (int i = 0; i < (int) str.length(); i++) {
+ if (str[i] == QLatin1Char('\n')) {
+ t += '\n';
+ column = 0;
+ }
+ else {
+ if (column >= level)
+ t += str[i];
+ column++;
+ }
+ }
+ return t;
+}
+
+QString DocParser::slashed(const QString& str)
+{
+ QString result = str;
+ result.replace("/", "\\/");
+ return "/" + result + "/";
+}
+
+#define COMMAND_BRIEF Doc::alias("brief")
+
+#ifdef QDOC_QML
+#define COMMAND_QMLBRIEF Doc::alias("qmlbrief")
+#endif
+
+#ifdef QDOC2DOX
+#define DOXYGEN_INDENT 2
+#define DOXYGEN_TAB_SIZE 4
+#define DOXYGEN_INDENT_STRING " "
+#define DOXYGEN_TAB_STRING " "
+
+static QRegExp ws_rx("\\s");
+static QRegExp not_ws_rx("\\S");
+
+int DoxWriter::doxPass = 0;
+QString DoxWriter::currentClass;
+QSet<QString> DoxWriter::anchors;
+QStringMap DoxWriter::exampleTitles;
+QStringMap DoxWriter::headerFileTitles;
+QStringMap DoxWriter::fileTitles;
+QStringMap DoxWriter::groupTitles;
+QStringMap DoxWriter::moduleTitles;
+QStringMap DoxWriter::pageTitles;
+QStringMap DoxWriter::externalPageTitles;
+QStringMap DoxWriter::exampleTitlesInverse;
+QStringMap DoxWriter::headerFileTitlesInverse;
+QStringMap DoxWriter::fileTitlesInverse;
+QStringMap DoxWriter::groupTitlesInverse;
+QStringMap DoxWriter::moduleTitlesInverse;
+QStringMap DoxWriter::pageTitlesInverse;
+QStringMap DoxWriter::externalPageTitlesInverse;
+QStringMultiMap DoxWriter::variables;
+QStringMultiMap DoxWriter::properties;
+QStringMultiMap DoxWriter::enums;
+#endif
+
+Doc::Doc(const Location& start_loc,
+ const Location& end_loc,
+ const QString& source,
+ const QSet<QString>& metaCommandSet)
+{
+ priv = new DocPrivate(start_loc,end_loc,source);
+ DocParser parser;
+ parser.parse(source,priv,metaCommandSet);
+#ifdef QDOC2DOX
+ if (DoxWriter::isDoxPass()) {
+ DoxWriter doxWriter(source,priv);
+ if (DoxWriter::isDoxPass(1))
+ doxWriter.pass1();
+ else
+ doxWriter.pass2();
+ }
+#endif
+}
+
+Doc::Doc(const Doc& doc)
+ : priv(0)
+{
+ operator=(doc);
+}
+
+Doc::~Doc()
+{
+ if (priv && priv->deref())
+ delete priv;
+}
+
+Doc &Doc::operator=(const Doc& doc)
+{
+ if (doc.priv)
+ doc.priv->ref();
+ if (priv && priv->deref())
+ delete priv;
+ priv = doc.priv;
+ return *this;
+}
+
+void Doc::renameParameters(const QStringList &oldNames,
+ const QStringList &newNames)
+{
+ if (priv && oldNames != newNames) {
+ detach();
+
+ priv->params = newNames.toSet();
+
+ Atom *atom = priv->text.firstAtom();
+ while (atom) {
+ if (atom->type() == Atom::FormattingLeft
+ && atom->string() == ATOM_FORMATTING_PARAMETER) {
+ atom = atom->next();
+ if (!atom)
+ return;
+ int index = oldNames.indexOf(atom->string());
+ if (index != -1 && index < newNames.count())
+ atom->setString(newNames.at(index));
+ }
+ atom = atom->next();
+ }
+ }
+}
+
+void Doc::simplifyEnumDoc()
+{
+ if (priv) {
+ if (priv->isEnumDocSimplifiable()) {
+ detach();
+
+ Text newText;
+
+ Atom *atom = priv->text.firstAtom();
+ while (atom) {
+ if ((atom->type() == Atom::ListLeft) &&
+ (atom->string() == ATOM_LIST_VALUE)) {
+ while (atom && ((atom->type() != Atom::ListRight) ||
+ (atom->string() != ATOM_LIST_VALUE)))
+ atom = atom->next();
+ if (atom)
+ atom = atom->next();
+ }
+ else {
+ newText << *atom;
+ atom = atom->next();
+ }
+ }
+ priv->text = newText;
+ }
+ }
+}
+
+void Doc::setBody(const Text &text)
+{
+ detach();
+ priv->text = text;
+}
+
+/*!
+ Returns the starting location of a qdoc comment.
+ */
+const Location &Doc::location() const
+{
+ static const Location dummy;
+ return priv == 0 ? dummy : priv->start_loc;
+}
+
+const QString &Doc::source() const
+{
+ static QString null;
+ return priv == 0 ? null : priv->src;
+}
+
+bool Doc::isEmpty() const
+{
+ return priv == 0 || priv->src.isEmpty();
+}
+
+const Text& Doc::body() const
+{
+ static const Text dummy;
+ return priv == 0 ? dummy : priv->text;
+}
+
+Text Doc::briefText() const
+{
+ return body().subText(Atom::BriefLeft, Atom::BriefRight);
+}
+
+Text Doc::trimmedBriefText(const QString &className) const
+{
+ QString classNameOnly = className;
+ if (className.contains("::"))
+ classNameOnly = className.split("::").last();
+
+ Text originalText = briefText();
+ Text resultText;
+ const Atom *atom = originalText.firstAtom();
+ if (atom) {
+ QString briefStr;
+ QString whats;
+ bool standardWording = true;
+
+ /*
+ This code is really ugly. The entire \brief business
+ should be rethought.
+ */
+ while (atom && (atom->type() == Atom::AutoLink || atom->type() == Atom::String)) {
+ briefStr += atom->string();
+ atom = atom->next();
+ }
+
+ QStringList w = briefStr.split(" ");
+ if (!w.isEmpty() && w.first() == "The")
+ w.removeFirst();
+ else {
+ location().warning(
+ tr("Nonstandard wording in '\\%1' text for '%2' (expected 'The')")
+ .arg(COMMAND_BRIEF).arg(className));
+ standardWording = false;
+ }
+
+ if (!w.isEmpty() && (w.first() == className || w.first() == classNameOnly))
+ w.removeFirst();
+ else {
+ location().warning(
+ tr("Nonstandard wording in '\\%1' text for '%2' (expected '%3')")
+ .arg(COMMAND_BRIEF).arg(className).arg(className));
+ standardWording = false;
+ }
+
+ if (!w.isEmpty() && (w.first() == "class" || w.first() == "widget"
+ || w.first() == "namespace" || w.first() == "header"))
+ w.removeFirst();
+ else {
+ location().warning(
+ tr("Nonstandard wording in '\\%1' text for '%2' ("
+ "expected 'class', 'widget', 'namespace' or 'header')")
+ .arg(COMMAND_BRIEF).arg(className));
+ standardWording = false;
+ }
+
+ if (!w.isEmpty() && (w.first() == "is" || w.first() == "provides"))
+ w.removeFirst();
+
+ if (!w.isEmpty() && (w.first() == "a" || w.first() == "an"))
+ w.removeFirst();
+
+ whats = w.join(" ");
+ if (whats.endsWith("."))
+ whats.truncate(whats.length() - 1);
+
+ if (whats.isEmpty()) {
+ location().warning(
+ tr("Nonstandard wording in '\\%1' text for '%2' (expected more text)")
+ .arg(COMMAND_BRIEF).arg(className));
+ standardWording = false;
+ }
+ else
+ whats[0] = whats[0].toUpper();
+
+ // ### move this once \brief is abolished for properties
+ if (standardWording)
+ resultText << whats;
+ }
+ return resultText;
+}
+
+Text Doc::legaleseText() const
+{
+ if (priv == 0 || !priv->hasLegalese)
+ return Text();
+ else
+ return body().subText(Atom::LegaleseLeft, Atom::LegaleseRight);
+}
+
+const QString& Doc::baseName() const
+{
+ static QString null;
+ if (priv == 0 || priv->extra == 0) {
+ return null;
+ }
+ else {
+ return priv->extra->baseName;
+ }
+}
+
+Doc::SectioningUnit Doc::granularity() const
+{
+ if (priv == 0 || priv->extra == 0) {
+ return DocPrivateExtra().granularity;
+ }
+ else {
+ return priv->extra->granularity;
+ }
+}
+
+#if notyet // ###
+Doc::SectioningUnit Doc::sectioningUnit() const
+{
+ if (priv == 0 || priv->extra == 0) {
+ return DocPrivateExtra().sectioningUnit;
+ }
+ else {
+ return priv->extra->sectioningUnit;
+ }
+}
+#endif
+
+const QSet<QString> &Doc::parameterNames() const
+{
+ return priv == 0 ? *null_Set_QString() : priv->params;
+}
+
+const QStringList &Doc::enumItemNames() const
+{
+ return priv == 0 ? *null_QStringList() : priv->enumItemList;
+}
+
+const QStringList &Doc::omitEnumItemNames() const
+{
+ return priv == 0 ? *null_QStringList() : priv->omitEnumItemList;
+}
+
+const QSet<QString> &Doc::metaCommandsUsed() const
+{
+ return priv == 0 ? *null_Set_QString() : priv->metacommandsUsed;
+}
+
+QStringList Doc::metaCommandArgs(const QString& metacommand) const
+{
+ return priv == 0 ? QStringList() : priv->metaCommandMap.value(metacommand);
+}
+
+const QList<Text> &Doc::alsoList() const
+{
+ return priv == 0 ? *null_QList_Text() : priv->alsoList;
+}
+
+bool Doc::hasTableOfContents() const
+{
+ return priv && priv->extra && !priv->extra->tableOfContents.isEmpty();
+}
+
+bool Doc::hasKeywords() const
+{
+ return priv && priv->extra && !priv->extra->keywords.isEmpty();
+}
+
+bool Doc::hasTargets() const
+{
+ return priv && priv->extra && !priv->extra->targets.isEmpty();
+}
+
+const QList<Atom *> &Doc::tableOfContents() const
+{
+ priv->constructExtra();
+ return priv->extra->tableOfContents;
+}
+
+const QList<int> &Doc::tableOfContentsLevels() const
+{
+ priv->constructExtra();
+ return priv->extra->tableOfContentsLevels;
+}
+
+const QList<Atom *> &Doc::keywords() const
+{
+ priv->constructExtra();
+ return priv->extra->keywords;
+}
+
+const QList<Atom *> &Doc::targets() const
+{
+ priv->constructExtra();
+ return priv->extra->targets;
+}
+
+const QStringMap &Doc::metaTagMap() const
+{
+ return priv && priv->extra ? priv->extra->metaMap : *null_QStringMap();
+}
+
+void Doc::initialize(const Config& config)
+{
+ DocParser::tabSize = config.getInt(CONFIG_TABSIZE);
+ DocParser::exampleFiles = config.getStringList(CONFIG_EXAMPLES);
+ DocParser::exampleDirs = config.getStringList(CONFIG_EXAMPLEDIRS);
+ DocParser::sourceFiles = config.getStringList(CONFIG_SOURCES);
+ DocParser::sourceDirs = config.getStringList(CONFIG_SOURCEDIRS);
+ DocParser::quoting = config.getBool(CONFIG_QUOTINGINFORMATION);
+
+ QStringMap reverseAliasMap;
+
+ QSet<QString> commands = config.subVars(CONFIG_ALIAS);
+ QSet<QString>::ConstIterator c = commands.begin();
+ while (c != commands.end()) {
+ QString alias = config.getString(CONFIG_ALIAS + Config::dot + *c);
+ if (reverseAliasMap.contains(alias)) {
+ config.lastLocation().warning(tr("Command name '\\%1' cannot stand"
+ " for both '\\%2' and '\\%3'")
+ .arg(alias)
+ .arg(reverseAliasMap[alias])
+ .arg(*c));
+ }
+ else {
+ reverseAliasMap.insert(alias, *c);
+ }
+ aliasMap()->insert(*c, alias);
+ ++c;
+ }
+
+ int i = 0;
+ while (cmds[i].english) {
+ cmds[i].alias = new QString(alias(cmds[i].english));
+ cmdHash()->insert(*cmds[i].alias, cmds[i].no);
+
+ if (cmds[i].no != i)
+ Location::internalError(tr("command %1 missing").arg(i));
+ i++;
+ }
+
+ QSet<QString> macroNames = config.subVars(CONFIG_MACRO);
+ QSet<QString>::ConstIterator n = macroNames.begin();
+ while (n != macroNames.end()) {
+ QString macroDotName = CONFIG_MACRO + Config::dot + *n;
+ Macro macro;
+ macro.numParams = -1;
+ macro.defaultDef = config.getString(macroDotName);
+ if (!macro.defaultDef.isEmpty()) {
+ macro.defaultDefLocation = config.lastLocation();
+ macro.numParams = Config::numParams(macro.defaultDef);
+ }
+ bool silent = false;
+
+ QSet<QString> formats = config.subVars(macroDotName);
+ QSet<QString>::ConstIterator f = formats.begin();
+ while (f != formats.end()) {
+ QString def = config.getString(macroDotName + Config::dot + *f);
+ if (!def.isEmpty()) {
+ macro.otherDefs.insert(*f, def);
+ int m = Config::numParams(macro.defaultDef);
+ if (macro.numParams == -1) {
+ macro.numParams = m;
+ }
+ else if (macro.numParams != m) {
+ if (!silent) {
+ QString other = tr("default");
+ if (macro.defaultDef.isEmpty())
+ other = macro.otherDefs.begin().key();
+ config.lastLocation().warning(tr("Macro '\\%1' takes"
+ " inconsistent number"
+ " of arguments (%2"
+ " %3, %4 %5)")
+ .arg(*n)
+ .arg(*f)
+ .arg(m)
+ .arg(other)
+ .arg(macro.numParams));
+ silent = true;
+ }
+ if (macro.numParams < m)
+ macro.numParams = m;
+ }
+ }
+ ++f;
+ }
+
+ if (macro.numParams != -1)
+ macroHash()->insert(*n, macro);
+ ++n;
+ }
+}
+
+void Doc::terminate()
+{
+ DocParser::exampleFiles.clear();
+ DocParser::exampleDirs.clear();
+ DocParser::sourceFiles.clear();
+ DocParser::sourceDirs.clear();
+ aliasMap()->clear();
+ cmdHash()->clear();
+ macroHash()->clear();
+
+ int i = 0;
+ while (cmds[i].english) {
+ delete cmds[i].alias;
+ cmds[i].alias = 0;
+ ++i;
+ }
+}
+
+QString Doc::alias(const QString &english)
+{
+ return aliasMap()->value(english, english);
+}
+
+/*!
+ Trims the deadwood out of \a str. i.e., this function
+ cleans up \a str.
+ */
+void Doc::trimCStyleComment(Location& location, QString& str)
+{
+ QString cleaned;
+ Location m = location;
+ bool metAsterColumn = true;
+ int asterColumn = location.columnNo() + 1;
+ int i;
+
+ for (i = 0; i < (int) str.length(); i++) {
+ if (m.columnNo() == asterColumn) {
+ if (str[i] != '*')
+ break;
+ cleaned += ' ';
+ metAsterColumn = true;
+ }
+ else {
+ if (str[i] == '\n') {
+ if (!metAsterColumn)
+ break;
+ metAsterColumn = false;
+ }
+ cleaned += str[i];
+ }
+ m.advance(str[i]);
+ }
+ if (cleaned.length() == str.length())
+ str = cleaned;
+
+ for (int i = 0; i < 3; i++)
+ location.advance(str[i]);
+ str = str.mid(3, str.length() - 5);
+}
+
+CodeMarker *Doc::quoteFromFile(const Location &location,
+ Quoter &quoter,
+ const QString &fileName)
+{
+ quoter.reset();
+
+ QString code;
+
+ QString userFriendlyFilePath;
+ QString filePath = Config::findFile(location,
+ DocParser::exampleFiles,
+ DocParser::exampleDirs,
+ fileName, userFriendlyFilePath);
+ if (filePath.isEmpty()) {
+ location.warning(tr("Cannot find example file '%1'").arg(fileName));
+ }
+ else {
+ QFile inFile(filePath);
+ if (!inFile.open(QFile::ReadOnly)) {
+ location.warning(tr("Cannot open example file '%1'").arg(userFriendlyFilePath));
+ }
+ else {
+ QTextStream inStream(&inFile);
+ code = DocParser::untabifyEtc(inStream.readAll());
+ }
+ }
+
+ QString dirPath = QFileInfo(filePath).path();
+ CodeMarker *marker = CodeMarker::markerForFileName(fileName);
+ quoter.quoteFromFile(userFriendlyFilePath,
+ code,
+ marker->markedUpCode(code, 0, dirPath));
+ return marker;
+}
+
+QString Doc::canonicalTitle(const QString &title)
+{
+ // The code below is equivalent to the following chunk, but _much_
+ // faster (accounts for ~10% of total running time)
+ //
+ // QRegExp attributeExpr("[^A-Za-z0-9]+");
+ // QString result = title.toLower();
+ // result.replace(attributeExpr, " ");
+ // result = result.simplified();
+ // result.replace(QLatin1Char(' '), QLatin1Char('-'));
+
+ QString result;
+ result.reserve(title.size());
+
+ bool slurping = false;
+ bool begun = false;
+ int lastAlnum = 0;
+ for (int i = 0; i != title.size(); ++i) {
+ uint c = title.at(i).unicode();
+ if (c >= 'A' && c <= 'Z')
+ c -= 'A' - 'a';
+ bool alnum = (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9');
+ if (alnum) {
+ result += QLatin1Char(c);
+ begun = true;
+ slurping = false;
+ lastAlnum = result.size();
+ }
+ else if (!slurping) {
+ if (begun)
+ result += QLatin1Char('-');
+ slurping = true;
+ }
+ else {
+ // !alnum && slurping -> nothin
+ }
+ }
+ result.truncate(lastAlnum);
+ return result;
+}
+
+void Doc::detach()
+{
+ if (!priv) {
+ priv = new DocPrivate;
+ return;
+ }
+ if (priv->count == 1)
+ return;
+
+ --priv->count;
+
+ DocPrivate *newPriv = new DocPrivate(*priv);
+ newPriv->count = 1;
+ if (priv->extra)
+ newPriv->extra = new DocPrivateExtra(*priv->extra);
+
+ priv = newPriv;
+}
+
+#ifdef QDOC2DOX
+/*!
+ Sets the doxygen writer pass to \a pass. You can use
+ isDoxPass(), with or without a parameter, to test if
+ you are in a doxygen writer run or in a specific pass
+ of a doxygen writer run.
+
+ This function is only called from main() if either the
+ \e doxygen1 or \e doxygen2 flag is passed to qdoc3 on
+ the command line.
+ */
+void DoxWriter::setDoxPass(int pass)
+{
+ qDebug() << "SETTING doxygen pass to " << pass
+ << " in DoxWriter::setDoxPass()";
+ doxPass = pass;
+}
+
+/*!
+ Returns true if the doxygen pass is set to \a pass,
+ which means we are in the specified \a pass of a doxygen
+ writer run of qdoc3.
+ */
+bool DoxWriter::isDoxPass(int pass) { return (doxPass == pass); }
+
+/*!
+ Returns true if the doxygen pass is 1 or 2, which
+ means this is a doxygen writer run to transform qdoc
+ comments into doxygen comments.
+ */
+bool DoxWriter::isDoxPass() { return (doxPass > 0); }
+
+bool DoxWriter::conversionRequired() const
+{
+ /*
+ Loop through all the topic commands searching for
+ one that must be transformed to doxygen format. If
+ one is found, return true.
+ */
+ QCommandMap::const_iterator i;
+ i = priv->metaCommandMap.constBegin();
+ while (i != priv->metaCommandMap.constEnd()) {
+ QString s = i.key();
+ if (s == "enum")
+ return true;
+ else if (s == "example")
+ return true;
+ else if (s == "externalpage")
+ return true;
+ else if (s == "group")
+ return true;
+ else if (s == "headerfile")
+ return true;
+ else if (s == "module")
+ return true;
+ else if (s == "page")
+ return true;
+ else if (s == "property")
+ return true;
+ else if (s == "typedef")
+ return true;
+ else if (s == "variable")
+ return true;
+ else if (s == "overload")
+ return true;
+ else if (s == "reimp")
+ return true;
+ else if (s == "relates")
+ return true;
+ else if (s == "macro")
+ return true;
+ else {
+#if 0
+ if (s == "class")
+ else if (s == "namespace")
+ else if (s == "service")
+ else if (s == "inheaderfile")
+ else if (s == "file")
+ else if (s == "fn")
+ else if (s == "contentspage")
+ else if (s == "nextpage")
+ else if (s == "previous")
+ else if (s == "indexpage")
+ else if (s == "startpage")
+#endif
+ }
+ ++i;
+ }
+
+ /*
+ Loop through all the qdoc atoms searching for one
+ that must be transformed to doxygen format. If one
+ is found, return true.
+ */
+ const Atom* next = priv->text.firstAtom();
+ while (next != 0) {
+ Atom::Type atomType = next->type();
+ switch (atomType) {
+ case Atom::C:
+ case Atom::CaptionLeft:
+ case Atom::Code:
+ case Atom::CodeBad:
+ case Atom::CodeNew:
+ case Atom::CodeOld:
+ case Atom::CodeQuoteArgument:
+ case Atom::CodeQuoteCommand:
+ case Atom::FootnoteLeft:
+ case Atom::FormatElse:
+ case Atom::FormatEndif:
+ case Atom::FormatIf:
+ case Atom::GeneratedList:
+ case Atom::Image:
+ case Atom::ImageText:
+ case Atom::InlineImage:
+ case Atom::LegaleseLeft:
+ case Atom::LineBreak:
+ case Atom::Link:
+ case Atom::LinkNode:
+ case Atom::ListLeft:
+ case Atom::ListItemNumber:
+ case Atom::ListTagLeft:
+ case Atom::ListItemLeft:
+ case Atom::QuotationLeft:
+ case Atom::RawString:
+ case Atom::SectionLeft:
+ case Atom::SectionHeadingLeft:
+ case Atom::SidebarLeft:
+ case Atom::SnippetCommand:
+ case Atom::SnippetIdentifier:
+ case Atom::SnippetLocation:
+ case Atom::TableLeft:
+ case Atom::TableHeaderLeft:
+ case Atom::TableRowLeft:
+ case Atom::TableItemLeft:
+ case Atom::TableOfContents:
+ case Atom::Target:
+ return true;
+ case Atom::AbstractLeft:
+ case Atom::AbstractRight:
+ case Atom::AutoLink:
+ case Atom::BaseName:
+ case Atom::BriefLeft:
+ case Atom::BriefRight:
+ case Atom::CaptionRight:
+ case Atom::FormattingLeft:
+ case Atom::FormattingRight:
+ case Atom::Nop:
+ case Atom::ParaLeft:
+ case Atom::ParaRight:
+ case Atom::FootnoteRight:
+ case Atom::LegaleseRight:
+ case Atom::ListTagRight:
+ case Atom::ListItemRight:
+ case Atom::ListRight:
+ case Atom::QuotationRight:
+ case Atom::SectionRight:
+ case Atom::SectionHeadingRight:
+ case Atom::SidebarRight:
+ case Atom::String:
+ case Atom::TableRight:
+ case Atom::TableHeaderRight:
+ case Atom::TableRowRight:
+ case Atom::TableItemRight:
+ default:
+ break;
+ }
+ next = next->next();
+ }
+ return false;
+}
+
+/*!
+ A convenience function to write a qdoc metacommand as a
+ doxygen command, without conversion. i.e., some of the
+ qdoc metacommands don't require conversion for doxygen.
+ */
+void DoxWriter::writeCommand(QCommandMap::const_iterator cmd)
+{
+ concatenate("\\" + cmd.key() + " " + cmd.value()[0]);
+ newLine();
+}
+
+/*!
+ Convert the qdoc commands in the metacommand map to
+ doxygen format. This function is called only in pass2().
+ The metacommand map contains all the metacommands that
+ were found in the qdoc comment that is being converted.
+ The metacommands are the ones that begin with the '\'.
+ These are not considered part of the text of the comment.
+ The text is converted by convertText().
+ */
+void DoxWriter::convertMetaCommands()
+{
+ QCommandMap& metaCmdMap = priv->metaCommandMap;
+ QCommandMap::iterator cmd;
+ int c;
+
+ currentPage.clear();
+ currentFn.clear();
+ currentTitle.clear();
+ currentEnum.clear();
+ currentProperty.clear();
+ currentVariable.clear();
+ currentClass.clear();
+ currentExample.clear();
+ currentGroup.clear();
+ currentModule.clear();
+ currentMacro.clear();
+ currentService.clear();
+ currentTypedef.clear();
+ currentHeaderFile.clear();
+ commentType = OtherComment;
+
+ if ((cmd = metaCmdMap.find("class")) != metaCmdMap.end()) {
+ currentClass = cmd.value()[0];
+ if ((c = currentClass.indexOf(' ')) > 0)
+ currentClass = currentClass.left(c);
+ writeCommand(cmd);
+ metaCmdMap.erase(cmd);
+ commentType = ClassComment;
+ }
+ else if ((cmd = metaCmdMap.find("fn")) != metaCmdMap.end()) {
+ currentFn = cmd.value()[0];
+ writeCommand(cmd);
+ metaCmdMap.erase(cmd);
+ commentType = FnComment;
+ }
+ else if ((cmd = metaCmdMap.find("enum")) != metaCmdMap.end()) {
+ currentEnum = cmd.value()[0];
+ if ((c = currentEnum.lastIndexOf("::")) > 0) {
+ currentClass = currentEnum.left(c);
+ currentEnum = currentEnum.right(currentEnum.size()-c-2);
+ qDebug() << "currentEnum =" << currentEnum;
+ qDebug() << "currentClass =" << currentClass;
+ }
+ writeCommand(cmd);
+ metaCmdMap.erase(cmd);
+ commentType = EnumComment;
+ }
+ else if ((cmd = metaCmdMap.find("property")) != metaCmdMap.end()) {
+ currentClass = cmd.value()[0];
+ if ((c = currentClass.lastIndexOf("::")) > 0) {
+ currentProperty = currentClass.right(currentClass.size()-c-2);
+ currentClass = currentClass.left(c);
+ qDebug() << "currentProperty =" << currentProperty;
+ qDebug() << "currentClass =" << currentClass;
+ }
+ writeCommand(cmd);
+ metaCmdMap.erase(cmd);
+ commentType = PropertyComment;
+ }
+ else if ((cmd = metaCmdMap.find("variable")) != metaCmdMap.end()) {
+ currentClass = cmd.value()[0];
+ if ((c = currentClass.lastIndexOf("::")) > 0) {
+ currentVariable = currentClass.right(currentClass.size()-c-2);
+ currentClass = currentClass.left(c);
+ qDebug() << "currentVariable =" << currentVariable;
+ qDebug() << "currentClass =" << currentClass;
+ }
+ concatenate("\\var " + cmd.value()[0]);
+ newLine();
+ metaCmdMap.erase(cmd);
+ commentType = VariableComment;
+ }
+
+ if ((cmd = metaCmdMap.find("page")) != metaCmdMap.end()) {
+ currentPage = cmd.value()[0];
+ QString htmlFile = currentPage;
+ const QString* title = getPageTitle(htmlFile);
+ QStringList parts = htmlFile.split('.');
+ metaCmdMap.erase(cmd);
+ if (title) {
+ concatenate("\\page " + parts[0] + " " + *title);
+ newLine();
+ }
+ commentType = PageComment;
+ qDebug() << "currentPage =" << currentPage;
+ }
+
+ if ((cmd = metaCmdMap.find("example")) != metaCmdMap.end()) {
+ currentExample = cmd.value()[0];
+ metaCmdMap.erase(cmd);
+ commentType = ExampleComment;
+ qDebug() << "currentExample =" << currentExample;
+ }
+
+ if ((cmd = metaCmdMap.find("macro")) != metaCmdMap.end()) {
+ currentMacro = cmd.value()[0];
+ metaCmdMap.erase(cmd);
+ commentType = MacroComment;
+ qDebug() << "currentMacro =" << currentMacro;
+ }
+
+ if ((cmd = metaCmdMap.find("group")) != metaCmdMap.end()) {
+ currentGroup = cmd.value()[0];
+ metaCmdMap.erase(cmd);
+ commentType = GroupComment;
+ qDebug() << "currentGroup =" << currentGroup;
+ }
+
+ if ((cmd = metaCmdMap.find("module")) != metaCmdMap.end()) {
+ currentModule = cmd.value()[0];
+ metaCmdMap.erase(cmd);
+ commentType = ModuleComment;
+ qDebug() << "currentModule =" << currentModule;
+ }
+
+ if ((cmd = metaCmdMap.find("headerfile")) != metaCmdMap.end()) {
+ currentHeaderFile = cmd.value()[0];
+ metaCmdMap.erase(cmd);
+ commentType = HeaderFileComment;
+ qDebug() << "currentHeaderFile =" << currentHeaderFile;
+ }
+
+ if ((cmd = metaCmdMap.find("typedef")) != metaCmdMap.end()) {
+ currentClass = cmd.value()[0];
+ if ((c = currentClass.lastIndexOf("::")) > 0) {
+ currentTypedef = currentClass.right(currentClass.size()-c-2);
+ currentClass = currentClass.left(c);
+ }
+ metaCmdMap.erase(cmd);
+ commentType = TypedefComment;
+ qDebug() << "currentTypedef =" << currentTypedef;
+ qDebug() << "currentClass =" << currentClass;
+ }
+
+ cmd = priv->metaCommandMap.begin();
+ while (cmd != priv->metaCommandMap.end()) {
+ for (int i=0; i<cmd.value().size(); i++) {
+ concatenate("\\" + cmd.key() + " " + cmd.value()[i]);
+ newLine();
+ }
+ //qDebug() << " " << cmd.key() << ": " << cmd.value();
+ ++cmd;
+ }
+}
+
+/*!
+ Convert the qdoc text to doxygen format. The metacommands
+ are converted by convertMetaCommands(). This function is
+ called in pass2().
+ */
+void DoxWriter::convertText()
+{
+ const Atom* prev = 0;
+ const Atom* next = priv->text.firstAtom();
+ while (next != 0) {
+ next->dump();
+ Atom::Type atomType = next->type();
+ switch (atomType) {
+ case Atom::AbstractLeft:
+ break;
+ case Atom::AbstractRight:
+ break;
+ case Atom::AutoLink:
+ concatenate(next->string());
+ break;
+ case Atom::BaseName:
+ break;
+ case Atom::BriefLeft:
+ concatenate("\\brief ");
+ break;
+ case Atom::BriefRight:
+ newLine();
+ break;
+ case Atom::C:
+ tt(next);
+ break;
+ case Atom::CaptionLeft:
+ unhandled(next);
+ break;
+ case Atom::CaptionRight:
+ unhandled(next);
+ break;
+ case Atom::Code:
+ code(next);
+ break;
+ case Atom::CodeBad:
+ code(next);
+ break;
+ case Atom::CodeNew:
+ newLine();
+ concatenate("you can rewrite it as");
+ code(next);
+ break;
+ case Atom::CodeOld:
+ newLine();
+ concatenate("For example, if you have code like");
+ code(next);
+ break;
+ case Atom::CodeQuoteArgument:
+ unhandled(next);
+ break;
+ case Atom::CodeQuoteCommand:
+ next = codeQuoteCommand(next);
+ break;
+ case Atom::FootnoteLeft:
+ break;
+ case Atom::FootnoteRight:
+ break;
+ case Atom::FormatElse:
+ formatElse();
+ break;
+ case Atom::FormatEndif:
+ formatEndif();
+ break;
+ case Atom::FormatIf:
+ formatIf(next);
+ break;
+ case Atom::FormattingLeft:
+ formattingLeft(next,next->next());
+ break;
+ case Atom::FormattingRight:
+ formattingRight(next,prev);
+ break;
+ case Atom::GeneratedList:
+ break;
+ case Atom::Image:
+ break;
+ case Atom::ImageText:
+ break;
+ case Atom::InlineImage:
+ break;
+ case Atom::LegaleseLeft:
+ break;
+ case Atom::LegaleseRight:
+ break;
+ case Atom::LineBreak:
+ break;
+ case Atom::Link:
+ next = link(next);
+ break;
+ case Atom::LinkNode:
+ break;
+ case Atom::ListLeft:
+ {
+ bool nested = false;
+ if (structs.isEmpty()) {
+ const Atom* i = next->next();
+ while (i->type() != Atom::ListRight) {
+ if ((i->type() == Atom::ListLeft) ||
+ (i->type() == Atom::TableLeft)) {
+ nested = true;
+ break;
+ }
+ i = i->next();
+ }
+ }
+ else
+ nested = true;
+ StructDesc d(BulletList,nested);
+ if (next->string() == "numeric")
+ d.structType = NumericList;
+ else if (next->string() == "value") {
+ d.structType = ValueList;
+ }
+ else if (next->string() != "bullet")
+ qDebug() << "UNKNOWN LIST TYPE" << next->string();
+ structs.push(d);
+ if (nested || (d.structType != BulletList)) {
+ if (d.structType == BulletList)
+ concatenate("<ul>");
+ else if (d.structType == NumericList)
+ concatenate("<ol>");
+ else if (d.structType == ValueList)
+ concatenate("<dl>");
+ newLine();
+ }
+ }
+ break;
+ case Atom::ListItemNumber:
+ structs.top().count = next->string().toInt();
+ break;
+ case Atom::ListTagLeft:
+ {
+ structs.top().count++;
+ concatenate("<dt>");
+ const Atom* n = next->next();
+ if (n->type() == Atom::String) {
+ qDebug() << "ENUM VALUE" << n->string();
+ }
+ else
+ qDebug() << "NOT EN ENUM";
+ }
+ break;
+ case Atom::ListTagRight:
+ concatenate("</dt>");
+ break;
+ case Atom::ListItemLeft:
+ {
+ newLine();
+ const StructDesc& d = structs.top();
+ if (d.structType == BulletList) {
+ if (!d.nested)
+ concatenate("\\arg ");
+ else
+ concatenate("<li>");
+ }
+ else if (d.structType == NumericList)
+ concatenate("<li>");
+ else if (d.structType == ValueList)
+ concatenate("<dd>");
+ }
+ break;
+ case Atom::ListItemRight:
+ {
+ const StructDesc& d = structs.top();
+ if (d.structType == BulletList) {
+ if (d.nested) {
+ concatenate("</li>");
+ newLine();
+ }
+ }
+ else if (d.structType == NumericList) {
+ concatenate("</li>");
+ newLine();
+ }
+ else if (d.structType == ValueList) {
+ concatenate("</dd>");
+ newLine();
+ }
+ }
+ break;
+ case Atom::ListRight:
+ {
+ if (!structs.isEmpty()) {
+ const StructDesc& d = structs.top();
+ if (d.nested || (d.structType != BulletList)) {
+ if (d.structType == BulletList)
+ concatenate("</ul>");
+ else if (d.structType == NumericList)
+ concatenate("</ol>");
+ else if (d.structType == ValueList)
+ concatenate("</dl>");
+ newLine();
+ }
+ structs.pop();
+ }
+ }
+ break;
+ case Atom::Nop:
+ // nothing.
+ break;
+ case Atom::ParaLeft:
+ if (structs.isEmpty())
+ newLine();
+ break;
+ case Atom::ParaRight:
+ {
+ if (structs.isEmpty())
+ newLine();
+ else {
+ const StructDesc& d = structs.top();
+ if (d.nested || (d.structType != BulletList)) {
+ Atom::Type t = next->next()->type();
+ if ((t != Atom::ListItemRight) &&
+ (t != Atom::TableItemRight))
+ newLine();
+ }
+ else
+ newLine();
+ }
+ }
+ break;
+ case Atom::QuotationLeft:
+ break;
+ case Atom::QuotationRight:
+ break;
+ case Atom::RawString:
+ concatenate(next->string());
+ break;
+ case Atom::SectionLeft:
+ // nothing.
+ break;
+ case Atom::SectionRight:
+ // nothing.
+ break;
+ case Atom::SectionHeadingLeft:
+ next = sectionHeading(next);
+ break;
+ case Atom::SectionHeadingRight:
+ newLine();
+ break;
+ case Atom::SidebarLeft:
+ break;
+ case Atom::SidebarRight:
+ break;
+ case Atom::SnippetCommand:
+ newLine();
+ concatenate("\\snippet ");
+ break;
+ case Atom::SnippetIdentifier:
+ newText += next->string();
+ lineLength += next->string().size();
+ newLine();
+ break;
+ case Atom::SnippetLocation:
+ newText += next->string() + " ";
+ lineLength += next->string().size() + 1;
+ break;
+ case Atom::String:
+ wrap(next->string());
+ break;
+ case Atom::TableLeft:
+ {
+ bool nested = false;
+ if (structs.isEmpty()) {
+ const Atom* i = next->next();
+ while (i->type() != Atom::TableRight) {
+ if ((i->type() == Atom::ListLeft) ||
+ (i->type() == Atom::TableLeft)) {
+ nested = true;
+ break;
+ }
+ i = i->next();
+ }
+ }
+ else
+ nested = true;
+ StructDesc d(Table,nested);
+ structs.push(d);
+ if (next->string().isEmpty())
+ concatenate("<table>");
+ else {
+ QString attrs = "width=\"" + next->string() + "\"";
+ attrs += " align=\"center\"";
+ concatenate("<table " + attrs + ">");
+ }
+ newLine();
+ }
+ break;
+ case Atom::TableRight:
+ concatenate("</table>");
+ if (!structs.isEmpty())
+ structs.pop();
+ newLine();
+ break;
+ case Atom::TableHeaderLeft:
+ concatenate("<tr>");
+ if (!structs.isEmpty())
+ structs.top().inTableHeader = true;
+ newLine();
+ break;
+ case Atom::TableHeaderRight:
+ concatenate("</tr>");
+ if (!structs.isEmpty())
+ structs.top().inTableHeader = false;
+ newLine();
+ break;
+ case Atom::TableRowLeft:
+ if (!structs.isEmpty()) {
+ structs.top().inTableRow = true;
+ concatenate("<tr valign=\"top\" class=\"");
+ if (structs.top().odd)
+ concatenate("odd\">");
+ else
+ concatenate("even\">");
+ structs.top().odd = !structs.top().odd;
+ }
+ newLine();
+ break;
+ case Atom::TableRowRight:
+ concatenate("</tr>");
+ if (!structs.isEmpty())
+ structs.top().inTableRow = false;
+ newLine();
+ break;
+ case Atom::TableItemLeft:
+ if (!structs.isEmpty()) {
+ structs.top().inTableItem = true;
+ concatenate("<td>");
+ if (structs.top().inTableHeader)
+ concatenate("<b> ");
+ }
+ break;
+ case Atom::TableItemRight:
+ if (!structs.isEmpty()) {
+ structs.top().inTableItem = false;
+ if (structs.top().inTableHeader)
+ concatenate(" </b>");
+ concatenate("</td>");
+ }
+ newLine();
+ break;
+ case Atom::TableOfContents:
+ break;
+ case Atom::Target:
+ {
+ QString text = next->string();
+ text.remove(ws_rx);
+ newLine();
+ concatenate("\\anchor ");
+ newText += text;
+ lineLength += text.size();
+ newLine();
+ }
+ break;
+ case Atom::UnhandledFormat:
+ unhandled(next);
+ break;
+ case Atom::UnknownCommand:
+ unhandled(next);
+ break;
+ default:
+ //next->dump();
+ break;
+ }
+ prev = next;
+ next = next->next();
+ }
+}
+
+/*!
+
+ Pass one looks for topic commands and target and section
+ commands, and maybe other stuff. These are serialized to
+ text files, which are read back in by pass2().
+ */
+void DoxWriter::pass1()
+{
+ QCommandMap& metaCmdMap = priv->metaCommandMap;
+ if (!metaCmdMap.isEmpty()) {
+ int c;
+ QCommandMap::iterator cmd;
+ if ((cmd = metaCmdMap.find("enum")) != metaCmdMap.end()) {
+ commentType = EnumComment;
+ currentEnum = cmd.value()[0];
+ if ((c = currentEnum.lastIndexOf("::")) > 0) {
+ currentClass = currentEnum.left(c);
+ currentEnum = currentEnum.right(currentEnum.size()-c-2);
+ qDebug() << "currentEnum =" << currentEnum;
+ qDebug() << "currentClass =" << currentClass;
+ if (enums.contains(currentEnum,currentClass)) {
+ qWarning() << "DoxWriter::pass1():"
+ << "Duplicate enum:"
+ << currentClass << currentEnum;
+ }
+ else
+ enums.insert(currentEnum,currentClass);
+ }
+ }
+ else if ((cmd = metaCmdMap.find("property")) != metaCmdMap.end()) {
+ commentType = PropertyComment;
+ currentClass = cmd.value()[0];
+ if ((c = currentClass.lastIndexOf("::")) > 0) {
+ currentProperty = currentClass.right(currentClass.size()-c-2);
+ currentClass = currentClass.left(c);
+ qDebug() << "currentProperty =" << currentProperty;
+ qDebug() << "currentClass =" << currentClass;
+ if (properties.contains(currentProperty,currentClass)) {
+ qWarning() << "DoxWriter::pass1():"
+ << "Duplicate property:"
+ << currentClass << currentProperty;
+ }
+ else
+ properties.insert(currentProperty,currentClass);
+ }
+ }
+ else if ((cmd = metaCmdMap.find("variable")) != metaCmdMap.end()) {
+ commentType = VariableComment;
+ currentClass = cmd.value()[0];
+ if ((c = currentClass.lastIndexOf("::")) > 0) {
+ currentVariable = currentClass.right(currentClass.size()-c-2);
+ currentClass = currentClass.left(c);
+ qDebug() << "currentVariable =" << currentVariable;
+ qDebug() << "currentClass =" << currentClass;
+ if (variables.contains(currentVariable,currentClass)) {
+ qWarning() << "DoxWriter::pass1():"
+ << "Duplicate variable:"
+ << currentClass << currentVariable;
+ }
+ else
+ variables.insert(currentVariable,currentClass);
+ }
+ }
+ }
+
+ /*
+ */
+ const Atom* next = priv->text.firstAtom();
+ while (next != 0) {
+ switch (next->type()) {
+ case Atom::SectionHeadingLeft:
+ {
+ QString text;
+ next = next->next();
+ while (next) {
+ if (next->type() == Atom::SectionHeadingRight)
+ break;
+ else
+ text += next->string();
+ next = next->next();
+ }
+ //text.remove(ws_rx);
+ insertAnchor(text);
+ }
+ break;
+ case Atom::Target:
+ {
+ QString text = next->string();
+ //text.remove(ws_rx);
+ insertAnchor(text);
+ }
+ default:
+ break;
+ }
+ next = next->next();
+ }
+}
+
+/*!
+ Output a parsed, tokenized qdoc comment as a doxygen
+ comment in diff format for input to the patch command.
+ */
+void DoxWriter::pass2()
+{
+ if (!conversionRequired()) {
+ qDebug() << "NO CONVERSION - FILE:" << priv->start_loc.fileName()
+ << "START:" << priv->start_loc.lineNo()
+ << "END:" << priv->end_loc.lineNo() - 1;
+ return;
+ }
+
+ /*
+ Transformation to doxygen required...
+ */
+ newText = "\n/*! \n";
+ convertMetaCommands();
+ convertText();
+ if (newText[newText.size()-1] == ' ')
+ newText.remove(newText.size()-1,1);
+ newText += " */\n";
+ qDebug() << "CONVERTED COMMENT - FILE:" << priv->start_loc.fileName()
+ << "START:" << priv->start_loc.lineNo()
+ << "END:" << priv->end_loc.lineNo() - 1;
+ qDebug() << newText;
+}
+
+/*!
+ Unparse the second parameter of a "\l" command.
+ */
+const Atom* DoxWriter::link(const Atom* atom)
+{
+ QString first_text = atom->string();
+ QString second_text;
+ const QString* value = 0;
+
+ const Atom* next = atom->next(Atom::FormattingLeft,Atom::LINK_);
+ if (next) {
+ next->dump();
+ while (1) {
+ next = next->next();
+ next->dump();
+ if (next->type() == Atom::FormattingRight) {
+ if (next->string() == Atom::LINK_)
+ break;
+ else {
+ // ignore it.
+ }
+ }
+ else
+ second_text += next->string();
+ }
+ int i = first_text.indexOf('#');
+ if (i >= 0)
+ first_text = first_text.right(first_text.size() - i - 1);
+ //newLine();
+ if ((value = getExternalPage(first_text))) {
+ //qDebug() << "USED AN EXTERNAL PAGE TITLE" << first_text;
+ QString href = "<a href=\""+*value+"\">"+first_text+"</a>";
+ concatenate(href);
+ }
+ else if (first_text.startsWith("http:",Qt::CaseInsensitive)) {
+ if (first_text == second_text) {
+ concatenate(first_text);
+ }
+ else {
+ QString href = "<a href=\""+first_text+"\">"+second_text+"</a>";
+ concatenate(href);
+ }
+ }
+ else if ((value = getPageFile(first_text))) {
+ //qDebug() << "USED A PAGE TITLE" << first_text;
+ QStringList parts = (*value).split('.');
+ QString ref = "\\ref " + parts[0] + " \"" + second_text + "\"";
+ concatenate(ref);
+ }
+ else if ((value = getGroup(first_text))) {
+ //qDebug() << "USED A GROUP TITLE" << first_text;
+ concatenate("\\ref " + *value + " \"" + second_text + "\"");
+ }
+ else if ((value = getModule(first_text))) {
+ //qDebug() << "USED A MODULE TITLE" << first_text;
+ concatenate("\\ref " + *value + " \"" + second_text + "\"");
+ }
+ else if ((value = getExamplePath(first_text))) {
+ //qDebug() << "USED AN EXAMPLE TITLE" << first_text;
+ first_text.remove(ws_rx);
+ QString ref = "\\ref " + first_text + " \"" + second_text + "\"";
+ concatenate(ref);
+ }
+ else if ((value = getFile(first_text))) {
+ //qDebug() << "USED A FILE TITLE" << first_text;
+ // I think this command is no longer available.
+ first_text.remove(ws_rx);
+ QString ref = "\\ref " + first_text + " \"" + second_text + "\"";
+ concatenate(ref);
+ }
+ else if ((value = getHeaderFile(first_text))) {
+ //qDebug() << "USED A HEADER FILE TITLE" << first_text;
+ first_text.remove(ws_rx);
+ QString ref = "\\ref " + first_text + " \"" + second_text + "\"";
+ concatenate(ref);
+ }
+ else if (isAnchor(first_text)) {
+ //qDebug() << "USED AN ANCHOR" << first_text;
+ first_text.remove(ws_rx);
+ QString ref = "\\ref " + first_text + " \"" + second_text + "\"";
+ concatenate(ref);
+ }
+ else if ((value = getPageTitle(first_text))) {
+ //qDebug() << "USED AN INVERSE PAGE TITLE" << first_text;
+ QStringList parts = first_text.split('.');
+ QString ref = "\\ref " + parts[0] + " \"" + second_text + "\"";
+ concatenate(ref);
+ }
+ else if ((value = getExampleTitle(first_text))) {
+ //qDebug() << "USED AN INVERSE EXAMPLE TITLE" << first_text;
+ QString title = *value;
+ title.remove(ws_rx);
+ QString ref = "\\ref " + title + " \"" + second_text + "\"";
+ concatenate(ref);
+ }
+ else if ((value = getGroupTitle(first_text))) {
+ //qDebug() << "USED AN INVERSE GROUP TITLE" << first_text;
+ concatenate("\\ref " + first_text + " \"" + second_text + "\"");
+ }
+ else if ((value = getModuleTitle(first_text))) {
+ //qDebug() << "USED AN INVERSE MODULE TITLE" << first_text;
+ concatenate("\\ref " + first_text + " \"" + second_text + "\"");
+ }
+ else if ((value = getFileTitle(first_text))) {
+ qDebug() << "USED AN INVERSE FILE TITLE" << first_text;
+ }
+ else if ((value = getHeaderFileTitle(first_text))) {
+ qDebug() << "USED AN INVERSE HEADER FILE TITLE" << first_text;
+ }
+ else if ((first_text.indexOf("::") >= 0) ||
+ (first_text.indexOf("()") >= 0) ||
+ (first_text[0] == 'Q')) {
+ //qDebug() << "AUTO-LINKABLE" << first_text;
+ if (first_text == second_text)
+ concatenate(first_text);
+ else {
+ QString link = first_text + " " + second_text;
+ concatenate("\\link " + link + "\\endlink");
+ }
+ }
+ else {
+ QString link;
+ QStringList propertyClasses;
+ QStringList variableClasses;
+ QStringList enumClasses;
+ bool p = isProperty(first_text,propertyClasses);
+ bool v = isVariable(first_text,variableClasses);
+ bool e = isEnum(first_text,enumClasses);
+ if (e) {
+ if (enumClasses.size() == 1)
+ link = enumClasses[0];
+ else if (enumClasses.contains(currentClass))
+ link = currentClass;
+ else {
+ QString msg = "Unqualified enum name: " + first_text;
+ QString details = "Classes: " + enumClasses.join(", ");
+ priv->start_loc.error(msg,details);
+ }
+ if (!link.isEmpty())
+ qDebug() << "FOUND ENUM" << link << first_text;
+ }
+ else if (p && v) {
+ if (propertyClasses.size() == 1) {
+ if (variableClasses.size() == 1) {
+ if (propertyClasses[0] == variableClasses[0])
+ link = propertyClasses[0];
+ }
+ }
+ if (link.isEmpty()) {
+ if (propertyClasses.contains(currentClass) ||
+ variableClasses.contains(currentClass))
+ link = currentClass;
+ else {
+ propertyClasses += variableClasses;
+ QString msg = "Unqualified property or variable name: "
+ + first_text;
+ QString details = "Classes: " +
+ propertyClasses.join(", ");
+ priv->start_loc.error(msg,details);
+ }
+ }
+ }
+ else if (p) {
+ if (propertyClasses.size() == 1)
+ link = propertyClasses[0];
+ else if (propertyClasses.contains(currentClass))
+ link = currentClass;
+ else {
+ QString msg = "Unqualified property name: " + first_text;
+ QString details = "Classes: " + propertyClasses.join(", ");
+ priv->start_loc.error(msg,details);
+ }
+ }
+ else if (v) {
+ if (variableClasses.size() == 1)
+ link = variableClasses[0];
+ else if (variableClasses.contains(currentClass))
+ link = currentClass;
+ else {
+ QString msg = "Unqualified variable name: " + first_text;
+ QString details = "Classes: " + variableClasses.join(", ");
+ priv->start_loc.error(msg,details);
+ }
+ }
+ else {
+ qDebug() << "NOT AUTO-LINKABLE" << first_text;
+ QString s = first_text + " " + second_text;
+ concatenate("\\link " + s + "\\endlink");
+ }
+ if (!link.isEmpty()) {
+ link += "::" + first_text + " " + second_text;
+ concatenate("\\link " + link + "\\endlink");
+ }
+ }
+ }
+ else
+ qDebug() << "LINK with no second parameter!!!!";
+ return next? next : atom;
+}
+
+/*!
+ If the current line length is 0, the current line is
+ indented according to the context.
+ */
+void DoxWriter::indentLine()
+{
+ if (lineLength == 0) {
+ newText += DOXYGEN_INDENT_STRING;
+ lineLength = DOXYGEN_INDENT;
+ if (!structs.isEmpty()) {
+ for (int i=1; i<structs.size(); ++i) {
+ newText += DOXYGEN_TAB_STRING;
+ lineLength += DOXYGEN_TAB_SIZE;
+ }
+ }
+ }
+}
+
+/*!
+ Concatenates a newline to the doxygen text, increments the
+ line count, and resets the line length to 0.
+ */
+void DoxWriter::newLine()
+{
+ newText += "\n";
+ ++lineCount;
+ lineLength = 0;
+}
+
+static const int maxLineLength = 70;
+
+/*!
+ Concatenate the \a text to the doxygen comment currently
+ under construction and increment the current line length
+ by the size of the \a text.
+
+ If incrementing the current line length by the \a text size
+ would make the current line length longer than the maximum
+ line length, then call newLine() and indentLine() \e before
+ concatenating the \a text.
+ */
+void DoxWriter::concatenate(QString text)
+{
+ if ((lineLength + text.size()) > maxLineLength)
+ newLine();
+ indentLine();
+ newText += text;
+ lineLength += text.size();
+}
+
+static bool punctuation(QChar c)
+{
+ switch (c.toAscii()) {
+ case '.':
+ case ',':
+ case ':':
+ case ';':
+ case '/':
+ case '+':
+ case '-':
+ case '?':
+ case '!':
+ case '\"':
+ return true;
+ default:
+ break;
+ }
+ return false;
+}
+
+/*!
+ Concatenate the \a text string to the doxygen text, doing
+ line wrapping where necessary.
+ */
+void DoxWriter::wrap(QString text)
+{
+ int from = 0;
+ int to = -1;
+
+ if ((lineLength == 0) || (lineLength >= maxLineLength)) {
+ if (!text.isEmpty() && (text[0] == ' '))
+ text = text.right(text.size() - 1);
+ }
+
+ indentLine();
+ while (text.size()) {
+ int avail = maxLineLength - lineLength;
+ from = text.indexOf(' ',from);
+ if (from >= 0) {
+ if (from < avail)
+ to = from++;
+ else if (from == 1 && punctuation(text[0]))
+ to = from++;
+ else {
+ if (to >= 0) {
+ newText += text.left(to+1);
+ lineLength += to + 1;
+ text = text.right(text.size() - to - 1);
+ }
+ else {
+ newLine();
+ indentLine();
+ newText += text.left(from+1);
+ lineLength += from + 1;
+ text = text.right(text.size() - from - 1);
+ }
+ from = 0;
+ to = -1;
+ if (text.size() && (lineLength > maxLineLength)) {
+ newLine();
+ indentLine();
+ }
+ }
+ }
+ else
+ break;
+ }
+ if (text.size()) {
+ if (lineLength >= maxLineLength) {
+ newLine();
+ indentLine();
+ }
+ newText += text;
+ lineLength += text.size();
+ }
+}
+
+/*!
+ This will output something, but it depends on what the
+ \a atom string and the \a next atom string are.
+ */
+void DoxWriter::formattingLeft(const Atom* atom, const Atom* next)
+{
+ if (atom->string() == "parameter") {
+ concatenate("\\a ");
+ return;
+ }
+ else if (atom->string() == "underline") {
+ concatenate("<u>");
+ return;
+ }
+ else if (atom->string() == "superscript") {
+ concatenate("<sup>");
+ return;
+ }
+ else if (atom->string() == "subscript") {
+ concatenate("<sub>");
+ return;
+ }
+ int ws = -1;
+ if (next)
+ ws = next->string().indexOf(ws_rx);
+ if (atom->string() == "bold") {
+ if (ws < 0)
+ concatenate("\\b ");
+ else
+ concatenate("<b>");
+ }
+ else if (atom->string() == "italic") {
+ if (ws < 0)
+ concatenate("\\e ");
+ else
+ concatenate("<i>");
+ }
+ else if (atom->string() == "teletype") {
+ if (ws < 0)
+ concatenate("\\c ");
+ else
+ concatenate("<tt>");
+ }
+ else
+ qDebug() << "UNHANDLED FormattingLeft: " << atom->string();
+}
+
+/*!
+ This will output something, but it depends on what the
+ \a atom string and the \a prev atom string are.
+ */
+void DoxWriter::formattingRight(const Atom* atom, const Atom* prev)
+{
+ if (atom->string() == "parameter")
+ return;
+ else if (atom->string() == "underline") {
+ concatenate("</u>");
+ return;
+ }
+ else if (atom->string() == "superscript") {
+ concatenate("</sup>");
+ return;
+ }
+ else if (atom->string() == "subscript") {
+ concatenate("</sub>");
+ return;
+ }
+ int ws = -1;
+ if (prev)
+ ws = prev->string().indexOf(ws_rx);
+ if (ws < 0)
+ return;
+ if (atom->string() == "bold")
+ concatenate("</b>");
+ else if (atom->string() == "italic")
+ concatenate("</i>");
+ else if (atom->string() == "teletype")
+ concatenate("</tt>");
+ else
+ qDebug() << "UNHANDLED FormattingRight: " << atom->string();
+}
+
+/*!
+ Output a \c or a <tt>...</tt>.
+ */
+void DoxWriter::tt(const Atom* atom)
+{
+ if (atom->string().indexOf(ws_rx) < 0) {
+ concatenate("\\c ");
+ concatenate(atom->string());
+ }
+ else {
+ concatenate("<tt>");
+ concatenate(atom->string());
+ concatenate("</tt>");
+ }
+}
+
+/*!
+ */
+void DoxWriter::formatIf(const Atom* atom)
+{
+ if (atom->string() == "HTML") {
+ newLine();
+ concatenate("\\htmlonly");
+ newLine();
+ }
+}
+
+/*!
+ */
+void DoxWriter::formatEndif()
+{
+ newLine();
+ concatenate("\\endhtmlonly");
+ newLine();
+}
+
+/*!
+ */
+void DoxWriter::formatElse()
+{
+ // nothing.
+}
+
+/*!
+ Pass 1: Construct a section identifier and insert it into
+ the anchor set.
+
+ Pass 2: Convert section1, section2, and section3 commands
+ to section, subsection, and subsubsection respectively.
+ Warn if a section command higher than 3 is seen.
+ */
+const Atom* DoxWriter::sectionHeading(const Atom* atom)
+{
+ QString heading_level = atom->string();
+ QString heading_text;
+ const Atom* next = atom->next();
+ while (next) {
+ next->dump();
+ if (next->type() == Atom::SectionHeadingRight) {
+ if (next->string() == heading_level)
+ break;
+ else {
+ qDebug() << "WRONG SectionHeading number!!!!";
+ }
+ }
+ else
+ heading_text += next->string();
+ next = next->next();
+ }
+
+ QString heading_identifier = heading_text;
+ heading_identifier.remove(ws_rx);
+
+ newLine();
+ if (heading_level == "1")
+ heading_level = "\\section ";
+ else if (heading_level == "2")
+ heading_level = "\\subsection ";
+ else if (heading_level == "3")
+ heading_level = "\\subsubsection ";
+ else if (heading_level == "4") {
+ heading_level = "\\subsubsection ";
+ qDebug() << "WARNING section4 converted to \\subsubsection";
+ }
+ else {
+ heading_level = "\\subsubsection ";
+ qDebug() << "WARNING section5 converted to \\subsubsection";
+ }
+ concatenate(heading_level);
+ newText += heading_identifier + " ";
+ lineLength += heading_identifier.size() + 1;
+ newText += heading_text;
+ lineLength += heading_text.size();
+ newLine();
+ return next? next : atom;
+}
+
+/*!
+ Report an unhandled atom.
+ */
+void DoxWriter::unhandled(const Atom* atom)
+{
+ qDebug() << "UNHANDLED ATOM";
+ atom->dump();
+}
+
+/*!
+ Output a code/endcode block.
+ */
+void DoxWriter::code(const Atom* atom)
+{
+ newLine();
+ concatenate("\\code");
+ writeCode(atom->string());
+ concatenate("\\endcode");
+ newLine();
+}
+
+/*!
+ Output a code/endcode block depending on the
+ CodeQuote Command and CodeQuoteArgument parameters.
+ */
+const Atom* DoxWriter::codeQuoteCommand(const Atom* atom)
+{
+ QString command = atom->string();
+ atom = atom->next();
+ concatenate("\\code");
+ if (command == "codeline") {
+ newLine();
+ concatenate(atom->string());
+ newLine();
+ }
+ else if (command == "dots") {
+ newLine();
+ concatenate(atom->string());
+ newLine();
+ }
+ else {
+ writeCode(atom->string());
+ }
+ concatenate("\\endcode");
+ return atom;
+}
+
+/*!
+ Appends a block of code to the comment.
+ */
+void DoxWriter::writeCode(QString text)
+{
+ int cr_count = text.count('\n') - 1;
+ if (cr_count >= 0) {
+ int last_cr = text.lastIndexOf('\n');
+ newText += text.left(last_cr);
+ lineCount += cr_count;
+ }
+ else
+ newText += text;
+ newLine();
+}
+
+/*!
+ Inserts \a text into the anchor set. This function is called
+ during doxygen pass 1.
+ */
+void DoxWriter::insertAnchor(const QString& text)
+{
+ anchors.insert(text);
+}
+
+/*!
+ Returns true if \a text identifies an anchor, section,
+ subsection, subsubsection, or page.
+ */
+bool DoxWriter::isAnchor(const QString& text)
+{
+ return anchors.contains(text);
+}
+
+/*!
+ Write the set of anchors to a file, one per line.
+ */
+void DoxWriter::writeAnchors()
+{
+ QFile file("anchors.txt");
+ if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
+ qWarning("Unable to open anchors.txt for writing.");
+ return;
+ }
+
+ QTextStream out(&file);
+ QSet<QString>::const_iterator i = anchors.constBegin();
+ while (i != anchors.constEnd()) {
+ out << *i << "\n";
+ ++i;
+ }
+ file.close();
+}
+
+/*!
+ Read the set of anchors from the anchors file, one per line,
+ and insert each one into the anchor set.
+ */
+void DoxWriter::readAnchors()
+{
+ QFile file("anchors.txt");
+ if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
+ qWarning("Unable to open anchors.txt for reading.");
+ return;
+ }
+
+ QTextStream in(&file);
+ while (!in.atEnd()) {
+ QString line = in.readLine();
+ anchors.insert(line);
+ }
+ file.close();
+#if 0
+ QSet<QString>::const_iterator i = anchors.constBegin();
+ while (i != anchors.constEnd()) {
+ qDebug() << *i;
+ ++i;
+ }
+#endif
+}
+
+/*!
+ Inserts \a title into one of the title maps. \a title is
+ mapped to the \a node name. This function is called during
+ doxygen pass 1.
+ */
+void DoxWriter::insertTitle(FakeNode* node, const QString& title)
+{
+ switch (node->subType()) {
+ case FakeNode::Example:
+ if (exampleTitles.contains(title)) {
+ qWarning() << "DoxWriter::insertTitle():"
+ << "Duplicate example title:"
+ << title;
+ }
+ else {
+ exampleTitles[title] = node->name();
+ exampleTitlesInverse[node->name()] = title;
+ }
+ break;
+ case FakeNode::HeaderFile:
+ if (headerFileTitles.contains(title)) {
+ qWarning() << "DoxWriter::insertTitle():"
+ << "Duplicate header file title:"
+ << title;
+ }
+ else {
+ headerFileTitles[title] = node->name();
+ headerFileTitlesInverse[node->name()] = title;
+ }
+ break;
+ case FakeNode::File:
+ if (fileTitles.contains(title)) {
+ qWarning() << "DoxWriter::insertTitle():"
+ << "Duplicate file title:"
+ << title;
+ }
+ else {
+ fileTitles[title] = node->name();
+ fileTitlesInverse[node->name()] = title;
+ }
+ break;
+ case FakeNode::Group:
+ if (groupTitles.contains(title)) {
+ qWarning() << "DoxWriter::insertTitle():"
+ << "Duplicate group title:"
+ << title;
+ }
+ else {
+ groupTitles[title] = node->name();
+ groupTitlesInverse[node->name()] = title;
+ }
+ break;
+ case FakeNode::Module:
+ if (moduleTitles.contains(title)) {
+ qWarning() << "DoxWriter::insertTitle():"
+ << "Duplicate module title:"
+ << title;
+ }
+ else {
+ moduleTitles[title] = node->name();
+ moduleTitlesInverse[node->name()] = title;
+ }
+ break;
+ case FakeNode::Page:
+ if (pageTitles.contains(title)) {
+ qWarning() << "DoxWriter::insertTitle():"
+ << "Duplicate page title:"
+ << title;
+ }
+ else {
+ pageTitles[title] = node->name();
+ pageTitlesInverse[node->name()] = title;
+ }
+ break;
+ case FakeNode::ExternalPage:
+ if (externalPageTitles.contains(title)) {
+ qWarning() << "DoxWriter::insertTitle():"
+ << "Duplicate external page title:"
+ << title;
+ }
+ else {
+ externalPageTitles[title] = node->name();
+ externalPageTitlesInverse[node->name()] = title;
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+/*!
+ */
+const QString* DoxWriter::getPageFile(const QString& title)
+{
+ QStringMapEntry entry = pageTitles.find(title);
+ return (entry == pageTitles.end()) ? 0 : &entry.value();
+}
+
+/*!
+ */
+const QString* DoxWriter::getExamplePath(const QString& title)
+{
+ QStringMapEntry entry = exampleTitles.find(title);
+ return (entry == exampleTitles.end()) ? 0 : &entry.value();
+}
+
+/*!
+ */
+const QString* DoxWriter::getFile(const QString& title)
+{
+ QStringMapEntry entry = fileTitles.find(title);
+ return (entry == fileTitles.end()) ? 0 : &entry.value();
+}
+
+/*!
+ */
+const QString* DoxWriter::getHeaderFile(const QString& title)
+{
+ QStringMapEntry entry = headerFileTitles.find(title);
+ return (entry == headerFileTitles.end()) ? 0 : &entry.value();
+}
+
+/*!
+ */
+const QString* DoxWriter::getGroup(const QString& title)
+{
+ QStringMapEntry entry = groupTitles.find(title);
+ return (entry == groupTitles.end()) ? 0 : &entry.value();
+}
+
+/*!
+ */
+const QString* DoxWriter::getModule(const QString& title)
+{
+ QStringMapEntry entry = moduleTitles.find(title);
+ return (entry == moduleTitles.end()) ? 0 : &entry.value();
+}
+
+/*!
+ */
+const QString* DoxWriter::getExternalPage(const QString& title)
+{
+ QStringMapEntry entry = externalPageTitles.find(title);
+ return (entry == externalPageTitles.end()) ? 0 : &entry.value();
+}
+
+/*!
+ */
+const QString* DoxWriter::getPageTitle(const QString& text)
+{
+ QStringMapEntry entry = pageTitlesInverse.find(text);
+ return (entry == pageTitlesInverse.end()) ? 0 : &entry.value();
+}
+
+/*!
+ */
+const QString* DoxWriter::getExampleTitle(const QString& text)
+{
+ QStringMapEntry entry = exampleTitlesInverse.find(text);
+ return (entry == exampleTitlesInverse.end()) ? 0 : &entry.value();
+}
+
+/*!
+ */
+const QString* DoxWriter::getFileTitle(const QString& text)
+{
+ QStringMapEntry entry = fileTitlesInverse.find(text);
+ return (entry == fileTitlesInverse.end()) ? 0 : &entry.value();
+}
+
+/*!
+ */
+const QString* DoxWriter::getHeaderFileTitle(const QString& text)
+{
+ QStringMapEntry entry = headerFileTitlesInverse.find(text);
+ return (entry == headerFileTitlesInverse.end()) ? 0 : &entry.value();
+}
+
+/*!
+ */
+const QString* DoxWriter::getGroupTitle(const QString& text)
+{
+ QStringMapEntry entry = groupTitlesInverse.find(text);
+ return (entry == groupTitlesInverse.end()) ? 0 : &entry.value();
+}
+
+/*!
+ */
+const QString* DoxWriter::getModuleTitle(const QString& text)
+{
+ QStringMapEntry entry = moduleTitlesInverse.find(text);
+ return (entry == moduleTitlesInverse.end()) ? 0 : &entry.value();
+}
+
+/*!
+ */
+const QString* DoxWriter::getExternalPageTitle(const QString& text)
+{
+ QStringMapEntry entry = externalPageTitlesInverse.find(text);
+ return (entry == externalPageTitlesInverse.end()) ? 0 : &entry.value();
+}
+
+/*!
+ Serialize \a map to file \a name.
+ */
+void DoxWriter::writeMap(const QStringMap& map, const QString& name)
+{
+
+ QFile file(name);
+ if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
+ qWarning() << "Unable to open" << name << "for writing.";
+ return;
+ }
+
+ QTextStream out(&file);
+ QStringMap::const_iterator i = map.constBegin();
+ while (i != map.constEnd()) {
+ out << i.key() << "\n";
+ out << i.value() << "\n";
+ ++i;
+ }
+ file.close();
+}
+
+/*!
+ Read file \a name into the \a map.
+ */
+void DoxWriter::readMap(QStringMap& map, QStringMap& inverseMap, const QString& name)
+{
+ QFile file(name);
+ if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
+ qWarning() << "Unable to open" << name << "for reading.";
+ return;
+ }
+
+ QTextStream in(&file);
+ while (!in.atEnd()) {
+ QString title = in.readLine();
+ QString value = in.readLine();
+ map[title] = value;
+ inverseMap[value] = title;
+ }
+ file.close();
+}
+
+/*!
+ Write the sets of titles to text files, one per line.
+ */
+void DoxWriter::writeTitles()
+{
+ if (!pageTitles.isEmpty())
+ writeMap(pageTitles,"pagetitles.txt");
+ if (!fileTitles.isEmpty())
+ writeMap(fileTitles,"filetitles.txt");
+ if (!headerFileTitles.isEmpty())
+ writeMap(headerFileTitles,"headerfiletitles.txt");
+ if (!exampleTitles.isEmpty())
+ writeMap(exampleTitles,"exampletitles.txt");
+ if (!moduleTitles.isEmpty())
+ writeMap(moduleTitles,"moduletitles.txt");
+ if (!groupTitles.isEmpty())
+ writeMap(groupTitles,"grouptitles.txt");
+ if (!externalPageTitles.isEmpty())
+ writeMap(externalPageTitles,"externalpagetitles.txt");
+}
+
+/*!
+ Read the sets of titles from the titles files, one per line,
+ and insert each one into the appropriate title set.
+ */
+void DoxWriter::readTitles()
+{
+ readMap(pageTitles,pageTitlesInverse,"pagetitles.txt");
+ readMap(fileTitles,fileTitlesInverse,"filetitles.txt");
+ readMap(headerFileTitles,headerFileTitlesInverse,"headerfiletitles.txt");
+ readMap(exampleTitles,exampleTitlesInverse,"exampletitles.txt");
+ readMap(moduleTitles,moduleTitlesInverse,"moduletitles.txt");
+ readMap(groupTitles,groupTitlesInverse,"grouptitles.txt");
+ readMap(externalPageTitles,
+ externalPageTitlesInverse,
+ "externalpagetitles.txt");
+}
+
+/*!
+ Serialize \a map to file \a name.
+ */
+void DoxWriter::writeMultiMap(const QStringMultiMap& map, const QString& name)
+{
+
+ QFile file(name);
+ if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
+ qWarning() << "Unable to open" << name << "for writing.";
+ return;
+ }
+
+ QTextStream out(&file);
+ QStringMultiMap::const_iterator i = map.constBegin();
+ while (i != map.constEnd()) {
+ out << i.key() << "\n";
+ out << i.value() << "\n";
+ ++i;
+ }
+ file.close();
+}
+
+/*!
+ Write the4 property names and variable names to text files.
+ */
+void DoxWriter::writeMembers()
+{
+ if (!variables.isEmpty())
+ writeMultiMap(variables,"variables.txt");
+ if (!properties.isEmpty())
+ writeMultiMap(properties,"properties.txt");
+ if (!enums.isEmpty())
+ writeMultiMap(enums,"enums.txt");
+}
+
+/*!
+ Read file \a name into the \a map.
+ */
+void DoxWriter::readMultiMap(QStringMultiMap& map, const QString& name)
+{
+ QFile file(name);
+ if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
+ qWarning() << "Unable to open" << name << "for reading.";
+ return;
+ }
+
+ QTextStream in(&file);
+ while (!in.atEnd()) {
+ QString member = in.readLine();
+ QString className = in.readLine();
+ map.insert(member,className);
+ }
+ file.close();
+}
+
+/*!
+ Read the property names and variable names from the test files.
+ */
+void DoxWriter::readMembers()
+{
+ readMultiMap(variables,"variables.txt");
+ readMultiMap(properties,"properties.txt");
+ readMultiMap(enums,"enums.txt");
+}
+
+/*!
+ Return true if \a name is a property. Loads \a classes with
+ the names of all the classes in which \a name is a property.
+ */
+bool DoxWriter::isProperty(const QString& name, QStringList& classes)
+{
+ classes = properties.values(name);
+ return !classes.isEmpty();
+}
+
+/*!
+ Return true if \a name is a variable. Loads \a classes with
+ the names of all the classes in which \a name is a variable.
+ */
+bool DoxWriter::isVariable(const QString& name, QStringList& classes)
+{
+ classes = variables.values(name);
+ return !classes.isEmpty();
+}
+
+/*!
+ Return true if \a name is an enum type. Loads \a classes with
+ the names of all the classes in which \a name is an enum type.
+ */
+bool DoxWriter::isEnum(const QString& name, QStringList& classes)
+{
+ classes = enums.values(name);
+ return !classes.isEmpty();
+}
+#endif
+
+QT_END_NAMESPACE
diff --git a/tools/qdoc3/doc.h b/tools/qdoc3/doc.h
new file mode 100644
index 0000000..6cb6f0a
--- /dev/null
+++ b/tools/qdoc3/doc.h
@@ -0,0 +1,315 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*
+ doc.h
+*/
+
+#ifndef DOC_H
+#define DOC_H
+
+#include <QSet>
+#include <QString>
+
+#include "location.h"
+
+QT_BEGIN_NAMESPACE
+
+class Atom;
+class CodeMarker;
+class Config;
+class DocPrivate;
+class Quoter;
+class Text;
+class FakeNode;
+
+typedef QMap<QString, QStringList> QCommandMap;
+typedef QMap<QString, QString> QStringMap;
+typedef QStringMap::const_iterator QStringMapEntry;
+typedef QMultiMap<QString, QString> QStringMultiMap;
+
+class Doc
+{
+ public:
+ // the order is important
+ enum SectioningUnit {
+ Book = -2,
+ Part,
+ Chapter,
+ Section1,
+ Section2,
+ Section3,
+ Section4
+ };
+
+ Doc() : priv(0) {}
+ Doc(const Location &start_loc,
+ const Location &end_loc,
+ const QString &source,
+ const QSet<QString> &metaCommandSet);
+ Doc(const Doc &doc);
+ ~Doc();
+
+ Doc& operator=( const Doc& doc );
+
+ void renameParameters(const QStringList &oldNames,
+ const QStringList &newNames);
+ void simplifyEnumDoc();
+ void setBody(const Text &body);
+
+ const Location &location() const;
+ bool isEmpty() const;
+ const QString& source() const;
+ const Text& body() const;
+ Text briefText() const;
+ Text trimmedBriefText(const QString &className) const;
+ Text legaleseText() const;
+ const QString& baseName() const;
+ SectioningUnit granularity() const;
+ const QSet<QString> &parameterNames() const;
+ const QStringList &enumItemNames() const;
+ const QStringList &omitEnumItemNames() const;
+ const QSet<QString> &metaCommandsUsed() const;
+ QStringList metaCommandArgs( const QString& metaCommand ) const;
+ const QList<Text> &alsoList() const;
+ bool hasTableOfContents() const;
+ bool hasKeywords() const;
+ bool hasTargets() const;
+ const QList<Atom *> &tableOfContents() const;
+ const QList<int> &tableOfContentsLevels() const;
+ const QList<Atom *> &keywords() const;
+ const QList<Atom *> &targets() const;
+ const QStringMap &metaTagMap() const;
+
+ static void initialize( const Config &config );
+ static void terminate();
+ static QString alias( const QString &english );
+ static void trimCStyleComment( Location& location, QString& str );
+ static CodeMarker *quoteFromFile(const Location &location,
+ Quoter &quoter,
+ const QString &fileName);
+ static QString canonicalTitle(const QString &title);
+
+ private:
+ void detach();
+ DocPrivate *priv;
+};
+
+#ifdef QDOC2DOX
+
+class DoxWriter
+{
+ public:
+ DoxWriter(const QString& source, DocPrivate* docPrivate)
+ : commentType(OtherComment),
+ lineLength(0),
+ lineCount(0),
+ priv(docPrivate),
+ oldText(source) {}
+ ~DoxWriter() {}
+
+ void pass1();
+ void pass2();
+
+ static void setDoxPass(int pass);
+ static bool isDoxPass(int pass);
+ static bool isDoxPass();
+ static void insertTitle(FakeNode* node, const QString& title);
+ static void writeTitles();
+ static void readTitles();
+ static void writeMembers();
+ static void readMembers();
+ static void writeAnchors();
+ static void readAnchors();
+
+ private:
+ void indentLine();
+ void newLine();
+ void concatenate(QString text);
+ void wrap(QString text);
+ bool conversionRequired() const;
+ void convertMetaCommands();
+ void convertText();
+ const Atom* link(const Atom* atom);
+ void formattingLeft(const Atom* atom, const Atom* next);
+ void formattingRight(const Atom* atom, const Atom* prev);
+ void tt(const Atom* atom);
+ void formatIf(const Atom* atom);
+ void formatEndif();
+ void formatElse();
+ const Atom* sectionHeading(const Atom* atom);
+ void unhandled(const Atom* atom);
+ void code(const Atom* atom);
+ const Atom* codeQuoteCommand(const Atom* atom);
+ void writeCode(QString text);
+ void writeCommand(QCommandMap::const_iterator cmd);
+
+ static void insertAnchor(const QString& text);
+ static bool isAnchor(const QString& text);
+
+ static const QString* getPageFile(const QString& title);
+ static const QString* getFile(const QString& title);
+ static const QString* getExamplePath(const QString& title);
+ static const QString* getHeaderFile(const QString& title);
+ static const QString* getGroup(const QString& title);
+ static const QString* getModule(const QString& title);
+ static const QString* getExternalPage(const QString& title);
+ static const QString* getPageTitle(const QString& text);
+ static const QString* getFileTitle(const QString& text);
+ static const QString* getExampleTitle(const QString& text);
+ static const QString* getHeaderFileTitle(const QString& text);
+ static const QString* getGroupTitle(const QString& text);
+ static const QString* getModuleTitle(const QString& text);
+ static const QString* getExternalPageTitle(const QString& text);
+
+ static bool isProperty(const QString& title, QStringList& classes);
+ static bool isVariable(const QString& title, QStringList& classes);
+ static bool isEnum(const QString& title, QStringList& classes);
+
+ private:
+ static void writeMap(const QStringMap& map, const QString& name);
+ static void readMap(QStringMap& map,
+ QStringMap& inverseMap,
+ const QString& name);
+ static void writeMultiMap(const QStringMultiMap& map, const QString& name);
+ static void readMultiMap(QStringMultiMap& map, const QString& name);
+
+ public: // VS 6, SunCC need this to be public
+ enum StructType { BulletList, NumericList, ValueList, Table };
+ private:
+ struct StructDesc {
+ StructType structType;
+ int count;
+ bool nested;
+ bool inTableHeader;
+ bool inTableRow;
+ bool inTableItem;
+ bool odd;
+
+ StructDesc()
+ : structType(BulletList),
+ count(0),
+ nested(false),
+ inTableHeader(false),
+ inTableRow(false),
+ inTableItem(false),
+ odd(true) { }
+
+ StructDesc(StructType t, bool n)
+ : structType(t),
+ count(0),
+ nested(n),
+ inTableHeader(false),
+ inTableRow(false),
+ inTableItem(false),
+ odd(true) { }
+ };
+
+ typedef QStack<StructDesc> StructStack;
+
+ enum CommentType {
+ ClassComment,
+ EnumComment,
+ ExampleComment,
+ FnComment,
+ GroupComment,
+ HeaderFileComment,
+ MacroComment,
+ ModuleComment,
+ PageComment,
+ PropertyComment,
+ ServiceComment,
+ TypedefComment,
+ VariableComment,
+ OtherComment
+ };
+
+ private:
+ CommentType commentType;
+ int lineLength;
+ int lineCount;
+ DocPrivate* priv;
+ QString oldText;
+ QString newText;
+ StructStack structs;
+
+ QString currentPage;
+ QString currentFn;
+ QString currentTitle;
+ QString currentEnum;
+ QString currentProperty;
+ QString currentVariable;
+ QString currentExample;
+ QString currentGroup;
+ QString currentModule;
+ QString currentMacro;
+ QString currentService;
+ QString currentTypedef;
+ QString currentHeaderFile;
+ static QString currentClass;
+
+ static int doxPass;
+ static QSet<QString> anchors;
+ static QStringMap exampleTitles;
+ static QStringMap headerFileTitles;
+ static QStringMap fileTitles;
+ static QStringMap groupTitles;
+ static QStringMap moduleTitles;
+ static QStringMap pageTitles;
+ static QStringMap externalPageTitles;
+ static QStringMap exampleTitlesInverse;
+ static QStringMap headerFileTitlesInverse;
+ static QStringMap fileTitlesInverse;
+ static QStringMap groupTitlesInverse;
+ static QStringMap moduleTitlesInverse;
+ static QStringMap pageTitlesInverse;
+ static QStringMap externalPageTitlesInverse;
+
+ static QStringMultiMap variables;
+ static QStringMultiMap properties;
+ static QStringMultiMap enums;
+};
+
+#endif
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/tools/qdoc3/documentation.pri b/tools/qdoc3/documentation.pri
new file mode 100644
index 0000000..d952183
--- /dev/null
+++ b/tools/qdoc3/documentation.pri
@@ -0,0 +1,5 @@
+# NOTE: THIS FILE IS SHARED BY qdoc3.pro AND projects.pro
+#
+# So while changing this file, please make sure to that your changes
+# work in root qt soure dir with (n)make docs and for building with qdoc
+#
diff --git a/tools/qdoc3/editdistance.cpp b/tools/qdoc3/editdistance.cpp
new file mode 100644
index 0000000..11cc18f
--- /dev/null
+++ b/tools/qdoc3/editdistance.cpp
@@ -0,0 +1,111 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*
+ editdistance.cpp
+*/
+
+#include "editdistance.h"
+
+QT_BEGIN_NAMESPACE
+
+int editDistance( const QString& s, const QString& t )
+{
+#define D( i, j ) d[(i) * n + (j)]
+ int i;
+ int j;
+ int m = s.length() + 1;
+ int n = t.length() + 1;
+ int *d = new int[m * n];
+ int result;
+
+ for ( i = 0; i < m; i++ )
+ D( i, 0 ) = i;
+ for ( j = 0; j < n; j++ )
+ D( 0, j ) = j;
+ for ( i = 1; i < m; i++ ) {
+ for ( j = 1; j < n; j++ ) {
+ if ( s[i - 1] == t[j - 1] ) {
+ D( i, j ) = D( i - 1, j - 1 );
+ } else {
+ int x = D( i - 1, j );
+ int y = D( i - 1, j - 1 );
+ int z = D( i, j - 1 );
+ D( i, j ) = 1 + qMin( qMin(x, y), z );
+ }
+ }
+ }
+ result = D( m - 1, n - 1 );
+ delete[] d;
+ return result;
+#undef D
+}
+
+QString nearestName( const QString& actual, const QSet<QString>& candidates )
+{
+ int deltaBest = 10000;
+ int numBest = 0;
+ QString best;
+
+ QSet<QString>::ConstIterator c = candidates.begin();
+ while ( c != candidates.end() ) {
+ if ( (*c)[0] == actual[0] ) {
+ int delta = editDistance( actual, *c );
+ if ( delta < deltaBest ) {
+ deltaBest = delta;
+ numBest = 1;
+ best = *c;
+ } else if ( delta == deltaBest ) {
+ numBest++;
+ }
+ }
+ ++c;
+ }
+
+ if ( numBest == 1 && deltaBest <= 2 &&
+ actual.length() + best.length() >= 5 ) {
+ return best;
+ } else {
+ return "";
+ }
+}
+
+QT_END_NAMESPACE
diff --git a/tools/qdoc3/editdistance.h b/tools/qdoc3/editdistance.h
new file mode 100644
index 0000000..d69f6b9
--- /dev/null
+++ b/tools/qdoc3/editdistance.h
@@ -0,0 +1,59 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*
+ editdistance.h
+*/
+
+#ifndef EDITDISTANCE_H
+#define EDITDISTANCE_H
+
+#include <QSet>
+#include <QString>
+
+QT_BEGIN_NAMESPACE
+
+int editDistance( const QString& s, const QString& t );
+QString nearestName( const QString& actual, const QSet<QString>& candidates );
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/tools/qdoc3/generator.cpp b/tools/qdoc3/generator.cpp
new file mode 100644
index 0000000..d89d6af
--- /dev/null
+++ b/tools/qdoc3/generator.cpp
@@ -0,0 +1,995 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*
+ generator.cpp
+*/
+
+#include <qdir.h>
+
+#include "codemarker.h"
+#include "config.h"
+#include "doc.h"
+#include "editdistance.h"
+#include "generator.h"
+#include "node.h"
+#include "openedlist.h"
+#include "quoter.h"
+#include "separator.h"
+#include "tokenizer.h"
+
+QT_BEGIN_NAMESPACE
+
+QList<Generator *> Generator::generators;
+QMap<QString, QMap<QString, QString> > Generator::fmtLeftMaps;
+QMap<QString, QMap<QString, QString> > Generator::fmtRightMaps;
+QMap<QString, QStringList> Generator::imgFileExts;
+QSet<QString> Generator::outputFormats;
+QStringList Generator::imageFiles;
+QStringList Generator::imageDirs;
+QString Generator::outDir;
+QString Generator::project;
+
+static Text stockLink(const QString &target)
+{
+ return Text() << Atom(Atom::Link, target) << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK)
+ << target << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK);
+}
+
+Generator::Generator()
+ : amp("&amp;"), lt("&lt;"), gt("&gt;"), quot("&quot;"), tag("</?@[^>]*>")
+{
+ generators.prepend(this);
+}
+
+Generator::~Generator()
+{
+ generators.removeAll(this);
+}
+
+void Generator::initializeGenerator(const Config & /* config */)
+{
+}
+
+void Generator::terminateGenerator()
+{
+}
+
+void Generator::initialize(const Config &config)
+{
+ outputFormats = config.getStringSet(CONFIG_OUTPUTFORMATS);
+ if (!outputFormats.isEmpty()) {
+ outDir = config.getString(CONFIG_OUTPUTDIR);
+ if (outDir.isEmpty())
+ config.lastLocation().fatal(tr("No output directory specified in configuration file"));
+
+ QDir dirInfo;
+ if (dirInfo.exists(outDir)) {
+ if (!Config::removeDirContents(outDir))
+ config.lastLocation().error(tr("Cannot empty output directory '%1'").arg(outDir));
+ }
+ else {
+ if (!dirInfo.mkpath(outDir))
+ config.lastLocation().fatal(tr("Cannot create output directory '%1'").arg(outDir));
+ }
+
+ if (!dirInfo.mkdir(outDir + "/images"))
+ config.lastLocation().fatal(tr("Cannot create output directory '%1'")
+ .arg(outDir + "/images"));
+ }
+
+ imageFiles = config.getStringList(CONFIG_IMAGES);
+ imageDirs = config.getStringList(CONFIG_IMAGEDIRS);
+
+ QString imagesDotFileExtensions = CONFIG_IMAGES + Config::dot + CONFIG_FILEEXTENSIONS;
+ QSet<QString> formats = config.subVars(imagesDotFileExtensions);
+ QSet<QString>::ConstIterator f = formats.begin();
+ while (f != formats.end()) {
+ imgFileExts[*f] = config.getStringList(imagesDotFileExtensions + Config::dot + *f);
+ ++f;
+ }
+
+ QList<Generator *>::ConstIterator g = generators.begin();
+ while (g != generators.end()) {
+ if (outputFormats.contains((*g)->format())) {
+ (*g)->initializeGenerator(config);
+ QStringList extraImages = config.getStringList(CONFIG_EXTRAIMAGES + Config::dot
+ + (*g)->format());
+ QStringList::ConstIterator e = extraImages.begin();
+ while (e != extraImages.end()) {
+ QString userFriendlyFilePath;
+ QString filePath = Config::findFile(config.lastLocation(), imageFiles, imageDirs, *e,
+ imgFileExts[(*g)->format()], userFriendlyFilePath);
+ if (!filePath.isEmpty())
+ Config::copyFile(config.lastLocation(), filePath, userFriendlyFilePath,
+ (*g)->outputDir() + "/images");
+ ++e;
+ }
+ }
+ ++g;
+ }
+
+ QRegExp secondParamAndAbove("[\2-\7]");
+ QSet<QString> formattingNames = config.subVars(CONFIG_FORMATTING);
+ QSet<QString>::ConstIterator n = formattingNames.begin();
+ while (n != formattingNames.end()) {
+ QString formattingDotName = CONFIG_FORMATTING + Config::dot + *n;
+
+ QSet<QString> formats = config.subVars(formattingDotName);
+ QSet<QString>::ConstIterator f = formats.begin();
+ while (f != formats.end()) {
+ QString def = config.getString(formattingDotName + Config::dot +
+ *f);
+ if (!def.isEmpty()) {
+ int numParams = Config::numParams(def);
+ int numOccs = def.count("\1");
+
+ if (numParams != 1) {
+ config.lastLocation().warning(tr("Formatting '%1' must have exactly one"
+ " parameter (found %2)")
+ .arg(*n).arg(numParams));
+ }
+ else if (numOccs > 1) {
+ config.lastLocation().fatal(tr("Formatting '%1' must contain exactly one"
+ " occurrence of '\\1' (found %2)")
+ .arg(*n).arg(numOccs));
+ }
+ else {
+ int paramPos = def.indexOf("\1");
+ fmtLeftMaps[*f].insert(*n, def.left(paramPos));
+ fmtRightMaps[*f].insert(*n, def.mid(paramPos + 1));
+ }
+ }
+ ++f;
+ }
+ ++n;
+ }
+
+ project = config.getString(CONFIG_PROJECT);
+}
+
+void Generator::terminate()
+{
+ QList<Generator *>::ConstIterator g = generators.begin();
+ while (g != generators.end()) {
+ if (outputFormats.contains((*g)->format()))
+ (*g)->terminateGenerator();
+ ++g;
+ }
+
+ fmtLeftMaps.clear();
+ fmtRightMaps.clear();
+ imgFileExts.clear();
+ imageFiles.clear();
+ imageDirs.clear();
+ outDir = "";
+}
+
+Generator *Generator::generatorForFormat(const QString& format)
+{
+ QList<Generator *>::ConstIterator g = generators.begin();
+ while (g != generators.end()) {
+ if ((*g)->format() == format)
+ return *g;
+ ++g;
+ }
+ return 0;
+}
+
+void Generator::startText(const Node * /* relative */,
+ CodeMarker * /* marker */)
+{
+}
+
+void Generator::endText(const Node * /* relative */,
+ CodeMarker * /* marker */)
+{
+}
+
+int Generator::generateAtom(const Atom * /* atom */,
+ const Node * /* relative */,
+ CodeMarker * /* marker */)
+{
+ return 0;
+}
+
+void Generator::generateClassLikeNode(const InnerNode * /* classe */,
+ CodeMarker * /* marker */)
+{
+}
+
+void Generator::generateFakeNode(const FakeNode * /* fake */,
+ CodeMarker * /* marker */)
+{
+}
+
+void Generator::generateText(const Text& text,
+ const Node *relative,
+ CodeMarker *marker)
+{
+ if (text.firstAtom() != 0) {
+ int numAtoms = 0;
+ startText(relative, marker);
+ generateAtomList(text.firstAtom(),
+ relative,
+ marker,
+ true,
+ numAtoms);
+ endText(relative, marker);
+ }
+}
+
+#ifdef QDOC_QML
+void Generator::generateQmlText(const Text& text,
+ const Node *relative,
+ CodeMarker *marker)
+{
+ if (text.firstAtom() != 0) {
+ startText(relative, marker);
+ const Atom *atom = text.firstAtom();
+ while (atom) {
+ if (atom->type() != Atom::QmlText)
+ atom = atom->next();
+ else {
+ atom = atom->next();
+ while (atom && (atom->type() != Atom::EndQmlText)) {
+ int n = 1 + generateAtom(atom, relative, marker);
+ while (n-- > 0)
+ atom = atom->next();
+ }
+ }
+ }
+ endText(relative, marker);
+ }
+}
+#endif
+
+void Generator::generateBody(const Node *node, CodeMarker *marker)
+{
+ bool quiet = false;
+
+ if (node->type() == Node::Function) {
+#if 0
+ const FunctionNode *func = (const FunctionNode *) node;
+ if (func->isOverload() && func->metaness() != FunctionNode::Ctor)
+ generateOverload(node, marker);
+#endif
+ }
+ else if (node->type() == Node::Fake) {
+ const FakeNode *fake = static_cast<const FakeNode *>(node);
+ if (fake->subType() == FakeNode::Example)
+ generateExampleFiles(fake, marker);
+ else if (fake->subType() == FakeNode::File)
+ quiet = true;
+ }
+
+ if (node->doc().isEmpty()) {
+ if (!quiet) // ### might be unnecessary
+ node->location().warning(tr("No documentation for '%1'")
+ .arg(marker->plainFullName(node)));
+ }
+ else {
+ generateText(node->doc().body(), node, marker);
+
+ if (node->type() == Node::Enum) {
+ const EnumNode *enume = (const EnumNode *) node;
+
+ QSet<QString> definedItems;
+ QList<EnumItem>::ConstIterator it = enume->items().begin();
+ while (it != enume->items().end()) {
+ definedItems.insert((*it).name());
+ ++it;
+ }
+
+ QSet<QString> documentedItems = enume->doc().enumItemNames().toSet();
+ QSet<QString> allItems = definedItems + documentedItems;
+ if (allItems.count() > definedItems.count() ||
+ allItems.count() > documentedItems.count()) {
+ QSet<QString>::ConstIterator a = allItems.begin();
+ while (a != allItems.end()) {
+ if (!definedItems.contains(*a)) {
+ QString details;
+ QString best = nearestName(*a, definedItems);
+ if (!best.isEmpty() && !documentedItems.contains(best))
+ details = tr("Maybe you meant '%1'?").arg(best);
+
+ node->doc().location().warning(
+ tr("No such enum item '%1' in %2").arg(*a).arg(marker->plainFullName(node)),
+ details);
+ }
+ else if (!documentedItems.contains(*a)) {
+ node->doc().location().warning(
+ tr("Undocumented enum item '%1' in %2").arg(*a).arg(marker->plainFullName(node)));
+ }
+ ++a;
+ }
+ }
+ }
+ else if (node->type() == Node::Function) {
+ const FunctionNode *func = static_cast<const FunctionNode *>(node);
+
+ QSet<QString> definedParams;
+ QList<Parameter>::ConstIterator p = func->parameters().begin();
+ while (p != func->parameters().end()) {
+ if ((*p).name().isEmpty() && (*p).leftType() != QLatin1String("...")
+ && func->name() != QLatin1String("operator++")
+ && func->name() != QLatin1String("operator--")) {
+ node->doc().location().warning(tr("Missing parameter name"));
+ }
+ else {
+ definedParams.insert((*p).name());
+ }
+ ++p;
+ }
+
+ QSet<QString> documentedParams = func->doc().parameterNames();
+ QSet<QString> allParams = definedParams + documentedParams;
+ if (allParams.count() > definedParams.count()
+ || allParams.count() > documentedParams.count()) {
+ QSet<QString>::ConstIterator a = allParams.begin();
+ while (a != allParams.end()) {
+ if (!definedParams.contains(*a)) {
+ QString details;
+ QString best = nearestName(*a, definedParams);
+ if (!best.isEmpty())
+ details = tr("Maybe you meant '%1'?").arg(best);
+
+ node->doc().location().warning(
+ tr("No such parameter '%1' in %2").arg(*a).arg(marker->plainFullName(node)),
+ details);
+ }
+ else if (!(*a).isEmpty() && !documentedParams.contains(*a)) {
+ bool needWarning = (func->status() > Node::Obsolete);
+ if (func->overloadNumber() > 1) {
+ FunctionNode *primaryFunc =
+ func->parent()->findFunctionNode(func->name());
+ if (primaryFunc) {
+ foreach (const Parameter &param, primaryFunc->parameters()) {
+ if (param.name() == *a) {
+ needWarning = false;
+ break;
+ }
+ }
+ }
+ }
+ if (needWarning)
+ node->doc().location().warning(
+ tr("Undocumented parameter '%1' in %2").arg(*a).arg(marker->plainFullName(node)));
+ }
+ ++a;
+ }
+ }
+/* Something like this return value check should be implemented at some point. */
+ if (func->status() > Node::Obsolete && func->returnType() == "bool"
+ && func->reimplementedFrom() == 0 && !func->isOverload()) {
+ QString body = func->doc().body().toString();
+ if (!body.contains("return", Qt::CaseInsensitive))
+ node->doc().location().warning(tr("Undocumented return value"));
+ }
+
+ if (func->reimplementedFrom() != 0)
+ generateReimplementedFrom(func, marker);
+ }
+ }
+
+ if (node->type() == Node::Fake) {
+ const FakeNode *fake = static_cast<const FakeNode *>(node);
+ if (fake->subType() == FakeNode::File) {
+ Text text;
+ Quoter quoter;
+ Doc::quoteFromFile(fake->doc().location(), quoter, fake->name());
+ QString code = quoter.quoteTo(fake->location(), "", "");
+ text << Atom(Atom::Code, code);
+ generateText(text, fake, marker);
+ }
+ }
+}
+
+void Generator::generateAlsoList(const Node *node, CodeMarker *marker)
+{
+ QList<Text> alsoList = node->doc().alsoList();
+ supplementAlsoList(node, alsoList);
+
+ if (!alsoList.isEmpty()) {
+ Text text;
+ text << Atom::ParaLeft << "See also ";
+
+ for (int i = 0; i < alsoList.size(); ++i)
+ text << alsoList.at(i) << separator(i, alsoList.size());
+
+ text << Atom::ParaRight;
+ generateText(text, node, marker);
+ }
+}
+
+void Generator::generateInherits(const ClassNode *classe, CodeMarker *marker)
+{
+ QList<RelatedClass>::ConstIterator r;
+ int index;
+
+ if (!classe->baseClasses().isEmpty()) {
+ Text text;
+ text << Atom::ParaLeft << "Inherits ";
+
+ r = classe->baseClasses().begin();
+ index = 0;
+ while (r != classe->baseClasses().end()) {
+ text << Atom(Atom::LinkNode, CodeMarker::stringForNode((*r).node))
+ << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK)
+ << Atom(Atom::String, (*r).dataTypeWithTemplateArgs)
+ << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK);
+
+ if ((*r).access == Node::Protected) {
+ text << " (protected)";
+ } else if ((*r).access == Node::Private) {
+ text << " (private)";
+ }
+ text << separator(index++, classe->baseClasses().count());
+ ++r;
+ }
+ text << Atom::ParaRight;
+ generateText(text, classe, marker);
+ }
+}
+
+void Generator::generateInheritedBy(const ClassNode *classe,
+ CodeMarker *marker)
+{
+ if (!classe->derivedClasses().isEmpty()) {
+ Text text;
+ text << Atom::ParaLeft << "Inherited by ";
+
+ appendSortedNames(text, classe, classe->derivedClasses(), marker);
+ text << Atom::ParaRight;
+ generateText(text, classe, marker);
+ }
+}
+
+void Generator::generateExampleFiles(const FakeNode *fake, CodeMarker *marker)
+{
+ if (fake->childNodes().isEmpty())
+ return;
+
+ OpenedList openedList(OpenedList::Bullet);
+
+ Text text;
+ text << Atom::ParaLeft << "Files:" << Atom::ParaRight
+ << Atom(Atom::ListLeft, openedList.styleString());
+ foreach (const Node *child, fake->childNodes()) {
+ QString exampleFile = child->name();
+ openedList.next();
+ text << Atom(Atom::ListItemNumber, openedList.numberString())
+ << Atom(Atom::ListItemLeft, openedList.styleString()) << Atom::ParaLeft
+ << Atom(Atom::Link, exampleFile)
+ << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK)
+ << exampleFile
+ << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK)
+ << Atom::ParaRight << Atom(Atom::ListItemRight, openedList.styleString());
+ }
+ text << Atom(Atom::ListRight, openedList.styleString());
+ generateText(text, fake, marker);
+}
+
+void Generator::generateModuleWarning(const ClassNode *classe, CodeMarker *marker)
+{
+ QString module = classe->moduleName();
+ if (!module.isEmpty()) {
+ Text text;
+ if (Tokenizer::isTrue("defined(consoleedition)")
+ && !editionModuleMap["Console"].contains(module)) {
+ text << Atom::ParaLeft
+ << Atom(Atom::FormattingLeft, ATOM_FORMATTING_BOLD)
+ << "This class is not part of the Qt Console Edition."
+ << Atom(Atom::FormattingRight, ATOM_FORMATTING_BOLD)
+ << Atom::ParaRight;
+ }
+ else if (Tokenizer::isTrue("defined(desktoplightedition)")
+ && !editionModuleMap["DesktopLight"].contains(module)) {
+ text << Atom::ParaLeft
+ << Atom(Atom::FormattingLeft, ATOM_FORMATTING_BOLD)
+ << "This class is not part of the Qt Desktop Light Edition."
+ << Atom(Atom::FormattingRight, ATOM_FORMATTING_BOLD)
+ << Atom::ParaRight;
+ }
+ else if (module == "Qt3Support" && Tokenizer::isTrue("defined(opensourceedition)")) {
+ text << Atom::ParaLeft << Atom(Atom::FormattingLeft, ATOM_FORMATTING_BOLD)
+ << "Note to Qt Desktop Light Edition users:"
+ << Atom(Atom::FormattingRight, ATOM_FORMATTING_BOLD)
+ << " This class is only available in the "
+ << Atom(Atom::AutoLink, "Qt Desktop Edition")
+ << "." << Atom::ParaRight;
+ }
+
+ generateText(text, classe, marker);
+ }
+}
+
+QString Generator::indent(int level, const QString& markedCode)
+{
+ if (level == 0)
+ return markedCode;
+
+ QString t;
+ int column = 0;
+
+ int i = 0;
+ while (i < (int) markedCode.length()) {
+ if (markedCode.at(i) == QLatin1Char('<')) {
+ while (i < (int) markedCode.length()) {
+ t += markedCode.at(i++);
+ if (markedCode.at(i - 1) == QLatin1Char('>'))
+ break;
+ }
+ } else {
+ if (markedCode.at(i) == QLatin1Char('\n')) {
+ column = 0;
+ } else {
+ if (column == 0) {
+ for (int j = 0; j < level; j++)
+ t += QLatin1Char(' ');
+ }
+ column++;
+ }
+ t += markedCode.at(i++);
+ }
+ }
+ return t;
+}
+
+QString Generator::plainCode(const QString& markedCode)
+{
+ QString t = markedCode;
+ t.replace(tag, QString());
+ t.replace(quot, QLatin1String("\""));
+ t.replace(gt, QLatin1String(">"));
+ t.replace(lt, QLatin1String("<"));
+ t.replace(amp, QLatin1String("&"));
+ return t;
+}
+
+QString Generator::typeString(const Node *node)
+{
+ switch (node->type()) {
+ case Node::Namespace:
+ return "namespace";
+ case Node::Class:
+ return "class";
+ case Node::Fake:
+ default:
+ return "documentation";
+ case Node::Enum:
+ return "enum";
+ case Node::Typedef:
+ return "typedef";
+ case Node::Function:
+ return "function";
+ case Node::Property:
+ return "property";
+ }
+}
+
+QString Generator::imageFileName(const Node *relative, const QString& fileBase)
+{
+ QString userFriendlyFilePath;
+ QString filePath = Config::findFile(
+ relative->doc().location(), imageFiles, imageDirs, fileBase,
+ imgFileExts[format()], userFriendlyFilePath);
+
+ if (filePath.isEmpty())
+ return QString();
+
+ return QLatin1String("images/")
+ + Config::copyFile(relative->doc().location(),
+ filePath, userFriendlyFilePath,
+ outputDir() + QLatin1String("/images"));
+}
+
+void Generator::setImageFileExtensions(const QStringList& extensions)
+{
+ imgFileExts[format()] = extensions;
+}
+
+void Generator::unknownAtom(const Atom *atom)
+{
+ Location::internalError(tr("unknown atom type '%1' in %2 generator")
+ .arg(atom->typeString()).arg(format()));
+}
+
+bool Generator::matchAhead(const Atom *atom, Atom::Type expectedAtomType)
+{
+ return atom->next() != 0 && atom->next()->type() == expectedAtomType;
+}
+
+void Generator::supplementAlsoList(const Node *node, QList<Text> &alsoList)
+{
+ if (node->type() == Node::Function) {
+ const FunctionNode *func = static_cast<const FunctionNode *>(node);
+ if (func->overloadNumber() == 1) {
+ QString alternateName;
+ const FunctionNode *alternateFunc = 0;
+
+ if (func->name().startsWith("set") && func->name().size() >= 4) {
+ alternateName = func->name()[3].toLower();
+ alternateName += func->name().mid(4);
+ alternateFunc = func->parent()->findFunctionNode(alternateName);
+
+ if (!alternateFunc) {
+ alternateName = "is" + func->name().mid(3);
+ alternateFunc = func->parent()->findFunctionNode(alternateName);
+ if (!alternateFunc) {
+ alternateName = "has" + func->name().mid(3);
+ alternateFunc = func->parent()->findFunctionNode(alternateName);
+ }
+ }
+ } else if (!func->name().isEmpty()) {
+ alternateName = "set";
+ alternateName += func->name()[0].toUpper();
+ alternateName += func->name().mid(1);
+ alternateFunc = func->parent()->findFunctionNode(alternateName);
+ }
+
+ if (alternateFunc && alternateFunc->access() != Node::Private) {
+ int i;
+ for (i = 0; i < alsoList.size(); ++i) {
+ if (alsoList.at(i).toString().contains(alternateName))
+ break;
+ }
+
+ if (i == alsoList.size()) {
+ alternateName += "()";
+
+ Text also;
+ also << Atom(Atom::Link, alternateName)
+ << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK)
+ << alternateName
+ << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK);
+ alsoList.prepend(also);
+ }
+ }
+ }
+ }
+}
+
+QMap<QString, QString>& Generator::formattingLeftMap()
+{
+ return fmtLeftMaps[format()];
+}
+
+QMap<QString, QString>& Generator::formattingRightMap()
+{
+ return fmtRightMaps[format()];
+}
+
+QString Generator::trimmedTrailing(const QString &string)
+{
+ QString trimmed = string;
+ while (trimmed.length() > 0 && trimmed[trimmed.length() - 1].isSpace())
+ trimmed.truncate(trimmed.length() - 1);
+ return trimmed;
+}
+
+void Generator::generateStatus(const Node *node, CodeMarker *marker)
+{
+ Text text;
+
+ switch (node->status()) {
+ case Node::Commendable:
+ case Node::Main:
+ break;
+ case Node::Preliminary:
+ text << Atom::ParaLeft << Atom(Atom::FormattingLeft, ATOM_FORMATTING_BOLD) << "This "
+ << typeString(node) << " is under development and is subject to change."
+ << Atom(Atom::FormattingRight, ATOM_FORMATTING_BOLD) << Atom::ParaRight;
+ break;
+ case Node::Deprecated:
+ text << Atom::ParaLeft;
+ if (node->isInnerNode())
+ text << Atom(Atom::FormattingLeft, ATOM_FORMATTING_BOLD);
+ text << "This " << typeString(node) << " is deprecated.";
+ if (node->isInnerNode())
+ text << Atom(Atom::FormattingRight, ATOM_FORMATTING_BOLD);
+ text << Atom::ParaRight;
+ break;
+ case Node::Obsolete:
+ text << Atom::ParaLeft;
+ if (node->isInnerNode())
+ text << Atom(Atom::FormattingLeft, ATOM_FORMATTING_BOLD);
+ text << "This " << typeString(node) << " is obsolete.";
+ if (node->isInnerNode())
+ text << Atom(Atom::FormattingRight, ATOM_FORMATTING_BOLD);
+ text << " It is provided to keep old source code working. We strongly advise against "
+ << "using it in new code." << Atom::ParaRight;
+ break;
+ case Node::Compat:
+ // reimplemented in HtmlGenerator subclass
+ if (node->isInnerNode()) {
+ text << Atom::ParaLeft << Atom(Atom::FormattingLeft, ATOM_FORMATTING_BOLD) << "This "
+ << typeString(node) << " is part of the Qt 3 compatibility layer."
+ << Atom(Atom::FormattingRight, ATOM_FORMATTING_BOLD)
+ << " It is provided to keep old source code working. We strongly advise against "
+ << "using it in new code. See "
+ << Atom(Atom::AutoLink, "Porting to Qt 4")
+ << " for more information."
+ << Atom::ParaRight;
+ }
+ break;
+ case Node::Internal:
+ default:
+ break;
+ }
+ generateText(text, node, marker);
+}
+
+void Generator::generateThreadSafeness(const Node *node, CodeMarker *marker)
+{
+ Text text;
+ Text theStockLink;
+ Node::ThreadSafeness parent = node->parent()->inheritedThreadSafeness();
+
+ switch (node->threadSafeness()) {
+ case Node::UnspecifiedSafeness:
+ break;
+ case Node::NonReentrant:
+ text << Atom::ParaLeft << Atom(Atom::FormattingLeft, ATOM_FORMATTING_BOLD) << "Warning:"
+ << Atom(Atom::FormattingRight, ATOM_FORMATTING_BOLD) << " This "
+ << typeString(node) << " is not " << stockLink("reentrant") << "." << Atom::ParaRight;
+ break;
+ case Node::Reentrant:
+ case Node::ThreadSafe:
+ text << Atom::ParaLeft << Atom(Atom::FormattingLeft, ATOM_FORMATTING_BOLD);
+ if (parent == Node::ThreadSafe) {
+ text << "Warning:";
+ } else {
+ text << "Note:";
+ }
+ text << Atom(Atom::FormattingRight, ATOM_FORMATTING_BOLD) << " ";
+
+ if (node->threadSafeness() == Node::ThreadSafe)
+ theStockLink = stockLink("thread-safe");
+ else
+ theStockLink = stockLink("reentrant");
+
+ if (node->isInnerNode()) {
+ const InnerNode *innerNode = static_cast<const InnerNode *>(node);
+ text << "All the functions in this " << typeString(node) << " are "
+ << theStockLink;
+
+ NodeList except;
+ NodeList::ConstIterator c = innerNode->childNodes().begin();
+ while (c != innerNode->childNodes().end()) {
+ if ((*c)->threadSafeness() != Node::UnspecifiedSafeness)
+ except.append(*c);
+ ++c;
+ }
+ if (except.isEmpty()) {
+ text << ".";
+ }
+ else {
+ text << ", except ";
+
+ NodeList::ConstIterator e = except.begin();
+ int index = 0;
+ while (e != except.end()) {
+ appendFullName(text, *e, innerNode, marker);
+ text << separator(index++, except.count());
+ ++e;
+ }
+ }
+ }
+ else {
+ text << "This " << typeString(node) << " is " << theStockLink << ".";
+ }
+ text << Atom::ParaRight;
+ }
+ generateText(text, node, marker);
+}
+
+void Generator::generateSince(const Node *node, CodeMarker *marker)
+{
+ if (!node->since().isEmpty()) {
+ Text text;
+ text << Atom::ParaLeft << "This " << typeString(node)
+ << " was introduced in ";
+ if (project.isEmpty())
+ text << "version";
+ else
+ text << project;
+ text << " " << node->since() << "." << Atom::ParaRight;
+ generateText(text, node, marker);
+ }
+}
+
+/*!
+ No longer in use.
+ */
+void Generator::generateOverload(const Node *node, CodeMarker *marker)
+{
+ Text text;
+ text << Atom::ParaLeft
+ << "This function overloads ";
+ QString t = node->name() + "()";
+ text << Atom::AutoLink << t
+ << Atom::ParaRight;
+ generateText(text, node, marker);
+}
+
+void Generator::generateReimplementedFrom(const FunctionNode *func,
+ CodeMarker *marker)
+{
+ if (func->reimplementedFrom() != 0) {
+ const FunctionNode *from = func->reimplementedFrom();
+ if (from->access() != Node::Private && from->parent()->access() != Node::Private) {
+ Text text;
+ text << Atom::ParaLeft << "Reimplemented from ";
+ appendFullName(text, from->parent(), func, marker, from);
+ text << "." << Atom::ParaRight;
+ generateText(text, func, marker);
+ }
+ }
+}
+
+const Atom *Generator::generateAtomList(const Atom *atom,
+ const Node *relative,
+ CodeMarker *marker,
+ bool generate,
+ int &numAtoms)
+{
+ while (atom) {
+ if (atom->type() == Atom::FormatIf) {
+ int numAtoms0 = numAtoms;
+ bool rightFormat = canHandleFormat(atom->string());
+ atom = generateAtomList(atom->next(),
+ relative,
+ marker,
+ generate && rightFormat,
+ numAtoms);
+ if (!atom)
+ return 0;
+
+ if (atom->type() == Atom::FormatElse) {
+ ++numAtoms;
+ atom = generateAtomList(atom->next(),
+ relative,
+ marker,
+ generate && !rightFormat,
+ numAtoms);
+ if (!atom)
+ return 0;
+ }
+
+ if (atom->type() == Atom::FormatEndif) {
+ if (generate && numAtoms0 == numAtoms) {
+ relative->location().warning(tr("Output format %1 not handled").
+ arg(format()));
+ Atom unhandledFormatAtom(Atom::UnhandledFormat, format());
+ generateAtomList(&unhandledFormatAtom,
+ relative,
+ marker,
+ generate,
+ numAtoms);
+ }
+ atom = atom->next();
+ }
+ }
+ else if (atom->type() == Atom::FormatElse || atom->type() == Atom::FormatEndif) {
+ return atom;
+ }
+ else {
+ int n = 1;
+ if (generate) {
+ n += generateAtom(atom, relative, marker);
+ numAtoms += n;
+ }
+ while (n-- > 0)
+ atom = atom->next();
+ }
+ }
+ return 0;
+}
+
+void Generator::appendFullName(Text& text,
+ const Node *apparentNode,
+ const Node *relative,
+ CodeMarker *marker,
+ const Node *actualNode)
+{
+ if (actualNode == 0)
+ actualNode = apparentNode;
+ text << Atom(Atom::LinkNode, CodeMarker::stringForNode(actualNode))
+ << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK)
+ << Atom(Atom::String, marker->plainFullName(apparentNode, relative))
+ << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK);
+}
+
+void Generator::appendSortedNames(Text& text,
+ const ClassNode *classe,
+ const QList<RelatedClass> &classes,
+ CodeMarker *marker)
+{
+ QList<RelatedClass>::ConstIterator r;
+ QMap<QString,Text> classMap;
+ int index = 0;
+
+ r = classes.begin();
+ while (r != classes.end()) {
+ if ((*r).node->access() == Node::Public && (*r).node->status() != Node::Internal
+ && !(*r).node->doc().isEmpty()) {
+ Text className;
+ appendFullName(className, (*r).node, classe, marker);
+ classMap[className.toString().toLower()] = className;
+ }
+ ++r;
+ }
+
+ QStringList classNames = classMap.keys();
+ classNames.sort();
+
+ foreach (const QString &className, classNames) {
+ text << classMap[className];
+ text << separator(index++, classNames.count());
+ }
+}
+
+int Generator::skipAtoms(const Atom *atom, Atom::Type type) const
+{
+ int skipAhead = 0;
+ atom = atom->next();
+ while (atom != 0 && atom->type() != type) {
+ skipAhead++;
+ atom = atom->next();
+ }
+ return skipAhead;
+}
+
+QString Generator::fullName(const Node *node,
+ const Node *relative,
+ CodeMarker *marker) const
+{
+ if (node->type() == Node::Fake)
+ return static_cast<const FakeNode *>(node)->title();
+ else if (node->type() == Node::Class &&
+ !(static_cast<const ClassNode *>(node))->serviceName().isEmpty())
+ return (static_cast<const ClassNode *>(node))->serviceName();
+ else
+ return marker->plainFullName(node, relative);
+}
+
+QT_END_NAMESPACE
diff --git a/tools/qdoc3/generator.h b/tools/qdoc3/generator.h
new file mode 100644
index 0000000..3db2d8d
--- /dev/null
+++ b/tools/qdoc3/generator.h
@@ -0,0 +1,177 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*
+ generator.h
+*/
+
+#ifndef GENERATOR_H
+#define GENERATOR_H
+
+#include <qlist.h>
+#include <qmap.h>
+#include <qregexp.h>
+#include <qstring.h>
+#include <qstringlist.h>
+
+#include "node.h"
+#include "text.h"
+
+QT_BEGIN_NAMESPACE
+
+class ClassNode;
+class Config;
+class CodeMarker;
+class FakeNode;
+class FunctionNode;
+class InnerNode;
+class Location;
+class NamespaceNode;
+class Node;
+class Tree;
+
+class Generator
+{
+ public:
+ Generator();
+ virtual ~Generator();
+
+ virtual void initializeGenerator(const Config &config);
+ virtual void terminateGenerator();
+ virtual QString format() = 0;
+ virtual bool canHandleFormat(const QString &format) { return format == this->format(); }
+ virtual void generateTree(const Tree *tree, CodeMarker *marker) = 0;
+
+ static void initialize(const Config& config);
+ static void terminate();
+ static Generator *generatorForFormat(const QString& format);
+
+ protected:
+ virtual void startText(const Node *relative, CodeMarker *marker);
+ virtual void endText(const Node *relative, CodeMarker *marker);
+ virtual int generateAtom(const Atom *atom,
+ const Node *relative,
+ CodeMarker *marker);
+ virtual void generateClassLikeNode(const InnerNode *inner, CodeMarker *marker);
+ virtual void generateFakeNode(const FakeNode *fake, CodeMarker *marker);
+
+ virtual void generateText(const Text& text,
+ const Node *relative,
+ CodeMarker *marker);
+#ifdef QDOC_QML
+ virtual void generateQmlText(const Text& text,
+ const Node *relative,
+ CodeMarker *marker);
+#endif
+ virtual void generateBody(const Node *node, CodeMarker *marker);
+ virtual void generateAlsoList(const Node *node, CodeMarker *marker);
+ virtual void generateInherits(const ClassNode *classe,
+ CodeMarker *marker);
+ virtual void generateInheritedBy(const ClassNode *classe,
+ CodeMarker *marker);
+
+ void generateThreadSafeness(const Node *node, CodeMarker *marker);
+ void generateSince(const Node *node, CodeMarker *marker);
+ void generateStatus(const Node *node, CodeMarker *marker);
+ const Atom *generateAtomList(const Atom *atom,
+ const Node *relative,
+ CodeMarker *marker,
+ bool generate,
+ int& numGeneratedAtoms);
+ void generateExampleFiles(const FakeNode *fake, CodeMarker *marker);
+ void generateModuleWarning(const ClassNode *classe, CodeMarker *marker);
+
+ virtual int skipAtoms(const Atom *atom, Atom::Type type) const;
+ virtual QString fullName(const Node *node,
+ const Node *relative,
+ CodeMarker *marker) const;
+
+ const QString& outputDir() { return outDir; }
+ QString indent(int level, const QString& markedCode);
+ QString plainCode(const QString& markedCode);
+ virtual QString typeString(const Node *node);
+ virtual QString imageFileName(const Node *relative, const QString& fileBase);
+ void setImageFileExtensions(const QStringList& extensions);
+ void unknownAtom(const Atom *atom);
+ QMap<QString, QString> &formattingLeftMap();
+ QMap<QString, QString> &formattingRightMap();
+
+ QMap<QString, QStringList> editionModuleMap;
+ QMap<QString, QStringList> editionGroupMap;
+
+ static QString trimmedTrailing(const QString &string);
+ static bool matchAhead(const Atom *atom, Atom::Type expectedAtomType);
+ static void supplementAlsoList(const Node *node, QList<Text> &alsoList);
+
+ private:
+ void generateOverload(const Node *node, CodeMarker *marker);
+ void generateReimplementedFrom(const FunctionNode *func,
+ CodeMarker *marker);
+ void appendFullName(Text& text,
+ const Node *apparentNode,
+ const Node *relative,
+ CodeMarker *marker,
+ const Node *actualNode = 0);
+ void appendSortedNames(Text& text,
+ const ClassNode *classe,
+ const QList<RelatedClass> &classes,
+ CodeMarker *marker);
+
+ QString amp;
+ QString lt;
+ QString gt;
+ QString quot;
+ QRegExp tag;
+
+ static QList<Generator *> generators;
+ static QMap<QString, QMap<QString, QString> > fmtLeftMaps;
+ static QMap<QString, QMap<QString, QString> > fmtRightMaps;
+ static QMap<QString, QStringList> imgFileExts;
+ static QSet<QString> outputFormats;
+ static QStringList imageFiles;
+ static QStringList imageDirs;
+ static QString outDir;
+ static QString project;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/tools/qdoc3/helpprojectwriter.cpp b/tools/qdoc3/helpprojectwriter.cpp
new file mode 100644
index 0000000..d0fa7c0
--- /dev/null
+++ b/tools/qdoc3/helpprojectwriter.cpp
@@ -0,0 +1,653 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtXml>
+#include <QHash>
+#include <QMap>
+
+#include "atom.h"
+#include "helpprojectwriter.h"
+#include "htmlgenerator.h"
+#include "config.h"
+#include "node.h"
+#include "tree.h"
+
+QT_BEGIN_NAMESPACE
+
+HelpProjectWriter::HelpProjectWriter(const Config &config, const QString &defaultFileName)
+{
+ // The output directory should already have been checked by the calling
+ // generator.
+ outputDir = config.getString(CONFIG_OUTPUTDIR);
+
+ QStringList names = config.getStringList(CONFIG_QHP + Config::dot + "projects");
+
+ foreach (const QString &projectName, names) {
+ HelpProject project;
+ project.name = projectName;
+
+ QString prefix = CONFIG_QHP + Config::dot + projectName + Config::dot;
+ project.helpNamespace = config.getString(prefix + "namespace");
+ project.virtualFolder = config.getString(prefix + "virtualFolder");
+ project.fileName = config.getString(prefix + "file");
+ if (project.fileName.isEmpty())
+ project.fileName = defaultFileName;
+ project.extraFiles = config.getStringSet(prefix + "extraFiles");
+ project.indexTitle = config.getString(prefix + "indexTitle");
+ project.indexRoot = config.getString(prefix + "indexRoot");
+ project.filterAttributes = config.getStringList(prefix + "filterAttributes").toSet();
+ QSet<QString> customFilterNames = config.subVars(prefix + "customFilters");
+ foreach (const QString &filterName, customFilterNames) {
+ QString name = config.getString(prefix + "customFilters" + Config::dot + filterName + Config::dot + "name");
+ QSet<QString> filters = config.getStringList(prefix + "customFilters" + Config::dot + filterName + Config::dot + "filterAttributes").toSet();
+ project.customFilters[name] = filters;
+ }
+ //customFilters = config.defs.
+
+ foreach (QString name, config.getStringSet(prefix + "excluded"))
+ project.excluded.insert(name.replace("\\", "/"));
+
+ foreach (const QString &name, config.getStringList(prefix + "subprojects")) {
+ SubProject subproject;
+ QString subprefix = prefix + "subprojects" + Config::dot + name + Config::dot;
+ subproject.title = config.getString(subprefix + "title");
+ subproject.indexTitle = config.getString(subprefix + "indexTitle");
+ subproject.sortPages = config.getBool(subprefix + "sortPages");
+ readSelectors(subproject, config.getStringList(subprefix + "selectors"));
+ project.subprojects[name] = subproject;
+ }
+
+ if (project.subprojects.isEmpty()) {
+ SubProject subproject;
+ readSelectors(subproject, config.getStringList(prefix + "selectors"));
+ project.subprojects[""] = subproject;
+ }
+
+ projects.append(project);
+ }
+}
+
+void HelpProjectWriter::readSelectors(SubProject &subproject, const QStringList &selectors)
+{
+ QHash<QString, Node::Type> typeHash;
+ typeHash["namespace"] = Node::Namespace;
+ typeHash["class"] = Node::Class;
+ typeHash["fake"] = Node::Fake;
+ typeHash["enum"] = Node::Enum;
+ typeHash["typedef"] = Node::Typedef;
+ typeHash["function"] = Node::Function;
+ typeHash["property"] = Node::Property;
+ typeHash["variable"] = Node::Variable;
+ typeHash["target"] = Node::Target;
+
+ QHash<QString, FakeNode::SubType> subTypeHash;
+ subTypeHash["example"] = FakeNode::Example;
+ subTypeHash["headerfile"] = FakeNode::HeaderFile;
+ subTypeHash["file"] = FakeNode::File;
+ subTypeHash["group"] = FakeNode::Group;
+ subTypeHash["module"] = FakeNode::Module;
+ subTypeHash["page"] = FakeNode::Page;
+ subTypeHash["externalpage"] = FakeNode::ExternalPage;
+
+ QSet<FakeNode::SubType> allSubTypes = QSet<FakeNode::SubType>::fromList(subTypeHash.values());
+
+ foreach (const QString &selector, selectors) {
+ QStringList pieces = selector.split(":");
+ if (pieces.size() == 1) {
+ QString lower = selector.toLower();
+ if (typeHash.contains(lower))
+ subproject.selectors[typeHash[lower]] = allSubTypes;
+ } else if (pieces.size() >= 2) {
+ QString lower = pieces[0].toLower();
+ pieces = pieces[1].split(",");
+ if (typeHash.contains(lower)) {
+ QSet<FakeNode::SubType> subTypes;
+ for (int i = 0; i < pieces.size(); ++i) {
+ QString lower = pieces[i].toLower();
+ if (subTypeHash.contains(lower))
+ subTypes.insert(subTypeHash[lower]);
+ }
+ subproject.selectors[typeHash[lower]] = subTypes;
+ }
+ }
+ }
+}
+
+void HelpProjectWriter::addExtraFile(const QString &file)
+{
+ for (int i = 0; i < projects.size(); ++i)
+ projects[i].extraFiles.insert(file);
+}
+
+void HelpProjectWriter::addExtraFiles(const QSet<QString> &files)
+{
+ for (int i = 0; i < projects.size(); ++i)
+ projects[i].extraFiles.unite(files);
+}
+
+/*
+ Returns a list of strings describing the keyword details for a given node.
+
+ The first string is the human-readable name to be shown in Assistant.
+ The second string is a unique identifier.
+ The third string is the location of the documentation for the keyword.
+*/
+QStringList HelpProjectWriter::keywordDetails(const Node *node) const
+{
+ QStringList details;
+
+ if (node->parent() && !node->parent()->name().isEmpty()) {
+ // "name"
+ if (node->type() == Node::Enum || node->type() == Node::Typedef)
+ details << node->parent()->name()+"::"+node->name();
+ else
+ details << node->name();
+ // "id"
+ details << node->parent()->name()+"::"+node->name();
+ } else if (node->type() == Node::Fake) {
+ const FakeNode *fake = static_cast<const FakeNode *>(node);
+ details << fake->fullTitle();
+ details << fake->fullTitle();
+ } else {
+ details << node->name();
+ details << node->name();
+ }
+ details << tree->fullDocumentLocation(node);
+
+ return details;
+}
+
+bool HelpProjectWriter::generateSection(HelpProject &project,
+ QXmlStreamWriter & /* writer */, const Node *node)
+{
+ if (!node->url().isEmpty())
+ return false;
+
+ if (node->access() == Node::Private || node->status() == Node::Internal)
+ return false;
+
+ if (node->name().isEmpty())
+ return true;
+
+ QString docPath = node->doc().location().filePath();
+ if (!docPath.isEmpty() && project.excluded.contains(docPath))
+ return false;
+
+ QString objName;
+ if (node->type() == Node::Fake) {
+ const FakeNode *fake = static_cast<const FakeNode *>(node);
+ objName = fake->fullTitle();
+ } else
+ objName = tree->fullDocumentName(node);
+
+ // Only add nodes to the set for each subproject if they match a selector.
+ // Those that match will be listed in the table of contents.
+
+ foreach (const QString &name, project.subprojects.keys()) {
+ SubProject subproject = project.subprojects[name];
+ // No selectors: accept all nodes.
+ if (subproject.selectors.isEmpty())
+ project.subprojects[name].nodes[objName] = node;
+ else if (subproject.selectors.contains(node->type())) {
+ // Accept only the node types in the selectors hash.
+ if (node->type() != Node::Fake)
+ project.subprojects[name].nodes[objName] = node;
+ else {
+ // Accept only fake nodes with subtypes contained in the selector's
+ // mask.
+ const FakeNode *fakeNode = static_cast<const FakeNode *>(node);
+ if (subproject.selectors[node->type()].contains(fakeNode->subType()) &&
+ fakeNode->subType() != FakeNode::ExternalPage &&
+ !fakeNode->fullTitle().isEmpty())
+
+ project.subprojects[name].nodes[objName] = node;
+ }
+ }
+ }
+
+ switch (node->type()) {
+
+ case Node::Class:
+ project.keywords.append(keywordDetails(node));
+ project.files.insert(tree->fullDocumentLocation(node));
+ break;
+
+ case Node::Namespace:
+ project.keywords.append(keywordDetails(node));
+ project.files.insert(tree->fullDocumentLocation(node));
+ break;
+
+ case Node::Enum:
+ project.keywords.append(keywordDetails(node));
+ {
+ const EnumNode *enumNode = static_cast<const EnumNode*>(node);
+ foreach (const EnumItem &item, enumNode->items()) {
+ QStringList details;
+
+ if (enumNode->itemAccess(item.name()) == Node::Private)
+ continue;
+
+ if (!node->parent()->name().isEmpty()) {
+ details << node->parent()->name()+"::"+item.name(); // "name"
+ details << node->parent()->name()+"::"+item.name(); // "id"
+ } else {
+ details << item.name(); // "name"
+ details << item.name(); // "id"
+ }
+ details << tree->fullDocumentLocation(node);
+ project.keywords.append(details);
+ }
+ }
+ break;
+
+ case Node::Property:
+ project.keywords.append(keywordDetails(node));
+ break;
+
+ case Node::Function:
+ {
+ const FunctionNode *funcNode = static_cast<const FunctionNode *>(node);
+
+ // Only insert keywords for non-constructors. Constructors are covered
+ // by the classes themselves.
+
+ if (funcNode->metaness() != FunctionNode::Ctor)
+ project.keywords.append(keywordDetails(node));
+
+ // Insert member status flags into the entries for the parent
+ // node of the function, or the node it is related to.
+ // Since parent nodes should have already been inserted into
+ // the set of files, we only need to ensure that related nodes
+ // are inserted.
+
+ if (node->relates()) {
+ project.memberStatus[node->relates()].insert(node->status());
+ project.files.insert(tree->fullDocumentLocation(node->relates()));
+ } else if (node->parent())
+ project.memberStatus[node->parent()].insert(node->status());
+ }
+ break;
+
+ case Node::Typedef:
+ {
+ const TypedefNode *typedefNode = static_cast<const TypedefNode *>(node);
+ QStringList typedefDetails = keywordDetails(node);
+ const EnumNode *enumNode = typedefNode->associatedEnum();
+ // Use the location of any associated enum node in preference
+ // to that of the typedef.
+ if (enumNode)
+ typedefDetails[2] = tree->fullDocumentLocation(enumNode);
+
+ project.keywords.append(typedefDetails);
+ }
+ break;
+
+ // Fake nodes (such as manual pages) contain subtypes, titles and other
+ // attributes.
+ case Node::Fake: {
+ const FakeNode *fakeNode = static_cast<const FakeNode*>(node);
+ if (fakeNode->subType() != FakeNode::ExternalPage &&
+ !fakeNode->fullTitle().isEmpty()) {
+
+ if (fakeNode->subType() != FakeNode::File) {
+ if (fakeNode->doc().hasKeywords()) {
+ foreach (const Atom *keyword, fakeNode->doc().keywords()) {
+ if (!keyword->string().isEmpty()) {
+ QStringList details;
+ details << keyword->string()
+ << keyword->string()
+ << tree->fullDocumentLocation(node) + "#" + Doc::canonicalTitle(keyword->string());
+ project.keywords.append(details);
+ } else
+ fakeNode->doc().location().warning(
+ tr("Bad keyword in %1").arg(tree->fullDocumentLocation(node))
+ );
+ }
+ }
+ project.keywords.append(keywordDetails(node));
+ }
+/*
+ if (fakeNode->doc().hasTableOfContents()) {
+ foreach (const Atom *item, fakeNode->doc().tableOfContents()) {
+ QString title = Text::sectionHeading(item).toString();
+ if (!title.isEmpty()) {
+ QStringList details;
+ details << title
+ << title
+ << tree->fullDocumentLocation(node) + "#" + Doc::canonicalTitle(title);
+ project.keywords.append(details);
+ } else
+ fakeNode->doc().location().warning(
+ tr("Bad contents item in %1").arg(tree->fullDocumentLocation(node))
+ );
+ }
+ }
+*/
+ project.files.insert(tree->fullDocumentLocation(node));
+ }
+ break;
+ }
+ default:
+ ;
+ }
+
+ // Add all images referenced in the page to the set of files to include.
+ const Atom *atom = node->doc().body().firstAtom();
+ while (atom) {
+ if (atom->type() == Atom::Image || atom->type() == Atom::InlineImage) {
+ // Images are all placed within a single directory regardless of
+ // whether the source images are in a nested directory structure.
+ QStringList pieces = atom->string().split("/");
+ project.files.insert("images/" + pieces.last());
+ }
+ atom = atom->next();
+ }
+
+ return true;
+}
+
+void HelpProjectWriter::generateSections(HelpProject &project,
+ QXmlStreamWriter &writer, const Node *node)
+{
+ if (!generateSection(project, writer, node))
+ return;
+
+ if (node->isInnerNode()) {
+ const InnerNode *inner = static_cast<const InnerNode *>(node);
+
+ // Ensure that we don't visit nodes more than once.
+ QMap<QString, const Node*> childMap;
+ foreach (const Node *node, inner->childNodes()) {
+ if (node->access() == Node::Private)
+ continue;
+ if (node->type() == Node::Fake)
+ childMap[static_cast<const FakeNode *>(node)->fullTitle()] = node;
+ else {
+ if (node->type() == Node::Function) {
+ const FunctionNode *funcNode = static_cast<const FunctionNode *>(node);
+ if (funcNode->isOverload())
+ continue;
+ }
+ childMap[tree->fullDocumentName(node)] = node;
+ }
+ }
+
+ foreach (const Node *child, childMap)
+ generateSections(project, writer, child);
+ }
+}
+
+void HelpProjectWriter::generate(const Tree *tre)
+{
+ this->tree = tre;
+ for (int i = 0; i < projects.size(); ++i)
+ generateProject(projects[i]);
+}
+
+void HelpProjectWriter::writeNode(HelpProject &project, QXmlStreamWriter &writer,
+ const Node *node)
+{
+ QString href = tree->fullDocumentLocation(node);
+ QString objName = node->name();
+
+ switch (node->type()) {
+
+ case Node::Class:
+ writer.writeStartElement("section");
+ writer.writeAttribute("ref", href);
+ if (node->parent() && !node->parent()->name().isEmpty())
+ writer.writeAttribute("title", tr("%1::%2 Class Reference").arg(node->parent()->name()).arg(objName));
+ else
+ writer.writeAttribute("title", tr("%1 Class Reference").arg(objName));
+
+ // Write subsections for all members, obsolete members and Qt 3
+ // members.
+ if (!project.memberStatus[node].isEmpty()) {
+ QString membersPath = href.left(href.size()-5) + "-members.html";
+ writer.writeStartElement("section");
+ writer.writeAttribute("ref", membersPath);
+ writer.writeAttribute("title", tr("List of all members"));
+ writer.writeEndElement(); // section
+ project.files.insert(membersPath);
+ }
+ if (project.memberStatus[node].contains(Node::Compat)) {
+ QString compatPath = href.left(href.size()-5) + "-qt3.html";
+ writer.writeStartElement("section");
+ writer.writeAttribute("ref", compatPath);
+ writer.writeAttribute("title", tr("Qt 3 support members"));
+ writer.writeEndElement(); // section
+ project.files.insert(compatPath);
+ }
+ if (project.memberStatus[node].contains(Node::Obsolete)) {
+ QString obsoletePath = href.left(href.size()-5) + "-obsolete.html";
+ writer.writeStartElement("section");
+ writer.writeAttribute("ref", obsoletePath);
+ writer.writeAttribute("title", tr("Obsolete members"));
+ writer.writeEndElement(); // section
+ project.files.insert(obsoletePath);
+ }
+
+ writer.writeEndElement(); // section
+ break;
+
+ case Node::Namespace:
+ writer.writeStartElement("section");
+ writer.writeAttribute("ref", href);
+ writer.writeAttribute("title", objName);
+ writer.writeEndElement(); // section
+ break;
+
+ case Node::Fake: {
+ // Fake nodes (such as manual pages) contain subtypes, titles and other
+ // attributes.
+ const FakeNode *fakeNode = static_cast<const FakeNode*>(node);
+
+ writer.writeStartElement("section");
+ writer.writeAttribute("ref", href);
+ writer.writeAttribute("title", fakeNode->fullTitle());
+ // qDebug() << "Title:" << fakeNode->fullTitle();
+
+ if (fakeNode->subType() == FakeNode::HeaderFile) {
+
+ // Write subsections for all members, obsolete members and Qt 3
+ // members.
+ if (!project.memberStatus[node].isEmpty()) {
+ QString membersPath = href.left(href.size()-5) + "-members.html";
+ writer.writeStartElement("section");
+ writer.writeAttribute("ref", membersPath);
+ writer.writeAttribute("title", tr("List of all members"));
+ writer.writeEndElement(); // section
+ project.files.insert(membersPath);
+ }
+ if (project.memberStatus[node].contains(Node::Compat)) {
+ QString compatPath = href.left(href.size()-5) + "-qt3.html";
+ writer.writeStartElement("section");
+ writer.writeAttribute("ref", compatPath);
+ writer.writeAttribute("title", tr("Qt 3 support members"));
+ writer.writeEndElement(); // section
+ project.files.insert(compatPath);
+ }
+ if (project.memberStatus[node].contains(Node::Obsolete)) {
+ QString obsoletePath = href.left(href.size()-5) + "-obsolete.html";
+ writer.writeStartElement("section");
+ writer.writeAttribute("ref", obsoletePath);
+ writer.writeAttribute("title", tr("Obsolete members"));
+ writer.writeEndElement(); // section
+ project.files.insert(obsoletePath);
+ }
+ }
+
+ writer.writeEndElement(); // section
+ }
+ break;
+ default:
+ ;
+ }
+}
+
+void HelpProjectWriter::generateProject(HelpProject &project)
+{
+ const Node *rootNode;
+ if (!project.indexRoot.isEmpty())
+ rootNode = tree->findFakeNodeByTitle(project.indexRoot);
+ else
+ rootNode = tree->root();
+
+ if (!rootNode)
+ return;
+
+ project.files.clear();
+ project.keywords.clear();
+
+ QFile file(outputDir + QDir::separator() + project.fileName);
+ if (!file.open(QFile::WriteOnly | QFile::Text))
+ return;
+
+ QXmlStreamWriter writer(&file);
+ writer.setAutoFormatting(true);
+ writer.writeStartDocument();
+ writer.writeStartElement("QtHelpProject");
+ writer.writeAttribute("version", "1.0");
+
+ // Write metaData, virtualFolder and namespace elements.
+ writer.writeTextElement("namespace", project.helpNamespace);
+ writer.writeTextElement("virtualFolder", project.virtualFolder);
+
+ // Write customFilter elements.
+ QHash<QString, QSet<QString> >::ConstIterator it;
+ for (it = project.customFilters.begin(); it != project.customFilters.end(); ++it) {
+ writer.writeStartElement("customFilter");
+ writer.writeAttribute("name", it.key());
+ foreach (const QString &filter, it.value())
+ writer.writeTextElement("filterAttribute", filter);
+ writer.writeEndElement(); // customFilter
+ }
+
+ // Start the filterSection.
+ writer.writeStartElement("filterSection");
+
+ // Write filterAttribute elements.
+ foreach (const QString &filterName, project.filterAttributes)
+ writer.writeTextElement("filterAttribute", filterName);
+
+ writer.writeStartElement("toc");
+ writer.writeStartElement("section");
+ QString indexPath = tree->fullDocumentLocation(tree->findFakeNodeByTitle(project.indexTitle));
+ if (indexPath.isEmpty())
+ indexPath = "index.html";
+ writer.writeAttribute("ref", HtmlGenerator::cleanRef(indexPath));
+ writer.writeAttribute("title", project.indexTitle);
+ project.files.insert(tree->fullDocumentLocation(rootNode));
+
+ generateSections(project, writer, rootNode);
+
+ foreach (const QString &name, project.subprojects.keys()) {
+ SubProject subproject = project.subprojects[name];
+
+ if (!name.isEmpty()) {
+ writer.writeStartElement("section");
+ QString indexPath = tree->fullDocumentLocation(tree->findFakeNodeByTitle(subproject.indexTitle));
+ writer.writeAttribute("ref", HtmlGenerator::cleanRef(indexPath));
+ writer.writeAttribute("title", subproject.title);
+ project.files.insert(indexPath);
+ }
+ if (subproject.sortPages) {
+ QStringList titles = subproject.nodes.keys();
+ titles.sort();
+ foreach (const QString &title, titles)
+ writeNode(project, writer, subproject.nodes[title]);
+ } else {
+ // Find a contents node and navigate from there, using the NextLink values.
+ foreach (const Node *node, subproject.nodes) {
+ QString nextTitle = node->links().value(Node::NextLink).first;
+ if (!nextTitle.isEmpty() &&
+ node->links().value(Node::ContentsLink).first.isEmpty()) {
+
+ FakeNode *nextPage = const_cast<FakeNode *>(tree->findFakeNodeByTitle(nextTitle));
+
+ // Write the contents node.
+ writeNode(project, writer, node);
+
+ while (nextPage) {
+ writeNode(project, writer, nextPage);
+ nextTitle = nextPage->links().value(Node::NextLink).first;
+ if (nextTitle.isEmpty())
+ break;
+ nextPage = const_cast<FakeNode *>(tree->findFakeNodeByTitle(nextTitle));
+ }
+ break;
+ }
+ }
+ }
+
+ if (!name.isEmpty())
+ writer.writeEndElement(); // section
+ }
+
+ writer.writeEndElement(); // section
+ writer.writeEndElement(); // toc
+
+ writer.writeStartElement("keywords");
+ foreach (const QStringList &details, project.keywords) {
+ writer.writeStartElement("keyword");
+ writer.writeAttribute("name", details[0]);
+ writer.writeAttribute("id", details[1]);
+ writer.writeAttribute("ref", HtmlGenerator::cleanRef(details[2]));
+ writer.writeEndElement(); //keyword
+ }
+ writer.writeEndElement(); // keywords
+
+ writer.writeStartElement("files");
+ foreach (const QString &usedFile, project.files) {
+ if (!usedFile.isEmpty())
+ writer.writeTextElement("file", usedFile);
+ }
+ foreach (const QString &usedFile, project.extraFiles)
+ writer.writeTextElement("file", usedFile);
+ writer.writeEndElement(); // files
+
+ writer.writeEndElement(); // filterSection
+ writer.writeEndElement(); // QtHelpProject
+ writer.writeEndDocument();
+ file.close();
+}
+
+QT_END_NAMESPACE
diff --git a/tools/qdoc3/helpprojectwriter.h b/tools/qdoc3/helpprojectwriter.h
new file mode 100644
index 0000000..f90b2a4
--- /dev/null
+++ b/tools/qdoc3/helpprojectwriter.h
@@ -0,0 +1,110 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef HELPPROJECTWRITER_H
+#define HELPPROJECTWRITER_H
+
+#include <QString>
+#include <QXmlStreamReader>
+#include <QXmlStreamWriter>
+
+#include "config.h"
+#include "node.h"
+
+QT_BEGIN_NAMESPACE
+
+class Tree;
+typedef QPair<QString, const Node*> QStringNodePair;
+
+struct SubProject
+{
+ QString title;
+ QString indexTitle;
+ QHash<Node::Type, QSet<FakeNode::SubType> > selectors;
+ bool sortPages;
+ QHash<QString, const Node *> nodes;
+};
+
+struct HelpProject
+{
+ QString name;
+ QString helpNamespace;
+ QString virtualFolder;
+ QString fileName;
+ QString indexRoot;
+ QString indexTitle;
+ QList<QStringList> keywords;
+ QSet<QString> files;
+ QSet<QString> extraFiles;
+ QSet<QString> filterAttributes;
+ QHash<QString, QSet<QString> > customFilters;
+ QSet<QString> excluded;
+ QMap<QString, SubProject> subprojects;
+ QHash<const Node *, QSet<Node::Status> > memberStatus;
+};
+
+class HelpProjectWriter
+{
+public:
+ HelpProjectWriter(const Config &config, const QString &defaultFileName);
+ void addExtraFile(const QString &file);
+ void addExtraFiles(const QSet<QString> &files);
+ void generate(const Tree *tre);
+
+private:
+ void generateProject(HelpProject &project);
+ void generateSections(HelpProject &project, QXmlStreamWriter &writer,
+ const Node *node);
+ bool generateSection(HelpProject &project, QXmlStreamWriter &writer,
+ const Node *node);
+ QStringList keywordDetails(const Node *node) const;
+ void writeNode(HelpProject &project, QXmlStreamWriter &writer, const Node *node);
+ void readSelectors(SubProject &subproject, const QStringList &selectors);
+
+ const Tree *tree;
+
+ QString outputDir;
+ QList<HelpProject> projects;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/tools/qdoc3/htmlgenerator.cpp b/tools/qdoc3/htmlgenerator.cpp
new file mode 100644
index 0000000..13d52bf
--- /dev/null
+++ b/tools/qdoc3/htmlgenerator.cpp
@@ -0,0 +1,3195 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*
+ htmlgenerator.cpp
+*/
+
+#include "codemarker.h"
+#include "helpprojectwriter.h"
+#include "htmlgenerator.h"
+#include "node.h"
+#include "separator.h"
+#include "tree.h"
+#include <ctype.h>
+
+#include <qdebug.h>
+#include <qlist.h>
+#include <qiterator.h>
+
+QT_BEGIN_NAMESPACE
+
+#define COMMAND_VERSION Doc::alias("version")
+
+static bool showBrokenLinks = false;
+
+HtmlGenerator::HtmlGenerator()
+ : helpProjectWriter(0), inLink(false), inContents(false),
+ inSectionHeading(false), inTableHeader(false), numTableRows(0),
+ threeColumnEnumValueTable(true), funcLeftParen("\\S(\\()"),
+ tre(0), slow(false)
+{
+}
+
+HtmlGenerator::~HtmlGenerator()
+{
+ if (helpProjectWriter)
+ delete helpProjectWriter;
+}
+
+void HtmlGenerator::initializeGenerator(const Config &config)
+{
+ static const struct {
+ const char *key;
+ const char *left;
+ const char *right;
+ } defaults[] = {
+ { ATOM_FORMATTING_BOLD, "<b>", "</b>" },
+ { ATOM_FORMATTING_INDEX, "<!--", "-->" },
+ { ATOM_FORMATTING_ITALIC, "<i>", "</i>" },
+ { ATOM_FORMATTING_PARAMETER, "<i>", "</i>" },
+ { ATOM_FORMATTING_SUBSCRIPT, "<sub>", "</sub>" },
+ { ATOM_FORMATTING_SUPERSCRIPT, "<sup>", "</sup>" },
+ { ATOM_FORMATTING_TELETYPE, "<tt>", "</tt>" },
+ { ATOM_FORMATTING_UNDERLINE, "<u>", "</u>" },
+ { 0, 0, 0 }
+ };
+
+ Generator::initializeGenerator(config);
+ setImageFileExtensions(QStringList() << "png" << "jpg" << "jpeg" << "gif");
+ int i = 0;
+ while (defaults[i].key) {
+ formattingLeftMap().insert(defaults[i].key, defaults[i].left);
+ formattingRightMap().insert(defaults[i].key, defaults[i].right);
+ i++;
+ }
+
+ style = config.getString(HtmlGenerator::format() + Config::dot + HTMLGENERATOR_STYLE);
+ postHeader = config.getString(HtmlGenerator::format() + Config::dot + HTMLGENERATOR_POSTHEADER);
+ footer = config.getString(HtmlGenerator::format() + Config::dot + HTMLGENERATOR_FOOTER);
+ address = config.getString(HtmlGenerator::format() + Config::dot + HTMLGENERATOR_ADDRESS);
+ pleaseGenerateMacRef = config.getBool(HtmlGenerator::format() + Config::dot + HTMLGENERATOR_GENERATEMACREFS);
+
+ project = config.getString(CONFIG_PROJECT);
+
+ projectDescription = config.getString(CONFIG_DESCRIPTION);
+ if (projectDescription.isEmpty() && !project.isEmpty())
+ projectDescription = project + " Reference Documentation";
+
+ projectUrl = config.getString(CONFIG_URL);
+
+ QSet<QString> editionNames = config.subVars(CONFIG_EDITION);
+ QSet<QString>::ConstIterator edition = editionNames.begin();
+ while (edition != editionNames.end()) {
+ QString editionName = *edition;
+ QStringList editionModules = config.getStringList(
+ CONFIG_EDITION + Config::dot + editionName + Config::dot + "modules");
+ QStringList editionGroups = config.getStringList(
+ CONFIG_EDITION + Config::dot + editionName + Config::dot + "groups");
+
+ if (!editionModules.isEmpty())
+ editionModuleMap[editionName] = editionModules;
+ if (!editionGroups.isEmpty())
+ editionGroupMap[editionName] = editionGroups;
+
+ ++edition;
+ }
+
+ slow = config.getBool(CONFIG_SLOW);
+
+ stylesheets = config.getStringList(HtmlGenerator::format() + Config::dot + HTMLGENERATOR_STYLESHEETS);
+ customHeadElements = config.getStringList(HtmlGenerator::format() + Config::dot + HTMLGENERATOR_CUSTOMHEADELEMENTS);
+ codeIndent = config.getInt(CONFIG_CODEINDENT);
+
+ helpProjectWriter = new HelpProjectWriter(config, project.toLower() + ".qhp");
+}
+
+void HtmlGenerator::terminateGenerator()
+{
+ Generator::terminateGenerator();
+}
+
+QString HtmlGenerator::format()
+{
+ return "HTML";
+}
+
+/*!
+ This is where the html files and dcf files are written.
+ \note The html file generation is done in the base class,
+ PageGenerator::generateTree().
+ */
+void HtmlGenerator::generateTree(const Tree *tree, CodeMarker *marker)
+{
+ // Copy the stylesheets from the directory containing the qdocconf file.
+ // ### This should be changed to use a special directory in doc/src.
+ QStringList::ConstIterator styleIter = stylesheets.begin();
+ QDir configPath = QDir::current();
+ while (styleIter != stylesheets.end()) {
+ QString filePath = configPath.absoluteFilePath(*styleIter);
+ Config::copyFile(Location(), filePath, filePath, outputDir());
+ ++styleIter;
+ }
+
+ tre = tree;
+ nonCompatClasses.clear();
+ mainClasses.clear();
+ compatClasses.clear();
+ moduleClassMap.clear();
+ moduleNamespaceMap.clear();
+ funcIndex.clear();
+ legaleseTexts.clear();
+ serviceClasses.clear();
+ findAllClasses(tree->root());
+ findAllFunctions(tree->root());
+ findAllLegaleseTexts(tree->root());
+ findAllNamespaces(tree->root());
+#ifdef ZZZ_QDOC_QML
+ findAllQmlClasses(tree->root());
+#endif
+
+ PageGenerator::generateTree(tree, marker);
+
+ dcfClassesRoot.ref = "classes.html";
+ dcfClassesRoot.title = "Classes";
+ qSort(dcfClassesRoot.subsections);
+
+ dcfOverviewsRoot.ref = "overviews.html";
+ dcfOverviewsRoot.title = "Overviews";
+ qSort(dcfOverviewsRoot.subsections);
+
+ dcfExamplesRoot.ref = "examples.html";
+ dcfExamplesRoot.title = "Tutorial & Examples";
+ qSort(dcfExamplesRoot.subsections);
+
+ DcfSection qtRoot;
+ appendDcfSubSection(&qtRoot, dcfClassesRoot);
+ appendDcfSubSection(&qtRoot, dcfOverviewsRoot);
+ appendDcfSubSection(&qtRoot, dcfExamplesRoot);
+
+ generateDcf(project.toLower().simplified().replace(" ", "-"),
+ "index.html",
+ projectDescription, qtRoot);
+ generateDcf("designer",
+ "designer-manual.html",
+ "Qt Designer Manual",
+ dcfDesignerRoot);
+ generateDcf("linguist",
+ "linguist-manual.html",
+ "Qt Linguist Manual",
+ dcfLinguistRoot);
+ generateDcf("assistant",
+ "assistant-manual.html",
+ "Qt Assistant Manual",
+ dcfAssistantRoot);
+ generateDcf("qmake",
+ "qmake-manual.html",
+ "qmake Manual",
+ dcfQmakeRoot);
+
+ generateIndex(project.toLower().simplified().replace(" ", "-"),
+ projectUrl,
+ projectDescription);
+
+ helpProjectWriter->generate(tre);
+}
+
+void HtmlGenerator::startText(const Node * /* relative */,
+ CodeMarker * /* marker */)
+{
+ inLink = false;
+ inContents = false;
+ inSectionHeading = false;
+ inTableHeader = false;
+ numTableRows = 0;
+ threeColumnEnumValueTable = true;
+ link.clear();
+ sectionNumber.clear();
+}
+
+int HtmlGenerator::generateAtom(const Atom *atom,
+ const Node *relative,
+ CodeMarker *marker)
+{
+ int skipAhead = 0;
+ static bool in_para = false;
+
+ switch (atom->type()) {
+ case Atom::AbstractLeft:
+ break;
+ case Atom::AbstractRight:
+ break;
+ case Atom::AutoLink:
+ if (!inLink && !inContents && !inSectionHeading) {
+ const Node *node = 0;
+ QString link = getLink(atom, relative, marker, node);
+ if (!link.isEmpty()) {
+ beginLink(link, node, relative, marker);
+ generateLink(atom, relative, marker);
+ endLink();
+ }
+ else {
+ out() << protect(atom->string());
+ }
+ }
+ else {
+ out() << protect(atom->string());
+ }
+ break;
+ case Atom::BaseName:
+ break;
+ case Atom::BriefLeft:
+ if (relative->type() == Node::Fake) {
+ skipAhead = skipAtoms(atom, Atom::BriefRight);
+ break;
+ }
+
+ out() << "<p>";
+ if (relative->type() == Node::Property ||
+ relative->type() == Node::Variable) {
+ QString str;
+ atom = atom->next();
+ while (atom != 0 && atom->type() != Atom::BriefRight) {
+ if (atom->type() == Atom::String || atom->type() == Atom::AutoLink)
+ str += atom->string();
+ skipAhead++;
+ atom = atom->next();
+ }
+ str[0] = str[0].toLower();
+ if (str.right(1) == ".")
+ str.truncate(str.length() - 1);
+ out() << "This ";
+ if (relative->type() == Node::Property)
+ out() << "property";
+ else
+ out() << "variable";
+ QStringList words = str.split(" ");
+ if (!(words.first() == "contains" || words.first() == "specifies"
+ || words.first() == "describes" || words.first() == "defines"
+ || words.first() == "holds" || words.first() == "determines"))
+ out() << " holds ";
+ else
+ out() << " ";
+ out() << str << ".";
+ }
+ break;
+ case Atom::BriefRight:
+ if (relative->type() != Node::Fake)
+ out() << "</p>\n";
+ break;
+ case Atom::C:
+ out() << formattingLeftMap()[ATOM_FORMATTING_TELETYPE];
+ if (inLink) {
+ out() << protect(plainCode(atom->string()));
+ }
+ else {
+ out() << highlightedCode(atom->string(), marker, relative);
+ }
+ out() << formattingRightMap()[ATOM_FORMATTING_TELETYPE];
+ break;
+ case Atom::Code:
+ out() << "<pre>" << trimmedTrailing(highlightedCode(indent(codeIndent, atom->string()),
+ marker, relative))
+ << "</pre>\n";
+ break;
+#ifdef QDOC_QML
+ case Atom::Qml:
+ out() << "<pre>" << trimmedTrailing(highlightedCode(indent(codeIndent, atom->string()),
+ marker, relative))
+ << "</pre>\n";
+ break;
+#endif
+ case Atom::CodeNew:
+ out() << "<p>you can rewrite it as</p>\n"
+ << "<pre>" << trimmedTrailing(highlightedCode(indent(codeIndent, atom->string()),
+ marker, relative))
+ << "</pre>\n";
+ break;
+ case Atom::CodeOld:
+ out() << "<p>For example, if you have code like</p>\n";
+ // fallthrough
+ case Atom::CodeBad:
+ out() << "<pre><font color=\"#404040\">"
+ << trimmedTrailing(protect(plainCode(indent(codeIndent, atom->string()))))
+ << "</font></pre>\n";
+ break;
+ case Atom::FootnoteLeft:
+ // ### For now
+ if (in_para) {
+ out() << "</p>\n";
+ in_para = false;
+ }
+ out() << "<!-- ";
+ break;
+ case Atom::FootnoteRight:
+ // ### For now
+ out() << "-->";
+ break;
+ case Atom::FormatElse:
+ case Atom::FormatEndif:
+ case Atom::FormatIf:
+ break;
+ case Atom::FormattingLeft:
+ out() << formattingLeftMap()[atom->string()];
+ if (atom->string() == ATOM_FORMATTING_PARAMETER) {
+ if (atom->next() != 0 && atom->next()->type() == Atom::String) {
+ QRegExp subscriptRegExp("([a-z]+)_([0-9n])");
+ if (subscriptRegExp.exactMatch(atom->next()->string())) {
+ out() << subscriptRegExp.cap(1) << "<sub>"
+ << subscriptRegExp.cap(2) << "</sub>";
+ skipAhead = 1;
+ }
+ }
+ }
+ break;
+ case Atom::FormattingRight:
+ if (atom->string() == ATOM_FORMATTING_LINK) {
+ endLink();
+ }
+ else {
+ out() << formattingRightMap()[atom->string()];
+ }
+ break;
+ case Atom::GeneratedList:
+ if (atom->string() == "annotatedclasses") {
+ generateAnnotatedList(relative, marker, nonCompatClasses);
+ }
+ else if (atom->string() == "classes") {
+ generateCompactList(relative, marker, nonCompatClasses);
+ }
+ else if (atom->string().contains("classesbymodule")) {
+ QString arg = atom->string().trimmed();
+ QString moduleName = atom->string().mid(atom->string().indexOf(
+ "classesbymodule") + 15).trimmed();
+ if (moduleClassMap.contains(moduleName))
+ generateAnnotatedList(relative, marker, moduleClassMap[moduleName]);
+ }
+ else if (atom->string().contains("classesbyedition")) {
+
+ QString arg = atom->string().trimmed();
+ QString editionName = atom->string().mid(atom->string().indexOf(
+ "classesbyedition") + 16).trimmed();
+
+ if (editionModuleMap.contains(editionName)) {
+
+ // Add all classes in the modules listed for that edition.
+ QMap<QString, const Node *> editionClasses;
+ foreach (const QString &moduleName, editionModuleMap[editionName]) {
+ if (moduleClassMap.contains(moduleName))
+ editionClasses.unite(moduleClassMap[moduleName]);
+ }
+
+ // Add additional groups and remove groups of classes that
+ // should be excluded from the edition.
+
+ QMultiMap <QString, Node *> groups = tre->groups();
+ foreach (const QString &groupName, editionGroupMap[editionName]) {
+ QList<Node *> groupClasses;
+ if (groupName.startsWith("-")) {
+ groupClasses = groups.values(groupName.mid(1));
+ foreach (const Node *node, groupClasses)
+ editionClasses.remove(node->name());
+ }
+ else {
+ groupClasses = groups.values(groupName);
+ foreach (const Node *node, groupClasses)
+ editionClasses.insert(node->name(), node);
+ }
+ }
+ generateAnnotatedList(relative, marker, editionClasses);
+ }
+ }
+ else if (atom->string() == "classhierarchy") {
+ generateClassHierarchy(relative, marker, nonCompatClasses);
+ }
+ else if (atom->string() == "compatclasses") {
+ generateCompactList(relative, marker, compatClasses);
+ }
+ else if (atom->string() == "functionindex") {
+ generateFunctionIndex(relative, marker);
+ }
+ else if (atom->string() == "legalese") {
+ generateLegaleseList(relative, marker);
+ }
+ else if (atom->string() == "mainclasses") {
+ generateCompactList(relative, marker, mainClasses);
+ }
+ else if (atom->string() == "services") {
+ generateCompactList(relative, marker, serviceClasses);
+ }
+ else if (atom->string() == "overviews") {
+ generateOverviewList(relative, marker);
+ }
+ else if (atom->string() == "namespaces") {
+ generateAnnotatedList(relative, marker, namespaceIndex);
+ }
+ else if (atom->string() == "related") {
+ const FakeNode *fake = static_cast<const FakeNode *>(relative);
+ if (fake && !fake->groupMembers().isEmpty()) {
+ QMap<QString, const Node *> groupMembersMap;
+ foreach (const Node *node, fake->groupMembers()) {
+ if (node->type() == Node::Fake)
+ groupMembersMap[fullName(node, relative, marker)] = node;
+ }
+ generateAnnotatedList(fake, marker, groupMembersMap);
+ }
+ }
+ else if (atom->string() == "relatedinline") {
+ const FakeNode *fake = static_cast<const FakeNode *>(relative);
+ if (fake && !fake->groupMembers().isEmpty()) {
+ // Reverse the list into the original scan order.
+ // Should be sorted. But on what? It may not be a
+ // regular class or page definition.
+ QList<const Node *> list;
+ foreach (const Node *node, fake->groupMembers())
+ list.prepend(node);
+ foreach (const Node *node, list)
+ generateBody(node, marker);
+ }
+ }
+ break;
+ case Atom::Image:
+ case Atom::InlineImage:
+ {
+ QString fileName = imageFileName(relative, atom->string());
+ QString text;
+ if (atom->next() != 0)
+ text = atom->next()->string();
+ if (atom->type() == Atom::Image)
+ out() << "<p align=\"center\">";
+ if (fileName.isEmpty()) {
+ out() << "<font color=\"red\">[Missing image "
+ << protect(atom->string()) << "]</font>";
+ }
+ else {
+ out() << "<img src=\"" << protect(fileName) << "\"";
+ if (!text.isEmpty())
+ out() << " alt=\"" << protect(text) << "\"";
+ out() << " />";
+ helpProjectWriter->addExtraFile(fileName);
+ }
+ if (atom->type() == Atom::Image)
+ out() << "</p>";
+ }
+ break;
+ case Atom::ImageText:
+ break;
+ case Atom::LegaleseLeft:
+ out() << "<div style=\"padding: 0.5em; background: #e0e0e0; color: black\">";
+ break;
+ case Atom::LegaleseRight:
+ out() << "</div>";
+ break;
+ case Atom::LineBreak:
+ out() << "<br />";
+ break;
+ case Atom::Link:
+ {
+ const Node *node = 0;
+ QString myLink = getLink(atom, relative, marker, node);
+ if (myLink.isEmpty())
+ relative->doc().location().warning(tr("Cannot link to '%1' in %2")
+ .arg(atom->string())
+ .arg(marker->plainFullName(relative)));
+ beginLink(myLink, node, relative, marker);
+ skipAhead = 1;
+ }
+ break;
+ case Atom::LinkNode:
+ {
+ const Node *node = CodeMarker::nodeForString(atom->string());
+ beginLink(linkForNode(node, relative), node, relative, marker);
+ skipAhead = 1;
+ }
+ break;
+ case Atom::ListLeft:
+ if (in_para) {
+ out() << "</p>\n";
+ in_para = false;
+ }
+ if (atom->string() == ATOM_LIST_BULLET) {
+ out() << "<ul>\n";
+ }
+ else if (atom->string() == ATOM_LIST_TAG) {
+ out() << "<dl>\n";
+ }
+ else if (atom->string() == ATOM_LIST_VALUE) {
+ threeColumnEnumValueTable = isThreeColumnEnumValueTable(atom);
+ if (threeColumnEnumValueTable) {
+ out() << "<p><table border=\"1\" cellpadding=\"2\" cellspacing=\"1\" width=\"100%\">\n"
+ "<tr><th width=\"25%\">Constant</th><th width=\"15%\">Value</th>"
+ "<th width=\"60%\">Description</th></tr>\n";
+ }
+ else {
+ out() << "<p><table border=\"1\" cellpadding=\"2\" cellspacing=\"1\" width=\"40%\">\n"
+ << "<tr><th width=\"60%\">Constant</th><th width=\"40%\">Value</th></tr>\n";
+ }
+ }
+ else {
+ out() << "<ol type=";
+ if (atom->string() == ATOM_LIST_UPPERALPHA) {
+ out() << "\"A\"";
+ }
+ else if (atom->string() == ATOM_LIST_LOWERALPHA) {
+ out() << "\"a\"";
+ }
+ else if (atom->string() == ATOM_LIST_UPPERROMAN) {
+ out() << "\"I\"";
+ }
+ else if (atom->string() == ATOM_LIST_LOWERROMAN) {
+ out() << "\"i\"";
+ }
+ else { // (atom->string() == ATOM_LIST_NUMERIC)
+ out() << "\"1\"";
+ }
+ if (atom->next() != 0 && atom->next()->string().toInt() != 1)
+ out() << " start=\"" << atom->next()->string() << "\"";
+ out() << ">\n";
+ }
+ break;
+ case Atom::ListItemNumber:
+ break;
+ case Atom::ListTagLeft:
+ if (atom->string() == ATOM_LIST_TAG) {
+ out() << "<dt>";
+ }
+ else { // (atom->string() == ATOM_LIST_VALUE)
+ // ### Trenton
+
+ out() << "<tr><td valign=\"top\"><tt>"
+ << protect(plainCode(marker->markedUpEnumValue(atom->next()->string(),
+ relative)))
+ << "</tt></td><td align=\"center\" valign=\"top\">";
+
+ QString itemValue;
+ if (relative->type() == Node::Enum) {
+ const EnumNode *enume = static_cast<const EnumNode *>(relative);
+ itemValue = enume->itemValue(atom->next()->string());
+ }
+
+ if (itemValue.isEmpty())
+ out() << "?";
+ else
+ out() << "<tt>" << protect(itemValue) << "</tt>";
+
+ skipAhead = 1;
+ }
+ break;
+ case Atom::ListTagRight:
+ if (atom->string() == ATOM_LIST_TAG)
+ out() << "</dt>\n";
+ break;
+ case Atom::ListItemLeft:
+ if (atom->string() == ATOM_LIST_TAG) {
+ out() << "<dd>";
+ }
+ else if (atom->string() == ATOM_LIST_VALUE) {
+ if (threeColumnEnumValueTable) {
+ out() << "</td><td valign=\"top\">";
+ if (matchAhead(atom, Atom::ListItemRight))
+ out() << "&nbsp;";
+ }
+ }
+ else {
+ out() << "<li>";
+ }
+ if (matchAhead(atom, Atom::ParaLeft))
+ skipAhead = 1;
+ break;
+ case Atom::ListItemRight:
+ if (atom->string() == ATOM_LIST_TAG) {
+ out() << "</dd>\n";
+ }
+ else if (atom->string() == ATOM_LIST_VALUE) {
+ out() << "</td></tr>\n";
+ }
+ else {
+ out() << "</li>\n";
+ }
+ break;
+ case Atom::ListRight:
+ if (atom->string() == ATOM_LIST_BULLET) {
+ out() << "</ul>\n";
+ }
+ else if (atom->string() == ATOM_LIST_TAG) {
+ out() << "</dl>\n";
+ }
+ else if (atom->string() == ATOM_LIST_VALUE) {
+ out() << "</table></p>\n";
+ }
+ else {
+ out() << "</ol>\n";
+ }
+ break;
+ case Atom::Nop:
+ break;
+ case Atom::ParaLeft:
+ out() << "<p>";
+ in_para = true;
+ break;
+ case Atom::ParaRight:
+ endLink();
+ if (in_para) {
+ out() << "</p>\n";
+ in_para = false;
+ }
+ //if (!matchAhead(atom, Atom::ListItemRight) && !matchAhead(atom, Atom::TableItemRight))
+ // out() << "</p>\n";
+ break;
+ case Atom::QuotationLeft:
+ out() << "<blockquote>";
+ break;
+ case Atom::QuotationRight:
+ out() << "</blockquote>\n";
+ break;
+ case Atom::RawString:
+ out() << atom->string();
+ break;
+ case Atom::SectionLeft:
+#if 0
+ {
+ int nextLevel = atom->string().toInt();
+ if (sectionNumber.size() < nextLevel) {
+ do {
+ sectionNumber.append("1");
+ } while (sectionNumber.size() < nextLevel);
+ }
+ else {
+ while (sectionNumber.size() > nextLevel) {
+ sectionNumber.removeLast();
+ }
+ sectionNumber.last() = QString::number(sectionNumber.last().toInt() + 1);
+ }
+ out() << "<a name=\"sec-" << sectionNumber.join("-") << "\"></a>\n";
+ }
+#else
+ out() << "<a name=\"" << Doc::canonicalTitle(Text::sectionHeading(atom).toString())
+ << "\"></a>\n";
+#endif
+ break;
+ case Atom::SectionRight:
+ break;
+ case Atom::SectionHeadingLeft:
+ out() << "<h" + QString::number(atom->string().toInt() + hOffset(relative)) + ">";
+ inSectionHeading = true;
+ break;
+ case Atom::SectionHeadingRight:
+ out() << "</h" + QString::number(atom->string().toInt() + hOffset(relative)) + ">\n";
+ inSectionHeading = false;
+ break;
+ case Atom::SidebarLeft:
+ break;
+ case Atom::SidebarRight:
+ break;
+ case Atom::String:
+ if (inLink && !inContents && !inSectionHeading) {
+ generateLink(atom, relative, marker);
+ }
+ else {
+ out() << protect(atom->string());
+ }
+ break;
+ case Atom::TableLeft:
+ if (in_para) {
+ out() << "</p>\n";
+ in_para = false;
+ }
+ if (!atom->string().isEmpty()) {
+ if (atom->string().contains("%"))
+ out() << "<p><table width=\"" << atom->string() << "\" "
+ << "align=\"center\" cellpadding=\"2\" "
+ << "cellspacing=\"1\" border=\"0\">\n";
+ else
+ out() << "<p><table align=\"center\" cellpadding=\"2\" cellspacing=\"1\" border=\"0\">\n";
+ }
+ else {
+ out() << "<p><table align=\"center\" cellpadding=\"2\" cellspacing=\"1\" border=\"0\">\n";
+ }
+ numTableRows = 0;
+ break;
+ case Atom::TableRight:
+ out() << "</table></p>\n";
+ break;
+ case Atom::TableHeaderLeft:
+ out() << "<thead><tr valign=\"top\" class=\"qt-style\">";
+ inTableHeader = true;
+ break;
+ case Atom::TableHeaderRight:
+ out() << "</tr>";
+ if (matchAhead(atom, Atom::TableHeaderLeft)) {
+ skipAhead = 1;
+ out() << "\n<tr valign=\"top\" class=\"qt-style\">";
+ }
+ else {
+ out() << "</thead>\n";
+ inTableHeader = false;
+ }
+ break;
+ case Atom::TableRowLeft:
+ if (++numTableRows % 2 == 1)
+ out() << "<tr valign=\"top\" class=\"odd\">";
+ else
+ out() << "<tr valign=\"top\" class=\"even\">";
+ break;
+ case Atom::TableRowRight:
+ out() << "</tr>\n";
+ break;
+ case Atom::TableItemLeft:
+ {
+ if (inTableHeader)
+ out() << "<th";
+ else
+ out() << "<td";
+
+ QStringList spans = atom->string().split(",");
+ if (spans.size() == 2) {
+ if (spans.at(0) != "1")
+ out() << " colspan=\"" << spans.at(0) << "\"";
+ if (spans.at(1) != "1")
+ out() << " rowspan=\"" << spans.at(1) << "\"";
+ out() << ">";
+ }
+ if (matchAhead(atom, Atom::ParaLeft))
+ skipAhead = 1;
+ }
+ break;
+ case Atom::TableItemRight:
+ if (inTableHeader)
+ out() << "</th>";
+ else
+ out() << "</td>";
+ if (matchAhead(atom, Atom::ParaLeft))
+ skipAhead = 1;
+ break;
+ case Atom::TableOfContents:
+ {
+ int numColumns = 1;
+ const Node *node = relative;
+
+ Doc::SectioningUnit sectioningUnit = Doc::Section4;
+ QStringList params = atom->string().split(",");
+ QString columnText = params.at(0);
+ QStringList pieces = columnText.split(" ", QString::SkipEmptyParts);
+ if (pieces.size() >= 2) {
+ columnText = pieces.at(0);
+ pieces.pop_front();
+ QString path = pieces.join(" ").trimmed();
+ node = findNodeForTarget(path, relative, marker, atom);
+ }
+
+ if (params.size() == 2) {
+ numColumns = qMax(columnText.toInt(), numColumns);
+ sectioningUnit = (Doc::SectioningUnit)params.at(1).toInt();
+ }
+
+ if (node)
+ generateTableOfContents(node, marker, sectioningUnit, numColumns,
+ relative);
+ }
+ break;
+ case Atom::Target:
+ out() << "<a name=\"" << Doc::canonicalTitle(atom->string()) << "\"></a>";
+ break;
+ case Atom::UnhandledFormat:
+ out() << "<font color=\"red\"><b>&lt;Missing HTML&gt;</b></font>";
+ break;
+ case Atom::UnknownCommand:
+ out() << "<font color=\"red\"><b><code>\\" << protect(atom->string())
+ << "</code></b></font>";
+ break;
+#ifdef QDOC_QML
+ case Atom::QmlText:
+ case Atom::EndQmlText:
+ // don't do anything with these. They are just tags.
+ break;
+#endif
+ default:
+ unknownAtom(atom);
+ }
+ return skipAhead;
+}
+
+void HtmlGenerator::generateClassLikeNode(const InnerNode *inner,
+ CodeMarker *marker)
+{
+ QList<Section> sections;
+ QList<Section>::ConstIterator s;
+
+ const ClassNode *classe = 0;
+ const NamespaceNode *namespasse = 0;
+
+ QString title;
+ QString rawTitle;
+ QString fullTitle;
+ if (inner->type() == Node::Namespace) {
+ namespasse = static_cast<const NamespaceNode *>(inner);
+ rawTitle = marker->plainName(inner);
+ fullTitle = marker->plainFullName(inner);
+ title = rawTitle + " Namespace Reference";
+ }
+ else if (inner->type() == Node::Class) {
+ classe = static_cast<const ClassNode *>(inner);
+ rawTitle = marker->plainName(inner);
+ fullTitle = marker->plainFullName(inner);
+ title = rawTitle + " Class Reference";
+ }
+
+ DcfSection classSection;
+ classSection.title = title;
+ classSection.ref = linkForNode(inner, 0);
+ classSection.keywords += qMakePair(inner->name(), classSection.ref);
+
+ Text subtitleText;
+ if (rawTitle != fullTitle)
+ subtitleText << "(" << Atom(Atom::AutoLink, fullTitle) << ")"
+ << Atom(Atom::LineBreak);
+
+ QString fixedModule = inner->moduleName();
+ if (fixedModule == "Qt3SupportLight")
+ fixedModule = "Qt3Support";
+ if (!fixedModule.isEmpty())
+ subtitleText << "[" << Atom(Atom::AutoLink, fixedModule) << " module]";
+
+ if (fixedModule.isEmpty()) {
+ QMultiMap<QString, QString> publicGroups = tre->publicGroups();
+ QList<QString> groupNames = publicGroups.values(inner->name());
+ if (!groupNames.isEmpty()) {
+ qSort(groupNames.begin(), groupNames.end());
+ subtitleText << "[";
+ for (int j=0; j<groupNames.count(); j++) {
+ subtitleText << Atom(Atom::AutoLink, groupNames[j]);
+ if (j<groupNames.count()-1)
+ subtitleText <<", ";
+ }
+ subtitleText << "]";
+ }
+ }
+
+ generateHeader(title, inner, marker, true);
+ generateTitle(title, subtitleText, SmallSubTitle, inner, marker);
+
+ generateBrief(inner, marker);
+ generateIncludes(inner, marker);
+ generateStatus(inner, marker);
+ if (classe) {
+ generateModuleWarning(classe, marker);
+ generateInherits(classe, marker);
+ generateInheritedBy(classe, marker);
+ }
+ generateThreadSafeness(inner, marker);
+ generateSince(inner, marker);
+
+ out() << "<ul>\n";
+
+ QString membersLink = generateListOfAllMemberFile(inner, marker);
+ if (!membersLink.isEmpty())
+ out() << "<li><a href=\"" << membersLink << "\">"
+ << "List of all members, including inherited members</a></li>\n";
+
+ QString obsoleteLink = generateLowStatusMemberFile(inner, marker, CodeMarker::Obsolete);
+ if (!obsoleteLink.isEmpty())
+ out() << "<li><a href=\"" << obsoleteLink << "\">"
+ << "Obsolete members</a></li>\n";
+
+ QString compatLink = generateLowStatusMemberFile(inner, marker, CodeMarker::Compat);
+ if (!compatLink.isEmpty())
+ out() << "<li><a href=\"" << compatLink << "\">"
+ << "Qt 3 support members</a></li>\n";
+
+ out() << "</ul>\n";
+
+ bool needOtherSection = false;
+
+ sections = marker->sections(inner, CodeMarker::Summary, CodeMarker::Okay);
+ s = sections.begin();
+ while (s != sections.end()) {
+ if (s->members.isEmpty()) {
+ if (!s->inherited.isEmpty())
+ needOtherSection = true;
+ } else {
+ out() << "<a name=\"" << registerRef((*s).name.toLower()) << "\"></a>\n";
+ out() << "<h3>" << protect((*s).name) << "</h3>\n";
+
+ generateSectionList(*s, inner, marker, CodeMarker::Summary);
+ }
+ ++s;
+ }
+
+ if (needOtherSection) {
+ out() << "<h3>Additional Inherited Members</h3>\n"
+ "<ul>\n";
+
+ s = sections.begin();
+ while (s != sections.end()) {
+ if (s->members.isEmpty() && !s->inherited.isEmpty())
+ generateSectionInheritedList(*s, inner, marker);
+ ++s;
+ }
+ out() << "</ul>\n";
+ }
+
+ out() << "<a name=\"" << registerRef("details") << "\"></a>\n";
+
+ if (!inner->doc().isEmpty()) {
+ out() << "<hr />\n"
+ << "<h2>" << "Detailed Description" << "</h2>\n";
+ generateBody(inner, marker);
+ generateAlsoList(inner, marker);
+ }
+
+ sections = marker->sections(inner, CodeMarker::Detailed, CodeMarker::Okay);
+ s = sections.begin();
+ while (s != sections.end()) {
+ out() << "<hr />\n";
+ out() << "<h2>" << protect((*s).name) << "</h2>\n";
+
+ NodeList::ConstIterator m = (*s).members.begin();
+ while (m != (*s).members.end()) {
+ if ((*m)->access() != Node::Private) { // ### check necessary?
+ if ((*m)->type() != Node::Class)
+ generateDetailedMember(*m, inner, marker);
+ else {
+ out() << "<h3> class ";
+ generateFullName(*m, inner, marker);
+ out() << "</h3>";
+ generateBrief(*m, marker, inner);
+ }
+
+ QStringList names;
+ names << (*m)->name();
+ if ((*m)->type() == Node::Function) {
+ const FunctionNode *func = reinterpret_cast<const FunctionNode *>(*m);
+ if (func->metaness() == FunctionNode::Ctor || func->metaness() == FunctionNode::Dtor
+ || func->overloadNumber() != 1)
+ names.clear();
+ } else if ((*m)->type() == Node::Property) {
+ const PropertyNode *prop = reinterpret_cast<const PropertyNode *>(*m);
+ if (!prop->getters().isEmpty() && !names.contains(prop->getters().first()->name()))
+ names << prop->getters().first()->name();
+ if (!prop->setters().isEmpty())
+ names << prop->setters().first()->name();
+ if (!prop->resetters().isEmpty())
+ names << prop->resetters().first()->name();
+ } else if ((*m)->type() == Node::Enum) {
+ const EnumNode *enume = reinterpret_cast<const EnumNode *>(*m);
+ if (enume->flagsType())
+ names << enume->flagsType()->name();
+
+ foreach (const QString &enumName,
+ enume->doc().enumItemNames().toSet()
+ - enume->doc().omitEnumItemNames().toSet())
+ names << plainCode(marker->markedUpEnumValue(enumName, enume));
+ }
+ foreach (const QString &name, names)
+ classSection.keywords += qMakePair(name, linkForNode(*m, 0));
+ }
+ ++m;
+ }
+ ++s;
+ }
+ generateFooter(inner);
+
+ if (!membersLink.isEmpty()) {
+ DcfSection membersSection;
+ membersSection.title = "List of all members";
+ membersSection.ref = membersLink;
+ appendDcfSubSection(&classSection, membersSection);
+ }
+ if (!obsoleteLink.isEmpty()) {
+ DcfSection obsoleteSection;
+ obsoleteSection.title = "Obsolete members";
+ obsoleteSection.ref = obsoleteLink;
+ appendDcfSubSection(&classSection, obsoleteSection);
+ }
+ if (!compatLink.isEmpty()) {
+ DcfSection compatSection;
+ compatSection.title = "Qt 3 support members";
+ compatSection.ref = compatLink;
+ appendDcfSubSection(&classSection, compatSection);
+ }
+
+ appendDcfSubSection(&dcfClassesRoot, classSection);
+}
+
+void HtmlGenerator::generateFakeNode(const FakeNode *fake, CodeMarker *marker)
+{
+ SubTitleSize subTitleSize = LargeSubTitle;
+ DcfSection fakeSection;
+ fakeSection.title = fake->fullTitle();
+ fakeSection.ref = linkForNode(fake, 0);
+
+ QList<Section> sections;
+ QList<Section>::const_iterator s;
+
+ QString htmlTitle = fake->fullTitle();
+ if (fake->subType() == FakeNode::File && !fake->subTitle().isEmpty()) {
+ subTitleSize = SmallSubTitle;
+ htmlTitle += " (" + fake->subTitle() + ")";
+ }
+
+ generateHeader(htmlTitle, fake, marker, true);
+ generateTitle(fake->fullTitle(), Text() << fake->subTitle(), subTitleSize,
+ fake, marker);
+
+ if (fake->subType() == FakeNode::Module) {
+ // Generate brief text and status for modules.
+ generateBrief(fake, marker);
+ generateStatus(fake, marker);
+
+ if (moduleNamespaceMap.contains(fake->name())) {
+ out() << "<h2>Namespaces</h2>\n";
+ generateAnnotatedList(fake, marker, moduleNamespaceMap[fake->name()]);
+ }
+ if (moduleClassMap.contains(fake->name())) {
+ out() << "<h2>Classes</h2>\n";
+ generateAnnotatedList(fake, marker, moduleClassMap[fake->name()]);
+ }
+ }
+ else if (fake->subType() == FakeNode::HeaderFile) {
+ // Generate brief text and status for modules.
+ generateBrief(fake, marker);
+ generateStatus(fake, marker);
+
+ out() << "<ul>\n";
+
+ QString membersLink = generateListOfAllMemberFile(fake, marker);
+ if (!membersLink.isEmpty())
+ out() << "<li><a href=\"" << membersLink << "\">"
+ << "List of all members, including inherited members</a></li>\n";
+
+ QString obsoleteLink = generateLowStatusMemberFile(fake, marker, CodeMarker::Obsolete);
+ if (!obsoleteLink.isEmpty())
+ out() << "<li><a href=\"" << obsoleteLink << "\">"
+ << "Obsolete members</a></li>\n";
+
+ QString compatLink = generateLowStatusMemberFile(fake, marker, CodeMarker::Compat);
+ if (!compatLink.isEmpty())
+ out() << "<li><a href=\"" << compatLink << "\">"
+ << "Qt 3 support members</a></li>\n";
+
+ out() << "</ul>\n";
+
+ if (!membersLink.isEmpty()) {
+ DcfSection membersSection;
+ membersSection.title = "List of all members";
+ membersSection.ref = membersLink;
+ appendDcfSubSection(&fakeSection, membersSection);
+ }
+ if (!obsoleteLink.isEmpty()) {
+ DcfSection obsoleteSection;
+ obsoleteSection.title = "Obsolete members";
+ obsoleteSection.ref = obsoleteLink;
+ appendDcfSubSection(&fakeSection, obsoleteSection);
+ }
+ if (!compatLink.isEmpty()) {
+ DcfSection compatSection;
+ compatSection.title = "Qt 3 support members";
+ compatSection.ref = compatLink;
+ appendDcfSubSection(&fakeSection, compatSection);
+ }
+ }
+
+ sections = marker->sections(fake, CodeMarker::Summary, CodeMarker::Okay);
+ s = sections.begin();
+ while (s != sections.end()) {
+ out() << "<a name=\"" << registerRef((*s).name) << "\"></a>\n";
+ out() << "<h3>" << protect((*s).name) << "</h3>\n";
+ generateSectionList(*s, fake, marker, CodeMarker::Summary);
+ ++s;
+ }
+
+ Text brief = fake->doc().briefText();
+ if (fake->subType() == FakeNode::Module && !brief.isEmpty()) {
+ out() << "<a name=\"" << registerRef("details") << "\"></a>\n";
+ out() << "<h2>" << "Detailed Description" << "</h2>\n";
+ }
+
+ generateBody(fake, marker);
+#ifdef QDOC_QML
+ if (fake->subType() == FakeNode::QmlClass) {
+ //qDebug() << "generateFakeNode(): QML CLASS" << fake->name();
+ const QmlNode* qmlNode = static_cast<const QmlNode*>(fake);
+ const ClassNode* cn = qmlNode->classNode();
+ if (cn) {
+ //qDebug() << " CPP CLASS" << cn->name();
+ generateQmlText(cn->doc().body(), cn, marker);
+ }
+ }
+#endif
+
+ generateAlsoList(fake, marker);
+
+ if (!fake->groupMembers().isEmpty()) {
+ QMap<QString, const Node *> groupMembersMap;
+ foreach (const Node *node, fake->groupMembers()) {
+ if (node->type() == Node::Class || node->type() == Node::Namespace)
+ groupMembersMap[node->name()] = node;
+ }
+ generateAnnotatedList(fake, marker, groupMembersMap);
+ }
+
+ fakeSection.keywords += qMakePair(fakeSection.title, fakeSection.ref);
+
+ sections = marker->sections(fake, CodeMarker::Detailed, CodeMarker::Okay);
+ s = sections.begin();
+ while (s != sections.end()) {
+ out() << "<hr />\n";
+ out() << "<h2>" << protect((*s).name) << "</h2>\n";
+
+ NodeList::ConstIterator m = (*s).members.begin();
+ while (m != (*s).members.end()) {
+ generateDetailedMember(*m, fake, marker);
+ fakeSection.keywords += qMakePair((*m)->name(), linkForNode(*m, 0));
+ ++m;
+ }
+ ++s;
+ }
+ generateFooter(fake);
+
+ if (fake->subType() == FakeNode::Example) {
+ appendDcfSubSection(&dcfExamplesRoot, fakeSection);
+ }
+ else if (fake->subType() != FakeNode::File) {
+ QString contentsPage = fake->links().value(Node::ContentsLink).first;
+
+ if (contentsPage == "Qt Designer Manual") {
+ appendDcfSubSection(&dcfDesignerRoot, fakeSection);
+ }
+ else if (contentsPage == "Qt Linguist Manual") {
+ appendDcfSubSection(&dcfLinguistRoot, fakeSection);
+ }
+ else if (contentsPage == "Qt Assistant Manual") {
+ appendDcfSubSection(&dcfAssistantRoot, fakeSection);
+ }
+ else if (contentsPage == "qmake Manual") {
+ appendDcfSubSection(&dcfQmakeRoot, fakeSection);
+ }
+ else {
+ appendDcfSubSection(&dcfOverviewsRoot, fakeSection);
+ }
+ }
+}
+
+QString HtmlGenerator::fileExtension(const Node * /* node */)
+{
+ return "html";
+}
+
+void HtmlGenerator::generateHeader(const QString& title,
+ const Node *node,
+ CodeMarker *marker,
+ bool mainPage)
+{
+ out() << "<?xml version=\"1.0\" encoding=\"iso-8859-1\"?>\n";
+
+ out() << "<!DOCTYPE html\n"
+ " PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"DTD/xhtml1-strict.dtd\">\n"
+ "<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\" lang=\"en\">\n";
+
+ QString shortVersion;
+ if ((project != "Qtopia") && (project != "Qt Extended")) {
+ shortVersion = project + " " + shortVersion + ": ";
+ if (node && !node->doc().location().isEmpty())
+ out() << "<!-- " << node->doc().location().filePath() << " -->\n";
+
+ shortVersion = tre->version();
+ if (shortVersion.count(QChar('.')) == 2)
+ shortVersion.truncate(shortVersion.lastIndexOf(QChar('.')));
+ if (!shortVersion.isEmpty()) {
+ if (project == "QSA")
+ shortVersion = "QSA " + shortVersion + ": ";
+ else
+ shortVersion = "Qt " + shortVersion + ": ";
+ }
+ }
+
+ out() << "<head>\n"
+ " <title>" << shortVersion << protect(title) << "</title>\n";
+ if (!style.isEmpty())
+ out() << " <style type=\"text/css\">" << style << "</style>\n";
+
+ const QMap<QString, QString> &metaMap = node->doc().metaTagMap();
+ if (!metaMap.isEmpty()) {
+ QMapIterator<QString, QString> i(metaMap);
+ while (i.hasNext()) {
+ i.next();
+ out() << " <meta name=\"" << protect(i.key()) << "\" contents=\""
+ << protect(i.value()) << "\" />\n";
+ }
+ }
+
+ navigationLinks.clear();
+
+ if (node && !node->links().empty()) {
+ QPair<QString,QString> linkPair;
+ QPair<QString,QString> anchorPair;
+ const Node *linkNode;
+
+ if (node->links().contains(Node::PreviousLink)) {
+ linkPair = node->links()[Node::PreviousLink];
+ linkNode = findNodeForTarget(linkPair.first, node, marker);
+ if (!linkNode || linkNode == node)
+ anchorPair = linkPair;
+ else
+ anchorPair = anchorForNode(linkNode);
+
+ out() << " <link rel=\"prev\" href=\""
+ << anchorPair.first << "\" />\n";
+
+ navigationLinks += "[Previous: <a href=\"" + anchorPair.first + "\">";
+ if (linkPair.first == linkPair.second && !anchorPair.second.isEmpty())
+ navigationLinks += protect(anchorPair.second);
+ else
+ navigationLinks += protect(linkPair.second);
+ navigationLinks += "</a>]\n";
+ }
+ if (node->links().contains(Node::ContentsLink)) {
+ linkPair = node->links()[Node::ContentsLink];
+ linkNode = findNodeForTarget(linkPair.first, node, marker);
+ if (!linkNode || linkNode == node)
+ anchorPair = linkPair;
+ else
+ anchorPair = anchorForNode(linkNode);
+
+ out() << " <link rel=\"contents\" href=\""
+ << anchorPair.first << "\" />\n";
+
+ navigationLinks += "[<a href=\"" + anchorPair.first + "\">";
+ if (linkPair.first == linkPair.second && !anchorPair.second.isEmpty())
+ navigationLinks += protect(anchorPair.second);
+ else
+ navigationLinks += protect(linkPair.second);
+ navigationLinks += "</a>]\n";
+ }
+ if (node->links().contains(Node::NextLink)) {
+ linkPair = node->links()[Node::NextLink];
+ linkNode = findNodeForTarget(linkPair.first, node, marker);
+ if (!linkNode || linkNode == node)
+ anchorPair = linkPair;
+ else
+ anchorPair = anchorForNode(linkNode);
+
+ out() << " <link rel=\"next\" href=\""
+ << anchorPair.first << "\" />\n";
+
+ navigationLinks += "[Next: <a href=\"" + anchorPair.first + "\">";
+ if (linkPair.first == linkPair.second && !anchorPair.second.isEmpty())
+ navigationLinks += protect(anchorPair.second);
+ else
+ navigationLinks += protect(linkPair.second);
+ navigationLinks += "</a>]\n";
+ }
+ if (node->links().contains(Node::IndexLink)) {
+ linkPair = node->links()[Node::IndexLink];
+ linkNode = findNodeForTarget(linkPair.first, node, marker);
+ if (!linkNode || linkNode == node)
+ anchorPair = linkPair;
+ else
+ anchorPair = anchorForNode(linkNode);
+ out() << " <link rel=\"index\" href=\""
+ << anchorPair.first << "\" />\n";
+ }
+ if (node->links().contains(Node::StartLink)) {
+ linkPair = node->links()[Node::StartLink];
+ linkNode = findNodeForTarget(linkPair.first, node, marker);
+ if (!linkNode || linkNode == node)
+ anchorPair = linkPair;
+ else
+ anchorPair = anchorForNode(linkNode);
+ out() << " <link rel=\"start\" href=\""
+ << anchorPair.first << "\" />\n";
+ }
+ }
+
+ foreach (const QString &stylesheet, stylesheets) {
+ out() << " <link href=\"" << stylesheet << "\" rel=\"stylesheet\" "
+ << "type=\"text/css\" />\n";
+ }
+
+ foreach (const QString &customHeadElement, customHeadElements) {
+ out() << " " << customHeadElement << "\n";
+ }
+
+ out() << "</head>\n"
+ "<body>\n";
+ if (mainPage)
+ generateMacRef(node, marker);
+ out() << QString(postHeader).replace("\\" + COMMAND_VERSION, tre->version());
+
+
+ if (node && !node->links().empty())
+ out() << "<p>\n" << navigationLinks << "</p>\n";
+}
+
+void HtmlGenerator::generateTitle(const QString& title,
+ const Text &subTitle,
+ SubTitleSize subTitleSize,
+ const Node *relative,
+ CodeMarker *marker)
+{
+ out() << "<h1 class=\"title\">" << protect(title);
+ if (!subTitle.isEmpty()) {
+ out() << "<br />";
+ if (subTitleSize == SmallSubTitle)
+ out() << "<span class=\"small-subtitle\">";
+ else
+ out() << "<span class=\"subtitle\">";
+ generateText(subTitle, relative, marker);
+ out() << "</span>\n";
+ }
+ out() << "</h1>\n";
+}
+
+void HtmlGenerator::generateFooter(const Node *node)
+{
+ if (node && !node->links().empty())
+ out() << "<p>\n" << navigationLinks << "</p>\n";
+
+ out() << QString(footer).replace("\\" + COMMAND_VERSION, tre->version())
+ << QString(address).replace("\\" + COMMAND_VERSION, tre->version())
+ << "</body>\n"
+ "</html>\n";
+}
+
+void HtmlGenerator::generateBrief(const Node *node, CodeMarker *marker,
+ const Node *relative)
+{
+ Text brief = node->doc().briefText();
+ if (!brief.isEmpty()) {
+ out() << "<p>";
+ generateText(brief, node, marker);
+ if (!relative || node == relative)
+ out() << " <a href=\"#";
+ else
+ out() << " <a href=\"" << linkForNode(node, relative) << "#";
+ out() << registerRef("details") << "\">More...</a></p>\n";
+ }
+}
+
+void HtmlGenerator::generateIncludes(const InnerNode *inner, CodeMarker *marker)
+{
+ if (!inner->includes().isEmpty()) {
+ out() << "<pre>" << trimmedTrailing(highlightedCode(indent(codeIndent,
+ marker->markedUpIncludes(
+ inner->includes())),
+ marker, inner))
+ << "</pre>";
+ }
+}
+
+void HtmlGenerator::generateTableOfContents(const Node *node, CodeMarker *marker,
+ Doc::SectioningUnit sectioningUnit,
+ int numColumns, const Node *relative)
+
+{
+ if (!node->doc().hasTableOfContents())
+ return;
+ QList<Atom *> toc = node->doc().tableOfContents();
+ if (toc.isEmpty())
+ return;
+
+ QString nodeName = "";
+ if (node != relative)
+ nodeName = node->name();
+
+ QStringList sectionNumber;
+ int columnSize = 0;
+
+ QString tdTag;
+ if (numColumns > 1) {
+ tdTag = "<td width=\"" + QString::number((100 + numColumns - 1) / numColumns) + "%\">";
+ out() << "<p><table width=\"100%\">\n<tr valign=\"top\">" << tdTag << "\n";
+ }
+
+ // disable nested links in table of contents
+ inContents = true;
+ inLink = true;
+
+ for (int i = 0; i < toc.size(); ++i) {
+ Atom *atom = toc.at(i);
+
+ int nextLevel = atom->string().toInt();
+ if (nextLevel > (int)sectioningUnit)
+ continue;
+
+ if (sectionNumber.size() < nextLevel) {
+ do {
+ out() << "<ul>";
+ sectionNumber.append("1");
+ } while (sectionNumber.size() < nextLevel);
+ } else {
+ while (sectionNumber.size() > nextLevel) {
+ out() << "</ul>\n";
+ sectionNumber.removeLast();
+ }
+ sectionNumber.last() = QString::number(sectionNumber.last().toInt() + 1);
+ }
+ int numAtoms;
+ Text headingText = Text::sectionHeading(atom);
+
+ if (sectionNumber.size() == 1 && columnSize > toc.size() / numColumns) {
+ out() << "</ul></td>" << tdTag << "<ul>\n";
+ columnSize = 0;
+ }
+ out() << "<li>";
+ out() << "<a href=\"" << nodeName << "#" << Doc::canonicalTitle(headingText.toString())
+ << "\">";
+ generateAtomList(headingText.firstAtom(), node, marker, true, numAtoms);
+ out() << "</a></li>\n";
+
+ ++columnSize;
+ }
+ while (!sectionNumber.isEmpty()) {
+ out() << "</ul>\n";
+ sectionNumber.removeLast();
+ }
+
+ if (numColumns > 1)
+ out() << "</td></tr></table></p>\n";
+
+ inContents = false;
+ inLink = false;
+}
+
+#if 0
+void HtmlGenerator::generateNavigationBar(const NavigationBar& bar,
+ const Node *node,
+ CodeMarker *marker)
+{
+ if (bar.prev.begin() != 0 || bar.current.begin() != 0 ||
+ bar.next.begin() != 0) {
+ out() << "<p align=\"right\">";
+ if (bar.prev.begin() != 0) {
+#if 0
+ out() << "[<a href=\"" << section.previousBaseName()
+ << ".html\">Prev: ";
+ generateText(section.previousHeading(), node, marker);
+ out() << "</a>]\n";
+#endif
+ }
+ if (bar.current.begin() != 0) {
+ out() << "[<a href=\"" << "home"
+ << ".html\">Home</a>]\n";
+ }
+ if (bar.next.begin() != 0) {
+ out() << "[<a href=\"" << fileBase(node, bar.next)
+ << ".html\">Next: ";
+ generateText(Text::sectionHeading(bar.next.begin()), node, marker);
+ out() << "</a>]\n";
+ }
+ out() << "</p>\n";
+ }
+}
+#endif
+
+QString HtmlGenerator::generateListOfAllMemberFile(const InnerNode *inner, CodeMarker *marker)
+{
+ QList<Section> sections;
+ QList<Section>::ConstIterator s;
+
+ sections = marker->sections(inner, CodeMarker::SeparateList, CodeMarker::Okay);
+ if (sections.isEmpty())
+ return QString();
+
+ QString fileName = fileBase(inner) + "-members." + fileExtension(inner);
+ beginSubPage(inner->location(), fileName);
+ QString title = "List of All Members for " + inner->name();
+ generateHeader(title, inner, marker, false);
+ generateTitle(title, Text(), SmallSubTitle, inner, marker);
+ out() << "<p>This is the complete list of members for ";
+ generateFullName(inner, 0, marker);
+ out() << ", including inherited members.</p>\n";
+
+ Section section = sections.first();
+ generateSectionList(section, 0, marker, CodeMarker::SeparateList);
+
+ generateFooter();
+ endSubPage();
+ return fileName;
+}
+
+QString HtmlGenerator::generateLowStatusMemberFile(const InnerNode *inner, CodeMarker *marker,
+ CodeMarker::Status status)
+{
+ QList<Section> sections = marker->sections(inner, CodeMarker::Summary, status);
+ QMutableListIterator<Section> j(sections);
+ while (j.hasNext()) {
+ if (j.next().members.size() == 0)
+ j.remove();
+ }
+ if (sections.isEmpty())
+ return QString();
+
+ int i;
+
+ QString title;
+ QString fileName;
+
+ if (status == CodeMarker::Compat) {
+ title = "Qt 3 Support Members for " + inner->name();
+ fileName = fileBase(inner) + "-qt3." + fileExtension(inner);
+ } else {
+ title = "Obsolete Members for " + inner->name();
+ fileName = fileBase(inner) + "-obsolete." + fileExtension(inner);
+ }
+
+ beginSubPage(inner->location(), fileName);
+ generateHeader(title, inner, marker, false);
+ generateTitle(title, Text(), SmallSubTitle, inner, marker);
+
+ if (status == CodeMarker::Compat) {
+ out() << "<p><b>The following class members are part of the "
+ "<a href=\"qt3support.html\">Qt 3 support layer</a>.</b> "
+ "They are provided to help you port old code to Qt 4. We advise against "
+ "using them in new code.</p>\n";
+ } else {
+ out() << "<p><b>The following class members are obsolete.</b> They are provided to keep "
+ "old source code working. We strongly advise against using them in new "
+ "code.</p>\n";
+ }
+
+ out() << "<p><ul><li><a href=\"" << linkForNode(inner, 0) << "\">" << protect(inner->name())
+ << " class reference</a></li></ul></p>\n";
+
+ for (i = 0; i < sections.size(); ++i) {
+ out() << "<h3>" << protect(sections.at(i).name) << "</h3>\n";
+
+ generateSectionList(sections.at(i), inner, marker, CodeMarker::Summary);
+ }
+
+ sections = marker->sections(inner, CodeMarker::Detailed, status);
+ for (i = 0; i < sections.size(); ++i) {
+ out() << "<hr />\n";
+ out() << "<h2>" << protect(sections.at(i).name) << "</h2>\n";
+
+ NodeList::ConstIterator m = sections.at(i).members.begin();
+ while (m != sections.at(i).members.end()) {
+ if ((*m)->access() != Node::Private)
+ generateDetailedMember(*m, inner, marker);
+ ++m;
+ }
+ }
+
+ generateFooter();
+ endSubPage();
+ return fileName;
+}
+
+void HtmlGenerator::generateClassHierarchy(const Node *relative, CodeMarker *marker,
+ const QMap<QString, const Node *> &classMap)
+{
+ if (classMap.isEmpty())
+ return;
+
+ QMap<QString, const Node *> topLevel;
+ QMap<QString, const Node *>::ConstIterator c = classMap.begin();
+ while (c != classMap.end()) {
+ const ClassNode *classe = static_cast<const ClassNode *>(*c);
+ if (classe->baseClasses().isEmpty())
+ topLevel.insert(classe->name(), classe);
+ ++c;
+ }
+
+ QStack<QMap<QString, const Node *> > stack;
+ stack.push(topLevel);
+
+ out() << "<ul>\n";
+ while (!stack.isEmpty()) {
+ if (stack.top().isEmpty()) {
+ stack.pop();
+ out() << "</ul>\n";
+ } else {
+ const ClassNode *child = static_cast<const ClassNode *>(*stack.top().begin());
+ out() << "<li>";
+ generateFullName(child, relative, marker);
+ out() << "</li>\n";
+ stack.top().erase(stack.top().begin());
+
+ QMap<QString, const Node *> newTop;
+ foreach (const RelatedClass &d, child->derivedClasses()) {
+ if (d.access != Node::Private)
+ newTop.insert(d.node->name(), d.node);
+ }
+ if (!newTop.isEmpty()) {
+ stack.push(newTop);
+ out() << "<ul>\n";
+ }
+ }
+ }
+}
+
+void HtmlGenerator::generateAnnotatedList(const Node *relative, CodeMarker *marker,
+ const QMap<QString, const Node *> &nodeMap)
+{
+ out() << "<p><table width=\"100%\" class=\"annotated\" cellpadding=\"2\" cellspacing=\"1\" border=\"0\">\n";
+
+ int row = 0;
+ foreach (const QString &name, nodeMap.keys()) {
+ const Node *node = nodeMap[name];
+
+ if (++row % 2 == 1)
+ out() << "<tr valign=\"top\" class=\"odd\">";
+ else
+ out() << "<tr valign=\"top\" class=\"even\">";
+ out() << "<th>";
+ generateFullName(node, relative, marker);
+ out() << "</th>";
+
+ if (!(node->type() == Node::Fake)) {
+ Text brief = node->doc().trimmedBriefText(name);
+ if (!brief.isEmpty()) {
+ out() << "<td>";
+ generateText(brief, node, marker);
+ out() << "</td>";
+ }
+ } else {
+ out() << "<td>";
+ out() << protect(node->doc().briefText().toString());
+ out() << "</td>";
+ }
+ out() << "</tr>\n";
+ }
+ out() << "</table></p>\n";
+}
+
+void HtmlGenerator::generateCompactList(const Node *relative, CodeMarker *marker,
+ const QMap<QString, const Node *> &classMap)
+{
+ const int NumParagraphs = 37; // '0' to '9', 'A' to 'Z', '_'
+ const int NumColumns = 4; // number of columns in the result
+
+ if (classMap.isEmpty())
+ return;
+
+ /*
+ First, find out the common prefix of all non-namespaced classes.
+ For Qt, the prefix is Q. It can easily be derived from the first
+ and last classes in alphabetical order (QAccel and QXtWidget in Qt 2.1).
+ */
+ int commonPrefixLen = 0;
+ QString commonPrefix;
+ QString first;
+ QString last;
+
+ QMap<QString, const Node *>::const_iterator iter = classMap.begin();
+ while (iter != classMap.end()) {
+ if (!iter.key().contains("::")) {
+ first = iter.key();
+ break;
+ }
+ ++iter;
+ }
+
+ if (first.isEmpty())
+ first = classMap.begin().key();
+
+ iter = classMap.end();
+ while (iter != classMap.begin()) {
+ --iter;
+ if (!iter.key().contains("::")) {
+ last = iter.key();
+ break;
+ }
+ }
+
+ if (last.isEmpty())
+ last = classMap.begin().key();
+
+ if (classMap.size() > 1) {
+ while (commonPrefixLen < first.length() + 1 && commonPrefixLen < last.length() + 1
+ && first[commonPrefixLen] == last[commonPrefixLen])
+ ++commonPrefixLen;
+ }
+
+ commonPrefix = first.left(commonPrefixLen);
+
+ /*
+ Divide the data into 37 paragraphs: 0, ..., 9, A, ..., Z,
+ underscore (_). QAccel will fall in paragraph 10 (A) and
+ QXtWidget in paragraph 33 (X). This is the only place where we
+ assume that NumParagraphs is 37. Each paragraph is a
+ QMap<QString, const Node *>.
+ */
+ QMap<QString, const Node *> paragraph[NumParagraphs];
+ QString paragraphName[NumParagraphs];
+
+ QMap<QString, const Node *>::ConstIterator c = classMap.begin();
+ while (c != classMap.end()) {
+ QStringList pieces = c.key().split("::");
+ QString key;
+ if (pieces.size() == 1)
+ key = pieces.last().mid(commonPrefixLen).toLower();
+ else
+ key = pieces.last().toLower();
+
+ int paragraphNo = NumParagraphs - 1;
+
+ if (key[0].digitValue() != -1) {
+ paragraphNo = key[0].digitValue();
+ } else if (key[0] >= QLatin1Char('a') && key[0] <= QLatin1Char('z')) {
+ paragraphNo = 10 + key[0].unicode() - 'a';
+ }
+
+ paragraphName[paragraphNo] = key[0].toUpper();
+ paragraph[paragraphNo].insert(key, c.value());
+ ++c;
+ }
+
+ /*
+ Each paragraph j has a size: paragraph[j].count(). In the
+ discussion, we will assume paragraphs 0 to 5 will have sizes
+ 3, 1, 4, 1, 5, 9.
+
+ We now want to compute the paragraph offset. Paragraphs 0 to 6
+ start at offsets 0, 3, 4, 8, 9, 14, 23.
+ */
+ int paragraphOffset[NumParagraphs + 1];
+ int i, j, k;
+
+ paragraphOffset[0] = 0;
+ for (j = 0; j < NumParagraphs; j++)
+ paragraphOffset[j + 1] = paragraphOffset[j] + paragraph[j].count();
+
+ int firstOffset[NumColumns + 1];
+ int currentOffset[NumColumns];
+ int currentParagraphNo[NumColumns];
+ int currentOffsetInParagraph[NumColumns];
+
+ int numRows = (classMap.count() + NumColumns - 1) / NumColumns;
+ int curParagNo = 0;
+
+ for (i = 0; i < NumColumns; i++) {
+ firstOffset[i] = qMin(i * numRows, classMap.size());
+ currentOffset[i] = firstOffset[i];
+
+ for (j = curParagNo; j < NumParagraphs; j++) {
+ if (paragraphOffset[j] > firstOffset[i])
+ break;
+ if (paragraphOffset[j] <= firstOffset[i])
+ curParagNo = j;
+ }
+ currentParagraphNo[i] = curParagNo;
+ currentOffsetInParagraph[i] = firstOffset[i] -
+ paragraphOffset[curParagNo];
+ }
+ firstOffset[NumColumns] = classMap.count();
+
+ out() << "<p><table width=\"100%\">\n";
+ for (k = 0; k < numRows; k++) {
+ out() << "<tr>\n";
+ for (i = 0; i < NumColumns; i++) {
+ if (currentOffset[i] >= firstOffset[i + 1]) {
+ // this column is finished
+ out() << "<td>\n</td>\n";
+ } else {
+ while (currentOffsetInParagraph[i] == paragraph[currentParagraphNo[i]].count()) {
+ ++currentParagraphNo[i];
+ currentOffsetInParagraph[i] = 0;
+ }
+
+ out() << "<td align=\"right\">";
+ if (currentOffsetInParagraph[i] == 0) {
+ // start a new paragraph
+ out() << "<b>" << paragraphName[currentParagraphNo[i]] << "&nbsp;</b>";
+ }
+ out() << "</td>\n";
+
+ // bad loop
+ QMap<QString, const Node *>::Iterator it;
+ it = paragraph[currentParagraphNo[i]].begin();
+ for (j = 0; j < currentOffsetInParagraph[i]; j++)
+ ++it;
+
+ out() << "<td>";
+ // Previously, we used generateFullName() for this, but we
+ // require some special formatting.
+ out() << "<a href=\"" << linkForNode(it.value(), relative) << "\">";
+ QStringList pieces = fullName(it.value(), relative, marker).split("::");
+ out() << protect(pieces.last());
+ out() << "</a>";
+ if (pieces.size() > 1) {
+ out() << " (";
+ generateFullName(it.value()->parent(), relative, marker);
+ out() << ")";
+ }
+ out() << "</td>\n";
+
+ currentOffset[i]++;
+ currentOffsetInParagraph[i]++;
+ }
+ }
+ out() << "</tr>\n";
+ }
+ out() << "</table></p>\n";
+}
+
+void HtmlGenerator::generateFunctionIndex(const Node *relative, CodeMarker *marker)
+{
+ out() << "<p align=\"center\"><font size=\"+1\"><b>";
+ for (int i = 0; i < 26; i++) {
+ QChar ch('a' + i);
+ out() << QString("<a href=\"#%1\">%2</a>&nbsp;").arg(ch).arg(ch.toUpper());
+ }
+ out() << "</b></font></p>\n";
+
+ char nextLetter = 'a';
+ char currentLetter;
+
+#if 1
+ out() << "<ul>\n";
+#endif
+ QMap<QString, QMap<QString, const Node *> >::ConstIterator f = funcIndex.begin();
+ while (f != funcIndex.end()) {
+#if 1
+ out() << "<li>";
+#else
+ out() << "<p>";
+#endif
+ out() << protect(f.key()) << ":";
+
+ currentLetter = f.key()[0].unicode();
+ while (islower(currentLetter) && currentLetter >= nextLetter) {
+ out() << QString("<a name=\"%1\"></a>").arg(nextLetter);
+ nextLetter++;
+ }
+
+ QMap<QString, const Node *>::ConstIterator s = (*f).begin();
+ while (s != (*f).end()) {
+ out() << " ";
+ generateFullName((*s)->parent(), relative, marker, *s);
+ ++s;
+ }
+#if 1
+ out() << "</li>";
+#else
+ out() << "</p>";
+#endif
+ out() << "\n";
+ ++f;
+ }
+#if 1
+ out() << "</ul>\n";
+#endif
+}
+
+void HtmlGenerator::generateLegaleseList(const Node *relative, CodeMarker *marker)
+{
+ QMap<Text, const Node *>::ConstIterator it = legaleseTexts.begin();
+ while (it != legaleseTexts.end()) {
+ Text text = it.key();
+ out() << "<hr />\n";
+ generateText(text, relative, marker);
+ out() << "<ul>\n";
+ do {
+ out() << "<li>";
+ generateFullName(it.value(), relative, marker);
+ out() << "</li>\n";
+ ++it;
+ } while (it != legaleseTexts.end() && it.key() == text);
+ out() << "</ul>\n";
+ }
+}
+
+void HtmlGenerator::generateSynopsis(const Node *node, const Node *relative,
+ CodeMarker *marker, CodeMarker::SynopsisStyle style)
+{
+ QString marked = marker->markedUpSynopsis(node, relative, style);
+ QRegExp templateTag("(<[^@>]*>)");
+ if (marked.indexOf(templateTag) != -1) {
+ QString contents = protect(marked.mid(templateTag.pos(1),
+ templateTag.cap(1).length()));
+ marked.replace(templateTag.pos(1), templateTag.cap(1).length(),
+ contents);
+ }
+ marked.replace(QRegExp("<@param>([a-z]+)_([1-9n])</@param>"), "<i>\\1<sub>\\2</sub></i>");
+ marked.replace("<@param>", "<i>");
+ marked.replace("</@param>", "</i>");
+
+ if (style == CodeMarker::Summary)
+ marked.replace("@name>", "b>");
+
+ if (style == CodeMarker::SeparateList) {
+ QRegExp extraRegExp("<@extra>.*</@extra>");
+ extraRegExp.setMinimal(true);
+ marked.replace(extraRegExp, "");
+ } else {
+ marked.replace("<@extra>", "&nbsp;&nbsp;<tt>");
+ marked.replace("</@extra>", "</tt>");
+ }
+
+ if (style != CodeMarker::Detailed) {
+ marked.replace("<@type>", "");
+ marked.replace("</@type>", "");
+ }
+ out() << highlightedCode(marked, marker, relative);
+}
+
+void HtmlGenerator::generateOverviewList(const Node *relative, CodeMarker * /* marker */)
+{
+ QMap<const FakeNode *, QMap<QString, FakeNode *> > fakeNodeMap;
+ QMap<QString, const FakeNode *> groupTitlesMap;
+ QMap<QString, FakeNode *> uncategorizedNodeMap;
+ QRegExp singleDigit("\\b([0-9])\\b");
+
+ const NodeList children = tre->root()->childNodes();
+ foreach (Node *child, children) {
+ if (child->type() == Node::Fake && child != relative) {
+ FakeNode *fakeNode = static_cast<FakeNode *>(child);
+
+ // Check whether the page is part of a group or is the group
+ // definition page.
+ QString group;
+ bool isGroupPage = false;
+ if (fakeNode->doc().metaCommandsUsed().contains("group")) {
+ group = fakeNode->doc().metaCommandArgs("group")[0];
+ isGroupPage = true;
+ }
+
+ // there are too many examples; they would clutter the list
+ if (fakeNode->subType() == FakeNode::Example)
+ continue;
+
+ // not interested either in individual (Qt Designer etc.) manual chapters
+ if (fakeNode->links().contains(Node::ContentsLink))
+ continue;
+
+ // Discard external nodes.
+ if (fakeNode->subType() == FakeNode::ExternalPage)
+ continue;
+
+ QString sortKey = fakeNode->fullTitle().toLower();
+ if (sortKey.startsWith("the "))
+ sortKey.remove(0, 4);
+ sortKey.replace(singleDigit, "0\\1");
+
+ if (!group.isEmpty()) {
+ if (isGroupPage) {
+ // If we encounter a group definition page, we add all
+ // the pages in that group to the list for that group.
+ foreach (Node *member, fakeNode->groupMembers()) {
+ if (member->type() != Node::Fake)
+ continue;
+ FakeNode *page = static_cast<FakeNode *>(member);
+ if (page) {
+ QString sortKey = page->fullTitle().toLower();
+ if (sortKey.startsWith("the "))
+ sortKey.remove(0, 4);
+ sortKey.replace(singleDigit, "0\\1");
+ fakeNodeMap[const_cast<const FakeNode *>(fakeNode)].insert(sortKey, page);
+ groupTitlesMap[fakeNode->fullTitle()] = const_cast<const FakeNode *>(fakeNode);
+ }
+ }
+ } else if (!isGroupPage) {
+ // If we encounter a page that belongs to a group then
+ // we add that page to the list for that group.
+ const FakeNode *groupNode = static_cast<const FakeNode *>(tre->root()->findNode(group, Node::Fake));
+ if (groupNode)
+ fakeNodeMap[groupNode].insert(sortKey, fakeNode);
+ //else
+ // uncategorizedNodeMap.insert(sortKey, fakeNode);
+ }// else
+ // uncategorizedNodeMap.insert(sortKey, fakeNode);
+ }// else
+ // uncategorizedNodeMap.insert(sortKey, fakeNode);
+ }
+ }
+
+ // We now list all the pages found that belong to groups.
+ // If only certain pages were found for a group, but the definition page
+ // for that group wasn't listed, the list of pages will be intentionally
+ // incomplete. However, if the group definition page was listed, all the
+ // pages in that group are listed for completeness.
+
+ if (!fakeNodeMap.isEmpty()) {
+ foreach (const QString &groupTitle, groupTitlesMap.keys()) {
+ const FakeNode *groupNode = groupTitlesMap[groupTitle];
+ out() << QString("<h3><a href=\"%1\">%2</a></h3>\n").arg(
+ linkForNode(groupNode, relative)).arg(
+ protect(groupNode->fullTitle()));
+
+ if (fakeNodeMap[groupNode].count() == 0)
+ continue;
+
+ out() << "<ul>\n";
+
+ foreach (const FakeNode *fakeNode, fakeNodeMap[groupNode]) {
+ QString title = fakeNode->fullTitle();
+ if (title.startsWith("The "))
+ title.remove(0, 4);
+ out() << "<li><a href=\"" << linkForNode(fakeNode, relative) << "\">"
+ << protect(title) << "</a></li>\n";
+ }
+ out() << "</ul>\n";
+ }
+ }
+
+ if (!uncategorizedNodeMap.isEmpty()) {
+ out() << QString("<h3>Miscellaneous</h3>\n");
+ out() << "<ul>\n";
+ foreach (const FakeNode *fakeNode, uncategorizedNodeMap) {
+ QString title = fakeNode->fullTitle();
+ if (title.startsWith("The "))
+ title.remove(0, 4);
+ out() << "<li><a href=\"" << linkForNode(fakeNode, relative) << "\">"
+ << protect(title) << "</a></li>\n";
+ }
+ out() << "</ul>\n";
+ }
+}
+
+void HtmlGenerator::generateSectionList(const Section& section, const Node *relative,
+ CodeMarker *marker, CodeMarker::SynopsisStyle style)
+{
+ if (!section.members.isEmpty()) {
+ bool twoColumn = false;
+ if (style == CodeMarker::SeparateList) {
+ twoColumn = (section.members.count() >= 16);
+ } else if (section.members.first()->type() == Node::Property) {
+ twoColumn = (section.members.count() >= 5);
+ }
+ if (twoColumn)
+ out() << "<p><table width=\"100%\" border=\"0\" cellpadding=\"0\""
+ " cellspacing=\"0\">\n"
+ << "<tr><td width=\"45%\" valign=\"top\">";
+ out() << "<ul>\n";
+
+ int i = 0;
+ NodeList::ConstIterator m = section.members.begin();
+ while (m != section.members.end()) {
+ if ((*m)->access() == Node::Private) {
+ ++m;
+ continue;
+ }
+
+ if (twoColumn && i == (int) (section.members.count() + 1) / 2)
+ out() << "</ul></td><td valign=\"top\"><ul>\n";
+
+ out() << "<li><div class=\"fn\"></div>";
+ if (style == CodeMarker::Accessors)
+ out() << "<b>";
+ generateSynopsis(*m, relative, marker, style);
+ if (style == CodeMarker::Accessors)
+ out() << "</b>";
+ out() << "</li>\n";
+ i++;
+ ++m;
+ }
+ out() << "</ul>\n";
+ if (twoColumn)
+ out() << "</td></tr>\n</table></p>\n";
+ }
+
+ if (style == CodeMarker::Summary && !section.inherited.isEmpty()) {
+ out() << "<ul>\n";
+ generateSectionInheritedList(section, relative, marker);
+ out() << "</ul>\n";
+ }
+}
+
+void HtmlGenerator::generateSectionInheritedList(const Section& section, const Node *relative,
+ CodeMarker *marker)
+{
+ QList<QPair<ClassNode *, int> >::ConstIterator p = section.inherited.begin();
+ while (p != section.inherited.end()) {
+ out() << "<li><div class=\"fn\"></div>";
+ out() << (*p).second << " ";
+ if ((*p).second == 1) {
+ out() << section.singularMember;
+ } else {
+ out() << section.pluralMember;
+ }
+ out() << " inherited from <a href=\"" << fileName((*p).first)
+ << "#" << HtmlGenerator::cleanRef(section.name.toLower()) << "\">"
+ << protect(marker->plainFullName((*p).first, relative))
+ << "</a></li>\n";
+ ++p;
+ }
+}
+
+void HtmlGenerator::generateLink(const Atom *atom, const Node * /* relative */, CodeMarker *marker)
+{
+ static QRegExp camelCase("[A-Z][A-Z][a-z]|[a-z][A-Z0-9]|_");
+
+ if (funcLeftParen.indexIn(atom->string()) != -1 && marker->recognizeLanguage("Cpp")) {
+ // hack for C++: move () outside of link
+ int k = funcLeftParen.pos(1);
+ out() << protect(atom->string().left(k));
+ if (link.isEmpty()) {
+ if (showBrokenLinks)
+ out() << "</i>";
+ } else {
+ out() << "</a>";
+ }
+ inLink = false;
+ out() << protect(atom->string().mid(k));
+ } else if (marker->recognizeLanguage("Java")) {
+ // hack for Java: remove () and use <tt> when appropriate
+ bool func = atom->string().endsWith("()");
+ bool tt = (func || atom->string().contains(camelCase));
+ if (tt)
+ out() << "<tt>";
+ if (func) {
+ out() << protect(atom->string().left(atom->string().length() - 2));
+ } else {
+ out() << protect(atom->string());
+ }
+ out() << "</tt>";
+ } else {
+ out() << protect(atom->string());
+ }
+}
+
+QString HtmlGenerator::cleanRef(const QString& ref)
+{
+ QString clean;
+
+ if (ref.isEmpty())
+ return clean;
+
+ clean.reserve(ref.size() + 20);
+ const QChar c = ref[0];
+ const uint u = c.unicode();
+
+ if ((u >= 'a' && u <= 'z') ||
+ (u >= 'A' && u <= 'Z') ||
+ (u >= '0' && u <= '9')) {
+ clean += c;
+ } else if (u == '~') {
+ clean += "dtor.";
+ } else if (u == '_') {
+ clean += "underscore.";
+ } else {
+ clean += "A";
+ }
+
+ for (int i = 1; i < (int) ref.length(); i++) {
+ const QChar c = ref[i];
+ const uint u = c.unicode();
+ if ((u >= 'a' && u <= 'z') ||
+ (u >= 'A' && u <= 'Z') ||
+ (u >= '0' && u <= '9') || u == '-' ||
+ u == '_' || u == ':' || u == '.') {
+ clean += c;
+ } else if (c.isSpace()) {
+ clean += "-";
+ } else if (u == '!') {
+ clean += "-not";
+ } else if (u == '&') {
+ clean += "-and";
+ } else if (u == '<') {
+ clean += "-lt";
+ } else if (u == '=') {
+ clean += "-eq";
+ } else if (u == '>') {
+ clean += "-gt";
+ } else if (u == '#') {
+ clean += "#";
+ } else {
+ clean += "-";
+ clean += QString::number((int)u, 16);
+ }
+ }
+ return clean;
+}
+
+QString HtmlGenerator::registerRef(const QString& ref)
+{
+ QString clean = HtmlGenerator::cleanRef(ref);
+
+ for (;;) {
+ QString& prevRef = refMap[clean.toLower()];
+ if (prevRef.isEmpty()) {
+ prevRef = ref;
+ break;
+ } else if (prevRef == ref) {
+ break;
+ }
+ clean += "x";
+ }
+ return clean;
+}
+
+QString HtmlGenerator::protect(const QString& string)
+{
+#define APPEND(x) \
+ if (html.isEmpty()) { \
+ html = string; \
+ html.truncate(i); \
+ } \
+ html += (x);
+
+ QString html;
+ int n = string.length();
+
+ for (int i = 0; i < n; ++i) {
+ QChar ch = string.at(i);
+
+ if (ch == QLatin1Char('&')) {
+ APPEND("&amp;");
+ } else if (ch == QLatin1Char('<')) {
+ APPEND("&lt;");
+ } else if (ch == QLatin1Char('>')) {
+ APPEND("&gt;");
+ } else if (ch == QLatin1Char('"')) {
+ APPEND("&quot;");
+ } else if (ch.unicode() > 0x007F
+ || (ch == QLatin1Char('*') && i + 1 < n && string.at(i) == QLatin1Char('/'))
+ || (ch == QLatin1Char('.') && i > 2 && string.at(i - 2) == QLatin1Char('.'))) {
+ // we escape '*/' and the last dot in 'e.g.' and 'i.e.' for the Javadoc generator
+ APPEND("&#x");
+ html += QString::number(ch.unicode(), 16);
+ html += QLatin1Char(';');
+ } else {
+ if (!html.isEmpty())
+ html += ch;
+ }
+ }
+
+ if (!html.isEmpty())
+ return html;
+ return string;
+
+#undef APPEND
+}
+
+static QRegExp linkTag("(<@link node=\"([^\"]+)\">).*(</@link>)");
+static QRegExp funcTag("(<@func target=\"([^\"]*)\">)(.*)(</@func>)");
+static QRegExp typeTag("(<@(type|headerfile|func)(?: +[^>]*)?>)(.*)(</@\\2>)");
+static QRegExp spanTag("</@(?:comment|preprocessor|string|char)>");
+static QRegExp unknownTag("</?@[^>]*>");
+
+bool parseArg(const QString &src,
+ const QString &tag,
+ int *pos,
+ int n,
+ QStringRef *contents,
+ QStringRef *par1 = 0,
+ bool debug = false)
+{
+#define SKIP_CHAR(c) \
+ if (debug) \
+ qDebug() << "looking for " << c << " at " << QString(src.data() + i, n - i); \
+ if (i >= n || src[i] != c) { \
+ if (debug) \
+ qDebug() << " char '" << c << "' not found"; \
+ return false; \
+ } \
+ ++i;
+
+
+#define SKIP_SPACE \
+ while (i < n && src[i] == ' ') \
+ ++i;
+
+ int i = *pos;
+ int j = i;
+
+ // assume "<@" has been parsed outside
+ //SKIP_CHAR('<');
+ //SKIP_CHAR('@');
+
+ if (tag != QStringRef(&src, i, tag.length())) {
+ if (0 && debug)
+ qDebug() << "tag " << tag << " not found at " << i;
+ return false;
+ }
+
+ if (debug)
+ qDebug() << "haystack:" << src << "needle:" << tag << "i:" <<i;
+
+ // skip tag
+ i += tag.length();
+
+ // parse stuff like: linkTag("(<@link node=\"([^\"]+)\">).*(</@link>)");
+ if (par1) {
+ SKIP_SPACE;
+ // read parameter name
+ j = i;
+ while (i < n && src[i].isLetter())
+ ++i;
+ if (src[i] == '=') {
+ if (debug)
+ qDebug() << "read parameter" << QString(src.data() + j, i - j);
+ SKIP_CHAR('=');
+ SKIP_CHAR('"');
+ // skip parameter name
+ j = i;
+ while (i < n && src[i] != '"')
+ ++i;
+ *par1 = QStringRef(&src, j, i - j);
+ SKIP_CHAR('"');
+ SKIP_SPACE;
+ } else {
+ if (debug)
+ qDebug() << "no optional parameter found";
+ }
+ }
+ SKIP_SPACE;
+ SKIP_CHAR('>');
+
+ // find contents up to closing "</@tag>
+ j = i;
+ for (; true; ++i) {
+ if (i + 4 + tag.length() > n)
+ return false;
+ if (src[i] != '<')
+ continue;
+ if (src[i + 1] != '/')
+ continue;
+ if (src[i + 2] != '@')
+ continue;
+ if (tag != QStringRef(&src, i + 3, tag.length()))
+ continue;
+ if (src[i + 3 + tag.length()] != '>')
+ continue;
+ break;
+ }
+
+ *contents = QStringRef(&src, j, i - j);
+
+ i += tag.length() + 4;
+
+ *pos = i;
+ if (debug)
+ qDebug() << " tag " << tag << " found: pos now: " << i;
+ return true;
+#undef SKIP_CHAR
+}
+
+static void addLink(const QString &linkTarget,
+ const QStringRef &nestedStuff,
+ QString *res)
+{
+ if (!linkTarget.isEmpty()) {
+ *res += "<a href=\"";
+ *res += linkTarget;
+ *res += "\">";
+ *res += nestedStuff;
+ *res += "</a>";
+ }
+ else {
+ *res += nestedStuff;
+ }
+}
+
+QString HtmlGenerator::highlightedCode(const QString& markedCode,
+ CodeMarker *marker,
+ const Node *relative)
+{
+ QString src = markedCode;
+ QString html;
+ QStringRef arg;
+ QStringRef par1;
+
+ const QChar charLangle = '<';
+ const QChar charAt = '@';
+
+ // replace all <@link> tags: "(<@link node=\"([^\"]+)\">).*(</@link>)"
+ static const QString linkTag("link");
+ for (int i = 0, n = src.size(); i < n;) {
+ if (src.at(i) == charLangle && src.at(i + 1) == charAt) {
+ i += 2;
+ if (parseArg(src, linkTag, &i, n, &arg, &par1)) {
+ QString link = linkForNode(
+ CodeMarker::nodeForString(par1.toString()), relative);
+ addLink(link, arg, &html);
+ }
+ else {
+ html += charLangle;
+ html += charAt;
+ }
+ }
+ else {
+ html += src.at(i++);
+ }
+ }
+
+
+ if (slow) {
+ // is this block ever used at all?
+ // replace all <@func> tags: "(<@func target=\"([^\"]*)\">)(.*)(</@func>)"
+ src = html;
+ html = QString();
+ static const QString funcTag("func");
+ for (int i = 0, n = src.size(); i < n;) {
+ if (src.at(i) == charLangle && src.at(i + 1) == charAt) {
+ i += 2;
+ if (parseArg(src, funcTag, &i, n, &arg, &par1)) {
+ QString link = linkForNode(
+ marker->resolveTarget(par1.toString(),
+ tre,
+ relative),
+ relative);
+ addLink(link, arg, &html);
+ par1 = QStringRef();
+ }
+ else {
+ html += charLangle;
+ html += charAt;
+ }
+ }
+ else {
+ html += src.at(i++);
+ }
+ }
+ }
+
+ // replace all "(<@(type|headerfile|func)(?: +[^>]*)?>)(.*)(</@\\2>)" tags
+ src = html;
+ html = QString();
+ static const QString typeTags[] = { "type", "headerfile", "func" };
+ for (int i = 0, n = src.size(); i < n;) {
+ if (src.at(i) == charLangle && src.at(i + 1) == charAt) {
+ i += 2;
+ bool handled = false;
+ for (int k = 0; k != 3; ++k) {
+ if (parseArg(src, typeTags[k], &i, n, &arg, &par1)) {
+ par1 = QStringRef();
+ QString link = linkForNode(
+ marker->resolveTarget(arg.toString(), tre, relative),
+ relative);
+ addLink(link, arg, &html);
+ handled = true;
+ break;
+ }
+ }
+ if (!handled) {
+ html += charLangle;
+ html += charAt;
+ }
+ }
+ else {
+ html += src.at(i++);
+ }
+ }
+
+ // replace all
+ // "<@comment>" -> "<span class=\"comment\">";
+ // "<@preprocessor>" -> "<span class=\"preprocessor\">";
+ // "<@string>" -> "<span class=\"string\">";
+ // "<@char>" -> "<span class=\"char\">";
+ // "</@(?:comment|preprocessor|string|char)>" -> "</span>"
+ src = html;
+ html = QString();
+ static const QString spanTags[] = {
+ "<@comment>", "<span class=\"comment\">",
+ "<@preprocessor>", "<span class=\"preprocessor\">",
+ "<@string>", "<span class=\"string\">",
+ "<@char>", "<span class=\"char\">",
+ "</@comment>", "</span>",
+ "</@preprocessor>","</span>",
+ "</@string>", "</span>",
+ "</@char>", "</span>"
+ // "<@char>", "<font color=blue>",
+ // "</@char>", "</font>",
+ // "<@func>", "<font color=green>",
+ // "</@func>", "</font>",
+ // "<@id>", "<i>",
+ // "</@id>", "</i>",
+ // "<@keyword>", "<b>",
+ // "</@keyword>", "</b>",
+ // "<@number>", "<font color=yellow>",
+ // "</@number>", "</font>",
+ // "<@op>", "<b>",
+ // "</@op>", "</b>",
+ // "<@param>", "<i>",
+ // "</@param>", "</i>",
+ // "<@string>", "<font color=green>",
+ // "</@string>", "</font>",
+ };
+ for (int i = 0, n = src.size(); i < n;) {
+ if (src.at(i) == charLangle) {
+ bool handled = false;
+ for (int k = 0; k != 8; ++k) {
+ const QString & tag = spanTags[2 * k];
+ if (tag == QStringRef(&src, i, tag.length())) {
+ html += spanTags[2 * k + 1];
+ i += tag.length();
+ handled = true;
+ break;
+ }
+ }
+ if (!handled) {
+ ++i;
+ if (src.at(i) == charAt ||
+ (src.at(i) == QLatin1Char('/') && src.at(i + 1) == charAt)) {
+ // drop 'our' unknown tags (the ones still containing '@')
+ while (i < n && src.at(i) != QLatin1Char('>'))
+ ++i;
+ ++i;
+ }
+ else {
+ // retain all others
+ html += charLangle;
+ }
+ }
+ }
+ else {
+ html += src.at(i);
+ ++i;
+ }
+ }
+
+ return html;
+}
+
+QString HtmlGenerator::fileBase(const Node *node)
+{
+ QString result;
+
+ result = PageGenerator::fileBase(node);
+
+ if (!node->isInnerNode()) {
+ switch (node->status()) {
+ case Node::Compat:
+ result += "-qt3";
+ break;
+ case Node::Obsolete:
+ result += "-obsolete";
+ break;
+ default:
+ ;
+ }
+ }
+ return result;
+}
+
+#if 0
+QString HtmlGenerator::fileBase(const Node *node,
+ const SectionIterator& section)
+{
+ QStringList::ConstIterator s = section.sectionNumber().end();
+ QStringList::ConstIterator b = section.baseNameStack().end();
+
+ QString suffix;
+ QString base = fileBase(node);
+
+ while (s != section.sectionNumber().begin()) {
+ --s;
+ --b;
+ if (!(*b).isEmpty()) {
+ base = *b;
+ break;
+ }
+ suffix.prepend("-" + *s);
+ }
+ return base + suffix;
+}
+#endif
+
+QString HtmlGenerator::fileName(const Node *node)
+{
+ if (node->type() == Node::Fake) {
+ if (static_cast<const FakeNode *>(node)->subType() == FakeNode::ExternalPage)
+ return node->name();
+ }
+
+ return PageGenerator::fileName(node);
+}
+
+QString HtmlGenerator::refForNode(const Node *node)
+{
+ const FunctionNode *func;
+ const TypedefNode *typedeffe;
+ QString ref;
+
+ switch (node->type()) {
+ case Node::Namespace:
+ case Node::Class:
+ default:
+ break;
+ case Node::Enum:
+ ref = node->name() + "-enum";
+ break;
+ case Node::Typedef:
+ typedeffe = static_cast<const TypedefNode *>(node);
+ if (typedeffe->associatedEnum()) {
+ return refForNode(typedeffe->associatedEnum());
+ } else {
+ ref = node->name() + "-typedef";
+ }
+ break;
+ case Node::Function:
+ func = static_cast<const FunctionNode *>(node);
+ if (func->associatedProperty()) {
+ return refForNode(func->associatedProperty());
+ } else {
+ ref = func->name();
+ if (func->overloadNumber() != 1)
+ ref += "-" + QString::number(func->overloadNumber());
+ }
+ break;
+ case Node::Property:
+ ref = node->name() + "-prop";
+ break;
+ case Node::Variable:
+ ref = node->name() + "-var";
+ break;
+ case Node::Target:
+ return protect(node->name());
+ }
+ return registerRef(ref);
+}
+
+QString HtmlGenerator::linkForNode(const Node *node, const Node *relative)
+{
+ QString link;
+ QString fn;
+ QString ref;
+
+ if (node == 0 || node == relative)
+ return QString();
+ if (!node->url().isEmpty())
+ return node->url();
+ if (fileBase(node).isEmpty())
+ return QString();
+ if (node->access() == Node::Private)
+ return QString();
+
+ fn = fileName(node);
+/* if (!node->url().isEmpty())
+ return fn;*/
+#if 0
+ // ### reintroduce this test, without breaking .dcf files
+ if (fn != outFileName())
+#endif
+ link += fn;
+
+ if (!node->isInnerNode()) {
+ ref = refForNode(node);
+ if (relative && fn == fileName(relative) && ref == refForNode(relative))
+ return QString();
+
+ link += "#";
+ link += ref;
+ }
+ return link;
+}
+
+QString HtmlGenerator::refForAtom(Atom *atom, const Node * /* node */)
+{
+ if (atom->type() == Atom::SectionLeft) {
+ return Doc::canonicalTitle(Text::sectionHeading(atom).toString());
+ } else if (atom->type() == Atom::Target) {
+ return Doc::canonicalTitle(atom->string());
+ } else {
+ return QString();
+ }
+}
+
+void HtmlGenerator::generateFullName(const Node *apparentNode,
+ const Node *relative,
+ CodeMarker *marker,
+ const Node *actualNode)
+{
+ if (actualNode == 0)
+ actualNode = apparentNode;
+ out() << "<a href=\"" << linkForNode(actualNode, relative);
+ if (true || relative == 0 || relative->status() != actualNode->status()) {
+ switch (actualNode->status()) {
+ case Node::Obsolete:
+ out() << "\" class=\"obsolete";
+ break;
+ case Node::Compat:
+ out() << "\" class=\"compat";
+ break;
+ default:
+ ;
+ }
+ }
+ out() << "\">";
+ out() << protect(fullName(apparentNode, relative, marker));
+ out() << "</a>";
+}
+
+void HtmlGenerator::generateDetailedMember(const Node *node,
+ const InnerNode *relative,
+ CodeMarker *marker)
+{
+ const EnumNode *enume;
+
+ generateMacRef(node, marker);
+ if (node->type() == Node::Enum
+ && (enume = static_cast<const EnumNode *>(node))->flagsType()) {
+ generateMacRef(enume->flagsType(), marker);
+ out() << "<h3 class=\"flags\">";
+ out() << "<a name=\"" + refForNode(node) + "\"></a>";
+ generateSynopsis(enume, relative, marker, CodeMarker::Detailed);
+ out() << "<br />";
+ generateSynopsis(enume->flagsType(), relative, marker, CodeMarker::Detailed);
+ out() << "</h3>\n";
+ }
+ else {
+ out() << "<h3 class=\"fn\">";
+ out() << "<a name=\"" + refForNode(node) + "\"></a>";
+ generateSynopsis(node, relative, marker, CodeMarker::Detailed);
+ out() << "</h3>\n";
+ }
+
+ generateStatus(node, marker);
+ generateBody(node, marker);
+ generateThreadSafeness(node, marker);
+ generateSince(node, marker);
+
+ if (node->type() == Node::Property) {
+ const PropertyNode *property = static_cast<const PropertyNode *>(node);
+ Section section;
+
+ section.members += property->getters();
+ section.members += property->setters();
+ section.members += property->resetters();
+
+ if (!section.members.isEmpty()) {
+ out() << "<p>Access functions:</p>\n";
+ generateSectionList(section, node, marker, CodeMarker::Accessors);
+ }
+ }
+ else if (node->type() == Node::Enum) {
+ const EnumNode *enume = static_cast<const EnumNode *>(node);
+ if (enume->flagsType()) {
+ out() << "<p>The " << protect(enume->flagsType()->name())
+ << " type is a typedef for "
+ << "<a href=\"qflags.html\">QFlags</a>&lt;"
+ << protect(enume->name())
+ << "&gt;. It stores an OR combination of " << protect(enume->name())
+ << " values.</p>\n";
+ }
+ }
+ generateAlsoList(node, marker);
+}
+
+void HtmlGenerator::findAllClasses(const InnerNode *node)
+{
+ NodeList::const_iterator c = node->childNodes().constBegin();
+ while (c != node->childNodes().constEnd()) {
+ if ((*c)->access() != Node::Private && (*c)->url().isEmpty()) {
+ if ((*c)->type() == Node::Class && !(*c)->doc().isEmpty()) {
+ QString className = (*c)->name();
+ if ((*c)->parent() && (*c)->parent()->type() == Node::Namespace &&
+ !(*c)->parent()->name().isEmpty())
+ className = (*c)->parent()->name()+"::"+className;
+
+ if (!(static_cast<const ClassNode *>(*c))->hideFromMainList()) {
+ if ((*c)->status() == Node::Compat) {
+ compatClasses.insert(className, *c);
+ }
+ else {
+ nonCompatClasses.insert(className, *c);
+ if ((*c)->status() == Node::Main)
+ mainClasses.insert(className, *c);
+ }
+ }
+
+ QString moduleName = (*c)->moduleName();
+ if (moduleName == "Qt3SupportLight") {
+ moduleClassMap[moduleName].insert((*c)->name(), *c);
+ moduleName = "Qt3Support";
+ }
+ if (!moduleName.isEmpty())
+ moduleClassMap[moduleName].insert((*c)->name(), *c);
+
+ QString serviceName =
+ (static_cast<const ClassNode *>(*c))->serviceName();
+ if (!serviceName.isEmpty())
+ serviceClasses.insert(serviceName, *c);
+ }
+ else if ((*c)->isInnerNode()) {
+ findAllClasses(static_cast<InnerNode *>(*c));
+ }
+ }
+ ++c;
+ }
+}
+
+void HtmlGenerator::findAllFunctions(const InnerNode *node)
+{
+ NodeList::ConstIterator c = node->childNodes().begin();
+ while (c != node->childNodes().end()) {
+ if ((*c)->access() != Node::Private) {
+ if ((*c)->isInnerNode() && (*c)->url().isEmpty()) {
+ findAllFunctions(static_cast<const InnerNode *>(*c));
+ }
+ else if ((*c)->type() == Node::Function) {
+ const FunctionNode *func = static_cast<const FunctionNode *>(*c);
+ if (func->status() > Node::Obsolete && func->metaness() != FunctionNode::Ctor
+ && func->metaness() != FunctionNode::Dtor) {
+ funcIndex[(*c)->name()].insert((*c)->parent()->name(), *c);
+ }
+ }
+ }
+ ++c;
+ }
+}
+
+void HtmlGenerator::findAllLegaleseTexts(const InnerNode *node)
+{
+ NodeList::ConstIterator c = node->childNodes().begin();
+ while (c != node->childNodes().end()) {
+ if ((*c)->access() != Node::Private) {
+ if (!(*c)->doc().legaleseText().isEmpty())
+ legaleseTexts.insertMulti((*c)->doc().legaleseText(), *c);
+ if ((*c)->isInnerNode())
+ findAllLegaleseTexts(static_cast<const InnerNode *>(*c));
+ }
+ ++c;
+ }
+}
+
+void HtmlGenerator::findAllNamespaces(const InnerNode *node)
+{
+ NodeList::ConstIterator c = node->childNodes().begin();
+ while (c != node->childNodes().end()) {
+ if ((*c)->access() != Node::Private) {
+ if ((*c)->isInnerNode() && (*c)->url().isEmpty()) {
+ findAllNamespaces(static_cast<const InnerNode *>(*c));
+ if ((*c)->type() == Node::Namespace) {
+ const NamespaceNode *nspace = static_cast<const NamespaceNode *>(*c);
+ // Ensure that the namespace's name is not empty (the root
+ // namespace has no name).
+ if (!nspace->name().isEmpty()) {
+ namespaceIndex.insert(nspace->name(), *c);
+ QString moduleName = (*c)->moduleName();
+ if (moduleName == "Qt3SupportLight") {
+ moduleNamespaceMap[moduleName].insert((*c)->name(), *c);
+ moduleName = "Qt3Support";
+ }
+ if (!moduleName.isEmpty())
+ moduleNamespaceMap[moduleName].insert((*c)->name(), *c);
+ }
+ }
+ }
+ }
+ ++c;
+ }
+}
+
+#ifdef ZZZ_QDOC_QML
+/*!
+ This function finds all the qml element nodes and
+ stores them in a map for later use.
+ */
+void HtmlGenerator::findAllQmlClasses(const InnerNode *node)
+{
+ NodeList::const_iterator c = node->childNodes().constBegin();
+ while (c != node->childNodes().constEnd()) {
+ if ((*c)->type() == Node::Fake) {
+ const FakeNode* fakeNode = static_cast<const FakeNode *>(*c);
+ if (fakeNode->subType() == FakeNode::QmlClass) {
+ const QmlNode* qmlNode = static_cast<const QmlNode*>(fakeNode);
+ //qDebug() << "HtmlGenerator: QML CLASS" << qmlNode->name();
+ const Node* n = qmlNode->classNode();
+ if (n)
+ //qDebug() << " FOUND IT!" << n->name();
+ }
+ qmlClasses.insert(fakeNode->name(),*c);
+ }
+ ++c;
+ }
+}
+#endif
+
+#if 0
+ else if ((*c)->isInnerNode()) {
+ findAllClasses(static_cast<InnerNode *>(*c));
+ }
+#endif
+
+int HtmlGenerator::hOffset(const Node *node)
+{
+ switch (node->type()) {
+ case Node::Namespace:
+ case Node::Class:
+ return 2;
+ case Node::Fake:
+ if (node->doc().briefText().isEmpty())
+ return 1;
+ else
+ return 2;
+ case Node::Enum:
+ case Node::Typedef:
+ case Node::Function:
+ case Node::Property:
+ default:
+ return 3;
+ }
+}
+
+bool HtmlGenerator::isThreeColumnEnumValueTable(const Atom *atom)
+{
+ while (atom != 0 && !(atom->type() == Atom::ListRight && atom->string() == ATOM_LIST_VALUE)) {
+ if (atom->type() == Atom::ListItemLeft && !matchAhead(atom, Atom::ListItemRight))
+ return true;
+ atom = atom->next();
+ }
+ return false;
+}
+
+const Node *HtmlGenerator::findNodeForTarget(const QString &target,
+ const Node *relative,
+ CodeMarker *marker,
+ const Atom *atom)
+{
+ const Node *node = 0;
+
+ if (target.isEmpty()) {
+ node = relative;
+ }
+ else if (target.endsWith(".html")) {
+ node = tre->root()->findNode(target, Node::Fake);
+ }
+ else if (marker) {
+ node = marker->resolveTarget(target, tre, relative);
+ if (!node)
+ node = tre->findFakeNodeByTitle(target);
+ if (!node && atom) {
+ node = tre->findUnambiguousTarget(target,
+ *const_cast<Atom**>(&atom));
+ }
+ }
+
+ if (!node)
+ relative->doc().location().warning(tr("Cannot link to '%1'").arg(target));
+
+ return node;
+}
+
+const QPair<QString,QString> HtmlGenerator::anchorForNode(const Node *node)
+{
+ QPair<QString,QString> anchorPair;
+
+ anchorPair.first = PageGenerator::fileName(node);
+ if (node->type() == Node::Fake) {
+ const FakeNode *fakeNode = static_cast<const FakeNode*>(node);
+ anchorPair.second = fakeNode->title();
+ }
+
+ return anchorPair;
+}
+
+QString HtmlGenerator::getLink(const Atom *atom,
+ const Node *relative,
+ CodeMarker *marker,
+ const Node *node)
+{
+ QString link;
+ node = 0;
+
+ if (atom->string().contains(":") &&
+ (atom->string().startsWith("file:")
+ || atom->string().startsWith("http:")
+ || atom->string().startsWith("https:")
+ || atom->string().startsWith("ftp:")
+ || atom->string().startsWith("mailto:"))) {
+
+ link = atom->string();
+ }
+ else {
+ QStringList path;
+ if (atom->string().contains('#')) {
+ path = atom->string().split('#');
+ }
+ else {
+ path.append(atom->string());
+ }
+
+ Atom *targetAtom = 0;
+
+ QString first = path.first().trimmed();
+ if (first.isEmpty()) {
+ node = relative;
+ }
+ else if (first.endsWith(".html")) {
+ node = tre->root()->findNode(first, Node::Fake);
+ }
+ else {
+ node = marker->resolveTarget(first, tre, relative);
+ if (!node)
+ node = tre->findFakeNodeByTitle(first);
+ if (!node)
+ node = tre->findUnambiguousTarget(first, targetAtom);
+ }
+
+ if (node) {
+ if (!node->url().isEmpty())
+ return node->url();
+ else
+ path.removeFirst();
+ }
+ else {
+ node = relative;
+ }
+
+ while (!path.isEmpty()) {
+ targetAtom = tre->findTarget(path.first(), node);
+ if (targetAtom == 0)
+ break;
+ path.removeFirst();
+ }
+
+ if (path.isEmpty()) {
+ link = linkForNode(node, relative);
+ if (targetAtom)
+ link += "#" + refForAtom(targetAtom, node);
+ }
+ }
+ return link;
+}
+
+void HtmlGenerator::generateDcf(const QString &fileBase,
+ const QString &startPage,
+ const QString &title,
+ DcfSection &dcfRoot)
+{
+ dcfRoot.ref = startPage;
+ dcfRoot.title = title;
+ generateDcfSections(dcfRoot, outputDir() + "/" + fileBase + ".dcf", fileBase + "/reference");
+}
+
+void HtmlGenerator::generateIndex(const QString &fileBase,
+ const QString &url,
+ const QString &title)
+{
+ tre->generateIndex(outputDir() + "/" + fileBase + ".index", url, title);
+}
+
+void HtmlGenerator::generateStatus(const Node *node, CodeMarker *marker)
+{
+ Text text;
+
+ switch (node->status()) {
+ case Node::Obsolete:
+ if (node->isInnerNode())
+ Generator::generateStatus(node, marker);
+ break;
+ case Node::Compat:
+ if (node->isInnerNode()) {
+ text << Atom::ParaLeft
+ << Atom(Atom::FormattingLeft,ATOM_FORMATTING_BOLD) << "This "
+ << typeString(node) << " is part of the Qt 3 support library."
+ << Atom(Atom::FormattingRight, ATOM_FORMATTING_BOLD)
+ << " It is provided to keep old source code working. We strongly advise against "
+ << "using it in new code. See ";
+
+ const FakeNode *fakeNode = tre->findFakeNodeByTitle("Porting To Qt 4");
+ Atom *targetAtom = 0;
+ if (fakeNode && node->type() == Node::Class) {
+ QString oldName(node->name());
+ targetAtom = tre->findTarget(oldName.replace("3", ""),
+ fakeNode);
+ }
+
+ if (targetAtom) {
+ text << Atom(Atom::Link, linkForNode(fakeNode, node) + "#" +
+ refForAtom(targetAtom, fakeNode));
+ }
+ else
+ text << Atom(Atom::Link, "Porting to Qt 4");
+
+ text << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK)
+ << Atom(Atom::String, "Porting to Qt 4")
+ << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK)
+ << " for more information."
+ << Atom::ParaRight;
+ }
+ generateText(text, node, marker);
+ break;
+ default:
+ Generator::generateStatus(node, marker);
+ }
+}
+
+void HtmlGenerator::generateMacRef(const Node *node, CodeMarker *marker)
+{
+ if (!pleaseGenerateMacRef || marker == 0)
+ return;
+
+ QStringList macRefs = marker->macRefsForNode(node);
+ foreach (const QString &macRef, macRefs)
+ out() << "<a name=\"" << "//apple_ref/" << macRef << "\" />\n";
+}
+
+void HtmlGenerator::beginLink(const QString &link,
+ const Node *node,
+ const Node *relative,
+ CodeMarker *marker)
+{
+ Q_UNUSED(marker)
+ Q_UNUSED(relative)
+
+ this->link = link;
+ if (link.isEmpty()) {
+ if (showBrokenLinks)
+ out() << "<i>";
+ }
+ else if (node == 0 || (relative != 0 &&
+ node->status() == relative->status())) {
+ out() << "<a href=\"" << link << "\">";
+ }
+ else {
+ switch (node->status()) {
+ case Node::Obsolete:
+ out() << "<a href=\"" << link << "\" class=\"obsolete\">";
+ break;
+ case Node::Compat:
+ out() << "<a href=\"" << link << "\" class=\"compat\">";
+ break;
+ default:
+ out() << "<a href=\"" << link << "\">";
+ }
+ }
+ inLink = true;
+}
+
+void HtmlGenerator::endLink()
+{
+ if (inLink) {
+ if (link.isEmpty()) {
+ if (showBrokenLinks)
+ out() << "</i>";
+ }
+ else {
+ out() << "</a>";
+ }
+ }
+ inLink = false;
+}
+
+QT_END_NAMESPACE
diff --git a/tools/qdoc3/htmlgenerator.h b/tools/qdoc3/htmlgenerator.h
new file mode 100644
index 0000000..de64190
--- /dev/null
+++ b/tools/qdoc3/htmlgenerator.h
@@ -0,0 +1,253 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*
+ htmlgenerator.h
+*/
+
+#ifndef HTMLGENERATOR_H
+#define HTMLGENERATOR_H
+
+#include <qmap.h>
+#include <qregexp.h>
+
+#include "codemarker.h"
+#include "config.h"
+#include "dcfsection.h"
+#include "pagegenerator.h"
+
+QT_BEGIN_NAMESPACE
+
+#if 0
+struct NavigationBar
+{
+ SectionIterator prev;
+ SectionIterator current;
+ SectionIterator next;
+};
+#endif
+
+class HelpProjectWriter;
+
+class HtmlGenerator : public PageGenerator
+{
+ public:
+ HtmlGenerator();
+ ~HtmlGenerator();
+
+ virtual void initializeGenerator(const Config& config);
+ virtual void terminateGenerator();
+ virtual QString format();
+ virtual void generateTree(const Tree *tree, CodeMarker *marker);
+
+ static QString protect(const QString& string);
+ static QString cleanRef(const QString& ref);
+
+ protected:
+ virtual void startText(const Node *relative, CodeMarker *marker);
+ virtual int generateAtom(const Atom *atom,
+ const Node *relative,
+ CodeMarker *marker);
+ virtual void generateClassLikeNode(const InnerNode *inner, CodeMarker *marker);
+ virtual void generateFakeNode(const FakeNode *fake, CodeMarker *marker);
+ virtual QString fileExtension(const Node *node);
+ virtual QString refForNode(const Node *node);
+ virtual QString linkForNode(const Node *node, const Node *relative);
+ virtual QString refForAtom(Atom *atom, const Node *node);
+
+ private:
+ enum SubTitleSize { SmallSubTitle, LargeSubTitle };
+
+ const QPair<QString,QString> anchorForNode(const Node *node);
+ const Node *findNodeForTarget(const QString &target,
+ const Node *relative,
+ CodeMarker *marker,
+ const Atom *atom = 0);
+ void generateHeader(const QString& title, const Node *node = 0,
+ CodeMarker *marker = 0, bool mainPage = true);
+ void generateTitle(const QString& title,
+ const Text &subTitle,
+ SubTitleSize subTitleSize,
+ const Node *relative,
+ CodeMarker *marker);
+ void generateFooter(const Node *node = 0);
+ void generateBrief(const Node *node,
+ CodeMarker *marker,
+ const Node *relative = 0);
+ void generateIncludes(const InnerNode *inner, CodeMarker *marker);
+#if 0
+ void generateNavigationBar(const NavigationBar& bar,
+ const Node *node,
+ CodeMarker *marker);
+#endif
+ void generateTableOfContents(const Node *node,
+ CodeMarker *marker,
+ Doc::SectioningUnit sectioningUnit,
+ int numColumns,
+ const Node *relative = 0);
+ QString generateListOfAllMemberFile(const InnerNode *inner, CodeMarker *marker);
+ QString generateLowStatusMemberFile(const InnerNode *inner,
+ CodeMarker *marker,
+ CodeMarker::Status status);
+ void generateClassHierarchy(const Node *relative,
+ CodeMarker *marker,
+ const QMap<QString,const Node *> &classMap);
+ void generateAnnotatedList(const Node *relative,
+ CodeMarker *marker,
+ const QMap<QString, const Node *> &nodeMap);
+ void generateCompactList(const Node *relative,
+ CodeMarker *marker,
+ const QMap<QString, const Node *> &classMap);
+ void generateFunctionIndex(const Node *relative, CodeMarker *marker);
+ void generateLegaleseList(const Node *relative, CodeMarker *marker);
+ void generateOverviewList(const Node *relative, CodeMarker *marker);
+ void generateSynopsis(const Node *node,
+ const Node *relative,
+ CodeMarker *marker,
+ CodeMarker::SynopsisStyle style);
+ void generateSectionList(const Section& section,
+ const Node *relative,
+ CodeMarker *marker,
+ CodeMarker::SynopsisStyle style);
+ void generateSectionInheritedList(const Section& section,
+ const Node *relative,
+ CodeMarker *marker);
+ void generateFullName(const Node *apparentNode,
+ const Node *relative,
+ CodeMarker *marker,
+ const Node *actualNode = 0);
+ void generateDetailedMember(const Node *node, const InnerNode *relative, CodeMarker *marker);
+ void generateLink(const Atom *atom, const Node *relative, CodeMarker *marker);
+ void generateStatus(const Node *node, CodeMarker *marker);
+
+ QString registerRef(const QString& ref);
+ QString highlightedCode(const QString& markedCode, CodeMarker *marker, const Node *relative);
+ QString fileBase(const Node *node);
+#if 0
+ QString fileBase(const Node *node, const SectionIterator& section);
+#endif
+ QString fileName(const Node *node);
+ void findAllClasses(const InnerNode *node);
+ void findAllFunctions(const InnerNode *node);
+ void findAllLegaleseTexts(const InnerNode *node);
+ void findAllNamespaces(const InnerNode *node);
+#ifdef ZZZ_QDOC_QML
+ void findAllQmlClasses(const InnerNode *node);
+#endif
+ static int hOffset(const Node *node);
+ static bool isThreeColumnEnumValueTable(const Atom *atom);
+ virtual QString getLink(const Atom *atom,
+ const Node *relative,
+ CodeMarker *marker,
+ const Node *node = 0);
+ virtual void generateDcf(const QString &fileBase,
+ const QString &startPage,
+ const QString &title, DcfSection &dcfRoot);
+ virtual void generateIndex(const QString &fileBase,
+ const QString &url,
+ const QString &title);
+ void generateMacRef(const Node *node, CodeMarker *marker);
+ void beginLink(const QString &link,
+ const Node *node,
+ const Node *relative,
+ CodeMarker *marker);
+ void endLink();
+
+#if 0
+ NavigationBar currentNavigationBar;
+#endif
+ QMap<QString, QString> refMap;
+ int codeIndent;
+ DcfSection dcfClassesRoot;
+ DcfSection dcfOverviewsRoot;
+ DcfSection dcfExamplesRoot;
+ DcfSection dcfDesignerRoot;
+ DcfSection dcfLinguistRoot;
+ DcfSection dcfAssistantRoot;
+ DcfSection dcfQmakeRoot;
+ HelpProjectWriter *helpProjectWriter;
+ bool inLink;
+ bool inContents;
+ bool inSectionHeading;
+ bool inTableHeader;
+ int numTableRows;
+ bool threeColumnEnumValueTable;
+ QString link;
+ QStringList sectionNumber;
+ QRegExp funcLeftParen;
+ QString style;
+ QString postHeader;
+ QString footer;
+ QString address;
+ bool pleaseGenerateMacRef;
+ QString project;
+ QString projectDescription;
+ QString projectUrl;
+ QString navigationLinks;
+ QStringList stylesheets;
+ QStringList customHeadElements;
+ const Tree *tre;
+ bool slow;
+ QMap<QString, QMap<QString, const Node *> > moduleClassMap;
+ QMap<QString, QMap<QString, const Node *> > moduleNamespaceMap;
+ QMap<QString, const Node *> nonCompatClasses;
+ QMap<QString, const Node *> mainClasses;
+ QMap<QString, const Node *> compatClasses;
+ QMap<QString, const Node *> namespaceIndex;
+ QMap<QString, const Node *> serviceClasses;
+#ifdef QDOC_QML
+ QMap<QString, const Node *> qmlClasses;
+#endif
+ QMap<QString, QMap<QString, const Node *> > funcIndex;
+ QMap<Text, const Node *> legaleseTexts;
+};
+
+#define HTMLGENERATOR_ADDRESS "address"
+#define HTMLGENERATOR_FOOTER "footer"
+#define HTMLGENERATOR_GENERATEMACREFS "generatemacrefs" // ### document me
+#define HTMLGENERATOR_POSTHEADER "postheader"
+#define HTMLGENERATOR_STYLE "style"
+#define HTMLGENERATOR_STYLESHEETS "stylesheets"
+#define HTMLGENERATOR_CUSTOMHEADELEMENTS "customheadelements"
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/tools/qdoc3/jambiapiparser.cpp b/tools/qdoc3/jambiapiparser.cpp
new file mode 100644
index 0000000..73d10a6
--- /dev/null
+++ b/tools/qdoc3/jambiapiparser.cpp
@@ -0,0 +1,547 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*
+ jambiapiparser.cpp
+*/
+
+#include <QtXml>
+
+#include "cppcodeparser.h"
+#include "jambiapiparser.h"
+#include "node.h"
+#include "tree.h"
+
+QT_BEGIN_NAMESPACE
+
+static const char USED_INTERNALLY[] = "";
+
+static Text textWithFixedBrief(const Text &text, const Text &beforeBrief,
+ const Text &afterBrief)
+{
+ Text result;
+
+ const Atom *atom = text.firstAtom();
+ while (atom) {
+ if (atom->type() == Atom::BriefLeft) {
+ result << Atom::ParaLeft << beforeBrief;
+ } else if (atom->type() == Atom::BriefRight) {
+ result << afterBrief << Atom::ParaRight;
+ } else {
+ result << *atom;
+ }
+ atom = atom->next();
+ }
+
+ return result;
+}
+
+static void setPass1JambifiedDoc(Node *javaNode, const Node *cppNode, const QString &qName = "")
+{
+ Doc newDoc(cppNode->doc());
+
+ if (javaNode->type() == Node::Function) {
+ const FunctionNode *javaFunc = static_cast<const FunctionNode *>(javaNode);
+ if (cppNode->type() == Node::Function) {
+ const FunctionNode *cppFunc = static_cast<const FunctionNode *>(cppNode);
+ if (const PropertyNode *property = cppFunc->associatedProperty()) {
+ newDoc = property->doc();
+ Text text(newDoc.body());
+
+ Node *mutableCppNode = const_cast<Node *>(cppNode);
+ if (property->getters().contains(mutableCppNode)) {
+ text = textWithFixedBrief(text, Text("Returns "), Text("."));
+ } else if (property->setters().contains(mutableCppNode)) {
+ Text afterBrief;
+ if (javaFunc->parameterNames().count() == 1
+ && !javaFunc->parameterNames().first().isEmpty()) {
+ afterBrief << " to "
+ << Atom(Atom::FormattingLeft, ATOM_FORMATTING_PARAMETER)
+ << javaFunc->parameterNames().first()
+ << Atom(Atom::FormattingRight, ATOM_FORMATTING_PARAMETER);
+ }
+ afterBrief << ".";
+ text = textWithFixedBrief(text, Text("Sets "), afterBrief);
+ } else if (property->resetters().contains(mutableCppNode)) {
+ text = textWithFixedBrief(text, Text("Resets "), Text("."));
+ }
+
+ newDoc.setBody(text);
+ } else {
+ QStringList javaParams = javaFunc->parameterNames();
+ QStringList cppParams = cppFunc->parameterNames();
+ newDoc.renameParameters(cppParams, javaParams);
+
+ if (cppNode->access() == Node::Private) {
+ Text text;
+ text << Atom::ParaLeft;
+ if (cppFunc->reimplementedFrom()) {
+ text << "This function is reimplemented for internal reasons.";
+ } else {
+ text << USED_INTERNALLY;
+ }
+ text << Atom::ParaRight;
+ newDoc.setBody(text);
+ }
+ }
+ } else if (cppNode->type() == Node::Variable) {
+ Text text(newDoc.body());
+
+ if (qName == "variablegetter") {
+ text = textWithFixedBrief(text, Text("Returns "), Text("."));
+ } else if (qName == "variablesetter") {
+ Text afterBrief;
+ if (javaFunc->parameterNames().count() == 1
+ && !javaFunc->parameterNames().first().isEmpty()) {
+ afterBrief << " to "
+ << Atom(Atom::FormattingLeft, ATOM_FORMATTING_PARAMETER)
+ << javaFunc->parameterNames().first()
+ << Atom(Atom::FormattingRight, ATOM_FORMATTING_PARAMETER);
+ }
+ afterBrief << ".";
+ text = textWithFixedBrief(text, Text("Sets "), afterBrief);
+ }
+
+ newDoc.setBody(text);
+ }
+ } else { // ### enum value names?
+
+ }
+
+ javaNode->setDoc(newDoc, true);
+}
+
+static void setStatus(Node *javaNode, const Node *cppNode)
+{
+ if (cppNode->status() == Node::Compat) {
+ javaNode->setStatus(Node::Obsolete);
+ } else {
+ javaNode->setStatus(cppNode->status());
+ }
+}
+
+static Text findEnumText(Node *javaEnum, const QString &enumItemName)
+{
+ const Text &body = javaEnum->doc().body();
+ const Atom *atom = body.firstAtom();
+ while (atom) {
+ if (atom->type() == Atom::ListTagLeft && atom->string() == ATOM_LIST_VALUE) {
+ atom = atom->next();
+ if (atom) {
+ // ### paras?
+ if (atom->string() == enumItemName)
+ return body.subText(Atom::ListItemLeft, Atom::ListItemRight, atom);
+ }
+ } else {
+ atom = atom->next();
+ }
+ }
+ return Text();
+}
+
+JambiApiParser::JambiApiParser(Tree *cppTree)
+ : cppTre(cppTree), javaTre(0), metJapiTag(false)
+{
+}
+
+JambiApiParser::~JambiApiParser()
+{
+}
+
+void JambiApiParser::initializeParser(const Config &config)
+{
+ CodeParser::initializeParser(config);
+}
+
+void JambiApiParser::terminateParser()
+{
+ CodeParser::terminateParser();
+}
+
+QString JambiApiParser::language()
+{
+ return "Java";
+}
+
+QString JambiApiParser::sourceFileNameFilter()
+{
+ return "*.japi";
+}
+
+void JambiApiParser::parseSourceFile(const Location &location, const QString &filePath, Tree *tree)
+{
+ javaTre = tree;
+ metJapiTag = false;
+
+ QXmlSimpleReader reader;
+ reader.setContentHandler(this);
+ reader.setErrorHandler(this);
+
+ QFile file(filePath);
+ if (!file.open(QFile::ReadOnly)) {
+ location.warning(tr("Cannot open JAPI file '%1'").arg(filePath));
+ return;
+ }
+
+ japiLocation = Location(filePath);
+ QXmlInputSource xmlSource(&file);
+ reader.parse(xmlSource);
+}
+
+void JambiApiParser::doneParsingSourceFiles(Tree * /* tree */)
+{
+ /*
+ Also import the overview documents.
+ */
+ foreach (Node *cppNode, cppTre->root()->childNodes()) {
+ if (cppNode->type() == Node::Fake) {
+ FakeNode *cppFake = static_cast<FakeNode *>(cppNode);
+ if (cppFake->subType() == FakeNode::Page) {
+ FakeNode *javaFake = new FakeNode(javaTre->root(), cppFake->name(),
+ cppFake->subType());
+ javaFake->setModuleName("com.trolltech.qt"); // ### hard-coded
+ javaFake->setTitle(cppFake->title());
+ javaFake->setSubTitle(cppFake->subTitle());
+ setStatus(javaFake, cppFake);
+ setPass1JambifiedDoc(javaFake, cppFake);
+ }
+ }
+ }
+
+ /*
+ Fix the docs.
+ */
+ if (javaTre) {
+ javaTre->resolveInheritance();
+ jambifyDocsPass2(javaTre->root());
+ javaTre = 0;
+ }
+}
+
+bool JambiApiParser::startElement(const QString & /* namespaceURI */,
+ const QString & /* localName */,
+ const QString &qName,
+ const QXmlAttributes &attributes)
+{
+ if (!metJapiTag && qName != "japi") {
+ // ### The file is not a JAPI file.
+ return true;
+ }
+ metJapiTag = true;
+
+ EnumNode *javaEnum = 0;
+ EnumNode *cppEnum = 0;
+ InnerNode *javaParent = javaTre->root();
+ InnerNode *cppParent = cppTre->root();
+
+ for (int i = 0; i < classAndEnumStack.count(); ++i) {
+ const ClassOrEnumInfo &info = classAndEnumStack.at(i);
+ if (info.cppNode) {
+ if (info.cppNode->type() == Node::Enum) {
+ Q_ASSERT(info.javaNode->type() == Node::Enum);
+ javaEnum = static_cast<EnumNode *>(info.javaNode);
+ cppEnum = static_cast<EnumNode *>(info.cppNode);
+ } else {
+ Q_ASSERT(info.javaNode->type() == Node::Class
+ || info.javaNode->type() == Node::Namespace);
+ javaParent = static_cast<InnerNode *>(info.javaNode);
+ cppParent = static_cast<InnerNode *>(info.cppNode);
+ }
+ }
+ }
+
+ if (qName == "class" || qName == "enum") {
+ Node::Type type = (qName == "class") ? Node::Class : Node::Enum;
+
+ QString javaExtends = attributes.value("java-extends");
+ QString javaImplements = attributes.value("javaimplements");
+
+ ClassOrEnumInfo info;
+ info.tag = qName;
+ info.javaName = attributes.value("java");
+ info.cppName = attributes.value("cpp");
+ info.cppNode = cppTre->findNode(info.cppName.split("::"), type, cppParent);
+ if (!info.cppNode && type == Node::Class) {
+ type = Node::Namespace;
+ info.cppNode = cppTre->findNode(info.cppName.split("::"), type, cppParent);
+ }
+
+ if (!info.cppNode) {
+ japiLocation.warning(tr("Cannot find C++ class or enum '%1'").arg(info.cppName));
+ } else {
+ if (qName == "class") {
+ ClassNode *javaClass = new ClassNode(javaParent, info.javaName);
+ javaClass->setModuleName(attributes.value("package"));
+ if (!javaExtends.isEmpty())
+ javaTre->addBaseClass(javaClass, Node::Public, javaExtends.split('.'),
+ javaExtends);
+ if (!javaImplements.isEmpty())
+ javaTre->addBaseClass(javaClass, Node::Public, javaImplements.split('.'),
+ javaExtends);
+
+ info.javaNode = javaClass;
+ } else {
+ info.javaNode = new EnumNode(javaParent, info.javaName);
+ }
+ info.javaNode->setLocation(japiLocation);
+ setStatus(info.javaNode, info.cppNode);
+
+ setPass1JambifiedDoc(info.javaNode, info.cppNode);
+ }
+ classAndEnumStack.push(info);
+ } else if (qName == "method" || qName == "signal") {
+ QString javaSignature = attributes.value("java");
+ if (javaSignature.startsWith("private"))
+ return true;
+
+ QString cppSignature = attributes.value("cpp");
+
+ CppCodeParser cppParser;
+ const FunctionNode *cppNode = cppParser.findFunctionNode(cppSignature, cppTre,
+ cppParent,
+ true /* fuzzy */);
+ if (!cppNode) {
+ bool quiet = false;
+
+ /*
+ Default constructors sometimes don't exist in C++.
+ */
+ if (!quiet && javaSignature == "public " + javaParent->name() + "()")
+ quiet = true;
+
+ if (!quiet)
+ japiLocation.warning(tr("Cannot find C++ function '%1' ('%2')")
+ .arg(cppSignature).arg(cppParent->name()));
+ }
+
+ FunctionNode *javaNode;
+ if (makeFunctionNode(javaParent, javaSignature, &javaNode)) {
+ javaNode->setLocation(japiLocation);
+ if (qName == "signal")
+ javaNode->setMetaness(FunctionNode::Signal);
+
+ if (cppNode) {
+ setStatus(javaNode, cppNode);
+
+ int overloadNo = cppNode->parameters().count() - javaNode->parameters().count() + 1;
+ if (overloadNo == 1) {
+ setPass1JambifiedDoc(javaNode, cppNode);
+ } else {
+ Text text;
+
+ text << Atom::ParaLeft << "Equivalent to "
+ << Atom(Atom::Link, javaNode->name() + "()")
+ << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK)
+ << javaNode->name()
+ << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK)
+ << "(";
+
+ for (int i = 0; i < cppNode->parameters().count(); ++i) {
+ if (i > 0)
+ text << ", ";
+ if (i < javaNode->parameters().count()) {
+ text << Atom(Atom::FormattingLeft, ATOM_FORMATTING_PARAMETER)
+ << javaNode->parameters().at(i).name()
+ << Atom(Atom::FormattingRight, ATOM_FORMATTING_PARAMETER);
+ } else {
+ // ### convert to Java
+ text << cppNode->parameters().at(i).defaultValue();
+ }
+ }
+
+ text << ").";
+
+ Doc doc;
+ doc.setBody(text);
+ javaNode->setDoc(doc, true);
+ }
+ javaNode->setOverload(overloadNo > 1);
+ }
+ }
+ } else if (qName == "variablesetter" || qName == "variablegetter") {
+ QString javaSignature = attributes.value("java");
+ if (javaSignature.startsWith("private"))
+ return true;
+
+ QString cppVariable = attributes.value("cpp");
+
+ VariableNode *cppNode = static_cast<VariableNode *>(cppParent->findNode(cppVariable,
+ Node::Variable));
+ FunctionNode *javaNode;
+ if (makeFunctionNode(javaParent, javaSignature, &javaNode)) {
+ javaNode->setLocation(japiLocation);
+
+ if (!cppNode) {
+#if 0
+ japiLocation.warning(tr("Cannot find C++ variable '%1' ('%2')")
+ .arg(cppVariable).arg(cppParent->name()));
+#endif
+ javaNode->setDoc(Doc(japiLocation, japiLocation,
+ USED_INTERNALLY,
+ QSet<QString>()), true);
+ } else {
+ setPass1JambifiedDoc(javaNode, cppNode, qName);
+ setStatus(javaNode, cppNode);
+ }
+ }
+ } else if (qName == "enum-value") {
+ QString javaName = attributes.value("java");
+ QString cppName = attributes.value("cpp");
+ QString value = attributes.value("value");
+
+ if (javaEnum) {
+ EnumItem item(javaName, value, findEnumText(javaEnum, javaName));
+ javaEnum->addItem(item);
+ }
+ }
+
+ return true;
+}
+
+bool JambiApiParser::endElement(const QString & /* namespaceURI */,
+ const QString & /* localName */,
+ const QString &qName)
+{
+ if (qName == "class" || qName == "enum")
+ classAndEnumStack.pop();
+ return true;
+}
+
+bool JambiApiParser::fatalError(const QXmlParseException &exception)
+{
+ japiLocation.setLineNo(exception.lineNumber());
+ japiLocation.setColumnNo(exception.columnNumber());
+ japiLocation.warning(tr("Syntax error in JAPI file (%1)").arg(exception.message()));
+ return true;
+}
+
+void JambiApiParser::jambifyDocsPass2(Node *node)
+{
+ const Doc &doc = node->doc();
+ if (!doc.isEmpty()) {
+ if (node->type() == Node::Enum) {
+ Doc newDoc(doc);
+ newDoc.simplifyEnumDoc();
+ node->setDoc(newDoc, true);
+ }
+ }
+
+ if (node->isInnerNode()) {
+ InnerNode *innerNode = static_cast<InnerNode *>(node);
+ foreach (Node *child, innerNode->childNodes())
+ jambifyDocsPass2(child);
+ }
+}
+
+bool JambiApiParser::makeFunctionNode(InnerNode *parent, const QString &synopsis,
+ FunctionNode **funcPtr)
+{
+ Node::Access access = Node::Public;
+ FunctionNode::Metaness metaness = FunctionNode::Plain;
+ bool final = false;
+ bool statique = false;
+
+ QString mySynopsis = synopsis.simplified();
+ int oldLen;
+ do {
+ oldLen = mySynopsis.length();
+
+ if (mySynopsis.startsWith("public ")) {
+ mySynopsis.remove(0, 7);
+ access = Node::Public;
+ }
+ if (mySynopsis.startsWith("protected ")) {
+ mySynopsis.remove(0, 10);
+ access = Node::Protected;
+ }
+ if (mySynopsis.startsWith("private ")) {
+ mySynopsis.remove(0, 8);
+ access = Node::Private;
+ }
+ if (mySynopsis.startsWith("native ")) {
+ mySynopsis.remove(0, 7);
+ metaness = FunctionNode::Native;
+ }
+ if (mySynopsis.startsWith("final ")) {
+ mySynopsis.remove(0, 6);
+ final = true;
+ }
+ if (mySynopsis.startsWith("static ")) {
+ mySynopsis.remove(0, 7);
+ statique = true;
+ }
+ } while (oldLen != mySynopsis.length());
+
+ // method or constructor
+ QRegExp funcRegExp("(?:(.*) )?([A-Za-z_0-9]+)\\((.*)\\)");
+ if (!funcRegExp.exactMatch(mySynopsis))
+ return false;
+
+ QString retType = funcRegExp.cap(1);
+ QString funcName = funcRegExp.cap(2);
+ QStringList params = funcRegExp.cap(3).split(",");
+
+ FunctionNode *func = new FunctionNode(parent, funcName);
+ func->setReturnType(retType);
+ func->setAccess(access);
+ func->setStatic(statique);
+ func->setConst(final);
+ func->setMetaness(metaness);
+
+ QRegExp paramRegExp(" ?([^ ].*) ([A-Za-z_0-9]+) ?");
+
+ foreach (const QString &param, params) {
+ if (paramRegExp.exactMatch(param)) {
+ func->addParameter(Parameter(paramRegExp.cap(1), "", paramRegExp.cap(2)));
+ } else {
+ // problem
+ }
+ }
+
+ if (funcPtr) {
+ *funcPtr = func;
+ } else if (!parent) {
+ delete func;
+ }
+ return true;
+}
+
+QT_END_NAMESPACE
diff --git a/tools/qdoc3/jambiapiparser.h b/tools/qdoc3/jambiapiparser.h
new file mode 100644
index 0000000..e0dfe3d
--- /dev/null
+++ b/tools/qdoc3/jambiapiparser.h
@@ -0,0 +1,99 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*
+ jambiapiparser.h
+*/
+
+#ifndef JAMBIAPIPARSER_H
+#define JAMBIAPIPARSER_H
+
+#include <QStack>
+#include <QXmlDefaultHandler>
+
+#include "codeparser.h"
+
+QT_BEGIN_NAMESPACE
+
+struct ClassOrEnumInfo
+{
+ QString tag;
+ QString javaName;
+ QString cppName;
+ Node *javaNode;
+ Node *cppNode;
+
+ ClassOrEnumInfo() : javaNode(0), cppNode(0) {}
+};
+
+class JambiApiParser : public CodeParser, private QXmlDefaultHandler
+{
+public:
+ JambiApiParser(Tree *cppTree);
+ ~JambiApiParser();
+
+ void initializeParser(const Config &config);
+ void terminateParser();
+ QString language();
+ QString sourceFileNameFilter();
+ void parseSourceFile(const Location &location, const QString &filePath, Tree *tree);
+ virtual void doneParsingSourceFiles(Tree *tree);
+
+private:
+ bool startElement(const QString &namespaceURI, const QString &localName,
+ const QString &qName, const QXmlAttributes &attributes);
+ bool endElement(const QString &namespaceURI, const QString &localName,
+ const QString &qName);
+ bool fatalError(const QXmlParseException &exception);
+ void jambifyDocsPass2(Node *node);
+ bool makeFunctionNode(InnerNode *parent, const QString &synopsis, FunctionNode **funcPtr);
+
+ Tree *cppTre;
+ Tree *javaTre;
+
+ bool metJapiTag;
+ Location japiLocation;
+ QStack<ClassOrEnumInfo> classAndEnumStack;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/tools/qdoc3/javacodemarker.cpp b/tools/qdoc3/javacodemarker.cpp
new file mode 100644
index 0000000..813f505
--- /dev/null
+++ b/tools/qdoc3/javacodemarker.cpp
@@ -0,0 +1,201 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*
+ javacodemarker.cpp
+*/
+
+#include "javacodemarker.h"
+#include "node.h"
+#include "text.h"
+#include "tree.h"
+
+QT_BEGIN_NAMESPACE
+
+JavaCodeMarker::JavaCodeMarker()
+{
+}
+
+JavaCodeMarker::~JavaCodeMarker()
+{
+}
+
+bool JavaCodeMarker::recognizeCode( const QString& /* code */ )
+{
+ return true;
+}
+
+bool JavaCodeMarker::recognizeExtension( const QString& ext )
+{
+ return ext == "java";
+}
+
+bool JavaCodeMarker::recognizeLanguage( const QString& lang )
+{
+ return lang == "Java";
+}
+
+QString JavaCodeMarker::plainName( const Node *node )
+{
+ return node->name();
+}
+
+QString JavaCodeMarker::plainFullName( const Node *node, const Node * /* relative */ )
+{
+ if (!node)
+ return QString();
+
+ QString fullName;
+ for ( ;; ) {
+ fullName.prepend( plainName(node) );
+ if ( node->parent() && node->parent()->name().isEmpty() )
+ break;
+ node = node->parent();
+ if (!node)
+ break;
+ fullName.prepend(".");
+ }
+ return fullName;
+}
+
+QString JavaCodeMarker::markedUpCode( const QString& code,
+ const Node * /* relative */,
+ const QString& /* dirPath */ )
+{
+ return protect( code );
+}
+
+QString JavaCodeMarker::markedUpSynopsis(const Node * /* node */,
+ const Node * /* relative */,
+ SynopsisStyle /* style */)
+{
+ return QString();
+}
+
+QString JavaCodeMarker::markedUpName( const Node *node )
+{
+ return linkTag(node, taggedNode(node));
+}
+
+QString JavaCodeMarker::markedUpFullName(const Node *node, const Node * /* relative */ )
+{
+ QString fullName;
+ for ( ;; ) {
+ fullName.prepend( markedUpName(node) );
+ if ( node->parent()->name().isEmpty() )
+ break;
+ node = node->parent();
+ fullName.prepend( "." );
+ }
+ return fullName;
+}
+
+QString JavaCodeMarker::markedUpEnumValue(const QString &enumValue,
+ const Node * /* relative */)
+{
+ return protect(enumValue);
+}
+
+QString JavaCodeMarker::markedUpIncludes( const QStringList& /* includes */ )
+{
+ return QString();
+}
+
+QString JavaCodeMarker::functionBeginRegExp( const QString& /* funcName */)
+{
+ return "^x$"; // ### invalid regexp
+}
+
+QString JavaCodeMarker::functionEndRegExp( const QString& /* funcName */ )
+{
+ return "^}";
+}
+
+QList<Section> JavaCodeMarker::sections(const InnerNode * /* inner */, SynopsisStyle /* style */,
+ Status /* status */)
+{
+ return QList<Section>();
+}
+
+const Node *JavaCodeMarker::resolveTarget(const QString &target, const Tree *tree,
+ const Node *relative)
+{
+ if (target.endsWith("()")) {
+ const FunctionNode *func;
+ QString funcName = target;
+ funcName.chop(2);
+
+ QStringList path = funcName.split('.');
+ if ((func = tree->findFunctionNode(path, relative, Tree::SearchBaseClasses)))
+ return func;
+ } else if (target.contains("#")) {
+ int hashAt = target.indexOf("#");
+ QString link = target.left(hashAt);
+ QString ref = target.mid(hashAt + 1);
+ const Node *node;
+ if (link.isEmpty()) {
+ node = relative;
+ } else {
+ QStringList path(link);
+ node = tree->findNode(path, tree->root(), Tree::SearchBaseClasses);
+ }
+ if (node && node->isInnerNode()) {
+ const Atom *atom = node->doc().body().firstAtom();
+ while (atom) {
+ if (atom->type() == Atom::Target && atom->string() == ref) {
+ Node *parentNode = const_cast<Node *>(node);
+ return new TargetNode(static_cast<InnerNode*>(parentNode),
+ ref);
+ }
+ atom = atom->next();
+ }
+ }
+ } else {
+ QStringList path = target.split('.');
+ const Node *node;
+ if ((node = tree->findNode(path, relative,
+ Tree::SearchBaseClasses | Tree::SearchEnumValues)))
+ return node;
+ }
+ return 0;
+}
+
+QT_END_NAMESPACE
diff --git a/tools/qdoc3/javacodemarker.h b/tools/qdoc3/javacodemarker.h
new file mode 100644
index 0000000..3381fc3
--- /dev/null
+++ b/tools/qdoc3/javacodemarker.h
@@ -0,0 +1,80 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*
+ javacodemarker.h
+*/
+
+#ifndef JAVACODEMARKER_H
+#define JAVACODEMARKER_H
+
+#include "codemarker.h"
+
+QT_BEGIN_NAMESPACE
+
+class JavaCodeMarker : public CodeMarker
+{
+public:
+ JavaCodeMarker();
+ ~JavaCodeMarker();
+
+ bool recognizeCode( const QString& code );
+ bool recognizeExtension( const QString& ext );
+ bool recognizeLanguage( const QString& lang );
+ QString plainName(const Node *node);
+ QString plainFullName(const Node *node, const Node *relative);
+ QString markedUpCode( const QString& code, const Node *relative,
+ const QString& dirPath );
+ QString markedUpSynopsis( const Node *node, const Node *relative,
+ SynopsisStyle style );
+ QString markedUpName( const Node *node );
+ QString markedUpFullName( const Node *node, const Node *relative );
+ QString markedUpEnumValue(const QString &enumValue, const Node *relative);
+ QString markedUpIncludes( const QStringList& includes );
+ QList<Section> sections(const InnerNode *innerNode, SynopsisStyle style, Status status);
+ QString functionBeginRegExp( const QString& funcName );
+ QString functionEndRegExp( const QString& funcName );
+ const Node *resolveTarget( const QString& target, const Tree *tree, const Node *relative );
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/tools/qdoc3/javadocgenerator.cpp b/tools/qdoc3/javadocgenerator.cpp
new file mode 100644
index 0000000..872699d
--- /dev/null
+++ b/tools/qdoc3/javadocgenerator.cpp
@@ -0,0 +1,453 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "javadocgenerator.h"
+
+QT_BEGIN_NAMESPACE
+
+enum JavaSignatureSyntax {
+ GeneratedJdocFile,
+ JavadocRef,
+ SlotSignature
+};
+
+static QString javaSignature(const FunctionNode *func, JavaSignatureSyntax syntax,
+ int maxParams = 65535)
+{
+ maxParams = qMin(maxParams, func->parameters().count());
+
+ QString result;
+
+ if (syntax == GeneratedJdocFile) {
+ if (func->access() == Node::Public) {
+ result += "public ";
+ } else if (func->access() == Node::Protected) {
+ result += "protected ";
+ } else {
+ result += "private ";
+ }
+
+ if (func->metaness() == FunctionNode::Native)
+ result += "native ";
+
+ if (func->isConst())
+ result += "final ";
+
+ // ### func->metaness() == FunctionNode::Abstract
+
+ if (func->isStatic())
+ result += "static ";
+
+ if (!func->returnType().isEmpty()) {
+ result += func->returnType();
+ result += ' ';
+ }
+ }
+
+ if (syntax == SlotSignature) {
+ result += "void mySlot";
+ } else {
+ result += func->name();
+ }
+ result += '(';
+ for (int i = 0; i < maxParams; ++i) {
+ if (i != 0)
+ result += ", ";
+ result += func->parameters().at(i).leftType();
+ if (syntax != JavadocRef) {
+ result += ' ';
+ result += func->parameters().at(i).name();
+ }
+ }
+ result += ')';
+
+ return result;
+}
+
+static QString packageName(const Node *node)
+{
+ while (node && node->type() != Node::Class && node->type() != Node::Fake)
+ node = node->parent();
+ if (!node)
+ return QString();
+ return node->moduleName();
+}
+
+JavadocGenerator::JavadocGenerator()
+ : oldDevice(0), currentDepth(0)
+{
+}
+
+JavadocGenerator::~JavadocGenerator()
+{
+}
+
+void JavadocGenerator::initializeGenerator(const Config &config)
+{
+ HtmlGenerator::initializeGenerator(config);
+
+ formattingLeftMap().insert(ATOM_FORMATTING_PARAMETER,
+ formattingLeftMap().value(ATOM_FORMATTING_TELETYPE));
+ formattingRightMap().insert(ATOM_FORMATTING_PARAMETER,
+ formattingRightMap().value(ATOM_FORMATTING_TELETYPE));
+}
+
+void JavadocGenerator::terminateGenerator()
+{
+ HtmlGenerator::terminateGenerator();
+}
+
+QString JavadocGenerator::format()
+{
+ return "javadoc";
+}
+
+void JavadocGenerator::generateTree(const Tree *tree, CodeMarker *marker)
+{
+ HtmlGenerator::generateTree(tree, marker);
+}
+
+QString JavadocGenerator::fileExtension(const Node *node)
+{
+ if (node->type() == Node::Fake) {
+ return "html";
+ } else {
+ return "jdoc";
+ }
+}
+
+QString JavadocGenerator::typeString(const Node *node)
+{
+ if (node->type() == Node::Function) {
+ const FunctionNode *func = static_cast<const FunctionNode *>(node);
+ return func->metaness() == FunctionNode::Signal ? "signal" : "method";
+ } else {
+ return HtmlGenerator::typeString(node);
+ }
+}
+
+QString JavadocGenerator::imageFileName(const Node *relative, const QString& fileBase)
+{
+ QString result = HtmlGenerator::imageFileName(relative, fileBase);
+ if (!result.isEmpty()) {
+ QString package = packageName(relative);
+ int numSubPackages = package.count('.') - 2;
+ while (numSubPackages > 0) {
+ result.prepend("%2E%2E/"); // javadoc 1.5.0_06 chokes on '../'
+ --numSubPackages;
+ }
+ }
+ return result;
+}
+
+static int textDepth = 0;
+
+void JavadocGenerator::startText(const Node *relative, CodeMarker *marker)
+{
+ if (textDepth++ == 0 && relative->type() != Node::Fake) {
+ Q_ASSERT(!oldDevice);
+ oldDevice = out().device();
+ Q_ASSERT(oldDevice);
+ out().setString(&buffer);
+ }
+ HtmlGenerator::startText(relative, marker);
+}
+
+void JavadocGenerator::endText(const Node *relative, CodeMarker *marker)
+{
+ HtmlGenerator::endText(relative, marker);
+ if (--textDepth == 0 && relative->type() != Node::Fake) {
+ Q_ASSERT(oldDevice);
+ out().setDevice(oldDevice);
+ oldDevice = 0;
+
+ /*
+ Need to escape XML metacharacters in .jdoc files.
+ */
+ buffer.replace("*/", "*&lt;!-- noop --&gt;/");
+ buffer.replace("&", "&amp;");
+ buffer.replace("\"", "&quot;");
+ buffer.replace("<", "&lt;");
+ buffer.replace(">", "&gt;");
+ out() << buffer;
+ buffer.clear();
+ }
+}
+
+int JavadocGenerator::generateAtom(const Atom *atom, const Node *relative, CodeMarker *marker)
+{
+ return HtmlGenerator::generateAtom(atom, relative, marker);
+}
+
+void JavadocGenerator::generateClassLikeNode(const InnerNode *inner, CodeMarker *marker)
+{
+ generateIndent();
+ out() << "<class name=\"" << protect(inner->name()) << "\"";
+ generateDoc(inner, marker);
+ out() << ">\n";
+
+ ++currentDepth;
+ foreach (Node *node, inner->childNodes()) {
+ if (node->isInnerNode()) {
+ generateClassLikeNode(static_cast<InnerNode *>(node), marker);
+ } else {
+ if (node->type() == Node::Enum) {
+ EnumNode *enume = static_cast<EnumNode *>(node);
+
+ generateIndent();
+ out() << "<enum name=\"" << protect(node->name()) << "\"";
+ generateDoc(node, marker);
+ out() << ">\n";
+
+ ++currentDepth;
+ const QList<EnumItem> &items = enume->items();
+ for (int i = 0; i < items.count(); ++i) {
+ const EnumItem &item = items.at(i);
+ generateIndent();
+ out() << "<enum-value name=\"" << protect(item.name()) << "\"";
+ generateEnumItemDoc(item.text(), enume, marker);
+ out() << "/>\n";
+ }
+ --currentDepth;
+
+ out() << "</enum>\n";
+ } else if (node->type() == Node::Function) {
+ FunctionNode *func = static_cast<FunctionNode *>(node);
+ generateIndent();
+ out() << (func->metaness() == FunctionNode::Signal ? "<signal" : "<method")
+ << " name=\""
+ << protect(javaSignature(func, GeneratedJdocFile))
+ << "\"";
+ generateDoc(node, marker);
+ out() << "/>\n";
+ }
+ }
+ }
+ --currentDepth;
+
+ generateIndent();
+ out() << "</class>\n";
+}
+
+void JavadocGenerator::generateFakeNode(const FakeNode *fake, CodeMarker *marker)
+{
+ HtmlGenerator::generateFakeNode(fake, marker);
+}
+
+void JavadocGenerator::generateText(const Text& text, const Node *relative, CodeMarker *marker)
+{
+ HtmlGenerator::generateText(text, relative, marker);
+}
+
+void JavadocGenerator::generateBody(const Node *node, CodeMarker *marker)
+{
+ generateText(node->doc().body(), node, marker);
+}
+
+void JavadocGenerator::generateAlsoList( const Node *node, CodeMarker *marker )
+{
+ QList<Text> alsoList = node->doc().alsoList();
+ supplementAlsoList(node, alsoList);
+
+ if (node->type() == Node::Fake
+ || (node->type() == Node::Function
+ && static_cast<const FunctionNode *>(node)->metaness() == FunctionNode::Signal)) {
+ Text text;
+
+ if (!alsoList.isEmpty()) {
+ text << Atom(Atom::ListLeft, ATOM_LIST_TAG)
+ << Atom(Atom::ListTagLeft, ATOM_LIST_TAG)
+ << Atom(Atom::FormattingLeft, ATOM_FORMATTING_BOLD)
+ << "See Also:"
+ << Atom(Atom::FormattingRight, ATOM_FORMATTING_BOLD)
+ << Atom(Atom::ListTagRight, ATOM_LIST_TAG)
+ << Atom(Atom::ListItemLeft, ATOM_LIST_TAG);
+
+ for (int i = 0; i < alsoList.count(); ++i) {
+ if (i != 0)
+ text << ", ";
+ text << alsoList.at(i);
+ }
+ text << Atom(Atom::ListItemRight, ATOM_LIST_TAG)
+ << Atom(Atom::ListRight, ATOM_LIST_TAG);
+ }
+
+ generateText(text, node, marker);
+ } else {
+ foreach (const Text &text, alsoList) {
+ out() << "\n@see ";
+ generateText(text, node, marker);
+ }
+ }
+}
+
+QString JavadocGenerator::refForNode( const Node *node )
+{
+ if (node->type() == Node::Function)
+ return javaSignature(static_cast<const FunctionNode *>(node), JavadocRef);
+
+ return HtmlGenerator::refForNode(node);
+}
+
+QString JavadocGenerator::linkForNode( const Node *node, const Node *relative )
+{
+ // ### EVIL, relative should never be null
+ if (!relative)
+ relative = node;
+
+ if (packageName(node).isEmpty()) {
+ // ### jasmin: Fixme
+ return QString();
+ }
+
+ QString result;
+ if (node->type() == Node::Fake) {
+ result = node->name();
+ } else {
+ if (!node->isInnerNode()) {
+ result = linkForNode(node->parent(), relative) + "#" + refForNode(node);
+ } else {
+ result = node->name() + ".html";
+ }
+ }
+
+ QStringList nodePackage = packageName(node).split(".");
+ QStringList relativePackage = packageName(relative).split(".");
+ if (nodePackage == QStringList(QString()) || relativePackage == QStringList(QString())) {
+ qWarning("I'm in trouble [%s][%s]", qPrintable(node->name()), qPrintable(relative->name()));
+ return QString();
+ }
+
+ int i = nodePackage.count() - 1;
+ while (nodePackage.value(i) != relativePackage.value(i)) {
+ result.prepend(nodePackage.at(i) + "/");
+ --i;
+ }
+
+ ++i;
+ while (i < relativePackage.count()) {
+ result.prepend("%2E%2E/"); // javadoc 1.5.0_06 chokes on '../'
+ ++i;
+ }
+
+ return result;
+}
+
+QString JavadocGenerator::refForAtom(Atom *atom, const Node *node)
+{
+ return HtmlGenerator::refForAtom(atom, node);
+}
+
+/*
+ Neutralize dumb functions called from HtmlGenerator.
+*/
+void JavadocGenerator::generateDcf(const QString & /* fileBase */, const QString & /* startPage */,
+ const QString & /* title */, DcfSection & /* dcfRoot */)
+{
+}
+
+void JavadocGenerator::generateIndex(const QString & /* fileBase */, const QString & /* url */,
+ const QString & /* title */)
+{
+}
+
+void JavadocGenerator::generateIndent()
+{
+ for (int i = 0; i < currentDepth; ++i)
+ out() << " ";
+}
+
+void JavadocGenerator::generateDoc(const Node *node, CodeMarker *marker)
+{
+ const Text &text = node->doc().body();
+ if (!text.isEmpty()) {
+ out() << " doc=\"/**\n";
+ Generator::generateStatus(node, marker);
+ generateText(text, node, marker);
+ if (node && node->type() == Node::Function) {
+ const FunctionNode *func = static_cast<const FunctionNode *>(node);
+ if (func->metaness() == FunctionNode::Signal) {
+ QStringList slotSignatures;
+ for (int i = func->parameters().count(); i >= 0; --i)
+ slotSignatures += javaSignature(func, SlotSignature, i);
+
+ Text text;
+
+ text << Atom(Atom::ListLeft, ATOM_LIST_TAG)
+ << Atom(Atom::ListTagLeft, ATOM_LIST_TAG)
+ << Atom(Atom::FormattingLeft, ATOM_FORMATTING_BOLD);
+
+ if (slotSignatures.count() == 1) {
+ text << "Compatible Slot Signature:";
+ } else {
+ text << "Compatible Slot Signatures:";
+ }
+
+ text << Atom(Atom::FormattingRight, ATOM_FORMATTING_BOLD)
+ << Atom(Atom::ListTagRight, ATOM_LIST_TAG);
+
+ for (int i = 0; i < slotSignatures.count(); ++i) {
+ text << Atom(Atom::ListItemLeft, ATOM_LIST_TAG)
+ << Atom(Atom::C, marker->markedUpCode(slotSignatures.at(i), 0, ""))
+ << Atom(Atom::ListItemRight, ATOM_LIST_TAG);
+ }
+ text << Atom(Atom::ListRight, ATOM_LIST_TAG);
+ generateText(text, node, marker);
+ }
+ }
+ if (node)
+ generateAlsoList(node, marker);
+ out() << " */\"";
+ }
+}
+
+void JavadocGenerator::generateEnumItemDoc(const Text &text, const Node *node, CodeMarker *marker)
+{
+ out() << " doc=\"/**\n";
+ if (text.isEmpty()) {
+ out() << "Internal.";
+ } else {
+ generateText(text, node, marker);
+ }
+ out() << " */\"";
+}
+
+QT_END_NAMESPACE
diff --git a/tools/qdoc3/javadocgenerator.h b/tools/qdoc3/javadocgenerator.h
new file mode 100644
index 0000000..aac938e
--- /dev/null
+++ b/tools/qdoc3/javadocgenerator.h
@@ -0,0 +1,95 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef JAVADOCGENERATOR_H
+#define JAVADOCGENERATOR_H
+
+#include "htmlgenerator.h"
+
+QT_BEGIN_NAMESPACE
+
+class JavadocGenerator : public HtmlGenerator
+{
+public:
+ JavadocGenerator();
+ ~JavadocGenerator();
+
+ void initializeGenerator(const Config &config);
+ void terminateGenerator();
+ QString format();
+ bool canHandleFormat(const QString &format) { return format == "HTML" || format == "javadoc"; }
+ void generateTree(const Tree *tree, CodeMarker *marker);
+ QString typeString(const Node *node);
+ QString imageFileName(const Node *relative, const QString &fileBase);
+
+protected:
+ QString fileExtension(const Node *node);
+ void startText( const Node *relative, CodeMarker *marker );
+ void endText( const Node *relative, CodeMarker *marker );
+ int generateAtom( const Atom *atom, const Node *relative, CodeMarker *marker );
+ void generateClassLikeNode(const InnerNode *inner, CodeMarker *marker);
+ void generateFakeNode( const FakeNode *fake, CodeMarker *marker );
+
+ void generateText( const Text& text, const Node *relative, CodeMarker *marker );
+ void generateBody( const Node *node, CodeMarker *marker );
+ void generateAlsoList( const Node *node, CodeMarker *marker );
+
+ QString refForNode( const Node *node );
+ QString linkForNode( const Node *node, const Node *relative );
+ QString refForAtom(Atom *atom, const Node *node);
+
+private:
+ void generateDcf(const QString &fileBase, const QString &startPage,
+ const QString &title, DcfSection &dcfRoot);
+ void generateIndex(const QString &fileBase, const QString &url,
+ const QString &title);
+ void generateIndent();
+ void generateDoc(const Node *node, CodeMarker *marker);
+ void generateEnumItemDoc(const Text &text, const Node *node, CodeMarker *marker);
+
+ QString buffer;
+ QIODevice *oldDevice;
+ int currentDepth;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/tools/qdoc3/linguistgenerator.cpp b/tools/qdoc3/linguistgenerator.cpp
new file mode 100644
index 0000000..1350654
--- /dev/null
+++ b/tools/qdoc3/linguistgenerator.cpp
@@ -0,0 +1,245 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*
+ linguistgenerator.cpp
+*/
+
+#include "codemarker.h"
+#include "pagegenerator.h"
+#include "linguistgenerator.h"
+#include "node.h"
+#include "separator.h"
+#include "tree.h"
+#include <ctype.h>
+
+#include <qlist.h>
+#include <qiterator.h>
+
+QT_BEGIN_NAMESPACE
+
+#define COMMAND_VERSION Doc::alias("version")
+
+LinguistGenerator::LinguistGenerator()
+ : PageGenerator()
+{
+}
+
+LinguistGenerator::~LinguistGenerator()
+{
+}
+
+void LinguistGenerator::initializeGenerator(const Config &config)
+{
+ Generator::initializeGenerator(config);
+}
+
+void LinguistGenerator::terminateGenerator()
+{
+ PageGenerator::terminateGenerator();
+}
+
+QString LinguistGenerator::format()
+{
+ return "Linguist";
+}
+
+QString LinguistGenerator::fileExtension(const Node * /* node */)
+{
+ return "ts";
+}
+
+void LinguistGenerator::generateClassLikeNode(const InnerNode *inner, CodeMarker *marker)
+{
+ out().setCodec("utf-8");
+
+ QDomDocument document("TS");
+ QDomElement documentElement = document.createElement("TS");
+ documentElement.setAttribute("version", "1.1");
+
+ QList<QDomElement> contextElements = generateIndexSections(document, inner, marker);
+ foreach (const QDomElement &element, contextElements)
+ documentElement.appendChild(element);
+
+ QDomProcessingInstruction process = document.createProcessingInstruction(
+ "xml", QString("version=\"1.0\" encoding=\"%1\"").arg("utf-8"));
+ document.appendChild(process);
+ document.appendChild(documentElement);
+
+ out() << document;
+ out().flush();
+}
+
+void LinguistGenerator::generateFakeNode( const FakeNode *fake, CodeMarker *marker )
+{
+ out().setCodec("utf-8");
+
+ QDomDocument document("TS");
+ QDomElement documentElement = document.createElement("TS");
+ documentElement.setAttribute("version", "1.1");
+
+ QList<QDomElement> contextElements = generateIndexSections(document, fake, marker);
+ foreach (const QDomElement &element, contextElements)
+ documentElement.appendChild(element);
+
+ QDomProcessingInstruction process = document.createProcessingInstruction(
+ "xml", QString("version=\"1.0\" encoding=\"%1\"").arg("utf-8"));
+ document.appendChild(process);
+ document.appendChild(documentElement);
+
+ out() << document;
+ out().flush();
+}
+
+QList<QDomElement> LinguistGenerator::generateIndexSections(
+ QDomDocument &document, const Node *node, CodeMarker *marker)
+{
+ QList<QDomElement> contexts;
+
+ if (node->isInnerNode()) {
+ const InnerNode *inner = static_cast<const InnerNode *>(node);
+
+ foreach (const Node *child, inner->childNodes()) {
+ // Recurse to generate a DOM element for this child node and all
+ // its children.
+ contexts += generateIndexSections(document, child, marker);
+ }
+/*
+ foreach (const Node *child, inner->relatedNodes()) {
+ QDomElement childElement = generateIndexSections(document, child, marker);
+ element.appendChild(childElement);
+ }
+*/
+ }
+
+ // Add documentation to this node if it exists.
+ if (!node->doc().isEmpty()) {
+
+ QString nodeName = fullName(node);
+ QString signature;
+
+ if (node->type() == Node::Function) {
+ QStringList pieces;
+ const FunctionNode *functionNode = static_cast<const FunctionNode*>(node);
+ foreach (const Parameter &parameter, functionNode->parameters()) {
+ QString typeString = parameter.leftType() + parameter.rightType();
+ if (typeString.split(" ").size() > 1)
+ pieces.append(typeString + parameter.name());
+ else
+ pieces.append(typeString + " " + parameter.name());
+ }
+ signature = "(" + pieces.join(", ") + ")";
+ }
+
+ QDomElement contextElement = document.createElement("context");
+ QDomElement nameElement = document.createElement("name");
+ nameElement.appendChild(document.createTextNode(nodeName + signature));
+ contextElement.appendChild(nameElement);
+
+ QDomElement messageElement = document.createElement("message");
+ contextElement.appendChild(messageElement);
+
+ QDomElement sourceElement = document.createElement("source");
+ QString sourceText = simplified(node->doc().source());
+ if (!signature.isEmpty() && signature != "()" && !sourceText.contains("\\fn"))
+ sourceText.prepend(QString("\\fn %1%2\n").arg(nodeName).arg(signature));
+ sourceElement.appendChild(document.createTextNode(sourceText));
+ messageElement.appendChild(sourceElement);
+
+ QDomElement translationElement = document.createElement("translation");
+ translationElement.setAttribute("type", "unfinished");
+ messageElement.appendChild(translationElement);
+
+ QDomElement locationElement = document.createElement("location");
+ locationElement.setAttribute("filename", node->doc().location().filePath());
+ locationElement.setAttribute("line", node->doc().location().lineNo());
+ messageElement.appendChild(locationElement);
+
+ contexts.append(contextElement);
+ }
+
+ return contexts;
+}
+
+QString LinguistGenerator::fullName(const Node *node) const
+{
+ if (!node)
+ return "";
+ else if (node->parent() && !node->parent()->name().isEmpty())
+ return fullName(node->parent()) + "::" + node->name();
+ else
+ return node->name();
+}
+
+QString LinguistGenerator::simplified(const QString &text) const
+{
+ QStringList lines = text.split("\n");
+
+ while (lines.size() > 0 && lines.first().trimmed().isEmpty())
+ lines.pop_front();
+
+ while (lines.size() > 0 && lines.last().trimmed().isEmpty())
+ lines.pop_back();
+
+ int min = 0;
+ bool set = false;
+ foreach (const QString &line, lines) {
+ int j = 0;
+ while (j < line.length()) {
+ if (line[j] != ' ')
+ break;
+ ++j;
+ }
+ if (j < line.length()) {
+ if (!set) {
+ min = j;
+ set = true;
+ } else
+ min = qMin(min, j);
+ }
+ }
+ for (int i = 0; i < lines.size(); ++i)
+ lines[i] = lines[i].mid(min);
+
+ return lines.join("\n");
+}
+
+QT_END_NAMESPACE
diff --git a/tools/qdoc3/linguistgenerator.h b/tools/qdoc3/linguistgenerator.h
new file mode 100644
index 0000000..cae7e33
--- /dev/null
+++ b/tools/qdoc3/linguistgenerator.h
@@ -0,0 +1,85 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*
+ LinguistGenerator.h
+*/
+
+#ifndef LINGUISTGENERATOR_H
+#define LINGUISTGENERATOR_H
+
+#include <qmap.h>
+#include <qregexp.h>
+#include <qdom.h>
+
+#include "codemarker.h"
+#include "config.h"
+#include "pagegenerator.h"
+
+QT_BEGIN_NAMESPACE
+
+class LinguistGenerator : public PageGenerator
+{
+public:
+ LinguistGenerator();
+ ~LinguistGenerator();
+
+ virtual void initializeGenerator( const Config& config );
+ virtual void terminateGenerator();
+ virtual QString format();
+
+protected:
+ virtual void generateClassLikeNode(const InnerNode *inner,
+ CodeMarker *marker);
+ virtual void generateFakeNode( const FakeNode *fake, CodeMarker *marker );
+ virtual QString fileExtension(const Node *node);
+
+ QList<QDomElement> generateIndexSections(QDomDocument &document,
+ const Node *node, CodeMarker *marker);
+ virtual QString fullName(const Node *node) const;
+
+private:
+ QString simplified(const QString &text) const;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/tools/qdoc3/location.cpp b/tools/qdoc3/location.cpp
new file mode 100644
index 0000000..62a18f0
--- /dev/null
+++ b/tools/qdoc3/location.cpp
@@ -0,0 +1,401 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtDebug>
+#include "config.h"
+#include "location.h"
+
+#include <qregexp.h>
+#include <qtranslator.h>
+#include <stdlib.h>
+#include <limits.h>
+
+#include <stdio.h>
+
+QT_BEGIN_NAMESPACE
+
+QT_STATIC_CONST_IMPL Location Location::null;
+
+int Location::tabSize;
+QString Location::programName;
+QRegExp *Location::spuriousRegExp = 0;
+
+/*!
+ \class Location
+
+ \brief The Location class keeps track of where we are in a file.
+
+ It maintains a stack of file positions. A file position
+ consists of the file path, line number, and column number.
+ The location is used for printing error messages that are
+ tied to a location in a file.
+ */
+
+/*!
+ Constructs an empty location.
+ */
+Location::Location()
+ : stk(0), stkTop(&stkBottom), stkDepth(0), etcetera(false)
+{
+ // nothing.
+}
+
+/*!
+ Constructs a location with (fileName, 1, 1) on its file
+ position stack.
+ */
+Location::Location(const QString& fileName)
+ : stk(0), stkTop(&stkBottom), stkDepth(0), etcetera(false)
+{
+ push(fileName);
+}
+
+/*!
+ The copy constructor copies the contents of \a other into
+ this Location using the assignment operator.
+ */
+Location::Location(const Location& other)
+ : stk(0), stkTop(&stkBottom), stkDepth(0), etcetera(false)
+{
+ *this = other;
+}
+
+/*!
+ The assignment operator does a deep copy of the entire
+ state of \a other into this Location.
+ */
+Location& Location::operator=(const Location& other)
+{
+ QStack<StackEntry> *oldStk = stk;
+
+ stkBottom = other.stkBottom;
+ if (other.stk == 0) {
+ stk = 0;
+ stkTop = &stkBottom;
+ }
+ else {
+ stk = new QStack<StackEntry>(*other.stk);
+ stkTop = &stk->top();
+ }
+ stkDepth = other.stkDepth;
+ etcetera = other.etcetera;
+ delete oldStk;
+ return *this;
+}
+
+/*!
+ If the file position on top of the stack has a line number
+ less than 1, set its line number to 1 and its column number
+ to 1. Otherwise, do nothing.
+ */
+void Location::start()
+{
+ if (stkTop->lineNo < 1) {
+ stkTop->lineNo = 1;
+ stkTop->columnNo = 1;
+ }
+}
+
+/*!
+ Advance the current file position, using \a ch to decide how to do
+ that. If \a ch is a \c{'\\n'}, increment the current line number and
+ set the column number to 1. If \ch is a \c{'\\t'}, increment to the
+ next tab column. Otherwise, increment the column number by 1.
+
+ The current file position is the one on top of the position stack.
+ */
+void Location::advance(QChar ch)
+{
+ if (ch == QLatin1Char('\n')) {
+ stkTop->lineNo++;
+ stkTop->columnNo = 1;
+ }
+ else if (ch == QLatin1Char('\t')) {
+ stkTop->columnNo =
+ 1 + tabSize * (stkTop->columnNo + tabSize-1) / tabSize;
+ }
+ else {
+ stkTop->columnNo++;
+ }
+}
+
+/*!
+ Pushes \a filePath onto the file position stack. The current
+ file position becomes (\a filePath, 1, 1).
+
+ \sa pop()
+*/
+void Location::push(const QString& filePath)
+{
+ if (stkDepth++ >= 1) {
+ if (stk == 0)
+ stk = new QStack<StackEntry>;
+ stk->push(StackEntry());
+ stkTop = &stk->top();
+ }
+
+ stkTop->filePath = filePath;
+ stkTop->lineNo = INT_MIN;
+ stkTop->columnNo = 1;
+}
+
+/*!
+ Pops the top of the internal stack. The current file position
+ becomes the next one in the new top of stack.
+
+ \sa push()
+*/
+void Location::pop()
+{
+ if (--stkDepth == 0) {
+ stkBottom = StackEntry();
+ }
+ else {
+ stk->pop();
+ if (stk->isEmpty()) {
+ delete stk;
+ stk = 0;
+ stkTop = &stkBottom;
+ }
+ else {
+ stkTop = &stk->top();
+ }
+ }
+}
+
+/*! \fn bool Location::isEmpty() const
+
+ Returns true if there is no file name set yet; returns false
+ otherwise. The functions filePath(), lineNo() and columnNo()
+ must not be called on an empty Location object.
+ */
+
+/*! \fn const QString& Location::filePath() const
+ Returns the current path and file name.
+ Must not be called on an empty Location object.
+
+ \sa lineNo(), columnNo()
+ */
+
+/*!
+ Returns the file name part of the file path, ie the
+ current file. Must not be called on an empty Location
+ object.
+ */
+QString Location::fileName() const
+{
+ QString fp = filePath();
+ return fp.mid(fp.lastIndexOf('/') + 1);
+}
+
+/*! \fn int Location::lineNo() const
+ Returns the current line number.
+ Must not be called on an empty Location object.
+
+ \sa filePath(), columnNo()
+*/
+
+/*! \fn int Location::columnNo() const
+ Returns the current column number.
+ Must not be called on an empty Location object.
+
+ \sa filePath(), lineNo()
+*/
+
+/*!
+ Writes \a message and \a detals to stderr as a formatted
+ warning message.
+ */
+void Location::warning(const QString& message, const QString& details) const
+{
+ emitMessage(Warning, message, details);
+}
+
+/*!
+ Writes \a message and \a detals to stderr as a formatted
+ error message.
+ */
+void Location::error(const QString& message, const QString& details) const
+{
+ emitMessage(Error, message, details);
+}
+
+/*!
+ Writes \a message and \a detals to stderr as a formatted
+ error message and then exits the program.
+ */
+void Location::fatal(const QString& message, const QString& details) const
+{
+ emitMessage(Error, message, details);
+ information("Aborting");
+ exit(EXIT_FAILURE);
+}
+
+/*!
+ Gets several parameters from the \a config, including
+ tab size, program name, and a regular expression that
+ appears to be used for matching certain error messages
+ so that emitMessage() can avoid printing them.
+ */
+void Location::initialize(const Config& config)
+{
+ tabSize = config.getInt(CONFIG_TABSIZE);
+ programName = config.programName();
+
+ QRegExp regExp = config.getRegExp(CONFIG_SPURIOUS);
+ if (regExp.isValid()) {
+ spuriousRegExp = new QRegExp(regExp);
+ }
+ else {
+ config.lastLocation().warning(tr("Invalid regular expression '%1'")
+ .arg(regExp.pattern()));
+ }
+}
+
+/*!
+ Apparently, all this does is delete the regular expression
+ used for intercepting certain error messages that should
+ not be emitted by emitMessage().
+ */
+void Location::terminate()
+{
+ delete spuriousRegExp;
+ spuriousRegExp = 0;
+}
+
+/*!
+ Prints \a message to \c stdout followed by a \c{'\n'}.
+ */
+void Location::information(const QString& message)
+{
+ printf("%s\n", message.toLatin1().data());
+ fflush(stdout);
+}
+
+/*!
+ Report a program bug, including the \a hint.
+ */
+void Location::internalError(const QString& hint)
+{
+ Location::null.fatal(tr("Internal error (%1)").arg(hint),
+ tr("There is a bug in %1. Seek advice from your local"
+ " %2 guru.")
+ .arg(programName).arg(programName));
+}
+
+/*!
+ Formats \a message and \a details into a single string
+ and outputs that string to \c stderr. \a type specifies
+ whether the \a message is an error or a warning.
+ */
+void Location::emitMessage(MessageType type,
+ const QString& message,
+ const QString& details) const
+{
+ if (type == Warning &&
+ spuriousRegExp != 0 &&
+ spuriousRegExp->exactMatch(message))
+ return;
+
+ QString result = message;
+ if (!details.isEmpty())
+ result += "\n[" + details + "]";
+ result.replace("\n", "\n ");
+ if (type == Error)
+ result.prepend(tr("error: "));
+ result.prepend(toString());
+ fprintf(stderr, "%s\n", result.toLatin1().data());
+ fflush(stderr);
+}
+
+/*!
+ Converts the location to a string to be prepended to error
+ messages.
+ */
+QString Location::toString() const
+{
+ QString str;
+
+ if (isEmpty()) {
+ str = programName;
+ }
+ else {
+ Location loc2 = *this;
+ loc2.setEtc(false);
+ loc2.pop();
+ if (!loc2.isEmpty()) {
+ QString blah = tr("In file included from ");
+ for (;;) {
+ str += blah;
+ str += loc2.top();
+ loc2.pop();
+ if (loc2.isEmpty())
+ break;
+ str += tr(",");
+ str += QLatin1Char('\n');
+ blah.fill(' ');
+ }
+ str += tr(":");
+ str += QLatin1Char('\n');
+ }
+ str += top();
+ }
+ str += QLatin1String(": ");
+ return str;
+}
+
+QString Location::top() const
+{
+ QString str = filePath();
+ if (lineNo() >= 1) {
+ str += QLatin1Char(':');
+ str += QString::number(lineNo());
+#if 0
+ if (columnNo() >= 1)
+ str += ":" + QString::number(columnNo());
+#endif
+ }
+ if (etc())
+ str += QLatin1String(" (etc.)");
+ return str;
+}
+
+QT_END_NAMESPACE
diff --git a/tools/qdoc3/location.h b/tools/qdoc3/location.h
new file mode 100644
index 0000000..d8f3106
--- /dev/null
+++ b/tools/qdoc3/location.h
@@ -0,0 +1,131 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*
+ location.h
+*/
+
+#ifndef LOCATION_H
+#define LOCATION_H
+
+#include <qstack.h>
+
+#include "tr.h"
+
+#define QDOC_QML
+
+QT_BEGIN_NAMESPACE
+
+class Config;
+class QRegExp;
+
+class Location
+{
+ public:
+ Location();
+ Location(const QString& filePath);
+ Location(const Location& other);
+ ~Location() { delete stk; }
+
+ Location& operator=(const Location& other);
+
+ void start();
+ void advance(QChar ch);
+ void advanceLines(int n) { stkTop->lineNo += n; stkTop->columnNo = 1; }
+
+ void push(const QString& filePath);
+ void pop();
+ void setEtc(bool etc) { etcetera = etc; }
+ void setLineNo(int no) { stkTop->lineNo = no; }
+ void setColumnNo(int no) { stkTop->columnNo = no; }
+
+ bool isEmpty() const { return stkDepth == 0; }
+ int depth() const { return stkDepth; }
+ const QString& filePath() const { return stkTop->filePath; }
+ QString fileName() const;
+ int lineNo() const { return stkTop->lineNo; }
+ int columnNo() const { return stkTop->columnNo; }
+ bool etc() const { return etcetera; }
+ void warning(const QString& message,
+ const QString& details = QString()) const;
+ void error(const QString& message,
+ const QString& details = QString()) const;
+ void fatal(const QString& message,
+ const QString& details = QString()) const;
+
+ QT_STATIC_CONST Location null;
+
+ static void initialize(const Config& config);
+ static void terminate();
+ static void information(const QString& message);
+ static void internalError(const QString& hint);
+
+ private:
+ enum MessageType { Warning, Error };
+
+ struct StackEntry
+ {
+ QString filePath;
+ int lineNo;
+ int columnNo;
+ };
+
+ void emitMessage(MessageType type,
+ const QString& message,
+ const QString& details) const;
+ QString toString() const;
+ QString top() const;
+
+ private:
+ StackEntry stkBottom;
+ QStack<StackEntry> *stk;
+ StackEntry *stkTop;
+ int stkDepth;
+ bool etcetera;
+
+ static int tabSize;
+ static QString programName;
+ static QRegExp *spuriousRegExp;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/tools/qdoc3/loutgenerator.cpp b/tools/qdoc3/loutgenerator.cpp
new file mode 100644
index 0000000..0d1f646
--- /dev/null
+++ b/tools/qdoc3/loutgenerator.cpp
@@ -0,0 +1,63 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*
+ loutgenerator.cpp
+*/
+
+#include "loutgenerator.h"
+
+QT_BEGIN_NAMESPACE
+
+LoutGenerator::LoutGenerator()
+{
+}
+
+LoutGenerator::~LoutGenerator()
+{
+}
+
+QString LoutGenerator::format()
+{
+ return "lout";
+}
+
+QT_END_NAMESPACE
diff --git a/tools/qdoc3/loutgenerator.h b/tools/qdoc3/loutgenerator.h
new file mode 100644
index 0000000..0d6f5f1
--- /dev/null
+++ b/tools/qdoc3/loutgenerator.h
@@ -0,0 +1,67 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*
+ loutgenerator.h
+*/
+
+#ifndef LOUTGENERATOR_H
+#define LOUTGENERATOR_H
+
+#include "bookgenerator.h"
+
+QT_BEGIN_NAMESPACE
+
+class LoutGenerator : public BookGenerator
+{
+public:
+ LoutGenerator();
+ ~LoutGenerator();
+
+ virtual QString format();
+
+protected:
+ // ###
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/tools/qdoc3/main.cpp b/tools/qdoc3/main.cpp
new file mode 100644
index 0000000..3e6f832
--- /dev/null
+++ b/tools/qdoc3/main.cpp
@@ -0,0 +1,496 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*
+ main.cpp
+*/
+
+#include <QtCore>
+#include <stdlib.h>
+#include "apigenerator.h"
+#include "codemarker.h"
+#include "codeparser.h"
+#include "config.h"
+#include "cppcodemarker.h"
+#include "cppcodeparser.h"
+#include "cpptoqsconverter.h"
+#include "doc.h"
+#include "htmlgenerator.h"
+#include "jambiapiparser.h"
+#include "javacodemarker.h"
+#include "javadocgenerator.h"
+#include "linguistgenerator.h"
+#include "loutgenerator.h"
+#include "mangenerator.h"
+#include "plaincodemarker.h"
+#include "polyarchiveextractor.h"
+#include "polyuncompressor.h"
+#include "qsakernelparser.h"
+#include "qscodemarker.h"
+#include "qscodeparser.h"
+#include "sgmlgenerator.h"
+#include "webxmlgenerator.h"
+#include "tokenizer.h"
+#include "tree.h"
+
+QT_BEGIN_NAMESPACE
+
+/*
+ The default indent for code is 4.
+ The default value for false is 0.
+ The default language is c++.
+ The default output format is html.
+ The default tab size is 8.
+ And those are all the default values for configuration variables.
+ */
+static const struct {
+ const char *key;
+ const char *value;
+} defaults[] = {
+ { CONFIG_CODEINDENT, "4" },
+ { CONFIG_FALSEHOODS, "0" },
+ { CONFIG_LANGUAGE, "Cpp" },
+ { CONFIG_OUTPUTFORMATS, "HTML" },
+ { CONFIG_TABSIZE, "8" },
+ { 0, 0 }
+};
+
+static bool slow = false;
+static QStringList defines;
+static QHash<QString, Tree *> trees;
+
+//static int doxygen = 0;
+
+/*!
+ Find the Tree for language \a lang and return a pointer to it.
+ If there is no Tree for language \a lang in the Tree table, add
+ a new one. The Tree table is indexed by \a lang strings.
+ */
+static Tree* treeForLanguage(const QString &lang)
+{
+ Tree* tree = trees.value(lang);
+ if (tree == 0) {
+ tree = new Tree;
+ trees.insert( lang, tree );
+ }
+ return tree;
+}
+
+/*!
+ Print the help message to \c stdout.
+ */
+static void printHelp()
+{
+ Location::information(tr("Usage: qdoc [options] file1.qdocconf ...\n"
+ "Options:\n"
+ " -help "
+ "Display this information and exit\n"
+ " -version "
+ "Display version of qdoc and exit\n"
+ " -D<name> "
+ "Define <name> as a macro while parsing sources\n"
+ " -slow "
+ "Turn on features that slow down qdoc") );
+}
+
+/*!
+ Prints the qdoc version number to stdout.
+ */
+static void printVersion()
+{
+ Location::information(tr("qdoc version 4.4.1"));
+}
+
+/*!
+ Processes the qdoc config file \a fileName. This is the
+ controller for all of qdoc.
+ */
+static void processQdocconfFile(const QString &fileName)
+{
+ QList<QTranslator *> translators;
+
+ /*
+ The Config instance represents the configuration data for qdoc.
+ All the other classes are initialized with the config. Here we
+ initialize the configuration with some default values.
+ */
+ Config config(tr("qdoc"));
+ int i = 0;
+ while (defaults[i].key) {
+ config.setStringList(defaults[i].key,
+ QStringList() << defaults[i].value);
+ ++i;
+ }
+ config.setStringList(CONFIG_SLOW, QStringList(slow ? "true" : "false"));
+
+ /*
+ With the default configuration values in place, load
+ the qdoc configuration file. Note that the configuration
+ file may include other configuration files.
+
+ The Location class keeps track of the current location
+ in the file being processed, mainly for error reporting
+ purposes.
+ */
+ Location::initialize(config);
+ config.load(fileName);
+
+ /*
+ Add the defines to the configuration variables.
+ */
+ QStringList defs = defines + config.getStringList(CONFIG_DEFINES);
+ config.setStringList(CONFIG_DEFINES,defs);
+ Location::terminate();
+
+ QString prevCurrentDir = QDir::currentPath();
+ QString dir = QFileInfo(fileName).path();
+ if (!dir.isEmpty())
+ QDir::setCurrent(dir);
+
+ /*
+ Initialize all the classes and data structures with the
+ qdoc configuration.
+ */
+ Location::initialize(config);
+ Tokenizer::initialize(config);
+ Doc::initialize(config);
+ CppToQsConverter::initialize(config);
+ CodeMarker::initialize(config);
+ CodeParser::initialize(config);
+ Generator::initialize(config);
+
+ /*
+ Load the language translators, if the configuration specifies any.
+ */
+ QStringList fileNames = config.getStringList(CONFIG_TRANSLATORS);
+ QStringList::Iterator fn = fileNames.begin();
+ while (fn != fileNames.end()) {
+ QTranslator *translator = new QTranslator(0);
+ if (!translator->load(*fn))
+ config.lastLocation().error(tr("Cannot load translator '%1'")
+ .arg(*fn));
+ QCoreApplication::instance()->installTranslator(translator);
+ translators.append(translator);
+ ++fn;
+ }
+
+ //QSet<QString> outputLanguages = config.getStringSet(CONFIG_OUTPUTLANGUAGES);
+
+ /*
+ Get the source language (Cpp) from the configuration
+ and the location in the configuration file where the
+ source language was set.
+ */
+ QString lang = config.getString(CONFIG_LANGUAGE);
+ Location langLocation = config.lastLocation();
+
+#ifdef QDOC2DOX
+ // qdoc -> doxygen
+ if (doxygen == 2) {
+ qDebug() << "READING anchors.txt";
+ DoxWriter::readAnchors();
+ qDebug() << "READING title maps";
+ DoxWriter::readTitles();
+ qDebug() << "READING member multimaps";
+ DoxWriter::readMembers();
+ }
+#endif
+
+ /*
+ Initialize the tree where all the parsed sources will be stored.
+ The tree gets built as the source files are parsed, and then the
+ documentation output is generated by traversing the tree.
+ */
+ Tree *tree = new Tree;
+ tree->setVersion(config.getString(CONFIG_VERSION));
+
+ /*
+ There must be a code parser for the source code language, e.g. C++.
+ If there isn't one, give up.
+ */
+ CodeParser *codeParser = CodeParser::parserForLanguage(lang);
+ if (codeParser == 0)
+ config.lastLocation().fatal(tr("Cannot parse programming language '%1'").arg(lang));
+
+ /*
+ By default, the only output format is HTML.
+ */
+ QSet<QString> outputFormats = config.getStringSet(CONFIG_OUTPUTFORMATS);
+ Location outputFormatsLocation = config.lastLocation();
+
+ /*
+ There must be a code marker for the source code language, e.g. C++.
+ If there isn't one, give up.
+ */
+ CodeMarker *marker = CodeMarker::markerForLanguage(lang);
+ if (!marker && !outputFormats.isEmpty())
+ langLocation.fatal(tr("Cannot output documentation for programming language '%1'").arg(lang));
+
+ /*
+ Read some XML indexes. What are they???
+ */
+ QStringList indexFiles = config.getStringList(CONFIG_INDEXES);
+ tree->readIndexes(indexFiles);
+
+ /*
+ Get all the header files: "*.ch *.h *.h++ *.hh *.hpp *.hxx"
+ Put them in a set.
+ */
+ QSet<QString> excludedDirs;
+ QStringList excludedDirsList = config.getStringList(CONFIG_EXCLUDEDIRS);
+ foreach (const QString &excludeDir, excludedDirsList)
+ excludedDirs.insert(QDir::fromNativeSeparators(excludeDir));
+ QSet<QString> headers = QSet<QString>::fromList(
+ config.getAllFiles(CONFIG_HEADERS, CONFIG_HEADERDIRS,
+ codeParser->headerFileNameFilter(),
+ excludedDirs));
+
+ /*
+ Parse each header file in the set and add it to the big tree.
+ */
+ QSet<QString>::ConstIterator h = headers.begin();
+ while (h != headers.end()) {
+ codeParser->parseHeaderFile(config.location(), *h, tree);
+ ++h;
+ }
+ codeParser->doneParsingHeaderFiles(tree);
+
+ /*
+ Get all the source text files: "*.cpp *.qdoc *.mm"
+ Put them in a set.
+ */
+ QSet<QString> sources = QSet<QString>::fromList(
+ config.getAllFiles(CONFIG_SOURCES, CONFIG_SOURCEDIRS,
+ codeParser->sourceFileNameFilter(),
+ excludedDirs));
+
+ /*
+ Parse each source text file in the set and add it to the big tree.
+ */
+ QSet<QString>::ConstIterator s = sources.begin();
+ while (s != sources.end()) {
+ codeParser->parseSourceFile(config.location(), *s, tree);
+ ++s;
+ }
+ codeParser->doneParsingSourceFiles(tree);
+
+ /*
+ Now the big tree has been built from all the header and
+ source files. Resolve all the class names, function names,
+ targets, URLs, links, and other stuff that needs resolving.
+ */
+ tree->resolveGroups();
+ tree->resolveTargets();
+
+#ifdef QDOC2DOX
+ // qdoc -> doxygen
+ if (doxygen == 1) {
+ DoxWriter::writeAnchors();
+ DoxWriter::writeTitles();
+ DoxWriter::writeMembers();
+ }
+
+ if (doxygen == 0) {
+#endif
+
+ /*
+ Now the tree has been built, and all the stuff that needed
+ resolving has been resolved. Now it is time to traverse
+ the big tree and generate the documentation output.
+ */
+ QSet<QString>::ConstIterator of = outputFormats.begin();
+ while (of != outputFormats.end()) {
+ Generator *generator = Generator::generatorForFormat(*of);
+ if (generator == 0)
+ outputFormatsLocation.fatal(tr("Unknown output format '%1'")
+ .arg(*of));
+ generator->generateTree(tree, marker);
+ ++of;
+ }
+
+ /*
+ Generate the XML tag file, if it was requested.
+ */
+ QString tagFile = config.getString(CONFIG_TAGFILE);
+ if (!tagFile.isEmpty())
+ tree->generateTagFile(tagFile);
+
+ tree->setVersion("");
+ Generator::terminate();
+
+#ifdef QDOC2DOX
+ }
+#endif
+
+ CodeParser::terminate();
+ CodeMarker::terminate();
+ CppToQsConverter::terminate();
+ Doc::terminate();
+ Tokenizer::terminate();
+ Location::terminate();
+ QDir::setCurrent(prevCurrentDir);
+
+ foreach (QTranslator *translator, translators)
+ delete translator;
+
+ delete tree;
+}
+
+QT_END_NAMESPACE
+
+int main(int argc, char **argv)
+{
+ QT_USE_NAMESPACE
+
+ QCoreApplication app(argc, argv);
+ QString cf = "qsauncompress \1 \2";
+ PolyArchiveExtractor qsaExtractor(QStringList() << "qsa",cf);
+ cf = "tar -C \2 -xf \1";
+ PolyArchiveExtractor tarExtractor(QStringList() << "tar",cf);
+ cf = "tar -C \2 -Zxf \1";
+ PolyArchiveExtractor tazExtractor(QStringList() << "taz",cf);
+ cf = "tar -C \2 -jxf \1";
+ PolyArchiveExtractor tbz2Extractor(QStringList() << "tbz" << "tbz2",cf);
+ cf = "tar -C \2 -zxf \1";
+ PolyArchiveExtractor tgzExtractor(QStringList() << "tgz",cf);
+ cf = "unzip \1 -d \2";
+ PolyArchiveExtractor zipExtractor(QStringList() << "zip",cf);
+ cf = "bunzip2 -c \1 > \2";
+ PolyUncompressor bz2Uncompressor(QStringList() << "bz" << "bz2",cf);
+ cf = "gunzip -c \1 > \2";
+ PolyUncompressor gzAndZUncompressor(QStringList() << "gz" << "z" << "Z",cf);
+ cf = "unzip -c \1 > \2";
+ PolyUncompressor zipUncompressor(QStringList() << "zip",cf);
+
+ /*
+ Create code parsers for the languages to be parsed,
+ and create a tree for C++.
+ */
+ CppCodeParser cppParser;
+ Tree *cppTree = treeForLanguage(cppParser.language());
+
+ QsCodeParser qsParser(cppTree);
+ QsaKernelParser qsaKernelParser(cppTree);
+ JambiApiParser jambiParser(cppTree);
+
+ /*
+ Create code markers for plain text, C++, Java, and qs.
+ */
+ PlainCodeMarker plainMarker;
+ CppCodeMarker cppMarker;
+ JavaCodeMarker javaMarker;
+ QsCodeMarker qsMarker;
+
+ ApiGenerator apiGenerator;
+ HtmlGenerator htmlGenerator;
+ JavadocGenerator javadocGenerator;
+ LinguistGenerator linguistGenerator;
+ LoutGenerator loutGenerator;
+ ManGenerator manGenerator;
+ SgmlGenerator smglGenerator;
+ WebXMLGenerator webxmlGenerator;
+
+ QStringList qdocFiles;
+ QString opt;
+ int i = 1;
+
+ while (i < argc) {
+ opt = argv[i++];
+
+ if (opt == "-help") {
+ printHelp();
+ return EXIT_SUCCESS;
+ }
+ else if (opt == "-version") {
+ printVersion();
+ return EXIT_SUCCESS;
+ }
+ else if (opt == "--") {
+ while (i < argc)
+ qdocFiles.append(argv[i++]);
+ }
+ else if (opt.startsWith("-D")) {
+ QString define = opt.mid(2);
+ defines += define;
+ }
+ else if (opt == "-slow") {
+ slow = true;
+ }
+
+#ifdef QDOC2DOX
+ else if (opt == "-doxygen1") {
+ // qdoc -> doxygen
+ // Don't use this; it isn't ready yet.
+ // Now it's a fossil.
+ qDebug() << "doxygen pass 1";
+ doxygen = 1;
+ DoxWriter::setDoxPass(1);
+ }
+ else if (opt == "-doxygen2") {
+ // qdoc -> doxygen
+ // Don't use this; it isn't ready yet.
+ // Now it's a fossil.
+ qDebug() << "doxygen pass 2";
+ doxygen = 2;
+ DoxWriter::setDoxPass(2);
+ }
+#endif
+
+ else {
+ qdocFiles.append(opt);
+ }
+ }
+
+ if (qdocFiles.isEmpty()) {
+ printHelp();
+ return EXIT_FAILURE;
+ }
+
+ /*
+ Main loop.
+ */
+ foreach (QString qf, qdocFiles)
+ processQdocconfFile(qf);
+
+ qDeleteAll(trees);
+ return EXIT_SUCCESS;
+}
+
diff --git a/tools/qdoc3/mangenerator.cpp b/tools/qdoc3/mangenerator.cpp
new file mode 100644
index 0000000..619a680
--- /dev/null
+++ b/tools/qdoc3/mangenerator.cpp
@@ -0,0 +1,228 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*
+ mangenerator.cpp
+*/
+
+#include <qdatetime.h>
+#include <qregexp.h>
+
+#include "mangenerator.h"
+#include "node.h"
+#include "tree.h"
+
+QT_BEGIN_NAMESPACE
+
+ManGenerator::ManGenerator()
+{
+ date = QDate::currentDate().toString( "d MMMM yyyy" );
+}
+
+ManGenerator::~ManGenerator()
+{
+}
+
+QString ManGenerator::format()
+{
+ return "man";
+}
+
+int ManGenerator::generateAtom( const Atom *atom, const Node * /* relative */,
+ CodeMarker * /* marker */ )
+{
+#if 0
+ switch ( atom->type() ) {
+ case Atom::AbstractBegin:
+ break;
+ case Atom::AbstractEnd:
+ break;
+ case Atom::Alias:
+ break;
+ case Atom::AliasArg:
+ break;
+ case Atom::BaseName:
+ break;
+ case Atom::BriefBegin:
+ break;
+ case Atom::BriefEnd:
+ break;
+ case Atom::C:
+ break;
+ case Atom::CaptionBegin:
+ break;
+ case Atom::CaptionEnd:
+ break;
+ case Atom::CitationBegin:
+ break;
+ case Atom::CitationEnd:
+ break;
+ case Atom::Code:
+ break;
+ case Atom::FootnoteBegin:
+ break;
+ case Atom::FootnoteEnd:
+ break;
+ case Atom::FormatBegin:
+ break;
+ case Atom::FormatEnd:
+ break;
+ case Atom::GeneratedList:
+ break;
+ case Atom::Image:
+ break;
+ case Atom::ImageText:
+ break;
+ case Atom::Link:
+ break;
+ case Atom::LinkNode:
+ break;
+ case Atom::ListBegin:
+ break;
+ case Atom::ListItemNumber:
+ break;
+ case Atom::ListItemBegin:
+ out() << ".IP " << atom->string() << ".\n";
+ break;
+ case Atom::ListItemEnd:
+ break;
+ case Atom::ListEnd:
+ break;
+ case Atom::Nop:
+ break;
+ case Atom::ParaBegin:
+ out() << ".PP\n";
+ break;
+ case Atom::ParaEnd:
+ out() << "\n";
+ break;
+ case Atom::RawFormat:
+ break;
+ case Atom::RawString:
+ break;
+ case Atom::SectionBegin:
+ break;
+ case Atom::SectionEnd:
+ break;
+ case Atom::SectionHeadingBegin:
+ break;
+ case Atom::SectionHeadingEnd:
+ break;
+ case Atom::SidebarBegin:
+ break;
+ case Atom::SidebarEnd:
+ break;
+ case Atom::String:
+ out() << protectTextLine( atom->string() );
+ break;
+ case Atom::TableBegin:
+ break;
+ case Atom::TableEnd:
+ break;
+ case Atom::TableOfContents:
+ break;
+ case Atom::Target:
+ break;
+ case Atom::UnknownCommand:
+ ;
+ }
+#endif
+ unknownAtom( atom );
+ return 0;
+}
+
+void ManGenerator::generateClassLikeNode( const InnerNode *classe,
+ CodeMarker *marker )
+{
+ generateHeader( classe->name() );
+ out() << ".SH NAME\n"
+ << classe->name() << "\n"
+ << ".SH SYNOPSYS\n";
+ generateBody( classe, marker );
+ generateFooter();
+}
+
+void ManGenerator::generateFakeNode( const FakeNode *fake, CodeMarker *marker )
+{
+ generateHeader( "foo" );
+ generateBody( fake, marker );
+ generateFooter();
+}
+
+QString ManGenerator::fileExtension(const Node * /* node */)
+{
+ return "3qt";
+}
+
+void ManGenerator::generateHeader( const QString& name )
+{
+ out() << ".TH " << protectArg( name )
+ << " " << protectArg( "3qt" )
+ << " " << protectArg( date )
+ << " " << protectArg( "Nokia Corporation and/or its subsidiary(-ies)" )
+ << " " << protectArg( "Qt Toolkit" ) << "\n";
+}
+
+void ManGenerator::generateFooter()
+{
+}
+
+QString ManGenerator::protectArg( const QString& str )
+{
+ for ( int i = 0; i < (int) str.length(); i++ ) {
+ if ( str[i] == ' ' || str[i].isSpace() ) {
+ QString quoted = str;
+ quoted.replace( "\"", "\"\"" );
+ return "\"" + quoted + "\"";
+ }
+ }
+ return str;
+}
+
+QString ManGenerator::protectTextLine( const QString& str )
+{
+ QString t = str;
+ if ( t.startsWith(".") || t.startsWith("'") )
+ t.prepend( "\\&" );
+ return t;
+}
+
+QT_END_NAMESPACE
diff --git a/tools/qdoc3/mangenerator.h b/tools/qdoc3/mangenerator.h
new file mode 100644
index 0000000..0136c5d
--- /dev/null
+++ b/tools/qdoc3/mangenerator.h
@@ -0,0 +1,79 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*
+ mangenerator.h
+*/
+
+#ifndef MANGENERATOR_H
+#define MANGENERATOR_H
+
+#include "pagegenerator.h"
+
+QT_BEGIN_NAMESPACE
+
+class ManGenerator : public PageGenerator
+{
+public:
+ ManGenerator();
+ ~ManGenerator();
+
+ virtual QString format();
+
+protected:
+ virtual int generateAtom( const Atom *atom, const Node *relative,
+ CodeMarker *marker );
+ virtual void generateClassLikeNode(const InnerNode *node, CodeMarker *marker);
+ virtual void generateFakeNode( const FakeNode *fake, CodeMarker *marker );
+ virtual QString fileExtension(const Node *node);
+
+private:
+ void generateHeader( const QString& name );
+ void generateFooter();
+ QString protectArg( const QString& str );
+ QString protectTextLine( const QString& str );
+
+ QString date;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/tools/qdoc3/node.cpp b/tools/qdoc3/node.cpp
new file mode 100644
index 0000000..231149e
--- /dev/null
+++ b/tools/qdoc3/node.cpp
@@ -0,0 +1,1024 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*
+ node.cpp
+*/
+
+#include "node.h"
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class Node
+ \brief A node in a Tree.
+ */
+
+/*!
+ */
+Node::~Node()
+{
+ if (par)
+ par->removeChild(this);
+ if (rel)
+ rel->removeRelated(this);
+}
+
+/*!
+ */
+void Node::setDoc(const Doc& doc, bool replace)
+{
+ if (!d.isEmpty() && !replace) {
+ doc.location().warning(tr("Overrides a previous doc"));
+ d.location().warning(tr("(The previous doc is here)"));
+ }
+ d = doc;
+}
+
+/*!
+ */
+Node::Node(Type type, InnerNode *parent, const QString& name)
+ : typ(type),
+ acc(Public),
+ sta(Commendable),
+ saf(UnspecifiedSafeness),
+ par(parent),
+ rel(0),
+ nam(name)
+{
+ if (par)
+ par->addChild(this);
+}
+
+/*!
+ */
+QString Node::url() const
+{
+ return u;
+}
+
+/*!
+ */
+void Node::setUrl(const QString &url)
+{
+ u = url;
+}
+
+/*!
+ */
+void Node::setRelates(InnerNode *pseudoParent)
+{
+ if (rel)
+ rel->removeRelated(this);
+ rel = pseudoParent;
+ pseudoParent->related.append(this);
+}
+
+/*!
+ */
+void Node::setLink(LinkType linkType, const QString &link, const QString &desc)
+{
+ QPair<QString,QString> linkPair;
+ linkPair.first = link;
+ linkPair.second = desc;
+ linkMap[linkType] = linkPair;
+}
+
+/*!
+ */
+Node::Status Node::inheritedStatus() const
+{
+ Status parentStatus = Commendable;
+ if (par)
+ parentStatus = par->inheritedStatus();
+ return (Status)qMin((int)sta, (int)parentStatus);
+}
+
+/*!
+ */
+Node::ThreadSafeness Node::threadSafeness() const
+{
+ if (par && saf == par->inheritedThreadSafeness())
+ return UnspecifiedSafeness;
+ return saf;
+}
+
+/*!
+ */
+Node::ThreadSafeness Node::inheritedThreadSafeness() const
+{
+ if (par && saf == UnspecifiedSafeness)
+ return par->inheritedThreadSafeness();
+ return saf;
+}
+
+/*!
+ */
+QString Node::fileBase() const
+{
+ QString base = name();
+ if (base.endsWith(".html"))
+ base.chop(5);
+ base.replace(QRegExp("[^A-Za-z0-9]+"), " ");
+ base = base.trimmed();
+ base.replace(" ", "-");
+ return base.toLower();
+}
+
+/*!
+ \class InnerNode
+ */
+
+/*!
+ */
+InnerNode::~InnerNode()
+{
+ deleteChildren();
+ removeFromRelated();
+}
+
+/*!
+ */
+Node *InnerNode::findNode(const QString& name)
+{
+ Node *node = childMap.value(name);
+ if (node)
+ return node;
+ return primaryFunctionMap.value(name);
+}
+
+/*!
+ */
+Node *InnerNode::findNode(const QString& name, Type type)
+{
+ if (type == Function) {
+ return primaryFunctionMap.value(name);
+ }
+ else {
+ Node *node = childMap.value(name);
+ if (node && node->type() == type) {
+ return node;
+ }
+ else {
+ return 0;
+ }
+ }
+}
+
+/*!
+ */
+FunctionNode *InnerNode::findFunctionNode(const QString& name)
+{
+ return static_cast<FunctionNode *>(primaryFunctionMap.value(name));
+}
+
+/*!
+ */
+FunctionNode *InnerNode::findFunctionNode(const FunctionNode *clone)
+{
+ QMap<QString, Node *>::ConstIterator c =
+ primaryFunctionMap.find(clone->name());
+ if (c != primaryFunctionMap.end()) {
+ if (isSameSignature(clone, (FunctionNode *) *c)) {
+ return (FunctionNode *) *c;
+ }
+ else if (secondaryFunctionMap.contains(clone->name())) {
+ const NodeList& secs = secondaryFunctionMap[clone->name()];
+ NodeList::ConstIterator s = secs.begin();
+ while (s != secs.end()) {
+ if (isSameSignature(clone, (FunctionNode *) *s))
+ return (FunctionNode *) *s;
+ ++s;
+ }
+ }
+ }
+ return 0;
+}
+
+/*!
+ */
+void InnerNode::setOverload(const FunctionNode *func, bool overlode)
+{
+ Node *node = (Node *) func;
+ Node *&primary = primaryFunctionMap[func->name()];
+
+ if (secondaryFunctionMap.contains(func->name())) {
+ NodeList& secs = secondaryFunctionMap[func->name()];
+ if (overlode) {
+ if (primary == node) {
+ primary = secs.first();
+ secs.erase(secs.begin());
+ secs.append(node);
+ }
+ else {
+ secs.removeAll(node);
+ secs.append(node);
+ }
+ }
+ else {
+ if (primary != node) {
+ secs.removeAll(node);
+ secs.prepend(primary);
+ primary = node;
+ }
+ }
+ }
+}
+
+/*!
+ */
+void InnerNode::makeUndocumentedChildrenInternal()
+{
+ foreach (Node *child, childNodes()) {
+ if (child->doc().isEmpty()) {
+ child->setAccess(Node::Private);
+ child->setStatus(Node::Internal);
+ }
+ }
+}
+
+/*!
+ */
+void InnerNode::normalizeOverloads()
+{
+ QMap<QString, Node *>::Iterator p1 = primaryFunctionMap.begin();
+ while (p1 != primaryFunctionMap.end()) {
+ FunctionNode *primaryFunc = (FunctionNode *) *p1;
+ if (secondaryFunctionMap.contains(primaryFunc->name()) &&
+ (primaryFunc->status() != Commendable ||
+ primaryFunc->access() == Private)) {
+
+ NodeList& secs = secondaryFunctionMap[primaryFunc->name()];
+ NodeList::ConstIterator s = secs.begin();
+ while (s != secs.end()) {
+ FunctionNode *secondaryFunc = (FunctionNode *) *s;
+
+ // Any non-obsolete, non-compatibility, non-private functions
+ // (i.e, visible functions) are preferable to the primary
+ // function.
+
+ if (secondaryFunc->status() == Commendable &&
+ secondaryFunc->access() != Private) {
+
+ *p1 = secondaryFunc;
+ int index = secondaryFunctionMap[primaryFunc->name()].indexOf(secondaryFunc);
+ secondaryFunctionMap[primaryFunc->name()].replace(index, primaryFunc);
+ break;
+ }
+ ++s;
+ }
+ }
+ ++p1;
+ }
+
+ QMap<QString, Node *>::ConstIterator p = primaryFunctionMap.begin();
+ while (p != primaryFunctionMap.end()) {
+ FunctionNode *primaryFunc = (FunctionNode *) *p;
+ if (primaryFunc->isOverload())
+ primaryFunc->ove = false;
+ if (secondaryFunctionMap.contains(primaryFunc->name())) {
+ NodeList& secs = secondaryFunctionMap[primaryFunc->name()];
+ NodeList::ConstIterator s = secs.begin();
+ while (s != secs.end()) {
+ FunctionNode *secondaryFunc = (FunctionNode *) *s;
+ if (!secondaryFunc->isOverload())
+ secondaryFunc->ove = true;
+ ++s;
+ }
+ }
+ ++p;
+ }
+
+ NodeList::ConstIterator c = childNodes().begin();
+ while (c != childNodes().end()) {
+ if ((*c)->isInnerNode())
+ ((InnerNode *) *c)->normalizeOverloads();
+ ++c;
+ }
+}
+
+/*!
+ */
+void InnerNode::removeFromRelated()
+{
+ while (!related.isEmpty()) {
+ Node *p = static_cast<Node *>(related.takeFirst());
+
+ if (p != 0 && p->relates() == this) p->clearRelated();
+ }
+}
+
+/*!
+ */
+void InnerNode::deleteChildren()
+{
+ qDeleteAll(children);
+}
+
+/*!
+ Returns true.
+ */
+bool InnerNode::isInnerNode() const
+{
+ return true;
+}
+
+/*!
+ */
+const Node *InnerNode::findNode(const QString& name) const
+{
+ InnerNode *that = (InnerNode *) this;
+ return that->findNode(name);
+}
+
+/*!
+ */
+const Node *InnerNode::findNode(const QString& name, Type type) const
+{
+ InnerNode *that = (InnerNode *) this;
+ return that->findNode(name, type);
+}
+
+/*!
+ */
+const FunctionNode *InnerNode::findFunctionNode(const QString& name) const
+{
+ InnerNode *that = (InnerNode *) this;
+ return that->findFunctionNode(name);
+}
+
+/*!
+ */
+const FunctionNode *InnerNode::findFunctionNode(
+ const FunctionNode *clone) const
+{
+ InnerNode *that = (InnerNode *) this;
+ return that->findFunctionNode(clone);
+}
+
+/*!
+ */
+const EnumNode *InnerNode::findEnumNodeForValue(const QString &enumValue) const
+{
+ foreach (const Node *node, enumChildren) {
+ const EnumNode *enume = static_cast<const EnumNode *>(node);
+ if (enume->hasItem(enumValue))
+ return enume;
+ }
+ return 0;
+}
+
+/*!
+ */
+int InnerNode::overloadNumber(const FunctionNode *func) const
+{
+ Node *node = (Node *) func;
+ if (primaryFunctionMap[func->name()] == node) {
+ return 1;
+ }
+ else {
+ return secondaryFunctionMap[func->name()].indexOf(node) + 2;
+ }
+}
+
+/*!
+ */
+int InnerNode::numOverloads(const QString& funcName) const
+{
+ if (primaryFunctionMap.contains(funcName)) {
+ return secondaryFunctionMap[funcName].count() + 1;
+ }
+ else {
+ return 0;
+ }
+}
+
+/*!
+ */
+NodeList InnerNode::overloads(const QString &funcName) const
+{
+ NodeList result;
+ Node *primary = primaryFunctionMap.value(funcName);
+ if (primary) {
+ result << primary;
+ result += secondaryFunctionMap[funcName];
+ }
+ return result;
+}
+
+/*!
+ */
+InnerNode::InnerNode(Type type, InnerNode *parent, const QString& name)
+ : Node(type, parent, name)
+{
+}
+
+/*!
+ */
+void InnerNode::addInclude(const QString& include)
+{
+ inc.append(include);
+}
+
+/*!
+ */
+void InnerNode::setIncludes(const QStringList& includes)
+{
+ inc = includes;
+}
+
+/*!
+ f1 is always the clone
+ */
+bool InnerNode::isSameSignature(const FunctionNode *f1, const FunctionNode *f2)
+{
+ if (f1->parameters().count() != f2->parameters().count())
+ return false;
+ if (f1->isConst() != f2->isConst())
+ return false;
+
+ QList<Parameter>::ConstIterator p1 = f1->parameters().begin();
+ QList<Parameter>::ConstIterator p2 = f2->parameters().begin();
+ while (p2 != f2->parameters().end()) {
+ if ((*p1).hasType() && (*p2).hasType()) {
+ if ((*p1).rightType() != (*p2).rightType())
+ return false;
+
+ QString t1 = p1->leftType();
+ QString t2 = p2->leftType();
+
+ if (t1.length() < t2.length())
+ qSwap(t1, t2);
+
+ /*
+ ### hack for C++ to handle superfluous
+ "Foo::" prefixes gracefully
+ */
+ if (t1 != t2 && t1 != (f2->parent()->name() + "::" + t2))
+ return false;
+ }
+ ++p1;
+ ++p2;
+ }
+ return true;
+}
+
+/*!
+ */
+void InnerNode::addChild(Node *child)
+{
+ children.append(child);
+ if (child->type() == Function) {
+ FunctionNode *func = (FunctionNode *) child;
+ if (!primaryFunctionMap.contains(func->name())) {
+ primaryFunctionMap.insert(func->name(), func);
+ }
+ else {
+ NodeList &secs = secondaryFunctionMap[func->name()];
+ secs.append(func);
+ }
+ }
+ else {
+ if (child->type() == Enum)
+ enumChildren.append(child);
+ childMap.insert(child->name(), child);
+ }
+}
+
+/*!
+ */
+void InnerNode::removeChild(Node *child)
+{
+ children.removeAll(child);
+ enumChildren.removeAll(child);
+ if (child->type() == Function) {
+ QMap<QString, Node *>::Iterator prim =
+ primaryFunctionMap.find(child->name());
+ NodeList& secs = secondaryFunctionMap[child->name()];
+ if (*prim == child) {
+ if (secs.isEmpty()) {
+ primaryFunctionMap.remove(child->name());
+ }
+ else {
+ primaryFunctionMap.insert(child->name(), secs.takeFirst());
+ }
+ }
+ else {
+ secs.removeAll(child);
+ }
+ QMap<QString, Node *>::Iterator ent = childMap.find( child->name() );
+ if ( *ent == child )
+ childMap.erase( ent );
+ }
+ else {
+ QMap<QString, Node *>::Iterator ent = childMap.find(child->name());
+ if (*ent == child)
+ childMap.erase(ent);
+ }
+}
+
+/*!
+ Find the module (QtCore, QtGui, etc.) to which the class belongs.
+ We do this by obtaining the full path to the header file's location
+ and examine everything between "src/" and the filename. This is
+ semi-dirty because we are assuming a particular directory structure.
+
+ This function is only really useful if the class's module has not
+ been defined in the header file with a QT_MODULE macro or with an
+ \inmodule command in the documentation.
+*/
+QString Node::moduleName() const
+{
+ if (!mod.isEmpty())
+ return mod;
+
+ QString path = location().filePath();
+ QString pattern = QString("src") + QDir::separator();
+ int start = path.lastIndexOf(pattern);
+
+ if (start == -1)
+ return "";
+
+ QString moduleDir = path.mid(start + pattern.size());
+ int finish = moduleDir.indexOf(QDir::separator());
+
+ if (finish == -1)
+ return "";
+
+ QString moduleName = moduleDir.left(finish);
+
+ if (moduleName == "corelib")
+ return "QtCore";
+ else if (moduleName == "uitools")
+ return "QtUiTools";
+ else if (moduleName == "gui")
+ return "QtGui";
+ else if (moduleName == "network")
+ return "QtNetwork";
+ else if (moduleName == "opengl")
+ return "QtOpenGL";
+ else if (moduleName == "qt3support")
+ return "Qt3Support";
+ else if (moduleName == "svg")
+ return "QtSvg";
+ else if (moduleName == "sql")
+ return "QtSql";
+ else if (moduleName == "qtestlib")
+ return "QtTest";
+ else if (moduleDir.contains("webkit"))
+ return "QtWebKit";
+ else if (moduleName == "xml")
+ return "QtXml";
+ else
+ return "";
+}
+
+/*!
+ */
+void InnerNode::removeRelated(Node *pseudoChild)
+{
+ related.removeAll(pseudoChild);
+}
+
+/*!
+ \class LeafNode
+ */
+
+/*!
+ Returns false because this is an InnerNode.
+ */
+bool LeafNode::isInnerNode() const
+{
+ return false;
+}
+
+/*!
+ */
+LeafNode::LeafNode(Type type, InnerNode *parent, const QString& name)
+ : Node(type, parent, name)
+{
+}
+
+/*!
+ \class NamespaceNode
+ */
+
+/*!
+ */
+NamespaceNode::NamespaceNode(InnerNode *parent, const QString& name)
+ : InnerNode(Namespace, parent, name)
+{
+}
+
+/*!
+ \class ClassNode
+ */
+
+/*!
+ */
+ClassNode::ClassNode(InnerNode *parent, const QString& name)
+ : InnerNode(Class, parent, name)
+{
+ hidden = false;
+}
+
+/*!
+ */
+void ClassNode::addBaseClass(Access access,
+ ClassNode *node,
+ const QString &dataTypeWithTemplateArgs)
+{
+ bas.append(RelatedClass(access, node, dataTypeWithTemplateArgs));
+ node->der.append(RelatedClass(access, this));
+}
+
+/*!
+ */
+void ClassNode::fixBaseClasses()
+{
+ int i;
+
+ i = 0;
+ while (i < bas.size()) {
+ ClassNode *baseClass = bas.at(i).node;
+ if (baseClass->access() == Node::Private) {
+ bas.removeAt(i);
+
+ const QList<RelatedClass> &basesBases = baseClass->baseClasses();
+ for (int j = basesBases.size() - 1; j >= 0; --j)
+ bas.insert(i, basesBases.at(j));
+ }
+ else {
+ ++i;
+ }
+ }
+
+ i = 0;
+ while (i < der.size()) {
+ ClassNode *derivedClass = der.at(i).node;
+ if (derivedClass->access() == Node::Private) {
+ der.removeAt(i);
+
+ const QList<RelatedClass> &dersDers =
+ derivedClass->derivedClasses();
+ for (int j = dersDers.size() - 1; j >= 0; --j)
+ der.insert(i, dersDers.at(j));
+ }
+ else {
+ ++i;
+ }
+ }
+}
+
+/*!
+ \class FakeNode
+ */
+
+/*!
+ */
+FakeNode::FakeNode(InnerNode *parent, const QString& name, SubType subType)
+ : InnerNode(Fake, parent, name), sub(subType)
+{
+}
+
+/*!
+ */
+QString FakeNode::fullTitle() const
+{
+ if (sub == File) {
+ if (title().isEmpty())
+ return name().mid(name().lastIndexOf('/') + 1) + " Example File";
+ else
+ return title();
+ }
+ else if (sub == HeaderFile) {
+ if (title().isEmpty())
+ return name();
+ else
+ return name() + " - " + title();
+ }
+ else {
+ return title();
+ }
+}
+
+/*!
+ */
+QString FakeNode::subTitle() const
+{
+ if (!stle.isEmpty())
+ return stle;
+
+ if (sub == File) {
+ if (title().isEmpty() && name().contains("/"))
+ return name();
+ }
+ return QString();
+}
+
+/*!
+ \class EnumNode
+ */
+
+/*!
+ */
+EnumNode::EnumNode(InnerNode *parent, const QString& name)
+ : LeafNode(Enum, parent, name), ft(0)
+{
+}
+
+/*!
+ */
+void EnumNode::addItem(const EnumItem& item)
+{
+ itms.append(item);
+ names.insert(item.name());
+}
+
+/*!
+ */
+Node::Access EnumNode::itemAccess(const QString &name) const
+{
+ if (doc().omitEnumItemNames().contains(name)) {
+ return Private;
+ }
+ else {
+ return Public;
+ }
+}
+
+/*!
+ Returns the enum value associated with the enum \a name.
+ */
+QString EnumNode::itemValue(const QString &name) const
+{
+ foreach (const EnumItem &item, itms) {
+ if (item.name() == name)
+ return item.value();
+ }
+ return QString();
+}
+
+/*!
+ \class TypedefNode
+ */
+
+/*!
+ */
+TypedefNode::TypedefNode(InnerNode *parent, const QString& name)
+ : LeafNode(Typedef, parent, name), ae(0)
+{
+}
+
+/*!
+ */
+void TypedefNode::setAssociatedEnum(const EnumNode *enume)
+{
+ ae = enume;
+}
+
+/*!
+ \class Parameter
+ */
+
+/*!
+ */
+Parameter::Parameter(const QString& leftType,
+ const QString& rightType,
+ const QString& name,
+ const QString& defaultValue)
+ : lef(leftType), rig(rightType), nam(name), def(defaultValue)
+{
+}
+
+/*!
+ */
+Parameter::Parameter(const Parameter& p)
+ : lef(p.lef), rig(p.rig), nam(p.nam), def(p.def)
+{
+}
+
+/*!
+ */
+Parameter& Parameter::operator=(const Parameter& p)
+{
+ lef = p.lef;
+ rig = p.rig;
+ nam = p.nam;
+ def = p.def;
+ return *this;
+}
+
+/*!
+ \class FunctionNode
+ */
+
+/*!
+ */
+FunctionNode::FunctionNode(InnerNode *parent, const QString& name)
+ : LeafNode(Function, parent, name), met(Plain), vir(NonVirtual),
+ con(false), sta(false), ove(false), rf(0), ap(0)
+{
+}
+
+/*!
+ */
+void FunctionNode::setOverload(bool overlode)
+{
+ parent()->setOverload(this, overlode);
+ ove = overlode;
+}
+
+/*!
+ */
+void FunctionNode::addParameter(const Parameter& parameter)
+{
+ params.append(parameter);
+}
+
+/*!
+ */
+void FunctionNode::borrowParameterNames(const FunctionNode *source)
+{
+ QList<Parameter>::Iterator t = params.begin();
+ QList<Parameter>::ConstIterator s = source->params.begin();
+ while (s != source->params.end() && t != params.end()) {
+ if (!(*s).name().isEmpty())
+ (*t).setName((*s).name());
+ ++s;
+ ++t;
+ }
+}
+
+/*!
+ */
+void FunctionNode::setReimplementedFrom(FunctionNode *from)
+{
+ rf = from;
+ from->rb.append(this);
+}
+
+/*!
+ */
+void FunctionNode::setAssociatedProperty(PropertyNode *property)
+{
+ ap = property;
+}
+
+/*!
+ */
+int FunctionNode::overloadNumber() const
+{
+ return parent()->overloadNumber(this);
+}
+
+/*!
+ */
+int FunctionNode::numOverloads() const
+{
+ return parent()->numOverloads(name());
+}
+
+/*!
+ */
+QStringList FunctionNode::parameterNames() const
+{
+ QStringList names;
+ QList<Parameter>::ConstIterator p = parameters().begin();
+ while (p != parameters().end()) {
+ names << (*p).name();
+ ++p;
+ }
+ return names;
+}
+
+/*!
+ \class PropertyNode
+ */
+
+/*!
+ */
+PropertyNode::PropertyNode(InnerNode *parent, const QString& name)
+ : LeafNode(Property, parent, name),
+ sto(Trool_Default),
+ des(Trool_Default),
+ overrides(0)
+{
+}
+
+/*!
+ */
+void PropertyNode::setOverriddenFrom(const PropertyNode *baseProperty)
+{
+ for (int i = 0; i < NumFunctionRoles; ++i) {
+ if (funcs[i].isEmpty())
+ funcs[i] = baseProperty->funcs[i];
+ }
+ if (sto == Trool_Default)
+ sto = baseProperty->sto;
+ if (des == Trool_Default)
+ des = baseProperty->des;
+ overrides = baseProperty;
+}
+
+/*!
+ */
+QString PropertyNode::qualifiedDataType() const
+{
+ if (setters().isEmpty() && resetters().isEmpty()) {
+ if (dt.contains("*") || dt.contains("&")) {
+ // 'QWidget *' becomes 'QWidget *' const
+ return dt + " const";
+ }
+ else {
+ /*
+ 'int' becomes 'const int' ('int const' is
+ correct C++, but looks wrong)
+ */
+ return "const " + dt;
+ }
+ }
+ else {
+ return dt;
+ }
+}
+
+/*!
+ */
+PropertyNode::Trool PropertyNode::toTrool(bool boolean)
+{
+ return boolean ? Trool_True : Trool_False;
+}
+
+/*!
+ */
+bool PropertyNode::fromTrool(Trool troolean, bool defaultValue)
+{
+ switch (troolean) {
+ case Trool_True:
+ return true;
+ case Trool_False:
+ return false;
+ default:
+ return defaultValue;
+ }
+}
+
+/*!
+ \class TargetNode
+ */
+
+/*!
+ */
+TargetNode::TargetNode(InnerNode *parent, const QString& name)
+ : LeafNode(Target, parent, name)
+{
+}
+
+/*!
+ Returns false because this is a TargetNode.
+ */
+bool TargetNode::isInnerNode() const
+{
+ return false;
+}
+
+QT_END_NAMESPACE
diff --git a/tools/qdoc3/node.h b/tools/qdoc3/node.h
new file mode 100644
index 0000000..c34b82b
--- /dev/null
+++ b/tools/qdoc3/node.h
@@ -0,0 +1,587 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*
+ node.h
+*/
+
+#ifndef NODE_H
+#define NODE_H
+
+#include <qdir.h>
+#include <qmap.h>
+#include <qpair.h>
+#include <qstringlist.h>
+
+#include "codechunk.h"
+#include "doc.h"
+#include "location.h"
+#include "text.h"
+
+QT_BEGIN_NAMESPACE
+
+class InnerNode;
+
+class Node
+{
+ public:
+ enum Type {
+ Namespace,
+ Class,
+ Fake,
+ Enum,
+ Typedef,
+ Function,
+ Property,
+ Variable,
+ Target
+ };
+
+ enum Access { Public, Protected, Private };
+
+ enum Status {
+ Compat,
+ Obsolete,
+ Deprecated,
+ Preliminary,
+ Commendable,
+ Main,
+ Internal
+ }; // don't reorder thisw enum
+
+ enum ThreadSafeness {
+ UnspecifiedSafeness,
+ NonReentrant,
+ Reentrant,
+ ThreadSafe
+ };
+
+ enum LinkType {
+ StartLink,
+ NextLink,
+ PreviousLink,
+ ContentsLink,
+ IndexLink /*,
+ GlossaryLink,
+ CopyrightLink,
+ ChapterLink,
+ SectionLink,
+ SubsectionLink,
+ AppendixLink */
+ };
+
+ virtual ~Node();
+
+ void setAccess(Access access) { acc = access; }
+ void setLocation(const Location& location) { loc = location; }
+ void setDoc(const Doc& doc, bool replace = false);
+ void setStatus(Status status) { sta = status; }
+ void setThreadSafeness(ThreadSafeness safeness) { saf = safeness; }
+ void setSince(const QString &since) { sinc = since; }
+ void setRelates(InnerNode *pseudoParent);
+ void setModuleName(const QString &module) { mod = module; }
+ void setLink(LinkType linkType, const QString &link, const QString &desc);
+ void setUrl(const QString &url);
+ void setTemplateStuff(const QString &templateStuff) { tpl = templateStuff; }
+
+ virtual bool isInnerNode() const = 0;
+ Type type() const { return typ; }
+ InnerNode *parent() const { return par; }
+ InnerNode *relates() const { return rel; }
+ const QString& name() const { return nam; }
+ QMap<LinkType, QPair<QString,QString> > links() const { return linkMap; }
+ QString moduleName() const;
+ QString url() const;
+
+ Access access() const { return acc; }
+ const Location& location() const { return loc; }
+ const Doc& doc() const { return d; }
+ Status status() const { return sta; }
+ Status inheritedStatus() const;
+ ThreadSafeness threadSafeness() const;
+ ThreadSafeness inheritedThreadSafeness() const;
+ QString since() const { return sinc; }
+ QString templateStuff() const { return tpl; }
+
+ void clearRelated() { rel = 0; }
+
+ QString fileBase() const;
+
+ protected:
+ Node(Type type, InnerNode *parent, const QString& name);
+
+ private:
+#ifdef Q_WS_WIN
+ Type typ;
+ Access acc;
+ Status sta;
+ ThreadSafeness saf;
+#else
+ Type typ : 4;
+ Access acc : 2;
+ Status sta : 3;
+ ThreadSafeness saf : 2;
+#endif
+ InnerNode *par;
+ InnerNode *rel;
+ QString nam;
+ Location loc;
+ Doc d;
+ QMap<LinkType, QPair<QString, QString> > linkMap;
+ QString mod;
+ QString u;
+ QString sinc;
+ QString tpl;
+};
+
+class FunctionNode;
+class EnumNode;
+
+typedef QList<Node *> NodeList;
+
+class InnerNode : public Node
+{
+ public:
+ ~InnerNode();
+
+ Node *findNode(const QString& name);
+ Node *findNode(const QString& name, Type type);
+ FunctionNode *findFunctionNode(const QString& name);
+ FunctionNode *findFunctionNode(const FunctionNode *clone);
+ void addInclude(const QString &include);
+ void setIncludes(const QStringList &includes);
+ void setOverload(const FunctionNode *func, bool overlode);
+ void normalizeOverloads();
+ void makeUndocumentedChildrenInternal();
+ void deleteChildren();
+ void removeFromRelated();
+
+ virtual bool isInnerNode() const;
+ const Node *findNode(const QString& name) const;
+ const Node *findNode(const QString& name, Type type) const;
+ const FunctionNode *findFunctionNode(const QString& name) const;
+ const FunctionNode *findFunctionNode(const FunctionNode *clone) const;
+ const EnumNode *findEnumNodeForValue(const QString &enumValue) const;
+ const NodeList & childNodes() const { return children; }
+ const NodeList & relatedNodes() const { return related; }
+ int overloadNumber(const FunctionNode *func) const;
+ int numOverloads(const QString& funcName) const;
+ NodeList overloads(const QString &funcName) const;
+ const QStringList& includes() const { return inc; }
+
+ protected:
+ InnerNode(Type type, InnerNode *parent, const QString& name);
+
+ private:
+ friend class Node;
+
+ static bool isSameSignature(const FunctionNode *f1, const FunctionNode *f2);
+ void addChild(Node *child);
+ void removeChild(Node *child);
+ void removeRelated(Node *pseudoChild);
+
+ QStringList inc;
+ NodeList children;
+ NodeList enumChildren;
+ NodeList related;
+ QMap<QString, Node *> childMap;
+ QMap<QString, Node *> primaryFunctionMap;
+ QMap<QString, NodeList> secondaryFunctionMap;
+};
+
+class LeafNode : public Node
+{
+ public:
+ LeafNode();
+
+ virtual bool isInnerNode() const;
+
+ protected:
+ LeafNode(Type type, InnerNode *parent, const QString& name);
+};
+
+class NamespaceNode : public InnerNode
+{
+ public:
+ NamespaceNode(InnerNode *parent, const QString& name);
+};
+
+class ClassNode;
+
+struct RelatedClass
+{
+ RelatedClass() { }
+ RelatedClass(Node::Access access0,
+ ClassNode* node0,
+ const QString& dataTypeWithTemplateArgs0 = "")
+ : access(access0),
+ node(node0),
+ dataTypeWithTemplateArgs(dataTypeWithTemplateArgs0) { }
+
+ Node::Access access;
+ ClassNode* node;
+ QString dataTypeWithTemplateArgs;
+};
+
+class ClassNode : public InnerNode
+{
+ public:
+ ClassNode(InnerNode *parent, const QString& name);
+
+ void addBaseClass(Access access,
+ ClassNode *node,
+ const QString &dataTypeWithTemplateArgs = "");
+ void fixBaseClasses();
+
+ const QList<RelatedClass> &baseClasses() const { return bas; }
+ const QList<RelatedClass> &derivedClasses() const { return der; }
+
+ bool hideFromMainList() const { return hidden; }
+ void setHideFromMainList(bool value) { hidden = value; }
+
+ QString serviceName() const { return sname; }
+ void setServiceName(const QString& value) { sname = value; }
+
+ private:
+ QList<RelatedClass> bas;
+ QList<RelatedClass> der;
+ bool hidden;
+ QString sname;
+};
+
+class FakeNode : public InnerNode
+{
+ public:
+ enum SubType {
+ Example,
+ HeaderFile,
+ File,
+ Group,
+ Module,
+ Page,
+ ExternalPage,
+ QmlClass
+ };
+
+ FakeNode(InnerNode *parent, const QString& name, SubType subType);
+
+ void setTitle(const QString &title) { tle = title; }
+ void setSubTitle(const QString &subTitle) { stle = subTitle; }
+ void addGroupMember(Node *node) { gr.append(node); }
+
+ SubType subType() const { return sub; }
+ QString title() const { return tle; }
+ QString fullTitle() const;
+ QString subTitle() const;
+ const NodeList &groupMembers() const { return gr; }
+
+ private:
+ SubType sub;
+ QString tle;
+ QString stle;
+ NodeList gr;
+};
+
+class QmlNode : public FakeNode
+{
+ public:
+ QmlNode(InnerNode *parent, const QString& name, const ClassNode* cn)
+ : FakeNode(parent, name, QmlClass), cnode(cn) { }
+
+ const ClassNode* classNode() const { return cnode; }
+
+ private:
+ const ClassNode* cnode;
+};
+
+class EnumItem
+{
+ public:
+ EnumItem() { }
+ EnumItem(const QString& name, const QString& value)
+ : nam(name), val(value) { }
+ EnumItem(const QString& name, const QString& value, const Text &txt)
+ : nam(name), val(value), txt(txt) { }
+
+ const QString& name() const { return nam; }
+ const QString& value() const { return val; }
+ const Text &text() const { return txt; }
+
+ private:
+ QString nam;
+ QString val;
+ Text txt;
+};
+
+class TypedefNode;
+
+class EnumNode : public LeafNode
+{
+ public:
+ EnumNode(InnerNode *parent, const QString& name);
+
+ void addItem(const EnumItem& item);
+ void setFlagsType(TypedefNode *typedeff);
+ bool hasItem(const QString &name) const { return names.contains(name); }
+
+ const QList<EnumItem>& items() const { return itms; }
+ Access itemAccess(const QString& name) const;
+ const TypedefNode *flagsType() const { return ft; }
+ QString itemValue(const QString &name) const;
+
+ private:
+ QList<EnumItem> itms;
+ QSet<QString> names;
+ const TypedefNode *ft;
+};
+
+class TypedefNode : public LeafNode
+{
+ public:
+ TypedefNode(InnerNode *parent, const QString& name);
+
+ const EnumNode *associatedEnum() const { return ae; }
+
+ private:
+ void setAssociatedEnum(const EnumNode *enume);
+
+ friend class EnumNode;
+
+ const EnumNode *ae;
+};
+
+inline void EnumNode::setFlagsType(TypedefNode *typedeff)
+{
+ ft = typedeff;
+ typedeff->setAssociatedEnum(this);
+}
+
+
+class Parameter
+{
+ public:
+ Parameter() {}
+ Parameter(const QString& leftType, const QString& rightType = "",
+ const QString& name = "", const QString& defaultValue = "");
+ Parameter(const Parameter& p);
+
+ Parameter& operator=(const Parameter& p);
+
+ void setName(const QString& name) { nam = name; }
+
+ bool hasType() const { return lef.length() + rig.length() > 0; }
+ const QString& leftType() const { return lef; }
+ const QString& rightType() const { return rig; }
+ const QString& name() const { return nam; }
+ const QString& defaultValue() const { return def; }
+
+ private:
+ QString lef;
+ QString rig;
+ QString nam;
+ QString def;
+};
+
+class PropertyNode;
+
+class FunctionNode : public LeafNode
+{
+ public:
+ enum Metaness {
+ Plain,
+ Signal,
+ Slot,
+ Ctor,
+ Dtor,
+ MacroWithParams,
+ MacroWithoutParams,
+ Native };
+ enum Virtualness { NonVirtual, ImpureVirtual, PureVirtual };
+
+ FunctionNode(InnerNode *parent, const QString &name);
+
+ void setReturnType(const QString& returnType) { rt = returnType; }
+ void setMetaness(Metaness metaness) { met = metaness; }
+ void setVirtualness(Virtualness virtualness) { vir = virtualness; }
+ void setConst(bool conste) { con = conste; }
+ void setStatic(bool statique) { sta = statique; }
+ void setOverload(bool overlode);
+ void addParameter(const Parameter& parameter);
+ inline void setParameters(const QList<Parameter>& parameters);
+ void borrowParameterNames(const FunctionNode *source);
+ void setReimplementedFrom(FunctionNode *from);
+
+ const QString& returnType() const { return rt; }
+ Metaness metaness() const { return met; }
+ bool isMacro() const {
+ return met == MacroWithParams || met == MacroWithoutParams;
+ }
+ Virtualness virtualness() const { return vir; }
+ bool isConst() const { return con; }
+ bool isStatic() const { return sta; }
+ bool isOverload() const { return ove; }
+ int overloadNumber() const;
+ int numOverloads() const;
+ const QList<Parameter>& parameters() const { return params; }
+ QStringList parameterNames() const;
+ const FunctionNode *reimplementedFrom() const { return rf; }
+ const QList<FunctionNode *> &reimplementedBy() const { return rb; }
+ const PropertyNode *associatedProperty() const { return ap; }
+
+ private:
+ void setAssociatedProperty(PropertyNode *property);
+
+ friend class InnerNode;
+ friend class PropertyNode;
+
+ QString rt;
+#ifdef Q_WS_WIN
+ Metaness met;
+ Virtualness vir;
+#else
+ Metaness met : 4;
+ Virtualness vir : 2;
+#endif
+ bool con : 1;
+ bool sta : 1;
+ bool ove : 1;
+ QList<Parameter> params;
+ const FunctionNode *rf;
+ const PropertyNode *ap;
+ QList<FunctionNode *> rb;
+};
+
+class PropertyNode : public LeafNode
+{
+ public:
+ enum FunctionRole { Getter, Setter, Resetter };
+ enum { NumFunctionRoles = Resetter + 1 };
+
+ PropertyNode(InnerNode *parent, const QString& name);
+
+ void setDataType(const QString& dataType) { dt = dataType; }
+ void addFunction(FunctionNode *function, FunctionRole role);
+ void setStored(bool stored) { sto = toTrool(stored); }
+ void setDesignable(bool designable) { des = toTrool(designable); }
+ void setOverriddenFrom(const PropertyNode *baseProperty);
+
+ const QString &dataType() const { return dt; }
+ QString qualifiedDataType() const;
+ NodeList functions() const;
+ NodeList functions(FunctionRole role) const { return funcs[(int)role]; }
+ NodeList getters() const { return functions(Getter); }
+ NodeList setters() const { return functions(Setter); }
+ NodeList resetters() const { return functions(Resetter); }
+ bool isStored() const { return fromTrool(sto, storedDefault()); }
+ bool isDesignable() const { return fromTrool(des, designableDefault()); }
+ const PropertyNode *overriddenFrom() const { return overrides; }
+
+ private:
+ enum Trool { Trool_True, Trool_False, Trool_Default };
+
+ static Trool toTrool(bool boolean);
+ static bool fromTrool(Trool troolean, bool defaultValue);
+
+ bool storedDefault() const { return true; }
+ bool designableDefault() const { return !setters().isEmpty(); }
+
+ QString dt;
+ NodeList funcs[NumFunctionRoles];
+ Trool sto;
+ Trool des;
+ const PropertyNode *overrides;
+};
+
+inline void FunctionNode::setParameters(const QList<Parameter> &parameters)
+{
+ params = parameters;
+}
+
+inline void PropertyNode::addFunction(FunctionNode *function, FunctionRole role)
+{
+ funcs[(int)role].append(function);
+ function->setAssociatedProperty(this);
+}
+
+inline NodeList PropertyNode::functions() const
+{
+ NodeList list;
+ for (int i = 0; i < NumFunctionRoles; ++i)
+ list += funcs[i];
+ return list;
+}
+
+class VariableNode : public LeafNode
+{
+ public:
+ VariableNode(InnerNode *parent, const QString &name);
+
+ void setLeftType(const QString &leftType) { lt = leftType; }
+ void setRightType(const QString &rightType) { rt = rightType; }
+ void setStatic(bool statique) { sta = statique; }
+
+ const QString &leftType() const { return lt; }
+ const QString &rightType() const { return rt; }
+ QString dataType() const { return lt + rt; }
+ bool isStatic() const { return sta; }
+
+ private:
+ QString lt;
+ QString rt;
+ bool sta;
+};
+
+inline VariableNode::VariableNode(InnerNode *parent, const QString &name)
+ : LeafNode(Variable, parent, name), sta(false)
+{
+}
+
+class TargetNode : public LeafNode
+{
+ public:
+ TargetNode(InnerNode *parent, const QString& name);
+
+ virtual bool isInnerNode() const;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/tools/qdoc3/openedlist.cpp b/tools/qdoc3/openedlist.cpp
new file mode 100644
index 0000000..04d9871
--- /dev/null
+++ b/tools/qdoc3/openedlist.cpp
@@ -0,0 +1,228 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*
+ openedlist.cpp
+*/
+
+#include <qregexp.h>
+
+#include "atom.h"
+#include "openedlist.h"
+
+QT_BEGIN_NAMESPACE
+
+static const char roman[] = "m\2d\5c\2l\5x\2v\5i";
+
+OpenedList::OpenedList( Style style )
+ : sty( style ), ini( 1 ), nex( 0 )
+{
+}
+
+OpenedList::OpenedList( const Location& location, const QString& hint )
+ : sty( Bullet ), ini( 1 )
+{
+ QRegExp hintSyntax( "(\\W*)([0-9]+|[A-Z]+|[a-z]+)(\\W*)" );
+
+ if ( hintSyntax.exactMatch(hint) ) {
+ bool ok;
+ int asNumeric = hint.toInt( &ok );
+ int asRoman = fromRoman( hintSyntax.cap(2) );
+ int asAlpha = fromAlpha( hintSyntax.cap(2) );
+
+ if ( ok ) {
+ sty = Numeric;
+ ini = asNumeric;
+ } else if ( asRoman > 0 && asRoman != 100 && asRoman != 500 ) {
+ sty = ( hint == hint.toLower() ) ? LowerRoman : UpperRoman;
+ ini = asRoman;
+ } else {
+ sty = ( hint == hint.toLower() ) ? LowerAlpha : UpperAlpha;
+ ini = asAlpha;
+ }
+ pref = hintSyntax.cap( 1 );
+ suff = hintSyntax.cap( 3 );
+ } else if ( !hint.isEmpty() ) {
+ location.warning( tr("Unrecognized list style '%1'").arg(hint) );
+ }
+ nex = ini - 1;
+}
+
+QString OpenedList::styleString() const
+{
+ switch ( style() ) {
+ case Bullet:
+ default:
+ return ATOM_LIST_BULLET;
+ case Tag:
+ return ATOM_LIST_TAG;
+ case Value:
+ return ATOM_LIST_VALUE;
+ case Numeric:
+ return ATOM_LIST_NUMERIC;
+ case UpperAlpha:
+ return ATOM_LIST_UPPERALPHA;
+ case LowerAlpha:
+ return ATOM_LIST_LOWERALPHA;
+ case UpperRoman:
+ return ATOM_LIST_UPPERROMAN;
+ case LowerRoman:
+ return ATOM_LIST_LOWERROMAN;
+ }
+}
+
+QString OpenedList::numberString() const
+{
+ return QString::number( number() );
+/*
+ switch ( style() ) {
+ case Numeric:
+ return QString::number( number() );
+ case UpperAlpha:
+ return toAlpha( number() ).toUpper();
+ case LowerAlpha:
+ return toAlpha( number() );
+ case UpperRoman:
+ return toRoman( number() ).toUpper();
+ case LowerRoman:
+ return toRoman( number() );
+ case Bullet:
+ default:
+ return "*";
+ }*/
+}
+
+QString OpenedList::toAlpha( int n )
+{
+ QString str;
+
+ while ( n > 0 ) {
+ n--;
+ str.prepend( (n % 26) + 'a' );
+ n /= 26;
+ }
+ return str;
+}
+
+int OpenedList::fromAlpha( const QString& str )
+{
+ int n = 0;
+ int u;
+
+ for ( int i = 0; i < (int) str.length(); i++ ) {
+ u = str[i].toLower().unicode();
+ if ( u >= 'a' && u <= 'z' ) {
+ n *= 26;
+ n += u - 'a' + 1;
+ } else {
+ return 0;
+ }
+ }
+ return n;
+}
+
+QString OpenedList::toRoman( int n )
+{
+ /*
+ See p. 30 of Donald E. Knuth's "TeX: The Program".
+ */
+ QString str;
+ int j = 0;
+ int k;
+ int u;
+ int v = 1000;
+
+ for ( ;; ) {
+ while ( n >= v ) {
+ str += roman[j];
+ n -= v;
+ }
+
+ if ( n <= 0 )
+ break;
+
+ k = j + 2;
+ u = v / roman[k - 1];
+ if ( roman[k - 1] == 2 ) {
+ k += 2;
+ u /= 5;
+ }
+ if ( n + u >= v ) {
+ str += roman[k];
+ n += u;
+ } else {
+ j += 2;
+ v /= roman[j - 1];
+ }
+ }
+ return str;
+}
+
+int OpenedList::fromRoman( const QString& str )
+{
+ int n = 0;
+ int j;
+ int u;
+ int v = 0;
+
+ for ( int i = str.length() - 1; i >= 0; i-- ) {
+ j = 0;
+ u = 1000;
+ while ( roman[j] != 'i' && roman[j] != str[i].toLower() ) {
+ j += 2;
+ u /= roman[j - 1];
+ }
+ if ( u < v ) {
+ n -= u;
+ } else {
+ n += u;
+ }
+ v = u;
+ }
+
+ if ( str.toLower() == toRoman(n) ) {
+ return n;
+ } else {
+ return 0;
+ }
+}
+
+QT_END_NAMESPACE
diff --git a/tools/qdoc3/openedlist.h b/tools/qdoc3/openedlist.h
new file mode 100644
index 0000000..0506e79
--- /dev/null
+++ b/tools/qdoc3/openedlist.h
@@ -0,0 +1,91 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*
+ openedlist.h
+*/
+
+#ifndef OPENEDLIST_H
+#define OPENEDLIST_H
+
+#include <qstring.h>
+
+#include "location.h"
+
+QT_BEGIN_NAMESPACE
+
+class OpenedList
+{
+public:
+ enum Style { Bullet, Tag, Value, Numeric, UpperAlpha, LowerAlpha,
+ UpperRoman, LowerRoman };
+
+ OpenedList()
+ : sty( Bullet ), ini( 1 ), nex( 0 ) { }
+ OpenedList( Style style );
+ OpenedList( const Location& location, const QString& hint );
+
+ void next() { nex++; }
+
+ bool isStarted() const { return nex >= ini; }
+ Style style() const { return sty; }
+ QString styleString() const;
+ int number() const { return nex; }
+ QString numberString() const;
+ QString prefix() const { return pref; }
+ QString suffix() const { return suff; }
+
+private:
+ static QString toAlpha( int n );
+ static int fromAlpha( const QString& str );
+ static QString toRoman( int n );
+ static int fromRoman( const QString& str );
+
+ Style sty;
+ int ini;
+ int nex;
+ QString pref;
+ QString suff;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/tools/qdoc3/pagegenerator.cpp b/tools/qdoc3/pagegenerator.cpp
new file mode 100644
index 0000000..2d50279
--- /dev/null
+++ b/tools/qdoc3/pagegenerator.cpp
@@ -0,0 +1,219 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*
+ pagegenerator.cpp
+*/
+
+#include <QtCore>
+#include <qfile.h>
+#include <qfileinfo.h>
+
+#include "pagegenerator.h"
+#include "tree.h"
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ Nothing to do in the constructor.
+ */
+PageGenerator::PageGenerator()
+{
+ // nothing.
+}
+
+/*!
+ The destructor
+ */
+PageGenerator::~PageGenerator()
+{
+ while (!outStreamStack.isEmpty())
+ endSubPage();
+}
+
+/*!
+ This function is recursive.
+ */
+void PageGenerator::generateTree(const Tree *tree, CodeMarker *marker)
+{
+ generateInnerNode(tree->root(), marker);
+}
+
+QString PageGenerator::fileBase(const Node *node)
+{
+ if (node->relates())
+ node = node->relates();
+ else if (!node->isInnerNode())
+ node = node->parent();
+
+ QString base = node->doc().baseName();
+ if (!base.isEmpty())
+ return base;
+
+ const Node *p = node;
+
+ forever {
+ base.prepend(p->name());
+ const Node *pp = p->parent();
+ if (!pp || pp->name().isEmpty() || pp->type() == Node::Fake)
+ break;
+ base.prepend(QLatin1Char('-'));
+ p = pp;
+ }
+
+ if (node->type() == Node::Fake) {
+#ifdef QDOC2_COMPAT
+ if (base.endsWith(".html"))
+ base.truncate(base.length() - 5);
+#endif
+ }
+
+ // the code below is effectively equivalent to:
+ // base.replace(QRegExp("[^A-Za-z0-9]+"), " ");
+ // base = base.trimmed();
+ // base.replace(" ", "-");
+ // base = base.toLower();
+ // as this function accounted for ~8% of total running time
+ // we optimize a bit...
+
+ QString res;
+ // +5 prevents realloc in fileName() below
+ res.reserve(base.size() + 5);
+ bool begun = false;
+ for (int i = 0; i != base.size(); ++i) {
+ QChar c = base.at(i);
+ uint u = c.unicode();
+ if (u >= 'A' && u <= 'Z')
+ u -= 'A' - 'a';
+ if ((u >= 'a' && u <= 'z') || (u >= '0' && u <= '9')) {
+ res += QLatin1Char(u);
+ begun = true;
+ } else if (begun) {
+ res += QLatin1Char('-');
+ begun = false;
+ }
+ }
+ while (res.endsWith(QLatin1Char('-')))
+ res.chop(1);
+ return res;
+}
+
+QString PageGenerator::fileName(const Node *node)
+{
+ if (!node->url().isEmpty())
+ return node->url();
+
+ QString name = fileBase(node);
+ name += QLatin1Char('.');
+ name += fileExtension(node);
+ return name;
+}
+
+QString PageGenerator::outFileName()
+{
+ return QFileInfo(static_cast<QFile *>(out().device())->fileName()).fileName();
+}
+
+void PageGenerator::beginSubPage(const Location& location,
+ const QString& fileName)
+{
+ QFile *outFile = new QFile(outputDir() + "/" + fileName);
+ if (!outFile->open(QFile::WriteOnly))
+ location.fatal(tr("Cannot open output file '%1'")
+ .arg(outFile->fileName()));
+ QTextStream *out = new QTextStream(outFile);
+ out->setCodec("ISO-8859-1");
+ outStreamStack.push(out);
+}
+
+void PageGenerator::endSubPage()
+{
+ outStreamStack.top()->flush();
+ delete outStreamStack.top()->device();
+ delete outStreamStack.pop();
+}
+
+QTextStream &PageGenerator::out()
+{
+ return *outStreamStack.top();
+}
+
+/*!
+ Recursive writing of html files from the root \a node.
+ */
+void PageGenerator::generateInnerNode(const InnerNode *node,
+ CodeMarker *marker)
+{
+ if (!node->url().isNull())
+ return;
+
+ if (node->type() == Node::Fake) {
+ const FakeNode *fakeNode = static_cast<const FakeNode *>(node);
+ if (fakeNode->subType() == FakeNode::ExternalPage)
+ return;
+ }
+
+ if (node->parent() != 0) {
+ beginSubPage(node->location(), fileName(node));
+ if (node->type() == Node::Namespace || node->type() == Node::Class) {
+ generateClassLikeNode(node, marker);
+ }
+ else if (node->type() == Node::Fake) {
+ const FakeNode* fakeNode = static_cast<const FakeNode *>(node);
+#ifdef QDOC_QML
+ if (fakeNode->subType() == FakeNode::QmlClass) {
+ //qDebug() << "FILENAME:" << fileName(node);
+ }
+#endif
+ generateFakeNode(static_cast<const FakeNode *>(node), marker);
+ }
+ endSubPage();
+ }
+
+ NodeList::ConstIterator c = node->childNodes().begin();
+ while (c != node->childNodes().end()) {
+ if ((*c)->isInnerNode() && (*c)->access() != Node::Private)
+ generateInnerNode((const InnerNode *) *c, marker);
+ ++c;
+ }
+}
+
+QT_END_NAMESPACE
diff --git a/tools/qdoc3/pagegenerator.h b/tools/qdoc3/pagegenerator.h
new file mode 100644
index 0000000..002d390
--- /dev/null
+++ b/tools/qdoc3/pagegenerator.h
@@ -0,0 +1,85 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*
+ pagegenerator.h
+*/
+
+#ifndef PAGEGENERATOR_H
+#define PAGEGENERATOR_H
+
+#include <QStack>
+#include <qtextstream.h>
+
+#include "generator.h"
+#include "location.h"
+
+QT_BEGIN_NAMESPACE
+
+class ClassNode;
+class InnerNode;
+class NamespaceNode;
+
+class PageGenerator : public Generator
+{
+ public:
+ PageGenerator();
+ ~PageGenerator();
+
+ virtual void generateTree(const Tree *tree, CodeMarker *marker);
+
+ protected:
+ virtual QString fileBase(const Node *node);
+ virtual QString fileExtension(const Node *node) = 0;
+ QString fileName(const Node *node);
+ QString outFileName();
+ void beginSubPage(const Location& location, const QString& fileName);
+ void endSubPage();
+ virtual void generateInnerNode(const InnerNode *node, CodeMarker *marker);
+ QTextStream& out();
+
+ private:
+ QStack<QTextStream *> outStreamStack;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/tools/qdoc3/plaincodemarker.cpp b/tools/qdoc3/plaincodemarker.cpp
new file mode 100644
index 0000000..5ed4bbe
--- /dev/null
+++ b/tools/qdoc3/plaincodemarker.cpp
@@ -0,0 +1,139 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "plaincodemarker.h"
+
+QT_BEGIN_NAMESPACE
+
+PlainCodeMarker::PlainCodeMarker()
+{
+}
+
+PlainCodeMarker::~PlainCodeMarker()
+{
+}
+
+bool PlainCodeMarker::recognizeCode( const QString& /* code */ )
+{
+ return true;
+}
+
+bool PlainCodeMarker::recognizeExtension( const QString& /* ext */ )
+{
+ return true;
+}
+
+bool PlainCodeMarker::recognizeLanguage( const QString& /* lang */ )
+{
+ return false;
+}
+
+QString PlainCodeMarker::plainName( const Node * /* node */ )
+{
+ return "";
+}
+
+QString PlainCodeMarker::plainFullName(const Node * /* node */, const Node * /* relative */)
+{
+ return "";
+}
+
+QString PlainCodeMarker::markedUpCode( const QString& code,
+ const Node * /* relative */,
+ const QString& /* dirPath */ )
+{
+ return protect( code );
+}
+
+QString PlainCodeMarker::markedUpSynopsis( const Node * /* node */,
+ const Node * /* relative */,
+ SynopsisStyle /* style */ )
+{
+ return "foo";
+}
+
+QString PlainCodeMarker::markedUpName( const Node * /* node */ )
+{
+ return "";
+}
+
+QString PlainCodeMarker::markedUpFullName( const Node * /* node */,
+ const Node * /* relative */ )
+{
+ return "";
+}
+
+QString PlainCodeMarker::markedUpEnumValue(const QString & /* enumValue */,
+ const Node * /* relative */)
+{
+ return "";
+}
+
+QString PlainCodeMarker::markedUpIncludes( const QStringList& /* includes */ )
+{
+ return "";
+}
+
+QString PlainCodeMarker::functionBeginRegExp( const QString& /* funcName */ )
+{
+ return "";
+}
+
+QString PlainCodeMarker::functionEndRegExp( const QString& /* funcName */ )
+{
+ return "";
+}
+
+QList<Section> PlainCodeMarker::sections(const InnerNode * /* innerNode */,
+ SynopsisStyle /* style */,
+ Status /* status */)
+{
+ return QList<Section>();
+}
+
+const Node *PlainCodeMarker::resolveTarget( const QString& /* target */,
+ const Tree * /* tree */,
+ const Node * /* relative */ )
+{
+ return 0;
+}
+
+QT_END_NAMESPACE
diff --git a/tools/qdoc3/plaincodemarker.h b/tools/qdoc3/plaincodemarker.h
new file mode 100644
index 0000000..24a1dfe
--- /dev/null
+++ b/tools/qdoc3/plaincodemarker.h
@@ -0,0 +1,79 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*
+ plaincodemarker.h
+*/
+
+#ifndef PLAINCODEMARKER_H
+#define PLAINCODEMARKER_H
+
+#include "codemarker.h"
+
+QT_BEGIN_NAMESPACE
+
+class PlainCodeMarker : public CodeMarker
+{
+public:
+ PlainCodeMarker();
+ ~PlainCodeMarker();
+
+ bool recognizeCode( const QString& code );
+ bool recognizeExtension( const QString& ext );
+ bool recognizeLanguage( const QString& lang );
+ QString plainName( const Node *node );
+ QString plainFullName( const Node *node, const Node *relative );
+ QString markedUpCode( const QString& code, const Node *relative, const QString& dirPath );
+ QString markedUpSynopsis( const Node *node, const Node *relative,
+ SynopsisStyle style );
+ QString markedUpName( const Node *node );
+ QString markedUpFullName( const Node *node, const Node *relative );
+ QString markedUpEnumValue(const QString &enumValue, const Node *relative);
+ QString markedUpIncludes( const QStringList& includes );
+ QString functionBeginRegExp( const QString& funcName );
+ QString functionEndRegExp( const QString& funcName );
+ QList<Section> sections(const InnerNode *innerNode, SynopsisStyle style, Status status);
+ const Node *resolveTarget(const QString &target, const Tree *tree, const Node *relative);
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/tools/qdoc3/polyarchiveextractor.cpp b/tools/qdoc3/polyarchiveextractor.cpp
new file mode 100644
index 0000000..b7fdfef
--- /dev/null
+++ b/tools/qdoc3/polyarchiveextractor.cpp
@@ -0,0 +1,94 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*
+ polyarchiveextractor.cpp
+*/
+
+#include "command.h"
+#include "polyarchiveextractor.h"
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class PolyArchiveExtractor
+
+ \brief The PolyArchiveExtractor class is a class for unpacking
+ archive files.
+
+ This subclass of ArchiveExtractor contains a parameterized
+ command for doing the archive extraction.
+
+ It has an extractArchive() function you call to do the
+ actual archive extraction.
+ */
+
+/*!
+ The constructor takes the list of filename \a extensions,
+ which it passes to the base class, and the \a commandFormat,
+ which it stores locally. The \a commandFormat is a command
+ template string.
+ */
+PolyArchiveExtractor::PolyArchiveExtractor( const QStringList& extensions,
+ const QString& commandFormat )
+ : ArchiveExtractor( extensions ), cmd( commandFormat )
+{
+}
+
+/*!
+ The destructor doesn't have to do anything.
+ */
+PolyArchiveExtractor::~PolyArchiveExtractor()
+{
+}
+
+/*!
+ Call this function to do the actual archive extraction. It calls
+ the executeCommand() function to do the work. That's all it does.
+ */
+void PolyArchiveExtractor::extractArchive( const Location& location,
+ const QString& filePath,
+ const QString& outputDir )
+{
+ executeCommand( location, cmd, QStringList() << filePath << outputDir );
+}
+
+QT_END_NAMESPACE
diff --git a/tools/qdoc3/polyarchiveextractor.h b/tools/qdoc3/polyarchiveextractor.h
new file mode 100644
index 0000000..9c637da
--- /dev/null
+++ b/tools/qdoc3/polyarchiveextractor.h
@@ -0,0 +1,70 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*
+ polyarchiveextractor.h
+*/
+
+#ifndef POLYARCHIVEEXTRACTOR_H
+#define POLYARCHIVEEXTRACTOR_H
+
+#include "archiveextractor.h"
+
+QT_BEGIN_NAMESPACE
+
+class PolyArchiveExtractor : public ArchiveExtractor
+{
+ public:
+ PolyArchiveExtractor(const QStringList& extensions,
+ const QString& commandFormat);
+ ~PolyArchiveExtractor();
+
+ virtual void extractArchive(const Location& location,
+ const QString& filePath,
+ const QString& outputDir);
+
+ private:
+ QString cmd;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/tools/qdoc3/polyuncompressor.cpp b/tools/qdoc3/polyuncompressor.cpp
new file mode 100644
index 0000000..c08ede6
--- /dev/null
+++ b/tools/qdoc3/polyuncompressor.cpp
@@ -0,0 +1,109 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "command.h"
+#include "polyuncompressor.h"
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class PolyUncompressor
+
+ \brief The PolyUncompressor class is a class for uncompressing
+ compressed files.
+
+ This subclass of Uncompressor contains a parameterized
+ command for doing the uncompression
+
+ It has an uncompressFile() function you call to do the
+ actual uncompression.
+ */
+
+/*!
+ The constructor takes the list of filename \a extensions,
+ which it passes to the base class, and the \a commandFormat,
+ which it stores locally. The \a commandFormat is a command
+ template string.
+ */
+PolyUncompressor::PolyUncompressor( const QStringList& extensions,
+ const QString& commandFormat )
+ : Uncompressor( extensions ), cmd( commandFormat )
+{
+}
+
+/*!
+ The destructor doesn't have to do anything.
+ */
+PolyUncompressor::~PolyUncompressor()
+{
+}
+
+/*!
+ From \a filePath, derive a file path for the uncompressed
+ file and return it. If it can't figure out what the file
+ path should be, it just concatenates ".out" to the
+ \a filePath and returns that.
+ */
+QString PolyUncompressor::uncompressedFilePath( const QString& filePath )
+{
+ QStringList::ConstIterator e = fileExtensions().begin();
+ while ( e != fileExtensions().end() ) {
+ QString dotExt = "." + *e;
+ if ( filePath.endsWith(dotExt) )
+ return filePath.left( filePath.length() - dotExt.length() );
+ ++e;
+ }
+ return filePath + ".out"; // doesn't really matter
+}
+
+/*!
+ Call this function to do the actual uncompressing. It calls
+ the executeCommand() function to do the work. That's all it does.
+ */
+void PolyUncompressor::uncompressFile( const Location& location,
+ const QString& filePath,
+ const QString& outputFilePath )
+{
+ executeCommand( location, cmd,
+ QStringList() << filePath << outputFilePath );
+}
+
+QT_END_NAMESPACE
diff --git a/tools/qdoc3/polyuncompressor.h b/tools/qdoc3/polyuncompressor.h
new file mode 100644
index 0000000..162f672
--- /dev/null
+++ b/tools/qdoc3/polyuncompressor.h
@@ -0,0 +1,71 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*
+ polyuncompressor.h
+*/
+
+#ifndef POLYUNCOMPRESSOR_H
+#define POLYUNCOMPRESSOR_H
+
+#include "uncompressor.h"
+
+QT_BEGIN_NAMESPACE
+
+class PolyUncompressor : public Uncompressor
+{
+ public:
+ PolyUncompressor(const QStringList& extensions,
+ const QString& commandFormat);
+ ~PolyUncompressor();
+
+ virtual QString uncompressedFilePath(const QString& filePath);
+ virtual void uncompressFile(const Location& location,
+ const QString& filePath,
+ const QString& outputFilePath);
+
+ private:
+ QString cmd;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/tools/qdoc3/qdoc3.pro b/tools/qdoc3/qdoc3.pro
new file mode 100644
index 0000000..3268585
--- /dev/null
+++ b/tools/qdoc3/qdoc3.pro
@@ -0,0 +1,108 @@
+DEFINES += QDOC2_COMPAT
+#DEFINES += QT_NO_CAST_TO_ASCII
+#DEFINES += QT_NO_CAST_FROM_ASCII
+
+QT = core xml
+CONFIG += console
+build_all:!build_pass {
+ CONFIG -= build_all
+ CONFIG += release
+}
+mac:CONFIG -= app_bundle
+HEADERS += apigenerator.h \
+ archiveextractor.h \
+ atom.h \
+ bookgenerator.h \
+ ccodeparser.h \
+ codechunk.h \
+ codemarker.h \
+ codeparser.h \
+ command.h \
+ config.h \
+ cppcodemarker.h \
+ cppcodeparser.h \
+ cpptoqsconverter.h \
+ dcfsection.h \
+ doc.h \
+ editdistance.h \
+ generator.h \
+ helpprojectwriter.h \
+ htmlgenerator.h \
+ jambiapiparser.h \
+ javacodemarker.h \
+ javadocgenerator.h \
+ linguistgenerator.h \
+ location.h \
+ loutgenerator.h \
+ mangenerator.h \
+ node.h \
+ openedlist.h \
+ pagegenerator.h \
+ plaincodemarker.h \
+ polyarchiveextractor.h \
+ polyuncompressor.h \
+ qsakernelparser.h \
+ qscodemarker.h \
+ qscodeparser.h \
+ quoter.h \
+ separator.h \
+ sgmlgenerator.h \
+ text.h \
+ tokenizer.h \
+ tr.h \
+ tree.h \
+ uncompressor.h \
+ webxmlgenerator.h
+SOURCES += apigenerator.cpp \
+ archiveextractor.cpp \
+ atom.cpp \
+ bookgenerator.cpp \
+ ccodeparser.cpp \
+ codechunk.cpp \
+ codemarker.cpp \
+ codeparser.cpp \
+ command.cpp \
+ config.cpp \
+ cppcodemarker.cpp \
+ cppcodeparser.cpp \
+ cpptoqsconverter.cpp \
+ dcfsection.cpp \
+ doc.cpp \
+ editdistance.cpp \
+ generator.cpp \
+ helpprojectwriter.cpp \
+ htmlgenerator.cpp \
+ jambiapiparser.cpp \
+ javacodemarker.cpp \
+ javadocgenerator.cpp \
+ linguistgenerator.cpp \
+ location.cpp \
+ loutgenerator.cpp \
+ mangenerator.cpp \
+ main.cpp \
+ node.cpp \
+ openedlist.cpp \
+ pagegenerator.cpp \
+ plaincodemarker.cpp \
+ polyarchiveextractor.cpp \
+ polyuncompressor.cpp \
+ qsakernelparser.cpp \
+ qscodemarker.cpp \
+ qscodeparser.cpp \
+ quoter.cpp \
+ separator.cpp \
+ sgmlgenerator.cpp \
+ text.cpp \
+ tokenizer.cpp \
+ tree.cpp \
+ uncompressor.cpp \
+ webxmlgenerator.cpp \
+ yyindent.cpp
+
+
+win32 {
+ QT_WINCONFIG = release
+ CONFIG(debug, debug|release) {
+ QT_WINCONFIG = debug
+ }
+}
diff --git a/tools/qdoc3/qsakernelparser.cpp b/tools/qdoc3/qsakernelparser.cpp
new file mode 100644
index 0000000..b856957
--- /dev/null
+++ b/tools/qdoc3/qsakernelparser.cpp
@@ -0,0 +1,186 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <qfile.h>
+
+#include "qsakernelparser.h"
+#include "tokenizer.h"
+#include "tree.h"
+
+QT_BEGIN_NAMESPACE
+
+QsaKernelParser::QsaKernelParser( Tree *cppTree )
+ : cppTre( cppTree )
+{
+}
+
+QsaKernelParser::~QsaKernelParser()
+{
+}
+
+QString QsaKernelParser::language()
+{
+ return "QSA Kernel C++";
+}
+
+QString QsaKernelParser::sourceFileNameFilter()
+{
+ return "*.cpp";
+}
+
+void QsaKernelParser::parseSourceFile( const Location& location,
+ const QString& filePath,
+ Tree * /* tree */ )
+{
+ FILE *in = fopen( QFile::encodeName(filePath), "r" );
+ if ( in == 0 ) {
+ location.error( tr("Cannot open QSA kernel file '%1'").arg(filePath) );
+ return;
+ }
+
+ Location fileLocation( filePath );
+ Tokenizer fileTokenizer( fileLocation, in );
+ tokenizer = &fileTokenizer;
+ readToken();
+
+ QString ident;
+ QString className;
+ int delimDepth = 0;
+
+ while ( tok != Tok_Eoi ) {
+ if ( tok == Tok_Ident ) {
+ ident = tokenizer->lexeme();
+ readToken();
+ if ( tok == Tok_Gulbrandsen && tokenizer->braceDepth() == 0 &&
+ tokenizer->parenDepth() == 0 ) {
+ className = ident;
+ } else if ( ident.startsWith("add") && ident.endsWith("Member") &&
+ tok == Tok_LeftParen ) {
+ bool isProperty = ident.endsWith( "VariableMember" );
+ bool isStatic = ident.startsWith( "addStatic" );
+ bool isWritable = !isStatic;
+
+ readToken();
+ if ( tok == Tok_String ) {
+ QString member = tokenizer->lexeme();
+ member = member.mid( 1, member.length() - 2 );
+
+ readToken();
+ if ( tok == Tok_Comma )
+ readToken();
+ if ( tok == Tok_Ident && tokenizer->lexeme() == "QSMember" )
+ readToken();
+ if ( tok == Tok_LeftParen ) {
+ delimDepth++;
+ readToken();
+ }
+
+ while ( tok != Tok_Eoi && tok != Tok_RightParen &&
+ tok != Tok_Semicolon ) {
+ if ( tok == Tok_Ident ) {
+ ident = tokenizer->lexeme();
+ if ( ident == "Custom" ) {
+ isProperty = true;
+ } else if ( ident == "AttributeNonWritable" ) {
+ isWritable = false;
+ } else if ( ident == "AttributeStatic" ) {
+ isStatic = true;
+ }
+ }
+ readToken();
+ }
+
+ ClassNode *classe =
+ (ClassNode *) cppTre->findNode( QStringList(className),
+ Node::Class );
+ if ( classe == 0 ) {
+ classe = new ClassNode( cppTre->root(), className );
+ classe->setLocation( tokenizer->location() );
+ }
+
+ if ( isProperty ) {
+ PropertyNode *property = new PropertyNode(classe, member);
+ property->setLocation( tokenizer->location() );
+ property->setDataType( "Object" );
+#if 0
+ property->setGetter( member );
+ if ( isWritable ) {
+ QString setter = member;
+ setter[0] = setter[0].toUpper();
+ setter.prepend( "set" );
+ property->setSetter( setter );
+ }
+#endif
+ } else {
+ FunctionNode *func = new FunctionNode( classe, member );
+ func->setLocation( tokenizer->location() );
+ func->setAccess( FunctionNode::Public );
+ func->setMetaness( FunctionNode::Slot );
+ if ( member == "toLocaleString" ||
+ member == "toString" ) {
+ func->setReturnType( "QString" );
+ } else if ( member == "valueOf" ) {
+ func->setReturnType( "Object" );
+ } else {
+ func->setReturnType( "Object" );
+ func->addParameter( Parameter("...") );
+ }
+ func->setStatic( false ); // ###
+ }
+ }
+ }
+ } else {
+ readToken();
+ }
+ }
+ fclose( in );
+}
+
+void QsaKernelParser::doneParsingSourceFiles( Tree * /* tree */ )
+{
+}
+
+void QsaKernelParser::readToken()
+{
+ tok = tokenizer->getToken();
+}
+
+QT_END_NAMESPACE
diff --git a/tools/qdoc3/qsakernelparser.h b/tools/qdoc3/qsakernelparser.h
new file mode 100644
index 0000000..59273cd
--- /dev/null
+++ b/tools/qdoc3/qsakernelparser.h
@@ -0,0 +1,77 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*
+ qsakernelparser.h
+*/
+
+#ifndef QSAKERNELPARSER_H
+#define QSAKERNELPARSER_H
+
+#include "codeparser.h"
+
+QT_BEGIN_NAMESPACE
+
+class Tokenizer;
+
+class QsaKernelParser : public CodeParser
+{
+public:
+ QsaKernelParser( Tree *cppTree );
+ ~QsaKernelParser();
+
+ virtual QString language();
+ virtual QString sourceFileNameFilter();
+ virtual void parseSourceFile( const Location& location,
+ const QString& filePath, Tree *tree );
+ virtual void doneParsingSourceFiles( Tree *tree );
+
+private:
+ void readToken();
+
+ Tree *cppTre;
+ Tokenizer *tokenizer;
+ int tok;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/tools/qdoc3/qscodemarker.cpp b/tools/qdoc3/qscodemarker.cpp
new file mode 100644
index 0000000..ed89b60
--- /dev/null
+++ b/tools/qdoc3/qscodemarker.cpp
@@ -0,0 +1,385 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*
+ qscodemarker.cpp
+*/
+
+#include "node.h"
+#include "qscodemarker.h"
+
+QT_BEGIN_NAMESPACE
+
+QsCodeMarker::QsCodeMarker()
+{
+}
+
+QsCodeMarker::~QsCodeMarker()
+{
+}
+
+bool QsCodeMarker::recognizeCode( const QString& /* code */ )
+{
+ return true;
+}
+
+bool QsCodeMarker::recognizeExtension( const QString& ext )
+{
+ return ext == "js" || ext == "qs";
+}
+
+bool QsCodeMarker::recognizeLanguage( const QString& lang )
+{
+ return lang == "JavaScript" || lang == "Qt Script";
+}
+
+QString QsCodeMarker::plainName( const Node *node )
+{
+ QString name = node->name();
+ if ( node->type() == Node::Function )
+ name += "()";
+ return name;
+}
+
+QString QsCodeMarker::plainFullName( const Node *node, const Node * /* relative */ )
+{
+ QString fullName;
+ for ( ;; ) {
+ fullName.prepend( plainName(node) );
+ if ( node->parent()->name().isEmpty() )
+ break;
+ node = node->parent();
+ fullName.prepend(".");
+ }
+ return fullName;
+}
+
+QString QsCodeMarker::markedUpCode( const QString& code,
+ const Node * /* relative */,
+ const QString& /* dirPath */ )
+{
+ return protect( code );
+}
+
+QString QsCodeMarker::markedUpSynopsis( const Node *node,
+ const Node * /* relative */,
+ SynopsisStyle style )
+{
+ QString synopsis;
+ QStringList extras;
+ QString name;
+
+ name = taggedNode( node );
+ if ( style != Detailed )
+ name = linkTag( node, name );
+ name = "<@name>" + name + "</@name>";
+
+ if ( style == Detailed && !node->parent()->name().isEmpty() &&
+ node->type() != Node::Enum )
+ name.prepend( taggedNode(node->parent()) + "." );
+
+ switch ( node->type() ) {
+ case Node::Class:
+ synopsis = "class " + name;
+ break;
+ case Node::Function:
+ {
+ const FunctionNode *func = (const FunctionNode *) node;
+
+ synopsis = name;
+
+ if ( style == SeparateList ) {
+ synopsis += "()";
+ } else {
+ synopsis += " (";
+ if ( !func->parameters().isEmpty() ) {
+ synopsis += " ";
+ int numOptional = 0;
+ QList<Parameter>::ConstIterator p = func->parameters().begin();
+ while ( p != func->parameters().end() ) {
+ if ( !(*p).defaultValue().isEmpty() ) {
+ if ( p == func->parameters().begin() ) {
+ synopsis += "[ ";
+ } else {
+ synopsis += " [ , ";
+ }
+ numOptional++;
+ } else {
+ if ( p != func->parameters().begin() )
+ synopsis += ", ";
+ }
+ if ( !(*p).name().isEmpty() )
+ synopsis += "<@param>" + protect( (*p).name() ) +
+ "</@param> : ";
+ synopsis += protect( (*p).leftType() );
+ ++p;
+ }
+ for ( int i = 0; i < numOptional; i++ )
+ synopsis += " ]";
+ synopsis += " ";
+ }
+ synopsis += ")";
+ }
+
+ if ( style != SeparateList && !func->returnType().isEmpty() )
+ synopsis += " : " + protect( func->returnType() );
+
+ if ( style == Detailed && func->metaness() == FunctionNode::Signal )
+ extras << "[signal]";
+ }
+ break;
+ case Node::Property:
+ {
+ const PropertyNode *property = (const PropertyNode *) node;
+
+ synopsis = name;
+ if ( style != SeparateList )
+ synopsis += " : " + property->dataType();
+ if ( style == Detailed && property->setters().isEmpty() )
+ extras << "[read only]";
+ }
+ break;
+ case Node::Enum:
+ {
+ /*
+ The letters A to F and X (upper- and lower-case) can
+ appear in a hexadecimal constant (e.g. 0x3F).
+ */
+ QRegExp letterRegExp( "[G-WYZg-wyz_]" );
+ const EnumNode *enume = (const EnumNode *) node;
+
+ synopsis = name;
+ if ( style == Summary && !enume->items().isEmpty() ) {
+ synopsis += " : ";
+ QString comma;
+ QList<EnumItem>::ConstIterator it = enume->items().begin();
+ while ( it != enume->items().end() ) {
+ if ( enume->itemAccess((*it).name()) == Node::Public ) {
+ synopsis += comma;
+ synopsis += (*it).name();
+ if ( (*it).value().indexOf(letterRegExp) != -1 )
+ synopsis += " = " + (*it).value();
+ comma = ", ";
+ }
+ ++it;
+ }
+ }
+ }
+ break;
+ case Node::Namespace:
+ case Node::Typedef:
+ default:
+ synopsis = name;
+ }
+
+ if ( style == Summary ) {
+ if ( node->status() == Node::Preliminary ) {
+ extras << "(preliminary)";
+ } else if ( node->status() == Node::Deprecated ) {
+ extras << "(deprecated)";
+ } else if ( node->status() == Node::Obsolete ) {
+ extras << "(obsolete)";
+ }
+ }
+
+ QString extra;
+ if ( !extras.isEmpty() )
+ extra = "<@extra>" + extras.join(" ") + "</@extra>";
+ return synopsis + extra;
+}
+
+QString QsCodeMarker::markedUpName( const Node *node )
+{
+ QString name = linkTag( node, taggedNode(node) );
+ if ( node->type() == Node::Function )
+ name += "()";
+ return name;
+}
+
+QString QsCodeMarker::markedUpFullName( const Node *node,
+ const Node * /* relative */ )
+{
+ QString fullName;
+ for ( ;; ) {
+ fullName.prepend( markedUpName(node) );
+ if ( node->parent()->name().isEmpty() )
+ break;
+ node = node->parent();
+ fullName.prepend( "<@op>.</@op>" );
+ }
+ return fullName;
+}
+
+QString QsCodeMarker::markedUpEnumValue(const QString & /* enumValue */,
+ const Node * /* relative */)
+{
+ return QString();
+}
+
+QString QsCodeMarker::markedUpIncludes( const QStringList& /* includes */ )
+{
+ return QString();
+}
+
+QString QsCodeMarker::functionBeginRegExp( const QString& funcName )
+{
+ return "^function[ \t].*\\b" + QRegExp::escape( funcName );
+}
+
+QString QsCodeMarker::functionEndRegExp( const QString& /* funcName */ )
+{
+ return "^}";
+}
+
+QList<Section> QsCodeMarker::sections( const InnerNode *inner, SynopsisStyle style, Status status )
+{
+ QList<Section> sections;
+
+ if (inner->type() != Node::Class)
+ return sections;
+
+ const ClassNode *classe = static_cast<const ClassNode *>(inner);
+
+ if ( style == Summary ) {
+ FastSection enums(classe, "Enums", "enum", "enums");
+ FastSection functions(classe, "Functions", "function", "functions");
+ FastSection readOnlyProperties(classe, "Read-Only Properties", "property", "properties");
+ FastSection signalz(classe, "Signals", "signal", "signals");
+ FastSection writableProperties(classe, "Writable Properties", "property", "properties");
+
+ QStack<const ClassNode *> stack;
+ stack.push( classe );
+
+ while ( !stack.isEmpty() ) {
+ const ClassNode *ancestorClass = stack.pop();
+
+ NodeList::ConstIterator c = ancestorClass->childNodes().begin();
+ while ( c != ancestorClass->childNodes().end() ) {
+ if ( (*c)->access() == Node::Public ) {
+ if ( (*c)->type() == Node::Enum ) {
+ insert( enums, *c, style, status );
+ } else if ( (*c)->type() == Node::Function ) {
+ const FunctionNode *func = (const FunctionNode *) *c;
+ if ( func->metaness() == FunctionNode::Signal ) {
+ insert( signalz, *c, style, status );
+ } else {
+ insert( functions, *c, style, status );
+ }
+ } else if ( (*c)->type() == Node::Property ) {
+ const PropertyNode *property =
+ (const PropertyNode *) *c;
+ if ( property->setters().isEmpty() ) {
+ insert( readOnlyProperties, *c, style, status );
+ } else {
+ insert( writableProperties, *c, style, status );
+ }
+ }
+ }
+ ++c;
+ }
+
+ QList<RelatedClass>::ConstIterator r = ancestorClass->baseClasses().begin();
+ while ( r != ancestorClass->baseClasses().end() ) {
+ stack.prepend( (*r).node );
+ ++r;
+ }
+ }
+ append( sections, enums );
+ append( sections, writableProperties );
+ append( sections, readOnlyProperties );
+ append( sections, functions );
+ append( sections, signalz );
+ } else if ( style == Detailed ) {
+ FastSection enums( classe, "Enum Documentation" );
+ FastSection functionsAndSignals( classe, "Function and Signal Documentation" );
+ FastSection properties( classe, "Property Documentation" );
+
+ NodeList::ConstIterator c = classe->childNodes().begin();
+ while ( c != classe->childNodes().end() ) {
+ if ( (*c)->access() == Node::Public ) {
+ if ( (*c)->type() == Node::Enum ) {
+ insert( enums, *c, style, status );
+ } else if ( (*c)->type() == Node::Function ) {
+ insert( functionsAndSignals, *c, style, status );
+ } else if ( (*c)->type() == Node::Property ) {
+ insert( properties, *c, style, status );
+ }
+ }
+ ++c;
+ }
+ append( sections, enums );
+ append( sections, properties );
+ append( sections, functionsAndSignals );
+ } else { // ( style == SeparateList )
+ FastSection all( classe );
+
+ QStack<const ClassNode *> stack;
+ stack.push( classe );
+
+ while ( !stack.isEmpty() ) {
+ const ClassNode *ancestorClass = stack.pop();
+
+ NodeList::ConstIterator c = ancestorClass->childNodes().begin();
+ while ( c != ancestorClass->childNodes().end() ) {
+ if ( (*c)->access() == Node::Public )
+ insert( all, *c, style, status );
+ ++c;
+ }
+
+ QList<RelatedClass>::ConstIterator r = ancestorClass->baseClasses().begin();
+ while ( r != ancestorClass->baseClasses().end() ) {
+ stack.prepend( (*r).node );
+ ++r;
+ }
+ }
+ append( sections, all );
+ }
+ return sections;
+}
+
+const Node *QsCodeMarker::resolveTarget( const QString& /* target */,
+ const Tree * /* tree */,
+ const Node * /* relative */ )
+{
+ return 0;
+}
+
+QT_END_NAMESPACE
diff --git a/tools/qdoc3/qscodemarker.h b/tools/qdoc3/qscodemarker.h
new file mode 100644
index 0000000..a283e88
--- /dev/null
+++ b/tools/qdoc3/qscodemarker.h
@@ -0,0 +1,80 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*
+ qscodemarker.h
+*/
+
+#ifndef QSCODEMARKER_H
+#define QSCODEMARKER_H
+
+#include "codemarker.h"
+
+QT_BEGIN_NAMESPACE
+
+class QsCodeMarker : public CodeMarker
+{
+public:
+ QsCodeMarker();
+ ~QsCodeMarker();
+
+ bool recognizeCode( const QString& code );
+ bool recognizeExtension( const QString& ext );
+ bool recognizeLanguage( const QString& lang );
+ QString plainName(const Node *node);
+ QString plainFullName(const Node *node, const Node *relative);
+ QString markedUpCode( const QString& code, const Node *relative,
+ const QString& dirPath );
+ QString markedUpSynopsis( const Node *node, const Node *relative,
+ SynopsisStyle style );
+ QString markedUpName( const Node *node );
+ QString markedUpFullName( const Node *node, const Node *relative );
+ QString markedUpEnumValue(const QString &enumValue, const Node *relative);
+ QString markedUpIncludes( const QStringList& includes );
+ QList<Section> sections(const InnerNode *innerNode, SynopsisStyle style, Status status);
+ QString functionBeginRegExp( const QString& funcName );
+ QString functionEndRegExp( const QString& funcName );
+ const Node *resolveTarget( const QString& target, const Tree *tree, const Node *relative );
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/tools/qdoc3/qscodeparser.cpp b/tools/qdoc3/qscodeparser.cpp
new file mode 100644
index 0000000..1a8902e
--- /dev/null
+++ b/tools/qdoc3/qscodeparser.cpp
@@ -0,0 +1,944 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*
+ qscodeparser.cpp
+*/
+
+#include <qfile.h>
+#include <qregexp.h>
+
+#include "config.h"
+#include "qscodeparser.h"
+#include "text.h"
+#include "tokenizer.h"
+#include "tree.h"
+
+QT_BEGIN_NAMESPACE
+
+#define CONFIG_QUICK "quick"
+#define CONFIG_REPLACES "replaces"
+
+#define COMMAND_BRIEF Doc::alias( "brief")
+#define COMMAND_CODE Doc::alias( "code")
+#define COMMAND_ENDCODE Doc::alias( "endcode")
+#define COMMAND_ENDQUICKCODE Doc::alias( "endquickcode")
+#define COMMAND_FILE Doc::alias( "file")
+#define COMMAND_GROUP Doc::alias( "group")
+#define COMMAND_MODULE Doc::alias( "module")
+#define COMMAND_PAGE Doc::alias( "page")
+#define COMMAND_QUICKCLASS Doc::alias( "quickclass")
+#define COMMAND_QUICKCODE Doc::alias( "quickcode")
+#define COMMAND_QUICKENUM Doc::alias( "quickenum")
+#define COMMAND_QUICKFN Doc::alias( "quickfn")
+#define COMMAND_QUICKIFY Doc::alias( "quickify")
+#define COMMAND_QUICKPROPERTY Doc::alias( "quickproperty")
+#define COMMAND_PROTECTED Doc::alias( "protected")
+#define COMMAND_REPLACE Doc::alias( "replace")
+
+static QString balancedParens = "(?:[^()]+|\\([^()]*\\))*";
+
+QsCodeParser::QsCodeParser(Tree *cppTree)
+ : cppTre(cppTree), qsTre(0), replaceRegExp("/(.+)/([^/]*)/")
+{
+}
+
+QsCodeParser::~QsCodeParser()
+{
+}
+
+void QsCodeParser::initializeParser(const Config& config)
+{
+ CppCodeParser::initializeParser(config);
+
+ nodeTypeMap.insert(COMMAND_QUICKCLASS, Node::Class);
+ nodeTypeMap.insert(COMMAND_QUICKENUM, Node::Enum);
+ nodeTypeMap.insert(COMMAND_QUICKPROPERTY, Node::Property);
+ nodeTypeMap.insert(COMMAND_QUICKFN, Node::Function);
+
+ QString quickDotReplaces = CONFIG_QUICK + Config::dot + CONFIG_REPLACES;
+ QStringList replaces = config.getStringList(quickDotReplaces);
+ QStringList::ConstIterator r = replaces.begin();
+ while (r != replaces.end()) {
+ if (replaceRegExp.exactMatch(*r)) {
+ QRegExp before(replaceRegExp.cap(1));
+ before.setMinimal(true);
+ QString after = replaceRegExp.cap(2);
+
+ if (before.isValid()) {
+ replaceBefores << before;
+ replaceAfters << after;
+ }
+ else {
+ config.lastLocation().warning(
+ tr("Invalid regular expression '%1'")
+ .arg(before.pattern()));
+ }
+ }
+ else {
+ config.lastLocation().warning(tr("Bad syntax in '%1'")
+ .arg(quickDotReplaces));
+ }
+ ++r;
+ }
+}
+
+void QsCodeParser::terminateParser()
+{
+ nodeTypeMap.clear();
+ classesWithNoQuickDoc.clear();
+ replaceBefores.clear();
+ replaceAfters.clear();
+ CppCodeParser::terminateParser();
+}
+
+QString QsCodeParser::language()
+{
+ return "Qt Script";
+}
+
+QString QsCodeParser::headerFileNameFilter()
+{
+ return "*";
+}
+
+QString QsCodeParser::sourceFileNameFilter()
+{
+ return "*.qs *.qsd";
+}
+
+void QsCodeParser::parseHeaderFile(const Location& location,
+ const QString& filePath,
+ Tree *tree)
+{
+ qsTre = tree;
+
+ FILE *in = fopen(QFile::encodeName(filePath), "r");
+ if (in == 0) {
+ location.error(tr("Cannot open Qt Script class list '%1'")
+ .arg(filePath));
+ return;
+ }
+
+ Location fileLocation(filePath);
+ Tokenizer fileTokenizer(fileLocation, in);
+ int tok = fileTokenizer.getToken();
+ while (tok != Tok_Eoi) {
+ if (tok == Tok_Ident) {
+ ClassNode *quickClass = new ClassNode(qsTre->root(),
+ fileTokenizer.lexeme());
+ quickClass->setLocation(fileTokenizer.location());
+ }
+ else {
+ fileTokenizer.location().error(tr("Unexpected token '%1' in Qt"
+ " Script class list")
+ .arg(fileTokenizer.lexeme()));
+ break;
+ }
+ tok = fileTokenizer.getToken();
+ }
+ fclose(in);
+}
+
+void QsCodeParser::parseSourceFile(const Location& location,
+ const QString& filePath,
+ Tree *tree)
+{
+ qsTre = tree;
+ CppCodeParser::parseSourceFile(location, filePath, tree);
+}
+
+void QsCodeParser::doneParsingHeaderFiles(Tree *tree)
+{
+ NodeList::ConstIterator c = tree->root()->childNodes().begin();
+ while (c != tree->root()->childNodes().end()) {
+ if ((*c)->type() == Node::Class)
+ quickifyClass((ClassNode *) *c);
+ ++c;
+ }
+ cppTre->root()->deleteChildren(); // save memory
+ tree->resolveInheritance();
+ tree->resolveProperties();
+}
+
+void QsCodeParser::doneParsingSourceFiles(Tree *tree)
+{
+ tree->root()->normalizeOverloads();
+
+ NodeList::ConstIterator c = tree->root()->childNodes().begin();
+ while (c != tree->root()->childNodes().end()) {
+ if ((*c)->type() == Node::Class) {
+ QMap<QString, Node *>::ConstIterator cwnqd =
+ classesWithNoQuickDoc.find((*c)->name());
+ if (cwnqd != classesWithNoQuickDoc.end()) {
+ (*cwnqd)->location().warning(tr("No '\\%1' documentation for"
+ " class '%2'")
+ .arg(COMMAND_QUICKCLASS)
+ .arg(cwnqd.key()));
+ (*cwnqd)->setDoc(Doc(), true);
+ }
+ }
+ ++c;
+ }
+
+ // ### check which enum types are used
+}
+
+FunctionNode *QsCodeParser::findFunctionNode(const QString& synopsis,
+ Tree *tree)
+{
+ QStringList parentPath;
+ FunctionNode *clone;
+ FunctionNode *func = 0;
+
+ if (makeFunctionNode(synopsis, &parentPath, &clone)) {
+ func = tree->findFunctionNode(parentPath, clone);
+ delete clone;
+ }
+ return func;
+}
+
+QSet<QString> QsCodeParser::topicCommands()
+{
+ return QSet<QString>() << COMMAND_FILE << COMMAND_GROUP << COMMAND_MODULE
+ << COMMAND_PAGE << COMMAND_QUICKCLASS
+ << COMMAND_QUICKENUM << COMMAND_QUICKFN
+ << COMMAND_QUICKPROPERTY;
+}
+
+Node *QsCodeParser::processTopicCommand(const Doc& doc,
+ const QString& command,
+ const QString& arg)
+{
+ if (command == COMMAND_QUICKFN) {
+ QStringList parentPath;
+ FunctionNode *quickFunc = 0;
+ FunctionNode *clone;
+
+ if (makeFunctionNode(arg, &parentPath, &clone)) {
+ FunctionNode *kernelFunc = findKernelFunction(parentPath,
+ clone->name());
+ if (kernelFunc != 0)
+ kernelFunc->setAccess(Node::Private);
+
+ quickFunc = qsTre->findFunctionNode(parentPath, clone);
+ if (quickFunc == 0 && kernelFunc != 0) {
+ quickFunc = new FunctionNode(kernelFunc->parent(),
+ kernelFunc->name());
+ quickFunc->setLocation(kernelFunc->location());
+ quickFunc->setReturnType(clone->returnType());
+ quickFunc->setParameters(clone->parameters());
+ }
+
+ if (quickFunc == 0) {
+ doc.location().warning(tr("Cannot find '%1' specified with '\\%2'")
+ .arg(arg).arg(command));
+ }
+ else {
+ quickFunc->setAccess(Node::Public);
+ QStringList qtParams = quickFunc->parameterNames();
+ quickFunc->borrowParameterNames(clone);
+ QStringList quickParams = quickFunc->parameterNames();
+ setQuickDoc(quickFunc, doc, qtParams, quickParams);
+ }
+ delete clone;
+ }
+ else {
+ doc.location().warning(tr("Cannot find '%1' specified with '\\%2'")
+ .arg(arg).arg(command));
+ }
+ return 0;
+ }
+ else if (nodeTypeMap.contains(command)) {
+ QStringList subArgs = arg.split(" ");
+ QString dataType;
+
+ if (subArgs.count() == 3 && subArgs[1] == ":") {
+ dataType = subArgs[2];
+ }
+ else if (subArgs.count() != 1) {
+ doc.location().warning(tr("Invalid syntax in '\\%1'")
+ .arg(command));
+ }
+
+ QStringList path = subArgs[0].split(".");
+ Node *quickNode = qsTre->findNode(path, nodeTypeMap[command]);
+ if (quickNode == 0) {
+ doc.location().warning(tr("Cannot find '%1' specified with '\\%2'")
+ .arg(arg).arg(command));
+ }
+ else {
+ setQuickDoc(quickNode, doc);
+ if (quickNode->type() == Node::Class) {
+ classesWithNoQuickDoc.remove(quickNode->name());
+ if (doc.briefText().isEmpty())
+ doc.location().warning(tr("Missing '\\%1' for class '%2'")
+ .arg(COMMAND_BRIEF)
+ .arg(quickNode->name()));
+ }
+ else if (quickNode->type() == Node::Property) {
+ PropertyNode *quickProperty = (PropertyNode *) quickNode;
+ if (quickProperty->dataType() == "Object") {
+ if (dataType.isEmpty()) {
+ doc.location().warning(tr("Missing data type in '\\%1'"
+ " (assuming 'Object')")
+ .arg(command));
+ }
+ else {
+ quickProperty->setDataType(dataType);
+ }
+ }
+ else if (dataType != quickProperty->dataType()) {
+ doc.location().warning(tr("Ignored contradictory data type"
+ " in '\\%1'")
+ .arg(command));
+ }
+ }
+ }
+ return 0;
+ }
+ else {
+ return CppCodeParser::processTopicCommand(doc, command, arg);
+ }
+}
+
+QSet<QString> QsCodeParser::otherMetaCommands()
+{
+ return commonMetaCommands() << COMMAND_ENDQUICKCODE << COMMAND_QUICKCODE
+ << COMMAND_QUICKIFY << COMMAND_REPLACE;
+}
+
+void QsCodeParser::processOtherMetaCommand(const Doc& doc,
+ const QString& command,
+ const QString& arg,
+ Node *node)
+{
+ if (command == COMMAND_PROTECTED) {
+ doc.location().warning(tr("Cannot use '\\%1' in %2")
+ .arg(COMMAND_PROTECTED).arg(language()));
+ }
+ else {
+ CppCodeParser::processOtherMetaCommand(doc,command,arg,node);
+ }
+}
+
+ClassNode *QsCodeParser::tryClass(const QString& className)
+{
+ return (ClassNode*) cppTre->findNode(QStringList(className),Node::Class);
+}
+
+FunctionNode *QsCodeParser::findKernelFunction(const QStringList& parentPath,
+ const QString& name)
+{
+ FunctionNode clone(0, name);
+ clone.setReturnType("Object");
+ clone.addParameter(Parameter("..."));
+ return qsTre->findFunctionNode(parentPath, &clone);
+}
+
+void QsCodeParser::extractRegExp(const QRegExp& regExp,
+ QString& source,
+ const Doc& doc)
+{
+ QRegExp blankLineRegExp(
+ "[ \t]*(?:\n(?:[ \t]*\n)+[ \t]*|[ \n\t]*\\\\code|"
+ "\\\\endcode[ \n\t]*)");
+ QStringList paras = source.trimmed().split(blankLineRegExp);
+ paras = paras.filter(regExp);
+ if (paras.count() == 0) {
+ doc.location().warning(tr("Cannot find regular expression '%1'")
+ .arg(regExp.pattern()));
+ }
+ else if (paras.count() > 1) {
+ doc.location().warning(tr("Regular rexpression '%1' matches multiple"
+ "times").arg(regExp.pattern()));
+ }
+ else {
+ source = paras.first() + "\n\n";
+ }
+}
+
+void QsCodeParser::extractTarget(const QString& target,
+ QString& source,
+ const Doc& doc)
+{
+ QRegExp targetRegExp(
+ "(\\\\target\\s+(\\S+)[^\n]*\n"
+ "(?:(?!\\s*\\\\code)[^\n]+\n|\\s*\\\\code.*\\\\endcode\\s*\n)*)"
+ "(?:\\s*\n|[^\n]*$)");
+ targetRegExp.setMinimal(true);
+
+ int pos = 0;
+ while ((pos = source.indexOf(targetRegExp, pos)) != -1) {
+ if (targetRegExp.cap(2) == target) {
+ source = targetRegExp.cap(1) + "\n\n";
+ return;
+ }
+ pos += targetRegExp.matchedLength();
+ }
+ doc.location().warning(tr("Cannot find target '%1'").arg(target));
+}
+
+void QsCodeParser::renameParameters(QString& source,
+ const Doc& /* doc */,
+ const QStringList& qtParams,
+ const QStringList& quickParams)
+{
+ QRegExp paramRegExp("(\\\\a\\s*\\{?\\s*)([A-Za-z0-9_]+)");
+
+ int pos = 0;
+ while ((pos = paramRegExp.indexIn(source, pos)) != -1) {
+ pos += paramRegExp.cap(1).length();
+ QString before = paramRegExp.cap(2);
+ int index = qtParams.indexOf(before);
+ if (index != -1) {
+ QString after = quickParams[index];
+ source.replace(pos, before.size(), after);
+ }
+ }
+}
+
+void QsCodeParser::applyReplacementList(QString& source, const Doc& doc)
+{
+ QStringList args = doc.metaCommandArgs(COMMAND_REPLACE);
+ QStringList::ConstIterator a = args.begin();
+ while (a != args.end()) {
+ if (replaceRegExp.exactMatch(*a)) {
+ QRegExp before(replaceRegExp.cap(1));
+ before.setMinimal(true);
+ QString after = replaceRegExp.cap(2);
+
+ if (before.isValid()) {
+ int oldLen = source.size();
+ source.replace(before, after);
+
+ // this condition is sufficient but not necessary
+ if (oldLen == source.size() && !source.contains(after))
+ doc.location().warning(
+ tr("Regular expression '%1' did not match anything")
+ .arg(before.pattern()));
+ }
+ else {
+ doc.location().warning(
+ tr("Invalid regular expression '%1'")
+ .arg(before.pattern()));
+ }
+ }
+ else {
+ doc.location().warning(tr("Bad syntax in '\\%1'")
+ .arg(COMMAND_REPLACE));
+ }
+ ++a;
+ }
+
+ QRegExp codeRegExp("\\\\" + COMMAND_CODE + "(.*)\\\\" + COMMAND_ENDCODE);
+ codeRegExp.setMinimal(true);
+
+ QRegExp quickcodeRegExp(
+ "\\\\" + COMMAND_QUICKCODE + "(.*)\\\\" + COMMAND_ENDQUICKCODE);
+ quickcodeRegExp.setMinimal(true);
+
+ int quickcodePos = doc.source().indexOf(quickcodeRegExp);
+ if (quickcodePos != -1) {
+ int codePos = source.indexOf(codeRegExp);
+ if (codePos == -1) {
+ doc.location().warning(
+ tr("Cannot find any '\\%1' snippet corresponding to '\\%2'")
+ .arg(COMMAND_CODE).arg(COMMAND_QUICKCODE));
+ }
+ else {
+ source.replace(codeRegExp.pos(1), codeRegExp.cap(1).length(),
+ quickcodeRegExp.cap(1));
+ codePos = codeRegExp.pos(1) + quickcodeRegExp.cap(1).length();
+
+ if (doc.source().indexOf(quickcodeRegExp, quickcodePos + 1) != -1) {
+ doc.location().warning(
+ tr("Cannot use '\\%1' twice in a row")
+ .arg(COMMAND_QUICKCODE));
+ }
+ else if (source.indexOf(codeRegExp, codePos + 1) != -1) {
+ doc.location().warning(tr("Ambiguous '\\%1'")
+ .arg(COMMAND_QUICKCODE));
+ }
+ }
+ }
+}
+
+void QsCodeParser::quickifyClass(ClassNode *quickClass)
+{
+ QString qtClassName = quickClass->name();
+ QString bare = quickClass->name();
+ if (bare != "Qt" && bare != "Object") {
+ if (bare.startsWith("Q")) {
+ bare = bare.mid(1);
+ }
+ else {
+ qtClassName.prepend("Q");
+ classesWithNoQ.insert(bare);
+ }
+ }
+
+ ClassNode *qtClass = 0;
+ ClassNode *wrapperClass = 0;
+
+ if ((wrapperClass = tryClass("Quick" + bare)) != 0 ||
+ (wrapperClass = tryClass("QS" + bare + "Class")) != 0) {
+ qtClass = tryClass(qtClassName);
+ if (qtClass == 0) {
+ qtClass = wrapperClass;
+ wrapperClass = 0;
+ }
+ }
+ else if ((wrapperClass = tryClass("Quick" + bare + "Ptr")) != 0) {
+ QRegExp ptrToQtType("(Q[A-Za-z0-9_]+)\\s*\\*");
+ FunctionNode *ctor =
+ wrapperClass->findFunctionNode(wrapperClass->name());
+ if (ctor != 0 && !ctor->parameters().isEmpty() &&
+ ptrToQtType.exactMatch(ctor->parameters().first().leftType()))
+ qtClassName = ptrToQtType.cap(1);
+ qtClass = tryClass(qtClassName);
+ }
+ else {
+ wrapperClass = tryClass("Q" + bare + "Ptr");
+ if (wrapperClass == 0)
+ wrapperClass = tryClass("Quick" + bare + "Interface");
+ qtClass = tryClass(qtClassName);
+ }
+
+ if (qtClass == 0) {
+ if (wrapperClass == 0) {
+ quickClass->location().warning(tr("Cannot find Qt class '%1'")
+ .arg(qtClassName));
+ }
+ else {
+ quickClass->location().warning(tr("Cannot find Qt class '%1'"
+ " wrapped by '%2'")
+ .arg(qtClassName)
+ .arg(wrapperClass->name()));
+ }
+ return;
+ }
+
+ QList<RelatedClass>::ConstIterator r = qtClass->baseClasses().begin();
+ while (r != qtClass->baseClasses().end()) {
+ ClassNode *quickBaseClass = cpp2qs.findClassNode(qsTre,
+ (*r).node->name());
+ if (quickBaseClass)
+ quickClass->addBaseClass((*r).access, quickBaseClass);
+ ++r;
+ }
+ if (quickClass->baseClasses().isEmpty() && quickClass->name() != "Object")
+ quickClass->addBaseClass(Node::Public,
+ cpp2qs.findClassNode(qsTre,"Object"));
+
+ QSet<QString> funcBlackList;
+ QSet<QString> propertyBlackList;
+
+ NodeList children;
+ if (wrapperClass != 0) {
+ children = wrapperClass->childNodes();
+
+ funcBlackList.insert(wrapperClass->name());
+ funcBlackList.insert("~" + wrapperClass->name());
+ }
+ children += qtClass->childNodes();
+
+ for (int pass = 0; pass < 2; pass++) {
+ NodeList::ConstIterator c = children.begin();
+ while (c != children.end()) {
+ if ((*c)->access() != Node::Private &&
+ (*c)->status() == Node::Commendable) {
+ if (pass == 0) {
+ if ((*c)->type() == Node::Enum) {
+ EnumNode *enume = (EnumNode *) *c;
+ quickifyEnum(quickClass, enume);
+ }
+ else if ((*c)->type() == Node::Property) {
+ if (!propertyBlackList.contains((*c)->name())) {
+ PropertyNode *property = (PropertyNode *) *c;
+ quickifyProperty(quickClass, qtClass, property);
+ if (!property->getters().isEmpty())
+ funcBlackList.insert(property->getters().first()->name());
+ if (!property->setters().isEmpty())
+ funcBlackList.insert(property->setters().first()->name());
+ if (!property->resetters().isEmpty())
+ funcBlackList.insert(property->resetters().first()->name());
+ propertyBlackList.insert(property->name());
+ }
+ }
+ }
+ else if ((*c)->type() == Node::Function) {
+ FunctionNode *func = (FunctionNode *) *c;
+ quickifyFunction(quickClass, qtClass, func,
+ funcBlackList.contains((*c)->name()) &&
+ func->parameters().count() < 2);
+ }
+ }
+ ++c;
+ }
+ }
+ setQtDoc(quickClass, qtClass->doc());
+ classesWithNoQuickDoc.insert(quickClass->name(), quickClass);
+}
+
+void QsCodeParser::quickifyEnum(ClassNode *quickClass, EnumNode *enume)
+{
+ EnumNode *quickEnum = new EnumNode(quickClass, enume->name());
+ quickEnum->setLocation(enume->location());
+#if 0 // ### not yet
+ quickEnum->setAccess(Node::Protected);
+#endif
+
+ QList<EnumItem>::ConstIterator it = enume->items().begin();
+ while (it != enume->items().end()) {
+ QString name = (*it).name();
+ QString value = (*it).value();
+ quickEnum->addItem(EnumItem(name, value));
+ ++it;
+ }
+ setQtDoc(quickEnum, enume->doc());
+}
+
+void QsCodeParser::quickifyFunction(ClassNode *quickClass, ClassNode *qtClass,
+ FunctionNode *func, bool onBlackList)
+{
+ if (func->metaness() == FunctionNode::Dtor)
+ return;
+
+ FunctionNode *kernelFunc = findKernelFunction(
+ QStringList() << quickClass->name(), func->name());
+
+ QString quickName = func->name();
+ if (func->metaness() == FunctionNode::Ctor)
+ quickName = quickClass->name();
+ FunctionNode *quickFunc = new FunctionNode(quickClass, quickName);
+ quickFunc->setLocation(func->location());
+
+ if (onBlackList) {
+ quickFunc->setAccess(Node::Protected);
+ }
+ else {
+ if (kernelFunc != 0 && func->numOverloads() == 1 &&
+ (func->parameters().count() == 0 ||
+ func->parameters().last().defaultValue().isEmpty())) {
+ kernelFunc->setAccess(Node::Private);
+ }
+ else {
+ if (func->metaness() == FunctionNode::Plain)
+ quickFunc->setAccess(Node::Protected);
+ }
+ }
+
+ quickFunc->setReturnType(cpp2qs.convertedDataType(qsTre,
+ func->returnType()));
+ if (func->metaness() != FunctionNode::Slot)
+ quickFunc->setMetaness(func->metaness());
+ quickFunc->setVirtualness(FunctionNode::ImpureVirtual);
+ quickFunc->setOverload(func->isOverload());
+
+ QList<Parameter>::ConstIterator q = func->parameters().begin();
+ while (q != func->parameters().end()) {
+ QString dataType = cpp2qs.convertedDataType(qsTre, (*q).leftType(),
+ (*q).rightType());
+ if (dataType.isEmpty()) {
+ dataType = "UNKNOWN";
+ quickFunc->setAccess(Node::Private);
+ }
+ Parameter param(dataType, "", (*q).name(),
+ (*q).defaultValue().isEmpty() ? "" : "undefined");
+ quickFunc->addParameter(param);
+ ++q;
+ }
+
+ if (func->doc().isEmpty()) {
+ if (func->parent() != (InnerNode *) qtClass) {
+ func = qtClass->findFunctionNode(func);
+ if (func != 0)
+ setQtDoc(quickFunc, func->doc());
+ }
+ }
+ else {
+ setQtDoc(quickFunc, func->doc());
+ }
+}
+
+void QsCodeParser::quickifyProperty(ClassNode *quickClass,
+ ClassNode * /* qtClass */,
+ PropertyNode *property)
+{
+ PropertyNode *quickProperty = new PropertyNode(quickClass,
+ property->name());
+ quickProperty->setLocation(property->location());
+ quickProperty->setDataType(cpp2qs.convertedDataType(qsTre,
+ property->dataType()));
+#if 0
+ quickProperty->setGetter(property->getter());
+ quickProperty->setSetter(property->setter());
+ quickProperty->setResetter(property->resetter());
+#endif
+ quickProperty->setStored(property->isStored());
+ quickProperty->setDesignable(property->isDesignable());
+
+ setQtDoc(quickProperty, property->doc());
+}
+
+QString QsCodeParser::quickifiedDoc(const QString& source)
+{
+ QString result;
+ int i = 0;
+
+ while (i < (int) source.length()) {
+ if (leftWordBoundary(source, i)) {
+ if (source[i] == 'Q') {
+ if (source[i + 1] == 'C' && source.mid(i, 8) == "QCString") {
+ i += 2;
+ }
+ else {
+ int end = i + 1;
+ while (isWord(source[end]))
+ ++end;
+ if (!classesWithNoQ.contains(
+ source.mid(i + 1, end - (i + 1))))
+ result += "Q";
+ i++;
+ }
+ }
+ else if (source[i] == 'T' && source.mid(i, 4) == "TRUE" &&
+ rightWordBoundary(source, i + 4)) {
+ result += "\\c{true}";
+ i += 4;
+ }
+ else if (source[i] == 'F' && source.mid(i, 5) == "FALSE" &&
+ rightWordBoundary(source, i + 5)) {
+ result += "\\c{false}";
+ i += 5;
+ }
+ else if (source[i] == 'c' && source.mid(i, 6) == "const ") {
+ i += 6;
+ }
+ else {
+ result += source[i++];
+ }
+ }
+ else if ((source[i] == ':' && source[i + 1] == ':') ||
+ (source[i] == '-' && source[i + 1] == '>')) {
+ result += '.';
+ i += 2;
+ }
+ else if (source[i] == '\\') {
+ // ### make independent of the command name
+ if (source.mid(i, 5) == "\\code") {
+ do {
+ result += source[i++];
+ } while (source[i - 1] != '\n');
+
+ int begin = i;
+ int end = source.indexOf("\\endcode", i);
+ if (end != -1) {
+ QString code = source.mid(begin, end - begin);
+ result += cpp2qs.convertedCode(qsTre, code,
+ classesWithNoQ);
+ i = end;
+ }
+ }
+ else {
+ result += source[i++];
+ }
+ }
+ else {
+ result += source[i++];
+ }
+ }
+
+ QList<QRegExp>::ConstIterator b = replaceBefores.begin();
+ QStringList::ConstIterator a = replaceAfters.begin();
+ while (a != replaceAfters.end()) {
+ result.replace(*b, *a);
+ ++b;
+ ++a;
+ }
+ return result;
+}
+
+void QsCodeParser::setQtDoc(Node *quickNode, const Doc& doc)
+{
+ if (!doc.isEmpty()) {
+ Doc quickDoc(doc.location(), doc.location(),
+ quickifiedDoc(doc.source()),
+ CppCodeParser::topicCommands() +
+ CppCodeParser::otherMetaCommands());
+ quickNode->setDoc(quickDoc, true);
+ }
+}
+
+void QsCodeParser::setQuickDoc(Node *quickNode,
+ const Doc& doc,
+ const QStringList& qtParams,
+ const QStringList& quickParams)
+{
+ QRegExp quickifyCommand("\\\\" + COMMAND_QUICKIFY + "([^\n]*)(?:\n|$)");
+
+ if (quickNode->type() == Node::Function) {
+ FunctionNode *quickFunc = (FunctionNode *) quickNode;
+ quickFunc->setOverload(false);
+ }
+
+ if (doc.metaCommandsUsed().contains(COMMAND_QUICKIFY)) {
+ QString source = doc.source();
+ int pos = source.indexOf(quickifyCommand);
+ if (pos != -1) {
+ QString quickifiedSource = quickNode->doc().source();
+ if (!qtParams.isEmpty() && qtParams != quickParams)
+ renameParameters(quickifiedSource, doc, qtParams,
+ quickParams);
+ applyReplacementList(quickifiedSource, doc);
+
+ do {
+ QString extract = quickifiedSource;
+ QString arg = quickifyCommand.cap(1).simplified();
+ if (!arg.isEmpty()) {
+ if (arg.startsWith("/") && arg.endsWith("/") &&
+ arg.length() > 2) {
+ QString pattern = arg.mid(1, arg.length() - 2);
+ extractRegExp(QRegExp(pattern), extract, doc);
+ }
+ else {
+ extractTarget(arg, extract, doc);
+ }
+ }
+ source.replace(pos, quickifyCommand.matchedLength(), extract);
+ pos += extract.length();
+ } while ((pos = source.indexOf(quickifyCommand, pos)) != -1);
+
+ QRegExp quickcodeRegExp(
+ "\\\\" + COMMAND_QUICKCODE + "(.*)\\\\" +
+ COMMAND_ENDQUICKCODE);
+ quickcodeRegExp.setMinimal(true);
+ source.replace(quickcodeRegExp, "");
+ }
+
+ Doc quickDoc(doc.location(),
+ doc.location(),
+ source,
+ (CppCodeParser::topicCommands() + topicCommands() +
+ CppCodeParser::otherMetaCommands()) << COMMAND_REPLACE);
+ quickNode->setDoc(quickDoc, true);
+ processOtherMetaCommands(quickDoc, quickNode);
+ }
+ else {
+ quickNode->setDoc(doc, true);
+ processOtherMetaCommands(doc, quickNode);
+ }
+}
+
+bool QsCodeParser::makeFunctionNode(const QString& synopsis,
+ QStringList *parentPathPtr,
+ FunctionNode **funcPtr)
+{
+ QRegExp funcRegExp(
+ "\\s*([A-Za-z0-9_]+)\\.([A-Za-z0-9_]+)\\s*\\((" +
+ balancedParens +
+ ")\\)(?:\\s*:\\s*([A-Za-z0-9_]+))?\\s*");
+ QRegExp paramRegExp(
+ "\\s*(\\[)?\\s*(?:([A-Za-z0-9_]+)\\s*:\\s*)?"
+ "([A-Za-z0-9_]+|\\.\\.\\.)\\s*(\\[)?[\\s\\]]*");
+
+ if (!funcRegExp.exactMatch(synopsis))
+ return false;
+
+ ClassNode *classe = (ClassNode*)
+ qsTre->findNode(QStringList(funcRegExp.cap(1)),Node::Class);
+ if (classe == 0)
+ return false;
+
+ FunctionNode *clone = new FunctionNode(0, funcRegExp.cap(2));
+ bool optional = false;
+
+ QString paramStr = funcRegExp.cap(3);
+ QStringList params = paramStr.split(",");
+ QStringList::ConstIterator p = params.begin();
+ while (p != params.end()) {
+ if (paramRegExp.exactMatch(*p)) {
+ if (!paramRegExp.cap(1).isEmpty())
+ optional = true;
+ clone->addParameter(Parameter(paramRegExp.cap(3),
+ "",
+ paramRegExp.cap(2),
+ optional ? "undefined" : ""));
+ if (!paramRegExp.cap(4).isEmpty())
+ optional = true;
+ }
+ else {
+ delete clone;
+ return false;
+ }
+ ++p;
+ }
+ QString returnType = funcRegExp.cap(4);
+ if (!returnType.isEmpty())
+ clone->setReturnType(returnType);
+ if (parentPathPtr != 0)
+ *parentPathPtr = QStringList() << classe->name();
+ if (funcPtr != 0) {
+ *funcPtr = clone;
+ }
+ else {
+ delete clone;
+ }
+ return true;
+}
+
+bool QsCodeParser::isWord(QChar ch)
+{
+ return ch.isLetterOrNumber() || ch == QChar('_');
+}
+
+bool QsCodeParser::leftWordBoundary(const QString& str, int pos)
+{
+ return !isWord(str[pos - 1]) && isWord(str[pos]);
+}
+
+bool QsCodeParser::rightWordBoundary(const QString& str, int pos)
+{
+ return isWord(str[pos - 1]) && !isWord(str[pos]);
+}
+
+QT_END_NAMESPACE
diff --git a/tools/qdoc3/qscodeparser.h b/tools/qdoc3/qscodeparser.h
new file mode 100644
index 0000000..bd599a8
--- /dev/null
+++ b/tools/qdoc3/qscodeparser.h
@@ -0,0 +1,128 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*
+ qscodeparser.h
+*/
+
+#ifndef QSCODEPARSER_H
+#define QSCODEPARSER_H
+
+#include "cppcodeparser.h"
+#include "cpptoqsconverter.h"
+
+QT_BEGIN_NAMESPACE
+
+class QsCodeParser : public CppCodeParser
+{
+ public:
+ QsCodeParser(Tree *cppTree);
+ ~QsCodeParser();
+
+ virtual void initializeParser(const Config& config);
+ virtual void terminateParser();
+ virtual QString language();
+ virtual QString headerFileNameFilter();
+ virtual QString sourceFileNameFilter();
+ virtual void parseHeaderFile(const Location& location,
+ const QString& filePath, Tree *tree);
+ virtual void parseSourceFile(const Location& location,
+ const QString& filePath, Tree *tree);
+ virtual void doneParsingHeaderFiles(Tree *tree);
+ virtual void doneParsingSourceFiles(Tree *tree);
+
+ FunctionNode *findFunctionNode(const QString& synopsis, Tree *tree);
+
+ protected:
+ virtual QSet<QString> topicCommands();
+ virtual Node *processTopicCommand(const Doc& doc, const QString& command,
+ const QString& arg);
+ virtual QSet<QString> otherMetaCommands();
+ virtual void processOtherMetaCommand(const Doc& doc,
+ const QString& command,
+ const QString& arg, Node *node);
+
+ private:
+ ClassNode *tryClass(const QString& className);
+ FunctionNode *findKernelFunction(const QStringList& parentPath,
+ const QString& name);
+ void extractRegExp(const QRegExp& regExp, QString& source,
+ const Doc& doc);
+ void extractTarget(const QString& target, QString& source,
+ const Doc& doc);
+ void renameParameters(QString& source, const Doc& doc,
+ const QStringList& qtNames,
+ const QStringList& quickNames);
+ void applyReplacementList(QString& source, const Doc& doc);
+ void quickifyClass(ClassNode *quickClass);
+ void quickifyEnum(ClassNode *quickClass, EnumNode *enume);
+ void quickifyFunction(ClassNode *quickClass, ClassNode *qtClass,
+ FunctionNode *func, bool onBlackList);
+ void quickifyProperty(ClassNode *quickClass, ClassNode *qtClass,
+ PropertyNode *property);
+ QString quickifiedDoc(const QString& source);
+ void setQtDoc(Node *quickNode, const Doc& doc);
+ void setQuickDoc(Node *quickNode, const Doc& doc,
+ const QStringList& qtParams = QStringList(),
+ const QStringList& quickParams = QStringList());
+ bool makeFunctionNode(const QString& synopsis, QStringList *parentPathPtr,
+ FunctionNode **funcPtr);
+
+ static bool isWord(QChar ch);
+ static bool leftWordBoundary(const QString& str, int pos);
+ static bool rightWordBoundary(const QString& str, int pos);
+
+ QMap<QString,Node::Type> nodeTypeMap;
+ QMap<QString,Node*> classesWithNoQuickDoc;
+ QList<QRegExp> replaceBefores;
+ QStringList replaceAfters;
+ QSet<QString> classesWithNoQ;
+ Tree* cppTre;
+ Tree* qsTre;
+ QRegExp replaceRegExp;
+ CppToQsConverter cpp2qs;
+
+ static int tabSize;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/tools/qdoc3/quoter.cpp b/tools/qdoc3/quoter.cpp
new file mode 100644
index 0000000..40ca15a
--- /dev/null
+++ b/tools/qdoc3/quoter.cpp
@@ -0,0 +1,369 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <qfileinfo.h>
+#include <qregexp.h>
+#include <qdebug.h>
+
+#include "quoter.h"
+
+QT_BEGIN_NAMESPACE
+
+static void replaceMultipleNewlines(QString &s)
+{
+ const int n = s.size();
+ bool slurping = false;
+ int j = -1;
+ const QChar newLine = QLatin1Char('\n');
+ QChar *d = s.data();
+ for (int i = 0; i != n; ++i) {
+ const QChar c = d[i];
+ bool hit = (c == newLine);
+ if (slurping && hit)
+ continue;
+ d[++j] = c;
+ slurping = hit;
+ }
+ s.resize(++j);
+}
+
+// This is equivalent to line.split( QRegExp("\n(?!\n|$)") ) but much faster
+static QStringList splitLines(const QString &line)
+{
+ QStringList result;
+ int i = line.size();
+ while (true) {
+ int j = i - 1;
+ while (j >= 0 && line.at(j) == QLatin1Char('\n'))
+ --j;
+ while (j >= 0 && line.at(j) != QLatin1Char('\n'))
+ --j;
+ result.prepend(line.mid(j + 1, i - j - 1));
+ if (j < 0)
+ break;
+ i = j;
+ }
+ return result;
+}
+
+/*
+ Transforms 'int x = 3 + 4' into 'int x=3+4'. A white space is kept
+ between 'int' and 'x' because it is meaningful in C++.
+*/
+static void trimWhiteSpace( QString& str )
+{
+ enum { Normal, MetAlnum, MetSpace } state = Normal;
+ const int n = str.length();
+
+ int j = -1;
+ QChar *d = str.data();
+ for ( int i = 0; i != n; ++i ) {
+ const QChar c = d[i];
+ if ( c.isLetterOrNumber() ) {
+ if ( state == Normal ) {
+ state = MetAlnum;
+ } else {
+ if ( state == MetSpace )
+ str[++j] = c;
+ state = Normal;
+ }
+ str[++j] = c;
+ } else if ( c.isSpace() ) {
+ if ( state == MetAlnum )
+ state = MetSpace;
+ } else {
+ state = Normal;
+ str[++j] = c;
+ }
+ }
+ str.resize(++j);
+}
+
+Quoter::Quoter()
+ : silent( false )
+{
+ /* We're going to hard code these delimiters:
+ * C++, Qt, Qt Script, Java:
+ //! [<id>]
+ * .pro files:
+ #! [<id>]
+ * .xq, .xml, .html files:
+ <!-- [<id>] -->
+ */
+ commentHash["pro"] = "#!";
+ commentHash["py"] = "#!";
+ commentHash["html"] = "<!--";
+ commentHash["qrc"] = "<!--";
+ commentHash["ui"] = "<!--";
+ commentHash["xml"] = "<!--";
+ commentHash["xq"] = "<!--";
+}
+
+void Quoter::reset()
+{
+ silent = false;
+ plainLines.clear();
+ markedLines.clear();
+ codeLocation = Location::null;
+}
+
+void Quoter::quoteFromFile( const QString& userFriendlyFilePath,
+ const QString& plainCode,
+ const QString& markedCode )
+{
+ silent = false;
+
+ /*
+ Split the source code into logical lines. Empty lines are
+ treated specially. Before:
+
+ p->alpha();
+ p->beta();
+
+ p->gamma();
+
+
+ p->delta();
+
+ After:
+
+ p->alpha();
+ p->beta();\n
+ p->gamma();\n\n
+ p->delta();
+
+ Newlines are preserved because they affect codeLocation.
+ */
+ codeLocation = Location( userFriendlyFilePath );
+
+ plainLines = splitLines(plainCode);
+ markedLines = splitLines(markedCode);
+ if (markedLines.count() != plainLines.count()) {
+ codeLocation.warning(tr("Something is wrong with qdoc's handling of marked code"));
+ markedLines = plainLines;
+ }
+
+ /*
+ Squeeze blanks (cat -s).
+ */
+ QStringList::Iterator m = markedLines.begin();
+ while ( m != markedLines.end() ) {
+ replaceMultipleNewlines( *m );
+ ++m;
+ }
+ codeLocation.start();
+}
+
+QString Quoter::quoteLine( const Location& docLocation, const QString& command,
+ const QString& pattern )
+{
+ if ( plainLines.isEmpty() ) {
+ failedAtEnd( docLocation, command );
+ return QString();
+ }
+
+ if ( pattern.isEmpty() ) {
+ docLocation.warning( tr("Missing pattern after '\\%1'").arg(command) );
+ return QString();
+ }
+
+ if ( match(docLocation, pattern, plainLines.first()) )
+ return getLine();
+
+ if ( !silent ) {
+ docLocation.warning( tr("Command '\\%1' failed").arg(command) );
+ codeLocation.warning( tr("Pattern '%1' didn't match here")
+ .arg(pattern) );
+ silent = true;
+ }
+ return QString();
+}
+
+QString Quoter::quoteSnippet(const Location &docLocation, const QString &identifier)
+{
+ QString comment = commentForCode();
+ QString delimiter = comment + QString(" [%1]").arg(identifier);
+ QString t;
+
+ while (!plainLines.isEmpty()) {
+ if (match(docLocation, delimiter, plainLines.first())) {
+ getLine();
+ break;
+ }
+ getLine();
+ }
+ while (!plainLines.isEmpty()) {
+ QString line = plainLines.first();
+ if (match(docLocation, delimiter, line)) {
+ QString lastLine = getLine();
+ int dIndex = lastLine.indexOf(delimiter);
+ if (dIndex > 0) {
+ QString leading = lastLine.left(dIndex);
+ dIndex = leading.indexOf(comment);
+ if (dIndex != -1)
+ leading = leading.left(dIndex);
+ if (!leading.trimmed().isEmpty())
+ t += leading;
+ }
+ return t;
+ }
+ // Remove special macros to support Qt namespacing.
+ if (line.startsWith("QT_BEGIN_NAMESPACE")) {
+ getLine();
+ } else if (line.startsWith("QT_END_NAMESPACE")) {
+ getLine();
+ t += QLatin1Char('\n');
+ } else if (!line.startsWith(comment)) {
+ // Ordinary code
+ t += getLine();
+ } else {
+ // Normal comments
+ if (line.contains(QLatin1Char('\n')))
+ t += QLatin1Char('\n');
+ getLine();
+ }
+ }
+ failedAtEnd(docLocation, QString("snippet (%1)").arg(delimiter));
+ return t;
+}
+
+QString Quoter::quoteTo( const Location& docLocation, const QString& command,
+ const QString& pattern )
+{
+ QString t;
+ QString comment = commentForCode();
+
+ if ( pattern.isEmpty() ) {
+ while ( !plainLines.isEmpty() ) {
+ QString line = plainLines.first();
+ // Remove special macros to support Qt namespacing.
+ if (line.startsWith("QT_BEGIN_NAMESPACE")) {
+ getLine();
+ } else if (line.startsWith("QT_END_NAMESPACE")) {
+ getLine();
+ t += QLatin1Char('\n');
+ } else if (!line.startsWith(comment))
+ // Ordinary code
+ t += getLine();
+ else {
+ // Normal comments
+ if (line.contains(QLatin1Char('\n')))
+ t += QLatin1Char('\n');
+ getLine();
+ }
+ }
+ } else {
+ while ( !plainLines.isEmpty() ) {
+ if ( match(docLocation, pattern, plainLines.first()) ) {
+ return t;
+ }
+ t += getLine();
+ }
+ failedAtEnd( docLocation, command );
+ }
+ return t;
+}
+
+QString Quoter::quoteUntil( const Location& docLocation, const QString& command,
+ const QString& pattern )
+{
+ QString t = quoteTo( docLocation, command, pattern );
+ t += getLine();
+ return t;
+}
+
+QString Quoter::getLine()
+{
+ if ( plainLines.isEmpty() )
+ return QString();
+
+ plainLines.removeFirst();
+
+ QString t = markedLines.takeFirst();
+ t += QLatin1Char('\n');
+ codeLocation.advanceLines( t.count( QLatin1Char('\n') ) );
+ return t;
+}
+
+bool Quoter::match( const Location& docLocation, const QString& pattern0,
+ const QString& line )
+{
+ QString str = line;
+ while ( str.endsWith(QLatin1Char('\n')) )
+ str.truncate( str.length() - 1 );
+
+ QString pattern = pattern0;
+ if ( pattern.startsWith(QLatin1Char('/'))
+ && pattern.endsWith(QLatin1Char('/'))
+ && pattern.length() > 2 ) {
+ QRegExp rx( pattern.mid(1, pattern.length() - 2) );
+ if ( !silent && !rx.isValid() ) {
+ docLocation.warning( tr("Invalid regular expression '%1'")
+ .arg(rx.pattern()) );
+ silent = true;
+ }
+ return str.indexOf( rx ) != -1;
+ }
+ trimWhiteSpace(str);
+ trimWhiteSpace(pattern);
+ return str.indexOf(pattern) != -1;
+}
+
+void Quoter::failedAtEnd( const Location& docLocation, const QString& command )
+{
+ if (!silent && !command.isEmpty()) {
+ if ( codeLocation.filePath().isEmpty() ) {
+ docLocation.warning( tr("Unexpected '\\%1'").arg(command) );
+ } else {
+ docLocation.warning( tr("Command '\\%1' failed at end of file '%2'")
+ .arg(command).arg(codeLocation.filePath()) );
+ }
+ silent = true;
+ }
+}
+
+QString Quoter::commentForCode() const
+{
+ QString suffix = QFileInfo(codeLocation.fileName()).suffix();
+ return commentHash.value(suffix, "//!");
+}
+
+QT_END_NAMESPACE
diff --git a/tools/qdoc3/quoter.h b/tools/qdoc3/quoter.h
new file mode 100644
index 0000000..7906754
--- /dev/null
+++ b/tools/qdoc3/quoter.h
@@ -0,0 +1,89 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*
+ quoter.h
+*/
+
+#ifndef QUOTER_H
+#define QUOTER_H
+
+#include <qhash.h>
+#include <qstringlist.h>
+
+#include "location.h"
+
+QT_BEGIN_NAMESPACE
+
+class Quoter
+{
+public:
+ Quoter();
+
+ void reset();
+ void quoteFromFile( const QString& userFriendlyFileName,
+ const QString& plainCode, const QString& markedCode );
+ QString quoteLine( const Location& docLocation, const QString& command,
+ const QString& pattern );
+ QString quoteTo( const Location& docLocation, const QString& command,
+ const QString& pattern );
+ QString quoteUntil( const Location& docLocation, const QString& command,
+ const QString& pattern );
+ QString quoteSnippet(const Location &docLocation, const QString &identifier);
+
+private:
+ QString getLine();
+ void failedAtEnd( const Location& docLocation, const QString& command );
+ bool match( const Location& docLocation, const QString& pattern,
+ const QString& line );
+ QString commentForCode() const;
+
+ bool silent;
+ bool validRegExp;
+ QStringList plainLines;
+ QStringList markedLines;
+ Location codeLocation;
+ QHash<QString,QString> commentHash;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/tools/qdoc3/separator.cpp b/tools/qdoc3/separator.cpp
new file mode 100644
index 0000000..0d730e1
--- /dev/null
+++ b/tools/qdoc3/separator.cpp
@@ -0,0 +1,69 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*
+ separator.cpp
+*/
+
+#include "separator.h"
+#include "tr.h"
+
+QT_BEGIN_NAMESPACE
+
+QString separator( int index, int count )
+{
+ if ( index == count - 1 )
+ return tr( ".", "terminator" );
+
+ if ( count == 2 ) {
+ return tr( " and ", "separator when N = 2" );
+ } else {
+ if ( index == 0 ) {
+ return tr( ", ", "first separator when N > 2" );
+ } else if ( index < count - 2 ) {
+ return tr( ", ", "general separator when N > 2" );
+ } else {
+ return tr( ", and ", "last separator when N > 2" );
+ }
+ }
+}
+
+QT_END_NAMESPACE
diff --git a/tools/qdoc3/separator.h b/tools/qdoc3/separator.h
new file mode 100644
index 0000000..f29868c
--- /dev/null
+++ b/tools/qdoc3/separator.h
@@ -0,0 +1,57 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*
+ separator.h
+*/
+
+#ifndef SEPARATOR_H
+#define SEPARATOR_H
+
+#include <qstring.h>
+
+QT_BEGIN_NAMESPACE
+
+QString separator( int index, int count );
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/tools/qdoc3/sgmlgenerator.cpp b/tools/qdoc3/sgmlgenerator.cpp
new file mode 100644
index 0000000..575bb53
--- /dev/null
+++ b/tools/qdoc3/sgmlgenerator.cpp
@@ -0,0 +1,63 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*
+ sgmlgenerator.cpp
+*/
+
+#include "sgmlgenerator.h"
+
+QT_BEGIN_NAMESPACE
+
+SgmlGenerator::SgmlGenerator()
+{
+}
+
+SgmlGenerator::~SgmlGenerator()
+{
+}
+
+QString SgmlGenerator::format()
+{
+ return "SGML";
+}
+
+QT_END_NAMESPACE
diff --git a/tools/qdoc3/sgmlgenerator.h b/tools/qdoc3/sgmlgenerator.h
new file mode 100644
index 0000000..e4ffa80
--- /dev/null
+++ b/tools/qdoc3/sgmlgenerator.h
@@ -0,0 +1,67 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*
+ sgmlgenerator.h
+*/
+
+#ifndef SGMLGENERATOR_H
+#define SGMLGENERATOR_H
+
+#include "bookgenerator.h"
+
+QT_BEGIN_NAMESPACE
+
+class SgmlGenerator : public BookGenerator
+{
+public:
+ SgmlGenerator();
+ ~SgmlGenerator();
+
+ virtual QString format();
+
+protected:
+ // ###
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/tools/qdoc3/test/arthurtext.qdocconf b/tools/qdoc3/test/arthurtext.qdocconf
new file mode 100644
index 0000000..473152d
--- /dev/null
+++ b/tools/qdoc3/test/arthurtext.qdocconf
@@ -0,0 +1,6 @@
+include(qt.qdocconf)
+headerdirs =
+sourcedirs =
+sources = $QTDIR/doc/src/qt4-arthur.qdoc
+outputdir = $QTDIR/demos/arthur/html
+HTML.{postheader,address} = "" \ No newline at end of file
diff --git a/tools/qdoc3/test/assistant.qdocconf b/tools/qdoc3/test/assistant.qdocconf
new file mode 100644
index 0000000..7bb6bbf
--- /dev/null
+++ b/tools/qdoc3/test/assistant.qdocconf
@@ -0,0 +1,45 @@
+include(compat.qdocconf)
+include(macros.qdocconf)
+include(qt-cpp-ignore.qdocconf)
+include(qt-html-templates.qdocconf)
+include(qt-defines.qdocconf)
+
+project = Qt Assistant
+description = Qt Assistant Manual
+url = http://doc.trolltech.com/4.5
+
+indexes = $QT_BUILD_TREE/doc-build/html-qt/qt.index
+
+qhp.projects = Assistant
+
+qhp.Assistant.file = assistant.qhp
+qhp.Assistant.namespace = com.trolltech.assistant.450
+qhp.Assistant.virtualFolder = qdoc
+qhp.Assistant.indexTitle = Qt Assistant Manual
+qhp.Assistant.extraFiles = classic.css images/qt-logo.png images/trolltech-logo.png
+qhp.Assistant.filterAttributes = qt 4.5.0 tools assistant
+qhp.Assistant.customFilters.Assistant.name = Qt Assistant Manual
+qhp.Assistant.customFilters.Assistant.filterAttributes = qt tools assistant
+qhp.Assistant.subprojects = manual examples
+qhp.Assistant.subprojects.manual.title = Manual
+qhp.Assistant.subprojects.manual.indexTitle = Qt Assistant Manual
+qhp.Assistant.subprojects.manual.selectors = fake:page
+qhp.Assistant.subprojects.examples.title = Examples
+qhp.Assistant.subprojects.examples.indexTitle = Qt Examples
+qhp.Assistant.subprojects.examples.selectors = fake:example
+qhp.Assistant.subprojects.examples.sortPages = true
+
+language = Cpp
+
+sources = $QT_SOURCE_TREE/doc/src/assistant-manual.qdoc \
+ $QT_SOURCE_TREE/doc/src/examples/simpletextviewer.qdoc
+
+sources.fileextensions = "*.cpp *.qdoc"
+
+exampledirs = $QT_SOURCE_TREE \
+ $QT_SOURCE_TREE/examples \
+ $QT_SOURCE_TREE/doc/src
+
+imagedirs = $QT_SOURCE_TREE/doc/src/images
+outputdir = $QT_BUILD_TREE/doc-build/html-assistant
+base = file:$QT_BUILD_TREE/doc-build/html-assistant
diff --git a/tools/qdoc3/test/carbide-eclipse-integration.qdocconf b/tools/qdoc3/test/carbide-eclipse-integration.qdocconf
new file mode 100644
index 0000000..683048d
--- /dev/null
+++ b/tools/qdoc3/test/carbide-eclipse-integration.qdocconf
@@ -0,0 +1,12 @@
+include(eclipse-integration.qdocconf)
+
+defines += NO_QT_ECLIPSE_INSTALLATION_PAGE
+macro.theEclipseIntegration = Carbide.c++
+macro.TheEclipseIntegration = Carbide.c++
+
+HTML.footer = "<p /><address><hr /><div align=\"center\">\n" \
+ "<table width=\"100%\" cellspacing=\"0\" border=\"0\"><tr class=\"address\">\n" \
+ "<td width=\"40%\" align="left">Copyright &copy; 2009 <a href=\"http://doc.trolltech.com/trolltech.html\">Nokia Corporation</a></td>\n" \
+ "<td width=\"30%\" align=\"center\"><a href=\"http://doc.trolltech.com\">Trademarks</a></td>\n" \
+ "<td width=\"40%\" align=\"right\"><div align=\"right\">Carbide.c++</div></td>\n" \
+ "</tr></table></div></address>"
diff --git a/tools/qdoc3/test/classic.css b/tools/qdoc3/test/classic.css
new file mode 100644
index 0000000..0d0b664
--- /dev/null
+++ b/tools/qdoc3/test/classic.css
@@ -0,0 +1,131 @@
+h3.fn,span.fn
+{
+ margin-left: 1cm;
+ text-indent: -1cm;
+}
+
+a:link
+{
+ color: #004faf;
+ text-decoration: none
+}
+
+a:visited
+{
+ color: #672967;
+ text-decoration: none
+}
+
+a.obsolete
+{
+ color: #661100;
+ text-decoration: none
+}
+
+a.compat
+{
+ color: #661100;
+ text-decoration: none
+}
+
+a.obsolete:visited
+{
+ color: #995500;
+ text-decoration: none
+}
+
+a.compat:visited
+{
+ color: #995500;
+ text-decoration: none
+}
+
+td.postheader
+{
+ font-family: sans-serif
+}
+
+tr.address
+{
+ font-family: sans-serif
+}
+
+body
+{
+ background: #ffffff;
+ color: black
+}
+
+table tr.odd {
+ background: #f0f0f0;
+ color: black;
+}
+
+table tr.even {
+ background: #e4e4e4;
+ color: black;
+}
+
+table.annotated th {
+ padding: 3px;
+ text-align: left
+}
+
+table.annotated td {
+ padding: 3px;
+}
+
+table tr pre
+{
+ padding-top: none;
+ padding-bottom: none;
+ padding-left: none;
+ padding-right: none;
+ border: none;
+ background: none
+}
+
+tr.qt-style
+{
+ background: #96E066;
+ color: black
+}
+
+body pre
+{
+ padding: 0.2em;
+ border: #e7e7e7 1px solid;
+ background: #f1f1f1;
+ color: black
+}
+
+span.preprocessor, span.preprocessor a
+{
+ color: darkblue;
+}
+
+span.comment
+{
+ color: darkred;
+ font-style: italic
+}
+
+span.string,span.char
+{
+ color: darkgreen;
+}
+
+.title
+{
+ text-align: center
+}
+
+.subtitle
+{
+ font-size: 0.8em
+}
+
+.small-subtitle
+{
+ font-size: 0.65em
+}
diff --git a/tools/qdoc3/test/compat.qdocconf b/tools/qdoc3/test/compat.qdocconf
new file mode 100644
index 0000000..5745ed9
--- /dev/null
+++ b/tools/qdoc3/test/compat.qdocconf
@@ -0,0 +1,31 @@
+alias.i = e
+alias.include = input
+
+macro.0 = "\\\\0"
+macro.b = "\\\\b"
+macro.n = "\\\\n"
+macro.r = "\\\\r"
+macro.i = "\\o"
+macro.i11 = "\\o{1,1}"
+macro.i12 = "\\o{1,2}"
+macro.i13 = "\\o{1,3}"
+macro.i14 = "\\o{1,4}"
+macro.i15 = "\\o{1,5}"
+macro.i16 = "\\o{1,6}"
+macro.i17 = "\\o{1,7}"
+macro.i18 = "\\o{1,8}"
+macro.i19 = "\\o{1,9}"
+macro.i21 = "\\o{2,1}"
+macro.i31 = "\\o{3,1}"
+macro.i41 = "\\o{4,1}"
+macro.i51 = "\\o{5,1}"
+macro.i61 = "\\o{6,1}"
+macro.i71 = "\\o{7,1}"
+macro.i81 = "\\o{8,1}"
+macro.i91 = "\\o{9,1}"
+macro.img = "\\image"
+macro.endquote = "\\endquotation"
+macro.relatesto = "\\relates"
+
+spurious = "Missing comma in .*" \
+ "Missing pattern .*"
diff --git a/tools/qdoc3/test/designer.qdocconf b/tools/qdoc3/test/designer.qdocconf
new file mode 100644
index 0000000..26636cd
--- /dev/null
+++ b/tools/qdoc3/test/designer.qdocconf
@@ -0,0 +1,51 @@
+include(compat.qdocconf)
+include(macros.qdocconf)
+include(qt-cpp-ignore.qdocconf)
+include(qt-html-templates.qdocconf)
+include(qt-defines.qdocconf)
+
+project = Qt Designer
+description = Qt Designer Manual
+url = http://doc.trolltech.com/4.5
+
+indexes = $QT_BUILD_TREE/doc-build/html-qt/qt.index
+
+qhp.projects = Designer
+
+qhp.Designer.file = designer.qhp
+qhp.Designer.namespace = com.trolltech.designer.450
+qhp.Designer.virtualFolder = qdoc
+qhp.Designer.indexTitle = Qt Designer Manual
+qhp.Designer.extraFiles = classic.css images/qt-logo.png images/trolltech-logo.png
+qhp.Designer.filterAttributes = qt 4.5.0 tools designer
+qhp.Designer.customFilters.Designer.name = Qt Designer Manual
+qhp.Designer.customFilters.Designer.filterAttributes = qt tools designer
+qhp.Designer.subprojects = manual examples
+qhp.Designer.subprojects.manual.title = Manual
+qhp.Designer.subprojects.manual.indexTitle = Qt Designer Manual
+qhp.Designer.subprojects.manual.selectors = fake:page
+qhp.Designer.subprojects.examples.title = Examples
+qhp.Designer.subprojects.examples.indexTitle = Qt Examples
+qhp.Designer.subprojects.examples.selectors = fake:example
+qhp.Designer.subprojects.examples.sortPages = true
+
+language = Cpp
+
+sources = $QT_SOURCE_TREE/doc/src/designer-manual.qdoc \
+ $QT_SOURCE_TREE/doc/src/examples/calculatorbuilder.qdoc \
+ $QT_SOURCE_TREE/doc/src/examples/calculatorform.qdoc \
+ $QT_SOURCE_TREE/doc/src/examples/customwidgetplugin.qdoc \
+ $QT_SOURCE_TREE/doc/src/examples/taskmenuextension.qdoc \
+ $QT_SOURCE_TREE/doc/src/examples/containerextension.qdoc \
+ $QT_SOURCE_TREE/doc/src/examples/worldtimeclockbuilder.qdoc \
+ $QT_SOURCE_TREE/doc/src/examples/worldtimeclockplugin.qdoc
+
+sources.fileextensions = "*.cpp *.qdoc"
+
+exampledirs = $QT_SOURCE_TREE \
+ $QT_SOURCE_TREE/examples \
+ $QT_SOURCE_TREE/doc/src
+
+imagedirs = $QT_SOURCE_TREE/doc/src/images
+outputdir = $QT_BUILD_TREE/doc-build/html-designer
+base = file:$QT_BUILD_TREE/doc-build/html-designer
diff --git a/tools/qdoc3/test/eclipse-integration.qdocconf b/tools/qdoc3/test/eclipse-integration.qdocconf
new file mode 100644
index 0000000..1fdaa1b
--- /dev/null
+++ b/tools/qdoc3/test/eclipse-integration.qdocconf
@@ -0,0 +1,13 @@
+include(qt.qdocconf)
+
+headerdirs =
+sourcedirs =
+sourcedirs = $QTDIR/../qteclipsetools/main/doc
+imagedirs = $QTDIR/../qteclipsetools/main/doc
+outputdir = $QTDIR/../qteclipsetools/main/doc/html
+
+project = Qt Eclipse Integration
+description = "Qt Eclipse Integration"
+url = http://doc.trolltech.com/eclipse-integration-4.4
+
+HTML.{postheader,address} = ""
diff --git a/tools/qdoc3/test/jambi.qdocconf b/tools/qdoc3/test/jambi.qdocconf
new file mode 100644
index 0000000..3644b69
--- /dev/null
+++ b/tools/qdoc3/test/jambi.qdocconf
@@ -0,0 +1,47 @@
+include(compat.qdocconf)
+include(macros.qdocconf)
+
+project = Qt Jambi
+description = Qt Jambi Reference Documentation
+url = http://doc.trolltech.com/qtjambi
+
+version = 4.4.0_01
+
+language = Java
+
+sources = $JAMBI/doc/japi/qdoc.japi
+outputformats = javadoc HTML
+outputdir = $JAMBI/doc/html/com/trolltech/qt
+
+imagedirs = $QTDIR/doc/src/images \
+ $QTDIR/examples \
+ $JAMBI/doc/src/images \
+ ../doc/src/images
+
+extraimages.javadoc = qt-logo \
+ qt-logo.png
+
+HTML.stylesheets = classic.css
+
+HTML.postheader = "<table border=\"0\" cellpadding=\"0\" cellspacing=\"0\" width=\"100%\">\n" \
+ "<tr>\n" \
+ "<td align=\"left\" valign=\"top\" width=\"32\">" \
+ "<img src=\"images/qt-logo.png\" align=\"left\" width=\"32\" height=\"32\" border=\"0\" />" \
+ "</td>\n" \
+ "<td width=\"1\">&nbsp;&nbsp;</td>" \
+ "<td class=\"postheader\" valign=\"center\">" \
+ "<a href=\"qtjambi-index.html\">" \
+ "<font color=\"#004faf\">Home</font></a>&nbsp;&middot;" \
+ " <a href=\"qtjambi-overviews.html\">" \
+ "<font color=\"#004faf\">Overviews</font></a>&nbsp;&middot;" \
+ " <a href=\"qtjambi-examples.html\">" \
+ "<font color=\"#004faf\">Examples</font></a>&nbsp;" \
+ "</td>\n" \
+ "</tr></table>"
+
+HTML.footer = "<p /><address><hr /><div align=\"center\">\n" \
+ "<table width=\"100%\" cellspacing=\"0\" border=\"0\"><tr class=\"address\">\n" \
+ "<td width=\"30%\">Copyright &copy; \$THISYEAR\$ <a href=\"trolltech.html\">Trolltech</a></td>\n" \
+ "<td width=\"40%\" align=\"center\"><a href=\"trademarks.html\">Trademarks</a></td>\n" \
+ "<td width=\"30%\" align=\"right\"><div align=\"right\">Qt Jambi \\version</div></td>\n" \
+ "</tr></table></div></address>"
diff --git a/tools/qdoc3/test/linguist.qdocconf b/tools/qdoc3/test/linguist.qdocconf
new file mode 100644
index 0000000..5e889a8
--- /dev/null
+++ b/tools/qdoc3/test/linguist.qdocconf
@@ -0,0 +1,47 @@
+include(compat.qdocconf)
+include(macros.qdocconf)
+include(qt-cpp-ignore.qdocconf)
+include(qt-html-templates.qdocconf)
+include(qt-defines.qdocconf)
+
+project = Qt Linguist
+description = Qt Linguist Manual
+url = http://doc.trolltech.com/4.5
+
+indexes = $QT_BUILD_TREE/doc-build/html-qt/qt.index
+
+qhp.projects = Linguist
+
+qhp.Linguist.file = linguist.qhp
+qhp.Linguist.namespace = com.trolltech.linguist.450
+qhp.Linguist.virtualFolder = qdoc
+qhp.Linguist.indexTitle = Qt Linguist Manual
+qhp.Linguist.extraFiles = classic.css images/qt-logo.png images/trolltech-logo.png
+qhp.Linguist.filterAttributes = qt 4.5.0 tools linguist
+qhp.Linguist.customFilters.Linguist.name = Qt Linguist Manual
+qhp.Linguist.customFilters.Linguist.filterAttributes = qt tools linguist
+qhp.Linguist.subprojects = manual examples
+qhp.Linguist.subprojects.manual.title = Manual
+qhp.Linguist.subprojects.manual.indexTitle = Qt Linguist Manual
+qhp.Linguist.subprojects.manual.selectors = fake:page
+qhp.Linguist.subprojects.examples.title = Examples
+qhp.Linguist.subprojects.examples.indexTitle = Qt Examples
+qhp.Linguist.subprojects.examples.selectors = fake:example
+qhp.Linguist.subprojects.examples.sortPages = true
+
+language = Cpp
+
+sources = $QT_SOURCE_TREE/doc/src/linguist-manual.qdoc \
+ $QT_SOURCE_TREE/doc/src/examples/hellotr.qdoc \
+ $QT_SOURCE_TREE/doc/src/examples/arrowpad.qdoc \
+ $QT_SOURCE_TREE/doc/src/examples/trollprint.qdoc
+
+sources.fileextensions = "*.cpp *.qdoc"
+
+exampledirs = $QT_SOURCE_TREE \
+ $QT_SOURCE_TREE/examples \
+ $QT_SOURCE_TREE/doc/src
+
+imagedirs = $QT_SOURCE_TREE/doc/src/images
+outputdir = $QT_BUILD_TREE/doc-build/html-linguist
+base = file:$QT_BUILD_TREE/doc-build/html-linguist
diff --git a/tools/qdoc3/test/macros.qdocconf b/tools/qdoc3/test/macros.qdocconf
new file mode 100644
index 0000000..d14f80a
--- /dev/null
+++ b/tools/qdoc3/test/macros.qdocconf
@@ -0,0 +1,27 @@
+macro.Aring.HTML = "&Aring;"
+macro.aring.HTML = "&aring;"
+macro.Auml.HTML = "&Auml;"
+macro.author = "\\bold{Author:}"
+macro.br.HTML = "<br />"
+macro.BR.HTML = "<br />"
+macro.aacute.HTML = "&aacute;"
+macro.eacute.HTML = "&eacute;"
+macro.iacute.HTML = "&iacute;"
+macro.gui = "\\bold"
+macro.hr.HTML = "<hr />"
+macro.key = "\\bold"
+macro.menu = "\\bold"
+macro.note = "\\bold{Note:}"
+macro.oslash.HTML = "&oslash;"
+macro.ouml.HTML = "&ouml;"
+macro.QA = "\\e{Qt Assistant}"
+macro.QD = "\\e{Qt Designer}"
+macro.QL = "\\e{Qt Linguist}"
+macro.param = "\\e"
+macro.raisedaster.HTML = "<sup>*</sup>"
+macro.rarrow.HTML = "&rarr;"
+macro.reg.HTML = "<sup>&reg;</sup>"
+macro.return = "Returns"
+macro.starslash = "\\c{*/}"
+macro.uuml.HTML = "&uuml;"
+macro.mdash.HTML = "&mdash;"
diff --git a/tools/qdoc3/test/qmake.qdocconf b/tools/qdoc3/test/qmake.qdocconf
new file mode 100644
index 0000000..c357cfb
--- /dev/null
+++ b/tools/qdoc3/test/qmake.qdocconf
@@ -0,0 +1,40 @@
+include(compat.qdocconf)
+include(macros.qdocconf)
+include(qt-cpp-ignore.qdocconf)
+include(qt-html-templates.qdocconf)
+include(qt-defines.qdocconf)
+
+project = QMake
+description = QMake Manual
+url = http://doc.trolltech.com/4.5
+
+indexes = $QT_BUILD_TREE/doc-build/html-qt/qt.index
+
+qhp.projects = qmake
+
+qhp.qmake.file = qmake.qhp
+qhp.qmake.namespace = com.trolltech.qmake.450
+qhp.qmake.virtualFolder = qdoc
+qhp.qmake.indexTitle = QMake Manual
+qhp.qmake.extraFiles = classic.css images/qt-logo.png images/trolltech-logo.png
+qhp.qmake.filterAttributes = qt 4.5.0 tools qmake
+qhp.qmake.customFilters.qmake.name = qmake Manual
+qhp.qmake.customFilters.qmake.filterAttributes = qt tools qmake
+qhp.qmake.subprojects = manual
+qhp.qmake.subprojects.manual.title = Manual
+qhp.qmake.subprojects.manual.indexTitle = qmake Manual
+qhp.qmake.subprojects.manual.selectors = fake:page
+
+language = Cpp
+
+sources = $QT_SOURCE_TREE/doc/src/qmake-manual.qdoc
+
+sources.fileextensions = "*.cpp *.qdoc"
+
+exampledirs = $QT_SOURCE_TREE \
+ $QT_SOURCE_TREE/examples \
+ $QT_SOURCE_TREE/doc/src
+
+imagedirs = $QT_SOURCE_TREE/doc/src/images
+outputdir = $QT_BUILD_TREE/doc-build/html-qmake
+base = file:$QT_BUILD_TREE/doc-build/html-qmake
diff --git a/tools/qdoc3/test/qt-api-only-with-xcode.qdocconf b/tools/qdoc3/test/qt-api-only-with-xcode.qdocconf
new file mode 100644
index 0000000..0389386
--- /dev/null
+++ b/tools/qdoc3/test/qt-api-only-with-xcode.qdocconf
@@ -0,0 +1,29 @@
+include(qt-build-docs-with-xcode.qdocconf)
+
+# Ensures that the generated index contains a URL that can be used by the
+# tools documentation (assistant.qdocconf, designer.qdocconf, linguist.qdocconf,
+# qmake.qdocconf).
+
+url = ./
+
+# Ensures that the documentation for the tools is not included in the generated
+# .qhp file.
+
+qhp.Qt.excluded = $QT_SOURCE_TREE/doc/src/assistant-manual.qdoc \
+ $QT_SOURCE_TREE/doc/src/examples/simpletextviewer.qdoc \
+ $QT_SOURCE_TREE/doc/src/designer-manual.qdoc \
+ $QT_SOURCE_TREE/doc/src/examples/calculatorbuilder.qdoc \
+ $QT_SOURCE_TREE/doc/src/examples/calculatorform.qdoc \
+ $QT_SOURCE_TREE/doc/src/examples/customwidgetplugin.qdoc \
+ $QT_SOURCE_TREE/doc/src/examples/taskmenuextension.qdoc \
+ $QT_SOURCE_TREE/doc/src/examples/containerextension.qdoc \
+ $QT_SOURCE_TREE/doc/src/examples/worldtimeclockbuilder.qdoc \
+ $QT_SOURCE_TREE/doc/src/examples/worldtimeclockplugin.qdoc \
+ $QT_SOURCE_TREE/doc/src/linguist-manual.qdoc \
+ $QT_SOURCE_TREE/doc/src/examples/hellotr.qdoc \
+ $QT_SOURCE_TREE/doc/src/examples/arrowpad.qdoc \
+ $QT_SOURCE_TREE/doc/src/examples/trollprint.qdoc \
+ $QT_SOURCE_TREE/doc/src/qmake-manual.qdoc
+
+outputdir = $QT_BUILD_TREE/doc-build/html-qt
+base = file:$QT_BUILD_TREE/doc-build/html-qt
diff --git a/tools/qdoc3/test/qt-api-only.qdocconf b/tools/qdoc3/test/qt-api-only.qdocconf
new file mode 100644
index 0000000..2e91ba2
--- /dev/null
+++ b/tools/qdoc3/test/qt-api-only.qdocconf
@@ -0,0 +1,30 @@
+include(qt-build-docs.qdocconf)
+
+# Ensures that the generated index contains a URL that can be used by the
+# tools documentation (assistant.qdocconf, designer.qdocconf, linguist.qdocconf,
+# qmake.qdocconf).
+
+url = ./
+
+# Ensures that the documentation for the tools is not included in the generated
+# .qhp file.
+
+qhp.Qt.excluded = $QT_SOURCE_TREE/doc/src/assistant-manual.qdoc \
+ $QT_SOURCE_TREE/doc/src/examples/simpletextviewer.qdoc \
+ $QT_SOURCE_TREE/doc/src/designer-manual.qdoc \
+ $QT_SOURCE_TREE/doc/src/examples/calculatorbuilder.qdoc \
+ $QT_SOURCE_TREE/doc/src/examples/calculatorform.qdoc \
+ $QT_SOURCE_TREE/doc/src/examples/customwidgetplugin.qdoc \
+ $QT_SOURCE_TREE/doc/src/examples/taskmenuextension.qdoc \
+ $QT_SOURCE_TREE/doc/src/examples/containerextension.qdoc \
+ $QT_SOURCE_TREE/doc/src/examples/worldtimeclockbuilder.qdoc \
+ $QT_SOURCE_TREE/doc/src/examples/worldtimeclockplugin.qdoc \
+ $QT_SOURCE_TREE/doc/src/linguist-manual.qdoc \
+ $QT_SOURCE_TREE/doc/src/examples/hellotr.qdoc \
+ $QT_SOURCE_TREE/doc/src/examples/arrowpad.qdoc \
+ $QT_SOURCE_TREE/doc/src/examples/trollprint.qdoc \
+ $QT_SOURCE_TREE/doc/src/qmake-manual.qdoc
+
+outputdir = $QT_BUILD_TREE/doc-build/html-qt
+tagfile = $QT_BUILD_TREE/doc-build/html-qt/qt.tags
+base = file:$QT_BUILD_TREE/doc-build/html-qt
diff --git a/tools/qdoc3/test/qt-build-docs-with-xcode.qdocconf b/tools/qdoc3/test/qt-build-docs-with-xcode.qdocconf
new file mode 100644
index 0000000..e4be476
--- /dev/null
+++ b/tools/qdoc3/test/qt-build-docs-with-xcode.qdocconf
@@ -0,0 +1,3 @@
+include( qt-build-docs.qdocconf )
+
+HTML.generatemacrefs = "true"
diff --git a/tools/qdoc3/test/qt-build-docs.qdocconf b/tools/qdoc3/test/qt-build-docs.qdocconf
new file mode 100644
index 0000000..a085768
--- /dev/null
+++ b/tools/qdoc3/test/qt-build-docs.qdocconf
@@ -0,0 +1,109 @@
+include(compat.qdocconf)
+include(macros.qdocconf)
+include(qt-cpp-ignore.qdocconf)
+include(qt-html-templates.qdocconf)
+include(qt-defines.qdocconf)
+
+project = Qt
+description = Qt Reference Documentation
+url = http://doc.trolltech.com/4.5
+
+edition.Console.modules = QtCore QtDBus QtNetwork QtScript QtSql QtXml \
+ QtXmlPatterns QtTest
+edition.Desktop.modules = QtCore QtDBus QtGui QtNetwork QtOpenGL QtScript QtSql QtSvg \
+ QtWebKit QtXml QtXmlPatterns Qt3Support QtHelp \
+ QtDesigner QtAssistant QAxContainer Phonon \
+ QAxServer QtUiTools QtTest QtDBus
+edition.DesktopLight.modules = QtCore QtDBus QtGui Qt3SupportLight QtTest
+edition.DesktopLight.groups = -graphicsview-api
+
+qhp.projects = Qt
+
+qhp.Qt.file = qt.qhp
+qhp.Qt.namespace = com.trolltech.qt.450
+qhp.Qt.virtualFolder = qdoc
+qhp.Qt.indexTitle = Qt Reference Documentation
+qhp.Qt.indexRoot =
+
+# Files not referenced in any qdoc file (last four are needed by qtdemo)
+# See also extraimages.HTML
+qhp.Qt.extraFiles = classic.css \
+ images/qt-logo.png \
+ images/trolltech-logo \
+ images/taskmenuextension-example.png \
+ images/coloreditorfactoryimage.png \
+ images/dynamiclayouts-example.png \
+ images/stylesheet-coffee-plastique.png
+
+qhp.Qt.filterAttributes = qt 4.5.0 qtrefdoc
+qhp.Qt.customFilters.Qt.name = Qt 4.5.0
+qhp.Qt.customFilters.Qt.filterAttributes = qt 4.5.0
+qhp.Qt.subprojects = classes overviews examples
+qhp.Qt.subprojects.classes.title = Classes
+qhp.Qt.subprojects.classes.indexTitle = Qt's Classes
+qhp.Qt.subprojects.classes.selectors = class fake:headerfile
+qhp.Qt.subprojects.classes.sortPages = true
+qhp.Qt.subprojects.overviews.title = Overviews
+qhp.Qt.subprojects.overviews.indexTitle = All Overviews and HOWTOs
+qhp.Qt.subprojects.overviews.selectors = fake:page,group,module
+qhp.Qt.subprojects.examples.title = Tutorials and Examples
+qhp.Qt.subprojects.examples.indexTitle = Qt Examples
+qhp.Qt.subprojects.examples.selectors = fake:example
+
+language = Cpp
+
+headerdirs = $QT_SOURCE_TREE/src \
+ $QT_SOURCE_TREE/extensions/activeqt \
+ $QT_SOURCE_TREE/tools/assistant/lib \
+ $QT_SOURCE_TREE/tools/assistant/compat/lib \
+ $QT_SOURCE_TREE/tools/designer/src/uitools \
+ $QT_SOURCE_TREE/tools/designer/src/lib/extension \
+ $QT_SOURCE_TREE/tools/designer/src/lib/sdk \
+ $QT_SOURCE_TREE/tools/designer/src/lib/uilib \
+ $QT_SOURCE_TREE/tools/qtestlib/src \
+ $QT_SOURCE_TREE/tools/qdbus/src
+sourcedirs = $QT_SOURCE_TREE/src \
+ $QT_SOURCE_TREE/doc/src \
+ $QT_SOURCE_TREE/extensions/activeqt \
+ $QT_SOURCE_TREE/tools/assistant/lib \
+ $QT_SOURCE_TREE/tools/assistant/compat/lib \
+ $QT_SOURCE_TREE/tools/designer/src/uitools \
+ $QT_SOURCE_TREE/tools/designer/src/lib/extension \
+ $QT_SOURCE_TREE/tools/designer/src/lib/sdk \
+ $QT_SOURCE_TREE/tools/designer/src/lib/uilib \
+ $QT_SOURCE_TREE/tools/qtestlib/src \
+ $QT_SOURCE_TREE/tools/qdbus
+
+excludedirs = $QT_SOURCE_TREE/src/3rdparty/clucene \
+ $QT_SOURCE_TREE/src/3rdparty/des \
+ $QT_SOURCE_TREE/src/3rdparty/freetype \
+ $QT_SOURCE_TREE/src/3rdparty/harfbuzz \
+ $QT_SOURCE_TREE/src/3rdparty/kdebase \
+ $QT_SOURCE_TREE/src/3rdparty/libjpeg \
+ $QT_SOURCE_TREE/src/3rdparty/libmng \
+ $QT_SOURCE_TREE/src/3rdparty/libpng \
+ $QT_SOURCE_TREE/src/3rdparty/libtiff \
+ $QT_SOURCE_TREE/src/3rdparty/md4 \
+ $QT_SOURCE_TREE/src/3rdparty/md5 \
+ $QT_SOURCE_TREE/src/3rdparty/patches \
+ $QT_SOURCE_TREE/src/3rdparty/sha1 \
+ $QT_SOURCE_TREE/src/3rdparty/sqlite \
+ $QT_SOURCE_TREE/src/3rdparty/webkit/JavaScriptCore \
+ $QT_SOURCE_TREE/src/3rdparty/webkit/WebCore \
+ $QT_SOURCE_TREE/src/3rdparty/wintab \
+ $QT_SOURCE_TREE/src/3rdparty/zlib \
+ $QT_SOURCE_TREE/doc/src/snippets
+
+sources.fileextensions = "*.cpp *.qdoc *.mm"
+examples.fileextensions = "*.cpp *.h *.js *.xq *.svg *.xml *.ui *.qhp *.qhcp"
+
+exampledirs = $QT_SOURCE_TREE/doc/src \
+ $QT_SOURCE_TREE/examples \
+ $QT_SOURCE_TREE/examples/tutorials \
+ $QT_SOURCE_TREE \
+ $QT_SOURCE_TREE/qmake/examples
+imagedirs = $QT_SOURCE_TREE/doc/src/images \
+ $QT_SOURCE_TREE/examples
+outputdir = $QT_BUILD_TREE/doc/html
+tagfile = $QT_BUILD_TREE/doc/html/qt.tags
+base = file:$QT_BUILD_TREE/doc/html
diff --git a/tools/qdoc3/test/qt-cpp-ignore.qdocconf b/tools/qdoc3/test/qt-cpp-ignore.qdocconf
new file mode 100644
index 0000000..107c692
--- /dev/null
+++ b/tools/qdoc3/test/qt-cpp-ignore.qdocconf
@@ -0,0 +1,87 @@
+Cpp.ignoretokens = QAXFACTORY_EXPORT \
+ QDESIGNER_COMPONENTS_LIBRARY \
+ QDESIGNER_EXTENSION_LIBRARY \
+ QDESIGNER_SDK_LIBRARY \
+ QDESIGNER_SHARED_LIBRARY \
+ QDESIGNER_UILIB_LIBRARY \
+ QM_EXPORT_CANVAS \
+ QM_EXPORT_DNS \
+ QM_EXPORT_DOM \
+ QM_EXPORT_FTP \
+ QM_EXPORT_HTTP \
+ QM_EXPORT_ICONVIEW \
+ QM_EXPORT_NETWORK \
+ QM_EXPORT_OPENGL \
+ QM_EXPORT_SQL \
+ QM_EXPORT_TABLE \
+ QM_EXPORT_WORKSPACE \
+ QM_EXPORT_XML \
+ QT_ASCII_CAST_WARN \
+ QT_ASCII_CAST_WARN_CONSTRUCTOR \
+ QT_BEGIN_HEADER \
+ QT_DESIGNER_STATIC \
+ QT_END_HEADER \
+ QT_FASTCALL \
+ QT_WIDGET_PLUGIN_EXPORT \
+ Q_COMPAT_EXPORT \
+ Q_CORE_EXPORT \
+ Q_EXPLICIT \
+ Q_EXPORT \
+ Q_EXPORT_CODECS_CN \
+ Q_EXPORT_CODECS_JP \
+ Q_EXPORT_CODECS_KR \
+ Q_EXPORT_PLUGIN \
+ Q_GFX_INLINE \
+ Q_AUTOTEST_EXPORT \
+ Q_GUI_EXPORT \
+ Q_GUI_EXPORT_INLINE \
+ Q_GUI_EXPORT_STYLE_CDE \
+ Q_GUI_EXPORT_STYLE_COMPACT \
+ Q_GUI_EXPORT_STYLE_MAC \
+ Q_GUI_EXPORT_STYLE_MOTIF \
+ Q_GUI_EXPORT_STYLE_MOTIFPLUS \
+ Q_GUI_EXPORT_STYLE_PLATINUM \
+ Q_GUI_EXPORT_STYLE_POCKETPC \
+ Q_GUI_EXPORT_STYLE_SGI \
+ Q_GUI_EXPORT_STYLE_WINDOWS \
+ Q_GUI_EXPORT_STYLE_WINDOWSXP \
+ QHELP_EXPORT \
+ Q_INLINE_TEMPLATE \
+ Q_INTERNAL_WIN_NO_THROW \
+ Q_NETWORK_EXPORT \
+ Q_OPENGL_EXPORT \
+ Q_OUTOFLINE_TEMPLATE \
+ Q_SQL_EXPORT \
+ Q_SVG_EXPORT \
+ Q_SCRIPT_EXPORT \
+ Q_SCRIPTTOOLS_EXPORT \
+ Q_TESTLIB_EXPORT \
+ Q_TYPENAME \
+ Q_XML_EXPORT \
+ Q_XMLSTREAM_EXPORT \
+ Q_XMLPATTERNS_EXPORT \
+ QDBUS_EXPORT \
+ QT_BEGIN_NAMESPACE \
+ QT_BEGIN_INCLUDE_NAMESPACE \
+ QT_END_NAMESPACE \
+ QT_END_INCLUDE_NAMESPACE \
+ PHONON_EXPORT
+Cpp.ignoredirectives = Q_DECLARE_HANDLE \
+ Q_DECLARE_INTERFACE \
+ Q_DECLARE_METATYPE \
+ Q_DECLARE_OPERATORS_FOR_FLAGS \
+ Q_DECLARE_PRIVATE \
+ Q_DECLARE_PUBLIC \
+ Q_DECLARE_SHARED \
+ Q_DECLARE_TR_FUNCTIONS \
+ Q_DECLARE_TYPEINFO \
+ Q_DISABLE_COPY \
+ QT_FORWARD_DECLARE_CLASS \
+ Q_DUMMY_COMPARISON_OPERATOR \
+ Q_ENUMS \
+ Q_FLAGS \
+ Q_INTERFACES \
+ __attribute__ \
+ K_DECLARE_PRIVATE \
+ PHONON_OBJECT \
+ PHONON_HEIR
diff --git a/tools/qdoc3/test/qt-defines.qdocconf b/tools/qdoc3/test/qt-defines.qdocconf
new file mode 100644
index 0000000..b22727f
--- /dev/null
+++ b/tools/qdoc3/test/qt-defines.qdocconf
@@ -0,0 +1,26 @@
+defines = Q_QDOC \
+ QT_.*_SUPPORT \
+ QT_.*_LIB \
+ QT_COMPAT \
+ QT_KEYPAD_NAVIGATION \
+ QT3_SUPPORT \
+ Q_WS_.* \
+ Q_OS_.* \
+ Q_BYTE_ORDER \
+ QT_DEPRECATED \
+ Q_NO_USING_KEYWORD \
+ __cplusplus
+
+versionsym = QT_VERSION_STR
+
+codeindent = 1
+
+# Files not referenced in any qdoc file (last four needed by qtdemo)
+# See also qhp.Qt.extraFiles
+extraimages.HTML = qt-logo \
+ trolltech-logo \
+ taskmenuextension-example.png \
+ coloreditorfactoryimage.png \
+ dynamiclayouts-example.png \
+ stylesheet-coffee-plastique.png
+ \ No newline at end of file
diff --git a/tools/qdoc3/test/qt-for-jambi.qdocconf b/tools/qdoc3/test/qt-for-jambi.qdocconf
new file mode 100644
index 0000000..2285e0e
--- /dev/null
+++ b/tools/qdoc3/test/qt-for-jambi.qdocconf
@@ -0,0 +1,12 @@
+include(qt.qdocconf)
+
+# Comment out for full run:
+# sourcedirs = $QTDIR/doc/src $QTDIR/src/corelib/tools
+
+sourcedirs += $JAMBI/doc/src
+exampledirs += $JAMBI/com/trolltech/examples
+
+macro.QJ = "Qt Jambi"
+macro.QC = "Qt/C++"
+
+outputformats =
diff --git a/tools/qdoc3/test/qt-html-templates.qdocconf b/tools/qdoc3/test/qt-html-templates.qdocconf
new file mode 100644
index 0000000..f09192a
--- /dev/null
+++ b/tools/qdoc3/test/qt-html-templates.qdocconf
@@ -0,0 +1,32 @@
+HTML.stylesheets = classic.css
+HTML.postheader = "<table border=\"0\" cellpadding=\"0\" cellspacing=\"0\" width=\"100%\">\n" \
+ "<tr>\n" \
+ "<td align=\"left\" valign=\"top\" width=\"32\">" \
+ "<a href=\"http://qtsoftware.com/products/qt\"><img src=\"images/qt-logo.png\" align=\"left\" border=\"0\" /></a>" \
+ "</td>\n" \
+ "<td width=\"1\">&nbsp;&nbsp;</td>" \
+ "<td class=\"postheader\" valign=\"center\">" \
+ "<a href=\"index.html\">" \
+ "<font color=\"#004faf\">Home</font></a>&nbsp;&middot;" \
+ " <a href=\"namespaces.html\">" \
+ "<font color=\"#004faf\">All&nbsp;Namespaces</font></a>&nbsp;&middot;" \
+ " <a href=\"classes.html\">" \
+ "<font color=\"#004faf\">All&nbsp;Classes</font></a>&nbsp;&middot;" \
+ " <a href=\"mainclasses.html\">" \
+ "<font color=\"#004faf\">Main&nbsp;Classes</font></a>&nbsp;&middot;" \
+ " <a href=\"groups.html\">" \
+ "<font color=\"#004faf\">Grouped&nbsp;Classes</font></a>&nbsp;&middot;" \
+ " <a href=\"modules.html\">" \
+ "<font color=\"#004faf\">Modules</font></a>&nbsp;&middot;" \
+ " <a href=\"functions.html\">" \
+ "<font color=\"#004faf\">Functions</font></a>" \
+ "</td>\n" \
+ "<td align=\"right\" valign=\"top\" width=\"230\"></td></tr></table>"
+
+HTML.footer = "<p /><address><hr /><div align=\"center\">\n" \
+ "<table width=\"100%\" cellspacing=\"0\" border=\"0\"><tr class=\"address\">\n" \
+ "<td width=\"30%\" align=\"left\">Copyright &copy; %THISYEAR% Nokia Corporation " \
+ "and/or its subsidiary(-ies)</td>\n" \
+ "<td width=\"40%\" align=\"center\"><a href=\"trademarks.html\">Trademarks</a></td>\n" \
+ "<td width=\"30%\" align=\"right\"><div align=\"right\">Qt \\version</div></td>\n" \
+ "</tr></table></div></address>"
diff --git a/tools/qdoc3/test/qt-inc.qdocconf b/tools/qdoc3/test/qt-inc.qdocconf
new file mode 100644
index 0000000..97893dc
--- /dev/null
+++ b/tools/qdoc3/test/qt-inc.qdocconf
@@ -0,0 +1,146 @@
+include(compat.qdocconf)
+include(macros.qdocconf)
+
+project = Qt
+description = Qt Reference Documentation
+url = http://doc.trolltech.com/4.5
+
+edition.Console = QtCore QtNetwork QtSql QtXml QtScript QtTest
+edition.Desktop = QtCore QtGui QtNetwork QtOpenGL QtSql QtSvg QtXml QtScript \
+ QtDesigner QtAssistant Qt3Support QAxContainer \
+ QAxServer QtUiTools QtTest QtDBus
+edition.DesktopLight = QtCore QtGui Qt3SupportLight QtTest
+
+language = Cpp
+
+norecursion = true
+sources.fileextensions = "*.cpp *.qdoc"
+sourcedirs = $QDOC_CURRENT_DIR
+headerdirs = $QDOC_CURRENT_DIR
+exampledirs = $QTDIR/doc/src \
+ $QTDIR/examples \
+ $QTDIR \
+ $QTDIR/qmake/examples
+imagedirs = $QTDIR/doc/src/images \
+ $QTDIR/examples
+outputdir = $QTDIR/doc/html
+indexdir = $QTDIR/doc/indexes
+indexes = $QDOC_INPUT_INDEXES
+outputindex = $QDOC_OUTPUT_INDEX
+base = file:$QTDIR/doc/html
+versionsym = QT_VERSION_STR
+defines = Q_QDOC \
+ QT_.*_SUPPORT \
+ QT_.*_LIB \
+ QT_COMPAT \
+ QT_KEYPAD_NAVIGATION \
+ QT3_SUPPORT \
+ Q_WS_.* \
+ Q_OS_.* \
+ Q_BYTE_ORDER \
+ __cplusplus
+
+codeindent = 1
+extraimages.HTML = qt-logo \
+ trolltech-logo
+
+Cpp.ignoretokens = QAXFACTORY_EXPORT \
+ QDESIGNER_COMPONENTS_LIBRARY \
+ QDESIGNER_EXTENSION_LIBRARY \
+ QDESIGNER_SDK_LIBRARY \
+ QDESIGNER_SHARED_LIBRARY \
+ QDESIGNER_UILIB_LIBRARY \
+ QM_EXPORT_CANVAS \
+ QM_EXPORT_DNS \
+ QM_EXPORT_DOM \
+ QM_EXPORT_FTP \
+ QM_EXPORT_HTTP \
+ QM_EXPORT_ICONVIEW \
+ QM_EXPORT_NETWORK \
+ QM_EXPORT_OPENGL \
+ QM_EXPORT_SQL \
+ QM_EXPORT_TABLE \
+ QM_EXPORT_WORKSPACE \
+ QM_EXPORT_XML \
+ QT_ASCII_CAST_WARN \
+ QT_BEGIN_HEADER \
+ QT_DESIGNER_STATIC \
+ QT_END_HEADER \
+ QT_WIDGET_PLUGIN_EXPORT \
+ Q_COMPAT_EXPORT \
+ Q_CORE_EXPORT \
+ Q_EXPLICIT \
+ Q_EXPORT \
+ Q_EXPORT_CODECS_CN \
+ Q_EXPORT_CODECS_JP \
+ Q_EXPORT_CODECS_KR \
+ Q_EXPORT_PLUGIN \
+ Q_GFX_INLINE \
+ Q_GUI_EXPORT \
+ Q_GUI_EXPORT_INLINE \
+ Q_GUI_EXPORT_STYLE_CDE \
+ Q_GUI_EXPORT_STYLE_COMPACT \
+ Q_GUI_EXPORT_STYLE_MAC \
+ Q_GUI_EXPORT_STYLE_MOTIF \
+ Q_GUI_EXPORT_STYLE_MOTIFPLUS \
+ Q_GUI_EXPORT_STYLE_PLATINUM \
+ Q_GUI_EXPORT_STYLE_POCKETPC \
+ Q_GUI_EXPORT_STYLE_SGI \
+ Q_GUI_EXPORT_STYLE_WINDOWS \
+ Q_GUI_EXPORT_STYLE_WINDOWSXP \
+ Q_INLINE_TEMPLATE \
+ Q_NETWORK_EXPORT \
+ Q_OPENGL_EXPORT \
+ Q_OUTOFLINE_TEMPLATE \
+ Q_SQL_EXPORT \
+ Q_SVG_EXPORT \
+ Q_SCRIPT_EXPORT \
+ Q_TESTLIB_EXPORT \
+ Q_TYPENAME \
+ Q_XML_EXPORT \
+ QDBUS_EXPORT
+Cpp.ignoredirectives = Q_DECLARE_HANDLE \
+ Q_DECLARE_INTERFACE \
+ Q_DECLARE_METATYPE \
+ Q_DECLARE_OPERATORS_FOR_FLAGS \
+ Q_DECLARE_PRIVATE \
+ Q_DECLARE_PUBLIC \
+ Q_DECLARE_SHARED \
+ Q_DECLARE_TR_FUNCTIONS \
+ Q_DECLARE_TYPEINFO \
+ Q_DISABLE_COPY \
+ Q_DUMMY_COMPARISON_OPERATOR \
+ Q_ENUMS \
+ Q_FLAGS \
+ Q_INTERFACES \
+ __attribute__
+
+HTML.stylesheets = $QTDIR/util/qdoc3/test/classic.css
+HTML.postheader = "<table border=\"0\" cellpadding=\"0\" cellspacing=\"0\" width=\"100%\">\n" \
+ "<tr>\n" \
+ "<td align=\"left\" valign=\"top\" width=\"32\">" \
+ "<a href=\"http://qtsoftware.com/products/qt\"><img src=\"images/qt-logo.png\" align=\"left\" width=\"32\" height=\"32\" border=\"0\" /></a>" \
+ "</td>\n" \
+ "<td width=\"1\">&nbsp;&nbsp;</td>" \
+ "<td class=\"postheader\" valign=\"center\">" \
+ "<a href=\"index.html\">" \
+ "<font color=\"#004faf\">Home</font></a>&nbsp;&middot;" \
+ " <a href=\"classes.html\">" \
+ "<font color=\"#004faf\">All&nbsp;Classes</font></a>&nbsp;&middot;" \
+ " <a href=\"mainclasses.html\">" \
+ "<font color=\"#004faf\">Main&nbsp;Classes</font></a>&nbsp;&middot;" \
+ " <a href=\"groups.html\">" \
+ "<font color=\"#004faf\">Grouped&nbsp;Classes</font></a>&nbsp;&middot;" \
+ " <a href=\"modules.html\">" \
+ "<font color=\"#004faf\">Modules</font></a>&nbsp;&middot;" \
+ " <a href=\"functions.html\">" \
+ "<font color=\"#004faf\">Functions</font></a>" \
+ "</td>\n" \
+ "<td align=\"right\" valign=\"top\" width=\"230\"><a href=\"http://qtsoftware.com\"><img src=\"images/trolltech-logo.png\" align=\"right\" width=\"203\" height=\"32\" border=\"0\" /></a></td></tr></table>"
+
+HTML.footer = "<p /><address><hr /><div align=\"center\">\n" \
+ "<table width=\"100%\" cellspacing=\"0\" border=\"0\"><tr class=\"address\">\n" \
+ "<td width=\"30%\">Copyright &copy; %THISYEAR% <a href=\"trolltech.html\">Trolltech</a></td>\n" \
+ "<td width=\"40%\" align=\"center\"><a href=\"trademarks.html\">Trademarks</a></td>\n" \
+ "<td width=\"30%\" align=\"right\"><div align=\"right\">Qt \\version</div></td>\n" \
+ "</tr></table></div></address>"
diff --git a/tools/qdoc3/test/qt-linguist.qdocconf b/tools/qdoc3/test/qt-linguist.qdocconf
new file mode 100644
index 0000000..7564226
--- /dev/null
+++ b/tools/qdoc3/test/qt-linguist.qdocconf
@@ -0,0 +1,4 @@
+include(qt.qdocconf)
+
+outputdir = $QTDIR/doc/linguist
+outputformats = Linguist
diff --git a/tools/qdoc3/test/qt-webxml.qdocconf b/tools/qdoc3/test/qt-webxml.qdocconf
new file mode 100644
index 0000000..3ad0457
--- /dev/null
+++ b/tools/qdoc3/test/qt-webxml.qdocconf
@@ -0,0 +1,11 @@
+include(qt.qdocconf)
+
+quotinginformation = true
+imagedirs = $QTDIR/doc/src/images \
+ $QTDIR/examples
+
+outputdir = $QTDIR/doc/webxml
+outputformats = WebXML
+
+generateindex = true
+url = .
diff --git a/tools/qdoc3/test/qt-with-extensions.qdocconf b/tools/qdoc3/test/qt-with-extensions.qdocconf
new file mode 100644
index 0000000..45188c7
--- /dev/null
+++ b/tools/qdoc3/test/qt-with-extensions.qdocconf
@@ -0,0 +1,8 @@
+include( qt.qdocconf )
+
+project = Qt with Extensions
+headerdirs += $QUICK/src/extensions
+headers += qsa/date.h
+sourcedirs += $QUICK/src/extensions
+spurious = ".*"
+outputformats = ""
diff --git a/tools/qdoc3/test/qt-with-xcode.qdocconf b/tools/qdoc3/test/qt-with-xcode.qdocconf
new file mode 100644
index 0000000..932f6d9
--- /dev/null
+++ b/tools/qdoc3/test/qt-with-xcode.qdocconf
@@ -0,0 +1,3 @@
+include( qt.qdocconf )
+
+HTML.generatemacrefs = "true"
diff --git a/tools/qdoc3/test/qt.qdocconf b/tools/qdoc3/test/qt.qdocconf
new file mode 100644
index 0000000..298311e
--- /dev/null
+++ b/tools/qdoc3/test/qt.qdocconf
@@ -0,0 +1,115 @@
+include(compat.qdocconf)
+include(macros.qdocconf)
+include(qt-cpp-ignore.qdocconf)
+include(qt-html-templates.qdocconf)
+include(qt-defines.qdocconf)
+
+project = Qt
+versionsym =
+version = %VERSION%
+description = Qt Reference Documentation
+url = http://doc.trolltech.com/4.5
+
+edition.Console.modules = QtCore QtDBus QtNetwork QtScript QtSql QtXml \
+ QtXmlPatterns QtTest
+edition.Desktop.modules = QtCore QtDBus QtGui QtNetwork QtOpenGL QtScript QtScriptTools QtSql QtSvg \
+ QtWebKit QtXml QtXmlPatterns Qt3Support QtHelp \
+ QtDesigner QtAssistant QAxContainer Phonon \
+ QAxServer QtUiTools QtTest QtDBus
+edition.DesktopLight.modules = QtCore QtDBus QtGui Qt3SupportLight QtTest
+edition.DesktopLight.groups = -graphicsview-api
+
+qhp.projects = Qt
+
+qhp.Qt.file = qt.qhp
+qhp.Qt.namespace = com.trolltech.qt.450
+qhp.Qt.virtualFolder = qdoc
+qhp.Qt.indexTitle = Qt Reference Documentation
+qhp.Qt.indexRoot =
+
+# Files not referenced in any qdoc file (last four are needed by qtdemo)
+# See also extraimages.HTML
+qhp.Qt.extraFiles = classic.css \
+ images/qt-logo.png \
+ images/trolltech-logo \
+ images/taskmenuextension-example.png \
+ images/coloreditorfactoryimage.png \
+ images/dynamiclayouts-example.png \
+ images/stylesheet-coffee-plastique.png
+
+qhp.Qt.filterAttributes = qt 4.5.0 qtrefdoc
+qhp.Qt.customFilters.Qt.name = Qt 4.5.0
+qhp.Qt.customFilters.Qt.filterAttributes = qt 4.5.0
+qhp.Qt.subprojects = classes overviews examples
+qhp.Qt.subprojects.classes.title = Classes
+qhp.Qt.subprojects.classes.indexTitle = Qt's Classes
+qhp.Qt.subprojects.classes.selectors = class fake:headerfile
+qhp.Qt.subprojects.classes.sortPages = true
+qhp.Qt.subprojects.overviews.title = Overviews
+qhp.Qt.subprojects.overviews.indexTitle = All Overviews and HOWTOs
+qhp.Qt.subprojects.overviews.selectors = fake:page,group,module
+qhp.Qt.subprojects.examples.title = Tutorials and Examples
+qhp.Qt.subprojects.examples.indexTitle = Qt Examples
+qhp.Qt.subprojects.examples.selectors = fake:example
+
+language = Cpp
+
+headerdirs = $QTDIR/src \
+ $QTDIR/extensions/activeqt \
+ $QTDIR/tools/assistant/lib \
+ $QTDIR/tools/assistant/compat/lib \
+ $QTDIR/tools/designer/src/uitools \
+ $QTDIR/tools/designer/src/lib/extension \
+ $QTDIR/tools/designer/src/lib/sdk \
+ $QTDIR/tools/designer/src/lib/uilib \
+ $QTDIR/tools/qtestlib/src \
+ $QTDIR/tools/qdbus/src
+sourcedirs = $QTDIR/src \
+ $QTDIR/doc/src \
+ $QTDIR/extensions/activeqt \
+ $QTDIR/tools/assistant/lib \
+ $QTDIR/tools/assistant/compat/lib \
+ $QTDIR/tools/designer/src/uitools \
+ $QTDIR/tools/designer/src/lib/extension \
+ $QTDIR/tools/designer/src/lib/sdk \
+ $QTDIR/tools/designer/src/lib/uilib \
+ $QTDIR/tools/qtestlib/src \
+ $QTDIR/tools/qdbus
+
+excludedirs = $QTDIR/src/3rdparty/clucene \
+ $QTDIR/src/3rdparty/des \
+ $QTDIR/src/3rdparty/freetype \
+ $QTDIR/src/3rdparty/harfbuzz \
+ $QTDIR/src/3rdparty/kdebase \
+ $QTDIR/src/3rdparty/libjpeg \
+ $QTDIR/src/3rdparty/libmng \
+ $QTDIR/src/3rdparty/libpng \
+ $QTDIR/src/3rdparty/libtiff \
+ $QTDIR/src/3rdparty/md4 \
+ $QTDIR/src/3rdparty/md5 \
+ $QTDIR/src/3rdparty/patches \
+ $QTDIR/src/3rdparty/sha1 \
+ $QTDIR/src/3rdparty/sqlite \
+ $QTDIR/src/3rdparty/webkit/JavaScriptCore \
+ $QTDIR/src/3rdparty/webkit/WebCore \
+ $QTDIR/src/3rdparty/wintab \
+ $QTDIR/src/3rdparty/zlib \
+ $QTDIR/doc/src/snippets \
+ $QTDIR/src/3rdparty/phonon/gstreamer \
+ $QTDIR/src/3rdparty/phonon/ds9 \
+ $QTDIR/src/3rdparty/phonon/qt7 \
+ $QTDIR/src/3rdparty/phonon/waveout
+
+sources.fileextensions = "*.cpp *.qdoc *.mm"
+examples.fileextensions = "*.cpp *.h *.js *.xq *.svg *.xml *.ui *.qhp *.qhcp"
+
+exampledirs = $QTDIR/doc/src \
+ $QTDIR/examples \
+ $QTDIR/examples/tutorials \
+ $QTDIR \
+ $QTDIR/qmake/examples
+imagedirs = $QTDIR/doc/src/images \
+ $QTDIR/examples
+outputdir = $QTDIR/doc/html
+tagfile = $QTDIR/doc/html/qt.tags
+base = file:$QTDIR/doc/html
diff --git a/tools/qdoc3/test/standalone-eclipse-integration.qdocconf b/tools/qdoc3/test/standalone-eclipse-integration.qdocconf
new file mode 100644
index 0000000..c3c4291
--- /dev/null
+++ b/tools/qdoc3/test/standalone-eclipse-integration.qdocconf
@@ -0,0 +1,11 @@
+include(eclipse-integration.qdocconf)
+
+macro.theEclipseIntegration = the Qt Eclipse Integration
+macro.TheEclipseIntegration = The Qt Eclipse Integration
+
+HTML.footer = "<p /><address><hr /><div align=\"center\">\n" \
+ "<table width=\"100%\" cellspacing=\"0\" border=\"0\"><tr class=\"address\">\n" \
+ "<td width=\"40%\" align="left">Copyright &copy; 2009 <a href=\"http://doc.trolltech.com\">Nokia Corporation</a></td>\n" \
+ "<td width=\"30%\" align=\"center\"><a href=\"http://doc.trolltech.com/trademarks.html\">Trademarks</a></td>\n" \
+ "<td width=\"40%\" align=\"right\"><div align=\"right\">Qt Eclipse Integration 1.4.3</div></td>\n" \
+ "</tr></table></div></address>"
diff --git a/tools/qdoc3/text.cpp b/tools/qdoc3/text.cpp
new file mode 100644
index 0000000..0b0a8d7
--- /dev/null
+++ b/tools/qdoc3/text.cpp
@@ -0,0 +1,270 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*
+ text.cpp
+*/
+
+#include <qregexp.h>
+#include "text.h"
+#include <stdio.h>
+
+QT_BEGIN_NAMESPACE
+
+Text::Text()
+ : first(0), last(0)
+{
+}
+
+Text::Text(const QString &str)
+ : first(0), last(0)
+{
+ operator<<(str);
+}
+
+Text::Text( const Text& text )
+ : first(0), last(0)
+{
+ operator=( text );
+}
+
+Text::~Text()
+{
+ clear();
+}
+
+Text& Text::operator=( const Text& text )
+{
+ if ( this != &text ) {
+ clear();
+ operator<<( text );
+ }
+ return *this;
+}
+
+Text& Text::operator<<( Atom::Type atomType )
+{
+ return operator<<( Atom(atomType) );
+}
+
+Text& Text::operator<<( const QString& string )
+{
+ return operator<<( Atom(Atom::String, string) );
+}
+
+Text& Text::operator<<( const Atom& atom )
+{
+ if ( first == 0 ) {
+ first = new Atom( atom.type(), atom.string() );
+ last = first;
+ } else {
+ last = new Atom( last, atom.type(), atom.string() );
+ }
+ return *this;
+}
+
+Text& Text::operator<<( const Text& text )
+{
+ const Atom *atom = text.firstAtom();
+ while ( atom != 0 ) {
+ operator<<( *atom );
+ atom = atom->next();
+ }
+ return *this;
+}
+
+void Text::stripFirstAtom()
+{
+ if ( first != 0 ) {
+ if ( first == last )
+ last = 0;
+ Atom *oldFirst = first;
+ first = first->next();
+ delete oldFirst;
+ }
+}
+
+void Text::stripLastAtom()
+{
+ if ( last != 0 ) {
+ Atom *oldLast = last;
+ if ( first == last ) {
+ first = 0;
+ last = 0;
+ } else {
+ last = first;
+ while ( last->next() != oldLast )
+ last = last->next();
+ last->setNext( 0 );
+ }
+ delete oldLast;
+ }
+}
+
+QString Text::toString() const
+{
+ QString str;
+ const Atom *atom = firstAtom();
+ while ( atom != 0 ) {
+ if ( atom->type() == Atom::String || atom->type() == Atom::AutoLink )
+ str += atom->string();
+ atom = atom->next();
+ }
+ return str;
+}
+
+Text Text::subText( Atom::Type left, Atom::Type right, const Atom *from ) const
+{
+ const Atom *begin = from ? from : firstAtom();
+ const Atom *end;
+
+ while ( begin != 0 && begin->type() != left )
+ begin = begin->next();
+ if ( begin != 0 )
+ begin = begin->next();
+
+ end = begin;
+ while ( end != 0 && end->type() != right )
+ end = end->next();
+ if ( end == 0 )
+ begin = 0;
+ return subText( begin, end );
+}
+
+Text Text::sectionHeading(const Atom *sectionLeft)
+{
+ if ( sectionLeft != 0 ) {
+ const Atom *begin = sectionLeft;
+ while ( begin != 0 && begin->type() != Atom::SectionHeadingLeft )
+ begin = begin->next();
+ if ( begin != 0 )
+ begin = begin->next();
+
+ const Atom *end = begin;
+ while ( end != 0 && end->type() != Atom::SectionHeadingRight )
+ end = end->next();
+
+ if ( end != 0 )
+ return subText( begin, end );
+ }
+ return Text();
+}
+
+const Atom *Text::sectionHeadingAtom(const Atom *sectionLeft)
+{
+ if ( sectionLeft != 0 ) {
+ const Atom *begin = sectionLeft;
+ while ( begin != 0 && begin->type() != Atom::SectionHeadingLeft )
+ begin = begin->next();
+ if ( begin != 0 )
+ begin = begin->next();
+
+ return begin;
+ }
+ return 0;
+}
+
+void Text::dump() const
+{
+ const Atom *atom = firstAtom();
+ while ( atom != 0 ) {
+ QString str = atom->string();
+ str.replace( "\\", "\\\\" );
+ str.replace( "\"", "\\\"" );
+ str.replace( "\n", "\\n" );
+ str.replace( QRegExp("[^\x20-\x7e]"), "?" );
+ if ( !str.isEmpty() )
+ str = " \"" + str + "\"";
+ fprintf(stderr, " %-15s%s\n", atom->typeString().toLatin1().data(), str.toLatin1().data() );
+ atom = atom->next();
+ }
+}
+
+Text Text::subText( const Atom *begin, const Atom *end )
+{
+ Text text;
+ if ( begin != 0 ) {
+ while ( begin != end ) {
+ text << *begin;
+ begin = begin->next();
+ }
+ }
+ return text;
+}
+
+void Text::clear()
+{
+ while ( first != 0 ) {
+ Atom *atom = first;
+ first = first->next();
+ delete atom;
+ }
+ first = 0;
+ last = 0;
+}
+
+int Text::compare(const Text &text1, const Text &text2)
+{
+ if (text1.isEmpty())
+ return text2.isEmpty() ? 0 : -1;
+ if (text2.isEmpty())
+ return 1;
+
+ const Atom *atom1 = text1.firstAtom();
+ const Atom *atom2 = text2.firstAtom();
+
+ for (;;) {
+ if (atom1->type() != atom2->type())
+ return (int)atom1->type() - (int)atom2->type();
+ int cmp = QString::compare(atom1->string(), atom2->string());
+ if (cmp != 0)
+ return cmp;
+
+ if (atom1 == text1.lastAtom())
+ return atom2 == text2.lastAtom() ? 0 : -1;
+ if (atom2 == text2.lastAtom())
+ return 1;
+ atom1 = atom1->next();
+ atom2 = atom2->next();
+ }
+}
+
+QT_END_NAMESPACE
diff --git a/tools/qdoc3/text.h b/tools/qdoc3/text.h
new file mode 100644
index 0000000..82506df
--- /dev/null
+++ b/tools/qdoc3/text.h
@@ -0,0 +1,106 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*
+ text.h
+*/
+
+#ifndef TEXT_H
+#define TEXT_H
+
+#include "atom.h"
+
+QT_BEGIN_NAMESPACE
+
+class Text
+{
+ public:
+ Text();
+ explicit Text(const QString &str);
+ Text(const Text& text);
+ ~Text();
+
+ Text& operator=(const Text& text);
+
+ Atom *firstAtom() { return first; }
+ Atom *lastAtom() { return last; }
+ Text& operator<<(Atom::Type atomType);
+ Text& operator<<(const QString& string);
+ Text& operator<<(const Atom& atom);
+ Text& operator<<(const Text& text);
+ void stripFirstAtom();
+ void stripLastAtom();
+
+ bool isEmpty() const { return first == 0; }
+ QString toString() const;
+ const Atom *firstAtom() const { return first; }
+ const Atom *lastAtom() const { return last; }
+ Text subText(Atom::Type left, Atom::Type right, const Atom *from = 0) const;
+ void dump() const;
+
+ static Text subText(const Atom *begin, const Atom *end = 0);
+ static Text sectionHeading(const Atom *sectionBegin);
+ static const Atom *sectionHeadingAtom(const Atom *sectionLeft);
+ static int compare(const Text &text1, const Text &text2);
+
+ private:
+ void clear();
+
+ Atom *first;
+ Atom *last;
+};
+
+inline bool operator==(const Text &text1, const Text &text2)
+{ return Text::compare(text1, text2) == 0; }
+inline bool operator!=(const Text &text1, const Text &text2)
+{ return Text::compare(text1, text2) != 0; }
+inline bool operator<(const Text &text1, const Text &text2)
+{ return Text::compare(text1, text2) < 0; }
+inline bool operator<=(const Text &text1, const Text &text2)
+{ return Text::compare(text1, text2) <= 0; }
+inline bool operator>(const Text &text1, const Text &text2)
+{ return Text::compare(text1, text2) > 0; }
+inline bool operator>=(const Text &text1, const Text &text2)
+{ return Text::compare(text1, text2) >= 0; }
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/tools/qdoc3/tokenizer.cpp b/tools/qdoc3/tokenizer.cpp
new file mode 100644
index 0000000..24b1b5a
--- /dev/null
+++ b/tools/qdoc3/tokenizer.cpp
@@ -0,0 +1,753 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "config.h"
+#include "tokenizer.h"
+
+#include <qdebug.h>
+#include <qfile.h>
+#include <qhash.h>
+#include <qregexp.h>
+#include <qstring.h>
+
+#include <ctype.h>
+#include <string.h>
+
+QT_BEGIN_NAMESPACE
+
+#define LANGUAGE_CPP "Cpp"
+
+/* qmake ignore Q_OBJECT */
+
+/*
+ Keep in sync with tokenizer.h.
+*/
+static const char *kwords[] = {
+ "char", "class", "const", "double", "enum", "explicit",
+ "friend", "inline", "int", "long", "namespace", "operator",
+ "private", "protected", "public", "short", "signals", "signed",
+ "slots", "static", "struct", "template", "typedef", "typename",
+ "union", "unsigned", "using", "virtual", "void", "volatile",
+ "__int64", "Q_OBJECT", "Q_OVERRIDE", "Q_PROPERTY",
+ "Q_DECLARE_SEQUENTIAL_ITERATOR",
+ "Q_DECLARE_MUTABLE_SEQUENTIAL_ITERATOR",
+ "Q_DECLARE_ASSOCIATIVE_ITERATOR",
+ "Q_DECLARE_MUTABLE_ASSOCIATIVE_ITERATOR",
+ "Q_DECLARE_FLAGS",
+ "Q_SIGNALS",
+ "Q_SLOTS",
+ "QT_COMPAT",
+ "QT_COMPAT_CONSTRUCTOR",
+ "QT_DEPRECATED",
+ "QT_MOC_COMPAT",
+ "QT_MODULE",
+ "QT3_SUPPORT",
+ "QT3_SUPPORT_CONSTRUCTOR",
+ "QT3_MOC_SUPPORT",
+ "QDOC_PROPERTY"
+};
+
+static const int KwordHashTableSize = 4096;
+static int kwordHashTable[KwordHashTableSize];
+
+static QHash<QByteArray, bool> *ignoredTokensAndDirectives = 0;
+
+static QRegExp *comment = 0;
+static QRegExp *versionX = 0;
+static QRegExp *definedX = 0;
+
+static QRegExp *defines = 0;
+static QRegExp *falsehoods = 0;
+
+/*
+ This function is a perfect hash function for the 37 keywords of C99
+ (with a hash table size of 512). It should perform well on our
+ Qt-enhanced C++ subset.
+*/
+static int hashKword(const char *s, int len)
+{
+ return (((uchar) s[0]) + (((uchar) s[2]) << 5) +
+ (((uchar) s[len - 1]) << 3)) % KwordHashTableSize;
+}
+
+static void insertKwordIntoHash(const char *s, int number)
+{
+ int k = hashKword(s, strlen(s));
+ while (kwordHashTable[k]) {
+ if (++k == KwordHashTableSize)
+ k = 0;
+ }
+ kwordHashTable[k] = number;
+}
+
+Tokenizer::Tokenizer(const Location& loc, FILE *in)
+{
+ init();
+ QFile file;
+ file.open(in, QIODevice::ReadOnly);
+ yyIn = file.readAll();
+ file.close();
+ yyPos = 0;
+ start(loc);
+}
+
+Tokenizer::Tokenizer(const Location& loc, const QByteArray &in)
+ : yyIn(in)
+{
+ init();
+ yyPos = 0;
+ start(loc);
+}
+
+Tokenizer::~Tokenizer()
+{
+ delete[] yyLexBuf1;
+ delete[] yyLexBuf2;
+}
+
+int Tokenizer::getToken()
+{
+ char *t = yyPrevLex;
+ yyPrevLex = yyLex;
+ yyLex = t;
+
+ while (yyCh != EOF) {
+ yyTokLoc = yyCurLoc;
+ yyLexLen = 0;
+
+ if (isspace(yyCh)) {
+ do {
+ yyCh = getChar();
+ } while (isspace(yyCh));
+ }
+ else if (isalpha(yyCh) || yyCh == '_') {
+ do {
+ yyCh = getChar();
+ } while (isalnum(yyCh) || yyCh == '_');
+
+ int k = hashKword(yyLex, yyLexLen);
+ for (;;) {
+ int i = kwordHashTable[k];
+ if (i == 0) {
+ return Tok_Ident;
+ }
+ else if (i == -1) {
+ if (!parsingMacro && ignoredTokensAndDirectives->contains(yyLex)) {
+ if (ignoredTokensAndDirectives->value(yyLex)) { // it's a directive
+ int parenDepth = 0;
+ while (yyCh != EOF && (yyCh != ')' || parenDepth > 1)) {
+ if (yyCh == '(')
+ ++parenDepth;
+ else if (yyCh == ')')
+ --parenDepth;
+ yyCh = getChar();
+ }
+ if (yyCh == ')')
+ yyCh = getChar();
+ }
+ break;
+ }
+ }
+ else if (strcmp(yyLex, kwords[i - 1]) == 0) {
+ int ret = (int) Tok_FirstKeyword + i - 1;
+ if (ret != Tok_explicit && ret != Tok_inline && ret != Tok_typename)
+ return ret;
+ break;
+ }
+
+ if (++k == KwordHashTableSize)
+ k = 0;
+ }
+ }
+ else if (isdigit(yyCh)) {
+ do {
+ yyCh = getChar();
+ } while (isalnum(yyCh) || yyCh == '.' || yyCh == '+' ||
+ yyCh == '-');
+ return Tok_Number;
+ }
+ else {
+ switch (yyCh) {
+ case '!':
+ case '%':
+ yyCh = getChar();
+ if (yyCh == '=')
+ yyCh = getChar();
+ return Tok_SomeOperator;
+ case '"':
+ yyCh = getChar();
+
+ while (yyCh != EOF && yyCh != '"') {
+ if (yyCh == '\\')
+ yyCh = getChar();
+ yyCh = getChar();
+ }
+ yyCh = getChar();
+
+ if (yyCh == EOF)
+ yyTokLoc.warning(tr("Unterminated C++ string literal"),
+ tr("Maybe you forgot '/*!' at the beginning of the file?"));
+ else
+ return Tok_String;
+ break;
+ case '#':
+ return getTokenAfterPreprocessor();
+ case '&':
+ yyCh = getChar();
+ if (yyCh == '&' || yyCh == '=') {
+ yyCh = getChar();
+ return Tok_SomeOperator;
+ }
+ else {
+ return Tok_Ampersand;
+ }
+ case '\'':
+ yyCh = getChar();
+ if (yyCh == '\\')
+ yyCh = getChar();
+ do {
+ yyCh = getChar();
+ } while (yyCh != EOF && yyCh != '\'');
+
+ if (yyCh == EOF) {
+ yyTokLoc.warning(tr("Unterminated C++ character"
+ " literal"));
+ }
+ else {
+ yyCh = getChar();
+ return Tok_Number;
+ }
+ break;
+ case '(':
+ yyCh = getChar();
+ if (yyNumPreprocessorSkipping == 0)
+ yyParenDepth++;
+ if (isspace(yyCh)) {
+ do {
+ yyCh = getChar();
+ } while (isspace(yyCh));
+ yyLexLen = 1;
+ yyLex[1] = '\0';
+ }
+ if (yyCh == '*') {
+ yyCh = getChar();
+ return Tok_LeftParenAster;
+ }
+ return Tok_LeftParen;
+ case ')':
+ yyCh = getChar();
+ if (yyNumPreprocessorSkipping == 0)
+ yyParenDepth--;
+ return Tok_RightParen;
+ case '*':
+ yyCh = getChar();
+ if (yyCh == '=') {
+ yyCh = getChar();
+ return Tok_SomeOperator;
+ } else {
+ return Tok_Aster;
+ }
+ case '^':
+ yyCh = getChar();
+ if (yyCh == '=') {
+ yyCh = getChar();
+ return Tok_SomeOperator;
+ } else {
+ return Tok_Caret;
+ }
+ case '+':
+ yyCh = getChar();
+ if (yyCh == '+' || yyCh == '=')
+ yyCh = getChar();
+ return Tok_SomeOperator;
+ case ',':
+ yyCh = getChar();
+ return Tok_Comma;
+ case '-':
+ yyCh = getChar();
+ if (yyCh == '-' || yyCh == '=') {
+ yyCh = getChar();
+ } else if (yyCh == '>') {
+ yyCh = getChar();
+ if (yyCh == '*')
+ yyCh = getChar();
+ }
+ return Tok_SomeOperator;
+ case '.':
+ yyCh = getChar();
+ if (yyCh == '*') {
+ yyCh = getChar();
+ } else if (yyCh == '.') {
+ do {
+ yyCh = getChar();
+ } while (yyCh == '.');
+ return Tok_Ellipsis;
+ } else if (isdigit(yyCh)) {
+ do {
+ yyCh = getChar();
+ } while (isalnum(yyCh) || yyCh == '.' || yyCh == '+' ||
+ yyCh == '-');
+ return Tok_Number;
+ }
+ return Tok_SomeOperator;
+ case '/':
+ yyCh = getChar();
+ if (yyCh == '/') {
+ do {
+ yyCh = getChar();
+ } while (yyCh != EOF && yyCh != '\n');
+ } else if (yyCh == '*') {
+ bool metDoc = false; // empty doc is no doc
+ bool metSlashAsterBang = false;
+ bool metAster = false;
+ bool metAsterSlash = false;
+
+ yyCh = getChar();
+ if (yyCh == '!')
+ metSlashAsterBang = true;
+
+ while (!metAsterSlash) {
+ if (yyCh == EOF) {
+ yyTokLoc.warning(tr("Unterminated C++ comment"));
+ break;
+ } else {
+ if (yyCh == '*') {
+ metAster = true;
+ } else if (metAster && yyCh == '/') {
+ metAsterSlash = true;
+ } else {
+ metAster = false;
+ if (isgraph(yyCh))
+ metDoc = true;
+ }
+ }
+ yyCh = getChar();
+ }
+ if (metSlashAsterBang && metDoc)
+ return Tok_Doc;
+ else if (yyParenDepth > 0)
+ return Tok_Comment;
+ } else {
+ if (yyCh == '=')
+ yyCh = getChar();
+ return Tok_SomeOperator;
+ }
+ break;
+ case ':':
+ yyCh = getChar();
+ if (yyCh == ':') {
+ yyCh = getChar();
+ return Tok_Gulbrandsen;
+ } else {
+ return Tok_Colon;
+ }
+ case ';':
+ yyCh = getChar();
+ return Tok_Semicolon;
+ case '<':
+ yyCh = getChar();
+ if (yyCh == '<') {
+ yyCh = getChar();
+ if (yyCh == '=')
+ yyCh = getChar();
+ return Tok_SomeOperator;
+ } else if (yyCh == '=') {
+ yyCh = getChar();
+ return Tok_SomeOperator;
+ } else {
+ return Tok_LeftAngle;
+ }
+ case '=':
+ yyCh = getChar();
+ if (yyCh == '=') {
+ yyCh = getChar();
+ return Tok_SomeOperator;
+ } else {
+ return Tok_Equal;
+ }
+ case '>':
+ yyCh = getChar();
+ if (yyCh == '>') {
+ yyCh = getChar();
+ if (yyCh == '=')
+ yyCh = getChar();
+ return Tok_SomeOperator;
+ } else if (yyCh == '=') {
+ yyCh = getChar();
+ return Tok_SomeOperator;
+ } else {
+ return Tok_RightAngle;
+ }
+ case '?':
+ yyCh = getChar();
+ return Tok_SomeOperator;
+ case '[':
+ yyCh = getChar();
+ if (yyNumPreprocessorSkipping == 0)
+ yyBracketDepth++;
+ return Tok_LeftBracket;
+ case '\\':
+ yyCh = getChar();
+ yyCh = getChar(); // skip one character
+ break;
+ case ']':
+ yyCh = getChar();
+ if (yyNumPreprocessorSkipping == 0)
+ yyBracketDepth--;
+ return Tok_RightBracket;
+ case '{':
+ yyCh = getChar();
+ if (yyNumPreprocessorSkipping == 0)
+ yyBraceDepth++;
+ return Tok_LeftBrace;
+ case '}':
+ yyCh = getChar();
+ if (yyNumPreprocessorSkipping == 0)
+ yyBraceDepth--;
+ return Tok_RightBrace;
+ case '|':
+ yyCh = getChar();
+ if (yyCh == '|' || yyCh == '=')
+ yyCh = getChar();
+ return Tok_SomeOperator;
+ case '~':
+ yyCh = getChar();
+ return Tok_Tilde;
+ case '@':
+ yyCh = getChar();
+ return Tok_At;
+ default:
+ // ### We should really prevent qdoc from looking at snippet files rather than
+ // ### suppress warnings when reading them.
+ if (yyNumPreprocessorSkipping == 0 && !yyTokLoc.fileName().endsWith(".qdoc")) {
+ yyTokLoc.warning(tr("Hostile character 0x%1 in C++ source")
+ .arg((uchar)yyCh, 1, 16));
+ }
+ yyCh = getChar();
+ }
+ }
+ }
+
+ if (yyPreprocessorSkipping.count() > 1) {
+ yyTokLoc.warning(tr("Expected #endif before end of file"));
+ // clear it out or we get an infinite loop!
+ while (!yyPreprocessorSkipping.isEmpty()) {
+ popSkipping();
+ }
+ }
+
+ strcpy(yyLex, "end-of-input");
+ yyLexLen = strlen(yyLex);
+ return Tok_Eoi;
+}
+
+void Tokenizer::initialize(const Config &config)
+{
+ QString versionSym = config.getString(CONFIG_VERSIONSYM);
+
+ comment = new QRegExp("/(?:\\*.*\\*/|/.*\n|/[^\n]*$)");
+ comment->setMinimal(true);
+ versionX = new QRegExp("$cannot possibly match^");
+ if (!versionSym.isEmpty())
+ versionX->setPattern("[ \t]*(?:" + QRegExp::escape(versionSym)
+ + ")[ \t]+\"([^\"]*)\"[ \t]*");
+ definedX = new QRegExp("defined ?\\(?([A-Z_0-9a-z]+) ?\\)");
+
+ QStringList d = config.getStringList(CONFIG_DEFINES);
+ d += "qdoc";
+ defines = new QRegExp(d.join("|"));
+ falsehoods = new QRegExp(config.getStringList(CONFIG_FALSEHOODS).join("|"));
+
+ memset(kwordHashTable, 0, sizeof(kwordHashTable));
+ for (int i = 0; i < Tok_LastKeyword - Tok_FirstKeyword + 1; i++)
+ insertKwordIntoHash(kwords[i], i + 1);
+
+ ignoredTokensAndDirectives = new QHash<QByteArray, bool>;
+
+ QStringList tokens = config.getStringList(LANGUAGE_CPP + Config::dot + CONFIG_IGNORETOKENS);
+ foreach (const QString &t, tokens) {
+ const QByteArray tb = t.toAscii();
+ ignoredTokensAndDirectives->insert(tb, false);
+ insertKwordIntoHash(tb.data(), -1);
+ }
+
+ QStringList directives = config.getStringList(LANGUAGE_CPP + Config::dot
+ + CONFIG_IGNOREDIRECTIVES);
+ foreach (const QString &d, directives) {
+ const QByteArray db = d.toAscii();
+ ignoredTokensAndDirectives->insert(db, true);
+ insertKwordIntoHash(db.data(), -1);
+ }
+}
+
+void Tokenizer::terminate()
+{
+ delete comment;
+ comment = 0;
+ delete versionX;
+ versionX = 0;
+ delete definedX;
+ definedX = 0;
+ delete defines;
+ defines = 0;
+ delete falsehoods;
+ falsehoods = 0;
+ delete ignoredTokensAndDirectives;
+ ignoredTokensAndDirectives = 0;
+}
+
+void Tokenizer::init()
+{
+ yyLexBuf1 = new char[(int) yyLexBufSize];
+ yyLexBuf2 = new char[(int) yyLexBufSize];
+ yyPrevLex = yyLexBuf1;
+ yyPrevLex[0] = '\0';
+ yyLex = yyLexBuf2;
+ yyLex[0] = '\0';
+ yyLexLen = 0;
+ yyPreprocessorSkipping.push(false);
+ yyNumPreprocessorSkipping = 0;
+ yyBraceDepth = 0;
+ yyParenDepth = 0;
+ yyBracketDepth = 0;
+ yyCh = '\0';
+ parsingMacro = false;
+}
+
+void Tokenizer::start(const Location& loc)
+{
+ yyTokLoc = loc;
+ yyCurLoc = loc;
+ yyCurLoc.start();
+ strcpy(yyPrevLex, "beginning-of-input");
+ strcpy(yyLex, "beginning-of-input");
+ yyLexLen = strlen(yyLex);
+ yyBraceDepth = 0;
+ yyParenDepth = 0;
+ yyBracketDepth = 0;
+ yyCh = '\0';
+ yyCh = getChar();
+}
+
+/*
+ Returns the next token, if # was met. This function interprets the
+ preprocessor directive, skips over any #ifdef'd out tokens, and returns the
+ token after all of that.
+*/
+int Tokenizer::getTokenAfterPreprocessor()
+{
+ yyCh = getChar();
+ while (isspace(yyCh) && yyCh != '\n')
+ yyCh = getChar();
+
+ /*
+ #directive condition
+ */
+ QString directive;
+ QString condition;
+
+ while (isalpha(yyCh)) {
+ directive += QChar(yyCh);
+ yyCh = getChar();
+ }
+ if (!directive.isEmpty()) {
+ while (yyCh != EOF && yyCh != '\n') {
+ if (yyCh == '\\')
+ yyCh = getChar();
+ condition += yyCh;
+ yyCh = getChar();
+ }
+ condition.replace(*comment, "");
+ condition = condition.simplified();
+
+ /*
+ The #if, #ifdef, #ifndef, #elif, #else, and #endif
+ directives have an effect on the skipping stack. For
+ instance, if the code processed so far is
+
+ #if 1
+ #if 0
+ #if 1
+ // ...
+ #else
+
+ the skipping stack contains, from bottom to top, false true
+ true (assuming 0 is false and 1 is true). If at least one
+ entry of the stack is true, the tokens are skipped.
+
+ This mechanism is simple yet hard to understand.
+ */
+ if (directive[0] == QChar('i')) {
+ if (directive == QString("if"))
+ pushSkipping(!isTrue(condition));
+ else if (directive == QString("ifdef"))
+ pushSkipping(!defines->exactMatch(condition));
+ else if (directive == QString("ifndef"))
+ pushSkipping(defines->exactMatch(condition));
+ } else if (directive[0] == QChar('e')) {
+ if (directive == QString("elif")) {
+ bool old = popSkipping();
+ if (old)
+ pushSkipping(!isTrue(condition));
+ else
+ pushSkipping(true);
+ } else if (directive == QString("else")) {
+ pushSkipping(!popSkipping());
+ } else if (directive == QString("endif")) {
+ popSkipping();
+ }
+ } else if (directive == QString("define")) {
+ if (versionX->exactMatch(condition))
+ yyVersion = versionX->cap(1);
+ }
+ }
+
+ int tok;
+ do {
+ /*
+ We set yyLex now, and after getToken() this will be
+ yyPrevLex. This way, we skip over the preprocessor
+ directive.
+ */
+ qstrcpy(yyLex, yyPrevLex);
+
+ /*
+ If getToken() meets another #, it will call
+ getTokenAfterPreprocessor() once again, which could in turn
+ call getToken() again, etc. Unless there are 10,000 or so
+ preprocessor directives in a row, this shouldn't overflow
+ the stack.
+ */
+ tok = getToken();
+ } while (yyNumPreprocessorSkipping > 0);
+ return tok;
+}
+
+/*
+ Pushes a new skipping value onto the stack. This corresponds to entering a
+ new #if block.
+*/
+void Tokenizer::pushSkipping(bool skip)
+{
+ yyPreprocessorSkipping.push(skip);
+ if (skip)
+ yyNumPreprocessorSkipping++;
+}
+
+/*
+ Pops a skipping value from the stack. This corresponds to reaching a #endif.
+*/
+bool Tokenizer::popSkipping()
+{
+ if (yyPreprocessorSkipping.isEmpty()) {
+ yyTokLoc.warning(tr("Unexpected #elif, #else or #endif"));
+ return true;
+ }
+
+ bool skip = yyPreprocessorSkipping.pop();
+ if (skip)
+ yyNumPreprocessorSkipping--;
+ return skip;
+}
+
+/*
+ Returns true if the condition evaluates as true, otherwise false. The
+ condition is represented by a string. Unsophisticated parsing techniques are
+ used. The preprocessing method could be named StriNg-Oriented PreProcessing,
+ as SNOBOL stands for StriNg-Oriented symBOlic Language.
+*/
+bool Tokenizer::isTrue(const QString &condition)
+{
+ int firstOr = -1;
+ int firstAnd = -1;
+ int parenDepth = 0;
+
+ /*
+ Find the first logical operator at top level, but be careful
+ about precedence. Examples:
+
+ X || Y // the or
+ X || Y || Z // the leftmost or
+ X || Y && Z // the or
+ X && Y || Z // the or
+ (X || Y) && Z // the and
+ */
+ for (int i = 0; i < (int) condition.length() - 1; i++) {
+ QChar ch = condition[i];
+ if (ch == QChar('(')) {
+ parenDepth++;
+ } else if (ch == QChar(')')) {
+ parenDepth--;
+ } else if (parenDepth == 0) {
+ if (condition[i + 1] == ch) {
+ if (ch == QChar('|')) {
+ firstOr = i;
+ break;
+ } else if (ch == QChar('&')) {
+ if (firstAnd == -1)
+ firstAnd = i;
+ }
+ }
+ }
+ }
+ if (firstOr != -1)
+ return isTrue(condition.left(firstOr)) ||
+ isTrue(condition.mid(firstOr + 2));
+ if (firstAnd != -1)
+ return isTrue(condition.left(firstAnd)) &&
+ isTrue(condition.mid(firstAnd + 2));
+
+ QString t = condition.simplified();
+ if (t.isEmpty())
+ return true;
+
+ if (t[0] == QChar('!'))
+ return !isTrue(t.mid(1));
+ if (t[0] == QChar('(') && t.right(1)[0] == QChar(')'))
+ return isTrue(t.mid(1, t.length() - 2));
+
+ if (definedX->exactMatch(t))
+ return defines->exactMatch(definedX->cap(1));
+ else
+ return !falsehoods->exactMatch(t);
+}
+
+QT_END_NAMESPACE
diff --git a/tools/qdoc3/tokenizer.h b/tools/qdoc3/tokenizer.h
new file mode 100644
index 0000000..745c427
--- /dev/null
+++ b/tools/qdoc3/tokenizer.h
@@ -0,0 +1,183 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*
+ tokenizer.h
+*/
+
+#ifndef TOKENIZER_H
+#define TOKENIZER_H
+
+#include <qstack.h>
+#include <qstring.h>
+
+#include <stdio.h>
+
+#include "location.h"
+
+QT_BEGIN_NAMESPACE
+
+/*
+ Here come the C++ tokens we support. The first part contains
+ all-purpose tokens; then come keywords.
+
+ If you add a keyword, make sure to modify the keyword array in
+ tokenizer.cpp as well, and possibly adjust Tok_FirstKeyword and
+ Tok_LastKeyword.
+*/
+enum { Tok_Eoi, Tok_Ampersand, Tok_Aster, Tok_Caret, Tok_LeftParen,
+ Tok_RightParen, Tok_LeftParenAster, Tok_Equal, Tok_LeftBrace,
+ Tok_RightBrace, Tok_Semicolon, Tok_Colon, Tok_LeftAngle,
+ Tok_RightAngle, Tok_Comma, Tok_Ellipsis, Tok_Gulbrandsen,
+ Tok_LeftBracket, Tok_RightBracket, Tok_Tilde, Tok_SomeOperator,
+ Tok_Number, Tok_String, Tok_Doc, Tok_Comment, Tok_Ident, Tok_At,
+ Tok_char, Tok_class, Tok_const, Tok_double, Tok_enum,
+ Tok_explicit, Tok_friend, Tok_inline, Tok_int, Tok_long,
+ Tok_namespace, Tok_operator, Tok_private, Tok_protected,
+ Tok_public, Tok_short, Tok_signals, Tok_signed, Tok_slots,
+ Tok_static, Tok_struct, Tok_template, Tok_typedef,
+ Tok_typename, Tok_union, Tok_unsigned, Tok_using, Tok_virtual,
+ Tok_void, Tok_volatile, Tok_int64, Tok_Q_OBJECT, Tok_Q_OVERRIDE,
+ Tok_Q_PROPERTY, Tok_Q_DECLARE_SEQUENTIAL_ITERATOR,
+ Tok_Q_DECLARE_MUTABLE_SEQUENTIAL_ITERATOR,
+ Tok_Q_DECLARE_ASSOCIATIVE_ITERATOR,
+ Tok_Q_DECLARE_MUTABLE_ASSOCIATIVE_ITERATOR,
+ Tok_Q_DECLARE_FLAGS, Tok_Q_SIGNALS, Tok_Q_SLOTS, Tok_QT_COMPAT,
+ Tok_QT_COMPAT_CONSTRUCTOR, Tok_QT_DEPRECATED, Tok_QT_MOC_COMPAT,
+ Tok_QT_MODULE, Tok_QT3_SUPPORT, Tok_QT3_SUPPORT_CONSTRUCTOR,
+ Tok_QT3_MOC_SUPPORT, Tok_QDOC_PROPERTY,
+ Tok_FirstKeyword = Tok_char, Tok_LastKeyword = Tok_QDOC_PROPERTY };
+
+/*
+ The Tokenizer class implements lexical analysis of C++ source
+ files.
+
+ Not every operator or keyword of C++ is recognized; only those
+ that are interesting to us. Some Qt keywords or macros are also
+ recognized.
+*/
+
+class Tokenizer
+{
+ public:
+ Tokenizer(const Location& loc, const QByteArray &in);
+ Tokenizer(const Location& loc, FILE *in);
+
+ ~Tokenizer();
+
+ int getToken();
+ void setParsingFnOrMacro(bool macro) { parsingMacro = macro; }
+ bool parsingFnOrMacro() const { return parsingMacro; }
+
+ const Location &location() const { return yyTokLoc; }
+ QString previousLexeme() const { return QString(yyPrevLex); }
+ QString lexeme() const { return QString(yyLex); }
+ QString version() const { return yyVersion; }
+ int braceDepth() const { return yyBraceDepth; }
+ int parenDepth() const { return yyParenDepth; }
+ int bracketDepth() const { return yyBracketDepth; }
+
+ static void initialize(const Config &config);
+ static void terminate();
+ static bool isTrue(const QString &condition);
+
+ private:
+ void init();
+ void start(const Location& loc);
+ /*
+ This limit on the length of a lexeme seems fairly high, but a
+ doc comment can be arbitrarily long. The previous 65,536 limit
+ was reached by Mark Summerfield.
+ */
+ enum { yyLexBufSize = 524288 };
+
+ int getch()
+ {
+ return yyPos == yyIn.size() ? EOF : yyIn[yyPos++];
+ }
+
+ inline int getChar()
+ {
+ if (yyCh == EOF)
+ return EOF;
+ if (yyLexLen < yyLexBufSize - 1) {
+ yyLex[yyLexLen++] = (char) yyCh;
+ yyLex[yyLexLen] = '\0';
+ }
+ yyCurLoc.advance(yyCh);
+ int ch = getch();
+ if (ch == EOF)
+ return EOF;
+ // cast explicitely to make sure the value of ch
+ // is in range [0..255] to avoid assert messages
+ // when using debug CRT that checks its input.
+ return int(uint(uchar(ch)));
+ }
+
+ int getTokenAfterPreprocessor();
+ void pushSkipping(bool skip);
+ bool popSkipping();
+
+ Location yyTokLoc;
+ Location yyCurLoc;
+ char *yyLexBuf1;
+ char *yyLexBuf2;
+ char *yyPrevLex;
+ char *yyLex;
+ size_t yyLexLen;
+ QStack<bool> yyPreprocessorSkipping;
+ int yyNumPreprocessorSkipping;
+ int yyBraceDepth;
+ int yyParenDepth;
+ int yyBracketDepth;
+ int yyCh;
+
+ QString yyVersion;
+ bool parsingMacro;
+
+ protected:
+ QByteArray yyIn;
+ int yyPos;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/tools/qdoc3/tr.h b/tools/qdoc3/tr.h
new file mode 100644
index 0000000..0966e45
--- /dev/null
+++ b/tools/qdoc3/tr.h
@@ -0,0 +1,60 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*
+ tr.h
+*/
+
+#ifndef TR_H
+#define TR_H
+
+#include <qstring.h>
+
+QT_BEGIN_NAMESPACE
+
+inline QString tr( const char *sourceText, const char * /* comment */ = 0 )
+{
+ return QString( QLatin1String(sourceText) );
+}
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/tools/qdoc3/tree.cpp b/tools/qdoc3/tree.cpp
new file mode 100644
index 0000000..0fbd438
--- /dev/null
+++ b/tools/qdoc3/tree.cpp
@@ -0,0 +1,2012 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*
+ tree.cpp
+*/
+
+#include <QtCore>
+#include <QDomDocument>
+
+#include "atom.h"
+#include "doc.h"
+#include "htmlgenerator.h"
+#include "location.h"
+#include "node.h"
+#include "text.h"
+#include "tree.h"
+
+QT_BEGIN_NAMESPACE
+
+struct InheritanceBound
+{
+ Node::Access access;
+ QStringList basePath;
+ QString dataTypeWithTemplateArgs;
+ InnerNode *parent;
+
+ InheritanceBound()
+ : access(Node::Public) { }
+ InheritanceBound(Node::Access access0,
+ const QStringList& basePath0,
+ const QString &dataTypeWithTemplateArgs0,
+ InnerNode *parent)
+ : access(access0), basePath(basePath0),
+ dataTypeWithTemplateArgs(dataTypeWithTemplateArgs0),
+ parent(parent) { }
+};
+
+struct Target
+{
+ Node *node;
+ Atom *atom;
+ int priority;
+};
+
+typedef QMap<PropertyNode::FunctionRole, QString> RoleMap;
+typedef QMap<PropertyNode *, RoleMap> PropertyMap;
+typedef QMultiMap<QString, Node *> GroupMap;
+typedef QMultiHash<QString, FakeNode *> FakeNodeHash;
+typedef QMultiHash<QString, Target> TargetHash;
+
+class TreePrivate
+{
+public:
+ QMap<ClassNode *, QList<InheritanceBound> > unresolvedInheritanceMap;
+ PropertyMap unresolvedPropertyMap;
+ GroupMap groupMap;
+ QMultiMap<QString, QString> publicGroupMap;
+ FakeNodeHash fakeNodesByTitle;
+ TargetHash targetHash;
+ QList<QPair<ClassNode*,QString> > basesList;
+ QList<QPair<FunctionNode*,QString> > relatedList;
+};
+
+/*!
+ \class Tree
+ */
+
+/*!
+ The default constructor is the only constructor.
+ */
+Tree::Tree()
+ : roo(0, "")
+{
+ priv = new TreePrivate;
+}
+
+/*!
+ The destructor deletes the internal, private tree.
+ */
+Tree::~Tree()
+{
+ delete priv;
+}
+
+/*!
+ */
+Node *Tree::findNode(const QStringList &path, Node *relative, int findFlags)
+{
+ return const_cast<Node*>(const_cast<const Tree*>(this)->findNode(path,
+ relative,
+ findFlags));
+}
+
+/*!
+ */
+const Node *Tree::findNode(const QStringList &path,
+ const Node *relative,
+ int findFlags) const
+{
+ if (!relative)
+ relative = root();
+
+ do {
+ const Node *node = relative;
+ int i;
+
+ for (i = 0; i < path.size(); ++i) {
+ if (node == 0 || !node->isInnerNode())
+ break;
+
+ const Node *next =
+ static_cast<const InnerNode*>(node)->findNode(path.at(i));
+ if (!next && (findFlags & SearchEnumValues) && i == path.size()-1)
+ next = static_cast<const InnerNode*>(node)->findEnumNodeForValue(path.at(i));
+
+ if (!next && node->type() == Node::Class && (findFlags & SearchBaseClasses)) {
+ NodeList baseClasses = allBaseClasses(static_cast<const ClassNode *>(node));
+ foreach (const Node *baseClass, baseClasses) {
+ next = static_cast<const InnerNode *>(baseClass)->findNode(path.at(i));
+ if (!next && (findFlags & SearchEnumValues) && i == path.size() - 1)
+ next = static_cast<const InnerNode *>(baseClass)
+ ->findEnumNodeForValue(path.at(i));
+ if (next)
+ break;
+ }
+ }
+ node = next;
+ }
+ if (node && i == path.size()
+ && (!(findFlags & NonFunction) || node->type() != Node::Function
+ || ((FunctionNode *)node)->metaness() == FunctionNode::MacroWithoutParams))
+ return node;
+ relative = relative->parent();
+ } while (relative);
+
+ return 0;
+}
+
+/*!
+ */
+Node *Tree::findNode(const QStringList &path,
+ Node::Type type,
+ Node *relative,
+ int findFlags)
+{
+ return const_cast<Node*>(const_cast<const Tree*>(this)->findNode(path,
+ type,
+ relative,
+ findFlags));
+}
+
+/*!
+ */
+const Node *Tree::findNode(const QStringList &path,
+ Node::Type type,
+ const Node *relative,
+ int findFlags) const
+{
+ const Node *node = findNode(path, relative, findFlags);
+ if (node != 0 && node->type() == type)
+ return node;
+ return 0;
+}
+
+/*!
+ */
+FunctionNode *Tree::findFunctionNode(const QStringList& path,
+ Node *relative,
+ int findFlags)
+{
+ return const_cast<FunctionNode *>(
+ const_cast<const Tree *>(this)->findFunctionNode(path, relative, findFlags));
+}
+
+/*!
+ */
+const FunctionNode *Tree::findFunctionNode(const QStringList &path,
+ const Node *relative,
+ int findFlags) const
+{
+ if (!relative)
+ relative = root();
+ do {
+ const Node *node = relative;
+ int i;
+
+ for (i = 0; i < path.size(); ++i) {
+ if (node == 0 || !node->isInnerNode())
+ break;
+
+ const Node *next;
+ if (i == path.size() - 1)
+ next = ((InnerNode *) node)->findFunctionNode(path.at(i));
+ else
+ next = ((InnerNode *) node)->findNode(path.at(i));
+
+ if (!next && node->type() == Node::Class && (findFlags & SearchBaseClasses)) {
+ NodeList baseClasses = allBaseClasses(static_cast<const ClassNode *>(node));
+ foreach (const Node *baseClass, baseClasses) {
+ if (i == path.size() - 1)
+ next = static_cast<const InnerNode *>(baseClass)->
+ findFunctionNode(path.at(i));
+ else
+ next = static_cast<const InnerNode *>(baseClass)->findNode(path.at(i));
+
+ if (next)
+ break;
+ }
+ }
+
+ node = next;
+ }
+ if (node && i == path.size() && node->type() == Node::Function) {
+ // CppCodeParser::processOtherMetaCommand ensures that reimplemented
+ // functions are private.
+ const FunctionNode *func = static_cast<const FunctionNode*>(node);
+
+ while (func->access() == Node::Private) {
+ const FunctionNode *from = func->reimplementedFrom();
+ if (from != 0) {
+ if (from->access() != Node::Private)
+ return from;
+ else
+ func = from;
+ } else
+ break;
+ }
+ return func;
+ }
+ relative = relative->parent();
+ } while (relative);
+
+ return 0;
+}
+
+/*!
+ */
+FunctionNode *Tree::findFunctionNode(const QStringList &parentPath,
+ const FunctionNode *clone,
+ Node *relative,
+ int findFlags)
+{
+ return const_cast<FunctionNode *>(
+ const_cast<const Tree *>(this)->findFunctionNode(parentPath,
+ clone,
+ relative,
+ findFlags));
+}
+
+/*!
+ */
+const FunctionNode *Tree::findFunctionNode(const QStringList &parentPath,
+ const FunctionNode *clone,
+ const Node *relative,
+ int findFlags) const
+{
+ const Node *parent = findNode(parentPath, relative, findFlags);
+ if (parent == 0 || !parent->isInnerNode()) {
+ return 0;
+ } else {
+ return ((InnerNode *)parent)->findFunctionNode(clone);
+ }
+}
+
+static const int NumSuffixes = 3;
+static const char * const suffixes[NumSuffixes] = { "", "s", "es" };
+
+/*!
+ */
+const FakeNode *Tree::findFakeNodeByTitle(const QString &title) const
+{
+ for (int pass = 0; pass < NumSuffixes; ++pass) {
+ FakeNodeHash::const_iterator i =
+ priv->fakeNodesByTitle.find(Doc::canonicalTitle(title + suffixes[pass]));
+ if (i != priv->fakeNodesByTitle.constEnd()) {
+ FakeNodeHash::const_iterator j = i;
+ ++j;
+ if (j != priv->fakeNodesByTitle.constEnd() && j.key() == i.key()) {
+ QList<Location> internalLocations;
+ while (j != priv->fakeNodesByTitle.constEnd()) {
+ if (j.key() == i.key() && j.value()->url().isEmpty())
+ internalLocations.append(j.value()->doc().location());
+ ++j;
+ }
+ if (internalLocations.size() > 0) {
+ i.value()->doc().location().warning(
+ tr("Page '%1' defined in more than one location:").arg(title));
+ foreach (const Location &location, internalLocations)
+ location.warning(tr("(defined here)"));
+ }
+ }
+ return i.value();
+ }
+ }
+ return 0;
+}
+
+/*!
+ */
+const Node*
+Tree::findUnambiguousTarget(const QString &target, Atom *&atom) const
+{
+ Target bestTarget = {0, 0, INT_MAX};
+ int numBestTargets = 0;
+
+ for (int pass = 0; pass < NumSuffixes; ++pass) {
+ TargetHash::const_iterator i =
+ priv->targetHash.find(Doc::canonicalTitle(target + suffixes[pass]));
+ if (i != priv->targetHash.constEnd()) {
+ TargetHash::const_iterator j = i;
+ do {
+ const Target &candidate = j.value();
+ if (candidate.priority < bestTarget.priority) {
+ bestTarget = candidate;
+ numBestTargets = 1;
+ } else if (candidate.priority == bestTarget.priority) {
+ ++numBestTargets;
+ }
+ ++j;
+ } while (j != priv->targetHash.constEnd() && j.key() == i.key());
+
+ if (numBestTargets == 1) {
+ atom = bestTarget.atom;
+ return bestTarget.node;
+ }
+ }
+ }
+ return 0;
+}
+
+/*!
+ */
+Atom *Tree::findTarget(const QString &target, const Node *node) const
+{
+ for (int pass = 0; pass < NumSuffixes; ++pass) {
+ QString key = Doc::canonicalTitle(target + suffixes[pass]);
+ TargetHash::const_iterator i = priv->targetHash.find(key);
+
+ if (i != priv->targetHash.constEnd()) {
+ do {
+ if (i.value().node == node)
+ return i.value().atom;
+ ++i;
+ } while (i != priv->targetHash.constEnd() && i.key() == key);
+ }
+ }
+ return 0;
+}
+
+/*!
+ */
+void Tree::addBaseClass(ClassNode *subclass, Node::Access access,
+ const QStringList &basePath,
+ const QString &dataTypeWithTemplateArgs,
+ InnerNode *parent)
+{
+ priv->unresolvedInheritanceMap[subclass].append(
+ InheritanceBound(access,
+ basePath,
+ dataTypeWithTemplateArgs,
+ parent)
+ );
+}
+
+
+/*!
+ */
+void Tree::addPropertyFunction(PropertyNode *property,
+ const QString &funcName,
+ PropertyNode::FunctionRole funcRole)
+{
+ priv->unresolvedPropertyMap[property].insert(funcRole, funcName);
+}
+
+/*!
+ */
+void Tree::addToGroup(Node *node, const QString &group)
+{
+ priv->groupMap.insert(group, node);
+}
+
+/*!
+ */
+QMultiMap<QString, Node *> Tree::groups() const
+{
+ return priv->groupMap;
+}
+
+/*!
+ */
+void Tree::addToPublicGroup(Node *node, const QString &group)
+{
+ priv->publicGroupMap.insert(node->name(), group);
+ addToGroup(node, group);
+}
+
+/*!
+ */
+QMultiMap<QString, QString> Tree::publicGroups() const
+{
+ return priv->publicGroupMap;
+}
+
+/*!
+ */
+void Tree::resolveInheritance(NamespaceNode *rootNode)
+{
+ if (!rootNode)
+ rootNode = root();
+
+ for (int pass = 0; pass < 2; pass++) {
+ NodeList::ConstIterator c = rootNode->childNodes().begin();
+ while (c != rootNode->childNodes().end()) {
+ if ((*c)->type() == Node::Class)
+ resolveInheritance(pass, (ClassNode *) *c);
+ else if ((*c)->type() == Node::Namespace) {
+ NamespaceNode *ns = static_cast<NamespaceNode*>(*c);
+ resolveInheritance(ns);
+ }
+ ++c;
+ }
+ if (rootNode == root())
+ priv->unresolvedInheritanceMap.clear();
+ }
+}
+
+/*!
+ */
+void Tree::resolveProperties()
+{
+ PropertyMap::ConstIterator propEntry;
+
+ propEntry = priv->unresolvedPropertyMap.begin();
+ while (propEntry != priv->unresolvedPropertyMap.end()) {
+ PropertyNode *property = propEntry.key();
+ InnerNode *parent = property->parent();
+ QString getterName = (*propEntry)[PropertyNode::Getter];
+ QString setterName = (*propEntry)[PropertyNode::Setter];
+ QString resetterName = (*propEntry)[PropertyNode::Resetter];
+
+ NodeList::ConstIterator c = parent->childNodes().begin();
+ while (c != parent->childNodes().end()) {
+ if ((*c)->type() == Node::Function) {
+ FunctionNode *function = static_cast<FunctionNode *>(*c);
+ if (function->access() == property->access() &&
+ (function->status() == property->status() ||
+ function->doc().isEmpty())) {
+ if (function->name() == getterName) {
+ property->addFunction(function, PropertyNode::Getter);
+ } else if (function->name() == setterName) {
+ property->addFunction(function, PropertyNode::Setter);
+ } else if (function->name() == resetterName) {
+ property->addFunction(function, PropertyNode::Resetter);
+ }
+ }
+ }
+ ++c;
+ }
+ ++propEntry;
+ }
+
+ propEntry = priv->unresolvedPropertyMap.begin();
+ while (propEntry != priv->unresolvedPropertyMap.end()) {
+ PropertyNode *property = propEntry.key();
+ // redo it to set the property functions
+ if (property->overriddenFrom())
+ property->setOverriddenFrom(property->overriddenFrom());
+ ++propEntry;
+ }
+
+ priv->unresolvedPropertyMap.clear();
+}
+
+/*!
+ */
+void Tree::resolveInheritance(int pass, ClassNode *classe)
+{
+ if (pass == 0) {
+ QList<InheritanceBound> bounds = priv->unresolvedInheritanceMap[classe];
+ QList<InheritanceBound>::ConstIterator b = bounds.begin();
+ while (b != bounds.end()) {
+ ClassNode *baseClass = (ClassNode*)findNode((*b).basePath,
+ Node::Class);
+ if (!baseClass && (*b).parent)
+ baseClass = (ClassNode*)findNode((*b).basePath,
+ Node::Class,
+ (*b).parent);
+ if (baseClass)
+ classe->addBaseClass((*b).access,
+ baseClass,
+ (*b).dataTypeWithTemplateArgs);
+ ++b;
+ }
+ }
+ else {
+ NodeList::ConstIterator c = classe->childNodes().begin();
+ while (c != classe->childNodes().end()) {
+ if ((*c)->type() == Node::Function) {
+ FunctionNode *func = (FunctionNode *) *c;
+ FunctionNode *from = findVirtualFunctionInBaseClasses(classe, func);
+ if (from != 0) {
+ if (func->virtualness() == FunctionNode::NonVirtual)
+ func->setVirtualness(FunctionNode::ImpureVirtual);
+ func->setReimplementedFrom(from);
+ }
+ }
+ else if ((*c)->type() == Node::Property) {
+ fixPropertyUsingBaseClasses(classe, static_cast<PropertyNode *>(*c));
+ }
+ ++c;
+ }
+ }
+}
+
+/*!
+ */
+void Tree::resolveGroups()
+{
+ GroupMap::const_iterator i;
+ QString prevGroup;
+ for (i = priv->groupMap.constBegin(); i != priv->groupMap.constEnd(); ++i) {
+ if (i.value()->access() == Node::Private)
+ continue;
+
+ FakeNode *fake =
+ static_cast<FakeNode*>(findNode(QStringList(i.key()),Node::Fake));
+ if (fake && fake->subType() == FakeNode::Group) {
+ fake->addGroupMember(i.value());
+ }
+ else {
+ if (prevGroup != i.key())
+ i.value()->doc().location().warning(tr("No such group '%1'").arg(i.key()));
+ }
+
+ prevGroup = i.key();
+ }
+
+ //priv->groupMap.clear();
+}
+
+/*!
+ */
+void Tree::resolveTargets()
+{
+ // need recursion
+
+ foreach (Node *child, roo.childNodes()) {
+ if (child->type() == Node::Fake) {
+ FakeNode *node = static_cast<FakeNode *>(child);
+ priv->fakeNodesByTitle.insert(Doc::canonicalTitle(node->title()), node);
+ }
+
+ if (child->doc().hasTableOfContents()) {
+ const QList<Atom *> &toc = child->doc().tableOfContents();
+ Target target;
+ target.node = child;
+ target.priority = 3;
+
+ for (int i = 0; i < toc.size(); ++i) {
+ target.atom = toc.at(i);
+ QString title = Text::sectionHeading(target.atom).toString();
+ if (!title.isEmpty())
+ priv->targetHash.insert(Doc::canonicalTitle(title), target);
+ }
+ }
+ if (child->doc().hasKeywords()) {
+ const QList<Atom *> &keywords = child->doc().keywords();
+ Target target;
+ target.node = child;
+ target.priority = 1;
+
+ for (int i = 0; i < keywords.size(); ++i) {
+ target.atom = keywords.at(i);
+ priv->targetHash.insert(Doc::canonicalTitle(target.atom->string()), target);
+ }
+ }
+ if (child->doc().hasTargets()) {
+ const QList<Atom *> &toc = child->doc().targets();
+ Target target;
+ target.node = child;
+ target.priority = 2;
+
+ for (int i = 0; i < toc.size(); ++i) {
+ target.atom = toc.at(i);
+ priv->targetHash.insert(Doc::canonicalTitle(target.atom->string()), target);
+ }
+ }
+ }
+}
+
+/*!
+ */
+void Tree::fixInheritance(NamespaceNode *rootNode)
+{
+ if (!rootNode)
+ rootNode = root();
+
+ NodeList::ConstIterator c = rootNode->childNodes().begin();
+ while (c != rootNode->childNodes().end()) {
+ if ((*c)->type() == Node::Class)
+ static_cast<ClassNode *>(*c)->fixBaseClasses();
+ else if ((*c)->type() == Node::Namespace) {
+ NamespaceNode *ns = static_cast<NamespaceNode*>(*c);
+ fixInheritance(ns);
+ }
+ ++c;
+ }
+}
+
+/*!
+ */
+FunctionNode *Tree::findVirtualFunctionInBaseClasses(ClassNode *classe,
+ FunctionNode *clone)
+{
+ QList<RelatedClass>::ConstIterator r = classe->baseClasses().begin();
+ while (r != classe->baseClasses().end()) {
+ FunctionNode *func;
+ if (((func = findVirtualFunctionInBaseClasses((*r).node, clone)) != 0 ||
+ (func = (*r).node->findFunctionNode(clone)) != 0)) {
+ if (func->virtualness() != FunctionNode::NonVirtual)
+ return func;
+ }
+ ++r;
+ }
+ return 0;
+}
+
+/*!
+ */
+void Tree::fixPropertyUsingBaseClasses(ClassNode *classe,
+ PropertyNode *property)
+{
+ QList<RelatedClass>::const_iterator r = classe->baseClasses().begin();
+ while (r != classe->baseClasses().end()) {
+ PropertyNode *baseProperty =
+ static_cast<PropertyNode *>(r->node->findNode(property->name(),
+ Node::Property));
+ if (baseProperty) {
+ fixPropertyUsingBaseClasses(r->node, baseProperty);
+ property->setOverriddenFrom(baseProperty);
+ }
+ else {
+ fixPropertyUsingBaseClasses(r->node, property);
+ }
+ ++r;
+ }
+}
+
+/*!
+ */
+NodeList Tree::allBaseClasses(const ClassNode *classe) const
+{
+ NodeList result;
+ foreach (const RelatedClass &r, classe->baseClasses()) {
+ result += r.node;
+ result += allBaseClasses(r.node);
+ }
+ return result;
+}
+
+/*!
+ */
+void Tree::readIndexes(const QStringList &indexFiles)
+{
+ foreach (const QString &indexFile, indexFiles)
+ readIndexFile(indexFile);
+}
+
+/*!
+ Read the QDomDocument at \a path and get the index from it.
+ */
+void Tree::readIndexFile(const QString &path)
+{
+ QFile file(path);
+ if (file.open(QFile::ReadOnly)) {
+ QDomDocument document;
+ document.setContent(&file);
+ file.close();
+
+ QDomElement indexElement = document.documentElement();
+ QString indexUrl = indexElement.attribute("url", "");
+ priv->basesList.clear();
+ priv->relatedList.clear();
+
+ // Scan all elements in the XML file, constructing a map that contains
+ // base classes for each class found.
+
+ QDomElement child = indexElement.firstChildElement();
+ while (!child.isNull()) {
+ readIndexSection(child, root(), indexUrl);
+ child = child.nextSiblingElement();
+ }
+
+ // Now that all the base classes have been found for this index,
+ // arrange them into an inheritance hierarchy.
+
+ resolveIndex();
+ }
+}
+
+/*!
+ */
+void Tree::readIndexSection(const QDomElement &element,
+ InnerNode *parent,
+ const QString &indexUrl)
+{
+ QString name = element.attribute("name");
+ QString href = element.attribute("href");
+
+ Node *section;
+ Location location;
+
+ if (element.nodeName() == "namespace") {
+ section = new NamespaceNode(parent, name);
+
+ if (!indexUrl.isEmpty())
+ location = Location(indexUrl + "/" + name.toLower() + ".html");
+ else if (!indexUrl.isNull())
+ location = Location(name.toLower() + ".html");
+
+ }
+ else if (element.nodeName() == "class") {
+ section = new ClassNode(parent, name);
+ priv->basesList.append(QPair<ClassNode*,QString>(
+ static_cast<ClassNode*>(section), element.attribute("bases")));
+
+ if (!indexUrl.isEmpty())
+ location = Location(indexUrl + "/" + name.toLower() + ".html");
+ else if (!indexUrl.isNull())
+ location = Location(name.toLower() + ".html");
+
+ }
+ else if (element.nodeName() == "page") {
+ FakeNode::SubType subtype;
+ if (element.attribute("subtype") == "example")
+ subtype = FakeNode::Example;
+ else if (element.attribute("subtype") == "header")
+ subtype = FakeNode::HeaderFile;
+ else if (element.attribute("subtype") == "file")
+ subtype = FakeNode::File;
+ else if (element.attribute("subtype") == "group")
+ subtype = FakeNode::Group;
+ else if (element.attribute("subtype") == "module")
+ subtype = FakeNode::Module;
+ else if (element.attribute("subtype") == "page")
+ subtype = FakeNode::Page;
+ else if (element.attribute("subtype") == "externalpage")
+ subtype = FakeNode::ExternalPage;
+ else
+ return;
+
+ FakeNode *fakeNode = new FakeNode(parent, name, subtype);
+ fakeNode->setTitle(element.attribute("title"));
+
+ if (element.hasAttribute("location"))
+ name = element.attribute("location", "");
+
+ if (!indexUrl.isEmpty())
+ location = Location(indexUrl + "/" + name);
+ else if (!indexUrl.isNull())
+ location = Location(name);
+
+ section = fakeNode;
+
+ }
+ else if (element.nodeName() == "enum") {
+ EnumNode *enumNode = new EnumNode(parent, name);
+
+ if (!indexUrl.isEmpty())
+ location =
+ Location(indexUrl + "/" + parent->name().toLower() + ".html");
+ else if (!indexUrl.isNull())
+ location = Location(parent->name().toLower() + ".html");
+
+ QDomElement child = element.firstChildElement("value");
+ while (!child.isNull()) {
+ EnumItem item(child.attribute("name"), child.attribute("value"));
+ enumNode->addItem(item);
+ child = child.nextSiblingElement("value");
+ }
+
+ section = enumNode;
+
+ } else if (element.nodeName() == "typedef") {
+ section = new TypedefNode(parent, name);
+
+ if (!indexUrl.isEmpty())
+ location =
+ Location(indexUrl + "/" + parent->name().toLower() + ".html");
+ else if (!indexUrl.isNull())
+ location = Location(parent->name().toLower() + ".html");
+
+ }
+ else if (element.nodeName() == "property") {
+ section = new PropertyNode(parent, name);
+
+ if (!indexUrl.isEmpty())
+ location =
+ Location(indexUrl + "/" + parent->name().toLower() + ".html");
+ else if (!indexUrl.isNull())
+ location = Location(parent->name().toLower() + ".html");
+
+ } else if (element.nodeName() == "function") {
+ FunctionNode::Virtualness virt;
+ if (element.attribute("virtual") == "non")
+ virt = FunctionNode::NonVirtual;
+ else if (element.attribute("virtual") == "impure")
+ virt = FunctionNode::ImpureVirtual;
+ else if (element.attribute("virtual") == "pure")
+ virt = FunctionNode::PureVirtual;
+ else
+ return;
+
+ FunctionNode::Metaness meta;
+ if (element.attribute("meta") == "plain")
+ meta = FunctionNode::Plain;
+ else if (element.attribute("meta") == "signal")
+ meta = FunctionNode::Signal;
+ else if (element.attribute("meta") == "slot")
+ meta = FunctionNode::Slot;
+ else if (element.attribute("meta") == "constructor")
+ meta = FunctionNode::Ctor;
+ else if (element.attribute("meta") == "destructor")
+ meta = FunctionNode::Dtor;
+ else if (element.attribute("meta") == "macro")
+ meta = FunctionNode::MacroWithParams;
+ else if (element.attribute("meta") == "macrowithparams")
+ meta = FunctionNode::MacroWithParams;
+ else if (element.attribute("meta") == "macrowithoutparams")
+ meta = FunctionNode::MacroWithoutParams;
+ else
+ return;
+
+ FunctionNode *functionNode = new FunctionNode(parent, name);
+ functionNode->setReturnType(element.attribute("return"));
+ functionNode->setVirtualness(virt);
+ functionNode->setMetaness(meta);
+ functionNode->setConst(element.attribute("const") == "true");
+ functionNode->setStatic(element.attribute("static") == "true");
+ functionNode->setOverload(element.attribute("overload") == "true");
+
+ if (element.hasAttribute("relates")
+ && element.attribute("relates") != parent->name()) {
+ priv->relatedList.append(
+ QPair<FunctionNode*,QString>(functionNode,
+ element.attribute("relates")));
+ }
+
+ QDomElement child = element.firstChildElement("parameter");
+ while (!child.isNull()) {
+ // Do not use the default value for the parameter; it is not
+ // required, and has been known to cause problems.
+ Parameter parameter(child.attribute("left"),
+ child.attribute("right"),
+ child.attribute("name"),
+ ""); // child.attribute("default")
+ functionNode->addParameter(parameter);
+ child = child.nextSiblingElement("parameter");
+ }
+
+ section = functionNode;
+
+ if (!indexUrl.isEmpty())
+ location =
+ Location(indexUrl + "/" + parent->name().toLower() + ".html");
+ else if (!indexUrl.isNull())
+ location = Location(parent->name().toLower() + ".html");
+
+ }
+ else if (element.nodeName() == "variable") {
+ section = new VariableNode(parent, name);
+
+ if (!indexUrl.isEmpty())
+ location = Location(indexUrl + "/" + parent->name().toLower() + ".html");
+ else if (!indexUrl.isNull())
+ location = Location(parent->name().toLower() + ".html");
+
+ }
+ else if (element.nodeName() == "keyword") {
+ Target target;
+ target.node = parent;
+ target.priority = 1;
+ target.atom = new Atom(Atom::Target, name);
+ priv->targetHash.insert(name, target);
+ return;
+
+ }
+ else if (element.nodeName() == "target") {
+ Target target;
+ target.node = parent;
+ target.priority = 2;
+ target.atom = new Atom(Atom::Target, name);
+ priv->targetHash.insert(name, target);
+ return;
+
+ }
+ else if (element.nodeName() == "contents") {
+ Target target;
+ target.node = parent;
+ target.priority = 3;
+ target.atom = new Atom(Atom::Target, name);
+ priv->targetHash.insert(name, target);
+ return;
+
+ }
+ else
+ return;
+
+ QString access = element.attribute("access");
+ if (access == "public")
+ section->setAccess(Node::Public);
+ else if (access == "protected")
+ section->setAccess(Node::Protected);
+ else if (access == "private")
+ section->setAccess(Node::Private);
+ else
+ section->setAccess(Node::Public);
+
+ if (element.nodeName() != "page") {
+ QString threadSafety = element.attribute("threadsafety");
+ if (threadSafety == "non-reentrant")
+ section->setThreadSafeness(Node::NonReentrant);
+ else if (threadSafety == "reentrant")
+ section->setThreadSafeness(Node::Reentrant);
+ else if (threadSafety == "thread safe")
+ section->setThreadSafeness(Node::ThreadSafe);
+ else
+ section->setThreadSafeness(Node::UnspecifiedSafeness);
+ }
+ else
+ section->setThreadSafeness(Node::UnspecifiedSafeness);
+
+ QString status = element.attribute("status");
+ if (status == "compat")
+ section->setStatus(Node::Compat);
+ else if (status == "obsolete")
+ section->setStatus(Node::Obsolete);
+ else if (status == "deprecated")
+ section->setStatus(Node::Deprecated);
+ else if (status == "preliminary")
+ section->setStatus(Node::Preliminary);
+ else if (status == "commendable")
+ section->setStatus(Node::Commendable);
+ else if (status == "internal")
+ section->setStatus(Node::Internal);
+ else if (status == "main")
+ section->setStatus(Node::Main);
+ else
+ section->setStatus(Node::Commendable);
+
+ section->setModuleName(element.attribute("module"));
+ if (!indexUrl.isEmpty()) {
+ if (indexUrl.startsWith("."))
+ section->setUrl(href);
+ else
+ section->setUrl(indexUrl + "/" + href);
+ }
+
+ // Create some content for the node.
+ QSet<QString> emptySet;
+
+ Doc doc(location, location, " ", emptySet); // placeholder
+ section->setDoc(doc);
+
+ if (section->isInnerNode()) {
+ InnerNode *inner = static_cast<InnerNode*>(section);
+ if (inner) {
+ QDomElement child = element.firstChildElement();
+
+ while (!child.isNull()) {
+ if (element.nodeName() == "class")
+ readIndexSection(child, inner, indexUrl);
+ else if (element.nodeName() == "page")
+ readIndexSection(child, inner, indexUrl);
+ else if (element.nodeName() == "namespace" && !name.isEmpty())
+ // The root node in the index is a namespace with an empty name.
+ readIndexSection(child, inner, indexUrl);
+ else
+ readIndexSection(child, parent, indexUrl);
+
+ child = child.nextSiblingElement();
+ }
+ }
+ }
+}
+
+/*!
+ */
+QString Tree::readIndexText(const QDomElement &element)
+{
+ QString text;
+ QDomNode child = element.firstChild();
+ while (!child.isNull()) {
+ if (child.isText())
+ text += child.toText().nodeValue();
+ child = child.nextSibling();
+ }
+ return text;
+}
+
+/*!
+ */
+void Tree::resolveIndex()
+{
+ QPair<ClassNode*,QString> pair;
+
+ foreach (pair, priv->basesList) {
+ foreach (const QString &base, pair.second.split(",")) {
+ Node *baseClass = root()->findNode(base, Node::Class);
+ if (baseClass) {
+ pair.first->addBaseClass(Node::Public,
+ static_cast<ClassNode*>(baseClass));
+ }
+ }
+ }
+
+ QPair<FunctionNode*,QString> relatedPair;
+
+ foreach (relatedPair, priv->relatedList) {
+ Node *classNode = root()->findNode(relatedPair.second, Node::Class);
+ if (classNode)
+ relatedPair.first->setRelates(static_cast<ClassNode*>(classNode));
+ }
+}
+
+/*!
+ Generate the index section with the given \a writer for the \a node
+ specified, returning true if an element was written; otherwise returns
+ false.
+ */
+bool Tree::generateIndexSection(QXmlStreamWriter &writer,
+ const Node *node,
+ bool generateInternalNodes) const
+{
+ if (!node->url().isEmpty())
+ return false;
+
+ QString nodeName;
+ switch (node->type()) {
+ case Node::Namespace:
+ nodeName = "namespace";
+ break;
+ case Node::Class:
+ nodeName = "class";
+ break;
+ case Node::Fake:
+ nodeName = "page";
+ break;
+ case Node::Enum:
+ nodeName = "enum";
+ break;
+ case Node::Typedef:
+ nodeName = "typedef";
+ break;
+ case Node::Property:
+ nodeName = "property";
+ break;
+ case Node::Function:
+ nodeName = "function";
+ break;
+ case Node::Variable:
+ nodeName = "variable";
+ break;
+ case Node::Target:
+ nodeName = "target";
+ break;
+ default:
+ return false;
+ }
+
+ QString access;
+ switch (node->access()) {
+ case Node::Public:
+ access = "public";
+ break;
+ case Node::Protected:
+ access = "protected";
+ break;
+ case Node::Private:
+ // Do not include private non-internal nodes in the index.
+ // (Internal public and protected nodes are marked as private
+ // by qdoc. We can check their internal status to determine
+ // whether they were really private to begin with.)
+ if (node->status() == Node::Internal && generateInternalNodes)
+ access = "internal";
+ else
+ return false;
+ break;
+ default:
+ return false;
+ }
+
+ QString objName = node->name();
+
+ // Special case: only the root node should have an empty name.
+ if (objName.isEmpty() && node != root())
+ return false;
+
+ writer.writeStartElement(nodeName);
+
+ QXmlStreamAttributes attributes;
+ writer.writeAttribute("access", access);
+
+ if (node->type() != Node::Fake) {
+ QString threadSafety;
+ switch (node->threadSafeness()) {
+ case Node::NonReentrant:
+ threadSafety = "non-reentrant";
+ break;
+ case Node::Reentrant:
+ threadSafety = "reentrant";
+ break;
+ case Node::ThreadSafe:
+ threadSafety = "thread safe";
+ break;
+ case Node::UnspecifiedSafeness:
+ default:
+ threadSafety = "unspecified";
+ break;
+ }
+ writer.writeAttribute("threadsafety", threadSafety);
+ }
+
+ QString status;
+ switch (node->status()) {
+ case Node::Compat:
+ status = "compat";
+ break;
+ case Node::Obsolete:
+ status = "obsolete";
+ break;
+ case Node::Deprecated:
+ status = "deprecated";
+ break;
+ case Node::Preliminary:
+ status = "preliminary";
+ break;
+ case Node::Commendable:
+ status = "commendable";
+ break;
+ case Node::Internal:
+ status = "internal";
+ break;
+ case Node::Main:
+ default:
+ status = "main";
+ break;
+ }
+ writer.writeAttribute("status", status);
+
+ writer.writeAttribute("name", objName);
+ QString fullName = fullDocumentName(node);
+ if (fullName != objName)
+ writer.writeAttribute("fullname", fullName);
+ writer.writeAttribute("href", fullDocumentLocation(node));
+ if (node->type() != Node::Fake)
+ writer.writeAttribute("location", node->location().fileName());
+
+ switch (node->type()) {
+
+ case Node::Class:
+ {
+ // Classes contain information about their base classes.
+
+ const ClassNode *classNode = static_cast<const ClassNode*>(node);
+ QList<RelatedClass> bases = classNode->baseClasses();
+ QSet<QString> baseStrings;
+ foreach (const RelatedClass &related, bases) {
+ ClassNode *baseClassNode = related.node;
+ baseStrings.insert(baseClassNode->name());
+ }
+ writer.writeAttribute("bases", QStringList(baseStrings.toList()).join(","));
+ writer.writeAttribute("module", node->moduleName());
+ }
+ break;
+
+ case Node::Namespace:
+ writer.writeAttribute("module", node->moduleName());
+ break;
+
+ case Node::Fake:
+ {
+ /*
+ Fake nodes (such as manual pages) contain subtypes,
+ titles and other attributes.
+ */
+
+ const FakeNode *fakeNode = static_cast<const FakeNode*>(node);
+ switch (fakeNode->subType()) {
+ case FakeNode::Example:
+ writer.writeAttribute("subtype", "example");
+ break;
+ case FakeNode::HeaderFile:
+ writer.writeAttribute("subtype", "header");
+ break;
+ case FakeNode::File:
+ writer.writeAttribute("subtype", "file");
+ break;
+ case FakeNode::Group:
+ writer.writeAttribute("subtype", "group");
+ break;
+ case FakeNode::Module:
+ writer.writeAttribute("subtype", "module");
+ break;
+ case FakeNode::Page:
+ writer.writeAttribute("subtype", "page");
+ break;
+ case FakeNode::ExternalPage:
+ writer.writeAttribute("subtype", "externalpage");
+ break;
+ default:
+ break;
+ }
+ writer.writeAttribute("title", fakeNode->title());
+ writer.writeAttribute("fulltitle", fakeNode->fullTitle());
+ writer.writeAttribute("subtitle", fakeNode->subTitle());
+ writer.writeAttribute("location", fakeNode->doc().location().fileName());
+ }
+ break;
+
+ case Node::Function:
+ {
+ /*
+ Function nodes contain information about the type of
+ function being described.
+ */
+
+ const FunctionNode *functionNode =
+ static_cast<const FunctionNode*>(node);
+
+ switch (functionNode->virtualness()) {
+ case FunctionNode::NonVirtual:
+ writer.writeAttribute("virtual", "non");
+ break;
+ case FunctionNode::ImpureVirtual:
+ writer.writeAttribute("virtual", "impure");
+ break;
+ case FunctionNode::PureVirtual:
+ writer.writeAttribute("virtual", "pure");
+ break;
+ default:
+ break;
+ }
+ switch (functionNode->metaness()) {
+ case FunctionNode::Plain:
+ writer.writeAttribute("meta", "plain");
+ break;
+ case FunctionNode::Signal:
+ writer.writeAttribute("meta", "signal");
+ break;
+ case FunctionNode::Slot:
+ writer.writeAttribute("meta", "slot");
+ break;
+ case FunctionNode::Ctor:
+ writer.writeAttribute("meta", "constructor");
+ break;
+ case FunctionNode::Dtor:
+ writer.writeAttribute("meta", "destructor");
+ break;
+ case FunctionNode::MacroWithParams:
+ writer.writeAttribute("meta", "macrowithparams");
+ break;
+ case FunctionNode::MacroWithoutParams:
+ writer.writeAttribute("meta", "macrowithoutparams");
+ break;
+ default:
+ break;
+ }
+ writer.writeAttribute("const", functionNode->isConst()?"true":"false");
+ writer.writeAttribute("static", functionNode->isStatic()?"true":"false");
+ writer.writeAttribute("overload", functionNode->isOverload()?"true":"false");
+ if (functionNode->isOverload())
+ writer.writeAttribute("overload-number", QString::number(functionNode->overloadNumber()));
+ if (functionNode->relates())
+ writer.writeAttribute("relates", functionNode->relates()->name());
+ const PropertyNode *propertyNode = functionNode->associatedProperty();
+ if (propertyNode)
+ writer.writeAttribute("associated-property", propertyNode->name());
+ writer.writeAttribute("type", functionNode->returnType());
+ }
+ break;
+
+ case Node::Property:
+ {
+ const PropertyNode *propertyNode = static_cast<const PropertyNode*>(node);
+ writer.writeAttribute("type", propertyNode->dataType());
+ foreach (const Node *fnNode, propertyNode->getters()) {
+ if (fnNode) {
+ const FunctionNode *functionNode = static_cast<const FunctionNode*>(fnNode);
+ writer.writeStartElement("getter");
+ writer.writeAttribute("name", functionNode->name());
+ writer.writeEndElement(); // getter
+ }
+ }
+ foreach (const Node *fnNode, propertyNode->setters()) {
+ if (fnNode) {
+ const FunctionNode *functionNode = static_cast<const FunctionNode*>(fnNode);
+ writer.writeStartElement("setter");
+ writer.writeAttribute("name", functionNode->name());
+ writer.writeEndElement(); // getter
+ }
+ }
+ foreach (const Node *fnNode, propertyNode->resetters()) {
+ if (fnNode) {
+ const FunctionNode *functionNode = static_cast<const FunctionNode*>(fnNode);
+ writer.writeStartElement("resetter");
+ writer.writeAttribute("name", functionNode->name());
+ writer.writeEndElement(); // getter
+ }
+ }
+ }
+ break;
+
+ case Node::Variable:
+ {
+ const VariableNode *variableNode =
+ static_cast<const VariableNode*>(node);
+ writer.writeAttribute("type", variableNode->dataType());
+ writer.writeAttribute("static",
+ variableNode->isStatic() ? "true" : "false");
+ }
+ break;
+ default:
+ break;
+ }
+
+ // Inner nodes and function nodes contain child nodes of some sort, either
+ // actual child nodes or function parameters. For these, we close the
+ // opening tag, create child elements, then add a closing tag for the
+ // element. Elements for all other nodes are closed in the opening tag.
+
+ if (node->isInnerNode()) {
+
+ const InnerNode *inner = static_cast<const InnerNode*>(node);
+
+ // For internal pages, we canonicalize the target, keyword and content
+ // item names so that they can be used by qdoc for other sets of
+ // documentation.
+ // The reason we do this here is that we don't want to ruin
+ // externally composed indexes, containing non-qdoc-style target names
+ // when reading in indexes.
+
+ if (inner->doc().hasTargets()) {
+ bool external = false;
+ if (inner->type() == Node::Fake) {
+ const FakeNode *fakeNode = static_cast<const FakeNode *>(inner);
+ if (fakeNode->subType() == FakeNode::ExternalPage)
+ external = true;
+ }
+
+ foreach (const Atom *target, inner->doc().targets()) {
+ QString targetName = target->string();
+ if (!external)
+ targetName = Doc::canonicalTitle(targetName);
+
+ writer.writeStartElement("target");
+ writer.writeAttribute("name", targetName);
+ writer.writeEndElement(); // target
+ }
+ }
+ if (inner->doc().hasKeywords()) {
+ foreach (const Atom *keyword, inner->doc().keywords()) {
+ writer.writeStartElement("keyword");
+ writer.writeAttribute("name",
+ Doc::canonicalTitle(keyword->string()));
+ writer.writeEndElement(); // keyword
+ }
+ }
+ if (inner->doc().hasTableOfContents()) {
+ for (int i = 0; i < inner->doc().tableOfContents().size(); ++i) {
+ Atom *item = inner->doc().tableOfContents()[i];
+ int level = inner->doc().tableOfContentsLevels()[i];
+
+ QString title = Text::sectionHeading(item).toString();
+ writer.writeStartElement("contents");
+ writer.writeAttribute("name", Doc::canonicalTitle(title));
+ writer.writeAttribute("title", title);
+ writer.writeAttribute("level", QString::number(level));
+ writer.writeEndElement(); // contents
+ }
+ }
+
+ }
+ else if (node->type() == Node::Function) {
+
+ const FunctionNode *functionNode = static_cast<const FunctionNode*>(node);
+ // Write a signature attribute for convenience.
+ QStringList signatureList;
+ QStringList resolvedParameters;
+
+ foreach (const Parameter &parameter, functionNode->parameters()) {
+ QString leftType = parameter.leftType();
+ const Node *leftNode =
+ const_cast<Tree*>(this)->findNode(parameter.leftType().split("::"),
+ Node::Typedef, 0, SearchBaseClasses|NonFunction);
+ if (!leftNode) {
+ leftNode = const_cast<Tree *>(this)->findNode(
+ parameter.leftType().split("::"), Node::Typedef,
+ node->parent(), SearchBaseClasses|NonFunction);
+ }
+ if (leftNode) {
+ if (leftNode->type() == Node::Typedef) {
+ const TypedefNode *typedefNode =
+ static_cast<const TypedefNode *>(leftNode);
+ if (typedefNode->associatedEnum()) {
+ leftType = "QFlags<"+fullDocumentName(typedefNode->associatedEnum())+">";
+ }
+ }
+ else
+ leftType = fullDocumentName(leftNode);
+ }
+ resolvedParameters.append(leftType);
+ signatureList.append(leftType + " " + parameter.name());
+ }
+
+ QString signature = functionNode->name()+"("+signatureList.join(", ")+")";
+ if (functionNode->isConst())
+ signature += " const";
+ writer.writeAttribute("signature", signature);
+
+ for (int i = 0; i < functionNode->parameters().size(); ++i) {
+ Parameter parameter = functionNode->parameters()[i];
+ writer.writeStartElement("parameter");
+ writer.writeAttribute("left", resolvedParameters[i]);
+ writer.writeAttribute("right", parameter.rightType());
+ writer.writeAttribute("name", parameter.name());
+ writer.writeAttribute("default", parameter.defaultValue());
+ writer.writeEndElement(); // parameter
+ }
+
+ }
+ else if (node->type() == Node::Enum) {
+
+ const EnumNode *enumNode = static_cast<const EnumNode*>(node);
+ if (enumNode->flagsType()) {
+ writer.writeAttribute("typedef",
+ fullDocumentName(enumNode->flagsType()));
+ }
+ foreach (const EnumItem &item, enumNode->items()) {
+ writer.writeStartElement("value");
+ writer.writeAttribute("name", item.name());
+ writer.writeAttribute("value", item.value());
+ writer.writeEndElement(); // value
+ }
+
+ }
+ else if (node->type() == Node::Typedef) {
+
+ const TypedefNode *typedefNode = static_cast<const TypedefNode*>(node);
+ if (typedefNode->associatedEnum()) {
+ writer.writeAttribute("enum",
+ fullDocumentName(typedefNode->associatedEnum()));
+ }
+ }
+
+ return true;
+}
+
+/*!
+ */
+void Tree::generateIndexSections(QXmlStreamWriter &writer,
+ const Node *node,
+ bool generateInternalNodes) const
+{
+ if (generateIndexSection(writer, node, generateInternalNodes)) {
+
+ if (node->isInnerNode()) {
+ const InnerNode *inner = static_cast<const InnerNode *>(node);
+
+ // Recurse to write an element for this child node and all its children.
+ foreach (const Node *child, inner->childNodes())
+ generateIndexSections(writer, child, generateInternalNodes);
+
+/*
+ foreach (const Node *child, inner->relatedNodes()) {
+ QDomElement childElement = generateIndexSections(document, child);
+ element.appendChild(childElement);
+ }
+*/
+ }
+ writer.writeEndElement();
+ }
+}
+
+/*!
+ Outputs an index file.
+ */
+void Tree::generateIndex(const QString &fileName,
+ const QString &url,
+ const QString &title,
+ bool generateInternalNodes) const
+{
+ QFile file(fileName);
+ if (!file.open(QFile::WriteOnly | QFile::Text))
+ return ;
+
+ QXmlStreamWriter writer(&file);
+ writer.setAutoFormatting(true);
+ writer.writeStartDocument();
+ writer.writeDTD("<!DOCTYPE QDOCINDEX>");
+
+ writer.writeStartElement("INDEX");
+ writer.writeAttribute("url", url);
+ writer.writeAttribute("title", title);
+ writer.writeAttribute("version", version());
+
+ generateIndexSections(writer, root(), generateInternalNodes);
+
+ writer.writeEndElement(); // INDEX
+ writer.writeEndElement(); // QDOCINDEX
+ writer.writeEndDocument();
+ file.close();
+}
+
+/*!
+ Generate the tag file section with the given \a writer for the \a node
+ specified, returning true if an element was written; otherwise returns
+ false.
+ */
+void Tree::generateTagFileCompounds(QXmlStreamWriter &writer,
+ const InnerNode *inner) const
+{
+ foreach (const Node *node, inner->childNodes()) {
+
+ if (!node->url().isEmpty())
+ continue;
+
+ QString kind;
+ switch (node->type()) {
+ case Node::Namespace:
+ kind = "namespace";
+ break;
+ case Node::Class:
+ kind = "class";
+ break;
+ case Node::Enum:
+ case Node::Typedef:
+ case Node::Property:
+ case Node::Function:
+ case Node::Variable:
+ case Node::Target:
+ default:
+ continue;
+ }
+
+ QString access;
+ switch (node->access()) {
+ case Node::Public:
+ access = "public";
+ break;
+ case Node::Protected:
+ access = "protected";
+ break;
+ case Node::Private:
+ default:
+ continue;
+ }
+
+ QString objName = node->name();
+
+ // Special case: only the root node should have an empty name.
+ if (objName.isEmpty() && node != root())
+ continue;
+
+ // *** Write the starting tag for the element here. ***
+ writer.writeStartElement("compound");
+ writer.writeAttribute("kind", kind);
+
+ if (node->type() == Node::Class) {
+ writer.writeTextElement("name", fullDocumentName(node));
+ writer.writeTextElement("filename", fullDocumentLocation(node));
+
+ // Classes contain information about their base classes.
+ const ClassNode *classNode = static_cast<const ClassNode*>(node);
+ QList<RelatedClass> bases = classNode->baseClasses();
+ foreach (const RelatedClass &related, bases) {
+ ClassNode *baseClassNode = related.node;
+ writer.writeTextElement("base", baseClassNode->name());
+ }
+
+ // Recurse to write all members.
+ generateTagFileMembers(writer, static_cast<const InnerNode *>(node));
+ writer.writeEndElement();
+
+ // Recurse to write all compounds.
+ generateTagFileCompounds(writer, static_cast<const InnerNode *>(node));
+ } else {
+ writer.writeTextElement("name", fullDocumentName(node));
+ writer.writeTextElement("filename", fullDocumentLocation(node));
+
+ // Recurse to write all members.
+ generateTagFileMembers(writer, static_cast<const InnerNode *>(node));
+ writer.writeEndElement();
+
+ // Recurse to write all compounds.
+ generateTagFileCompounds(writer, static_cast<const InnerNode *>(node));
+ }
+ }
+}
+
+/*!
+ */
+void Tree::generateTagFileMembers(QXmlStreamWriter &writer,
+ const InnerNode *inner) const
+{
+ foreach (const Node *node, inner->childNodes()) {
+
+ if (!node->url().isEmpty())
+ continue;
+
+ QString nodeName;
+ QString kind;
+ switch (node->type()) {
+ case Node::Enum:
+ nodeName = "member";
+ kind = "enum";
+ break;
+ case Node::Typedef:
+ nodeName = "member";
+ kind = "typedef";
+ break;
+ case Node::Property:
+ nodeName = "member";
+ kind = "property";
+ break;
+ case Node::Function:
+ nodeName = "member";
+ kind = "function";
+ break;
+ case Node::Namespace:
+ nodeName = "namespace";
+ break;
+ case Node::Class:
+ nodeName = "class";
+ break;
+ case Node::Variable:
+ case Node::Target:
+ default:
+ continue;
+ }
+
+ QString access;
+ switch (node->access()) {
+ case Node::Public:
+ access = "public";
+ break;
+ case Node::Protected:
+ access = "protected";
+ break;
+ case Node::Private:
+ default:
+ continue;
+ }
+
+ QString objName = node->name();
+
+ // Special case: only the root node should have an empty name.
+ if (objName.isEmpty() && node != root())
+ continue;
+
+ // *** Write the starting tag for the element here. ***
+ writer.writeStartElement(nodeName);
+ if (!kind.isEmpty())
+ writer.writeAttribute("kind", kind);
+
+ switch (node->type()) {
+
+ case Node::Class:
+ writer.writeCharacters(fullDocumentName(node));
+ writer.writeEndElement();
+ break;
+ case Node::Namespace:
+ writer.writeCharacters(fullDocumentName(node));
+ writer.writeEndElement();
+ break;
+ case Node::Function:
+ {
+ /*
+ Function nodes contain information about
+ the type of function being described.
+ */
+
+ const FunctionNode *functionNode =
+ static_cast<const FunctionNode*>(node);
+ writer.writeAttribute("protection", access);
+
+ switch (functionNode->virtualness()) {
+ case FunctionNode::NonVirtual:
+ writer.writeAttribute("virtualness", "non");
+ break;
+ case FunctionNode::ImpureVirtual:
+ writer.writeAttribute("virtualness", "virtual");
+ break;
+ case FunctionNode::PureVirtual:
+ writer.writeAttribute("virtual", "pure");
+ break;
+ default:
+ break;
+ }
+ writer.writeAttribute("static",
+ functionNode->isStatic() ? "yes" : "no");
+
+ if (functionNode->virtualness() == FunctionNode::NonVirtual)
+ writer.writeTextElement("type", functionNode->returnType());
+ else
+ writer.writeTextElement("type",
+ "virtual " + functionNode->returnType());
+
+ writer.writeTextElement("name", objName);
+ QStringList pieces = fullDocumentLocation(node).split("#");
+ writer.writeTextElement("anchorfile", pieces[0]);
+ writer.writeTextElement("anchor", pieces[1]);
+
+ // Write a signature attribute for convenience.
+ QStringList signatureList;
+
+ foreach (const Parameter &parameter, functionNode->parameters()) {
+ QString leftType = parameter.leftType();
+ const Node *leftNode = const_cast<Tree *>(this)->findNode(parameter.leftType().split("::"),
+ Node::Typedef, 0, SearchBaseClasses|NonFunction);
+ if (!leftNode) {
+ leftNode = const_cast<Tree *>(this)->findNode(
+ parameter.leftType().split("::"), Node::Typedef,
+ node->parent(), SearchBaseClasses|NonFunction);
+ }
+ if (leftNode) {
+ const TypedefNode *typedefNode = static_cast<const TypedefNode *>(leftNode);
+ if (typedefNode->associatedEnum()) {
+ leftType = "QFlags<"+fullDocumentName(typedefNode->associatedEnum())+">";
+ }
+ }
+ signatureList.append(leftType + " " + parameter.name());
+ }
+
+ QString signature = "("+signatureList.join(", ")+")";
+ if (functionNode->isConst())
+ signature += " const";
+ if (functionNode->virtualness() == FunctionNode::PureVirtual)
+ signature += " = 0";
+ writer.writeTextElement("arglist", signature);
+ }
+ writer.writeEndElement(); // member
+ break;
+
+ case Node::Property:
+ {
+ const PropertyNode *propertyNode = static_cast<const PropertyNode*>(node);
+ writer.writeAttribute("type", propertyNode->dataType());
+ writer.writeTextElement("name", objName);
+ QStringList pieces = fullDocumentLocation(node).split("#");
+ writer.writeTextElement("anchorfile", pieces[0]);
+ writer.writeTextElement("anchor", pieces[1]);
+ writer.writeTextElement("arglist", "");
+ }
+ writer.writeEndElement(); // member
+ break;
+
+ case Node::Enum:
+ {
+ const EnumNode *enumNode = static_cast<const EnumNode*>(node);
+ writer.writeTextElement("name", objName);
+ QStringList pieces = fullDocumentLocation(node).split("#");
+ writer.writeTextElement("anchor", pieces[1]);
+ writer.writeTextElement("arglist", "");
+ writer.writeEndElement(); // member
+
+ for (int i = 0; i < enumNode->items().size(); ++i) {
+ EnumItem item = enumNode->items().value(i);
+ writer.writeStartElement("member");
+ writer.writeAttribute("name", item.name());
+ writer.writeTextElement("anchor", pieces[1]);
+ writer.writeTextElement("arglist", "");
+ writer.writeEndElement(); // member
+ }
+ }
+ break;
+
+ case Node::Typedef:
+ {
+ const TypedefNode *typedefNode = static_cast<const TypedefNode*>(node);
+ if (typedefNode->associatedEnum())
+ writer.writeAttribute("type", fullDocumentName(typedefNode->associatedEnum()));
+ else
+ writer.writeAttribute("type", "");
+ writer.writeTextElement("name", objName);
+ QStringList pieces = fullDocumentLocation(node).split("#");
+ writer.writeTextElement("anchorfile", pieces[0]);
+ writer.writeTextElement("anchor", pieces[1]);
+ writer.writeTextElement("arglist", "");
+ }
+ writer.writeEndElement(); // member
+ break;
+
+ case Node::Variable:
+ case Node::Target:
+ default:
+ break;
+ }
+ }
+}
+
+/*!
+ */
+void Tree::generateTagFile(const QString &fileName) const
+{
+ QFile file(fileName);
+ if (!file.open(QFile::WriteOnly | QFile::Text))
+ return ;
+
+ QXmlStreamWriter writer(&file);
+ writer.setAutoFormatting(true);
+ writer.writeStartDocument();
+
+ writer.writeStartElement("tagfile");
+
+ generateTagFileCompounds(writer, root());
+
+ writer.writeEndElement(); // tagfile
+ writer.writeEndDocument();
+ file.close();
+}
+
+/*!
+ */
+void Tree::addExternalLink(const QString &url, const Node *relative)
+{
+ FakeNode *fakeNode = new FakeNode(root(), url, FakeNode::ExternalPage);
+ fakeNode->setAccess(Node::Public);
+
+ // Create some content for the node.
+ QSet<QString> emptySet;
+ Location location(relative->doc().location());
+ Doc doc(location, location, " ", emptySet); // placeholder
+ fakeNode->setDoc(doc);
+}
+
+/*!
+ Returns the full document location for HTML-based documentation.
+ This should be moved into the HTML generator.
+ */
+QString Tree::fullDocumentLocation(const Node *node) const
+{
+ if (!node)
+ return "";
+ if (!node->url().isEmpty())
+ return node->url();
+
+ if (node->type() == Node::Namespace) {
+
+ // The root namespace has no name - check for this before creating
+ // an attribute containing the location of any documentation.
+
+ if (!node->fileBase().isEmpty())
+ return node->fileBase() + ".html";
+ else
+ return "";
+ }
+ else if (node->type() == Node::Fake) {
+ return node->fileBase() + ".html";
+ }
+ else if (node->fileBase().isEmpty())
+ return "";
+
+ QString parentName;
+ Node *parentNode = 0;
+
+ if ((parentNode = node->relates()))
+ parentName = fullDocumentLocation(node->relates());
+ else if ((parentNode = node->parent()))
+ parentName = fullDocumentLocation(node->parent());
+
+ switch (node->type()) {
+ case Node::Class:
+ case Node::Namespace:
+ if (parentNode && !parentNode->name().isEmpty())
+ return parentName.replace(".html", "") + "-"
+ + node->fileBase().toLower() + ".html";
+ else
+ return node->fileBase() + ".html";
+ case Node::Function:
+ {
+ /*
+ Functions can be destructors, overloaded, or
+ have associated properties.
+ */
+ const FunctionNode *functionNode =
+ static_cast<const FunctionNode *>(node);
+
+ // Functions can be compatibility functions or be obsolete.
+ switch (node->status()) {
+ case Node::Compat:
+ parentName.replace(".html", "-qt3.html");
+ break;
+ case Node::Obsolete:
+ parentName.replace(".html", "-obsolete.html");
+ break;
+ default:
+ ;
+ }
+
+ if (functionNode->metaness() == FunctionNode::Dtor)
+ return parentName + "#dtor." + functionNode->name().mid(1);
+
+ if (functionNode->associatedProperty())
+ return fullDocumentLocation(functionNode->associatedProperty());
+
+ if (functionNode->overloadNumber() > 1)
+ return parentName + "#" + functionNode->name()
+ + "-" + QString::number(functionNode->overloadNumber());
+ else
+ return parentName + "#" + functionNode->name();
+ }
+
+ /*
+ Use node->name() instead of node->fileBase() as
+ the latter returns the name in lower-case. For
+ HTML anchors, we need to preserve the case.
+ */
+ case Node::Enum:
+ return parentName + "#" + node->name() + "-enum";
+ case Node::Typedef:
+ return parentName + "#" + node->name() + "-typedef";
+ case Node::Property:
+ return parentName + "#" + node->name() + "-prop";
+ case Node::Variable:
+ return parentName + "#" + node->name() + "-var";
+ case Node::Target:
+ return parentName + "#" + Doc::canonicalTitle(node->name());
+ case Node::Fake:
+ {
+ QString pageName = node->name();
+ return pageName.replace("/", "-").replace(".", "-") + ".html";
+ }
+ break;
+ default:
+ break;
+ }
+
+ return "";
+}
+
+/*!
+ */
+QString Tree::fullDocumentName(const Node *node) const
+{
+ if (!node)
+ return "";
+
+ QStringList pieces;
+ const Node *n = node;
+
+ do {
+ if (!n->name().isEmpty())
+ pieces.insert(0, n->name());
+
+ if (n->type() == Node::Fake)
+ break;
+
+ // Examine the parent node if one exists.
+ if (n->parent())
+ n = n->parent();
+ else
+ break;
+ } while (true);
+
+ // Create a name based on the type of the ancestor node.
+ if (n->type() == Node::Fake)
+ return pieces.join("#");
+ else
+ return pieces.join("::");
+}
+
+QT_END_NAMESPACE
diff --git a/tools/qdoc3/tree.h b/tools/qdoc3/tree.h
new file mode 100644
index 0000000..8aebcaa
--- /dev/null
+++ b/tools/qdoc3/tree.h
@@ -0,0 +1,157 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*
+ tree.h
+*/
+
+#ifndef TREE_H
+#define TREE_H
+
+#include "node.h"
+#include <QDomElement>
+#include <QXmlStreamWriter>
+
+QT_BEGIN_NAMESPACE
+
+class QStringList;
+class TreePrivate;
+
+class Tree
+{
+ public:
+ enum FindFlag { SearchBaseClasses = 0x1,
+ SearchEnumValues = 0x2,
+ NonFunction = 0x4 };
+
+ Tree();
+ ~Tree();
+
+ Node *findNode(const QStringList &path, Node *relative=0, int findFlags=0);
+ Node *findNode(const QStringList &path,
+ Node::Type type,
+ Node *relative = 0,
+ int findFlags = 0);
+ FunctionNode *findFunctionNode(const QStringList &path,
+ Node *relative = 0,
+ int findFlags = 0);
+ FunctionNode *findFunctionNode(const QStringList &parentPath,
+ const FunctionNode *clone,
+ Node *relative = 0,
+ int findFlags = 0);
+ void addBaseClass(ClassNode *subclass,
+ Node::Access access,
+ const QStringList &basePath,
+ const QString &dataTypeWithTemplateArgs,
+ InnerNode *parent = 0);
+ void addPropertyFunction(PropertyNode *property,
+ const QString &funcName,
+ PropertyNode::FunctionRole funcRole);
+ void addToGroup(Node *node, const QString &group);
+ void addToPublicGroup(Node *node, const QString &group);
+ QMultiMap<QString, Node *> groups() const;
+ QMultiMap<QString, QString> publicGroups() const;
+ void resolveInheritance(NamespaceNode *rootNode = 0);
+ void resolveProperties();
+ void resolveGroups();
+ void resolveTargets();
+ void fixInheritance(NamespaceNode *rootNode = 0);
+ void setVersion(const QString &version) { vers = version; }
+ NamespaceNode *root() { return &roo; }
+
+ QString version() const { return vers; }
+ const Node *findNode(const QStringList &path,
+ const Node *relative = 0,
+ int findFlags = 0) const;
+ const Node *findNode(const QStringList &path,
+ Node::Type type, const
+ Node *relative = 0,
+ int findFlags = 0) const;
+ const FunctionNode *findFunctionNode(const QStringList &path,
+ const Node *relative = 0,
+ int findFlags = 0) const;
+ const FunctionNode *findFunctionNode(const QStringList &parentPath,
+ const FunctionNode *clone,
+ const Node *relative = 0,
+ int findFlags = 0) const;
+ const FakeNode *findFakeNodeByTitle(const QString &title) const;
+ const Node *findUnambiguousTarget(const QString &target, Atom *&atom) const;
+ Atom *findTarget(const QString &target, const Node *node) const;
+ const NamespaceNode *root() const { return &roo; }
+ void readIndexes(const QStringList &indexFiles);
+ bool generateIndexSection(QXmlStreamWriter &writer, const Node *node,
+ bool generateInternalNodes = false) const;
+ void generateIndexSections(QXmlStreamWriter &writer, const Node *node,
+ bool generateInternalNodes = false) const;
+ void generateIndex(const QString &fileName,
+ const QString &url,
+ const QString &title,
+ bool generateInternalNodes = false) const;
+ void generateTagFileCompounds(QXmlStreamWriter &writer,
+ const InnerNode *inner) const;
+ void generateTagFileMembers(QXmlStreamWriter &writer,
+ const InnerNode *inner) const;
+ void generateTagFile(const QString &fileName) const;
+ void addExternalLink(const QString &url, const Node *relative);
+ QString fullDocumentName(const Node *node) const;
+ QString fullDocumentLocation(const Node *node) const;
+
+ private:
+ void resolveInheritance(int pass, ClassNode *classe);
+ FunctionNode *findVirtualFunctionInBaseClasses(ClassNode *classe,
+ FunctionNode *clone);
+ void fixPropertyUsingBaseClasses(ClassNode *classe, PropertyNode *property);
+ NodeList allBaseClasses(const ClassNode *classe) const;
+ void readIndexFile(const QString &path);
+ void readIndexSection(const QDomElement &element, InnerNode *parent,
+ const QString &indexUrl);
+ QString readIndexText(const QDomElement &element);
+ void resolveIndex();
+
+ private:
+ NamespaceNode roo;
+ QString vers;
+ TreePrivate *priv;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/tools/qdoc3/uncompressor.cpp b/tools/qdoc3/uncompressor.cpp
new file mode 100644
index 0000000..8f3897d
--- /dev/null
+++ b/tools/qdoc3/uncompressor.cpp
@@ -0,0 +1,108 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*
+ uncompressor.cpp
+*/
+
+#include "uncompressor.h"
+
+QT_BEGIN_NAMESPACE
+
+QList<Uncompressor *> Uncompressor::uncompressors;
+
+
+/*!
+ \class Uncompressor
+
+ \brief The Uncompressor class is a base class for classes that
+ know how to uncompress a certain kind of compressed file.
+
+ The uncompressor contains a list of the filename extensions
+ of the file types that the uncompressor knows how to uncompress.
+
+ It maintains a static list of all the instances of Uncompressor
+ that have been created. It also has a static function for searching
+ that list to find the uncompressor to use for uncompressing a file
+ with a certain extension.
+ */
+
+/*!
+ The constructor takes a list of filename extensions, which it
+ copies and saves internally. This uncompressor is prepended
+ to the stack list.
+ */
+Uncompressor::Uncompressor( const QStringList& extensions )
+ : fileExts( extensions )
+{
+ uncompressors.prepend( this );
+}
+
+/*!
+ The destructor deletes all the filename extensions.
+ */
+Uncompressor::~Uncompressor()
+{
+ uncompressors.removeAll( this );
+}
+
+/*!
+ This function searches the static list of uncompressors to find the
+ first one that can handle \a fileName. If it finds an acceptable
+ uncompressor, it returns a pointer to it. Otherwise it returns null.
+*/
+Uncompressor*
+Uncompressor::uncompressorForFileName( const QString& fileName )
+{
+ int dot = -1;
+ while ( (dot = fileName.indexOf(".", dot + 1)) != -1 ) {
+ QString ext = fileName.mid( dot + 1 );
+ QList<Uncompressor *>::ConstIterator u = uncompressors.begin();
+ while ( u != uncompressors.end() ) {
+ if ( (*u)->fileExtensions().contains(ext) )
+ return *u;
+ ++u;
+ }
+ }
+ return 0;
+}
+
+QT_END_NAMESPACE
diff --git a/tools/qdoc3/uncompressor.h b/tools/qdoc3/uncompressor.h
new file mode 100644
index 0000000..4d5c9af
--- /dev/null
+++ b/tools/qdoc3/uncompressor.h
@@ -0,0 +1,79 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*
+ uncompressor.h
+*/
+
+#ifndef UNCOMPRESSOR_H
+#define UNCOMPRESSOR_H
+
+#include <qstringlist.h>
+
+#include "location.h"
+
+QT_BEGIN_NAMESPACE
+
+class Uncompressor
+{
+public:
+ Uncompressor( const QStringList& extensions );
+ virtual ~Uncompressor();
+
+ virtual QString uncompressedFilePath( const QString& filePath ) = 0;
+ virtual void uncompressFile( const Location& location,
+ const QString& filePath,
+ const QString& outputFilePath ) = 0;
+
+ static Uncompressor *uncompressorForFileName( const QString& fileName );
+
+protected:
+ const QStringList& fileExtensions() const { return fileExts; }
+
+private:
+ QStringList fileExts;
+
+ static QList<Uncompressor *> uncompressors;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/tools/qdoc3/webxmlgenerator.cpp b/tools/qdoc3/webxmlgenerator.cpp
new file mode 100644
index 0000000..0438adf
--- /dev/null
+++ b/tools/qdoc3/webxmlgenerator.cpp
@@ -0,0 +1,1195 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*
+ webxmlgenerator.cpp
+*/
+
+#include <QtXml>
+
+#include "codemarker.h"
+#include "pagegenerator.h"
+#include "webxmlgenerator.h"
+#include "node.h"
+#include "separator.h"
+#include "tree.h"
+
+QT_BEGIN_NAMESPACE
+
+#define COMMAND_VERSION Doc::alias("version")
+
+WebXMLGenerator::WebXMLGenerator()
+ : PageGenerator()
+{
+}
+
+WebXMLGenerator::~WebXMLGenerator()
+{
+}
+
+void WebXMLGenerator::initializeGenerator(const Config &config)
+{
+ Generator::initializeGenerator(config);
+
+ project = config.getString(CONFIG_PROJECT);
+
+ projectDescription = config.getString(CONFIG_DESCRIPTION);
+ if (projectDescription.isEmpty() && !project.isEmpty())
+ projectDescription = project + " Reference Documentation";
+
+ projectUrl = config.getString(CONFIG_URL);
+
+ generateIndex = config.getBool(CONFIG_GENERATEINDEX);
+}
+
+void WebXMLGenerator::terminateGenerator()
+{
+ PageGenerator::terminateGenerator();
+}
+
+QString WebXMLGenerator::format()
+{
+ return "WebXML";
+}
+
+QString WebXMLGenerator::fileExtension(const Node * /* node */)
+{
+ return "xml";
+}
+
+void WebXMLGenerator::generateTree(const Tree *tree, CodeMarker *marker)
+{
+ tre = tree;
+ moduleClassMap.clear();
+ moduleNamespaceMap.clear();
+ serviceClasses.clear();
+ findAllClasses(tree->root());
+ findAllNamespaces(tree->root());
+
+ PageGenerator::generateTree(tree, marker);
+
+ if (generateIndex)
+ tre->generateIndex(outputDir() + "/" + project.toLower() + ".index",
+ projectUrl, projectDescription, false);
+}
+
+void WebXMLGenerator::startText(const Node *relative, CodeMarker *marker)
+{
+ inLink = false;
+ inContents = false;
+ inSectionHeading = false;
+ numTableRows = 0;
+ sectionNumber.clear();
+ PageGenerator::startText(relative, marker);
+}
+
+int WebXMLGenerator::generateAtom(QXmlStreamWriter &writer, const Atom *atom,
+ const Node *relative, CodeMarker *marker)
+{
+ Q_UNUSED(writer);
+
+ int skipAhead = 0;
+
+ switch (atom->type()) {
+ default:
+ PageGenerator::generateAtom(atom, relative, marker);
+ }
+ return skipAhead;
+}
+
+void WebXMLGenerator::generateClassLikeNode(const InnerNode *inner,
+ CodeMarker *marker)
+{
+ QByteArray data;
+ QXmlStreamWriter writer(&data);
+ writer.setAutoFormatting(true);
+ writer.writeStartDocument();
+ writer.writeStartElement("WebXML");
+ writer.writeStartElement("document");
+
+ generateIndexSections(writer, inner, marker);
+
+ writer.writeEndElement(); // document
+ writer.writeEndElement(); // WebXML
+ writer.writeEndDocument();
+
+ out() << data;
+ out().flush();
+}
+
+void WebXMLGenerator::generateFakeNode(const FakeNode *fake, CodeMarker *marker)
+{
+ QByteArray data;
+ QXmlStreamWriter writer(&data);
+ writer.setAutoFormatting(true);
+ writer.writeStartDocument();
+ writer.writeStartElement("WebXML");
+ writer.writeStartElement("document");
+
+ generateIndexSections(writer, fake, marker);
+
+ writer.writeEndElement(); // document
+ writer.writeEndElement(); // WebXML
+ writer.writeEndDocument();
+
+ out() << data;
+ out().flush();
+}
+
+void WebXMLGenerator::generateIndexSections(QXmlStreamWriter &writer,
+ const Node *node, CodeMarker *marker)
+{
+ if (tre->generateIndexSection(writer, node, true)) {
+
+ // Add documentation to this node if it exists.
+ writer.writeStartElement("description");
+ writer.writeAttribute("path", node->doc().location().filePath());
+ writer.writeAttribute("line", QString::number(node->doc().location().lineNo()));
+ writer.writeAttribute("column", QString::number(node->doc().location().columnNo()));
+
+ if (node->type() == Node::Fake) {
+
+ const FakeNode *fake = static_cast<const FakeNode *>(node);
+
+ generateRelations(writer, node, marker);
+
+ if (fake->subType() == FakeNode::Module) {
+ writer.writeStartElement("generatedlist");
+ writer.writeAttribute("contents", "classesbymodule");
+
+ if (moduleNamespaceMap.contains(fake->name())) {
+ writer.writeStartElement("section");
+ writer.writeStartElement("heading");
+ writer.writeAttribute("level", "1");
+ writer.writeCharacters("Namespaces");
+ writer.writeEndElement(); // heading
+ generateAnnotatedList(writer, fake, marker, moduleNamespaceMap[fake->name()]);
+ writer.writeEndElement(); // section
+ }
+ if (moduleClassMap.contains(fake->name())) {
+ writer.writeStartElement("section");
+ writer.writeStartElement("heading");
+ writer.writeAttribute("level", "1");
+ writer.writeCharacters("Classes");
+ writer.writeEndElement(); // heading
+ generateAnnotatedList(writer, fake, marker, moduleClassMap[fake->name()]);
+ writer.writeEndElement(); // section
+ }
+
+ writer.writeEndElement(); // generatedlist
+ }
+ }
+
+ startText(node, marker);
+
+ const Atom *atom = node->doc().body().firstAtom();
+ while (atom)
+ atom = addAtomElements(writer, atom, node, marker);
+
+ QList<Text> alsoList = node->doc().alsoList();
+ supplementAlsoList(node, alsoList);
+
+ if (!alsoList.isEmpty()) {
+ writer.writeStartElement("see-also");
+ for (int i = 0; i < alsoList.size(); ++i) {
+ const Atom *atom = alsoList.at(i).firstAtom();
+ while (atom)
+ atom = addAtomElements(writer, atom, node, marker);
+ }
+ writer.writeEndElement(); // see-also
+ }
+
+ writer.writeEndElement(); // description
+
+ if (node->isInnerNode()) {
+ const InnerNode *inner = static_cast<const InnerNode *>(node);
+
+ // Recurse to generate an element for this child node and all its children.
+ foreach (const Node *child, inner->childNodes())
+ generateIndexSections(writer, child, marker);
+
+ writer.writeStartElement("related");
+ if (inner->relatedNodes().size() > 0) {
+ foreach (const Node *child, inner->relatedNodes())
+ generateIndexSections(writer, child, marker);
+ }
+ writer.writeEndElement(); // related
+ }
+ writer.writeEndElement();
+ }
+}
+
+void WebXMLGenerator::generateInnerNode(const InnerNode *node, CodeMarker *marker)
+{
+ if (!node->url().isNull())
+ return;
+
+ if (node->type() == Node::Fake) {
+ const FakeNode *fakeNode = static_cast<const FakeNode *>(node);
+ if (fakeNode->subType() == FakeNode::ExternalPage)
+ return;
+ }
+
+ if ( node->parent() != 0 ) {
+ beginSubPage( node->location(), fileName(node) );
+ if ( node->type() == Node::Namespace || node->type() == Node::Class) {
+ generateClassLikeNode(node, marker);
+ } else if ( node->type() == Node::Fake ) {
+ generateFakeNode(static_cast<const FakeNode *>(node), marker);
+ }
+ endSubPage();
+ }
+
+ NodeList::ConstIterator c = node->childNodes().begin();
+ while ( c != node->childNodes().end() ) {
+ if ((*c)->isInnerNode() && (
+ (*c)->access() != Node::Private || (*c)->status() == Node::Internal))
+ generateInnerNode( (const InnerNode *) *c, marker );
+ ++c;
+ }
+}
+
+const Atom *WebXMLGenerator::addAtomElements(QXmlStreamWriter &writer,
+ const Atom *atom, const Node *relative, CodeMarker *marker)
+{
+ switch (atom->type()) {
+ case Atom::AbstractLeft:
+ case Atom::AbstractRight:
+ break;
+ case Atom::AutoLink:
+ if (!inLink && !inSectionHeading) {
+ const Node *node = findNode(atom, relative, marker);
+ if (node) {
+ startLink(writer, atom, node, relative);
+ if (inLink) {
+ writer.writeCharacters(atom->string());
+ writer.writeEndElement(); // link
+ inLink = false;
+ }
+ } else
+ writer.writeCharacters(atom->string());
+ } else
+ writer.writeCharacters(atom->string());
+ break;
+ case Atom::BaseName:
+ break;
+ case Atom::BriefLeft:
+
+ writer.writeStartElement("brief");
+ switch (relative->type()) {
+ case Node::Property:
+ writer.writeCharacters("This property");
+ break;
+ case Node::Variable:
+ writer.writeCharacters("This variable");
+ break;
+ default:
+ break;
+ }
+ if (relative->type() == Node::Property || relative->type() == Node::Variable) {
+ QString str;
+ const Atom *a = atom->next();
+ while (a != 0 && a->type() != Atom::BriefRight) {
+ if (a->type() == Atom::String || a->type() == Atom::AutoLink)
+ str += a->string();
+ a = a->next();
+ }
+ str[0] = str[0].toLower();
+ if (str.right(1) == ".")
+ str.chop(1);
+
+ QStringList words = str.split(" ");
+ if (!(words.first() == "contains" || words.first() == "specifies"
+ || words.first() == "describes" || words.first() == "defines"
+ || words.first() == "holds" || words.first() == "determines"))
+ writer.writeCharacters(" holds ");
+ else
+ writer.writeCharacters(" ");
+ }
+ break;
+
+ case Atom::BriefRight:
+ if (relative->type() == Node::Property || relative->type() == Node::Variable)
+ writer.writeCharacters(".");
+
+ writer.writeEndElement(); // brief
+ break;
+
+ case Atom::C:
+ writer.writeStartElement("teletype");
+ if (inLink)
+ writer.writeAttribute("type", "normal");
+ else
+ writer.writeAttribute("type", "highlighted");
+
+ writer.writeCharacters(plainCode(atom->string()));
+ writer.writeEndElement(); // teletype
+ break;
+
+ case Atom::Code:
+ writer.writeTextElement("code", trimmedTrailing(plainCode(atom->string())));
+ break;
+
+#ifdef QDOC_QML
+ case Atom::Qml:
+ writer.writeTextElement("qml", trimmedTrailing(plainCode(atom->string())));
+#endif
+
+ case Atom::CodeBad:
+ writer.writeTextElement("badcode", trimmedTrailing(plainCode(atom->string())));
+ break;
+
+ case Atom::CodeNew:
+ writer.writeTextElement("para", "you can rewrite it as");
+ writer.writeTextElement("newcode", trimmedTrailing(plainCode(atom->string())));
+ break;
+
+ case Atom::CodeOld:
+ writer.writeTextElement("para", "For example, if you have code like");
+ writer.writeTextElement("oldcode", trimmedTrailing(plainCode(atom->string())));
+ break;
+
+ case Atom::CodeQuoteArgument:
+ if (quoteCommand == "dots") {
+ writer.writeAttribute("indent", atom->string());
+ writer.writeCharacters("...");
+ } else
+ writer.writeCharacters(atom->string());
+ writer.writeEndElement(); // code
+ break;
+
+ case Atom::CodeQuoteCommand:
+ quoteCommand = atom->string();
+ writer.writeStartElement(quoteCommand);
+ break;
+
+ case Atom::FootnoteLeft:
+ writer.writeStartElement("footnote");
+ break;
+
+ case Atom::FootnoteRight:
+ writer.writeEndElement(); // footnote
+ break;
+/*
+ case Atom::FormatElse:
+ writer.writeStartElement("else");
+ writer.writeEndElement(); // else
+ break;
+*/
+ case Atom::FormatEndif:
+ writer.writeEndElement(); // raw
+ break;
+ case Atom::FormatIf:
+ writer.writeStartElement("raw");
+ writer.writeAttribute("format", atom->string());
+ break;
+ case Atom::FormattingLeft:
+ {
+ if (atom->string() == ATOM_FORMATTING_BOLD)
+ writer.writeStartElement("bold");
+ else if (atom->string() == ATOM_FORMATTING_ITALIC)
+ writer.writeStartElement("italic");
+ else if (atom->string() == ATOM_FORMATTING_UNDERLINE)
+ writer.writeStartElement("underline");
+ else if (atom->string() == ATOM_FORMATTING_SUBSCRIPT)
+ writer.writeStartElement("subscript");
+ else if (atom->string() == ATOM_FORMATTING_SUPERSCRIPT)
+ writer.writeStartElement("superscript");
+ else if (atom->string() == ATOM_FORMATTING_TELETYPE)
+ writer.writeStartElement("teletype");
+ else if (atom->string() == ATOM_FORMATTING_PARAMETER)
+ writer.writeStartElement("argument");
+ else if (atom->string() == ATOM_FORMATTING_INDEX)
+ writer.writeStartElement("index");
+ }
+ break;
+/* out() << formattingLeftMap()[atom->string()];
+ if ( atom->string() == ATOM_FORMATTING_PARAMETER ) {
+ if ( atom->next() != 0 && atom->next()->type() == Atom::String ) {
+ QRegExp subscriptRegExp( "([a-z]+)_([0-9n])" );
+ if ( subscriptRegExp.exactMatch(atom->next()->string()) ) {
+ out() << subscriptRegExp.cap( 1 ) << "<sub>"
+ << subscriptRegExp.cap( 2 ) << "</sub>";
+ skipAhead = 1;
+ }
+ }
+ }*/
+ case Atom::FormattingRight:
+ {
+ if (atom->string() == ATOM_FORMATTING_BOLD)
+ writer.writeEndElement();
+ else if (atom->string() == ATOM_FORMATTING_ITALIC)
+ writer.writeEndElement();
+ else if (atom->string() == ATOM_FORMATTING_UNDERLINE)
+ writer.writeEndElement();
+ else if (atom->string() == ATOM_FORMATTING_SUBSCRIPT)
+ writer.writeEndElement();
+ else if (atom->string() == ATOM_FORMATTING_SUPERSCRIPT)
+ writer.writeEndElement();
+ else if (atom->string() == ATOM_FORMATTING_TELETYPE)
+ writer.writeEndElement();
+ else if (atom->string() == ATOM_FORMATTING_PARAMETER)
+ writer.writeEndElement();
+ else if (atom->string() == ATOM_FORMATTING_INDEX)
+ writer.writeEndElement();
+ }
+ if (inLink) {
+ writer.writeEndElement(); // link
+ inLink = false;
+ }
+ break;
+/* if ( atom->string() == ATOM_FORMATTING_LINK ) {
+ if (inLink) {
+ if ( link.isEmpty() ) {
+ if (showBrokenLinks)
+ out() << "</i>";
+ } else {
+ out() << "</a>";
+ }
+ }
+ inLink = false;
+ } else {
+ out() << formattingRightMap()[atom->string()];
+ }*/
+ case Atom::GeneratedList:
+ writer.writeStartElement("generatedlist");
+ writer.writeAttribute("contents", atom->string());
+ writer.writeEndElement(); // generatedlist
+/*
+ if (atom->string() == "annotatedclasses") {
+ generateAnnotatedList(relative, marker, nonCompatClasses);
+ } else if (atom->string() == "classes") {
+ generateCompactList(relative, marker, nonCompatClasses);
+ } else if (atom->string().contains("classesbymodule")) {
+ QString arg = atom->string().trimmed();
+ QString moduleName = atom->string().mid(atom->string().indexOf(
+ "classesbymodule") + 15).trimmed();
+ if (moduleClassMap.contains(moduleName))
+ generateAnnotatedList(relative, marker, moduleClassMap[moduleName]);
+ } else if (atom->string().contains("classesbyedition")) {
+ QString arg = atom->string().trimmed();
+ QString editionName = atom->string().mid(atom->string().indexOf(
+ "classesbyedition") + 16).trimmed();
+ if (editionModuleMap.contains(editionName)) {
+ QMap<QString, const Node *> editionClasses;
+ foreach (const QString &moduleName, editionModuleMap[editionName]) {
+ if (moduleClassMap.contains(moduleName))
+ editionClasses.unite(moduleClassMap[moduleName]);
+ }
+ generateAnnotatedList(relative, marker, editionClasses);
+ }
+ } else if (atom->string() == "classhierarchy") {
+ generateClassHierarchy(relative, marker, nonCompatClasses);
+ } else if (atom->string() == "compatclasses") {
+ generateCompactList(relative, marker, compatClasses);
+ } else if (atom->string() == "functionindex") {
+ generateFunctionIndex(relative, marker);
+ } else if (atom->string() == "legalese") {
+ generateLegaleseList(relative, marker);
+ } else if (atom->string() == "mainclasses") {
+ generateCompactList(relative, marker, mainClasses);
+ } else if (atom->string() == "services") {
+ generateCompactList(relative, marker, serviceClasses);
+ } else if (atom->string() == "overviews") {
+ generateOverviewList(relative, marker);
+ } else if (atom->string() == "namespaces") {
+ generateAnnotatedList(relative, marker, namespaceIndex);
+ } else if (atom->string() == "related") {
+ const FakeNode *fake = static_cast<const FakeNode *>(relative);
+ if (fake && !fake->groupMembers().isEmpty()) {
+ QMap<QString, const Node *> groupMembersMap;
+ foreach (Node *node, fake->groupMembers()) {
+ if (node->type() == Node::Fake)
+ groupMembersMap[fullName(node, relative, marker)] = node;
+ }
+ generateAnnotatedList(fake, marker, groupMembersMap);
+ }
+ } else if (atom->string() == "relatedinline") {
+ const FakeNode *fake = static_cast<const FakeNode *>(relative);
+ if (fake && !fake->groupMembers().isEmpty()) {
+ // Reverse the list into the original scan order.
+ // Should be sorted. But on what? It may not be a
+ // regular class or page definition.
+ QList<const Node *> list;
+ foreach (const Node *node, fake->groupMembers())
+ list.prepend(node);
+ foreach (const Node *node, list)
+ generateBody(node, marker );
+ }
+ }
+ break;
+*/
+ break;
+ case Atom::Image:
+ writer.writeStartElement("image");
+ writer.writeAttribute("href", imageFileName(relative, atom->string()));
+ writer.writeEndElement(); // image
+ break;
+
+ case Atom::InlineImage:
+ writer.writeStartElement("inlineimage");
+ writer.writeAttribute("href", imageFileName(relative, atom->string()));
+ writer.writeEndElement(); // inlineimage
+ break;
+
+ case Atom::ImageText:
+ break;
+
+ case Atom::LegaleseLeft:
+ writer.writeStartElement("legalese");
+ break;
+
+ case Atom::LegaleseRight:
+ writer.writeEndElement(); // legalese
+ break;
+
+ case Atom::Link:
+ case Atom::LinkNode:
+ if (!inLink) {
+ const Node *node = findNode(atom, relative, marker);
+ if (node)
+ startLink(writer, atom, node, relative);
+ }
+ break;
+
+ case Atom::ListLeft:
+ writer.writeStartElement("list");
+
+ if (atom->string() == ATOM_LIST_BULLET)
+ writer.writeAttribute("type", "bullet");
+ else if (atom->string() == ATOM_LIST_TAG)
+ writer.writeAttribute("type", "definition");
+ else if (atom->string() == ATOM_LIST_VALUE)
+ writer.writeAttribute("type", "enum");
+ else {
+ writer.writeAttribute("type", "ordered");
+ if (atom->string() == ATOM_LIST_UPPERALPHA)
+ writer.writeAttribute("start", "A");
+ else if (atom->string() == ATOM_LIST_LOWERALPHA)
+ writer.writeAttribute("start", "a");
+ else if (atom->string() == ATOM_LIST_UPPERROMAN)
+ writer.writeAttribute("start", "I");
+ else if (atom->string() == ATOM_LIST_LOWERROMAN)
+ writer.writeAttribute("start", "i");
+ else // (atom->string() == ATOM_LIST_NUMERIC)
+ writer.writeAttribute("start", "1");
+ }
+ break;
+
+ case Atom::ListItemNumber:
+ break;
+
+ case Atom::ListTagLeft:
+ {
+ writer.writeStartElement("definition");
+
+ writer.writeTextElement("term", plainCode(
+ marker->markedUpEnumValue(atom->next()->string(), relative)));
+ }
+ break;
+
+ case Atom::ListTagRight:
+ writer.writeEndElement(); // definition
+ break;
+
+ case Atom::ListItemLeft:
+ writer.writeStartElement("item");
+ break;
+
+ case Atom::ListItemRight:
+ writer.writeEndElement(); // item
+ break;
+
+ case Atom::ListRight:
+ writer.writeEndElement(); // list
+ break;
+
+ case Atom::Nop:
+ break;
+
+ case Atom::ParaLeft:
+ writer.writeStartElement("para");
+ break;
+
+ case Atom::ParaRight:
+ writer.writeEndElement(); // para
+ break;
+
+ case Atom::QuotationLeft:
+ writer.writeStartElement("quote");
+ break;
+
+ case Atom::QuotationRight:
+ writer.writeEndElement(); // quote
+ break;
+
+ case Atom::RawString:
+ writer.writeCharacters(atom->string());
+ break;
+
+ case Atom::SectionLeft:
+ writer.writeStartElement("section");
+ writer.writeAttribute("id", Doc::canonicalTitle(Text::sectionHeading(atom).toString()));
+ break;
+
+ case Atom::SectionRight:
+ writer.writeEndElement(); // section
+ break;
+
+ case Atom::SectionHeadingLeft:
+ writer.writeStartElement("heading");
+ writer.writeAttribute("level", atom->string()); // + hOffset(relative)
+ inSectionHeading = true;
+ break;
+
+ case Atom::SectionHeadingRight:
+ writer.writeEndElement(); // heading
+ inSectionHeading = false;
+ break;
+
+ case Atom::SidebarLeft:
+ case Atom::SidebarRight:
+ break;
+
+ case Atom::SnippetCommand:
+ writer.writeStartElement(atom->string());
+ break;
+
+ case Atom::SnippetIdentifier:
+ writer.writeAttribute("identifier", atom->string());
+ writer.writeEndElement(); // snippet
+ break;
+
+ case Atom::SnippetLocation:
+ writer.writeAttribute("location", atom->string());
+ break;
+
+ case Atom::String:
+ writer.writeCharacters(atom->string());
+ break;
+
+ case Atom::TableLeft:
+ writer.writeStartElement("table");
+ if (atom->string().contains("%"))
+ writer.writeAttribute("width", atom->string());
+ break;
+
+ case Atom::TableRight:
+ writer.writeEndElement(); // table
+ break;
+
+ case Atom::TableHeaderLeft:
+ writer.writeStartElement("header");
+ break;
+
+ case Atom::TableHeaderRight:
+ writer.writeEndElement(); // header
+ break;
+
+ case Atom::TableRowLeft:
+ writer.writeStartElement("row");
+ break;
+
+ case Atom::TableRowRight:
+ writer.writeEndElement(); // row
+ break;
+
+ case Atom::TableItemLeft:
+ {
+ writer.writeStartElement("item");
+ QStringList spans = atom->string().split(",");
+ if (spans.size() == 2) {
+ if (spans.at(0) != "1")
+ writer.writeAttribute("colspan", spans.at(0).trimmed());
+ if (spans.at(1) != "1")
+ writer.writeAttribute("rowspan", spans.at(1).trimmed());
+ }
+ }
+ break;
+
+ case Atom::TableItemRight:
+ writer.writeEndElement(); // item
+ break;
+
+ case Atom::TableOfContents:
+ writer.writeStartElement("tableofcontents");
+ writer.writeAttribute("details", atom->string());
+ {
+ int numColumns = 1;
+ const Node *node = relative;
+
+ Doc::SectioningUnit sectioningUnit = Doc::Section4;
+ QStringList params = atom->string().split(",");
+ QString columnText = params.at(0);
+ QStringList pieces = columnText.split(" ", QString::SkipEmptyParts);
+ if (pieces.size() >= 2) {
+ columnText = pieces.at(0);
+ pieces.pop_front();
+ QString path = pieces.join(" ").trimmed();
+ node = findNode(path, relative, marker);
+ if (node)
+ writer.writeAttribute("href", fileName(node));
+ }
+
+ if (params.size() == 2) {
+ numColumns = qMax(columnText.toInt(), numColumns);
+ sectioningUnit = (Doc::SectioningUnit)params.at(1).toInt();
+ writer.writeAttribute("columns", QString::number(numColumns));
+ writer.writeAttribute("unit", QString::number(sectioningUnit));
+ }
+
+ if (node)
+ generateTableOfContents(writer, node, sectioningUnit, numColumns,
+ relative);
+ }
+ writer.writeEndElement(); // tableofcontents
+ break;
+
+ case Atom::Target:
+ writer.writeStartElement("target");
+ writer.writeAttribute("name", Doc::canonicalTitle(atom->string()));
+ writer.writeEndElement(); // target
+ break;
+
+ case Atom::UnhandledFormat:
+ case Atom::UnknownCommand:
+ writer.writeCharacters(atom->typeString());
+ break;
+ default:
+ break;
+ }
+
+ if (atom)
+ return atom->next();
+
+ return 0;
+}
+/*
+ QDomElement atomElement = document.createElement(atom->typeString().toLower());
+ QDomText atomValue = document.createTextNode(atom->string());
+ atomElement.appendChild(atomValue);
+ descriptionElement.appendChild(atomElement);
+*/
+
+/*
+ ### Warning: findNode() is a modified version of HtmlGenerator::getLink().
+*/
+const Node *WebXMLGenerator::findNode(const Atom *atom, const Node *relative, CodeMarker *marker)
+{
+ return findNode(atom->string(), relative, marker);
+}
+
+const Node *WebXMLGenerator::findNode(const QString &title, const Node *relative, CodeMarker *marker)
+{
+ QString link;
+ if (title.contains(":") &&
+ (title.startsWith("file:")
+ || title.startsWith("http:")
+ || title.startsWith("https:")
+ || title.startsWith("ftp:")
+ || title.startsWith("mailto:"))) {
+
+ return 0;
+ } else if (title.count('@') == 1) {
+ return 0;
+ } else {
+ QStringList path;
+ if (title.contains('#')) {
+ path = title.split('#');
+ } else {
+ path.append(title);
+ }
+
+ const Node *node = 0;
+ Atom *targetAtom = 0;
+
+ QString first = path.first().trimmed();
+ if (first.isEmpty()) {
+ node = relative;
+ } else if (first.endsWith(".html")) {
+ node = tre->root()->findNode(first, Node::Fake);
+ } else {
+ node = marker->resolveTarget(first, tre, relative);
+ if (!node)
+ node = tre->findFakeNodeByTitle(first);
+ if (!node)
+ node = tre->findUnambiguousTarget(first, targetAtom);
+ }
+
+ if (node) {
+ if (!node->url().isEmpty())
+ return node;
+ else
+ path.removeFirst();
+ } else {
+ return 0;
+ }
+
+ while (!path.isEmpty()) {
+ targetAtom = tre->findTarget(path.first(), node);
+ if (targetAtom == 0)
+ break;
+ path.removeFirst();
+ }
+/* We would ideally treat targets as nodes to be consistent.
+ if (targetAtom && node && node->isInnerNode()) {
+ Node *parentNode = const_cast<Node *>(node);
+ node = new TargetNode(static_cast<InnerNode*>(parentNode), first);
+ }
+*/
+ return node;
+ }
+ return 0;
+}
+
+void WebXMLGenerator::startLink(QXmlStreamWriter &writer, const Atom *atom,
+ const Node *node, const Node *relative)
+{
+ QString location = tre->fullDocumentLocation(node);
+ if (!location.isEmpty()) {
+ writer.writeStartElement("link");
+ writer.writeAttribute("raw", atom->string());
+ if (atom->string().contains("#") || node == relative) {
+ QString target = atom->string().split("#").last();
+ Atom *targetAtom = tre->findTarget(target, node);
+ if (targetAtom)
+ location += "#" + Doc::canonicalTitle(target);
+ }
+ writer.writeAttribute("href", location);
+ QString type = targetType(node);
+ writer.writeAttribute("type", type);
+ switch (node->type()) {
+ case Node::Enum:
+ writer.writeAttribute("enum", tre->fullDocumentName(node));
+ break;
+ case Node::Fake:
+ writer.writeAttribute("page", tre->fullDocumentName(node));
+ break;
+ case Node::Property:
+ {
+ const PropertyNode *propertyNode = static_cast<const PropertyNode *>(node);
+ if (propertyNode->getters().size() > 0)
+ writer.writeAttribute("getter", tre->fullDocumentName(propertyNode->getters()[0]));
+ }
+ default:
+ ;
+ }
+ inLink = true;
+ }
+}
+
+QString WebXMLGenerator::targetType(const Node *node)
+{
+ switch (node->type()) {
+ case Node::Namespace:
+ return "namespace";
+ break;
+ case Node::Class:
+ return "class";
+ break;
+ case Node::Fake:
+ return "page";
+ break;
+ case Node::Enum:
+ return "enum";
+ break;
+ case Node::Typedef:
+ return "typedef";
+ break;
+ case Node::Property:
+ return "property";
+ break;
+ case Node::Function:
+ return "function";
+ break;
+ case Node::Variable:
+ return "variable";
+ break;
+ case Node::Target:
+ return "target";
+ break;
+ default:
+ return "";
+ }
+ return "";
+}
+
+void WebXMLGenerator::generateRelations(QXmlStreamWriter &writer, const Node *node, CodeMarker *marker)
+{
+ if (node && !node->links().empty()) {
+ QPair<QString,QString> linkPair;
+ QPair<QString,QString> anchorPair;
+ const Node *linkNode;
+
+ foreach (Node::LinkType relation, node->links().keys()) {
+
+ linkPair = node->links()[relation];
+ linkNode = findNode(linkPair.first, node, marker);
+
+ if (!linkNode)
+ linkNode = node;
+
+ if (linkNode == node)
+ anchorPair = linkPair;
+ else
+ anchorPair = anchorForNode(linkNode);
+
+ writer.writeStartElement("relation");
+ writer.writeAttribute("href", anchorPair.first);
+ writer.writeAttribute("type", targetType(linkNode));
+
+ switch (relation) {
+ case Node::StartLink:
+ writer.writeAttribute("meta", "start");
+ break;
+ case Node::NextLink:
+ writer.writeAttribute("meta", "next");
+ break;
+ case Node::PreviousLink:
+ writer.writeAttribute("meta", "previous");
+ break;
+ case Node::ContentsLink:
+ writer.writeAttribute("meta", "contents");
+ break;
+ case Node::IndexLink:
+ writer.writeAttribute("meta", "index");
+ break;
+ default:
+ writer.writeAttribute("meta", "");
+ }
+ writer.writeAttribute("description", anchorPair.second);
+ writer.writeEndElement(); // link
+ }
+ }
+}
+
+// Classes adapted from HtmlGenerator.
+
+void WebXMLGenerator::generateTableOfContents(QXmlStreamWriter &writer, const Node *node,
+ Doc::SectioningUnit sectioningUnit,
+ int numColumns, const Node *relative)
+
+{
+ if (!node->doc().hasTableOfContents())
+ return;
+ QList<Atom *> toc = node->doc().tableOfContents();
+ if (toc.isEmpty())
+ return;
+
+ QString nodeName = "";
+ if (node != relative)
+ nodeName = node->name();
+
+ QStringList sectionNumber;
+ int columnSize = 0;
+
+ if (numColumns > 1) {
+ writer.writeStartElement("table");
+ writer.writeAttribute("width", "100%");
+ writer.writeStartElement("row");
+ writer.writeStartElement("item");
+ writer.writeAttribute("width", QString::number((100 + numColumns - 1) / numColumns) + "%");
+ }
+
+ // disable nested links in table of contents
+ inContents = true;
+ inLink = true;
+
+ for (int i = 0; i < toc.size(); ++i) {
+ Atom *atom = toc.at(i);
+
+ int nextLevel = atom->string().toInt();
+ if (nextLevel > (int)sectioningUnit)
+ continue;
+
+ if (sectionNumber.size() < nextLevel) {
+ do {
+ writer.writeStartElement("list");
+ sectionNumber.append("1");
+ } while (sectionNumber.size() < nextLevel);
+ } else {
+ while (sectionNumber.size() > nextLevel) {
+ writer.writeEndElement();
+ sectionNumber.removeLast();
+ }
+ sectionNumber.last() = QString::number(sectionNumber.last().toInt() + 1);
+ }
+ Text headingText = Text::sectionHeading(atom);
+
+ if (sectionNumber.size() == 1 && columnSize > toc.size() / numColumns) {
+ writer.writeEndElement(); // list
+ writer.writeEndElement(); // item
+ writer.writeStartElement("item");
+ writer.writeAttribute("width", QString::number((100 + numColumns - 1) / numColumns) + "%");
+ writer.writeStartElement("list");
+ columnSize = 0;
+ }
+
+ writer.writeStartElement("item");
+ writer.writeStartElement("para");
+ writer.writeStartElement("link");
+ writer.writeAttribute("href", nodeName + "#" + Doc::canonicalTitle(headingText.toString()));
+ writer.writeAttribute("type", "page");
+ writer.writeCharacters(headingText.toString());
+ writer.writeEndElement(); // link
+ writer.writeEndElement(); // para
+ writer.writeEndElement(); // item
+
+ ++columnSize;
+ }
+ while (!sectionNumber.isEmpty()) {
+ writer.writeEndElement(); // list
+ sectionNumber.removeLast();
+ }
+
+ if (numColumns > 1) {
+ writer.writeEndElement(); // item
+ writer.writeEndElement(); // row
+ writer.writeEndElement(); // table
+ }
+
+ inContents = false;
+ inLink = false;
+}
+
+void WebXMLGenerator::generateAnnotatedList(QXmlStreamWriter &writer,
+ const Node *relative, CodeMarker *marker, const QMap<QString, const Node *> &nodeMap)
+{
+ writer.writeStartElement("table");
+ writer.writeAttribute("width", "100%");
+
+ foreach (QString name, nodeMap.keys()) {
+ const Node *node = nodeMap[name];
+
+ writer.writeStartElement("row");
+ writer.writeStartElement("heading");
+ generateFullName(writer, node, relative, marker);
+ writer.writeEndElement(); // heading
+
+ writer.writeStartElement("item");
+ writer.writeCharacters(node->doc().briefText().toString());
+ writer.writeEndElement(); // item
+ writer.writeEndElement(); // row
+ }
+ writer.writeEndElement(); // table
+}
+
+void WebXMLGenerator::generateFullName(QXmlStreamWriter &writer,
+ const Node *apparentNode, const Node *relative, CodeMarker *marker,
+ const Node *actualNode)
+{
+ if ( actualNode == 0 )
+ actualNode = apparentNode;
+ writer.writeStartElement("link");
+ writer.writeAttribute("href", tre->fullDocumentLocation(actualNode));
+ writer.writeAttribute("type", targetType(actualNode));
+ writer.writeCharacters(fullName(apparentNode, relative, marker));
+ writer.writeEndElement(); // link
+}
+
+// Classes copied (and slightly adapted) from the HtmlGenerator. These need
+// refactoring into a common ancestor class.
+
+void WebXMLGenerator::findAllClasses(const InnerNode *node)
+{
+ NodeList::const_iterator c = node->childNodes().constBegin();
+ while (c != node->childNodes().constEnd()) {
+ if ((*c)->access() != Node::Private && (*c)->url().isEmpty()) {
+ if ((*c)->type() == Node::Class && !(*c)->doc().isEmpty()) {
+ QString className = (*c)->name();
+ if ((*c)->parent() && (*c)->parent()->type() == Node::Namespace &&
+ !(*c)->parent()->name().isEmpty())
+ className = (*c)->parent()->name()+"::"+className;
+
+ QString moduleName = (*c)->moduleName();
+ if (!moduleName.isEmpty())
+ moduleClassMap[moduleName].insert((*c)->name(), *c);
+
+ QString serviceName =
+ (static_cast<const ClassNode *>(*c))->serviceName();
+ if (!serviceName.isEmpty())
+ serviceClasses.insert(serviceName, *c);
+ } else if ((*c)->isInnerNode()) {
+ findAllClasses(static_cast<InnerNode *>(*c));
+ }
+ }
+ ++c;
+ }
+}
+
+void WebXMLGenerator::findAllNamespaces(const InnerNode *node)
+{
+ NodeList::ConstIterator c = node->childNodes().begin();
+ while (c != node->childNodes().end()) {
+ if ((*c)->access() != Node::Private) {
+ if ((*c)->isInnerNode() && (*c)->url().isEmpty()) {
+ findAllNamespaces(static_cast<const InnerNode *>(*c));
+ if ((*c)->type() == Node::Namespace) {
+ const NamespaceNode *nspace = static_cast<const NamespaceNode *>(*c);
+ // Ensure that the namespace's name is not empty (the root
+ // namespace has no name).
+ if (!nspace->name().isEmpty()) {
+ namespaceIndex.insert(nspace->name(), *c);
+ QString moduleName = (*c)->moduleName();
+ if (!moduleName.isEmpty())
+ moduleNamespaceMap[moduleName].insert((*c)->name(), *c);
+ }
+ }
+ }
+ }
+ ++c;
+ }
+}
+
+const QPair<QString,QString> WebXMLGenerator::anchorForNode(const Node *node)
+{
+ QPair<QString,QString> anchorPair;
+
+ anchorPair.first = PageGenerator::fileName(node);
+ if (node->type() == Node::Fake) {
+ const FakeNode *fakeNode = static_cast<const FakeNode*>(node);
+ anchorPair.second = fakeNode->title();
+ }
+
+ return anchorPair;
+}
+
+QT_END_NAMESPACE
diff --git a/tools/qdoc3/webxmlgenerator.h b/tools/qdoc3/webxmlgenerator.h
new file mode 100644
index 0000000..f7b4445
--- /dev/null
+++ b/tools/qdoc3/webxmlgenerator.h
@@ -0,0 +1,122 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*
+ WebXMLGenerator.h
+*/
+
+#ifndef WEBXMLGENERATOR_H
+#define WEBXMLGENERATOR_H
+
+#include "codemarker.h"
+#include "config.h"
+#include "pagegenerator.h"
+
+QT_BEGIN_NAMESPACE
+
+class WebXMLGenerator : public PageGenerator
+{
+public:
+ WebXMLGenerator();
+ ~WebXMLGenerator();
+
+ virtual void initializeGenerator(const Config &config);
+ virtual void terminateGenerator();
+ virtual QString format();
+ virtual void generateTree(const Tree *tree, CodeMarker *marker);
+
+protected:
+ virtual void startText( const Node *relative, CodeMarker *marker );
+ virtual int generateAtom(QXmlStreamWriter &writer, const Atom *atom,
+ const Node *relative, CodeMarker *marker );
+ virtual void generateClassLikeNode(const InnerNode *inner, CodeMarker *marker);
+ virtual void generateFakeNode(const FakeNode *fake, CodeMarker *marker);
+ virtual QString fileExtension(const Node *node);
+
+ virtual const Atom *addAtomElements(QXmlStreamWriter &writer, const Atom *atom,
+ const Node *relative, CodeMarker *marker);
+ virtual void generateIndexSections(QXmlStreamWriter &writer, const Node *node,
+ CodeMarker *marker);
+ virtual void generateInnerNode( const InnerNode *node, CodeMarker *marker );
+
+private:
+ const QPair<QString,QString> anchorForNode(const Node *node);
+ void findAllClasses(const InnerNode *node);
+ void findAllNamespaces(const InnerNode *node);
+ const Node *findNode(const Atom *atom, const Node *relative, CodeMarker *marker);
+ const Node *findNode(const QString &title, const Node *relative, CodeMarker *marker);
+ void generateAnnotatedList(QXmlStreamWriter &writer, const Node *relative,
+ CodeMarker *marker, const QMap<QString,
+ const Node *> &nodeMap);
+ void generateFullName(QXmlStreamWriter &writer, const Node *apparentNode,
+ const Node *relative, CodeMarker *marker,
+ const Node *actualNode = 0);
+ void generateRelations(QXmlStreamWriter &writer, const Node *node, CodeMarker *marker);
+ void generateTableOfContents(QXmlStreamWriter &writer, const Node *node,
+ Doc::SectioningUnit sectioningUnit,
+ int numColumns, const Node *relative);
+ void startLink(QXmlStreamWriter &writer, const Atom *atom, const Node *node,
+ const Node *relative);
+ QString targetType(const Node *node);
+
+ const Tree *tre;
+ bool generateIndex;
+ bool inLink;
+ bool inContents;
+ bool inSectionHeading;
+ bool inTableHeader;
+ int numTableRows;
+ bool threeColumnEnumValueTable;
+ QMap<QString, QMap<QString, const Node *> > moduleClassMap;
+ QMap<QString, QMap<QString, const Node *> > moduleNamespaceMap;
+ QMap<QString, const Node *> namespaceIndex;
+ QMap<QString, const Node *> serviceClasses;
+ QString link;
+ QString project;
+ QString projectDescription;
+ QString projectUrl;
+ QString quoteCommand;
+ QStringList sectionNumber;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/tools/qdoc3/yyindent.cpp b/tools/qdoc3/yyindent.cpp
new file mode 100644
index 0000000..bbca39b
--- /dev/null
+++ b/tools/qdoc3/yyindent.cpp
@@ -0,0 +1,1190 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*
+ This file is a self-contained interactive indenter for C++ and Qt
+ Script.
+
+ The general problem of indenting a C++ program is ill posed. On
+ the one hand, an indenter has to analyze programs written in a
+ free-form formal language that is best described in terms of
+ tokens, not characters, not lines. On the other hand, indentation
+ applies to lines and white space characters matter, and otherwise
+ the programs to indent are formally invalid in general, as they
+ are begin edited.
+
+ The approach taken here works line by line. We receive a program
+ consisting of N lines or more, and we want to compute the
+ indentation appropriate for the Nth line. Lines beyond the Nth
+ lines are of no concern to us, so for simplicity we pretend the
+ program has exactly N lines and we call the Nth line the "bottom
+ line". Typically, we have to indent the bottom line when it's
+ still empty, so we concentrate our analysis on the N - 1 lines
+ that precede.
+
+ By inspecting the (N - 1)-th line, the (N - 2)-th line, ...
+ backwards, we determine the kind of the bottom line and indent it
+ accordingly.
+
+ * The bottom line is a comment line. See
+ bottomLineStartsInCComment() and
+ indentWhenBottomLineStartsInCComment().
+ * The bottom line is a continuation line. See isContinuationLine()
+ and indentForContinuationLine().
+ * The bottom line is a standalone line. See
+ indentForStandaloneLine().
+
+ Certain tokens that influence the indentation, notably braces,
+ are looked for in the lines. This is done by simple string
+ comparison, without a real tokenizer. Confusing constructs such
+ as comments and string literals are removed beforehand.
+*/
+
+#include <qregexp.h>
+#include <qstringlist.h>
+
+QT_BEGIN_NAMESPACE
+
+/* qmake ignore Q_OBJECT */
+
+/*
+ The indenter avoids getting stuck in almost infinite loops by
+ imposing arbitrary limits on the number of lines it analyzes when
+ looking for a construct.
+
+ For example, the indenter never considers more than BigRoof lines
+ backwards when looking for the start of a C-style comment.
+*/
+static const int SmallRoof = 40;
+static const int BigRoof = 400;
+
+/*
+ The indenter supports a few parameters:
+
+ * ppHardwareTabSize is the size of a '\t' in your favorite editor.
+ * ppIndentSize is the size of an indentation, or software tab
+ size.
+ * ppContinuationIndentSize is the extra indent for a continuation
+ line, when there is nothing to align against on the previous
+ line.
+ * ppCommentOffset is the indentation within a C-style comment,
+ when it cannot be picked up.
+*/
+
+static int ppHardwareTabSize = 8;
+static int ppIndentSize = 4;
+static int ppContinuationIndentSize = 8;
+
+static const int ppCommentOffset = 2;
+
+void setTabSize( int size )
+{
+ ppHardwareTabSize = size;
+}
+
+void setIndentSize( int size )
+{
+ ppIndentSize = size;
+ ppContinuationIndentSize = 2 * size;
+}
+
+static QRegExp *literal = 0;
+static QRegExp *label = 0;
+static QRegExp *inlineCComment = 0;
+static QRegExp *braceX = 0;
+static QRegExp *iflikeKeyword = 0;
+
+/*
+ Returns the first non-space character in the string t, or
+ QChar::Null if the string is made only of white space.
+*/
+static QChar firstNonWhiteSpace( const QString& t )
+{
+ int i = 0;
+ while ( i < (int) t.length() ) {
+ if ( !t[i].isSpace() )
+ return t[i];
+ i++;
+ }
+ return QChar::Null;
+}
+
+/*
+ Returns true if string t is made only of white space; otherwise
+ returns false.
+*/
+static bool isOnlyWhiteSpace( const QString& t )
+{
+ return firstNonWhiteSpace( t ).isNull();
+}
+
+/*
+ Assuming string t is a line, returns the column number of a given
+ index. Column numbers and index are identical for strings that don't
+ contain '\t's.
+*/
+int columnForIndex( const QString& t, int index )
+{
+ int col = 0;
+ if ( index > (int) t.length() )
+ index = t.length();
+
+ for ( int i = 0; i < index; i++ ) {
+ if ( t[i] == QChar('\t') ) {
+ col = ( (col / ppHardwareTabSize) + 1 ) * ppHardwareTabSize;
+ } else {
+ col++;
+ }
+ }
+ return col;
+}
+
+/*
+ Returns the indentation size of string t.
+*/
+int indentOfLine( const QString& t )
+{
+ return columnForIndex( t, t.indexOf(firstNonWhiteSpace(t)) );
+}
+
+/*
+ Replaces t[k] by ch, unless t[k] is '\t'. Tab characters are better
+ left alone since they break the "index equals column" rule. No
+ provisions are taken against '\n' or '\r', which shouldn't occur in
+ t anyway.
+*/
+static inline void eraseChar( QString& t, int k, QChar ch )
+{
+ if ( t[k] != '\t' )
+ t[k] = ch;
+}
+
+/*
+ Removes some nefast constructs from a code line and returns the
+ resulting line.
+*/
+static QString trimmedCodeLine( const QString& t )
+{
+ QString trimmed = t;
+ int k;
+
+ /*
+ Replace character and string literals by X's, since they may
+ contain confusing characters (such as '{' and ';'). "Hello!" is
+ replaced by XXXXXXXX. The literals are rigourously of the same
+ length before and after; otherwise, we would break alignment of
+ continuation lines.
+ */
+ k = 0;
+ while ( (k = trimmed.indexOf(*literal, k)) != -1 ) {
+ for ( int i = 0; i < literal->matchedLength(); i++ )
+ eraseChar( trimmed, k + i, 'X' );
+ k += literal->matchedLength();
+ }
+
+ /*
+ Replace inline C-style comments by spaces. Other comments are
+ handled elsewhere.
+ */
+ k = 0;
+ while ( (k = trimmed.indexOf(*inlineCComment, k)) != -1 ) {
+ for ( int i = 0; i < inlineCComment->matchedLength(); i++ )
+ eraseChar( trimmed, k + i, ' ' );
+ k += inlineCComment->matchedLength();
+ }
+
+ /*
+ Replace goto and switch labels by whitespace, but be careful
+ with this case:
+
+ foo1: bar1;
+ bar2;
+ */
+ while ( trimmed.lastIndexOf(':') != -1 && trimmed.indexOf(*label) != -1 ) {
+ QString cap1 = label->cap( 1 );
+ int pos1 = label->pos( 1 );
+ int stop = cap1.length();
+
+ if ( pos1 + stop < (int) trimmed.length() && ppIndentSize < stop )
+ stop = ppIndentSize;
+
+ int i = 0;
+ while ( i < stop ) {
+ eraseChar( trimmed, pos1 + i, ' ' );
+ i++;
+ }
+ while ( i < (int) cap1.length() ) {
+ eraseChar( trimmed, pos1 + i, ';' );
+ i++;
+ }
+ }
+
+ /*
+ Remove C++-style comments.
+ */
+ k = trimmed.indexOf( "//" );
+ if ( k != -1 )
+ trimmed.truncate( k );
+
+ return trimmed;
+}
+
+/*
+ Returns '(' if the last parenthesis is opening, ')' if it is
+ closing, and QChar::Null if there are no parentheses in t.
+*/
+static inline QChar lastParen( const QString& t )
+{
+ int i = t.length();
+ while ( i > 0 ) {
+ i--;
+ if ( t[i] == QChar('(') || t[i] == QChar(')') )
+ return t[i];
+ }
+ return QChar::Null;
+}
+
+/*
+ Returns true if typedIn the same as okayCh or is null; otherwise
+ returns false.
+*/
+static inline bool okay( QChar typedIn, QChar okayCh )
+{
+ return typedIn == QChar::Null || typedIn == okayCh;
+}
+
+/*
+ The "linizer" is a group of functions and variables to iterate
+ through the source code of the program to indent. The program is
+ given as a list of strings, with the bottom line being the line
+ to indent. The actual program might contain extra lines, but
+ those are uninteresting and not passed over to us.
+*/
+
+struct LinizerState
+{
+ QString line;
+ int braceDepth;
+ bool leftBraceFollows;
+
+ QStringList::ConstIterator iter;
+ bool inCComment;
+ bool pendingRightBrace;
+};
+
+static QStringList *yyProgram = 0;
+static LinizerState *yyLinizerState = 0;
+
+// shorthands
+static const QString *yyLine = 0;
+static const int *yyBraceDepth = 0;
+static const bool *yyLeftBraceFollows = 0;
+
+/*
+ Saves and restores the state of the global linizer. This enables
+ backtracking.
+*/
+#define YY_SAVE() \
+ LinizerState savedState = *yyLinizerState
+#define YY_RESTORE() \
+ *yyLinizerState = savedState
+
+/*
+ Advances to the previous line in yyProgram and update yyLine
+ accordingly. yyLine is cleaned from comments and other damageable
+ constructs. Empty lines are skipped.
+*/
+static bool readLine()
+{
+ int k;
+
+ yyLinizerState->leftBraceFollows =
+ ( firstNonWhiteSpace(yyLinizerState->line) == QChar('{') );
+
+ do {
+ if ( yyLinizerState->iter == yyProgram->begin() ) {
+ yyLinizerState->line.clear();
+ return false;
+ }
+
+ --yyLinizerState->iter;
+ yyLinizerState->line = *yyLinizerState->iter;
+
+ yyLinizerState->line = trimmedCodeLine( yyLinizerState->line );
+
+ /*
+ Remove C-style comments that span multiple lines. If the
+ bottom line starts in a C-style comment, we are not aware
+ of that and eventually yyLine will contain a slash-aster.
+
+ Notice that both if's can be executed, since
+ yyLinizerState->inCComment is potentially set to false in
+ the first if. The order of the if's is also important.
+ */
+
+ if ( yyLinizerState->inCComment ) {
+ QString slashAster( "/*" );
+
+ k = yyLinizerState->line.indexOf( slashAster );
+ if ( k == -1 ) {
+ yyLinizerState->line.clear();
+ } else {
+ yyLinizerState->line.truncate( k );
+ yyLinizerState->inCComment = false;
+ }
+ }
+
+ if ( !yyLinizerState->inCComment ) {
+ QString asterSlash( "*/" );
+
+ k = yyLinizerState->line.indexOf( asterSlash );
+ if ( k != -1 ) {
+ for ( int i = 0; i < k + 2; i++ )
+ eraseChar( yyLinizerState->line, i, ' ' );
+ yyLinizerState->inCComment = true;
+ }
+ }
+
+ /*
+ Remove preprocessor directives.
+ */
+ k = 0;
+ while ( k < (int) yyLinizerState->line.length() ) {
+ QChar ch = yyLinizerState->line[k];
+ if ( ch == QChar('#') ) {
+ yyLinizerState->line.clear();
+ } else if ( !ch.isSpace() ) {
+ break;
+ }
+ k++;
+ }
+
+ /*
+ Remove trailing spaces.
+ */
+ k = yyLinizerState->line.length();
+ while ( k > 0 && yyLinizerState->line[k - 1].isSpace() )
+ k--;
+ yyLinizerState->line.truncate( k );
+
+ /*
+ '}' increment the brace depth and '{' decrements it and not
+ the other way around, as we are parsing backwards.
+ */
+ yyLinizerState->braceDepth +=
+ yyLinizerState->line.count( '}' ) -
+ yyLinizerState->line.count( '{' );
+
+ /*
+ We use a dirty trick for
+
+ } else ...
+
+ We don't count the '}' yet, so that it's more or less
+ equivalent to the friendly construct
+
+ }
+ else ...
+ */
+ if ( yyLinizerState->pendingRightBrace )
+ yyLinizerState->braceDepth++;
+ yyLinizerState->pendingRightBrace =
+ ( yyLinizerState->line.indexOf(*braceX) == 0 );
+ if ( yyLinizerState->pendingRightBrace )
+ yyLinizerState->braceDepth--;
+ } while ( yyLinizerState->line.isEmpty() );
+
+ return true;
+}
+
+/*
+ Resets the linizer to its initial state, with yyLine containing the
+ line above the bottom line of the program.
+*/
+static void startLinizer()
+{
+ yyLinizerState->braceDepth = 0;
+ yyLinizerState->inCComment = false;
+ yyLinizerState->pendingRightBrace = false;
+
+ yyLine = &yyLinizerState->line;
+ yyBraceDepth = &yyLinizerState->braceDepth;
+ yyLeftBraceFollows = &yyLinizerState->leftBraceFollows;
+
+ yyLinizerState->iter = yyProgram->end();
+ --yyLinizerState->iter;
+ yyLinizerState->line = *yyLinizerState->iter;
+ readLine();
+}
+
+/*
+ Returns true if the start of the bottom line of yyProgram (and
+ potentially the whole line) is part of a C-style comment;
+ otherwise returns false.
+*/
+static bool bottomLineStartsInCComment()
+{
+ QString slashAster( "/*" );
+ QString asterSlash( "*/" );
+
+ /*
+ We could use the linizer here, but that would slow us down
+ terribly. We are better to trim only the code lines we need.
+ */
+ QStringList::ConstIterator p = yyProgram->end();
+ --p; // skip bottom line
+
+ for ( int i = 0; i < BigRoof; i++ ) {
+ if ( p == yyProgram->begin() )
+ return false;
+ --p;
+
+ if ( (*p).indexOf(slashAster) != -1 || (*p).indexOf(asterSlash) != -1 ) {
+ QString trimmed = trimmedCodeLine( *p );
+
+ if ( trimmed.indexOf(slashAster) != -1 ) {
+ return true;
+ } else if ( trimmed.indexOf(asterSlash) != -1 ) {
+ return false;
+ }
+ }
+ }
+ return false;
+}
+
+/*
+ Returns the recommended indent for the bottom line of yyProgram
+ assuming that it starts in a C-style comment, a condition that is
+ tested elsewhere.
+
+ Essentially, we're trying to align against some text on the
+ previous line.
+*/
+static int indentWhenBottomLineStartsInCComment()
+{
+ int k = yyLine->lastIndexOf( "/*" );
+ if ( k == -1 ) {
+ /*
+ We found a normal text line in a comment. Align the
+ bottom line with the text on this line.
+ */
+ return indentOfLine( *yyLine );
+ } else {
+ /*
+ The C-style comment starts on this line. If there is
+ text on the same line, align with it. Otherwise, align
+ with the slash-aster plus a given offset.
+ */
+ int indent = columnForIndex( *yyLine, k );
+ k += 2;
+ while ( k < (int) yyLine->length() ) {
+ if ( !(*yyLine)[k].isSpace() )
+ return columnForIndex( *yyLine, k );
+ k++;
+ }
+ return indent + ppCommentOffset;
+ }
+}
+
+/*
+ A function called match...() modifies the linizer state. If it
+ returns true, yyLine is the top line of the matched construct;
+ otherwise, the linizer is left in an unknown state.
+
+ A function called is...() keeps the linizer state intact.
+*/
+
+/*
+ Returns true if the current line (and upwards) forms a braceless
+ control statement; otherwise returns false.
+
+ The first line of the following example is a "braceless control
+ statement":
+
+ if ( x )
+ y;
+*/
+static bool matchBracelessControlStatement()
+{
+ int delimDepth = 0;
+
+ if ( yyLine->endsWith("else") )
+ return true;
+
+ if ( !yyLine->endsWith(")") )
+ return false;
+
+ for ( int i = 0; i < SmallRoof; i++ ) {
+ int j = yyLine->length();
+ while ( j > 0 ) {
+ j--;
+ QChar ch = (*yyLine)[j];
+
+ switch ( ch.unicode() ) {
+ case ')':
+ delimDepth++;
+ break;
+ case '(':
+ delimDepth--;
+ if ( delimDepth == 0 ) {
+ if ( yyLine->indexOf(*iflikeKeyword) != -1 ) {
+ /*
+ We have
+
+ if ( x )
+ y
+
+ "if ( x )" is not part of the statement
+ "y".
+ */
+ return true;
+ }
+ }
+ if ( delimDepth == -1 ) {
+ /*
+ We have
+
+ if ( (1 +
+ 2)
+
+ and not
+
+ if ( 1 +
+ 2 )
+ */
+ return false;
+ }
+ break;
+ case '{':
+ case '}':
+ case ';':
+ /*
+ We met a statement separator, but not where we
+ expected it. What follows is probably a weird
+ continuation line. Be careful with ';' in for,
+ though.
+ */
+ if ( ch != QChar(';') || delimDepth == 0 )
+ return false;
+ }
+ }
+
+ if ( !readLine() )
+ break;
+ }
+ return false;
+}
+
+/*
+ Returns true if yyLine is an unfinished line; otherwise returns
+ false.
+
+ In many places we'll use the terms "standalone line", "unfinished
+ line" and "continuation line". The meaning of these should be
+ evident from this code example:
+
+ a = b; // standalone line
+ c = d + // unfinished line
+ e + // unfinished continuation line
+ f + // unfinished continuation line
+ g; // continuation line
+*/
+static bool isUnfinishedLine()
+{
+ bool unf = false;
+
+ YY_SAVE();
+
+ if ( yyLine->isEmpty() )
+ return false;
+
+ QChar lastCh = (*yyLine)[(int) yyLine->length() - 1];
+ if ( QString("{};").indexOf(lastCh) == -1 && !yyLine->endsWith("...") ) {
+ /*
+ It doesn't end with ';' or similar. If it's neither
+ "Q_OBJECT" nor "if ( x )", it must be an unfinished line.
+ */
+ unf = ( yyLine->indexOf("Q_OBJECT") == -1 &&
+ !matchBracelessControlStatement() );
+ } else if ( lastCh == QChar(';') ) {
+ if ( lastParen(*yyLine) == QChar('(') ) {
+ /*
+ Exception:
+
+ for ( int i = 1; i < 10;
+ */
+ unf = true;
+ } else if ( readLine() && yyLine->endsWith(";") &&
+ lastParen(*yyLine) == QChar('(') ) {
+ /*
+ Exception:
+
+ for ( int i = 1;
+ i < 10;
+ */
+ unf = true;
+ }
+ }
+
+ YY_RESTORE();
+ return unf;
+}
+
+/*
+ Returns true if yyLine is a continuation line; otherwise returns
+ false.
+*/
+static bool isContinuationLine()
+{
+ bool cont = false;
+
+ YY_SAVE();
+ if ( readLine() )
+ cont = isUnfinishedLine();
+ YY_RESTORE();
+ return cont;
+}
+
+/*
+ Returns the recommended indent for the bottom line of yyProgram,
+ assuming it's a continuation line.
+
+ We're trying to align the continuation line against some parenthesis
+ or other bracked left opened on a previous line, or some interesting
+ operator such as '='.
+*/
+static int indentForContinuationLine()
+{
+ int braceDepth = 0;
+ int delimDepth = 0;
+
+ bool leftBraceFollowed = *yyLeftBraceFollows;
+
+ for ( int i = 0; i < SmallRoof; i++ ) {
+ int hook = -1;
+
+ int j = yyLine->length();
+ while ( j > 0 && hook < 0 ) {
+ j--;
+ QChar ch = (*yyLine)[j];
+
+ switch ( ch.unicode() ) {
+ case ')':
+ case ']':
+ delimDepth++;
+ break;
+ case '}':
+ braceDepth++;
+ break;
+ case '(':
+ case '[':
+ delimDepth--;
+ /*
+ An unclosed delimiter is a good place to align at,
+ at least for some styles (including Trolltech's).
+ */
+ if ( delimDepth == -1 )
+ hook = j;
+ break;
+ case '{':
+ braceDepth--;
+ /*
+ A left brace followed by other stuff on the same
+ line is typically for an enum or an initializer.
+ Such a brace must be treated just like the other
+ delimiters.
+ */
+ if ( braceDepth == -1 ) {
+ if ( j < (int) yyLine->length() - 1 ) {
+ hook = j;
+ } else {
+ return 0; // shouldn't happen
+ }
+ }
+ break;
+ case '=':
+ /*
+ An equal sign is a very natural alignment hook
+ because it's usually the operator with the lowest
+ precedence in statements it appears in. Case in
+ point:
+
+ int x = 1 +
+ 2;
+
+ However, we have to beware of constructs such as
+ default arguments and explicit enum constant
+ values:
+
+ void foo( int x = 0,
+ int y = 0 );
+
+ And not
+
+ void foo( int x = 0,
+ int y = 0 );
+
+ These constructs are caracterized by a ',' at the
+ end of the unfinished lines or by unbalanced
+ parentheses.
+ */
+ if ( QString("!=<>").indexOf((*yyLine)[j - 1]) == -1 &&
+ (*yyLine)[j + 1] != '=' ) {
+ if ( braceDepth == 0 && delimDepth == 0 &&
+ j < (int) yyLine->length() - 1 &&
+ !yyLine->endsWith(",") &&
+ (yyLine->contains('(') == yyLine->contains(')')) )
+ hook = j;
+ }
+ }
+ }
+
+ if ( hook >= 0 ) {
+ /*
+ Yes, we have a delimiter or an operator to align
+ against! We don't really align against it, but rather
+ against the following token, if any. In this example,
+ the following token is "11":
+
+ int x = ( 11 +
+ 2 );
+
+ If there is no such token, we use a continuation indent:
+
+ static QRegExp foo( QString(
+ "foo foo foo foo foo foo foo foo foo") );
+ */
+ hook++;
+ while ( hook < (int) yyLine->length() ) {
+ if ( !(*yyLine)[hook].isSpace() )
+ return columnForIndex( *yyLine, hook );
+ hook++;
+ }
+ return indentOfLine( *yyLine ) + ppContinuationIndentSize;
+ }
+
+ if ( braceDepth != 0 )
+ break;
+
+ /*
+ The line's delimiters are balanced. It looks like a
+ continuation line or something.
+ */
+ if ( delimDepth == 0 ) {
+ if ( leftBraceFollowed ) {
+ /*
+ We have
+
+ int main()
+ {
+
+ or
+
+ Bar::Bar()
+ : Foo( x )
+ {
+
+ The "{" should be flush left.
+ */
+ if ( !isContinuationLine() )
+ return indentOfLine( *yyLine );
+ } else if ( isContinuationLine() || yyLine->endsWith(",") ) {
+ /*
+ We have
+
+ x = a +
+ b +
+ c;
+
+ or
+
+ int t[] = {
+ 1, 2, 3,
+ 4, 5, 6
+
+ The "c;" should fall right under the "b +", and the
+ "4, 5, 6" right under the "1, 2, 3,".
+ */
+ return indentOfLine( *yyLine );
+ } else {
+ /*
+ We have
+
+ stream << 1 +
+ 2;
+
+ We could, but we don't, try to analyze which
+ operator has precedence over which and so on, to
+ obtain the excellent result
+
+ stream << 1 +
+ 2;
+
+ We do have a special trick above for the assignment
+ operator above, though.
+ */
+ return indentOfLine( *yyLine ) + ppContinuationIndentSize;
+ }
+ }
+
+ if ( !readLine() )
+ break;
+ }
+ return 0;
+}
+
+/*
+ Returns the recommended indent for the bottom line of yyProgram if
+ that line is standalone (or should be indented likewise).
+
+ Indenting a standalone line is tricky, mostly because of braceless
+ control statements. Grossly, we are looking backwards for a special
+ line, a "hook line", that we can use as a starting point to indent,
+ and then modify the indentation level according to the braces met
+ along the way to that hook.
+
+ Let's consider a few examples. In all cases, we want to indent the
+ bottom line.
+
+ Example 1:
+
+ x = 1;
+ y = 2;
+
+ The hook line is "x = 1;". We met 0 opening braces and 0 closing
+ braces. Therefore, "y = 2;" inherits the indent of "x = 1;".
+
+ Example 2:
+
+ if ( x ) {
+ y;
+
+ The hook line is "if ( x ) {". No matter what precedes it, "y;" has
+ to be indented one level deeper than the hook line, since we met one
+ opening brace along the way.
+
+ Example 3:
+
+ if ( a )
+ while ( b ) {
+ c;
+ }
+ d;
+
+ To indent "d;" correctly, we have to go as far as the "if ( a )".
+ Compare with
+
+ if ( a ) {
+ while ( b ) {
+ c;
+ }
+ d;
+
+ Still, we're striving to go back as little as possible to
+ accommodate people with irregular indentation schemes. A hook line
+ near at hand is much more reliable than a remote one.
+*/
+static int indentForStandaloneLine()
+{
+ for ( int i = 0; i < SmallRoof; i++ ) {
+ if ( !*yyLeftBraceFollows ) {
+ YY_SAVE();
+
+ if ( matchBracelessControlStatement() ) {
+ /*
+ The situation is this, and we want to indent "z;":
+
+ if ( x &&
+ y )
+ z;
+
+ yyLine is "if ( x &&".
+ */
+ return indentOfLine( *yyLine ) + ppIndentSize;
+ }
+ YY_RESTORE();
+ }
+
+ if ( yyLine->endsWith(";") || yyLine->contains('{') ) {
+ /*
+ The situation is possibly this, and we want to indent
+ "z;":
+
+ while ( x )
+ y;
+ z;
+
+ We return the indent of "while ( x )". In place of "y;",
+ any arbitrarily complex compound statement can appear.
+ */
+
+ if ( *yyBraceDepth > 0 ) {
+ do {
+ if ( !readLine() )
+ break;
+ } while ( *yyBraceDepth > 0 );
+ }
+
+ LinizerState hookState;
+
+ while ( isContinuationLine() )
+ readLine();
+ hookState = *yyLinizerState;
+
+ readLine();
+ if ( *yyBraceDepth <= 0 ) {
+ do {
+ if ( !matchBracelessControlStatement() )
+ break;
+ hookState = *yyLinizerState;
+ } while ( readLine() );
+ }
+
+ *yyLinizerState = hookState;
+
+ while ( isContinuationLine() )
+ readLine();
+
+ /*
+ Never trust lines containing only '{' or '}', as some
+ people (Richard M. Stallman) format them weirdly.
+ */
+ if ( yyLine->trimmed().length() > 1 )
+ return indentOfLine( *yyLine ) - *yyBraceDepth * ppIndentSize;
+ }
+
+ if ( !readLine() )
+ return -*yyBraceDepth * ppIndentSize;
+ }
+ return 0;
+}
+
+/*
+ Constructs global variables used by the indenter.
+*/
+static void initializeIndenter()
+{
+ literal = new QRegExp( "([\"'])(?:\\\\.|[^\\\\])*\\1" );
+ literal->setMinimal( true );
+ label = new QRegExp(
+ "^\\s*((?:case\\b([^:]|::)+|[a-zA-Z_0-9]+)(?:\\s+slots)?:)(?!:)" );
+ inlineCComment = new QRegExp( "/\\*.*\\*/" );
+ inlineCComment->setMinimal( true );
+ braceX = new QRegExp( "^\\s*\\}\\s*(?:else|catch)\\b" );
+ iflikeKeyword = new QRegExp( "\\b(?:catch|do|for|if|while)\\b" );
+
+ yyLinizerState = new LinizerState;
+}
+
+/*
+ Destroys global variables used by the indenter.
+*/
+static void terminateIndenter()
+{
+ delete literal;
+ delete label;
+ delete inlineCComment;
+ delete braceX;
+ delete iflikeKeyword;
+ delete yyLinizerState;
+}
+
+/*
+ Returns the recommended indent for the bottom line of program.
+ Unless null, typedIn stores the character of yyProgram that
+ triggered reindentation.
+
+ This function works better if typedIn is set properly; it is
+ slightly more conservative if typedIn is completely wild, and
+ slighly more liberal if typedIn is always null. The user might be
+ annoyed by the liberal behavior.
+*/
+int indentForBottomLine( const QStringList& program, QChar typedIn )
+{
+ if ( program.isEmpty() )
+ return 0;
+
+ initializeIndenter();
+
+ yyProgram = new QStringList( program );
+ startLinizer();
+
+ const QString& bottomLine = program.last();
+ QChar firstCh = firstNonWhiteSpace( bottomLine );
+ int indent;
+
+ if ( bottomLineStartsInCComment() ) {
+ /*
+ The bottom line starts in a C-style comment. Indent it
+ smartly, unless the user has already played around with it,
+ in which case it's better to leave her stuff alone.
+ */
+ if ( isOnlyWhiteSpace(bottomLine) ) {
+ indent = indentWhenBottomLineStartsInCComment();
+ } else {
+ indent = indentOfLine( bottomLine );
+ }
+ } else if ( okay(typedIn, '#') && firstCh == QChar('#') ) {
+ /*
+ Preprocessor directives go flush left.
+ */
+ indent = 0;
+ } else {
+ if ( isUnfinishedLine() ) {
+ indent = indentForContinuationLine();
+ } else {
+ indent = indentForStandaloneLine();
+ }
+
+ if ( okay(typedIn, '}') && firstCh == QChar('}') ) {
+ /*
+ A closing brace is one level more to the left than the
+ code it follows.
+ */
+ indent -= ppIndentSize;
+ } else if ( okay(typedIn, ':') ) {
+ QRegExp caseLabel(
+ "\\s*(?:case\\b(?:[^:]|::)+"
+ "|(?:public|protected|private|signals|default)(?:\\s+slots)?\\s*"
+ ")?:.*" );
+
+ if ( caseLabel.exactMatch(bottomLine) ) {
+ /*
+ Move a case label (or the ':' in front of a
+ constructor initialization list) one level to the
+ left, but only if the user did not play around with
+ it yet. Some users have exotic tastes in the
+ matter, and most users probably are not patient
+ enough to wait for the final ':' to format their
+ code properly.
+
+ We don't attempt the same for goto labels, as the
+ user is probably the middle of "foo::bar". (Who
+ uses goto, anyway?)
+ */
+ if ( indentOfLine(bottomLine) <= indent )
+ indent -= ppIndentSize;
+ else
+ indent = indentOfLine( bottomLine );
+ }
+ }
+ }
+ delete yyProgram;
+ terminateIndenter();
+ return qMax( 0, indent );
+}
+
+QT_END_NAMESPACE
+
+#ifdef Q_TEST_YYINDENT
+/*
+ Test driver.
+*/
+
+#include <qfile.h>
+#include <qtextstream.h>
+
+#include <errno.h>
+
+QT_BEGIN_NAMESPACE
+
+static QString fileContents( const QString& fileName )
+{
+ QFile f( fileName );
+ if ( !f.open(QFile::ReadOnly) ) {
+ qWarning( "yyindent error: Cannot open file '%s' for reading: %s",
+ fileName.toLatin1().data(), strerror(errno) );
+ return QString();
+ }
+
+ QTextStream t( &f );
+ QString contents = t.read();
+ f.close();
+ if ( contents.isEmpty() )
+ qWarning( "yyindent error: File '%s' is empty", fileName.toLatin1().data() );
+ return contents;
+}
+
+QT_END_NAMESPACE
+
+int main( int argc, char **argv )
+{
+ QT_USE_NAMESPACE
+
+ if ( argc != 2 ) {
+ qWarning( "usage: yyindent file.cpp" );
+ return 1;
+ }
+
+ QString code = fileContents( argv[1] );
+ QStringList program = QStringList::split( '\n', code, true );
+ QStringList p;
+ QString out;
+
+ while ( !program.isEmpty() && program.last().trimmed().isEmpty() )
+ program.remove( program.fromLast() );
+
+ QStringList::ConstIterator line = program.begin();
+ while ( line != program.end() ) {
+ p.push_back( *line );
+ QChar typedIn = firstNonWhiteSpace( *line );
+ if ( p.last().endsWith(":") )
+ typedIn = ':';
+
+ int indent = indentForBottomLine( p, typedIn );
+
+ if ( !(*line).trimmed().isEmpty() ) {
+ for ( int j = 0; j < indent; j++ )
+ out += " ";
+ out += (*line).trimmed();
+ }
+ out += "\n";
+ ++line;
+ }
+
+ while ( out.endsWith("\n") )
+ out.truncate( out.length() - 1 );
+
+ printf( "%s\n", out.toLatin1().data() );
+ return 0;
+}
+
+#endif // Q_TEST_YYINDENT