summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorOswald Buddenhagen <oswald.buddenhagen@theqtcompany.com>2015-10-21 15:51:54 +0200
committerOswald Buddenhagen <oswald.buddenhagen@theqtcompany.com>2015-10-23 10:47:25 +0000
commite15ca59111fe05f5ecc0f7fb48c34b5b22115a12 (patch)
tree1f66c3191f4745dea9bf126b8c22a4d8c5a90c58 /src
parent4e2d1893a89ce3dcfaacea091acae1f673c14a9e (diff)
downloadqttools-e15ca59111fe05f5ecc0f7fb48c34b5b22115a12.tar.gz
move qdoc back to qttools
we can do that now, as the bootstrap lib is now a properly exported module, and qmldevtools is now bootstrapped as well. this removes the abomination of a copy of the qml parser in qtbase. unfortunately qtbase/2422251ee5025a067b14b989153764ab36e43f10 is reverted, as qtdeclarative is still missing the respective change. this introduces no regression in discoverability or usability, as a full doc build already needed qttools - for qhelpgenerator. Change-Id: Ic9c4c9732ddf5998637b9e42e27939ba50b31479 Reviewed-by: Jędrzej Nowacki <jedrzej.nowacki@theqtcompany.com> Reviewed-by: Martin Smith <martin.smith@digia.com> Reviewed-by: Lars Knoll <lars.knoll@theqtcompany.com> Reviewed-by: Topi Reiniö <topi.reinio@digia.com>
Diffstat (limited to 'src')
-rw-r--r--src/qdoc/TODO.txt87
-rw-r--r--src/qdoc/atom.cpp460
-rw-r--r--src/qdoc/atom.h257
-rw-r--r--src/qdoc/codechunk.cpp142
-rw-r--r--src/qdoc/codechunk.h116
-rw-r--r--src/qdoc/codemarker.cpp668
-rw-r--r--src/qdoc/codemarker.h187
-rw-r--r--src/qdoc/codeparser.cpp437
-rw-r--r--src/qdoc/codeparser.h97
-rw-r--r--src/qdoc/config.cpp1221
-rw-r--r--src/qdoc/config.h319
-rw-r--r--src/qdoc/cppcodemarker.cpp1326
-rw-r--r--src/qdoc/cppcodemarker.h85
-rw-r--r--src/qdoc/cppcodeparser.cpp2611
-rw-r--r--src/qdoc/cppcodeparser.h255
-rw-r--r--src/qdoc/doc.cpp3380
-rw-r--r--src/qdoc/doc.h196
-rw-r--r--src/qdoc/doc/config/qdoc.qdocconf72
-rw-r--r--src/qdoc/doc/corefeatures.qdoc35
-rw-r--r--src/qdoc/doc/examples/componentset/ProgressBar.qml135
-rw-r--r--src/qdoc/doc/examples/componentset/Switch.qml142
-rw-r--r--src/qdoc/doc/examples/componentset/TabWidget.qml183
-rw-r--r--src/qdoc/doc/examples/componentset/componentset.pro5
-rw-r--r--src/qdoc/doc/examples/componentset/uicomponents.qdoc.sample38
-rw-r--r--src/qdoc/doc/examples/cpp.qdoc.sample126
-rw-r--r--src/qdoc/doc/examples/examples.qdoc97
-rw-r--r--src/qdoc/doc/examples/layoutmanagement.qdocinc13
-rw-r--r--src/qdoc/doc/examples/main.cpp46
-rw-r--r--src/qdoc/doc/examples/mainwindow.cpp243
-rw-r--r--src/qdoc/doc/examples/minimum.qdocconf38
-rw-r--r--src/qdoc/doc/examples/objectmodel.qdocinc11
-rw-r--r--src/qdoc/doc/examples/qml.qdoc.sample116
-rw-r--r--src/qdoc/doc/examples/samples.qdocinc109
-rw-r--r--src/qdoc/doc/examples/signalandslots.qdocinc9
-rw-r--r--src/qdoc/doc/files/basicqt.qdoc.sample67
-rw-r--r--src/qdoc/doc/files/compat.qdocconf12
-rw-r--r--src/qdoc/doc/files/qtgui.qdocconf49
-rw-r--r--src/qdoc/doc/images/happy.gifbin0 -> 11526 bytes
-rw-r--r--src/qdoc/doc/images/happyguy.jpgbin0 -> 53442 bytes
-rw-r--r--src/qdoc/doc/images/link-to-qquickitem.pngbin0 -> 46571 bytes
-rw-r--r--src/qdoc/doc/images/links-to-broken-links.pngbin0 -> 16569 bytes
-rw-r--r--src/qdoc/doc/images/links-to-links.pngbin0 -> 10042 bytes
-rw-r--r--src/qdoc/doc/images/qa-table.pngbin0 -> 7057 bytes
-rw-r--r--src/qdoc/doc/images/qt-logo.pngbin0 -> 1495 bytes
-rw-r--r--src/qdoc/doc/images/training.jpgbin0 -> 8368 bytes
-rw-r--r--src/qdoc/doc/qa-pages.qdoc109
-rw-r--r--src/qdoc/doc/qdoc-guide/qdoc-guide.qdoc632
-rw-r--r--src/qdoc/doc/qdoc-guide/qtwritingstyle-cpp.qdoc187
-rw-r--r--src/qdoc/doc/qdoc-guide/qtwritingstyle-qml.qdoc164
-rw-r--r--src/qdoc/doc/qdoc-manual-DITA.qdoc164
-rw-r--r--src/qdoc/doc/qdoc-manual-cmdindex.qdoc156
-rw-r--r--src/qdoc/doc/qdoc-manual-contextcmds.qdoc1058
-rw-r--r--src/qdoc/doc/qdoc-manual-intro.qdoc325
-rw-r--r--src/qdoc/doc/qdoc-manual-markupcmds.qdoc4081
-rw-r--r--src/qdoc/doc/qdoc-manual-qdocconf.qdoc1714
-rw-r--r--src/qdoc/doc/qdoc-manual-topiccmds.qdoc1594
-rw-r--r--src/qdoc/doc/qdoc-manual.qdoc78
-rw-r--r--src/qdoc/doc/qdoc-minimum-qdocconf.qdoc89
-rw-r--r--src/qdoc/doc/qtgui-qdocconf.qdoc299
-rw-r--r--src/qdoc/editdistance.cpp106
-rw-r--r--src/qdoc/editdistance.h51
-rw-r--r--src/qdoc/generator.cpp2171
-rw-r--r--src/qdoc/generator.h260
-rw-r--r--src/qdoc/helpprojectwriter.cpp858
-rw-r--r--src/qdoc/helpprojectwriter.h118
-rw-r--r--src/qdoc/htmlgenerator.cpp4898
-rw-r--r--src/qdoc/htmlgenerator.h292
-rw-r--r--src/qdoc/jscodemarker.cpp142
-rw-r--r--src/qdoc/jscodemarker.h69
-rw-r--r--src/qdoc/location.cpp448
-rw-r--r--src/qdoc/location.h133
-rw-r--r--src/qdoc/main.cpp794
-rw-r--r--src/qdoc/node.cpp3038
-rw-r--r--src/qdoc/node.h1167
-rw-r--r--src/qdoc/openedlist.cpp220
-rw-r--r--src/qdoc/openedlist.h86
-rw-r--r--src/qdoc/plaincodemarker.cpp119
-rw-r--r--src/qdoc/plaincodemarker.h69
-rw-r--r--src/qdoc/puredocparser.cpp225
-rw-r--r--src/qdoc/puredocparser.h69
-rw-r--r--src/qdoc/qdoc.pro96
-rw-r--r--src/qdoc/qdocdatabase.cpp1744
-rw-r--r--src/qdoc/qdocdatabase.h455
-rw-r--r--src/qdoc/qdocindexfiles.cpp1628
-rw-r--r--src/qdoc/qdocindexfiles.h86
-rw-r--r--src/qdoc/qdoctagfiles.cpp385
-rw-r--r--src/qdoc/qdoctagfiles.h68
-rw-r--r--src/qdoc/qmlcodemarker.cpp274
-rw-r--r--src/qdoc/qmlcodemarker.h79
-rw-r--r--src/qdoc/qmlcodeparser.cpp333
-rw-r--r--src/qdoc/qmlcodeparser.h84
-rw-r--r--src/qdoc/qmlmarkupvisitor.cpp844
-rw-r--r--src/qdoc/qmlmarkupvisitor.h173
-rw-r--r--src/qdoc/qmlvisitor.cpp827
-rw-r--r--src/qdoc/qmlvisitor.h121
-rw-r--r--src/qdoc/quoter.cpp374
-rw-r--r--src/qdoc/quoter.h86
-rw-r--r--src/qdoc/separator.cpp69
-rw-r--r--src/qdoc/separator.h50
-rw-r--r--src/qdoc/text.cpp300
-rw-r--r--src/qdoc/text.h99
-rw-r--r--src/qdoc/tokenizer.cpp799
-rw-r--r--src/qdoc/tokenizer.h177
-rw-r--r--src/qdoc/tree.cpp1525
-rw-r--r--src/qdoc/tree.h255
-rw-r--r--src/qdoc/yyindent.cpp1182
-rw-r--r--src/src.pro1
107 files changed, 51487 insertions, 0 deletions
diff --git a/src/qdoc/TODO.txt b/src/qdoc/TODO.txt
new file mode 100644
index 000000000..9bf4a2864
--- /dev/null
+++ b/src/qdoc/TODO.txt
@@ -0,0 +1,87 @@
+ * 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.
+ * 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 *>
+ * warning for unnamed parameters in property access functions
+ * \center...\endcenter
+
+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 \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
diff --git a/src/qdoc/atom.cpp b/src/qdoc/atom.cpp
new file mode 100644
index 000000000..f50f401c5
--- /dev/null
+++ b/src/qdoc/atom.cpp
@@ -0,0 +1,460 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL21$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** 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 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** As a special exception, The Qt Company gives you certain additional
+** rights. These rights are described in The Qt Company LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <qregexp.h>
+#include "atom.h"
+#include "location.h"
+#include "qdocdatabase.h"
+#include <stdio.h>
+#include <qdebug.h>
+
+QT_BEGIN_NAMESPACE
+
+/*! \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::AtomType
+
+ \value AnnotatedList
+ \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 DivLeft
+ \value DivRight
+ \value EndQmlText
+ \value FormatElse
+ \value FormatEndif
+ \value FormatIf
+ \value FootnoteLeft
+ \value FootnoteRight
+ \value FormattingLeft
+ \value FormattingRight
+ \value GeneratedList
+ \value Image
+ \value ImageText
+ \value ImportantNote
+ \value InlineImage
+ \value JavaScript
+ \value EndJavaScript
+ \value Keyword
+ \value LineBreak
+ \value Link
+ \value LinkNode
+ \value ListLeft
+ \value ListItemNumber
+ \value ListTagLeft
+ \value ListTagRight
+ \value ListItemLeft
+ \value ListItemRight
+ \value ListRight
+ \value NavAutoLink
+ \value NavLink
+ \value Nop
+ \value Note
+ \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 SinceList
+ \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
+*/
+
+QString Atom::noError_ = QString();
+
+static const struct {
+ const char *english;
+ int no;
+} atms[] = {
+ { "AnnotatedList", Atom::AnnotatedList },
+ { "AutoLink", Atom::AutoLink },
+ { "BaseName", Atom::BaseName },
+ { "br", Atom::BR},
+ { "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 },
+ { "DivLeft", Atom::DivLeft },
+ { "DivRight", Atom::DivRight },
+ { "EndQmlText", Atom::EndQmlText },
+ { "FootnoteLeft", Atom::FootnoteLeft },
+ { "FootnoteRight", Atom::FootnoteRight },
+ { "FormatElse", Atom::FormatElse },
+ { "FormatEndif", Atom::FormatEndif },
+ { "FormatIf", Atom::FormatIf },
+ { "FormattingLeft", Atom::FormattingLeft },
+ { "FormattingRight", Atom::FormattingRight },
+ { "GeneratedList", Atom::GeneratedList },
+ { "GuidLink", Atom::GuidLink},
+ { "hr", Atom::HR},
+ { "Image", Atom::Image },
+ { "ImageText", Atom::ImageText },
+ { "ImportantLeft", Atom::ImportantLeft },
+ { "ImportantRight", Atom::ImportantRight },
+ { "InlineImage", Atom::InlineImage },
+ { "JavaScript", Atom::JavaScript },
+ { "EndJavaScript", Atom::EndJavaScript },
+ { "Keyword", Atom::Keyword },
+ { "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 },
+ { "NavAutoLink", Atom::NavAutoLink },
+ { "NavLink", Atom::NavLink },
+ { "Nop", Atom::Nop },
+ { "NoteLeft", Atom::NoteLeft },
+ { "NoteRight", Atom::NoteRight },
+ { "ParaLeft", Atom::ParaLeft },
+ { "ParaRight", Atom::ParaRight },
+ { "Qml", Atom::Qml},
+ { "QmlText", Atom::QmlText },
+ { "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 },
+ { "SinceList", Atom::SinceList },
+ { "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(AtomType type, const QString& string)
+
+ Constructs an atom of the specified \a type with the single
+ parameter \a string and does not put the new atom in a list.
+*/
+
+/*! \fn Atom::Atom(AtomType type, const QString& p1, const QString& p2)
+
+ Constructs an atom of the specified \a type with the two
+ parameters \a p1 and \a p2 and does not put the new atom
+ in a list.
+*/
+
+/*! \fn Atom(Atom *previous, AtomType type, const QString& string)
+
+ Constructs an atom of the specified \a type with the single
+ parameter \a string and inserts the new atom into the list
+ after the \a previous atom.
+*/
+
+/*! \fn Atom::Atom(Atom* previous, AtomType type, const QString& p1, const QString& p2)
+
+ Constructs an atom of the specified \a type with the two
+ parameters \a p1 and \a p2 and inserts the new atom into
+ the list after the \a previous atom.
+*/
+
+/*! \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 AtomType \a t.
+ Otherwise return 0.
+ */
+const Atom* Atom::next(AtomType t) const
+{
+ return (next_ && (next_->type() == t)) ? next_ : 0;
+}
+
+/*!
+ Return the next Atom in the list if it is of AtomType \a t
+ and its string part is \a s. Otherwise return 0.
+ */
+const Atom* Atom::next(AtomType t, const QString& s) const
+{
+ return (next_ && (next_->type() == t) && (next_->string() == s)) ? next_ : 0;
+}
+
+/*! \fn const Atom *Atom::next() const
+ Return the next atom in the atom list.
+ \also type(), string()
+*/
+
+/*! \fn AtomType 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(QCoreApplication::translate("QDoc::Atom", "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(QLatin1String("\\"), QLatin1String("\\\\"));
+ str.replace(QLatin1String("\""), QLatin1String("\\\""));
+ str.replace(QLatin1String("\n"), QLatin1String("\\n"));
+ str.replace(QRegExp(QLatin1String("[^\x20-\x7e]")), QLatin1String("?"));
+ if (!str.isEmpty())
+ str = QLatin1String(" \"") + str + QLatin1Char('"');
+ fprintf(stderr,
+ " %-15s%s\n",
+ typeString().toLatin1().data(),
+ str.toLatin1().data());
+}
+
+/*!
+ The only constructor for LinkAtom. It creates an Atom of
+ type Atom::Link. \a p1 being the link target. \a p2 is the
+ parameters in square brackets. Normally there is just one
+ word in the square brackets, but there can be up to three
+ words separated by spaces. The constructor splits \a p2 on
+ the space character.
+ */
+LinkAtom::LinkAtom(const QString& p1, const QString& p2)
+ : Atom(p1),
+ resolved_(false),
+ genus_(Node::DontCare),
+ goal_(Node::NoType),
+ domain_(0),
+ squareBracketParams_(p2)
+{
+ // nada.
+}
+
+/*!
+ This function resolves the parameters that were enclosed in
+ square brackets. If the parameters have already been resolved,
+ it does nothing and returns immediately.
+ */
+void LinkAtom::resolveSquareBracketParams()
+{
+ if (resolved_)
+ return;
+ QStringList params = squareBracketParams_.toLower().split(QLatin1Char(' '));
+ foreach (const QString& p, params) {
+ if (!domain_) {
+ domain_ = QDocDatabase::qdocDB()->findTree(p);
+ if (domain_) {
+ continue;
+ }
+ }
+ if (goal_ == Node::NoType) {
+ goal_ = Node::goal(p);
+ if (goal_ != Node::NoType)
+ continue;
+ }
+ if (p == "qml") {
+ genus_ = Node::QML;
+ continue;
+ }
+ if (p == "cpp") {
+ genus_ = Node::CPP;
+ continue;
+ }
+ if (p == "doc") {
+ genus_ = Node::DOC;
+ continue;
+ }
+ error_ = squareBracketParams_;
+ break;
+ }
+ resolved_ = true;
+}
+
+/*!
+ Standard copy constructor of LinkAtom \a t.
+ */
+LinkAtom::LinkAtom(const LinkAtom& t)
+ : Atom(Link, t.string()),
+ resolved_(t.resolved_),
+ genus_(t.genus_),
+ goal_(t.goal_),
+ domain_(t.domain_),
+ error_(t.error_),
+ squareBracketParams_(t.squareBracketParams_)
+{
+ // nothing
+}
+
+/*!
+ Special copy constructor of LinkAtom \a t, where
+ where the new LinkAtom will not be the first one
+ in the list.
+ */
+LinkAtom::LinkAtom(Atom* previous, const LinkAtom& t)
+ : Atom(previous, Link, t.string()),
+ resolved_(t.resolved_),
+ genus_(t.genus_),
+ goal_(t.goal_),
+ domain_(t.domain_),
+ error_(t.error_),
+ squareBracketParams_(t.squareBracketParams_)
+{
+ previous->next_ = this;
+}
+
+QT_END_NAMESPACE
diff --git a/src/qdoc/atom.h b/src/qdoc/atom.h
new file mode 100644
index 000000000..86b8ba7b3
--- /dev/null
+++ b/src/qdoc/atom.h
@@ -0,0 +1,257 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL21$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** 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 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** As a special exception, The Qt Company gives you certain additional
+** rights. These rights are described in The Qt Company LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef ATOM_H
+#define ATOM_H
+
+#include <qstringlist.h>
+#include "node.h"
+#include <qdebug.h>
+
+QT_BEGIN_NAMESPACE
+
+class Tree;
+class LinkAtom;
+
+class Atom
+{
+public:
+ enum AtomType {
+ AnnotatedList,
+ AutoLink,
+ BaseName,
+ BR,
+ BriefLeft,
+ BriefRight,
+ C,
+ CaptionLeft,
+ CaptionRight,
+ Code,
+ CodeBad,
+ CodeNew,
+ CodeOld,
+ CodeQuoteArgument,
+ CodeQuoteCommand,
+ DivLeft,
+ DivRight,
+ EndQmlText,
+ FootnoteLeft,
+ FootnoteRight,
+ FormatElse,
+ FormatEndif,
+ FormatIf,
+ FormattingLeft,
+ FormattingRight,
+ GeneratedList,
+ GuidLink,
+ HR,
+ Image,
+ ImageText,
+ ImportantLeft,
+ ImportantRight,
+ InlineImage,
+ JavaScript,
+ EndJavaScript,
+ Keyword,
+ LegaleseLeft,
+ LegaleseRight,
+ LineBreak,
+ Link,
+ LinkNode,
+ ListLeft,
+ ListItemNumber,
+ ListTagLeft,
+ ListTagRight,
+ ListItemLeft,
+ ListItemRight,
+ ListRight,
+ NavAutoLink,
+ NavLink,
+ Nop,
+ NoteLeft,
+ NoteRight,
+ ParaLeft,
+ ParaRight,
+ Qml,
+ QmlText,
+ QuotationLeft,
+ QuotationRight,
+ RawString,
+ SectionLeft,
+ SectionRight,
+ SectionHeadingLeft,
+ SectionHeadingRight,
+ SidebarLeft,
+ SidebarRight,
+ SinceList,
+ SnippetCommand,
+ SnippetIdentifier,
+ SnippetLocation,
+ String,
+ TableLeft,
+ TableRight,
+ TableHeaderLeft,
+ TableHeaderRight,
+ TableRowLeft,
+ TableRowRight,
+ TableItemLeft,
+ TableItemRight,
+ TableOfContents,
+ Target,
+ UnhandledFormat,
+ UnknownCommand,
+ Last = UnknownCommand
+ };
+
+ friend class LinkAtom;
+
+ Atom(const QString& string)
+ : next_(0), type_(Link)
+ {
+ strs << string;
+ }
+
+ Atom(AtomType type, const QString& string = "")
+ : next_(0), type_(type)
+ {
+ strs << string;
+ }
+
+ Atom(AtomType type, const QString& p1, const QString& p2)
+ : next_(0), type_(type)
+ {
+ strs << p1;
+ if (!p2.isEmpty())
+ strs << p2;
+ }
+
+ Atom(Atom* previous, AtomType type, const QString& string = "")
+ : next_(previous->next_), type_(type)
+ {
+ strs << string;
+ previous->next_ = this;
+ }
+
+ Atom(Atom* previous, AtomType type, const QString& p1, const QString& p2)
+ : next_(previous->next_), type_(type)
+ {
+ strs << p1;
+ if (!p2.isEmpty())
+ strs << p2;
+ previous->next_ = this;
+ }
+
+ virtual ~Atom() { }
+
+ void appendChar(QChar ch) { strs[0] += ch; }
+ void appendString(const QString& string) { strs[0] += string; }
+ void chopString() { strs[0].chop(1); }
+ void setString(const QString& string) { strs[0] = string; }
+ Atom* next() { return next_; }
+ void setNext(Atom* newNext) { next_ = newNext; }
+
+ const Atom* next() const { return next_; }
+ const Atom* next(AtomType t) const;
+ const Atom* next(AtomType t, const QString& s) const;
+ AtomType type() const { return type_; }
+ QString typeString() const;
+ const QString& string() const { return strs[0]; }
+ const QString& string(int i) const { return strs[i]; }
+ int count() const { return strs.size(); }
+ void dump() const;
+ const QStringList& strings() const { return strs; }
+
+ virtual bool isLinkAtom() const { return false; }
+ virtual Node::Genus genus() { return Node::DontCare; }
+ virtual bool specifiesDomain() { return false; }
+ virtual Tree* domain() { return 0; }
+ virtual Node::NodeType goal() { return Node::NoType; }
+ virtual const QString& error() { return noError_; }
+ virtual void resolveSquareBracketParams() { }
+
+ protected:
+ static QString noError_;
+ Atom* next_;
+ AtomType type_;
+ QStringList strs;
+};
+
+class LinkAtom : public Atom
+{
+ public:
+ LinkAtom(const QString& p1, const QString& p2);
+ LinkAtom(const LinkAtom& t);
+ LinkAtom(Atom* previous, const LinkAtom& t);
+ virtual ~LinkAtom() { }
+
+ virtual bool isLinkAtom() const Q_DECL_OVERRIDE { return true; }
+ virtual Node::Genus genus() Q_DECL_OVERRIDE { resolveSquareBracketParams(); return genus_; }
+ virtual bool specifiesDomain() Q_DECL_OVERRIDE { resolveSquareBracketParams(); return (domain_ != 0); }
+ virtual Tree* domain() Q_DECL_OVERRIDE { resolveSquareBracketParams(); return domain_; }
+ virtual Node::NodeType goal() Q_DECL_OVERRIDE { resolveSquareBracketParams(); return goal_; }
+ virtual const QString& error() Q_DECL_OVERRIDE { return error_; }
+ virtual void resolveSquareBracketParams() Q_DECL_OVERRIDE;
+
+ protected:
+ bool resolved_;
+ Node::Genus genus_;
+ Node::NodeType goal_;
+ Tree* domain_;
+ QString error_;
+ QString squareBracketParams_;
+};
+
+#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_SPAN "span "
+#define ATOM_FORMATTING_SUBSCRIPT "subscript"
+#define ATOM_FORMATTING_SUPERSCRIPT "superscript"
+#define ATOM_FORMATTING_TELETYPE "teletype"
+#define ATOM_FORMATTING_UICONTROL "uicontrol"
+#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/src/qdoc/codechunk.cpp b/src/qdoc/codechunk.cpp
new file mode 100644
index 000000000..5799e0bac
--- /dev/null
+++ b/src/qdoc/codechunk.cpp
@@ -0,0 +1,142 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL21$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** 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 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** As a special exception, The Qt Company gives you certain additional
+** rights. These rights are described in The Qt Company LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** $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/src/qdoc/codechunk.h b/src/qdoc/codechunk.h
new file mode 100644
index 000000000..259012df9
--- /dev/null
+++ b/src/qdoc/codechunk.h
@@ -0,0 +1,116 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL21$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** 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 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** As a special exception, The Qt Company gives you certain additional
+** rights. These rights are described in The Qt Company LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** $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 conversion 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(); }
+ void clear() { s.clear(); }
+ 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/src/qdoc/codemarker.cpp b/src/qdoc/codemarker.cpp
new file mode 100644
index 000000000..a668205a6
--- /dev/null
+++ b/src/qdoc/codemarker.cpp
@@ -0,0 +1,668 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL21$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** 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 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** As a special exception, The Qt Company gives you certain additional
+** rights. These rights are described in The Qt Company LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <qobjectdefs.h>
+#include "codemarker.h"
+#include "config.h"
+#include "node.h"
+#include <qdebug.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()
+{
+ markers.prepend(this);
+}
+
+/*!
+ When a code marker destroys itself, it removes itself from
+ the static list of code markers.
+ */
+CodeMarker::~CodeMarker()
+{
+ markers.removeAll(this);
+}
+
+/*!
+ A code market performs no initialization by default. Marker-specific
+ initialization is performed in subclasses.
+ */
+void CodeMarker::initializeMarker(const Config& ) // config
+{
+}
+
+/*!
+ 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(CONFIG_LANGUAGE);
+ QList<CodeMarker *>::ConstIterator m = markers.constBegin();
+ while (m != markers.constEnd()) {
+ (*m)->initializeMarker(config);
+ ++m;
+ }
+}
+
+/*!
+ All the code markers in the static list are terminated here.
+ */
+void CodeMarker::terminate()
+{
+ QList<CodeMarker *>::ConstIterator m = markers.constBegin();
+ while (m != markers.constEnd()) {
+ (*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.constBegin();
+ while (m != markers.constEnd()) {
+ 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.constBegin();
+ while (m != markers.constEnd()) {
+ if ((*m)->recognizeExtension(ext))
+ return *m;
+ ++m;
+ }
+ --dot;
+ }
+ return defaultMarker;
+}
+
+CodeMarker *CodeMarker::markerForLanguage(const QString& lang)
+{
+ QList<CodeMarker *>::ConstIterator m = markers.constBegin();
+ while (m != markers.constEnd()) {
+ 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<quintptr>(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;
+}
+
+void CodeMarker::appendProtectedString(QString *output, const QStringRef &str)
+{
+ int n = str.length();
+ output->reserve(output->size() + n * 2 + 30);
+ const QChar *data = str.constData();
+ for (int i = 0; i != n; ++i) {
+ switch (data[i].unicode()) {
+ case '&': *output += samp; break;
+ case '<': *output += slt; break;
+ case '>': *output += sgt; break;
+ case '"': *output += squot; break;
+ default : *output += data[i];
+ }
+ }
+}
+
+QString CodeMarker::typified(const QString &string, bool trailingSpace)
+{
+ 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;
+ }
+ }
+ }
+ if (trailingSpace && string.size()) {
+ if (!string.endsWith(QLatin1Char('*'))
+ && !string.endsWith(QLatin1Char('&')))
+ result += QLatin1Char(' ');
+ }
+ return result;
+}
+
+QString CodeMarker::taggedNode(const Node* node)
+{
+ QString tag;
+ QString name = node->name();
+
+ 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;
+ case Node::QmlType:
+ /*
+ Remove the "QML:" prefix, if present.
+ There shouldn't be any of these "QML:"
+ prefixes in the documentation sources
+ after the switch to using QML module
+ qualifiers, but this code is kept to
+ be backward compatible.
+ */
+ if (node->name().startsWith(QLatin1String("QML:")))
+ name = name.mid(4);
+ tag = QLatin1String("@property");
+ break;
+ case Node::Document:
+ tag = QLatin1String("@property");
+ break;
+ case Node::QmlMethod:
+ case Node::QmlSignal:
+ case Node::QmlSignalHandler:
+ tag = QLatin1String("@function");
+ break;
+ default:
+ tag = QLatin1String("@unknown");
+ break;
+ }
+ return (QLatin1Char('<') + tag + QLatin1Char('>') + protect(name)
+ + QLatin1String("</") + tag + QLatin1Char('>'));
+}
+
+QString CodeMarker::taggedQmlNode(const Node* node)
+{
+ QString tag;
+ switch (node->type()) {
+ case Node::QmlProperty:
+ tag = QLatin1String("@property");
+ break;
+ case Node::QmlSignal:
+ tag = QLatin1String("@signal");
+ break;
+ case Node::QmlSignalHandler:
+ tag = QLatin1String("@signalhandler");
+ break;
+ case Node::QmlMethod:
+ tag = QLatin1String("@method");
+ break;
+ default:
+ tag = QLatin1String("@unknown");
+ break;
+ }
+ 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, const QString* name)
+{
+ QString nodeName;
+ if (name != 0)
+ nodeName = *name;
+ else
+ 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, QLatin1Char('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;
+
+ if ((node->type() == Node::QmlMethod) ||
+ (node->type() == Node::QmlSignal) ||
+ (node->type() == Node::QmlSignalHandler)) {
+ //const FunctionNode* func = static_cast<const FunctionNode *>(node);
+ //return QLatin1Char('E') + func->name();
+ return QLatin1Char('E') + nodeName;
+ }
+
+ return QLatin1Char('B') + nodeName;
+}
+
+void CodeMarker::insert(FastSection &fastSection,
+ Node *node,
+ SynopsisStyle style,
+ Status status)
+{
+ bool irrelevant = false;
+ bool inheritedMember = false;
+ if (!node->relates()) {
+ Aggregate* p = node->parent();
+ if (p->isQmlPropertyGroup())
+ p = p->parent();
+ if (p != fastSection.parent_) {
+ if ((!p->isQmlType() && !p->isJsType()) || !p->isAbstract())
+ inheritedMember = true;
+ }
+ }
+
+ 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 != Subpage);
+ 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 == Subpage) {
+ QString key = sortName(node);
+ fastSection.memberMap.insertMulti(key, node);
+ }
+ else {
+ if (node->parent()->isClass() || node->parent()->isNamespace()) {
+ if (fastSection.inherited.isEmpty()
+ || fastSection.inherited.last().first != node->parent()) {
+ QPair<Aggregate *, int> p(node->parent(), 0);
+ fastSection.inherited.append(p);
+ }
+ fastSection.inherited.last().second++;
+ }
+ }
+ }
+}
+
+/*!
+ Returns \c true if \a node represents a reimplemented member
+ function in the class of the FastSection \a fs. If it is
+ a reimplemented function, then it is inserted into the
+ reimplemented member map in \a fs. The test is performed
+ only if \a status is \e OK. True is returned if \a node
+ is inserted into the map. Otherwise, false is returned.
+ */
+bool CodeMarker::insertReimpFunc(FastSection& fs, Node* node, Status status)
+{
+ if ((node->access() != Node::Private) && (node->relates() == 0)) {
+ const FunctionNode* fn = static_cast<const FunctionNode*>(node);
+ if ((fn->reimplementedFrom() != 0) && (status == Okay)) {
+ if (fn->parent() == fs.parent_) {
+ QString key = sortName(fn);
+ if (!fs.reimpMemberMap.contains(key)) {
+ fs.reimpMemberMap.insert(key,node);
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+}
+
+/*!
+ If \a fs is not empty, convert it to a Section and append
+ the new Section to \a sectionList.
+ */
+void CodeMarker::append(QList<Section>& sectionList, const FastSection& fs, bool includeKeys)
+{
+ if (!fs.isEmpty()) {
+ if (fs.classMapList_.isEmpty()) {
+ Section section(fs.name,fs.divClass,fs.singularMember,fs.pluralMember);
+ if (includeKeys) {
+ section.keys = fs.memberMap.keys();
+ }
+ section.members = fs.memberMap.values();
+ section.reimpMembers = fs.reimpMemberMap.values();
+ section.inherited = fs.inherited;
+ sectionList.append(section);
+ }
+ else {
+ Section section(fs.name,fs.divClass,fs.singularMember,fs.pluralMember);
+ sectionList.append(section);
+ Section* s = &sectionList[sectionList.size()-1];
+ for (int i=0; i<fs.classMapList_.size(); i++) {
+ ClassMap* classMap = fs.classMapList_[i];
+ ClassKeysNodes* ckn = new ClassKeysNodes;
+ ckn->first = classMap->first;
+ ckn->second.second = classMap->second.values();
+ ckn->second.first = classMap->second.keys();
+ s->classKeysNodesList_.append(ckn);
+ }
+ }
+ }
+}
+
+/*!
+ The destructor must delete each member of the
+ list of QML class lists, if it is not empty;
+ */
+Section::~Section()
+{
+ if (!classKeysNodesList_.isEmpty()) {
+ for (int i=0; i<classKeysNodesList_.size(); i++) {
+ ClassKeysNodes* classKeysNodes = classKeysNodesList_[i];
+ classKeysNodesList_[i] = 0;
+ delete classKeysNodes;
+ }
+ }
+}
+
+/*!
+ The destructor must delete the QML class maps in the class
+ map list, if the class map list is not empty.
+ */
+FastSection::~FastSection()
+{
+ if (!classMapList_.isEmpty()) {
+ for (int i=0; i<classMapList_.size(); i++) {
+ ClassMap* classMap = classMapList_[i];
+ classMapList_[i] = 0;
+ delete classMap;
+ }
+ }
+}
+
+static QString encode(const QString &string)
+{
+ return string;
+}
+
+QStringList CodeMarker::macRefsForNode(Node *node)
+{
+ QString result = QLatin1String("cpp/");
+ switch (node->type()) {
+ case Node::Class:
+ {
+ const ClassNode *classe = static_cast<const ClassNode *>(node);
+ {
+ 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;
+ Q_UNUSED(isMacro)
+ 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/");
+ }
+ 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);
+ }
+ break;
+ case Node::Variable:
+ result += QLatin1String("data/") + macName(node);
+ break;
+ case Node::Property:
+ {
+ NodeList list = static_cast<const PropertyNode*>(node)->functions();
+ QStringList stringList;
+ foreach (Node* node, list) {
+ stringList += macRefsForNode(node);
+ }
+ return stringList;
+ }
+ case Node::Namespace:
+ case Node::Document:
+ case Node::QmlType:
+ 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('/') + protect(myName);
+ }
+ else {
+ return node->plainFullName() + QLatin1Char('/') + protect(myName);
+ }
+}
+
+/*!
+ Returns an empty list of documentation sections.
+ */
+QList<Section> CodeMarker::qmlSections(QmlTypeNode* , SynopsisStyle , Status )
+{
+ return QList<Section>();
+}
+
+QT_END_NAMESPACE
diff --git a/src/qdoc/codemarker.h b/src/qdoc/codemarker.h
new file mode 100644
index 000000000..011c8623d
--- /dev/null
+++ b/src/qdoc/codemarker.h
@@ -0,0 +1,187 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL21$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** 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 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** As a special exception, The Qt Company gives you certain additional
+** rights. These rights are described in The Qt Company LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*
+ codemarker.h
+*/
+
+#ifndef CODEMARKER_H
+#define CODEMARKER_H
+
+#include <qpair.h>
+
+#include "atom.h"
+#include "node.h"
+
+QT_BEGIN_NAMESPACE
+
+class Config;
+
+typedef QMultiMap<QString, Node*> MemberMap; // the string is the member signature
+typedef QPair<const QmlTypeNode*, MemberMap> ClassMap; // the node is the QML type
+typedef QList<ClassMap*> ClassMapList;
+
+typedef QPair<QStringList, NodeList> KeysAndNodes;
+typedef QPair<const QmlTypeNode*, KeysAndNodes> ClassKeysNodes;
+typedef QList<ClassKeysNodes*> ClassKeysNodesList;
+
+struct Section
+{
+ QString name;
+ QString divClass;
+ QString singularMember;
+ QString pluralMember;
+ QStringList keys;
+ NodeList members;
+ NodeList reimpMembers;
+ QList<QPair<Aggregate *, int> > inherited;
+ ClassKeysNodesList classKeysNodesList_;
+
+ Section() { }
+ Section(const QString& name0,
+ const QString& divClass0,
+ const QString& singularMember0,
+ const QString& pluralMember0)
+ : name(name0),
+ divClass(divClass0),
+ singularMember(singularMember0),
+ pluralMember(pluralMember0) { }
+ ~Section();
+ void appendMember(Node* node) { members.append(node); }
+ void appendReimpMember(Node* node) { reimpMembers.append(node); }
+};
+
+struct FastSection
+{
+ const Aggregate *parent_;
+ QString name;
+ QString divClass;
+ QString singularMember;
+ QString pluralMember;
+ QMultiMap<QString, Node *> memberMap;
+ QMultiMap<QString, Node *> reimpMemberMap;
+ ClassMapList classMapList_;
+ QList<QPair<Aggregate *, int> > inherited;
+
+ FastSection(const Aggregate *parent,
+ const QString& name0,
+ const QString& divClass0,
+ const QString& singularMember0,
+ const QString& pluralMember0)
+ : parent_(parent),
+ name(name0),
+ divClass(divClass0),
+ singularMember(singularMember0),
+ pluralMember(pluralMember0) { }
+ ~FastSection();
+ bool isEmpty() const {
+ return (memberMap.isEmpty() &&
+ inherited.isEmpty() &&
+ reimpMemberMap.isEmpty() &&
+ classMapList_.isEmpty());
+ }
+
+};
+
+class CodeMarker
+{
+public:
+ enum SynopsisStyle { Summary, Detailed, Subpage, 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 Atom::AtomType atomType() const = 0;
+ virtual QString markedUpCode(const QString& code,
+ const Node *relative,
+ const Location &location) = 0;
+ virtual QString markedUpSynopsis(const Node *node,
+ const Node *relative,
+ SynopsisStyle style) = 0;
+ virtual QString markedUpQmlItem(const Node* , bool) { return QString(); }
+ 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 Aggregate *inner,
+ SynopsisStyle style,
+ Status status) = 0;
+ virtual QList<Section> qmlSections(QmlTypeNode* qmlTypeNode,
+ SynopsisStyle style,
+ Status status = Okay);
+ virtual QStringList macRefsForNode(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);
+
+ QString typified(const QString &string, bool trailingSpace = false);
+
+protected:
+ virtual QString sortName(const Node *node, const QString* name = 0);
+ static QString protect(const QString &string);
+ static void appendProtectedString(QString *output, const QStringRef &str);
+ QString taggedNode(const Node* node);
+ QString taggedQmlNode(const Node* node);
+ QString linkTag(const Node *node, const QString& body);
+ void insert(FastSection &fastSection,
+ Node *node,
+ SynopsisStyle style,
+ Status status);
+ bool insertReimpFunc(FastSection& fs, Node* node, Status status);
+ void append(QList<Section>& sectionList, const FastSection& fastSection, bool includeKeys = false);
+
+private:
+ QString macName(const Node *parent, const QString &name = QString());
+
+ static QString defaultLang;
+ static QList<CodeMarker *> markers;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/qdoc/codeparser.cpp b/src/qdoc/codeparser.cpp
new file mode 100644
index 000000000..92a0d5212
--- /dev/null
+++ b/src/qdoc/codeparser.cpp
@@ -0,0 +1,437 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL21$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** 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 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** As a special exception, The Qt Company gives you certain additional
+** rights. These rights are described in The Qt Company LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*
+ codeparser.cpp
+*/
+
+#include "codeparser.h"
+#include "node.h"
+#include "tree.h"
+#include "config.h"
+#include "generator.h"
+#include "qdocdatabase.h"
+#include <qdebug.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_INQMLMODULE Doc::alias(QLatin1String("inqmlmodule"))
+#define COMMAND_INJSMODULE Doc::alias(QLatin1String("injsmodule"))
+#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_PAGEKEYWORDS Doc::alias(QLatin1String("pagekeywords"))
+#define COMMAND_PRELIMINARY Doc::alias(QLatin1String("preliminary"))
+#define COMMAND_INPUBLICGROUP Doc::alias(QLatin1String("inpublicgroup"))
+#define COMMAND_QTVARIABLE Doc::alias(QLatin1String("qtvariable"))
+#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"))
+#define COMMAND_WRAPPER Doc::alias(QLatin1String("wrapper"))
+#define COMMAND_NOAUTOLIST Doc::alias(QLatin1String("noautolist"))
+
+QList<CodeParser *> CodeParser::parsers;
+bool CodeParser::showInternal_ = false;
+bool CodeParser::singleExec_ = false;
+
+/*!
+ The constructor adds this code parser to the static
+ list of code parsers.
+ */
+CodeParser::CodeParser()
+{
+ qdb_ = QDocDatabase::qdocDB();
+ parsers.prepend(this);
+}
+
+/*!
+ The destructor removes this code parser from the static
+ list of code parsers.
+ */
+CodeParser::~CodeParser()
+{
+ parsers.removeAll(this);
+}
+
+/*!
+ Initialize the code parser base class.
+ */
+void CodeParser::initializeParser(const Config& config)
+{
+ showInternal_ = config.getBool(CONFIG_SHOWINTERNAL);
+ singleExec_ = config.getBool(CONFIG_SINGLEEXEC);
+}
+
+/*!
+ Terminating a code parser is trivial.
+ */
+void CodeParser::terminateParser()
+{
+ // nothing.
+}
+
+QStringList CodeParser::headerFileNameFilter()
+{
+ return sourceFileNameFilter();
+}
+
+void CodeParser::parseHeaderFile(const Location& location, const QString& filePath)
+{
+ parseSourceFile(location, filePath);
+}
+
+void CodeParser::doneParsingHeaderFiles()
+{
+ doneParsingSourceFiles();
+}
+
+/*!
+ 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.constBegin();
+ while (p != parsers.constEnd()) {
+ (*p)->initializeParser(config);
+ ++p;
+ }
+}
+
+/*!
+ All the code parsers in the static list are terminated here.
+ */
+void CodeParser::terminate()
+{
+ QList<CodeParser *>::ConstIterator p = parsers.constBegin();
+ while (p != parsers.constEnd()) {
+ (*p)->terminateParser();
+ ++p;
+ }
+}
+
+CodeParser *CodeParser::parserForLanguage(const QString& language)
+{
+ QList<CodeParser *>::ConstIterator p = parsers.constBegin();
+ while (p != parsers.constEnd()) {
+ if ((*p)->language() == language)
+ return *p;
+ ++p;
+ }
+ return 0;
+}
+
+CodeParser *CodeParser::parserForHeaderFile(const QString &filePath)
+{
+ QString fileName = QFileInfo(filePath).fileName();
+
+ QList<CodeParser *>::ConstIterator p = parsers.constBegin();
+ while (p != parsers.constEnd()) {
+
+ QStringList headerPatterns = (*p)->headerFileNameFilter();
+ foreach (const QString &pattern, headerPatterns) {
+ QRegExp re(pattern, Qt::CaseInsensitive, QRegExp::Wildcard);
+ if (re.exactMatch(fileName))
+ return *p;
+ }
+ ++p;
+ }
+ return 0;
+}
+
+CodeParser *CodeParser::parserForSourceFile(const QString &filePath)
+{
+ QString fileName = QFileInfo(filePath).fileName();
+
+ QList<CodeParser *>::ConstIterator p = parsers.constBegin();
+ while (p != parsers.constEnd()) {
+
+ QStringList sourcePatterns = (*p)->sourceFileNameFilter();
+ foreach (const QString &pattern, sourcePatterns) {
+ QRegExp re(pattern, Qt::CaseInsensitive, QRegExp::Wildcard);
+ if (re.exactMatch(fileName))
+ return *p;
+ }
+ ++p;
+ }
+ return 0;
+}
+
+static QSet<QString> commonMetaCommands_;
+/*!
+ Returns the set of strings representing the common metacommands.
+ */
+const QSet<QString>& CodeParser::commonMetaCommands()
+{
+ if (commonMetaCommands_.isEmpty()) {
+ commonMetaCommands_ << COMMAND_COMPAT
+ << COMMAND_DEPRECATED
+ << COMMAND_INGROUP
+ << COMMAND_INMODULE
+ << COMMAND_INQMLMODULE
+ << COMMAND_INTERNAL
+ << COMMAND_MAINCLASS
+ << COMMAND_NONREENTRANT
+ << COMMAND_OBSOLETE
+ << COMMAND_PAGEKEYWORDS
+ << COMMAND_PRELIMINARY
+ << COMMAND_INPUBLICGROUP
+ << COMMAND_QTVARIABLE
+ << COMMAND_REENTRANT
+ << COMMAND_SINCE
+ << COMMAND_SUBTITLE
+ << COMMAND_THREADSAFE
+ << COMMAND_TITLE
+ << COMMAND_WRAPPER
+ << COMMAND_INJSMODULE
+ << COMMAND_NOAUTOLIST;
+ }
+ return commonMetaCommands_;
+}
+
+/*!
+ 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 ArgLocPair& arg,
+ Node* node)
+{
+ if (command == COMMAND_COMPAT) {
+ location.warning(tr("\\compat command used, but Qt3 compatibility is no longer supported"));
+ node->setStatus(Node::Compat);
+ }
+ else if (command == COMMAND_DEPRECATED) {
+ node->setStatus(Node::Obsolete);
+ }
+ else if ((command == COMMAND_INGROUP) || (command == COMMAND_INPUBLICGROUP)) {
+ // Note: \ingroup and \inpublicgroup are now the same.
+ // Not that they were ever different.
+ qdb_->addToGroup(arg.first, node);
+ }
+ else if (command == COMMAND_INMODULE) {
+ qdb_->addToModule(arg.first,node);
+ }
+ else if (command == COMMAND_INQMLMODULE) {
+ qdb_->addToQmlModule(arg.first,node);
+ }
+ else if (command == COMMAND_INJSMODULE) {
+ qdb_->addToJsModule(arg.first, node);
+ }
+ else if (command == COMMAND_MAINCLASS) {
+ node->doc().location().warning(tr("'\\mainclass' is deprecated. Consider '\\ingroup mainclasses'"));
+ }
+ else if (command == COMMAND_OBSOLETE) {
+ 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) {
+ if (!showInternal_) {
+ node->setAccess(Node::Private);
+ node->setStatus(Node::Internal);
+ if (node->type() == Node::QmlPropertyGroup) {
+ const QmlPropertyGroupNode* qpgn = static_cast<const QmlPropertyGroupNode*>(node);
+ NodeList::ConstIterator p = qpgn->childNodes().constBegin();
+ while (p != qpgn->childNodes().constEnd()) {
+ if ((*p)->type() == Node::QmlProperty) {
+ (*p)->setAccess(Node::Private);
+ (*p)->setStatus(Node::Internal);
+ }
+ ++p;
+ }
+ }
+ }
+ }
+ else if (command == COMMAND_REENTRANT) {
+ node->setThreadSafeness(Node::Reentrant);
+ }
+ else if (command == COMMAND_SINCE) {
+ node->setSince(arg.first);
+ }
+ else if (command == COMMAND_WRAPPER) {
+ node->setWrapper();
+ }
+ else if (command == COMMAND_PAGEKEYWORDS) {
+ node->addPageKeywords(arg.first);
+ }
+ else if (command == COMMAND_THREADSAFE) {
+ node->setThreadSafeness(Node::ThreadSafe);
+ }
+ else if (command == COMMAND_TITLE) {
+ node->setTitle(arg.first);
+ if (!node->isDocumentNode() && !node->isCollectionNode())
+ location.warning(tr("Ignored '\\%1'").arg(COMMAND_SUBTITLE));
+ else if (node->isExample())
+ qdb_->addExampleNode(static_cast<ExampleNode*>(node));
+ }
+ else if (command == COMMAND_SUBTITLE) {
+ node->setSubTitle(arg.first);
+ if (!node->isDocumentNode() && !node->isCollectionNode())
+ location.warning(tr("Ignored '\\%1'").arg(COMMAND_SUBTITLE));
+ }
+ else if (command == COMMAND_QTVARIABLE) {
+ node->setQtVariable(arg.first);
+ if (!node->isModule() && !node->isQmlModule())
+ location.warning(tr("Command '\\%1' is only meanigfule in '\\module' and '\\qmlmodule'.")
+ .arg(COMMAND_QTVARIABLE));
+ }
+ else if (command == COMMAND_NOAUTOLIST) {
+ node->setNoAutoList(true);
+ }
+}
+
+/*!
+ \internal
+ */
+void CodeParser::extractPageLinkAndDesc(const QString& arg,
+ QString* link,
+ QString* desc)
+{
+ QRegExp bracedRegExp(QLatin1String("\\{([^{}]*)\\}(?:\\{([^{}]*)\\})?"));
+
+ if (bracedRegExp.exactMatch(arg)) {
+ *link = bracedRegExp.cap(1);
+ *desc = bracedRegExp.cap(2);
+ if (desc->isEmpty())
+ *desc = *link;
+ }
+ else {
+ int spaceAt = arg.indexOf(QLatin1Char(' '));
+ if (arg.contains(QLatin1String(".html")) && spaceAt != -1) {
+ *link = arg.leftRef(spaceAt).trimmed().toString();
+ *desc = arg.midRef(spaceAt).trimmed().toString();
+ }
+ else {
+ *link = arg;
+ *desc = arg;
+ }
+ }
+}
+
+/*!
+ \internal
+ */
+void CodeParser::setLink(Node* node, Node::LinkType linkType, const QString& arg)
+{
+ QString link;
+ QString desc;
+ extractPageLinkAndDesc(arg, &link, &desc);
+ node->setLink(linkType, link, desc);
+}
+
+/*!
+ Returns \c true if the file being parsed is a .h file.
+ */
+bool CodeParser::isParsingH() const
+{
+ return currentFile_.endsWith(".h");
+}
+
+/*!
+ Returns \c true if the file being parsed is a .cpp file.
+ */
+bool CodeParser::isParsingCpp() const
+{
+ return currentFile_.endsWith(".cpp");
+}
+
+/*!
+ Returns \c true if the file being parsed is a .qdoc file.
+ */
+bool CodeParser::isParsingQdoc() const
+{
+ return currentFile_.endsWith(".qdoc");
+}
+
+/*!
+ For each node that will produce a documentation page, this function
+ ensures that the node belongs to a module. Normally, the qdoc comment
+ for an entity that will produce a documentation page will contain an
+ \inmodule command to tell qdoc which module the entity belongs to.
+
+ But now we normally run qdoc on each module in two passes. The first
+ produces an index file; the second pass generates the docs after
+ reading all the index files it needs.
+
+ This means that all the pages generated during each pass 2 run of
+ qdoc almost certainly belong to a single module, and the name of
+ that module is, as a rule, used as the project name in the qdocconf
+ file used when running qdoc on the module.
+
+ So this function first asks if the node \a n has a non-empty module
+ name. If it it does not have a non-empty module name, it sets the
+ module name to be the project name.
+
+ In some cases it prints a qdoc warning that it has done this. Namely,
+ for C++ classes and namespaces.
+ */
+void CodeParser::checkModuleInclusion(Node* n)
+{
+ if (n->physicalModuleName().isEmpty()) {
+ n->setPhysicalModuleName(Generator::defaultModuleName());
+ switch (n->type()) {
+ case Node::Class:
+ if (n->access() != Node::Private && !n->doc().isEmpty()) {
+ n->doc().location().warning(tr("Class %1 has no \\inmodule command; "
+ "using project name by default: %2")
+ .arg(n->name()).arg(Generator::defaultModuleName()));
+ }
+ break;
+ case Node::Namespace:
+ if (n->access() != Node::Private && !n->name().isEmpty() && !n->doc().isEmpty()) {
+ n->doc().location().warning(tr("Namespace %1 has no \\inmodule command; "
+ "using project name by default: %2")
+ .arg(n->name()).arg(Generator::defaultModuleName()));
+ }
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+QT_END_NAMESPACE
diff --git a/src/qdoc/codeparser.h b/src/qdoc/codeparser.h
new file mode 100644
index 000000000..9d9e9286e
--- /dev/null
+++ b/src/qdoc/codeparser.h
@@ -0,0 +1,97 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL21$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** 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 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** As a special exception, The Qt Company gives you certain additional
+** rights. These rights are described in The Qt Company LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef CODEPARSER_H
+#define CODEPARSER_H
+
+#include <qset.h>
+#include "node.h"
+
+QT_BEGIN_NAMESPACE
+
+class Config;
+class QString;
+class QDocDatabase;
+
+class CodeParser
+{
+ Q_DECLARE_TR_FUNCTIONS(QDoc::CodeParser)
+
+public:
+ CodeParser();
+ virtual ~CodeParser();
+
+ virtual void initializeParser(const Config& config);
+ virtual void terminateParser();
+ virtual QString language() = 0;
+ virtual QStringList headerFileNameFilter();
+ virtual QStringList sourceFileNameFilter() = 0;
+ virtual void parseHeaderFile(const Location& location, const QString& filePath);
+ virtual void parseSourceFile(const Location& location, const QString& filePath) = 0;
+ virtual void doneParsingHeaderFiles();
+ virtual void doneParsingSourceFiles() = 0;
+
+ bool isParsingH() const;
+ bool isParsingCpp() const;
+ bool isParsingQdoc() const;
+ const QString& currentFile() const { return currentFile_; }
+ void checkModuleInclusion(Node* n);
+
+ static void initialize(const Config& config);
+ static void terminate();
+ static CodeParser *parserForLanguage(const QString& language);
+ static CodeParser *parserForHeaderFile(const QString &filePath);
+ static CodeParser *parserForSourceFile(const QString &filePath);
+ static void setLink(Node* node, Node::LinkType linkType, const QString& arg);
+
+protected:
+ const QSet<QString>& commonMetaCommands();
+ void processCommonMetaCommand(const Location& location,
+ const QString& command,
+ const ArgLocPair& arg,
+ Node *node);
+ static void extractPageLinkAndDesc(const QString& arg,
+ QString* link,
+ QString* desc);
+ QString currentFile_;
+ QDocDatabase* qdb_;
+
+private:
+ static QList<CodeParser *> parsers;
+ static bool showInternal_;
+ static bool singleExec_;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/qdoc/config.cpp b/src/qdoc/config.cpp
new file mode 100644
index 000000000..6c47b86d2
--- /dev/null
+++ b/src/qdoc/config.cpp
@@ -0,0 +1,1221 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL21$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** 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 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** As a special exception, The Qt Company gives you certain additional
+** rights. These rights are described in The Qt Company LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*
+ config.cpp
+*/
+
+#include <qdir.h>
+#include <qvariant.h>
+#include <qfile.h>
+#include <qtemporaryfile.h>
+#include <qtextstream.h>
+#include <qdebug.h>
+#include "config.h"
+#include "generator.h"
+#include <stdlib.h>
+
+QT_BEGIN_NAMESPACE
+
+QString ConfigStrings::ALIAS = QStringLiteral("alias");
+QString ConfigStrings::AUTOLINKERRORS = QStringLiteral("autolinkerrors");
+QString ConfigStrings::BASE = QStringLiteral("base");
+QString ConfigStrings::BASEDIR = QStringLiteral("basedir");
+QString ConfigStrings::BUILDVERSION = QStringLiteral("buildversion");
+QString ConfigStrings::CODEINDENT = QStringLiteral("codeindent");
+QString ConfigStrings::CODEPREFIX = QStringLiteral("codeprefix");
+QString ConfigStrings::CODESUFFIX = QStringLiteral("codesuffix");
+QString ConfigStrings::CPPCLASSESPAGE = QStringLiteral("cppclassespage");
+QString ConfigStrings::DEFINES = QStringLiteral("defines");
+QString ConfigStrings::DEPENDS = QStringLiteral("depends");
+QString ConfigStrings::DESCRIPTION = QStringLiteral("description");
+QString ConfigStrings::EDITION = QStringLiteral("edition");
+QString ConfigStrings::ENDHEADER = QStringLiteral("endheader");
+QString ConfigStrings::EXAMPLEDIRS = QStringLiteral("exampledirs");
+QString ConfigStrings::EXAMPLES = QStringLiteral("examples");
+QString ConfigStrings::EXAMPLESINSTALLPATH = QStringLiteral("examplesinstallpath");
+QString ConfigStrings::EXCLUDEDIRS = QStringLiteral("excludedirs");
+QString ConfigStrings::EXCLUDEFILES = QStringLiteral("excludefiles");
+QString ConfigStrings::EXTRAIMAGES = QStringLiteral("extraimages");
+QString ConfigStrings::FALSEHOODS = QStringLiteral("falsehoods");
+QString ConfigStrings::FORMATTING = QStringLiteral("formatting");
+QString ConfigStrings::GENERATEINDEX = QStringLiteral("generateindex");
+QString ConfigStrings::HEADERDIRS = QStringLiteral("headerdirs");
+QString ConfigStrings::HEADERS = QStringLiteral("headers");
+QString ConfigStrings::HEADERSCRIPTS = QStringLiteral("headerscripts");
+QString ConfigStrings::HEADERSTYLES = QStringLiteral("headerstyles");
+QString ConfigStrings::HOMEPAGE = QStringLiteral("homepage");
+QString ConfigStrings::IGNOREDIRECTIVES = QStringLiteral("ignoredirectives");
+QString ConfigStrings::IGNORETOKENS = QStringLiteral("ignoretokens");
+QString ConfigStrings::IMAGEDIRS = QStringLiteral("imagedirs");
+QString ConfigStrings::IMAGES = QStringLiteral("images");
+QString ConfigStrings::INDEXES = QStringLiteral("indexes");
+QString ConfigStrings::LANDINGPAGE = QStringLiteral("landingpage");
+QString ConfigStrings::LANGUAGE = QStringLiteral("language");
+QString ConfigStrings::MACRO = QStringLiteral("macro");
+QString ConfigStrings::MANIFESTMETA = QStringLiteral("manifestmeta");
+QString ConfigStrings::NATURALLANGUAGE = QStringLiteral("naturallanguage");
+QString ConfigStrings::NAVIGATION = QStringLiteral("navigation");
+QString ConfigStrings::NOLINKERRORS = QStringLiteral("nolinkerrors");
+QString ConfigStrings::OBSOLETELINKS = QStringLiteral("obsoletelinks");
+QString ConfigStrings::OUTPUTDIR = QStringLiteral("outputdir");
+QString ConfigStrings::OUTPUTENCODING = QStringLiteral("outputencoding");
+QString ConfigStrings::OUTPUTLANGUAGE = QStringLiteral("outputlanguage");
+QString ConfigStrings::OUTPUTFORMATS = QStringLiteral("outputformats");
+QString ConfigStrings::OUTPUTPREFIXES = QStringLiteral("outputprefixes");
+QString ConfigStrings::OUTPUTSUFFIXES = QStringLiteral("outputsuffixes");
+QString ConfigStrings::PROJECT = QStringLiteral("project");
+QString ConfigStrings::REDIRECTDOCUMENTATIONTODEVNULL = QStringLiteral("redirectdocumentationtodevnull");
+QString ConfigStrings::QHP = QStringLiteral("qhp");
+QString ConfigStrings::QUOTINGINFORMATION = QStringLiteral("quotinginformation");
+QString ConfigStrings::SCRIPTDIRS = QStringLiteral("scriptdirs");
+QString ConfigStrings::SCRIPTS = QStringLiteral("scripts");
+QString ConfigStrings::SHOWINTERNAL = QStringLiteral("showinternal");
+QString ConfigStrings::SINGLEEXEC = QStringLiteral("singleexec");
+QString ConfigStrings::SOURCEDIRS = QStringLiteral("sourcedirs");
+QString ConfigStrings::SOURCEENCODING = QStringLiteral("sourceencoding");
+QString ConfigStrings::SOURCES = QStringLiteral("sources");
+QString ConfigStrings::SPURIOUS = QStringLiteral("spurious");
+QString ConfigStrings::STYLEDIRS = QStringLiteral("styledirs");
+QString ConfigStrings::STYLE = QStringLiteral("style");
+QString ConfigStrings::STYLES = QStringLiteral("styles");
+QString ConfigStrings::STYLESHEETS = QStringLiteral("stylesheets");
+QString ConfigStrings::SYNTAXHIGHLIGHTING = QStringLiteral("syntaxhighlighting");
+QString ConfigStrings::TEMPLATEDIR = QStringLiteral("templatedir");
+QString ConfigStrings::TABSIZE = QStringLiteral("tabsize");
+QString ConfigStrings::TAGFILE = QStringLiteral("tagfile");
+QString ConfigStrings::TRANSLATORS = QStringLiteral("translators");
+QString ConfigStrings::URL = QStringLiteral("url");
+QString ConfigStrings::VERSION = QStringLiteral("version");
+QString ConfigStrings::VERSIONSYM = QStringLiteral("versionsym");
+QString ConfigStrings::FILEEXTENSIONS = QStringLiteral("fileextensions");
+QString ConfigStrings::IMAGEEXTENSIONS = QStringLiteral("imageextensions");
+QString ConfigStrings::QMLONLY = QStringLiteral("qmlonly");
+QString ConfigStrings::QMLTYPESPAGE = QStringLiteral("qmltypespage");
+QString ConfigStrings::WRITEQAPAGES = QStringLiteral("writeqapages");
+
+/*!
+ An entry in a stack, where each entry is a list
+ of string values.
+ */
+class MetaStackEntry
+{
+public:
+ void open();
+ void close();
+
+ QStringList accum;
+ QStringList next;
+};
+Q_DECLARE_TYPEINFO(MetaStackEntry, Q_MOVABLE_TYPE);
+
+/*!
+ Start accumulating values in a list by appending an empty
+ string to the list.
+ */
+void MetaStackEntry::open()
+{
+ next.append(QString());
+}
+
+/*!
+ Stop accumulating values and append the list of accumulated
+ values to the complete list of accumulated values.
+
+ */
+void MetaStackEntry::close()
+{
+ accum += next;
+ next.clear();
+}
+
+/*!
+ \class MetaStack
+
+ This class maintains a stack of values of config file variables.
+*/
+class MetaStack : private QStack<MetaStackEntry>
+{
+ Q_DECLARE_TR_FUNCTIONS(QDoc::MetaStack)
+
+public:
+ MetaStack();
+
+ void process(QChar ch, const Location& location);
+ QStringList getExpanded(const Location& location);
+};
+
+/*!
+ The default constructor pushes a new stack entry and
+ opens it.
+ */
+MetaStack::MetaStack()
+{
+ push(MetaStackEntry());
+ top().open();
+}
+
+/*!
+ Processes the character \a ch using the \a location.
+ It really just builds up a name by appending \a ch to
+ it.
+ */
+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.constBegin();
+ while (pre != prefixes.constEnd()) {
+ QStringList::ConstIterator suf = suffixes.constBegin();
+ while (suf != suffixes.constEnd()) {
+ top().next << (*pre + *suf);
+ ++suf;
+ }
+ ++pre;
+ }
+ } else if (ch == QLatin1Char(',') && count() > 1) {
+ top().close();
+ top().open();
+ } else {
+ /*
+ This is where all the processing is done.
+ */
+ QStringList::Iterator pre = top().next.begin();
+ while (pre != top().next.end()) {
+ *pre += ch;
+ ++pre;
+ }
+ }
+}
+
+/*!
+ Returns the accumulated string values.
+ */
+QStringList MetaStack::getExpanded(const Location& location)
+{
+ if (count() > 1)
+ location.fatal(tr("Missing '}'"));
+
+ top().close();
+ return top().accum;
+}
+
+const QString Config::dot = QLatin1String(".");
+bool Config::debug_ = false;
+bool Config::generateExamples = true;
+QString Config::overrideOutputDir;
+QString Config::installDir;
+QSet<QString> Config::overrideOutputFormats;
+QMap<QString, QString> Config::extractedDirs;
+int Config::numInstances;
+QStack<QString> Config::workingDirs_;
+QMap<QString, QStringList> Config::includeFilesMap_;
+
+/*!
+ \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;
+ lastLocation_ = Location::null;
+ configVars_.clear();
+ numInstances++;
+ includeFilesMap_.clear();
+}
+
+/*!
+ The destructor has nothing special to do.
+ */
+Config::~Config()
+{
+ includeFilesMap_.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);
+ lastLocation_ = 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)
+{
+ configVars_.insert(var,ConfigVar(var, values, QDir::currentPath()));
+}
+
+/*!
+ 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 or \c -1 if \a var is not set.
+ */
+int Config::getInt(const QString& var) const
+{
+ QStringList strs = getStringList(var);
+ if (strs.isEmpty())
+ return -1;
+
+ QStringList::ConstIterator s = strs.constBegin();
+ int sum = 0;
+
+ while (s != strs.constEnd()) {
+ sum += (*s).toInt();
+ ++s;
+ }
+ return sum;
+}
+
+/*!
+ Function to return the correct outputdir.
+ outputdir can be set using the qdocconf or the command-line
+ variable -outputdir.
+ */
+QString Config::getOutputDir() const
+{
+ QString t;
+ if (overrideOutputDir.isNull())
+ t = getString(CONFIG_OUTPUTDIR);
+ else
+ t = overrideOutputDir;
+ if (Generator::singleExec()) {
+ QString project = getString(CONFIG_PROJECT);
+ t += QLatin1Char('/') + project.toLower();
+ }
+ if (!Generator::useOutputSubdirs()) {
+ t = t.left(t.lastIndexOf('/'));
+ QString singleOutputSubdir = getString("HTML.outputsubdir");
+ if (singleOutputSubdir.isEmpty())
+ singleOutputSubdir = "html";
+ t += QLatin1Char('/') + singleOutputSubdir;
+ }
+ return t;
+}
+
+/*!
+ Function to return the correct outputformats.
+ outputformats can be set using the qdocconf or the command-line
+ variable -outputformat.
+ */
+QSet<QString> Config::getOutputFormats() const
+{
+ if (overrideOutputFormats.isEmpty())
+ return getStringSet(CONFIG_OUTPUTFORMATS);
+ else
+ return overrideOutputFormats;
+}
+
+/*!
+ First, this function looks up the configuration variable \a var
+ in the location map and, if found, sets the internal variable
+ \c{lastLocation_} 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
+{
+ QList<ConfigVar> configVars = configVars_.values(var);
+ QString value;
+ if (!configVars.empty()) {
+ int i = configVars.size() - 1;
+ while (i >= 0) {
+ const ConfigVar& cv = configVars[i];
+ if (!cv.location_.isEmpty())
+ const_cast<Config *>(this)->lastLocation_ = cv.location_;
+ if (!cv.values_.isEmpty()) {
+ if (!cv.plus_)
+ value.clear();
+ for (int j=0; j<cv.values_.size(); ++j) {
+ if (!value.isEmpty() && !value.endsWith(QChar('\n')))
+ value.append(QChar(' '));
+ value.append(cv.values_[j]);
+ }
+ }
+ --i;
+ }
+ }
+ return value;
+}
+
+/*!
+ 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. If found, it sets the internal variable
+ \c{lastLocation_} to the Location that \a var maps to.
+
+ Then it looks up the configuration variable \a var in the map of
+ configuration variable records. If found, it gets a list of all
+ the records for \a var. Then it appends all the values for \a var
+ to a list and returns the list. As it appends the values from each
+ record, if the \a var used '=' instead of '+=' the list is cleared
+ before the values are appended. \note '+=' should always be used.
+ The final list is returned.
+ */
+QStringList Config::getStringList(const QString& var) const
+{
+ QList<ConfigVar> configVars = configVars_.values(var);
+ QStringList values;
+ if (!configVars.empty()) {
+ int i = configVars.size() - 1;
+ while (i >= 0) {
+ if (!configVars[i].location_.isEmpty())
+ const_cast<Config *>(this)->lastLocation_ = configVars[i].location_;
+ if (configVars[i].plus_)
+ values.append(configVars[i].values_);
+ else
+ values = configVars[i].values_;
+ --i;
+ }
+ }
+ return values;
+}
+
+/*!
+ Returns the a path list where all paths from the config variable \a var
+ are canonicalized. If \a validate is true, a warning for invalid paths is
+ generated.
+
+ First, this function looks up the configuration variable \a var
+ in the location map and, if found, sets the internal variable
+ \c{lastLocation_} the Location that \a var maps to.
+
+ Then it looks up the configuration variable \a var in the string
+ list map, which maps to one or more records that each contains a
+ list of file paths.
+
+ \sa Location::canonicalRelativePath()
+ */
+QStringList Config::getCanonicalPathList(const QString& var, bool validate) const
+{
+ QStringList t;
+ QList<ConfigVar> configVars = configVars_.values(var);
+ if (!configVars.empty()) {
+ int i = configVars.size() - 1;
+ while (i >= 0) {
+ const ConfigVar& cv = configVars[i];
+ if (!cv.location_.isEmpty())
+ const_cast<Config *>(this)->lastLocation_ = cv.location_;
+ if (!cv.plus_)
+ t.clear();
+ const QString d = cv.currentPath_;
+ const QStringList& sl = cv.values_;
+ if (!sl.isEmpty()) {
+ t.reserve(t.size() + sl.size());
+ for (int i=0; i<sl.size(); ++i) {
+ QDir dir(sl[i].simplified());
+ QString path = dir.path();
+ if (dir.isRelative())
+ dir.setPath(d + QLatin1Char('/') + path);
+ if (validate && !QFileInfo::exists(dir.path()))
+ lastLocation_.warning(tr("Cannot find file or directory: %1").arg(path));
+ else
+ t.append(dir.canonicalPath());
+ }
+ }
+ --i;
+ }
+ }
+ return t;
+}
+
+/*!
+ 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.constBegin();
+
+ while (s != subRegExps.constEnd()) {
+ 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.constBegin();
+ QList<QRegExp> regExps;
+
+ while (s != strs.constEnd()) {
+ regExps += QRegExp(*s);
+ ++s;
+ }
+ return regExps;
+}
+
+/*!
+ This function is slower than it could be. What it does is
+ find all the keys that begin with \a var + dot and return
+ the matching keys in a set, stripped of the matching prefix
+ and dot.
+ */
+QSet<QString> Config::subVars(const QString& var) const
+{
+ QSet<QString> result;
+ QString varDot = var + QLatin1Char('.');
+ ConfigVarMultimap::ConstIterator i = configVars_.constBegin();
+ while (i != configVars_.constEnd()) {
+ if (i.key().startsWith(varDot)) {
+ QString subVar = i.key().mid(varDot.length());
+ int dot = subVar.indexOf(QLatin1Char('.'));
+ if (dot != -1)
+ subVar.truncate(dot);
+ if (!result.contains(subVar))
+ result.insert(subVar);
+ }
+ ++i;
+ }
+ return result;
+}
+
+/*!
+ Same as subVars(), but in this case we return a config var
+ multimap with the matching keys (stripped of the prefix \a var
+ and mapped to their values. The pairs are inserted into \a t
+ */
+void Config::subVarsAndValues(const QString& var, ConfigVarMultimap& t) const
+{
+ QString varDot = var + QLatin1Char('.');
+ ConfigVarMultimap::ConstIterator v = configVars_.constBegin();
+ while (v != configVars_.constEnd()) {
+ if (v.key().startsWith(varDot)) {
+ QString subVar = v.key().mid(varDot.length());
+ int dot = subVar.indexOf(QLatin1Char('.'));
+ if (dot != -1)
+ subVar.truncate(dot);
+ t.insert(subVar,v.value());
+ }
+ ++v;
+ }
+}
+
+/*!
+ Get all .qdocinc files.
+ */
+QString Config::getIncludeFilePath(const QString& fileName) const
+{
+ QString ext = fileName.mid(fileName.lastIndexOf('.'));
+ ext.prepend('*');
+
+ if (!includeFilesMap_.contains(ext)) {
+ QSet<QString> t;
+ QStringList result;
+ QStringList dirs = getCanonicalPathList(CONFIG_SOURCEDIRS);
+ QStringList::ConstIterator d = dirs.constBegin();
+ while (d != dirs.constEnd()) {
+ result += getFilesHere(*d, ext, location(), t, t);
+ ++d;
+ }
+ includeFilesMap_.insert(ext, result);
+ }
+ const QStringList& paths = (*includeFilesMap_.find(ext));
+ for (int i=0; i<paths.size(); ++i) {
+ if (paths[i].endsWith(fileName))
+ return paths[i];
+ }
+ return QString();
+}
+
+/*!
+ 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. The files
+ in \a excludedFiles are not included in the return list.
+ */
+QStringList Config::getAllFiles(const QString &filesVar,
+ const QString &dirsVar,
+ const QSet<QString> &excludedDirs,
+ const QSet<QString> &excludedFiles)
+{
+ QStringList result = getCanonicalPathList(filesVar);
+ QStringList dirs = getCanonicalPathList(dirsVar);
+
+ QString nameFilter = getString(filesVar + dot + CONFIG_FILEEXTENSIONS);
+
+ QStringList::ConstIterator d = dirs.constBegin();
+ while (d != dirs.constEnd()) {
+ result += getFilesHere(*d, nameFilter, location(), excludedDirs, excludedFiles);
+ ++d;
+ }
+ return result;
+}
+
+QStringList Config::getExampleQdocFiles(const QSet<QString> &excludedDirs,
+ const QSet<QString> &excludedFiles)
+{
+ QStringList result;
+ QStringList dirs = getCanonicalPathList("exampledirs");
+ QString nameFilter = " *.qdoc";
+
+ QStringList::ConstIterator d = dirs.constBegin();
+ while (d != dirs.constEnd()) {
+ result += getFilesHere(*d, nameFilter, location(), excludedDirs, excludedFiles);
+ ++d;
+ }
+ return result;
+}
+
+QStringList Config::getExampleImageFiles(const QSet<QString> &excludedDirs,
+ const QSet<QString> &excludedFiles)
+{
+ QStringList result;
+ QStringList dirs = getCanonicalPathList("exampledirs");
+ QString nameFilter = getString(CONFIG_EXAMPLES + dot + CONFIG_IMAGEEXTENSIONS);
+
+ QStringList::ConstIterator d = dirs.constBegin();
+ while (d != dirs.constEnd()) {
+ result += getFilesHere(*d, nameFilter, location(), excludedDirs, excludedFiles);
+ ++d;
+ }
+ return result;
+}
+
+/*!
+ \a fileName is the path of the file to find.
+
+ \a files and \a dirs are the lists where we must find the
+ components of \a fileName.
+
+ \a location is used for obtaining the file and line numbers
+ for report qdoc errors.
+ */
+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.constBegin();
+ while (f != files.constEnd()) {
+ 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.constBegin();
+ while (d != dirs.constEnd()) {
+ fileInfo.setFile(QDir(*d), firstComponent);
+ if (fileInfo.exists())
+ break;
+ ++d;
+ }
+ }
+
+ userFriendlyFilePath = QString();
+ if (!fileInfo.exists())
+ return QString();
+
+ QStringList::ConstIterator c = components.constBegin();
+ for (;;) {
+ bool isArchive = (c != components.constEnd() - 1);
+ QString userFriendly = *c;
+
+ userFriendlyFilePath += userFriendly;
+
+ if (isArchive) {
+ QString extracted = extractedDirs[fileInfo.filePath()];
+ ++c;
+ fileInfo.setFile(QDir(extracted), *c);
+ } else {
+ break;
+ }
+
+ userFriendlyFilePath += QLatin1Char('?');
+ }
+ 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.constBegin();
+ while (e != fileExtensions.constEnd()) {
+ QString filePath = findFile(location,
+ files,
+ dirs,
+ fileBase + QLatin1Char('.') + *e,
+ userFriendlyFilePath);
+ if (!filePath.isEmpty())
+ return filePath;
+ ++e;
+ }
+ return findFile(location, files, dirs, fileBase, userFriendlyFilePath);
+}
+
+/*!
+ Copies the \a sourceFilePath to the file name constructed by
+ concatenating \a targetDirPath and the file name from the
+ \a userFriendlySourceFilePath. \a location is for identifying
+ the file and line number where a qdoc error occurred. The
+ constructed output file name is returned.
+ */
+QString Config::copyFile(const Location& location,
+ const QString& sourceFilePath,
+ const QString& userFriendlySourceFilePath,
+ const QString& targetDirPath)
+{
+ QFile inFile(sourceFilePath);
+ if (!inFile.open(QFile::ReadOnly)) {
+ location.warning(tr("Cannot open input file for copy: '%1': %2")
+ .arg(sourceFilePath).arg(inFile.errorString()));
+ return QString();
+ }
+
+ QString outFileName = userFriendlySourceFilePath;
+ int slash = outFileName.lastIndexOf(QLatin1Char('/'));
+ if (slash != -1)
+ outFileName = outFileName.mid(slash);
+ if ((outFileName.size()) > 0 && (outFileName[0] != '/'))
+ outFileName = targetDirPath + QLatin1Char('/') + outFileName;
+ else
+ outFileName = targetDirPath + outFileName;
+ QFile outFile(outFileName);
+ if (!outFile.open(QFile::WriteOnly)) {
+ location.warning(tr("Cannot open output file for copy: '%1': %2")
+ .arg(outFileName).arg(outFile.errorString()));
+ return QString();
+ }
+
+ 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() != QLatin1String(".") && (*it).fileName() != QLatin1String("..")) {
+ if (removeDirContents((*it).absoluteFilePath())) {
+ if (!dirInfo.rmdir((*it).fileName()))
+ ok = false;
+ }
+ else {
+ ok = false;
+ }
+ }
+ }
+ ++it;
+ }
+ return ok;
+}
+
+/*!
+ Returns \c 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(',');
+}
+
+/*!
+ \a fileName is a master qdocconf file. It contains a list of
+ qdocconf files and nothing else. Read the list and return it.
+ */
+QStringList Config::loadMaster(const QString& fileName)
+{
+ Location location = Location::null;
+ QFile fin(fileName);
+ if (!fin.open(QFile::ReadOnly | QFile::Text)) {
+ if (!Config::installDir.isEmpty()) {
+ int prefix = location.filePath().length() - location.fileName().length();
+ fin.setFileName(Config::installDir + QLatin1Char('/') + fileName.right(fileName.length() - prefix));
+ }
+ if (!fin.open(QFile::ReadOnly | QFile::Text))
+ location.fatal(tr("Cannot open master qdocconf file '%1': %2").arg(fileName).arg(fin.errorString()));
+ }
+ QTextStream stream(&fin);
+#ifndef QT_NO_TEXTCODEC
+ stream.setCodec("UTF-8");
+#endif
+ QStringList qdocFiles;
+ QString line = stream.readLine();
+ while (!line.isNull()) {
+ qdocFiles.append(line);
+ line = stream.readLine();
+ }
+ fin.close();
+ return qdocFiles;
+}
+
+/*!
+ 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 qdoc configuration file.
+ */
+void Config::load(Location location, const QString& fileName)
+{
+ QFileInfo fileInfo(fileName);
+ QString path = fileInfo.canonicalPath();
+ pushWorkingDir(path);
+ QDir::setCurrent(path);
+ QRegExp keySyntax(QLatin1String("\\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(fileInfo.fileName());
+ if (!fin.open(QFile::ReadOnly | QFile::Text)) {
+ if (!Config::installDir.isEmpty()) {
+ int prefix = location.filePath().length() - location.fileName().length();
+ fin.setFileName(Config::installDir + QLatin1Char('/') + fileName.right(fileName.length() - prefix));
+ }
+ if (!fin.open(QFile::ReadOnly | QFile::Text))
+ location.fatal(tr("Cannot open file '%1': %2").arg(fileName).arg(fin.errorString()));
+ }
+
+ QTextStream stream(&fin);
+#ifndef QT_NO_TEXTCODEC
+ stream.setCodec("UTF-8");
+#endif
+ QString text = stream.readAll();
+ text += QLatin1String("\n\n");
+ text += QLatin1Char('\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 rhsValues;
+ 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() == QLatin1String("include")) {
+ QString includeFile;
+
+ if (cc != '(')
+ location.fatal(tr("Bad include syntax"));
+ SKIP_CHAR();
+ SKIP_SPACES();
+
+ while (!c.isSpace() && cc != '#' && cc != ')') {
+
+ if (cc == '$') {
+ QString var;
+ SKIP_CHAR();
+ while (c.isLetterOrNumber() || cc == '_') {
+ var += c;
+ SKIP_CHAR();
+ }
+ if (!var.isEmpty()) {
+ const QByteArray val = qgetenv(var.toLatin1().data());
+ if (val.isNull()) {
+ location.fatal(tr("Environment variable '%1' undefined").arg(var));
+ }
+ else {
+ includeFile += QString::fromLatin1(val);
+ }
+ }
+ } else {
+ 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(QDir(path), includeFile).filePath());
+ }
+ else {
+ /*
+ It wasn't an include statement, so it's something else.
+ We must see either '=' or '+=' next. If not, fatal error.
+ */
+ 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::fromLatin1("abfnrtv").indexOf(c)) != -1) {
+ word += QLatin1Char("\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;
+#if 0
+ if (metWord)
+ rhsValues << QString(" " + word);
+ else
+#endif
+ rhsValues << 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())
+ rhsValues << 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()) {
+ const QByteArray val = qgetenv(var.toLatin1().constData());
+ if (val.isNull()) {
+ location.fatal(tr("Environment variable '%1' undefined").arg(var));
+ }
+ else {
+ word += QString::fromLatin1(val);
+ }
+ }
+ }
+ else {
+ if (!inQuote && cc == '=')
+ location.fatal(tr("Unexpected '='"));
+ PUT_CHAR();
+ }
+ }
+
+ QStringList::ConstIterator key = keys.constBegin();
+ while (key != keys.constEnd()) {
+ if (!keySyntax.exactMatch(*key))
+ keyLoc.fatal(tr("Invalid key '%1'").arg(*key));
+
+ ConfigVarMultimap::Iterator i;
+ i = configVars_.insert(*key, ConfigVar(*key, rhsValues, QDir::currentPath(), keyLoc));
+ i.value().plus_ = plus;
+ ++key;
+ }
+ }
+ } else {
+ location.fatal(tr("Unexpected character '%1' at beginning of line").arg(c));
+ }
+ }
+ popWorkingDir();
+ if (!workingDirs_.isEmpty())
+ QDir::setCurrent(workingDirs_.top());
+}
+
+QStringList Config::getFilesHere(const QString& uncleanDir,
+ const QString& nameFilter,
+ const Location &location,
+ const QSet<QString> &excludedDirs,
+ const QSet<QString> &excludedFiles)
+{
+ QString dir = location.isEmpty() ? QDir::cleanPath(uncleanDir) : QDir(uncleanDir).canonicalPath();
+ QStringList result;
+ if (excludedDirs.contains(dir))
+ return result;
+
+ QDir dirInfo(dir);
+ QStringList fileNames;
+ QStringList::const_iterator fn;
+
+ dirInfo.setNameFilters(nameFilter.split(QLatin1Char(' ')));
+ dirInfo.setSorting(QDir::Name);
+ dirInfo.setFilter(QDir::Files);
+ fileNames = dirInfo.entryList();
+ fn = fileNames.constBegin();
+ while (fn != fileNames.constEnd()) {
+ if (!fn->startsWith(QLatin1Char('~'))) {
+ QString s = dirInfo.filePath(*fn);
+ QString c = QDir::cleanPath(s);
+ if (!excludedFiles.contains(c))
+ result.append(c);
+ }
+ ++fn;
+ }
+
+ dirInfo.setNameFilters(QStringList(QLatin1String("*")));
+ dirInfo.setFilter(QDir::Dirs|QDir::NoDotAndDotDot);
+ fileNames = dirInfo.entryList();
+ fn = fileNames.constBegin();
+ while (fn != fileNames.constEnd()) {
+ result += getFilesHere(dirInfo.filePath(*fn), nameFilter, location, excludedDirs, excludedFiles);
+ ++fn;
+ }
+ return result;
+}
+
+/*!
+ Push \a dir onto the stack of working directories.
+ */
+void Config::pushWorkingDir(const QString& dir)
+{
+ workingDirs_.push(dir);
+}
+
+/*!
+ If the stack of working directories is not empty, pop the
+ top entry and return it. Otherwise return an empty string.
+ */
+QString Config::popWorkingDir()
+{
+ if (!workingDirs_.isEmpty())
+ return workingDirs_.pop();
+
+ qDebug() << "RETURNED EMPTY WORKING DIR";
+ return QString();
+}
+
+QT_END_NAMESPACE
diff --git a/src/qdoc/config.h b/src/qdoc/config.h
new file mode 100644
index 000000000..740568ca0
--- /dev/null
+++ b/src/qdoc/config.h
@@ -0,0 +1,319 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL21$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** 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 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** As a special exception, The Qt Company gives you certain additional
+** rights. These rights are described in The Qt Company LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*
+ config.h
+*/
+
+#ifndef CONFIG_H
+#define CONFIG_H
+
+#include <qmap.h>
+#include <qset.h>
+#include <qstringlist.h>
+#include <qstack.h>
+#include <qpair.h>
+#include "location.h"
+
+QT_BEGIN_NAMESPACE
+
+/*
+ This struct contains all the information for
+ one config variable found in a qdocconf file.
+ */
+struct ConfigVar {
+ bool plus_;
+ QString name_;
+ QStringList values_;
+ QString currentPath_;
+ Location location_;
+
+ ConfigVar() : plus_(false) { }
+
+ ConfigVar(const QString& name, const QStringList& values, const QString& dir)
+ : plus_(true), name_(name), values_(values), currentPath_(dir) { }
+
+ ConfigVar(const QString& name, const QStringList& values, const QString& dir, const Location& loc)
+ : plus_(false), name_(name), values_(values), currentPath_(dir), location_(loc) { }
+};
+
+/*
+ In this multimap, the key is a config variable name.
+ */
+typedef QMultiMap<QString, ConfigVar> ConfigVarMultimap;
+
+class Config
+{
+ Q_DECLARE_TR_FUNCTIONS(QDoc::Config)
+
+public:
+ Config(const QString& programName);
+ ~Config();
+
+ static bool debug_;
+
+ 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 lastLocation_; }
+ bool getBool(const QString& var) const;
+ int getInt(const QString& var) const;
+ QString getOutputDir() const;
+ QSet<QString> getOutputFormats() const;
+ QString getString(const QString& var) const;
+ QSet<QString> getStringSet(const QString& var) const;
+ QStringList getStringList(const QString& var) const;
+ QStringList getCanonicalPathList(const QString& var, bool validate = false) const;
+ QRegExp getRegExp(const QString& var) const;
+ QList<QRegExp> getRegExpList(const QString& var) const;
+ QSet<QString> subVars(const QString& var) const;
+ void subVarsAndValues(const QString& var, ConfigVarMultimap& t) const;
+ QStringList getAllFiles(const QString& filesVar,
+ const QString& dirsVar,
+ const QSet<QString> &excludedDirs = QSet<QString>(),
+ const QSet<QString> &excludedFiles = QSet<QString>());
+ QString getIncludeFilePath(const QString& fileName) const;
+ QStringList getExampleQdocFiles(const QSet<QString> &excludedDirs, const QSet<QString> &excludedFiles);
+ QStringList getExampleImageFiles(const QSet<QString> &excludedDirs, const QSet<QString> &excludedFiles);
+
+ static QStringList loadMaster(const QString& fileName);
+ static QStringList getFilesHere(const QString& dir,
+ const QString& nameFilter,
+ const Location &location = Location(),
+ const QSet<QString> &excludedDirs = QSet<QString>(),
+ const QSet<QString> &excludedFiles = 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);
+ static void pushWorkingDir(const QString& dir);
+ static QString popWorkingDir();
+
+ static const QString dot;
+
+ static bool generateExamples;
+ static QString installDir;
+ static QString overrideOutputDir;
+ static QSet<QString> overrideOutputFormats;
+
+private:
+ static bool isMetaKeyChar(QChar ch);
+ void load(Location location, const QString& fileName);
+
+ QString prog;
+ Location loc;
+ Location lastLocation_;
+ ConfigVarMultimap configVars_;
+
+ static QMap<QString, QString> uncompressedFiles;
+ static QMap<QString, QString> extractedDirs;
+ static int numInstances;
+ static QStack<QString> workingDirs_;
+ static QMap<QString, QStringList> includeFilesMap_;
+};
+
+struct ConfigStrings
+{
+ static QString ALIAS;
+ static QString AUTOLINKERRORS;
+ static QString BASE;
+ static QString BASEDIR;
+ static QString BUILDVERSION;
+ static QString CODEINDENT;
+ static QString CODEPREFIX;
+ static QString CODESUFFIX;
+ static QString CPPCLASSESPAGE;
+ static QString DEFINES;
+ static QString DEPENDS;
+ static QString DESCRIPTION;
+ static QString EDITION;
+ static QString ENDHEADER;
+ static QString EXAMPLEDIRS;
+ static QString EXAMPLES;
+ static QString EXAMPLESINSTALLPATH;
+ static QString EXCLUDEDIRS;
+ static QString EXCLUDEFILES;
+ static QString EXTRAIMAGES;
+ static QString FALSEHOODS;
+ static QString FORMATTING;
+ static QString GENERATEINDEX;
+ static QString HEADERDIRS;
+ static QString HEADERS;
+ static QString HEADERSCRIPTS;
+ static QString HEADERSTYLES;
+ static QString HOMEPAGE;
+ static QString IGNOREDIRECTIVES;
+ static QString IGNORETOKENS;
+ static QString IMAGEDIRS;
+ static QString IMAGES;
+ static QString INDEXES;
+ static QString LANDINGPAGE;
+ static QString LANGUAGE;
+ static QString MACRO;
+ static QString MANIFESTMETA;
+ static QString NATURALLANGUAGE;
+ static QString NAVIGATION;
+ static QString NOLINKERRORS;
+ static QString OBSOLETELINKS;
+ static QString OUTPUTDIR;
+ static QString OUTPUTENCODING;
+ static QString OUTPUTLANGUAGE;
+ static QString OUTPUTFORMATS;
+ static QString OUTPUTPREFIXES;
+ static QString OUTPUTSUFFIXES;
+ static QString PROJECT;
+ static QString REDIRECTDOCUMENTATIONTODEVNULL;
+ static QString QHP;
+ static QString QUOTINGINFORMATION;
+ static QString SCRIPTDIRS;
+ static QString SCRIPTS;
+ static QString SHOWINTERNAL;
+ static QString SINGLEEXEC;
+ static QString SOURCEDIRS;
+ static QString SOURCEENCODING;
+ static QString SOURCES;
+ static QString SPURIOUS;
+ static QString STYLEDIRS;
+ static QString STYLE;
+ static QString STYLES;
+ static QString STYLESHEETS;
+ static QString SYNTAXHIGHLIGHTING;
+ static QString TEMPLATEDIR;
+ static QString TABSIZE;
+ static QString TAGFILE;
+ static QString TRANSLATORS;
+ static QString URL;
+ static QString VERSION;
+ static QString VERSIONSYM;
+ static QString FILEEXTENSIONS;
+ static QString IMAGEEXTENSIONS;
+ static QString QMLONLY;
+ static QString QMLTYPESPAGE;
+ static QString WRITEQAPAGES;
+};
+
+#define CONFIG_ALIAS ConfigStrings::ALIAS
+#define CONFIG_AUTOLINKERRORS ConfigStrings::AUTOLINKERRORS
+#define CONFIG_BASE ConfigStrings::BASE
+#define CONFIG_BASEDIR ConfigStrings::BASEDIR
+#define CONFIG_BUILDVERSION ConfigStrings::BUILDVERSION
+#define CONFIG_CODEINDENT ConfigStrings::CODEINDENT
+#define CONFIG_CODEPREFIX ConfigStrings::CODEPREFIX
+#define CONFIG_CODESUFFIX ConfigStrings::CODESUFFIX
+#define CONFIG_CPPCLASSESPAGE ConfigStrings::CPPCLASSESPAGE
+#define CONFIG_DEFINES ConfigStrings::DEFINES
+#define CONFIG_DEPENDS ConfigStrings::DEPENDS
+#define CONFIG_DESCRIPTION ConfigStrings::DESCRIPTION
+#define CONFIG_EDITION ConfigStrings::EDITION
+#define CONFIG_ENDHEADER ConfigStrings::ENDHEADER
+#define CONFIG_EXAMPLEDIRS ConfigStrings::EXAMPLEDIRS
+#define CONFIG_EXAMPLES ConfigStrings::EXAMPLES
+#define CONFIG_EXAMPLESINSTALLPATH ConfigStrings::EXAMPLESINSTALLPATH
+#define CONFIG_EXCLUDEDIRS ConfigStrings::EXCLUDEDIRS
+#define CONFIG_EXCLUDEFILES ConfigStrings::EXCLUDEFILES
+#define CONFIG_EXTRAIMAGES ConfigStrings::EXTRAIMAGES
+#define CONFIG_FALSEHOODS ConfigStrings::FALSEHOODS
+#define CONFIG_FORMATTING ConfigStrings::FORMATTING
+#define CONFIG_GENERATEINDEX ConfigStrings::GENERATEINDEX
+#define CONFIG_HEADERDIRS ConfigStrings::HEADERDIRS
+#define CONFIG_HEADERS ConfigStrings::HEADERS
+#define CONFIG_HEADERSCRIPTS ConfigStrings::HEADERSCRIPTS
+#define CONFIG_HEADERSTYLES ConfigStrings::HEADERSTYLES
+#define CONFIG_HOMEPAGE ConfigStrings::HOMEPAGE
+#define CONFIG_IGNOREDIRECTIVES ConfigStrings::IGNOREDIRECTIVES
+#define CONFIG_IGNORETOKENS ConfigStrings::IGNORETOKENS
+#define CONFIG_IMAGEDIRS ConfigStrings::IMAGEDIRS
+#define CONFIG_IMAGES ConfigStrings::IMAGES
+#define CONFIG_INDEXES ConfigStrings::INDEXES
+#define CONFIG_LANDINGPAGE ConfigStrings::LANDINGPAGE
+#define CONFIG_LANGUAGE ConfigStrings::LANGUAGE
+#define CONFIG_MACRO ConfigStrings::MACRO
+#define CONFIG_MANIFESTMETA ConfigStrings::MANIFESTMETA
+#define CONFIG_NATURALLANGUAGE ConfigStrings::NATURALLANGUAGE
+#define CONFIG_NAVIGATION ConfigStrings::NAVIGATION
+#define CONFIG_NOLINKERRORS ConfigStrings::NOLINKERRORS
+#define CONFIG_OBSOLETELINKS ConfigStrings::OBSOLETELINKS
+#define CONFIG_OUTPUTDIR ConfigStrings::OUTPUTDIR
+#define CONFIG_OUTPUTENCODING ConfigStrings::OUTPUTENCODING
+#define CONFIG_OUTPUTLANGUAGE ConfigStrings::OUTPUTLANGUAGE
+#define CONFIG_OUTPUTFORMATS ConfigStrings::OUTPUTFORMATS
+#define CONFIG_OUTPUTPREFIXES ConfigStrings::OUTPUTPREFIXES
+#define CONFIG_OUTPUTSUFFIXES ConfigStrings::OUTPUTSUFFIXES
+#define CONFIG_PROJECT ConfigStrings::PROJECT
+#define CONFIG_REDIRECTDOCUMENTATIONTODEVNULL ConfigStrings::REDIRECTDOCUMENTATIONTODEVNULL
+#define CONFIG_QHP ConfigStrings::QHP
+#define CONFIG_QUOTINGINFORMATION ConfigStrings::QUOTINGINFORMATION
+#define CONFIG_SCRIPTDIRS ConfigStrings::SCRIPTDIRS
+#define CONFIG_SCRIPTS ConfigStrings::SCRIPTS
+#define CONFIG_SHOWINTERNAL ConfigStrings::SHOWINTERNAL
+#define CONFIG_SINGLEEXEC ConfigStrings::SINGLEEXEC
+#define CONFIG_SOURCEDIRS ConfigStrings::SOURCEDIRS
+#define CONFIG_SOURCEENCODING ConfigStrings::SOURCEENCODING
+#define CONFIG_SOURCES ConfigStrings::SOURCES
+#define CONFIG_SPURIOUS ConfigStrings::SPURIOUS
+#define CONFIG_STYLEDIRS ConfigStrings::STYLEDIRS
+#define CONFIG_STYLE ConfigStrings::STYLE
+#define CONFIG_STYLES ConfigStrings::STYLES
+#define CONFIG_STYLESHEETS ConfigStrings::STYLESHEETS
+#define CONFIG_SYNTAXHIGHLIGHTING ConfigStrings::SYNTAXHIGHLIGHTING
+#define CONFIG_TEMPLATEDIR ConfigStrings::TEMPLATEDIR
+#define CONFIG_TABSIZE ConfigStrings::TABSIZE
+#define CONFIG_TAGFILE ConfigStrings::TAGFILE
+#define CONFIG_TRANSLATORS ConfigStrings::TRANSLATORS
+#define CONFIG_URL ConfigStrings::URL
+#define CONFIG_VERSION ConfigStrings::VERSION
+#define CONFIG_VERSIONSYM ConfigStrings::VERSIONSYM
+#define CONFIG_FILEEXTENSIONS ConfigStrings::FILEEXTENSIONS
+#define CONFIG_IMAGEEXTENSIONS ConfigStrings::IMAGEEXTENSIONS
+#define CONFIG_QMLONLY ConfigStrings::QMLONLY
+#define CONFIG_QMLTYPESPAGE ConfigStrings::QMLTYPESPAGE
+#define CONFIG_WRITEQAPAGES ConfigStrings::WRITEQAPAGES
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/qdoc/cppcodemarker.cpp b/src/qdoc/cppcodemarker.cpp
new file mode 100644
index 000000000..01ff827d5
--- /dev/null
+++ b/src/qdoc/cppcodemarker.cpp
@@ -0,0 +1,1326 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL21$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** 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 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** As a special exception, The Qt Company gives you certain additional
+** rights. These rights are described in The Qt Company LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*
+ cppcodemarker.cpp
+*/
+
+#include "atom.h"
+#include "cppcodemarker.h"
+#include "node.h"
+#include "text.h"
+#include "tree.h"
+#include <qdebug.h>
+#include <ctype.h>
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ The constructor does nothing.
+ */
+CppCodeMarker::CppCodeMarker()
+{
+ // nothing.
+}
+
+/*!
+ The destructor does nothing.
+ */
+CppCodeMarker::~CppCodeMarker()
+{
+ // nothing.
+}
+
+/*!
+ Returns \c true.
+ */
+bool CppCodeMarker::recognizeCode(const QString & /* code */)
+{
+ return true;
+}
+
+/*!
+ Returns \c true if \a ext is any of a list of file extensions
+ for the C++ language.
+ */
+bool CppCodeMarker::recognizeExtension(const QString& extension)
+{
+ QByteArray ext = extension.toLatin1();
+ return ext == "c" ||
+ ext == "c++" ||
+ ext == "qdoc" ||
+ ext == "qtt" ||
+ ext == "qtx" ||
+ ext == "cc" ||
+ ext == "cpp" ||
+ ext == "cxx" ||
+ ext == "ch" ||
+ ext == "h" ||
+ ext == "h++" ||
+ ext == "hh" ||
+ ext == "hpp" ||
+ ext == "hxx";
+}
+
+/*!
+ Returns \c true if \a lang is either "C" or "Cpp".
+ */
+bool CppCodeMarker::recognizeLanguage(const QString &lang)
+{
+ return lang == QLatin1String("C") || lang == QLatin1String("Cpp");
+}
+
+/*!
+ Returns the type of atom used to represent C++ code in the documentation.
+*/
+Atom::AtomType CppCodeMarker::atomType() const
+{
+ return Atom::Code;
+}
+
+QString CppCodeMarker::markedUpCode(const QString &code,
+ const Node *relative,
+ const Location &location)
+{
+ return addMarkUp(code, relative, location);
+}
+
+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) && !node->isQmlNode() && !node->isJsNode())
+ 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:
+ case Node::QmlSignal:
+ case Node::QmlSignalHandler:
+ case Node::QmlMethod:
+ func = (const FunctionNode *) node;
+
+ if (style != Subpage && !func->returnType().isEmpty())
+ synopsis = typified(func->returnType(), true);
+ synopsis += name;
+ if (func->metaness() != FunctionNode::MacroWithoutParams) {
+ synopsis += QLatin1Char('(');
+ if (!func->parameters().isEmpty()) {
+ QVector<Parameter>::ConstIterator p = func->parameters().constBegin();
+ while (p != func->parameters().constEnd()) {
+ if (p != func->parameters().constBegin())
+ synopsis += ", ";
+ synopsis += typified((*p).dataType(), true);
+ if (style != Subpage && !(*p).name().isEmpty())
+ synopsis +=
+ "<@param>" + protect((*p).name()) + "</@param>";
+ synopsis += protect((*p).rightType());
+ if (style != Subpage && !(*p).defaultValue().isEmpty())
+ synopsis += " = " + protect((*p).defaultValue());
+ ++p;
+ }
+ }
+ synopsis += QLatin1Char(')');
+ }
+ 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 == Subpage) {
+ 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 += QLatin1Char('[') + bracketed.join(' ') + QStringLiteral("] ");
+ }
+ 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 += QLatin1Char(' ');
+ synopsis += QLatin1Char('}');
+ }
+ 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 == Subpage) {
+ synopsis = name + " : " + typified(variable->dataType());
+ }
+ else {
+ synopsis = typified(variable->leftType(), true) +
+ 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 extra + synopsis;
+}
+
+/*!
+ */
+QString CppCodeMarker::markedUpQmlItem(const Node* node, bool summary)
+{
+ QString name = taggedQmlNode(node);
+ if (summary)
+ name = linkTag(node,name);
+ else if (node->isQmlProperty() || node->isJsProperty()) {
+ const QmlPropertyNode* pn = static_cast<const QmlPropertyNode*>(node);
+ if (pn->isAttached())
+ name.prepend(pn->element() + QLatin1Char('.'));
+ }
+ name = "<@name>" + name + "</@name>";
+ QString synopsis;
+ if (node->isQmlProperty() || node->isJsProperty()) {
+ const QmlPropertyNode* pn = static_cast<const QmlPropertyNode*>(node);
+ synopsis = name + " : " + typified(pn->dataType());
+ }
+ else if ((node->type() == Node::QmlMethod) ||
+ (node->type() == Node::QmlSignal) ||
+ (node->type() == Node::QmlSignalHandler)) {
+ const FunctionNode* func = static_cast<const FunctionNode*>(node);
+ if (!func->returnType().isEmpty())
+ synopsis = typified(func->returnType(), true) + name;
+ else
+ synopsis = name;
+ synopsis += QLatin1Char('(');
+ if (!func->parameters().isEmpty()) {
+ QVector<Parameter>::ConstIterator p = func->parameters().constBegin();
+ while (p != func->parameters().constEnd()) {
+ if (p != func->parameters().constBegin())
+ synopsis += ", ";
+ synopsis += typified((*p).dataType(), true);
+ if (!(*p).name().isEmpty())
+ synopsis += "<@param>" + protect((*p).name()) + "</@param>";
+ synopsis += protect((*p).rightType());
+ ++p;
+ }
+ }
+ synopsis += QLatin1Char(')');
+ }
+ else
+ synopsis = name;
+
+ QString extra;
+ if (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)
+{
+ if (relative->type() != Node::Enum)
+ return enumValue;
+
+ 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.constBegin();
+ while (inc != includes.constEnd()) {
+ code += "<@preprocessor>#include &lt;<@headerfile>" + *inc + "</@headerfile>&gt;</@preprocessor>\n";
+ ++inc;
+ }
+ return code;
+}
+
+QString CppCodeMarker::functionBeginRegExp(const QString& funcName)
+{
+ return QLatin1Char('^') + QRegExp::escape(funcName) + QLatin1Char('$');
+
+}
+
+QString CppCodeMarker::functionEndRegExp(const QString& /* funcName */)
+{
+ return "^\\}$";
+}
+
+QList<Section> CppCodeMarker::sections(const Aggregate *inner,
+ SynopsisStyle style,
+ Status status)
+{
+ QList<Section> sections;
+
+ if (inner->isClass()) {
+ if (style == Summary) {
+ FastSection privateFunctions(inner,
+ "Private Functions",
+ QString(),
+ "private function",
+ "private functions");
+ FastSection privateSlots(inner, "Private Slots", QString(), "private slot", "private slots");
+ FastSection privateTypes(inner, "Private Types", QString(), "private type", "private types");
+ FastSection protectedFunctions(inner,
+ "Protected Functions",
+ QString(),
+ "protected function",
+ "protected functions");
+ FastSection protectedSlots(inner,
+ "Protected Slots",
+ QString(),
+ "protected slot",
+ "protected slots");
+ FastSection protectedTypes(inner,
+ "Protected Types",
+ QString(),
+ "protected type",
+ "protected types");
+ FastSection protectedVariables(inner,
+ "Protected Variables",
+ QString(),
+ "protected type",
+ "protected variables");
+ FastSection publicFunctions(inner,
+ "Public Functions",
+ QString(),
+ "public function",
+ "public functions");
+ FastSection publicSignals(inner, "Signals", QString(), "signal", "signals");
+ FastSection publicSlots(inner, "Public Slots", QString(), "public slot", "public slots");
+ FastSection publicTypes(inner, "Public Types", QString(), "public type", "public types");
+ FastSection publicVariables(inner,
+ "Public Variables",
+ QString(),
+ "public variable",
+ "public variables");
+ FastSection properties(inner, "Properties", QString(), "property", "properties");
+ FastSection relatedNonMembers(inner,
+ "Related Non-Members",
+ QString(),
+ "related non-member",
+ "related non-members");
+ FastSection staticPrivateMembers(inner,
+ "Static Private Members",
+ QString(),
+ "static private member",
+ "static private members");
+ FastSection staticProtectedMembers(inner,
+ "Static Protected Members",
+ QString(),
+ "static protected member",
+ "static protected members");
+ FastSection staticPublicMembers(inner,
+ "Static Public Members",
+ QString(),
+ "static public member",
+ "static public members");
+ FastSection macros(inner, "Macros", QString(), "macro", "macros");
+
+ NodeList::ConstIterator r = inner->relatedNodes().constBegin();
+ while (r != inner->relatedNodes().constEnd()) {
+ 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 Aggregate *> stack;
+ stack.push(inner);
+ while (!stack.isEmpty()) {
+ const Aggregate* ancestor = stack.pop();
+
+ NodeList::ConstIterator c = ancestor->childNodes().constBegin();
+ while (c != ancestor->childNodes().constEnd()) {
+ 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();
+ if (func->hasAssociatedProperties() && !func->hasActiveAssociatedProperty()) {
+ ++c;
+ continue;
+ }
+ }
+ 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) {
+ if (!insertReimpFunc(publicFunctions,*c,status)) {
+ 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) {
+ if (!insertReimpFunc(protectedFunctions,*c,status)) {
+ 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) {
+ if (!insertReimpFunc(privateFunctions,*c,status)) {
+ insert(privateFunctions, *c, style, status);
+ }
+ }
+ else {
+ insert(privateTypes,*c,style,status);
+ }
+ }
+ ++c;
+ }
+
+ if (ancestor->isClass()) {
+ const ClassNode* cn = static_cast<const ClassNode*>(ancestor);
+ QList<RelatedClass>::ConstIterator r = cn->baseClasses().constBegin();
+ while (r != cn->baseClasses().constEnd()) {
+ if ((*r).node_)
+ 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(inner,"Member Function Documentation","func","member","members");
+ FastSection memberTypes(inner,"Member Type Documentation","types","member","members");
+ FastSection memberVariables(inner,"Member Variable Documentation","vars","member","members");
+ FastSection properties(inner,"Property Documentation","prop","member","members");
+ FastSection relatedNonMembers(inner,"Related Non-Members","relnonmem","member","members");
+ FastSection macros(inner,"Macro Documentation","macros","member","members");
+
+ NodeList::ConstIterator r = inner->relatedNodes().constBegin();
+ while (r != inner->relatedNodes().constEnd()) {
+ 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 = inner->childNodes().constBegin();
+ while (c != inner->childNodes().constEnd()) {
+ 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->hasAssociatedProperties() || !function->doc().isEmpty())
+ 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(inner,QString(),QString(),"member","members");
+
+ QStack<const Aggregate*> stack;
+ stack.push(inner);
+
+ while (!stack.isEmpty()) {
+ const Aggregate* ancestor = stack.pop();
+ NodeList::ConstIterator c = ancestor->childNodes().constBegin();
+ while (c != ancestor->childNodes().constEnd()) {
+ if ((*c)->access() != Node::Private && (*c)->type() != Node::Property)
+ insert(all, *c, style, status);
+ ++c;
+ }
+
+ if (ancestor->isClass()) {
+ const ClassNode* cn = static_cast<const ClassNode*>(ancestor);
+ QList<RelatedClass>::ConstIterator r = cn->baseClasses().constBegin();
+ while (r != cn->baseClasses().constEnd()) {
+ if ((*r).node_)
+ stack.prepend((*r).node_);
+ ++r;
+ }
+ }
+ }
+ append(sections, all);
+ }
+ }
+ else {
+ if (style == Summary || style == Detailed) {
+ FastSection namespaces(inner,
+ "Namespaces",
+ style == Detailed ? "nmspace" : QString(),
+ "namespace",
+ "namespaces");
+ FastSection classes(inner,
+ "Classes",
+ style == Detailed ? "classes" : QString(),
+ "class",
+ "classes");
+ FastSection types(inner,
+ style == Summary ? "Types" : "Type Documentation",
+ style == Detailed ? "types" : QString(),
+ "type",
+ "types");
+ FastSection variables(inner,
+ style == Summary ? "Variables" : "Variable Documentation",
+ style == Detailed ? "vars" : QString(),
+ "variable",
+ "variables");
+ FastSection staticVariables(inner,
+ "Static Variables",
+ QString(),
+ "static variable",
+ "static variables");
+ FastSection functions(inner,
+ style == Summary ?
+ "Functions" : "Function Documentation",
+ style == Detailed ? "func" : QString(),
+ "function",
+ "functions");
+ FastSection macros(inner,
+ style == Summary ?
+ "Macros" : "Macro Documentation",
+ style == Detailed ? "macros" : QString(),
+ "macro",
+ "macros");
+
+ NodeList nodeList = inner->childNodes();
+ nodeList += inner->relatedNodes();
+
+ NodeList::ConstIterator n = nodeList.constBegin();
+ while (n != nodeList.constEnd()) {
+ 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;
+ case Node::Variable:
+ {
+ const VariableNode* var = static_cast<const VariableNode*>(*n);
+ if (!var->doc().isEmpty()) {
+ if (var->isStatic())
+ insert(staticVariables,*n,style,status);
+ else
+ insert(variables, *n, style, status);
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ ++n;
+ }
+ if (inner->isNamespace()) {
+ const NamespaceNode* ns = static_cast<const NamespaceNode*>(inner);
+ if (!ns->orphans().isEmpty()) {
+ foreach (Node* n, ns->orphans()) {
+ // Use inner as a temporary parent when inserting orphans
+ Aggregate* p = n->parent();
+ n->setParent(const_cast<Aggregate*>(inner));
+ if (n->isClass())
+ insert(classes, n, style, status);
+ else if (n->isNamespace())
+ insert(namespaces, n, style, status);
+ n->setParent(p);
+ }
+ }
+ }
+ append(sections, namespaces);
+ append(sections, classes);
+ append(sections, types);
+ append(sections, variables);
+ append(sections, staticVariables);
+ append(sections, functions);
+ append(sections, macros);
+ }
+ }
+
+ return sections;
+}
+
+/*
+ @char
+ @class
+ @comment
+ @function
+ @keyword
+ @number
+ @op
+ @preprocessor
+ @string
+ @type
+*/
+
+QString CppCodeMarker::addMarkUp(const QString &in,
+ const Node * /* relative */,
+ const Location & /* location */)
+{
+ static QSet<QString> types;
+ static QSet<QString> keywords;
+
+ if (types.isEmpty()) {
+ // initialize statics
+ Q_ASSERT(keywords.isEmpty());
+ static const QString typeTable[] = {
+ QLatin1String("bool"), QLatin1String("char"), QLatin1String("double"), QLatin1String("float"), QLatin1String("int"), QLatin1String("long"), QLatin1String("short"),
+ QLatin1String("signed"), QLatin1String("unsigned"), QLatin1String("uint"), QLatin1String("ulong"), QLatin1String("ushort"), QLatin1String("uchar"), QLatin1String("void"),
+ QLatin1String("qlonglong"), QLatin1String("qulonglong"),
+ QLatin1String("qint"), QLatin1String("qint8"), QLatin1String("qint16"), QLatin1String("qint32"), QLatin1String("qint64"),
+ QLatin1String("quint"), QLatin1String("quint8"), QLatin1String("quint16"), QLatin1String("quint32"), QLatin1String("quint64"),
+ QLatin1String("qreal"), QLatin1String("cond")
+ };
+
+ static const QString keywordTable[] = {
+ QLatin1String("and"), QLatin1String("and_eq"), QLatin1String("asm"), QLatin1String("auto"), QLatin1String("bitand"), QLatin1String("bitor"), QLatin1String("break"),
+ QLatin1String("case"), QLatin1String("catch"), QLatin1String("class"), QLatin1String("compl"), QLatin1String("const"), QLatin1String("const_cast"),
+ QLatin1String("continue"), QLatin1String("default"), QLatin1String("delete"), QLatin1String("do"), QLatin1String("dynamic_cast"), QLatin1String("else"),
+ QLatin1String("enum"), QLatin1String("explicit"), QLatin1String("export"), QLatin1String("extern"), QLatin1String("false"), QLatin1String("for"), QLatin1String("friend"),
+ QLatin1String("goto"), QLatin1String("if"), QLatin1String("include"), QLatin1String("inline"), QLatin1String("monitor"), QLatin1String("mutable"), QLatin1String("namespace"),
+ QLatin1String("new"), QLatin1String("not"), QLatin1String("not_eq"), QLatin1String("operator"), QLatin1String("or"), QLatin1String("or_eq"), QLatin1String("private"), QLatin1String("protected"),
+ QLatin1String("public"), QLatin1String("register"), QLatin1String("reinterpret_cast"), QLatin1String("return"), QLatin1String("sizeof"),
+ QLatin1String("static"), QLatin1String("static_cast"), QLatin1String("struct"), QLatin1String("switch"), QLatin1String("template"), QLatin1String("this"),
+ QLatin1String("throw"), QLatin1String("true"), QLatin1String("try"), QLatin1String("typedef"), QLatin1String("typeid"), QLatin1String("typename"), QLatin1String("union"),
+ QLatin1String("using"), QLatin1String("virtual"), QLatin1String("volatile"), QLatin1String("wchar_t"), QLatin1String("while"), QLatin1String("xor"),
+ QLatin1String("xor_eq"), QLatin1String("synchronized"),
+ // Qt specific
+ QLatin1String("signals"), QLatin1String("slots"), QLatin1String("emit")
+ };
+
+ types.reserve(sizeof(typeTable) / sizeof(QString));
+ for (int j = sizeof(typeTable) / sizeof(QString) - 1; j; --j)
+ types.insert(typeTable[j]);
+
+ keywords.reserve(sizeof(keywordTable) / sizeof(QString));
+ for (int j = sizeof(keywordTable) / sizeof(QString) - 1; j; --j)
+ keywords.insert(keywordTable[j]);
+ }
+#define readChar() \
+ ch = (i < (int)code.length()) ? code[i++].cell() : EOF
+
+ QString code = in;
+ QString out;
+ QStringRef text;
+ int braceDepth = 0;
+ int parenDepth = 0;
+ int i = 0;
+ int start = 0;
+ int finish = 0;
+ QChar ch;
+ QRegExp classRegExp("Qt?(?:[A-Z3]+[a-z][A-Za-z]*|t)");
+ QRegExp functionRegExp("q([A-Z][a-z]+)+");
+ QRegExp findFunctionRegExp(QStringLiteral("^\\s*\\("));
+
+ readChar();
+
+ while (ch != EOF) {
+ QString tag;
+ bool target = false;
+
+ if (ch.isLetter() || ch == '_') {
+ QString ident;
+ do {
+ ident += ch;
+ finish = i;
+ readChar();
+ } while (ch.isLetterOrNumber() || ch == '_');
+
+ if (classRegExp.exactMatch(ident)) {
+ tag = QStringLiteral("type");
+ } else if (functionRegExp.exactMatch(ident)) {
+ tag = QStringLiteral("func");
+ target = true;
+ } else if (types.contains(ident)) {
+ tag = QStringLiteral("type");
+ } else if (keywords.contains(ident)) {
+ tag = QStringLiteral("keyword");
+ } else if (braceDepth == 0 && parenDepth == 0) {
+ if (code.indexOf(findFunctionRegExp, i - 1) == i - 1)
+ tag = QStringLiteral("func");
+ target = true;
+ }
+ } else if (ch.isDigit()) {
+ do {
+ finish = i;
+ readChar();
+ } while (ch.isLetterOrNumber() || ch == '.');
+ tag = QStringLiteral("number");
+ } else {
+ switch (ch.unicode()) {
+ case '+':
+ case '-':
+ case '!':
+ case '%':
+ case '^':
+ case '&':
+ case '*':
+ case ',':
+ case '.':
+ case '<':
+ case '=':
+ case '>':
+ case '?':
+ case '[':
+ case ']':
+ case '|':
+ case '~':
+ finish = i;
+ readChar();
+ tag = QStringLiteral("op");
+ break;
+ case '"':
+ finish = i;
+ readChar();
+
+ while (ch != EOF && ch != '"') {
+ if (ch == '\\')
+ readChar();
+ readChar();
+ }
+ finish = i;
+ readChar();
+ tag = QStringLiteral("string");
+ break;
+ case '#':
+ finish = i;
+ readChar();
+ while (ch != EOF && ch != '\n') {
+ if (ch == '\\')
+ readChar();
+ finish = i;
+ readChar();
+ }
+ tag = QStringLiteral("preprocessor");
+ break;
+ case '\'':
+ finish = i;
+ readChar();
+
+ while (ch != EOF && ch != '\'') {
+ if (ch == '\\')
+ readChar();
+ readChar();
+ }
+ finish = i;
+ readChar();
+ tag = QStringLiteral("char");
+ break;
+ case '(':
+ finish = i;
+ readChar();
+ parenDepth++;
+ break;
+ case ')':
+ finish = i;
+ readChar();
+ parenDepth--;
+ break;
+ case ':':
+ finish = i;
+ readChar();
+ if (ch == ':') {
+ finish = i;
+ readChar();
+ tag = QStringLiteral("op");
+ }
+ break;
+ case '/':
+ finish = i;
+ readChar();
+ if (ch == '/') {
+ do {
+ finish = i;
+ readChar();
+ } while (ch != EOF && ch != '\n');
+ tag = QStringLiteral("comment");
+ } else if (ch == '*') {
+ bool metAster = false;
+ bool metAsterSlash = false;
+
+ finish = i;
+ readChar();
+
+ while (!metAsterSlash) {
+ if (ch == EOF)
+ break;
+
+ if (ch == '*')
+ metAster = true;
+ else if (metAster && ch == '/')
+ metAsterSlash = true;
+ else
+ metAster = false;
+ finish = i;
+ readChar();
+ }
+ tag = QStringLiteral("comment");
+ } else {
+ tag = QStringLiteral("op");
+ }
+ break;
+ case '{':
+ finish = i;
+ readChar();
+ braceDepth++;
+ break;
+ case '}':
+ finish = i;
+ readChar();
+ braceDepth--;
+ break;
+ default:
+ finish = i;
+ readChar();
+ }
+ }
+
+ text = code.midRef(start, finish - start);
+ start = finish;
+
+ if (!tag.isEmpty()) {
+ out += QStringLiteral("<@");
+ out += tag;
+ if (target) {
+ out += QStringLiteral(" target=\"");
+ out += text;
+ out += QStringLiteral("()\"");
+ }
+ out += QStringLiteral(">");
+ }
+
+ appendProtectedString(&out, text);
+
+ if (!tag.isEmpty()) {
+ out += QStringLiteral("</@");
+ out += tag;
+ out += QStringLiteral(">");
+ }
+ }
+
+ if (start < code.length()) {
+ appendProtectedString(&out, code.midRef(start));
+ }
+
+ return out;
+}
+
+/*!
+ This function is for documenting QML properties. It returns
+ the list of documentation sections for the children of the
+ \a qmlTypeNode.
+ */
+QList<Section> CppCodeMarker::qmlSections(QmlTypeNode* qmlTypeNode, SynopsisStyle style, Status status)
+{
+ QList<Section> sections;
+ if (qmlTypeNode) {
+ if (style == Summary) {
+ FastSection qmlproperties(qmlTypeNode,
+ "Properties",
+ QString(),
+ "property",
+ "properties");
+ FastSection qmlattachedproperties(qmlTypeNode,
+ "Attached Properties",
+ QString(),
+ "property",
+ "properties");
+ FastSection qmlsignals(qmlTypeNode,
+ "Signals",
+ QString(),
+ "signal",
+ "signals");
+ FastSection qmlsignalhandlers(qmlTypeNode,
+ "Signal Handlers",
+ QString(),
+ "signal handler",
+ "signal handlers");
+ FastSection qmlattachedsignals(qmlTypeNode,
+ "Attached Signals",
+ QString(),
+ "signal",
+ "signals");
+ FastSection qmlmethods(qmlTypeNode,
+ "Methods",
+ QString(),
+ "method",
+ "methods");
+ FastSection qmlattachedmethods(qmlTypeNode,
+ "Attached Methods",
+ QString(),
+ "method",
+ "methods");
+
+ QmlTypeNode* qcn = qmlTypeNode;
+ while (qcn != 0) {
+ NodeList::ConstIterator c = qcn->childNodes().constBegin();
+ while (c != qcn->childNodes().constEnd()) {
+ if ((*c)->status() == Node::Internal) {
+ ++c;
+ continue;
+ }
+ if ((*c)->isQmlPropertyGroup() || (*c)->isJsPropertyGroup()) {
+ insert(qmlproperties, *c, style, status);
+ }
+ else if ((*c)->isQmlProperty() || (*c)->isJsProperty()) {
+ const QmlPropertyNode* pn = static_cast<const QmlPropertyNode*>(*c);
+ if (pn->isAttached())
+ insert(qmlattachedproperties,*c,style, status);
+ else {
+ insert(qmlproperties,*c,style, status);
+ }
+ }
+ else if ((*c)->isQmlSignal() || (*c)->isJsSignal()) {
+ const FunctionNode* sn = static_cast<const FunctionNode*>(*c);
+ if (sn->isAttached())
+ insert(qmlattachedsignals,*c,style, status);
+ else
+ insert(qmlsignals,*c,style, status);
+ }
+ else if ((*c)->isQmlSignalHandler() || (*c)->isJsSignalHandler()) {
+ insert(qmlsignalhandlers,*c,style, status);
+ }
+ else if ((*c)->isQmlMethod() || (*c)->isJsMethod()) {
+ const FunctionNode* mn = static_cast<const FunctionNode*>(*c);
+ if (mn->isAttached())
+ insert(qmlattachedmethods,*c,style, status);
+ else
+ insert(qmlmethods,*c,style, status);
+ }
+ ++c;
+ }
+ if (qcn->qmlBaseNode() != 0) {
+ qcn = static_cast<QmlTypeNode*>(qcn->qmlBaseNode());
+ if (!qcn->isAbstract())
+ qcn = 0;
+ }
+ else
+ qcn = 0;
+ }
+ append(sections,qmlproperties);
+ append(sections,qmlattachedproperties);
+ append(sections,qmlsignals);
+ append(sections,qmlsignalhandlers);
+ append(sections,qmlattachedsignals);
+ append(sections,qmlmethods);
+ append(sections,qmlattachedmethods);
+ }
+ else if (style == Detailed) {
+ FastSection qmlproperties(qmlTypeNode, "Property Documentation","qmlprop","member","members");
+ FastSection qmlattachedproperties(qmlTypeNode,"Attached Property Documentation","qmlattprop",
+ "member","members");
+ FastSection qmlsignals(qmlTypeNode,"Signal Documentation","qmlsig","signal","signals");
+ FastSection qmlsignalhandlers(qmlTypeNode,"Signal Handler Documentation","qmlsighan","signal handler","signal handlers");
+ FastSection qmlattachedsignals(qmlTypeNode,"Attached Signal Documentation","qmlattsig",
+ "signal","signals");
+ FastSection qmlmethods(qmlTypeNode,"Method Documentation","qmlmeth","member","members");
+ FastSection qmlattachedmethods(qmlTypeNode,"Attached Method Documentation","qmlattmeth",
+ "member","members");
+ QmlTypeNode* qcn = qmlTypeNode;
+ while (qcn != 0) {
+ NodeList::ConstIterator c = qcn->childNodes().constBegin();
+ while (c != qcn->childNodes().constEnd()) {
+ if ((*c)->status() == Node::Internal) {
+ ++c;
+ continue;
+ }
+ if ((*c)->isQmlPropertyGroup() || (*c)->isJsPropertyGroup()) {
+ insert(qmlproperties,*c,style, status);
+ }
+ else if ((*c)->isQmlProperty() || (*c)->isJsProperty()) {
+ const QmlPropertyNode* pn = static_cast<const QmlPropertyNode*>(*c);
+ if (pn->isAttached())
+ insert(qmlattachedproperties,*c,style, status);
+ else
+ insert(qmlproperties,*c,style, status);
+ }
+ else if ((*c)->isQmlSignal() || (*c)->isJsSignal()) {
+ const FunctionNode* sn = static_cast<const FunctionNode*>(*c);
+ if (sn->isAttached())
+ insert(qmlattachedsignals,*c,style, status);
+ else
+ insert(qmlsignals,*c,style, status);
+ }
+ else if ((*c)->isQmlSignalHandler() || (*c)->isJsSignalHandler()) {
+ insert(qmlsignalhandlers,*c,style, status);
+ }
+ else if ((*c)->isQmlMethod() || (*c)->isJsMethod()) {
+ const FunctionNode* mn = static_cast<const FunctionNode*>(*c);
+ if (mn->isAttached())
+ insert(qmlattachedmethods,*c,style, status);
+ else
+ insert(qmlmethods,*c,style, status);
+ }
+ ++c;
+ }
+ if (qcn->qmlBaseNode() != 0) {
+ qcn = static_cast<QmlTypeNode*>(qcn->qmlBaseNode());
+ if (!qcn->isAbstract())
+ qcn = 0;
+ }
+ else
+ qcn = 0;
+ }
+ append(sections,qmlproperties);
+ append(sections,qmlattachedproperties);
+ append(sections,qmlsignals);
+ append(sections,qmlsignalhandlers);
+ append(sections,qmlattachedsignals);
+ append(sections,qmlmethods);
+ append(sections,qmlattachedmethods);
+ }
+ else {
+ /*
+ This is where the list of all members including inherited
+ members is prepared.
+ */
+ ClassMap* classMap = 0;
+ FastSection all(qmlTypeNode,QString(),QString(),"member","members");
+ QmlTypeNode* current = qmlTypeNode;
+ while (current != 0) {
+ /*
+ If the QML type is abstract, do not create
+ a new entry in the list for it. Instead,
+ add its members to the current entry.
+
+ However, if the first class is abstract,
+ there is no current entry. In that case,
+ create a new entry in the list anyway.
+ I'm not sure that is correct, but it at
+ least can prevent a crash.
+ */
+ if (!current->isAbstract() || !classMap) {
+ classMap = new ClassMap;
+ classMap->first = current;
+ all.classMapList_.append(classMap);
+ }
+ NodeList::ConstIterator c = current->childNodes().constBegin();
+ while (c != current->childNodes().constEnd()) {
+ if ((*c)->isQmlPropertyGroup() || (*c)->isJsPropertyGroup()) {
+ const QmlPropertyGroupNode* qpgn = static_cast<const QmlPropertyGroupNode*>(*c);
+ NodeList::ConstIterator p = qpgn->childNodes().constBegin();
+ while (p != qpgn->childNodes().constEnd()) {
+ if ((*p)->isQmlProperty() || (*c)->isJsProperty()) {
+ QString key = (*p)->name();
+ key = sortName(*p, &key);
+ all.memberMap.insert(key,*p);
+ classMap->second.insert(key,*p);
+ }
+ ++p;
+ }
+ }
+ else {
+ QString key = (*c)->name();
+ key = sortName(*c, &key);
+ all.memberMap.insert(key,*c);
+ classMap->second.insert(key,*c);
+ }
+ ++c;
+ }
+ current = current->qmlBaseNode();
+ while (current) {
+ if (current->isAbstract())
+ break;
+ if (current->isInternal())
+ current = current->qmlBaseNode();
+ else
+ break;
+ }
+ }
+ append(sections, all, true);
+ }
+ }
+
+ return sections;
+}
+
+QT_END_NAMESPACE
diff --git a/src/qdoc/cppcodemarker.h b/src/qdoc/cppcodemarker.h
new file mode 100644
index 000000000..aa759f299
--- /dev/null
+++ b/src/qdoc/cppcodemarker.h
@@ -0,0 +1,85 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL21$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** 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 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** As a special exception, The Qt Company gives you certain additional
+** rights. These rights are described in The Qt Company LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*
+ cppcodemarker.h
+*/
+
+#ifndef CPPCODEMARKER_H
+#define CPPCODEMARKER_H
+
+#include "codemarker.h"
+
+QT_BEGIN_NAMESPACE
+
+class CppCodeMarker : public CodeMarker
+{
+ Q_DECLARE_TR_FUNCTIONS(QDoc::CppCodeMarker)
+
+public:
+ CppCodeMarker();
+ ~CppCodeMarker();
+
+ virtual bool recognizeCode(const QString& code) Q_DECL_OVERRIDE;
+ virtual bool recognizeExtension(const QString& ext) Q_DECL_OVERRIDE;
+ virtual bool recognizeLanguage(const QString& lang) Q_DECL_OVERRIDE;
+ virtual Atom::AtomType atomType() const Q_DECL_OVERRIDE;
+ virtual QString markedUpCode(const QString& code,
+ const Node *relative,
+ const Location &location) Q_DECL_OVERRIDE;
+ virtual QString markedUpSynopsis(const Node *node,
+ const Node *relative,
+ SynopsisStyle style) Q_DECL_OVERRIDE;
+ virtual QString markedUpQmlItem(const Node *node, bool summary) Q_DECL_OVERRIDE;
+ virtual QString markedUpName(const Node *node) Q_DECL_OVERRIDE;
+ virtual QString markedUpFullName(const Node *node, const Node *relative) Q_DECL_OVERRIDE;
+ virtual QString markedUpEnumValue(const QString &enumValue, const Node *relative) Q_DECL_OVERRIDE;
+ virtual QString markedUpIncludes(const QStringList& includes) Q_DECL_OVERRIDE;
+ virtual QString functionBeginRegExp(const QString& funcName) Q_DECL_OVERRIDE;
+ virtual QString functionEndRegExp(const QString& funcName) Q_DECL_OVERRIDE;
+ virtual QList<Section> sections(const Aggregate *innerNode,
+ SynopsisStyle style,
+ Status status) Q_DECL_OVERRIDE;
+ virtual QList<Section> qmlSections(QmlTypeNode* qmlTypeNode,
+ SynopsisStyle style,
+ Status status = Okay) Q_DECL_OVERRIDE;
+
+private:
+ QString addMarkUp(const QString& protectedCode,
+ const Node *relative,
+ const Location &location);
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/qdoc/cppcodeparser.cpp b/src/qdoc/cppcodeparser.cpp
new file mode 100644
index 000000000..0405cc2c2
--- /dev/null
+++ b/src/qdoc/cppcodeparser.cpp
@@ -0,0 +1,2611 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL21$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** 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 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** As a special exception, The Qt Company gives you certain additional
+** rights. These rights are described in The Qt Company LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*
+ cppcodeparser.cpp
+*/
+
+#include <qfile.h>
+#include <stdio.h>
+#include <errno.h>
+#include "codechunk.h"
+#include "config.h"
+#include "cppcodeparser.h"
+#include "tokenizer.h"
+#include "qdocdatabase.h"
+#include <qdebug.h>
+#include "generator.h"
+
+QT_BEGIN_NAMESPACE
+
+/* qmake ignore Q_OBJECT */
+
+static bool inMacroCommand_ = false;
+static bool parsingHeaderFile_ = false;
+QStringList CppCodeParser::exampleFiles;
+QStringList CppCodeParser::exampleDirs;
+CppCodeParser* CppCodeParser::cppParser_ = 0;
+
+/*!
+ The constructor initializes some regular expressions
+ and calls reset().
+ */
+CppCodeParser::CppCodeParser()
+ : varComment("/\\*\\s*([a-zA-Z_0-9]+)\\s*\\*/"), sep("(?:<[^>]+>)?::")
+{
+ reset();
+ cppParser_ = this;
+}
+
+/*!
+ The destructor is trivial.
+ */
+CppCodeParser::~CppCodeParser()
+{
+ // nothing.
+}
+
+/*!
+ The constructor initializes a map of special node types
+ for identifying important nodes. And it initializes
+ some filters for identifying certain kinds of files.
+ */
+void CppCodeParser::initializeParser(const Config &config)
+{
+ CodeParser::initializeParser(config);
+
+ /*
+ All these can appear in a C++ namespace. Don't add
+ anything that can't be in a C++ namespace.
+ */
+ nodeTypeMap.insert(COMMAND_NAMESPACE, Node::Namespace);
+ nodeTypeMap.insert(COMMAND_CLASS, 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);
+
+ exampleFiles = config.getCanonicalPathList(CONFIG_EXAMPLES);
+ exampleDirs = config.getCanonicalPathList(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 *.dita *.ui";
+
+ QStringList exampleImagePatterns = config.getStringList(
+ CONFIG_EXAMPLES + Config::dot + CONFIG_IMAGEEXTENSIONS);
+
+ if (!exampleImagePatterns.isEmpty())
+ exampleImageFilter = exampleImagePatterns.join(' ');
+ else
+ exampleImageFilter = "*.png";
+}
+
+/*!
+ Clear the map of common node types and call
+ the same function in the base class.
+ */
+void CppCodeParser::terminateParser()
+{
+ nodeTypeMap.clear();
+ CodeParser::terminateParser();
+}
+
+/*!
+ Returns "Cpp".
+ */
+QString CppCodeParser::language()
+{
+ return "Cpp";
+}
+
+/*!
+ Returns a list of extensions for header files.
+ */
+QStringList CppCodeParser::headerFileNameFilter()
+{
+ return QStringList() << "*.ch" << "*.h" << "*.h++" << "*.hh" << "*.hpp" << "*.hxx";
+}
+
+/*!
+ Returns a list of extensions for source files, i.e. not
+ header files.
+ */
+QStringList CppCodeParser::sourceFileNameFilter()
+{
+ return QStringList() << "*.c++" << "*.cc" << "*.cpp" << "*.cxx" << "*.mm";
+}
+
+/*!
+ Parse the C++ header file identified by \a filePath and add
+ the parsed contents to the database. The \a location is used
+ for reporting errors.
+ */
+void CppCodeParser::parseHeaderFile(const Location& location, const QString& filePath)
+{
+ QFile in(filePath);
+ currentFile_ = filePath;
+ if (!in.open(QIODevice::ReadOnly)) {
+ location.error(tr("Cannot open C++ header file '%1'").arg(filePath));
+ currentFile_.clear();
+ return;
+ }
+
+ reset();
+ Location fileLocation(filePath);
+ Tokenizer fileTokenizer(fileLocation, in);
+ tokenizer = &fileTokenizer;
+ readToken();
+ parsingHeaderFile_ = true;
+ matchDeclList(qdb_->primaryTreeRoot());
+ parsingHeaderFile_ = false;
+ if (!fileTokenizer.version().isEmpty())
+ qdb_->setVersion(fileTokenizer.version());
+ in.close();
+
+ if (fileLocation.fileName() == "qiterator.h")
+ parseQiteratorDotH(location, filePath);
+ currentFile_.clear();
+}
+
+/*!
+ Get ready to parse the C++ cpp file identified by \a filePath
+ and add its parsed contents to the database. \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)
+{
+ QFile in(filePath);
+ currentFile_ = filePath;
+ if (!in.open(QIODevice::ReadOnly)) {
+ location.error(tr("Cannot open C++ source file '%1' (%2)").arg(filePath).arg(strerror(errno)));
+ currentFile_.clear();
+ return;
+ }
+
+ reset();
+ Location fileLocation(filePath);
+ Tokenizer fileTokenizer(fileLocation, in);
+ tokenizer = &fileTokenizer;
+ readToken();
+
+ /*
+ The set of open namespaces is cleared before parsing
+ each source file. The word "source" here means cpp file.
+ */
+ qdb_->clearOpenNamespaces();
+
+ matchDocsAndStuff();
+ in.close();
+ currentFile_.clear();
+}
+
+/*!
+ This is called after all the C++ header files have been
+ parsed. The most important thing it does is resolve C++
+ class inheritance links in the tree. It also initializes
+ a bunch of other collections.
+ */
+void CppCodeParser::doneParsingHeaderFiles()
+{
+ QMapIterator<QString, QString> i(sequentialIteratorClasses);
+ while (i.hasNext()) {
+ i.next();
+ instantiateIteratorMacro(i.key(), i.value(), sequentialIteratorDefinition);
+ }
+ i = mutableSequentialIteratorClasses;
+ while (i.hasNext()) {
+ i.next();
+ instantiateIteratorMacro(i.key(), i.value(), mutableSequentialIteratorDefinition);
+ }
+ i = associativeIteratorClasses;
+ while (i.hasNext()) {
+ i.next();
+ instantiateIteratorMacro(i.key(), i.value(), associativeIteratorDefinition);
+ }
+ i = mutableAssociativeIteratorClasses;
+ while (i.hasNext()) {
+ i.next();
+ instantiateIteratorMacro(i.key(), i.value(), mutableAssociativeIteratorDefinition);
+ }
+ sequentialIteratorDefinition.clear();
+ mutableSequentialIteratorDefinition.clear();
+ associativeIteratorDefinition.clear();
+ mutableAssociativeIteratorDefinition.clear();
+ sequentialIteratorClasses.clear();
+ mutableSequentialIteratorClasses.clear();
+ associativeIteratorClasses.clear();
+ mutableAssociativeIteratorClasses.clear();
+}
+
+/*!
+ This is called after all the source files (i.e., not the
+ header files) have been parsed. Currently nothing to do.
+ */
+void CppCodeParser::doneParsingSourceFiles()
+{
+ // contents moved to QdocDatabase::resolveIssues()
+}
+
+static QSet<QString> topicCommands_;
+/*!
+ Returns the set of strings reopresenting the topic commands.
+ */
+const QSet<QString>& CppCodeParser::topicCommands()
+{
+ if (topicCommands_.isEmpty()) {
+ topicCommands_ << COMMAND_CLASS
+ << COMMAND_DITAMAP
+ << 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_TYPEDEF
+ << COMMAND_VARIABLE
+ << COMMAND_QMLTYPE
+ << COMMAND_QMLPROPERTY
+ << COMMAND_QMLPROPERTYGROUP
+ << COMMAND_QMLATTACHEDPROPERTY
+ << COMMAND_QMLSIGNAL
+ << COMMAND_QMLATTACHEDSIGNAL
+ << COMMAND_QMLMETHOD
+ << COMMAND_QMLATTACHEDMETHOD
+ << COMMAND_QMLBASICTYPE
+ << COMMAND_QMLMODULE
+ << COMMAND_JSTYPE
+ << COMMAND_JSPROPERTY
+ << COMMAND_JSPROPERTYGROUP
+ << COMMAND_JSATTACHEDPROPERTY
+ << COMMAND_JSSIGNAL
+ << COMMAND_JSATTACHEDSIGNAL
+ << COMMAND_JSMETHOD
+ << COMMAND_JSATTACHEDMETHOD
+ << COMMAND_JSBASICTYPE
+ << COMMAND_JSMODULE;
+ }
+ return topicCommands_;
+}
+
+/*!
+ Process the topic \a command found in the \a doc with argument \a arg.
+ */
+Node* CppCodeParser::processTopicCommand(const Doc& doc,
+ const QString& command,
+ const ArgLocPair& arg)
+{
+ ExtraFuncData extra;
+ if (command == COMMAND_FN) {
+ QStringList parentPath;
+ FunctionNode *func = 0;
+ FunctionNode *clone = 0;
+
+ if (!makeFunctionNode(arg.first, &parentPath, &clone, extra) &&
+ !makeFunctionNode("void " + arg.first, &parentPath, &clone, extra)) {
+ doc.startLocation().warning(tr("Invalid syntax in '\\%1'").arg(COMMAND_FN));
+ }
+ else {
+ func = qdb_->findFunctionNode(parentPath, clone);
+ if (func == 0) {
+ if (parentPath.isEmpty() && !lastPath_.isEmpty())
+ func = qdb_->findFunctionNode(lastPath_, clone);
+ }
+
+ /*
+ If the node was not found, then search for it in the
+ open C++ namespaces. We don't expect this search to
+ be necessary often. Nor do we expect it to succeed
+ very often.
+ */
+ if (func == 0)
+ func = qdb_->findNodeInOpenNamespace(parentPath, clone);
+
+ if (func == 0) {
+ doc.location().warning(tr("Cannot find '%1' in '\\%2' %3")
+ .arg(clone->name() + "(...)")
+ .arg(COMMAND_FN)
+ .arg(arg.first),
+ 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
+ lastPath_ = parentPath;
+ if (func) {
+ func->borrowParameterNames(clone);
+ func->setParentPath(clone->parentPath());
+ }
+ delete clone;
+ }
+ return func;
+ }
+ else if (command == COMMAND_MACRO) {
+ QStringList parentPath;
+ FunctionNode *func = 0;
+
+ extra.root = qdb_->primaryTreeRoot();
+ extra.isMacro = true;
+ if (makeFunctionNode(arg.first, &parentPath, &func, extra)) {
+ if (!parentPath.isEmpty()) {
+ doc.startLocation().warning(tr("Invalid syntax in '\\%1'").arg(COMMAND_MACRO));
+ delete func;
+ func = 0;
+ }
+ else {
+ func->setMetaness(FunctionNode::MacroWithParams);
+ QVector<Parameter> params = func->parameters();
+ for (int i = 0; i < params.size(); ++i) {
+ Parameter &param = params[i];
+ if (param.name().isEmpty() && !param.dataType().isEmpty()
+ && param.dataType() != "...")
+ param = Parameter("", "", param.dataType());
+ }
+ func->setParameters(params);
+ }
+ return func;
+ }
+ else if (QRegExp("[A-Za-z_][A-Za-z0-9_]+").exactMatch(arg.first)) {
+ func = new FunctionNode(qdb_->primaryTreeRoot(), arg.first);
+ func->setAccess(Node::Public);
+ func->setLocation(doc.startLocation());
+ func->setMetaness(FunctionNode::MacroWithoutParams);
+ }
+ else {
+ doc.location().warning(tr("Invalid syntax in '\\%1'").arg(COMMAND_MACRO));
+
+ }
+ return func;
+ }
+ else if (nodeTypeMap.contains(command)) {
+ /*
+ We should only get in here if the command refers to
+ something that can appear in a C++ namespace,
+ i.e. a class, another namespace, an enum, a typedef,
+ a property or a variable. I think these are handled
+ this way to allow the writer to refer to the entity
+ without including the namespace qualifier.
+ */
+ Node::NodeType type = nodeTypeMap[command];
+ QStringList paths = arg.first.split(QLatin1Char(' '));
+ QStringList path = paths[0].split("::");
+ Node *node = 0;
+
+ node = qdb_->findNodeInOpenNamespace(path, type);
+ if (node == 0)
+ node = qdb_->findNodeByNameAndType(path, type);
+ if (node == 0) {
+ doc.location().warning(tr("Cannot find '%1' specified with '\\%2' in any header file")
+ .arg(arg.first).arg(command));
+ lastPath_ = path;
+
+ }
+ else if (node->isAggregate()) {
+ if (type == Node::Namespace) {
+ NamespaceNode* ns = static_cast<NamespaceNode*>(node);
+ ns->markSeen();
+ }
+ /*
+ This treats a class as a namespace.
+ */
+ if ((type == Node::Class) || (type == Node::Namespace)) {
+ if (path.size() > 1) {
+ path.pop_back();
+ QString ns = path.join("::");
+ qdb_->insertOpenNamespace(ns);
+ }
+ }
+ }
+ return node;
+ }
+ else if (command == COMMAND_EXAMPLE) {
+ if (Config::generateExamples) {
+ ExampleNode* en = new ExampleNode(qdb_->primaryTreeRoot(), arg.first);
+ en->setLocation(doc.startLocation());
+ createExampleFileNodes(en);
+ return en;
+ }
+ }
+ else if (command == COMMAND_EXTERNALPAGE) {
+ DocumentNode* dn = new DocumentNode(qdb_->primaryTreeRoot(),
+ arg.first,
+ Node::ExternalPage,
+ Node::ArticlePage);
+ dn->setLocation(doc.startLocation());
+ return dn;
+ }
+ else if (command == COMMAND_FILE) {
+ DocumentNode* dn = new DocumentNode(qdb_->primaryTreeRoot(),
+ arg.first,
+ Node::File,
+ Node::NoPageType);
+ dn->setLocation(doc.startLocation());
+ return dn;
+ }
+ else if (command == COMMAND_HEADERFILE) {
+ DocumentNode* dn = new DocumentNode(qdb_->primaryTreeRoot(),
+ arg.first,
+ Node::HeaderFile,
+ Node::ApiPage);
+ dn->setLocation(doc.startLocation());
+ return dn;
+ }
+ else if (command == COMMAND_GROUP) {
+ CollectionNode* cn = qdb_->addGroup(arg.first);
+ cn->setLocation(doc.startLocation());
+ cn->markSeen();
+ return cn;
+ }
+ else if (command == COMMAND_MODULE) {
+ CollectionNode* cn = qdb_->addModule(arg.first);
+ cn->setLocation(doc.startLocation());
+ cn->markSeen();
+ return cn;
+ }
+ else if (command == COMMAND_QMLMODULE) {
+ QStringList blankSplit = arg.first.split(QLatin1Char(' '));
+ CollectionNode* cn = qdb_->addQmlModule(blankSplit[0]);
+ cn->setLogicalModuleInfo(blankSplit);
+ cn->setLocation(doc.startLocation());
+ cn->markSeen();
+ return cn;
+ }
+ else if (command == COMMAND_JSMODULE) {
+ QStringList blankSplit = arg.first.split(QLatin1Char(' '));
+ CollectionNode* cn = qdb_->addJsModule(blankSplit[0]);
+ cn->setLogicalModuleInfo(blankSplit);
+ cn->setLocation(doc.startLocation());
+ cn->markSeen();
+ return cn;
+ }
+ else if (command == COMMAND_PAGE) {
+ Node::PageType ptype = Node::ArticlePage;
+ QStringList args = arg.first.split(QLatin1Char(' '));
+ if (args.size() > 1) {
+ QString t = args[1].toLower();
+ if (t == "howto")
+ ptype = Node::HowToPage;
+ else if (t == "api")
+ ptype = Node::ApiPage;
+ else if (t == "example")
+ ptype = Node::ExamplePage;
+ else if (t == "overview")
+ ptype = Node::OverviewPage;
+ else if (t == "tutorial")
+ ptype = Node::TutorialPage;
+ else if (t == "faq")
+ ptype = Node::FAQPage;
+ else if (t == "ditamap")
+ ptype = Node::DitaMapPage;
+ }
+ DocumentNode* dn = 0;
+ if (ptype == Node::DitaMapPage)
+ dn = new DitaMapNode(qdb_->primaryTreeRoot(), args[0]);
+ else
+ dn = new DocumentNode(qdb_->primaryTreeRoot(), args[0], Node::Page, ptype);
+ dn->setLocation(doc.startLocation());
+ return dn;
+ }
+ else if (command == COMMAND_DITAMAP) {
+ DocumentNode* dn = new DitaMapNode(qdb_->primaryTreeRoot(), arg.first);
+ dn->setLocation(doc.startLocation());
+ return dn;
+ }
+ else if ((command == COMMAND_QMLTYPE) || (command == COMMAND_JSTYPE)) {
+ QmlTypeNode* qcn = new QmlTypeNode(qdb_->primaryTreeRoot(), arg.first);
+ if (command == COMMAND_JSTYPE)
+ qcn->setGenus(Node::JS);
+ qcn->setLocation(doc.startLocation());
+ return qcn;
+ }
+ else if ((command == COMMAND_QMLBASICTYPE) || (command == COMMAND_JSBASICTYPE)) {
+ QmlBasicTypeNode* n = new QmlBasicTypeNode(qdb_->primaryTreeRoot(), arg.first);
+ if (command == COMMAND_JSBASICTYPE)
+ n->setGenus(Node::JS);
+ n->setLocation(doc.startLocation());
+ return n;
+ }
+ else if ((command == COMMAND_QMLSIGNAL) ||
+ (command == COMMAND_QMLMETHOD) ||
+ (command == COMMAND_QMLATTACHEDSIGNAL) ||
+ (command == COMMAND_QMLATTACHEDMETHOD) ||
+ (command == COMMAND_JSSIGNAL) ||
+ (command == COMMAND_JSMETHOD) ||
+ (command == COMMAND_JSATTACHEDSIGNAL) ||
+ (command == COMMAND_JSATTACHEDMETHOD)) {
+ QString module;
+ QString qmlTypeName;
+ QString type;
+ if (splitQmlMethodArg(arg.first, type, module, qmlTypeName)) {
+ QmlTypeNode* qmlType = qdb_->findQmlType(module, qmlTypeName);
+ if (qmlType) {
+ bool attached = false;
+ Node::NodeType nodeType = Node::QmlMethod;
+ if ((command == COMMAND_QMLSIGNAL) ||
+ (command == COMMAND_JSSIGNAL))
+ nodeType = Node::QmlSignal;
+ else if ((command == COMMAND_QMLATTACHEDSIGNAL) ||
+ (command == COMMAND_JSATTACHEDSIGNAL)) {
+ nodeType = Node::QmlSignal;
+ attached = true;
+ }
+ else if ((command == COMMAND_QMLMETHOD) ||
+ (command == COMMAND_JSMETHOD)) {
+ // do nothing
+ }
+ else if ((command == COMMAND_QMLATTACHEDMETHOD) ||
+ (command == COMMAND_JSATTACHEDMETHOD))
+ attached = true;
+ else
+ return 0; // never get here.
+ FunctionNode* fn = makeFunctionNode(doc,
+ arg.first,
+ qmlType,
+ nodeType,
+ attached,
+ command);
+ if (fn) {
+ fn->setLocation(doc.startLocation());
+ if ((command == COMMAND_JSSIGNAL) ||
+ (command == COMMAND_JSMETHOD) ||
+ (command == COMMAND_JSATTACHEDSIGNAL) ||
+ (command == COMMAND_JSATTACHEDMETHOD))
+ fn->setGenus(Node::JS);
+ }
+ return fn;
+ }
+ }
+ }
+ return 0;
+}
+
+/*!
+ A QML property group argument has the form...
+
+ <QML-module>::<QML-type>::<name>
+
+ This function splits the argument into those parts.
+ A <QML-module> is the QML equivalent of a C++ namespace.
+ So this function splits \a arg on "::" and stores the
+ parts in \a module, \a qmlTypeName, and \a name, and returns
+ true. If any part is not found, a qdoc warning is emitted
+ and false is returned.
+ */
+bool CppCodeParser::splitQmlPropertyGroupArg(const QString& arg,
+ QString& module,
+ QString& qmlTypeName,
+ QString& name)
+{
+ QStringList colonSplit = arg.split("::");
+ if (colonSplit.size() == 3) {
+ module = colonSplit[0];
+ qmlTypeName = colonSplit[1];
+ name = colonSplit[2];
+ return true;
+ }
+ QString msg = "Unrecognizable QML module/component qualifier for " + arg;
+ location().warning(tr(msg.toLatin1().data()));
+ return false;
+}
+
+/*!
+ A QML property argument has the form...
+
+ <type> <QML-type>::<name>
+ <type> <QML-module>::<QML-type>::<name>
+
+ This function splits the argument into one of those
+ two forms. The three part form is the old form, which
+ was used before the creation of Qt Quick 2 and Qt
+ Components. A <QML-module> is the QML equivalent of a
+ C++ namespace. So this function splits \a arg on "::"
+ and stores the parts in \a type, \a module, \a qmlTypeName,
+ and \a name, and returns \c true. If any part other than
+ \a module is not found, a qdoc warning is emitted and
+ false is returned.
+
+ \note The two QML types \e{Component} and \e{QtObject}
+ never have a module qualifier.
+ */
+bool CppCodeParser::splitQmlPropertyArg(const QString& arg,
+ QString& type,
+ QString& module,
+ QString& qmlTypeName,
+ QString& name)
+{
+ QStringList blankSplit = arg.split(QLatin1Char(' '));
+ if (blankSplit.size() > 1) {
+ type = blankSplit[0];
+ QStringList colonSplit(blankSplit[1].split("::"));
+ if (colonSplit.size() == 3) {
+ module = colonSplit[0];
+ qmlTypeName = colonSplit[1];
+ name = colonSplit[2];
+ return true;
+ }
+ if (colonSplit.size() == 2) {
+ module.clear();
+ qmlTypeName = colonSplit[0];
+ name = colonSplit[1];
+ return true;
+ }
+ QString msg = "Unrecognizable QML module/component qualifier for " + arg;
+ location().warning(tr(msg.toLatin1().data()));
+ }
+ else {
+ QString msg = "Missing property type for " + arg;
+ location().warning(tr(msg.toLatin1().data()));
+ }
+ return false;
+}
+
+/*!
+ A QML signal or method argument has the form...
+
+ <type> <QML-type>::<name>(<param>, <param>, ...)
+ <type> <QML-module>::<QML-type>::<name>(<param>, <param>, ...)
+
+ This function splits the \a{arg}ument into one of those
+ two forms, sets \a type, \a module, and \a qmlTypeName,
+ and returns true. If the argument doesn't match either
+ form, an error message is emitted and false is returned.
+
+ \note The two QML types \e{Component} and \e{QtObject} never
+ have a module qualifier.
+ */
+bool CppCodeParser::splitQmlMethodArg(const QString& arg,
+ QString& type,
+ QString& module,
+ QString& qmlTypeName)
+{
+ QString name;
+ int leftParen = arg.indexOf(QChar('('));
+ if (leftParen > 0)
+ name = arg.left(leftParen);
+ else
+ name = arg;
+ int firstBlank = name.indexOf(QChar(' '));
+ if (firstBlank > 0) {
+ type = name.left(firstBlank);
+ name = name.right(name.length() - firstBlank - 1);
+ }
+ else
+ type.clear();
+
+ QStringList colonSplit(name.split("::"));
+ if (colonSplit.size() > 1) {
+ if (colonSplit.size() > 2) {
+ module = colonSplit[0];
+ qmlTypeName = colonSplit[1];
+ }
+ else {
+ module.clear();
+ qmlTypeName = colonSplit[0];
+ }
+ return true;
+ }
+ QString msg = "Unrecognizable QML module/component qualifier for " + arg;
+ location().warning(tr(msg.toLatin1().data()));
+ return false;
+}
+
+/*!
+ Process the topic \a command group found in the \a doc with arguments \a args.
+
+ Currently, this function is called only for \e{qmlproperty}
+ and \e{qmlattachedproperty}.
+ */
+void CppCodeParser::processQmlProperties(const Doc& doc,
+ NodeList& nodes,
+ DocList& docs,
+ bool jsProps)
+{
+ QString arg;
+ QString type;
+ QString topic;
+ QString module;
+ QString qmlTypeName;
+ QString property;
+ QmlPropertyNode* qpn = 0;
+ QmlTypeNode* qmlType = 0;
+ QmlPropertyGroupNode* qpgn = 0;
+
+ Topic qmlPropertyGroupTopic;
+ const TopicList& topics = doc.topicsUsed();
+ for (int i=0; i<topics.size(); ++i) {
+ if ((topics.at(i).topic == COMMAND_QMLPROPERTYGROUP) ||
+ (topics.at(i).topic == COMMAND_JSPROPERTYGROUP)) {
+ qmlPropertyGroupTopic = topics.at(i);
+ break;
+ }
+ }
+ if (qmlPropertyGroupTopic.isEmpty() && topics.size() > 1) {
+ qmlPropertyGroupTopic = topics.at(0);
+ if (jsProps)
+ qmlPropertyGroupTopic.topic = COMMAND_JSPROPERTYGROUP;
+ else
+ qmlPropertyGroupTopic.topic = COMMAND_QMLPROPERTYGROUP;
+ arg = qmlPropertyGroupTopic.args;
+ if (splitQmlPropertyArg(arg, type, module, qmlTypeName, property)) {
+ int i = property.indexOf('.');
+ if (i != -1) {
+ property = property.left(i);
+ qmlPropertyGroupTopic.args = module + "::" + qmlTypeName + "::" + property;
+ doc.location().warning(tr("No QML property group command found; using \\%1 %2")
+ .arg(COMMAND_QMLPROPERTYGROUP).arg(qmlPropertyGroupTopic.args));
+ }
+ else {
+ /*
+ Assumption: No '.' in the property name
+ means there is no property group.
+ */
+ qmlPropertyGroupTopic.clear();
+ }
+ }
+ }
+
+ if (!qmlPropertyGroupTopic.isEmpty()) {
+ arg = qmlPropertyGroupTopic.args;
+ if (splitQmlPropertyGroupArg(arg, module, qmlTypeName, property)) {
+ qmlType = qdb_->findQmlType(module, qmlTypeName);
+ if (qmlType) {
+ qpgn = new QmlPropertyGroupNode(qmlType, property);
+ qpgn->setLocation(doc.startLocation());
+ if (jsProps)
+ qpgn->setGenus(Node::JS);
+ nodes.append(qpgn);
+ docs.append(doc);
+ }
+ }
+ }
+ for (int i=0; i<topics.size(); ++i) {
+ if (topics.at(i).topic == COMMAND_QMLPROPERTYGROUP) {
+ continue;
+ }
+ topic = topics.at(i).topic;
+ arg = topics.at(i).args;
+ if ((topic == COMMAND_QMLPROPERTY) || (topic == COMMAND_QMLATTACHEDPROPERTY) ||
+ (topic == COMMAND_JSPROPERTY) || (topic == COMMAND_JSATTACHEDPROPERTY)) {
+ bool attached = ((topic == COMMAND_QMLATTACHEDPROPERTY) ||
+ (topic == COMMAND_JSATTACHEDPROPERTY));
+ if (splitQmlPropertyArg(arg, type, module, qmlTypeName, property)) {
+ qmlType = qdb_->findQmlType(module, qmlTypeName);
+ if (qmlType) {
+ if (qmlType->hasQmlProperty(property, attached) != 0) {
+ QString msg = tr("QML property documented multiple times: '%1'").arg(arg);
+ doc.startLocation().warning(msg);
+ }
+ else if (qpgn) {
+ qpn = new QmlPropertyNode(qpgn, property, type, attached);
+ qpn->setLocation(doc.startLocation());
+ if (jsProps)
+ qpn->setGenus(Node::JS);
+ }
+ else {
+ qpn = new QmlPropertyNode(qmlType, property, type, attached);
+ qpn->setLocation(doc.startLocation());
+ if (jsProps)
+ qpn->setGenus(Node::JS);
+ nodes.append(qpn);
+ docs.append(doc);
+ }
+ }
+ }
+ } else if (qpgn) {
+ doc.startLocation().warning(
+ tr("Invalid use of '\\%1'; not allowed in a '\\%2'").arg(
+ topic, qmlPropertyGroupTopic.topic));
+ }
+ }
+}
+
+static QSet<QString> otherMetaCommands_;
+/*!
+ Returns the set of strings representing the common metacommands
+ plus some other metacommands.
+ */
+const QSet<QString>& CppCodeParser::otherMetaCommands()
+{
+ if (otherMetaCommands_.isEmpty()) {
+ otherMetaCommands_ = commonMetaCommands();
+ otherMetaCommands_ << COMMAND_INHEADERFILE
+ << COMMAND_OVERLOAD
+ << COMMAND_REIMP
+ << COMMAND_RELATES
+ << COMMAND_CONTENTSPAGE
+ << COMMAND_NEXTPAGE
+ << COMMAND_PREVIOUSPAGE
+ << COMMAND_INDEXPAGE
+ << COMMAND_STARTPAGE
+ << COMMAND_QMLINHERITS
+ << COMMAND_QMLINSTANTIATES
+ << COMMAND_QMLDEFAULT
+ << COMMAND_QMLREADONLY
+ << COMMAND_QMLABSTRACT
+ << COMMAND_ABSTRACT;
+ }
+ return otherMetaCommands_;
+}
+
+/*!
+ 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 ArgLocPair& argLocPair,
+ Node *node)
+{
+ QString arg = argLocPair.first;
+ if (command == COMMAND_INHEADERFILE) {
+ if (node != 0 && node->isAggregate()) {
+ ((Aggregate *) node)->addInclude(arg);
+ }
+ else {
+ doc.location().warning(tr("Ignored '\\%1'").arg(COMMAND_INHEADERFILE));
+ }
+ }
+ else if (command == COMMAND_OVERLOAD) {
+ if (node && node->isFunction())
+ ((FunctionNode *) node)->setOverloadFlag(true);
+ else
+ doc.location().warning(tr("Ignored '\\%1'").arg(COMMAND_OVERLOAD));
+ }
+ else if (command == COMMAND_REIMP) {
+ if (node != 0 && node->parent() && !node->parent()->isInternal()) {
+ if (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."));
+ }
+ /*
+ 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("'\\%1' in %2() should be '\\internal' "
+ "because its base function is private "
+ "or internal").arg(COMMAND_REIMP).arg(node->name()));
+ }
+ func->setReimplemented(true);
+ }
+ else {
+ doc.location().warning(tr("Ignored '\\%1' in %2").arg(COMMAND_REIMP).arg(node->name()));
+ }
+ }
+ }
+ else if (command == COMMAND_RELATES) {
+ QStringList path = arg.split("::");
+ Node* n = qdb_->findRelatesNode(path);
+ if (!n) {
+ // Store just a string to write to the index file
+ if (Generator::preparing())
+ node->setRelates(arg);
+ else
+ doc.location().warning(tr("Cannot find '%1' in '\\%2'").arg(arg).arg(COMMAND_RELATES));
+
+ }
+ else if (node->parent() != n)
+ node->setRelates(static_cast<Aggregate*>(n));
+ else
+ doc.location().warning(tr("Invalid use of '\\%1' (already a member of '%2')")
+ .arg(COMMAND_RELATES, arg));
+ }
+ 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 if (command == COMMAND_QMLINHERITS) {
+ if (node->name() == arg)
+ doc.location().warning(tr("%1 tries to inherit itself").arg(arg));
+ else if (node->isQmlType() || node->isJsType()) {
+ QmlTypeNode* qmlType = static_cast<QmlTypeNode*>(node);
+ qmlType->setQmlBaseName(arg);
+ QmlTypeNode::addInheritedBy(arg,node);
+ }
+ }
+ else if (command == COMMAND_QMLINSTANTIATES) {
+ if (node->isQmlType() || node->isJsType()) {
+ ClassNode* classNode = qdb_->findClassNode(arg.split("::"));
+ if (classNode)
+ node->setClassNode(classNode);
+ else
+ doc.location().warning(tr("C++ class %1 not found: \\instantiates %1").arg(arg));
+ }
+ else
+ doc.location().warning(tr("\\instantiates is only allowed in \\qmltype"));
+ }
+ else if (command == COMMAND_QMLDEFAULT) {
+ if (node->type() == Node::QmlProperty) {
+ QmlPropertyNode* qpn = static_cast<QmlPropertyNode*>(node);
+ qpn->setDefault();
+ }
+ else if (node->type() == Node::QmlPropertyGroup) {
+ QmlPropertyGroupNode* qpgn = static_cast<QmlPropertyGroupNode*>(node);
+ NodeList::ConstIterator p = qpgn->childNodes().constBegin();
+ while (p != qpgn->childNodes().constEnd()) {
+ if ((*p)->type() == Node::QmlProperty) {
+ QmlPropertyNode* qpn = static_cast<QmlPropertyNode*>(*p);
+ qpn->setDefault();
+ }
+ ++p;
+ }
+ }
+ }
+ else if (command == COMMAND_QMLREADONLY) {
+ if (node->type() == Node::QmlProperty) {
+ QmlPropertyNode* qpn = static_cast<QmlPropertyNode*>(node);
+ qpn->setReadOnly(1);
+ }
+ else if (node->type() == Node::QmlPropertyGroup) {
+ QmlPropertyGroupNode* qpgn = static_cast<QmlPropertyGroupNode*>(node);
+ NodeList::ConstIterator p = qpgn->childNodes().constBegin();
+ while (p != qpgn->childNodes().constEnd()) {
+ if ((*p)->type() == Node::QmlProperty) {
+ QmlPropertyNode* qpn = static_cast<QmlPropertyNode*>(*p);
+ qpn->setReadOnly(1);
+ }
+ ++p;
+ }
+ }
+ }
+ else if ((command == COMMAND_QMLABSTRACT) || (command == COMMAND_ABSTRACT)) {
+ if (node->isQmlType() || node->isJsType())
+ node->setAbstract(true);
+ }
+ else {
+ processCommonMetaCommand(doc.location(),command,argLocPair,node);
+ }
+}
+
+/*!
+ 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.constBegin();
+ while (cmd != metaCommands.constEnd()) {
+ ArgList args = doc.metaCommandArgs(*cmd);
+ ArgList::ConstIterator arg = args.constBegin();
+ while (arg != args.constEnd()) {
+ processOtherMetaCommand(doc, *cmd, *arg, node);
+ ++arg;
+ }
+ ++cmd;
+ }
+}
+
+/*!
+ Resets the C++ code parser to its default initialized state.
+ */
+void CppCodeParser::reset()
+{
+ tokenizer = 0;
+ tok = 0;
+ access = Node::Public;
+ metaness_ = FunctionNode::Plain;
+ lastPath_.clear();
+ physicalModuleName.clear();
+}
+
+/*!
+ 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;
+ }
+ return false;
+}
+
+/*!
+ Skip to \a target. If \a target is found before the end
+ of input, return true. Otherwise return false.
+ */
+bool CppCodeParser::skipTo(int target)
+{
+ while ((tok != Tok_Eoi) && (tok != target))
+ readToken();
+ return tok == target;
+}
+
+/*!
+ 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::matchModuleQualifier(QString& name)
+{
+ bool matches = (lexeme() == QString('.'));
+ if (matches) {
+ do {
+ name += lexeme();
+ readToken();
+ } while ((tok == Tok_Ident) || (lexeme() == QString('.')));
+ }
+ return matches;
+}
+
+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;
+}
+
+/*
+ This function is no longer used.
+ */
+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)) {
+ /*
+ This is a hack until we replace this "parser"
+ with the real one used in Qt Creator.
+ */
+ if (!inMacroCommand_ && lexeme() == "(" &&
+ ((previousLexeme() == "QT_PREPEND_NAMESPACE") || (previousLexeme() == "NS"))) {
+ readToken();
+ readToken();
+ dataType->append(previousLexeme());
+ readToken();
+ }
+ else
+ 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;
+}
+
+/*!
+ Parse the next function parameter, if there is one, and
+ append it to parameter vector \a pvect. Return true if
+ a parameter is parsed and appended to \a pvect.
+ Otherwise return false.
+ */
+bool CppCodeParser::matchParameter(QVector<Parameter>& pvect, bool& isQPrivateSignal)
+{
+ if (match(Tok_QPrivateSignal)) {
+ isQPrivateSignal = true;
+ return true;
+ }
+
+ Parameter p;
+ CodeChunk chunk;
+ if (!matchDataType(&chunk, &p.name_)) {
+ return false;
+ }
+ p.dataType_ = chunk.toString();
+ chunk.clear();
+ match(Tok_Comment);
+ if (match(Tok_Equal)) {
+ int pdepth = tokenizer->parenDepth();
+ while (tokenizer->parenDepth() >= pdepth &&
+ (tok != Tok_Comma || (tokenizer->parenDepth() > pdepth)) &&
+ tok != Tok_Eoi) {
+ chunk.append(lexeme());
+ readToken();
+ }
+ }
+ p.defaultValue_ = chunk.toString();
+ pvect.append(p);
+ return true;
+}
+
+/*!
+ If the current token is any of several function modifiers,
+ return that token value after reading the next token. If it
+ is not one of the function modieifer tokens, return -1 but
+ don\t read the next token.
+ */
+int CppCodeParser::matchFunctionModifier()
+{
+ switch (tok) {
+ case Tok_friend:
+ case Tok_inline:
+ case Tok_explicit:
+ case Tok_static:
+ case Tok_QT_DEPRECATED:
+ readToken();
+ return tok;
+ case Tok_QT_COMPAT:
+ case Tok_QT_COMPAT_CONSTRUCTOR:
+ case Tok_QT_MOC_COMPAT:
+ case Tok_QT3_SUPPORT:
+ case Tok_QT3_SUPPORT_CONSTRUCTOR:
+ case Tok_QT3_MOC_SUPPORT:
+ readToken();
+ return Tok_QT_COMPAT;
+ default:
+ break;
+ }
+ return -1;
+}
+
+bool CppCodeParser::matchFunctionDecl(Aggregate *parent,
+ QStringList *parentPathPtr,
+ FunctionNode **funcPtr,
+ const QString &templateStuff,
+ ExtraFuncData& extra)
+{
+ CodeChunk returnType;
+ QStringList parentPath;
+ QString name;
+
+ bool matched_QT_DEPRECATED = false;
+ bool matched_friend = false;
+ bool matched_static = false;
+ bool matched_inline = false;
+ bool matched_explicit = false;
+ bool matched_compat = false;
+
+ int token = tok;
+ while (token != -1) {
+ switch (token) {
+ case Tok_friend:
+ matched_friend = true;
+ break;
+ case Tok_inline:
+ matched_inline = true;
+ break;
+ case Tok_explicit:
+ matched_explicit = true;
+ break;
+ case Tok_static:
+ matched_static = true;
+ break;
+ case Tok_QT_DEPRECATED:
+ // no break here.
+ matched_QT_DEPRECATED = true;
+ case Tok_QT_COMPAT:
+ matched_compat = true;
+ break;
+ }
+ token = matchFunctionModifier();
+ }
+
+ FunctionNode::Virtualness virtuality = FunctionNode::NonVirtual;
+ if (match(Tok_virtual)) {
+ virtuality = FunctionNode::NormalVirtual;
+ if (!matched_compat)
+ matched_compat = matchCompat();
+ }
+
+ if (!matchDataType(&returnType)) {
+ if (tokenizer->parsingFnOrMacro()
+ && (match(Tok_Q_DECLARE_FLAGS) ||
+ match(Tok_Q_PROPERTY) ||
+ match(Tok_Q_PRIVATE_PROPERTY)))
+ returnType = CodeChunk(previousLexeme());
+ else {
+ return false;
+ }
+ }
+
+ if (returnType.toString() == "QBool")
+ returnType = CodeChunk("bool");
+
+ if (!matched_compat)
+ matched_compat = matchCompat();
+
+ 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();
+ /*
+ This is a hack to let QML module identifiers through.
+ */
+ matchModuleQualifier(name);
+ 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 (matched_compat)
+ var->setStatus(Node::Compat);
+ var->setStatic(matched_static);
+ return false;
+ }
+ if (tok != Tok_LeftParen)
+ return false;
+ }
+ readToken();
+
+ // A left paren was seen. Parse the parameters
+ bool isQPrivateSignal = false;
+ QVector<Parameter> pvect;
+ if (tok != Tok_RightParen) {
+ do {
+ if (!matchParameter(pvect, isQPrivateSignal))
+ return false;
+ } while (match(Tok_Comma));
+ }
+ // The parameters must end with a right paren
+ if (!match(Tok_RightParen))
+ return false;
+
+ // look for const
+ bool matchedConst = match(Tok_const);
+
+ // look for 0 indicating pure virtual
+ if (match(Tok_Equal) && match(Tok_Number))
+ virtuality = FunctionNode::PureVirtual;
+
+ // look for colon indicating ctors which must be skipped
+ if (match(Tok_Colon)) {
+ while (tok != Tok_LeftBrace && tok != Tok_Eoi)
+ readToken();
+ }
+
+ // If no ';' expect a body, which must be skipped.
+ bool body_expected = false;
+ bool body_present = false;
+ if (!match(Tok_Semicolon) && tok != Tok_Eoi) {
+ body_expected = true;
+ int nesting = tokenizer->braceDepth();
+ if (!match(Tok_LeftBrace))
+ return false;
+ // skip the body
+ while (tokenizer->braceDepth() >= nesting && tok != Tok_Eoi)
+ readToken();
+ body_present = true;
+ match(Tok_RightBrace);
+ }
+
+ FunctionNode *func = 0;
+ bool createFunctionNode = false;
+ if (parsingHeaderFile_) {
+ if (matched_friend) {
+ if (matched_inline) {
+ // nothing yet
+ }
+ if (body_present) {
+ if (body_expected) {
+ // nothing yet
+ }
+ createFunctionNode = true;
+ if (parent && parent->parent())
+ parent = parent->parent();
+ else
+ return false;
+ }
+ }
+ else
+ createFunctionNode = true;
+ }
+ else
+ createFunctionNode = true;
+
+ if (createFunctionNode) {
+ func = new FunctionNode(extra.type, parent, name, extra.isAttached);
+ if (matched_friend)
+ access = Node::Public;
+ func->setAccess(access);
+ func->setLocation(location());
+ func->setReturnType(returnType.toString());
+ func->setParentPath(parentPath);
+ func->setTemplateStuff(templateStuff);
+ if (matched_compat)
+ func->setStatus(Node::Compat);
+ if (matched_QT_DEPRECATED)
+ func->setStatus(Node::Deprecated);
+ if (matched_explicit) { /* What can be done? */ }
+ func->setMetaness(metaness_);
+ if (parent) {
+ if (name == parent->name())
+ func->setMetaness(FunctionNode::Ctor);
+ else if (name.startsWith(QLatin1Char('~')))
+ func->setMetaness(FunctionNode::Dtor);
+ }
+ func->setStatic(matched_static);
+ func->setConst(matchedConst);
+ func->setVirtualness(virtuality);
+ if (isQPrivateSignal)
+ func->setPrivateSignal();
+ if (!pvect.isEmpty()) {
+ func->setParameters(pvect);
+ }
+ }
+ 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;
+
+ classe->addUnresolvedBaseClass(access, baseClass.toPath(), baseClass.toString());
+ 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 declaration.
+
+ This function only handles one level of class nesting, but that is
+ sufficient for Qt because there are no cases of class nesting more
+ than one level deep.
+ */
+bool CppCodeParser::matchClassDecl(Aggregate *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_Gulbrandsen) {
+ Node* n = parent->findChildNode(previousLexeme(),Node::Class);
+ if (n) {
+ parent = static_cast<Aggregate*>(n);
+ if (parent) {
+ readToken();
+ if (tok != Tok_Ident)
+ return false;
+ 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 (!physicalModuleName.isEmpty())
+ classe->setPhysicalModuleName(physicalModuleName);
+ 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(Aggregate *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* ns = 0;
+ if (parent)
+ ns = static_cast<NamespaceNode*>(parent->findChildNode(namespaceName, Node::Namespace));
+ if (!ns) {
+ ns = new NamespaceNode(parent, namespaceName);
+ ns->setAccess(access);
+ ns->setLocation(location());
+ }
+
+ readToken(); // skip '{'
+ bool matched = matchDeclList(ns);
+ return matched && match(Tok_RightBrace);
+}
+
+/*!
+ Match a C++ \c using clause. Return \c true if the match
+ is successful. Otherwise false.
+
+ If the \c using clause is for a namespace, an open namespace
+ <is inserted for qdoc to look in to find things.
+
+ If the \c using clause is a base class member function, the
+ member function is added to \a parent as an unresolved
+ \c using clause.
+ */
+bool CppCodeParser::matchUsingDecl(Aggregate* parent)
+{
+ bool usingNamespace = false;
+ readToken(); // skip 'using'
+
+ if (tok == Tok_namespace) {
+ usingNamespace = true;
+ readToken();
+ }
+
+ int openLeftAngles = 0;
+ int openLeftParens = 0;
+ bool usingOperator = false;
+ QString name;
+ while (tok != Tok_Semicolon) {
+ if ((tok != Tok_Ident) && (tok != Tok_Gulbrandsen)) {
+ if (tok == Tok_LeftAngle) {
+ ++openLeftAngles;
+ }
+ else if (tok == Tok_RightAngle) {
+ if (openLeftAngles <= 0)
+ return false;
+ --openLeftAngles;
+ }
+ else if (tok == Tok_Comma) {
+ if (openLeftAngles <= 0)
+ return false;
+ }
+ else if (tok == Tok_operator) {
+ usingOperator = true;
+ }
+ else if (tok == Tok_SomeOperator) {
+ if (!usingOperator)
+ return false;
+ }
+ else if (tok == Tok_LeftParen) {
+ ++openLeftParens;
+ }
+ else if (tok == Tok_RightParen) {
+ if (openLeftParens <= 0)
+ return false;
+ --openLeftParens;
+ }
+ else {
+ return false;
+ }
+ }
+ name += lexeme();
+ readToken();
+ }
+
+ if (usingNamespace) {
+ // 'using namespace Foo;'.
+ qdb_->insertOpenNamespace(name);
+ }
+ else if (parent && parent->isClass()) {
+ ClassNode* cn = static_cast<ClassNode*>(parent);
+ cn->addUnresolvedUsingClause(name);
+ }
+ return true;
+}
+
+bool CppCodeParser::matchEnumItem(Aggregate *parent, EnumNode *enume)
+{
+ if (!match(Tok_Ident))
+ return false;
+
+ QString name = previousLexeme();
+ CodeChunk val;
+ int parenLevel = 0;
+
+ if (match(Tok_Equal)) {
+ while (tok != Tok_RightBrace && tok != Tok_Eoi) {
+ if (tok == Tok_LeftParen)
+ parenLevel++;
+ else if (tok == Tok_RightParen)
+ parenLevel--;
+ else if (tok == Tok_Comma) {
+ if (parenLevel <= 0)
+ break;
+ }
+ 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(QLatin1Char('0')) && last.size() > 1) {
+ if (last.startsWith("0x") || last.startsWith("0X"))
+ strVal = last.left(2) + QString::number(n + 1, 16);
+ else
+ strVal = QLatin1Char('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(Aggregate *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(Aggregate *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->findChildNode(name, Node::Typedef)) {
+ TypedefNode* td = new TypedefNode(parent, name);
+ td->setAccess(access);
+ td->setLocation(location());
+ }
+ return true;
+}
+
+bool CppCodeParser::matchProperty(Aggregate *parent)
+{
+ int expected_tok = Tok_LeftParen;
+ if (match(Tok_Q_PRIVATE_PROPERTY)) {
+ expected_tok = Tok_Comma;
+ if (!skipTo(Tok_Comma))
+ return false;
+ }
+ else if (!match(Tok_Q_PROPERTY) &&
+ !match(Tok_Q_OVERRIDE) &&
+ !match(Tok_QDOC_PROPERTY)) {
+ return false;
+ }
+
+ if (!match(expected_tok))
+ 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;
+
+ // Keywords with no associated values
+ if (key == "CONSTANT") {
+ property->setConstant();
+ continue;
+ }
+ else if (key == "FINAL") {
+ property->setFinal();
+ continue;
+ }
+
+ if (match(Tok_Ident) || match(Tok_Number)) {
+ 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")
+ qdb_->addPropertyFunction(property, value, PropertyNode::Getter);
+ else if (key == "WRITE") {
+ qdb_->addPropertyFunction(property, value, PropertyNode::Setter);
+ property->setWritable(true);
+ }
+ else if (key == "STORED")
+ property->setStored(value.toLower() == "true");
+ else if (key == "DESIGNABLE") {
+ QString v = value.toLower();
+ if (v == "true")
+ property->setDesignable(true);
+ else if (v == "false")
+ property->setDesignable(false);
+ else {
+ property->setDesignable(false);
+ property->setRuntimeDesFunc(value);
+ }
+ }
+ else if (key == "RESET")
+ qdb_->addPropertyFunction(property, value, PropertyNode::Resetter);
+ else if (key == "NOTIFY") {
+ qdb_->addPropertyFunction(property, value, PropertyNode::Notifier);
+ } else if (key == "REVISION") {
+ int revision;
+ bool ok;
+ revision = value.toInt(&ok);
+ if (ok)
+ property->setRevision(revision);
+ else
+ location().warning(tr("Invalid revision number: %1").arg(value));
+ } else if (key == "SCRIPTABLE") {
+ QString v = value.toLower();
+ if (v == "true")
+ property->setScriptable(true);
+ else if (v == "false")
+ property->setScriptable(false);
+ else {
+ property->setScriptable(false);
+ property->setRuntimeScrFunc(value);
+ }
+ }
+ }
+ match(Tok_RightParen);
+ return true;
+}
+
+/*!
+ Parse a C++ declaration.
+ */
+bool CppCodeParser::matchDeclList(Aggregate *parent)
+{
+ ExtraFuncData extra;
+ 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(parent);
+ break;
+ case Tok_template:
+ {
+ CodeChunk dataType;
+ readToken();
+ matchTemplateAngles(&dataType);
+ templateStuff = dataType.toString();
+ }
+ 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_Q_PRIVATE_PROPERTY:
+ case Tok_QDOC_PROPERTY:
+ if (!matchProperty(parent)) {
+ location().warning(tr("Failed to parse token %1 in property declaration").arg(lexeme()));
+ skipTo(Tok_RightParen);
+ match(Tok_RightParen);
+ }
+ 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 name = previousLexeme();
+ TypedefNode *flagsNode = new TypedefNode(parent, flagsType);
+ flagsNode->setAccess(access);
+ flagsNode->setLocation(location());
+ EnumNode* en = static_cast<EnumNode*>(parent->findChildNode(name, Node::Enum));
+ if (en)
+ en->setFlagsType(flagsNode);
+ }
+ }
+ match(Tok_RightParen);
+ break;
+ case Tok_QT_MODULE:
+ readToken();
+ if (match(Tok_LeftParen) && match(Tok_Ident))
+ physicalModuleName = previousLexeme();
+ if (!physicalModuleName.startsWith("Qt"))
+ physicalModuleName.prepend("Qt");
+ match(Tok_RightParen);
+ break;
+ default:
+ if (!matchFunctionDecl(parent, 0, 0, templateStuff, extra)) {
+ 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()
+{
+ ExtraFuncData extra;
+ const QSet<QString>& topicCommandsAllowed = topicCommands();
+ const QSet<QString>& otherMetacommandsAllowed = otherMetaCommands();
+ const 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);
+ Location end_loc(location());
+
+ /*
+ Doc parses the comment.
+ */
+ Doc doc(start_loc,end_loc,comment,metacommandsAllowed, topicCommandsAllowed);
+ QString topic;
+ bool isQmlPropertyTopic = false;
+ bool isJsPropertyTopic = false;
+
+ const TopicList& topics = doc.topicsUsed();
+ if (!topics.isEmpty()) {
+ topic = topics[0].topic;
+ if ((topic == COMMAND_QMLPROPERTY) ||
+ (topic == COMMAND_QMLPROPERTYGROUP) ||
+ (topic == COMMAND_QMLATTACHEDPROPERTY)) {
+ isQmlPropertyTopic = true;
+ }
+ else if ((topic == COMMAND_JSPROPERTY) ||
+ (topic == COMMAND_JSPROPERTYGROUP) ||
+ (topic == COMMAND_JSATTACHEDPROPERTY)) {
+ isJsPropertyTopic = true;
+ }
+ }
+ NodeList nodes;
+ DocList docs;
+
+ if (topic.isEmpty()) {
+ QStringList parentPath;
+ FunctionNode *clone;
+ FunctionNode *func = 0;
+
+ if (matchFunctionDecl(0, &parentPath, &clone, QString(), extra)) {
+ func = qdb_->findFunctionNode(parentPath, clone);
+ /*
+ If the node was not found, then search for it in the
+ open C++ namespaces. We don't expect this search to
+ be necessary often. Nor do we expect it to succeed
+ very often.
+ */
+ if (func == 0)
+ func = qdb_->findNodeInOpenNamespace(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 if (isQmlPropertyTopic || isJsPropertyTopic) {
+ Doc nodeDoc = doc;
+ processQmlProperties(nodeDoc, nodes, docs, isJsPropertyTopic);
+ }
+ else {
+ ArgList args;
+ const QSet<QString>& topicCommandsUsed = topicCommandsAllowed & doc.metaCommandsUsed();
+ if (topicCommandsUsed.count() > 0) {
+ topic = *topicCommandsUsed.constBegin();
+ args = doc.metaCommandArgs(topic);
+ }
+ if (topicCommandsUsed.count() > 1) {
+ QString topics;
+ QSet<QString>::ConstIterator t = topicCommandsUsed.constBegin();
+ while (t != topicCommandsUsed.constEnd()) {
+ topics += " \\" + *t + QLatin1Char(',');
+ ++t;
+ }
+ topics[topics.lastIndexOf(',')] = '.';
+ int i = topics.lastIndexOf(',');
+ topics[i] = ' ';
+ topics.insert(i+1,"and");
+ doc.location().warning(tr("Multiple topic commands found in comment: %1").arg(topics));
+ }
+ ArgList::ConstIterator a = args.constBegin();
+ while (a != args.constEnd()) {
+ 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);
+ checkModuleInclusion(*n);
+ if ((*n)->isAggregate() && ((Aggregate *)*n)->includes().isEmpty()) {
+ Aggregate *m = static_cast<Aggregate *>(*n);
+ while (m->parent() && m->physicalModuleName().isEmpty()) {
+ m = m->parent();
+ }
+ if (m == *n)
+ ((Aggregate *)*n)->addInclude((*n)->name());
+ else
+ ((Aggregate *)*n)->setIncludes(m->includes());
+ }
+ ++d;
+ ++n;
+ }
+ }
+ else if (tok == Tok_using) {
+ matchUsingDecl(0);
+ }
+ else {
+ QStringList parentPath;
+ FunctionNode *clone;
+ FunctionNode *node = 0;
+
+ if (matchFunctionDecl(0, &parentPath, &clone, QString(), extra)) {
+ /*
+ 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 = qdb_->findFunctionNode(parentPath, clone);
+ if (node != 0 && node->metaness() != FunctionNode::Signal)
+ node->setLocation(clone->location());
+ delete clone;
+ }
+ else {
+ if (tok != Tok_Doc)
+ readToken();
+ }
+ }
+ }
+ return true;
+}
+
+/*!
+ This function uses a Tokenizer to parse the function \a signature
+ in an attempt to match it to the signature of a child node of \a root.
+ If a match is found, \a funcPtr is set to point to the matching node
+ and true is returned.
+ */
+bool CppCodeParser::makeFunctionNode(const QString& signature,
+ QStringList* parentPathPtr,
+ FunctionNode** funcPtr,
+ ExtraFuncData& extra)
+{
+ Tokenizer* outerTokenizer = tokenizer;
+ int outerTok = tok;
+
+ QByteArray latin1 = signature.toLatin1();
+ Tokenizer stringTokenizer(location(), latin1);
+ stringTokenizer.setParsingFnOrMacro(true);
+ tokenizer = &stringTokenizer;
+ readToken();
+
+ inMacroCommand_ = extra.isMacro;
+ bool ok = matchFunctionDecl(extra.root, parentPathPtr, funcPtr, QString(), extra);
+ inMacroCommand_ = false;
+ // potential memory leak with funcPtr
+
+ tokenizer = outerTokenizer;
+ tok = outerTok;
+ return ok;
+}
+
+/*!
+ This function uses a Tokenizer to parse the \a parameters of a
+ function into the parameter vector \a {pvect}.
+ */
+bool CppCodeParser::parseParameters(const QString& parameters,
+ QVector<Parameter>& pvect,
+ bool& isQPrivateSignal)
+{
+ Tokenizer* outerTokenizer = tokenizer;
+ int outerTok = tok;
+
+ QByteArray latin1 = parameters.toLatin1();
+ Tokenizer stringTokenizer(Location(), latin1);
+ stringTokenizer.setParsingFnOrMacro(true);
+ tokenizer = &stringTokenizer;
+ readToken();
+
+ inMacroCommand_ = false;
+ do {
+ if (!matchParameter(pvect, isQPrivateSignal))
+ return false;
+ } while (match(Tok_Comma));
+
+ tokenizer = outerTokenizer;
+ tok = outerTok;
+ return true;
+}
+
+/*!
+ Create a new FunctionNode for a QML method or signal, as
+ specified by \a type, as a child of \a parent. \a sig is
+ the complete signature, and if \a attached is true, the
+ method or signal is "attached". \a qdoctag is the text of
+ the \a type.
+
+ \a parent is the QML class node. The QML module and QML
+ type names have already been consumed to find \a parent.
+ What remains in \a sig is the method signature. The method
+ must be a child of \a parent.
+ */
+FunctionNode* CppCodeParser::makeFunctionNode(const Doc& doc,
+ const QString& sig,
+ Aggregate* parent,
+ Node::NodeType type,
+ bool attached,
+ QString qdoctag)
+{
+ QStringList pp;
+ FunctionNode* fn = 0;
+ ExtraFuncData extra(parent, type, attached);
+ if (!makeFunctionNode(sig, &pp, &fn, extra) && !makeFunctionNode("void " + sig, &pp, &fn, extra)) {
+ doc.location().warning(tr("Invalid syntax in '\\%1'").arg(qdoctag));
+ }
+ return fn;
+}
+
+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.remove("\\\n");
+ QStringList lines = text.split(QLatin1Char('\n'));
+ lines = lines.filter("Q_DECLARE");
+ lines.replaceInStrings(QRegExp("#define Q[A-Z_]*\\(C\\)"), QString());
+
+ 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)
+{
+ QString resultingCode = macroDef;
+ resultingCode.replace(QRegExp("\\bC\\b"), container);
+ resultingCode.remove(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(QDocDatabase::qdocDB()->primaryTreeRoot());
+}
+
+void CppCodeParser::createExampleFileNodes(DocumentNode *dn)
+{
+ QString examplePath = dn->name();
+ QString proFileName = examplePath + QLatin1Char('/') + examplePath.split(QLatin1Char('/')).last() + ".pro";
+ QString userFriendlyFilePath;
+
+ QString fullPath = Config::findFile(dn->doc().location(),
+ exampleFiles,
+ exampleDirs,
+ proFileName,
+ userFriendlyFilePath);
+
+ if (fullPath.isEmpty()) {
+ QString tmp = proFileName;
+ proFileName = examplePath + QLatin1Char('/') + "qbuild.pro";
+ userFriendlyFilePath.clear();
+ fullPath = Config::findFile(dn->doc().location(),
+ exampleFiles,
+ exampleDirs,
+ proFileName,
+ userFriendlyFilePath);
+ if (fullPath.isEmpty()) {
+ proFileName = examplePath + QLatin1Char('/') + examplePath.split(QLatin1Char('/')).last() + ".qmlproject";
+ userFriendlyFilePath.clear();
+ fullPath = Config::findFile(dn->doc().location(),
+ exampleFiles,
+ exampleDirs,
+ proFileName,
+ userFriendlyFilePath);
+ if (fullPath.isEmpty()) {
+ QString details = QLatin1String("Example directories: ") + exampleDirs.join(QLatin1Char(' '));
+ if (!exampleFiles.isEmpty())
+ details += QLatin1String(", example files: ") + exampleFiles.join(QLatin1Char(' '));
+ dn->location().warning(tr("Cannot find file '%1' or '%2'").arg(tmp).arg(proFileName), details);
+ dn->location().warning(tr(" EXAMPLE PATH DOES NOT EXIST: %1").arg(examplePath), details);
+ return;
+ }
+ }
+ }
+
+ int sizeOfBoringPartOfName = fullPath.size() - proFileName.size();
+ if (fullPath.startsWith("./"))
+ sizeOfBoringPartOfName = sizeOfBoringPartOfName - 2;
+ fullPath.truncate(fullPath.lastIndexOf('/'));
+
+ QStringList exampleFiles = Config::getFilesHere(fullPath,exampleNameFilter);
+ QString imagesPath = fullPath + "/images";
+ QStringList imageFiles = Config::getFilesHere(imagesPath,exampleImageFilter);
+ 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 *.qmlproject qmldir");
+ }
+
+ foreach (const QString &exampleFile, exampleFiles) {
+ new DocumentNode(dn,
+ exampleFile.mid(sizeOfBoringPartOfName),
+ Node::File,
+ Node::NoPageType);
+ }
+ foreach (const QString &imageFile, imageFiles) {
+ new DocumentNode(dn,
+ imageFile.mid(sizeOfBoringPartOfName),
+ Node::Image,
+ Node::NoPageType);
+ }
+}
+
+QT_END_NAMESPACE
diff --git a/src/qdoc/cppcodeparser.h b/src/qdoc/cppcodeparser.h
new file mode 100644
index 000000000..ec0448232
--- /dev/null
+++ b/src/qdoc/cppcodeparser.h
@@ -0,0 +1,255 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL21$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** 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 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** As a special exception, The Qt Company gives you certain additional
+** rights. These rights are described in The Qt Company LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef CPPCODEPARSER_H
+#define CPPCODEPARSER_H
+
+#include <qregexp.h>
+
+#include "codeparser.h"
+
+QT_BEGIN_NAMESPACE
+
+class ClassNode;
+class CodeChunk;
+class CppCodeParserPrivate;
+class FunctionNode;
+class Aggregate;
+class Tokenizer;
+
+class CppCodeParser : public CodeParser
+{
+ Q_DECLARE_TR_FUNCTIONS(QDoc::CppCodeParser)
+
+ struct ExtraFuncData {
+ Aggregate* root; // Used as the parent.
+ Node::NodeType type; // The node type: Function, etc.
+ bool isAttached; // If true, the method is attached.
+ bool isMacro; // If true, we are parsing a macro signature.
+ ExtraFuncData() : root(0), type(Node::Function), isAttached(false), isMacro(false) { }
+ ExtraFuncData(Aggregate* r, Node::NodeType t, bool a)
+ : root(r), type(t), isAttached(a), isMacro(false) { }
+ };
+
+public:
+ CppCodeParser();
+ ~CppCodeParser();
+ static CppCodeParser* cppParser() { return cppParser_; }
+
+ virtual void initializeParser(const Config& config) Q_DECL_OVERRIDE;
+ virtual void terminateParser() Q_DECL_OVERRIDE;
+ virtual QString language() Q_DECL_OVERRIDE;
+ virtual QStringList headerFileNameFilter() Q_DECL_OVERRIDE;
+ virtual QStringList sourceFileNameFilter() Q_DECL_OVERRIDE;
+ virtual void parseHeaderFile(const Location& location, const QString& filePath) Q_DECL_OVERRIDE;
+ virtual void parseSourceFile(const Location& location, const QString& filePath) Q_DECL_OVERRIDE;
+ virtual void doneParsingHeaderFiles() Q_DECL_OVERRIDE;
+ virtual void doneParsingSourceFiles() Q_DECL_OVERRIDE;
+ bool parseParameters(const QString& parameters, QVector<Parameter>& pvect, bool& isQPrivateSignal);
+
+protected:
+ const QSet<QString>& topicCommands();
+ const QSet<QString>& otherMetaCommands();
+ virtual Node* processTopicCommand(const Doc& doc,
+ const QString& command,
+ const ArgLocPair& arg);
+ void processQmlProperties(const Doc& doc, NodeList& nodes, DocList& docs, bool jsProps);
+ bool splitQmlPropertyGroupArg(const QString& arg,
+ QString& module,
+ QString& element,
+ QString& name);
+ bool splitQmlPropertyArg(const QString& arg,
+ QString& type,
+ QString& module,
+ QString& element,
+ QString& name);
+ bool splitQmlMethodArg(const QString& arg,
+ QString& type,
+ QString& module,
+ QString& element);
+ virtual void processOtherMetaCommand(const Doc& doc,
+ const QString& command,
+ const ArgLocPair& argLocPair,
+ Node *node);
+ void processOtherMetaCommands(const Doc& doc, Node *node);
+
+ protected:
+ void reset();
+ void readToken();
+ const Location& location();
+ QString previousLexeme();
+ QString lexeme();
+
+ private:
+ bool match(int target);
+ bool skipTo(int target);
+ bool matchCompat();
+ bool matchModuleQualifier(QString& name);
+ bool matchTemplateAngles(CodeChunk *type = 0);
+ bool matchTemplateHeader();
+ bool matchDataType(CodeChunk *type, QString *var = 0);
+ bool matchParameter(QVector<Parameter>& pvect, bool& isQPrivateSignal);
+ bool matchFunctionDecl(Aggregate *parent,
+ QStringList *parentPathPtr,
+ FunctionNode **funcPtr,
+ const QString &templateStuff,
+ ExtraFuncData& extra);
+ bool matchBaseSpecifier(ClassNode *classe, bool isClass);
+ bool matchBaseList(ClassNode *classe, bool isClass);
+ bool matchClassDecl(Aggregate *parent,
+ const QString &templateStuff = QString());
+ bool matchNamespaceDecl(Aggregate *parent);
+ bool matchUsingDecl(Aggregate* parent);
+ bool matchEnumItem(Aggregate *parent, EnumNode *enume);
+ bool matchEnumDecl(Aggregate *parent);
+ bool matchTypedefDecl(Aggregate *parent);
+ bool matchProperty(Aggregate *parent);
+ bool matchDeclList(Aggregate *parent);
+ bool matchDocsAndStuff();
+ bool makeFunctionNode(const QString &synopsis,
+ QStringList *parentPathPtr,
+ FunctionNode **funcPtr,
+ ExtraFuncData& params);
+ FunctionNode* makeFunctionNode(const Doc& doc,
+ const QString& sig,
+ Aggregate* parent,
+ Node::NodeType type,
+ bool attached,
+ QString qdoctag);
+ void parseQiteratorDotH(const Location &location, const QString &filePath);
+ void instantiateIteratorMacro(const QString &container,
+ const QString &includeFile,
+ const QString &macroDef);
+ void createExampleFileNodes(DocumentNode *dn);
+ int matchFunctionModifier();
+
+ protected:
+ QMap<QString, Node::NodeType> nodeTypeMap;
+ Tokenizer *tokenizer;
+ int tok;
+ Node::Access access;
+ FunctionNode::Metaness metaness_;
+ QString physicalModuleName;
+ QStringList lastPath_;
+ QRegExp varComment;
+ QRegExp sep;
+
+ private:
+ QString sequentialIteratorDefinition;
+ QString mutableSequentialIteratorDefinition;
+ QString associativeIteratorDefinition;
+ QString mutableAssociativeIteratorDefinition;
+ QMap<QString, QString> sequentialIteratorClasses;
+ QMap<QString, QString> mutableSequentialIteratorClasses;
+ QMap<QString, QString> associativeIteratorClasses;
+ QMap<QString, QString> mutableAssociativeIteratorClasses;
+
+ static QStringList exampleFiles;
+ static QStringList exampleDirs;
+ static CppCodeParser* cppParser_;
+ QString exampleNameFilter;
+ QString exampleImageFilter;
+};
+
+#define COMMAND_ABSTRACT Doc::alias("abstract")
+#define COMMAND_CLASS Doc::alias("class")
+#define COMMAND_CONTENTSPAGE Doc::alias("contentspage")
+#define COMMAND_DITAMAP Doc::alias("ditamap")
+#define COMMAND_ENUM Doc::alias("enum")
+#define COMMAND_EXAMPLE Doc::alias("example")
+#define COMMAND_EXTERNALPAGE Doc::alias("externalpage")
+#define COMMAND_FILE Doc::alias("file")
+#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")
+#define COMMAND_MACRO Doc::alias("macro")
+#define COMMAND_MODULE Doc::alias("module")
+#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_STARTPAGE Doc::alias("startpage")
+#define COMMAND_TYPEDEF Doc::alias("typedef")
+#define COMMAND_VARIABLE Doc::alias("variable")
+#define COMMAND_QMLABSTRACT Doc::alias("qmlabstract")
+#define COMMAND_QMLTYPE Doc::alias("qmltype")
+#define COMMAND_QMLPROPERTY Doc::alias("qmlproperty")
+#define COMMAND_QMLPROPERTYGROUP Doc::alias("qmlpropertygroup")
+#define COMMAND_QMLATTACHEDPROPERTY Doc::alias("qmlattachedproperty")
+#define COMMAND_QMLINHERITS Doc::alias("inherits")
+#define COMMAND_QMLINSTANTIATES Doc::alias("instantiates")
+#define COMMAND_QMLSIGNAL Doc::alias("qmlsignal")
+#define COMMAND_QMLATTACHEDSIGNAL Doc::alias("qmlattachedsignal")
+#define COMMAND_QMLMETHOD Doc::alias("qmlmethod")
+#define COMMAND_QMLATTACHEDMETHOD Doc::alias("qmlattachedmethod")
+#define COMMAND_QMLDEFAULT Doc::alias("default")
+#define COMMAND_QMLREADONLY Doc::alias("readonly")
+#define COMMAND_QMLBASICTYPE Doc::alias("qmlbasictype")
+#define COMMAND_QMLMODULE Doc::alias("qmlmodule")
+#define COMMAND_AUDIENCE Doc::alias("audience")
+#define COMMAND_CATEGORY Doc::alias("category")
+#define COMMAND_PRODNAME Doc::alias("prodname")
+#define COMMAND_COMPONENT Doc::alias("component")
+#define COMMAND_AUTHOR Doc::alias("author")
+#define COMMAND_PUBLISHER Doc::alias("publisher")
+#define COMMAND_COPYRYEAR Doc::alias("copyryear")
+#define COMMAND_COPYRHOLDER Doc::alias("copyrholder")
+#define COMMAND_PERMISSIONS Doc::alias("permissions")
+#define COMMAND_LIFECYCLEVERSION Doc::alias("lifecycleversion")
+#define COMMAND_LIFECYCLEWSTATUS Doc::alias("lifecyclestatus")
+#define COMMAND_LICENSEYEAR Doc::alias("licenseyear")
+#define COMMAND_LICENSENAME Doc::alias("licensename")
+#define COMMAND_LICENSEDESCRIPTION Doc::alias("licensedescription")
+#define COMMAND_RELEASEDATE Doc::alias("releasedate")
+#define COMMAND_QTVARIABLE Doc::alias("qtvariable")
+// Some of these are not used currenmtly, but they are included now for completeness.
+#define COMMAND_JSTYPE Doc::alias("jstype")
+#define COMMAND_JSPROPERTY Doc::alias("jsproperty")
+#define COMMAND_JSPROPERTYGROUP Doc::alias("jspropertygroup")
+#define COMMAND_JSATTACHEDPROPERTY Doc::alias("jsattachedproperty")
+#define COMMAND_JSSIGNAL Doc::alias("jssignal")
+#define COMMAND_JSATTACHEDSIGNAL Doc::alias("jsattachedsignal")
+#define COMMAND_JSMETHOD Doc::alias("jsmethod")
+#define COMMAND_JSATTACHEDMETHOD Doc::alias("jsattachedmethod")
+#define COMMAND_JSBASICTYPE Doc::alias("jsbasictype")
+#define COMMAND_JSMODULE Doc::alias("jsmodule")
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/qdoc/doc.cpp b/src/qdoc/doc.cpp
new file mode 100644
index 000000000..4ed589454
--- /dev/null
+++ b/src/qdoc/doc.cpp
@@ -0,0 +1,3380 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL21$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** 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 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** As a special exception, The Qt Company gives you certain additional
+** rights. These rights are described in The Qt Company LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** $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 "atom.h"
+#include "tokenizer.h"
+#include <qdatetime.h>
+#include <qfile.h>
+#include <qfileinfo.h>
+#include <qhash.h>
+#include <qtextstream.h>
+#include <qregexp.h>
+#include <ctype.h>
+#include <limits.h>
+#include <qdebug.h>
+#include "generator.h"
+
+QT_BEGIN_NAMESPACE
+
+Q_GLOBAL_STATIC(QSet<QString>, null_Set_QString)
+Q_GLOBAL_STATIC(TopicList, nullTopicList)
+Q_GLOBAL_STATIC(QStringList, null_QStringList)
+Q_GLOBAL_STATIC(QList<Text>, null_QList_Text)
+Q_GLOBAL_STATIC(QStringMultiMap, null_QStringMultiMap)
+
+struct Macro
+{
+ QString defaultDef;
+ Location defaultDefLocation;
+ QStringMap otherDefs;
+ int numParams;
+};
+
+enum {
+ CMD_A,
+ CMD_ANNOTATEDLIST,
+ CMD_B,
+ CMD_BADCODE,
+ CMD_BOLD,
+ CMD_BR,
+ CMD_BRIEF,
+ CMD_C,
+ CMD_CAPTION,
+ CMD_CHAPTER,
+ CMD_CODE,
+ CMD_CODELINE,
+ CMD_DIV,
+ CMD_DOTS,
+ CMD_E,
+ CMD_ELSE,
+ CMD_ENDCHAPTER,
+ CMD_ENDCODE,
+ CMD_ENDDIV,
+ CMD_ENDFOOTNOTE,
+ CMD_ENDIF,
+ CMD_ENDLEGALESE,
+ CMD_ENDLINK,
+ CMD_ENDLIST,
+ CMD_ENDMAPREF,
+ CMD_ENDOMIT,
+ CMD_ENDPART,
+ CMD_ENDQUOTATION,
+ CMD_ENDRAW,
+ CMD_ENDSECTION1,
+ CMD_ENDSECTION2,
+ CMD_ENDSECTION3,
+ CMD_ENDSECTION4,
+ CMD_ENDSIDEBAR,
+ CMD_ENDTABLE,
+ CMD_ENDTOPICREF,
+ CMD_FOOTNOTE,
+ CMD_GENERATELIST,
+ CMD_GRANULARITY,
+ CMD_HEADER,
+ CMD_HR,
+ CMD_I,
+ CMD_IF,
+ CMD_IMAGE,
+ CMD_IMPORTANT,
+ CMD_INCLUDE,
+ CMD_INLINEIMAGE,
+ CMD_INDEX,
+ CMD_INPUT,
+ CMD_KEYWORD,
+ CMD_L,
+ CMD_LEGALESE,
+ CMD_LI,
+ CMD_LINK,
+ CMD_LIST,
+ CMD_MAPREF,
+ CMD_META,
+ CMD_NEWCODE,
+ CMD_NOTE,
+ 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_SINCELIST,
+ CMD_SKIPLINE,
+ CMD_SKIPTO,
+ CMD_SKIPUNTIL,
+ CMD_SNIPPET,
+ CMD_SPAN,
+ CMD_SUB,
+ CMD_SUP,
+ CMD_TABLE,
+ CMD_TABLEOFCONTENTS,
+ CMD_TARGET,
+ CMD_TOPICREF,
+ CMD_TT,
+ CMD_UICONTROL,
+ CMD_UNDERLINE,
+ CMD_UNICODE,
+ CMD_VALUE,
+ CMD_WARNING,
+ CMD_QML,
+ CMD_ENDQML,
+ CMD_CPP,
+ CMD_ENDCPP,
+ CMD_QMLTEXT,
+ CMD_ENDQMLTEXT,
+ CMD_CPPTEXT,
+ CMD_ENDCPPTEXT,
+ CMD_JS,
+ CMD_ENDJS,
+ NOT_A_CMD
+};
+
+static struct {
+ const char *english;
+ int no;
+ QString *alias;
+} cmds[] = {
+ { "a", CMD_A, 0 },
+ { "annotatedlist", CMD_ANNOTATEDLIST, 0 },
+ { "b", CMD_B, 0 },
+ { "badcode", CMD_BADCODE, 0 },
+ { "bold", CMD_BOLD, 0 },
+ { "br", CMD_BR, 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},
+ { "div", CMD_DIV, 0 },
+ { "dots", CMD_DOTS, 0 },
+ { "e", CMD_E, 0 },
+ { "else", CMD_ELSE, 0 },
+ { "endchapter", CMD_ENDCHAPTER, 0 },
+ { "endcode", CMD_ENDCODE, 0 },
+ { "enddiv", CMD_ENDDIV, 0 },
+ { "endfootnote", CMD_ENDFOOTNOTE, 0 },
+ { "endif", CMD_ENDIF, 0 },
+ { "endlegalese", CMD_ENDLEGALESE, 0 },
+ { "endlink", CMD_ENDLINK, 0 },
+ { "endlist", CMD_ENDLIST, 0 },
+ { "endmapref", CMD_ENDMAPREF, 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 },
+ { "endtopicref", CMD_ENDTOPICREF, 0 },
+ { "footnote", CMD_FOOTNOTE, 0 },
+ { "generatelist", CMD_GENERATELIST, 0 },
+ { "granularity", CMD_GRANULARITY, 0 }, // ### don't document for now
+ { "header", CMD_HEADER, 0 },
+ { "hr", CMD_HR, 0 },
+ { "i", CMD_I, 0 },
+ { "if", CMD_IF, 0 },
+ { "image", CMD_IMAGE, 0 },
+ { "important", CMD_IMPORTANT, 0 },
+ { "include", CMD_INCLUDE, 0 },
+ { "inlineimage", CMD_INLINEIMAGE, 0 },
+ { "index", CMD_INDEX, 0 }, // ### don't document for now
+ { "input", CMD_INPUT, 0 },
+ { "keyword", CMD_KEYWORD, 0 },
+ { "l", CMD_L, 0 },
+ { "legalese", CMD_LEGALESE, 0 },
+ { "li", CMD_LI, 0 },
+ { "link", CMD_LINK, 0 },
+ { "list", CMD_LIST, 0 },
+ { "mapref", CMD_MAPREF, 0 },
+ { "meta", CMD_META, 0 },
+ { "newcode", CMD_NEWCODE, 0 },
+ { "note", CMD_NOTE, 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 },
+ { "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 },
+ { "sincelist", CMD_SINCELIST, 0 },
+ { "skipline", CMD_SKIPLINE, 0 },
+ { "skipto", CMD_SKIPTO, 0 },
+ { "skipuntil", CMD_SKIPUNTIL, 0 },
+ { "snippet", CMD_SNIPPET, 0 },
+ { "span", CMD_SPAN, 0 },
+ { "sub", CMD_SUB, 0 },
+ { "sup", CMD_SUP, 0 },
+ { "table", CMD_TABLE, 0 },
+ { "tableofcontents", CMD_TABLEOFCONTENTS, 0 },
+ { "target", CMD_TARGET, 0 },
+ { "topicref", CMD_TOPICREF, 0 },
+ { "tt", CMD_TT, 0 },
+ { "uicontrol", CMD_UICONTROL, 0 },
+ { "underline", CMD_UNDERLINE, 0 },
+ { "unicode", CMD_UNICODE, 0 },
+ { "value", CMD_VALUE, 0 },
+ { "warning", CMD_WARNING, 0 },
+ { "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 },
+ { "js", CMD_JS, 0 },
+ { "endjs", CMD_ENDJS, 0 },
+ { 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:
+ Doc::Sections granularity_;
+ Doc::Sections section_; // ###
+ QList<Atom*> tableOfContents_;
+ QVector<int> tableOfContentsLevels_;
+ QList<Atom*> keywords_;
+ QList<Atom*> targets_;
+ QStringMultiMap metaMap_;
+
+ DocPrivateExtra()
+ : granularity_(Doc::Part)
+ , section_(Doc::NoSection)
+ { }
+};
+
+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();
+}
+
+typedef QMap<QString, ArgList> CommandMap;
+
+class DocPrivate : public Shared
+{
+public:
+ DocPrivate(const Location& start = Location::null,
+ const Location& end = Location::null,
+ const QString& source = QString());
+ ~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;
+ CommandMap metaCommandMap;
+ bool hasLegalese : 1;
+ bool hasSectioningUnits : 1;
+ DocPrivateExtra *extra;
+ TopicList topics_;
+ DitaRefList ditamap_;
+};
+
+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.
+}
+
+/*!
+ If the doc is a ditamap, the destructor deletes each element
+ in the ditamap structure. These were allocated as needed.
+ */
+DocPrivate::~DocPrivate()
+{
+ delete extra;
+ foreach (DitaRef* t, ditamap_) {
+ delete t;
+ }
+}
+
+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(QLatin1Char(':'));
+ }
+ 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
+{
+ Q_DECLARE_TR_FUNCTIONS(QDoc::DocParser)
+
+public:
+ void parse(const QString &source,
+ DocPrivate *docPrivate,
+ const QSet<QString> &metaCommandSet,
+ const QSet<QString>& possibleTopics);
+
+ 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 insertTarget(const QString& target, bool keyword);
+ void include(const QString& fileName, const QString& identifier);
+ void startFormat(const QString& format, int cmd);
+ bool openCommand(int cmd);
+ bool closeCommand(int endCmd);
+ void startSection(Doc::Sections unit, int cmd);
+ void endSection(int unit, int endCmd);
+ void parseAlso();
+ void append(const QString &string);
+ void append(Atom::AtomType type, const QString& string = QString());
+ void append(Atom::AtomType type, const QString& p1, const QString& p2);
+ void append(const QString& p1, const QString& p2);
+ void appendChar(QChar ch);
+ void appendWord(const QString &word);
+ void appendToCode(const QString &code);
+ void appendToCode(const QString &code, Atom::AtomType defaultType);
+ void startNewPara();
+ void enterPara(Atom::AtomType leftType = Atom::ParaLeft,
+ Atom::AtomType rightType = Atom::ParaRight,
+ const QString& string = QString());
+ void leavePara();
+ void leaveValue();
+ void leaveValueList();
+ void leaveTableRow();
+ CodeMarker *quoteFromFile();
+ void expandMacro(const QString& name, const QString& def, int numParams);
+ QString expandMacroToString(const QString &name, const QString &def, int numParams);
+ Doc::Sections getSectioningUnit();
+ QString getArgument(bool verbatim = false);
+ QString getBracedArgument(bool verbatim);
+ QString getBracketedArgument();
+ 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();
+ bool isLeftBracketAhead();
+ void skipSpacesOnLine();
+ void skipSpacesOrOneEndl();
+ void skipAllSpaces();
+ void skipToNextPreprocessorCommand();
+
+ QStack<int> openedInputs;
+
+ QString in;
+ int pos;
+ int len;
+ Location cachedLoc;
+ int cachedPos;
+
+ DocPrivate* priv;
+ enum ParagraphState {
+ OutsideParagraph,
+ InSingleLineParagraph,
+ InMultiLineParagraph
+ };
+ ParagraphState paraState;
+ bool inTableHeader;
+ bool inTableRow;
+ bool inTableItem;
+ bool indexStartedPara; // ### rename
+ Atom::AtomType pendingParaLeftType;
+ Atom::AtomType pendingParaRightType;
+ QString pendingParaString;
+
+ int braceDepth;
+ int minIndent;
+ Doc::Sections currentSection;
+ QMap<QString, Location> targetMap_;
+ QMap<int, QString> pendingFormats;
+ QStack<int> openedCommands;
+ QStack<OpenedList> openedLists;
+ Quoter quoter;
+ QStack<DitaRef*> ditarefs_;
+};
+
+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,
+ const QSet<QString>& possibleTopics)
+{
+ in = source;
+ pos = 0;
+ len = in.length();
+ cachedLoc = docPrivate->start_loc;
+ cachedPos = 0;
+ priv = docPrivate;
+ priv->text << Atom::Nop;
+ priv->topics_.clear();
+
+ paraState = OutsideParagraph;
+ inTableHeader = false;
+ inTableRow = false;
+ inTableItem = false;
+ indexStartedPara = false;
+ pendingParaLeftType = Atom::Nop;
+ pendingParaRightType = Atom::Nop;
+
+ braceDepth = 0;
+ minIndent = INT_MAX;
+ currentSection = Doc::NoSection;
+ openedCommands.push(CMD_OMIT);
+ quoter.reset();
+
+ CodeMarker *marker = 0;
+ Atom *currentLinkAtom = 0;
+ QString p1, p2;
+ 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();
+ p1 = getArgument();
+ append(Atom::FormattingLeft,ATOM_FORMATTING_PARAMETER);
+ append(Atom::String, p1);
+ append(Atom::FormattingRight,ATOM_FORMATTING_PARAMETER);
+ priv->params.insert(p1);
+ break;
+ case CMD_BADCODE:
+ leavePara();
+ append(Atom::CodeBad,getCode(CMD_BADCODE, marker));
+ break;
+ case CMD_BR:
+ enterPara();
+ append(Atom::BR);
+ break;
+ case CMD_BOLD:
+ location().warning(tr("'\\bold' is deprecated. Use '\\b'"));
+ case CMD_B:
+ startFormat(ATOM_FORMATTING_BOLD, cmd);
+ break;
+ case CMD_BRIEF:
+ leavePara();
+ enterPara(Atom::BriefLeft, Atom::BriefRight);
+ break;
+ case CMD_C:
+ enterPara();
+ p1 = untabifyEtc(getArgument(true));
+ marker = CodeMarker::markerForCode(p1);
+ append(Atom::C, marker->markedUpCode(p1, 0, location()));
+ break;
+ case CMD_CAPTION:
+ leavePara();
+ enterPara(Atom::CaptionLeft, Atom::CaptionRight);
+ break;
+ case CMD_CHAPTER:
+ startSection(Doc::Chapter, cmd);
+ break;
+ case CMD_CODE:
+ leavePara();
+ append(Atom::Code, getCode(CMD_CODE, 0));
+ break;
+ case CMD_QML:
+ leavePara();
+ append(Atom::Qml, getCode(CMD_QML, CodeMarker::markerForLanguage(QLatin1String("QML"))));
+ break;
+ case CMD_QMLTEXT:
+ append(Atom::QmlText);
+ break;
+ case CMD_JS:
+ leavePara();
+ append(Atom::JavaScript, getCode(CMD_JS, CodeMarker::markerForLanguage(QLatin1String("JavaScript"))));
+ break;
+ case CMD_DIV:
+ leavePara();
+ p1 = getArgument(true);
+ append(Atom::DivLeft, p1);
+ openedCommands.push(cmd);
+ break;
+ case CMD_ENDDIV:
+ leavePara();
+ append(Atom::DivRight);
+ closeCommand(cmd);
+ break;
+ case CMD_CODELINE:
+ {
+ 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, " ");
+ }
+ }
+ break;
+ case CMD_DOTS:
+ {
+ 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);
+ }
+ }
+ 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_ENDCHAPTER:
+ endSection(Doc::Chapter, cmd);
+ break;
+ case CMD_ENDCODE:
+ closeCommand(cmd);
+ break;
+ case CMD_ENDQML:
+ closeCommand(cmd);
+ break;
+ case CMD_ENDQMLTEXT:
+ append(Atom::EndQmlText);
+ break;
+ case CMD_ENDJS:
+ closeCommand(cmd);
+ break;
+ case CMD_ENDFOOTNOTE:
+ if (closeCommand(cmd)) {
+ leavePara();
+ append(Atom::FootnoteRight);
+ paraState = InMultiLineParagraph; // ###
+ }
+ 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(QLatin1Char(' ')))
+ 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_ENDMAPREF:
+ case CMD_ENDTOPICREF:
+ if (closeCommand(cmd)) {
+ ditarefs_.pop(); // zzz
+ }
+ break;
+ case CMD_ENDOMIT:
+ closeCommand(cmd);
+ break;
+ case CMD_ENDPART:
+ endSection(Doc::Part, 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(Doc::Section1, cmd);
+ break;
+ case CMD_ENDSECTION2:
+ endSection(Doc::Section2, cmd);
+ break;
+ case CMD_ENDSECTION3:
+ endSection(Doc::Section3, cmd);
+ break;
+ case CMD_ENDSECTION4:
+ endSection(Doc::Section4, 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_FOOTNOTE:
+ if (openCommand(cmd)) {
+ enterPara();
+ append(Atom::FootnoteLeft);
+ paraState = OutsideParagraph; // ###
+ }
+ break;
+ case CMD_ANNOTATEDLIST:
+ append(Atom::AnnotatedList, getArgument());
+ break;
+ case CMD_SINCELIST:
+ append(Atom::SinceList, getRestOfLine().simplified());
+ break;
+ case CMD_GENERATELIST:
+ {
+ QString arg1 = getArgument();
+ QString arg2 = getOptionalArgument();
+ if (!arg2.isEmpty())
+ arg1 += " " + arg2;
+ append(Atom::GeneratedList, arg1);
+ }
+ 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:
+ location().warning(tr("'\\i' is deprecated. Use '\\e' for italic or '\\li' for list item"));
+ case CMD_E:
+ startFormat(ATOM_FORMATTING_ITALIC, cmd);
+ break;
+ case CMD_HR:
+ leavePara();
+ append(Atom::HR);
+ 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_IMPORTANT:
+ leavePara();
+ enterPara(Atom::ImportantLeft, Atom::ImportantRight);
+ break;
+ case CMD_INCLUDE:
+ case CMD_INPUT:
+ {
+ QString fileName = getArgument();
+ QString identifier = getRestOfLine();
+ include(fileName, identifier);
+ }
+ break;
+ case CMD_INLINEIMAGE:
+ enterPara();
+ append(Atom::InlineImage, getArgument());
+ append(Atom::ImageText, getRestOfLine());
+ append(Atom::String, " ");
+ break;
+ case CMD_INDEX:
+ if (paraState == OutsideParagraph) {
+ 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 (isLeftBracketAhead()) {
+ p2 = getBracketedArgument();
+ }
+ if (isLeftBraceAhead()) {
+ p1 = getArgument();
+ append(p1, p2);
+ if (!p2.isEmpty() && !(priv->text.lastAtom()->error().isEmpty())) {
+ location().warning(tr("Check parameter in '[ ]' of '\\l' command: '%1', "
+ "possible misspelling, or unrecognized module name")
+ .arg(priv->text.lastAtom()->error()));
+ }
+ if (isLeftBraceAhead()) {
+ currentLinkAtom = priv->text.lastAtom();
+ startFormat(ATOM_FORMATTING_LINK, cmd);
+ }
+ else {
+ append(Atom::FormattingLeft, ATOM_FORMATTING_LINK);
+ append(Atom::String, cleanLink(p1));
+ append(Atom::FormattingRight, ATOM_FORMATTING_LINK);
+ }
+ }
+ else {
+ p1 = getArgument();
+ append(p1, p2);
+ if (!p2.isEmpty() && !(priv->text.lastAtom()->error().isEmpty())) {
+ location().warning(tr("Check parameter in '[ ]' of '\\l' command: '%1', "
+ "possible misspelling, or unrecognized module name")
+ .arg(priv->text.lastAtom()->error()));
+ }
+ append(Atom::FormattingLeft, ATOM_FORMATTING_LINK);
+ append(Atom::String, cleanLink(p1));
+ append(Atom::FormattingRight, ATOM_FORMATTING_LINK);
+ }
+ p2.clear();
+ break;
+ case CMD_LEGALESE:
+ leavePara();
+ if (openCommand(cmd))
+ append(Atom::LegaleseLeft);
+ docPrivate->hasLegalese = true;
+ break;
+ case CMD_LINK:
+ if (openCommand(cmd)) {
+ enterPara();
+ p1 = getArgument();
+ append(p1);
+ append(Atom::FormattingLeft, ATOM_FORMATTING_LINK);
+ skipSpacesOrOneEndl();
+ }
+ break;
+ case CMD_LIST:
+ if (openCommand(cmd)) {
+ leavePara();
+ openedLists.push(OpenedList(location(),
+ getOptionalArgument()));
+ }
+ break;
+ case CMD_TOPICREF:
+ case CMD_MAPREF:
+ if (openCommand(cmd)) {
+ DitaRef* t = 0;
+ if (cmd == CMD_MAPREF)
+ t = new MapRef();
+ else
+ t = new TopicRef();
+ t->setNavtitle(getArgument(true));
+ if (cmd == CMD_MAPREF)
+ t->setHref(getArgument());
+ else
+ t->setHref(getOptionalArgument());
+ if (ditarefs_.isEmpty())
+ priv->ditamap_.append(t);
+ else
+ ditarefs_.top()->appendSubref(t);
+ ditarefs_.push(t);
+ }
+ break;
+ case CMD_META:
+ priv->constructExtra();
+ p1 = getArgument();
+ priv->extra->metaMap_.insert(p1, getArgument());
+ break;
+ case CMD_NEWCODE:
+ location().warning(tr("Unexpected '\\%1'").arg(cmdName(CMD_NEWCODE)));
+ break;
+ case CMD_NOTE:
+ leavePara();
+ enterPara(Atom::NoteLeft, Atom::NoteRight);
+ break;
+ case CMD_O:
+ location().warning(tr("'\\o' is deprecated. Use '\\li'"));
+ case CMD_LI:
+ 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) {
+ p1 = "1,1";
+ p2.clear();
+ if (isLeftBraceAhead()) {
+ p1 = getArgument();
+ if (isLeftBraceAhead()) {
+ p2 = getArgument();
+ }
+ }
+
+ if (!inTableHeader && !inTableRow) {
+ location().warning(tr("Missing '\\%1' or '\\%2' before '\\%3'")
+ .arg(cmdName(CMD_HEADER))
+ .arg(cmdName(CMD_ROW))
+ .arg(cmdName(CMD_LI)));
+ append(Atom::TableRowLeft);
+ inTableRow = true;
+ }
+ else if (inTableItem) {
+ append(Atom::TableItemRight);
+ inTableItem = false;
+ }
+
+ append(Atom::TableItemLeft, p1, p2);
+ 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();
+ append(Atom::CodeOld, getCode(CMD_OLDCODE, marker));
+ append(Atom::CodeNew, getCode(CMD_NEWCODE, marker));
+ break;
+ case CMD_OMIT:
+ getUntilEnd(cmd);
+ break;
+ case CMD_OMITVALUE:
+ p1 = getArgument();
+ if (!priv->enumItemList.contains(p1))
+ priv->enumItemList.append(p1);
+ if (!priv->omitEnumItemList.contains(p1))
+ priv->omitEnumItemList.append(p1);
+ 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, QString()));
+ 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();
+ p1 = getRestOfLine();
+ if (!quoting) {
+ quoter.quoteTo(location(), cmdStr,
+ slashed(marker->functionBeginRegExp(p1)));
+ append(Atom::Code,
+ quoter.quoteUntil(location(), cmdStr,
+ slashed(marker->functionEndRegExp(p1))));
+ quoter.reset();
+ }
+ else {
+ append(Atom::CodeQuoteCommand, cmdStr);
+ append(Atom::CodeQuoteArgument, slashed(marker->functionEndRegExp(p1)));
+ }
+ break;
+ case CMD_RAW:
+ leavePara();
+ p1 = getRestOfLine();
+ if (p1.isEmpty())
+ location().warning(tr("Missing format name after '\\%1'")
+ .arg(cmdName(CMD_RAW)));
+ append(Atom::FormatIf, p1);
+ append(Atom::RawString, untabifyEtc(getUntilEnd(cmd)));
+ append(Atom::FormatElse);
+ append(Atom::FormatEndif);
+ break;
+ case CMD_ROW:
+ if (openedCommands.top() == CMD_TABLE) {
+ p1.clear();
+ if (isLeftBraceAhead())
+ p1 = getArgument(true);
+ leaveTableRow();
+ append(Atom::TableRowLeft,p1);
+ 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_SPAN:
+ p1 = ATOM_FORMATTING_SPAN + getArgument(true);
+ startFormat(p1, cmd);
+ break;
+ case CMD_SNIPPET:
+ leavePara();
+ {
+ QString snippet = getArgument();
+ QString identifier = getRestOfLine();
+ if (quoting) {
+ append(Atom::SnippetCommand, cmdStr);
+ append(Atom::SnippetLocation, snippet);
+ append(Atom::SnippetIdentifier, identifier);
+ }
+ else {
+ marker = Doc::quoteFromFile(location(),quoter,snippet);
+ appendToCode(quoter.quoteSnippet(location(), identifier), marker->atomType());
+ }
+ }
+ break;
+ case CMD_SUB:
+ startFormat(ATOM_FORMATTING_SUBSCRIPT, cmd);
+ break;
+ case CMD_SUP:
+ startFormat(ATOM_FORMATTING_SUPERSCRIPT, cmd);
+ break;
+ case CMD_TABLE:
+ //p1 = getRestOfLine();
+ p1 = getOptionalArgument();
+ p2 = getOptionalArgument();
+ if (openCommand(cmd)) {
+ leavePara();
+ append(Atom::TableLeft, p1, p2);
+ inTableHeader = false;
+ inTableRow = false;
+ inTableItem = false;
+ }
+ break;
+ case CMD_TABLEOFCONTENTS:
+ p1 = "1";
+ if (isLeftBraceAhead())
+ p1 = getArgument();
+ p1 += QLatin1Char(',');
+ p1 += QString::number((int)getSectioningUnit());
+ append(Atom::TableOfContents, p1);
+ break;
+ case CMD_TARGET:
+ insertTarget(getRestOfLine(),false);
+ break;
+ case CMD_TT:
+ startFormat(ATOM_FORMATTING_TELETYPE, cmd);
+ break;
+ case CMD_UICONTROL:
+ startFormat(ATOM_FORMATTING_UICONTROL, cmd);
+ break;
+ case CMD_UNDERLINE:
+ startFormat(ATOM_FORMATTING_UNDERLINE, cmd);
+ break;
+ case CMD_UNICODE:
+ enterPara();
+ p1 = getArgument();
+ {
+ bool ok;
+ uint unicodeChar = p1.toUInt(&ok, 0);
+ if (!ok ||
+ (unicodeChar == 0x0000) ||
+ (unicodeChar > 0xFFFE)) {
+ location().warning(tr("Invalid Unicode character '%1' specified "
+ "with '%2'")
+ .arg(p1, cmdName(CMD_UNICODE)));
+ }
+ else {
+ append(Atom::String, QChar(unicodeChar));
+ }
+ }
+ break;
+ case CMD_VALUE:
+ leaveValue();
+ if (openedLists.top().style() == OpenedList::Value) {
+ p1 = getArgument();
+ if (!priv->enumItemList.contains(p1))
+ priv->enumItemList.append(p1);
+
+ openedLists.top().next();
+ append(Atom::ListTagLeft, ATOM_LIST_VALUE);
+ append(Atom::String, p1);
+ append(Atom::ListTagRight, ATOM_LIST_VALUE);
+ append(Atom::ListItemLeft, ATOM_LIST_VALUE);
+
+ skipSpacesOrOneEndl();
+ if (isBlankLine())
+ append(Atom::Nop);
+ }
+ else {
+ // ### problems
+ }
+ break;
+ case CMD_WARNING:
+ leavePara();
+ enterPara();
+ append(Atom::FormattingLeft, ATOM_FORMATTING_BOLD);
+ append(Atom::String, "Warning:");
+ append(Atom::FormattingRight, ATOM_FORMATTING_BOLD);
+ append(Atom::String, " ");
+ break;
+ case CMD_OVERLOAD:
+ priv->metacommandsUsed.insert(cmdStr);
+ p1.clear();
+ if (!isBlankLine())
+ p1 = getRestOfLine();
+ if (!p1.isEmpty()) {
+ append(Atom::ParaLeft);
+ append(Atom::String, "This function overloads ");
+ append(Atom::AutoLink,p1);
+ append(Atom::String, ".");
+ append(Atom::ParaRight);
+ }
+ else {
+ append(Atom::ParaLeft);
+ append(Atom::String,"This is an overloaded function.");
+ append(Atom::ParaRight);
+ p1 = getMetaCommandArgument(cmdStr);
+ }
+ priv->metaCommandMap[cmdStr].append(ArgLocPair(p1,location()));
+ break;
+ case NOT_A_CMD:
+ if (metaCommandSet.contains(cmdStr)) {
+ priv->metacommandsUsed.insert(cmdStr);
+ QString arg = getMetaCommandArgument(cmdStr);
+ priv->metaCommandMap[cmdStr].append(ArgLocPair(arg,location()));
+ if (possibleTopics.contains(cmdStr)) {
+ priv->topics_.append(Topic(cmdStr,arg));
+ }
+ }
+ else if (macroHash()->contains(cmdStr)) {
+ const Macro &macro = macroHash()->value(cmdStr);
+ int numPendingFi = 0;
+ QStringMap::ConstIterator d;
+ d = macro.otherDefs.constBegin();
+ while (d != macro.otherDefs.constEnd()) {
+ append(Atom::FormatIf, d.key());
+ expandMacro(cmdStr, *d, macro.numParams);
+ ++d;
+
+ if (d == macro.otherDefs.constEnd()) {
+ 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, expandMacroToString(cmdStr, macro.defaultDef, macro.numParams));
+ 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 == OutsideParagraph) {
+ if (ch.isSpace()) {
+ ++pos;
+ newWord = false;
+ }
+ else {
+ enterPara();
+ newWord = true;
+ }
+ }
+ else {
+ if (ch.isSpace()) {
+ ++pos;
+ if ((ch == '\n') &&
+ (paraState == InSingleLineParagraph ||
+ 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 > 0) {
+ if (word.startsWith(QString("__")))
+ appendWord(word);
+ else
+ 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)));
+ }
+
+ if (currentSection > Doc::NoSection) {
+ append(Atom::SectionRight, QString::number(currentSection));
+ currentSection = Doc::NoSection;
+ }
+
+ if (priv->extra && priv->extra->granularity_ < priv->extra->section_)
+ priv->extra->granularity_ = priv->extra->section_;
+ priv->text.stripFirstAtom();
+}
+
+/*!
+ Returns the current location.
+ */
+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::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());
+ priv->constructExtra();
+ if (keyword) {
+ append(Atom::Keyword, target);
+ priv->extra->keywords_.append(priv->text.lastAtom());
+ }
+ else {
+ append(Atom::Target, target);
+ priv->extra->targets_.append(priv->text.lastAtom());
+ }
+ }
+}
+
+void DocParser::include(const QString& fileName, const QString& identifier)
+{
+ if (location().depth() > 16)
+ location().fatal(tr("Too many nested '\\%1's").arg(cmdName(CMD_INCLUDE)));
+
+ QString userFriendlyFilePath;
+ QString filePath = Doc::config()->getIncludeFilePath(fileName);
+#if 0
+ QString filePath = Config::findFile(location(),
+ sourceFiles,
+ sourceDirs,
+ fileName,
+ userFriendlyFilePath);
+#endif
+ if (filePath.isEmpty()) {
+ location().warning(tr("Cannot find qdoc include file '%1'").arg(fileName));
+ }
+ else {
+ QFile inFile(filePath);
+ if (!inFile.open(QFile::ReadOnly)) {
+ location().warning(tr("Cannot open qdoc include file '%1'")
+ .arg(userFriendlyFilePath));
+ }
+ else {
+ location().push(userFriendlyFilePath);
+
+ QTextStream inStream(&inFile);
+ QString includedStuff = inStream.readAll();
+ inFile.close();
+
+ if (identifier.isEmpty()) {
+ in.insert(pos, includedStuff);
+ len = in.length();
+ openedInputs.push(pos + includedStuff.length());
+ }
+ else {
+ QStringList lineBuffer = includedStuff.split(QLatin1Char('\n'));
+ int i = 0;
+ int startLine = -1;
+ while (i < lineBuffer.size()) {
+ if (lineBuffer[i].startsWith("//!")) {
+ if (lineBuffer[i].contains(identifier)) {
+ startLine = i+1;
+ break;
+ }
+ }
+ ++i;
+ }
+ if (startLine < 0) {
+ location().warning(tr("Cannot find '%1' in '%2'")
+ .arg(identifier)
+ .arg(userFriendlyFilePath));
+ return;
+
+ }
+ QString result;
+ i = startLine;
+ do {
+ if (lineBuffer[i].startsWith("//!")) {
+ if (i<lineBuffer.size()) {
+ if (lineBuffer[i].contains(identifier)) {
+ break;
+ }
+ }
+ }
+ else
+ result += lineBuffer[i] + QLatin1Char('\n');
+ ++i;
+ } while (i < lineBuffer.size());
+ if (result.isEmpty()) {
+ location().warning(tr("Empty qdoc snippet '%1' in '%2'")
+ .arg(identifier)
+ .arg(userFriendlyFilePath));
+ }
+ else {
+ in.insert(pos, result);
+ len = in.length();
+ openedInputs.push(pos + result.length());
+ }
+ }
+ }
+ }
+}
+
+void DocParser::startFormat(const QString& format, int cmd)
+{
+ enterPara();
+
+ QMap<int, QString>::ConstIterator f = pendingFormats.constBegin();
+ while (f != pendingFormats.constEnd()) {
+ 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_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;
+ }
+ else if (outer == CMD_TOPICREF)
+ ok = (cmd == CMD_TOPICREF || cmd == CMD_MAPREF);
+ else if (outer == CMD_MAPREF)
+ ok = false;
+ }
+
+ if (ok) {
+ openedCommands.push(cmd);
+ }
+ else {
+ location().warning(tr("Can't use '\\%1' in '\\%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::Sections unit, int cmd)
+{
+ leaveValueList();
+
+ if (currentSection == Doc::NoSection) {
+ currentSection = (Doc::Sections) (unit);
+ priv->constructExtra();
+ priv->extra->section_ = currentSection;
+ }
+ else
+ endSection(unit,cmd);
+
+ append(Atom::SectionLeft, QString::number(unit));
+ priv->constructExtra();
+ priv->extra->tableOfContents_.append(priv->text.lastAtom());
+ priv->extra->tableOfContentsLevels_.append(unit);
+ enterPara(Atom::SectionHeadingLeft,
+ Atom::SectionHeadingRight,
+ QString::number(unit));
+ currentSection = unit;
+
+}
+
+void DocParser::endSection(int , int) // (int unit, int endCmd)
+{
+ leavePara();
+ append(Atom::SectionRight, QString::number(currentSection));
+ currentSection = (Doc::NoSection);
+}
+
+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::AtomType type, const QString &string)
+{
+ Atom::AtomType lastType = priv->text.lastAtom()->type();
+ if ((lastType == Atom::Code) && priv->text.lastAtom()->string().endsWith(QLatin1String("\n\n")))
+ priv->text.lastAtom()->chopString();
+ priv->text << Atom(type, string);
+}
+
+void DocParser::append(const QString &string)
+{
+ Atom::AtomType lastType = priv->text.lastAtom()->type();
+ if ((lastType == Atom::Code) && priv->text.lastAtom()->string().endsWith(QLatin1String("\n\n")))
+ priv->text.lastAtom()->chopString();
+ priv->text << Atom(string); // The Atom type is Link.
+}
+
+void DocParser::append(Atom::AtomType type, const QString& p1, const QString& p2)
+{
+ Atom::AtomType lastType = priv->text.lastAtom()->type();
+ if ((lastType == Atom::Code) && priv->text.lastAtom()->string().endsWith(QLatin1String("\n\n")))
+ priv->text.lastAtom()->chopString();
+ priv->text << Atom(type, p1, p2);
+}
+
+void DocParser::append(const QString& p1, const QString& p2)
+{
+ Atom::AtomType lastType = priv->text.lastAtom()->type();
+ if ((lastType == Atom::Code) && priv->text.lastAtom()->string().endsWith(QLatin1String("\n\n")))
+ priv->text.lastAtom()->chopString();
+ if (p2.isEmpty())
+ priv->text << Atom(p1); // The Atom type is Link.
+ else
+ priv->text << LinkAtom(p1, p2);
+}
+
+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::AtomType lastType = priv->text.lastAtom()->type();
+ if (lastType != Atom::Qml && lastType != Atom::Code && lastType != Atom::JavaScript)
+ append(Atom::Qml);
+ priv->text.lastAtom()->appendString(markedCode);
+}
+
+void DocParser::appendToCode(const QString &markedCode, Atom::AtomType defaultType)
+{
+ Atom::AtomType lastType = priv->text.lastAtom()->type();
+ if (lastType != Atom::Qml && lastType != Atom::Code && lastType != Atom::JavaScript)
+ append(defaultType, markedCode);
+ else
+ priv->text.lastAtom()->appendString(markedCode);
+}
+
+void DocParser::startNewPara()
+{
+ leavePara();
+ enterPara();
+}
+
+void DocParser::enterPara(Atom::AtomType leftType,
+ Atom::AtomType rightType,
+ const QString& string)
+{
+ if (paraState == OutsideParagraph) {
+
+ if ((priv->text.lastAtom()->type() != Atom::ListItemLeft) &&
+ (priv->text.lastAtom()->type() != Atom::DivLeft)) {
+ leaveValueList();
+ }
+
+ append(leftType, string);
+ indexStartedPara = false;
+ pendingParaLeftType = leftType;
+ pendingParaRightType = rightType;
+ pendingParaString = string;
+ if (leftType == Atom::SectionHeadingLeft) {
+ paraState = InSingleLineParagraph;
+ }
+ else {
+ paraState = InMultiLineParagraph;
+ }
+ skipSpacesOrOneEndl();
+ }
+}
+
+void DocParser::leavePara()
+{
+ if (paraState != OutsideParagraph) {
+ 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(QLatin1Char(' '))) {
+ priv->text.lastAtom()->chopString();
+ }
+ append(pendingParaRightType, pendingParaString);
+ }
+ paraState = OutsideParagraph;
+ indexStartedPara = false;
+ pendingParaRightType = Atom::Nop;
+ pendingParaString.clear();
+ }
+}
+
+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 (((paramNo = def[j].unicode()) >= 1) &&
+ (paramNo <= numParams)) {
+ if (!rawString.isEmpty()) {
+ append(Atom::RawString, rawString);
+ rawString.clear();
+ }
+ append(Atom::String, args[paramNo - 1]);
+ j += 1;
+ }
+ else {
+ rawString += def[j++];
+ }
+ }
+ if (!rawString.isEmpty())
+ append(Atom::RawString, rawString);
+ }
+}
+
+QString DocParser::expandMacroToString(const QString &name, const QString &def, int numParams)
+{
+ if (numParams == 0) {
+ return 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 (((paramNo = def[j].unicode()) >= 1) &&
+ (paramNo <= numParams)) {
+ rawString += args[paramNo - 1];
+ j += 1;
+ }
+ else {
+ rawString += def[j++];
+ }
+ }
+ return rawString;
+ }
+}
+
+Doc::Sections 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::NoSection;
+ }
+ else {
+ location().warning(tr("Invalid section '%1'").arg(name));
+ return Doc::NoSection;
+ }
+}
+
+/*!
+ Gets an argument that is enclosed in braces and returns it
+ without the enclosing braces. On entry, the current character
+ is the left brace. On exit, the current character is the one
+ that comes after the right brace.
+
+ If \a verbatim is true, extra whitespace is retained in the
+ returned string. Otherwise, extra whitespace is removed.
+ */
+QString DocParser::getBracedArgument(bool verbatim)
+{
+ QString arg;
+ int delimDepth = 0;
+ if (pos < (int) in.length() && in[pos] == '{') {
+ pos++;
+ while (pos < (int) in.length() && delimDepth >= 0) {
+ switch (in[pos].unicode()) {
+ case '{':
+ delimDepth++;
+ arg += QLatin1Char('{');
+ pos++;
+ break;
+ case '}':
+ delimDepth--;
+ if (delimDepth >= 0)
+ arg += QLatin1Char('}');
+ 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:
+ if (in[pos].isSpace() && !verbatim)
+ arg += QChar(' ');
+ else
+ arg += in[pos];
+ pos++;
+ }
+ }
+ if (delimDepth > 0)
+ location().warning(tr("Missing '}'"));
+ }
+ return arg;
+}
+
+/*!
+ 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.
+*/
+QString DocParser::getArgument(bool verbatim)
+{
+ skipSpacesOrOneEndl();
+
+ int delimDepth = 0;
+ int startPos = pos;
+ QString arg = getBracedArgument(verbatim);
+ if (arg.isEmpty()) {
+ 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();
+}
+
+/*!
+ Gets an argument that is enclosed in brackets and returns it
+ without the enclosing brackets. On entry, the current character
+ is the left bracket. On exit, the current character is the one
+ that comes after the right bracket.
+ */
+QString DocParser::getBracketedArgument()
+{
+ QString arg;
+ int delimDepth = 0;
+ skipSpacesOrOneEndl();
+ if (pos < in.length() && in[pos] == '[') {
+ pos++;
+ while (pos < in.length() && delimDepth >= 0) {
+ switch (in[pos].unicode()) {
+ case '[':
+ delimDepth++;
+ arg += QLatin1Char('[');
+ pos++;
+ break;
+ case ']':
+ delimDepth--;
+ if (delimDepth >= 0)
+ arg += QLatin1Char(']');
+ pos++;
+ break;
+ case '\\':
+ arg += in[pos];
+ pos++;
+ break;
+ default:
+ arg += in[pos];
+ pos++;
+ }
+ }
+ if (delimDepth > 0)
+ location().warning(tr("Missing ']'"));
+ }
+ return arg;
+}
+
+QString DocParser::getOptionalArgument()
+{
+ skipSpacesOrOneEndl();
+ if (pos + 1 < (int) in.length() && in[pos] == '\\' &&
+ in[pos + 1].isLetterOrNumber()) {
+ return QString();
+ }
+ 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 += QLatin1Char(' ');
+ 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);
+ if (!marker)
+ marker = CodeMarker::markerForCode(code);
+ return marker->markedUpCode(code, 0, location());
+}
+
+/*!
+ Was used only for generating doxygen output.
+ */
+QString DocParser::getUnmarkedCode(int cmd)
+{
+ QString code = getUntilEnd(cmd);
+ 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] == '{';
+}
+
+bool DocParser::isLeftBracketAhead()
+{
+ 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] == '[';
+}
+
+/*!
+ Skips to the next non-space character or EOL.
+ */
+void DocParser::skipSpacesOnLine()
+{
+ while ((pos < in.length()) &&
+ in[pos].isSpace() &&
+ (in[pos].unicode() != '\n'))
+ ++pos;
+}
+
+/*!
+ Skips spaces and on EOL.
+ */
+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) + QLatin1Char('|') +
+ cmdName(CMD_ELSE) + QLatin1Char('|') +
+ 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_BADCODE:
+ return CMD_ENDCODE;
+ case CMD_CHAPTER:
+ return CMD_ENDCHAPTER;
+ case CMD_CODE:
+ return CMD_ENDCODE;
+ case CMD_DIV:
+ return CMD_ENDDIV;
+ case CMD_QML:
+ return CMD_ENDQML;
+ case CMD_QMLTEXT:
+ return CMD_ENDQMLTEXT;
+ case CMD_JS:
+ return CMD_ENDJS;
+ 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;
+ case CMD_TOPICREF:
+ return CMD_ENDTOPICREF;
+ case CMD_MAPREF:
+ return CMD_ENDMAPREF;
+ 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(QLatin1Char('\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(QLatin1Char('/'), "\\/");
+ return QLatin1Char('/') + result + QLatin1Char('/');
+}
+
+/*!
+ Parse the qdoc comment \a source. Build up a list of all the topic
+ commands found including their arguments. This constructor is used
+ when there can be more than one topic command in theqdoc comment.
+ Normally, there is only one topic command in a qdoc comment, but in
+ QML documentation, there is the case where the qdoc \e{qmlproperty}
+ command can appear multiple times in a qdoc comment.
+ */
+Doc::Doc(const Location& start_loc,
+ const Location& end_loc,
+ const QString& source,
+ const QSet<QString>& metaCommandSet,
+ const QSet<QString>& topics)
+{
+ priv = new DocPrivate(start_loc,end_loc,source);
+ DocParser parser;
+ parser.parse(source,priv,metaCommandSet,topics);
+}
+
+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;
+}
+
+/*!
+ Returns the starting location of a qdoc comment.
+ */
+const Location& Doc::startLocation() const
+{
+ return location();
+}
+
+/*!
+ Returns the ending location of a qdoc comment.
+ */
+const Location& Doc::endLocation() const
+{
+ static const Location dummy;
+ return priv == 0 ? dummy : priv->end_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(bool inclusive) const
+{
+ return body().subText(Atom::BriefLeft, Atom::BriefRight, 0, inclusive);
+}
+
+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;
+ /*
+ This code is really ugly. The entire \brief business
+ should be rethought.
+ */
+ while (atom) {
+ if (atom->type() == Atom::AutoLink || atom->type() == Atom::String) {
+ briefStr += atom->string();
+ }
+ atom = atom->next();
+ }
+
+ QStringList w = briefStr.split(QLatin1Char(' '));
+ if (!w.isEmpty() && w.first() == "Returns") {
+ }
+ else {
+ if (!w.isEmpty() && w.first() == "The")
+ w.removeFirst();
+
+ if (!w.isEmpty() && (w.first() == className || w.first() == classNameOnly))
+ w.removeFirst();
+
+ if (!w.isEmpty() && ((w.first() == "class") ||
+ (w.first() == "function") ||
+ (w.first() == "macro") ||
+ (w.first() == "widget") ||
+ (w.first() == "namespace") ||
+ (w.first() == "header")))
+ w.removeFirst();
+
+ 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(QLatin1Char('.')))
+ whats.truncate(whats.length() - 1);
+
+ if (!whats.isEmpty())
+ whats[0] = whats[0].toUpper();
+
+ // ### move this once \brief is abolished for properties
+ resultText << whats;
+ }
+ return resultText;
+}
+
+Text Doc::legaleseText() const
+{
+ if (priv == 0 || !priv->hasLegalese)
+ return Text();
+ else
+ return body().subText(Atom::LegaleseLeft, Atom::LegaleseRight);
+}
+
+Doc::Sections Doc::granularity() const
+{
+ if (priv == 0 || priv->extra == 0) {
+ return DocPrivateExtra().granularity_;
+ }
+ else {
+ return priv->extra->granularity_;
+ }
+}
+
+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;
+}
+
+/*!
+ Returns a reference to the list of topic commands used in the
+ current qdoc comment. Normally there is only one, but there
+ can be multiple \e{qmlproperty} commands, for example.
+ */
+const TopicList& Doc::topicsUsed() const
+{
+ return priv == 0 ? *nullTopicList() : priv->topics_;
+}
+
+ArgList Doc::metaCommandArgs(const QString& metacommand) const
+{
+ return priv == 0 ? ArgList() : 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 QVector<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 QStringMultiMap &Doc::metaTagMap() const
+{
+ return priv && priv->extra ? priv->extra->metaMap_ : *null_QStringMultiMap();
+}
+
+const Config* Doc::config_ = 0;
+
+void Doc::initialize(const Config& config)
+{
+ DocParser::tabSize = config.getInt(CONFIG_TABSIZE);
+ DocParser::exampleFiles = config.getCanonicalPathList(CONFIG_EXAMPLES);
+ DocParser::exampleDirs = config.getCanonicalPathList(CONFIG_EXAMPLEDIRS);
+ DocParser::sourceFiles = config.getCanonicalPathList(CONFIG_SOURCES);
+ DocParser::sourceDirs = config.getCanonicalPathList(CONFIG_SOURCEDIRS);
+ DocParser::quoting = config.getBool(CONFIG_QUOTINGINFORMATION);
+
+ QmlTypeNode::qmlOnly = config.getBool(CONFIG_QMLONLY);
+ QStringMap reverseAliasMap;
+ config_ = &config;
+
+ QSet<QString> commands = config.subVars(CONFIG_ALIAS);
+ QSet<QString>::ConstIterator c = commands.constBegin();
+ while (c != commands.constEnd()) {
+ 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.constBegin();
+ while (n != macroNames.constEnd()) {
+ 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.constBegin();
+ while (f != formats.constEnd()) {
+ QString def = config.getString(macroDotName + Config::dot + *f);
+ if (!def.isEmpty()) {
+ macro.otherDefs.insert(*f, def);
+ int m = Config::numParams(def);
+ 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.constBegin().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;
+ }
+}
+
+/*!
+ All the heap allocated variables are deleted.
+ */
+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()) {
+ QString details = QLatin1String("Example directories: ") + DocParser::exampleDirs.join(QLatin1Char(' '));
+ if (!DocParser::exampleFiles.isEmpty())
+ details += QLatin1String(", example files: ") + DocParser::exampleFiles.join(QLatin1Char(' '));
+ location.warning(tr("Cannot find file to quote from: '%1'").arg(fileName), details);
+ }
+ else {
+ QFile inFile(filePath);
+ if (!inFile.open(QFile::ReadOnly)) {
+ location.warning(tr("Cannot open file to quote from: '%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, location));
+ 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 dashAppended = 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;
+ dashAppended = false;
+ lastAlnum = result.size();
+ }
+ else if (!dashAppended) {
+ if (begun)
+ result += QLatin1Char('-');
+ dashAppended = true;
+ }
+ }
+ 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;
+}
+
+/*!
+ The destructor deletes all the sub-TopicRefs.
+ */
+TopicRef::~TopicRef()
+{
+ foreach (DitaRef* t, subrefs_) {
+ delete t;
+ }
+}
+
+/*!
+ Returns a reference to the structure that will be used
+ for generating a DITA mao.
+ */
+const DitaRefList& Doc::ditamap() const { return priv->ditamap_; }
+
+QT_END_NAMESPACE
diff --git a/src/qdoc/doc.h b/src/qdoc/doc.h
new file mode 100644
index 000000000..b064b5878
--- /dev/null
+++ b/src/qdoc/doc.h
@@ -0,0 +1,196 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL21$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** 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 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** As a special exception, The Qt Company gives you certain additional
+** rights. These rights are described in The Qt Company LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*
+ doc.h
+*/
+
+#ifndef DOC_H
+#define DOC_H
+
+#include <qset.h>
+#include <qstring.h>
+#include <qmap.h>
+
+#include "location.h"
+
+QT_BEGIN_NAMESPACE
+
+class Atom;
+class CodeMarker;
+class Config;
+class DocPrivate;
+class Quoter;
+class Text;
+class DitaRef;
+
+typedef QPair<QString, Location> ArgLocPair;
+typedef QList<ArgLocPair> ArgList;
+typedef QMap<QString, QString> QStringMap;
+typedef QMultiMap<QString, QString> QStringMultiMap;
+
+struct Topic
+{
+ QString topic;
+ QString args;
+ Topic() { }
+ Topic(QString& t, const QString &a) : topic(t), args(a) { }
+ bool isEmpty() const { return topic.isEmpty(); }
+ void clear() { topic.clear(); args.clear(); }
+};
+typedef QList<Topic> TopicList;
+
+typedef QList<DitaRef*> DitaRefList;
+
+class DitaRef
+{
+public:
+ DitaRef() { }
+ virtual ~DitaRef() { }
+
+ const QString& navtitle() const { return navtitle_; }
+ const QString& href() const { return href_; }
+ void setNavtitle(const QString& t) { navtitle_ = t; }
+ void setHref(const QString& t) { href_ = t; }
+ virtual bool isMapRef() const = 0;
+ virtual const DitaRefList* subrefs() const { return 0; }
+ virtual void appendSubref(DitaRef* ) { }
+
+private:
+ QString navtitle_;
+ QString href_;
+};
+
+class TopicRef : public DitaRef
+{
+public:
+ TopicRef() { }
+ ~TopicRef();
+
+ virtual bool isMapRef() const Q_DECL_OVERRIDE { return false; }
+ virtual const DitaRefList* subrefs() const Q_DECL_OVERRIDE { return &subrefs_; }
+ virtual void appendSubref(DitaRef* t) Q_DECL_OVERRIDE { subrefs_.append(t); }
+
+private:
+ DitaRefList subrefs_;
+};
+
+class MapRef : public DitaRef
+{
+public:
+ MapRef() { }
+ ~MapRef() { }
+
+ virtual bool isMapRef() const Q_DECL_OVERRIDE { return true; }
+};
+
+class Doc
+{
+ Q_DECLARE_TR_FUNCTIONS(QDoc::Doc)
+
+public:
+ // the order is important
+ enum Sections {
+ NoSection = -2,
+ Part = -1,
+ Chapter = 1,
+ Section1 = 1,
+ Section2 = 2,
+ Section3 = 3,
+ Section4 = 4
+ };
+
+ Doc() : priv(0) {}
+ Doc(const Location& start_loc,
+ const Location& end_loc,
+ const QString& source,
+ const QSet<QString>& metaCommandSet,
+ const QSet<QString>& topics);
+ 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 DitaRefList& ditamap() const;
+
+ const Location &location() const;
+ const Location& startLocation() const;
+ const Location& endLocation() const;
+ bool isEmpty() const;
+ const QString& source() const;
+ const Text& body() const;
+ Text briefText(bool inclusive = false) const;
+ Text trimmedBriefText(const QString &className) const;
+ Text legaleseText() const;
+ Sections granularity() const;
+ const QSet<QString> &parameterNames() const;
+ const QStringList &enumItemNames() const;
+ const QStringList &omitEnumItemNames() const;
+ const QSet<QString> &metaCommandsUsed() const;
+ const TopicList& topicsUsed() const;
+ ArgList 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 QVector<int> &tableOfContentsLevels() const;
+ const QList<Atom *> &keywords() const;
+ const QList<Atom *> &targets() const;
+ const QStringMultiMap &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);
+ static const Config* config() { return config_; }
+
+private:
+ void detach();
+ DocPrivate *priv;
+ static const Config* config_;
+};
+Q_DECLARE_TYPEINFO(Doc, Q_MOVABLE_TYPE);
+typedef QList<Doc> DocList;
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/qdoc/doc/config/qdoc.qdocconf b/src/qdoc/doc/config/qdoc.qdocconf
new file mode 100644
index 000000000..9d841e9b6
--- /dev/null
+++ b/src/qdoc/doc/config/qdoc.qdocconf
@@ -0,0 +1,72 @@
+include($QT_INSTALL_DOCS/global/qt-module-defaults.qdocconf)
+
+project = QDoc
+description = QDoc Manual
+version = $QT_VERSION
+
+sourcedirs = ..
+
+exampledirs = .. \
+ ../examples
+
+imagedirs = ../images \
+ ../../../../widgets/doc/images
+# ../../../doc/src/templates/images
+
+tagfile = ../html/qdoc.tags
+
+examples.fileextensions = "*.cpp *.h *.js *.xq *.svg *.xml *.ui *.qhp *.qhcp *.qml *.css *.qdoc *.qdocinc *.sample"
+
+qhp.projects = QDoc
+
+qhp.QDoc.file = qdoc.qhp
+qhp.QDoc.namespace = org.qt-project.qdoc.$QT_VERSION_TAG
+qhp.QDoc.virtualFolder = qdoc
+qhp.QDoc.indexTitle = QDoc Manual
+qhp.QDoc.indexRoot =
+
+qhp.QDoc.filterAttributes = qdoc qtrefdoc
+qhp.QDoc.customFilters.QDoc.name = QDoc
+qhp.QDoc.customFilters.QDoc.filterAttributes = qdoc
+qhp.QDoc.subprojects = overviews
+qhp.QDoc.subprojects.overviews.title = Overviews
+qhp.QDoc.subprojects.overviews.indexTitle = QDoc Manual
+qhp.QDoc.subprojects.overviews.selectors = fake:page,group,module
+
+depends += \
+ activeqt \
+ qtassistant \
+ qtbluetooth \
+ qtconcurrent \
+ qtcontacts \
+ qtcore \
+ qtdbus \
+ qtdesigner \
+ qtdoc \
+ qthelp \
+ qtimageformats \
+ qtgui \
+ qtlocation \
+ qtlinguist \
+ qtmultimedia \
+ qtnetwork \
+ qtopengl \
+ qtorganizer \
+ qtprintsupport \
+ qtqml \
+ qtquick \
+ qtscript \
+ qtscripttools \
+ qtsensors \
+ qtsql \
+ qtsvg \
+ qttestlib \
+ qtuitools \
+ qtversit \
+ qtwidgets \
+ qtwebkit \
+ qtwebkitexamples \
+ qtxml \
+ qtxmlpatterns
+
+navigation.landingpage = "QDoc Manual"
diff --git a/src/qdoc/doc/corefeatures.qdoc b/src/qdoc/doc/corefeatures.qdoc
new file mode 100644
index 000000000..bbee7410f
--- /dev/null
+++ b/src/qdoc/doc/corefeatures.qdoc
@@ -0,0 +1,35 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the documentation of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:FDL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Free Documentation License Usage
+** Alternatively, this file may be used under the terms of the GNU Free
+** Documentation License version 1.3 as published by the Free Software
+** Foundation and appearing in the file included in the packaging of
+** this file. Please review the following information to ensure
+** the GNU Free Documentation License version 1.3 requirements
+** will be met: http://www.gnu.org/copyleft/fdl.html.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*!
+ \page corefeatures.html
+ \title Core Features
+
+ \input examples/signalandslots.qdocinc
+ \input examples/objectmodel.qdocinc
+ \input examples/layoutmanagement.qdocinc
+*/
diff --git a/src/qdoc/doc/examples/componentset/ProgressBar.qml b/src/qdoc/doc/examples/componentset/ProgressBar.qml
new file mode 100644
index 000000000..c4e8c103e
--- /dev/null
+++ b/src/qdoc/doc/examples/componentset/ProgressBar.qml
@@ -0,0 +1,135 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the examples of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** You may use this file under the terms of the BSD license as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 1.0
+
+/*!
+ \qmltype ProgressBar
+ \inqmlmodule UIComponents
+ \brief A component that shows the progress of an event
+
+ A ProgressBar shows the linear progress of an event as its \l value.
+ The range is specified using the \l {minimum} and the \l{maximum} values.
+
+ The ProgressBar component is part of the \l {UI Components} module.
+
+ This documentation is part of the \l{componentset}{UIComponents} example.
+*/
+Item {
+ id: progressbar
+
+ /*!
+ The minimum value of the ProgressBar range.
+ The \l value must not be less than this value.
+ */
+ property int minimum: 0
+
+ /*!
+ The maximum value of the ProgressBar range.
+ The \l value must not be more than this value.
+ */
+ property int maximum: 100
+
+ /*!
+ The value of the progress.
+ */
+ property int value: 0
+
+ /*!
+ \qmlproperty color ProgressBar::color
+ The color of the ProgressBar's gradient. Must bind to a color type.
+
+ \omit
+ The "\qmlproperty <type> <property name>" is needed because
+ property alias need to have their types manually entered.
+
+ QDoc will not publish the documentation within omit and endomit.
+ \endomit
+
+ \sa secondColor
+ */
+ property alias color: gradient1.color
+
+ /*!
+ \qmlproperty color ProgressBar::secondColor
+ The second color of the ProgressBar's gradient.
+ Must bind to a color type.
+
+ \omit
+ The "\qmlproperty <type> <property name>" is needed because
+ property alias need to have their types manually entered.
+
+ QDoc will not publish the documentation within omit and endomit.
+ \endomit
+
+ \sa color
+ */
+ property alias secondColor: gradient2.color
+
+ width: 250; height: 23
+ clip: true
+
+ Rectangle {
+ id: highlight
+
+ /*!
+ An internal documentation comment. The widthDest property is not
+ a public API and therefore will not be exposed.
+ */
+ property int widthDest: ((progressbar.width * (value - minimum)) / (maximum - minimum) - 6)
+
+ width: highlight.widthDest
+ Behavior on width { SmoothedAnimation { velocity: 1200 } }
+
+ anchors { left: parent.left; top: parent.top; bottom: parent.bottom; margins: 3 }
+ radius: 1
+ gradient: Gradient {
+ GradientStop { id: gradient1; position: 0.0 }
+ GradientStop { id: gradient2; position: 1.0 }
+ }
+
+ }
+ Text {
+ anchors { right: highlight.right; rightMargin: 6; verticalCenter: parent.verticalCenter }
+ color: "white"
+ font.bold: true
+ text: Math.floor((value - minimum) / (maximum - minimum) * 100) + '%'
+ }
+}
diff --git a/src/qdoc/doc/examples/componentset/Switch.qml b/src/qdoc/doc/examples/componentset/Switch.qml
new file mode 100644
index 000000000..7b7e7af31
--- /dev/null
+++ b/src/qdoc/doc/examples/componentset/Switch.qml
@@ -0,0 +1,142 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the examples of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** You may use this file under the terms of the BSD license as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 1.0
+
+/*!
+ \qmltype ToggleSwitch
+ \inqmlmodule UIComponents
+ \brief A component that can be turned on or off
+
+ A toggle switch has two states: an \c on and an \c off state. The \c off
+ state is when the \l on property is set to \c false.
+
+ The ToggleSwitch component is part of the \l {UI Components} module.
+
+ This documentation is part of the \l{componentset}{UIComponents} example.
+
+*/
+Item {
+ id: toggleswitch
+ width: background.width; height: background.height
+
+ /*!
+ Indicates the state of the switch. If \c false, then the switch is in
+ the \c off state.
+
+ \omit
+ The \qmlproperty <type> <propertyname> is not necessary as QDoc
+ will associate this property to the ToggleSwitch
+
+ QDoc will not publish the documentation within omit and endomit.
+ \endomit
+ */
+ property bool on: false
+
+
+ /*!
+ A method to toggle the switch. If the switch is \c on, the toggling it
+ will turn it \c off. Toggling a switch in the \c off position will
+ turn it \c on.
+ */
+ function toggle() {
+ if (toggleswitch.state == "on")
+ toggleswitch.state = "off";
+ else
+ toggleswitch.state = "on";
+ }
+
+
+ /*!
+ \internal
+
+ An internal function to synchronize the switch's internals. This
+ function is not for public access. The \internal command will
+ prevent QDoc from publishing this comment in the public API.
+ */
+ function releaseSwitch() {
+ if (knob.x == 1) {
+ if (toggleswitch.state == "off") return;
+ }
+ if (knob.x == 78) {
+ if (toggleswitch.state == "on") return;
+ }
+ toggle();
+ }
+
+ Rectangle {
+ id: background
+ width: 130; height: 48
+ radius: 48
+ color: "lightsteelblue"
+ MouseArea { anchors.fill: parent; onClicked: toggle() }
+ }
+
+ Rectangle {
+ id: knob
+ width: 48; height: 48
+ radius: width
+ color: "lightblue"
+
+ MouseArea {
+ anchors.fill: parent
+ drag.target: knob; drag.axis: Drag.XAxis; drag.minimumX: 1; drag.maximumX: 78
+ onClicked: toggle()
+ onReleased: releaseSwitch()
+ }
+ }
+
+ states: [
+ State {
+ name: "on"
+ PropertyChanges { target: knob; x: 78 }
+ PropertyChanges { target: toggleswitch; on: true }
+ },
+ State {
+ name: "off"
+ PropertyChanges { target: knob; x: 1 }
+ PropertyChanges { target: toggleswitch; on: false }
+ }
+ ]
+
+ transitions: Transition {
+ NumberAnimation { properties: "x"; easing.type: Easing.InOutQuad; duration: 200 }
+ }
+}
diff --git a/src/qdoc/doc/examples/componentset/TabWidget.qml b/src/qdoc/doc/examples/componentset/TabWidget.qml
new file mode 100644
index 000000000..008c5e14e
--- /dev/null
+++ b/src/qdoc/doc/examples/componentset/TabWidget.qml
@@ -0,0 +1,183 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the examples of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** You may use this file under the terms of the BSD license as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 1.0
+
+/*!
+ \qmltype TabWidget
+ \inqmlmodule UIComponents
+ \brief A widget that places its children as tabs
+
+ A TabWidget places its children as tabs in a view. Selecting
+ a tab involves selecting the tab at the top.
+
+ The TabWidget component is part of the \l {UI Components} module.
+
+ This documentation is part of the \l{componentset}{UIComponents} example.
+
+ \section1 Adding Tabs
+
+ To add a tab, declare the tab as a child of the TabWidget.
+
+ \code
+ TabWidget {
+ id: tabwidget
+
+ Rectangle {
+ id: tab1
+ color: "red"
+ //... omitted
+ }
+ Rectangle {
+ id: tab2
+ color: "blue"
+ //... omitted
+ }
+
+ }
+ \endcode
+
+*/
+Item {
+ id: tabWidget
+
+ /*!
+ \internal
+
+ Setting the default property to stack.children means any child items
+ of the TabWidget are actually added to the 'stack' item's children.
+
+ See the \l{"Property Binding in QML"}
+ documentation for details on default properties.
+
+ This is an implementation detail, not meant for public knowledge. Putting
+ the \internal command at the beginning will cause QDoc to not publish this
+ documentation in the public API page.
+
+ Normally, a property alias needs to have a
+ "\qmlproperty <type> <propertyname>" to assign the alias a type.
+
+ */
+ default property alias content: stack.children
+
+
+ /*!
+ The currently active tab in the TabWidget.
+ */
+ property int current: 0
+
+ /*!
+ A sample \c{read-only} property.
+ A contrived property to demonstrate QDoc's ability to detect
+ read-only properties.
+
+ The signature is:
+ \code
+ readonly property int sampleReadOnlyProperty: 0
+ \endcode
+
+ Note that the property must be initialized to a value.
+
+ */
+ readonly property int sampleReadOnlyProperty: 0
+
+ /*!
+ \internal
+
+ This handler is an implementation
+ detail. The \c{\internal} command will prevent QDoc from publishing this
+ documentation on the public API.
+ */
+ onCurrentChanged: setOpacities()
+ Component.onCompleted: setOpacities()
+
+ /*!
+ \internal
+
+ An internal function to set the opacity.
+ The \internal command will prevent QDoc from publishing this
+ documentation on the public API.
+ */
+ function setOpacities() {
+ for (var i = 0; i < stack.children.length; ++i) {
+ stack.children[i].opacity = (i == current ? 1 : 0)
+ }
+ }
+
+ Row {
+ id: header
+
+ Repeater {
+ model: stack.children.length
+ delegate: Rectangle {
+ width: tabWidget.width / stack.children.length; height: 36
+
+ Rectangle {
+ width: parent.width; height: 1
+ anchors { bottom: parent.bottom; bottomMargin: 1 }
+ color: "#acb2c2"
+ }
+ BorderImage {
+ anchors { fill: parent; leftMargin: 2; topMargin: 5; rightMargin: 1 }
+ border { left: 7; right: 7 }
+ source: "tab.png"
+ visible: tabWidget.current == index
+ }
+ Text {
+ horizontalAlignment: Qt.AlignHCenter; verticalAlignment: Qt.AlignVCenter
+ anchors.fill: parent
+ text: stack.children[index].title
+ elide: Text.ElideRight
+ font.bold: tabWidget.current == index
+ }
+ MouseArea {
+ anchors.fill: parent
+ onClicked: tabWidget.current = index
+ }
+ }
+ }
+ }
+
+ Item {
+ id: stack
+ width: tabWidget.width
+ anchors.top: header.bottom; anchors.bottom: tabWidget.bottom
+ }
+}
diff --git a/src/qdoc/doc/examples/componentset/componentset.pro b/src/qdoc/doc/examples/componentset/componentset.pro
new file mode 100644
index 000000000..5b44737c2
--- /dev/null
+++ b/src/qdoc/doc/examples/componentset/componentset.pro
@@ -0,0 +1,5 @@
+SOURCES = componentset.pro \
+ ProgressBar.qml \
+ Switch.qml \
+ TabWidget.qml \
+ uicomponents.qdoc
diff --git a/src/qdoc/doc/examples/componentset/uicomponents.qdoc.sample b/src/qdoc/doc/examples/componentset/uicomponents.qdoc.sample
new file mode 100644
index 000000000..7a14f88f4
--- /dev/null
+++ b/src/qdoc/doc/examples/componentset/uicomponents.qdoc.sample
@@ -0,0 +1,38 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the documentation of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:FDL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Free Documentation License Usage
+** Alternatively, this file may be used under the terms of the GNU Free
+** Documentation License version 1.3 as published by the Free Software
+** Foundation and appearing in the file included in the packaging of
+** this file. Please review the following information to ensure
+** the GNU Free Documentation License version 1.3 requirements
+** will be met: http://www.gnu.org/copyleft/fdl.html.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*!
+ \qmlmodule UIComponents 1.0
+ \title UI Components
+ \brief Basic set of UI components
+
+ This is a listing of a list of UI components implemented by QML types. These
+ files are available for general import and they are based off the \l{Qt
+ Quick Code Samples}.
+
+ This module is part of the \l{componentset}{UIComponents} example.
+*/
diff --git a/src/qdoc/doc/examples/cpp.qdoc.sample b/src/qdoc/doc/examples/cpp.qdoc.sample
new file mode 100644
index 000000000..38a131783
--- /dev/null
+++ b/src/qdoc/doc/examples/cpp.qdoc.sample
@@ -0,0 +1,126 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the documentation of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:FDL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Free Documentation License Usage
+** Alternatively, this file may be used under the terms of the GNU Free
+** Documentation License version 1.3 as published by the Free Software
+** Foundation and appearing in the file included in the packaging of
+** this file. Please review the following information to ensure
+** the GNU Free Documentation License version 1.3 requirements
+** will be met: http://www.gnu.org/copyleft/fdl.html.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//![class]
+/*!
+ \class QCache
+ \brief The QCache class is a template class that provides a cache.
+
+ \ingroup tools
+ \ingroup shared
+
+ \reentrant
+
+ QCache\<Key, T\> defines a cache that stores objects of type T
+ associated with keys of type Key. For example, here's the
+ definition of a cache that stores objects of type Employee
+ associated with an integer key:
+
+ \snippet code/doc_src_qcache.cpp 0
+
+ Here's how to insert an object in the cache:
+
+ \snippet code/doc_src_qcache.cpp 1
+
+ ... detailed description ommitted
+
+ \sa QPixmapCache, QHash, QMap
+*/
+//![class]
+
+//![function]
+/*!
+ \fn QString &QString::remove(int position, int n)
+
+ Removes \a n characters from the string, starting at the given \a
+ position index, and returns a reference to the string.
+
+ If the specified \a position index is within the string, but \a
+ position + \a n is beyond the end of the string, the string is
+ truncated at the specified \a position.
+
+ \snippet qstring/main.cpp 37
+
+ \sa insert(), replace()
+*/
+QString &QString::remove(int pos, int len)
+//! [function]
+
+//! [return]
+/*!
+ Returns \c true if a QScroller object was already created for \a target; \c false otherwise.
+
+ \sa scroller()
+*/
+bool QScroller::hasScroller(QObject *target)
+//! [return]
+
+//! [property]
+/*!
+ \property QVariantAnimation::duration
+ \brief the duration of the animation
+
+ This property describes the duration in milliseconds of the
+ animation. The default duration is 250 milliseconds.
+
+ \sa QAbstractAnimation::duration()
+ */
+int QVariantAnimation::duration() const
+//! [property]
+
+//! [signals]
+/*!
+ \fn QAbstractTransition::triggered()
+
+ This signal is emitted when the transition has been triggered (after
+ onTransition() has been called).
+*/
+//! [signals]
+
+//! [enums]
+/*!
+ \enum QSql::TableType
+
+ This enum type describes types of SQL tables.
+
+ \value Tables All the tables visible to the user.
+ \value SystemTables Internal tables used by the database.
+ \value Views All the views visible to the user.
+ \value AllTables All of the above.
+*/
+//! [enums]
+
+//! [overloaded notifier]
+/*!
+\property QSpinBox::value
+\brief the value of the spin box
+
+setValue() will emit valueChanged() if the new value is different
+from the old one. The \l{QSpinBox::}{value} property has a second notifier
+signal which includes the spin box's prefix and suffix.
+*/
+//! [overloaded notifier]
diff --git a/src/qdoc/doc/examples/examples.qdoc b/src/qdoc/doc/examples/examples.qdoc
new file mode 100644
index 000000000..28810e30d
--- /dev/null
+++ b/src/qdoc/doc/examples/examples.qdoc
@@ -0,0 +1,97 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the documentation of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:FDL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Free Documentation License Usage
+** Alternatively, this file may be used under the terms of the GNU Free
+** Documentation License version 1.3 as published by the Free Software
+** Foundation and appearing in the file included in the packaging of
+** this file. Please review the following information to ensure
+** the GNU Free Documentation License version 1.3 requirements
+** will be met: http://www.gnu.org/copyleft/fdl.html.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*!
+ \example componentset
+ \title QML Documentation Example
+
+ This example demonstrates one of the ways to document QML types.
+
+ In particular, there are sample types that are documented with QDoc
+ commands comments. There are documentation comments for the QML types
+ and their public interfaces. The types are grouped into a module, the
+ \l{UI Components} module.
+
+ The \l{componentset/uicomponents.qdoc.sample}{uicomponents.qdoc} file generates
+ the overview page for the \l{UI Components} module page.
+
+ The generated documentation is available in the \l{UI Components} module.
+
+ \section1 QML Class
+
+ The QML types use the \l{qmltype-command}{\\qmltype} to document the
+ type. In addition, they have the \l{inmodule-command}{\\inmodule}
+ command in order for QDoc to associate them to the \c UIComponents module.
+
+ QDoc uses the \l{brief-command}{\\brief} command to place a basic
+ description when listing the types.
+
+ \section1 Properties, Signals, Handlers, and Methods
+
+ The types have their properties, signals, handlers, and methods
+ defined in their respective QML files. QDoc associates the properties and
+ methods to the types, therefore, you only need to place the
+ documentation above the property, method, or signal.
+
+ To document the type of a \e {property alias}, you must use the
+ \l{qmlproperty-command}{\\qmlproperty} command to specify the data type.
+
+ \code
+ \qmlproperty int anAliasedProperty
+ An aliased property of type int.
+ \endcode
+
+ \section2 Internal Documentation
+
+ You may declare that a documentation is for internal use by placing the
+ \l{internal-command}{\\internal} command after the beginning QDoc comment
+ \begincomment. QDoc will prevent the internal documentation from appearing
+ in the public API.
+
+ If you wish to omit certain parts of the documentation, you may use the
+ \l{omit-command}{\\omit} and \l{omit-command}{\\endomit} command.
+
+ \section1 QML Types with C++ Implementation
+
+ This example only demonstrates the documentation for types in QML
+ files, but the regular \l{qml-documentation}{QML commands} may be placed
+ inside C++ classes to define the public API of the QML type.
+
+*/
+
+
+/*!
+ \qmlmodule UIComponents 1.0
+ \title UI Components
+ \brief Basic set of UI components
+
+ This is a listing of a list of UI components implemented by QML types. These
+ files are available for general import and they are based on the
+ \l{Qt Quick Examples and Tutorials}{Qt Quick Code Samples}.
+
+ This module is part of the \l{componentset}{UIComponents} example.
+*/
diff --git a/src/qdoc/doc/examples/layoutmanagement.qdocinc b/src/qdoc/doc/examples/layoutmanagement.qdocinc
new file mode 100644
index 000000000..780b03c8f
--- /dev/null
+++ b/src/qdoc/doc/examples/layoutmanagement.qdocinc
@@ -0,0 +1,13 @@
+\section1 Layout Classes
+
+The Qt layout system provides a simple and powerful way of specifying
+the layout of child widgets.
+
+By specifying the logical layout once, you get the following benefits:
+
+\list
+ \li Positioning of child widgets.
+ \li Sensible default sizes for windows.
+ \li Sensible minimum sizes for windows.
+ \li ...
+\endlist
diff --git a/src/qdoc/doc/examples/main.cpp b/src/qdoc/doc/examples/main.cpp
new file mode 100644
index 000000000..849405e0a
--- /dev/null
+++ b/src/qdoc/doc/examples/main.cpp
@@ -0,0 +1,46 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL21$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** 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 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** As a special exception, The Qt Company gives you certain additional
+** rights. These rights are described in The Qt Company LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QApplication>
+#include <QPushButton>
+
+int main(int argc, char *argv[])
+{
+ QApplication app(argc, argv);
+
+ QPushButton hello("Hello world!");
+ hello.resize(100, 30);
+
+ hello.show();
+ return app.exec();
+}
diff --git a/src/qdoc/doc/examples/mainwindow.cpp b/src/qdoc/doc/examples/mainwindow.cpp
new file mode 100644
index 000000000..68b878c07
--- /dev/null
+++ b/src/qdoc/doc/examples/mainwindow.cpp
@@ -0,0 +1,243 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL21$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** 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 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** As a special exception, The Qt Company gives you certain additional
+** rights. These rights are described in The Qt Company LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtWidgets>
+
+#include "mainwindow.h"
+#include "scribblearea.h"
+
+//! [0]
+MainWindow::MainWindow()
+{
+ scribbleArea = new ScribbleArea;
+ setCentralWidget(scribbleArea);
+
+ createActions();
+ createMenus();
+
+ setWindowTitle(tr("Scribble"));
+ resize(500, 500);
+}
+//! [0]
+
+//! [1]
+void MainWindow::closeEvent(QCloseEvent *event)
+//! [1] //! [2]
+{
+ if (maybeSave()) {
+ event->accept();
+ } else {
+ event->ignore();
+ }
+}
+//! [2]
+
+//! [3]
+void MainWindow::open()
+//! [3] //! [4]
+{
+ if (maybeSave()) {
+ QString fileName = QFileDialog::getOpenFileName(this,
+ tr("Open File"), QDir::currentPath());
+ if (!fileName.isEmpty())
+ scribbleArea->openImage(fileName);
+ }
+}
+//! [4]
+
+//! [5]
+void MainWindow::save()
+//! [5] //! [6]
+{
+ QAction *action = qobject_cast<QAction *>(sender());
+ QByteArray fileFormat = action->data().toByteArray();
+ saveFile(fileFormat);
+}
+//! [6]
+
+//! [7]
+void MainWindow::penColor()
+//! [7] //! [8]
+{
+ QColor newColor = QColorDialog::getColor(scribbleArea->penColor());
+ if (newColor.isValid())
+ scribbleArea->setPenColor(newColor);
+}
+//! [8]
+
+//! [9]
+void MainWindow::penWidth()
+//! [9] //! [10]
+{
+ bool ok;
+ int newWidth = QInputDialog::getInteger(this, tr("Scribble"),
+ tr("Select pen width:"),
+ scribbleArea->penWidth(),
+ 1, 50, 1, &ok);
+ if (ok)
+ scribbleArea->setPenWidth(newWidth);
+}
+//! [10]
+
+//! [11]
+void MainWindow::about()
+//! [11] //! [12]
+{
+ QMessageBox::about(this, tr("About Scribble"),
+ tr("<p>The <b>Scribble</b> example shows how to use QMainWindow as the "
+ "base widget for an application, and how to reimplement some of "
+ "QWidget's event handlers to receive the events generated for "
+ "the application's widgets:</p><p> We reimplement the mouse event "
+ "handlers to facilitate drawing, the paint event handler to "
+ "update the application and the resize event handler to optimize "
+ "the application's appearance. In addition we reimplement the "
+ "close event handler to intercept the close events before "
+ "terminating the application.</p><p> The example also demonstrates "
+ "how to use QPainter to draw an image in real time, as well as "
+ "to repaint widgets.</p>"));
+}
+//! [12]
+
+//! [13]
+void MainWindow::createActions()
+//! [13] //! [14]
+{
+ openAct = new QAction(tr("&Open..."), this);
+ openAct->setShortcuts(QKeySequence::Open);
+ connect(openAct, SIGNAL(triggered()), this, SLOT(open()));
+
+ foreach (const QByteArray &format, QImageWriter::supportedImageFormats()) {
+ QString text = tr("%1...").arg(QString(format).toUpper());
+
+ QAction *action = new QAction(text, this);
+ action->setData(format);
+ connect(action, SIGNAL(triggered()), this, SLOT(save()));
+ saveAsActs.append(action);
+ }
+
+ printAct = new QAction(tr("&Print..."), this);
+ connect(printAct, SIGNAL(triggered()), scribbleArea, SLOT(print()));
+
+ exitAct = new QAction(tr("E&xit"), this);
+ exitAct->setShortcuts(QKeySequence::Quit);
+ connect(exitAct, SIGNAL(triggered()), this, SLOT(close()));
+
+ penColorAct = new QAction(tr("&Pen Color..."), this);
+ connect(penColorAct, SIGNAL(triggered()), this, SLOT(penColor()));
+
+ penWidthAct = new QAction(tr("Pen &Width..."), this);
+ connect(penWidthAct, SIGNAL(triggered()), this, SLOT(penWidth()));
+
+ clearScreenAct = new QAction(tr("&Clear Screen"), this);
+ clearScreenAct->setShortcut(tr("Ctrl+L"));
+ connect(clearScreenAct, SIGNAL(triggered()),
+ scribbleArea, SLOT(clearImage()));
+
+ aboutAct = new QAction(tr("&About"), this);
+ connect(aboutAct, SIGNAL(triggered()), this, SLOT(about()));
+
+ aboutQtAct = new QAction(tr("About &Qt"), this);
+ connect(aboutQtAct, SIGNAL(triggered()), qApp, SLOT(aboutQt()));
+}
+//! [14]
+
+//! [15]
+void MainWindow::createMenus()
+//! [15] //! [16]
+{
+ saveAsMenu = new QMenu(tr("&Save As"), this);
+ foreach (QAction *action, saveAsActs)
+ saveAsMenu->addAction(action);
+
+ fileMenu = new QMenu(tr("&File"), this);
+ fileMenu->addAction(openAct);
+ fileMenu->addMenu(saveAsMenu);
+ fileMenu->addAction(printAct);
+ fileMenu->addSeparator();
+ fileMenu->addAction(exitAct);
+
+ optionMenu = new QMenu(tr("&Options"), this);
+ optionMenu->addAction(penColorAct);
+ optionMenu->addAction(penWidthAct);
+ optionMenu->addSeparator();
+ optionMenu->addAction(clearScreenAct);
+
+ helpMenu = new QMenu(tr("&Help"), this);
+ helpMenu->addAction(aboutAct);
+ helpMenu->addAction(aboutQtAct);
+
+ menuBar()->addMenu(fileMenu);
+ menuBar()->addMenu(optionMenu);
+ menuBar()->addMenu(helpMenu);
+}
+//! [16]
+
+//! [17]
+bool MainWindow::maybeSave()
+//! [17] //! [18]
+{
+ if (scribbleArea->isModified()) {
+ QMessageBox::StandardButton ret;
+ ret = QMessageBox::warning(this, tr("Scribble"),
+ tr("The image has been modified.\n"
+ "Do you want to save your changes?"),
+ QMessageBox::Save | QMessageBox::Discard
+ | QMessageBox::Cancel);
+ if (ret == QMessageBox::Save) {
+ return saveFile("png");
+ } else if (ret == QMessageBox::Cancel) {
+ return false;
+ }
+ }
+ return true;
+}
+//! [18]
+
+//! [19]
+bool MainWindow::saveFile(const QByteArray &fileFormat)
+//! [19] //! [20]
+{
+ QString initialPath = QDir::currentPath() + "/untitled." + fileFormat;
+
+ QString fileName = QFileDialog::getSaveFileName(this, tr("Save As"),
+ initialPath,
+ tr("%1 Files (*.%2);;All Files (*)")
+ .arg(QString(fileFormat.toUpper()))
+ .arg(QString(fileFormat)));
+ if (fileName.isEmpty()) {
+ return false;
+ } else {
+ return scribbleArea->saveImage(fileName, fileFormat);
+ }
+}
+//! [20]
diff --git a/src/qdoc/doc/examples/minimum.qdocconf b/src/qdoc/doc/examples/minimum.qdocconf
new file mode 100644
index 000000000..e360685f1
--- /dev/null
+++ b/src/qdoc/doc/examples/minimum.qdocconf
@@ -0,0 +1,38 @@
+# QDoc is a tool that constantly evolves to suit our needs,
+# and there are some compatibility issues between old and new
+# practices. For that reason, any QDoc configuration file needs to
+# include compat.qdocconf.
+
+#include(compat.qdocconf)
+
+
+# The outputdir variable specifies the directory
+# where QDoc will put the generated documentation.
+
+outputdir = html
+
+
+# The headerdirs variable specifies the directories
+# containing the header files associated
+# with the .cpp source files used in the documentation.
+
+headerdirs = .
+
+
+# The sourcedirs variable specifies the
+# directories containing the .cpp or .qdoc
+# files used in the documentation.
+
+#sourcedirs = .
+
+
+# The exampledirs variable specifies the directories containing
+# the source code of the example files.
+
+exampledirs = .
+
+
+# The imagedirs variable specifies the
+# directories containing the images used in the documentation.
+
+imagedirs = ./images
diff --git a/src/qdoc/doc/examples/objectmodel.qdocinc b/src/qdoc/doc/examples/objectmodel.qdocinc
new file mode 100644
index 000000000..02b5991c4
--- /dev/null
+++ b/src/qdoc/doc/examples/objectmodel.qdocinc
@@ -0,0 +1,11 @@
+\section1 Qt Object Model
+
+The standard C++ object model provides very efficient runtime support
+for the object paradigm. But its static nature is inflexibile in
+certain problem domains. Graphical user interface programming is a
+domain that requires both runtime efficiency and a high level of
+flexibility. Qt provides this, by combining the speed of C++ with the
+flexibility of the Qt Object Model.
+
+...
+
diff --git a/src/qdoc/doc/examples/qml.qdoc.sample b/src/qdoc/doc/examples/qml.qdoc.sample
new file mode 100644
index 000000000..cacd91224
--- /dev/null
+++ b/src/qdoc/doc/examples/qml.qdoc.sample
@@ -0,0 +1,116 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the documentation of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:FDL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Free Documentation License Usage
+** Alternatively, this file may be used under the terms of the GNU Free
+** Documentation License version 1.3 as published by the Free Software
+** Foundation and appearing in the file included in the packaging of
+** this file. Please review the following information to ensure
+** the GNU Free Documentation License version 1.3 requirements
+** will be met: http://www.gnu.org/copyleft/fdl.html.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//![qmltype]
+ \qmltype TextEdit
+ \instantiates QQuickTextEdit
+ \inqmlmodule QtQuick
+ \ingroup qtquick-visual
+ \ingroup qtquick-input
+ \inherits Item
+ \brief Displays multiple lines of editable formatted text
+
+ The TextEdit item displays a block of editable, formatted text.
+
+ It can display both plain and rich text. For example:
+
+ \qml
+ TextEdit {
+ width: 240
+ text: "<b>Hello</b> <i>World!</i>"
+ font.family: "Helvetica"
+ font.pointSize: 20
+ color: "blue"
+ focus: true
+ }
+ \endqml
+
+ \image declarative-textedit.gif
+
+ ... omitted detailed description
+
+ \sa Text, TextInput, {examples/quick/text/textselection}{Text Selection example}
+//![qmltype]
+
+//![function]
+/*
+ \qmlmethod QtQuick2::ListModel::remove(int index, int count = 1)
+
+ Deletes the content at \a index from the model.
+
+ \sa clear()
+*/
+void QQuickListModel::remove(QQmlV8Function *args)
+//! [function]
+
+//! [return]
+/*
+ Returns \c true if a QScroller object was already created for \a target; \c false otherwise.
+
+ \sa scroller()
+*/
+bool QScroller::hasScroller(QObject *target)
+//! [return]
+
+//! [property]
+/*
+ \property QVariantAnimation::duration
+ \brief the duration of the animation
+
+ This property describes the duration in milliseconds of the
+ animation. The default duration is 250 milliseconds.
+
+ \sa QAbstractAnimation::duration()
+ */
+int QVariantAnimation::duration() const
+//! [property]
+
+//! [signals]
+/*
+ This signal is emitted when the user clicks the button. A click is defined
+ as a press followed by a release. The corresponding handler is
+ \c onClicked.
+*/
+signal clicked()
+//! [signals]
+
+//! [enums]
+/*!
+\qmlproperty enumeration QtQuick2::Text::font.weight
+
+Sets the font's weight.
+
+The weight can be one of:
+\list
+\li Font.Light
+\li Font.Normal - the default
+\li Font.DemiBold
+\li Font.Bold
+\li Font.Black
+\endlist
+*/
+//! [enums]
diff --git a/src/qdoc/doc/examples/samples.qdocinc b/src/qdoc/doc/examples/samples.qdocinc
new file mode 100644
index 000000000..d5679fdcd
--- /dev/null
+++ b/src/qdoc/doc/examples/samples.qdocinc
@@ -0,0 +1,109 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the documentation of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:FDL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Free Documentation License Usage
+** Alternatively, this file may be used under the terms of the GNU Free
+** Documentation License version 1.3 as published by the Free Software
+** Foundation and appearing in the file included in the packaging of
+** this file. Please review the following information to ensure
+** the GNU Free Documentation License version 1.3 requirements
+** will be met: http://www.gnu.org/copyleft/fdl.html.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//! [qvector3d-class]
+/*!
+ \class QVector3D
+ \brief The QVector3D class represents a vector or vertex in 3D space.
+ \since 4.6
+ \ingroup painting-3D
+
+ Vectors are one of the main building blocks of 3D representation and
+ drawing. They consist of three coordinates, traditionally called
+ x, y, and z.
+
+ The QVector3D class can also be used to represent vertices in 3D space.
+ We therefore do not need to provide a separate vertex class.
+
+ \b{Note:} By design values in the QVector3D instance are stored as \c float.
+ This means that on platforms where the \c qreal arguments to QVector3D
+ functions are represented by \c double values, it is possible to
+ lose precision.
+
+ \sa QVector2D, QVector4D, QQuaternion
+*/
+//! [qvector3d-class]
+
+//! [qvector3d-function]
+/*!
+ \fn QVector3D::QVector3D(const QPoint& point)
+
+ Constructs a vector with x and y coordinates from a 2D \a point, and a
+ z coordinate of 0.
+*/
+//! [qvector3d-function]
+
+//! [sample-page]
+/*!
+ \page generic-guide.html
+ \title Generic QDoc Guide
+ \nextpage Creating QDoc Configuration Files
+ There are three essential materials for generating documentation with qdoc:
+
+ \list
+ \li \c qdoc binary
+ \li \c qdocconf configuration files
+ \li \c Documentation in \c C++, \c QML, and \c .qdoc files
+ \endlist
+*/
+//! [sample-page]
+
+//! [sample-faq]
+/*!
+ \page altruism-faq.html faq
+ \title Altruism Frequently Asked Questions
+
+ \brief All the questions about altruism, answered.
+
+ ...
+*/
+//! [sample-faq]
+
+//! [sample-example]
+/*!
+ \title UI Components: Tab Widget Example
+ \example declarative/ui-components/tabwidget
+
+ This example shows how to create a tab widget. It also demonstrates how
+ \l {Property aliases}{property aliases} and
+ \l {Introduction to the QML Language#Default Properties}{default properties} can be used to collect and
+ assemble the child items declared within an \l Item.
+
+ \image qml-tabwidget-example.png
+*/
+//! [sample-example]
+
+//! [sample-overview]
+/*!
+ \page overview-qt-technology.html overview
+ \title Overview of a Qt Technology
+
+ \brief provides a technology never seen before.
+
+*/
+//! [sample-overview]
+
diff --git a/src/qdoc/doc/examples/signalandslots.qdocinc b/src/qdoc/doc/examples/signalandslots.qdocinc
new file mode 100644
index 000000000..e14ede144
--- /dev/null
+++ b/src/qdoc/doc/examples/signalandslots.qdocinc
@@ -0,0 +1,9 @@
+\section1 Signals and Slots
+
+Signals and slots are used for communication between objects. The signals and
+slots mechanism is a central feature of Qt and probably the part that differs
+most from the features provided by other frameworks.
+
+\section2 Introduction
+
+In GUI programming, when we ...
diff --git a/src/qdoc/doc/files/basicqt.qdoc.sample b/src/qdoc/doc/files/basicqt.qdoc.sample
new file mode 100644
index 000000000..ce8df096f
--- /dev/null
+++ b/src/qdoc/doc/files/basicqt.qdoc.sample
@@ -0,0 +1,67 @@
+ /*!
+ \page basicqt.html
+ \contentspage {Basic Qt} {Contents}
+ \nextpage Getting Started
+
+ \indexpage Index
+ \startpage Basic Qt
+
+ \title Basic Qt
+
+ The Qt toolkit is a C++ class library and a set of tools for
+ building multiplatform GUI programs using a "write once,
+ compile anywhere approach".
+
+ Table of contents:
+
+ \list
+ \li \l {Getting Started}
+ \li \l {Creating Dialogs}
+ \li \l {Creating Main Windows}
+ \endlist
+ */
+
+ /*!
+ \page gettingstarted.html
+ \previouspage Basic Qt
+ \contentspage {Basic Qt} {Contents}
+ \nextpage Creating Dialogs
+
+ \indexpage Index
+ \startpage Basic Qt
+
+ \title Getting Started
+
+ This chapter shows how to combine basic C++ with the
+ functionality provided by Qt to create a few small graphical
+ interface (GUI) applications.
+*/
+
+/ *!
+ \page creatingdialogs.html
+ \previouspage Getting Started
+ \contentspage {Basic Qt} {Contents}
+
+ \indexpage Index
+ \startpage Basic Qt
+
+ \title Creating Dialogs
+
+ This chapter will teach you how to create dialog boxes using Qt.
+*/
+
+/*!
+ \page index.html
+
+ \indexpage Index
+ \startpage Basic Qt
+
+ \title Index
+
+ \list
+ \li \l {Basic Qt}
+ \li \l {Creating Dialogs}
+ \li \l {Getting Started}
+ \endlist
+*/
+
diff --git a/src/qdoc/doc/files/compat.qdocconf b/src/qdoc/doc/files/compat.qdocconf
new file mode 100644
index 000000000..3e7ea6c89
--- /dev/null
+++ b/src/qdoc/doc/files/compat.qdocconf
@@ -0,0 +1,12 @@
+alias.include = input
+
+macro.0 = "\\\\0"
+macro.b = "\\\\b"
+macro.n = "\\\\n"
+macro.r = "\\\\r"
+macro.img = "\\image"
+macro.endquote = "\\endquotation"
+macro.relatesto = "\\relates"
+
+spurious = "Missing comma in .*" \
+ "Missing pattern .*"
diff --git a/src/qdoc/doc/files/qtgui.qdocconf b/src/qdoc/doc/files/qtgui.qdocconf
new file mode 100644
index 000000000..0b2d28108
--- /dev/null
+++ b/src/qdoc/doc/files/qtgui.qdocconf
@@ -0,0 +1,49 @@
+include($QT_INSTALL_DOCS/global/qt-module-defaults.qdocconf)
+
+project = QtGui
+description = Qt GUI Reference Documentation
+version = $QT_VERSION
+
+examplesinstallpath = qtbase/gui
+
+qhp.projects = QtGui
+
+qhp.QtGui.file = qtgui.qhp
+qhp.QtGui.namespace = org.qt-project.qtgui.$QT_VERSION_TAG
+qhp.QtGui.virtualFolder = qtgui
+qhp.QtGui.indexTitle = Qt GUI
+qhp.QtGui.indexRoot =
+
+qhp.QtGui.filterAttributes = qtgui $QT_VERSION qtrefdoc
+qhp.QtGui.customFilters.Qt.name = Qtgui $QT_VERSION
+qhp.QtGui.customFilters.Qt.filterAttributes = qtgui $QT_VERSION
+
+qhp.QtGui.subprojects = classes
+qhp.QtGui.subprojects.classes.title = C++ Classes
+qhp.QtGui.subprojects.classes.indexTitle = Qt GUI C++ Classes
+qhp.QtGui.subprojects.classes.selectors = class fake:headerfile
+qhp.QtGui.subprojects.classes.sortPages = true
+
+tagfile = ../../../doc/qtgui/qtgui.tags
+
+depends += \
+ qtcore \
+ qtnetwork \
+ qtopengl \
+ qtsvg \
+ qtqml \
+ qtquick \
+ qtwidgets \
+ qtdoc
+
+headerdirs += ..
+
+sourcedirs += .. \
+ ../../../examples/gui/doc/src
+
+exampledirs += ../../../examples/gui \
+ snippets
+
+imagedirs += images \
+ ../../../examples/gui/doc/images \
+ ../../../doc/src/images \
diff --git a/src/qdoc/doc/images/happy.gif b/src/qdoc/doc/images/happy.gif
new file mode 100644
index 000000000..a4597f6fa
--- /dev/null
+++ b/src/qdoc/doc/images/happy.gif
Binary files differ
diff --git a/src/qdoc/doc/images/happyguy.jpg b/src/qdoc/doc/images/happyguy.jpg
new file mode 100644
index 000000000..e8604793c
--- /dev/null
+++ b/src/qdoc/doc/images/happyguy.jpg
Binary files differ
diff --git a/src/qdoc/doc/images/link-to-qquickitem.png b/src/qdoc/doc/images/link-to-qquickitem.png
new file mode 100644
index 000000000..00e03c371
--- /dev/null
+++ b/src/qdoc/doc/images/link-to-qquickitem.png
Binary files differ
diff --git a/src/qdoc/doc/images/links-to-broken-links.png b/src/qdoc/doc/images/links-to-broken-links.png
new file mode 100644
index 000000000..775143bd4
--- /dev/null
+++ b/src/qdoc/doc/images/links-to-broken-links.png
Binary files differ
diff --git a/src/qdoc/doc/images/links-to-links.png b/src/qdoc/doc/images/links-to-links.png
new file mode 100644
index 000000000..9d2cc2fae
--- /dev/null
+++ b/src/qdoc/doc/images/links-to-links.png
Binary files differ
diff --git a/src/qdoc/doc/images/qa-table.png b/src/qdoc/doc/images/qa-table.png
new file mode 100644
index 000000000..5818739fa
--- /dev/null
+++ b/src/qdoc/doc/images/qa-table.png
Binary files differ
diff --git a/src/qdoc/doc/images/qt-logo.png b/src/qdoc/doc/images/qt-logo.png
new file mode 100644
index 000000000..6b72d5fb7
--- /dev/null
+++ b/src/qdoc/doc/images/qt-logo.png
Binary files differ
diff --git a/src/qdoc/doc/images/training.jpg b/src/qdoc/doc/images/training.jpg
new file mode 100644
index 000000000..c2ce5c3b2
--- /dev/null
+++ b/src/qdoc/doc/images/training.jpg
Binary files differ
diff --git a/src/qdoc/doc/qa-pages.qdoc b/src/qdoc/doc/qa-pages.qdoc
new file mode 100644
index 000000000..a96673901
--- /dev/null
+++ b/src/qdoc/doc/qa-pages.qdoc
@@ -0,0 +1,109 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the documentation of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:FDL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Free Documentation License Usage
+** Alternatively, this file may be used under the terms of the GNU Free
+** Documentation License version 1.3 as published by the Free Software
+** Foundation and appearing in the file included in the packaging of
+** this file. Please review the following information to ensure
+** the GNU Free Documentation License version 1.3 requirements
+** will be met: http://www.gnu.org/copyleft/fdl.html.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*!
+ \page 28-qdoc-qa-pages.html
+ \previouspage Generating DITA XML Output
+ \contentspage QDoc Manual
+ \nextpage QDoc Manual
+
+ \title QA Pages
+
+ qdoc can generate some extra HTML pages that can be useful for
+ debugging qdoc documentation. These \e QA pages make it easier for
+ those who write documentation to find links that either go to the
+ wrong targets or don't go anywhere at all.
+
+ \section2 Generating the QA Pages
+
+ Add \c {-write-qa-pages} to the command line to tell qdoc to
+ generate the QA pages. If this option is not provided, the QA
+ pages will not be generated, and previolusly generated QA pages
+ will be deleted.
+
+ \section2 Finding the Module's Main QA Page
+
+ The main QA page for a module is not linked into the module's
+ generated documentation, but it is located in the same output
+ directory. To find the top-level QA page for module \e {xxx}, set
+ your browser to the qdoc output directory for module \e {xxx}.
+ Several files whose names begin with \e {aaa} appear at the top of
+ the list. These are the QA pages for module \e{xxx}. The file
+ names begin with \e {aaa} to ensure that they are easy to find at
+ the top of the directory.
+
+ For module \e{xxx}, find the file \e{aaa-xxx-qa-page.html}. This
+ is the top-level QA page for module \e{xxx}. Load that file into
+ the browser. The top-level QA page shows a table that contains
+ links to several QA sub-pages.
+
+ For example, the main QA page for QtCore is \c{aaa-qtcore-qa-page.html}.
+ This was the table for QtCore at one point:
+
+ \image qa-table.png
+
+ Each table entry shows the number of links from QtCore to some
+ other module, except for the last entry, which shows the number of
+ broken links in QtCore. Click the \b qtquick entry to load the QA
+ subpage showing the links from QtCore to QtQuick.
+
+ \section2 Links To Links Page
+
+ Clicking the \b qtquick table entry on the main QA page for QtCore
+ loads the QA subpage showing a table containing all the links from
+ QtCore to QtQuick. The table contains all the links constructed
+ with the \l {l-command} {\\l command}, as well as the autolinks.
+
+ \image links-to-links.png
+
+ At the time this table was generated, there were six links from
+ QtCore to QtQuick. The first column of each table entry contains
+ a link to some link in QtCore. The link text as it appears in
+ QtCore is shown. The second and third columns contain the source
+ file name and line number for where qdoc saw the link in a qdoc
+ comment.
+
+ \note The line number will normally refer to the first line of the
+ comment where qdoc saw the link.
+
+ Clicking on a link in the table takes you to that link in the
+ documentation. There the link will be marked with three red
+ asterisks. For example, clicking on the link in the fifth table
+ entry takes you here:
+
+ \image link-to-qquickitem.png
+
+ The link is marked with three red asterisks. Now you can click on
+ the actual link to check that it goes to the correct place. In
+ this case, the link should go to the reference page for the
+ QQuickItem class. You can check each link in the table this
+ way. If you find a link that goes to the wrong place, use the
+ source file name and line number to find the link, and fix the
+ problem using the square bracket notation for the \l {l-command}
+ {\\l command}.
+
+ */
diff --git a/src/qdoc/doc/qdoc-guide/qdoc-guide.qdoc b/src/qdoc/doc/qdoc-guide/qdoc-guide.qdoc
new file mode 100644
index 000000000..af1fa1ba1
--- /dev/null
+++ b/src/qdoc/doc/qdoc-guide/qdoc-guide.qdoc
@@ -0,0 +1,632 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the documentation of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:FDL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Free Documentation License Usage
+** Alternatively, this file may be used under the terms of the GNU Free
+** Documentation License version 1.3 as published by the Free Software
+** Foundation and appearing in the file included in the packaging of
+** this file. Please review the following information to ensure
+** the GNU Free Documentation License version 1.3 requirements
+** will be met: http://www.gnu.org/copyleft/fdl.html.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+/*!
+ \page qdoc-guide.html
+ \title Getting Started with QDoc
+ \nextpage Creating QDoc Configuration Files
+
+ Qt uses QDoc to generate its documentation set into HTML and DITA XML
+ formats. QDoc uses a set of configuration files to generate documentation
+ from QDoc comments. The comments have types called
+ \l{writing-topic-commands}{topics} that determine whether a comment is a
+ class documentation or a property documentation. A comment may also have
+ \l{writing-markup}{mark up} to enhance the layout and formatting of the
+ final output.
+
+ There are three essential materials for generating documentation with qdoc:
+ \list
+ \li \c QDoc binary
+ \li \c qdocconf configuration files
+ \li \c Documentation in \c C++, \c QML, and \c .qdoc files
+ \endlist
+
+ This section intends to cover the basic necessities for creating a
+ documentation set. Additionally, the guide presents special considerations
+ and options to documenting non-C++ API documentation as well as QML
+ documentation. Finally, the guide will provide a sample project
+ documentation and an example of a QML type documentation.
+
+ For specific QDoc information, consult the
+ \l{QDoc Manual}.
+ \section1 Chapters
+
+ \list 1
+ \li \l{Creating QDoc Configuration Files}
+ \li \l{Writing Documentation}
+ \li \l{Categories of Documentation}
+ \list
+ \li \l{C++ Documentation Style}
+ \li \l{QML Documentation Style}
+ \endlist
+ \li \l{QML Documentation Example}
+ \endlist
+
+*/
+
+/*!
+ \page qdoc-guide-conf.html
+ \title Creating QDoc Configuration Files
+ \previouspage Getting Started with QDoc
+ \nextpage Writing Documentation
+ To generate documentation, QDoc uses configuration files, with the
+ \c qdocconf extension, to store configuration settings.
+
+ The \l{The QDoc Configuration File} article covers the various configuration
+ variables in greater detail.
+
+ \section1 QDoc Configuration Files
+ QDoc's configuration settings can reside in a single \e qdocconf file, but
+ can also be in other qdocconf files. The \c {include(<filepath>)} command
+ allows configuration files to include other configuration files.
+
+ QDoc has two outputs, HTML documentation and documentation in DITA XML
+ format. The main distinction between the two outputs is that HTML
+ documentation needs to have its HTML styling information in the
+ configuration files. DITA XML documentation does not, and a separate process
+ can style the documentation in DITA at a later time. DITA XML is therefore
+ more flexible in allowing different styles to apply to the same information.
+
+ To run qdoc, the project configuration file is supplied as an argument.
+ \code
+ qdoc project.qdocconf
+ \endcode
+
+ The project configuration contains information that qdoc uses to create the
+ documentation.
+
+ \section2 Project Information
+
+ QDoc uses the \c project information to generate the documentation.
+ \code
+ project = QDoc Project
+ description = Sample QDoc project
+ \endcode
+
+ \target qdoc-input-output-dir
+ \section2 Input and Output Directories
+
+ Specifying the path to the source directories allow QDoc to find sources and
+ generate documentation.
+
+ \badcode
+ sourcedirs = <path to source code>
+ exampledirs = <path to examples directory>
+ imagedirs = <path to image directory>
+
+ sources.fileextensions = "*.cpp *.qdoc *.mm *.qml"
+ headers.fileextensions = "*.h *.ch *.h++ *.hh *.hpp *.hxx"
+ examples.fileextensions = "*.cpp *.h *.js *.xq *.svg *.xml *.ui *.qhp *.qhcp *.qml"
+ examples.imageextensions = "*.png *.jpeg *.jpg *.gif *.mng"
+ \endcode
+
+ QDoc will process headers and sources from the ones specified in the
+ \c fileextensions variable.
+
+ Likewise, QDoc needs the path to the output directory. The \c outputformats
+ variable determines the type of documentation. These variables should be
+ in separate configuration files to modularize the documentation build.
+ \badcode
+ outputdir = $SAMPLE_PROJECT/doc/html
+ outputformats = HTML
+ \endcode
+
+ QDoc can resolve the paths relative to the qdocconf file as well as
+ environment variables.
+
+ \note During each QDoc run, the output directory is deleted.
+ \section2 Extra Files
+
+ QDoc will output generated documentation into the directory specified in
+ the \l{Input and Output Directories}{output} directory. It is also possible
+ to specify extra files that QDoc should export.
+
+ \badcode
+ HTML.extraimages = extraImage.png \
+ extraImage2.png
+ \endcode
+
+ The \c extraImage.png and the \c extraImage2.png files will be copied to the
+ HTML output directory.
+
+ \section2 Qt Help Framework Configuration
+
+ QDoc will also export a \e {Qt Help Project} file, in a \c qhp file.
+ The qhp file is then used by the \c qhelpgenerator to package the
+ documentation into a \c qch file. Qt Creator and Qt Assistant reads the qch
+ file to display the documentation.
+
+ The \l {Creating Help Project Files} article covers the configuration
+ options.
+
+ \section2 HTML Configuration
+
+ QDoc has an HTML generator that will export a set of documentation into
+ HTML files using various configuration settings. QDoc will place the
+ generated documentation into the directory specified by the \c outputdir
+ variable.
+
+ \badcode
+ outputformats = HTML
+ outputdir = <path to output directory>
+ \endcode
+
+ QDoc needs to know where the styles and templates for generating HTML
+ are located. Typically, the templates directory contains a \c scripts,
+ \c images, and a \c style directory, containing scripts and CSS files.
+
+ \badcode
+ HTML.templatedir = <path to templates>
+ \endcode
+
+ The main configuration variables are:
+ \badcode
+ HTML.postheader
+ HTML.postpostheader
+ HTML.postheader
+ HTML.footer
+
+ HTML.headerstyles
+ HTML.stylesheets = style.css \
+ style1.css
+
+ HTML.scripts = script.js
+ \endcode
+
+ The \c{HTML.headerstyles} variable inserts the style information into the
+ HTML file and the \c{HTML.stylesheets} specifies which files QDoc should
+ copy into the output directory. As well, QDoc will embed the string
+ in the \c postheader, \c footer, and related variables into each HTML file.
+
+ The \l {HTML Specific Configuration Variables} article outlines the usage
+ of each variable.
+
+ \section2 DITA XML Configuration
+
+ DITA XML output is enabled using the \c outputformats variable. Unlike HTML
+ documentation, QDoc does not need HTML style templates for generating
+ documentation in DITA XML format.
+
+ \badcode
+ outputformats = DITAXML
+ outputdir
+ \endcode
+
+ \section2 Qt Index Reference
+ Documentation projects can link to Qt APIs and other articles by specifying
+ the path to the \c qt.index file. When qdoc generates the Qt Reference
+ Documentation, it will also generate an index file, containing the URLs to
+ the articles. Other projects can use the links in the index file so that
+ they can link to other articles and API documentation within Qt.
+
+ \badcode
+ indexes = $QT_INSTALL_DOCS/html/qt.index $OTHER_PROJECT/html/qt.index
+ \endcode
+ It is possible to specify multiple index files from several projects.
+
+ \section1 Macros and Other Configurations
+
+ Macros for substituting HTML characters exist and are helpful for generating
+ specific HTML-valid characters.
+
+ \badcode
+ macro.pi.HTML = "&Pi;"
+ \endcode
+ The snippet code will replace any instances of \c{\\pi} with \c &Pi; in the
+ HTML file, which will appear as the Greek \pi symbol when viewed in
+ browsers.
+
+ \section2 QML Additions
+
+ QDoc is able to parse QML files for QDoc comments. QDoc will parse files
+ with the QML extension, \c{.qml}, if the extension type is included in the
+ \l{Input and Output Directories}{fileextensions} variable.
+
+ Also, the generated HTML files can have a prefix and a suffix following the
+ QML module name, specified in the QDoc configuration file.
+ \badcode
+ outputprefixes = QML
+ outputprefixes.QML = uicomponents-
+ outputsuffixes = QML
+ outputsuffixes.QML = -tp
+ \endcode
+
+ \b {See also}: \l {outputprefixes-variable}{outputprefixes},
+ \l {outputsuffixes-variable}{outputsuffixes}.
+
+*/
+
+/*!
+ \page qdoc-guide-writing.html
+ \title Writing Documentation
+ \previouspage Creating QDoc Configuration Files
+ \nextpage Categories of Documentation
+
+ \section1 QDoc Comments
+
+ Documentation is contained within qdoc \e comments, delimited by
+ \beginqdoc and \endqdoc comments. Note that these are valid comments
+ in C++, QML, and JavaScript.
+
+ QDoc will parse C++ and QML files to look for qdoc comments. To explicitly
+ omit a certain file type, omit it from the
+ \l{Input and Output Directories}{configuration} file.
+
+ \section1 QDoc Commands
+
+ QDoc uses \e commands to retrieve information about the documentation. \c
+ Topic commands determine the type of documentation element, the \c context
+ commands provide hints and information about a topic, and \c markup commands
+ provide information on how QDoc should format a piece of documentation.
+
+ \target writing-topic-commands
+ \section2 QDoc Topics
+ Each qdoc comment must have a \e topic type. A topic distinguishes it from
+ other topics. To specify a topic type, use one of the several
+ \l{Topic Commands}{topic commands}.
+
+ QDoc will collect similar topics and create a page for each one. For
+ example, all the enumerations, properties, functions, and class description
+ of a particular C++ class will reside in one page. A generic page is
+ specified using the \l{page-command}{\\page} command and the filename is the
+ argument.
+
+ Example of topic commands:
+ \list
+ \li \l{enum-command}{\\enum} - for enumeration documentation
+ \li \l{class-command}{\\class} - for C++ class documentation
+ \li \l{qmltype-command}{\\qmltype} - for QML type documentation
+ \li \l{page-command}{\\page} - for creating a page.
+ \endlist
+
+ The \l{page-command}{\\page} command is for creating articles that are not
+ part of source documentation. The command can also accept two arguments: the
+ file name of the article and the documentation type. The possible types are:
+ \list
+ \li \c howto
+ \li \c overview
+ \li \c tutorial
+ \li \c faq
+ \li \c article - \e default when there is no type
+ \endlist
+
+ \snippet examples/samples.qdocinc sample-faq
+
+ The \l{Topic Commands} page has information on all of the available topic
+ commands.
+
+ \target writing-context
+ \section2 Topic Contexts
+
+ Context commands give QDoc a hint about the \e context of the topic. For
+ example, if a C++ function is obsolete, then it should be marked obsolete
+ with the \l{obsolete-command}{\\obsolete} command. Likewise,
+ \l{nextpage-command}{page navigation} and \l{title-command}{page title} 
+ give extra page information to QDoc.
+
+ QDoc will create additional links or pages for these contexts. For example,
+ a group is created using the \l{group-command}{\\group} command and the
+ members have the \l{ingroup-command}{\\ingroup} command. The group name is
+ supplied as an argument.
+
+ The \l{Context Commands} page has a listing of all the available context
+ commands.
+
+ \target writing-markup
+ \section2 Documentation Markup
+
+ QDoc can do \e markup of text similar to other markup or
+ documentation tools. QDoc can mark a section of text in \b{bold},
+ when the text is marked up with the \l{b-command}{\\b} command.
+
+ \code
+ \b{This} text will be in \b{bold}.
+ \endcode
+
+ The \l{Markup Commands} page has a full listing of the available markup
+ commands.
+
+ \section1 Anatomy of Documentation
+
+ Essentially, for QDoc to create a page, there must be some essential
+ ingredients present.
+
+ \list
+ \li Assign a topic to a QDoc comment - A comment could be a page, a
+ property documentation, a class documentation, or any of the available
+ \l{Topic Commands}{topic commands}.
+
+ \li Give the topic a context - QDoc can associate certain topics to other
+ pages such as associating obsolete functions when the documentation is
+ marked with \l{obsolete-command}{\\obsolete}.
+
+ \li Mark sections of the document with
+ \l{Markup Commands}{markup commands} - QDoc can create layouts and
+ format the documentation for the documentation.
+ \endlist
+
+ In Qt, the \l{QVector3D} class was documented with the following QDoc
+ comment:
+ \snippet examples/samples.qdocinc qvector3d-class
+
+ It has a constructor, \l{QVector3D::QVector3D()}, which was documented with
+ the following QDoc comment:
+ \snippet examples/samples.qdocinc qvector3d-function
+
+ The different comments may reside in different files and QDoc will collect
+ them depending on their topic and their context. The resulting documentation
+ from the snippets are generated into the \l{QVector3D} class documentation.
+
+ Note that if the documentation immediately precedes the function or class
+ in the source code, then it does not need to have a topic. QDoc will assume
+ that the documentation above the code is the documentation for that code.
+
+ An article is created using \l{page-command}{\\page} command. The first
+ argument is the HTML file that QDoc will create. The topic is supplemented
+ with context commands, the \l{title-command}{\\title} and
+ \l{nextpage-command}{\\nextpage} commands. There are several other
+ QDoc commands such as the \l{list-command}{\\list} command.
+ \snippet examples/samples.qdocinc sample-page
+
+ The section on \l{QDoc Topics}{topic commands} gives an overview on several
+ other topic types.
+
+
+*/
+
+/*!
+ \page qdoc-categories.html
+ \title Categories of Documentation
+ \previouspage Writing Documentation
+ \nextpage QML Documentation Example
+ \brief Describes the different types such as How-To's, Tutorials, Overviews,
+ Examples, and Class Documentation.
+
+ There are several types of predefined documentation \e categories or
+ \e types:
+ \list
+ \li How-To's
+ \li Tutorial
+ \li Overview
+ \li Article
+ \li FAQ (Frequently Asked Questions)
+ \li C++ API Documentation
+ \li QML Type Documentation
+ \li Code Example
+ \endlist
+
+ QDoc has the ability to format a page depending on the type. Further,
+ stylesheets can provide additional control on the display of each category.
+
+ \section1 API Documentation
+ QDoc excels in the creation of API documentation given a set of source code
+ and documentation in QDoc comments. Specifically, QDoc is aware of Qt's
+ architecture and can validate the existence of Qt C++ class, function, or
+ property documentation. QDoc gives warnings and errors if it cannot
+ associate a documentation with a code entity or if a code entity does not
+ have documentation.
+
+ In general, every Qt code entity such as properties, classes, methods,
+ signals, and enumerations have a corresponding
+ \l{qdoc-topics}{topic command}. QDoc will associate the documentation to the
+ source using C++ naming rules.
+
+ QDoc will parse the header files (typically \c .h files) to build a tree of
+ the class structures. Then QDoc will parse the source files and
+ documentation files to attach documentation to the class structure.
+ Afterwards, QDoc will generate a page for the class.
+
+ \note QDoc uses the header files to inform itself about the class and will
+ not properly process QDoc comments in header files.
+
+ \section2 Language Styles
+
+ To produce quality API documentation, the Qt API references follow a
+ particular language guidelines. While the contents of this page demonstrates
+ how to create API documentation, the style guidelines demonstrate how
+ the reference materials follow a consistent use of language.
+
+ \list
+ \li \l{C++ Documentation Style}
+ \li \l{QML Documentation Style}
+ \endlist
+
+ \keyword qml-documentation
+ \section2 Documenting QML Types
+
+ In the world of \l{Qt Quick}{QML}, there are additional entities we need to
+ document such as QML signals, attached properties, and QML methods.
+ Internally, they use Qt technologies, however, QML API documentation
+ requires different layout and naming conventions from the Qt C++ API
+ documentation.
+
+ A list of QML related QDoc commands:
+ \list
+ \li \l{qmlattachedproperty-command}{\\qmlattachedproperty}
+ \li \l{qmlattachedsignal-command}{\\qmlattachedsignal}
+ \li \l{qmlbasictype-command}{\\qmlbasictype}
+ \li \l{qmltype-command}{\\qmltype} - creates a QML type documentation
+ \li \l{qmlmethod-command}{\\qmlmethod}
+ \li \l{qmlproperty-command}{\\qmlproperty}
+ \li \l{qmlsignal-command}{\\qmlsignal}
+ \li \l{inherits-command}{\\inherits}
+ \li \l{qmlmodule-command}{\\qmlmodule}
+ \li \l{inqmlmodule-command}{\\inqmlmodule}
+ \li \l{instantiates-command}{\\instantiates}
+
+ \endlist
+
+ \note Remember to enable QML parsing by including the \c{*.qml} filetype in
+ the \l{qdoc-input-output-dir}{fileextension} variable.
+
+ To document a QML type, start by creating a QDoc comment that uses the
+ \l{qmltype-command} {\\qmltype} command as its topic command.
+
+ \section3 QML Parser
+
+ If your QML type is defined in a \e qml file, document it there.
+ If your QML type is represented by a C++ class, document it in the
+ \e cpp file for that C++ class and include an
+ \l{instantiates-command}{\\instantiates} command to specify the
+ name of the C++ class. Don't document a QML type in a \e{cpp} file
+ if the QML type is defined in a \e{qml} file.
+
+ When documenting a QML type in a \e{qml} file, place each QDoc
+ comment directly above the entity to which the comment applies.
+ For example, place the QDoc comment containing the \e{\\qmltype}
+ command (the topic comment) directly above the outer QML type in
+ the \e{qml} file. Place the comment for documenting a QML property
+ directly above the property declaration, and so on for QML signal
+ handlers and QML methods. Note that when documenting QML
+ properties in a \e{qml} file, you don't normally include the
+ \e{\\qmlproperty} command as a topic command (which you must do
+ when documenting QML types in \e{cpp} files), because the QML
+ parser automatically associates each QDoc comment with the next
+ QML declaration it parses. The same is true for QML signal handler
+ and QML method comments. But it is sometimes useful to include one
+ or more \e{\\qmlproperty} commands in the comment, e.g. when the
+ property type is another QML type and you want the user to only
+ use certain properties within that other QML type, but not all of
+ them. But when documenting a property that has an alias, place the
+ QDoc comment for it directly above the alias declaration. In these
+ cases, the QDoc comment \e must contain a \e{\\qmlproperty}
+ command, because that is the only way QDoc can know the type of
+ the aliased property.
+
+ When documenting a QML type in the \e cpp file of its
+ corresponding C++ class (if it has one), you normally place each
+ QDoc comment directly above the entity it documents. However, QDoc
+ does not use the QML parser to parse these files (the C++ parser
+ is used), so these QML QDoc comments can appear anywhere in the
+ \e{cpp} file. Note that QML QDoc comments in \e cpp files \e must
+ use the QML topic commands. i.e., the \l{qmltype-command}
+ {\\qmltype} command \e must appear in the QDoc comment for the
+ QML type, and a \l{qmlproperty-command} {\\qmlproperty} command \e
+ must appear in each QML property QDoc comment.
+
+ \section3 QML Modules
+
+ A QML type belongs to a \e module. The module
+ may include all the related types for a platform or contain a certain
+ version of \l{Qt Quick}. For example, the Qt Quick 2 QML types belong
+ to the Qt Quick 2 module while there is also a Qt Quick 1 module for the older
+ types introduced in Qt 4.
+
+ QML modules allow grouping QML types. The \l{qmltype-command}
+ {\\qmltype} topic command must have an \l{inqmlmodule-command}
+ {\\inqmlmodule} context command to relate the type to a QML
+ module. Similarly, a \l{qmlmodule-command}{\\qmlmodule} topic
+ command must exist in a separate \c{.qdoc} file to create the
+ overview page for the module. The overview page will list the
+ QML types of the QML module.
+
+ The links to the QML types must therefore also contain the module name.
+ For example, if a type called \c TabWidget is in the \c UIComponents
+ module, it must be linked as \c {UIComponents::TabWidget}.
+
+ The \l{componentset}{UIComponents} example demonstrates proper usage of
+ QDoc commands to document QML types and QML modules.
+
+ \section3 Read-only and Internal QML Properties
+
+ QDoc detects QML properties that are marked as \c readonly. Note that the
+ property must be initialized with a value.
+
+ \code
+ readonly property int sampleReadOnlyProperty: 0
+ \endcode
+ For example, the example \l{TabWidget} type has a fictitious read-only
+ property \c sampleReadOnlyProperty. Its declaration has the \c readonly
+ identifier and it has an initial value.
+
+ Properties and signals that are not meant for the public interface may
+ be marked with the \l{internal-command}{\\internal} command. QDoc will not
+ publish the documentation in the generated outputs.
+
+ \section1 Articles & Overviews
+ Articles and overviews are a style of writing best used for providing
+ summary detail on a topic or concept. It may introduce a technology or
+ discuss how a concept may be applied, but without discussing exact steps
+ in too much detail. However, this type of content could provide the entry
+ point for readers to find instructional and reference materials that do,
+ such as tutorials, examples and class documentation. An example of an
+ overview might be a product page, such as a top level discussion of
+ Qt Quick, individual modules, design principles, or tools.
+
+ To signify that a document is an article, you append the article keyword
+ to the \\page command:
+
+ \snippet examples/samples.qdocinc sample-overview
+
+ The \l{writing-topic-commands}{writing topic commands} section has a listing
+ of the available \\page command arguments.
+
+ \section1 Tutorials, How-To's, FAQ's
+
+ Tutorials, How-To's, and FAQ's are all instructional material, in that they
+ instruct or prescribe to the reader. Tutorials are content designed to guide
+ the reader along a progressive learning path for a concept or technology.
+ How-To's and FAQ's (\e{Frequently Asked Questions}) provide guidance by
+ presenting material in the form of answers to commonly asked topics.
+ How-To's and FAQ's are designed for easy reference and are not necessarily
+ presented in a linear progression.
+
+ To create these types, mark the pages by providing a \c type argument to the
+ \l{page-command}{\\page} command. The \c type argument is the second
+ argument, with the file name being the first.
+ \snippet examples/samples.qdocinc sample-faq
+
+ The \l{writing-topic-commands}{writing topic commands} section has a listing
+ of the available \\page command arguments.
+
+ \section1 Code Examples
+ Examples are an effective way to demonstrate practical usage of a given
+ technology or concept. When it comes to middleware this is usually in the
+ form of an application using simple code and clear explanations of what the
+ code is doing. Any module, API, project, pattern etc. should have at least
+ one good example.
+
+ An example may have an accompanying tutorial. The tutorial instructs and
+ describes the code, while the code example is the code content that users
+ may study. Code examples may have accompanying text that are not in the
+ tutorial.
+
+ QDoc will create a page containing the example code with a description
+ using the \l{example-command}{\\example} command.
+
+ \snippet examples/samples.qdocinc sample-example
+
+ QDoc will use the directory specified in the input
+ \l{Input and Output Directories}{exampledirs} variable to find the Qt
+ Project (\c .pro) file to generate the example files. The generated HTML
+ will have the filename, \c {declarative-ui-components-tabwidget.html}. QDoc
+ will also list all of the example code.
+
+ \note The example's project file must be the same as the
+ directory name.
+*/
+
+
diff --git a/src/qdoc/doc/qdoc-guide/qtwritingstyle-cpp.qdoc b/src/qdoc/doc/qdoc-guide/qtwritingstyle-cpp.qdoc
new file mode 100644
index 000000000..8cbf74cd6
--- /dev/null
+++ b/src/qdoc/doc/qdoc-guide/qtwritingstyle-cpp.qdoc
@@ -0,0 +1,187 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the documentation of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:FDL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Free Documentation License Usage
+** Alternatively, this file may be used under the terms of the GNU Free
+** Documentation License version 1.3 as published by the Free Software
+** Foundation and appearing in the file included in the packaging of
+** this file. Please review the following information to ensure
+** the GNU Free Documentation License version 1.3 requirements
+** will be met: http://www.gnu.org/copyleft/fdl.html.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*!
+\page qtwritingstyle-cpp.html
+\title C++ Documentation Style
+\brief Style guidelines for C++ documentation
+
+To generate the documentation, QDoc goes through the source code and generates
+documentation for C++ types such as classes. QDoc then associates member
+functions, properties, and other types to the appropriate class.
+
+Note that the documentation must be in the implementation files such as \c .cpp.
+
+\section1 Class Documentation
+
+Class documentation is generated using the \l{class-command}{\\class} command and
+the name of the class as the first argument.
+
+\snippet examples/cpp.qdoc.sample class
+
+\l{Context commands} add information about the class, such as its module or
+which version the class was added.
+
+Some common context commands are:
+\list
+\li \l{brief-command}{\\brief} - the class' brief description \b (mandatory)
+\li \l{since-command}{\\since} - the version to which the class was added \b (mandatory)
+\li \l{internal-command}{\\internal} - marks the class as internal. Internal
+classes do not appear in the public API documentation.
+\endlist
+
+
+\section2 The Brief and Detailed Description
+
+The \e{brief description} is marked with the \l{brief-command}{\\brief} command
+and it is for summarizing the purpose or functionality of the class. For C++
+classes, QDoc will take the class and create annotated information for the
+class. The annotated information appears in lists and tables which display the
+class.
+
+The C++ brief should start with:
+\code
+"The <C++ class name> class"
+\endcode
+
+The \e{detailed description} section starts after the brief description. It
+provides more information about the class. The detailed description may contain
+images, snippet code, or links to other relevant documents. There
+must be an empty line which separates the brief and detailed description.
+
+\section1 Member Functions
+
+Typically, function documentation immediately precedes the implementation of the
+function in the \c .cpp file. For function documentation that is not immediately
+above the implementation, the \l{fn-command}{\\fn} is needed.
+
+\snippet examples/cpp.qdoc.sample function
+
+The function documentation starts with a verb, indicating the operation the
+function performs. This also applies to constructors and destructors.
+
+Some common verbs for function documentation:
+\list
+\li "Constructs..." - for constructors
+\li "Destroys..." - for destructors
+\li "Returns..." - for accessor functions
+\endlist
+
+The function documentation must document:
+\list
+\li the return type
+\li the parameters
+\li the actions of the functions
+\endlist
+
+The \l{a-command}{\\a} command marks the parameter in the documentation.
+The return type documentation should link to the type documentation or be
+marked with the \l{c-command}{\\c} command in the case of boolean values.
+
+\snippet examples/cpp.qdoc.sample return
+
+\section1 Properties
+
+The property documentation resides immediately above the read function's
+implementation. The \l{writing-topic-commands}{topic command} for properties is
+\l{property-command}{\\property}.
+
+\snippet examples/cpp.qdoc.sample property
+
+Property documentation usually starts with "This property...", but these are
+alternate expressions:
+\list
+\li "This property holds..."
+\li "This property describes..."
+\li "This property represents..."
+\li "Returns \c true when... and \c false when..." - for properties that
+are read.
+\li "Sets the..." - for properties that configure a type.
+\endlist
+
+Property documentation must include:
+\list
+\li description and behavior of the property
+\li accepted values for the property
+\li the default value of the property
+\endlist
+Similar to \l{Member Functions}{functions}, the default type may be linked
+or marked with the \c{\c} command.
+
+An example of a value range style is:
+\quotation
+The values range from 0.0 (no blur) to maximumRadius (maximum blur). By default, the property is set to 0.0 (no blur).
+\endquotation
+
+\section1 Signals, Notifiers, and Slots
+The \l{writing-topic-commands}{topic command} for signals, notifiers, and slots
+is \l{fn-command}{\\fn}. Signal documentation state when they are triggered
+or emitted.
+
+\snippet examples/cpp.qdoc.sample signals
+
+Signal documentation typically begin with "This signal is triggered when...".
+Here are alternate styles:
+\list
+\li "This signal is triggered when..."
+\li "Triggered when..."
+\li "Emitted when..."
+\endlist
+
+For slots or notifiers, the condition when they are executed or triggered by
+a signal should be documented.
+\list
+\li "Executed when..."
+\li "This slot is executed when..."
+\endlist
+
+For properties that have overloaded signals, QDoc groups the overloaded
+notifiers together. To refer to a specific version of a notifier or signal,
+simply refer to the property and mention that there are different versions of
+the notifier.
+
+\snippet examples/cpp.qdoc.sample overloaded notifier
+
+\section1 Enums, Namespaces, and Other Types
+
+Enums, namespaces, and macros have a \l{writing-topic-commands}{topic command} for their documentation:
+\list
+\li \l{enum-command}{\\enum}
+\li \l{typedef-command}{\\typedef}
+\li \l{macro-command}{\\macro}
+\endlist
+
+The language style for these types mention that they are an enum or a macro and
+continues with the type description.
+
+For enumerations, the \l{value-command}{\\value} command is for listing the
+values. QDoc creates a table of values for the enum.
+
+\snippet examples/cpp.qdoc.sample enums
+
+*/
+
diff --git a/src/qdoc/doc/qdoc-guide/qtwritingstyle-qml.qdoc b/src/qdoc/doc/qdoc-guide/qtwritingstyle-qml.qdoc
new file mode 100644
index 000000000..6955a042c
--- /dev/null
+++ b/src/qdoc/doc/qdoc-guide/qtwritingstyle-qml.qdoc
@@ -0,0 +1,164 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the documentation of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:FDL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Free Documentation License Usage
+** Alternatively, this file may be used under the terms of the GNU Free
+** Documentation License version 1.3 as published by the Free Software
+** Foundation and appearing in the file included in the packaging of
+** this file. Please review the following information to ensure
+** the GNU Free Documentation License version 1.3 requirements
+** will be met: http://www.gnu.org/copyleft/fdl.html.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*!
+\page qtwritingstyle-qml.html
+\title QML Documentation Style
+\brief Style guidelines for QML documentation
+
+QDoc can process QML types defined as C++ classes and QML types defined in
+\c .qml files. For C++ classes documented as QML types, the QDoc comments are
+in the \c .cpp file while QML types defined in QML are in the \c .qml
+file. The C++ classes must also be documented
+documented with the QML \l{topic-commands}{topic commands}:
+
+\list
+\li \l{qmlattachedproperty-command}{\\qmlattachedproperty}
+\li \l{qmlattachedsignal-command}{\\qmlattachedsignal}
+\li \l{qmlbasictype-command}{\\qmlbasictype}
+\li \l{qmltype-command}{\\qmltype}
+\li \l{qmlmethod-command}{\\qmlmethod}
+\li \l{qmlproperty-command}{\\qmlproperty}
+\li \l{qmlsignal-command}{\\qmlsignal}
+\li \l{qmlmodule-command}{\\qmlmodule}
+\li \l{inqmlmodule-command}{\\inqmlmodule}
+\li \l{instantiates-command}{\\instantiates}
+\endlist
+
+For QML types defined in \c .qml files, QDoc will parse the QML and determine
+the properties, signals, and the type within the QML definition. The QDoc
+block then needs to be immediately above the declaration. For QML types
+implemented in C++, QDoc will output warnings if the C++ class documentation
+does not exist. The class documentation may be marked as
+\l{internal-command}{internal} if it is not a public API.
+
+\section1 QML Types
+
+The \l{qmltype-command}{\\qmltype} command is for QML type documentation.
+
+\snippet examples/qml.qdoc.sample qmltype
+
+The \l{instantiates-command}{\\instantiates} accepts the C++ class which
+implements the QML type as the argument. For types implemented in QML, this
+is not needed.
+
+The \e{brief description} provides a summary for the QML type. The brief does
+not need to be a complete sentence and may start with a verb. QDoc will append
+the brief description onto the QML type in tables and generated lists.
+
+\code
+\qmltype ColorAnimation
+\brief Animates changes in color values
+\endcode
+
+Here are some alternate verbs for the brief statement:
+\list
+\li "Provides..."
+\li "Specifies..."
+\li "Describes..."
+\endlist
+
+The \e{detailed description} follows the brief and may contain images, snippet,
+and link to other documentation.
+
+\section1 Properties
+
+The property description focuses on what the property \e does and may use the
+following style:
+
+Property documentation usually starts with "This property..." but for certain
+properties, these are the common expressions:
+\list
+\li "This property holds..."
+\li "This property describes..."
+\li "This property represents..."
+\li "Returns \c true when... and \c false when..." - for properties that
+are marked \c{read-only}.
+\li "Sets the..." - for properties that configure a type.
+\endlist
+
+\section1 Signals and Handlers Documentation
+
+QML signals are documented either in the QML file or in the C++ implementation
+with the \l{qmlsignal-command}{\\qmlsignal} command. Signal documentation
+must include the condition for emitting the signal, mention the corresponding
+signal handler, and document whether the signal accepts a parameter.
+
+\snippet examples/qml.qdoc.sample signals
+
+These are the possible documentation styles for signals:
+\list
+\li "This signal is triggered when..."
+\li "Triggered when..."
+\li "Emitted when..."
+\endlist
+
+\section1 Methods and JavaScript Functions
+
+Typically, function documentation immediately precedes the implementation of the
+function in the \c .cpp file. The \l{topic-commands}{topic command} for
+functions is \l{fn-command}{\\fn}. For functions in QML or JavaScript, the
+documentation must reside immediately above the function declaration.
+
+The function documentation starts with a verb, indicating the operation the
+function performs.
+
+\snippet examples/qml.qdoc.sample function
+
+Some common verbs for function documentation:
+\list
+\li "Copies..." - for constructors
+\li "Destroys..." - for destructors
+\li "Returns..." - for accessor functions
+\endlist
+
+The function documentation must document:
+\list
+\li the return type
+\li the parameters
+\li the actions of the functions
+\endlist
+
+The \l{a-command}{\\a} command marks the parameter in the documentation.
+The return type documentation should link to the type documentation or be
+marked with the \l{c-command}{\\c} command in the case of boolean values.
+
+\section1 Enumerations
+
+QML enumerations are documented as QML properties with the
+\l{qmlproperty-command}{\\qmlproperty} command. The type of the property
+is \c enumeration.
+
+\snippet examples/qml.qdoc.sample enums
+
+The QDoc comment lists the values of the enumeration. If the enumeration is
+implemented in C++, the documentation may link to the corresponding C++
+enumeration. However, the QDoc comment should advise that the enumeration
+is a C++ enumeration.
+
+*/
+
diff --git a/src/qdoc/doc/qdoc-manual-DITA.qdoc b/src/qdoc/doc/qdoc-manual-DITA.qdoc
new file mode 100644
index 000000000..72882c8eb
--- /dev/null
+++ b/src/qdoc/doc/qdoc-manual-DITA.qdoc
@@ -0,0 +1,164 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the documentation of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:FDL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Free Documentation License Usage
+** Alternatively, this file may be used under the terms of the GNU Free
+** Documentation License version 1.3 as published by the Free Software
+** Foundation and appearing in the file included in the packaging of
+** this file. Please review the following information to ensure
+** the GNU Free Documentation License version 1.3 requirements
+** will be met: http://www.gnu.org/copyleft/fdl.html.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*!
+ \page 21-0-qdoc-creating-dita-maps.html
+ \previouspage Miscellaneous
+ \contentspage QDoc Manual
+ \nextpage The QDoc Configuration File
+
+ \title Creating DITA Maps
+
+ You can create DITA map files using three new qdoc commands, the \l{ditamap-command}
+ {ditamap} command, the \l{topicref-command} {topicref} command, and the \l{mapref-command}
+ {mapref} command. How these DITA maps will be used automatically or manually by the
+ documentation build process is still under consideration. This section will be updated
+ as the decisions are made.
+
+ \section1 What is a DITA Map?
+
+ A complete description of DITA can be found at the
+ \l{http://www.oasis-open.org/committees/tc_home.php?wg_abbrev=dita}
+ {OASIS Darwin Information Typing Architecture} site.
+
+ An explanation of the DITA map is found at that site
+ \l{http://docs.oasis-open.org/dita/v1.2/os/spec/langref/map.html}{here}.
+
+ \target ditamap-command
+ \section1 \\ditamap
+
+ The \\ditamap command is for creating a DITA map using qdoc commands.
+ The \\ditamap command is a kind of \\page command that produces a
+ \e{.ditamap} instead of a \e{.html} or \e{.xml} file. The file that
+ is created actually contains XML text, but the \e{.ditamap} suffix is
+ used to identify the file as containing a DITA MAP.
+
+ The argument is the name of the file to be created. In the following
+ example, the file \e{creator.ditamap} is output:
+ \code
+ \ditamap creator.ditamap
+ \endcode
+
+ \target topicref-command
+ \section1 \\topicref \\endtopicref
+
+ The \\topicref \\endtopicref commands are for creating a topicref
+ in the ditamap. The \\endtopicref command is required because
+ \\topicref commands can be nested.
+
+ \\topicref has two arguments. The first argument becomes the value
+ of the \e navtitle attribute. Normally, you use the title of the
+ topic being referenced. This title is often what will appear in a
+ table of contents constructed from the ditamap.
+
+ The second argument is the name of the page being referenced. The
+ second argument is actually optional, for example if you are using
+ a topicref as a container for other topicrefs and maprefs. It is
+ also optional if you want qdoc to find the page name for you by
+ looking up the title in its internal data structure. It is recommended
+ that you provide the second parameter if you know the page name.
+
+ \code
+ \topicref {QML Module QtQuick 2} {qtquick-2.xml}
+ \mapref {Creator Manual} {creator-manual.ditamap} \endmapref
+ \topicref {QML Mouse Events} {qtquick2-mouseevents.xml} \endtopicref
+ \topicref {Property Binding} {qtquick2-propertybinding.xml} \endtopicref
+ \endtopicref
+ \endcode
+
+ \target mapref-command
+ \section1 \\mapref
+
+ The \\mapref command is for creating a mapref in the ditamap. A
+ mapref refers to another ditamap, which you want to include in
+ your ditamap. Like the \\topicref command, the \\mapref command
+ has two arguments, but for the \\mapref command, both arguments
+ are required. The arguments are essentially the same as described
+ for \\topicref, but for \\mapref, the second command must be the
+ name of another ditamap, i.e. it must have the \e{.ditamap}
+ suffix. You must provide the file name. qdoc can't look up the
+ file name for you.
+
+ \code
+ \mapref {Creator Manual} {creator-manual.ditamap} \endmapref
+ \endcode
+
+ \section1 An Example Ditamap Page
+
+ The following example uses the three qdoc ditamap commands described above.
+
+ \code
+ \ditamap creator.ditamap
+ \title The DITA Map for Creator
+
+ \topicref {QML Module QtQuick 1}
+ \topicref {QML Mouse Events} \endtopicref
+ \topicref {Property Binding} \endtopicref
+ \endtopicref
+
+ \topicref {QML Module QtQuick 2} {qtquick-2.xml}
+ \mapref {Creator Manual} {creator-manual.ditamap} \endmapref
+ \topicref {QML Mouse Events} {qtquick2-mouseevents.xml} \endtopicref
+ \topicref {Property Binding} {qtquick2-propertybinding.xml} \endtopicref
+ \endtopicref
+
+ \topicref {QML Module QtQuick.Particles 2} {qtquick-particles-2.xml}
+ \topicref {Age} {qml-qtquick-particles2-age.xml} \endtopicref
+ \endtopicref
+ \endcode
+
+ \section1 The Resulting Ditamap File
+
+ This is the \e{.ditamap} file you get when you input the qdoc
+ ditamap page shown above. Note that you can write ditamap files
+ directly in XML just as easily as you can write them using the
+ qdoc commands. The choice is yours.
+
+ \code
+ <?xml version="1.0" encoding="UTF-8"?>
+ <!DOCTYPE map PUBLIC "-//OASIS//DTD DITA Map//EN" "map.dtd">
+ <map>
+ <topicmeta>
+ <shortdesc>The DITA Map for Creator</shortdesc>
+ </topicmeta>
+ <topicref navtitle="QML Module QtQuick 1" href="qtquick-1.xml">
+ <topicref navtitle="QML Mouse Events" href="qtquick2-mouseevents.xml"/>
+ <topicref navtitle="Property Binding" href="qtquick2-propertybinding.xml"/>
+ </topicref>
+ <topicref navtitle="QML Module QtQuick 2" href="qtquick-2.xml">
+ <mapref navtitle="Creator Manual" href="creator-manual.ditamap"/>
+ <topicref navtitle="QML Mouse Events" href="qtquick2-mouseevents.xml"/>
+ <topicref navtitle="Property Binding" href="qtquick2-propertybinding.xml"/>
+ </topicref>
+ <topicref navtitle="QML Module QtQuick.Particles 2" href="qtquick-particles-2.xml">
+ <topicref navtitle="Age" href="qml-qtquick-particles2-age.xml"/>
+ </topicref>
+ </map>
+ \endcode
+
+*/
+
diff --git a/src/qdoc/doc/qdoc-manual-cmdindex.qdoc b/src/qdoc/doc/qdoc-manual-cmdindex.qdoc
new file mode 100644
index 000000000..d3f188c26
--- /dev/null
+++ b/src/qdoc/doc/qdoc-manual-cmdindex.qdoc
@@ -0,0 +1,156 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the documentation of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:FDL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Free Documentation License Usage
+** Alternatively, this file may be used under the terms of the GNU Free
+** Documentation License version 1.3 as published by the Free Software
+** Foundation and appearing in the file included in the packaging of
+** this file. Please review the following information to ensure
+** the GNU Free Documentation License version 1.3 requirements
+** will be met: http://www.gnu.org/copyleft/fdl.html.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*!
+ \page 27-qdoc-commands-alphabetical.html
+ \previouspage Introduction to QDoc
+ \contentspage QDoc Manual
+ \nextpage Topic Commands
+
+ \title Command Index
+
+ This is a complete, alphabetized list of the QDoc commands.
+
+ \list
+
+ \li \l {a-command} {\\a}
+ \li \l {abstract-command} {\\abstract}
+ \li \l {annotatedlist-command} {\\annotatedlist}
+ \li \l {b-command} {\\b}
+ \li \l {b-command} {\\bold} \span {class="newStuff"} {(deprecated, use \\b)}
+ \li \l {brief-command} {\\brief}
+ \li \l {c-command} {\\c}
+ \li \l {caption-command} {\\caption}
+ \li \l {chapter-command} {\\chapter}
+ \li \l {class-command} {\\class}
+ \li \l {code-command} {\\code}
+ \li \l {codeline-command} {\\codeline},
+ \li \l {compat-command} {\\compat}
+ \li \l {contentspage-command} {\\contentspage}
+ \li \l {default-command} {\\default}
+ \li \l {div-command} {\\div}
+ \li \l {dots-command} {\\dots}
+ \li \l {e-command} {\\e}
+ \li \l {else-command} {\\else}
+ \li \l {endif-command} {\\endif}
+ \li \l {enum-command} {\\enum}
+ \li \l {example-command} {\\example}
+ \li \l {externalpage-command} {\\externalpage}
+ \li \l {fn-command} {\\fn}
+ \li \l {footnote-command} {\\footnote}
+ \li \l {generatelist-command} {\\generatelist}
+ \li \l {group-command} {\\group}
+ \li \l {header-command} {\\header}
+ \li \l {headerfile-command} {\\headerfile}
+ \li \l {e-command} {\\i} \span {class="newStuff"} {(deprecated, use \\e)}
+ \li \l {if-command} {\\if}
+ \li \l {image-command} {\\image}
+ \li \l {include-command} {\\include}
+ \li \l {indexpage-command} {\\indexpage}
+ \li \l {ingroup-command} {\\ingroup}
+ \li \l {inherits-command}{\\inherits}
+ \li \l {inlineimage-command} {\\inlineimage}
+ \li \l {inmodule-command} {\\inmodule}
+ \li \l {inqmlmodule-command} {\\inqmlmodule}
+ \li \l {instantiates-command} {\\instantiates}
+ \li \l {internal-command} {\\internal}
+ \li \l {keyword-command} {\\keyword}
+ \li \l {l-command} {\\l}
+ \li \l {legalese-command} {\\legalese}
+ \li \l {li-command} {\\li}
+ \li \l {list-command} {\\list}
+ \li \l {macro-command} {\\macro}
+ \li \l {meta-command} {\\meta}
+ \li \l {module-command} {\\module}
+ \li \l {namespace-command} {\\namespace}
+ \li \l {nextpage-command} {\\nextpage}
+ \li \l {newcode-command} {\\newcode}
+ \li \l {noautolist-command} {\\noautolist}
+ \li \l {nonreentrant-command} {\\nonreentrant}
+ \li \l {note-command} {\\note}
+ \li \l {li-command} {\\o} \span {class="newStuff"} {(deprecated, use \\li)}
+
+ \li \l {obsolete-command} {\\obsolete}
+ \li \l {oldcode-command} {\\oldcode}
+ \li \l {omit-command} {\\omit}
+ \li \l {omitvalue-command} {\\omitvalue}
+ \li \l {overload-command} {\\overload}
+ \li \l {page-command} {\\page}
+ \li \l {part-command} {\\part}
+ \li \l {preliminary-command} {\\preliminary}
+ \li \l {previouspage-command} {\\previouspage}
+ \li \l {printline-command} {\\printline}
+ \li \l {printto-command} {\\printto}
+ \li \l {printuntil-command} {\\printuntil}
+ \li \l {property-command} {\\property}
+ \li \l {qmlabstract-command} {\\qmlabstract}
+ \li \l {qmlattachedproperty-command} {\\qmlattachedproperty}
+ \li \l {qmlattachedsignal-command} {\\qmlattachedsignal}
+ \li \l {qmlbasictype-command} {\\qmlbasictype}
+ \li \l {qmlclass-command} {\\qmlclass} \span {class="newStuff"} {(deprecated, use \\qmltype)}
+ \li \l {qmltype-command} {\\qmltype}
+ \li \l {qmlmethod-command} {\\qmlmethod}
+ \li \l {qmlproperty-command} {\\qmlproperty}
+ \li \l {qmlsignal-command} {\\qmlsignal}
+ \li \l {qmlmodule-command} {\\qmlmodule}
+ \li \l {quotation-command} {\\quotation}
+ \li \l {quotefile-command} {\\quotefile}
+ \li \l {quotefromfile-command} {\\quotefromfile}
+ \li \l {raw-command} {\\raw}
+ \li \l {reentrant-command} {\\reentrant}
+ \li \l {reimp-command} {\\reimp}
+ \li \l {relates-command} {\\relates}
+ \li \l {row-command} {\\row}
+ \li \l {sa-command} {\\sa}
+ \li \l {sectionOne-command} {\\section1}
+ \li \l {sectionTwo-command} {\\section2}
+ \li \l {sectionThree-command} {\\section3}
+ \li \l {sectionFour-command} {\\section4}
+ \li \l {since-command} {\\since}
+ \li \l {skipline-command} {\\skipline}
+ \li \l {skipto-command} {\\skipto}
+ \li \l {skipuntil-command} {\\skipuntil}
+ \li \l {snippet-command} {\\snippet},
+ \li \l {span-command} {\\span}
+ \li \l {startpage-command} {\\startpage}
+ \li \l {sub-command} {\\sub}
+ \li \l {subtitle-command} {\\subtitle}
+ \li \l {sup-command} {\\sup}
+ \li \l {table-command} {\\table}
+ \li \l {tableofcontents-command} {\\tableofcontents}
+ \li \l {target-command} {\\target}
+ \li \l {threadsafe-command} {\\threadsafe}
+ \li \l {title-command} {\\title}
+ \li \l {tt-command} {\\tt}
+ \li \l {typedef-command} {\\typedef}
+ \li \l {uicontrol-command} {\\uicontrol}
+ \li \l {underline-command} {\\underline}
+ \li \l {variable-command} {\\variable}
+ \li \l {value-command} {\\value}
+ \li \l {warning-command} {\\warning}
+ \endlist
+*/
diff --git a/src/qdoc/doc/qdoc-manual-contextcmds.qdoc b/src/qdoc/doc/qdoc-manual-contextcmds.qdoc
new file mode 100644
index 000000000..d707c77cf
--- /dev/null
+++ b/src/qdoc/doc/qdoc-manual-contextcmds.qdoc
@@ -0,0 +1,1058 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the documentation of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:FDL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Free Documentation License Usage
+** Alternatively, this file may be used under the terms of the GNU Free
+** Documentation License version 1.3 as published by the Free Software
+** Foundation and appearing in the file included in the packaging of
+** this file. Please review the following information to ensure
+** the GNU Free Documentation License version 1.3 requirements
+** will be met: http://www.gnu.org/copyleft/fdl.html.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*!
+ \page 14-qdoc-commands-contextcommands.html
+ \previouspage Topic Commands
+ \contentspage QDoc Manual
+ \nextpage Document Navigation
+
+ \title Context Commands
+
+ The context commands provide information about the element being
+ documented that QDoc can't deduce on its own. For example:
+ \list
+ \li Is this class thread-safe?
+ \li Is this function reentrant?
+ \li Of which module is this class a member ?
+ \endlist
+
+ Context commands can appear anywhere in a QDoc comment,
+ but they are normally placed near the top of the comment, just
+ below the \l {Topic Commands} {topic} command.
+
+ \list
+ \li \l {abstract-command} {\\abstract}
+ \li \l {compat-command}{\\compat},
+ \li \l {contentspage-command}{\\contentspage},
+ \li \l {indexpage-command}{\\indexpage},
+ \li \l {ingroup-command}{\\ingroup},
+ \li \l {inherits-command}{\\inherits},
+ \li \l {inmodule-command}{\\inmodule},
+ \li \l {internal-command}{\\internal},
+ \li \l {nextpage-command}{\\nextpage},
+ \li \l {nonreentrant-command}{\\nonreentrant},
+ \li \l {obsolete-command}{\\obsolete},
+ \li \l {overload-command}{\\overload},
+ \li \l {preliminary-command}{\\preliminary},
+ \li \l {previouspage-command}{\\previouspage},
+ \li \l {qmlabstract-command} {\\qmlabstract}
+ \li \l {reentrant-command}{\\reentrant},
+ \li \l {reimp-command}{\\reimp},
+ \li \l {relates-command}{\\relates},
+ \li \l {since-command}{\\since},
+ \li \l {startpage-command}{\\startpage},
+ \li \l {subtitle-command}{\\subtitle}
+ \li \l {threadsafe-command}{\\threadsafe},
+ \li \l {title-command}{\\title}
+ \endlist
+
+*/
+
+/*!
+ \page 15-qdoc-commands-navigation.html
+ \previouspage Context Commands
+ \contentspage QDoc Manual
+ \nextpage Status
+
+ \title Document Navigation
+
+ The navigation commands are for linking the pages of a document in
+ a meaningful sequence. Below is a sequence of QDoc comments that
+ shows a typical use of the navigation commands.
+
+ \section1 Example
+ \quotefile files/basicqt.qdoc.sample
+
+ QDoc renders the "Getting Started" page in \c{creatingdialogs.html}:
+
+ \quotation
+ \raw HTML
+ <table border="0" cellpadding="0" cellspacing="5" width="100%">
+
+ <tr>
+ <p>
+ [Previous: <a href="15-qdoc-commands-navigation.html#deadlink">
+ Basic Qt</a>]
+ [<a href="15-qdoc-commands-navigation.html#deadlink">Contents</a>]
+ [Next: <a href="15-qdoc-commands-navigation.html#deadlink">
+ Creating Dialogs</a>]
+ </p>
+
+ <h1 align="center">Getting Started<br /></h1>
+
+ <p>
+ This chapter shows how to combine basic C++ with the
+ functionality provided by Qt to create a few small graphical
+ interface (GUI) applications.
+ </p>
+
+ <p>
+ [Previous: <a href="15-qdoc-commands-navigation.html#deadlink">
+ Basic Qt</a>]
+ [<a href="15-qdoc-commands-navigation.html#deadlink">Contents</a>]
+ [Next: <a href="15-qdoc-commands-navigation.html#deadlink">
+ Creating Dialogs</a>]
+ </p>
+
+ </table>
+ \endraw
+ \endquotation
+
+ The \l {indexpage-command} {\\indexpage} and \l
+ {startpage-command} {\\startpage} commands create links to the
+ page's index page and start page. These links can be used by
+ browsers and search engines.
+
+ The index page is typically an alphabetical list of the document's
+ titles and topics, while the start page is the page considered by
+ the author to be the starting point of a multipage document.
+
+ The links are included in the generated HTML source code, but have
+ no visual effect on the documentation:
+
+ \code
+ <head>
+ ...
+ <link rel="index" href="index.html" />
+ <link rel="start" href="basicqt.html" />
+ ...
+ </head>
+ \endcode
+
+ \section1 Commands
+
+ \target previouspage-command
+ \section2 \\previouspage
+
+ The \\previouspage command links the current page to the previous
+ page in a sequence.a The command has two arguments, each enclosed
+ by curly braces: the first is the link target (the title of
+ the previous page), the second is the link text. If the page's
+ title is equivalent to the link text, the second argument can be
+ omitted.
+
+ The command must stand alone on its own line.
+
+ \target nextpage-command
+ \section2 \\nextpage
+
+ The \\nextpage command links the current page to the next page in
+ a sequence. The command follows the same syntax and argument
+ convention as the \l {previouspage-command} {\\previouspage}
+ command.
+
+ \target startpage-command
+ \section2 \\startpage
+
+ The \\startpage command specifies the first page of a sequence of
+ pages. The command must stand alone on its own line, and its
+ unique argument is the title of the first document.
+
+ QDoc will generate a link to the start page and include it in the
+ generated HTML file, but this has no visual effect on the
+ documentation. The generated link type tells browsers and search
+ engines which document is considered by the author to be the
+ starting point of the collection.
+
+ \target contentspage-command
+ \section2 \\contentspage
+
+ The \\contentspage command links the current page to a table of
+ contents page. The command follows the same syntax and argument
+ convention as the \l {previouspage-command} {\\previouspage}
+ command.
+
+ \target indexpage-command
+ \section2 \\indexpage
+
+ The \\indexpage command specifies an index page for the current
+ document. The command must stand alone on its own line, and its
+ unique argument is the title of the index document.
+
+ QDoc will generate a link to the index page and include it in the
+ generated HTML file, but this has no visual effect on the
+ documentation. The generated link type tells browsers and search
+ engines which document is considered by the author to be the
+ index page of the collection.
+*/
+
+/*!
+ \page 16-qdoc-commands-status.html
+ \previouspage Document Navigation
+ \contentspage QDoc Manual
+ \nextpage Thread Support
+
+ \title Status
+
+ These commands are for indicating that a documented element has
+ some special status. The element could be marked as about to be
+ made obsolete, or that it is provided for compatibility with an
+ earlier version, or is simply not to be included in the public
+ interface. The \l {since-command}{\\since} command is for
+ specifying the version number in which a function or class first
+ appeared. The \l {qmlabstract-command} {\\qmlabstract} command is
+ for marking a QML type as an abstract base class.
+
+ \target abstract-command
+ \target qmlabstract-command
+ \section1 \\abstract and \\qmlabstract
+
+ \\abstract is a synonym for the \\qmlabstract command. Add this
+ command to the \l {qmltype-command} {\\qmltype} comment for a QML
+ type when that type is meant to be used \e {only} as an abstract
+ base type. When a QML type is abstract, it means that the QML type
+ that can't be instantiated. Instead, the properties in its public
+ API are included in the public properties list on the reference
+ page for each QML type that inherits the abstract QML type. The
+ properties are documented as if they are properties of the
+ inheriting QML type.
+
+ Normally, when a QML type is marked with \e{\\qmlabstract}, it is
+ also marked with \e{\\internal} so that its reference page is not
+ generated. It the abstract QML type is not marked internal, it
+ will have a reference page in the documentation.
+
+ \target compat-command
+ \section1 \\compat
+
+ The \\compat command is for indicating that a class or function is
+ part of the support library provided to keep old source code
+ working.
+
+ The command must stand on its own line.
+
+ Usually an equivalent function or class is provided as an
+ alternative.
+
+ If the command is used in the documentation of a class, the
+ command expands to a warning that the referenced class is part of
+ the support library. The warning is located at the top of the
+ documentation page.
+
+ \code
+ \beginqdoc
+ \class MyQt3SupportClass
+ \compat
+ \endqdoc
+ \endcode
+
+ QDoc renders this at the top of the MyQt3SupportClass class
+ reference page.
+
+ \quotation
+ \b {This class is part of the Qt 3 support
+ library.} It is provided to keep old source code
+ working. We strongly advise against using it in new
+ code. See the \l
+ {http://doc.qt.io/qt-4.8/porting4.html} {Porting
+ Guide} for more information.
+ \endquotation
+
+ If the command is used when documenting a function, QDoc will
+ create and link to a separate page documenting Qt 3 support
+ members when generating the reference documentation for the
+ associated class.
+
+ \code
+ \beginqdoc
+ \fn MyClass::MyQt3SupportMemberFunction
+ \compat
+
+ Use MyNewFunction() instead.
+ \endqdoc
+ \endcode
+
+ QDoc renders this in \c{myclass-qt3.html} as:
+
+ \quotation
+ \raw HTML
+ <h1>Qt 3 Support Members for MyClass</h1>
+ \endraw
+
+ \b {The following class members are part of the Qt 3
+ support layer.} They are provided to help you port old code to
+ Qt 4. We advise against using them in new code.
+
+ ...
+
+ \list
+ \li void MyQt3SupportMemberFunction()
+ \li ...
+ \endlist
+
+ \raw HTML
+ <hr />
+ <h2>Member Function Documentation</h2>
+ <h3>void MyQt3SupportMemberFunction ()</h3>
+ <p>Use MyNewFunction() instead.</p>
+ \endraw
+ ...
+ \endquotation
+
+ \target default-command
+ \section1 \\default
+
+ The \\default command is for marking a QML property as the
+ \l {default-properties}
+ {default property}. The word \span {class="newStuff"} {default} is shown in red in
+ the documentation of the property.
+
+ \code
+ / *!
+ \qmlproperty list<Change> State::changes
+ This property holds the changes to apply for this state.
+ \default
+
+ By default these changes are applied against the default state. If the state
+ extends another state, then the changes are applied against the state being
+ extended.
+ * /
+ \endcode
+
+ See how QDoc renders this property on the reference page for the
+ \l {State::changes}{State} type.
+
+ \target obsolete-command
+ \section1 \\obsolete
+
+ The \\obsolete command is for indicating that a function is being
+ deprecated, and it should no longer be used in new code. There is
+ no guarantee for how long it will remain in the library.
+
+ The command must stand on its own line.
+
+ When generating the reference documentation for a class, QDoc will
+ create and link to a separate page documenting its obsolete
+ functions. Usually an equivalent function is provided as an
+ alternative.
+
+ \code
+ / *!
+ \fn MyClass::MyObsoleteFunction
+ \obsolete
+
+ Use MyNewFunction() instead.
+ * /
+ \endcode
+
+ QDoc renders this in \c{myclass-obsolete.html} as:
+
+ \quotation
+ \raw HTML
+ <h1>Obsolete Members for MyClass</h1>
+ \endraw
+
+ \b {The following class members are obsolete.} They are
+ provided to keep old source code working. We strongly advise
+ against using them in new code.
+
+ ...
+
+ \list
+ \li void MyObsoleteFunction() \c (obsolete)
+ \li ...
+ \endlist
+
+ \raw HTML
+ <hr />
+ <h2>Member Function Documentation</h2>
+ <h3>void MyObsoleteFunction ()</h3>
+ <p>Use MyNewFunction() instead.</p>
+ \endraw
+ ...
+ \endquotation
+
+ \target internal-command
+ \section1 \\internal
+
+ The \\internal command indicates that the referenced
+ function is not part of the public interface.
+
+ The command must stand on its own line.
+
+ QDoc ignores the documentation as well as the documented item,
+ when generating the associated class reference documentation.
+
+ \code
+ / *!
+ \internal
+
+ Tries to find the decimal separator. If it can't find
+ it and the thousand delimiter is != '.' it will try to
+ find a '.';
+ * /
+ int QDoubleSpinBoxPrivate::findDelimiter
+ (const QString &str, int index) const
+ {
+ int dotindex = str.indexOf(delimiter, index);
+ if (dotindex == -1 && thousand != dot && delimiter != dot)
+ dotindex = str.indexOf(dot, index);
+ return dotindex;
+ }
+ \endcode
+
+ This function will not be included in the documentation.
+
+ \target preliminary-command
+ \section1 \\preliminary
+
+ The \\preliminary command is for indicating that a referenced
+ function is still under development.
+
+ The command must stand on its own line.
+
+ The \\preliminary command expands to a notification in the
+ function documentation, and marks the function as preliminary when
+ it appears in lists.
+
+ \code
+ / *!
+ \preliminary
+
+ Returns information about the joining type attributes of the
+ character (needed for certain languages such as Arabic or
+ Syriac).
+
+ * /
+ QChar::JoiningType QChar::joiningType() const
+ {
+ return QChar::joiningType(ucs);
+ }
+ \endcode
+
+ QDoc renders this as:
+
+ \quotation
+ \raw HTML
+ <h3>
+ <a href="http://doc.qt.io/qt-5/qchar.html#JoiningType-enum">JoiningType</a>
+ QChar::joiningType() const</h3>
+ \endraw
+
+ \b {This function is under development and
+ subject to change.}
+
+ Returns information about the joining type attributes of the
+ character (needed for certain languages such as Arabic or
+ Syriac).
+ \endquotation
+
+ And the function's entry in QChar's list of public functions will be
+ rendered as:
+
+ \quotation
+ \list
+ \li ...
+ \li JoiningType \l {QChar::joiningType()} {joiningType}() const \c (preliminary)
+ \li ...
+ \endlist
+ \endquotation
+
+ \target since-command
+ \section1 \\since
+
+ The \\since command tells in which minor release
+ the associated functionality was added.
+
+ \code
+ / *!
+ \since 4.1
+
+ Returns an icon for \a standardIcon.
+
+ ...
+
+ \sa standardPixmap()
+ * /
+ QIcon QStyle::standardIcon(StandardPixmap standardIcon, const QStyleOption *option, const QWidget *widget) const
+ {
+ }
+ \endcode
+
+ QDoc renders this as:
+
+ \quotation
+ \raw HTML
+ <h3>QIcon QStyle::standardIcon(StandardPixmap standardIcon, const QStyleOption *option, const QWidget *widget) const</h3>
+ \endraw
+
+ This function was introduced in Qt version 4.1
+
+ Returns an icon for \a standardIcon.
+
+ ...
+
+ See also \l {QStyle::standardPixmap()} {standardPixmap()}.
+ \endquotation
+
+ QDoc generates the "Qt" reference from the \l
+ {25-qdoc-configuration-derivedprojects.html#project} {\c project}
+ configuration variable. For that reason this reference will change
+ according to the current documentation project.
+
+ See also \l {project}
+ {\c project}.
+*/
+
+
+/*!
+ \page 17-qdoc-commands-thread.html
+ \previouspage Status
+ \contentspage QDoc Manual
+ \nextpage Relating Things
+
+ \title Thread Support
+
+ The thread support commands are for specifying the level of
+ support for multithreaded programming in a class or function.
+ There are three levels of support: \c threadsafe, \c reentrant and
+ \c nonreentrant.
+
+ The default is \c nonreentrant which means that the associated
+ class or function cannot be called by multiple threads. \c
+ Reentrant and \c threadsafe are levels primarily used for classes.
+
+ \c Reentrant means that all the functions in the referenced class
+ can be called simultaneously by multiple threads, provided that
+ each invocation of the functions reference unique data. While \c
+ threadsafe means that all the functions in the referenced class
+ can be called simultaneously by multiple threads even when each
+ invocation references shared data.
+
+ When a class is marked \l {reentrant-command} {\\reentrant} or \l
+ {threadsafe-command} {\\threadsafe}, functions in that class can
+ be marked \c nonreentrant using the \l {nonreentrant-command}
+ {\\nonreentrant} command.
+
+ \section1 Example
+
+ \target reentrant-example
+ \code
+ \beginqdoc
+ \class QLocale
+ \brief The QLocale class converts between numbers and their
+ string representations in various languages.
+
+ \reentrant
+ \ingroup i18n
+ \ingroup text
+
+ QLocale is initialized with a language/country pair in its
+ constructor and offers number-to-string and string-to-number
+ conversion functions similar to those in QString.
+
+ ...
+
+ \nonreentrant
+
+ Sets the global default locale to \a locale. These values are
+ used when a QLocale object is constructed with no
+ arguments. If this function is not called, the system's locale
+ is used.
+
+ \warning In a multithreaded application, the default locale
+ should be set at application startup, before any non-GUI
+ threads are created.
+
+ \sa system(), c()
+ \endqdoc
+ void QLocale::setDefault(const QLocale &locale)
+ {
+ default_d = locale.d;
+ }
+ \endcode
+
+ QDoc renders this as:
+
+ \quotation
+ \raw HTML
+ <h1><center>QLocale Class Reference</center></h1>
+ \endraw
+
+ The QLocale class converts between numbers and their string
+ representations in various languages. More...
+
+ \code
+ #include <QLocale>
+ \endcode
+
+ \b {Note:} All the functions in this class are \l
+ {17-qdoc-commands-thread.html#reentrant} {reentrant}, except \l
+ {QLocale::setDefault()} {setDefault()}.
+
+ ...
+
+ \raw HTML
+ <hr />
+ <h2>Member Type Documentation</h2>
+ \endraw
+
+ ...
+
+ \raw HTML
+ <h3>void QLocale::setDefault ( const QLocale & locale ) </h3>
+ \endraw
+
+ Sets the global default locale to locale. These values are
+ used when a QLocale object is constructed with no
+ arguments. If this function is not called, the system's locale
+ is used.
+
+ \warning In a multithreaded application, the default locale
+ should be set at application startup, before any non-GUI
+ threads are created.
+
+ \warning This function is not reentrant.
+
+ See also \l {QLocale::system()} {system()} and \l
+ {QLocale::c()} {c()}.
+
+ ...
+ \endquotation
+
+ As shown above, QDoc generates a notification when a class is
+ declared reentrant, and lists the exceptions (the declared
+ nonreentrant functions). A link to the general documentation on \l
+ {17-qdoc-commands-thread.html#reentrant} {reentrancy and thread-safety} is
+ included. In addition a warning, "\b Warning: This function is
+ not reentrant.", is generated in the nonreentrant functions'
+ documentation.
+
+ QDoc will generate the same notification and warnings when a class
+ is declared threadsafe.
+
+ For more information see the general documentation on \l
+ {17-qdoc-commands-thread.html#reentrant} {reentrancy and thread-safety}.
+
+ \section1 Commands
+
+ \target threadsafe-command
+ \section2 \\threadsafe
+
+ The \\threadsafe command includes a line in the documentation to
+ indicate that the associated class or function is \e threadsafe
+ and can be called simultaneously by multiple threads, even when
+ separate invocations reference shared data.
+
+ The command must stand on its own line.
+
+ The documentation generated from this command will be similar to
+ the what is generated for the \l {reentrant-command} {\\reentrant}
+ command. See the example above in the \l {reentrant-example}
+ {introduction}.
+
+ See also \l{reentrant-command} {\\reentrant} and
+ \l{nonreentrant-command} {\\nonreentrant}.
+
+ \target reentrant-command
+ \section2 \\reentrant
+
+ The \\reentrant command indicates that the associated class or
+ function can be called simultaneously by multiple threads,
+ provided that each invocation references its own data. See the \l
+ {reentrant-example} {example} above.
+
+ The command must stand on its own line.
+
+ See also \l{nonreentrant-command} {\\nonreentrant} and
+ \l{threadsafe-command} {\\threadsafe}.
+
+ \target nonreentrant-command
+ \section2 \\nonreentrant
+
+ The \\nonreentrant command indicates that the associated class or
+ function cannot be called by multiple threads. Nonreentrant is the
+ default case.
+
+ The command must stand on its own line.
+
+ When a class is marked \l {reentrant-command} {\\reentrant} or \l
+ {threadsafe-command} {\\threadsafe}, functions in that class can
+ be marked \c nonreentrant using this command in the \l{fn-command}
+ {\\fn} comment of the functions to be excluded.
+
+ See also \l{reentrant-command} {\\reentrant} and
+ \l{threadsafe-command} {\\threadsafe}.
+*/
+
+/*!
+ \page 18-qdoc-commands-relating.html
+ \previouspage Thread Support
+ \contentspage QDoc Manual
+ \nextpage Grouping Things
+
+ \title Relating Things
+
+ The relating commands are for specifying how one documented
+ element relates to another documented element. Some examples:
+ \list
+ \li This function is an overload of another function.
+ \li This function is a reimplementation of another function.
+ \li This typedef is \e related to some class or header file.
+ \endlist
+
+ There is also a command for documenting that a QML type inherits
+ some other QML type.
+
+ \section1 Commands
+
+ \target inherits-command
+ \section2 \\inherits
+
+ The \\inherits command is for documenting that one QML type
+ inherits some other QML type. It must be included in the
+ inheriting element's \l{qmltype-command}{\\qmltype} comment.
+ The argument is the name of the inherited QML type.
+
+ \code
+ / *!
+ \qmltype PauseAnimation
+ \instantiates QDeclarativePauseAnimation
+ \ingroup qml-animation-transition
+ \since 4.7
+ \inherits Animation
+ \brief The PauseAnimation element provides a pause for an animation.
+
+ When used in a SequentialAnimation, PauseAnimation is a step
+ when nothing happens, for a specified duration.
+
+ A 500ms animation sequence, with a 100ms pause between two animations:
+
+ SequentialAnimation {
+ NumberAnimation { ... duration: 200 }
+ PauseAnimation { duration: 100 }
+ NumberAnimation { ... duration: 200 }
+ }
+
+ \sa {QML Animation and Transitions}, {declarative/animation/basics}{Animation basics example}
+ * /
+ \endcode
+
+ QDoc includes this line on the reference page for the
+ \l{http://qt-project.org/doc/qt-4.7/qml-pauseanimation.html} {PauseAnimation}
+ element:
+
+ \quotation
+ Inherits \l{http://qt-project.org/doc/qt-4.7/qml-animation.html} {Animation}
+ \endquotation
+
+ \target overload-command
+ \section2 \\overload
+
+ The \\overload command is for indicating that a function is a
+ secondary overload of its name.
+
+ The command must stand on its own line.
+
+ For a function name that is overloaded (except constructors), QDoc
+ expects one primary version of the function, and all the others
+ marked with the \b {\\overload command}. The primary version
+ should be fully documented. Each overload can have whatever extra
+ documentation you want to add for just that overloaded version.
+
+ From Qt 4.5, you can include the function name plus '()' as a
+ parameter to the \b{\\overload} command, which will include a
+ standard \e{This function overloads...} line of text with a link
+ to the documentation for the primary version of the function.
+
+ \code
+ / *!
+ \overload addAction()
+
+ This convenience function creates a new action with an
+ \a icon and some \a text. The function adds the newly
+ created action to the menu's list of actions, and
+ returns it.
+
+ \sa QWidget::addAction()
+ * /
+ QAction *QMenu::addAction(const QIcon &icon, const QString &text)
+ {
+ QAction *ret = new QAction(icon, text, this);
+ addAction(ret);
+ return ret;
+ }
+ \endcode
+
+ QDoc renders this as:
+
+ \quotation
+ \raw HTML
+ <h3><a href="http://doc.qt.io/qt-5/qaction.html">QAction</a>
+ * QMenu::addAction ( const QIcon & <i>icon</i>,
+ const QString & <i>text</i> )
+ </h3>
+ \endraw
+
+ This function overloads \l {QMenu::addAction()} {addAction()}
+
+ This convenience function creates a new action with an
+ \e icon and some \e text. The function adds the newly
+ created action to the menu's list of actions, and
+ returns it.
+
+ See also
+ \l {QWidget::addAction()} {QWidget::addAction}().
+ \endquotation
+
+ If you don't include the function name with the \b{\\overload}
+ command, then instead of the "This function overloads..." line
+ with the link to the documentation for the primary version, you
+ get the old standard line:
+
+ \quotation
+ This is an overloaded member function, provided for
+ convenience.
+ \endquotation.
+
+ \target reimp-command
+ \section2 \\reimp
+
+ The \\reimp command is for indicating that a function is a
+ reimplementation of a virtual function.
+
+ The command must stand on its own line.
+
+ QDoc will omit the reimplemented function from the class
+ reference.
+
+ \code
+ / *!
+ \reimp
+ * /
+ void QToolButton::nextCheckState()
+ {
+ Q_D(QToolButton);
+ if (!d->defaultAction)
+ QAbstractButton::nextCheckState();
+ else
+ d->defaultAction->trigger();
+ }
+ \endcode
+
+ This function will not be included in the documentation. Instead,
+ a link to the base function QAbstractButton::nextCheckState() will
+ appear in the documentation.
+
+ \target relates-command
+ \section2 \\relates
+
+ The \\relates command is for including the documentation of a
+ global element to some class or header file. The argument is a
+ class name or header file.
+
+ \code
+ / *!
+ \relates QChar
+
+ Reads a char from the stream \a in into char \a chr.
+
+ \sa {Format of the QDataStream operators}
+ * /
+ QDataStream &operator>>(QDataStream &in, QChar &chr)
+ {
+ quint16 u;
+ in >> u;
+ chr.unicode() = ushort(u);
+ return in;
+ }
+ \endcode
+
+ The documentation for this function will be included on the reference page
+ for class QChar.
+*/
+
+/*!
+ \page 19-qdoc-commands-grouping.html
+ \previouspage Relating Things
+ \contentspage QDoc Manual
+ \nextpage Naming Things
+
+ \title Grouping Things
+
+ The grouping commands relate classes to defined groups and
+ modules. The groups are used when generating lists of related
+ classes in the documentation, while the modules are elements of
+ Qt's structure.
+
+ \section1 Commands
+
+ \target ingroup-command
+ \section2 \\ingroup
+
+ The \\ingroup command indicates that the given
+ overview or documented class belongs to a certain group of
+ related docmentation.
+
+ A class or overview may belong to many groups.
+
+ The \\ingroup command's argument is a group name, but note
+ that the command considers the rest of the line as part of
+ its argument. Make sure that the group name is followed by
+ a linebreak.
+
+ \code
+ / *!
+ \class QDir
+ \brief The QDir class provides access to directory
+ structures and their contents.
+
+ \ingroup io
+ ...
+ * /
+ \endcode
+
+ This will include the QDir class in the \c io group, which means,
+ for example, that QDir will appear on the list created by calling
+ the \l {group-command} {\\group} command with the \c io argument.
+
+ To list overviews that are related to a certain group, you must
+ generate the list explicitly using the \l {generatelist-command}
+ {\\generatelist} command with the \c related argument.
+
+ See also \l {group-command} {\\group}.
+
+ \target inmodule-command
+ \section2 \\inmodule
+
+ The \\inmodule command relates a class to the module specified by
+ the command's argument.
+
+ For the basic classes in Qt, a class's module is determined by its
+ location, namely its directory. However, for extensions like
+ ActiveQt and Qt Designer, a class must be related to a module
+ explicitly.
+
+ The command's argument is a module name, but note that the command
+ considers the rest of the line as part of its argument. Make sure
+ that the module name is followed by a linebreak.
+
+ \code
+ /*!
+ \class QDesignerTaskMenuExtension
+ \inmodule QtDesigner
+ * /
+ \endcode
+
+ This ensures that the QDesignerTaskMenuExtension class is included
+ in the Qt Designer module, which means, for example, that the
+ class will appear on the list created by calling the \l
+ {generatelist-command} {\\generatelist} command with the \c
+ {{classesbymodule QtDesigner}} argument.
+
+ See also \l {module-command} {\\module} and \l
+ {generatelist-command} {\\generatelist}.
+*/
+
+/*!
+ \page 20-qdoc-commands-namingthings.html
+ \previouspage Grouping Things
+ \contentspage QDoc Manual
+ \nextpage Markup Commands
+
+ \title Naming Things
+
+ In general, a title command considers everything that follows it
+ until the first line break as its argument. If the title is so
+ long it must span multiple lines, end each line (except the last
+ one) with a backslash.
+
+ \section1 Commands
+
+ \target title-command
+ \section2 \\title
+
+ The \\title command sets the title for a documentation page, or
+ allows you to override it.
+
+ \code
+ / *!
+ \page signalandslots.html
+
+ \title Signals & Slots
+
+ Signals and slots are used for communication between
+ objects. The signals and slots mechanism is a central
+ feature of Qt, and probably the part that differs most
+ from the features provided by other frameworks.
+
+ ...
+ * /
+ \endcode
+
+ QDoc renders this as:
+
+ \quotation
+ \raw HTML
+ <h1><center>Signal and Slots</center></h1>
+ \endraw
+
+ Signals and slots are used for communication between
+ objects. The signals and slots mechanism is a central
+ feature of Qt and probably the part that differs most
+ from the features provided by other frameworks.
+ ...
+ \endquotation
+ See also \l {subtitle-command} {\\subtitle}.
+
+ \target subtitle-command
+ \section2 \\subtitle
+
+ The \\subtitle command sets a subtitle for a documentation page.
+
+ \code
+ \beginqdoc
+ \page qtopiacore-overview.html
+
+ \title Qtopia Core
+ \subtitle Qt for Embedded Linux
+
+ Qt/Embedded, the embedded Linux port of Qt, is a
+ complete and self-contained C++ GUI and platform
+ development tool for Linux-based embedded development.
+ ...
+ \endqdoc
+ \endcode
+
+ QDoc renders this as:
+
+ \quotation
+ \raw HTML
+ <h1><center>Qtopia Core</center></h1>
+ <h2><center>Qt for Embedded Linux</center></h2>
+ \endraw
+
+ Qt/Embedded, the embedded Linux port of Qt, is a
+ complete and self-contained C++ GUI and platform
+ development tool for Linux-based embedded development.
+ ...
+ \endquotation
+
+ See also \l {title-command} {\\title}.
+
+*/
diff --git a/src/qdoc/doc/qdoc-manual-intro.qdoc b/src/qdoc/doc/qdoc-manual-intro.qdoc
new file mode 100644
index 000000000..84f941684
--- /dev/null
+++ b/src/qdoc/doc/qdoc-manual-intro.qdoc
@@ -0,0 +1,325 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the documentation of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:FDL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Free Documentation License Usage
+** Alternatively, this file may be used under the terms of the GNU Free
+** Documentation License version 1.3 as published by the Free Software
+** Foundation and appearing in the file included in the packaging of
+** this file. Please review the following information to ensure
+** the GNU Free Documentation License version 1.3 requirements
+** will be met: http://www.gnu.org/copyleft/fdl.html.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*!
+ \page 01-qdoc-manual.html
+ \contentspage QDoc Manual
+ \previouspage QDoc Manual
+ \nextpage Command Index
+
+ \title Introduction to QDoc
+
+ QDoc is a tool used by Qt Developers to generate documentation for
+ software projects. It works by extracting \e {QDoc comments} from
+ project source files and then formatting these comments as HTML
+ pages or DITA XML documents. QDoc finds QDoc comments in \c
+ {.cpp} files and in \c {.qdoc} files. QDoc does not look for QDoc
+ comments in \c {.h} files. A QDoc comment always begins with an
+ exclamation mark (\b{!})). For example:
+
+ \code
+ / *!
+ \class QObject
+ \brief The QObject class is the base class of all Qt objects.
+
+ \ingroup objectmodel
+
+ \reentrant
+
+ QObject is the heart of the Qt \l{Object Model}. The
+ central feature in this model is a very powerful mechanism
+ for seamless object communication called \l{signals and
+ slots}. You can connect a signal to a slot with connect()
+ and destroy the connection with disconnect(). To avoid
+ never ending notification loops you can temporarily block
+ signals with blockSignals(). The protected functions
+ connectNotify() and disconnectNotify() make it possible to
+ track connections.
+
+ QObjects organize themselves in \l {Object Trees &
+ Ownership} {object trees}. When you create a QObject with
+ another object as parent, the object will automatically
+ add itself to the parent's \c children() list. The parent
+ takes ownership of the object. It will automatically
+ delete its children in its destructor. You can look for an
+ object by name and optionally type using findChild() or
+ findChildren().
+
+ Every object has an objectName() and its class name can be
+ found via the corresponding metaObject() (see
+ QMetaObject::className()). You can determine whether the
+ object's class inherits another class in the QObject
+ inheritance hierarchy by using the \c inherits() function.
+
+ ....
+ * /
+ \endcode
+
+ From the QDoc comment above, QDoc generates the HTML \l {QObject}
+ {QObject class reference} page.
+
+ This manual explains how to use the QDoc commands in QDoc comments
+ to embed good documentation in your source files. It also explains
+ how to make a \l {The QDoc Configuration File} {QDoc configuration
+ file}, which you will pass to QDoc on the command line.
+
+ \section1 Running QDoc
+
+ The name of the QDoc program is \c {qdoc}. To run qdoc from the
+ command line, give it the name of a configuration file:
+
+ \quotation
+ \c {$ ../../bin/qdoc ./config.qdocconf}
+ \endquotation
+
+ QDoc recognizes the \c {.qdocconf} suffix as a \l{The QDoc
+ Configuration File} {QDoc configuration file}. The configuration
+ file is where you tell QDoc where to find the project source
+ files, header files, and \c {.qdoc} files. It is also where you
+ tell QDoc what kind of output to generate (HTML, DITA XML,...),
+ and where to put the generated documentation. The configuration
+ file also contains other information for QDoc.
+
+ See \l{The QDoc Configuration File} for instructions on how to
+ set up a QDoc configuration file.
+
+ \section2 Running QDoc in Single Execution Mode
+
+ Beginning with Qt 5.5, a new way to run QDoc is available that
+ reduces the time it takes to generate the Qt5 documentation by as
+ much as 90%. The new way to run QDoc is \e{single execution} mode.
+ Single execution mode is not currently available in the Qt5 build
+ system, which still uses the \e {standard} mode. Single execution
+ mode is only available when you run QDoc yourself, which you will
+ want to do often as you document your module and integrate your
+ documentation with the other Qt modules.
+
+ To run QDoc in single execution mode, add \c {-single-exec} to the
+ command line and pass QDoc a master \c qdocconf file that is
+ simply a list of file paths for qdocconf files of all the Qt5
+ modules. For example:
+
+ \code
+ /Users/me/qt5/qtbase/bin/qdoc -outputdir /Users/me/qt5/qtbase/doc -installdir /Users/me/qt5/qtbase/doc /Users/me/qt5/master.qdocconf -single-exec
+ \endcode
+
+ The qdocconf file, \c {master.qdocconf}, just lists the qdocconf files for all the Qt5 modules to be processed:
+
+ \badcode
+ /Users/me/qt5/qtbase/src/corelib/doc/qtcore.qdocconf
+ /Users/me/qt5/qtbase/src/network/doc/qtnetwork.qdocconf
+ /Users/me/qt5/qtbase/src/sql/doc/qtsql.qdocconf
+ /Users/me/qt5/qtbase/src/xml/doc/qtxml.qdocconf
+ /Users/me/qt5/qtbase/src/testlib/doc/qttestlib.qdocconf
+ /Users/me/qt5/qtbase/src/concurrent/doc/qtconcurrent.qdocconf
+ /Users/me/qt5/qtbase/src/gui/doc/qtgui.qdocconf
+ /Users/me/qt5/qtbase/src/platformheaders/doc/qtplatformheaders.qdocconf
+ /Users/me/qt5/qtbase/src/widgets/doc/qtwidgets.qdocconf
+ /Users/me/qt5/qtbase/src/opengl/doc/qtopengl.qdocconf
+ /Users/me/qt5/qtbase/src/printsupport/doc/qtprintsupport.qdocconf
+ /Users/me/qt5/qtbase/src/tools/qdoc/doc/config/qdoc.qdocconf
+ /Users/me/qt5/qtbase/qmake/doc/qmake.qdocconf
+ /Users/me/qt5/qtsvg/src/svg/doc/qtsvg.qdocconf
+ /Users/me/qt5/qtxmlpatterns/src/xmlpatterns/doc/qtxmlpatterns.qdocconf
+ /Users/me/qt5/qtdeclarative/src/qml/doc/qtqml.qdocconf
+ /Users/me/qt5/qtdeclarative/src/quick/doc/qtquick.qdocconf
+ /Users/me/qt5/qtquickcontrols/src/controls/doc/qtquickcontrols.qdocconf
+ /Users/me/qt5/qtquickcontrols/src/layouts/doc/qtquicklayouts.qdocconf
+ /Users/me/qt5/qtquickcontrols/src/dialogs/doc/qtquickdialogs.qdocconf
+ /Users/me/qt5/qtmultimedia/src/multimedia/doc/qtmultimedia.qdocconf
+ /Users/me/qt5/qtmultimedia/src/multimediawidgets/doc/qtmultimediawidgets.qdocconf
+ /Users/me/qt5/qtactiveqt/src/activeqt/doc/activeqt.qdocconf
+ /Users/me/qt5/qtsensors/src/sensors/doc/qtsensors.qdocconf
+ /Users/me/qt5/qtwebkit/Source/qtwebkit.qdocconf
+ /Users/me/qt5/qttools/src/assistant/help/doc/qthelp.qdocconf
+ /Users/me/qt5/qttools/src/assistant/assistant/doc/qtassistant.qdocconf
+ /Users/me/qt5/qttools/src/designer/src/uitools/doc/qtuitools.qdocconf
+ /Users/me/qt5/qttools/src/designer/src/designer/doc/qtdesigner.qdocconf
+ /Users/me/qt5/qttools/src/linguist/linguist/doc/qtlinguist.qdocconf
+ /Users/me/qt5/qtwebkit-examples/doc/qtwebkitexamples.qdocconf
+ /Users/me/qt5/qtimageformats/src/imageformats/doc/qtimageformats.qdocconf
+ /Users/me/qt5/qtgraphicaleffects/src/effects/doc/qtgraphicaleffects.qdocconf
+ /Users/me/qt5/qtscript/src/script/doc/qtscript.qdocconf
+ /Users/me/qt5/qtscript/src/scripttools/doc/qtscripttools.qdocconf
+ /Users/me/qt5/qtserialport/src/serialport/doc/qtserialport.qdocconf
+ /Users/me/qt5/qtdoc/doc/config/qtdoc.qdocconf
+ \endcode
+
+ \section3 Why Standard Mode Is Slow
+
+ Currently, the Qt5 build system does not use QDoc's \e {single
+ execution} mode for generating the Qt5 documentation. It runs QDoc
+ in the \e {standard} mode. The standard mode was came about
+ because it was the easiest way to get convert the Qt4 QDoc to
+ handle the modularization of Qt in Qt5. In Qt4, QDoc ran once over
+ all the Qt4 sources to generate the HTML documentation for Qt.
+ While generating the Qt documentation, Qt4 QDoc also generated an
+ \e {index file} for Qt. That index file was meant to be used as
+ input to subsequent QDoc runs for generating HTML documentation
+ for other software libraries/products that were based on Qt. The
+ Qt index file allowed QDoc to link documentation written for those
+ other libraries/products to the Qt4 documentation.
+
+ When Qt5 came along, Qt was divided into modules. Since then,
+ many new modules have been added to Qt. As of version 5.5, there
+ are over 40 separate modules in Qt5, each with its own
+ documentation that links to (depends on) the documentation of
+ other Qt modules.
+
+ In \e {standard mode}, QDoc runs twice for each module. The first
+ QDoc run for a particular Qt module, parses all the module's
+ source files and then uses the information to generate the
+ module's index file. It is called the \e{prepare phase} because
+ it \e prepares the module's index file. The second QDoc run for
+ the module also parses all the module's source files and then
+ generates the module's documentation pages. This is called the \e
+ {generate phase} because it generates the module's documentation.
+
+ The module's documentation will likely contain HTML links to the
+ documentation of one or more of the other Qt modules. For example,
+ most Qt5 modules contain links to documentation in QtCore. When a
+ Qt module contains links into the documentation of other Qt
+ module's, that module is said to depend on those other Qt modules.
+ Hence when QDoc runs the \e {generate phase} for that module, it
+ must also load the index files for those modules so it can create
+ those thinks.
+
+ Hence, when the Qt build system generates the Qt documentation, it
+ first runs QDoc once for each module to perform the \e {prepare
+ phase} to generate all the index files. Then it runs QDoc once for
+ each module to perform the \e {generate phase}, where it uses the
+ dependent index files to generate the module's documentation,
+ including any cross-module links it finds. Each execution of
+ QDoc, both \e {prepare phase} and \e {generate phase}, parses
+ all the source files that are included in the module, and in the
+ \e {generate phase} also parses the index files for the dependent
+ modules. Nothing is retained or retainable between QDoc runs.
+
+ \section3 Why Single Execution Mode Is Much Faster
+
+ As the name implies, single execution mode uses a single QDoc
+ process to generate all the Qt5 documentation. The single QDoc
+ process still performs a \e{prepare phase} for each module and
+ then a \e{generate phase} for each module, but there are a few
+ differences. It begins by reading the master qdocconf file. Then
+ it reads each qdocconf file in the master list and performs the
+ \e{prepare phase} for each module. During the \e{prepare phase},
+ all the source files for the module are parsed to build a syntax
+ tree for the module. The module's index file is then generated,
+ although QDoc will not re-read the index files in the \e{generate
+ phase}. The important difference here is that the module's syntax
+ tree is retained after the index file is generated, so that after
+ the \e{prepare phase} has been run for all the modules, QDoc still
+ has all the syntax trees it built.
+
+ QDoc then processes each module again for the \e{generate phase}.
+ But now QDoc doesn't need to re-parse each module's source files,
+ because the module's syntax tree is still in memory. Nor does QDoc
+ need to re-read the index files for the dependent modules, again
+ because it still has the syntax trees for those modules in memry.
+ It remains only to traverse each module's syntax tree to generate
+ the documentation pages.
+
+ Hence, QDoc parses each source file once and only once and doesn't
+ need to read index files. This is what makes single execution mode
+ much faster than the standard mode. It is anticipated that the Qt
+ build system will eventually run QDoc in single execution mode.
+ However, changes to the master qdocconf file might be required, so
+ the method described above for running QDoc in single execution
+ mode might have to change, watch this space for updates.
+
+ \section1 How QDoc Works
+
+ QDoc begins by reading the configuration file you specified on the
+ command line. It stores all the variables from the configuration
+ file for later use. One of the first variables it uses is \c
+ {outputformats}. This variable tells QDoc which output generators
+ it will run. The default value is \e {HTML}, so if you don't set
+ \c {outputformats} in your configuration file, QDoc will generate
+ HTML output. That's usually what you will want anyway, but you can
+ also specify \e {DITAXML} to get DITA XML output instead.
+
+ Next, QDoc uses the values of the
+ \l {headerdirs-variable}
+ {headerdirs} variable and/or the \l
+ {22-qdoc-configuration-generalvariables.html#headers-variable}
+ {headers} variable to find and parse all the header files for your
+ project. QDoc does \e not scan header files for QDoc comments. It
+ parses the header files to build a master tree of all the items
+ that should be documented, in other words, the items that QDoc should find
+ QDoc comments for.
+
+ After parsing all the header files and building the master tree of
+ items to be documented, QDoc uses the value of the \l
+ {22-qdoc-configuration-generalvariables.html#sourcedirs-variable}
+ {sourcedirs} variable and/or the value of the \l
+ {22-qdoc-configuration-generalvariables.html#sources-variable}
+ {sources} variable to find and parse all the \c {.cpp} and \c
+ {.qdoc} files for your project. These are the files QDoc scans for
+ \e {QDoc comments}. Remember that a QDoc comment begins with
+ an exclamation mark: \b {/*!} .
+
+ For each QDoc comment it finds, it searches the master tree for
+ the item where the documentation belongs. Then it interprets the
+ qdoc commands in the comment and stores the interpreted commands
+ and the comment text in the tree node for the item.
+
+ Finally, QDoc traverses the master tree. For each node, if the
+ node has stored documentation, QDoc calls the output generator
+ specified by the \c {outputformats} variable to format and write
+ the documentation in the directory specified in the configuration
+ file in the \l
+ {22-qdoc-configuration-generalvariables.html#outputdir-variable}
+ {outputdir} variable.
+
+ \section1 Command Types
+
+ QDoc interprets three types of commands:
+
+ \list
+ \li \l {Topic Commands}
+ \li \l {Context Commands}
+ \li \l {Markup Commands}
+ \endlist
+
+ Topic commands identify the element you are documenting, for example
+ a C++ class, function, type, or an extra page of text
+ that doesn't map to an underlying C++ element.
+
+ Context commands tell QDoc how the element being documented
+ relates to other documented elements, for example, next and previous page
+ links, inclusion in page groups, or library modules. Context
+ commands can also provide information about the documented element
+ that QDoc can't get from the source files, for example, whether the
+ element is thread-safe, whether it is an overloaded or reimplemented function,
+ or whether it has been deprecated.
+
+ Markup commands tell QDoc how text and image elements in the
+ document should be rendered, or about the document's outline
+ structure.
+*/
+
diff --git a/src/qdoc/doc/qdoc-manual-markupcmds.qdoc b/src/qdoc/doc/qdoc-manual-markupcmds.qdoc
new file mode 100644
index 000000000..49cbfc065
--- /dev/null
+++ b/src/qdoc/doc/qdoc-manual-markupcmds.qdoc
@@ -0,0 +1,4081 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the documentation of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:FDL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Free Documentation License Usage
+** Alternatively, this file may be used under the terms of the GNU Free
+** Documentation License version 1.3 as published by the Free Software
+** Foundation and appearing in the file included in the packaging of
+** this file. Please review the following information to ensure
+** the GNU Free Documentation License version 1.3 requirements
+** will be met: http://www.gnu.org/copyleft/fdl.html.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*!
+ \page 03-qdoc-commands-markup.html
+ \contentspage QDoc Manual
+ \previouspage Naming Things
+ \nextpage Text Markup
+
+ \title Markup Commands
+
+ The markup commands indicate the generated documentation's visual
+ appearance and logical structure.
+
+ \list
+ \li \l {a-command} {\\a}
+ \li \l {annotatedlist-command} {\\annotatedlist}
+ \li \l {b-command} {\\b} \span {class="newStuff"}
+ \li \l {b-command} {\\bold} {(deprecated, use \\b)}
+ \li \l {brief-command} {\\brief}
+ \li \l {c-command} {\\c}
+ \li \l {caption-command} {\\caption}
+ \li \l {chapter-command} {\\chapter}
+ \li \l {code-command} {\\code}
+ \li \l {codeline-command} {\\codeline}
+ \li \l {div-command} {\\div}
+ \li \l {dots-command} {\\dots}
+ \li \l {e-command} {\\e} \span {class="newStuff"}
+ \li \l {else-command} {\\else}
+ \li \l {endif-command} {\\endif}
+ \li \l {footnote-command} {\\footnote}
+ \li \l {generatelist-command} {\\generatelist}
+ \li \l {header-command} {\\header}
+ \li \l {e-command} {\\i} \span {class="newStuff"} {(deprecated, use \\e)}
+ \li \l {if-command} {\\if}
+ \li \l {image-command} {\\image}
+ \li \l {include-command} {\\include}
+ \li \l {include-command} {\\input}
+ \li \l {inlineimage-command} {\\inlineimage}
+ \li \l {keyword-command} {\\keyword}
+ \li \l {l-command} {\\l}
+ \li \l {legalese-command} {\\legalese}
+ \li \l {li-command} {\\li} \span {class="newStuff"}
+ \li \l {list-command} {\\list}
+ \li \l {meta-command} {\\meta}
+ \li \l {noautolist-command} {\\noautolist}
+ \li \l {newcode-command} {\\newcode}
+ \li \l {li-command} {\\o} \span {class="newStuff"} {(deprecated, use \\li)}
+ \li \l {note-command} {\\note}
+ \li \l {oldcode-command} {\\oldcode}
+ \li \l {omit-command} {\\omit}
+ \li \l {part-command} {\\part}
+ \li \l {printline-command} {\\printline}
+ \li \l {printto-command} {\\printto}
+ \li \l {printuntil-command} {\\printuntil}
+ \li \l {quotation-command} {\\quotation}
+ \li \l {quotefile-command} {\\quotefile}
+ \li \l {quotefromfile-command} {\\quotefromfile}
+ \li \l {raw-command} {\\raw}
+ \li \l {row-command} {\\row}
+ \li \l {sa-command} {\\sa}
+ \li \l {sectionOne-command} {\\section1}
+ \li \l {sectionTwo-command} {\\section2}
+ \li \l {sectionThree-command} {\\section3}
+ \li \l {sectionFour-command} {\\section4}
+ \li \l {skipline-command} {\\skipline}
+ \li \l {skipto-command} {\\skipto}
+ \li \l {skipuntil-command} {\\skipuntil}
+ \li \l {snippet-command} {\\snippet}
+ \li \l {span-command} {\\span}
+ \li \l {sub-command} {\\sub}
+ \li \l {sup-command} {\\sup}
+ \li \l {table-command} {\\table}
+ \li \l {tableofcontents-command} {\\tableofcontents}
+ \li \l {target-command} {\\target}
+ \li \l {tt-command} {\\tt}
+ \li \l {uicontrol-command} {\\uicontrol} {(new 25/3/2012)}
+ \li \l {underline-command} {\\underline}
+ \li \l {raw-command} {\\unicode}
+ \li \l {warning-command} {\\warning}
+ \li \l {backslash-command} {\\\\}
+ \endlist
+*/
+
+
+/*!
+ \page 04-qdoc-commands-textmarkup.html
+ \contentspage QDoc Manual
+ \previouspage Markup Commands
+ \nextpage Document Structure
+
+ \title Text Markup
+
+ The text formatting commands indicate how text is to be rendered.
+
+ \target a-command
+ \section1 \\a (parameter marker)
+
+ The \\a command tells QDoc the next word is a formal parameter name.
+
+ A warning is emitted when a formal parameter is not documented or
+ is misspelled, so when you document a function you should mention
+ each formal parameter by name in the function description,
+ preceded by the \\a command. The parameter name is then rendered
+ in italics.
+
+ \code
+ / *!
+ Constructs a line edit containing the text
+ \a contents. The \a parent parameter is sent
+ to the QWidget constructor.
+ * /
+
+ QLineEdit::QLineEdit(const QString &contents, QWidget *parent) :QWidget(parent)
+ {
+ ...
+ }
+
+ \endcode
+
+ QDoc renders this as:
+
+ \quotation
+ \b {QLineEdit::QLineEdit ( const QString &
+ contents, QWidget *parent )}
+
+ Constructs a line edit containing the text \a contents.
+ The \a parent parameter is sent to the QWidget constructor.
+ \endquotation
+
+ The formal parameter name may be enclosed between curly brackets,
+ but that isn't required.
+
+ \target c-command
+ \section1 \\c (code font)
+
+ The \\c command is used for rendering variable names, user-defined
+ class names, and C++ keywords (for example, \c int and \c for) in the code
+ font.
+
+ The command renders its argument using a monospace font. For
+ example:
+
+ \code
+ / *!
+ The \c AnalogClock class provides a clock widget with hour
+ and minute hands that is automatically updated every
+ few seconds.
+ * /
+ \endcode
+
+ QDoc renders this as:
+
+ \quotation
+ The \c AnalogClock class provides a clock widget with hour
+ and minute hands, which are automatically updated every
+ few seconds.
+ \endquotation
+
+ If the text to be rendered in the code font contains spaces, enclose the
+ entire text in curly brackets.
+
+ \code
+ \c {QLineEdit::QLineEdit(const QString &contents, QWidget *parent) :QWidget(parent)}
+ \endcode
+
+ QDoc renders this as:
+
+ \quotation
+ \c {QLineEdit::QLineEdit(const QString &contents, QWidget *parent) :QWidget(parent)}
+ \endquotation
+
+ The \\c command accepts the special character \c \ within its
+ argument, which renders it as a normal character. So if you want
+ to use nested commands, you must use the \l {tt-command} {teletype
+ (\\tt)} command instead.
+
+ See also \l {tt-command} {\\tt} and \l {code-command} {\\code}.
+
+ \target div-command
+ \section1 \\div
+
+ The \\div and \\enddiv commands delimit a large or small block of
+ text (which may include other QDoc commands) to which special
+ formatting attributes should be applied.
+
+ An argument must be provided in curly braces, as in the qdoc
+ comment shown below. The argument is not interpreted but is used
+ as attribute(s) of the tag that is output by qdoc.
+
+ For example, we might want to render an inline image so that it
+ floats to the right of the current block of text:
+
+ \code
+ / *!
+ \div {class="float-right"}
+ \inlineimage qml-column.png
+ \enddiv
+
+ * /
+ \endcode
+
+ If qdoc is generating HTML, it will translate these commands to:
+
+ \code
+ <div class="float-right"><p><img src="images/qml-column.png" /></p></div>
+ \endcode
+
+ For HTML, the attribute value \e {float-right} then will refer to
+ a clause in the style.css file, which in this case could be:
+
+ \code
+ div.float-right
+ {
+ float: right; margin-left: 2em
+ }
+ \endcode
+
+ If qdoc is generating DITA XML, it will translate the commands to:
+
+ \code
+ <sectiondiv outputclass="float-right">
+ <p>
+ <fig>
+ <image href="images/qml-column.png" placement="inline"/>
+ </fig>
+ </p>
+ </sectiondiv>
+ \endcode
+
+ Your DITA XML publishing program must then recognize the \e
+ {outputclass} attribute value.
+
+ \note Note that the \b {\\div} command can be nested.
+
+ Below you can find an example taken from the index.qdoc file used to
+ generate index.html for Qt 4.7:
+
+ \code
+ \div {class="indexbox guide"}
+ \div {class="heading"}
+ Qt Developer Guide
+ \enddiv
+ \div {class="indexboxcont indexboxbar"}
+ \div {class="section indexIcon"} \emptyspan
+ \enddiv
+ \div {class="section"}
+ Qt is a cross-platform application and UI
+ framework. Using Qt, you can write web-enabled
+ applications once and deploy them across desktop,
+ mobile and embedded operating systems without
+ rewriting the source code.
+ \enddiv
+ \div {class="section sectionlist"}
+ \list
+ \li \l{Getting Started}
+ \li \l{Installation} {Installation}
+ \li \l{how-to-learn-qt.html} {How to learn Qt}
+ \li \l{tutorials.html} {Tutorials}
+ \li \l{Qt Examples} {Examples}
+ \li \l{qt4-7-intro.html} {What's new in Qt 4.7}
+ \endlist
+ \enddiv
+ \enddiv
+ \enddiv
+ \endcode
+
+ When all the class attribute values are defined as they are in the
+ style.css file that is used for rendering the Qt documentation,
+ the above example is rendered as:
+
+ \div {class="indexbox guide"}
+ \div {class="heading"}
+ Qt Developer Guide
+ \enddiv
+ \div {class="indexboxcont indexboxbar"}
+ \div {class="section indexIcon"} \emptyspan
+ \enddiv
+ \div {class="section"}
+ Qt is a cross-platform application and UI
+ framework. Using Qt, you can write web-enabled
+ applications once and deploy them across desktop,
+ mobile and embedded operating systems without
+ rewriting the source code.
+ \enddiv
+ \div {class="section sectionlist"}
+ \list
+ \li Getting Started
+ \li Installation
+ \li How to learn Qt
+ \li Tutorials
+ \li Examples
+ \li What's new in Qt 4.7
+ \endlist
+ \enddiv
+ \enddiv
+ \enddiv
+
+ When generating DITA XML, qdoc outputs the nested \e {div} commands as:
+
+ \code
+ <sectiondiv outputclass="indexbox guide">
+ <sectiondiv outputclass="heading">
+ <p>Qt Developer Guide</p>
+ </sectiondiv>
+ <sectiondiv outputclass="indexboxcont indexboxbar">
+ <sectiondiv outputclass="section indexIcon"/>
+ <sectiondiv outputclass="section">
+ <p>Qt is a cross-platform application and UI
+ framework. Using Qt, you can write
+ web-enabled applications once and deploy
+ them across desktop, mobile and embedded
+ operating systems without rewriting the
+ source code.
+ </p>
+ </sectiondiv>
+ <sectiondiv outputclass="section sectionlist">
+ <ul>
+ <li>
+ <xref href="gettingstarted.xml#id-606ee7a8-219b-47b7-8f94-91bc8c76e54c">Getting started</xref>
+ </li>
+ <li>
+ <xref href="installation.xml#id-075c20e2-aa1e-4f88-a316-a46517e50443">Installation</xref>
+ </li>
+ <li>
+ <xref href="how-to-learn-qt.xml#id-49f509b5-52f9-4cd9-9921-74217b9a5182">How to learn Qt</xref>
+ </li>
+ <li>
+ <xref href="tutorials.xml#id-a737f955-a904-455f-b4aa-0dc69ed5a64f">Tutorials</xref>
+ </li>
+ <li>
+ <xref href="all-examples.xml#id-98d95159-d65b-4706-b08f-13d80080448d">Examples</xref>
+ </li>
+ <li>
+ <xref href="qt4-7-intro.xml#id-519ae0e3-4242-4c2a-b2be-e05d1e95f177">What's new in Qt 4.7</xref>
+ </li>
+ </ul>
+ </sectiondiv>
+ </sectiondiv>
+ </sectiondiv>
+ \endcode
+
+ Your DITA XML publishing program must recognize the values of the
+ \e {outputclass} attribute.
+
+ See also \l {span-command} {\\span}.
+
+ \target span-command
+ \section1 \\span
+
+ The \\span command applies special formatting to a small block of text.
+
+ Two arguments must be provided, each argument in curly braces, as
+ shown in the QDoc comment below. The first argument is not
+ interpreted, but specifies the formatting attribute(s) of the tag
+ output by QDoc. The second argument is the text to be rendered with
+ the special formatting attributes.
+
+ For example, we might want to render the first word of each
+ element in a numeric list in blue.
+
+ \code
+ / *!
+ Global variables with complex types:
+ \list 1
+ \li \span {class="variableName"} {mutableComplex1} in globals.cpp at line 14
+ \li \span {class="variableName"} {mutableComplex2} in globals.cpp at line 15
+ \li \span {class="variableName"} {constComplex1} in globals.cpp at line 16
+ \li \span {class="variableName"} {constComplex2} in globals.cpp at line 17
+ \endlist
+ * /
+ \endcode
+
+ Class \e {variableName} refers to a clause in your style.css.
+
+ \code
+ .variableName
+ {
+ font-family: courier;
+ color: blue
+ }
+ \endcode
+
+ Using the \e {variableName} clause shown above, the example is rendered as:
+
+ Global variables with complex types:
+ \list 1
+ \li \span {class="variableName"} {mutableComplex1} in globals.cpp at line 14
+ \li \span {class="variableName"} {mutableComplex2} in globals.cpp at line 15
+ \li \span {class="variableName"} {constComplex1} in globals.cpp at line 16
+ \li \span {class="variableName"} {constComplex2} in globals.cpp at line 17
+ \endlist
+
+ \note The \b span command does not cause a new paragraph to be
+ started.
+
+ See also \l {div-command} {\\div}.
+
+ \target tt-command
+ \section1 \\tt (teletype font)
+
+ The \\tt command renders its argument in a monospace font. This
+ command behaves just like the \l {c-command} {\\c} command, except
+ that \\tt allows you to nest QDoc commands within the argument
+ (e.g. \l {e-command} {\\e}, \l {b-command} {\\b} and \l
+ {underline-command} {\\underline}).
+
+ \code
+ / *!
+ After having populated the main container with
+ child widgets, \c setupUi() scans the main container's list of
+ slots for names with the form
+ \tt{on_\e{objectName}_\e{signalName}().}
+ * /
+ \endcode
+
+ QDoc renders this as:
+
+ \quotation
+ After having populated the main container with
+ child widgets, \c setupUi() scans the main container's list of
+ slots for names with the form
+ \tt{on_\e{objectName}_\e{signalName}().}
+ \endquotation
+
+ If the text to be rendered in the code font contains spaces, enclose the
+ entire text in curly brackets.
+
+ \code
+ \tt {QLineEdit::QLineEdit(const QString &contents, QWidget *parent) :QWidget(parent)}
+ \endcode
+
+ QDoc renders this as:
+
+ \quotation
+ \tt {QLineEdit::QLineEdit(const QString &contents, QWidget *parent) :QWidget(parent)}
+ \endquotation
+
+ See also \l {c-command} {\\c}.
+
+ \target b-command
+ \section1 \\b
+
+ The \\b command renders its argument in bold font. This command used
+ to be called \\bold.
+
+ \code
+ / *!
+ This is regular text; \b {this text is
+ rendered using the \\b command}.
+ * /
+ \endcode
+
+ QDoc renders this as:
+
+ \quotation
+ This is regular text; \b {this text is rendered using
+ the \\b command}.
+ \endquotation
+
+ \target e-command
+ \section1 \\e (emphasis, italics) \span {class="newStuff"} {(new 5/3/2012)}
+
+ The \\e command renders its argument in a special font, normally italics. This
+ command used to be called \\i, which is now deprecated. Use \e for italics.
+
+ If the argument contains spaces or other punctuation, enclose the
+ argument in curly brackets.
+
+ \code
+ / *!
+ Here, we render \e {a few words} in italics.
+ * /
+ \endcode
+
+ QDoc renders this as:
+
+ \quotation
+ Here, we render \e {a few words} in italics.
+ \endquotation
+
+ If you want to use other QDoc commands within an argument that
+ contains spaces, you always need to enclose the argument in
+ braces. But QDoc is smart enough to count parentheses [3], so you
+ don't need braces in cases like this:
+
+ \code
+ / *!
+ An argument can sometimes contain whitespaces,
+ for example: \e QPushButton(tr("A Brand New Button"))
+ * /
+ \endcode
+
+ QDoc renders this as:
+
+ \quotation
+ An argument can sometimes contain whitespaces,
+ for example: \e QPushButton(tr("A Brand New Button"))
+ \endquotation
+
+ Finally, trailing punctuation is not included in an argument [4],
+ nor is "'s" [5]
+
+ \raw HTML
+ <table align="center" cellpadding="2"
+ cellspacing="1" border="0">
+ <tr valign="top" bgcolor="#a2c511">
+ <th></th>
+ <th>QDoc Syntax</th>
+ <th>Generated Documentation</th>
+ </tr>
+
+ <tr valign="top" bgcolor="#d0d0d0">
+ <td>1</td>
+ <td>A variation of a command button is a \e menu
+ button.</td>
+ <td>A variation of a command button is a <i>menu</i>
+ button.</td>
+ </tr>
+
+ <tr valign="top" bgcolor="#c0c0c0">
+ <td>2</td>
+ <td>The QPushButton widget provides a
+ \e {command button}.</td>
+ <td>The QPushButton widget provides a
+ <i>command button</i>.</td>
+ </tr>
+
+ <tr valign="top" bgcolor="#d0d0d0">
+ <td>3</td>
+ <td>Another class of buttons are option buttons
+ \e (see QRadioButton).</td>
+ <td>Another class of buttons are option buttons
+ <i> (see QRadioButton)</i>.</td>
+ </tr>
+
+ <tr valign="top" bgcolor="#c0c0c0">
+ <td>4</td>
+ <td>A push button emits the signal \e clicked().</td>
+ <td>A push button emits the signal <i>clicked</i>().</td>
+ </tr>
+
+ <tr valign="top" bgcolor="#d0d0d0">
+ <td>5</td>
+ <td>The \e QPushButton's checked property is
+ false by default.</td>
+ <td>The <i>QPushButton</i>'s checked property is
+ false by default.</td>
+ </tr>
+
+ </table>
+ \endraw
+
+ \target sub-command
+ \section1 \\sub
+
+ The \\sub command renders its argument lower than the baseline of
+ the regular text, using a smaller font.
+
+ \code
+ / *!
+ Definition (Range): Consider the sequence
+ {x\sub n}\sub {n > 1} . The set
+
+ {x\sub 2, x\sub 3, x\sub 4, ...} = {x\sub n ; n = 2, 3, 4, ...}
+
+ is called the range of the sequence.
+ * /
+ \endcode
+
+ QDoc renders this as:
+
+ \quotation
+ Definition (Range): Consider the sequence
+ {x\sub n}\sub {n > 1} . The set
+
+ {x\sub 2, x\sub 3, x\sub 4, ...} = {x\sub n ; n = 2, 3, 4, ...}
+
+ is called the range of the sequence.
+ \endquotation
+
+ If the argument contains spaces or other punctuation, enclose the
+ argument in curly brackets.
+
+ \target sup-command
+ \section1 \\sup
+
+ The \\sup command renders its argument higher than
+ the baseline of the regular text, using a smaller font.
+
+ \code
+ / *!
+ The series
+
+ 1 + a + a\sup 2 + a\sup 3 + a\sup 4 + ...
+
+ is called the \i {geometric series}.
+ * /
+ \endcode
+
+ QDoc renders this as:
+
+ \quotation
+ The series
+
+ 1 + a + a\sup 2 + a\sup 3 + a\sup 4 + ...
+
+ is called the \e {geometric series}.
+ \endquotation
+
+ If the argument contains spaces or other punctuation, enclose the
+ argument in curly brackets.
+
+ \target uicontrol-command
+ \section1 \\uicontrol
+
+ The \\uicontrol command is used to mark content as being used for UI
+ control elements. When using HTML, the output is rendered in bold.
+ When using DITA XML the content is enclosed in a \c{uicontrol} tag.
+
+ \sa \\b
+
+ \target underline-command
+ \section1 \\underline
+
+ The \\underline command renders its argument underlined.
+
+ \code
+ / *!
+ The \underline {F}ile menu gives the users the possibility
+ to edit an existing file, or save a new or modified
+ file, and exit the application.
+ * /
+ \endcode
+
+ QDoc renders this as:
+
+ \quotation
+ The \underline {F}ile menu gives the users the possibility
+ to edit an existing file, or save a new or modified
+ file, and exit the application.
+ \endquotation
+
+ If the argument contains spaces or other punctuation, enclose the
+ argument in curly brackets.
+
+ \target backslash-command
+ \section1 \\\\ (double backslash)
+
+ The \\\\ command expands to a double backslash.
+
+ QDoc commands always start with a single backslash. To display a
+ single backslash in the text you need to type two backslashes. If
+ you want to display two backslashes, you need to type four.
+
+ \code
+ / *!
+ The \\\\ command is useful if you want a
+ backslash to appear verbatim, for example,
+ writing C:\\windows\\home\\.
+ * /
+ \endcode
+
+ QDoc renders this as:
+
+ \quotation
+ The \\\\ command is useful if you want a
+ backslash to appear verbatim, for example,
+ writing C:\\windows\\home\\.
+ \endquotation
+
+ However, if you want your text to appear in a monospace font as
+ well, you can use the \l {c-command} {\\c} command instead, which
+ accepts and renders the backslash as any other character. For
+ example:
+
+ \code
+ / *!
+ The \\c command is useful if you want a
+ backslash to appear verbatim, and the word
+ that contains it written in a monospace font,
+ like this: \c {C:\windows\home\}.
+ * /
+ \endcode
+
+ QDoc renders this as:
+
+ \quotation
+ The \\c command is useful if you want a
+ backslash to appear verbatim, and the word
+ that contains it written in a monospace font,
+ like this: \c {C:\windows\home\}.
+ \endquotation
+
+*/
+
+
+/*!
+ \page 05-qdoc-commands-documentstructure.html
+ \previouspage Text Markup
+ \contentspage QDoc Manual
+ \nextpage Including Code Inline
+
+ \title Document Structure
+
+ The document structuring commands are for dividing your document
+ into sections. QDoc supports six kinds of sections: \c \part, \c
+ \chapter, \c \section1, \c \section2, \c \section3, and \c
+ \section4. The \c \section1..4 commands are the most useful. They
+ correspond to the traditional section, subsection, etc used in
+ outlining.
+
+ \target part-command
+ \section1 \\part
+
+ The \\part command is intended for use in a large document, like a
+ book.
+
+ In general a document structuring command considers everything
+ that follows it until the first line break as its argument. The
+ argument is rendered as the unit's title. If the title needs to be
+ spanned over several lines, make sure that each line (except the
+ last one) is ended with a backslash.
+
+ In total, there are six levels of sections in QDoc: \c \part, \c
+ \chapter, \c \section1, \c \section2, \c \section3 and \c
+ \section4. \c \section1 to \c \section4 correspond to the
+ traditional section, subsection, subsubsection and
+ subsubsubsection.
+
+ There is a strict ordering of the section units:
+
+ \code
+ part
+ |
+ chapter
+ |
+ section1
+ |
+ section2
+ |
+ section3
+ |
+ section4
+ \endcode
+
+ For example, a \c section1 unit can only appear as the top level
+ section or inside a \c chapter unit. Skipping a section unit, for
+ example from \c part to \c section1, is not allowed.
+
+ You can \e begin with either of the three: \c part, \c chapter or
+ \c section1.
+
+
+ \code
+ / *!
+ \part Basic Qt
+
+ This is the first part.
+
+
+ \chapter Getting Started
+
+ This is the first part's first chapter.
+
+
+ \section1 Hello Qt
+
+ This is the first chapter's first section.
+
+
+ \section1 Making Connections
+
+ This is the first chapter's second section.
+
+
+ \section1 Using the Reference Documentation
+
+ This is the first chapter's third section.
+
+
+ \chapter Creating Dialogs
+
+ This is the first part's second chapter.
+
+
+ \section1 Subclassing QDialog
+
+ This is the second chapter's first section.
+
+ ...
+
+
+ \part Intermediate Qt
+
+ This is the second part.
+
+
+ \chapter Layout Management
+
+ This is the second part's first chapter.
+
+
+ \section1 Basic Layouts
+
+ This is the first chapter's first section.
+
+ ...
+ * /
+ \endcode
+
+ QDoc renders this as:
+
+ \quotation
+ \raw HTML
+ <a name="Basic Qt">
+ <h1>Basic Qt</h1>
+ </a>
+ <p>This is the first part.</p>
+
+ <a name="Getting started">
+ <h2>Getting Started</h2>
+ </a>
+ This is the first part's first chapter.</p>
+
+ <a name="Hello Qt">
+ <h3>Hello Qt</h3>
+ </a>
+ <p>This is the first chapter's first section.</p>
+
+ <a name="Making Connections">
+ <h3>Making Connections</h3>
+ </a>
+ <p>This is the first chapter's second section.</p>
+
+ <a name="Using the Reference Documentation">
+ <h3>Using the Reference Documentation</h3>
+ </a>
+ <p>This is the first chapter's third section.</p>
+
+ <a name="Creating Dialogs">
+ <h2>Creating Dialogs</h2>
+ </a>
+ <p>This is the first part's second chapter.</p>
+
+ <a name="Subclassing QDialog">
+ <h3>Subclassing QDialog</h3>
+ </a>
+ <p>This is the second chapter's first section.</p>
+
+ ...
+
+ <a name="Intermediate Qt">
+ <h1>Intermediate Qt</h1>
+ </a>
+ <p>This is the second part.</p>
+
+ <a name="Layout Management">
+ <h2>Layout Management</h2>
+ </a>
+ <p>This is the second part's first chapter.</p>
+
+ <a name="Basic Layouts">
+ <h3>Basic Layouts</h3>
+ </a>
+ <p>This is the first chapter's first section.</p>
+
+ ...
+
+ \endraw
+ \endquotation
+
+ Each section is a logical unit in the document. The section
+ heading appears in the automatically generated table of contents
+ that normally appears in the upper right-hand corner of the page.
+
+ \target chapter-command
+ \section1 \\chapter
+
+ The \\chapter command is intended for use in
+ larger documents, and divides the document into chapters.
+
+ See \l{part} {\\part} for an explanation of the various
+ section units, command argument, and rendering.
+
+ \target sectionOne-command
+ \section1 \\section1
+
+ The \\section1 command starts a new section.
+
+ See \l{part} {\\part} for an explanation of the various
+ section units, command argument, and rendering.
+
+ \target sectionTwo-command
+ \section1 \\section2
+
+ The \\section2 command starts a new section.
+
+ See \l{part} {\\part} for an explanation of the various
+ section units, command argument, and rendering.
+
+ \target sectionThree-command
+ \section1 \\section3
+
+ The \\section3 command starts a new section.
+
+ See \l{part} {\\part} for an explanation of the various
+ section units, command argument, and rendering.
+
+ \target sectionFour-command
+ \section1 \\section4
+
+ The \\section4 command starts a new section.
+
+ See \l{part} {\\part} for an explanation of the various
+ section units, command argument, and rendering.
+
+*/
+
+
+/*!
+ \page 06-qdoc-commands-includecodeinline.html
+ \previouspage Document Structure
+ \contentspage QDoc Manual
+ \nextpage Including External Code
+
+ \title Including Code Inline
+
+ The following commands are used to render source code without
+ formatting. The source code begins on a new line, rendered in the
+ code.
+
+ \b{Note:} Although all these commands are for rendering C++
+ code, the
+ \l{07-0-qdoc-commands-includingexternalcode.html#snippet-command}
+ {\\snippet} and
+ \l{07-0-qdoc-commands-includingexternalcode.html#codeline-command}
+ {\\codeline} commands are preferred over the others. These
+ commands allow equivalent code snippets for other Qt language
+ bindings to be substituted for the C++ snippets in the
+ documentation.
+
+ \target code-command
+ \section1 \\code
+
+ The \\code and \\endcode commands enclose a snippet of source code.
+
+ \note The \l {c-command} {\\c} command can be used for short code
+ fragments within a sentence. The \\code command is for longer code
+ snippets. It renders the code verbatim in a separate paragraph in
+ the code font.
+
+ When processing any of the \\code, \l {newcode-command} {\\newcode} or \l
+ {oldcode-command} {\\oldcode} commands, QDoc removes all
+ indentation that is common for the verbatim code blocks within a
+ \c{/}\c{*!} ... \c{*}\c{/} comment before it adds the standard
+ indentation. For that reason the recommended style is to use 8
+ spaces for the verbatim code contained within these commands
+
+ \note This doesn't apply to externally quoted code using the \l
+ {quotefromfile-command} {\\quotefromfile} or \l
+ {quotefile-command} {\\quotefile} command.
+
+ \code
+ / *!
+ \code
+ #include <QApplication>
+ #include <QPushButton>
+
+ int main(int argc, char *argv[])
+ {
+ ...
+ }
+ \ endcode
+ * /
+ \endcode
+
+ QDoc renders this as:
+
+ \code
+ #include <QApplication>
+ #include <QPushButton>
+
+ int main(int argc, char *argv[])
+ {
+ ...
+ }
+ \endcode
+
+ Other QDoc commands are disabled within \\code... \\endcode, and
+ the special character '\\' is accepted and rendered like the rest
+ of the code.
+
+ To include code snippets from an external file, use the
+ \l{07-0-qdoc-commands-includingexternalcode.html#snippet-command}
+ {\\snippet} and
+ \l{07-0-qdoc-commands-includingexternalcode.html#codeline-command}
+ {\\codeline} commands.
+
+ See also \l {c-command} {\\c}, \l
+ {07-0-qdoc-commands-includingexternalcode.html#quotefromfile-command}
+ {\\quotefromfile}, \l{newcode-command} {\\newcode}, and \l {oldcode-command}
+ {\\oldcode}.
+
+ \target newcode-command
+ \section1 \\newcode
+
+ The \\newcode, \\oldcode, and \\endcode commands enable you to
+ show how to port a snippet of code to a new version of an API.
+
+ The \\newcode command and its companion the \\oldcode command are
+ a convenience combination of the \l {code-command} {\\code} commands:
+ this combination provides a text relating the two code snippets to each
+ other.
+
+ The \\newcode command requires a preceding \\oldcode statement.
+
+ Like the \l{code-command}{\\code} command, the \\newcode command renders its
+ code on a new line in the documentation using a monospace font and the
+ standard indentation.
+
+ \code
+ / *!
+ \oldcode
+ if (printer->setup(parent))
+ ...
+ \newcode
+ QPrintDialog dialog(printer, parent);
+ if (dialog.exec())
+ ...
+ \ endcode
+ * /
+ \endcode
+
+ QDoc renders this as:
+
+ \quotation
+ \oldcode
+ if (printer->setup(parent))
+ ...
+ \newcode
+ QPrintDialog dialog(printer, parent);
+ if (dialog.exec())
+ ...
+ \endcode
+ \endquotation
+
+ Other QDoc commands are disabled within \\oldcode ... \\endcode,
+ and the '\\' character doesn't need to be escaped.
+
+ \target oldcode-command
+ \section1 \\oldcode
+
+ The \\oldcode command requires a corresponding
+ \\newcode statement; otherwise QDoc fails to parse the command
+ and emits a warning.
+
+ See also \l {newcode-command} {\\newcode}.
+
+ \target qml-command
+ \section1 \\qml
+
+ The \\qml and \\endqml commands enclose a snippet of QML source
+ code. Currently, QDoc handles \\qml and \\endqml in exactly the same
+ way as \\code and \\endcode.
+
+ \code
+ / *!
+ \qml
+ import QtQuick 1.0
+
+ Row {
+ Rectangle {
+ width: 100; height: 100
+ color: "blue"
+ transform: Translate { y: 20 }
+ }
+ Rectangle {
+ width: 100; height: 100
+ color: "red"
+ transform: Translate { y: -20 }
+ }
+ }
+ \endqml
+ * /
+ \endcode
+
+ QDoc renders this as:
+
+ \qml
+ import QtQuick 1.0
+
+ Row {
+ Rectangle {
+ width: 100; height: 100
+ color: "blue"
+ transform: Translate { y: 20 }
+ }
+ Rectangle {
+ width: 100; height: 100
+ color: "red"
+ transform: Translate { y: -20 }
+ }
+ }
+ \endqml
+*/
+
+
+/*!
+ \page 07-0-qdoc-commands-includingexternalcode.html
+ \previouspage Including Code Inline
+ \contentspage QDoc Manual
+ \nextpage Creating Links
+
+ \title Including External Code
+
+ The following commands enable you to include code snippets from
+ external files. You can make QDoc include the complete contents of
+ a file, or you can quote specific parts of the file and skip
+ others. The typical use of the latter is to quote a file chunk by
+ chunk.
+
+ \b{Note:} Although all these commands are for rendering C++
+ code, the
+ \l{07-0-qdoc-commands-includingexternalcode.html#snippet-command}
+ {\\snippet} and
+ \l{07-0-qdoc-commands-includingexternalcode.html#codeline-command}
+ {\\codeline} commands are preferred over the others. These
+ commands allow equivalent code snippets for other Qt language
+ bindings to be substituted for the C++ snippets in the
+ documentation.
+
+ \target quotefile-command
+ \section1 \\quotefile
+
+ The \\quotefile command expands to the complete contents of the
+ file given as argument.
+
+ The command considers the rest of the line as part of its
+ argument, make sure to follow the file name with a line break.
+
+ The file's contents is rendered in a separate paragraph, using a
+ monospace font and the standard indentation. The code is shown
+ verbatim.
+
+ \code
+ / *!
+ This is a simple "Hello world" example:
+
+ \quotefile examples/main.cpp
+
+ It contains only the bare minimum you need
+ to get a Qt application up and running.
+ * /
+ \endcode
+
+ QDoc renders this as:
+
+ \quotation
+ This is a simple "Hello world" example:
+
+ \quotefile examples/main.cpp
+
+ It contains only the bare minimum you need to get a Qt
+ application up and running.
+ \endquotation
+
+ See also \l {quotefromfile-command} {\\quotefromfile} and
+ \l {code-command} {\\code}.
+
+
+ \target quotefromfile-command
+ \section1 \\quotefromfile
+
+ The \\quotefromfile command opens the file given as argument for
+ quoting.
+
+ The command considers the rest of the line as part of its
+ argument, make sure to follow the file name with a line break.
+
+ The command is intended for use when quoting parts from file with
+ the walkthrough commands: \l {printline-command} {\\printline}, \l
+ {printto-command} {\\printto}, \l {printuntil-command}
+ {\\printuntil}, \l {skipline-command} {\\skipline}, \l
+ {skipto-command} {\\skipto}, \l {skipuntil-command}
+ {\\skipuntil}. This enables you to quote specific portions of a
+ file.
+
+ \code
+ / *!
+ The whole application is contained within
+ the \c main() function:
+
+ \quotefromfile examples/main.cpp
+
+ \skipto main
+ \printuntil app(argc, argv)
+
+ First we create a QApplication object using
+ the \c argc and \c argv parameters.
+
+ \skipto QPushButton
+ \printuntil resize
+
+ Then we create a QPushButton, and give it a reasonable
+ size using the QWidget::resize() function.
+
+ ...
+ * /
+ \endcode
+
+ QDoc renders this as:
+
+ \quotation
+ The whole application is contained within
+ the \c main() function:
+
+ \quotefromfile examples/main.cpp
+
+ \skipto main
+ \printuntil app(argc, argv)
+
+ First we create a QApplication object using the \c argc
+ and \c argv parameters.
+
+ \skipto QPushButton
+ \printuntil resize
+
+ Then we create a QPushButton, and give it a reasonable
+ size using the QWidget::resize() function.
+
+ ...
+ \endquotation
+
+ QDoc remembers which file it is quoting from, and the current
+ position in that file (see \l {file} {\\printline} for more
+ information). There is no need to "close" the file.
+
+ See also \l {quotefile-command} {\\quotefile}, \l {code-command}
+ {\\code} and \l {dots} {\\dots}.
+
+ \target printline-command
+ \section1 \\printline
+
+ The \\printline command expands to the line from the current
+ position to the next non-blank line of the current source file.
+
+ To ensure that the documentation remains synchronized with the
+ source file, a substring of the line must be specified as an
+ argument to the command. Note that the command considers the rest
+ of the line as part of its argument, make sure to follow the
+ substring with a line break.
+
+ The line from the source file is rendered as a separate paragraph,
+ using a monospace font and the standard indentation. The code is
+ shown verbatim.
+
+ \code
+ / *!
+ There has to be exactly one QApplication object
+ in every GUI application that uses Qt.
+
+ \quotefromfile examples/main.cpp
+
+ \printline QApplication
+
+ This line includes the QApplication class
+ definition. QApplication manages various
+ application-wide resources, such as the
+ default font and cursor.
+
+ \printline QPushButton
+
+ This line includes the QPushButton class
+ definition. The QPushButton widget provides a command
+ button.
+
+ \printline main
+
+ The main function...
+ * /
+ \endcode
+
+ QDoc renders this as:
+
+ \quotation
+ There has to be exactly one QApplication object
+ in every GUI application that uses Qt.
+
+ \quotefromfile examples/main.cpp
+
+ \skipto QApplication
+ \printline QApplication
+
+ This line includes the QApplication class
+ definition. QApplication manages various
+ application-wide resources, such as the
+ default font and cursor.
+
+ \printline QPushButton
+
+ This line includes the QPushButton class
+ definition. The QPushButton widget provides a command
+ button.
+
+ \printline main
+
+ The main function...
+ \endquotation
+
+ \target file
+
+ QDoc reads the file sequentially. To move the current position
+ forward you can use either of the \l {skipline-command}
+ {\\skip...} commands. To move the current position backward, you
+ can use the \l {quotefromfile-command} {\\quotefromfile} command
+ again.
+
+ \target substring
+
+ If the substring argument is surrounded by slashes it is
+ interpreted as a \l {QRegExp}{regular expression}.
+
+ \code
+ / *!
+ \quotefromfile examples/mainwindow.cpp
+
+ \skipto closeEvent
+ \printuntil /^\}/
+
+ Close events are sent to widgets that the users want to
+ close, usually by clicking \c File|Exit or by clicking
+ the \c X title bar button. By reimplementing the event
+ handler, we can intercept attempts to close the
+ application.
+ * /
+ \endcode
+
+ QDoc renders this as:
+
+ \quotation
+ \quotefromfile examples/mainwindow.cpp
+
+ \skipto closeEvent
+ \printuntil /^\}/
+
+ Close events are sent to widgets that the users want to
+ close, usually by clicking \c File|Exit or by clicking
+ the \c X title bar button. By reimplementing the event
+ handler, we can intercept attempts to close the
+ application.
+ \endquotation
+
+ (\l {widgets/scribble} {The complete example file...})
+
+ The regular expression \c /^\}/ makes QDoc print until the first
+ '}' character occurring at the beginning of the line without
+ indentation. /.../ encloses the regular expression, and '^' means
+ the beginning of the line. The '}' character must be escaped since
+ it is a special character in regular expressions.
+
+ QDoc will emit a warning if the specified substring or regular
+ expression cannot be located, i.e. if the source code has changed.
+
+ See also \l {printto-command} {\\printto} and \l
+ {printuntil-command} {\\printuntil}.
+
+ \target printto-command
+ \section1 \\printto
+
+ The \\printto command expands to all the lines from the current
+ position up to and \e excluding the next line containing a given
+ substring.
+
+ The command considers the rest of the line as part of its
+ argument, make sure to follow the substring with a line break. The
+ command also follows the same conventions for \l {file}
+ {positioning} and \l {substring} {argument} as the \l
+ {printline-command} {\\printline} command.
+
+ The lines from the source file are rendered in a separate
+ paragraph, using a monospace font and the standard
+ indentation. The code is shown verbatim.
+
+ \code
+ / *!
+ The whole application is contained within the
+ \c main() function:
+
+ \quotefromfile examples/main.cpp
+ \printto hello
+
+ First we create a QApplication object using the \c argc and
+ \c argv parameters...
+ * /
+ \endcode
+
+ QDoc renders this as:
+
+ \quotation
+ The whole application is contained within the
+ \c main() function:
+
+ \quotefromfile examples/main.cpp
+ \skipto main
+ \printto hello
+
+ First we create a QApplication object using the \c argc
+ and \c argv parameters...
+ \endquotation
+
+ See also \l {printline-command} {\\printline} and \l
+ {printuntil-command} {\\printuntil}.
+
+ \target printuntil-command
+ \section1 \\printuntil
+
+ The \\printuntil command expands to all the lines from the current
+ position up to and \e including the next line containing a given
+ substring.
+
+ The command considers the rest of the line as part of its
+ argument, make sure to follow the substring with a line break. The
+ command also follows the same conventions for \l {file}
+ {positioning} and \l {substring} {argument} as the \l
+ {printline-command} {\\printline} command.
+
+ The lines from the source file are rendered in a separate
+ paragraph, using a monospace font and the standard
+ indentation. The code is shown verbatim.
+
+ \code
+ / *!
+ The whole application is contained within the
+ \c main() function:
+
+ \quotefromfile examples/main.cpp
+ \skipto main
+ \printuntil hello
+
+ First we create a QApplication object using the
+ \c argc and \c argv parameters, then we create
+ a QPushButton.
+ * /
+ \endcode
+
+ QDoc renders this as:
+
+ \quotation
+ The whole application is contained within the
+ \c main() function:
+
+ \quotefromfile examples/main.cpp
+ \skipto main
+ \printuntil hello
+
+ First we create a \l
+ {http://doc.qt.io/qt-5/qapplication.html} {QApplication}
+ object using the \c argc and \c argv parameters, then we
+ create a \l
+ {http://doc.qt.io/qt-5/qpushbutton.html} {QPushButton}.
+ \endquotation
+
+ See also \l {printline-command} {\\printline} and \l
+ {printto-command} {\\printto}.
+
+ \target skipline-command
+ \section1 \\skipline
+
+ The \\skipline command ignores the next non-blank line in the
+ current source file.
+
+ Doc reads the file sequentially, and the \\skipline command is
+ used to move the current position (omitting a line of the source
+ file). See the remark about \l {file} {file positioning} above.
+
+ The command considers the rest of the line as part of its
+ argument, make sure to follow the substring with a line break. The
+ command also follows the same conventions for \l {substring}
+ {argument} as the \l {printline-command} {\\printline} command,
+ and it is used in conjunction with the \l {quotefromfile-command}
+ {\\quotefromfile} command.
+
+ \code
+ / *!
+ QPushButton is a GUI push button that the user
+ can press and release.
+
+ \quotefromfile examples/main.cpp
+ \skipline QApplication
+ \printline QPushButton
+
+ This line includes the QPushButton class
+ definition. For each class that is part of the
+ public Qt API, there exists a header file of
+ the same name that contains its definition.
+ * /
+ \endcode
+
+ QDoc renders this as:
+
+ \quotation
+ \l
+ QPushButton is a GUI push button that the user
+ can press and release.
+
+ \quotefromfile examples/main.cpp
+ \skipto QApplication
+ \skipline QApplication
+ \printline QPushButton
+
+ This line includes the QPushButton class
+ definition. For each class that is part of the public
+ Qt API, there exists a header file of the same name
+ that contains its definition.
+ \endquotation
+
+ See also \l {skipto-command} {\\skipto}, \l {skipuntil-command}
+ {\\skipuntil} and \l {dots} {\\dots}.
+
+ \target skipto-command
+ \section1 \\skipto
+
+ The \\skipto command ignores all the lines from the current
+ position up to and \e excluding the next line containing a given
+ substring.
+
+ QDoc reads the file sequentially, and the \\skipto command is used
+ to move the current position (omitting one or several lines of the
+ source file). See the remark about \l {file} {file positioning}
+ above.
+
+ The command considers the rest of the line as part of its
+ argument, make sure to follow the substring with a line break.
+
+ The command also follows the same conventions for \l {substring}
+ {argument} as the \l {printline-command} {\\printline} command,
+ and it is used in conjunction with the \l {quotefromfile-command}
+ {\\quotefromfile} command.
+
+ \code
+ / *!
+ The whole application is contained within
+ the \c main() function:
+
+ \quotefromfile examples/main.cpp
+ \skipto main
+ \printuntil }
+
+ First we create a QApplication object. There
+ has to be exactly one such object in
+ every GUI application that uses Qt. Then
+ we create a QPushButton, resize it to a reasonable
+ size...
+ * /
+ \endcode
+
+ QDoc renders this as:
+
+ \quotation
+ The whole application is contained within
+ the \c main() function:
+
+ \quotefromfile examples/main.cpp
+ \skipto main
+ \printuntil }
+
+ First we create a QApplication object. There has to be
+ exactly one such object in every GUI application that
+ uses Qt. Then we create a QPushButton, resize it to a
+ reasonable size ...
+ \endquotation
+
+ See also \l {skipline-command} {\\skipline}, \l
+ {skipuntil-command} {\\skipuntil} and \l {dots} {\\dots}.
+
+ \target skipuntil-command
+ \section1 \\skipuntil
+
+ The \\skipuntil command ignores all the lines from the current
+ position up to and \e including the next line containing a given
+ substring.
+
+ QDoc reads the file sequentially, and the \\skipuntil command is
+ used to move the current position (omitting one or several lines
+ of the source file). See the remark about \l {file} {file
+ positioning} above.
+
+ The command considers the rest of the line as part of its
+ argument, make sure to follow the substring with a line break.
+
+ The command also follows the same conventions for \l {substring}
+ {argument} as the \l {printline-command} {\\printline} command,
+ and it is used in conjunction with the \l {quotefromfile-command}
+ {\\quotefromfile} command.
+
+ \code
+ / *!
+ The first thing we did in the \c main() function
+ was to create a QApplication object \c app.
+
+ \quotefromfile examples/main.cpp
+ \skipuntil show
+ \dots
+ \printuntil }
+
+ In the end we must remember to make \c main() pass the
+ control to Qt. QCoreApplication::exec() will return when
+ the application exits...
+ * /
+ \endcode
+
+ QDoc renders this as:
+
+ \quotation
+ The first thing we did in the \c main() function was to
+ create a QApplication object \c app.
+
+ \quotefromfile examples/main.cpp
+ \skipuntil show
+ \dots
+ \printuntil }
+
+ In the end we must remember to make \c main() pass the
+ control to Qt. QCoreApplication::exec()
+ will return when the application exits...
+ \endquotation
+
+ See also \l {skipline-command} {\\skipline}, \l {skipto-command}
+ {\\skipto} and \l {dots} {\\dots}.
+
+ \target dots-command
+ \section1 \\dots
+
+ The \\dots command indicates that parts of the source file have
+ been omitted when quoting a file.
+
+ The command is used in conjunction with the \l
+ {quotefromfile-command} {\\quotefromfile} command, and should be
+ stated on its own line. The dots are rendered on a new line, using
+ a monospace font.
+
+ \code
+ / *!
+ \quotefromfile examples/main.cpp
+ \skipto main
+ \printuntil {
+ \dots
+ \skipuntil exec
+ \printline }
+ * /
+ \endcode
+
+ QDoc renders this as:
+
+ \quotefromfile examples/main.cpp
+ \skipto main
+ \printuntil {
+ \dots
+ \skipuntil exec
+ \printline }
+
+ The default indentation is 4 spaces, but this can be adjusted
+ using the command's optional argument.
+
+ \code
+ / *!
+ \dots 0
+ \dots
+ \dots 8
+ \dots 12
+ \dots 16
+ * /
+ \endcode
+
+ QDoc renders this as:
+
+ \dots 0
+ \dots
+ \dots 8
+ \dots 12
+ \dots 16
+
+ See also \l {skipline-command} {\\skipline}, \l {skipto-command}
+ {\\skipto} and \l {skipuntil-command} {\\skipuntil}.
+
+ \target snippet-command
+ \section1 \\snippet
+
+ The \\snippet command causes a code snippet to be included
+ verbatim as preformatted text, which may be syntax highlighted.
+
+ Each code snippet is referenced by the file that holds it and by
+ a unique identifier for that file. Snippet files are typically
+ stored in a \c{snippets} directory inside the documentation
+ directory (for example, \c{$QTDIR/doc/src/snippets}).
+
+ For example, the following documentation references a snippet in a
+ file residing in a subdirectory of the documentation directory:
+
+ \code
+ \snippet snippets/textdocument-resources/main.cpp Adding a resource
+ \endcode
+
+ The text following the file name is the unique identifier for the
+ snippet. This is used to delimit the quoted code in the relevant
+ snippet file, as shown in the following example that corresponds to
+ the above \c{\\snippet} command:
+
+ \dots
+ \code
+ QImage image(64, 64, QImage::Format_RGB32);
+ image.fill(qRgb(255, 160, 128));
+
+ //! [Adding a resource]
+ document->addResource(QTextDocument::ImageResource,
+ QUrl("mydata://image.png"), QVariant(image));
+ //! [Adding a resource]
+ \endcode
+ \dots
+
+ \target codeline-command
+ \section1 \\codeline
+
+ The \\codeline command inserts a blank line of preformatted
+ text. It is used to insert gaps between snippets without closing
+ the current preformatted text area and opening a new one.
+
+*/
+
+
+/*!
+ \page 08-qdoc-commands-creatinglinks.html
+ \previouspage Including External Code
+ \contentspage QDoc Manual
+ \nextpage Including Images
+
+ \title Creating Links
+
+ These commands are for creating hyperlinks to classes, functions,
+ examples, and other targets.
+
+ \target l-command
+ \section1 \\l (link)
+
+ The \\l link command is used to create a hyperlink to many
+ different kinds of targets. The command's general syntax is:
+
+ \code
+ \l [ link criteria ] { link target } { link text }
+ \endcode
+
+ ...where the \c {link criteria} in square brackets are optional
+ but may be required when the \c {link target} is ambiguous. See
+ \l {Fixing Ambiguous Links} below.
+
+ Here is an example using the \\l command to link to an external page:
+
+ \code
+ / *!
+ Read the \l {http://doc.qt.io/qt-5/}
+ {Qt 5.0 Documentation} carefully.
+ * /
+ \endcode
+
+ QDoc renders this as:
+
+ \quotation
+ Read the \l {http://doc.qt.io/qt-5/}
+ {Qt 5.0 Documentation} carefully.
+ \endquotation
+
+ If the link target is equivalent to the link text, the second
+ argument can be omitted.
+
+ For example, if you have documentation like:
+
+ \code
+ / *!
+ \target assertions
+
+ Assertions make some statement about the text at the
+ point where they occur in the regexp, but they do not
+ match any characters.
+
+ ...
+
+ Regexps are built up from expressions, quantifiers, and
+ \l {assertions} {assertions}.
+ * /
+ \endcode
+
+ You can simplify this as follows:
+
+ \code
+ / *!
+ \target assertions
+
+ Assertions make some statement about the text at the
+ point where they occur in the regexp, but they do not
+ match any characters.
+
+ ...
+
+ Regexps are built up from expressions, quantifiers, and
+ \l assertions.
+ * /
+ \endcode
+
+ For the one-parameter version, the braces can often be omitted.
+ The \\l command supports several ways of linking:
+
+ \list
+
+ \li \c {\l QWidget} - The name of a class documented with the \l
+ {class-command} {\\class} command.
+
+ \li \c {\l QWidget::sizeHint()} - The signature of a function without
+ parameters. If a matching function without parameters can't be found,
+ the link is satisfied with the first matching function found.
+
+ \li \c {\l QWidget::removeAction(QAction* action)} - The signature
+ of a function with parameters. If an exact match is not found, the
+ link is not satisfied and qdoc reports a \e {Can't link to...} error.
+
+ \li \c {\l <QtGlobal>} - The subject of a \l {headerfile-command}
+ {\\headerfile} command.
+
+ \li \c {\l widgets/wiggly} - The relative path used in an \l
+ {example-command} {\\example} command.
+
+ \li \c {\l {QWidget Class Reference}} - The title used in a
+ \l {title-command} {\\title} command.
+
+ \li \c {\l {Introduction to QDoc}}- The text from one of the
+ \l{part-command} {\\part}, \l{chapter} {\\chapter}, or \l
+ {sectionOne-command} {\\section} commands.
+
+ \li \c {\l fontmatching} - The argument of a \l {target-command}
+ {\\target} command.
+
+ \li \c {\l {Shared Classes}} - A keyword named in a \l
+ {keyword-command} {\\keyword} command.
+
+ \li \c {\l http://qt-project.org/} - A URL.
+
+ \endlist
+
+ QDoc also tries to make a link out of any word that doesn't
+ resemble a normal English word, for example, Qt class names or
+ functions, like QWidget or QWidget::sizeHint(). In these cases,
+ the \\l command can actually be omitted, but by using the command,
+ you ensure that QDoc will emit a warning if it cannot find the
+ link target. In addition, if you only want the function name to
+ appear in the link, you can use the following syntax:
+
+ \list
+ \li \c {\l {QWidget::} {sizeHint()}}
+ \endlist
+
+ QDoc renders this as:
+
+ \quotation
+ \l {QWidget::} {sizeHint()}
+ \endquotation
+
+ \section2 Fixing Ambiguous Links
+
+ Because of the modularization of Qt beginning with Qt 5.0, The
+ possibility that qdoc will have to deal with ambiguous links has
+ increased. An ambiguous link is one that has a matching target in
+ more than one Qt module, e.g. the same section title can appear in
+ more than one Qt module, or the name of a C++ class in one module
+ can also be the name of a QML type in another module. A real
+ example in Qt5 is the name Qt itself. Qt is the name of both a C++
+ namespace in QtCore and a QML type in QtQml.
+
+ Suppose we want to link to the \l {Qt} {Qt C++ namespace}. At the
+ time qdoc generated this HTML page, that link was correct. Does
+ it still go to the C++ namespace? Qdoc generated that link from
+ this link command:
+
+ \list
+ \li \c {\l {Qt} {Qt C++ namespace}}
+ \endlist
+
+ Now suppose we want to link to the \l [QML] {Qt} {Qt QML type}.
+ At the time qdoc generated this HTML page, that link was also
+ correct, but we had to use this link command:
+
+ \list
+ \li \c {\l [QML] {Qt} {Qt QML type}}
+ \endlist
+
+ The \e {QML} in \e {square brackets} tells qdoc to accept a
+ matching target only if the traget is on a QML page. Qdoc actually
+ finds the C++ namespace target first, but since that target is on
+ a C++ page, qdoc ignores it and keeps looking until it finds the
+ same target on a QML page.
+
+ Without the guidance in the \e{\\l command} in the optional \e
+ {square bracket} argument, qdoc links to the first matching target
+ it finds. qdoc can't warn that the link was ambiguous in such
+ cases because it doesn't know that another matching target exists.
+
+ \section2 What arguments can appear in square brackets?
+
+ A link command with square bracket argument has the following syntax:
+ \list
+ \c {\l [QML|CPP|DOC|QtModuleName] {link target} {link text}}
+ \endlist
+
+ The \e {square bracket} argument is only allowed in the \c {\\l
+ (link)} command. The example above shows how \c QML is used as the
+ \e {square brackets} argument to force qdoc to match a QML target.
+ Most often, this will be a QML type, but it can also be a QML
+ member function of property.
+
+ In the example, qdoc didn't need a \e {square bracket} argument to
+ find the Qt C++ namespace page, because that one was the first
+ matching target qdoc found anyway. However, to force qdoc to find
+ a C++ target when a matching QML target gets in the way, \c CPP
+ can be used as the \e {square bracket} argument. For example:
+
+ \list
+ \li \c {\l [CPP] {Qt} {Qt C++ namespace}}
+ \endlist
+
+ ...will force qdoc to ignore the Qt QML type and continue
+ searching until it matches the Qt C++ namespace.
+
+ If the link target is neither a C++ nor a QML entity, \c {DOC} can
+ be used as the \e {square bracket} argument to prevent qdoc from
+ matching either of those. At this writing, there were no cases of
+ ambiguous links where using \c {DOC} was required.
+
+ Often, the documentor knows which Qt module the link target is
+ in. When the module name is known, use the module name as the \e
+ {square bracket} argument. In the example above, if we know that
+ the QML type named Qt is located in the QtQml module, we can write
+ the link command like this:
+
+ \list
+ \li \c {\l [QtQml] {Qt} {Qt QML type}}
+ \endlist
+
+ When a module name is used as the \e {square bracket} argument,
+ qdoc will search for link the target in that module only. This
+ makes searching for link targets more efficient.
+
+ Finally, the module name and entity type arguments can be
+ combined, separated by a blank, so something like this is also
+ allowed:
+
+ \list
+ \li \c {\l [CPP QtQml] {Window} {C++ class Window}}
+ \endlist
+
+ As of this writing, there were no cases where combining the two
+ was required.
+
+ See also \l {sa-command} {\\sa}, \l {target-command} {\\target},
+ and \l {keyword-command} {\\keyword}.
+
+
+ \target sa-command
+ \section1 \\sa (see also)
+
+ The \\sa command defines a list of links that will be rendered in
+ a separate "See also" section at the bottom of the documentation
+ unit.
+
+ The command takes a comma-separated list of links as its
+ argument. If the line ends with a comma, you can continue
+ the list on the next line. The general syntax is:
+
+ \code
+ \sa {the first link}, {the second link},
+ {the third link}, ...
+ \endcode
+
+ QDoc will automatically try to generate "See also" links
+ interconnecting a property's various functions. For example, a
+ setVisible() function will automatically get a link to visible()
+ and vice versa.
+
+ In general, QDoc will generate "See also" links that interconnect
+ the functions that access the same property. It recognizes four
+ different syntax versions:
+
+ \list
+ \li \c property()
+ \li \c setProperty()
+ \li \c isProperty()
+ \li \c hasProperty()
+ \endlist
+
+ The \\sa command supports the same kind of links as the \l
+ {l-command} {\\l} command.
+
+ \code
+ / *!
+ Appends the actions \a actions to this widget's
+ list of actions.
+
+ \sa removeAction(), QMenu, addAction()
+ * /
+ void QWidget::addActions(QList<QAction *> actions)
+ {
+ ...
+ }
+ \endcode
+
+ QDoc renders this as:
+
+ \quotation
+ \b {void QWidget::addActions ( QList<QAction*>
+ \e actions )}
+
+ Appends the actions \e actions to this widget's list of
+ actions.
+
+ See also \l {QWidget::removeAction()} {removeAction()},
+ \l QMenu, and \l {QWidget::addAction()} {addAction()}.
+ \endquotation
+
+ See also \l {l-command} {\\l}, \l {target-command} {\\target} and
+ \l {keyword-command} {\\keyword}.
+
+
+ \target target-command
+ \section1 \\target
+
+ The \\target command names a place in the documentation that you
+ can link to using the \l {l-command} {\\l (link)} and \l
+ {sa-command} {\\sa (see also)} commands.
+
+ The text up to the line break becomes the target name. Be sure to
+ follow the target name with a line break. Curly brackets are not
+ required around the target name, but they may be required when the
+ target name is used in a link command. See below.
+
+ \code
+ / *!
+ \target capturing parentheses
+ \section1 Capturing Text
+
+ Parentheses allow us to group elements together so that
+ we can quantify and capture them.
+
+ ...
+ * /
+ \endcode
+
+ The target name \e{capturing parentheses} can be linked from
+ within the same document containing the target in the following way:
+
+ \list
+ \li \c {\l {capturing parentheses}} (from within the same QDoc comment)
+ \endlist
+
+ \note The brackets in the link example are required because the
+ target name contains spaces.
+
+ See also \l {l-command} {\\l}, \l {sa-command} {\\sa} and \l
+ {keyword-command} {\\keyword}.
+
+ \target keyword-command
+ \section1 \\keyword
+
+ The \\keyword command names a place in the documentation that you
+ can link to using the \l {l-command} {\\l (link)} and \l
+ {sa-command} {\\sa (see also)} commands.
+
+ The \\keyword command is like the \l {target-command} {\\target}
+ command, except when linking to keyword the link goes to the top of
+ the QDoc comment where the \\keyword appears in. If you want to
+ create a link target to a \c section unit within a \\page, use
+ \\target instead. A keyword can be linked from anywhere using a
+ simple syntax.
+
+ Keywords must be unique over all the documents processed during
+ the QDoc run. The command uses the rest of the line as its
+ argument. Be sure to follow the keyword with a line break.
+
+
+ \code
+ / *!
+ \class QRegExp
+ \reentrant
+ \brief The QRegExp class provides pattern
+ matching using regular expressions.
+ \ingroup tools
+ \ingroup misc
+ \ingroup shared
+
+ \keyword regular expression
+
+ Regular expressions, or "regexps", provide a way to
+ find patterns within text.
+
+ ...
+ * /
+ \endcode
+
+ The location marked with the keyword can be linked to with:
+
+ \code
+ / *!
+ When a string is surrounded by slashes, it is
+ interpreted as a \l {QRegExp}{regular expression}.
+ * /
+ \endcode
+
+ QDoc renders this as:
+
+ \quotation
+ When a string is surrounded by slashes, it is
+ interpreted as a \l {regular expression}.
+ \endquotation
+
+ If the keyword text contains spaces, the brackets are required.
+
+ See also \l {l-command} {\\l (link)}, \l {sa-command} {\\sa (see
+ also)} and \l {target-command} {\\target}.
+
+*/
+
+
+/*!
+ \page 09-qdoc-commands-includingimages.html
+ \previouspage Creating Links
+ \contentspage QDoc Manual
+ \nextpage Tables and Lists
+
+ \title Including Images
+
+ The graphic commands makes it possible to include images in the
+ documentation. The images can be rendered as separate paragraphs,
+ or within running text.
+
+ \target image-command
+ \section1 \\image
+
+ The \\image command expands to the image specified by its first
+ argument, and renders it centered as a separate paragraph.
+
+ The command takes two arguments. The first argument is the name of
+ the image file. The second argument is optional and is a simple
+ description of the image, equivalent to the HTML alt="" in an image
+ tag. The description is used for tooltips and for browsers that don't
+ support images, like the Lynx text browser.
+
+ The remaining text \e{after} the file name is the optional,
+ description argument. Be sure to follow the file name or the
+ description with a line break. Curly brackets are required if the
+ description argument spans multiple lines.
+
+ \code
+ / *!
+ Qt is a C++ toolkit for cross-platform GUI application development.
+
+ \image happyguy.jpg "Happy guy"
+
+ Qt provides single-source portability across Microsoft
+ Windows, OS X, Linux, and all major commercial Unix
+ variants. It is also available for embedded devices.
+ * /
+ \endcode
+
+ QDoc renders this as:
+
+ \quotation
+ Qt is a C++ toolkit for cross-platform GUI application development.
+
+ \image happyguy.jpg image "Happy guy"
+
+ Qt provides single-source portability across Microsoft
+ Windows, OS X, Linux, and all major commercial Unix
+ variants. It is also available for embedded devices.
+ \endquotation
+
+ See also \l {inlineimage-command} {\\inlineimage} and \l
+ {caption-command} {\\caption}.
+
+ \target inlineimage-command
+ \section1 \\inlineimage
+
+ The \\inlineimage command expands to the image specified by its
+ argument. The image is rendered inline with the rest of the text.
+
+ The command takes two arguments. The first argument is the name of
+ the image file. The second argument is optional and is a simple
+ description of the image, equivalent to the HTML alt="" in an image
+ tag. The description is used for tooltips, and for when a browser
+ doesn't support images, like the Lynx text browser.
+
+ The most common use of the \\inlineimage command is in lists and
+ tables. Here is an example of including inline images in a list:
+
+ \code
+ / *!
+ \list 1
+ \li \inlineimage happy.gif Oh so happy!
+ \li \inlineimage happy.gif Oh so happy!
+ \li \inlineimage happy.gif Oh so happy!
+ \endlist
+ * /
+ \endcode
+
+ QDoc renders this as:
+
+ \list 1
+ \li \inlineimage happy.gif Oh so happy!
+ \li \inlineimage happy.gif Oh so happy!
+ \li \inlineimage happy.gif Oh so happy!
+ \endlist
+
+ Here is an example of including inline images in a table:
+
+ \code
+ / *!
+ \table
+ \header
+ \li Qt
+ \li Qt Creator
+ \row
+ \li \inlineimage happy.gif Oh so happy!
+ \li \inlineimage happy.gif Oh so happy!
+ \row
+ \li \inlineimage happy.gif Oh so happy!
+ \li \inlineimage happy.gif Oh so happy!
+ \endtable
+ * /
+ \endcode
+
+ QDoc renders this as:
+
+ \raw HTML
+ <table align="center" cellpadding="2"
+ cellspacing="1" border="0">
+ <tr valign="top" bgcolor="#a2c511">
+ <th>Qt</th>
+ <th>Qt Creator</th>
+ </tr>
+ <tr valign="top" bgcolor="#f0f0f0">
+ <td><img src="images/happy.gif" alt="Oh so happy!" />
+ </td>
+ <td><img src="images/happy.gif" alt="Oh so happy!" />
+ </td>
+ </tr>
+ <tr valign="top" bgcolor="#f0f0f0">
+ <td><img src="images/happy.gif" alt="Oh so happy!"/>
+ </td>
+ <td><img src="images/happy.gif" alt="Oh so happy!" />
+ </td>
+ </tr>
+ </table>
+ \endraw
+
+ The command can also be used to insert an image inline with the
+ text.
+
+ \code
+ / *!
+ \inlineimage training.jpg Qt Training
+ The Qt Programming course is offered as a
+ five day Open Enrollment Course. The classes
+ are open to the public. Although the course is open
+ to anyone who wants to learn, attendees should
+ have significant experience in C++ development
+ to derive maximum benefit from the course.
+ * /
+ \endcode
+
+ QDoc renders this as:
+
+ \quotation
+ \inlineimage training.jpg Qt Training
+ The Qt Programming course is offered as a
+ five day Open Enrollment Course. The classes
+ are open to the public. Although the course is open
+ to anyone who wants to learn, attendees should
+ have significant experience in C++ development
+ to derive maximum benefit from the course.
+ \endquotation
+
+ See also \l {image-command} {\\image} and \l {caption-command} {\\caption}.
+
+ \target caption-command
+ \section1 \\caption
+
+ The \\caption command provides a caption for an image.
+
+ The command takes all the text up to the end of the paragraph to
+ be the caption. Experiment until you get the effect you want.
+
+ \code
+ / *!
+ \table 100%
+ \row
+ \li \image windowsvista-pushbutton.png
+ \caption The QPushButton widget provides a command button.
+ \li \image windowsvista-toolbutton.png
+ \caption The QToolButton class provides a quick-access button to commands
+ or options, usually used inside a QToolBar.
+ \endtable
+ * /
+ \endcode
+
+ QDoc renders this as:
+
+ \table 100%
+ \row
+ \li \image windowsvista-pushbutton.png
+ \caption The QPushButton widget provides a command button.
+ \li \image windowsvista-toolbutton.png
+ \caption The QToolButton class provides a quick-access button to commands
+ or options, usually used inside a QToolBar.
+ \endtable
+
+ See also \l {image-command} {\\image} and \l {inlineimage-command}
+ {\\inlineimage}
+*/
+
+
+/*!
+ \page 10-qdoc-commands-tablesandlists.html
+ \previouspage Including Images
+ \contentspage QDoc Manual
+ \nextpage Special Content
+
+ \title Tables and Lists
+
+ These commands enable creating lists and tables. A list is
+ rendered left aligned as a separate paragraph. A table is rendered
+ centered as a separate paragraph. The table width depends on the
+ width of its contents.
+
+ \target table-command
+ \section1 \\table
+
+ The \\table and \\endtable commands delimit the contents of a
+ table.
+
+ The command accepts a single argument specifying the table's width
+ as a percentage of the page width:
+
+ \code
+ / *!
+ \table 100 %
+
+ ...
+
+ \endtable
+ * /
+ \endcode
+
+ The code above ensures that the table will fill all available
+ space. If the table's width is smaller than 100 %, the table will
+ be centered in the generated documentation.
+
+ A table can contain headers, rows and columns. A row starts with a
+ \l {row-command} {\\row} command and consists of cells, each of which
+ starts with an \l {li-command} {\\li} command. There is also a \l
+ {header-command} {\\header} command which is a special kind of row
+ that has a special format.
+
+ \code
+ / *!
+ \table
+ \header
+ \li Qt Core Feature
+ \li Brief Description
+ \row
+ \li \l {Signal and Slots}
+ \li Signals and slots are used for communication
+ between objects.
+ \row
+ \li \l {Layout Management}
+ \li The Qt layout system provides a simple
+ and powerful way of specifying the layout
+ of child widgets.
+ \row
+ \li \l {Drag and Drop}
+ \li Drag and drop provides a simple visual
+ mechanism which users can use to transfer
+ information between and within applications.
+ \endtable
+ * /
+ \endcode
+
+ QDoc renders this as:
+
+ \raw HTML
+ <table align="center" cellpadding="2"
+ cellspacing="1" border="0">
+ <tr valign="top" bgcolor="#a2c511">
+ <th>Qt Core Feature</th>
+ <th>Brief Description</th>
+ </tr>
+
+ <tr valign="top" bgcolor="#d0d0d0">
+ <td>
+ <a href="http://doc.qt.io/qt-5/signalsandslots.html">
+ Signals and Slots</a>
+ </td>
+ <td>Signals and slots are used for communication
+ between objects.</td>
+ </tr>
+
+ <tr valign="top" bgcolor="#c0c0c0">
+ <td>
+ <a href="http://doc.qt.io/qt-5/layout.html">
+ Layout Management</a></td>
+ <td>The Qt layout system provides a simple
+ and powerful way of specifying the layout
+ of child widgets.</td>
+ </tr>
+
+ <tr valign="top" bgcolor="#d0d0d0">
+ <td>
+ <a href="http://doc.qt.io/qt-5/dnd.html">
+ Drag and Drop</a></td>
+ <td>Drag and drop provides a simple visual
+ mechanism which users can use to transfer
+ information between and within applications.</td>
+ </tr>
+
+ </table>
+ \endraw
+
+ You can also make cells span several rows and columns. For
+ example:
+
+ \code
+ / *!
+ \table
+ \header
+ \li {3,1} This header cell spans three columns,
+ but only one row.
+ \row
+ \li {2, 1} This table cell spans two columns,
+ but only one row
+ \li {1, 2} This table cell spans only one column,
+ but two rows.
+ \row
+ \li A regular table cell
+ \li A regular table cell
+ \endtable
+ * /
+ \endcode
+
+ QDoc renders this as:
+
+ \raw HTML
+ <table align="center" cellpadding="2" cellspacing="1"
+ border="0">
+
+ <tr valign="top" bgcolor="#a2c511">
+ <th colspan="3" rowspan=" 1">
+ This header cell spans three columns, but only one row.
+ </th>
+ </tr>
+
+ <tr valign="top" bgcolor="#d0d0d0">
+ <td colspan="2" rowspan=" 1">
+ This table cell spans two columns, but only one row.
+ </td>
+ <td rowspan=" 2">
+ This table cell spans only one column, but two rows.
+ </td>
+ </tr>
+
+ <tr valign="top" bgcolor="#c0c0c0">
+ <td>A regular table cell</td>
+ <td>A regular table cell</td>
+ </tr>
+
+ </table>
+ \endraw
+
+ See also \l {header-command} {\\header}, \l {row-command} {\\row} and \l {li-command} {\\li}.
+
+ \target header-command
+ \section1 \\header
+
+ The \\header command indicates that the following table cells are
+ the current table's column headers.
+
+ The command can only be used within the \l{table-command}
+ {\\table...\\endtable} commands. A header can contain several
+ cells. A cell is created with the \l {li-command} {\\li} command.
+
+ A header cell's text is centered within the table cell and
+ rendered using a bold font.
+
+ \code
+ / *!
+ \table
+ \header
+ \li Qt Core Feature
+ \li Brief Description
+ \row
+ \li \l {Signal and Slots}
+ \li Signals and slots are used for communication
+ between objects.
+ \endtable
+ * /
+ \endcode
+
+ QDoc renders this as:
+
+ \raw HTML
+ <table align="center" cellpadding="2"
+ cellspacing="1" border="0">
+ <tr valign="top" bgcolor="#a2c511">
+ <th>Qt Core Feature</th>
+ <th>Brief Description</th>
+ </tr>
+
+ <tr valign="top" bgcolor="#d0d0d0">
+ <td>
+ <a href="http://doc.qt.io/qt-5/signalsandslots.html">
+ Signals and Slots</a>
+ </td>
+ <td>Signals and slots are used for communication
+ between objects.</td>
+ </tr>
+ </table>
+ \endraw
+
+ See also \l {table-command} {\\table}, \l {row-command} {\\row} and \l {li-command} {\\li}.
+
+ \target row-command
+ \section1 \\row
+
+ The \\row command begins a new row in a table. The \l {li-command}
+ {\\li items} that belong in the new row will immediately follow the
+ \\row.
+
+ The command can only be used within the \l{table-command}
+ {\\table...\\endtable} commands. A row can contain several
+ cells. A cell is created with the \l {li-command} {\\li} command.
+
+ The background cell color of each row alternates between two
+ shades of grey, making it easier to distinguish the rows from each
+ other. The cells' contents is left aligned.
+
+ \code
+ / *!
+ \table
+ \header
+ \li Qt Core Feature
+ \li Brief Description
+ \row
+ \li \l {Signal and Slots}
+ \li Signals and slots are used for communication
+ between objects.
+ \row
+ \li \l {Layout Management}
+ \li The Qt layout system provides a simple
+ and powerful way of specifying the layout
+ of child widgets.
+ \row
+ \li \l {Drag and Drop}
+ \li Drag and drop provides a simple visual
+ mechanism which users can use to transfer
+ information between and within applications.
+ \endtable
+ * /
+ \endcode
+
+ QDoc renders this as:
+
+ \raw HTML
+ <table align="center" cellpadding="2"
+ cellspacing="1" border="0">
+ <tr valign="top" bgcolor="#a2c511">
+ <th>Qt Core Feature</th>
+ <th>Brief Description</th>
+ </tr>
+
+ <tr valign="top" bgcolor="#d0d0d0">
+ <td>
+ <a href="http://doc.qt.io/qt-5/signalsandslots.html">
+ Signals and Slots</a>
+ </td>
+ <td>Signals and slots are used for communication
+ between objects.</td>
+ </tr>
+
+ <tr valign="top" bgcolor="#c0c0c0">
+ <td>
+ <a href="http://doc.qt.io/qt-5/layout.html">
+ Layout Management</a></td>
+ <td>The Qt layout system provides a simple
+ and powerful way of specifying the layout
+ of child widgets.</td>
+ </tr>
+
+ <tr valign="top" bgcolor="#d0d0d0">
+ <td>
+ <a href="http://doc.qt.io/qt-5/dnd.html">
+ Drag and Drop</a></td>
+ <td>Drag and drop provides a simple visual
+ mechanism which users can use to transfer
+ information between and within applications.</td>
+ </tr>
+
+ </table>
+ \endraw
+
+ See also \l {table-command} {\\table}, \l {header-command}
+ {\\header}, and \l {li-command} {\\li}.
+
+ \target value-command
+ \section1 \\value
+
+ The \\value command starts the documentation of a C++ enum item.
+
+ The command's first argument is the enum item. Then follows its
+ associated description. The description argument ends at the next
+ blank line or \\value. The arguments are rendered within a table.
+
+ The documentation will be located in the associated class, header
+ file or namespace documentation. See the \l {enum-command}
+ {\\enum} documentation for an example.
+
+ \note Since Qt 5.4, \\value command can also be used outside the
+ \l {enum-command} {\\enum} topic. In this case, QDoc renders a
+ two-column table listing the constant name (taken as-is from the
+ first argument) and its description. This can be used, for
+ example, in \l {qmlproperty-command}{\\qmlproperty} topic for
+ documenting acceptable values for a QML enumeration property.
+
+ See also \l {enum-command} {\\enum} and \l {omitvalue-command} {\\omitvalue}.
+
+ \target omitvalue-command
+ \section1 \\omitvalue
+
+ The \\omitvalue command excludes a C++ enum item from the
+ documentation.
+
+ The command's only argument is the name of the enum item that will
+ be omitted. See the \l {enum-command} {\\enum} documentation for
+ an example.
+
+ See also \l {enum-command} {\\enum} and \l {value-command}
+ {\\value}.
+
+ \target list-command
+ \section1 \\list
+
+ The \\list and \\endlist commands delimit a list of items.
+
+ Create each list item with the \l {li-command} {\\li} command. A
+ list always contains one or more items. Lists can be nested. For
+ example:
+
+ \code
+ / *!
+ \list
+ \li Qt Reference Documentation: Getting Started
+ \list
+ \li How to Learn Qt
+ \li Installation
+ \list
+ \li Qt/X11
+ \li Qt/Windows
+ \li Qt/Mac
+ \li Qt/Embedded
+ \endlist
+ \li Tutorial and Examples
+ \endlist
+ \endlist
+ * /
+ \endcode
+
+ QDoc renders this as:
+
+ \list
+ \li Qt Reference Documentation: Getting Started
+ \list
+ \li How to Learn Qt
+ \li Installation
+ \list
+ \li Qt/X11
+ \li Qt/Windows
+ \li Qt/Mac
+ \li Qt/Embedded
+ \endlist
+ \li Tutorial and Examples
+ \endlist
+ \endlist
+
+ The \\list command takes an optional argument providing
+ alternative appearances for the list items.
+
+ \code
+ / *!
+ \list
+ \li How to Learn Qt
+ \li Installation
+ \li Tutorial and Examples
+ \endlist
+ * /
+ \endcode
+
+ QDoc renders the list items with bullets (the default):
+
+ \list
+ \li How to Learn Qt
+ \li Installation
+ \li Tutorial and Examples
+ \endlist
+
+ \warning There appears to be a bug in qdoc here. If you include
+ any of the argument types, you get a numeric list. We're looking
+ into it.
+
+ If you provide 'A' as an argument to the \\list command, the
+ bullets are replaced with characters in alphabetical order:
+
+ \list A
+ \li How to Learn Qt
+ \li Installation
+ \li Tutorial and Examples
+ \endlist
+
+ If you replace 'A' with '1', the list items are numbered in
+ ascending order:
+
+ \list 1
+ \li How to Learn Qt
+ \li Installation
+ \li Tutorial and Examples
+
+ \endlist
+
+ If you provide 'i' as the argument, the bullets are replaced with
+ roman numerals:
+
+ \list i
+ \li How to Learn Qt
+ \li Installation
+ \li Tutorial and Examples
+ \endlist
+
+ Finally, you can make the list items appear with roman numbers
+ following in ascending order if you provide 'I' as the optional
+ argument:
+
+ \list I
+ \li How to Learn Qt
+ \li Installation
+ \li Tutorial and Examples
+ \endlist
+
+ You can also make the listing start at any character or number by
+ simply provide the number or character you want to start at. For
+ example:
+
+ \code
+ / *!
+ \list G
+ \li How to Learn Qt
+ \li Installation
+ \li Tutorial and Examples
+ \endlist
+ * /
+ \endcode
+
+ \note This doesn't work in DITA XML, so don't use it because it
+ produces a DITA XML file that doesn't validate. There probably is
+ a way to do this in DITA, so if we figure it out, we will put it
+ in. But this capability is not used anywhere other than right
+ here, so it probably isn't important. For now, if you use this
+ option, qdoc will ignore it and produce a list without it.
+
+ QDoc renders this as:
+
+ \list G
+ \li How to Learn Qt
+ \li Installation
+ \li Tutorial and Examples
+ \endlist
+
+ See also \l {li-command} {\\li}.
+
+ \target li-command
+ \section1 \\li (table cell, list item)
+
+ The \\li command marks a table cell or a list item. This command
+ is only used in \l{table-command} {tables} and \l{list-command}
+ {lists}.
+
+ It considers everything as its argument until the next \\li command, until the
+ next \l {table-command} {\\endtable}, or \l {list-command} {\\endlist}
+ command. See \l {table-command} {\\table} and \l {list-command} {\\list}
+ for examples.
+
+ If the command is used within a table, you can also specify
+ how many rows or columns the item should span.
+
+ \code
+ / *!
+ \table
+ \header
+ \li {3,1} This header cell spans three columns
+ but only one row.
+ \row
+ \li {2, 1} This table item spans two columns
+ but only one row
+ \li {1, 2} This table item spans only one column,
+ but two rows.
+ \row
+ \li A regular table item
+ \li A regular table item
+ \endtable
+ * /
+ \endcode
+
+ QDoc renders this as:
+
+ \raw HTML
+ <table align="center" cellpadding="2" cellspacing="1"
+ border="0">
+
+ <tr valign="top" bgcolor="#a2c511">
+ <th colspan="3" rowspan=" 1">
+ This header cell spans three columns, but only one row.
+ </th>
+ </tr>
+
+ <tr valign="top" bgcolor="#d0d0d0">
+ <td colspan="2" rowspan=" 1">
+ This table item spans two columns, but only one row.
+ </td>
+ <td rowspan=" 2">
+ This table item spans only one column, but two rows.
+ </td>
+ </tr>
+
+ <tr valign="top" bgcolor="#c0c0c0">
+ <td>A regular table item</td>
+ <td>A regular table item</td>
+ </tr>
+
+ </table>
+ \endraw
+
+ If not specified, the item will span one column and one row.
+
+ See also \l {table-command} {\\table}, \l {header-command}
+ {\\header}, and \l {list-command} {\\list}.
+
+*/
+
+
+/*!
+ \page 11-qdoc-commands-specialcontent.html
+ \previouspage Tables and Lists
+ \contentspage QDoc Manual
+ \nextpage Miscellaneous
+
+ \title Special Content
+
+ The document contents commands identify parts of the documentation,
+ parts with a special rendering, conceptual meaning or
+ function.
+
+ \target quotation-command
+ \section1 \\quotation
+
+ The \\quotation and \\endquotation commands delimit a long quotation.
+
+ The text in the delimited block is surrounded by
+ \b{<blockquote>} and \b{</blockquote>} in the html output,
+ e.g.:
+
+ \code
+ / *!
+ Although the prospect of a significantly broader market is
+ good news for Firstlogic, the notion also posed some
+ challenges. Dave Dobson, director of technology for the La
+ Crosse, Wisconsin-based company, said:
+
+ \quotation
+ As our solutions were being adopted into new
+ environments, we saw an escalating need for easier
+ integration with a wider range of enterprise
+ applications.
+ \endquotation
+ * /
+ \endcode
+
+ The text in the \b{\\quotation} block will appear in the generated HTML as:
+
+ \code
+ <blockquote>
+ <p>As our solutions were being adopted into new environments,
+ we saw an escalating need for easier integration with a wider
+ range of enterprise applications.</p>
+ </blockquote>
+ \endcode
+
+ The built-in style sheet for most browsers will render the
+ contents of the <blockquote> tag with left and right
+ indentations. The example above would be rendered as:
+
+ \quotation
+ As our solutions were being adopted into new
+ environments, we saw an escalating need for easier
+ integration with a wider range of enterprise
+ applications.
+ \endquotation
+
+ But you can redefine the \b{<blockquote>} tag in your style.css file.
+
+ \target footnote-command
+ \section1 \\footnote
+
+ The \\footnote and \\endfootnote commands delimit a footnote.
+
+ The footnote is rendered at the bottom of the page.
+
+ \warning The \b{\\footnote} and \b{\\endfootnote} commands
+ have not been implemented. The footnote is rendered as a regular
+ HTML paragraph.
+
+ \target note-command
+ \section1 \\note
+
+ The \\note command defines a new paragraph preceded by "Note:"
+ in bold.
+
+ \target tableofcontents-command
+ \section1 \\tableofcontents
+
+ The \\tableofcontents command has been disabled because QDoc
+ now generates a table of contents automatically.
+
+ The automatically generated table of contents appears in the upper
+ righthand corner of the page.
+
+ \target brief-command
+ \section1 \\brief
+
+ The \\brief command introduces a one-sentence description of a
+ class, namespace, header file, property, or variable.
+
+ The brief text is used to introduce the documentation of the
+ associated object, and in lists generated using the \l
+ {generatelist-command} {\\generatelist} command and the \l
+ {annotatedlist-command} {\\annotatedlist} command.
+
+ The \\brief command can be used in two significant different ways:
+ \l {brief class} {One for classes, namespaces and header files},
+ and \l {brief-property} {one for properties and variables}.
+
+ \target brief-property
+
+ When the \\brief command is used to describe a property or a
+ variable, the brief text must be a sentence fragment starting with
+ "whether" (for a boolean property or variable) or starting with
+ "the" (for any other property or variable).
+
+ For example the boolean QWidget::isWindow property:
+
+ \code
+ / *!
+ \property QWidget::isActiveWindow
+ \brief Whether this widget's window is the active window
+
+ The active window is the window that contains the widget that
+ has keyboard focus.
+
+ When popup windows are visible, this property is \c true
+ for both the active window \e and the popup.
+
+ \sa activateWindow(), QApplication::activeWindow()
+ * /
+ \endcode
+
+ and the QWidget::geometry property
+
+ \code
+ / *!
+ \property QWidget::geometry
+ \brief The geometry of the widget relative to its parent and
+ excluding the window frame
+
+ When changing the geometry, the widget, if visible,
+ receives a move event (moveEvent()) and/or a resize
+ event (resizeEvent()) immediately.
+
+ ...
+
+ \sa frameGeometry(), rect(), ...
+ * /
+ \endcode
+
+ QDoc renders this as:
+
+ \quotation
+ \raw HTML
+ <h3>geometry :
+ <a href="http://doc.qt.io/qt-5/qrect.html">QRect</a>
+ </h3>
+ \endraw
+
+ This property holds the geometry of the widget relative
+ to its parent and excluding the window frame.
+
+ ...
+
+ Access functions:
+ \list
+ \li \b {const QRect & geometry () const}
+ \li \b {void setGeometry ( int x, int y, int w, int h )}
+ \li \b {void setGeometry ( const QRect & )}
+ \endlist
+
+ See also \l
+ {QWidget::frameGeometry()} {frameGeometry()}, \l
+ {QWidget::rect()} {rect()}, ...
+ \endquotation
+
+ \target brief class
+
+ When the \\brief command is used to describe a class, we recommend
+ using a complete sentence like this:
+
+ \code
+ The <classname> class is|provides|contains|specifies...
+ \endcode
+
+ \warning Do not repeat your detailed description with the same sentence as
+ the brief statement will be the first paragraph of the detailed
+ description.
+
+ \code
+ / *!
+ \class PreviewWindow
+ \brief The PreviewWindow class is a custom widget
+ displaying the names of its currently set
+ window flags in a read-only text editor.
+
+ The PreviewWindow class inherits QWidget. The widget
+ displays the names of its window flags set with the
+ setWindowFlags() function. It is also provided with a
+ QPushButton that closes the window.
+
+ ...
+
+ \sa QWidget
+ * /
+ \endcode
+
+ QDoc renders this as:
+
+ \quotation
+ \raw HTML
+ <h1>PreviewWindow Class Reference</h1>
+ \endraw
+
+ The PreviewWindow class is a custom widget displaying
+ the names of its currently set window flags in a
+ read-only text editor. \l {preview window} {More...}
+
+ \raw HTML
+ <h3>Properties</h3>
+ \endraw
+
+ \list
+ \li 52 properties inherited from QWidget
+ \li 1 property inherited from QObject
+ \endlist
+
+ \raw HTML
+ <h3>Public Functions</h3>
+ \endraw
+
+ \list
+ \li \l {constructor} {PreviewWindow}(QWidget *parent = 0)
+ \li void \l {function} {setWindowFlags}(Qt::WindowFlags flags)
+ \endlist
+
+ \list
+ \li 183 public functions inherited from QWidget
+ \li 28 public functions inherited from QObject
+ \endlist
+
+ \raw HTML
+ <h3>Public Slots</h3>
+ \endraw
+
+ \list
+ \li 17 public slots inherited from QWidget
+ \li 1 public slot inherited from QObject
+ \endlist
+
+ \raw HTML
+ <h3>Additional Inherited Members</h3>
+ \endraw
+
+ \list
+ \li 1 signal inherited from QWidget
+ \li 1 signal inherited from QObject
+ \li 4 static public members inherited from QWidget
+ \li 4 static public members inherited from QObject
+ \li 39 protected functions inherited from QWidget
+ \li 7 protected functions inherited from QObject
+ \endlist
+
+ \target preview window
+
+ \raw HTML
+ <hr />
+ <h2>Detailed Description</h2>
+ \endraw
+
+ The PreviewWindow class is a custom widget displaying
+ the names of its currently set window flags in a
+ read-only text editor.
+
+ The PreviewWindow class inherits QWidget. The widget
+ displays the names of its window flags set with the \l
+ {function} {setWindowFlags()} function. It is also
+ provided with a QPushButton that closes the window.
+
+ ...
+
+ See also QWidget.
+
+ \raw HTML
+ <hr />
+ <h2>Member Function Documentation</h2>
+ \endraw
+
+ \target constructor
+ \raw HTML
+ <h3>PreviewWindow(QWidget *parent = 0)</h3>
+ \endraw
+
+ Constructs a preview window widget with \e parent.
+
+ \target function
+ \raw HTML
+ <h3>setWindowFlags(Qt::WindowFlags flags)</h3>
+ \endraw
+
+ Sets the widgets flags using the
+ QWidget::setWindowFlags() function.
+
+ Then runs through the available window flags,
+ creating a text that contains the names of the flags
+ that matches the flags parameter, displaying
+ the text in the widgets text editor.
+ \endquotation
+
+ Using \\brief in a \l{namespace-command}{\\namespace}:
+
+ \code
+ / *!
+ \namespace Qt
+
+ \brief The Qt namespace contains miscellaneous identifiers
+ used throughout the Qt library.
+ * /
+ \endcode
+
+ Using \\brief in a \l{headerfile-command}{\\headerfile}:
+
+ \code
+ / *!
+ \headerfile <QtGlobal>
+ \title Global Qt Declarations
+
+ \brief The <QtGlobal> header file provides basic
+ declarations and is included by all other Qt headers.
+
+ \sa <QtAlgorithms>
+ * /
+ \endcode
+
+ See also \l{property-command} {\\property}, \l{class-command}
+ {\\class}, \l{namespace-command} {\\namespace} and
+ \l{headerfile-command} {\\headerfile}.
+
+ \target legalese-command
+ \section1 \\legalese
+
+ The \\legalese and \\endlegalese commands delimit a license agreement.
+
+ In the generated HTML, the delimited text is surrounded by a \b
+ {<div class="LegaleseLeft">} and \b {</div>} tags.
+
+ An example of a license agreement enclosed in \\legalese
+ and \\endlegalese:
+
+ \code
+ / *!
+ \legalese
+ Copyright 1996 Daniel Dardailler.
+
+ Permission to use, copy, modify, distribute, and sell this
+ software for any purpose is hereby granted without fee,
+ provided that the above copyright notice appear in all
+ copies and that both that copyright notice and this
+ permission notice appear in supporting documentation, and
+ that the name of Daniel Dardailler not be used in
+ advertising or publicity pertaining to distribution of the
+ software without specific, written prior permission. Daniel
+ Dardailler makes no representations about the suitability of
+ this software for any purpose. It is provided "as is"
+ without express or implied warranty.
+
+ Modifications Copyright 1999 Matt Koss, under the same
+ license as above.
+ \endlegalese
+ * /
+ \endcode
+
+ It will appear in the generated HTML as:
+
+ \code
+ <div class="LegaleseLeft">
+ <p>Copyright 1996 Daniel Dardailler.</p>
+ <p>Permission to use, copy, modify, distribute, and sell
+ this software for any purpose is hereby granted without fee,
+ provided that the above copyright notice appear in all
+ copies and that both that copyright notice and this
+ permission notice appear in supporting documentation, and
+ that the name of Daniel Dardailler not be used in
+ advertising or publicity pertaining to distribution of the
+ software without specific, written prior permission. Daniel
+ Dardailler makes no representations about the suitability of
+ this software for any purpose. It is provided "as is"
+ without express or implied warranty.</p>
+
+ <p>Modifications Copyright 1999 Matt Koss, under the same
+ license as above.</p>
+ </div>
+ \endcode
+
+ If the \\endlegalese command is omitted, QDoc will process the
+ \\legalese command but considers the rest of the documentation
+ page as the license agreement.
+
+ Ideally, the license text is located with the licensed code.
+
+ Elsewhere, the documentation identified as \e{\\legalese} command
+ can be accumulated using \l {generatelist-command} {\\generatelist}
+ with \c {legalese-command} as the argument. This is useful for
+ generating an overview of the license agreements associated with
+ the source code.
+
+ \target warning-command
+ \section1 \\warning
+
+ The \\warning command prepends "Warning:" to the command's
+ argument, in bold font.
+
+ \code
+ / *!
+ Qt::HANDLE is a platform-specific handle type
+ for system objects. This is equivalent to
+ \c{void *} on Windows and OS X, and to
+ \c{unsigned long} on X11.
+
+ \warning Using this type is not portable.
+ * /
+ \endcode
+
+ QDoc renders this as:
+
+ \quotation
+ Qt::HANDLE is a platform-specific handle type
+ for system objects. This is equivalent to
+ \c{void *} on Windows and OS X, and to
+ \c{unsigned long} on X11.
+
+ \warning Using this type is not portable.
+ \endquotation
+
+*/
+
+
+/*!
+ \page 12-0-qdoc-commands-miscellaneous.html
+ \previouspage Special Content
+ \contentspage QDoc Manual
+ \nextpage Creating DITA Maps
+
+ \title Miscellaneous
+
+ These commands provide miscellaneous functions connected to the
+ visual appearance of the documentation, and to the process of
+ generating the documentation.
+
+ \target annotatedlist-command
+ \section1 \\annotatedlist
+
+ The \\annotatedlist command expands to a list of the members of a
+ group, each member listed with its \e {brief} text. Below is an
+ example from the Qt Reference Documentation:
+
+ \code
+ / *!
+ ...
+ \section1 Drag and Drop Classes
+
+ These classes deal with drag and drop and the necessary mime type
+ encoding and decoding.
+
+ \annotatedlist draganddrop
+
+ * /
+ \endcode
+
+ This generates a list of all the C++ classes and/or QML types in
+ the \e{draganddrop} group. A C++ class or QML type in the
+ \e{draganddrop} group will have \e{\\ingroup draganddrop} in its
+ \e{\\class} or \e{\\qmltype} comment.
+
+
+ \target generatelist-command
+ \section1 \\generatelist
+
+ The \\generatelist command expands to a list of links to the
+ documentation entities in a group. Below is an example from the Qt
+ Reference Documentation:
+
+ \code
+ / *!
+ \page classes.html
+ \title All Classes
+
+ For a shorter list that only includes the most
+ frequently used classes, see \l{Qt's Main Classes}.
+
+ \generatelist classes Q
+ * /
+ \endcode
+
+ This generates the \e {All Classes} page. The command accepts the
+ following arguments:
+
+ \target table example
+ \section2 \c annotatedclasses
+
+ The \c annotatedclasses argument provides a table containing the
+ names of all the classes, and a description of each class. Each
+ class name is a link to the class's reference documentation. For
+ example:
+
+ \table
+ \row
+ \li QDial
+ \li Rounded range control (like a speedometer or potentiometer)
+ \row
+ \li QDialog
+ \li The base class of dialog windows
+ \row
+ \li QDir
+ \li Access to directory structures and their contents
+ \endtable
+
+ A C++ class is documented with the \l {class-command} {\\class}
+ command. The annotation for the class is taken from the argument
+ of the class comment's \l {brief-command} {\\brief} command.
+
+ \target list example
+ \section2 \c {classes <prefix>}
+
+ The \c classes argument provides a complete alphabetical list of
+ the classes. The second argument, \c{<prefix>}, is the common
+ prefix for the class names. The class names will be sorted on the
+ character that follows the common prefix. e.g. The common prefix
+ for the Qt classes is \c Q. The common prefix argument is
+ optional. If no common prefix is provided, the class names will
+ be sorted on their first character.
+
+ Each class name becomes a link to the class's reference
+ documentation. This command is used to generate the
+ \e {All Classes} page this way:
+
+ \code
+ / *!
+ \page classes.html
+ \title All Classes
+ \ingroup classlists
+
+ \brief Alphabetical list of classes.
+
+ This is a list of all Qt classes. For a list of the classes
+ provided for compatibility with Qt3, see \l{Qt3 Support
+ Classes}. For classes that have been deprecated, see the
+ \l{Obsolete Classes} list.
+
+ \generatelist classes Q
+ * /
+ \endcode
+
+ A C++ class is documented with the \l {class-command} {\\class}
+ command.
+
+ \section2 \c classesbymodule
+
+ When this argument is used, a second argument is required, which
+ specifies the module whose classes are to be listed. QDoc
+ generates a table containing those classes. Each class is listed
+ with the text of its \l{brief-command} {\\brief} command.
+
+ For example, this command can be used on a module page as follows:
+
+ \code
+ / *!
+ \page phonon-module.html
+ \module Phonon
+ \title Phonon Module
+ \ingroup modules
+
+ \brief Contains namespaces and classes for multimedia functionality.
+
+ \generatelist{classesbymodule Phonon}
+
+ ...
+
+ * /
+ \endcode
+
+ Each class that is a member of the specified module must be marked
+ with the \l {inmodule-command} {\\inmodule} command in its \\class
+ comment.
+
+ \section2 \c qmltypesbymodule
+
+ Similar to \c classesbymodule argument, but used for listing the
+ QML types from the QML module specified with the second argument.
+
+ \note Support for this argument was introduced in QDoc 5.6.
+
+ \section2 \c jstypesbymodule
+
+ Similar to \c classesbymodule argument, but used for listing the
+ JavaScript types from the module specified with the second argument.
+
+ \note Support for this argument was introduced in QDoc 5.6.
+
+ \section2 \c compatclasses
+
+ The \c compatclasses argument generates a list in alphabetical
+ order of the support classes. It is normally used only to
+ generate the Qt3 Support Classes page this way:
+
+ \code
+ / *!
+ \page compatclasses.html
+ \title Qt3 Support Classes
+ \ingroup classlists
+
+ \brief Enable porting of code from Qt 3 to Qt 4.
+
+ These are the classes that Qt provides for compatibility with Qt
+ 3. Most of these are provided by the Qt3Support module.
+
+ \generatelist compatclasses
+ * /
+ \endcode
+
+ A support class is identified in the \\class comment with the \l
+ {compat-command} {\\compat} command.
+
+ \section2 \c functionindex
+
+ The \c functionindex argument provides a complete alphabetical
+ list of all the documented member functions. It is normally used
+ only to generate the \e {Qt function index} page
+ this way:
+
+ \code
+ / *!
+ \page functions.html
+ \title All Functions
+ \ingroup funclists
+
+ \brief All documented Qt functions listed alphabetically with a
+ link to where each one is declared.
+
+ This is the list of all documented member functions and global
+ functions in the Qt API. Each function has a link to the
+ class or header file where it is declared and documented.
+
+ \generatelist functionindex
+ * /
+ \endcode
+
+ \section2 \c legalese
+
+ The \c legalese argument tells QDoc to generate a complete list of
+ licenses in the documentation. Each license is identified using
+ the \l {legalese-command} {\\legalese} command. This command is
+ used to generate the \e {Qt license information}
+ page this way:
+
+ \code
+ / *!
+ \page licenses.html
+ \title Other Licenses Used in Qt
+ \ingroup licensing
+ \brief Information about other licenses used for Qt components and third-party code.
+
+ Qt contains some code that is not provided under the
+ \l{GNU General Public License (GPL)},
+ \l{GNU Lesser General Public License (LGPL)} or the
+ \l{Qt Commercial Edition}{Qt Commercial License Agreement}, but rather under
+ specific licenses from the original authors. Some pieces of code were developed
+ by The Qt Company and others originated from third parties.
+ This page lists the licenses used, names the authors, and links
+ to the places where it is used.
+
+ The Qt Company gratefully acknowledges these and other contributions
+ to Qt. We recommend that programs that use Qt also acknowledge
+ these contributions, and quote these license statements in an
+ appendix to the documentation.
+
+ See also: \l{Licenses for Fonts Used in Qt for Embedded Linux}
+
+ \generatelist legalese
+ * /
+ \endcode
+
+ \section2 \c overviews
+
+ The \c overviews argument is used to tell QDoc to generate a list
+ by concatenating the contents of all the \l {group-command}
+ {\\group} pages. Qt uses it to generate the \e {overviews} page
+ this way:
+
+ \code
+ / *!
+ \page overviews.html
+
+ \title All Overviews and HOWTOs
+
+ \generatelist overviews
+ * /
+ \endcode
+
+ \section2 \c related
+
+ The \c related argument is used in combination with the \l
+ {group-command} {\\group} and \l {ingroup-command} {\\ingroup}
+ commands to list all the overviews related to a specified
+ group. For example, the page for the \e {Programming with Qt}
+ page is generated this way:
+
+ \code
+ / *!
+ \group qt-basic-concepts
+ \title Programming with Qt
+
+ \brief The basic architecture of the Qt cross-platform application and UI framework.
+
+ Qt is a cross-platform application and UI framework for
+ writing web-enabled applications for desktop, mobile, and
+ embedded operating systems. This page contains links to
+ articles and overviews explaining key components and
+ techniuqes used in Qt development.
+
+ \generatelist {related}
+ * /
+ \endcode
+
+ Each page listed on this group page contains the command:
+
+ \code
+ \ingroup qt-basic-concepts
+ \endcode
+
+ \target if-command
+ \section1 \\if
+
+ The \\if command and the corresponding \\endif command
+ enclose parts of a QDoc comment that only will be included if
+ the condition specified by the command's argument is true.
+
+ The command reads the rest of the line and parses it as an C++ #if
+ statement.
+
+ \code
+ / *!
+ \if defined(opensourceedition)
+
+ \b{Note:} This edition is for the development of
+ \l{Qt Open Source Edition} {Free and Open Source}
+ software only; see \l{Qt Commercial Editions}.
+
+ \endif
+ * /
+ \endcode
+
+ This QDoc comment will only be rendered if the \c
+ opensourceedition preprocessor symbol is defined, and specified in
+ the \l {defines-variable} {defines} variable in the configuration
+ file to make QDoc process the code within #ifdef and #endif:
+
+ \code
+ defines = opensourceedition
+ \endcode
+
+ You can also define the preprocessor symbol manually on the
+ command line. For more information see the documentation of the \l
+ {defines-variable} {defines} variable.
+
+ See also \l{endif-command} {\\endif}, \l{else-command} {\\else},
+ \l {defines-variable} {defines} and \l {falsehoods-variable}
+ {falsehoods}.
+
+ \target endif-command
+ \section1 \\endif
+
+ The \\endif command and the corresponding \\if command
+ enclose parts of a QDoc comment that will be included if
+ the condition specified by the \l {if-command} {\\if} command's
+ argument is true.
+
+ For more information, see the documentation of the \l {if-command}
+ {\\if} command.
+
+ See also \l{if-command} {\\if}, \l{else-command} {\\else}, \l
+ {defines-variable} {defines} and \l {falsehoods-variable}
+ {falsehoods}.
+
+ \target else-command
+ \section1 \\else
+
+ The \\else command specifies an alternative if the
+ condition in the \l {if-command} {\\if} command is false.
+
+ The \\else command can only be used within \l {if-command}
+ {\\if...\\endif} commands, but is useful when there is only two
+ alternatives.
+
+ \code
+ / *!
+ The Qt 3 support library is provided to keep old
+ source code working.
+
+ In addition to the \c Qt3Support classes, Qt 4 provides
+ compatibility functions when it's possible for an old
+ API to cohabit with the new one.
+
+ \if !defined(QT3_SUPPORT)
+ \if defined(QT3_SUPPORTWARNINGS)
+ The compiler emits a warning when a
+ compatibility function is called. (This works
+ only with GCC 3.2+ and MSVC 7.)
+ \else
+ To use the Qt 3 support library, you need to
+ have the line QT += qt3support in your .pro
+ file (qmake automatically define the
+ QT3_SUPPORT symbol, turning on compatibility
+ function support).
+
+ You can also define the symbol manually (for example,
+ if you don't want to link against the \c
+ Qt3Support library), or you can define \c
+ QT3_SUPPORT_WARNINGS instead, telling the
+ compiler to emit a warning when a compatibility
+ function is called. (This works only with GCC
+ 3.2+ and MSVC 7.)
+ \endif
+ \endif
+ * /
+ \endcode
+
+ If the \c QT3_SUPPORT is defined, the comment will be rendered
+ like this:
+
+ \quotation
+ The Qt 3 support library is provided to keep old source
+ code working.
+
+ In addition to the Qt3Support classes, Qt 4 provides
+ compatibility functions when it's possible for an old
+ API to cohabit with the new one.
+ \endquotation
+
+ If \c QT3_SUPPORT is not defined but \c QT3_SUPPORT_WARNINGS is
+ defined, the comment will be rendered like this:
+
+ \quotation
+ The Qt 3 support library is provided to keep old source
+ code working.
+
+ In addition to the Qt3Support classes, Qt 4 provides
+ compatibility functions when it's possible for an old
+ API to cohabit with the new one.
+
+ The compiler emits a warning when a compatibility
+ function is called. (This works only with GCC 3.2+ and
+ MSVC 7.)
+ \endquotation
+
+ If none of the symbols are defined, the comment will be
+ rendered as
+
+ \quotation
+ The Qt 3 support library is provided to keep old
+ source code working.
+
+ In addition to the \c Qt3Support classes, Qt 4 provides
+ compatibility functions when it's possible for an old
+ API to cohabit with the new one.
+
+ To use the Qt 3 support library, you need to have the
+ line QT += qt3support in your .pro file (qmake
+ automatically define the QT3_SUPPORT symbol, turning on
+ compatibility function support).
+
+ You can also define the symbol manually (e.g., if you
+ don't want to link against the \c Qt3Support library),
+ or you can define \c QT3_SUPPORT_WARNINGS instead,
+ telling the compiler to emit a warning when a
+ compatibility function is called. (This works only with
+ GCC 3.2+ and MSVC 7.)
+ \endquotation
+
+ See also \l{if-command} {\\if}, \l{endif-command} {\\endif}, \l
+ {defines-variable} {defines} and \l {falsehoods-variable}
+ {falsehoods}.
+
+ \target include-command
+ \section1 \\include
+
+ The \\include command sends all or part of the file specified by
+ its first argument to the QDoc input stream to be processed as a
+ QDoc comment snippet. This command is often assigned the alias,
+ \e {input}, in the QDoc configuration file, for example \e {alias.include
+ = input}.
+
+ The command is useful when some snippet of commands and text is to
+ be used in multiple places in the documentation. In that case,
+ move the snippet into a separate file and use the \\include
+ command wherever you want to insert the snippet into the
+ documentation. To prevent QDoc from reading the file as a
+ stand-alone page of documentation, we recommend that you use the
+ \c .qdocinc extension for these \e {include} files.
+
+ The command can have either one or two arguments. The first
+ argument is always a file name. The contents of the file must be
+ QDoc input, in other words, a sequence of QDoc commands and text, but
+ without the enclosing QDoc comment \c{/}\c{*!} ... \c{*}\c{/} delimiters.
+ If you want to include the entire named file, don't use the second
+ argument. If you want to include only part of the file, see the
+ \l{2-argument-form}{two argument form} below. Here is an example
+ of the one argument form:
+
+ \code
+ / *!
+ \page corefeatures.html
+ \title Core Features
+
+ \include examples/signalandslots.qdocinc
+ \include examples/objectmodel.qdocinc
+ \include examples/layoutmanagement.qdocinc
+ * /
+ \endcode
+
+ QDoc renders this page \l{corefeatures.html} {as shown here}.
+
+ \target 2-argument-form}
+ \section2 \\include filename snippet-identifier
+
+ It is a waste of time to make a separate \c .qdocinc file for every
+ QDoc include snippet you want to use in multiple places in the
+ documentation, especially given that you probably have to put the
+ copyright/license notice in every one of these files. So if you
+ have a large number of snippets to be included, you can put them all in a
+ single file if you want, and surround each one with:
+ \code
+ //! [snippet-id1]
+
+ QDoc commands and text...
+
+ //! [snippet-id1]
+
+ //! [snippet-id2]
+
+ More QDoc commands and text...
+
+ //! [snippet-id2]
+ \endcode
+
+ Then you can use the two-argument form of the command:
+
+ \code
+ \input examples/signalandslots.qdocinc snippet-id2
+ \input examples/objectmodel.qdocinc another-snippet-id
+ \endcode
+
+ It works as expected. The sequence of QDoc commands and text found
+ between the two tags with the same name as the second argument is
+ sent to the QDoc input stream. You can even nest these snippets,
+ although it's not clear why you would want to do that.
+
+ \target meta-command
+ \section1 \\meta
+
+ The \\meta command is mainly used for including metadata in DITA
+ XML files. It is also used when generating HTML output for specifying
+ the \e maintainer(s) of a C++ class.
+
+ The command has two arguments: the first argument is the name of the
+ metadata attribute, and the second argument is the
+ value for the attribute. Each argument should be enclosed in curly
+ brackets, as shown in this example:
+
+ \code
+ / *!
+ \class QWidget
+ \brief The QWidget class is the base class of all user interface objects.
+
+ \ingroup basicwidgets
+
+ \meta {technology} {User Interface}
+ \meta {platform} {OS X 10.6}
+ \meta {platform} {Symbian}
+ \meta {platform} {MeeGo}
+ \meta {audience} {user}
+ \meta {audience} {programmer}
+ \meta {audience} {designer}
+ * /
+ \endcode
+
+ When running QDoc to generate HTML, the example above will have no
+ effect on the generated output, but if you run QDoc to generate
+ DITA XML, the example will generate the following:
+
+ \code
+ <?xml version="1.0" encoding="UTF-8"?>
+ <!DOCTYPE cxxClass PUBLIC "-//NOKIA//DTD DITA C++ API Class Reference Type v0.6.0//EN" "dtd/cxxClass.dtd">
+ <!--qwidget.cpp-->
+ <cxxClass id="id-9a14268e-6b09-4eee-b940-21a00a0961df">
+ <apiName>QWidget</apiName>
+ <shortdesc>the QWidget class is the base class of all user interface objects.</shortdesc>
+ <prolog>
+ <author>Qt Development Frameworks</author>
+ <publisher>Qt Project</publisher>
+ <copyright>
+ <copyryear year="2015"/>
+ <copyrholder>Qt Project</copyrholder>
+ </copyright>
+ <permissions view="all"/>
+ <metadata>
+ <audience type="designer"/>
+ <audience type="programmer"/>
+ <audience type="user"/>
+ <category>Class reference</category>
+ <prodinfo>
+ <prodname>Qt Reference Documentation</prodname>
+ <vrmlist>
+ <vrm version="4" release="7" modification="3"/>
+ </vrmlist>
+ <component>QtGui</component>
+ </prodinfo>
+ <othermeta name="platform" content="MeeGo"/>
+ <othermeta name="platform" content="Symbian"/>
+ <othermeta name="platform" content="OS X 10.6"/>
+ <othermeta name="technology" content="User Interface"/>
+ </metadata>
+ </prolog>
+ \endcode
+
+ In the example output, several values have been set using default
+ values obtained from the QDoc configuration file. See \l
+ {Generating DITA XML Output} for details.
+
+ \target noautolist-command
+ \section1 \\noautolist
+
+ The \\noautolist command indicates that the annotated list of C++
+ classes or QML types, which is automatically generated at the
+ bottom of the C++ or QML module page should be omitted, because
+ the classes or types have been listed manually. This command can
+ also be used with the \l {group-command}{\\group} command to omit
+ the list of group members, when they are listed manually.
+
+ The command must stand on its own line. See \l {Qt Sensors QML Types} for
+ an example. The page is generated from \c {qtsensors5.qdoc}. There you will
+ find a qdoc comment containing the \c{\qmlmodule} command for the QtSensors
+ module. The same qdoc comment contains two \c {\annotated-list} commands to
+ list the QML types in two separate groups. The QML types have been divided
+ into these two groups because it makes more sense to list them this way than
+ it does to list them in a single alphabetical list. At the bottom of the
+ comment, \c {\noautolist} has been used to tell qdoc not to generate the
+ automatic annotated list.
+
+ This command was introduced in QDoc 5.6.
+
+ \target omit-command
+ \section1 \\omit
+
+ The \\omit command and the corresponding \\endomit command
+ delimit parts of the documentation that you want QDoc to skip. For
+ example:
+
+ \code
+ / *!
+ \table
+ \row
+ \li Basic Widgets
+ \li Basic GUI widgets such as buttons, comboboxes
+ and scrollbars.
+
+ \omit
+ \row
+ \li Component Model
+ \li Interfaces and helper classes for the Qt
+ Component Model.
+ \endomit
+
+ \row
+ \li Database Classes
+ \li Database related classes, e.g. for SQL databases.
+ \endtable
+ * /
+ \endcode
+
+ QDoc renders this as:
+
+ \raw HTML
+ <table align="center" cellpadding="2"
+ cellspacing="1" border="0">
+
+ <tr valign="top" bgcolor="#d0d0d0">
+ <td>Basic Widgets</td>
+ <td>Basic GUI widgets such as buttons, comboboxes
+ and scrollbars.</td>
+ </tr>
+
+ <tr valign="top" bgcolor="#c0c0c0">
+ <td>Database Classes</td>
+ <td>Database related classes, e.g. for SQL databases.</td>
+ </tr>
+ </table>
+ \endraw
+
+ \target raw-command
+ \section1 \\raw \span {class="newStuff"} {(avoid)}
+
+ The \\raw command and the corresponding
+ \\endraw command delimit a block of raw mark-up language code.
+
+ \note Avoid using this command if possible, because it generates
+ DITA XML code that causes problems. If you are trying to generate
+ special table or list behavior, try to get the behavior you want
+ using the \l {span-command} {\\span} and \l {div-command} {\\div}
+ commands in your \l {table-command} {\\table} or \l {list-command}
+ {\\list}.
+
+ The command takes an argument specifying the code's format.
+ Currently, the only supported format is HTML.
+
+ The \\raw command is useful if you want some special HTML effects
+ in your documentation.
+
+ \code
+ / *!
+ Qt has some predefined QColor objects.
+
+ \raw HTML
+ <style type="text/css" id="colorstyles">
+ #color-blue { background-color: #0000ff; color: #ffffff }
+ #color-darkBlue { background-color: #000080; color: #ffffff }
+ #color-cyan { background-color: #00ffff; color: #000000 }
+ </style>
+
+ <p>
+ <tt id="color-blue">Blue(#0000ff)</tt>,
+ <tt id="color-darkBlue">dark blue(#000080)</tt> and
+ <tt id="color-cyan">cyan(#00ffff)</tt>.
+ </p>
+ \endraw
+ * /
+ \endcode
+
+ QDoc renders this as:
+
+ \quotation
+ Qt has some predefined QColor objects.
+
+ \raw HTML
+ <style type="text/css" id="colorstyles">
+ #color-blue { background-color: #0000ff; color: #ffffff }
+ #color-darkBlue { background-color: #000080; color: #ffffff }
+ #color-cyan { background-color: #00ffff; color: #000000 }
+ </style>
+
+ <p>
+ <tt id="color-blue">Blue(#0000ff)</tt>,
+ <tt id="color-darkBlue">dark blue(#000080)</tt> and
+ <tt id="color-cyan">cyan(#00ffff)</tt>.
+ </p>
+ \endraw
+ \endquotation
+
+ \note But you can achieve the exact same thing using qdoc
+ commands. In this case, all you have to do is include the color
+ styles in your style.css file. Then you can write:
+
+ \code
+ \tt {\span {id="color-blue"} {Blue(#0000ff)}},
+ \tt {\span {id="color-darkBlue"} {dark blue(#000080)}} and
+ \tt {\span {id="color-cyan"} {cyan(#00ffff)}}.
+ \endcode
+
+ ...which is rendered as:
+
+ \tt {\span {id="color-blue"} {Blue(#0000ff)}},
+ \tt {\span {id="color-darkBlue"} {dark blue(#000080)}} and
+ \tt {\span {id="color-cyan"} {cyan(#00ffff)}}.
+
+ \target unicode-command
+ \section1 \\unicode
+
+ The \\unicode command allows you to insert an arbitrary Unicode
+ character in the document.
+
+ The command takes an argument specifying the character as an
+ integer. By default, base 10 is assumed, unless a '0x' or '0'
+ prefix is specified (for base 16 and 8, respectively). For
+ example:
+
+ \code
+ O G\unicode{0xEA}nio e as Rosas
+
+ \unicode 0xC0 table en famille avec 15 \unicode 0x20AC par jour
+
+ \unicode 0x3A3 \e{a}\sub{\e{i}}
+ \endcode
+
+ QDoc renders this as:
+
+ \quotation
+ O G\unicode{0xEA}nio e as Rosas
+
+ \unicode 0xC0 table en famille avec 15 \unicode 0x20AC par jour
+
+ \unicode 0x3A3 \e{a}\sub{\e{i}}
+ \endquotation
+*/
+
diff --git a/src/qdoc/doc/qdoc-manual-qdocconf.qdoc b/src/qdoc/doc/qdoc-manual-qdocconf.qdoc
new file mode 100644
index 000000000..69980c1e1
--- /dev/null
+++ b/src/qdoc/doc/qdoc-manual-qdocconf.qdoc
@@ -0,0 +1,1714 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the documentation of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:FDL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Free Documentation License Usage
+** Alternatively, this file may be used under the terms of the GNU Free
+** Documentation License version 1.3 as published by the Free Software
+** Foundation and appearing in the file included in the packaging of
+** this file. Please review the following information to ensure
+** the GNU Free Documentation License version 1.3 requirements
+** will be met: http://www.gnu.org/copyleft/fdl.html.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*!
+ \page 21-0-qdoc-configuration.html
+ \previouspage Creating DITA Maps
+ \contentspage QDoc Manual
+ \nextpage Generic Configuration Variables
+
+ \title The QDoc Configuration File
+
+ Before running QDoc, you must create a QDoc configuration file to
+ tell QDoc where to find the source files that contain the QDoc
+ comments. The pathname to your configuration file is passed to
+ QDoc on the command line:
+
+ \quotation
+ \c {/current/dir$ ../../bin/qdoc ./config.qdocconf}
+ \endquotation
+
+ \section1 General Description
+
+ The configuration file is a list of entries of the form \e
+ {"variable = value"}. Using the configuration variables, you can
+ define where QDoc should find the various source files, images and
+ examples, where to put generated documentation etc. The
+ configuration file can also contain directives like \c
+ include. For an example, see \l {minimal-qdocconf}{a minimal qdocconf file}.
+
+ You can also use configuration variables to get QDoc to support
+ \l{Supporting Derived Projects} {derived projects}, i.e QDoc can
+ generate links in your project's documentation to elements in the
+ Qt online documentation. See the \l {Supporting Derived projects}
+ section.
+
+ The value of a configuration variable can be set using either '='
+ or '+='. The difference is that '=' overrides the previous value,
+ while '+=' adds a new value to the current one.
+
+ Some configuration variables accept a list of strings as their
+ value, for example:
+ \l {sourcedirs-variable}
+ {\c{sourcedirs}}, while others accept only a single string. Double
+ quotes around a value string are optional, but including them allows
+ you to use special characters like '=' and ' \" ' within the value
+ string, for example:
+
+ \badcode
+ HTML.postheader = "<a href=\"index.html\">Home</a>"
+ \endcode
+
+ If an entry spans many lines, use a backslash at the end of every
+ line but the last:
+
+ \badcode
+ sourcedirs = kernel \
+ tools \
+ widgets
+ \endcode
+
+ \section1 Configuration Variables
+
+ \section1 Variable List
+
+ \list
+ \li \l {alias-variable} {alias}
+ \li \l {Cpp.ignoredirectives-variable} {Cpp.ignoredirectives}
+ \li \l {Cpp.ignoretokens-variable} {Cpp.ignoretokens}
+ \li \l {defines-variable} {defines}
+ \li \l {edition-variable} {edition}
+ \li \l {exampledirs-variable} {exampledirs}
+ \li \l {examples-variable} {examples}
+ \li \l {examples.fileextensions-variable} {examples.fileextensions}
+ \li \l {excludedirs-variable} {excludedirs}
+ \li \l {excludefiles-variable} {excludefiles}
+ \li \l {extraimages-variable} {extraimages}
+ \li \l {falsehoods-variable} {falsehoods}
+ \li \l {headerdirs-variable} {headerdirs}
+ \li \l {headers-variable} {headers}
+ \li \l {headers.fileextensions-variable} {headers.fileextensions}
+ \li \l {HTML.footer-variable} {HTML.footer}
+ \li \l {HTML.postheader-variable} {HTML.postheader}
+ \li \l {HTML.style-variable} {HTML.style}
+ \li \l {imagedirs-variable} {imagedirs}
+ \li \l {images-variable} {images}
+ \li \l {images.fileextensions-variable} {images.fileextensions}
+ \li \l {language-variable} {language}
+ \li \l {macro-variable} {macro}
+ \li \l {manifestmeta-variable} {manifestmeta}
+ \li \l {outputdir-variable} {outputdir}
+ \li \l {outputformats-variable} {outputformats}
+ \li \l {outputprefixes-variable} {outputprefixes}
+ \li \l {outputsuffixes-variable} {outputsuffixes}
+ \li \l {sourcedirs-variable} {sourcedirs}
+ \li \l {sources-variable} {sources}
+ \li \l {sources.fileextensions-variable} {sources.fileextensions}
+ \li \l {spurious-variable} {spurious}
+ \li \l {tabsize-variable} {tabsize}
+ \li \l {version-variable} {version}
+ \li \l {versionsym-variable} {versionsym}
+ \endlist
+
+ \section1 Categories
+
+ \list
+ \li \l {Generic Configuration Variables}
+ \li \l {C++ Specific Configuration Variables}
+ \li \l {HTML Specific Configuration Variables}
+ \endlist
+
+ \section1 Configuration File Examples
+
+ \list
+ \li A minimum configuration file: \l minimum.qdocconf
+ \li The Qt configuration file: \l qtgui.qdocconf
+ \endlist
+*/
+
+
+/*!
+ \page 22-qdoc-configuration-generalvariables.html
+ \previouspage The QDoc Configuration File
+ \contentspage QDoc Manual
+ \nextpage Creating Help Project Files
+
+ \title Generic Configuration Variables
+
+ With the general QDoc configuration variables, you can define
+ where QDoc will find the various source files it needs to generate
+ the documentation, as well as the directory to put the generated
+ documentation. You can also do some minor manipulation of QDoc
+ itself, controlling its output and processing behavior.
+
+ \target alias-variable
+ \section1 alias
+
+ The \c alias variable renames a QDoc command.
+
+ The general syntax is \tt {alias.\e{original-command-name} = \e
+ temporary-command-name}.
+
+ \badcode
+ alias.e = i
+ \endcode
+
+ This renames the built-in command \\e (italics) to be \\i. The \c
+ alias variable is often used for compatibility reasons.
+
+ See also \l {macro-variable} {macro}.
+
+ \target codeindent-variable
+ \section1 codeindent
+
+ The \c codeindent variable specifies the level of indentation that
+ QDoc uses when writing code snippets.
+
+ QDoc originally used a hard-coded value of four spaces for code
+ indentation to ensure that code snippets could be easily
+ distinguished from surrounding text. Since we can use \l{HTML
+ Specific Configuration Variables#HTML.stylesheets} {stylesheets}
+ to adjust the appearance of certain types of HTML elements, this
+ level of indentation is not always required.
+
+ \target codeprefix-variable
+ \target codesuffix-variable
+ \section1 codeprefix, codesuffix
+
+ The \c codeprefix and \c codesuffix variables specify a pair of
+ strings that each code snippet is enclosed in.
+
+ \target defines-variable
+ \section1 defines
+
+ The \c defines variable specifies the C++ preprocessor symbols
+ that QDoc will recognize and respond to.
+
+ When a preprocessor symbol is specified using the \c defines
+ variable, you can also use the \l {if-command} {\\if} command to
+ enclose documentation that only will be included if the
+ preprocessor symbol is defined.
+
+ The values of the variable are regular expressions (see QRegExp
+ for details). By default, no symbol is defined, meaning that code
+ protected with #ifdef...#endif will be ignored.
+
+ \badcode
+ defines = Q_QDOC \
+ QT_.*_SUPPORT \
+ QT_.*_LIB \
+ QT_COMPAT \
+ QT3_SUPPORT \
+ Q_OS_.* \
+ Q_BYTE_ORDER \
+ __cplusplus
+ \endcode
+
+ This ensures that QDoc will process the code that requires these
+ symbols to be defined. For example:
+
+ \code
+ #ifdef Q_OS_WIN
+ HDC getDC() const;
+ void releaseDC(HDC) const;
+ #endif
+ \endcode
+
+ Since the Q_OS_.* regular expression (specified using the \c
+ defines variable) matches Q_OS_WIN, QDoc will process the code
+ within #ifdef and #endif in our example.
+
+ You can also define preprocessor symbols manually on the command
+ line using the -D option. For example:
+
+ \badcode
+ currentdirectory$ qdoc -Dconsoleedition qtgui.qdocconf
+ \endcode
+
+ In this case the -D option ensures that the \c consoleedition
+ preprocessor symbol is defined when QDoc processes the source
+ files defined in the qtgui.qdocconf file.
+
+ See also \l {falsehoods-variable} {falsehoods} and \l {if-command} {\\if}.
+
+ \target edition-variable
+ \section1 edition
+
+ The \c edition variable specifies which modules are included in
+ each edition of a package, and provides QDoc with information to
+ provide class lists for each edition.
+
+ This feature is mostly used when providing documentation for Qt
+ packages.
+
+ The \c edition variable is always used with a particular edition
+ name to define the modules for that edition:
+
+ \badcode
+ edition.Console = QtCore QtNetwork QtSql QtXml
+ edition.Desktop = QtCore QtGui QtNetwork QtOpenGL QtSql QtXml \
+ QtDesigner QtAssistant Qt3Support QAxContainer \
+ QAxServer
+ edition.DesktopLight = QtCore QtGui Qt3SupportLight
+ \endcode
+
+ In the above examples, the \c Console edition only includes the
+ contents of four modules. Only the classes from these modules will
+ be used when the \l{Miscellaneous#generatelist-command}
+ {generatelist} command is used to generate a list of classes for
+ this edition:
+
+ \badcode
+ \generatelist{classesbyedition Console}
+ \endcode
+
+ \target exampledirs-variable
+ \section1 exampledirs
+
+ The \c exampledirs variable specifies the directories containing
+ the source code of the example files.
+
+ The \l {examples-variable} {examples} and \l
+ {exampledirs-variable} {exampledirs} variables are used by the \l
+ {quotefromfile-command} {\\quotefromfile}, \l {quotefile-command}
+ {\\quotefile} and \l {example-command} {\\example} commands. If
+ both the \l {examples-variable} {examples} and \l
+ {exampledirs-variable} {exampledirs} variables are defined, QDoc
+ will search in both, first in \l {examples-variable} {examples}
+ then in \l {exampledirs-variable} {exampledirs}.
+
+ QDoc will search through the directories in the specified order,
+ and accept the first matching file it finds. It will only search
+ in the specified directories, \e not in subdirectories.
+
+ \badcode
+ exampledirs = $QTDIR/doc/src \
+ $QTDIR/examples \
+ $QTDIR \
+ $QTDIR/qmake/examples
+
+ examples = $QTDIR/examples/widgets/analogclock/analogclock.cpp
+ \endcode
+
+ When processing
+
+ \badcode
+ \quotefromfile widgets/calculator/calculator.cpp
+ \endcode
+
+ QDoc will see if there is a file called \c calculator.cpp
+ listed as a value in the \l {examples-variable} {\c examples} variable. If
+ there isn't, it will search in the \c exampledirs variable, and
+ first see if there exists a file called
+
+ \badcode
+ $QTDIR/doc/src/widgets/calculator/calculator.cpp
+ \endcode
+
+ If it doesn't, QDoc will continue looking for a file called
+
+ \badcode
+ $QTDIR/examples/widgets/calculator/calculator.cpp
+ \endcode
+
+ and so forth.
+
+ See also \l {examples-variable}{examples}.
+
+ \target examples-variable
+ \section1 examples
+
+ The \c examples variable allows you to specify individual example
+ files in addition to those located in the directories specified by
+ the \l {exampledirs-variable} {\c exampledirs} variable.
+
+ The \c examples and \l {exampledirs-variable} {\c exampledirs}
+ variables are used by the \l {quotefromfile-command}
+ {\\quotefromfile}, \l {quotefile-command} {\\quotefile} and \l
+ {example-command} {\\example} commands. If both the \c examples and \l
+ {exampledirs-variable} {\c exampledirs} variables are defined,
+ QDoc will search in both, first in \c examples then in \l
+ {exampledirs-variable} {\c exampledirs}.
+
+ QDoc will search through the values listed for the \c examples
+ variable, in the specified order, and accept the first one it
+ finds.
+
+ For an extensive example, see the \l {exampledirs-variable} {\c
+ exampledirs} command. But note that if you know the file is listed
+ in the \c examples variable, you don't need to specify its path:
+
+ \badcode
+ \quotefromfile calculator.cpp
+ \endcode
+
+ See also \l {exampledirs-variable} {exampledirs}.
+
+ \target examples.fileextensions-variable
+ \section1 examples.fileextensions
+
+ The \c examples.fileextensions variable specifies the file
+ extensions that qdoc will look for when collecting example files
+ for display in the documentation.
+
+ The default extensions are *.cpp, *.h, *.js, *.xq, *.svg, *.xml
+ and *.ui.
+
+ The extensions are given as standard wildcard expressions. You
+ can add a file extension to the filter using '+='. For example:
+
+ \badcode
+ examples.fileextensions += *.qrc
+ \endcode
+
+ See also \l{headers.fileextensions}.
+
+ \target excludedirs-variable
+ \section1 excludedirs
+
+ The \c excludedirs variable is for listing directories that should \e{not}
+ be processed by qdoc, even if the same directories are included by the
+ \l {sourcedirs-variable} {sourcedirs} or \l {headerdirs-variable} {headerdirs}
+ variables.
+
+ For example:
+
+ \badcode
+ sourcedirs = src/corelib
+ excludedirs = src/corelib/tmp
+ \endcode
+
+ When executed, QDoc will exclude the listed directories from
+ further consideration. Files in these directories will not be
+ read by qdoc.
+
+ See also \l {excludefiles-variable} {excludefiles}.
+
+ \target excludefiles-variable
+ \section1 excludefiles
+
+ The \c excludefiles variable allows you to specify individual files
+ that should \e{not} be processed by qdoc.
+
+ \badcode
+ excludefiles += $QT_CORE_SOURCES/../../src/widgets/kernel/qwidget.h \
+ $QT_CORE_SOURCES/../../src/widgets/kernel/qwidget.cpp
+ \endcode
+
+ If you include the above in your qdocconf file for qtbase, there
+ will be no qwidget.html generated for html and no qwidget.xml
+ generated for DITA XML.
+
+ See also \l {excludedirs-variable} {excludedirs}.
+
+ \target extraimages-variable
+ \section1 extraimages
+
+ The \c extraimages variable tells QDoc to incorporate specific
+ images in the generated documentation.
+
+ QDoc will not recognize images used within HTML (or any other
+ markup language). If we want the images to be copied from the
+ directories specified by \l {imagedirs} {\c imagedirs} (the images
+ in question must be located in these directories) to the output
+ directory, we must specify the images using the \c extraimages
+ variable.
+
+ The general syntax is \tt {extraimages.\e{format} = \e image}. The
+ file extension is optional.
+
+ For example, in \l qtgui.qdocconf we use a couple of images within
+ the HTML.postheader variable which value is pure HTML. For that
+ reason, these images are specified using the \c extraimages
+ variable:
+
+ \badcode
+ extraimages.HTML = qt-logo
+ \endcode
+
+ See also \l images and \l imagedirs.
+
+ \target falsehoods-variable
+ \section1 falsehoods
+
+ The \c falsehoods variable defines the truth value of specified
+ preprocessor symbols as false.
+
+ If this variable is not set for a preprocessor symbol, QDoc
+ assumes its truth value is true. The exception is '0', which value
+ always is false.
+
+ QDoc will recognize, and is able to evaluate, the following
+ preprocessor syntax:
+
+ \code
+ #ifdef NOTYET
+ ...
+ #endif
+
+ #if defined (NOTYET)
+ ...
+ #end if
+ \endcode
+
+ However, faced with unknown syntax like
+
+ \code
+ #if NOTYET
+ ...
+ #endif
+ \endcode
+
+ QDoc will evaluate it as true by default, \e unless the
+ preprocessor symbol is specified within the \c falsehoods variable
+ entry:
+
+ \badcode
+ falsehoods = NOTYET
+ \endcode
+
+ See also \l defines.
+
+ \target generateindex-variable
+ \section1 generateindex
+
+ The \c generateindex variable contains a boolean value that
+ specifies whether to generate an index file when HTML
+ documentation is generated.
+
+ By default, an index file is always generated with HTML
+ documentation, so this variable is typically only used when
+ disabling this feature (by setting the value to \c false) or when
+ enabling index generation for the WebXML output (by setting the
+ value to \c true).
+
+ \target headerdirs-variable
+ \section1 headerdirs
+
+ The \c headerdirs variable specifies the directories containing
+ the header files associated with the \c .cpp source files used in
+ the documentation.
+
+ \badcode
+ headerdirs = $QTDIR/src \
+ $QTDIR/extensions/activeqt \
+ $QTDIR/extensions/motif \
+ $QTDIR/tools/designer/src/lib/extension \
+ $QTDIR/tools/designer/src/lib/sdk \
+ $QTDIR/tools/designer/src/lib/uilib
+ \endcode
+
+ When executed, the first thing QDoc will do is to read through the
+ headers specified in the \l {headers} {\c headers} variable, and
+ the ones located in the directories specified in the \c headerdir
+ variable (including all subdirectories), building an internal
+ structure of the classes and their functions.
+
+ Then it will read through the sources specified in the \l
+ {sources-variable} {\c sources}, and the ones located in the
+ directories specified in the \l {sourcedirs-variable} {\c
+ sourcedirs} varible (including all subdirectories), merging the
+ documentation with the structure it retrieved from the header
+ files.
+
+ If both the \c headers and \c headerdirs variables are defined,
+ QDoc will read through both, first \l {headers} {\c headers} then
+ \c headerdirs.
+
+ In the specified directories, QDoc will only read the files with
+ the \c fileextensions specified in the \l {headers.fileextensions}
+ {\c headers.fileextensions} variable. The default extensions are
+ *.ch, *.h, *.h++, *.hh, *.hpp, and *.hxx". The files specified by
+ \l {headers} {\c headers} will be read without taking into account
+ their fileextensions.
+
+ See also \l headers and \l headers.fileextensions.
+
+ \target headers-variable
+ \section1 headers
+
+ The \c headers variable allows you to specify individual header
+ files in addition to those located in the directories specified by
+ the \l {headerdirs} {\c headerdirs} variable.
+
+ \badcode
+ headers = $QTDIR/src/gui/widgets/qlineedit.h \
+ $QTDIR/src/gui/widgets/qpushbutton.h
+ \endcode
+
+ When processing the \c headers variable, QDoc behaves in the same
+ way as it does when processing the \l {headerdirs} {\c headerdirs}
+ variable. For more information, see the \l {headerdirs} {\c
+ headerdirs} variable.
+
+ See also \l headerdirs.
+
+ \target headers.fileextensions-variable
+ \section1 headers.fileextensions
+
+ The \c headers.fileextensions variable specify the extension used
+ by the headers.
+
+ When processing the header files specified in the \l {headerdirs}
+ {\c headerdirs} variable, QDoc will only read the files with the
+ fileextensions specified in the \c headers.fileextensions
+ variable. In this way QDoc avoids spending time reading irrelevant
+ files.
+
+ The default extensions are *.ch, *.h, *.h++, *.hh, *.hpp, and
+ *.hxx.
+
+ The extensions are given as standard wildcard expressions. You
+ can add a file extension to the filter using '+='. For example:
+
+ \badcode
+ header.fileextensions += *.H
+ \endcode
+
+ \warning The above assignment may not work as described.
+
+ See also \l headerdirs.
+
+ \target imagedirs-variable
+ \section1 imagedirs
+
+ The \c imagedirs variable specifies the directories containing the
+ images used in the documentation.
+
+ The \l {images} {\c images} and \c imagedirs variables are used by
+ the \l {image-command} {\\image} and \l {inlineimage-command}
+ {\\inlineimage} commands. If both the \l {images} {\c images} and
+ \c imagedirs variables are defined, QDoc will search in both. First
+ in \l {images} {\c images}, then in \c imagedirs.
+
+ QDoc will search through the directories in the specified order,
+ and accept the first matching file it finds. It will only search
+ in the specified directories, \e not in subdirectories.
+
+ \badcode
+ imagedirs = $QTDIR/doc/src/images \
+ $QTDIR/examples
+
+ images = $QTDIR/doc/src/images/calculator-example.png
+ \endcode
+
+ When processing
+
+ \badcode
+ \image calculator-example.png
+ \endcode
+
+ QDoc will then see if there is a file called
+ calculator-example.png listed as a value in the \c images
+ variable. If there isn't, it will search in the \c imagedirs
+ variable for:
+
+ \badcode
+ $QTDIR/doc/src/images/calculator-example.png
+ \endcode
+
+ If the file doesn't exist, QDoc will look for a file called
+
+ \badcode
+ $QTDIR/examples/calculator-example.png
+ \endcode
+
+ You can filter the images in an image directory using the \l
+ {images.fileextensions} {\c images.fileextensions} variable. The
+ general idea behind the \l {images.fileextensions} {\c images.fileextensions}
+ variable is to enable different image format for different output format.
+
+ \warning The \l {images.fileextensions} {\c images.fileextensions}
+ variable's functionality is preliminary since QDoc at this point
+ only supports HTML.
+
+ See also \l images and \l images.fileextensions.
+
+ \target images-variable
+ \section1 images
+
+ The \c images variable allows you to specify individual image
+ files in addition to those located in the directories specified by
+ the \l {imagedirs} {\c imagedirs} variable.
+
+ \badcode
+ images = $QTDIR/doc/src/images/calculator-example.png
+ \endcode
+
+ When processing the \c images variable, QDoc behaves in the same
+ way as it does when processing the \l {imagedirs} {\c imagedirs}
+ variable. For more information, see the \l {imagedirs} {\c
+ imagedirs} variable.
+
+ See also \l imagedirs and \l images.fileextensions.
+
+ \target images.fileextensions-variable
+ \section1 images.fileextensions
+
+ The images.fileextensions variable filters the files within an
+ image directory.
+
+ The variable's values (the extensions) are given as standard
+ wildcard expressions. The general syntax is: \tt
+ {images.fileextensions.\e{format} = *.\e{extension}}.
+
+ The idea is to enable different image format for different output
+ format.
+
+ \badcode
+ images.fileextensions.HTML = *.png
+ images.fileextensions.LOUT = *.eps
+ \endcode
+
+ Then, when processing the \l {image-command} {\\image} and \l
+ {inlineimage-command} {\\inlineimage} commands, QDoc will only
+ search for files with extensions specified in the variable
+ containing the list of output formats.
+
+ \warning This is only a preliminary functionality since QDoc at this
+ point only supports HTML.
+
+ The default extensions for HTML are *.png, *.jpg, *.jpeg, and
+ *.gif.
+
+ You can add a file extension to the filter using '+='. For
+ example:
+
+ \badcode
+ images.fileextensions.HTML += *.eps
+ \endcode
+
+ See also \l imagedirs and \l images.
+
+ \target language-variable
+ \section1 language
+
+ The \c language variable specifies the language of the source code
+ that is used in the documentation.
+
+ Currently, C++ is the only language that QDoc understands. It is
+ also the default language, and doesn't really need to be
+ specified. However, a possible example of a language variable
+ statement:
+
+ \badcode
+ language = Cpp
+ \endcode
+
+ This identifies C++ as the language of the Qt source code.
+
+ \target macro-variable
+ \section1 macro
+
+ The \c macro variable is used to create your own simple QDoc
+ commands. The syntax is \tt {macro.\e{command} = \e{definition}},
+ where the definition is written using QDoc syntax.
+
+ A macro variable can be restricted for use in one type of output
+ generation. By appending \c {.HTML} to the macro name, for
+ example, the macro is only used when generating HTML output. By
+ appending \c {.DITAXML} to the macro name, the macro is only used
+ when generating DITA XML.
+
+ \badcode
+ macro.gui = "\\b"
+ macro.raisedaster.HTML = "<sup>*</sup>"
+ \endcode
+
+ The first macro defines the \\gui command to render its argument
+ using a bold font. The second macro defines the \\raisedaster
+ command to render a superscript asterisk, but only when generating
+ HTML.
+
+ A macro can also take up to seven parameters:
+
+ \badcode
+ macro.hello = "Hello \1!"
+ \endcode
+
+ Parameters are passed to macros the same way as to other commands:
+
+ \badcode
+ \hello World
+ \endcode
+
+ See also \l {alias-variable} {alias}.
+
+ \target manifestmeta-variable
+ \section1 manifestmeta
+
+ The \c manifestmeta variable specifies additional meta-content
+ for the example manifest files generated by QDoc.
+
+ See the \l{Manifest Meta Content} section for more information.
+
+ \target naturallanguage-variable
+ \section1 naturallanguage
+
+ The \c naturallanguage variable specifies the natural language
+ used for the documentation generated by qdoc.
+
+ \badcode
+ naturallanguage = zh-Hans
+ \endcode
+
+ By default, the natural language is \c en for compatibility with
+ legacy documentation.
+
+ qdoc will add the natural language information to the HTML it
+ generates, using the \c lang and \c xml:lang attributes.
+
+ See also \l {sourceencoding-variable} {sourceencoding},
+ \l {outputencoding-variable} {outputencoding},
+ \l{http://www.w3.org/TR/xhtml1/#C_7}
+ {C.7. The lang and xml:lang Attributes} and
+ \l{http://www.w3.org/TR/i18n-html-tech-lang/#ri20040429.113217290}
+ {Best Practice 13: Using Hans and Hant codes}.
+
+ \target outputdir-variable
+ \section1 outputdir
+
+ The \c outputdir variable specifies the directory where QDoc will
+ put the generated documentation.
+
+ \badcode
+ outputdir = $QTDIR/doc/html
+ \endcode
+
+ locates the generated Qt reference documentation in
+ $QTDIR/doc/html. For example, the documentation of the QWidget
+ class is located in
+
+ \badcode
+ $QTDIR/doc/html/qwidget.html
+ \endcode
+
+ The associated images will be put in an \c images subdirectory.
+
+ \warning When running QDoc multiple times using the same output
+ directory, all files from the previous run will be lost.
+
+ \target outputencoding-variable
+ \section1 outputencoding
+
+ The \c outputencoding variable specifies the encoding used for the
+ documentation generated by qdoc.
+
+ \badcode
+ outputencoding = UTF-8
+ \endcode
+
+ By default, the output encoding is \c ISO-8859-1 (Latin1) for
+ compatibility with legacy documentation. When generating
+ documentation for some languages, particularly non-European
+ languages, this is not sufficient and an encoding such as UTF-8 is
+ required.
+
+ qdoc will encode HTML using this encoding and generate the correct
+ declarations to indicate to browsers which encoding is being
+ used. The \l naturallanguage configuration variable should also be
+ specified to provide browsers with a complete set of character
+ encoding and language information.
+
+ See also \l outputencoding and \l naturallanguage.
+
+ \target outputformats-variable
+ \section1 outputformats
+
+ The \c outputformats variable specifies the format of
+ the generated documentation.
+
+ Currently, QDoc only supports the HTML format. It is also
+ the default format, and doesn't need to be specified.
+
+ \target outputprefixes-variable
+ \section1 outputprefixes
+
+ The \c outputprefixes variable specifies a mapping between types of files
+ and the prefixes to prepend to the HTML file names in the generated
+ documentation.
+
+ \badcode
+ outputprefixes = QML JS
+ outputprefixes.QML = uicomponents-
+ outputprefixes.JS = uicomponents-
+ \endcode
+
+ By default, files containing the API documentation for QML types
+ are prefixed with "qml-", and javaScript types with "js-". In the
+ above example, the prefix \c "uicomponents" is used instead for
+ both.
+
+ The output prefix is applied to file names for documentation on
+ QML and JS types.
+
+ \target outputsuffixes-variable
+ \section1 outputsuffixes
+
+ The \c outputsuffixes variable specifies a mapping between types of
+ files and module name suffixes to append to the HTML file names.
+
+ \badcode
+ outputsuffixes = QML
+ outputsuffixes.QML = -tp
+ \endcode
+
+ Given a QML module name \e FooBar and the default
+ \l {outputprefixes-variable}{output prefix} ("qml-"), the file name of
+ the generated HTML page for a QML type \e FooWidget would be
+ \c qml-foobar-tp-foowidget.html.
+
+ By default, no suffix is used. The output suffix, if defined, is applied
+ to file names for documentation on QML and JS types, and their respective
+ module pages.
+
+ The \c outputsuffixes variable was introduced in QDoc 5.6.
+
+ \target qhp-variable
+ \section1 qhp
+
+ The \c qhp variable is used to define the information to be
+ written out to Qt Help Project (\c{qhp}) files.
+
+ See the \l{Creating Help Project Files} chapter for information
+ about this process.
+
+ \target sourcedirs-variable
+ \section1 sourcedirs
+
+ The \c sourcedirs variable specifies the directories containing
+ the \c .cpp or \c .qdoc files used in the documentation.
+
+ \badcode
+ sourcedirs += .. \
+ ../../../examples/gui/doc/src
+ \endcode
+
+ When executed, the first thing QDoc will do is to read through the
+ headers specified in the \l {header-command} {\c header} variable,
+ and the ones located in the directories specified in the \c
+ headerdir variable (including all subdirectories), building an
+ internal structure of the classes and their functions.
+
+ Then it will read through the sources specified in the \l
+ {sources} {\c sources}, and the ones located in the directories
+ specified in the \l {sourcedirs} {\c sourcedirs} variable
+ (including all subdirectories), merging the documentation with the
+ structure it retrieved from the header files.
+
+ If both the \c sources and \c sourcedirs variables are defined,
+ QDoc will read through both, first \l {sources} {\c sources} then
+ \c sourcedirs.
+
+ In the specified directories, QDoc will only read the files with
+ the \c fileextensions specified in the \l {sources.fileextensions}
+ {\c sources.fileextensions} variable. The default extensions are
+ *.c++, *.cc, *.cpp and *.cxx. The files specified by \l {sources}
+ {\c sources} will be read independent of their fileextensions.
+
+ See also \l {sources-variable} {sources} and
+ \l {sources.fileextensions-variable} {sources.fileextensions}.
+
+ \target sourceencoding-variable
+ \section1 sourceencoding
+
+ The \c sourceencoding variable specifies the encoding used for the
+ source code and documentation.
+
+ \badcode
+ sourceencoding = UTF-8
+ \endcode
+
+ By default, the source encoding is \c ISO-8859-1 (Latin1) for
+ compatibility with legacy documentation. For some languages,
+ particularly non-European languages, this is not sufficient and an
+ encoding such as UTF-8 is required.
+
+ Although qdoc will use the encoding to read source and
+ documentation files, limitations of C++ compilers may prevent you
+ from using non-ASCII characters in source code comments. In cases
+ like these, it is possible to write API documentation completely
+ in documentation files.
+
+ See also \l {naturallanguage-variable} {naturallanguage} and
+ \l {outputencoding-variable} {outputencoding}.
+
+ \target sources-variable
+ \section1 sources
+
+ The \c sources variable allows you to specify individual source
+ files in addition to those located in the directories specified by
+ the \l {sourcedirs-variable} {sourcedirs} variable.
+
+ \badcode
+ sources = $QTDIR/src/gui/widgets/qlineedit.cpp \
+ $QTDIR/src/gui/widgets/qpushbutton.cpp
+ \endcode
+
+ When processing the \c sources variable, QDoc behaves in the same
+ way as it does when processing the \l {sourcedirs-variable}
+ {sourcedirs} variable. For more information, see the \l
+ {sourcedirs-variable} {sourcedirs} variable.
+
+ See also \l {sourcedirs-variable} {sourcedirs}.
+
+ \target sources.fileextensions-variable
+ \section1 sources.fileextensions
+
+ The \c sources.fileextensions variable filters the files within a
+ source directory.
+
+ When processing the source files specified in the \l {sourcedirs}
+ {\c sourcedirs} variable, QDoc will only read the files with the
+ fileextensions specified in the \c sources.fileextensions
+ variable. In this way QDoc avoid spending time reading irrelevant
+ files.
+
+ The default extensions are *.c++, *.cc, *.cpp and *.cxx.
+
+ The extensions are given as standard wildcard expressions. You
+ can add a file extension to the filter using '+='. For example:
+
+ \badcode
+ sources.fileextensions += *.CC
+ \endcode
+
+ \warning The above assignment may not work as described.
+
+ See also \l {sourcedirs-variable} {sourcedirs} and \l
+ (sources-variable} {sources}.
+
+
+ \target spurious-variable
+ \section1 spurious
+
+ The \c spurious variable excludes specified QDoc warnings from the
+ output. The warnings are specified using standard wildcard
+ expressions.
+
+ \badcode
+ spurious = "Cannot find .*" \
+ "Missing .*"
+ \endcode
+
+ makes sure that warnings matching either of these expressions,
+ will not be part of the output when running QDoc. For example
+ would the following warning be omitted from the output:
+
+ \badcode
+ src/opengl/qgl_mac.cpp:156: Missing parameter name
+ \endcode
+
+ \target syntaxhighlighting
+ \section1 syntaxhighlighting
+
+ The \c syntaxhighlighting variable specifies whether QDoc should
+ perform syntax highlighting on source code quoted in the
+ documentation it generates.
+
+ \badcode
+ syntaxhighlighting = true
+ \endcode
+
+ will enable syntax highlighting for all supported programming
+ languages.
+
+ \target tabsize-variable
+ \section1 tabsize
+
+ The \c tabsize variable defines the size of a tab character.
+
+ \badcode
+ tabsize = 4
+ \endcode
+
+ will give the tab character the size of 4 spaces. The default
+ value of the variable is 8, and doesn't need to be specified.
+
+ \target tagfile-variable
+ \section1 tagfile
+
+ The \c tagfile variable specifies the Doxygen tag file to be
+ written when HTML is generated.
+
+ \target version-variable
+ \section1 version
+
+ The \c version variable specifies the version number of the
+ documented software.
+
+ \badcode
+ version = 5.6.0
+ \endcode
+
+ When a version number is specified (using the \tt{\l version} or
+ \tt {\l versionsym} variables in a \c .qdocconf file), it is
+ accessible through the corresponding \\version command for use in
+ the documentation.
+
+ \warning The \\version command's functionality is not fully
+ implemented; currently it only works within raw HTML code.
+
+ See also \l versionsym.
+
+ \target versionsym-variable
+ \section1 versionsym
+
+ The \c versionsym variable specifies a C++ preprocessor symbol
+ that defines the version number of the documented software.
+
+ \badcode
+ versionsym = QT_VERSION_STR
+ \endcode
+
+ QT_VERSION_STR is defined in qglobal.h as follows
+
+ \badcode
+ #define QT_VERSION_STR "4.0.1"
+ \endcode
+
+ When a version number is specified (using the \tt{\l version} or
+ \tt {\l versionsym} variables in a \c .qdocconf file), it is
+ accessible through the corresponding \\version command for use in
+ the documentation.
+
+ \warning The \\version command's functionality is not fully
+ implemented. Currently, it only works within raw HTML code.
+
+ See also \l {version} {\\version}.
+*/
+
+/*!
+ \page 22-creating-help-project-files.html
+ \previouspage Generic Configuration Variables
+ \contentspage QDoc Manual
+ \nextpage C++ Specific Configuration Variables
+
+ \title Creating Help Project Files
+
+ \section1 Overview
+
+ Qt Assistant uses a system for managing Qt documentation that requires
+ QDoc to generate inventories of files in a format that is similar to the
+ old style DCF format, but with additional features.
+
+ QDoc allows configuration variables to be used to specify which pages are
+ to be used in each documentation set it generates. These are specified as
+ subvariables of the \c qhp variable with each set declared using a unique
+ identifier as a subvariable.
+
+ For example, the configuration file for the Qt Quick documentation set
+ specifies information about the set as subvariables with the
+ \c{qhp.QtQuick} prefix:
+
+ \badcode
+ qhp.projects = QtQuick
+
+ qhp.QtQuick.file = qtquick.qhp
+ qhp.QtQuick.namespace = org.qt-project.qtquick.$QT_VERSION_TAG
+ qhp.QtQuick.virtualFolder = qtquick
+ qhp.QtQuick.indexTitle = Qt Quick
+ qhp.QtQuick.indexRoot =
+
+ qhp.QtQuick.filterAttributes = qtquick $QT_VERSION qtrefdoc
+ qhp.QtQuick.customFilters.Qt.name = QtQuick $QT_VERSION
+ qhp.QtQuick.customFilters.Qt.filterAttributes = qtquick $QT_VERSION
+
+ qhp.QtQuick.subprojects = qmltypes classes examples
+
+ qhp.QtQuick.subprojects.qmltypes.title = QML Types
+ qhp.QtQuick.subprojects.qmltypes.indexTitle = Qt Quick QML Types
+ qhp.QtQuick.subprojects.qmltypes.selectors = qmlclass
+ qhp.QtQuick.subprojects.qmltypes.sortPages = true
+
+ qhp.QtQuick.subprojects.classes.title = Classes
+ qhp.QtQuick.subprojects.classes.title = C++ Classes
+ qhp.QtQuick.subprojects.classes.indexTitle = Qt Quick C++ Classes
+ qhp.QtQuick.subprojects.classes.selectors = class fake:headerfile
+ qhp.QtQuick.subprojects.classes.sortPages = true
+
+ qhp.QtQuick.subprojects.examples.title = Examples
+ qhp.QtQuick.subprojects.examples.indexTitle = Qt Quick Examples and Tutorials
+ qhp.QtQuick.subprojects.examples.selectors = fake:example
+ \endcode
+
+ The documentation set may include one or more subprojects, which are added
+ to the table of contents under the name specified by \c title. The page
+ in the documentation referred to by the \c indexTitle acts as the index page
+ for the subproject. The page types to list under the subproject are specified
+ by \c selectors. The entries are alphabetically sorted if \c sortPages is set
+ to \c true.
+
+ \section2 Using Selectors
+
+ The \c selectors property specifies which page types are listed under the
+ table of contents entry for a subproject. Multiple selectors can be listed,
+ separated by whitespace.
+
+ \table
+ \header \li Selector \li Description
+ \row \li \c namespace \li Namespaces
+ \row \li \c class \li Classes
+ \row \li \c qmltype \li QML Types
+ \row \li \c qmlclass \li Alias for \c qmltype.
+ \row \li \c module \li C++ Modules
+ \row \li \c qmlmodule \li QML Modules
+ \row \li \c doc[:subtype] \li Documentation pages with a specified
+ \c subtype. Multiple subtypes can be
+ listed as a comma-separated list.
+ \row \li \c fake \li Alias for \c doc.
+ \row \li \c group[:groupname] \li Documentation pages for members of a
+ specified group, as added using the
+ \l {ingroup-command}
+ {\\ingroup} groupname command.
+ Multiple group names can be listed as
+ a comma-separated list.
+ (Introduced in QDoc 5.6).
+ \endtable
+
+ Available subtypes for the \c doc selector:
+
+ \table
+ \header \li Subtype \li Description
+ \row \li \c example \li Examples
+ \row \li \c headerfile \li Header files
+ \row \li \c page \li Documentation pages defined with the
+ \l {page-command} {\\page} command.
+ \endtable
+
+ For example, the following configuration would select example pages and
+ pages that include the \c {\ingroup tutorials} command:
+
+ \badcode
+ qhp.QtQuickControls.subprojects = examples
+ qhp.QtQuickControls.subprojects.examples.title = Examples and Tutorials
+ qhp.QtQuickControls.subprojects.examples.indexTitle = Qt Quick Controls Examples
+ qhp.QtQuickControls.subprojects.examples.selectors = doc:example group:tutorials
+ qhp.QtQuickControls.subprojects.examples.sortPages = true
+ \endcode
+
+ \section2 Adding Table of Contents
+
+ To create a table of contents for a manual, create a subproject with
+ a \c{type} property and set it to \c{manual}. The page in the documentation
+ referred to by the \c{indexTitle} property must contain a list of links
+ that acts as a table of contents for the whole manual. QDoc will take the
+ information in this list and create a table of contents for the subproject.
+
+ For example, the configuration file for Qt Creator defines only one
+ subproject for its documentation, including all the documentation in a
+ single manual:
+
+ \badcode
+ qhp.QtCreator.subprojects = manual
+ qhp.QtCreator.subprojects.manual.title = Qt Creator Manual
+ qhp.QtCreator.subprojects.manual.indexTitle = Qt Creator Manual
+ qhp.QtCreator.subprojects.manual.type = manual
+ \endcode
+
+ In this example, the page entitled "Qt Creator Manual" contains a nested
+ list of links to pages in the documentation which is duplicated in
+ Qt Assistant's Contents tab.
+*/
+
+/*!
+ \page 23-qdoc-configuration-cppvariables.html
+ \previouspage Creating Help Project Files
+ \contentspage QDoc Manual
+ \nextpage HTML Specific Configuration Variables
+
+ \title C++ Specific Configuration Variables
+
+ The C++ specific configuration variables are provided to avoid
+ erroneous documentation due to non-standard C++ constructs.
+
+ \target Cpp.ignoredirectives-variable
+ \section1 Cpp.ignoredirectives
+ The \c Cpp.ignoredirectives variable makes QDoc ignore the
+ specified non-standard constructs, within C++ source code.
+
+ If not specified by the \tt {\l Cpp.ignoretokens} or \tt {\l
+ Cpp.ignoredirectives} variables, non-standard constructs
+ (typically macros) can result in erroneous documentation.
+
+ \badcode
+ Cpp.ignoredirectives = Q_DECLARE_INTERFACE \
+ Q_DECLARE_OPERATORS_FOR_FLAGS \
+ Q_DECLARE_PRIVATE \
+ Q_DECLARE_PUBLIC \
+ Q_DISABLE_COPY \
+ Q_DUMMY_COMPARISON_OPERATOR \
+ Q_ENUMS \
+ Q_FLAGS \
+ Q_INTERFACES \
+ __attribute__
+ \endcode
+
+ makes sure that when processing the code below, for example, QDoc
+ will simply ignore the 'Q_ENUMS' and 'Q_FLAGS' expressions:
+
+ \code
+ class Q_CORE_EXPORT Qt {
+ Q_OBJECT
+ Q_ENUMS(Orientation TextFormat BackgroundMode
+ DateFormat ScrollBarPolicy FocusPolicy
+ ContextMenuPolicy CaseSensitivity
+ LayoutDirection ArrowType)
+ Q_ENUMS(ToolButtonStyle)
+ Q_FLAGS(Alignment)
+ Q_FLAGS(Orientations)
+ Q_FLAGS(DockWidgetAreas)
+
+ public:
+ ...
+ };
+ \endcode
+
+ The Q_OBJECT macro, however, is an exception: QDoc recognizes this
+ particular non-standard construct, so there is no need specifying
+ it using the \tt {\l Cpp.ignoredirectives} variable.
+
+ Regarding the Q_CORE_EXPORT macro; see the documentation of the
+ \tt {\l Cpp.ignoretokens} variable.
+
+ See also \l Cpp.ignoretokens.
+
+ \target Cpp.ignoretokens-variable
+ \section1 Cpp.ignoretokens
+
+ The \c Cpp.ignoretokens variable makes QDoc ignore the specified
+ non-standard constructs, within C++ source code.
+
+ If not specified by the \tt {\l Cpp.ignoretokens} or \tt {\l
+ Cpp.ignoredirectives} variables, non-standard constructs
+ (typically macros) can result in erroneous documentation.
+
+ In \l qtgui.qdocconf:
+
+ \badcode
+ Cpp.ignoretokens = QAXFACTORY_EXPORT \
+ QM_EXPORT_CANVAS \
+ ...
+ Q_COMPAT_EXPORT \
+ Q_CORE_EXPORT \
+ Q_EXPLICIT \
+ Q_EXPORT \
+ ...
+ Q_XML_EXPORT
+ \endcode
+
+ makes sure that when processing the code below, for example, QDoc
+ will simply ignore the 'Q_CORE_EXPORT' expression:
+
+ \code
+ class Q_CORE_EXPORT Qt {
+ Q_OBJECT
+ Q_ENUMS(Orientation TextFormat BackgroundMode
+ DateFormat ScrollBarPolicy FocusPolicy
+ ContextMenuPolicy CaseSensitivity
+ LayoutDirection ArrowType)
+ Q_ENUMS(ToolButtonStyle)
+ Q_FLAGS(Alignment)
+ Q_FLAGS(Orientations)
+ Q_FLAGS(DockWidgetAreas)
+ public:
+ ...
+ };
+ \endcode
+
+ Regarding the Q_OBJECT, Q_ENUMS and Q_FLAGS macros; see the
+ documentation of the \tt {\l Cpp.ignoredirectives} variable.
+
+ See also \l Cpp.ignoredirectives.
+*/
+
+/*!
+ \page 24-qdoc-configuration-htmlvariables.html
+ \previouspage C++ Specific Configuration Variables
+ \contentspage QDoc Manual
+ \nextpage Supporting Derived Projects
+
+ \title HTML Specific Configuration Variables
+
+ The HTML specific configuration variables define the generated
+ documentation's style, or define the contents of the
+ documentation's footer or postheader. The format of the variable
+ values are raw HTML.
+
+ \target HTML.footer-variable
+ \section1 HTML.footer
+
+ The \c HTML.footer variable defines the content of the generated
+ HTML documentation's footer.
+
+ The footer is rendered at the bottom of the generated
+ documentation page.
+
+ The variable's value is given as raw HTML code enclosed by
+ quotation marks. Note that if the value spans several lines, each
+ line needs to be enclosed by quotation marks.
+
+ \badcode
+ HTML.footer = "<p /><address><hr /><div align=\"center\">\n" \
+ ...
+ "</tr></table></div></address>"
+ \endcode
+
+ \target HTML.postheader-variable
+ \section1 HTML.postheader
+
+ The \c HTML.postheader variable defines the content of the
+ generated HTML documentation's postheader.
+
+ The header is rendered at the top of the generated documentation
+ page.
+
+ The variable's value is given as raw HTML enclosed by quotation
+ marks. Note that if the value spans several lines, each line needs
+ to be enclosed by quotation marks.
+
+ \badcode
+ HTML.postheader = "<table border=\"0\"..." \
+ ...
+ "<img src=\"images/qt-logo.png\" \
+ "align=\"right\" width=\"203\" height=\"32\""\
+ "border=\"0\" />" \
+ "</td></tr>" \
+ "</table>"
+ \endcode
+
+ The complete variable entry in \l qtgui.qdocconf provides the
+ standard header of the \l {http://doc.qt.io/qt-5/qtgui-index.html}
+ {Qt GUI Documentation}.
+
+ \target HTML.style-variable
+ \section1 HTML.style
+
+ The HTML.style variable defines the style for
+ the generated HTML documentation.
+
+ The variable's value is given as raw HTML enclosed by quotation
+ marks. Note that if the value spans several lines, each line needs
+ to be enclosed by quotation marks.
+
+ \badcode
+ HTML.style = "h3.fn,span.fn" \
+ "{ margin-left: 1cm; text-indent: -1cm; }\n" \
+ "a:link { color: #004faf; text-decoration: none }\n" \
+ "a:visited" \
+ "{ color: #672967; text-decoration: none }\n" \
+ "td.postheader { font-family: sans-serif }\n" \
+ "tr.address { font-family: sans-serif }\n" \
+ "body { background: #ffffff; color: black; }"
+ \endcode
+
+ \target HTML.stylesheets-variable
+ \section1 HTML.stylesheets
+
+ The HTML.stylesheets variable defines a list of stylesheets
+ to use for the generated HTML documentation.
+
+ Using separate stylesheets for the documentation makes it easier
+ to customize and experiment with the style used once the contents
+ has been generated. Typically, it is only necessary to define a
+ single stylesheet for any set of documentation; for example:
+
+ \badcode
+ HTML.stylesheets = classic.css
+ \endcode
+
+ QDoc expects to find stylesheets in the directory containing the
+ \l qtgui.qdocconf file, and it will copy those specified to the output
+ directory alongside the HTML pages.
+
+ \target HTML.tocdepth
+ \section1 HTML.tocdepth
+
+ The HTML.tocdepth variable defines how many document sections are printed in
+ the table of contents. Setting tocdepth to \c 0 disables the table of
+ contents while not setting the variable prints all document sections.
+
+*/
+
+/*!
+ \page 25-qdoc-configuration-derivedprojects.html
+ \previouspage HTML Specific Configuration Variables
+ \contentspage QDoc Manual
+ \nextpage Example Manifest Files
+
+ \title Supporting Derived Projects
+
+ Some configuration variables allow you to use QDoc to support
+ Qt-based projects. They allow your project to contain links to the
+ online Qt documentation, which means that QDoc will be able to
+ create links to the class reference documentation, without any
+ explicit linking command.
+
+ \target description-variable
+ \section1 description
+
+ The description variable holds a short description of the
+ associated project.
+
+ See also \l project.
+
+ \target indexes-variable
+ \section1 indexes
+
+ The \c indexes variable lists the index files that will be used to
+ generate references.
+
+ For example. to make a derived Qt project contain links to the Qt
+ Reference documentation, you need to specify the associated index
+ file:
+
+ \badcode
+ indexes = $QTDIR/doc/html/qt.index
+ \endcode
+
+ See also \l project and \l url.
+
+ \target project-variable
+ \section1 project
+
+ The \c project variable provides a name for the project associated
+ with the \c .qdocconf file.
+
+ The project's name is used to form a file name for the associated
+ project's \e index file.
+
+ \badcode
+ project = QtCreator
+ \endcode
+
+ This will cause an index file called \c qtcreator.index to be
+ created.
+
+ See also \l description and \l indexes.
+
+ \target url-variable
+ \section1 url
+
+ The \c url variable holds the base URL for the reference
+ documentation associated with the current project.
+
+ The URL is stored in the generated index file for the
+ project. When we use the index on its own, QDoc will use this as
+ the base URL when constructing links to classes, functions, and
+ other things listed in the index.
+
+ \badcode
+ project = Qt
+ description = Qt Reference Documentation
+ url = http://doc.qt.io/qt-4.8/
+
+ ...
+ \endcode
+
+ This makes sure that whenever \c qt.index is used to generate
+ references to for example Qt classes, the base URL is \c
+ http://doc.qt.io/qt-4.8/.
+
+ See also \l indexes.
+
+ \target howto
+ \section1 How to Support Derived Projects
+
+ This feature makes use of the comprehensive indexes generated by
+ QDoc when it creates the Qt reference documentation.
+
+ For example, \l qtgui.qdocconf (the configuration file for Qt)
+ contains the following variable definitions:
+
+ \badcode
+ project = Qt
+ description = Qt Reference Documentation
+ url = http://doc.qt.io/qt-4.8/
+
+ ...
+ \endcode
+
+ The \l project variable name is used to form a file name for the
+ index file; in this case the \c qt.index file is created. The \l
+ url is stored in the index file. Afterwards, QDoc will use this
+ as the base URL when constructing links to classes, functions,
+ and other things listed in the index.
+
+*/
+
+/*!
+ \page 26-qdoc-configuration-example-manifest-files.html
+ \previouspage Supporting Derived Projects
+ \contentspage QDoc Manual
+
+ \title Example Manifest Files
+
+ QDoc generates XML files that contain information about all documented
+ examples and demos. These files, named \c {examples-manifest.xml} and
+ \c {demos-manifest.xml}, are used by Qt Creator to present a list of
+ examples in its welcome screen and to link to their documentation.
+
+ \section1 Manifest XML Structure
+
+ A manifest file has the following structure:
+
+ \badcode
+ <?xml version="1.0" encoding="UTF-8"?>
+ <instructionals module="QtGui">
+ <examples>
+ <example
+ name="Analog Clock Window Example"
+ docUrl="qthelp://org.qt-project.qtgui.502/qtgui/analogclock.html"
+ projectPath="gui/analogclock/analogclock.pro"
+ imageUrl="qthelp://org.qt-project.qtgui.502/qtgui/images/analogclock-window-example.png">
+ <description><![CDATA[The Analog Clock Window example shows how
+ to draw the contents of a custom window.]]></description>
+ <tags>analog,clock,window</tags>
+ <fileToOpen>gui/analogclock/main.cpp</fileToOpen>
+ </example>
+ ...
+ </examples>
+ </instructionals>
+ \endcode
+
+ Each \c {<example>} element contains information about a name,
+ description, the location of the project file and documentation,
+ as well as a list of tags associated with the example.
+
+ \target metacontent
+ \section1 Manifest Meta Content
+
+ It is possible to augment the manifest files with additional
+ meta-content - that is, extra attributes and tags for selected
+ examples, using the \c manifestmeta configuration command.
+
+ One use case for meta-content is highlighting a number of prominent
+ examples. Another is improving search functionality by adding
+ relevant keywords as tags for a certain category of examples.
+
+ The examples for which meta-content is applied to is specified using
+ one or more filters. Matching examples to filters is done based on
+ names, with each example name prefixed with a module name and a
+ slash. Simple wildcard matching is supported; by using \c {*} at the
+ end it's possible to match multiple examples with a single string.
+
+ Example:
+
+ \badcode
+ manifestmeta.filters = highlighted sql webkit global
+
+ manifestmeta.highlighted.names = "QtGui/Analog Clock Window Example" \
+ "QtWidgets/Analog Clock Example"
+ manifestmeta.highlighted.attributes = isHighlighted:true
+
+ manifestmeta.sql.names = "QtSql/*"
+ manifestmeta.sql.tags = database,sql
+
+ manifestmeta.webkit.names = "QtWebKitExamples/*"
+ manifestmeta.webkit.tags = webkit
+
+ manifestmeta.global.names = *
+ manifestmeta.global.tags = qt5
+ \endcode
+
+ Above, an \c isHighlighted attribute is added to two examples. If
+ the attribute value is omitted, QDoc uses the string \c {true} by
+ default. Extra tags are added for Qt WebKit and Qt SQL examples, and
+ another tag is applied to all examples by using just \c {*} as the
+ match string.
+*/
+/*!
+ \page 21-3-qt-dita-xml-output.html
+ \previouspage minimum.qdocconf
+ \contentspage QDoc Manual
+ \nextpage QA Pages
+
+ \title Generating DITA XML Output
+
+ QDoc can generate \l {http://dita.xml.org} {DITA XML output}.
+
+ In your configuration file, set your \c {outputformats} variable
+ to \c {DITAXML}, and send the output to an appropriate directory:
+
+ \badcode
+ outputdir = $QTDIR/doc/ditaxml
+ outputformats = DITAXML
+ \endcode
+
+ And include these macros in your configuration file to prevent
+ QDoc from doing some escaping that doesn't validate in XML:
+
+ \badcode
+ macro.aacute.DITAXML = "&aacute;"
+ macro.Aring.DITAXML = "&Aring;"
+ macro.aring.DITAXML = "&aring;"
+ macro.Auml.DITAXML = "&Auml;"
+ macro.br.DITAXML = " "
+ macro.BR.DITAXML = " "
+ macro.copyright.DITAXML = "&copy;"
+ macro.eacute.DITAXML = "&eacute;"
+ macro.hr.DITAXML = " "
+ macro.iacute.DITAXML = "&iacute;"
+ macro.oslash.DITAXML = "&oslash;"
+ macro.ouml.DITAXML = "&ouml;"
+ macro.raisedaster.DITAXML = "<sup>*</sup>"
+ macro.rarrow.DITAXML = "&rarr;"
+ macro.reg.DITAXML = "<sup>&reg;</sup>"
+ macro.uuml.DITAXML = "&uuml;"
+ macro.mdash.DITAXML = "&mdash;"
+ macro.emptyspan.DITAXML = " "
+ \endcode
+
+ You can also set default values for some of the tags in the DITA
+ \c {<prolog>} and \c {<metadata>} elements:
+
+ \badcode
+ dita.metadata.default.author = Qt Development Frameworks
+ dita.metadata.default.permissions = all
+ dita.metadata.default.publisher = Qt Project
+ dita.metadata.default.copyryear = 2015
+ dita.metadata.default.copyrholder = Qt Project
+ dita.metadata.default.audience = programmer
+ \endcode
+
+ See the \l {meta-command}
+ {\\meta} command for more details on DITA metadata.
+
+*/
+
+
+/*!
+ \page 21-1-minimum-qdocconf.html
+ \previouspage qtgui.qdocconf
+ \contentspage QDoc Manual
+ \nextpage Generating DITA XML Output
+
+ \title minimum.qdocconf
+
+ \quotefile examples/minimum.qdocconf
+*/
+
+/*!
+ \page 21-2-qtgui-qdocconf.html
+ \previouspage Supporting Derived Projects
+ \contentspage QDoc Manual
+ \nextpage minimum.qdocconf
+
+ \title qtgui.qdocconf
+
+ \quotefile files/qtgui.qdocconf
+*/
diff --git a/src/qdoc/doc/qdoc-manual-topiccmds.qdoc b/src/qdoc/doc/qdoc-manual-topiccmds.qdoc
new file mode 100644
index 000000000..1dfd03163
--- /dev/null
+++ b/src/qdoc/doc/qdoc-manual-topiccmds.qdoc
@@ -0,0 +1,1594 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the documentation of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:FDL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Free Documentation License Usage
+** Alternatively, this file may be used under the terms of the GNU Free
+** Documentation License version 1.3 as published by the Free Software
+** Foundation and appearing in the file included in the packaging of
+** this file. Please review the following information to ensure
+** the GNU Free Documentation License version 1.3 requirements
+** will be met: http://www.gnu.org/copyleft/fdl.html.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*!
+ \page 13-qdoc-commands-topics.html
+ \previouspage Command Index
+ \contentspage QDoc Manual
+ \nextpage Context Commands
+
+ \title Topic Commands
+
+ A topic command tells QDoc which source code element is being
+ documented. Some topic commands allow you to create documentation
+ pages that aren't tied to any underlying source code element.
+
+ When QDoc processes a QDoc comment, it tries to connect the
+ comment to an element in the source code by first looking for a
+ topic command that names the source code element. If there is no
+ topic command, QDoc tries to connect the comment to the source
+ code element that immediately follows the comment. If it can't do
+ either of these and if there is no topic command that indicates
+ the comment does not have an underlying source code element (e.g.
+ \l{page-command} {\\page}), then the comment is discarded.
+
+ \target topic argument
+
+ The name of the entity being documented is usually the only
+ argument for a topic command. Use the complete name. Sometimes
+ there can be a second parameter in the argument. See e.g. \l
+ {page-command} {\\page}.
+
+ \code
+ \enum QComboBox::InsertPolicy
+ \endcode
+
+ The \l {fn-command} {\\fn} command is a special case. For the \l
+ {fn-command} {\\fn} command, use the function's signature
+ including the class qualifier.
+
+ \code
+ \fn void QGraphicsWidget::setWindowFlags(Qt::WindowFlags wFlags)
+ \endcode
+
+ A topic command can appear anywhere in a comment but must stand
+ alone on its own line. It is good practice is to let the topic command
+ be the first line of the comment. If the argument spans several
+ lines, make sure that each line (except the last one) is ended
+ with a backslash. Moreover, QDoc counts parentheses, which means
+ that if it encounters a '(' it considers everything until the
+ closing ')' as its argument.
+
+ If a topic command is repeated with different arguments, the
+ same documentation will appear for both the units.
+
+ \code
+ / *!
+ \fn void PreviewWindow::setWindowFlags()
+ \fn void ControllerWindow::setWindowFlags()
+
+ Sets the widgets flags using the QWidget::setWindowFlags()
+ function.
+
+ Then runs through the available window flags, creating a text
+ that contains the names of the flags that matches the flags
+ parameter, displaying the text in the widgets text editor.
+ * /
+ \endcode
+
+ The \c PreviewWindow::setWindowFlags() and \c
+ ControllerWindow::setWindowFlags() functions will get the same
+ documentation.
+
+ \target class-command
+ \section1 \\class
+
+ The \\class command is for documenting a C++ class. The argument
+ is the complete name of the class. The command tells QDoc that a
+ class is part of the public API, and lets you enter a detailed
+ description.
+
+ \code
+ / *!
+ \class QMap::iterator
+
+ \brief The QMap::iterator class provides an STL-style
+ non-const iterator for QMap and QMultiMap.
+
+ QMap features both \l{STL-style iterators} and
+ \l{Java-style iterators}. The STL-style iterators ...
+ * /
+ \endcode
+
+ The HTML documentation for the named class is written to a
+ \c{.html} file named from the class name, in lower case, and with
+ the double colon qualifier(s) replaced with '-'. For example, the
+ documentation for the \c QMap::Iterator class is written to \c
+ qmap-iterator.html.
+
+ \target framework
+
+ The file contains the class description from the \\class comment,
+ plus the documentation generated from QDoc comments for all the
+ class members: a list of the class's types, properties,
+ functions, signals, and slots.
+
+ In addition to the detailed description of the class, the \\class
+ comment typically contains a \l {brief-command} {\\brief} command
+ and one or more \l{Markup Commands}. See the \\class command for
+ any of the Qt class for examples. Here is a very simple example:
+
+ \code
+ / *!
+ \class PreviewWindow
+ \brief The PreviewWindow class is a custom widget.
+ displaying the names of its currently set
+ window flags in a read-only text editor.
+
+ \ingroup miscellaneous
+
+ The PreviewWindow class inherits QWidget. The widget
+ displays the names of its window flags set with the \l
+ {function} {setWindowFlags()} function. It is also
+ provided with a QPushButton that closes the window.
+
+ ...
+
+ \sa QWidget
+ * /
+ \endcode
+
+ The way QDoc renders this \\class will depend a lot on your \c
+ {style.css} file, but the general outline of the class reference
+ page will look like this:
+
+ \quotation
+ \raw HTML
+ <h1>PreviewWindow Class Reference</h1>
+ \endraw
+
+ The PreviewWindow class is a custom widget displaying
+ the names of its currently set window flags in a
+ read-only text editor. \l {preview window} {More...}
+
+ \raw HTML
+ <h3>Properties</h3>
+ \endraw
+
+ \list
+ \li 52 properties inherited from QWidget
+ \li 1 property inherited from QObject
+ \endlist
+
+ \raw HTML
+ <h3>Public Functions</h3>
+ \endraw
+
+ \list
+ \li \l {constructor} {PreviewWindow}(QWidget *parent = 0)
+ \li void \l {function} {setWindowFlags}(Qt::WindowFlags flags)
+ \endlist
+
+ \list
+ \li 183 public functions inherited from QWidget
+ \li 28 public functions inherited from QObject
+ \endlist
+
+ \raw HTML
+ <h3>Public Slots</h3>
+ \endraw
+
+ \list
+ \li 17 public slots inherited from QWidget
+ \li 1 public slot inherited from QObject
+ \endlist
+
+ \raw HTML
+ <h3>Additional Inherited Members</h3>
+ \endraw
+
+ \list
+ \li 1 signal inherited from QWidget
+ \li 1 signal inherited from QObject
+ \li 4 static public members inherited from QWidget
+ \li 4 static public members inherited from QObject
+ \li 39 protected functions inherited from QWidget
+ \li 7 protected functions inherited from QObject
+ \endlist
+
+ \target preview window
+
+ \raw HTML
+ <hr />
+ <h2>Detailed Description</h2>
+ \endraw
+
+ The PreviewWindow class is a custom widget displaying
+ the names of its currently set window flags in a
+ read-only text editor.
+
+ The PreviewWindow class inherits QWidget. The widget
+ displays the names of its window flags set with the \l
+ {function} {setWindowFlags()} function. It is also
+ provided with a QPushButton that closes the window.
+
+ ...
+
+ See also QWidget.
+
+ \raw HTML
+ <hr />
+ <h2>Member Function Documentation</h2>
+ \endraw
+
+ \target constructor
+ \raw HTML
+ <h3>PreviewWindow(QWidget *parent = 0)</h3>
+ \endraw
+
+ Constructs a preview window widget with \e parent.
+
+ \target function
+ \raw HTML
+ <h3>setWindowFlags(Qt::WindowFlags flags)</h3>
+ \endraw
+
+ Sets the widgets flags using the
+ QWidget::setWindowFlags() function.
+
+ Then runs through the available window flags,
+ creating a text that contains the names of the flags
+ that matches the flags parameter, displaying
+ the text in the widgets text editor.
+ \endquotation
+
+ \target enum-command
+ \section1 \\enum
+
+ The \\enum command is for documenting a C++ enum type. The
+ argument is the full name of the enum type.
+
+ The enum values are documented in the \\enum comment using the \l
+ {value-command} {\\value} command. If an enum value is not
+ documented with \\value, QDoc emits a warning. These warnings can
+ be avoided using the \l {omitvalue-command} {\\omitvalue} command
+ to tell QDoc that an enum value should not be documented. The enum
+ documentation will be included on the class reference page, header
+ file page, or namespace page where the enum type is defined. For
+ example, consider the enum type \c {Corner} in the Qt namespace:
+
+ \code
+ enum Corner {
+ TopLeftCorner = 0x00000,
+ TopRightCorner = 0x00001,
+ BottomLeftCorner = 0x00002,
+ BottomRightCorner = 0x00003
+ #if defined(QT3_SUPPORT) && !defined(Q_MOC_RUN)
+ ,TopLeft = TopLeftCorner,
+ TopRight = TopRightCorner,
+ BottomLeft = BottomLeftCorner,
+ BottomRight = BottomRightCorner
+ #endif
+ };
+ \endcode
+
+ This enum can be cocumented this way:
+
+ \code
+ / *!
+ \enum Qt::Corner
+
+ This enum type specifies a corner in a rectangle:
+
+ \value TopLeftCorner
+ The top-left corner of the rectangle.
+ \value TopRightCorner
+ The top-right corner of the rectangle.
+ \value BottomLeftCorner
+ The bottom-left corner of the rectangle.
+ \value BottomRightCorner
+ The bottom-right corner of the rectangle.
+
+ \omitvalue TopLeft
+ \omitvalue TopRight
+ \omitvalue BottomLeft
+ \omitvalue BottomRight
+ * /
+ \endcode
+
+ Note the inclusion of the namespace qualifier. QDoc will render
+ this enum type in \c {qt.html} like this:
+
+ \quotation
+ \raw HTML
+ <h3 class="fn"><a name="Corner-enum"></a>enum Qt::Corner</h3>
+
+ <p>This enum type specifies a corner in a rectangle:</p>
+
+ <table border="1" cellpadding="2" cellspacing="1" width="100%">
+ <tr>
+ <th width="25%">Constant</th>
+ <th width="15%">Value</th>
+ <th width="60%">Description</th>
+ </tr>
+
+ <tr>
+ <td valign="top"><tt>Qt::TopLeftCorner</tt></td>
+ <td align="center" valign="top"><tt>0x00000</tt></td>
+ <td valign="top">The top-left corner of the rectangle.</td>
+ </tr>
+
+ <tr>
+ <td valign="top"><tt>Qt::TopRightCorner</tt></td>
+ <td align="center" valign="top"><tt>0x00001</tt></td>
+ <td valign="top">The top-right corner of the rectangle.</td>
+ </tr>
+
+ <tr>
+ <td valign="top"><tt>Qt::BottomLeftCorner</tt></td>
+ <td align="center" valign="top"><tt>0x00002</tt></td>
+ <td valign="top">The bottom-left corner of the rectangle.</td>
+ </tr>
+
+ <tr>
+ <td valign="top"><tt>Qt::BottomRightCorner</tt></td>
+ <td align="center" valign="top"><tt>0x00003</tt></td>
+ <td valign="top">The bottom-right corner of the rectangle.</td>
+ </tr>
+
+ </table>
+ \endraw
+ \endquotation
+
+ See also \l {value-command} {\\value} and \l {omitvalue-command} {\\omitvalue}.
+
+ \target example-command
+ \section1 \\example
+
+ The \\example command is for documenting an example. The argument
+ is the example's path relative to omne of the paths listed in the
+ \l {exampledirs-variable} {exampledirs} variable in the QDoc
+ configuration file.
+
+ The documentation page will be output to \c {path-to-example}.html.
+ QDoc will add a list of all the example's source files at the top
+ of the page.
+
+ For example, if \l {exampledirs-variable} {exampledirs} contains
+ \c $QTDIR/examples/widgets/imageviewer, then
+
+ \code
+ / *!
+ \example widgets/imageviewer
+ \title ImageViewer Example
+ \subtitle
+
+ The example shows how to combine QLabel and QScrollArea
+ to display an image.
+
+ ...
+ * /
+ \endcode
+
+ QDoc renders this example in widgets-imageviewer.html:
+
+ \quotation
+ \raw HTML
+ <center><h1>Image Viewer Example</h1></center>
+ \endraw
+
+ Files:
+ \list
+ \li \l{http://doc.qt.io/qt-5/qtwidgets-widgets-imageviewer-imageviewer-cpp.html}
+ {widgets/imageviewer/imageviewer.cpp}
+ \li \l{http://doc.qt.io/qt-5/qtwidgets-widgets-imageviewer-imageviewer-h.html}
+ {widgets/imageviewer/imageviewer.h}
+ \li \l{http://doc.qt.io/qt-5/qtwidgets-widgets-imageviewer-main-cpp.html}
+ {widgets/imageviewer/main.cpp}
+ \endlist
+
+ The example shows how to combine QLabel and QScrollArea
+ to display an image.
+
+ ...
+ \endquotation
+
+ \target externalpage-command
+ \section1 \\externalpage
+
+ The \\externalpage command assigns a title to an external URL.
+
+ \code
+ / *!
+ \externalpage http://doc.qt.io/
+ \title Qt Documentation Site
+ * /
+ \endcode
+
+ This allows you to include a link to the external page in your
+ documentation this way:
+
+ \code
+ / *!
+ At the \l {Qt Documentation Site} you can find the latest
+ documentation for Qt, Qt Creator, the Qt SDK and much more.
+ * /
+ \endcode
+
+ QDoc renders this as:
+
+ \quotation
+ At the \l {http://doc.qt.io/}{Qt Documentation Site}
+ you can find the latest documentation for Qt, Qt Creator, the Qt SDK
+ and much more.
+ \endquotation
+
+ To achieve the same result without using the \\externalpage
+ command, you would have to hard-code the address into your
+ documentation:
+
+ \code
+ / *!
+ At the \l {http://doc.qt.io/}{Qt Documentation Site}
+ you can find the latest documentation for Qt, Qt Creator, the Qt SDK
+ and much more.
+ * /
+ \endcode
+
+ The \\externalpage command makes it easier to maintain the
+ documentation. If the address changes, you only need to change the
+ argument of the \\externalpage command.
+
+ \target fn-command
+ \section1 \\fn (function)
+
+ The \\fn command is for documenting a function. The argument is
+ the function's signature, including its return type, const-ness,
+ and list of formal arguments with types. If the named function
+ doesn't exist, QDoc emits a warning.
+
+ \note The \\fn command is QDoc's default command: when no
+ topic command can be found in a QDoc comment, QDoc tries to tie
+ the documentation to the following code as if it is the
+ documentation for a function. Hence, it is normally not necessary
+ to include this command when documenting a function, if the
+ function's QDoc comment is written immediately above the function
+ implementation in the \c .cpp file. But it must be present when
+ documenting an inline function in the \c .cpp file that is
+ implemented in the \c .h file.
+
+ \code
+ / *!
+ \fn bool QToolBar::isAreaAllowed(Qt::ToolBarArea area) const
+
+ Returns \c true if this toolbar is dockable in the given
+ \a area; otherwise returns \c false.
+ * /
+ \endcode
+
+ QDoc renders this as:
+
+ \quotation
+ \raw HTML
+ <h3>bool QToolBar::isAreaAllowed(Qt::ToolBarArea area) const
+ </h3>
+ \endraw
+
+ Returns \c true if this toolbar is dockable in the given
+ \a area; otherwise returns \c false.
+ \endquotation
+
+ See also \l {overload-command} {\\overload}.
+
+ \target group-command
+ \section1 \\group
+
+ The \\group command creates a separate page that lists the classes
+ belonging to the group. The argument is the group name.
+
+ A class is included in a group by using the \l {ingroup-command}
+ {\\ingroup} command. Overview pages can also be related to a group
+ using the same command, but the list of overview pages must be
+ requested explicitly using the \l {generatelist-command}
+ {\\generatelist} command (see example below).
+
+ The \\group command is typically followed by a \l {title-command}
+ {\\title} command and a short introduction to the group. The
+ HTML page for the group is written to a \c {.html} file put in
+ <lower-case>\e{group}.html.
+
+ Each class name is listed as a link to the class reference page
+ followed by the text from the class's \l {brief-command} {\\brief}
+ texts.
+
+ \code
+ / *!
+ \group io
+
+ \title Input/Output and Networking
+
+ These classes are used to handle input and output to
+ and from external devices, processes, files etc., as
+ well as manipulating files and directories.
+ * /
+ \endcode
+
+ QDoc generates a group page in \c{io.html} that will look
+ like this:
+
+ \quotation
+ \raw HTML
+
+ <h1>Input/Output and Networking</h1>
+
+ <p>These classes are used to handle input and output
+ to and from external devices, processes, files etc., as
+ well as manipulating files and directories.</p>
+
+ <p>
+ <table width="100%">
+ <tr valign="top" bgcolor="#e0e0e0">
+ <td><b>
+ <a href="http://doc.qt.io/qt-5/qabstractsocket.html">QAbstractSocket</a>
+ </b></td>
+ <td>
+ The base functionality common to all socket types
+ </td></tr>
+
+ <tr valign="top" bgcolor="#e0e0e0">
+ <td><b>
+ <a href="http://doc.qt.io/qt-5/qbuffer.html">QBuffer</a>
+ </b></td>
+ <td>
+ QIODevice interface for a QByteArray
+ </td></tr>
+
+ <tr valign="top" bgcolor="#e0e0e0">
+ <td><b>
+ <a href="http://doc.qt.io/qt-5/qclipboard.html">QClipboard</a>
+ </b></td>
+ <td>
+ Access to the window system clipboard
+ </td></tr>
+ </table>
+ \endraw
+ \endquotation
+
+ Note that overview pages related to the group, must be listed
+ explicitly using the \l {generatelist-command} {\\generatelist}
+ command with the \c related argument.
+
+ \code
+ / *!
+ \group architecture
+
+ \title Architecture
+
+ These documents describe aspects of Qt's architecture
+ and design, including overviews of core Qt features and
+ technologies.
+
+ \generatelist{related}
+ * /
+ \endcode
+
+ See also \l {ingroup-command} {\\ingroup} and \l
+ {generatelist-command} {\\generatelist}.
+
+ \target headerfile-command
+ \section1 \\headerfile
+
+ The \\headerfile command is for documenting the global functions,
+ types and macros that are declared in a header file, but not in a
+ namespace. The argument is the name of the header file. The HTML
+ page is written to a \c {.html} file constructed from the header
+ file argument.
+
+ The documentation for a function, type, or macro that is declared
+ in the header file being documented, is included in the header file
+ page using the \l {relates-command} {\\relates} command.
+
+ If the argument doesn't exist as a header file, the \\headerfile
+ command creates a documentation page for the header file anyway.
+
+ \code
+ / *!
+ \headerfile <QtAlgorithms>
+
+ \title Generic Algorithms
+
+ \brief The <QtAlgorithms> header file provides
+ generic template-based algorithms.
+
+ Qt provides a number of global template functions in \c
+ <QtAlgorithms> that work on containers and perform
+ well-know algorithms.
+ * /
+ \endcode
+
+ QDoc generates a header file page \c{qtalgorithms.html} that looks
+ like this:
+
+ \quotation
+ \raw HTML
+ <center><h1>&lt;QtAlgorithms&gt; -
+ Generic Algorithms</h1></center>
+ <p>The <QtAlgorithms> header file provides generic
+ template-based algorithms.
+ <a href="13-qdoc-commands-topics.html#header-command">More...</a>
+ </p>
+
+ <h3>Functions</h3>
+ <ul>
+ <li>RandomAccessIterator
+ <a href="http://doc.qt.io/qt-5/qtalgorithms-obsolete.html#qBinaryFind">qBinaryFind</a></b>
+ (RandomAccessIterator begin, RandomAccessIterator end,
+ const T & value)</li>
+ <li>...</li></ul>
+ <hr />
+ \endraw
+
+ \target header
+
+ \raw HTML
+ <h2>Detailed Description</h2>
+ <p>The <QtAlgorithms> header file provides generic
+ template-based algorithms. </p>
+ \endraw
+
+ Qt provides a number of global template functions in \c
+ <QtAlgorithms> that work on containers and perform
+ well-know algorithms.
+
+ ...
+ \endquotation
+
+ \target macro-command
+ \section1 \\macro
+
+ The \\macro command is for documenting a C++ macro. The argument
+ is the macro in one of three styles: function-like macros like
+ Q_ASSERT(), declaration-style macros like Q_PROPERTY(), and macros
+ without parentheses like Q_OBJECT.
+
+ The \\macro comment must contain a \l {relates-command}
+ {\\relates} command that attaches the macro comment to a class,
+ header file, or namespace. Otherwise, the documentation will be
+ lost. Here are three example macro comments followed by what they
+ might look like in \c {qtglobal.html} or \c {qobject.html}:
+
+ \code
+ / *!
+ \macro void Q_ASSERT(bool test)
+ \relates <QtGlobal>
+
+ Prints a warning message containing the source code
+ file name and line number if \a test is false.
+
+ ...
+
+ \sa Q_ASSERT_X(), qFatal(), {Debugging Techniques}
+ * /
+ \endcode
+
+ \quotation
+ \raw HTML
+ <h3>void Q_ASSERT ( bool <i>test</i> )</h3>
+ \endraw
+
+ Prints a warning message containing the source code
+ file name and line number if \a test is false.
+
+ ...
+
+ See also Q_ASSERT_X(), qFatal() and \l {Debugging Techniques}.
+
+ \endquotation
+
+ \code
+ / *!
+ \macro Q_PROPERTY(...)
+ \relates QObject
+
+ This macro declares a QObject property. The syntax is:
+
+ ...
+
+ \sa {Qt's Property System}
+ * /
+ \endcode
+
+ \quotation
+ \raw HTML
+ <h3>Q_PROPERTY ( ... )</h3>
+ \endraw
+
+ This macro declares a QObject property. The syntax is:
+
+ ...
+
+ See also \l {Qt's Property System}.
+ \endquotation
+
+ \code
+ / *!
+ \macro Q_OBJECT
+ \relates QObject
+
+ The Q_OBJECT macro must appear in the private section
+ of a class definition that declares its own signals and
+ slots, or that uses other services provided by Qt's
+ meta-object system.
+
+ ...
+
+ \sa {Meta-Object System}, {Signals and Slots}, {Qt's
+ Property System}
+ * /
+ \endcode
+
+ \quotation
+ \raw HTML
+ <h3>Q_OBJECT</h3>
+ \endraw
+
+ The Q_OBJECT macro must appear in the private section
+ of a class definition that declares its own signals and
+ slots or that uses other services provided by Qt's
+ meta-object system.
+
+ ...
+
+ See also \l {Meta-Object System}, \l {Signals &
+ Slots} and \l {Qt's Property System}.
+ \endquotation
+
+ \target module-command
+ \section1 \\module
+
+ The \\module creates a page that lists the classes belonging to
+ the module specified by the command's argument. A class included
+ in the module by including the \l {inmodule-command} {\\inmodule}
+ command in the \\class comment.
+
+ The \\module command is typically followed by a \l {title-command}
+ {\\title} and a \l {brief-command} {\\brief} command. Each class
+ is listed as a link to the class reference page followed by the
+ text from the class's \l {brief-command} {\\brief} command. For
+ example:
+
+ \code
+ / *!
+ \module QtNetwork
+
+ \title Qt Network Module
+
+ \brief Contains classes for writing TCP/IP clients and servers.
+
+ The network module provides classes to make network
+ programming easier and portable. It offers both
+ high-level classes such as QNetworkAccessManager that
+ implements application-level protocols, and
+ lower-level classes such as QTcpSocket, QTcpServer, and
+ QUdpSocket.
+ * /
+ \endcode
+
+ QDoc renders this in \c {qtnetwork.html} like this:
+
+ \quotation
+ \raw HTML
+ <h1><center>Qt Network Module</center></h1>
+ \endraw
+
+ The Qt Network module offers classes that allow you to
+ write TCP/IP clients and servers.\l {module
+ details} {More...}
+
+ \raw HTML
+ <p>
+ <table width="100%">
+ <tr valign="top" bgcolor="#d0d0d0">
+ <td><b>
+ <a href="http://doc.qt.io/qt-5/qabstractsocket.html">QAbstractSocket</a>
+ </b></td>
+ <td>
+ The base functionality common to all socket types
+ </td></tr>
+
+ <tr valign="top" bgcolor="#d0d0d0">
+ <td><b>
+ <a href="http://doc.qt.io/archives/qt-4.7/qftp.html">QFtp</a>
+ </b></td>
+ <td>
+ Implementation of the FTP protocol
+ </td></tr>
+
+ <tr valign="top" bgcolor="#d0d0d0">
+ <td>...</td>
+ <td>...</td>
+ </tr>
+ </table>
+
+ <p><hr /></p>
+ \endraw
+
+ \target module details
+
+ \raw HTML
+ <h2>Detailed Description</h2>
+
+ <p>
+ The Qt Network module offers classes that allow you to
+ write TCP/IP clients and servers.
+ </p>
+
+ <p>
+ The network module provides classes to make network
+ programming easier and portable. It offers both
+ high-level classes such as QNetworkAccessManager that
+ implements application-level protocols, and
+ lower-level classes such as QTcpSocket, QTcpServer, and
+ QUdpSocket.
+ </p>
+ \endraw
+
+ ...
+
+ \endquotation
+
+ The \l {noautolist-command} {\\noautolist} command can be used here
+ to omit the automatically generated list of classes at the end.
+
+ See also \l {inmodule-command} {\\inmodule}
+
+ \target namespace-command
+ \section1 \\namespace
+
+ The \\namespace command is for documenting the contents of the C++
+ namespace named as its argument. The documentation outline QDoc
+ generates for a namespace is similar to the outline it generates
+ for a C++ class.
+
+ \code
+ / *!
+ \namespace Qt
+
+ \brief Contains miscellaneous identifiers used throughout the Qt library.
+ * /
+ \endcode
+
+ QDoc renders this in \c{qt.html} like this:
+
+ \quotation
+ \raw HTML
+ <center><h1>Qt Namespace Reference</h1></center>
+ <p>The Qt namespace contains miscellaneous
+ identifiers used throughout the Qt library.
+ <a href="13-qdoc-commands-topics.html#name">More...</a>
+ </p>
+
+ <pre>#include &lt;Qt&gt;</pre>
+ <ul>
+ <li>
+ <a href="http://doc.qt.io/archives/qt-4.7/qt-qt3.html">
+ Qt 3 support members</a></li>
+ </ul>
+
+
+ <h3>Types</h3>
+ <ul>
+ <li>flags
+ <a href="http://doc.qt.io/archives/qt-4.7/qt.html#AlignmentFlag-enum">Alignment</a></b></li>
+ <li>...</li></ul>
+ <hr />
+ \endraw
+
+ \target name
+
+ \raw HTML
+ <h2>Detailed Description</h2>
+ <p>Contains miscellaneous identifiers
+ used throughout the Qt library.</p>
+ \endraw
+
+ ...
+ \endquotation
+
+ \target page-command
+ \section1 \\page
+
+ The \\page command is for creating a stand-alone documentation
+ page. The argument can consist of two parts separated by a
+ space. The first part is the name of the file where QDoc should
+ store the page. The second part, if present, is a word that
+ specifies the page type. Currently, the second part can be one of
+ the following list of words:
+
+ \list
+
+ \li faq - A frequently asked question.
+
+ \li howto - A user guide on how to use some components of the
+ software.
+
+ \li example - A page that describes a working example.
+
+ \li overview - For text pages that provide an overview of some
+ important subject.
+
+ \li tutorial - For text pages that are part of a tutorial.
+
+ \li api - This is the type of page used for C++ class references and
+ QML type references. You should never use this one for the pages
+ you write, because this one is reserved for qdoc.
+
+ \endlist
+
+ The page title is set using the \l {title-command} {\\title}
+ command.
+
+ \code
+ / *!
+ \page aboutqt.html
+
+ \title About Qt
+
+ Qt is a C++ toolkit for cross-platform GUI
+ application development. Qt provides single-source
+ portability across Microsoft Windows, OS X, Linux,
+ and all major commercial Unix variants.
+
+ Qt provides application developers with all the
+ functionality needed to build applications with
+ state-of-the-art graphical user interfaces. Qt is fully
+ object-oriented, easily extensible, and allows true
+ component programming.
+
+ ...
+ * /
+ \endcode
+
+ QDoc renders this page in \c {aboutqt.html}.
+
+ \target property-command
+ \section1 \\property
+
+ The \\property command is for documenting a Qt property. The
+ argument is the full property name.
+
+ A property is defined using the Q_PROPERTY() macro. The macro
+ takes as arguments the property's name and its set, reset and get
+ functions.
+
+ \code
+ Q_PROPERTY(QString state READ state WRITE setState)
+ \endcode
+
+ The set, reset and get functions don't need to be documented,
+ documenting the property is sufficient. QDoc will generate a list
+ of the access function that will appear in the property
+ documentation which in turn will be located in the documentation
+ of the class that defines the property.
+
+ The \\property command comment typically includes a \l
+ {brief-command} {\\brief} command. For properties the \l
+ {brief-command} {\\brief} command's argument is a sentence
+ fragment that will be included in a one line description of the
+ property. The command follows the same rules for the \l
+ {brief-property} {description} as the \l {variable-command}
+ {\\variable} command.
+
+ \code
+ / *!
+ \property QPushButton::flat
+ \brief Whether the border is disabled.
+
+ This property's default is false.
+ * /
+ \endcode
+
+ QDoc includes this in \c {qpushbutton.html} like this:
+
+ \quotation
+ \raw HTML
+ <h3>flat : bool</h3>
+ \endraw
+
+ This property holds whether the border is disabled.
+
+ This property's default is false.
+
+ Access functions:
+
+ \list
+ \li \b { bool isFlat () const}
+ \li \b { void setFlat ( bool )}
+ \endlist
+
+ \endquotation
+
+ \code
+ / *!
+ \property QWidget::width
+ \brief The width of the widget excluding any window frame.
+
+ See the \l {Window Geometry} documentation for an
+ overview of window geometry.
+
+ \sa geometry, height, size
+ * /
+ \endcode
+
+ QDoc includes this in \c {qwidget.html} like this:
+
+ \quotation
+ \raw HTML
+ <h3>width : const int</h3>
+ \endraw
+
+ This property holds the width of the widget excluding
+ any window frame.
+
+ See the \l {Window Geometry} documentation for an
+ overview of window geometry.
+
+ Access functions:
+
+ \list
+ \li \b { int width () const}
+ \endlist
+
+ See also \l{QWidget::geometry} {geometry},
+ \l{QWidget::height} {height}, and \l{QWidget::size} {size}.
+ \endquotation
+
+ \target qmlattachedproperty-command
+ \section1 \\qmlattachedproperty
+
+ The \\qmlattachedproperty command is for documenting a QML
+ property that will be attached to some QML type. See
+ \l{http://qt-project.org/doc/qt-4.7/qdeclarativeintroduction.html#attached-properties}
+ {Attached Properties}. The argument is the rest of the line. The
+ argument text should be the property type, followed by the QML
+ element name where the property is being declared, the \c{::}
+ qualifier, and finally the property name. If we have a QML
+ attached property named \c isCurrentItem in QML \c ListView,
+ and the property has type \c {bool}, the \\qmlattachedproperty for
+ it would look like this:
+
+ \code
+ / *!
+ \qmlattachedproperty bool ListView::isCurrentItem
+ This attached property is \c true if this delegate is the current
+ item; otherwise false.
+
+ It is attached to each instance of the delegate.
+
+ This property may be used to adjust the appearance of the current
+ item, for example:
+
+ \snippet doc/src/snippets/declarative/listview/listview.qml isCurrentItem
+ * /
+ \endcode
+
+ QDoc includes this attached property on the QML reference page for the
+ \l{http://qt-project.org/doc/qt-4.7/qml-listview.html#isCurrentItem-prop}
+ {ListView} element.
+
+ \target qmlattachedsignal-command
+ \section1 \\qmlattachedsignal
+
+ The \\qmlattachedsignal command is for documenting an attachable
+ \l{Signal and Handler Event System}{signal}. The \\qmlattachedsignal
+ command is used just like the \l{qmlsignal-command} {\\qmlsignal} command.
+
+ The argument is the rest of the line. It should be the name of the
+ QML type where the signal is declared, the \c{::}
+ qualifier, and finally the signal name. For example, a QML
+ attached signal named \c add() in the \c GridView
+ element is documented like this:
+
+ \code
+ / *!
+ \qmlattachedsignal GridView::add()
+ This attached signal is emitted immediately after an item is added to the view.
+ * /
+ \endcode
+
+ QDoc includes this documentation on the QML reference page for the
+ \l GridView element.
+
+ \target qmlbasictype-command
+ \section1 \\qmlbasictype
+
+ The \\qmlbasictype command is for documenting a basic type for QML.
+ The argument is the type name. The type must be included in the
+ QML basic types group using the \l{ingroup-command}{\\ingroup}
+ command as shown below. This will cause QDoc to include the
+ documentation for the type on the
+ \l{http://qt-project.org/doc/qt-4.7/qdeclarativebasictypes.html}
+ {QML Basic Types} page. The \l{brief-command} {\\brief} command
+ is also required, because it appears on the
+ \l{http://qt-project.org/doc/qt-4.7/qdeclarativebasictypes.html}
+ {QML Basic Types} page as well.
+
+ \code
+ / *!
+ \qmlbasictype int
+ \ingroup qmlbasictypes
+
+ \brief An integer is a whole number, for example 0, 10, or -20.
+
+ An integer is a whole number, e.g. 0, 10, or -20. The possible
+ \c int values range from around -2000000000 to around
+ 2000000000, although most elements will only accept a reduced
+ range (which they mention in their documentation).
+
+ Example:
+ \qml
+ Item { width: 100; height: 200 }
+ \endqml
+
+ \sa {QML Basic Types}
+ * /
+ \endcode
+
+ QDoc outputs this as \l{http://qt-project.org/doc/qt-4.7/qml-int.html}
+ {qml-int.html}.
+
+ \target qmlclass-command
+ \section1 \\qmlclass
+
+ This command is deprecated. Use \l{qmltype-command} {\\qmltype}
+ instead.
+
+ The \\qmlclass command is for documenting a QML type that is
+ instantiated by a C++ class. The command has two arguments. The
+ first argument is the name of the QML type. The second argument
+ is the name of the C++ class that instantiates the QML type.
+
+ \code
+ / *!
+ \qmlclass Transform QGraphicsTransform
+ \ingroup qml-transform-elements
+ \since 4.7
+ \brief Provides a way of building advanced transformations on Items.
+
+ The Transform element is a base type which cannot be
+ instantiated directly. The following concrete Transform types
+ are available:
+
+ \list
+ \li \l Rotation
+ \li \l Scale
+ \li \l Translate
+ \endlist
+
+ The Transform elements let you create and control advanced
+ transformations that can be configured independently using
+ specialized properties.
+
+ You can assign any number of Transform elements to an \l
+ Item. Each Transform is applied in order, one at a time.
+
+ * /
+ \endcode
+
+ This example generates the
+ \l {http://qt-project.org/doc/qt-4.7/qml-transform.html} {QML Transform}
+ page. The \\qmlclass comment should include the \l
+ {since-command} {\\since} command, because all QML types are
+ new. It should also include the \l{brief-command} {\\brief}
+ command. If a type is a member of a group of QML
+ types, it should also include one or more \l{ingroup-command}
+ {\\ingroup} commands.
+
+ \target qmlmethod-command
+ \section1 \\qmlmethod
+
+ The \\qmlmethod command is for documenting a QML method. The
+ argument is the complete method signature, including return
+ type and parameter names and types.
+
+ \code
+ / *!
+ \qmlmethod void TextInput::select(int start, int end)
+
+ Causes the text from \a start to \a end to be selected.
+
+ If either start or end is out of range, the selection is not changed.
+
+ After having called this, selectionStart will become the lesser, and
+ selectionEnd the greater (regardless of the order passed to this method).
+
+ \sa selectionStart, selectionEnd
+ * /
+ \endcode
+
+ QDoc includes this documentation on the element reference page for the
+ \l{http://qt-project.org/doc/qt-4.7/qml-textinput.html#select-method}
+ {TextInput} element.
+
+ \target qmltype-command
+ \section1 \\qmltype
+
+ The \\qmltype command is for documenting a QML type. The command
+ has one argument, which is the name of the QML type.
+
+ If the QML type is instantiated by a C++ class, that class must be
+ specified using the \l{instantiates-command} {\\instantiates}
+ context command.
+
+ \code
+ / *!
+ \qmltype Transform
+ \instantiates QGraphicsTransform
+ \ingroup qml-transform-elements
+ \since 4.7
+ \brief The Transform elements provide a way to build
+ advanced transformations on Items.
+
+ The Transform element is a base type which cannot be
+ instantiated directly. The concrete Transform types are:
+
+ \list
+ \li \l Rotation
+ \li \l Scale
+ \li \l Translate
+ \endlist
+
+ The Transform elements let you create and control advanced
+ transformations that can be configured independently using
+ specialized properties.
+
+ You can assign any number of Transform elements to an \l
+ Item. Each Transform is applied in order, one at a time.
+
+ * /
+ \endcode
+
+ The example generates the \l
+ {http://qt-project.org/doc/qt-4.7/qml-transform.html} {QML Transform}
+ page. The \e{\\qmltype} comment includes \l{instantiates-command}
+ {\\instantiates} to specify that a Transform is instantiated by
+ the C++ class QGraphicsTransform. A \\qmltype comment should
+ always include a \l {since-command} {\\since} command, because all
+ QML types are new. It should also include a \l{brief-command}
+ {\\brief} description. If a QML type is a member of a QML type group,
+ the \\qmltype comment should include one or more \l{ingroup-command}
+ {\\ingroup} commands.
+
+
+ \target qmlproperty-command
+ \section1 \\qmlproperty
+
+ The \\qmlproperty command is for documenting a QML property. The
+ argument is the rest of the line. The argument text should be the
+ property type, followed by the QML type name, the \c{::}
+ qualifier, and finally the property name. If we have a QML
+ property named \c x in QML type \c Translate, and the property
+ has type \c {real}, the \\qmlproperty for it would look like this:
+
+ \code
+ / *!
+ \qmlproperty real Translate::x
+
+ The translation along the X axis.
+ * /
+ \endcode
+
+ QDoc includes this QML property on the QML reference page for the
+ \l {http://qt-project.org/doc/qt-4.7/qml-translate.html} {Translate}
+ element.
+
+ If the QML property is of enumeration type, or it holds a bit-wise
+ combination of flags, the \l{value-command}{\\value} command can
+ be used to document the acceptable values.
+
+ \target qmlsignal-command
+ \section1 \\qmlsignal
+
+ The \\qmlsignal command is for documenting a QML signal.
+ The argument is the rest of the line. The arguments should be: the QML type
+ where the signal is declared, the \c{::} qualifier, and finally the signal
+ name. If we have a QML signal named \c clicked(), the documentation for it
+ would look like this:
+
+ \code
+ / *!
+ \qmlsignal UIComponents::Button::clicked()
+ This signal is emitted when the user clicks the button. A click is defined
+ as a press followed by a release. The corresponding handler is
+ \c onClicked.
+ * /
+ \endcode
+
+ QDoc includes this documentation on the QML reference page for the
+ \l{http://qt-project.org/doc/qt-4.7/qml-mousearea.html#onEntered-signal}
+ {MouseArea} element.
+
+ \target qmlmodule-command
+ \section1 \\qmlmodule
+
+ Insert the \c{\\qmlmodule} command to create a \c QML module page. A QML
+ module is a collection of QML types or any related material. This
+ command is similar to the \l{group-command}.
+
+ A QML class may belong to a module by inserting the
+ \l{inqmlmodule-command}{\\inqmlmodule} command as a topic command.
+ Every member of a group must be linked to using the module name and two
+ colons (\c{::}).
+
+ \code
+ \beginqdoc
+ A link to the TabWidget of the UI Component is \l {UIComponent::TabWidget}.
+ \endqdoc
+ \endcode
+
+ QDoc will generate a page for the module with a listing of the members
+ of the module.
+
+ \code
+ \qmlmodule ClickableComponents
+
+ This is a list of the Clickable Components set. A Clickable component
+ responds to a \c clicked() event.
+ \endcode
+
+ The \l{componentset}{UIComponents} example demonstrates proper usage of
+ QDoc commands to document QML types and QML modules.
+
+ \target inqmlmodule-command
+ \section1 \\inqmlmodule
+
+ A QML class may belong to a \l{qmlmodule-command}{QML module} by inserting
+ the \l{inqmlmodule-command}{\\inqmlmodule} command as a topic command, with
+ the module name (without a version number) as the only argument. Every
+ member of a group must be linked to using the module name and two colons
+ (\c{::}).
+
+ \code
+ \qmltype ClickableButton
+ \inqmlmodule ClickableComponents
+
+ A clickable button that responds to the \c click() event.
+ \endcode
+
+ To link to the \c ClickableButton, use the
+ \c{\l ClickableComponents::ClickableButton} format.
+
+ The \l{componentset}{UIComponents} example demonstrates proper usage of
+ QDoc commands to document QML types and QML modules.
+
+ The \l {noautolist-command} {\\noautolist} command can be used here
+ to omit the automatically generated list of types at the end.
+
+ \target instantiates-command
+ \section1 \\instantiates
+
+ The \\instantiates command is used in the \l{qmltype-command} {QML
+ type} comment of an elemental QML type to specify the name of the
+ C++ class that instantiates the QML type.
+
+ If the QML type is not instantiated by a C++ class, this command
+ is not used.
+
+ \code
+ / *!
+ \qmltype Transform
+ \instantiates QGraphicsTransform
+ \ingroup qml-transform-elements
+ \since 4.7
+ \brief Provides elements provide a way to build
+ advanced transformations on Items.
+
+ The Transform element is a base type which cannot be
+ instantiated directly.
+ * /
+ \endcode
+
+ The example generates the \l
+ {http://qt-project.org/doc/qt-4.7/qml-transform.html} {QML Transform}
+ page. The \e{\\qmltype} comment includes \l{instantiates-command}
+ {\\instantiates} to specify that a Transform is instantiated by
+ the C++ class QGraphicsTransform. A \\qmltype comment should
+
+ \target typedef-command
+ \section1 \\typedef
+
+ The \\typedef command is for documenting a C++ typedef. The
+ argument is the name of the typedef. The documentation for
+ the typedef will be included in the reference documentation
+ for the class, namespace, or header file in which the typedef
+ is declared. To relate the \\typedef to a class, namespace, or
+ header file, the \\typedef comment must contain a
+ \l {relates-command} {\\relates} command.
+
+ \code
+ / *!
+ \typedef QObjectList
+ \relates QObject
+
+ Synonym for QList<QObject>.
+ * /
+ \endcode
+
+ QDoc includes this in \c {qobject.html} as:
+
+ \quotation
+ \raw HTML
+ <h3>typedef QObjectList</h3>
+ \endraw
+
+ Synonym for QList<QObject>.
+ \endquotation
+
+ Another, although more rare, example:
+
+ \code
+ / *!
+ \typedef QMsgHandler
+ \relates QtGlobal
+
+ This is a typedef for a pointer to a function with the
+ following signature:
+
+ \code
+ void myMsgHandler(QtMsgType, const char *);
+ \ endcode
+
+ \sa QtMsgType, qInstallMsgHandler()
+ * /
+ \endcode
+
+ QDoc includes this in \c {qtglobal.html} as:
+
+ \quotation
+ \raw HTML
+ <h3>typedef QtMsgHandler</h3>
+ \endraw
+
+ This is a typedef for a pointer to a function with the
+ following signature:
+
+ \raw HTML
+ <tt>
+ <pre> void myMsgHandler(QtMsgType, const char *);</pre>
+ </tt>
+ \endraw
+
+ See also QtMsgType and qInstallMsgHandler().
+ \endquotation
+
+ Other typedefs are located on the reference page for the class
+ that defines them.
+
+ \code
+ / *!
+ \typedef QLinkedList::Iterator
+
+ Qt-style synonym for QList::iterator.
+ * /
+ \endcode
+
+ QDoc includes this one on the reference page for class QLinkedList as:
+
+ \quotation
+ \raw HTML
+ <h3>typedef QLinkedList::Iterator</h3>
+ \endraw
+
+ Qt-style synonym for QList::iterator.
+ \endquotation
+
+ \target variable-command
+ \section1 \\variable
+
+ The \\variable command is for documenting a class member variable
+ or a constant. The argument is the variable or constant name. The
+ \\variable command comment includes a \l {brief-command} {\\brief}
+ command. QDoc generates the documentation based on the text from
+ \\brief command.
+
+ The documentation will be located in the in the associated class,
+ header file, or namespace documentation.
+
+ In case of a member variable:
+
+ \code
+ / *!
+ \variable QStyleOption::palette
+ \brief The palette that should be used when painting
+ the control
+ * /
+ \endcode
+
+ QDoc includes this in qstyleoption.html as:
+
+ \quotation
+ \raw HTML
+ <h3>
+ <a href="http://doc.qt.io/qt-5/qpalette.html">
+ QPalette
+ </a>
+ QStyleOption::palette
+ </h3>
+ \endraw
+
+ This variable holds the palette that should be used
+ when painting the control.
+ \endquotation
+
+ You can also document constants with the \\variable command. For
+ example, suppose you have the \c Type and \c UserType constants in
+ the QTreeWidgetItem class:
+
+ \code
+ enum { Type = 0, UserType = 1000 };
+ \endcode
+
+ For these, the \\variable command can be used this way:
+
+ \code
+ / *!
+ \variable QTreeWidgetItem::Type
+
+ The default type for tree widget items.
+
+ \sa UserType, type()
+ * /
+ \endcode
+ \code
+ / *!
+ \variable QTreeWidgetItem::UserType
+
+ The minimum value for custom types. Values below
+ UserType are reserved by Qt.
+
+ \sa Type, type()
+ * /
+ \endcode
+
+ QDoc includes these in qtreewidget.html as:
+
+ \quotation
+ \raw HTML
+ <h3>
+ const int QTreeWidgetItem::Type
+ </h3>
+ \endraw
+
+ The default type for tree widget items.
+
+ See also \l {QTreeWidgetItem::UserType} {UserType} and \l
+ {QTreeWidgetItem::type()} {type()}.
+
+ \raw HTML
+ <h3>
+ const int QTreeWidgetItem::UserType
+ </h3>
+ \endraw
+
+ The minimum value for custom types. Values below
+ UserType are reserved by Qt.
+
+ See also \l {QTreeWidgetItem::Type} {Type} and
+ \l{QTreeWidgetItem::type()} {type()}.
+
+ \endquotation
+*/
diff --git a/src/qdoc/doc/qdoc-manual.qdoc b/src/qdoc/doc/qdoc-manual.qdoc
new file mode 100644
index 000000000..a0e35c2e9
--- /dev/null
+++ b/src/qdoc/doc/qdoc-manual.qdoc
@@ -0,0 +1,78 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the documentation of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:FDL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Free Documentation License Usage
+** Alternatively, this file may be used under the terms of the GNU Free
+** Documentation License version 1.3 as published by the Free Software
+** Foundation and appearing in the file included in the packaging of
+** this file. Please review the following information to ensure
+** the GNU Free Documentation License version 1.3 requirements
+** will be met: http://www.gnu.org/copyleft/fdl.html.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*!
+ \page qdoc-index.html
+ \nextpage Introduction to QDoc
+
+ \title QDoc Manual
+
+ \list
+ \li \l {Introduction to QDoc}
+ \li \l {Getting Started with QDoc}
+ \li \l {Command Index}
+ \li \l {Topic Commands}
+ \li \l {Context Commands}
+ \list
+ \li \l {Document Navigation}
+ \li \l {Status}
+ \li \l {Thread Support}
+ \li \l {Relating Things}
+ \li \l {Grouping Things}
+ \li \l {Naming Things}
+ \endlist
+ \li \l{Markup Commands}
+ \list
+ \li \l {Text Markup}
+ \li \l {Document Structure}
+ \li \l {Including Code Inline}
+ \li \l {Including External Code}
+ \li \l {Creating Links}
+ \li \l {Including Images}
+ \li \l {Tables and Lists}
+ \li \l {Special Content}
+ \li \l {Miscellaneous}
+ \endlist
+ \li \l{Creating DITA Maps}
+ \li \l {The QDoc Configuration File}
+ \list
+ \li \l {Generic Configuration Variables}
+ \li \l {Creating Help Project Files}
+ \li \l {C++ Specific Configuration Variables}
+ \li \l {HTML Specific Configuration Variables}
+ \li \l {Supporting Derived Projects}
+ \li \l {Example Manifest Files}
+ \li \l {qtgui.qdocconf}
+ \li \l {minimum.qdocconf}
+ \li \l {Generating DITA XML Output}
+ \endlist
+ \li \l {QA Pages}
+ \endlist
+
+*/
+
+
diff --git a/src/qdoc/doc/qdoc-minimum-qdocconf.qdoc b/src/qdoc/doc/qdoc-minimum-qdocconf.qdoc
new file mode 100644
index 000000000..1fcd23a0f
--- /dev/null
+++ b/src/qdoc/doc/qdoc-minimum-qdocconf.qdoc
@@ -0,0 +1,89 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the documentation of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:FDL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Free Documentation License Usage
+** Alternatively, this file may be used under the terms of the GNU Free
+** Documentation License version 1.3 as published by the Free Software
+** Foundation and appearing in the file included in the packaging of
+** this file. Please review the following information to ensure
+** the GNU Free Documentation License version 1.3 requirements
+** will be met: http://www.gnu.org/copyleft/fdl.html.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+/*!
+\page qdoc-minimum-qdocconf.html
+\keyword minimal-qdocconf
+\title A Minimal qdocconf File
+
+\brief Describes a minimal .qdocconf file
+
+Below you will find the full contents of qtgui.qdocconf. The subsequent section
+will discuss every statement in the qdocconf file.
+
+Each line from the qdocconf file is first quoted. Below each statement you will
+find the meaning.
+
+\badcode
+ include(compat.qdocconf)
+ outputdir = html
+ headerdirs = .
+ sourcedirs = .
+ exampledirs = .
+ imagedirs = ./images
+\endcode
+
+\b Notes:
+
+\badcode
+ include(compat.qdocconf)
+\endcode
+
+For compatibility with older versions of Qt, it is recommended
+to include compat.qdocconf.
+
+\code
+ outputdir = html
+\endcode
+
+QDoc will put the documentation generated in the html directory.
+
+\badcode
+ headerdirs = .
+\endcode
+
+The header file associated with the \e .cpp source files can be found in the
+current directory.
+
+\badcode
+ sourcedirs = .
+\endcode
+
+The current directory is the directory containing the source files: the \e .cpp
+and \e .qdoc files used in the documentation.
+
+\badcode
+ exampledirs = .
+\endcode
+
+The source code of the example files can be found in the current directory.
+
+\badcode
+ imagedirs = ./images
+\endcode
+
+The image files can be found in the underlying directory \c images.
+*/
diff --git a/src/qdoc/doc/qtgui-qdocconf.qdoc b/src/qdoc/doc/qtgui-qdocconf.qdoc
new file mode 100644
index 000000000..d90584ff4
--- /dev/null
+++ b/src/qdoc/doc/qtgui-qdocconf.qdoc
@@ -0,0 +1,299 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the documentation of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:FDL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Free Documentation License Usage
+** Alternatively, this file may be used under the terms of the GNU Free
+** Documentation License version 1.3 as published by the Free Software
+** Foundation and appearing in the file included in the packaging of
+** this file. Please review the following information to ensure
+** the GNU Free Documentation License version 1.3 requirements
+** will be met: http://www.gnu.org/copyleft/fdl.html.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*!
+
+\page qtgui-qdocconf.html
+\title qtgui.qdocconf with Comments
+
+\brief A walkthrough of a typical qdocconf file.
+
+This document goes through a typical Qt 5 qdocconf file. The contents is taken from
+Qt GUI's \e qtgui.qdocconf file.
+
+Below you will find the full contents of \c qtgui.qdocconf. The subsequent section will discuss
+every statement in the qdocconf file.
+
+\badcode
+ include($QT_INSTALL_DOCS/global/qt-module-defaults.qdocconf)
+
+ project = QtGui
+ description = Qt GUI Reference Documentation
+ url = http://doc.qt.io/qt-5
+ version = $QT_VERSION
+
+ examplesinstallpath = gui
+
+ qhp.projects = QtGui
+
+ qhp.QtGui.file = qtgui.qhp
+ qhp.QtGui.namespace = org.qt-project.qtgui.$QT_VERSION_TAG
+ qhp.QtGui.virtualFolder = qtgui
+ qhp.QtGui.indexTitle = Qt GUI
+ qhp.QtGui.indexRoot =
+
+ qhp.QtGui.filterAttributes = qtgui $QT_VERSION qtrefdoc
+ qhp.QtGui.customFilters.Qt.name = Qtgui $QT_VERSION
+ qhp.QtGui.customFilters.Qt.filterAttributes = qtgui $QT_VERSION
+
+ qhp.QtGui.subprojects = classes
+ qhp.QtGui.subprojects.classes.title = C++ Classes
+ qhp.QtGui.subprojects.classes.indexTitle = Qt GUI C++ Classes
+ qhp.QtGui.subprojects.classes.selectors = class fake:headerfile
+ qhp.QtGui.subprojects.classes.sortPages = true
+
+ tagfile = ../../../doc/qtgui/qtgui.tags
+
+ depends += \
+ qtcore \
+ qtnetwork \
+ qtopengl \
+ qtsvg \
+ qtqml \
+ qtquick \
+ qtwidgets \
+ qtdoc
+
+ headerdirs += ..
+
+ sourcedirs += .. \
+ ../../../examples/gui/doc/src
+
+ excludedirs = ../../../examples/gui/doc/src/tmp
+
+ exampledirs += ../../../examples/gui \
+ snippets
+
+ imagedirs += images \
+ ../../../examples/gui/doc/images \
+ ../../../doc/src/images \
+\endcode
+
+\title Qtgui.qdocconf with notes
+
+\badcode
+ include($QT_INSTALL_DOCS/global/qt-module-defaults.qdocconf)
+\endcode
+
+QDoc inherits the default templates, macros, and settings from the directory
+specified from the \c $QT_INSTALL_DOCS variable. \c qmake prints the value of
+the variable.
+\badcode
+ qmake -query
+\endcode
+
+\b {See also}: \l {include}.
+
+\badcode
+ project = QtGui
+\endcode
+
+The \c project variable sets the name of the QDoc build. This name is also
+used to form the index file, which, in this case, will be \e qtgui.index. The
+name of the index file doesn't adopt the uppercase letters of the project name.
+
+\b {See also}: \l {project}.
+
+\badcode
+ description = Qt GUI Reference Documentation
+\endcode
+
+A short description of the project concerned.
+
+\badcode
+ url = http://doc.qt.io/qt-5
+\endcode
+
+The \c url variable holds the base url of the project.
+
+The URL is stored in the generated index file for the project.
+QDoc will use this as the base URL when constructing external links
+to content listed in the index.
+
+\note QDoc omits this value when the -installdir argument
+is specified when running QDoc.
+
+\keyword examplesinstallpath
+
+\badcode
+ examplesinstallpath = gui
+\endcode
+
+This \c examplesinstallpath variable indicates that the examples will be
+installed in the \e gui directory under the parent examples directory
+(for Qt, this is $QT_INSTALL_EXAMPLES).
+
+\note The examplepath variable has to match the example directory specified in
+ \c exampledirs.
+
+\b {See also}: \l {exampledirs}.
+
+\badcode
+ qhp.projects = QtGui
+ qhp.QtGui.file = qtgui.qhp
+\endcode
+
+The following parameters are for creating a QHP file (\e .qhp). The
+\e qhelpgenerator program can convert the QHP file into a QCH file (\e .qch),
+which can be opened in Qt Assistant or Qt Creator.
+
+\badcode
+ qhp.QtGui.namespace = org.qt-project.qtgui.$QT_VERSION_TAG
+\endcode
+
+A unique identifier which enables QHelpEngine to retrieve the helpfile
+from a given link. This namespace is also used as a base url for links
+to the helpfile.
+
+\badcode
+ qhp.QtGui.virtualFolder = qtgui
+\endcode
+
+Virtual folders group documentation together into a single location. A
+virtual folder will become the root directory of all files referenced in
+a compressed help file.
+
+When two manuals are located in the same virtual folder, it is possible to
+refer to sections of the other manual using relative paths. The virtual
+folder tag is mandatory and the folder must not contain any '/'.
+
+\badcode
+ qhp.QtGui.indexTitle = Qt GUI
+\endcode
+
+This is the title of the page that has the contents.
+
+\badcode
+ qhp.QtGui.indexRoot =
+\endcode
+
+Specifies the title of the root (namespace) page to generate the documentation for.
+Typically defined as an empty string.
+
+\badcode
+ qhp.QtGui.filterAttributes = qtgui $QT_VERSION qtrefdoc
+ qhp.QtGui.customFilters.Qt.name = QtGui $QT_VERSION
+ qhp.QtGui.customFilters.Qt.filterAttributes = qtgui $QT_VERSION
+\endcode
+
+The documentation set (one per QDoc project) can have any number of filter
+attributes assigned to it. A filter attribute is an ordinary string which
+can be freely chosen. Additionally, custom filters that reference above
+attributes can be defined. Qt Assistant will display the name of the custom
+filter in its \gui{Filtered by} drop-down list. Only the documentation sets
+that have their filter attributes match the attributes of the selected
+custom filter will be shown.
+
+\badcode
+ qhp.QtGui.subprojects = classes
+ qhp.QtGui.subprojects.classes.title = C++ Classes
+ qhp.QtGui.subprojects.classes.indexTitle = Qt GUI C++ Classes
+\endcode
+The subprojects specify the sections that are displayed in the table of contents
+for this project. In this example, the subproject, which is displayed in
+the Assistant's sidebar, is named "C++ Classes" and its index is the page
+titled "QT GUI C++ Classes".
+
+\badcode
+ qhp.QtGui.subprojects.classes.selectors = class fake:headerfile
+\endcode
+
+Lists all C++ classes and header files.
+
+See \l {Creating Help Project Files} for more information.
+
+\badcode
+ tagfile = ../../../doc/qtgui/qtgui.tags
+\endcode
+
+This specifies the Doxygen tag file that needs to be written when the html is generated
+by QDoc.
+
+\badcode
+depends += \
+ qtcore \
+ qtnetwork \
+ qtopengl \
+ qtsvg \
+ qtqml \
+ qtquick \
+ qtwidgets \
+ qtdoc
+\endcode
+
+Specifies the modules QDoc needs to load for generating output for Qt GUI.
+QDoc loads the index files for all modules listed in the depends statement in
+order to enable linking to pages in these modules.
+
+\badcode
+ headerdirs += ..
+\endcode
+
+Add the parent directory to the list of directories containing the header files
+associated with the \e .cpp source files.
+
+\badcode
+ sourcedirs += .. \
+ ../../../examples/gui/doc/src
+\endcode
+
+Add the specified directories to the list of directories containing the \e .cpp and
+\e .qdoc files used in the documentation.
+
+\badcode
+ excludedirs = ../../../examples/gui/doc/src/tmp
+\endcode
+
+The \c excludedirs variable is for listing directories that should not be processed
+by qdoc, even if the same directories are included by the \c sourcedirs or \c headerdirs
+variables.
+
+When executed, QDoc will ignore the directories listed.
+\b {See also}: \l {excludefiles}.
+
+\badcode
+ exampledirs += ../../../examples/gui \
+ snippets
+\endcode
+\b {See also}: \l {examples-variable}{examples}, \l {examplesinstallpath}.
+
+Add the two directories specified to the list of directories containing the source
+code of the example files.
+
+If QDoc encounters both \c exampledirs and \c examples, it will look first in the
+\c examples directory. QDoc will accept the first matching file it finds. QDoc will
+search in the directories specified, not in their subdirectories.
+
+\badcode
+ imagedirs += images \
+ ../../../examples/gui/doc/images \
+ ../../../doc/src/images \
+\endcode
+
+Add the directories specified above to the list of directories where the images
+can be found.
+*/
diff --git a/src/qdoc/editdistance.cpp b/src/qdoc/editdistance.cpp
new file mode 100644
index 000000000..c3336d2ab
--- /dev/null
+++ b/src/qdoc/editdistance.cpp
@@ -0,0 +1,106 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL21$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** 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 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** As a special exception, The Qt Company gives you certain additional
+** rights. These rights are described in The Qt Company LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** $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 )
+{
+ if (actual.isEmpty())
+ return QString();
+
+ int deltaBest = 10000;
+ int numBest = 0;
+ QString best;
+
+ QSet<QString>::ConstIterator c = candidates.constBegin();
+ while ( c != candidates.constEnd() ) {
+ 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 QString();
+ }
+}
+
+QT_END_NAMESPACE
diff --git a/src/qdoc/editdistance.h b/src/qdoc/editdistance.h
new file mode 100644
index 000000000..2a9b1710f
--- /dev/null
+++ b/src/qdoc/editdistance.h
@@ -0,0 +1,51 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL21$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** 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 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** As a special exception, The Qt Company gives you certain additional
+** rights. These rights are described in The Qt Company LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*
+ editdistance.h
+*/
+
+#ifndef EDITDISTANCE_H
+#define EDITDISTANCE_H
+
+#include <qset.h>
+#include <qstring.h>
+
+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/src/qdoc/generator.cpp b/src/qdoc/generator.cpp
new file mode 100644
index 000000000..3ce5bf99d
--- /dev/null
+++ b/src/qdoc/generator.cpp
@@ -0,0 +1,2171 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL21$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** 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 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** As a special exception, The Qt Company gives you certain additional
+** rights. These rights are described in The Qt Company LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*
+ generator.cpp
+*/
+#include <qdir.h>
+#include <qdebug.h>
+#include "codemarker.h"
+#include "config.h"
+#include "doc.h"
+#include "editdistance.h"
+#include "generator.h"
+#include "openedlist.h"
+#include "quoter.h"
+#include "separator.h"
+#include "tokenizer.h"
+#include "qdocdatabase.h"
+
+QT_BEGIN_NAMESPACE
+
+Generator* Generator::currentGenerator_;
+QStringList Generator::exampleDirs;
+QStringList Generator::exampleImgExts;
+QMap<QString, QMap<QString, QString> > Generator::fmtLeftMaps;
+QMap<QString, QMap<QString, QString> > Generator::fmtRightMaps;
+QList<Generator *> Generator::generators;
+QStringList Generator::imageDirs;
+QStringList Generator::imageFiles;
+QMap<QString, QStringList> Generator::imgFileExts;
+QString Generator::outDir_;
+QString Generator::outSubdir_;
+QStringList Generator::outFileNames_;
+QSet<QString> Generator::outputFormats;
+QHash<QString, QString> Generator::outputPrefixes;
+QHash<QString, QString> Generator::outputSuffixes;
+QString Generator::project_;
+QStringList Generator::scriptDirs;
+QStringList Generator::scriptFiles;
+QString Generator::sinceTitles[] =
+{
+ " New Namespaces",
+ " New Classes",
+ " New Member Functions",
+ " New Functions in Namespaces",
+ " New Global Functions",
+ " New Macros",
+ " New Enum Types",
+ " New Typedefs",
+ " New Properties",
+ " New Variables",
+ " New QML Types",
+ " New QML Properties",
+ " New QML Signals",
+ " New QML Signal Handlers",
+ " New QML Methods",
+ ""
+};
+QStringList Generator::styleDirs;
+QStringList Generator::styleFiles;
+bool Generator::debugging_ = false;
+bool Generator::noLinkErrors_ = false;
+bool Generator::autolinkErrors_ = false;
+bool Generator::redirectDocumentationToDevNull_ = false;
+Generator::QDocPass Generator::qdocPass_ = Generator::Neither;
+bool Generator::qdocSingleExec_ = false;
+bool Generator::qdocWriteQaPages_ = false;
+bool Generator::useOutputSubdirs_ = true;
+QmlTypeNode* Generator::qmlTypeContext_ = 0;
+
+void Generator::startDebugging(const QString& message)
+{
+ debugging_ = true;
+ qDebug() << "START DEBUGGING:" << message;
+}
+
+void Generator::stopDebugging(const QString& message)
+{
+ debugging_ = false;
+ qDebug() << "STOP DEBUGGING:" << message;
+}
+
+/*!
+ Prints \a message as an aid to debugging the release version.
+ */
+void Generator::debug(const QString& message)
+{
+ if (debugging())
+ qDebug() << " DEBUG:" << message;
+}
+
+/*!
+ Constructs the generator base class. Prepends the newly
+ constructed generator to the list of output generators.
+ Sets a pointer to the QDoc database singleton, which is
+ available to the generator subclasses.
+ */
+Generator::Generator()
+ : amp("&amp;"),
+ gt("&gt;"),
+ lt("&lt;"),
+ quot("&quot;"),
+ tag("</?@[^>]*>"),
+ inLink_(false),
+ inContents_(false),
+ inSectionHeading_(false),
+ inTableHeader_(false),
+ threeColumnEnumValueTable_(true),
+ showInternal_(false),
+ singleExec_(false),
+ numTableRows_(0)
+{
+ qdb_ = QDocDatabase::qdocDB();
+ generators.prepend(this);
+}
+
+/*!
+ Destroys the generator after removing it from the list of
+ output generators.
+ */
+Generator::~Generator()
+{
+ generators.removeAll(this);
+}
+
+void Generator::appendFullName(Text& text,
+ const Node *apparentNode,
+ const Node *relative,
+ const Node *actualNode)
+{
+ if (actualNode == 0)
+ actualNode = apparentNode;
+ text << Atom(Atom::LinkNode, CodeMarker::stringForNode(actualNode))
+ << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK)
+ << Atom(Atom::String, apparentNode->plainFullName(relative))
+ << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK);
+}
+
+void Generator::appendFullName(Text& text,
+ const Node *apparentNode,
+ const QString& fullName,
+ const Node *actualNode)
+{
+ if (actualNode == 0)
+ actualNode = apparentNode;
+ text << Atom(Atom::LinkNode, CodeMarker::stringForNode(actualNode))
+ << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK)
+ << Atom(Atom::String, fullName)
+ << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK);
+}
+
+void Generator::appendFullNames(Text& text, const NodeList& nodes, const Node* relative)
+{
+ NodeList::ConstIterator n = nodes.constBegin();
+ int index = 0;
+ while (n != nodes.constEnd()) {
+ appendFullName(text,*n,relative);
+ text << comma(index++,nodes.count());
+ ++n;
+ }
+}
+
+void Generator::appendSortedNames(Text& text, const ClassNode* cn, const QList<RelatedClass>& rc)
+{
+ QList<RelatedClass>::ConstIterator r;
+ QMap<QString,Text> classMap;
+ int index = 0;
+
+ r = rc.constBegin();
+ while (r != rc.constEnd()) {
+ ClassNode* rcn = (*r).node_;
+ if (rcn && rcn->access() == Node::Public &&
+ rcn->status() != Node::Internal &&
+ !rcn->doc().isEmpty()) {
+ Text className;
+ appendFullName(className, rcn, cn);
+ classMap[className.toString().toLower()] = className;
+ }
+ ++r;
+ }
+
+ QStringList classNames = classMap.keys();
+ classNames.sort();
+
+ foreach (const QString &className, classNames) {
+ text << classMap[className];
+ text << comma(index++, classNames.count());
+ }
+}
+
+void Generator::appendSortedQmlNames(Text& text, const Node* base, const NodeList& subs)
+{
+ QMap<QString,Text> classMap;
+ int index = 0;
+
+ for (int i = 0; i < subs.size(); ++i) {
+ Text t;
+ if (!base->isQtQuickNode() || !subs[i]->isQtQuickNode() ||
+ (base->logicalModuleName() == subs[i]->logicalModuleName())) {
+ appendFullName(t, subs[i], base);
+ classMap[t.toString().toLower()] = t;
+ }
+ }
+
+ QStringList names = classMap.keys();
+ names.sort();
+
+ foreach (const QString &name, names) {
+ text << classMap[name];
+ text << comma(index++, names.count());
+ }
+}
+
+/*!
+ For debugging qdoc.
+ */
+void Generator::writeOutFileNames()
+{
+ QFile files("outputlist.txt");
+ if (!files.open(QFile::WriteOnly))
+ return;
+ QTextStream filesout(&files);
+ foreach (const QString &file, outFileNames_) {
+ filesout << file << "\n";
+ }
+}
+
+/*!
+ Creates the file named \a fileName in the output directory.
+ Attaches a QTextStream to the created file, which is written
+ to all over the place using out().
+ */
+void Generator::beginSubPage(const Aggregate* node, const QString& fileName)
+{
+ QString path = outputDir() + QLatin1Char('/');
+ if (Generator::useOutputSubdirs() && !node->outputSubdirectory().isEmpty() &&
+ !outputDir().endsWith(node->outputSubdirectory()))
+ path += node->outputSubdirectory() + QLatin1Char('/');
+ path += fileName;
+
+ QFile* outFile = new QFile(redirectDocumentationToDevNull_ ? QStringLiteral("/dev/null") : path);
+ if (!redirectDocumentationToDevNull_ && outFile->exists())
+ node->location().error(tr("HTML file already exists; overwriting %1").arg(outFile->fileName()));
+ if (!outFile->open(QFile::WriteOnly))
+ node->location().fatal(tr("Cannot open output file '%1'").arg(outFile->fileName()));
+ Generator::debug("Writing: " + path);
+ outFileNames_ << fileName;
+ QTextStream* out = new QTextStream(outFile);
+
+#ifndef QT_NO_TEXTCODEC
+ if (outputCodec)
+ out->setCodec(outputCodec);
+#endif
+ outStreamStack.push(out);
+ const_cast<Aggregate*>(node)->setOutputFileName(fileName);
+}
+
+/*!
+ Flush the text stream associated with the subpage, and
+ then pop it off the text stream stack and delete it.
+ This terminates output of the subpage.
+ */
+void Generator::endSubPage()
+{
+ outStreamStack.top()->flush();
+ delete outStreamStack.top()->device();
+ delete outStreamStack.pop();
+}
+
+QString Generator::fileBase(const Node *node) const
+{
+ if (node->relates())
+ node = node->relates();
+ else if (!node->isAggregate())
+ node = node->parent();
+ if (node->type() == Node::QmlPropertyGroup) {
+ node = node->parent();
+ }
+
+ if (node->hasFileNameBase())
+ return node->fileNameBase();
+
+ QString base;
+ if (node->isDocumentNode()) {
+ base = node->name();
+ if (base.endsWith(".html") && !node->isExampleFile())
+ base.truncate(base.length() - 5);
+
+ if (node->isExample() || node->isExampleFile()) {
+ QString modPrefix(node->physicalModuleName());
+ if (modPrefix.isEmpty()) {
+ modPrefix = project_;
+ }
+ base.prepend(modPrefix.toLower() + QLatin1Char('-'));
+ }
+ if (node->isExample()) {
+ base.append(QLatin1String("-example"));
+ }
+ }
+ else if (node->isQmlType() || node->isQmlBasicType() ||
+ node->isJsType() || node->isJsBasicType()) {
+ base = node->name();
+ /*
+ To avoid file name conflicts in the html directory,
+ we prepend a prefix (by default, "qml-") and an optional suffix
+ to the file name. The suffix, if one exists, is appended to the
+ module name.
+ */
+ if (!node->logicalModuleName().isEmpty()) {
+ base.prepend(node->logicalModuleName()
+ + outputSuffix(node)
+ + QLatin1Char('-'));
+ }
+ base.prepend(outputPrefix(node));
+ }
+ else if (node->isCollectionNode()) {
+ base = node->name() + outputSuffix(node);
+ if (base.endsWith(".html"))
+ base.truncate(base.length() - 5);
+
+ if (node->isQmlModule()) {
+ base.append("-qmlmodule");
+ }
+ else if (node->isJsModule()) {
+ base.append("-jsmodule");
+ }
+ else if (node->isModule()) {
+ base.append("-module");
+ }
+ // Why not add "-group" for group pages?
+ }
+ else {
+ const Node *p = node;
+ forever {
+ const Node *pp = p->parent();
+ base.prepend(p->name());
+ if (!pp || pp->name().isEmpty() || pp->isDocumentNode())
+ break;
+ base.prepend(QLatin1Char('-'));
+ p = pp;
+ }
+ }
+
+ // the code below is effectively equivalent to:
+ // base.replace(QRegExp("[^A-Za-z0-9]+"), " ");
+ // base = base.trimmed();
+ // base.replace(QLatin1Char(' '), QLatin1Char('-'));
+ // 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);
+ Node* n = const_cast<Node*>(node);
+ n->setFileNameBase(res);
+ return res;
+}
+
+/*!
+ If the \a node has a URL, return the URL as the file name.
+ Otherwise, construct the file name from the fileBase() and
+ the fileExtension(), and return the constructed name.
+ */
+QString Generator::fileName(const Node* node) const
+{
+ if (!node->url().isEmpty())
+ return node->url();
+
+ QString name = fileBase(node);
+ name += QLatin1Char('.');
+ name += fileExtension();
+ return name;
+}
+
+QString Generator::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 += QLatin1Char('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 += QLatin1Char('-');
+ } 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 += QLatin1Char('#');
+ } else {
+ clean += QLatin1Char('-');
+ clean += QString::number((int)u, 16);
+ }
+ }
+ return clean;
+}
+
+QMap<QString, QString>& Generator::formattingLeftMap()
+{
+ return fmtLeftMaps[format()];
+}
+
+QMap<QString, QString>& Generator::formattingRightMap()
+{
+ return fmtRightMaps[format()];
+}
+
+/*!
+ Returns the full document location.
+ */
+QString Generator::fullDocumentLocation(const Node *node, bool useSubdir)
+{
+ if (!node)
+ return QString();
+ if (!node->url().isEmpty())
+ return node->url();
+
+ QString parentName;
+ QString anchorRef;
+ QString fdl;
+
+ /*
+ If the useSubdir parameter is set, then the output is
+ being sent to subdirectories of the output directory.
+ Prepend the subdirectory name + '/' to the result.
+ */
+ if (useSubdir) {
+ fdl = node->outputSubdirectory();
+ if (!fdl.isEmpty())
+ fdl.append(QLatin1Char('/'));
+ }
+ if (node->isNamespace()) {
+
+ // The root namespace has no name - check for this before creating
+ // an attribute containing the location of any documentation.
+
+ if (!fileBase(node).isEmpty())
+ parentName = fileBase(node) + QLatin1Char('.') + currentGenerator()->fileExtension();
+ else
+ return QString();
+ }
+ else if (node->isQmlType() || node->isQmlBasicType() ||
+ node->isJsType() || node->isJsBasicType()) {
+ QString fb = fileBase(node);
+ if (fb.startsWith(outputPrefix(node)))
+ return fb + QLatin1Char('.') + currentGenerator()->fileExtension();
+ else {
+ QString mq;
+ if (!node->logicalModuleName().isEmpty()) {
+ mq = node->logicalModuleName().replace(QChar('.'),QChar('-'));
+ mq = mq.toLower() + QLatin1Char('-');
+ }
+ return fdl + outputPrefix(node) + mq + fileBase(node) +
+ QLatin1Char('.') + currentGenerator()->fileExtension();
+ }
+ }
+ else if (node->isDocumentNode() || node->isCollectionNode()) {
+ parentName = fileBase(node) + QLatin1Char('.') + currentGenerator()->fileExtension();
+ }
+ else if (fileBase(node).isEmpty())
+ return QString();
+
+ Node *parentNode = 0;
+
+ if ((parentNode = node->relates())) {
+ parentName = fullDocumentLocation(node->relates());
+ }
+ else if ((parentNode = node->parent())) {
+ if (parentNode->isQmlPropertyGroup() || parentNode->isJsPropertyGroup()) {
+ parentNode = parentNode->parent();
+ parentName = fullDocumentLocation(parentNode);
+ }
+ else {
+ parentName = fullDocumentLocation(node->parent());
+ }
+ }
+
+ switch (node->type()) {
+ case Node::Class:
+ case Node::Namespace:
+ parentName = fileBase(node) + QLatin1Char('.') + currentGenerator()->fileExtension();
+ break;
+ case Node::Function:
+ {
+ const FunctionNode *fn = static_cast<const FunctionNode *>(node);
+
+ if (fn->metaness() == FunctionNode::Dtor)
+ anchorRef = "#dtor." + fn->name().mid(1);
+
+ else if (fn->hasOneAssociatedProperty() && fn->doc().isEmpty())
+ return fullDocumentLocation(fn->firstAssociatedProperty());
+
+ else if (fn->overloadNumber() > 0)
+ anchorRef = QLatin1Char('#') + cleanRef(fn->name())
+ + QLatin1Char('-') + QString::number(fn->overloadNumber());
+ else
+ anchorRef = QLatin1Char('#') + cleanRef(fn->name());
+ break;
+ }
+ /*
+ Use node->name() instead of fileBase(node) as
+ the latter returns the name in lower-case. For
+ HTML anchors, we need to preserve the case.
+ */
+ case Node::Enum:
+ anchorRef = QLatin1Char('#') + node->name() + "-enum";
+ break;
+ case Node::Typedef:
+ {
+ const TypedefNode *tdef = static_cast<const TypedefNode *>(node);
+ if (tdef->associatedEnum()) {
+ return fullDocumentLocation(tdef->associatedEnum());
+ }
+ anchorRef = QLatin1Char('#') + node->name() + "-typedef";
+ break;
+ }
+ case Node::Property:
+ anchorRef = QLatin1Char('#') + node->name() + "-prop";
+ break;
+ case Node::QmlProperty:
+ if (node->isAttached())
+ anchorRef = QLatin1Char('#') + node->name() + "-attached-prop";
+ else
+ anchorRef = QLatin1Char('#') + node->name() + "-prop";
+ break;
+ case Node::QmlSignal:
+ anchorRef = QLatin1Char('#') + node->name() + "-signal";
+ break;
+ case Node::QmlSignalHandler:
+ anchorRef = QLatin1Char('#') + node->name() + "-signal-handler";
+ break;
+ case Node::QmlMethod:
+ anchorRef = QLatin1Char('#') + node->name() + "-method";
+ break;
+ case Node::Variable:
+ anchorRef = QLatin1Char('#') + node->name() + "-var";
+ break;
+ case Node::QmlType:
+ case Node::Document:
+ case Node::Group:
+ case Node::Module:
+ case Node::QmlModule:
+ {
+ parentName = fileBase(node);
+ parentName.replace(QLatin1Char('/'), QLatin1Char('-')).replace(QLatin1Char('.'), QLatin1Char('-'));
+ parentName += QLatin1Char('.') + currentGenerator()->fileExtension();
+ }
+ break;
+ default:
+ break;
+ }
+
+ // Various objects can be compat (deprecated) or obsolete.
+ // Is this even correct?
+ if (!node->isClass() && !node->isNamespace()) {
+ switch (node->status()) {
+ case Node::Compat:
+ parentName.replace(QLatin1Char('.') + currentGenerator()->fileExtension(),
+ "-compat." + currentGenerator()->fileExtension());
+ break;
+ case Node::Obsolete:
+ parentName.replace(QLatin1Char('.') + currentGenerator()->fileExtension(),
+ "-obsolete." + currentGenerator()->fileExtension());
+ break;
+ default:
+ ;
+ }
+ }
+
+ return fdl + parentName.toLower() + anchorRef;
+}
+
+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
+ << Atom(Atom::FormattingLeft,ATOM_FORMATTING_BOLD)
+ << "See also "
+ << Atom(Atom::FormattingRight,ATOM_FORMATTING_BOLD);
+
+ for (int i = 0; i < alsoList.size(); ++i)
+ text << alsoList.at(i) << separator(i, alsoList.size());
+
+ text << Atom::ParaRight;
+ generateText(text, node, marker);
+ }
+}
+
+int Generator::generateAtom(const Atom * /* atom */,
+ const Node * /* relative */,
+ CodeMarker * /* marker */)
+{
+ return 0;
+}
+
+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 %2")
+ .arg(format()).arg(outFileName()));
+ 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;
+}
+
+/*!
+ Generate the body of the documentation from the qdoc comment
+ found with the entity represented by the \a node.
+ */
+void Generator::generateBody(const Node *node, CodeMarker *marker)
+{
+ bool quiet = false;
+
+ if (node->type() == Node::Document) {
+ const DocumentNode *dn = static_cast<const DocumentNode *>(node);
+ if ((dn->docSubtype() == Node::File) || (dn->docSubtype() == Node::Image)) {
+ quiet = true;
+ }
+ }
+ if (node->doc().isEmpty()) {
+ if (!node->isWrapper() && !quiet && !node->isReimplemented()) { // ### might be unnecessary
+ node->location().warning(tr("No documentation for '%1'").arg(node->plainFullName()));
+ }
+ }
+ else {
+ if (node->type() == Node::Function) {
+ const FunctionNode *func = static_cast<const FunctionNode *>(node);
+ if (func->reimplementedFrom() != 0)
+ generateReimplementedFrom(func, marker);
+ }
+
+ if (!generateText(node->doc().body(), node, marker)) {
+ if (node->isReimplemented())
+ return;
+ }
+
+ if (node->type() == Node::Enum) {
+ const EnumNode *enume = (const EnumNode *) node;
+
+ QSet<QString> definedItems;
+ QList<EnumItem>::ConstIterator it = enume->items().constBegin();
+ while (it != enume->items().constEnd()) {
+ 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.constBegin();
+ while (a != allItems.constEnd()) {
+ 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(node->plainFullName()), details);
+ if (*a == "Void")
+ qDebug() << "VOID:" << node->name() << definedItems;
+ }
+ else if (!documentedItems.contains(*a)) {
+ node->doc().location().warning(tr("Undocumented enum item '%1' in %2")
+ .arg(*a).arg(node->plainFullName()));
+ }
+ ++a;
+ }
+ }
+ }
+ else if (node->type() == Node::Function) {
+ const FunctionNode *func = static_cast<const FunctionNode *>(node);
+ QSet<QString> definedParams;
+ QVector<Parameter>::ConstIterator p = func->parameters().constBegin();
+ while (p != func->parameters().constEnd()) {
+ if ((*p).name().isEmpty() && (*p).dataType() != QLatin1String("...")
+ && (*p).dataType() != QLatin1String("void")
+ && 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.constBegin();
+ while (a != allParams.constEnd()) {
+ 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(node->plainFullName()),
+ details);
+ }
+ else if (!(*a).isEmpty() && !documentedParams.contains(*a)) {
+ bool needWarning = (func->status() > Node::Obsolete);
+ if (func->overloadNumber() > 0) {
+ FunctionNode *primaryFunc = func->parent()->findFunctionNode(func->name(), QString());
+ if (primaryFunc) {
+ foreach (const Parameter &param,
+ primaryFunc->parameters()) {
+ if (param.name() == *a) {
+ needWarning = false;
+ break;
+ }
+ }
+ }
+ }
+ if (needWarning && !func->isReimplemented())
+ node->doc().location().warning(
+ tr("Undocumented parameter '%1' in %2")
+ .arg(*a).arg(node->plainFullName()));
+ }
+ ++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 (node->isDocumentNode()) {
+ const DocumentNode *dn = static_cast<const DocumentNode *>(node);
+ if (dn->isExample()) {
+ generateExampleFiles(dn, marker);
+ }
+ else if (dn->docSubtype() == Node::File) {
+ Text text;
+ Quoter quoter;
+ Doc::quoteFromFile(dn->doc().location(), quoter, dn->name());
+ QString code = quoter.quoteTo(dn->location(), QString(), QString());
+ CodeMarker *codeMarker = CodeMarker::markerForFileName(dn->name());
+ text << Atom(codeMarker->atomType(), code);
+ generateText(text, dn, codeMarker);
+ }
+ }
+}
+
+void Generator::generateClassLikeNode(Aggregate* /* classe */, CodeMarker* /* marker */)
+{
+}
+
+void Generator::generateExampleFiles(const DocumentNode *dn, CodeMarker *marker)
+{
+ if (dn->childNodes().isEmpty())
+ return;
+ generateFileList(dn, marker, Node::File, QString("Files:"));
+ generateFileList(dn, marker, Node::Image, QString("Images:"));
+}
+
+void Generator::generateDocumentNode(DocumentNode* /* dn */, CodeMarker* /* marker */)
+{
+}
+
+void Generator::generateCollectionNode(CollectionNode* , CodeMarker* )
+{
+}
+
+/*!
+ This function is called when the documentation for an
+ example is being formatted. It outputs the list of source
+ files comprising the example, and the list of images used
+ by the example. The images are copied into a subtree of
+ \c{...doc/html/images/used-in-examples/...}
+ */
+void Generator::generateFileList(const DocumentNode* dn,
+ CodeMarker* marker,
+ Node::DocSubtype subtype,
+ const QString& tag)
+{
+ int count = 0;
+ Text text;
+ OpenedList openedList(OpenedList::Bullet);
+
+ text << Atom::ParaLeft << tag << Atom::ParaRight
+ << Atom(Atom::ListLeft, openedList.styleString());
+
+ foreach (const Node* child, dn->childNodes()) {
+ if (child->docSubtype() == subtype) {
+ ++count;
+ QString file = child->name();
+ if (subtype == Node::Image) {
+ if (!file.isEmpty()) {
+ QDir dirInfo;
+ QString userFriendlyFilePath;
+ const QString prefix("/images/used-in-examples/");
+ QString srcPath = Config::findFile(dn->location(),
+ QStringList(),
+ exampleDirs,
+ file,
+ exampleImgExts,
+ userFriendlyFilePath);
+ outFileNames_ << prefix.mid(1) + userFriendlyFilePath;
+ userFriendlyFilePath.truncate(userFriendlyFilePath.lastIndexOf('/'));
+ QString imgOutDir = outDir_ + prefix + userFriendlyFilePath;
+ if (!dirInfo.mkpath(imgOutDir))
+ dn->location().fatal(tr("Cannot create output directory '%1'").arg(imgOutDir));
+ Config::copyFile(dn->location(), srcPath, file, imgOutDir);
+ }
+
+ }
+
+ openedList.next();
+ text << Atom(Atom::ListItemNumber, openedList.numberString())
+ << Atom(Atom::ListItemLeft, openedList.styleString())
+ << Atom::ParaLeft
+ << Atom(Atom::Link, file)
+ << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK)
+ << file
+ << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK)
+ << Atom::ParaRight
+ << Atom(Atom::ListItemRight, openedList.styleString());
+ }
+ }
+ text << Atom(Atom::ListRight, openedList.styleString());
+ if (count > 0)
+ generateText(text, dn, marker);
+}
+
+void Generator::generateInheritedBy(const ClassNode *classe, CodeMarker *marker)
+{
+ if (!classe->derivedClasses().isEmpty()) {
+ Text text;
+ text << Atom::ParaLeft
+ << Atom(Atom::FormattingLeft,ATOM_FORMATTING_BOLD)
+ << "Inherited by: "
+ << Atom(Atom::FormattingRight,ATOM_FORMATTING_BOLD);
+
+ appendSortedNames(text, classe, classe->derivedClasses());
+ text << Atom::ParaRight;
+ generateText(text, classe, marker);
+ }
+}
+
+void Generator::generateInherits(const ClassNode *classe, CodeMarker *marker)
+{
+ QList<RelatedClass>::ConstIterator r;
+ int index;
+
+ if (!classe->baseClasses().isEmpty()) {
+ Text text;
+ text << Atom::ParaLeft
+ << Atom(Atom::FormattingLeft,ATOM_FORMATTING_BOLD)
+ << "Inherits: "
+ << Atom(Atom::FormattingRight,ATOM_FORMATTING_BOLD);
+
+ r = classe->baseClasses().constBegin();
+ index = 0;
+ while (r != classe->baseClasses().constEnd()) {
+ if ((*r).node_) {
+ text << Atom(Atom::LinkNode, CodeMarker::stringForNode((*r).node_))
+ << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK)
+ << Atom(Atom::String, (*r).signature_)
+ << 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);
+ }
+}
+
+/*!
+ Recursive writing of HTML files from the root \a node.
+ */
+void Generator::generateAggregate(Aggregate* node)
+{
+ if (!node->url().isNull())
+ return;
+ if (node->isIndexNode())
+ return;
+ if (node->isInternal() && !showInternal_)
+ return;
+
+ if (node->isDocumentNode()) {
+ DocumentNode* docNode = static_cast<DocumentNode*>(node);
+ if (docNode->docSubtype() == Node::ExternalPage)
+ return;
+ if (docNode->docSubtype() == Node::Image)
+ return;
+ if (docNode->docSubtype() == Node::Page) {
+ if (node->count() > 0)
+ qDebug("PAGE %s HAS CHILDREN", qPrintable(docNode->title()));
+ }
+ }
+ else if (node->isQmlPropertyGroup() || node->isJsPropertyGroup())
+ return;
+
+ /*
+ Obtain a code marker for the source file.
+ */
+ CodeMarker *marker = CodeMarker::markerForFileName(node->location().filePath());
+
+ if (node->parent() != 0) {
+ if ((node->isNamespace() && node->status() != Node::Intermediate)
+ || node->isClass()) {
+ beginSubPage(node, fileName(node));
+ generateClassLikeNode(node, marker);
+ endSubPage();
+ }
+ if (node->isQmlType() || node->isJsType()) {
+ beginSubPage(node, fileName(node));
+ QmlTypeNode* qcn = static_cast<QmlTypeNode*>(node);
+ generateQmlTypePage(qcn, marker);
+ endSubPage();
+ }
+ else if (node->isDocumentNode()) {
+ beginSubPage(node, fileName(node));
+ generateDocumentNode(static_cast<DocumentNode*>(node), marker);
+ endSubPage();
+ }
+ else if (node->isQmlBasicType() || node->isJsBasicType()) {
+ beginSubPage(node, fileName(node));
+ QmlBasicTypeNode* qbtn = static_cast<QmlBasicTypeNode*>(node);
+ generateQmlBasicTypePage(qbtn, marker);
+ endSubPage();
+ }
+ else if (node->isCollectionNode()) {
+ /*
+ A collection node collects: groups, C++ modules,
+ QML modules or JavaScript modules.
+
+ Don't output an HTML page for the collection
+ node unless the \group, \module, \qmlmodule or
+ \jsmodule command was actually seen by qdoc in
+ the qdoc comment for the node.
+
+ A key prerequisite in this case is the call to
+ mergeCollections(cn). We must determine whether
+ this group, module, QML module, or JavaScript
+ module has members in other modules. We know at
+ this point that cn's members list contains only
+ members in the current module. Therefore, before
+ outputting the page for cn, we must search for
+ members of cn in the other modules and add them
+ to the members list.
+ */
+ CollectionNode* cn = static_cast<CollectionNode*>(node);
+ if (cn->wasSeen()) {
+ qdb_->mergeCollections(cn);
+ beginSubPage(node, fileName(node));
+ generateCollectionNode(cn, marker);
+ endSubPage();
+ }
+ }
+ }
+
+ int i = 0;
+ while (i < node->childNodes().count()) {
+ Node *c = node->childNodes().at(i);
+ if (c->isAggregate() && c->access() != Node::Private) {
+ generateAggregate((Aggregate*)c);
+ }
+ ++i;
+ }
+}
+
+/*!
+ Generate a list of maintainers in the output
+ */
+void Generator::generateMaintainerList(const Aggregate* node, CodeMarker* marker)
+{
+ QStringList sl = getMetadataElements(node,"maintainer");
+
+ if (!sl.isEmpty()) {
+ Text text;
+ text << Atom::ParaLeft
+ << Atom(Atom::FormattingLeft,ATOM_FORMATTING_BOLD)
+ << "Maintained by: "
+ << Atom(Atom::FormattingRight,ATOM_FORMATTING_BOLD);
+
+ for (int i = 0; i < sl.size(); ++i)
+ text << sl.at(i) << separator(i, sl.size());
+
+ text << Atom::ParaRight;
+ generateText(text, node, marker);
+ }
+}
+
+/*!
+ Output the "Inherit by" list for the QML element,
+ if it is inherited by any other elements.
+ */
+void Generator::generateQmlInheritedBy(const QmlTypeNode* qcn,
+ CodeMarker* marker)
+{
+ if (qcn) {
+ NodeList subs;
+ QmlTypeNode::subclasses(qcn->name(),subs);
+ if (!subs.isEmpty()) {
+ Text text;
+ text << Atom::ParaLeft << "Inherited by ";
+ appendSortedQmlNames(text,qcn,subs);
+ text << Atom::ParaRight;
+ generateText(text, qcn, marker);
+ }
+ }
+}
+
+/*!
+ */
+void Generator::generateQmlInherits(QmlTypeNode* , CodeMarker* )
+{
+ // stub.
+}
+
+/*!
+ Extract sections of markup text surrounded by \e qmltext
+ and \e endqmltext and output them.
+ */
+bool Generator::generateQmlText(const Text& text,
+ const Node *relative,
+ CodeMarker *marker,
+ const QString& /* qmlName */ )
+{
+ const Atom* atom = text.firstAtom();
+ bool result = false;
+
+ if (atom != 0) {
+ initializeTextOutput();
+ 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();
+ }
+ }
+ }
+ result = true;
+ }
+ return result;
+}
+
+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 ";
+ QString fullName = from->parent()->name() + "::" + from->name() + "()";
+ appendFullName(text, from->parent(), fullName, from);
+ text << "." << Atom::ParaRight;
+ generateText(text, func, marker);
+ }
+ }
+}
+
+void Generator::generateSince(const Node *node, CodeMarker *marker)
+{
+ if (!node->since().isEmpty()) {
+ Text text;
+ text << Atom::ParaLeft
+ << "This "
+ << typeString(node);
+ if (node->type() == Node::Enum)
+ text << " was introduced or modified in ";
+ else
+ text << " was introduced in ";
+
+ QStringList since = node->since().split(QLatin1Char(' '));
+ if (since.count() == 1) {
+ // If there is only one argument, assume it is the Qt version number.
+ text << " Qt " << since[0];
+ } else {
+ // Otherwise, reconstruct the <project> <version> string.
+ text << " " << since.join(' ');
+ }
+
+ text << "." << Atom::ParaRight;
+ generateText(text, node, marker);
+ }
+}
+
+void Generator::generateStatus(const Node *node, CodeMarker *marker)
+{
+ Text text;
+
+ switch (node->status()) {
+ case Node::Active:
+ // Do nothing.
+ 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->isAggregate())
+ text << Atom(Atom::FormattingLeft, ATOM_FORMATTING_BOLD);
+ text << "This " << typeString(node) << " is deprecated.";
+ if (node->isAggregate())
+ text << Atom(Atom::FormattingRight, ATOM_FORMATTING_BOLD);
+ text << Atom::ParaRight;
+ break;
+ case Node::Obsolete:
+ text << Atom::ParaLeft;
+ if (node->isAggregate())
+ text << Atom(Atom::FormattingLeft, ATOM_FORMATTING_BOLD);
+ text << "This " << typeString(node) << " is obsolete.";
+ if (node->isAggregate())
+ 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->isAggregate()) {
+ text << Atom::ParaLeft
+ << Atom(Atom::FormattingLeft, ATOM_FORMATTING_BOLD)
+ << "This "
+ << typeString(node)
+ << " is part of the Qt 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."
+ << Atom::ParaRight;
+ }
+ break;
+ case Node::Internal:
+ default:
+ break;
+ }
+ generateText(text, node, marker);
+}
+
+/*!
+ Generates a bold line that says:
+ "The signal is private, not emitted by the user.
+ The function is public so the user can pass it to connect()."
+ */
+void Generator::generatePrivateSignalNote(const Node* node, CodeMarker* marker)
+{
+ Text text;
+ text << Atom::ParaLeft
+ << Atom(Atom::FormattingLeft, ATOM_FORMATTING_BOLD)
+ << "Note: "
+ << Atom(Atom::FormattingRight, ATOM_FORMATTING_BOLD)
+ << "This is a private signal. It can be used in signal connections but cannot be emitted by the user."
+ << Atom::ParaRight;
+ generateText(text, node, marker);
+}
+
+/*!
+ Generate the documentation for \a relative. i.e. \a relative
+ is the node that reporesentas the entity where a qdoc comment
+ was found, and \a text represents the qdoc comment.
+ */
+bool Generator::generateText(const Text& text,
+ const Node *relative,
+ CodeMarker *marker)
+{
+ bool result = false;
+ if (text.firstAtom() != 0) {
+ int numAtoms = 0;
+ initializeTextOutput();
+ generateAtomList(text.firstAtom(),
+ relative,
+ marker,
+ true,
+ numAtoms);
+ result = true;
+ }
+ return result;
+}
+
+void Generator::generateThreadSafeness(const Node *node, CodeMarker *marker)
+{
+ Text text;
+ Node::ThreadSafeness threadSafeness = node->threadSafeness();
+
+ Text rlink;
+ rlink << Atom(Atom::Link,"reentrant")
+ << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK)
+ << "reentrant"
+ << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK);
+
+ Text tlink;
+ tlink << Atom(Atom::Link,"thread-safe")
+ << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK)
+ << "thread-safe"
+ << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK);
+
+ switch (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 "
+ << rlink
+ << "."
+ << Atom::ParaRight;
+ break;
+ case Node::Reentrant:
+ case Node::ThreadSafe:
+ text << Atom::ParaLeft
+ << Atom(Atom::FormattingLeft,ATOM_FORMATTING_BOLD)
+ << "Note:"
+ << Atom(Atom::FormattingRight,ATOM_FORMATTING_BOLD)
+ << " ";
+
+ if (node->isAggregate()) {
+ const Aggregate* innerNode = static_cast<const Aggregate*>(node);
+ text << "All functions in this "
+ << typeString(node)
+ << " are ";
+ if (threadSafeness == Node::ThreadSafe)
+ text << tlink;
+ else
+ text << rlink;
+
+ bool exceptions = false;
+ NodeList reentrant;
+ NodeList threadsafe;
+ NodeList nonreentrant;
+ NodeList::ConstIterator c = innerNode->childNodes().constBegin();
+ while (c != innerNode->childNodes().constEnd()) {
+
+ if ((*c)->status() != Node::Obsolete){
+ switch ((*c)->threadSafeness()) {
+ case Node::Reentrant:
+ reentrant.append(*c);
+ if (threadSafeness == Node::ThreadSafe)
+ exceptions = true;
+ break;
+ case Node::ThreadSafe:
+ threadsafe.append(*c);
+ if (threadSafeness == Node::Reentrant)
+ exceptions = true;
+ break;
+ case Node::NonReentrant:
+ nonreentrant.append(*c);
+ exceptions = true;
+ break;
+ default:
+ break;
+ }
+ }
+ ++c;
+ }
+ if (!exceptions)
+ text << ".";
+ else if (threadSafeness == Node::Reentrant) {
+ if (nonreentrant.isEmpty()) {
+ if (!threadsafe.isEmpty()) {
+ text << ", but ";
+ appendFullNames(text,threadsafe,innerNode);
+ singularPlural(text,threadsafe);
+ text << " also " << tlink << ".";
+ }
+ else
+ text << ".";
+ }
+ else {
+ text << ", except for ";
+ appendFullNames(text,nonreentrant,innerNode);
+ text << ", which";
+ singularPlural(text,nonreentrant);
+ text << " nonreentrant.";
+ if (!threadsafe.isEmpty()) {
+ text << " ";
+ appendFullNames(text,threadsafe,innerNode);
+ singularPlural(text,threadsafe);
+ text << " " << tlink << ".";
+ }
+ }
+ }
+ else { // thread-safe
+ if (!nonreentrant.isEmpty() || !reentrant.isEmpty()) {
+ text << ", except for ";
+ if (!reentrant.isEmpty()) {
+ appendFullNames(text,reentrant,innerNode);
+ text << ", which";
+ singularPlural(text,reentrant);
+ text << " only " << rlink;
+ if (!nonreentrant.isEmpty())
+ text << ", and ";
+ }
+ if (!nonreentrant.isEmpty()) {
+ appendFullNames(text,nonreentrant,innerNode);
+ text << ", which";
+ singularPlural(text,nonreentrant);
+ text << " nonreentrant.";
+ }
+ text << ".";
+ }
+ }
+ }
+ else {
+ text << "This " << typeString(node) << " is ";
+ if (threadSafeness == Node::ThreadSafe)
+ text << tlink;
+ else
+ text << rlink;
+ text << ".";
+ }
+ text << Atom::ParaRight;
+ }
+ generateText(text,node,marker);
+}
+
+/*!
+ If the node is an overloaded signal, and a node with an example on how to connect to it
+ */
+void Generator::generateOverloadedSignal(const Node* node, CodeMarker* marker)
+{
+ if (node->type() != Node::Function)
+ return;
+ const FunctionNode *func = static_cast<const FunctionNode *>(node);
+ if (func->metaness() != FunctionNode::Signal)
+ return;
+ if (node->parent()->overloads(node->name()).count() <= 1)
+ return;
+
+
+ // Compute a friendly name for the object of that instance.
+ // e.g: "QAbstractSocket" -> "abstractSocket"
+ QString objectName = node->parent()->name();
+ if (objectName.size() >= 2) {
+ if (objectName[0] == 'Q')
+ objectName = objectName.mid(1);
+ objectName[0] = objectName[0].toLower();
+ }
+
+
+ // We have an overloaded signal, show an example
+ QString code = "connect(" + objectName + ", static_cast<" + func->returnType()
+ + QLatin1Char('(') + func->parent()->name() + "::*)(";
+ for (int i = 0; i < func->parameters().size(); ++i) {
+ if (i != 0)
+ code += ", ";
+ const Parameter &p = func->parameters().at(i);
+ code += p.dataType() + p.rightType();
+ }
+
+ code += QLatin1Char(')');
+ if (func->isConst())
+ code += " const";
+ code += ">(&" + func->parent()->name() + "::" + func->name() + "),\n [=](";
+
+ for (int i = 0; i < func->parameters().size(); ++i) {
+ if (i != 0)
+ code += ", ";
+ const Parameter &p = func->parameters().at(i);
+ code += p.dataType();
+ if (code[code.size()-1].isLetterOrNumber())
+ code += QLatin1Char(' ');
+ code += p.name() + p.rightType();
+ }
+
+ code += "){ /* ... */ });";
+
+ Text text;
+ text << Atom::ParaLeft
+ << Atom(Atom::FormattingLeft,ATOM_FORMATTING_BOLD)
+ << "Note:"
+ << Atom(Atom::FormattingRight,ATOM_FORMATTING_BOLD)
+ << "Signal "
+ << Atom(Atom::FormattingLeft,ATOM_FORMATTING_ITALIC)
+ << node->name()
+ << Atom(Atom::FormattingRight,ATOM_FORMATTING_ITALIC)
+ << " is overloaded in this class. "
+ "To connect to this one using the function pointer syntax, you must "
+ "specify the signal type in a static cast, as shown in this example:"
+ << Atom(Atom::Code, marker->markedUpCode(code, node, func->location()));
+
+ generateText(text, node, marker);
+}
+
+
+/*!
+ Traverses the database recursivly to generate all the documentation.
+ */
+void Generator::generateDocs()
+{
+ generateAggregate(qdb_->primaryTreeRoot());
+}
+
+Generator *Generator::generatorForFormat(const QString& format)
+{
+ QList<Generator *>::ConstIterator g = generators.constBegin();
+ while (g != generators.constEnd()) {
+ if ((*g)->format() == format)
+ return *g;
+ ++g;
+ }
+ return 0;
+}
+
+/*!
+ Looks up the tag \a t in the map of metadata values for the
+ current topic in \a inner. If a value for the tag is found,
+ the value is returned.
+
+ \note If \a t is found in the metadata map, it is erased.
+ i.e. Once you call this function for a particular \a t,
+ you consume \a t.
+ */
+QString Generator::getMetadataElement(const Aggregate* inner, const QString& t)
+{
+ QString s;
+ QStringMultiMap& metaTagMap = const_cast<QStringMultiMap&>(inner->doc().metaTagMap());
+ QStringMultiMap::iterator i = metaTagMap.find(t);
+ if (i != metaTagMap.end()) {
+ s = i.value();
+ metaTagMap.erase(i);
+ }
+ return s;
+}
+
+/*!
+ Looks up the tag \a t in the map of metadata values for the
+ current topic in \a inner. If values for the tag are found,
+ they are returned in a string list.
+
+ \note If \a t is found in the metadata map, all the pairs
+ having the key \a t are erased. i.e. Once you call this
+ function for a particular \a t, you consume \a t.
+ */
+QStringList Generator::getMetadataElements(const Aggregate* inner, const QString& t)
+{
+ QStringList s;
+ QStringMultiMap& metaTagMap = const_cast<QStringMultiMap&>(inner->doc().metaTagMap());
+ s = metaTagMap.values(t);
+ if (!s.isEmpty())
+ metaTagMap.remove(t);
+ return s;
+}
+
+/*!
+ Returns a relative path name for an image.
+ */
+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();
+
+ QString path = Config::copyFile(relative->doc().location(),
+ filePath,
+ userFriendlyFilePath,
+ outputDir() + QLatin1String("/images"));
+ int images_slash = path.lastIndexOf("images/");
+ QString relImagePath;
+ if (images_slash != -1)
+ relImagePath = path.mid(images_slash);
+ return relImagePath;
+}
+
+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('\n')) {
+ column = 0;
+ }
+ else {
+ if (column == 0) {
+ for (int j = 0; j < level; j++)
+ t += QLatin1Char(' ');
+ }
+ column++;
+ }
+ t += markedCode.at(i++);
+ }
+ return t;
+}
+
+void Generator::initialize(const Config &config)
+{
+
+ if (config.getBool(QString("HTML.nosubdirs")))
+ resetUseOutputSubdirs();
+
+ outFileNames_.clear();
+ outputFormats = config.getOutputFormats();
+ redirectDocumentationToDevNull_ = config.getBool(CONFIG_REDIRECTDOCUMENTATIONTODEVNULL);
+ if (!outputFormats.isEmpty()) {
+ outDir_ = config.getOutputDir();
+ if (outDir_.isEmpty()) {
+ config.lastLocation().fatal(tr("No output directory specified in "
+ "configuration file or on the command line"));
+ }
+ else {
+ outSubdir_ = outDir_.mid(outDir_.lastIndexOf('/') + 1);
+ }
+
+ QDir dirInfo;
+ if (dirInfo.exists(outDir_)) {
+ if (!generating() && Generator::useOutputSubdirs()) {
+ 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.exists(outDir_ + "/images") && !dirInfo.mkdir(outDir_ + "/images"))
+ config.lastLocation().fatal(tr("Cannot create images directory '%1'").arg(outDir_ + "/images"));
+ }
+
+ imageFiles = config.getCanonicalPathList(CONFIG_IMAGES);
+ imageDirs = config.getCanonicalPathList(CONFIG_IMAGEDIRS);
+ scriptFiles = config.getCanonicalPathList(CONFIG_SCRIPTS);
+ scriptDirs = config.getCanonicalPathList(CONFIG_SCRIPTDIRS);
+ styleFiles = config.getCanonicalPathList(CONFIG_STYLES);
+ styleDirs = config.getCanonicalPathList(CONFIG_STYLEDIRS);
+ exampleDirs = config.getCanonicalPathList(CONFIG_EXAMPLEDIRS);
+ exampleImgExts = config.getStringList(CONFIG_EXAMPLES + Config::dot + CONFIG_IMAGEEXTENSIONS);
+
+ QString imagesDotFileExtensions = CONFIG_IMAGES + Config::dot + CONFIG_FILEEXTENSIONS;
+ QSet<QString> formats = config.subVars(imagesDotFileExtensions);
+ QSet<QString>::ConstIterator f = formats.constBegin();
+ while (f != formats.constEnd()) {
+ imgFileExts[*f] = config.getStringList(imagesDotFileExtensions + Config::dot + *f);
+ ++f;
+ }
+
+ QList<Generator *>::ConstIterator g = generators.constBegin();
+ while (g != generators.constEnd()) {
+ if (outputFormats.contains((*g)->format())) {
+ currentGenerator_ = (*g);
+ (*g)->initializeGenerator(config);
+ QStringList extraImages = config.getCanonicalPathList((*g)->format() +
+ Config::dot +
+ CONFIG_EXTRAIMAGES, true);
+ QStringList::ConstIterator e = extraImages.constBegin();
+ while (e != extraImages.constEnd()) {
+ QString filePath = *e;
+ if (!filePath.isEmpty())
+ Config::copyFile(config.lastLocation(), filePath, filePath,
+ (*g)->outputDir() + "/images");
+ ++e;
+ }
+
+ // Documentation template handling
+ QStringList scripts = config.getCanonicalPathList((*g)->format()+Config::dot+CONFIG_SCRIPTS, true);
+ if (!scripts.isEmpty()) {
+ QDir dirInfo;
+ if (!dirInfo.exists(outDir_ + "/scripts") && !dirInfo.mkdir(outDir_ + "/scripts")) {
+ config.lastLocation().fatal(tr("Cannot create scripts directory '%1'")
+ .arg(outDir_ + "/scripts"));
+ }
+ else {
+ e = scripts.constBegin();
+ while (e != scripts.constEnd()) {
+ QString filePath = *e;
+ if (!filePath.isEmpty())
+ Config::copyFile(config.lastLocation(), filePath, filePath,
+ (*g)->outputDir() + "/scripts");
+ ++e;
+ }
+ }
+ }
+
+ QStringList styles = config.getCanonicalPathList((*g)->format()+Config::dot+CONFIG_STYLESHEETS, true);
+ if (!styles.isEmpty()) {
+ QDir dirInfo;
+ if (!dirInfo.exists(outDir_ + "/style") && !dirInfo.mkdir(outDir_ + "/style")) {
+ config.lastLocation().fatal(tr("Cannot create style directory '%1'")
+ .arg(outDir_ + "/style"));
+ }
+ else {
+ e = styles.constBegin();
+ while (e != styles.constEnd()) {
+ QString filePath = *e;
+ if (!filePath.isEmpty())
+ Config::copyFile(config.lastLocation(), filePath, filePath,
+ (*g)->outputDir() + "/style");
+ ++e;
+ }
+ }
+ }
+ }
+ ++g;
+ }
+
+ QRegExp secondParamAndAbove("[\2-\7]");
+ QSet<QString> formattingNames = config.subVars(CONFIG_FORMATTING);
+ QSet<QString>::ConstIterator n = formattingNames.constBegin();
+ while (n != formattingNames.constEnd()) {
+ QString formattingDotName = CONFIG_FORMATTING + Config::dot + *n;
+ QSet<QString> formats = config.subVars(formattingDotName);
+ QSet<QString>::ConstIterator f = formats.constBegin();
+ while (f != formats.constEnd()) {
+ 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);
+
+ outputPrefixes.clear();
+ QStringList items = config.getStringList(CONFIG_OUTPUTPREFIXES);
+ if (!items.isEmpty()) {
+ foreach (const QString &prefix, items)
+ outputPrefixes[prefix] = config.getString(CONFIG_OUTPUTPREFIXES + Config::dot + prefix);
+ }
+ else {
+ outputPrefixes[QLatin1String("QML")] = QLatin1String("qml-");
+ outputPrefixes[QLatin1String("JS")] = QLatin1String("js-");
+ }
+
+ outputSuffixes.clear();
+ items = config.getStringList(CONFIG_OUTPUTSUFFIXES);
+ if (!items.isEmpty()) {
+ foreach (const QString &suffix, items)
+ outputSuffixes[suffix] = config.getString(CONFIG_OUTPUTSUFFIXES + Config::dot + suffix);
+ }
+
+ noLinkErrors_ = config.getBool(CONFIG_NOLINKERRORS);
+ autolinkErrors_ = config.getBool(CONFIG_AUTOLINKERRORS);
+}
+
+/*!
+ Appends each directory path in \a moreImageDirs to the
+ list of image directories.
+ */
+void Generator::augmentImageDirs(QSet<QString>& moreImageDirs)
+{
+ if (moreImageDirs.isEmpty())
+ return;
+ QSet<QString>::const_iterator i = moreImageDirs.begin();
+ while (i != moreImageDirs.end()) {
+ imageDirs.append(*i);
+ ++i;
+ }
+}
+
+/*!
+ Sets the generator's pointer to the Config instance.
+ */
+void Generator::initializeGenerator(const Config& config)
+{
+ config_ = &config;
+ showInternal_ = config.getBool(CONFIG_SHOWINTERNAL);
+ singleExec_ = config.getBool(CONFIG_SINGLEEXEC);
+}
+
+bool Generator::matchAhead(const Atom *atom, Atom::AtomType expectedAtomType)
+{
+ return atom->next() != 0 && atom->next()->type() == expectedAtomType;
+}
+
+/*!
+ Used for writing to the current output stream. Returns a
+ reference to the current output stream, which is then used
+ with the \c {<<} operator for writing.
+ */
+QTextStream &Generator::out()
+{
+ return *outStreamStack.top();
+}
+
+QString Generator::outFileName()
+{
+ return QFileInfo(static_cast<QFile*>(out().device())->fileName()).fileName();
+}
+
+QString Generator::outputPrefix(const Node *node)
+{
+ // Prefix is applied to QML and JS types
+ if (node->isQmlType() || node->isQmlBasicType())
+ return outputPrefixes[QLatin1String("QML")];
+ if (node->isJsType() || node->isJsBasicType())
+ return outputPrefixes[QLatin1String("JS")];
+ return QString();
+}
+
+QString Generator::outputSuffix(const Node *node)
+{
+ // Suffix is applied to QML and JS types, as
+ // well as module pages.
+ if (node->isQmlModule() || node->isQmlType() || node->isQmlBasicType())
+ return outputSuffixes[QLatin1String("QML")];
+ if (node->isJsModule() || node->isJsType() || node->isJsBasicType())
+ return outputSuffixes[QLatin1String("JS")];
+ return QString();
+}
+
+bool Generator::parseArg(const QString& src,
+ const QString& tag,
+ int* pos,
+ int n,
+ QStringRef* contents,
+ QStringRef* par1,
+ bool debug)
+{
+#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
+}
+
+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;
+}
+
+void Generator::setImageFileExtensions(const QStringList& extensions)
+{
+ imgFileExts[format()] = extensions;
+}
+
+void Generator::singularPlural(Text& text, const NodeList& nodes)
+{
+ if (nodes.count() == 1)
+ text << " is";
+ else
+ text << " are";
+}
+
+int Generator::skipAtoms(const Atom *atom, Atom::AtomType type) const
+{
+ int skipAhead = 0;
+ atom = atom->next();
+ while (atom != 0 && atom->type() != type) {
+ skipAhead++;
+ atom = atom->next();
+ }
+ return skipAhead;
+}
+
+/*!
+ Resets the variables used during text output.
+ */
+void Generator::initializeTextOutput()
+{
+ inLink_ = false;
+ inContents_ = false;
+ inSectionHeading_ = false;
+ inTableHeader_ = false;
+ numTableRows_ = 0;
+ threeColumnEnumValueTable_ = true;
+ link_.clear();
+ sectionNumber_.clear();
+}
+
+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() == 0) {
+ 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, QString());
+
+ if (!alternateFunc) {
+ alternateName = "is" + func->name().mid(3);
+ alternateFunc = func->parent()->findFunctionNode(alternateName, QString());
+ if (!alternateFunc) {
+ alternateName = "has" + func->name().mid(3);
+ alternateFunc = func->parent()->findFunctionNode(alternateName, QString());
+ }
+ }
+ }
+ else if (!func->name().isEmpty()) {
+ alternateName = "set";
+ alternateName += func->name()[0].toUpper();
+ alternateName += func->name().mid(1);
+ alternateFunc = func->parent()->findFunctionNode(alternateName, QString());
+ }
+
+ 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);
+ }
+ }
+ }
+ }
+}
+
+void Generator::terminate()
+{
+ QList<Generator *>::ConstIterator g = generators.constBegin();
+ while (g != generators.constEnd()) {
+ if (outputFormats.contains((*g)->format()))
+ (*g)->terminateGenerator();
+ ++g;
+ }
+
+ fmtLeftMaps.clear();
+ fmtRightMaps.clear();
+ imgFileExts.clear();
+ imageFiles.clear();
+ imageDirs.clear();
+ outDir_.clear();
+}
+
+void Generator::terminateGenerator()
+{
+}
+
+/*!
+ Trims trailing whitespace off the \a string and returns
+ the trimmed string.
+ */
+QString Generator::trimmedTrailing(const QString& string, const QString &prefix, const QString &suffix)
+{
+ QString trimmed = string;
+ while (trimmed.length() > 0 && trimmed[trimmed.length() - 1].isSpace())
+ trimmed.truncate(trimmed.length() - 1);
+
+ trimmed.append(suffix);
+ trimmed.prepend(prefix);
+ return trimmed;
+}
+
+QString Generator::typeString(const Node *node)
+{
+ switch (node->type()) {
+ case Node::Namespace:
+ return "namespace";
+ case Node::Class:
+ return "class";
+ case Node::QmlType:
+ return "type";
+ case Node::QmlBasicType:
+ return "type";
+ case Node::Document:
+ return "documentation";
+ case Node::Enum:
+ return "enum";
+ case Node::Typedef:
+ return "typedef";
+ case Node::Function:
+ return "function";
+ case Node::Property:
+ return "property";
+ case Node::QmlPropertyGroup:
+ return "property group";
+ case Node::QmlProperty:
+ return "QML property";
+ case Node::QmlSignal:
+ return "QML signal";
+ case Node::QmlSignalHandler:
+ return "QML signal handler";
+ case Node::QmlMethod:
+ return "QML method";
+ default:
+ return "documentation";
+ }
+}
+
+void Generator::unknownAtom(const Atom *atom)
+{
+ Location::internalError(tr("unknown atom type '%1' in %2 generator")
+ .arg(atom->typeString()).arg(format()));
+}
+
+QT_END_NAMESPACE
diff --git a/src/qdoc/generator.h b/src/qdoc/generator.h
new file mode 100644
index 000000000..025f51698
--- /dev/null
+++ b/src/qdoc/generator.h
@@ -0,0 +1,260 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL21$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** 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 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** As a special exception, The Qt Company gives you certain additional
+** rights. These rights are described in The Qt Company LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef GENERATOR_H
+#define GENERATOR_H
+
+#include <qfile.h>
+#include <qfileinfo.h>
+#include <qlist.h>
+#include <qmap.h>
+#include <qregexp.h>
+#include <qstring.h>
+#include <qstringlist.h>
+#include <qtextstream.h>
+#include "config.h"
+#include "node.h"
+#include "text.h"
+
+QT_BEGIN_NAMESPACE
+
+typedef QMultiMap<QString, Node*> NodeMultiMap;
+typedef QMap<Node*, NodeMultiMap> ParentMaps;
+
+class Config;
+class CodeMarker;
+class Location;
+class QDocDatabase;
+
+class Generator
+{
+ Q_DECLARE_TR_FUNCTIONS(QDoc::Generator)
+
+public:
+ enum QDocPass { Neither, Prepare, Generate };
+ enum ListType { Generic, Obsolete };
+
+ Generator();
+ virtual ~Generator();
+
+ virtual bool canHandleFormat(const QString &format) { return format == this->format(); }
+ virtual QString format() = 0;
+ virtual void generateDocs();
+ virtual void initializeGenerator(const Config &config);
+ virtual void terminateGenerator();
+
+ QString fullDocumentLocation(const Node *node, bool useSubdir = false);
+ const Config* config() { return config_; }
+
+ static Generator *currentGenerator() { return currentGenerator_; }
+ static Generator *generatorForFormat(const QString& format);
+ static void initialize(const Config& config);
+ static const QString& outputDir() { return outDir_; }
+ static const QString& outputSubdir() { return outSubdir_; }
+ static void terminate();
+ static const QStringList& outputFileNames() { return outFileNames_; }
+ static void writeOutFileNames();
+ static void augmentImageDirs(QSet<QString>& moreImageDirs);
+ static void debug(const QString& message);
+ static void startDebugging(const QString& message);
+ static void stopDebugging(const QString& message);
+ static bool debugging() { return debugging_; }
+ static bool noLinkErrors() { return noLinkErrors_; }
+ static bool autolinkErrors() { return autolinkErrors_; }
+ static void setQDocPass(QDocPass t) { qdocPass_ = t; }
+ static bool preparing() { return (qdocPass_ == Prepare); }
+ static bool generating() { return (qdocPass_ == Generate); }
+ static bool singleExec() { return qdocSingleExec_; }
+ static bool writeQaPages() { return qdocWriteQaPages_; }
+ static void setSingleExec() { qdocSingleExec_ = true; }
+ static void setWriteQaPages() { qdocWriteQaPages_ = true; }
+ static QString defaultModuleName() { return project_; }
+ static void resetUseOutputSubdirs() { useOutputSubdirs_ = false; }
+ static bool useOutputSubdirs() { return useOutputSubdirs_; }
+ static void setQmlTypeContext(QmlTypeNode* t) { qmlTypeContext_ = t; }
+ static QmlTypeNode* qmlTypeContext() { return qmlTypeContext_; }
+ static QString cleanRef(const QString& ref);
+
+protected:
+ virtual void beginSubPage(const Aggregate* node, const QString& fileName);
+ virtual void endSubPage();
+ virtual QString fileBase(const Node* node) const;
+ virtual QString fileExtension() const = 0;
+ virtual void generateQAPage() { }
+ virtual void generateAlsoList(const Node *node, CodeMarker *marker);
+ virtual int generateAtom(const Atom *atom, const Node *relative, CodeMarker *marker);
+ virtual void generateBody(const Node *node, CodeMarker *marker);
+ virtual void generateClassLikeNode(Aggregate* inner, CodeMarker* marker);
+ virtual void generateQmlTypePage(QmlTypeNode* , CodeMarker* ) { }
+ virtual void generateQmlBasicTypePage(QmlBasicTypeNode* , CodeMarker* ) { }
+ virtual void generateDocumentNode(DocumentNode* dn, CodeMarker* marker);
+ virtual void generateCollectionNode(CollectionNode* cn, CodeMarker* marker);
+ virtual void generateInheritedBy(const ClassNode *classe, CodeMarker *marker);
+ virtual void generateInherits(const ClassNode *classe, CodeMarker *marker);
+ virtual void generateAggregate(Aggregate* node);
+ virtual void generateMaintainerList(const Aggregate* node, CodeMarker* marker);
+ virtual void generateQmlInheritedBy(const QmlTypeNode* qcn, CodeMarker* marker);
+ virtual void generateQmlInherits(QmlTypeNode* qcn, CodeMarker* marker);
+ virtual bool generateQmlText(const Text& text,
+ const Node *relative,
+ CodeMarker *marker,
+ const QString& qmlName);
+ virtual bool generateText(const Text& text, const Node *relative, CodeMarker *marker);
+ virtual QString imageFileName(const Node *relative, const QString& fileBase);
+ virtual int skipAtoms(const Atom *atom, Atom::AtomType type) const;
+ virtual QString typeString(const Node *node);
+
+ static bool matchAhead(const Atom *atom, Atom::AtomType expectedAtomType);
+ static QString outputPrefix(const Node* node);
+ static QString outputSuffix(const Node* node);
+ static void singularPlural(Text& text, const NodeList& nodes);
+ static void supplementAlsoList(const Node *node, QList<Text> &alsoList);
+ static QString trimmedTrailing(const QString &string,
+ const QString &prefix,
+ const QString &suffix);
+ static QString sinceTitles[];
+
+ void initializeTextOutput();
+ QString fileName(const Node* node) const;
+ QMap<QString, QString> &formattingLeftMap();
+ QMap<QString, QString> &formattingRightMap();
+ const Atom* generateAtomList(const Atom *atom,
+ const Node *relative,
+ CodeMarker *marker,
+ bool generate,
+ int& numGeneratedAtoms);
+ void generateExampleFiles(const DocumentNode *dn, CodeMarker *marker);
+ void generateFileList(const DocumentNode* dn,
+ CodeMarker* marker,
+ Node::DocSubtype subtype,
+ const QString& tag);
+ void generateSince(const Node *node, CodeMarker *marker);
+ void generateStatus(const Node *node, CodeMarker *marker);
+ void generatePrivateSignalNote(const Node* node, CodeMarker* marker);
+ void generateThreadSafeness(const Node *node, CodeMarker *marker);
+ QString getMetadataElement(const Aggregate* inner, const QString& t);
+ QStringList getMetadataElements(const Aggregate* inner, const QString& t);
+ void generateOverloadedSignal(const Node *node, CodeMarker *marker);
+ QString indent(int level, const QString& markedCode);
+ QTextStream& out();
+ QString outFileName();
+ bool parseArg(const QString& src,
+ const QString& tag,
+ int* pos,
+ int n,
+ QStringRef* contents,
+ QStringRef* par1 = 0,
+ bool debug = false);
+ QString plainCode(const QString& markedCode);
+ void setImageFileExtensions(const QStringList& extensions);
+ void unknownAtom(const Atom *atom);
+ void appendSortedQmlNames(Text& text, const Node* base, const NodeList& subs);
+
+ QMap<QString, QStringList> editionGroupMap;
+ QMap<QString, QStringList> editionModuleMap;
+ QString naturalLanguage;
+#ifndef QT_NO_TEXTCODEC
+ QTextCodec* outputCodec;
+ QString outputEncoding;
+#endif
+ QString tagFile_;
+ QStack<QTextStream*> outStreamStack;
+
+ void appendFullName(Text& text,
+ const Node *apparentNode,
+ const Node *relative,
+ const Node *actualNode = 0);
+ void appendFullName(Text& text,
+ const Node *apparentNode,
+ const QString& fullName,
+ const Node *actualNode);
+ void appendFullNames(Text& text, const NodeList& nodes, const Node* relative);
+ void appendSortedNames(Text& text, const ClassNode *classe, const QList<RelatedClass> &classes);
+
+private:
+ static Generator* currentGenerator_;
+ static QStringList exampleDirs;
+ static QStringList exampleImgExts;
+ static QMap<QString, QMap<QString, QString> > fmtLeftMaps;
+ static QMap<QString, QMap<QString, QString> > fmtRightMaps;
+ static QList<Generator *> generators;
+ static QStringList imageDirs;
+ static QStringList imageFiles;
+ static QMap<QString, QStringList> imgFileExts;
+ static QString project_;
+ static QString outDir_;
+ static QString outSubdir_;
+ static QStringList outFileNames_;
+ static QSet<QString> outputFormats;
+ static QHash<QString, QString> outputPrefixes;
+ static QHash<QString, QString> outputSuffixes;
+ static QStringList scriptDirs;
+ static QStringList scriptFiles;
+ static QStringList styleDirs;
+ static QStringList styleFiles;
+ static bool debugging_;
+ static bool noLinkErrors_;
+ static bool autolinkErrors_;
+ static bool redirectDocumentationToDevNull_;
+ static QDocPass qdocPass_;
+ static bool qdocSingleExec_;
+ static bool qdocWriteQaPages_;
+ static bool useOutputSubdirs_;
+ static QmlTypeNode* qmlTypeContext_;
+
+ void generateReimplementedFrom(const FunctionNode *func, CodeMarker *marker);
+
+ QString amp;
+ QString gt;
+ QString lt;
+ QString quot;
+ QRegExp tag;
+
+ protected:
+ const Config* config_;
+ QDocDatabase* qdb_;
+ bool inLink_;
+ bool inContents_;
+ bool inSectionHeading_;
+ bool inTableHeader_;
+ bool threeColumnEnumValueTable_;
+ bool showInternal_;
+ bool singleExec_;
+ int numTableRows_;
+ QString link_;
+ QString sectionNumber_;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/qdoc/helpprojectwriter.cpp b/src/qdoc/helpprojectwriter.cpp
new file mode 100644
index 000000000..8161913f1
--- /dev/null
+++ b/src/qdoc/helpprojectwriter.cpp
@@ -0,0 +1,858 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL21$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** 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 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** As a special exception, The Qt Company gives you certain additional
+** rights. These rights are described in The Qt Company LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <qcryptographichash.h>
+#include <qdebug.h>
+#include <qhash.h>
+#include <qmap.h>
+
+#include "atom.h"
+#include "helpprojectwriter.h"
+#include "htmlgenerator.h"
+#include "config.h"
+#include "node.h"
+#include "qdocdatabase.h"
+#include <qdebug.h>
+
+QT_BEGIN_NAMESPACE
+
+HelpProjectWriter::HelpProjectWriter(const Config &config,
+ const QString &defaultFileName,
+ Generator* g)
+{
+ reset(config, defaultFileName, g);
+}
+
+void HelpProjectWriter::reset(const Config &config,
+ const QString &defaultFileName,
+ Generator* g)
+{
+ projects.clear();
+ gen_ = g;
+ /*
+ Get the pointer to the singleton for the qdoc database and
+ store it locally. This replaces all the local accesses to
+ the node tree, which are now private.
+ */
+ qdb_ = QDocDatabase::qdocDB();
+
+ // The output directory should already have been checked by the calling
+ // generator.
+ outputDir = config.getOutputDir();
+
+ 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.extraFiles += config.getStringSet(CONFIG_QHP + Config::dot + "extraFiles");
+ project.indexTitle = config.getString(prefix + "indexTitle");
+ project.indexRoot = config.getString(prefix + "indexRoot");
+ project.filterAttributes = config.getStringList(prefix + "filterAttributes").toSet();
+ project.includeIndexNodes = config.getBool(prefix + "includeIndexNodes");
+ 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(QLatin1Char('\\'), QLatin1Char('/')));
+
+ 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");
+ subproject.type = config.getString(subprefix + "type");
+ readSelectors(subproject, config.getStringList(subprefix + "selectors"));
+ project.subprojects.append(subproject);
+ }
+
+ if (project.subprojects.isEmpty()) {
+ SubProject subproject;
+ readSelectors(subproject, config.getStringList(prefix + "selectors"));
+ project.subprojects.insert(0, subproject);
+ }
+
+ projects.append(project);
+ }
+}
+
+void HelpProjectWriter::readSelectors(SubProject &subproject, const QStringList &selectors)
+{
+ QHash<QString, Node::NodeType> typeHash;
+ typeHash["namespace"] = Node::Namespace;
+ typeHash["class"] = Node::Class;
+ typeHash["doc"] = Node::Document;
+ typeHash["fake"] = Node::Document; // Legacy alias for 'doc'
+ typeHash["enum"] = Node::Enum;
+ typeHash["typedef"] = Node::Typedef;
+ typeHash["function"] = Node::Function;
+ typeHash["property"] = Node::Property;
+ typeHash["variable"] = Node::Variable;
+ typeHash["group"] = Node::Group;
+ typeHash["module"] = Node::Module;
+ typeHash["qmlmodule"] = Node::QmlModule;
+ typeHash["qmlproperty"] = Node::QmlProperty;
+ typeHash["qmlsignal"] = Node::QmlSignal;
+ typeHash["qmlsignalhandler"] = Node::QmlSignalHandler;
+ typeHash["qmlmethod"] = Node::QmlMethod;
+ typeHash["qmlpropertygroup"] = Node::QmlPropertyGroup;
+ typeHash["qmlclass"] = Node::QmlType; // Legacy alias for 'qmltype'
+ typeHash["qmltype"] = Node::QmlType;
+ typeHash["qmlbasictype"] = Node::QmlBasicType;
+
+ QHash<QString, Node::DocSubtype> docSubtypeHash;
+ docSubtypeHash["example"] = Node::Example;
+ docSubtypeHash["headerfile"] = Node::HeaderFile;
+ docSubtypeHash["file"] = Node::File;
+ docSubtypeHash["page"] = Node::Page;
+ docSubtypeHash["externalpage"] = Node::ExternalPage;
+
+ QSet<Node::DocSubtype> allSubTypes = QSet<Node::DocSubtype>::fromList(docSubtypeHash.values());
+
+ foreach (const QString &selector, selectors) {
+ QStringList pieces = selector.split(QLatin1Char(':'));
+ if (pieces.size() == 1) {
+ QString lower = selector.toLower();
+ if (typeHash.contains(lower))
+ subproject.selectors[typeHash[lower]] = allSubTypes;
+ } else if (pieces.size() >= 2) {
+ QString docType = pieces[0].toLower();
+ pieces = pieces[1].split(QLatin1Char(','));
+ if (typeHash.contains(docType)) {
+ QSet<Node::DocSubtype> docSubtypes;
+ for (int i = 0; i < pieces.size(); ++i) {
+ QString piece = pieces[i].toLower();
+ if (typeHash[docType] == Node::Group) {
+ subproject.groups << piece;
+ continue;
+ }
+ if (docSubtypeHash.contains(piece))
+ docSubtypes.insert(docSubtypeHash[piece]);
+ }
+ subproject.selectors[typeHash[docType]] = docSubtypes;
+ }
+ }
+ }
+}
+
+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->isQmlType() || node->isQmlBasicType()) {
+ details << node->name();
+ details << "QML." + node->name();
+ }
+ else if (node->isJsType() || node->isJsBasicType()) {
+ details << node->name();
+ details << "JS." + node->name();
+ }
+ else if (node->isDocumentNode()) {
+ const DocumentNode *fake = static_cast<const DocumentNode *>(node);
+ details << fake->fullTitle();
+ details << fake->fullTitle();
+ }
+ else {
+ details << node->name();
+ details << node->name();
+ }
+ details << gen_->fullDocumentLocation(node, false);
+ return details;
+}
+
+bool HelpProjectWriter::generateSection(HelpProject &project,
+ QXmlStreamWriter & /* writer */,
+ const Node *node)
+{
+ if (!node->url().isEmpty() && !(project.includeIndexNodes && !node->url().startsWith("http")))
+ 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 = node->isDocumentNode() ? node->fullTitle() : node->fullDocumentName();
+ // 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.
+
+ for (int i = 0; i < project.subprojects.length(); i++) {
+ SubProject subproject = project.subprojects[i];
+ // No selectors: accept all nodes.
+ if (subproject.selectors.isEmpty()) {
+ project.subprojects[i].nodes[objName] = node;
+ }
+ else if (subproject.selectors.contains(node->type())) {
+ // Add all group members for 'group:name' selector
+ if (node->isGroup()) {
+ if (project.subprojects[i].groups.contains(node->name())) {
+ const CollectionNode* cn = static_cast<const CollectionNode*>(node);
+ foreach (const Node* m, cn->members()) {
+ QString memberName = m->isDocumentNode()
+ ? m->fullTitle() : m->fullDocumentName();
+ project.subprojects[i].nodes[memberName] = m;
+ }
+ }
+ }
+ // Accept only the node types in the selectors hash.
+ else if (node->type() != Node::Document)
+ project.subprojects[i].nodes[objName] = node;
+ else {
+ // Accept only doc nodes with subtypes contained in the selector's
+ // mask.
+ const DocumentNode *docNode = static_cast<const DocumentNode *>(node);
+ if (subproject.selectors[node->type()].contains(docNode->docSubtype()) &&
+ docNode->docSubtype() != Node::ExternalPage &&
+ !docNode->fullTitle().isEmpty()) {
+
+ project.subprojects[i].nodes[objName] = node;
+ }
+ }
+ }
+ }
+
+ switch (node->type()) {
+
+ case Node::Class:
+ project.keywords.append(keywordDetails(node));
+ break;
+ case Node::QmlType:
+ case Node::QmlBasicType:
+ if (node->doc().hasKeywords()) {
+ foreach (const Atom* keyword, node->doc().keywords()) {
+ if (!keyword->string().isEmpty()) {
+ QStringList details;
+ details << keyword->string()
+ << keyword->string()
+ << gen_->fullDocumentLocation(node, false);
+ project.keywords.append(details);
+ }
+ else
+ node->doc().location().warning(tr("Bad keyword in %1").arg(gen_->fullDocumentLocation(node, false)));
+ }
+ }
+ project.keywords.append(keywordDetails(node));
+ break;
+
+ case Node::Namespace:
+ project.keywords.append(keywordDetails(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 << gen_->fullDocumentLocation(node, false);
+ project.keywords.append(details);
+ }
+ }
+ break;
+
+ case Node::Group:
+ case Node::Module:
+ case Node::QmlModule:
+ {
+ const CollectionNode* cn = static_cast<const CollectionNode*>(node);
+ if (!cn->fullTitle().isEmpty()) {
+ if (cn->doc().hasKeywords()) {
+ foreach (const Atom* keyword, cn->doc().keywords()) {
+ if (!keyword->string().isEmpty()) {
+ QStringList details;
+ details << keyword->string()
+ << keyword->string()
+ << gen_->fullDocumentLocation(node, false);
+ project.keywords.append(details);
+ }
+ else
+ cn->doc().location().warning(
+ tr("Bad keyword in %1").arg(gen_->fullDocumentLocation(node, false))
+ );
+ }
+ }
+ project.keywords.append(keywordDetails(node));
+ }
+ }
+ break;
+
+ case Node::Property:
+ case Node::QmlProperty:
+ case Node::QmlSignal:
+ case Node::QmlSignalHandler:
+ case Node::QmlMethod:
+ 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());
+ } 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] = gen_->fullDocumentLocation(enumNode, false);
+
+ project.keywords.append(typedefDetails);
+ }
+ break;
+
+ case Node::Variable:
+ {
+ project.keywords.append(keywordDetails(node));
+ }
+ break;
+
+ // Document nodes (such as manual pages) contain subtypes, titles and other
+ // attributes.
+ case Node::Document: {
+ const DocumentNode *docNode = static_cast<const DocumentNode*>(node);
+ if (docNode->docSubtype() != Node::ExternalPage &&
+ docNode->docSubtype() != Node::Image &&
+ !docNode->fullTitle().isEmpty()) {
+
+ if (docNode->docSubtype() != Node::File) {
+ if (docNode->doc().hasKeywords()) {
+ foreach (const Atom *keyword, docNode->doc().keywords()) {
+ if (!keyword->string().isEmpty()) {
+ QStringList details;
+ details << keyword->string()
+ << keyword->string()
+ << gen_->fullDocumentLocation(node, false);
+ project.keywords.append(details);
+ } else
+ docNode->doc().location().warning(
+ tr("Bad keyword in %1").arg(gen_->fullDocumentLocation(node, false))
+ );
+ }
+ }
+ project.keywords.append(keywordDetails(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(QLatin1Char('/'));
+ project.files.insert("images/" + pieces.last());
+ }
+ atom = atom->next();
+ }
+
+ return true;
+}
+
+void HelpProjectWriter::generateSections(HelpProject &project,
+ QXmlStreamWriter &writer, const Node *node)
+{
+ /*
+ Don't include index nodes in the help file. Or DITA map nodes.
+ */
+ if (node->isIndexNode() || node->docSubtype() == Node::DitaMap)
+ return;
+ if (!generateSection(project, writer, node))
+ return;
+
+ if (node->isAggregate()) {
+ const Aggregate *inner = static_cast<const Aggregate *>(node);
+
+ // Ensure that we don't visit nodes more than once.
+ QMap<QString, const Node*> childMap;
+ foreach (const Node *childNode, inner->childNodes()) {
+ if (childNode->isIndexNode())
+ continue;
+
+ if (childNode->access() == Node::Private)
+ continue;
+
+ if (childNode->type() == Node::Document) {
+ childMap[static_cast<const DocumentNode *>(childNode)->fullTitle()] = childNode;
+ }
+ else if (childNode->isQmlPropertyGroup() || childNode->isJsPropertyGroup()) {
+ /*
+ Don't visit QML/JS property group nodes,
+ but visit their children, which are all
+ QML/JS property nodes.
+
+ This is probably not correct anymore,
+ because The Qml/Js Property Group is
+ an actual documented thing.
+ */
+ const Aggregate* inner = static_cast<const Aggregate*>(childNode);
+ foreach (const Node* n, inner->childNodes()) {
+ if (n->access() == Node::Private)
+ continue;
+ childMap[n->fullDocumentName()] = n;
+ }
+ }
+ else {
+ // Store member status of children
+ project.memberStatus[node].insert(childNode->status());
+ if (childNode->relates()) {
+ project.memberStatus[childNode->relates()].insert(childNode->status());
+ }
+
+ if (childNode->type() == Node::Function) {
+ const FunctionNode *funcNode = static_cast<const FunctionNode *>(childNode);
+ if (funcNode->isOverload())
+ continue;
+ }
+ childMap[childNode->fullDocumentName()] = childNode;
+ }
+ }
+ foreach (const Node *child, childMap)
+ generateSections(project, writer, child);
+ }
+}
+
+void HelpProjectWriter::generate()
+{
+ for (int i = 0; i < projects.size(); ++i)
+ generateProject(projects[i]);
+}
+
+void HelpProjectWriter::writeHashFile(QFile &file)
+{
+ QCryptographicHash hash(QCryptographicHash::Sha1);
+ hash.addData(&file);
+
+ QFile hashFile(file.fileName() + ".sha1");
+ if (!hashFile.open(QFile::WriteOnly | QFile::Text))
+ return;
+
+ hashFile.write(hash.result().toHex());
+ hashFile.close();
+}
+
+void HelpProjectWriter::writeSection(QXmlStreamWriter &writer, const QString &path,
+ const QString &value)
+{
+ writer.writeStartElement(QStringLiteral("section"));
+ writer.writeAttribute(QStringLiteral("ref"), path);
+ writer.writeAttribute(QStringLiteral("title"), value);
+ writer.writeEndElement(); // section
+}
+
+/*
+ Write subsections for all members, compatibility members and obsolete members.
+*/
+void HelpProjectWriter::addMembers(HelpProject &project, QXmlStreamWriter &writer,
+ const Node *node)
+{
+ QString href = gen_->fullDocumentLocation(node, false);
+ href = href.left(href.size()-5);
+ if (href.isEmpty())
+ return;
+
+ bool derivedClass = false;
+ if (node->type() == Node::Class)
+ derivedClass = !(static_cast<const ClassNode *>(node)->baseClasses().isEmpty());
+
+ // Do not generate a 'List of all members' for namespaces or header files,
+ // but always generate it for derived classes and QML classes
+ if (!node->isNamespace() && !node->isHeaderFile() &&
+ (derivedClass || node->isQmlType() || node->isJsType() ||
+ !project.memberStatus[node].isEmpty())) {
+ QString membersPath = href + QStringLiteral("-members.html");
+ writeSection(writer, membersPath, tr("List of all members"));
+ }
+ if (project.memberStatus[node].contains(Node::Compat)) {
+ QString compatPath = href + QStringLiteral("-compat.html");
+ writeSection(writer, compatPath, tr("Compatibility members"));
+ }
+ if (project.memberStatus[node].contains(Node::Obsolete)) {
+ QString obsoletePath = href + QStringLiteral("-obsolete.html");
+ writeSection(writer, obsoletePath, tr("Obsolete members"));
+ }
+}
+
+void HelpProjectWriter::writeNode(HelpProject &project, QXmlStreamWriter &writer,
+ const Node *node)
+{
+ QString href = gen_->fullDocumentLocation(node, false);
+ 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));
+
+ addMembers(project, writer, node);
+ writer.writeEndElement(); // section
+ break;
+
+ case Node::Namespace:
+ writeSection(writer, href, objName);
+ break;
+
+ case Node::QmlType:
+ writer.writeStartElement("section");
+ writer.writeAttribute("ref", href);
+ writer.writeAttribute("title", tr("%1 Type Reference").arg(node->fullTitle()));
+ addMembers(project, writer, node);
+ writer.writeEndElement(); // section
+ break;
+
+ case Node::Document: {
+ // Document nodes (such as manual pages) contain subtypes, titles and other
+ // attributes.
+ const DocumentNode *docNode = static_cast<const DocumentNode*>(node);
+
+ writer.writeStartElement("section");
+ writer.writeAttribute("ref", href);
+ writer.writeAttribute("title", docNode->fullTitle());
+
+ if (docNode->docSubtype() == Node::HeaderFile)
+ addMembers(project, writer, node);
+
+ writer.writeEndElement(); // section
+ }
+ break;
+ case Node::Group:
+ case Node::Module:
+ case Node::QmlModule:
+ {
+ const CollectionNode* cn = static_cast<const CollectionNode*>(node);
+ writer.writeStartElement("section");
+ writer.writeAttribute("ref", href);
+ writer.writeAttribute("title", cn->fullTitle());
+ writer.writeEndElement(); // section
+ }
+ break;
+ default:
+ ;
+ }
+}
+
+void HelpProjectWriter::generateProject(HelpProject &project)
+{
+ const Node *rootNode;
+
+ // Restrict searching only to the local (primary) tree
+ QVector<Tree*> searchOrder = qdb_->searchOrder();
+ qdb_->setLocalSearch();
+
+ if (!project.indexRoot.isEmpty())
+ rootNode = qdb_->findDocumentNodeByTitle(project.indexRoot);
+ else
+ rootNode = qdb_->primaryTreeRoot();
+
+ 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.constBegin(); it != project.customFilters.constEnd(); ++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");
+ const Node* node = qdb_->findDocumentNodeByTitle(project.indexTitle);
+ if (node == 0)
+ node = qdb_->findNodeByNameAndType(QStringList("index.html"), Node::Document);
+ QString indexPath;
+ if (node)
+ indexPath = gen_->fullDocumentLocation(node, false);
+ else
+ indexPath = "index.html";
+ writer.writeAttribute("ref", indexPath);
+ writer.writeAttribute("title", project.indexTitle);
+
+ generateSections(project, writer, rootNode);
+
+ for (int i = 0; i < project.subprojects.length(); i++) {
+ SubProject subproject = project.subprojects[i];
+
+ if (subproject.type == QLatin1String("manual")) {
+
+ const Node *indexPage = qdb_->findNodeForTarget(subproject.indexTitle, 0);
+ if (indexPage) {
+ Text indexBody = indexPage->doc().body();
+ const Atom *atom = indexBody.firstAtom();
+ QStack<int> sectionStack;
+ bool inItem = false;
+
+ while (atom) {
+ switch (atom->type()) {
+ case Atom::ListLeft:
+ sectionStack.push(0);
+ break;
+ case Atom::ListRight:
+ if (sectionStack.pop() > 0)
+ writer.writeEndElement(); // section
+ break;
+ case Atom::ListItemLeft:
+ inItem = true;
+ break;
+ case Atom::ListItemRight:
+ inItem = false;
+ break;
+ case Atom::Link:
+ if (inItem) {
+ if (sectionStack.top() > 0)
+ writer.writeEndElement(); // section
+
+ const Node *page = qdb_->findNodeForTarget(atom->string(), 0);
+ writer.writeStartElement("section");
+ QString indexPath = gen_->fullDocumentLocation(page, false);
+ writer.writeAttribute("ref", indexPath);
+ writer.writeAttribute("title", atom->string());
+
+ sectionStack.top() += 1;
+ }
+ break;
+ default:
+ ;
+ }
+
+ if (atom == indexBody.lastAtom())
+ break;
+ atom = atom->next();
+ }
+ } else
+ rootNode->doc().location().warning(
+ tr("Failed to find index: %1").arg(subproject.indexTitle)
+ );
+
+ } else {
+
+ writer.writeStartElement("section");
+ QString indexPath = gen_->fullDocumentLocation(qdb_->findNodeForTarget(subproject.indexTitle, 0),
+ false);
+ writer.writeAttribute("ref", indexPath);
+ writer.writeAttribute("title", subproject.title);
+
+ 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.
+ QSet<QString> visited;
+ bool contentsFound = false;
+ foreach (const Node *node, subproject.nodes) {
+ QString nextTitle = node->links().value(Node::NextLink).first;
+ if (!nextTitle.isEmpty() &&
+ node->links().value(Node::ContentsLink).first.isEmpty()) {
+
+ const Node *nextPage = qdb_->findNodeForTarget(nextTitle, 0);
+
+ // Write the contents node.
+ writeNode(project, writer, node);
+ contentsFound = true;
+
+ while (nextPage) {
+ writeNode(project, writer, nextPage);
+ nextTitle = nextPage->links().value(Node::NextLink).first;
+ if (nextTitle.isEmpty() || visited.contains(nextTitle))
+ break;
+ nextPage = qdb_->findNodeForTarget(nextTitle, 0);
+ visited.insert(nextTitle);
+ }
+ break;
+ }
+ }
+ // No contents/nextpage links found, write all nodes unsorted
+ if (!contentsFound) {
+ foreach (const Node *node, subproject.nodes)
+ writeNode(project, writer, node);
+ }
+ }
+
+ writer.writeEndElement(); // section
+ }
+ }
+
+ // Restore original search order
+ qdb_->setSearchOrder(searchOrder);
+
+ 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", details[2]);
+ writer.writeEndElement(); //keyword
+ }
+ writer.writeEndElement(); // keywords
+
+ writer.writeStartElement("files");
+
+ // The list of files to write is the union of generated files and
+ // other files (images and extras) included in the project
+ QSet<QString> files = QSet<QString>::fromList(gen_->outputFileNames());
+ files.unite(project.files);
+ files.unite(project.extraFiles);
+ foreach (const QString &usedFile, files) {
+ if (!usedFile.isEmpty())
+ writer.writeTextElement("file", usedFile);
+ }
+ writer.writeEndElement(); // files
+
+ writer.writeEndElement(); // filterSection
+ writer.writeEndElement(); // QtHelpProject
+ writer.writeEndDocument();
+ writeHashFile(file);
+ file.close();
+}
+
+QT_END_NAMESPACE
diff --git a/src/qdoc/helpprojectwriter.h b/src/qdoc/helpprojectwriter.h
new file mode 100644
index 000000000..e4cd1404c
--- /dev/null
+++ b/src/qdoc/helpprojectwriter.h
@@ -0,0 +1,118 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL21$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** 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 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** As a special exception, The Qt Company gives you certain additional
+** rights. These rights are described in The Qt Company LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef HELPPROJECTWRITER_H
+#define HELPPROJECTWRITER_H
+
+#include <qstring.h>
+#include <qxmlstream.h>
+
+#include "config.h"
+#include "node.h"
+
+QT_BEGIN_NAMESPACE
+
+class QDocDatabase;
+class Generator;
+typedef QPair<QString, const Node*> QStringNodePair;
+
+struct SubProject
+{
+ QString title;
+ QString indexTitle;
+ QHash<Node::NodeType, QSet<DocumentNode::DocSubtype> > selectors;
+ bool sortPages;
+ QString type;
+ QHash<QString, const Node *> nodes;
+ QStringList groups;
+};
+
+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;
+ QList<SubProject> subprojects;
+ QHash<const Node *, QSet<Node::Status> > memberStatus;
+ bool includeIndexNodes;
+};
+
+class HelpProjectWriter
+{
+ Q_DECLARE_TR_FUNCTIONS(QDoc::HelpProjectWriter)
+
+public:
+ HelpProjectWriter(const Config &config,
+ const QString &defaultFileName,
+ Generator* g);
+ void reset(const Config &config,
+ const QString &defaultFileName,
+ Generator* g);
+ void addExtraFile(const QString &file);
+ void addExtraFiles(const QSet<QString> &files);
+ void generate();
+
+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 writeHashFile(QFile &file);
+ void writeNode(HelpProject &project, QXmlStreamWriter &writer, const Node *node);
+ void readSelectors(SubProject &subproject, const QStringList &selectors);
+ void addMembers(HelpProject &project, QXmlStreamWriter &writer,
+ const Node *node);
+ void writeSection(QXmlStreamWriter &writer, const QString &path,
+ const QString &value);
+
+ QDocDatabase* qdb_;
+ Generator* gen_;
+
+ QString outputDir;
+ QList<HelpProject> projects;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/qdoc/htmlgenerator.cpp b/src/qdoc/htmlgenerator.cpp
new file mode 100644
index 000000000..3c03a0801
--- /dev/null
+++ b/src/qdoc/htmlgenerator.cpp
@@ -0,0 +1,4898 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL21$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** 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 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** As a special exception, The Qt Company gives you certain additional
+** rights. These rights are described in The Qt Company LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*
+ htmlgenerator.cpp
+*/
+
+#include "codemarker.h"
+#include "codeparser.h"
+#include "helpprojectwriter.h"
+#include "htmlgenerator.h"
+#include "node.h"
+#include "qdocdatabase.h"
+#include "separator.h"
+#include "tree.h"
+#include <ctype.h>
+#include <qdebug.h>
+#include <qlist.h>
+#include <qiterator.h>
+#include <qtextcodec.h>
+#include <quuid.h>
+#include <qmap.h>
+
+QT_BEGIN_NAMESPACE
+
+#define COMMAND_VERSION Doc::alias("version")
+int HtmlGenerator::id = 0;
+bool HtmlGenerator::debugging_on = false;
+
+QString HtmlGenerator::divNavTop;
+
+static bool showBrokenLinks = false;
+
+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|number|op|type|name|keyword)>");
+static QRegExp unknownTag("</?@[^>]*>");
+
+static void addLink(const QString &linkTarget,
+ const QStringRef &nestedStuff,
+ QString *res)
+{
+ if (!linkTarget.isEmpty()) {
+ *res += QLatin1String("<a href=\"");
+ *res += linkTarget;
+ *res += QLatin1String("\">");
+ *res += nestedStuff;
+ *res += QLatin1String("</a>");
+ }
+ else {
+ *res += nestedStuff;
+ }
+}
+
+/*!
+ Constructs the HTML output generator.
+ */
+HtmlGenerator::HtmlGenerator()
+ : codeIndent(0),
+ helpProjectWriter(0),
+ inObsoleteLink(false),
+ funcLeftParen("\\S(\\()"),
+ obsoleteLinks(false)
+{
+}
+
+/*!
+ Destroys the HTML output generator. Deletes the singleton
+ instance of HelpProjectWriter.
+ */
+HtmlGenerator::~HtmlGenerator()
+{
+ if (helpProjectWriter) {
+ delete helpProjectWriter;
+ helpProjectWriter = 0;
+ }
+}
+
+/*!
+ Initializes the HTML output generator's data structures
+ from the configuration class \a config.
+ */
+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, "<code>", "</code>" }, // <tt> tag is not supported in HTML5
+ { ATOM_FORMATTING_UICONTROL, "<b>", "</b>" },
+ { ATOM_FORMATTING_UNDERLINE, "<u>", "</u>" },
+ { 0, 0, 0 }
+ };
+
+ Generator::initializeGenerator(config);
+ obsoleteLinks = config.getBool(CONFIG_OBSOLETELINKS);
+ setImageFileExtensions(QStringList() << "png" << "jpg" << "jpeg" << "gif");
+
+ /*
+ The formatting maps are owned by Generator. They are cleared in
+ Generator::terminate().
+ */
+ int i = 0;
+ while (defaults[i].key) {
+ formattingLeftMap().insert(QLatin1String(defaults[i].key), QLatin1String(defaults[i].left));
+ formattingRightMap().insert(QLatin1String(defaults[i].key), QLatin1String(defaults[i].right));
+ i++;
+ }
+
+ style = config.getString(HtmlGenerator::format() +
+ Config::dot +
+ CONFIG_STYLE);
+ endHeader = config.getString(HtmlGenerator::format() +
+ Config::dot +
+ CONFIG_ENDHEADER);
+ postHeader = config.getString(HtmlGenerator::format() +
+ Config::dot +
+ HTMLGENERATOR_POSTHEADER);
+ postPostHeader = config.getString(HtmlGenerator::format() +
+ Config::dot +
+ HTMLGENERATOR_POSTPOSTHEADER);
+ prologue = config.getString(HtmlGenerator::format() +
+ Config::dot +
+ HTMLGENERATOR_PROLOGUE);
+
+ 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);
+ noNavigationBar = config.getBool(HtmlGenerator::format() +
+ Config::dot +
+ HTMLGENERATOR_NONAVIGATIONBAR);
+ tocDepth = config.getInt(HtmlGenerator::format() +
+ Config::dot +
+ HTMLGENERATOR_TOCDEPTH);
+
+ project = config.getString(CONFIG_PROJECT);
+
+ projectDescription = config.getString(CONFIG_DESCRIPTION);
+ if (projectDescription.isEmpty() && !project.isEmpty())
+ projectDescription = project + QLatin1String(" Reference Documentation");
+
+ projectUrl = config.getString(CONFIG_URL);
+ tagFile_ = config.getString(CONFIG_TAGFILE);
+
+#ifndef QT_NO_TEXTCODEC
+ outputEncoding = config.getString(CONFIG_OUTPUTENCODING);
+ if (outputEncoding.isEmpty())
+ outputEncoding = QLatin1String("UTF-8");
+ outputCodec = QTextCodec::codecForName(outputEncoding.toLocal8Bit());
+#endif
+
+ naturalLanguage = config.getString(CONFIG_NATURALLANGUAGE);
+ if (naturalLanguage.isEmpty())
+ naturalLanguage = QLatin1String("en");
+
+ QSet<QString> editionNames = config.subVars(CONFIG_EDITION);
+ QSet<QString>::ConstIterator edition = editionNames.constBegin();
+ while (edition != editionNames.constEnd()) {
+ 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;
+ }
+
+ codeIndent = config.getInt(CONFIG_CODEINDENT); // QTBUG-27798
+ codePrefix = config.getString(CONFIG_CODEPREFIX);
+ codeSuffix = config.getString(CONFIG_CODESUFFIX);
+
+ /*
+ The help file write should be allocated once and only once
+ per qdoc execution.
+ */
+ if (helpProjectWriter)
+ helpProjectWriter->reset(config, project.toLower() + ".qhp", this);
+ else
+ helpProjectWriter = new HelpProjectWriter(config, project.toLower() + ".qhp", this);
+
+ // Documentation template handling
+ headerScripts = config.getString(HtmlGenerator::format() + Config::dot + CONFIG_HEADERSCRIPTS);
+ headerStyles = config.getString(HtmlGenerator::format() + Config::dot + CONFIG_HEADERSTYLES);
+
+ QString prefix = CONFIG_QHP + Config::dot + project + Config::dot;
+ manifestDir = QLatin1String("qthelp://") + config.getString(prefix + QLatin1String("namespace"));
+ manifestDir += QLatin1Char('/') + config.getString(prefix + QLatin1String("virtualFolder")) + QLatin1Char('/');
+ readManifestMetaContent(config);
+ examplesPath = config.getString(CONFIG_EXAMPLESINSTALLPATH);
+ if (!examplesPath.isEmpty())
+ examplesPath += QLatin1Char('/');
+
+ //retrieve the config for the navigation bar
+ homepage = config.getString(CONFIG_NAVIGATION
+ + Config::dot
+ + CONFIG_HOMEPAGE);
+
+ landingpage = config.getString(CONFIG_NAVIGATION
+ + Config::dot
+ + CONFIG_LANDINGPAGE);
+
+ cppclassespage = config.getString(CONFIG_NAVIGATION
+ + Config::dot
+ + CONFIG_CPPCLASSESPAGE);
+
+ qmltypespage = config.getString(CONFIG_NAVIGATION
+ + Config::dot
+ + CONFIG_QMLTYPESPAGE);
+
+ buildversion = config.getString(CONFIG_BUILDVERSION);
+}
+
+/*!
+ Gracefully terminates the HTML output generator.
+ */
+void HtmlGenerator::terminateGenerator()
+{
+ Generator::terminateGenerator();
+}
+
+QString HtmlGenerator::format()
+{
+ return "HTML";
+}
+
+/*!
+ Generate targets for any \keyword commands that were seen
+ in the qdoc comment for the \a node.
+ */
+void HtmlGenerator::generateKeywordAnchors(const Node* node)
+{
+ Q_UNUSED(node);
+ // Disabled: keywords always link to the top of the QDoc
+ // comment they appear in, and do not use a dedicated anchor.
+#if 0
+ if (!node->doc().isEmpty()) {
+ const QList<Atom*>& keywords = node->doc().keywords();
+ foreach (Atom* a, keywords) {
+ out() << QLatin1String("<a name=\"") << Doc::canonicalTitle(a->string()) << QLatin1String("\"></a>");
+ }
+ }
+#endif
+}
+
+/*!
+ If qdoc is in the \c {-prepare} phase, traverse the primary
+ tree to generate the index file for the current module.
+
+ If qdoc is in the \c {-generate} phase, traverse the primary
+ tree to generate all the HTML documentation for the current
+ module. Then generate the help file and the tag file.
+ */
+void HtmlGenerator::generateDocs()
+{
+ Node* qflags = qdb_->findClassNode(QStringList("QFlags"));
+ if (qflags)
+ qflagsHref_ = linkForNode(qflags,0);
+ if (!preparing())
+ Generator::generateDocs();
+ if (Generator::generating() && Generator::writeQaPages())
+ generateQAPage();
+
+ if (!generating()) {
+ QString fileBase = project.toLower().simplified().replace(QLatin1Char(' '), QLatin1Char('-'));
+ qdb_->generateIndex(outputDir() + QLatin1Char('/') + fileBase + ".index",
+ projectUrl,
+ projectDescription,
+ this,
+ true);
+ }
+
+ if (!preparing()) {
+ helpProjectWriter->generate();
+ generateManifestFiles();
+ /*
+ Generate the XML tag file, if it was requested.
+ */
+ qdb_->generateTagFile(tagFile_, this);
+ }
+}
+
+/*!
+ Output the module's Quality Assurance page.
+ */
+void HtmlGenerator::generateQAPage()
+{
+ NamespaceNode* node = qdb_->primaryTreeRoot();
+ beginSubPage(node, "aaa-" + defaultModuleName().toLower() + "-qa-page.html");
+ CodeMarker* marker = CodeMarker::markerForFileName(node->location().filePath());
+ QString title = "Quality Assurance Page for " + defaultModuleName();
+ QString t = "Quality assurance information for checking the " + defaultModuleName() + " documentation.";
+ generateHeader(title, node, marker);
+ generateTitle(title, Text() << t, LargeSubTitle, node, marker);
+
+ QStringList strings;
+ QVector<int> counts;
+ QString depends = qdb_->getLinkCounts(strings, counts);
+ if (!strings.isEmpty()) {
+ t = "Intermodule Link Counts";
+ QString ref = registerRef(t);
+ out() << "<a name=\"" << ref << "\"></a>" << divNavTop << '\n';
+ out() << "<h2 id=\"" << ref << "\">" << protectEnc(t) << "</h2>\n";
+ out() << "<table class=\"valuelist\"><tr valign=\"top\" "
+ << "class=\"even\"><th class=\"tblConst\">Destination Module</th>"
+ << "<th class=\"tblval\">Link Count</th></tr>\n";
+ QString fileName;
+ for (int i = 0; i< strings.size(); ++i) {
+ fileName = generateLinksToLinksPage(strings.at(i), marker);
+ out() << "<tr><td class=\"topAlign\"><tt>"
+ << "<a href=\"" << fileName << "\">"
+ << strings.at(i) << "</a>"
+ << "</tt></td><td class=\"topAlign\"><tt>" << counts.at(i)
+ << "</tt></td></tr>\n";
+ }
+ int count = 0;
+ fileName = generateLinksToBrokenLinksPage(marker, count);
+ if (count != 0) {
+ out() << "<tr><td class=\"topAlign\"><tt>"
+ << "<a href=\"" << fileName << "\">"
+ << "Broken Links" << "</a>"
+ << "</tt></td><td class=\"topAlign\"><tt>" << count
+ << "</tt></td></tr>\n";
+
+ }
+
+ out() << "</table>\n";
+ t = "The Optimal \"depends\" Variable";
+ out() << "<h2>" << protectEnc(t) << "</h2>\n";
+ t = "Consider replacing the depends variable in " + defaultModuleName().toLower() +
+ ".qdocconf with this one, if the two are not identical:";
+ out() << "<p>" << protectEnc(t) << "</p>\n";
+ out() << "<p>" << protectEnc(depends) << "</p>\n";
+ }
+ generateFooter();
+ endSubPage();
+}
+
+/*!
+ This function writes an html file containing a list of
+ links to links that originate in the current module and
+ go to targets in the specified \a module. The \a marker
+ is used for the same thing the marker is always used for.
+ */
+QString HtmlGenerator::generateLinksToLinksPage(const QString& module, CodeMarker* marker)
+{
+ NamespaceNode* node = qdb_->primaryTreeRoot();
+ QString fileName = "aaa-links-to-" + module + ".html";
+ beginSubPage(node, fileName);
+ QString title = "Links from " + defaultModuleName() + " to " + module;
+ generateHeader(title, node, marker);
+ generateTitle(title, Text(), SmallSubTitle, node, marker);
+ out() << "<p>This is a list of links from " << defaultModuleName()
+ << " to " << module << ". ";
+ out() << "Click on a link to go to the location of the link. The link is marked ";
+ out() << "with red asterisks. ";
+ out() << "Click on the marked link to see if it goes to the right place.</p>\n";
+ TargetList* tlist = qdb_->getTargetList(module);
+ if (tlist) {
+ out() << "<table class=\"valuelist\"><tr valign=\"top\" class=\"odd\"><th class=\"tblConst\">Link to link...</th><th class=\"tblval\">In file...</th><th class=\"tbldscr\">Somewhere after line number...</th></tr>\n";
+ foreach (TargetLoc* t, *tlist) {
+ // e.g.: <a name="link-8421"></a><a href="layout.html">Layout Management</a>
+ out() << "<tr><td class=\"topAlign\">";
+ out() << "<a href=\"" << t->fileName_ << "#" << t->target_ << "\">";
+ out() << t->text_ << "</a></td>";
+ out() << "<td class=\"topAlign\">";
+ QString f = t->loc_->doc().location().filePath();
+ out() << f << "</td>";
+ out() << "<td class=\"topAlign\">";
+ out() << t->loc_->doc().location().lineNo() << "</td></tr>\n";
+ }
+ out() << "</table>\n";
+ }
+ generateFooter();
+ endSubPage();
+ return fileName;
+}
+
+/*!
+ This function writes an html file containing a list of
+ links to broken links that originate in the current
+ module and go nowwhere. It returns the name of the file
+ it generates, and it sets \a count to the number of
+ broken links that were found. The \a marker is used for
+ the same thing the marker is always used for.
+ */
+QString HtmlGenerator::generateLinksToBrokenLinksPage(CodeMarker* marker, int& count)
+{
+ QString fileName;
+ NamespaceNode* node = qdb_->primaryTreeRoot();
+ TargetList* tlist = qdb_->getTargetList("broken");
+ if (tlist && !tlist->isEmpty()) {
+ count = tlist->size();
+ fileName = "aaa-links-to-broken-links.html";
+ beginSubPage(node, fileName);
+ QString title = "Broken links in " + defaultModuleName();
+ generateHeader(title, node, marker);
+ generateTitle(title, Text(), SmallSubTitle, node, marker);
+ out() << "<p>This is a list of broken links in " << defaultModuleName() << ". ";
+ out() << "Click on a link to go to the broken link. ";
+ out() << "The link's target could not be found.</p>\n";
+ out() << "<table class=\"valuelist\"><tr valign=\"top\" class=\"odd\"><th class=\"tblConst\">Link to broken link...</th><th class=\"tblval\">In file...</th><th class=\"tbldscr\">Somewhere after line number...</th></tr>\n";
+ foreach (TargetLoc* t, *tlist) {
+ // e.g.: <a name="link-8421"></a><a href="layout.html">Layout Management</a>
+ out() << "<tr><td class=\"topAlign\">";
+ out() << "<a href=\"" << t->fileName_ << "#" << t->target_ << "\">";
+ out() << t->text_ << "</a></td>";
+ out() << "<td class=\"topAlign\">";
+ QString f = t->loc_->doc().location().filePath();
+ out() << f << "</td>";
+ out() << "<td class=\"topAlign\">";
+ out() << t->loc_->doc().location().lineNo() << "</td></tr>\n";
+ }
+ out() << "</table>\n";
+ generateFooter();
+ endSubPage();
+ }
+ return fileName;
+}
+
+/*!
+ Generate html from an instance of Atom.
+ */
+int HtmlGenerator::generateAtom(const Atom *atom, const Node *relative, CodeMarker *marker)
+{
+ int idx, skipAhead = 0;
+ static bool in_para = false;
+
+ switch (atom->type()) {
+ case Atom::AutoLink:
+ case Atom::NavAutoLink:
+ if (!inLink_ && !inContents_ && !inSectionHeading_) {
+ const Node *node = 0;
+ QString link = getAutoLink(atom, relative, &node);
+ if (link.isEmpty()) {
+ if (autolinkErrors())
+ relative->doc().location().warning(tr("Can't autolink to '%1'").arg(atom->string()));
+ }
+ else if (node && node->status() == Node::Obsolete) {
+ if ((relative->parent() != node) && !relative->isObsolete())
+ link.clear();
+ }
+ if (link.isEmpty()) {
+ out() << protectEnc(atom->string());
+ }
+ else {
+ if (Generator::writeQaPages() && node && (atom->type() != Atom::NavAutoLink)) {
+ QString text = atom->string();
+ QString target = qdb_->getNewLinkTarget(relative, node, outFileName(), text);
+ out() << "<a id=\"" << Doc::canonicalTitle(target) << "\" class=\"qa-mark\"></a>";
+ }
+ beginLink(link, node, relative);
+ generateLink(atom, marker);
+ endLink();
+ }
+ }
+ else {
+ out() << protectEnc(atom->string());
+ }
+ break;
+ case Atom::BaseName:
+ break;
+ case Atom::BriefLeft:
+ // Do not output the brief for QML nodes, doc nodes or collections
+ // (groups, modules, qml module nodes)
+ if (relative->isQmlType() ||
+ relative->isDocumentNode() ||
+ relative->isCollectionNode() ||
+ relative->isJsType()) {
+ 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.endsWith(QLatin1Char('.')))
+ str.truncate(str.length() - 1);
+ out() << "This ";
+ if (relative->type() == Node::Property)
+ out() << "property";
+ else
+ out() << "variable";
+ QStringList words = str.split(QLatin1Char(' '));
+ if (!(words.first() == QLatin1String("contains") || words.first() == QLatin1String("specifies")
+ || words.first() == QLatin1String("describes") || words.first() == QLatin1String("defines")
+ || words.first() == QLatin1String("holds") || words.first() == QLatin1String("determines")))
+ out() << " holds ";
+ else
+ out() << ' ';
+ out() << str << '.';
+ }
+ break;
+ case Atom::BriefRight:
+ if (!relative->isDocumentNode())
+ out() << "</p>\n";
+ break;
+ case Atom::C:
+ // This may at one time have been used to mark up C++ code but it is
+ // now widely used to write teletype text. As a result, text marked
+ // with the \c command is not passed to a code marker.
+ out() << formattingLeftMap()[ATOM_FORMATTING_TELETYPE];
+ if (inLink_) {
+ out() << protectEnc(plainCode(atom->string()));
+ }
+ else {
+ out() << protectEnc(plainCode(atom->string()));
+ }
+ out() << formattingRightMap()[ATOM_FORMATTING_TELETYPE];
+ break;
+ case Atom::CaptionLeft:
+ out() << "<p class=\"figCaption\">";
+ in_para = true;
+ break;
+ case Atom::CaptionRight:
+ endLink();
+ if (in_para) {
+ out() << "</p>\n";
+ in_para = false;
+ }
+ break;
+ case Atom::Code:
+ out() << "<pre class=\"cpp\">"
+ << trimmedTrailing(highlightedCode(indent(codeIndent,atom->string()),relative), codePrefix, codeSuffix)
+ << "</pre>\n";
+ break;
+ case Atom::Qml:
+ out() << "<pre class=\"qml\">"
+ << trimmedTrailing(highlightedCode(indent(codeIndent,atom->string()),relative), codePrefix, codeSuffix)
+ << "</pre>\n";
+ break;
+ case Atom::JavaScript:
+ out() << "<pre class=\"js\">"
+ << trimmedTrailing(highlightedCode(indent(codeIndent,atom->string()),relative), codePrefix, codeSuffix)
+ << "</pre>\n";
+ break;
+ case Atom::CodeNew:
+ out() << "<p>you can rewrite it as</p>\n"
+ << "<pre class=\"cpp\">"
+ << trimmedTrailing(highlightedCode(indent(codeIndent,atom->string()),relative), codePrefix, codeSuffix)
+ << "</pre>\n";
+ break;
+ case Atom::CodeOld:
+ out() << "<p>For example, if you have code like</p>\n";
+ // fallthrough
+ case Atom::CodeBad:
+ out() << "<pre class=\"cpp\">"
+ << trimmedTrailing(protectEnc(plainCode(indent(codeIndent,atom->string()))), codePrefix, codeSuffix)
+ << "</pre>\n";
+ break;
+ case Atom::DivLeft:
+ out() << "<div";
+ if (!atom->string().isEmpty())
+ out() << ' ' << atom->string();
+ out() << '>';
+ break;
+ case Atom::DivRight:
+ out() << "</div>";
+ 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:
+ if (atom->string().startsWith("span ")) {
+ out() << '<' + atom->string() << '>';
+ }
+ else
+ 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 if (atom->string().startsWith("span ")) {
+ out() << "</span>";
+ }
+ else {
+ out() << formattingRightMap()[atom->string()];
+ }
+ break;
+ case Atom::AnnotatedList:
+ {
+ const CollectionNode* cn = qdb_->getCollectionNode(atom->string(), Node::DOC);
+ if (cn)
+ generateList(cn, marker, atom->string());
+ }
+ break;
+ case Atom::GeneratedList:
+ if (atom->string() == QLatin1String("annotatedclasses")) {
+ generateAnnotatedList(relative, marker, qdb_->getCppClasses());
+ }
+ else if (atom->string() == QLatin1String("classes")) {
+ generateCompactList(Generic, relative, qdb_->getCppClasses(), true, QStringLiteral(""));
+ }
+ else if (atom->string().contains("classes ")) {
+ QString rootName = atom->string().mid(atom->string().indexOf("classes") + 7).trimmed();
+ generateCompactList(Generic, relative, qdb_->getCppClasses(), true, rootName);
+ }
+ else if (atom->string() == QLatin1String("qmlbasictypes")) {
+ generateCompactList(Generic, relative, qdb_->getQmlBasicTypes(), true, QStringLiteral(""));
+ }
+ else if (atom->string() == QLatin1String("qmltypes")) {
+ generateCompactList(Generic, relative, qdb_->getQmlTypes(), true, QStringLiteral(""));
+ }
+ else if ((idx = atom->string().indexOf(QStringLiteral("bymodule"))) != -1) {
+ QString moduleName = atom->string().mid(idx + 8).trimmed();
+ Node::Genus genus = Node::CPP;
+ if (atom->string().startsWith(QLatin1String("qml")))
+ genus = Node::QML;
+ else if (atom->string().startsWith(QLatin1String("js")))
+ genus = Node::JS;
+ QDocDatabase* qdb = QDocDatabase::qdocDB();
+ const CollectionNode* cn = qdb->getCollectionNode(moduleName, genus);
+ if (cn) {
+ if (genus == Node::CPP) {
+ NodeMap m;
+ cn->getMemberClasses(m);
+ if (!m.isEmpty()) {
+ generateAnnotatedList(relative, marker, m);
+ }
+ }
+ else
+ generateAnnotatedList(relative, marker, cn->members());
+ }
+ }
+ else if (atom->string() == QLatin1String("classhierarchy")) {
+ generateClassHierarchy(relative, qdb_->getCppClasses());
+ }
+ else if (atom->string() == QLatin1String("obsoleteclasses")) {
+ generateCompactList(Generic, relative, qdb_->getObsoleteClasses(), false, QStringLiteral("Q"));
+ }
+ else if (atom->string() == QLatin1String("obsoleteqmltypes")) {
+ generateCompactList(Generic, relative, qdb_->getObsoleteQmlTypes(), false, QStringLiteral(""));
+ }
+ else if (atom->string() == QLatin1String("obsoletecppmembers")) {
+ generateCompactList(Obsolete, relative, qdb_->getClassesWithObsoleteMembers(), false, QStringLiteral("Q"));
+ }
+ else if (atom->string() == QLatin1String("obsoleteqmlmembers")) {
+ generateCompactList(Obsolete, relative, qdb_->getQmlTypesWithObsoleteMembers(), false, QStringLiteral(""));
+ }
+ else if (atom->string() == QLatin1String("functionindex")) {
+ generateFunctionIndex(relative);
+ }
+ else if (atom->string() == QLatin1String("legalese")) {
+ generateLegaleseList(relative, marker);
+ }
+ else if (atom->string() == QLatin1String("overviews")) {
+ generateList(relative, marker, "overviews");
+ }
+ else if (atom->string() == QLatin1String("cpp-modules")) {
+ generateList(relative, marker, "cpp-modules");
+ }
+ else if (atom->string() == QLatin1String("qml-modules")) {
+ generateList(relative, marker, "qml-modules");
+ }
+ else if (atom->string() == QLatin1String("namespaces")) {
+ generateAnnotatedList(relative, marker, qdb_->getNamespaces());
+ }
+ else if (atom->string() == QLatin1String("related")) {
+ generateList(relative, marker, "related");
+ }
+#if 0
+ /*
+ This is not used in Qt5, as of 10/02/2014
+ Remove permanently if it is not missed.
+ */
+ else if (atom->string() == QLatin1String("relatedinline")) {
+ const DocumentNode *dn = static_cast<const DocumentNode *>(relative);
+ if (dn && !dn->members().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, dn->members())
+ list.prepend(node);
+ foreach (const Node *node, list)
+ generateBody(node, marker);
+ }
+ }
+#endif
+ break;
+ case Atom::SinceList:
+ {
+ const NodeMultiMap& nsmap = qdb_->getSinceMap(atom->string());
+ const NodeMap& ncmap = qdb_->getClassMap(atom->string());
+ const NodeMap& nqcmap = qdb_->getQmlTypeMap(atom->string());
+
+ if (!nsmap.isEmpty()) {
+ QList<Section> sections;
+ QList<Section>::ConstIterator s;
+
+ for (int i=0; i<LastSinceType; ++i)
+ sections.append(Section(sinceTitle(i),QString(),QString(),QString()));
+
+ NodeMultiMap::const_iterator n = nsmap.constBegin();
+ while (n != nsmap.constEnd()) {
+ Node* node = n.value();
+ switch (node->type()) {
+ case Node::QmlType:
+ sections[QmlClass].appendMember(node);
+ break;
+ case Node::Namespace:
+ sections[Namespace].appendMember(node);
+ break;
+ case Node::Class:
+ sections[Class].appendMember(node);
+ break;
+ case Node::Enum:
+ sections[Enum].appendMember(node);
+ break;
+ case Node::Typedef:
+ sections[Typedef].appendMember(node);
+ break;
+ case Node::Function: {
+ const FunctionNode* fn = static_cast<const FunctionNode*>(node);
+ if (fn->isMacro())
+ sections[Macro].appendMember(node);
+ else {
+ Node* p = fn->parent();
+ if (p) {
+ if (p->type() == Node::Class)
+ sections[MemberFunction].appendMember(node);
+ else if (p->type() == Node::Namespace) {
+ if (p->name().isEmpty())
+ sections[GlobalFunction].appendMember(node);
+ else
+ sections[NamespaceFunction].appendMember(node);
+ }
+ else
+ sections[GlobalFunction].appendMember(node);
+ }
+ else
+ sections[GlobalFunction].appendMember(node);
+ }
+ break;
+ }
+ case Node::Property:
+ sections[Property].appendMember(node);
+ break;
+ case Node::Variable:
+ sections[Variable].appendMember(node);
+ break;
+ case Node::QmlProperty:
+ sections[QmlProperty].appendMember(node);
+ break;
+ case Node::QmlSignal:
+ sections[QmlSignal].appendMember(node);
+ break;
+ case Node::QmlSignalHandler:
+ sections[QmlSignalHandler].appendMember(node);
+ break;
+ case Node::QmlMethod:
+ sections[QmlMethod].appendMember(node);
+ break;
+ default:
+ break;
+ }
+ ++n;
+ }
+
+ out() << "<ul>\n";
+ s = sections.constBegin();
+ while (s != sections.constEnd()) {
+ if (!(*s).members.isEmpty()) {
+
+ out() << "<li>"
+ << "<a href=\"#"
+ << Doc::canonicalTitle((*s).name)
+ << "\">"
+ << (*s).name
+ << "</a></li>\n";
+ }
+ ++s;
+ }
+ out() << "</ul>\n";
+
+ int idx = 0;
+ s = sections.constBegin();
+ while (s != sections.constEnd()) {
+ if (!(*s).members.isEmpty()) {
+ out() << "<a name=\""
+ << Doc::canonicalTitle((*s).name)
+ << "\"></a>\n";
+ out() << "<h3>" << protectEnc((*s).name) << "</h3>\n";
+ if (idx == Class)
+ generateCompactList(Generic, 0, ncmap, false, QStringLiteral("Q"));
+ else if (idx == QmlClass)
+ generateCompactList(Generic, 0, nqcmap, false, QStringLiteral(""));
+ else if (idx == MemberFunction) {
+ ParentMaps parentmaps;
+ ParentMaps::iterator pmap;
+ NodeList::const_iterator i = s->members.constBegin();
+ while (i != s->members.constEnd()) {
+ Node* p = (*i)->parent();
+ pmap = parentmaps.find(p);
+ if (pmap == parentmaps.end())
+ pmap = parentmaps.insert(p,NodeMultiMap());
+ pmap->insert((*i)->name(),(*i));
+ ++i;
+ }
+ pmap = parentmaps.begin();
+ while (pmap != parentmaps.end()) {
+ NodeList nlist = pmap->values();
+ out() << "<p>Class ";
+
+ out() << "<a href=\""
+ << linkForNode(pmap.key(), 0)
+ << "\">";
+ QStringList pieces = pmap.key()->fullName().split("::");
+ out() << protectEnc(pieces.last());
+ out() << "</a>" << ":</p>\n";
+
+ generateSection(nlist, 0, marker, CodeMarker::Summary);
+ out() << "<br/>";
+ ++pmap;
+ }
+ }
+ else
+ generateSection(s->members, 0, marker, CodeMarker::Summary);
+ }
+ ++idx;
+ ++s;
+ }
+ }
+ }
+ break;
+ case Atom::BR:
+ out() << "<br />\n";
+ break;
+ case Atom::HR:
+ out() << "<hr />\n";
+ 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 class=\"centerAlign\">";
+ if (fileName.isEmpty()) {
+ relative->location().warning(tr("Missing image: %1").arg(protectEnc(atom->string())));
+ out() << "<font color=\"red\">[Missing image "
+ << protectEnc(atom->string()) << "]</font>";
+ }
+ else {
+ QString prefix;
+ out() << "<img src=\"" << protectEnc(prefix + fileName) << '"';
+ if (!text.isEmpty())
+ out() << " alt=\"" << protectEnc(text) << '"';
+ else
+ out() << " alt=\"\"";
+ out() << " />";
+ helpProjectWriter->addExtraFile(fileName);
+ if (relative->isExample()) {
+ const ExampleNode* cen = static_cast<const ExampleNode*>(relative);
+ if (cen->imageFileName().isEmpty()) {
+ ExampleNode* en = const_cast<ExampleNode*>(cen);
+ en->setImageFileName(fileName);
+ }
+ }
+ }
+ if (atom->type() == Atom::Image)
+ out() << "</p>";
+ }
+ break;
+ case Atom::ImageText:
+ break;
+ case Atom::ImportantLeft:
+ out() << "<p>";
+ out() << formattingLeftMap()[ATOM_FORMATTING_BOLD];
+ out() << "Important: ";
+ out() << formattingRightMap()[ATOM_FORMATTING_BOLD];
+ break;
+ case Atom::ImportantRight:
+ out() << "</p>";
+ break;
+ case Atom::NoteLeft:
+ out() << "<p>";
+ out() << formattingLeftMap()[ATOM_FORMATTING_BOLD];
+ out() << "Note: ";
+ out() << formattingRightMap()[ATOM_FORMATTING_BOLD];
+ break;
+ case Atom::NoteRight:
+ out() << "</p>";
+ break;
+ case Atom::LegaleseLeft:
+ out() << "<div class=\"LegaleseLeft\">";
+ break;
+ case Atom::LegaleseRight:
+ out() << "</div>";
+ break;
+ case Atom::LineBreak:
+ out() << "<br/>";
+ break;
+ case Atom::Link:
+ case Atom::NavLink:
+ {
+ inObsoleteLink = false;
+ const Node *node = 0;
+ QString link = getLink(atom, relative, &node);
+ if (link.isEmpty() && (node != relative) && !noLinkErrors()) {
+ relative->doc().location().warning(tr("Can't link to '%1'").arg(atom->string()));
+ if (Generator::writeQaPages() && (atom->type() != Atom::NavAutoLink)) {
+ QString text = atom->next()->next()->string();
+ QString target = qdb_->getNewLinkTarget(relative, node, outFileName(), text, true);
+ out() << "<a id=\"" << Doc::canonicalTitle(target) << "\" class=\"qa-mark\"></a>";
+ }
+ }
+ else {
+ if (Generator::writeQaPages() && node && (atom->type() != Atom::NavLink)) {
+ QString text = atom->next()->next()->string();
+ QString target = qdb_->getNewLinkTarget(relative, node, outFileName(), text);
+ out() << "<a id=\"" << Doc::canonicalTitle(target) << "\" class=\"qa-mark\"></a>";
+ }
+ /*
+ mws saw this on 17/10/2014.
+ Is this correct? Setting node to 0 means the
+ following test always fails. Did we decide to
+ no longer warn about linking to obsolete things?
+ */
+ node = 0;
+ if (node && node->status() == Node::Obsolete) {
+ if ((relative->parent() != node) && !relative->isObsolete()) {
+ inObsoleteLink = true;
+ if (obsoleteLinks) {
+ relative->doc().location().warning(tr("Link to obsolete item '%1' in %2")
+ .arg(atom->string())
+ .arg(relative->plainFullName()));
+ }
+ }
+ }
+ }
+ beginLink(link, node, relative);
+ skipAhead = 1;
+ }
+ break;
+ case Atom::LinkNode:
+ {
+ const Node *node = CodeMarker::nodeForString(atom->string());
+ beginLink(linkForNode(node, relative), node, relative);
+ 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) {
+ out() << "<div class=\"table\"><table class=\"valuelist\">";
+ threeColumnEnumValueTable_ = isThreeColumnEnumValueTable(atom);
+ if (threeColumnEnumValueTable_) {
+ if (++numTableRows_ % 2 == 1)
+ out() << "<tr valign=\"top\" class=\"odd\">";
+ else
+ out() << "<tr valign=\"top\" class=\"even\">";
+
+ out() << "<th class=\"tblConst\">Constant</th>";
+
+ // If not in \enum topic, skip the value column
+ if (relative->type() == Node::Enum)
+ out() << "<th class=\"tblval\">Value</th>";
+
+ out() << "<th class=\"tbldscr\">Description</th></tr>\n";
+ }
+ else {
+ out() << "<tr><th class=\"tblConst\">Constant</th><th class=\"tblVal\">Value</th></tr>\n";
+ }
+ }
+ else {
+ out() << "<ol class=";
+ if (atom->string() == ATOM_LIST_UPPERALPHA) {
+ out() << "\"A\"";
+ } /* why type? changed to */
+ 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
+
+ QString t= protectEnc(plainCode(marker->markedUpEnumValue(atom->next()->string(),relative)));
+ out() << "<tr><td class=\"topAlign\"><code>" << t << "</code>";
+
+ if (relative->type() == Node::Enum) {
+ out() << "</td><td class=\"topAlign\">";
+ const EnumNode *enume = static_cast<const EnumNode *>(relative);
+ QString itemValue = enume->itemValue(atom->next()->string());
+
+ if (itemValue.isEmpty())
+ out() << '?';
+ else
+ out() << "<code>" << protectEnc(itemValue) << "</code>";
+ }
+ 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 class=\"topAlign\">";
+ 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></div>\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:
+ out() << "<a name=\"" << Doc::canonicalTitle(Text::sectionHeading(atom).toString())
+ << "\"></a>" << divNavTop << '\n';
+ break;
+ case Atom::SectionRight:
+ break;
+ case Atom::SectionHeadingLeft: {
+ int unit = atom->string().toInt() + hOffset(relative);
+ out() << "<h" + QString::number(unit) + QLatin1Char(' ');
+ if (unit < 3) {
+ out() << "id=\"" << Doc::canonicalTitle(Text::sectionHeading(atom).toString()) << "\"";
+ }
+ out() << '>';
+ 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, marker);
+ }
+ else {
+ out() << protectEnc(atom->string());
+ }
+ break;
+ case Atom::TableLeft:
+ {
+ QString p1, p2;
+ QString attr = "generic";
+ QString width;
+ if (in_para) {
+ out() << "</p>\n";
+ in_para = false;
+ }
+ if (atom->count() > 0) {
+ p1 = atom->string(0);
+ if (atom->count() > 1)
+ p2 = atom->string(1);
+ }
+ if (!p1.isEmpty()) {
+ if (p1 == QLatin1String("borderless"))
+ attr = p1;
+ else if (p1.contains(QLatin1Char('%')))
+ width = p1;
+ }
+ if (!p2.isEmpty()) {
+ if (p2 == QLatin1String("borderless"))
+ attr = p2;
+ else if (p2.contains(QLatin1Char('%')))
+ width = p2;
+ }
+ out() << "<div class=\"table\"><table class=\"" << attr << '"';
+ if (!width.isEmpty())
+ out() << " width=\"" << width << '"';
+ out() << ">\n ";
+ numTableRows_ = 0;
+ }
+ break;
+ case Atom::TableRight:
+ out() << "</table></div>\n";
+ break;
+ case Atom::TableHeaderLeft:
+ out() << "<thead><tr class=\"qt-style\">";
+ inTableHeader_ = true;
+ break;
+ case Atom::TableHeaderRight:
+ out() << "</tr>";
+ if (matchAhead(atom, Atom::TableHeaderLeft)) {
+ skipAhead = 1;
+ out() << "\n<tr class=\"qt-style\">";
+ }
+ else {
+ out() << "</thead>\n";
+ inTableHeader_ = false;
+ }
+ break;
+ case Atom::TableRowLeft:
+ if (!atom->string().isEmpty())
+ out() << "<tr " << atom->string() << '>';
+ else 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 ";
+
+ for (int i=0; i<atom->count(); ++i) {
+ if (i > 0)
+ out() << ' ';
+ QString p = atom->string(i);
+ if (p.contains('=')) {
+ out() << p;
+ }
+ else {
+ QStringList spans = p.split(QLatin1Char(','));
+ if (spans.size() == 2) {
+ if (spans.at(0) != "1")
+ out() << " colspan=\"" << spans.at(0) << '"';
+ if (spans.at(1) != "1")
+ out() << " rowspan=\"" << spans.at(1) << '"';
+ }
+ }
+ }
+ if (inTableHeader_)
+ out() << '>';
+ else {
+ out() << '>';
+ //out() << "><p>";
+ }
+ if (matchAhead(atom, Atom::ParaLeft))
+ skipAhead = 1;
+ }
+ break;
+ case Atom::TableItemRight:
+ if (inTableHeader_)
+ out() << "</th>";
+ else {
+ out() << "</td>";
+ //out() << "</p></td>";
+ }
+ if (matchAhead(atom, Atom::ParaLeft))
+ skipAhead = 1;
+ break;
+ case Atom::TableOfContents:
+ break;
+ case Atom::Keyword:
+ break;
+ case Atom::Target:
+ out() << "<a name=\"" << Doc::canonicalTitle(atom->string()) << "\"></a>";
+ break;
+ case Atom::UnhandledFormat:
+ out() << "<b class=\"redFont\">&lt;Missing HTML&gt;</b>";
+ break;
+ case Atom::UnknownCommand:
+ out() << "<b class=\"redFont\"><code>\\" << protectEnc(atom->string())
+ << "</code></b>";
+ break;
+ case Atom::QmlText:
+ case Atom::EndQmlText:
+ // don't do anything with these. They are just tags.
+ break;
+ default:
+ unknownAtom(atom);
+ }
+ return skipAhead;
+}
+
+/*!
+ Generate a reference page for a C++ class or a C++ namespace.
+ */
+void HtmlGenerator::generateClassLikeNode(Aggregate* inner, CodeMarker* marker)
+{
+ QList<Section> sections;
+ QList<Section>::ConstIterator s;
+
+ QString title;
+ QString rawTitle;
+ QString fullTitle;
+ if (inner->type() == Node::Namespace) {
+ rawTitle = inner->plainName();
+ fullTitle = inner->plainFullName();
+ title = rawTitle + " Namespace";
+ }
+ else if (inner->type() == Node::Class) {
+ rawTitle = inner->plainName();
+ fullTitle = inner->plainFullName();
+ title = rawTitle + " Class";
+ }
+
+ Text subtitleText;
+ if (rawTitle != fullTitle)
+ subtitleText << "(" << Atom(Atom::AutoLink, fullTitle) << ")" << Atom(Atom::LineBreak);
+
+ generateHeader(title, inner, marker);
+
+ sections = marker->sections(inner, CodeMarker::Summary, CodeMarker::Okay);
+ generateTableOfContents(inner,marker,&sections);
+ generateKeywordAnchors(inner);
+ generateTitle(title, subtitleText, SmallSubTitle, inner, marker);
+ generateBrief(inner, marker);
+ generateRequisites(inner, marker);
+ generateStatus(inner, marker);
+ generateThreadSafeness(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 << "\">"
+ << "Compatibility members</a></li>\n";
+
+ out() << "</ul>\n";
+
+ bool needOtherSection = false;
+
+ /*
+ sections is built above for the call to generateTableOfContents().
+ */
+ s = sections.constBegin();
+ while (s != sections.constEnd()) {
+ if (s->members.isEmpty() && s->reimpMembers.isEmpty()) {
+ if (!s->inherited.isEmpty())
+ needOtherSection = true;
+ }
+ else {
+ if (!s->members.isEmpty()) {
+ // out() << "<hr />\n";
+ QString ref = registerRef((*s).name.toLower());
+ out() << "<a name=\"" << ref << "\"></a>" << divNavTop << "\n";
+ out() << "<h2 id=\"" << ref << "\">" << protectEnc((*s).name) << "</h2>\n";
+ generateSection(s->members, inner, marker, CodeMarker::Summary);
+ }
+ if (!s->reimpMembers.isEmpty()) {
+ QString name = QString("Reimplemented ") + (*s).name;
+ QString ref = registerRef(name.toLower());
+ // out() << "<hr />\n";
+ out() << "<a name=\"" << ref << "\"></a>" << divNavTop << "\n";
+ out() << "<h2 id=\"" << ref << "\">" << protectEnc(name) << "</h2>\n";
+ generateSection(s->reimpMembers, inner, marker, CodeMarker::Summary);
+ }
+
+ if (!s->inherited.isEmpty()) {
+ out() << "<ul>\n";
+ generateSectionInheritedList(*s, inner);
+ out() << "</ul>\n";
+ }
+ }
+ ++s;
+ }
+
+ if (needOtherSection) {
+ out() << "<h3>Additional Inherited Members</h3>\n"
+ "<ul>\n";
+
+ s = sections.constBegin();
+ while (s != sections.constEnd()) {
+ if (s->members.isEmpty() && !s->inherited.isEmpty())
+ generateSectionInheritedList(*s, inner);
+ ++s;
+ }
+ out() << "</ul>\n";
+ }
+
+ QString detailsRef = registerRef("details");
+ out() << "<a name=\"" << detailsRef << "\"></a>" << divNavTop << '\n';
+
+ if (!inner->doc().isEmpty()) {
+ generateExtractionMark(inner, DetailedDescriptionMark);
+ //out() << "<hr />\n"
+ out() << "<div class=\"descr\">\n" // QTBUG-9504
+ << "<h2 id=\"" << detailsRef << "\">" << "Detailed Description" << "</h2>\n";
+ generateBody(inner, marker);
+ out() << "</div>\n"; // QTBUG-9504
+ generateAlsoList(inner, marker);
+ generateMaintainerList(inner, marker);
+ generateExtractionMark(inner, EndMark);
+ }
+
+ sections = marker->sections(inner, CodeMarker::Detailed, CodeMarker::Okay);
+ s = sections.constBegin();
+ while (s != sections.constEnd()) {
+ //out() << "<hr />\n";
+ if (!(*s).divClass.isEmpty())
+ out() << "<div class=\"" << (*s).divClass << "\">\n"; // QTBUG-9504
+ out() << "<h2>" << protectEnc((*s).name) << "</h2>\n";
+
+ NodeList::ConstIterator m = (*s).members.constBegin();
+ while (m != (*s).members.constEnd()) {
+ if ((*m)->access() != Node::Private) { // ### check necessary?
+ if ((*m)->type() != Node::Class)
+ generateDetailedMember(*m, inner, marker);
+ else {
+ out() << "<h3> class ";
+ generateFullName(*m, inner);
+ 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() != 0)
+ 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();
+ if (!prop->notifiers().isEmpty())
+ names << prop->notifiers().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));
+ }
+ }
+ ++m;
+ }
+ if (!(*s).divClass.isEmpty())
+ out() << "</div>\n"; // QTBUG-9504
+ ++s;
+ }
+ generateFooter(inner);
+}
+
+/*!
+ Generate the HTML page for a QML type. \qcn is the QML type.
+ \marker is the code markeup object.
+ */
+void HtmlGenerator::generateQmlTypePage(QmlTypeNode* qcn, CodeMarker* marker)
+{
+ Generator::setQmlTypeContext(qcn);
+ SubTitleSize subTitleSize = LargeSubTitle;
+ QList<Section>::const_iterator s;
+ QString htmlTitle = qcn->fullTitle();
+ if (qcn->isJsType())
+ htmlTitle += " JavaScript Type";
+ else
+ htmlTitle += " QML Type";
+
+ generateHeader(htmlTitle, qcn, marker);
+ QList<Section> sections = marker->qmlSections(qcn, CodeMarker::Summary);
+ generateTableOfContents(qcn, marker, &sections);
+ marker = CodeMarker::markerForLanguage(QLatin1String("QML"));
+ generateKeywordAnchors(qcn);
+ generateTitle(htmlTitle, Text() << qcn->subTitle(), subTitleSize, qcn, marker);
+ generateBrief(qcn, marker);
+ generateQmlRequisites(qcn, marker);
+
+ QString allQmlMembersLink = generateAllQmlMembersFile(qcn, marker);
+ QString obsoleteLink = generateQmlMemberFile(qcn, marker, CodeMarker::Obsolete);
+ if (!allQmlMembersLink.isEmpty() || !obsoleteLink.isEmpty()) {
+ out() << "<ul>\n";
+ if (!allQmlMembersLink.isEmpty()) {
+ out() << "<li><a href=\"" << allQmlMembersLink << "\">"
+ << "List of all members, including inherited members</a></li>\n";
+ }
+ if (!obsoleteLink.isEmpty()) {
+ out() << "<li><a href=\"" << obsoleteLink << "\">"
+ << "Obsolete members</a></li>\n";
+ }
+ out() << "</ul>\n";
+ }
+
+ s = sections.constBegin();
+ while (s != sections.constEnd()) {
+ QString ref = registerRef((*s).name.toLower());
+ out() << "<a name=\"" << ref
+ << "\"></a>" << divNavTop << '\n';
+ out() << "<h2 id=\"" << ref << "\">" << protectEnc((*s).name) << "</h2>\n";
+ generateQmlSummary(*s, qcn, marker);
+ ++s;
+ }
+
+ generateExtractionMark(qcn, DetailedDescriptionMark);
+ QString detailsRef = registerRef("details");
+ out() << "<a name=\"" << detailsRef << "\"></a>" << divNavTop << '\n';
+ out() << "<h2 id=\"" << detailsRef << "\">" << "Detailed Description" << "</h2>\n";
+ generateBody(qcn, marker);
+ ClassNode* cn = qcn->classNode();
+ if (cn)
+ generateQmlText(cn->doc().body(), cn, marker, qcn->name());
+ generateAlsoList(qcn, marker);
+ generateExtractionMark(qcn, EndMark);
+ //out() << "<hr />\n";
+
+ sections = marker->qmlSections(qcn,CodeMarker::Detailed);
+ s = sections.constBegin();
+ while (s != sections.constEnd()) {
+ out() << "<h2>" << protectEnc((*s).name) << "</h2>\n";
+ NodeList::ConstIterator m = (*s).members.constBegin();
+ while (m != (*s).members.constEnd()) {
+ generateDetailedQmlMember(*m, qcn, marker);
+ out() << "<br/>\n";
+ ++m;
+ }
+ ++s;
+ }
+ generateFooter(qcn);
+ Generator::setQmlTypeContext(0);
+}
+
+/*!
+ Generate the HTML page for the QML basic type represented
+ by the QML basic type node \a qbtn.
+ */
+void HtmlGenerator::generateQmlBasicTypePage(QmlBasicTypeNode* qbtn, CodeMarker* marker)
+{
+ SubTitleSize subTitleSize = LargeSubTitle;
+ QList<Section>::const_iterator s;
+ QString htmlTitle = qbtn->fullTitle();
+ if (qbtn->isJsType())
+ htmlTitle += " JavaScript Basic Type";
+ else
+ htmlTitle += " QML Basic Type";
+
+ marker = CodeMarker::markerForLanguage(QLatin1String("QML"));
+
+ generateHeader(htmlTitle, qbtn, marker);
+ QList<Section> sections = marker->sections(qbtn, CodeMarker::Summary, CodeMarker::Okay);
+ generateTableOfContents(qbtn,marker,&sections);
+ generateKeywordAnchors(qbtn);
+ generateTitle(htmlTitle,
+ Text() << qbtn->subTitle(),
+ subTitleSize,
+ qbtn,
+ marker);
+ generateExtractionMark(qbtn, DetailedDescriptionMark);
+ out() << "<div class=\"descr\"> <a name=\"" << registerRef("details") << "\"></a>\n"; // QTBUG-9504
+
+ generateBody(qbtn, marker);
+ out() << "</div>\n"; // QTBUG-9504
+ generateAlsoList(qbtn, marker);
+ generateExtractionMark(qbtn, EndMark);
+ generateFooter(qbtn);
+}
+
+/*!
+ Generate the HTML page for an entity that doesn't map
+ to any underlying parsable C++ class or QML component.
+ */
+void HtmlGenerator::generateDocumentNode(DocumentNode* dn, CodeMarker* marker)
+{
+ /*
+ If the document node is a page node, and if the page type
+ is DITA map page, write the node's contents as a dita
+ map and return without doing anything else.
+ */
+ if (dn->docSubtype() == Node::Page && dn->pageType() == Node::DitaMapPage) {
+ const DitaMapNode* dmn = static_cast<const DitaMapNode*>(dn);
+ writeDitaMap(dmn);
+ return;
+ }
+
+ SubTitleSize subTitleSize = LargeSubTitle;
+ QList<Section> sections;
+ QList<Section>::const_iterator s;
+ QString fullTitle = dn->fullTitle();
+
+ generateHeader(fullTitle, dn, marker);
+ /*
+ Generate the TOC for the new doc format.
+ Don't generate a TOC for the home page.
+ */
+ if ((dn->name() != QLatin1String("index.html")))
+ generateTableOfContents(dn,marker,0);
+
+ generateKeywordAnchors(dn);
+ generateTitle(fullTitle,
+ Text() << dn->subTitle(),
+ subTitleSize,
+ dn,
+ marker);
+
+ if (dn->docSubtype() == Node::HeaderFile) {
+ // Generate brief text and status for modules.
+ generateBrief(dn, marker);
+ generateStatus(dn, marker);
+ generateSince(dn, marker);
+
+ out() << "<ul>\n";
+
+ QString membersLink = generateListOfAllMemberFile(dn, marker);
+ if (!membersLink.isEmpty())
+ out() << "<li><a href=\"" << membersLink << "\">"
+ << "List of all members, including inherited members</a></li>\n";
+
+ QString obsoleteLink = generateLowStatusMemberFile(dn,
+ marker,
+ CodeMarker::Obsolete);
+ if (!obsoleteLink.isEmpty()) {
+ out() << "<li><a href=\"" << obsoleteLink << "\">"
+ << "Obsolete members</a></li>\n";
+ }
+
+ QString compatLink = generateLowStatusMemberFile(dn,
+ marker,
+ CodeMarker::Compat);
+ if (!compatLink.isEmpty())
+ out() << "<li><a href=\"" << compatLink << "\">"
+ << "Compatibility members</a></li>\n";
+
+ out() << "</ul>\n";
+ }
+
+ sections = marker->sections(dn, CodeMarker::Summary, CodeMarker::Okay);
+ s = sections.constBegin();
+ while (s != sections.constEnd()) {
+ QString ref = registerRef((*s).name);
+ out() << "<a name=\"" << ref << "\"></a>" << divNavTop << '\n';
+ out() << "<h2 id=\"" << ref << "\">" << protectEnc((*s).name) << "</h2>\n";
+ generateSectionList(*s, dn, marker, CodeMarker::Summary);
+ ++s;
+ }
+
+ generateExtractionMark(dn, DetailedDescriptionMark);
+ out() << "<div class=\"descr\"> <a name=\"" << registerRef("details") << "\"></a>\n"; // QTBUG-9504
+
+ generateBody(dn, marker);
+ out() << "</div>\n"; // QTBUG-9504
+ generateAlsoList(dn, marker);
+ generateExtractionMark(dn, EndMark);
+
+ sections = marker->sections(dn, CodeMarker::Detailed, CodeMarker::Okay);
+ s = sections.constBegin();
+ while (s != sections.constEnd()) {
+ //out() << "<hr />\n";
+ out() << "<h2>" << protectEnc((*s).name) << "</h2>\n";
+
+ NodeList::ConstIterator m = (*s).members.constBegin();
+ while (m != (*s).members.constEnd()) {
+ generateDetailedMember(*m, dn, marker);
+ ++m;
+ }
+ ++s;
+ }
+ generateFooter(dn);
+}
+
+/*!
+ Generate the HTML page for a group, module, or QML module.
+ */
+void HtmlGenerator::generateCollectionNode(CollectionNode* cn, CodeMarker* marker)
+{
+ SubTitleSize subTitleSize = LargeSubTitle;
+ QList<Section> sections;
+ QList<Section>::const_iterator s;
+ QString fullTitle = cn->fullTitle();
+ QString ref;
+
+ generateHeader(fullTitle, cn, marker);
+ generateTableOfContents(cn,marker,0);
+ generateKeywordAnchors(cn);
+ generateTitle(fullTitle, Text() << cn->subTitle(), subTitleSize, cn, marker);
+
+ if (cn->isModule()) {
+ // Generate brief text and status for modules.
+ generateBrief(cn, marker);
+ generateStatus(cn, marker);
+ generateSince(cn, marker);
+
+ NodeMultiMap nmm;
+ cn->getMemberNamespaces(nmm);
+ if (!nmm.isEmpty()) {
+ ref = registerRef("namespaces");
+ out() << "<a name=\"" << ref << "\"></a>" << divNavTop << '\n';
+ out() << "<h2 id=\"" << ref << "\">Namespaces</h2>\n";
+ generateAnnotatedList(cn, marker, nmm);
+ }
+ nmm.clear();
+ cn->getMemberClasses(nmm);
+ if (!nmm.isEmpty()) {
+ ref = registerRef("classes");
+ out() << "<a name=\"" << ref << "\"></a>" << divNavTop << '\n';
+ out() << "<h2 id=\"" << ref << "\">Classes</h2>\n";
+ generateAnnotatedList(cn, marker, nmm);
+ }
+ nmm.clear();
+ }
+
+ sections = marker->sections(cn, CodeMarker::Summary, CodeMarker::Okay);
+ s = sections.constBegin();
+ while (s != sections.constEnd()) {
+ ref = registerRef((*s).name);
+ out() << "<a name=\"" << ref << "\"></a>" << divNavTop << '\n';
+ out() << "<h2 id=\"" << ref << "\">" << protectEnc((*s).name) << "</h2>\n";
+ generateSectionList(*s, cn, marker, CodeMarker::Summary);
+ ++s;
+ }
+
+ Text brief = cn->doc().briefText();
+ if (cn->isModule() && !brief.isEmpty()) {
+ generateExtractionMark(cn, DetailedDescriptionMark);
+ ref = registerRef("details");
+ out() << "<a name=\"" << ref << "\"></a>" << divNavTop << '\n';
+ out() << "<div class=\"descr\">\n"; // QTBUG-9504
+ out() << "<h2 id=\"" << ref << "\">" << "Detailed Description" << "</h2>\n";
+ }
+ else {
+ generateExtractionMark(cn, DetailedDescriptionMark);
+ out() << "<div class=\"descr\"> <a name=\"" << registerRef("details") << "\"></a>\n"; // QTBUG-9504
+ }
+
+ generateBody(cn, marker);
+ out() << "</div>\n"; // QTBUG-9504
+ generateAlsoList(cn, marker);
+ generateExtractionMark(cn, EndMark);
+
+ if (!cn->noAutoList()) {
+ if (cn->isGroup())
+ generateAnnotatedList(cn, marker, cn->members());
+ else if (cn->isQmlModule() || cn->isJsModule())
+ generateAnnotatedList(cn, marker, cn->members());
+ }
+
+ sections = marker->sections(cn, CodeMarker::Detailed, CodeMarker::Okay);
+ s = sections.constBegin();
+ while (s != sections.constEnd()) {
+ //out() << "<hr />\n";
+ out() << "<h2>" << protectEnc((*s).name) << "</h2>\n";
+
+ NodeList::ConstIterator m = (*s).members.constBegin();
+ while (m != (*s).members.constEnd()) {
+ generateDetailedMember(*m, cn, marker);
+ ++m;
+ }
+ ++s;
+ }
+ generateFooter(cn);
+}
+
+/*!
+ Returns "html" for this subclass of Generator.
+ */
+QString HtmlGenerator::fileExtension() const
+{
+ return "html";
+}
+
+/*!
+ Output navigation list in the html file.
+ */
+void HtmlGenerator::generateNavigationBar(const QString &title,
+ const Node *node,
+ CodeMarker *marker,
+ const QString &buildversion,
+ bool tableItems)
+{
+ if (noNavigationBar)
+ return;
+
+ Text navigationbar;
+
+ // Set list item types based on the navigation bar type
+ Atom::AtomType itemLeft = tableItems ?
+ Atom::TableItemLeft : Atom::ListItemLeft;
+ Atom::AtomType itemRight = tableItems ?
+ Atom::TableItemRight : Atom::ListItemRight;
+
+ if (homepage == title)
+ return;
+ if (!homepage.isEmpty())
+ navigationbar << Atom(itemLeft)
+ << Atom(Atom::NavAutoLink, homepage)
+ << Atom(itemRight);
+ if (!landingpage.isEmpty() && landingpage != title)
+ navigationbar << Atom(itemLeft)
+ << Atom(Atom::NavAutoLink, landingpage)
+ << Atom(itemRight);
+
+ if (node->isClass()) {
+ if (!cppclassespage.isEmpty())
+ navigationbar << Atom(itemLeft)
+ << Atom(Atom::NavLink, cppclassespage)
+ << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK)
+ << Atom(Atom::String, QLatin1String("C++ Classes"))
+ << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK)
+ << Atom(itemRight);
+
+ if (!node->name().isEmpty())
+ navigationbar << Atom(itemLeft)
+ << Atom(Atom::String, node->name())
+ << Atom(itemRight);
+ }
+ else if (node->isQmlType() || node->isQmlBasicType() ||
+ node->isJsType() || node->isJsBasicType()) {
+ if (!qmltypespage.isEmpty())
+ navigationbar << Atom(itemLeft)
+ << Atom(Atom::NavLink, qmltypespage)
+ << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK)
+ << Atom(Atom::String, QLatin1String("QML Types"))
+ << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK)
+ << Atom(itemRight)
+ << Atom(itemLeft)
+ << Atom(Atom::String, title)
+ << Atom(itemRight);
+ }
+ else {
+ if (node->isExampleFile()) {
+ navigationbar << Atom(itemLeft)
+ << Atom(Atom::NavLink, node->parent()->name())
+ << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK)
+ << Atom(Atom::String, node->parent()->title())
+ << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK)
+ << Atom(itemRight);
+
+ }
+ navigationbar << Atom(itemLeft)
+ << Atom(Atom::String, title)
+ << Atom(itemRight);
+ }
+
+ generateText(navigationbar, node, marker);
+
+ if (buildversion.isEmpty())
+ return;
+
+ if (tableItems) {
+ out() << "</tr></table><table class=\"buildversion\"><tr>\n"
+ << "<td id=\"buildversion\" width=\"100%\" align=\"right\">"
+ << buildversion << "</td>\n";
+ } else {
+ out() << "<li id=\"buildversion\">" << buildversion << "</li>\n";
+ }
+}
+
+void HtmlGenerator::generateHeader(const QString& title,
+ const Node *node,
+ CodeMarker *marker)
+{
+#ifndef QT_NO_TEXTCODEC
+ out() << QString("<?xml version=\"1.0\" encoding=\"%1\"?>\n").arg(outputEncoding);
+#else
+ out() << QString("<?xml version=\"1.0\"?>\n");
+#endif
+ out() << "<!DOCTYPE html>\n";
+ out() << QString("<html lang=\"%1\">\n").arg(naturalLanguage);
+ out() << "<head>\n";
+ out() << " <meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\" />\n";
+ if (node && !node->doc().location().isEmpty())
+ out() << "<!-- " << node->doc().location().fileName() << " -->\n";
+
+ QString shortVersion = qdb_->version();
+ if (shortVersion.count(QChar('.')) == 2)
+ shortVersion.truncate(shortVersion.lastIndexOf(QChar('.')));
+
+ //determine the rest of the <title> element content: "title | titleSuffix version"
+ QString titleSuffix;
+ if (!landingpage.isEmpty()) {
+ //for normal pages: "title | landingpage version"
+ titleSuffix = landingpage;
+ }
+ else if (!homepage.isEmpty()) {
+ //for pages that set the homepage but not landing page: "title | homepage version"
+ if (title != homepage)
+ titleSuffix = homepage;
+ }
+ else if (!project.isEmpty()) {
+ //for projects outside of Qt or Qt 5: "title | project version"
+ if (title != project)
+ titleSuffix = project;
+ }
+ else
+ //default: "title | Qt version"
+ titleSuffix = QLatin1String("Qt ");
+
+ //for pages that duplicate the title and suffix (landing pages, home pages,
+ // and module landing pages, clear the duplicate
+ if (title == titleSuffix)
+ titleSuffix.clear();
+
+ //for pages that duplicate the version, clear the duplicate
+ if (title.contains(shortVersion) || titleSuffix.contains(shortVersion))
+ shortVersion.clear();
+
+ QString divider;
+ if (!titleSuffix.isEmpty() && !title.isEmpty())
+ divider = QLatin1String(" | ");
+
+ // Generating page title
+ out() << " <title>"
+ << protectEnc(title)
+ << divider
+ << titleSuffix
+ << QLatin1Char(' ')
+ << shortVersion
+ << "</title>\n";
+
+ // Include style sheet and script links.
+ out() << headerStyles;
+ out() << headerScripts;
+ if (endHeader.isEmpty())
+ out() << "</head>\n<body>\n";
+ else
+ out() << endHeader;
+
+#ifdef GENERATE_MAC_REFS
+ if (mainPage)
+ generateMacRef(node, marker);
+#endif
+
+ out() << QString(postHeader).replace("\\" + COMMAND_VERSION, qdb_->version());
+ bool usingTable = postHeader.trimmed().endsWith(QLatin1String("<tr>"));
+ generateNavigationBar(title, node, marker, buildversion, usingTable);
+ out() << QString(postPostHeader).replace("\\" + COMMAND_VERSION, qdb_->version());
+
+ navigationLinks.clear();
+ refMap.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 = qdb_->findNodeForTarget(linkPair.first, node);
+ if (!linkNode)
+ node->doc().location().warning(tr("Cannot link to '%1'").arg(linkPair.first));
+ if (!linkNode || linkNode == node)
+ anchorPair = linkPair;
+ else
+ anchorPair = anchorForNode(linkNode);
+
+ out() << " <link rel=\"prev\" href=\""
+ << anchorPair.first << "\" />\n";
+
+ navigationLinks += "<a class=\"prevPage\" 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 = qdb_->findNodeForTarget(linkPair.first, node);
+ if (!linkNode)
+ node->doc().location().warning(tr("Cannot link to '%1'").arg(linkPair.first));
+ if (!linkNode || linkNode == node)
+ anchorPair = linkPair;
+ else
+ anchorPair = anchorForNode(linkNode);
+
+ out() << " <link rel=\"next\" href=\""
+ << anchorPair.first << "\" />\n";
+
+ navigationLinks += "<a class=\"nextPage\" 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::StartLink)) {
+ linkPair = node->links()[Node::StartLink];
+ linkNode = qdb_->findNodeForTarget(linkPair.first, node);
+ if (!linkNode)
+ node->doc().location().warning(tr("Cannot link to '%1'").arg(linkPair.first));
+ if (!linkNode || linkNode == node)
+ anchorPair = linkPair;
+ else
+ anchorPair = anchorForNode(linkNode);
+ out() << " <link rel=\"start\" href=\""
+ << anchorPair.first << "\" />\n";
+ }
+ }
+
+ if (node && !node->links().empty())
+ out() << "<p class=\"naviNextPrevious headerNavi\">\n" << navigationLinks << "</p><p/>\n";
+}
+
+void HtmlGenerator::generateTitle(const QString& title,
+ const Text &subTitle,
+ SubTitleSize subTitleSize,
+ const Node *relative,
+ CodeMarker *marker)
+{
+ out() << QString(prologue).replace("\\" + COMMAND_VERSION, qdb_->version());
+ if (!title.isEmpty())
+ out() << "<h1 class=\"title\">" << protectEnc(title) << "</h1>\n";
+ if (!subTitle.isEmpty()) {
+ out() << "<span";
+ if (subTitleSize == SmallSubTitle)
+ out() << " class=\"small-subtitle\">";
+ else
+ out() << " class=\"subtitle\">";
+ generateText(subTitle, relative, marker);
+ out() << "</span>\n";
+ }
+}
+
+void HtmlGenerator::generateFooter(const Node *node)
+{
+ if (node && !node->links().empty())
+ out() << "<p class=\"naviNextPrevious footerNavi\">\n" << navigationLinks << "</p>\n";
+
+ out() << QString(footer).replace("\\" + COMMAND_VERSION, qdb_->version())
+ << QString(address).replace("\\" + COMMAND_VERSION, qdb_->version());
+
+ out() << "</body>\n";
+ out() << "</html>\n";
+}
+
+/*!
+Lists the required imports and includes in a table.
+The number of rows is known, so this path is simpler than the generateSection() path.
+*/
+void HtmlGenerator::generateRequisites(Aggregate *inner, CodeMarker *marker)
+{
+ QMap<QString, Text> requisites;
+ Text text;
+
+ const QString headerText = "Header";
+ const QString sinceText = "Since";
+ const QString inheritedBytext = "Inherited By";
+ const QString inheritsText = "Inherits";
+ const QString instantiatedByText = "Instantiated By";
+ const QString qtVariableText = "qmake";
+
+ //add the includes to the map
+ if (!inner->includes().isEmpty()) {
+ text.clear();
+ text << highlightedCode(indent(codeIndent,
+ marker->markedUpIncludes(inner->includes())),
+ inner);
+ requisites.insert(headerText, text);
+ }
+
+ //The order of the requisites matter
+ QStringList requisiteorder;
+ requisiteorder << headerText
+ << qtVariableText
+ << sinceText
+ << instantiatedByText
+ << inheritsText
+ << inheritedBytext;
+
+ //add the since and project into the map
+ if (!inner->since().isEmpty()) {
+ text.clear();
+ QStringList since = inner->since().split(QLatin1Char(' '));
+ if (since.count() == 1) {
+ // If there is only one argument, assume it is the Qt version number.
+ text << " Qt " << since[0];
+ }
+ else {
+ //Otherwise, reconstruct the <project> <version> string.
+ text << " " << since.join(' ');
+ }
+ text << Atom::ParaRight;
+ requisites.insert(sinceText, text);
+ }
+
+ if (inner->type() == Node::Class || inner->type() == Node::Namespace) {
+ //add the QT variable to the map
+ if (!inner->physicalModuleName().isEmpty()) {
+ const CollectionNode* cn = qdb_->getCollectionNode(inner->physicalModuleName(), Node::CPP);
+ if (cn && !cn->qtVariable().isEmpty()) {
+ text.clear();
+ text << "QT += " + cn->qtVariable();
+ requisites.insert(qtVariableText, text);
+ }
+ }
+ }
+
+ if (inner->type() == Node::Class) {
+ ClassNode* classe = static_cast<ClassNode*>(inner);
+ if (classe->qmlElement() != 0 && classe->status() != Node::Internal) {
+ text.clear();
+ text << Atom(Atom::LinkNode, CodeMarker::stringForNode(classe->qmlElement()))
+ << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK)
+ << Atom(Atom::String, classe->qmlElement()->name())
+ << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK);
+ requisites.insert(instantiatedByText, text);
+
+ }
+
+ //add the inherits to the map
+ QList<RelatedClass>::ConstIterator r;
+ int index;
+ if (!classe->baseClasses().isEmpty()) {
+ text.clear();
+ r = classe->baseClasses().constBegin();
+ index = 0;
+ while (r != classe->baseClasses().constEnd()) {
+ if ((*r).node_) {
+ text << Atom(Atom::LinkNode, CodeMarker::stringForNode((*r).node_))
+ << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK)
+ << Atom(Atom::String, (*r).signature_)
+ << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK);
+
+ if ((*r).access_ == Node::Protected) {
+ text << " (protected)";
+ }
+ else if ((*r).access_ == Node::Private) {
+ text << " (private)";
+ }
+ text << comma(index++, classe->baseClasses().count());
+ }
+ ++r;
+ }
+ text << Atom::ParaRight;
+ requisites.insert(inheritsText, text);
+ }
+
+ //add the inherited-by to the map
+ if (!classe->derivedClasses().isEmpty()) {
+ text.clear();
+ text << Atom::ParaLeft;
+ appendSortedNames(text, classe, classe->derivedClasses());
+ text << Atom::ParaRight;
+ requisites.insert(inheritedBytext, text);
+ }
+ }
+
+ if (!requisites.isEmpty()) {
+ //generate the table
+ out() << "<div class=\"table\"><table class=\"alignedsummary\">\n";
+
+ QStringList::ConstIterator i;
+ for (i = requisiteorder.constBegin(); i != requisiteorder.constEnd(); ++i) {
+
+ if (requisites.contains(*i)) {
+ out() << "<tr>"
+ << "<td class=\"memItemLeft rightAlign topAlign\"> "
+ << *i << ":"
+ "</td><td class=\"memItemRight bottomAlign\"> ";
+
+ if (*i == headerText)
+ out() << requisites.value(*i).toString();
+ else
+ generateText(requisites.value(*i), inner, marker);
+ out() << "</td></tr>";
+ }
+ }
+ out() << "</table></div>";
+ }
+}
+
+/*!
+Lists the required imports and includes in a table.
+The number of rows is known, so this path is simpler than the generateSection() path.
+*/
+void HtmlGenerator::generateQmlRequisites(QmlTypeNode *qcn, CodeMarker *marker)
+{
+ if (!qcn)
+ return;
+ QMap<QString, Text> requisites;
+ Text text;
+
+ const QString importText = "Import Statement:";
+ const QString sinceText = "Since:";
+ const QString inheritedBytext = "Inherited By:";
+ const QString inheritsText = "Inherits:";
+ const QString instantiatesText = "Instantiates:";
+
+ //The order of the requisites matter
+ QStringList requisiteorder;
+ requisiteorder << importText
+ << sinceText
+ << instantiatesText
+ << inheritsText
+ << inheritedBytext;
+
+ //add the module name and version to the map
+ QString logicalModuleVersion;
+ const CollectionNode* collection = qdb_->getCollectionNode(qcn->logicalModuleName(), qcn->genus());
+ if (collection)
+ logicalModuleVersion = collection->logicalModuleVersion();
+ else
+ logicalModuleVersion = qcn->logicalModuleVersion();
+ text.clear();
+ text << "import " + qcn->logicalModuleName() + QLatin1Char(' ') + logicalModuleVersion;
+ requisites.insert(importText, text);
+
+ //add the since and project into the map
+ if (!qcn->since().isEmpty()) {
+ text.clear();
+ QStringList since = qcn->since().split(QLatin1Char(' '));
+ if (since.count() == 1) {
+ // If there is only one argument, assume it is the Qt version number.
+ text << " Qt " << since[0];
+ }
+ else {
+ //Otherwise, reconstruct the <project> <version> string.
+ text << " " << since.join(' ');
+ }
+ text << Atom::ParaRight;
+ requisites.insert(sinceText, text);
+ }
+
+ //add the instantiates to the map
+ ClassNode* cn = qcn->classNode();
+ if (cn && (cn->status() != Node::Internal)) {
+ text.clear();
+ text << Atom(Atom::LinkNode,CodeMarker::stringForNode(qcn));
+ text << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK);
+ text << Atom(Atom::LinkNode,CodeMarker::stringForNode(cn));
+ text << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK);
+ text << Atom(Atom::String, cn->name());
+ text << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK);
+ requisites.insert(instantiatesText, text);
+ }
+
+ //add the inherits to the map
+ QmlTypeNode* base = qcn->qmlBaseNode();
+ while (base && base->isInternal()) {
+ base = base->qmlBaseNode();
+ }
+ if (base) {
+ text.clear();
+ text << Atom::ParaLeft
+ << Atom(Atom::LinkNode,CodeMarker::stringForNode(base))
+ << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK)
+ << Atom(Atom::String, base->name())
+ << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK)
+ << Atom::ParaRight;
+ requisites.insert(inheritsText, text);
+ }
+
+ //add the inherited-by to the map
+ NodeList subs;
+ QmlTypeNode::subclasses(qcn->name(), subs);
+ if (!subs.isEmpty()) {
+ text.clear();
+ text << Atom::ParaLeft;
+ appendSortedQmlNames(text, qcn, subs);
+ text << Atom::ParaRight;
+ requisites.insert(inheritedBytext, text);
+ }
+
+ if (!requisites.isEmpty()) {
+ //generate the table
+ out() << "<div class=\"table\"><table class=\"alignedsummary\">\n";
+
+ QStringList::ConstIterator i;
+ for (i = requisiteorder.constBegin(); i != requisiteorder.constEnd(); ++i) {
+
+ if (requisites.contains(*i)) {
+ out() << "<tr>"
+ << "<td class=\"memItemLeft rightAlign topAlign\"> "
+ << *i
+ << "</td><td class=\"memItemRight bottomAlign\"> ";
+
+ if (*i == importText)
+ out()<<requisites.value(*i).toString();
+ else
+ generateText(requisites.value(*i), qcn, marker);
+ out() << "</td></tr>";
+ }
+ }
+ out() << "</table></div>";
+ }
+}
+
+void HtmlGenerator::generateBrief(const Node *node, CodeMarker *marker,
+ const Node *relative)
+{
+ Text brief = node->doc().briefText();
+ if (!brief.isEmpty()) {
+ generateExtractionMark(node, BriefMark);
+ 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";
+
+
+ generateExtractionMark(node, EndMark);
+ }
+}
+
+void HtmlGenerator::generateIncludes(const Aggregate *inner, CodeMarker *marker)
+{
+ if (!inner->includes().isEmpty()) {
+ out() << "<pre class=\"cpp\">"
+ << trimmedTrailing(highlightedCode(indent(codeIndent,
+ marker->markedUpIncludes(inner->includes())),
+ inner), codePrefix, codeSuffix)
+ << "</pre>";
+ }
+}
+
+/*!
+ Revised for the new doc format.
+ Generates a table of contents beginning at \a node.
+ */
+void HtmlGenerator::generateTableOfContents(const Node *node,
+ CodeMarker *marker,
+ QList<Section>* sections)
+{
+ QList<Atom*> toc;
+ if (node->doc().hasTableOfContents())
+ toc = node->doc().tableOfContents();
+ if (tocDepth == 0 || (toc.isEmpty() && !sections && !node->isModule())) {
+ generateSidebar();
+ return;
+ }
+
+ QStringList sectionNumber;
+ int detailsBase = 0;
+
+ // disable nested links in table of contents
+ inContents_ = true;
+ inLink_ = true;
+
+ out() << "<div class=\"sidebar\">\n";
+ out() << "<div class=\"toc\">\n";
+ out() << "<h3><a name=\"toc\">Contents</a></h3>\n";
+ sectionNumber.append("1");
+ out() << "<ul>\n";
+
+ if (node->isModule()) {
+ if (node->hasNamespaces()) {
+ out() << "<li class=\"level"
+ << sectionNumber.size()
+ << "\"><a href=\"#"
+ << registerRef("namespaces")
+ << "\">Namespaces</a></li>\n";
+ }
+ if (node->hasClasses()) {
+ out() << "<li class=\"level"
+ << sectionNumber.size()
+ << "\"><a href=\"#"
+ << registerRef("classes")
+ << "\">Classes</a></li>\n";
+ }
+ out() << "<li class=\"level"
+ << sectionNumber.size()
+ << "\"><a href=\"#"
+ << registerRef("details")
+ << "\">Detailed Description</a></li>\n";
+ for (int i = 0; i < toc.size(); ++i) {
+ if (toc.at(i)->string().toInt() == 1) {
+ detailsBase = 1;
+ break;
+ }
+ }
+ }
+ else if (sections && (node->isClass() ||
+ node->isNamespace() ||
+ node->isQmlType() ||
+ node->isJsType())) {
+ QList<Section>::ConstIterator s = sections->constBegin();
+ while (s != sections->constEnd()) {
+ if (!s->members.isEmpty()) {
+ out() << "<li class=\"level"
+ << sectionNumber.size()
+ << "\"><a href=\"#"
+ << registerRef((*s).pluralMember)
+ << "\">" << (*s).name
+ << "</a></li>\n";
+ }
+ if (!s->reimpMembers.isEmpty()) {
+ QString ref = QString("Reimplemented ") + (*s).pluralMember;
+ out() << "<li class=\"level"
+ << sectionNumber.size()
+ << "\"><a href=\"#"
+ << registerRef(ref.toLower())
+ << "\">" << QString("Reimplemented ") + (*s).name
+ << "</a></li>\n";
+ }
+ ++s;
+ }
+ out() << "<li class=\"level"
+ << sectionNumber.size()
+ << "\"><a href=\"#"
+ << registerRef("details")
+ << "\">Detailed Description</a></li>\n";
+ for (int i = 0; i < toc.size(); ++i) {
+ if (toc.at(i)->string().toInt() == 1) {
+ detailsBase = 1;
+ break;
+ }
+ }
+ }
+
+ for (int i = 0; i < toc.size(); ++i) {
+ Atom *atom = toc.at(i);
+ int nextLevel = atom->string().toInt() + detailsBase;
+ if (nextLevel >= 0) {
+ 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);
+ }
+ }
+
+ //restrict the ToC depth to the one set by the HTML.tocdepth variable or
+ //print all levels if tocDepth is not set.
+ if (sectionNumber.size() <= tocDepth || tocDepth < 0) {
+ int numAtoms;
+ Text headingText = Text::sectionHeading(atom);
+ QString s = headingText.toString();
+ out() << "<li class=\"level"
+ << sectionNumber.size()
+ << "\">";
+ out() << "<a href=\""
+ << '#'
+ << Doc::canonicalTitle(s)
+ << "\">";
+ generateAtomList(headingText.firstAtom(), node, marker, true, numAtoms);
+ out() << "</a></li>\n";
+ }
+ }
+ while (!sectionNumber.isEmpty()) {
+ sectionNumber.removeLast();
+ }
+ out() << "</ul>\n";
+ out() << "</div>\n";
+ out() << "<div class=\"sidebar-content\" id=\"sidebar-content\"></div>";
+ out() << "</div>\n";
+ inContents_ = false;
+ inLink_ = false;
+}
+
+/*!
+ Outputs a placeholder div where the style can add customized sidebar content.
+ */
+void HtmlGenerator::generateSidebar() {
+ out() << "<div class=\"sidebar\">";
+ out() << "<div class=\"sidebar-content\" id=\"sidebar-content\"></div>";
+ out() << "</div>\n";
+}
+
+QString HtmlGenerator::generateListOfAllMemberFile(const Aggregate *inner,
+ CodeMarker *marker)
+{
+ QList<Section> sections;
+ QList<Section>::ConstIterator s;
+
+ sections = marker->sections(inner,
+ CodeMarker::Subpage,
+ CodeMarker::Okay);
+ if (sections.isEmpty())
+ return QString();
+
+ QString fileName = fileBase(inner) + "-members." + fileExtension();
+ beginSubPage(inner, fileName);
+ QString title = "List of All Members for " + inner->name();
+ generateHeader(title, inner, marker);
+ generateSidebar();
+ generateTitle(title, Text(), SmallSubTitle, inner, marker);
+ out() << "<p>This is the complete list of members for ";
+ generateFullName(inner, 0);
+ out() << ", including inherited members.</p>\n";
+
+ Section section = sections.first();
+ generateSectionList(section, inner, marker, CodeMarker::Subpage);
+
+ generateFooter();
+ endSubPage();
+ return fileName;
+}
+
+/*!
+ This function creates an html page on which are listed all
+ the members of QML class \a qml_cn, including the inherited
+ members. The \a marker is used for formatting stuff.
+ */
+QString HtmlGenerator::generateAllQmlMembersFile(QmlTypeNode* qml_cn, CodeMarker* marker)
+{
+ QList<Section> sections;
+ QList<Section>::ConstIterator s;
+
+ sections = marker->qmlSections(qml_cn,CodeMarker::Subpage);
+ if (sections.isEmpty())
+ return QString();
+
+ QString fileName = fileBase(qml_cn) + "-members." + fileExtension();
+ beginSubPage(qml_cn, fileName);
+ QString title = "List of All Members for " + qml_cn->name();
+ generateHeader(title, qml_cn, marker);
+ generateSidebar();
+ generateTitle(title, Text(), SmallSubTitle, qml_cn, marker);
+ out() << "<p>This is the complete list of members for ";
+ generateFullName(qml_cn, 0);
+ out() << ", including inherited members.</p>\n";
+
+ ClassKeysNodesList& cknl = sections.first().classKeysNodesList_;
+ if (!cknl.isEmpty()) {
+ for (int i=0; i<cknl.size(); i++) {
+ ClassKeysNodes* ckn = cknl[i];
+ const QmlTypeNode* qcn = ckn->first;
+ KeysAndNodes& kn = ckn->second;
+ QStringList& keys = kn.first;
+ NodeList& nodes = kn.second;
+ if (nodes.isEmpty())
+ continue;
+ if (i != 0) {
+ out() << "<p>The following members are inherited from ";
+ generateFullName(qcn,0);
+ out() << ".</p>\n";
+ }
+ out() << "<ul>\n";
+ for (int j=0; j<keys.size(); j++) {
+ if (nodes[j]->access() == Node::Private || nodes[j]->status() == Node::Internal) {
+ continue;
+ }
+ out() << "<li class=\"fn\">";
+ QString prefix;
+ if (!keys.isEmpty()) {
+ prefix = keys.at(j).mid(1);
+ prefix = prefix.left(keys.at(j).indexOf("::")+1);
+ }
+ generateQmlItem(nodes[j], qml_cn, marker, true);
+ if (nodes[j]->isAttached())
+ out() << " [attached]";
+ //generateSynopsis(nodes[j], qcn, marker, CodeMarker::Subpage, false, &prefix);
+ out() << "</li>\n";
+ }
+ out() << "</ul>\n";
+ }
+ }
+
+ generateFooter();
+ endSubPage();
+ return fileName;
+}
+
+QString HtmlGenerator::generateLowStatusMemberFile(Aggregate *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 = "Compatibility Members for " + inner->name();
+ fileName = fileBase(inner) + "-compat." + fileExtension();
+ }
+ else {
+ title = "Obsolete Members for " + inner->name();
+ fileName = fileBase(inner) + "-obsolete." + fileExtension();
+ }
+ if (status == CodeMarker::Obsolete) {
+ QString link;
+ if (useOutputSubdirs() && !Generator::outputSubdir().isEmpty())
+ link = QString("../" + Generator::outputSubdir() + QLatin1Char('/'));
+ link += fileName;
+ inner->setObsoleteLink(link);
+ }
+
+ beginSubPage(inner, fileName);
+ generateHeader(title, inner, marker);
+ generateSidebar();
+ generateTitle(title, Text(), SmallSubTitle, inner, marker);
+
+ if (status == CodeMarker::Compat) {
+ out() << "<p><b>The following members of class "
+ << "<a href=\"" << linkForNode(inner, 0) << "\">"
+ << protectEnc(inner->name()) << "</a>"
+ << " are part of the "
+ "Qt compatibility layer.</b> We advise against "
+ "using them in new code.</p>\n";
+ }
+ else {
+ out() << "<p><b>The following members of class "
+ << "<a href=\"" << linkForNode(inner, 0) << "\">"
+ << protectEnc(inner->name()) << "</a>"
+ << " are obsolete.</b> "
+ << "They are provided to keep old source code working. "
+ << "We strongly advise against using them in new code.</p>\n";
+ }
+
+ for (i = 0; i < sections.size(); ++i) {
+ out() << "<h2>" << protectEnc(sections.at(i).name) << "</h2>\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>" << protectEnc(sections.at(i).name) << "</h2>\n";
+
+ NodeList::ConstIterator m = sections.at(i).members.constBegin();
+ while (m != sections.at(i).members.constEnd()) {
+ if ((*m)->access() != Node::Private)
+ generateDetailedMember(*m, inner, marker);
+ ++m;
+ }
+ }
+
+ generateFooter();
+ endSubPage();
+ return fileName;
+}
+
+/*!
+ Generates a separate file where certain members of the QML
+ type \a qcn are listed. The \a marker is used to generate
+ the section lists, which are then traversed and output here.
+
+ Note that this function currently only handles correctly the
+ case where \a status is \c {CodeMarker::Obsolete}.
+ */
+QString HtmlGenerator::generateQmlMemberFile(QmlTypeNode* qcn,
+ CodeMarker *marker,
+ CodeMarker::Status status)
+{
+ QList<Section> sections = marker->qmlSections(qcn, CodeMarker::Summary, status);
+ QMutableListIterator<Section> j(sections);
+ while (j.hasNext()) {
+ if (j.next().members.size() == 0)
+ j.remove();
+ }
+ if (sections.isEmpty())
+ return QString();
+
+ QString title = "Obsolete Members for " + qcn->name();
+ QString fileName = fileBase(qcn) + "-obsolete." + fileExtension();
+
+ if (status == CodeMarker::Obsolete) {
+ QString link;
+ if (useOutputSubdirs() && !Generator::outputSubdir().isEmpty())
+ link = QString("../" + Generator::outputSubdir() + QLatin1Char('/'));
+ link += fileName;
+ qcn->setObsoleteLink(link);
+ }
+
+ beginSubPage(qcn, fileName);
+ generateHeader(title, qcn, marker);
+ generateSidebar();
+ generateTitle(title, Text(), SmallSubTitle, qcn, marker);
+
+ out() << "<p><b>The following members of QML type "
+ << "<a href=\"" << linkForNode(qcn, 0) << "\">"
+ << protectEnc(qcn->name()) << "</a>"
+ << " are obsolete.</b> "
+ << "They are provided to keep old source code working. "
+ << "We strongly advise against using them in new code.</p>\n";
+
+ QList<Section>::const_iterator s = sections.constBegin();
+ while (s != sections.constEnd()) {
+ QString ref = registerRef((*s).name.toLower());
+ out() << "<a name=\"" << ref
+ << "\"></a>" << divNavTop << '\n';
+ out() << "<h2 id=\"" << ref << "\">" << protectEnc((*s).name) << "</h2>\n";
+ generateQmlSummary(*s, qcn, marker);
+ ++s;
+ }
+
+ sections = marker->qmlSections(qcn, CodeMarker::Detailed, status);
+ QMutableListIterator<Section> k(sections);
+ while (k.hasNext()) {
+ if (k.next().members.size() == 0)
+ k.remove();
+ }
+ if (sections.isEmpty())
+ return QString();
+
+ s = sections.constBegin();
+ while (s != sections.constEnd()) {
+ out() << "<h2>" << protectEnc((*s).name) << "</h2>\n";
+ NodeList::ConstIterator m = (*s).members.constBegin();
+ while (m != (*s).members.constEnd()) {
+ generateDetailedQmlMember(*m, qcn, marker);
+ out() << "<br/>\n";
+ ++m;
+ }
+ ++s;
+ }
+
+ generateFooter();
+ endSubPage();
+ return fileName;
+}
+
+void HtmlGenerator::generateClassHierarchy(const Node *relative, NodeMap& classMap)
+{
+ if (classMap.isEmpty())
+ return;
+
+ NodeMap topLevel;
+ NodeMap::Iterator c = classMap.begin();
+ while (c != classMap.end()) {
+ ClassNode *classe = static_cast<ClassNode *>(*c);
+ if (classe->baseClasses().isEmpty())
+ topLevel.insert(classe->name(), classe);
+ ++c;
+ }
+
+ QStack<NodeMap > stack;
+ stack.push(topLevel);
+
+ out() << "<ul>\n";
+ while (!stack.isEmpty()) {
+ if (stack.top().isEmpty()) {
+ stack.pop();
+ out() << "</ul>\n";
+ }
+ else {
+ ClassNode* child = static_cast<ClassNode*>(*stack.top().begin());
+ out() << "<li>";
+ generateFullName(child, relative);
+ out() << "</li>\n";
+ stack.top().erase(stack.top().begin());
+
+ NodeMap newTop;
+ foreach (const RelatedClass &d, child->derivedClasses()) {
+ if (d.node_ && !d.isPrivate() && !d.node_->isInternal() && d.node_->hasDoc())
+ newTop.insert(d.node_->name(), d.node_);
+ }
+ if (!newTop.isEmpty()) {
+ stack.push(newTop);
+ out() << "<ul>\n";
+ }
+ }
+ }
+}
+
+/*!
+ Output an annotated list of the nodes in \a nodeMap.
+ A two-column table is output.
+ */
+void HtmlGenerator::generateAnnotatedList(const Node* relative,
+ CodeMarker* marker,
+ const NodeMultiMap& nmm)
+{
+ if (nmm.isEmpty())
+ return;
+ generateAnnotatedList(relative, marker, nmm.values());
+}
+
+/*!
+ */
+void HtmlGenerator::generateAnnotatedList(const Node *relative,
+ CodeMarker *marker,
+ const NodeList& unsortedNodes)
+{
+ NodeMultiMap nmm;
+ bool allInternal = true;
+ foreach (Node* node, unsortedNodes) {
+ if (!node->isInternal() && !node->isObsolete()) {
+ allInternal = false;
+ nmm.insert(node->fullName(relative), node);
+ }
+ }
+ if (allInternal)
+ return;
+ out() << "<div class=\"table\"><table class=\"annotated\">\n";
+ int row = 0;
+ NodeList nodes = nmm.values();
+ foreach (const Node* node, nodes) {
+ if (++row % 2 == 1)
+ out() << "<tr class=\"odd topAlign\">";
+ else
+ out() << "<tr class=\"even topAlign\">";
+ out() << "<td class=\"tblName\"><p>";
+ generateFullName(node, relative);
+ out() << "</p></td>";
+
+ if (!node->isDocumentNode()) {
+ Text brief = node->doc().trimmedBriefText(node->name());
+ if (!brief.isEmpty()) {
+ out() << "<td class=\"tblDescr\"><p>";
+ generateText(brief, node, marker);
+ out() << "</p></td>";
+ }
+ else if (!node->reconstitutedBrief().isEmpty()) {
+ out() << "<td class=\"tblDescr\"><p>";
+ out() << node->reconstitutedBrief();
+ out() << "</p></td>";
+ }
+ }
+ else {
+ out() << "<td class=\"tblDescr\"><p>";
+ if (!node->reconstitutedBrief().isEmpty()) {
+ out() << node->reconstitutedBrief();
+ }
+ else
+ out() << protectEnc(node->doc().briefText().toString());
+ out() << "</p></td>";
+ }
+ out() << "</tr>\n";
+ }
+ out() << "</table></div>\n";
+}
+
+/*!
+ This function finds the common prefix of the names of all
+ the classes in the class map \a nmm and then generates a
+ compact list of the class names alphabetized on the part
+ of the name not including the common prefix. You can tell
+ the function to use \a comonPrefix as the common prefix,
+ but normally you let it figure it out itself by looking at
+ the name of the first and last classes in the class map
+ \a nmm.
+ */
+void HtmlGenerator::generateCompactList(ListType listType,
+ const Node *relative,
+ const NodeMultiMap &nmm,
+ bool includeAlphabet,
+ QString commonPrefix)
+{
+ if (nmm.isEmpty())
+ return;
+
+ const int NumParagraphs = 37; // '0' to '9', 'A' to 'Z', '_'
+ int commonPrefixLen = commonPrefix.length();
+
+ /*
+ 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 NodeMultiMap.
+ */
+ NodeMultiMap paragraph[NumParagraphs+1];
+ QString paragraphName[NumParagraphs+1];
+ QSet<char> usedParagraphNames;
+
+ NodeMultiMap::ConstIterator c = nmm.constBegin();
+ while (c != nmm.constEnd()) {
+ QStringList pieces = c.key().split("::");
+ QString key;
+ int idx = commonPrefixLen;
+ if (idx > 0 && !pieces.last().startsWith(commonPrefix))
+ idx = 0;
+ if (pieces.size() == 1)
+ key = pieces.last().mid(idx).toLower();
+ else
+ key = pieces.last().toLower();
+
+ int paragraphNr = NumParagraphs - 1;
+
+ if (key[0].digitValue() != -1) {
+ paragraphNr = key[0].digitValue();
+ }
+ else if (key[0] >= QLatin1Char('a') && key[0] <= QLatin1Char('z')) {
+ paragraphNr = 10 + key[0].unicode() - 'a';
+ }
+
+ paragraphName[paragraphNr] = key[0].toUpper();
+ usedParagraphNames.insert(key[0].toLower().cell());
+ paragraph[paragraphNr].insert(c.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]; // 37 + 1
+ paragraphOffset[0] = 0;
+ for (int i=0; i<NumParagraphs; i++) // i = 0..36
+ paragraphOffset[i+1] = paragraphOffset[i] + paragraph[i].count();
+
+ /*
+ Output the alphabet as a row of links.
+ */
+ if (includeAlphabet) {
+ out() << "<p class=\"centerAlign functionIndex\"><b>";
+ for (int i = 0; i < 26; i++) {
+ QChar ch('a' + i);
+ if (usedParagraphNames.contains(char('a' + i)))
+ out() << QString("<a href=\"#%1\">%2</a>&nbsp;").arg(ch).arg(ch.toUpper());
+ }
+ out() << "</b></p>\n";
+ }
+
+ /*
+ Output a <div> element to contain all the <dl> elements.
+ */
+ out() << "<div class=\"flowListDiv\">\n";
+ numTableRows_ = 0;
+
+ int curParNr = 0;
+ int curParOffset = 0;
+ QString previousName;
+ bool multipleOccurrences = false;
+
+ for (int i=0; i<nmm.count(); i++) {
+ while ((curParNr < NumParagraphs) &&
+ (curParOffset == paragraph[curParNr].count())) {
+ ++curParNr;
+ curParOffset = 0;
+ }
+
+ /*
+ Starting a new paragraph means starting a new <dl>.
+ */
+ if (curParOffset == 0) {
+ if (i > 0)
+ out() << "</dl>\n";
+ if (++numTableRows_ % 2 == 1)
+ out() << "<dl class=\"flowList odd\">";
+ else
+ out() << "<dl class=\"flowList even\">";
+ out() << "<dt class=\"alphaChar\">";
+ if (includeAlphabet) {
+ QChar c = paragraphName[curParNr][0].toLower();
+ out() << QString("<a name=\"%1\"></a>").arg(c);
+ }
+ out() << "<b>"
+ << paragraphName[curParNr]
+ << "</b>";
+ out() << "</dt>\n";
+ }
+
+ /*
+ Output a <dd> for the current offset in the current paragraph.
+ */
+ out() << "<dd>";
+ if ((curParNr < NumParagraphs) &&
+ !paragraphName[curParNr].isEmpty()) {
+ NodeMultiMap::Iterator it;
+ NodeMultiMap::Iterator next;
+ it = paragraph[curParNr].begin();
+ for (int i=0; i<curParOffset; i++)
+ ++it;
+
+ if (listType == Generic) {
+ /*
+ Previously, we used generateFullName() for this, but we
+ require some special formatting.
+ */
+ out() << "<a href=\"" << linkForNode(it.value(), relative) << "\">";
+ }
+ else if (listType == Obsolete) {
+ QString fileName = fileBase(it.value()) + "-obsolete." + fileExtension();
+ QString link;
+ if (useOutputSubdirs()) {
+ link = QString("../" + it.value()->outputSubdirectory() + QLatin1Char('/'));
+ }
+ link += fileName;
+ out() << "<a href=\"" << link << "\">";
+ }
+
+ QStringList pieces;
+ if (it.value()->isQmlType() || it.value()->isJsType()) {
+ QString name = it.value()->name();
+ next = it;
+ ++next;
+ if (name != previousName)
+ multipleOccurrences = false;
+ if ((next != paragraph[curParNr].end()) && (name == next.value()->name())) {
+ multipleOccurrences = true;
+ previousName = name;
+ }
+ if (multipleOccurrences)
+ name += ": " + it.value()->tree()->camelCaseModuleName();
+ pieces << name;
+ }
+ else
+ pieces = it.value()->fullName(relative).split("::");
+ out() << protectEnc(pieces.last());
+ out() << "</a>";
+ if (pieces.size() > 1) {
+ out() << " (";
+ generateFullName(it.value()->parent(), relative);
+ out() << ')';
+ }
+ }
+ out() << "</dd>\n";
+ curParOffset++;
+ }
+ if (nmm.count() > 0)
+ out() << "</dl>\n";
+
+ out() << "</div>\n";
+}
+
+void HtmlGenerator::generateFunctionIndex(const Node *relative)
+{
+ out() << "<p class=\"centerAlign functionIndex\"><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></p>\n";
+
+ char nextLetter = 'a';
+ char currentLetter;
+
+ out() << "<ul>\n";
+ NodeMapMap& funcIndex = qdb_->getFunctionIndex();
+ QMap<QString, NodeMap >::ConstIterator f = funcIndex.constBegin();
+ while (f != funcIndex.constEnd()) {
+ out() << "<li>";
+ out() << protectEnc(f.key()) << ':';
+
+ currentLetter = f.key()[0].unicode();
+ while (islower(currentLetter) && currentLetter >= nextLetter) {
+ out() << QString("<a name=\"%1\"></a>").arg(nextLetter);
+ nextLetter++;
+ }
+
+ NodeMap::ConstIterator s = (*f).constBegin();
+ while (s != (*f).constEnd()) {
+ out() << ' ';
+ generateFullName((*s)->parent(), relative, *s);
+ ++s;
+ }
+ out() << "</li>";
+ out() << '\n';
+ ++f;
+ }
+ out() << "</ul>\n";
+}
+
+void HtmlGenerator::generateLegaleseList(const Node *relative, CodeMarker *marker)
+{
+ TextToNodeMap& legaleseTexts = qdb_->getLegaleseTexts();
+ QMap<Text, const Node *>::ConstIterator it = legaleseTexts.constBegin();
+ while (it != legaleseTexts.constEnd()) {
+ Text text = it.key();
+ //out() << "<hr />\n";
+ generateText(text, relative, marker);
+ out() << "<ul>\n";
+ do {
+ out() << "<li>";
+ generateFullName(it.value(), relative);
+ out() << "</li>\n";
+ ++it;
+ } while (it != legaleseTexts.constEnd() && it.key() == text);
+ out() << "</ul>\n";
+ }
+}
+
+void HtmlGenerator::generateQmlItem(const Node *node,
+ const Node *relative,
+ CodeMarker *marker,
+ bool summary)
+{
+ QString marked = marker->markedUpQmlItem(node,summary);
+ QRegExp templateTag("(<[^@>]*>)");
+ if (marked.indexOf(templateTag) != -1) {
+ QString contents = protectEnc(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 (summary)
+ marked.replace("@name>", "b>");
+
+ marked.replace("<@extra>", "<code>");
+ marked.replace("</@extra>", "</code>");
+
+ if (summary) {
+ marked.remove("<@type>");
+ marked.remove("</@type>");
+ }
+ out() << highlightedCode(marked, relative, false);
+}
+
+void HtmlGenerator::generateList(const Node* relative, CodeMarker* marker, const QString& selector)
+{
+ CNMap cnm;
+ Node::Genus genus = Node::DontCare;
+ if (selector == QLatin1String("overviews"))
+ genus = Node::DOC;
+ else if (selector == QLatin1String("cpp-modules"))
+ genus = Node::CPP;
+ else if (selector == QLatin1String("qml-modules"))
+ genus = Node::QML;
+ else if (selector == QLatin1String("js-modules"))
+ genus = Node::JS;
+ if (genus != Node::DontCare) {
+ NodeList nl;
+ qdb_->mergeCollections(genus, cnm, relative);
+ CollectionList cl = cnm.values();
+ foreach (CollectionNode* cn, cl)
+ nl.append(cn);
+ generateAnnotatedList(relative, marker, nl);
+ }
+ else {
+ /*
+ \generatelist {selector} is only allowed in a
+ comment where the topic is \group, \module,
+ \qmlmodule, or \jsmodule
+ */
+ if (!relative || !relative->isCollectionNode()) {
+ relative->doc().location().warning(tr("\\generatelist {%1} is only allowed in \\group, "
+ "\\module, \\qmlmodule, and \\jsmodule comments.").arg(selector));
+ return;
+ }
+ Node* n = const_cast<Node*>(relative);
+ CollectionNode* cn = static_cast<CollectionNode*>(n);
+ qdb_->mergeCollections(cn);
+ generateAnnotatedList(cn, marker, cn->members());
+ }
+}
+
+void HtmlGenerator::generateSection(const NodeList& nl,
+ const Node *relative,
+ CodeMarker *marker,
+ CodeMarker::SynopsisStyle style)
+{
+ bool alignNames = true;
+ if (!nl.isEmpty()) {
+ bool twoColumn = false;
+ if (style == CodeMarker::Subpage) {
+ alignNames = false;
+ twoColumn = (nl.count() >= 16);
+ }
+ else if (nl.first()->type() == Node::Property) {
+ twoColumn = (nl.count() >= 5);
+ alignNames = false;
+ }
+ if (alignNames) {
+ out() << "<div class=\"table\"><table class=\"alignedsummary\">\n";
+ }
+ else {
+ if (twoColumn)
+ out() << "<div class=\"table\"><table class=\"propsummary\">\n"
+ << "<tr><td class=\"topAlign\">";
+ out() << "<ul>\n";
+ }
+
+ int i = 0;
+ NodeList::ConstIterator m = nl.constBegin();
+ while (m != nl.constEnd()) {
+ if ((*m)->access() == Node::Private) {
+ ++m;
+ continue;
+ }
+
+ if (alignNames) {
+ out() << "<tr><td class=\"memItemLeft rightAlign topAlign\"> ";
+ }
+ else {
+ if (twoColumn && i == (int) (nl.count() + 1) / 2)
+ out() << "</ul></td><td class=\"topAlign\"><ul>\n";
+ out() << "<li class=\"fn\">";
+ }
+
+ generateSynopsis(*m, relative, marker, style, alignNames);
+ if (alignNames)
+ out() << "</td></tr>\n";
+ else
+ out() << "</li>\n";
+ i++;
+ ++m;
+ }
+ if (alignNames)
+ out() << "</table></div>\n";
+ else {
+ out() << "</ul>\n";
+ if (twoColumn)
+ out() << "</td></tr>\n</table></div>\n";
+ }
+ }
+}
+
+void HtmlGenerator::generateSectionList(const Section& section,
+ const Node *relative,
+ CodeMarker *marker,
+ CodeMarker::SynopsisStyle style)
+{
+ bool alignNames = true;
+ if (!section.members.isEmpty()) {
+ bool hasPrivateSignals = false;
+ bool twoColumn = false;
+ if (style == CodeMarker::Subpage) {
+ alignNames = false;
+ twoColumn = (section.members.count() >= 16);
+ }
+ else if (section.members.first()->type() == Node::Property) {
+ twoColumn = (section.members.count() >= 5);
+ alignNames = false;
+ }
+ if (alignNames) {
+ out() << "<div class=\"table\"><table class=\"alignedsummary\">\n";
+ }
+ else {
+ if (twoColumn)
+ out() << "<div class=\"table\"><table class=\"propsummary\">\n"
+ << "<tr><td class=\"topAlign\">";
+ out() << "<ul>\n";
+ }
+
+ int i = 0;
+ NodeList::ConstIterator m = section.members.constBegin();
+ while (m != section.members.constEnd()) {
+ if ((*m)->access() == Node::Private) {
+ ++m;
+ continue;
+ }
+
+ if (alignNames) {
+ out() << "<tr><td class=\"memItemLeft topAlign rightAlign\"> ";
+ }
+ else {
+ if (twoColumn && i == (int) (section.members.count() + 1) / 2)
+ out() << "</ul></td><td class=\"topAlign\"><ul>\n";
+ out() << "<li class=\"fn\">";
+ }
+
+ QString prefix;
+ if (!section.keys.isEmpty()) {
+ prefix = section.keys.at(i).mid(1);
+ prefix = prefix.left(section.keys.at(i).indexOf("::")+1);
+ }
+ generateSynopsis(*m, relative, marker, style, alignNames, &prefix);
+ if ((*m)->isFunction()) {
+ const FunctionNode* fn = static_cast<const FunctionNode*>(*m);
+ if (fn->isPrivateSignal()) {
+ hasPrivateSignals = true;
+ if (alignNames)
+ out() << "</td><td class=\"memItemRight bottomAlign\">[see note below]";
+ }
+ }
+ if (alignNames)
+ out() << "</td></tr>\n";
+ else
+ out() << "</li>\n";
+ i++;
+ ++m;
+ }
+ if (alignNames)
+ out() << "</table></div>\n";
+ else {
+ out() << "</ul>\n";
+ if (twoColumn)
+ out() << "</td></tr>\n</table></div>\n";
+ }
+ if (hasPrivateSignals && alignNames) {
+ generatePrivateSignalNote(relative, marker);
+ }
+ }
+
+ if (style == CodeMarker::Summary && !section.inherited.isEmpty()) {
+ out() << "<ul>\n";
+ generateSectionInheritedList(section, relative);
+ out() << "</ul>\n";
+ }
+}
+
+void HtmlGenerator::generateSectionInheritedList(const Section& section, const Node *relative)
+{
+ QList<QPair<Aggregate *, int> >::ConstIterator p = section.inherited.constBegin();
+ while (p != section.inherited.constEnd()) {
+ out() << "<li class=\"fn\">";
+ out() << (*p).second << ' ';
+ if ((*p).second == 1) {
+ out() << section.singularMember;
+ }
+ else {
+ out() << section.pluralMember;
+ }
+ out() << " inherited from <a href=\"" << fileName((*p).first)
+ << '#' << Generator::cleanRef(section.name.toLower()) << "\">"
+ << protectEnc((*p).first->plainFullName(relative))
+ << "</a></li>\n";
+ ++p;
+ }
+}
+
+// generateSynopsis(qmn,relative,marker,CodeMarker::Detailed,false);
+void HtmlGenerator::generateSynopsis(const Node *node,
+ const Node *relative,
+ CodeMarker *marker,
+ CodeMarker::SynopsisStyle style,
+ bool alignNames,
+ const QString* prefix)
+{
+ QString marked = marker->markedUpSynopsis(node, relative, style);
+
+ if (prefix)
+ marked.prepend(*prefix);
+ QRegExp templateTag("(<[^@>]*>)");
+ if (marked.indexOf(templateTag) != -1) {
+ QString contents = protectEnc(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.remove("<@name>"); // was "<b>"
+ marked.remove("</@name>"); // was "</b>"
+ }
+
+ if (style == CodeMarker::Subpage) {
+ QRegExp extraRegExp("<@extra>.*</@extra>");
+ extraRegExp.setMinimal(true);
+ marked.remove(extraRegExp);
+ } else {
+ marked.replace("<@extra>", "<code>");
+ marked.replace("</@extra>", "</code>");
+ }
+
+ if (style != CodeMarker::Detailed) {
+ marked.remove("<@type>");
+ marked.remove("</@type>");
+ }
+
+ out() << highlightedCode(marked, relative, alignNames);
+}
+
+QString HtmlGenerator::highlightedCode(const QString& markedCode,
+ const Node* relative,
+ bool alignNames)
+{
+ QString src = markedCode;
+ QString html;
+ html.reserve(src.size());
+ QStringRef arg;
+ QStringRef par1;
+
+ const QChar charLangle = '<';
+ const QChar charAt = '@';
+
+ static const QString typeTag("type");
+ static const QString headerTag("headerfile");
+ static const QString funcTag("func");
+ static const QString linkTag("link");
+
+ // replace all <@link> tags: "(<@link node=\"([^\"]+)\">).*(</@link>)"
+ // replace all <@func> tags: "(<@func target=\"([^\"]*)\">)(.*)(</@func>)"
+ // replace all "(<@(type|headerfile)(?: +[^>]*)?>)(.*)(</@\\2>)" tags
+ bool done = false;
+ for (int i = 0, srcSize = src.size(); i < srcSize;) {
+ if (src.at(i) == charLangle && src.at(i + 1) == charAt) {
+ if (alignNames && !done) {
+ html += QLatin1String("</td><td class=\"memItemRight bottomAlign\">");
+ done = true;
+ }
+ i += 2;
+ if (parseArg(src, linkTag, &i, srcSize, &arg, &par1)) {
+ html += QLatin1String("<b>");
+ const Node* n = CodeMarker::nodeForString(par1.toString());
+ QString link = linkForNode(n, relative);
+ addLink(link, arg, &html);
+ html += QLatin1String("</b>");
+ }
+ else if (parseArg(src, funcTag, &i, srcSize, &arg, &par1)) {
+ const Node* n = qdb_->findFunctionNode(par1.toString(), relative, Node::DontCare);
+ QString link = linkForNode(n, relative);
+ addLink(link, arg, &html);
+ par1 = QStringRef();
+ }
+ else if (parseArg(src, typeTag, &i, srcSize, &arg, &par1)) {
+ par1 = QStringRef();
+ const Node* n = qdb_->findTypeNode(arg.toString(), relative);
+ html += QLatin1String("<span class=\"type\">");
+ if (n && (n->isQmlBasicType() || n->isJsBasicType())) {
+ if (relative && (relative->isQmlType() || relative->isJsType()))
+ addLink(linkForNode(n,relative), arg, &html);
+ else
+ html += arg;
+ }
+ else
+ addLink(linkForNode(n,relative), arg, &html);
+ html += QLatin1String("</span>");
+ }
+ else if (parseArg(src, headerTag, &i, srcSize, &arg, &par1)) {
+ par1 = QStringRef();
+ if (arg.at(0) == QChar('&'))
+ html += arg;
+ else {
+ const Node* n = qdb_->findNodeForInclude(QStringList(arg.toString()));
+ if (n && n != relative)
+ addLink(linkForNode(n,relative), arg, &html);
+ else
+ html += arg;
+ }
+ }
+ else {
+ 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\">";
+ // "<@number>" -> "<span class=\"number\">";
+ // "<@op>" -> "<span class=\"operator\">";
+ // "<@type>" -> "<span class=\"type\">";
+ // "<@name>" -> "<span class=\"name\">";
+ // "<@keyword>" -> "<span class=\"keyword\">";
+ // "</@(?:comment|preprocessor|string|char|number|op|type|name|keyword)>" -> "</span>"
+ src = html;
+ html = QString();
+ html.reserve(src.size());
+ static const QLatin1String spanTags[] = {
+ QLatin1String("comment>"), QLatin1String("<span class=\"comment\">"),
+ QLatin1String("preprocessor>"), QLatin1String("<span class=\"preprocessor\">"),
+ QLatin1String("string>"), QLatin1String("<span class=\"string\">"),
+ QLatin1String("char>"), QLatin1String("<span class=\"char\">"),
+ QLatin1String("number>"), QLatin1String("<span class=\"number\">"),
+ QLatin1String("op>"), QLatin1String("<span class=\"operator\">"),
+ QLatin1String("type>"), QLatin1String("<span class=\"type\">"),
+ QLatin1String("name>"), QLatin1String("<span class=\"name\">"),
+ QLatin1String("keyword>"), QLatin1String("<span class=\"keyword\">")
+ };
+ int nTags = 9;
+ // Update the upper bound of k in the following code to match the length
+ // of the above array.
+ for (int i = 0, n = src.size(); i < n;) {
+ if (src.at(i) == QLatin1Char('<')) {
+ if (src.at(i + 1) == QLatin1Char('@')) {
+ i += 2;
+ bool handled = false;
+ for (int k = 0; k != nTags; ++k) {
+ const QLatin1String& tag = spanTags[2 * k];
+ if (i + tag.size() <= src.length() &&
+ tag == QStringRef(&src, i, tag.size())) {
+ html += spanTags[2 * k + 1];
+ i += tag.size();
+ handled = true;
+ break;
+ }
+ }
+ if (!handled) {
+ // drop 'our' unknown tags (the ones still containing '@')
+ while (i < n && src.at(i) != QLatin1Char('>'))
+ ++i;
+ ++i;
+ }
+ continue;
+ }
+ else if (src.at(i + 1) == QLatin1Char('/') && src.at(i + 2) == QLatin1Char('@')) {
+ i += 3;
+ bool handled = false;
+ for (int k = 0; k != nTags; ++k) {
+ const QLatin1String& tag = spanTags[2 * k];
+ if (i + tag.size() <= src.length() &&
+ tag == QStringRef(&src, i, tag.size())) {
+ html += QLatin1String("</span>");
+ i += tag.size();
+ handled = true;
+ break;
+ }
+ }
+ if (!handled) {
+ // drop 'our' unknown tags (the ones still containing '@')
+ while (i < n && src.at(i) != QLatin1Char('>'))
+ ++i;
+ ++i;
+ }
+ continue;
+ }
+ }
+ html += src.at(i);
+ ++i;
+ }
+ return html;
+}
+
+void HtmlGenerator::generateLink(const Atom* atom, 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() << protectEnc(atom->string().left(k));
+ if (link_.isEmpty()) {
+ if (showBrokenLinks)
+ out() << "</i>";
+ } else {
+ out() << "</a>";
+ }
+ inLink_ = false;
+ out() << protectEnc(atom->string().mid(k));
+ } else {
+ out() << protectEnc(atom->string());
+ }
+}
+
+QString HtmlGenerator::registerRef(const QString& ref)
+{
+ QString clean = Generator::cleanRef(ref);
+
+ for (;;) {
+ QString& prevRef = refMap[clean.toLower()];
+ if (prevRef.isEmpty()) {
+ prevRef = ref;
+ break;
+ } else if (prevRef == ref) {
+ break;
+ }
+ clean += QLatin1Char('x');
+ }
+ return clean;
+}
+
+QString HtmlGenerator::protectEnc(const QString &string)
+{
+#ifndef QT_NO_TEXTCODEC
+ return protect(string, outputEncoding);
+#else
+ return protect(string);
+#endif
+}
+
+QString HtmlGenerator::protect(const QString &string, const QString &outputEncoding)
+{
+#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 ((outputEncoding == QLatin1String("ISO-8859-1") && 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
+}
+
+QString HtmlGenerator::fileBase(const Node *node) const
+{
+ QString result;
+
+ result = Generator::fileBase(node);
+
+ if (!node->isAggregate()) {
+ switch (node->status()) {
+ case Node::Compat:
+ result += QLatin1String("-compat");
+ break;
+ case Node::Obsolete:
+ result += QLatin1String("-obsolete");
+ break;
+ default:
+ ;
+ }
+ }
+ return result;
+}
+
+QString HtmlGenerator::fileName(const Node *node)
+{
+ if (node->type() == Node::Document) {
+ if (static_cast<const DocumentNode *>(node)->docSubtype() == Node::ExternalPage)
+ return node->name();
+ if (static_cast<const DocumentNode *>(node)->docSubtype() == Node::Image)
+ return node->name();
+ }
+ return Generator::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->hasOneAssociatedProperty() && func->doc().isEmpty()) {
+ return refForNode(func->firstAssociatedProperty());
+ }
+ else {
+ ref = func->name();
+ if (func->overloadNumber() != 0)
+ ref += QLatin1Char('-') + QString::number(func->overloadNumber());
+ }
+ break;
+ case Node::Document:
+ break;
+ case Node::QmlProperty:
+ if (node->isAttached())
+ ref = node->name() + "-attached-prop";
+ else
+ ref = node->name() + "-prop";
+ break;
+ case Node::QmlPropertyGroup:
+ case Node::Property:
+ ref = node->name() + "-prop";
+ break;
+ case Node::QmlSignal:
+ ref = node->name() + "-signal";
+ break;
+ case Node::QmlSignalHandler:
+ ref = node->name() + "-signal-handler";
+ break;
+ case Node::QmlMethod:
+ func = static_cast<const FunctionNode *>(node);
+ ref = func->name() + "-method";
+ if (func->overloadNumber() != 0)
+ ref += QLatin1Char('-') + QString::number(func->overloadNumber());
+ break;
+ case Node::Variable:
+ ref = node->name() + "-var";
+ break;
+ }
+ return registerRef(ref);
+}
+
+/*!
+ This function is called for links, i.e. for words that
+ are marked with the qdoc link command. For autolinks
+ that are not marked with the qdoc link command, the
+ getAutoLink() function is called
+
+ It returns the string for a link found by using the data
+ in the \a atom to search the database. It also sets \a node
+ to point to the target node for that link. \a relative points
+ to the node holding the qdoc comment where the link command
+ was found.
+ */
+QString HtmlGenerator::getLink(const Atom *atom, const Node *relative, const Node** node)
+{
+ const QString& t = atom->string();
+ if (t.at(0) == QChar('h')) {
+ if (t.startsWith("http:") || t.startsWith("https:"))
+ return t;
+ }
+ else if (t.at(0) == QChar('f')) {
+ if (t.startsWith("file:") || t.startsWith("ftp:"))
+ return t;
+ }
+ else if (t.at(0) == QChar('m')) {
+ if (t.startsWith("mailto:"))
+ return t;
+ }
+ return getAutoLink(atom, relative, node);
+}
+
+/*!
+ This function is called for autolinks, i.e. for words that
+ are not marked with the qdoc link command that qdoc has
+ reason to believe should be links. For links marked with
+ the qdoc link command, the getLink() function is called.
+
+ It returns the string for a link found by using the data
+ in the \a atom to search the database. It also sets \a node
+ to point to the target node for that link. \a relative points
+ to the node holding the qdoc comment where the link command
+ was found.
+ */
+QString HtmlGenerator::getAutoLink(const Atom *atom, const Node *relative, const Node** node)
+{
+ QString ref;
+
+ *node = qdb_->findNodeForAtom(atom, relative, ref);
+ if (!(*node)) {
+ return QString();
+ }
+
+ QString link = (*node)->url();
+ if (link.isEmpty()) {
+ link = linkForNode(*node, relative);
+ if ((*node)->docSubtype() == Node::Image)
+ link = "images/used-in-examples/" + link;
+ }
+ if (!ref.isEmpty()) {
+ int hashtag = link.lastIndexOf(QChar('#'));
+ if (hashtag != -1)
+ link.truncate(hashtag);
+ link += QLatin1Char('#') + ref;
+ }
+ return link;
+}
+
+/*!
+ Construct the link string for the \a node and return it.
+ The \a relative node is use to decide the link we are
+ generating is in the same file as the target. Note the
+ relative node can be 0, which pretty much guarantees
+ that the link and the target aren't in the same file.
+ */
+QString HtmlGenerator::linkForNode(const Node *node, const Node *relative)
+{
+ if (node == 0)
+ return QString();
+ if (!node->url().isEmpty())
+ return node->url();
+ if (fileBase(node).isEmpty())
+ return QString();
+ if (node->access() == Node::Private)
+ return QString();
+
+ QString fn = fileName(node);
+ if (node && node->parent() &&
+ (node->parent()->isQmlType() || node->parent()->isJsType())
+ && node->parent()->isAbstract()) {
+ if (Generator::qmlTypeContext())
+ fn = fileName(Generator::qmlTypeContext());
+ }
+ QString link = fn;
+
+ if (!node->isAggregate() || node->isQmlPropertyGroup() || node->isJsPropertyGroup()) {
+ QString ref = refForNode(node);
+ if (relative && fn == fileName(relative) && ref == refForNode(relative))
+ return QString();
+
+ link += QLatin1Char('#');
+ link += ref;
+ }
+ /*
+ If the output is going to subdirectories, then if the
+ two nodes will be output to different directories, then
+ the link must go up to the parent directory and then
+ back down into the other subdirectory.
+ */
+ if (node && relative && (node != relative)) {
+ if (useOutputSubdirs() && !node->isExternalPage() &&
+ node->outputSubdirectory() != relative->outputSubdirectory()) {
+ if (link.startsWith(QString(node->outputSubdirectory() + QLatin1Char('/')))) {
+ link.prepend(QString("../"));
+ }
+ else {
+ link.prepend(QString("../" + node->outputSubdirectory() + QLatin1Char('/')));
+ }
+ }
+ }
+ return link;
+}
+
+void HtmlGenerator::generateFullName(const Node *apparentNode, const Node *relative, 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() << protectEnc(apparentNode->fullName(relative));
+ out() << "</a>";
+}
+
+void HtmlGenerator::generateDetailedMember(const Node *node,
+ const Aggregate *relative,
+ CodeMarker *marker)
+{
+ const EnumNode *etn;
+#ifdef GENERATE_MAC_REFS
+ generateMacRef(node, marker);
+#endif
+ generateExtractionMark(node, MemberMark);
+ generateKeywordAnchors(node);
+ QString nodeRef = refForNode(node);
+ if (node->isEnumType() && (etn = static_cast<const EnumNode *>(node))->flagsType()) {
+#ifdef GENERATE_MAC_REFS
+ generateMacRef(etn->flagsType(), marker);
+#endif
+ out() << "<h3 class=\"flags\" id=\"" << nodeRef << "\">";
+ out() << "<a name=\"" + nodeRef + "\"></a>";
+ generateSynopsis(etn, relative, marker, CodeMarker::Detailed);
+ out() << "<br/>";
+ generateSynopsis(etn->flagsType(),
+ relative,
+ marker,
+ CodeMarker::Detailed);
+ out() << "</h3>\n";
+ }
+ else {
+ out() << "<h3 class=\"fn\" id=\"" << nodeRef << "\">";
+ out() << "<a name=\"" + nodeRef + "\"></a>";
+ generateSynopsis(node, relative, marker, CodeMarker::Detailed);
+ out() << "</h3>" << divNavTop << '\n';
+ }
+
+ generateStatus(node, marker);
+ generateBody(node, marker);
+ generateOverloadedSignal(node, marker);
+ generateThreadSafeness(node, marker);
+ generateSince(node, marker);
+
+ if (node->isProperty()) {
+ 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><b>Access functions:</b></p>\n";
+ generateSectionList(section, node, marker, CodeMarker::Accessors);
+ }
+
+ Section notifiers;
+ notifiers.members += property->notifiers();
+
+ if (!notifiers.members.isEmpty()) {
+ out() << "<p><b>Notifier signal:</b></p>\n";
+ //out() << "<p>This signal is emitted when the property value is changed.</p>\n";
+ generateSectionList(notifiers, node, marker, CodeMarker::Accessors);
+ }
+ }
+ else if (node->isFunction()) {
+ const FunctionNode* fn = static_cast<const FunctionNode*>(node);
+ if (fn->isPrivateSignal())
+ generatePrivateSignalNote(node, marker);
+ generateAssociatedPropertyNotes(fn);
+ }
+ else if (node->isEnumType()) {
+ const EnumNode *etn = static_cast<const EnumNode *>(node);
+ if (etn->flagsType()) {
+ out() << "<p>The " << protectEnc(etn->flagsType()->name())
+ << " type is a typedef for "
+ << "<a href=\"" << qflagsHref_ << "\">QFlags</a>&lt;"
+ << protectEnc(etn->name())
+ << "&gt;. It stores an OR combination of "
+ << protectEnc(etn->name())
+ << " values.</p>\n";
+ }
+ }
+ generateAlsoList(node, marker);
+ generateExtractionMark(node, EndMark);
+}
+
+int HtmlGenerator::hOffset(const Node *node)
+{
+ switch (node->type()) {
+ case Node::Namespace:
+ case Node::Class:
+ return 2;
+ case Node::QmlBasicType:
+ case Node::QmlType:
+ case Node::Document:
+ return 1;
+ 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 QPair<QString,QString> HtmlGenerator::anchorForNode(const Node *node)
+{
+ QPair<QString,QString> anchorPair;
+
+ anchorPair.first = Generator::fileName(node);
+ if (node->type() == Node::Document) {
+ const DocumentNode *docNode = static_cast<const DocumentNode*>(node);
+ anchorPair.second = docNode->title();
+ }
+
+ return anchorPair;
+}
+
+void HtmlGenerator::generateStatus(const Node *node, CodeMarker *marker)
+{
+ Text text;
+
+ switch (node->status()) {
+ case Node::Obsolete:
+ if (node->isAggregate())
+ Generator::generateStatus(node, marker);
+ break;
+ case Node::Compat:
+ // Porting to Qt 4 no longer supported
+ break;
+ default:
+ Generator::generateStatus(node, marker);
+ }
+}
+
+#ifdef GENERATE_MAC_REFS
+/*
+ No longer valid.
+ */
+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 << "\"></a>\n";
+}
+#endif
+
+void HtmlGenerator::beginLink(const QString &link, const Node *node, const Node *relative)
+{
+ 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 {
+ if (inObsoleteLink) {
+ out() << "<sup>(obsolete)</sup>";
+ }
+ out() << "</a>";
+ }
+ }
+ inLink_ = false;
+ inObsoleteLink = false;
+}
+
+/*!
+ Generates the summary for the \a section. Only used for
+ sections of QML element documentation.
+ */
+void HtmlGenerator::generateQmlSummary(const Section& section,
+ const Node *relative,
+ CodeMarker *marker)
+{
+ if (!section.members.isEmpty()) {
+ out() << "<ul>\n";
+ NodeList::ConstIterator m;
+ m = section.members.constBegin();
+ while (m != section.members.constEnd()) {
+ out() << "<li class=\"fn\">";
+ generateQmlItem(*m,relative,marker,true);
+ if ((*m)->type() == Node::QmlPropertyGroup) {
+ const QmlPropertyGroupNode* qpgn = static_cast<const QmlPropertyGroupNode*>(*m);
+ if (!qpgn->childNodes().isEmpty()) {
+ NodeList::ConstIterator p = qpgn->childNodes().constBegin();
+ out() << "<ul>\n";
+ while (p != qpgn->childNodes().constEnd()) {
+ if ((*p)->type() == Node::QmlProperty) {
+ out() << "<li class=\"fn\">";
+ generateQmlItem(*p, relative, marker, true);
+ out() << "</li>\n";
+ }
+ ++p;
+ }
+ out() << "</ul>\n";
+ }
+ }
+ out() << "</li>\n";
+ ++m;
+ }
+ out() << "</ul>\n";
+ }
+}
+
+/*!
+ Outputs the html detailed documentation for a section
+ on a QML element reference page.
+ */
+void HtmlGenerator::generateDetailedQmlMember(Node *node,
+ const Aggregate *relative,
+ CodeMarker *marker)
+{
+ QmlPropertyNode* qpn = 0;
+#ifdef GENERATE_MAC_REFS
+ generateMacRef(node, marker);
+#endif
+ generateExtractionMark(node, MemberMark);
+ generateKeywordAnchors(node);
+
+ QString qmlItemHeader("<div class=\"qmlproto\">\n"
+ "<div class=\"table\"><table class=\"qmlname\">\n"
+ "<tr valign=\"top\" class=\"odd\" id=\"%1\">\n"
+ "<td class=\"%2\"><p>\n"
+ "<a name=\"%3\"></a>");
+
+ QString qmlItemFooter("</p></td></tr>\n"
+ "</table></div>\n"
+ "</div>");
+
+ out() << "<div class=\"qmlitem\">";
+ QString nodeRef = refForNode(node);
+ if (node->type() == Node::QmlPropertyGroup) {
+ const QmlPropertyGroupNode* qpgn = static_cast<const QmlPropertyGroupNode*>(node);
+ NodeList::ConstIterator p = qpgn->childNodes().constBegin();
+ out() << "<div class=\"qmlproto\">";
+ out() << "<div class=\"table\"><table class=\"qmlname\">";
+
+ QString heading = qpgn->name() + " group";
+ out() << "<tr valign=\"top\" class=\"even\" id=\"" << nodeRef << "\">";
+ out() << "<th class=\"centerAlign\"><p>";
+ out() << "<a name=\"" + nodeRef + "\"></a>";
+ out() << "<b>" << heading << "</b>";
+ out() << "</p></th></tr>";
+ while (p != qpgn->childNodes().constEnd()) {
+ if ((*p)->type() == Node::QmlProperty) {
+ qpn = static_cast<QmlPropertyNode*>(*p);
+ nodeRef = refForNode(qpn);
+ out() << "<tr valign=\"top\" class=\"odd\" id=\"" << nodeRef << "\">";
+ out() << "<td class=\"tblQmlPropNode\"><p>";
+ out() << "<a name=\"" + nodeRef + "\"></a>";
+
+ if (!qpn->isWritable())
+ out() << "<span class=\"qmlreadonly\">read-only</span>";
+ if (qpn->isDefault())
+ out() << "<span class=\"qmldefault\">default</span>";
+ generateQmlItem(qpn, relative, marker, false);
+ out() << "</p></td></tr>";
+ }
+ ++p;
+ }
+ out() << "</table></div>";
+ out() << "</div>";
+ }
+ else if (node->type() == Node::QmlProperty) {
+ qpn = static_cast<QmlPropertyNode*>(node);
+ out() << qmlItemHeader.arg(nodeRef, "tblQmlPropNode", refForNode(qpn));
+ if (!qpn->isReadOnlySet()) {
+ if (qpn->declarativeCppNode())
+ qpn->setReadOnly(!qpn->isWritable());
+ }
+ if (qpn->isReadOnly())
+ out() << "<span class=\"qmlreadonly\">read-only</span>";
+ if (qpn->isDefault())
+ out() << "<span class=\"qmldefault\">default</span>";
+ generateQmlItem(qpn, relative, marker, false);
+ out() << qmlItemFooter;
+ }
+ else if (node->type() == Node::QmlSignal ||
+ node->type() == Node::QmlSignalHandler ||
+ node->type() == Node::QmlMethod) {
+ out() << qmlItemHeader.arg(nodeRef, "tblQmlFuncNode", refForNode(node));
+ generateSynopsis(node, relative, marker, CodeMarker::Detailed, false);
+ out() << qmlItemFooter;
+ }
+ out() << "<div class=\"qmldoc\">";
+ generateStatus(node, marker);
+ generateBody(node, marker);
+ generateThreadSafeness(node, marker);
+ generateSince(node, marker);
+ generateAlsoList(node, marker);
+ out() << "</div>";
+ out() << "</div>";
+ generateExtractionMark(node, EndMark);
+}
+
+/*!
+ Output the "Inherits" line for the QML element,
+ if there should be one.
+ */
+void HtmlGenerator::generateQmlInherits(QmlTypeNode* qcn, CodeMarker* marker)
+{
+ if (!qcn)
+ return;
+ QmlTypeNode* base = qcn->qmlBaseNode();
+ while (base && base->isInternal()) {
+ base = base->qmlBaseNode();
+ }
+ if (base) {
+ Text text;
+ text << Atom::ParaLeft << "Inherits ";
+ text << Atom(Atom::LinkNode,CodeMarker::stringForNode(base));
+ text << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK);
+ text << Atom(Atom::String, base->name());
+ text << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK);
+ text << Atom::ParaRight;
+ generateText(text, qcn, marker);
+ }
+}
+
+/*!
+ Output the "[Xxx instantiates the C++ class QmlGraphicsXxx]"
+ line for the QML element, if there should be one.
+
+ If there is no class node, or if the class node status
+ is set to Node::Internal, do nothing.
+ */
+void HtmlGenerator::generateQmlInstantiates(QmlTypeNode* qcn, CodeMarker* marker)
+{
+ ClassNode* cn = qcn->classNode();
+ if (cn && (cn->status() != Node::Internal)) {
+ Text text;
+ text << Atom::ParaLeft;
+ text << Atom(Atom::LinkNode,CodeMarker::stringForNode(qcn));
+ text << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK);
+ QString name = qcn->name();
+ /*
+ Remove the "QML:" prefix, if present.
+ It shouldn't be present anymore.
+ */
+ if (name.startsWith(QLatin1String("QML:")))
+ name = name.mid(4); // remove the "QML:" prefix
+ text << Atom(Atom::String, name);
+ text << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK);
+ text << " instantiates the C++ class ";
+ text << Atom(Atom::LinkNode,CodeMarker::stringForNode(cn));
+ text << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK);
+ text << Atom(Atom::String, cn->name());
+ text << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK);
+ text << Atom::ParaRight;
+ generateText(text, qcn, marker);
+ }
+}
+
+/*!
+ Output the "[QmlGraphicsXxx is instantiated by QML Type Xxx]"
+ line for the class, if there should be one.
+
+ If there is no QML element, or if the class node status
+ is set to Node::Internal, do nothing.
+ */
+void HtmlGenerator::generateInstantiatedBy(ClassNode* cn, CodeMarker* marker)
+{
+ if (cn && cn->status() != Node::Internal && cn->qmlElement() != 0) {
+ const QmlTypeNode* qcn = cn->qmlElement();
+ Text text;
+ text << Atom::ParaLeft;
+ text << Atom(Atom::LinkNode,CodeMarker::stringForNode(cn));
+ text << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK);
+ text << Atom(Atom::String, cn->name());
+ text << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK);
+ if (qcn->isQmlType())
+ text << " is instantiated by QML Type ";
+ else
+ text << " is instantiated by Javascript Type ";
+ text << Atom(Atom::LinkNode,CodeMarker::stringForNode(qcn));
+ text << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK);
+ text << Atom(Atom::String, qcn->name());
+ text << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK);
+ text << Atom::ParaRight;
+ generateText(text, cn, marker);
+ }
+}
+
+void HtmlGenerator::generateExtractionMark(const Node *node, ExtractionMarkType markType)
+{
+ if (markType != EndMark) {
+ out() << "<!-- $$$" + node->name();
+ if (markType == MemberMark) {
+ if (node->type() == Node::Function) {
+ const FunctionNode *func = static_cast<const FunctionNode *>(node);
+ if (!func->hasAssociatedProperties()) {
+ if (func->overloadNumber() == 0)
+ out() << "[overload1]";
+ out() << "$$$" + func->name() + func->rawParameters().remove(' ');
+ }
+ } else if (node->type() == Node::Property) {
+ out() << "-prop";
+ const PropertyNode *prop = static_cast<const PropertyNode *>(node);
+ const NodeList &list = prop->functions();
+ foreach (const Node *propFuncNode, list) {
+ if (propFuncNode->type() == Node::Function) {
+ const FunctionNode *func = static_cast<const FunctionNode *>(propFuncNode);
+ out() << "$$$" + func->name() + func->rawParameters().remove(' ');
+ }
+ }
+ } else if (node->type() == Node::Enum) {
+ const EnumNode *enumNode = static_cast<const EnumNode *>(node);
+ foreach (const EnumItem &item, enumNode->items())
+ out() << "$$$" + item.name();
+ }
+ } else if (markType == BriefMark) {
+ out() << "-brief";
+ } else if (markType == DetailedDescriptionMark) {
+ out() << "-description";
+ }
+ out() << " -->\n";
+ } else {
+ out() << "<!-- @@@" + node->name() + " -->\n";
+ }
+}
+
+
+/*!
+ This function outputs one or more manifest files in XML.
+ They are used by Creator.
+ */
+void HtmlGenerator::generateManifestFiles()
+{
+ generateManifestFile("examples", "example");
+ generateManifestFile("demos", "demo");
+ qdb_->exampleNodeMap().clear();
+ manifestMetaContent.clear();
+}
+
+/*!
+ This function is called by generateManifestFiles(), once
+ for each manifest file to be generated. \a manifest is the
+ type of manifest file.
+ */
+void HtmlGenerator::generateManifestFile(const QString &manifest, const QString &element)
+{
+ ExampleNodeMap& exampleNodeMap = qdb_->exampleNodeMap();
+ if (exampleNodeMap.isEmpty())
+ return;
+ QString fileName = manifest +"-manifest.xml";
+ QFile file(outputDir() + QLatin1Char('/') + fileName);
+ bool demos = false;
+ if (manifest == QLatin1String("demos"))
+ demos = true;
+
+ bool proceed = false;
+ ExampleNodeMap::Iterator i = exampleNodeMap.begin();
+ while (i != exampleNodeMap.end()) {
+ const ExampleNode* en = i.value();
+ if (demos) {
+ if (en->name().startsWith("demos")) {
+ proceed = true;
+ break;
+ }
+ }
+ else if (!en->name().startsWith("demos")) {
+ proceed = true;
+ break;
+ }
+ ++i;
+ }
+ if (!proceed || !file.open(QFile::WriteOnly | QFile::Text))
+ return;
+
+ QXmlStreamWriter writer(&file);
+ writer.setAutoFormatting(true);
+ writer.writeStartDocument();
+ writer.writeStartElement("instructionals");
+ writer.writeAttribute("module", project);
+ writer.writeStartElement(manifest);
+
+ QStringList usedAttributes;
+ i = exampleNodeMap.begin();
+ while (i != exampleNodeMap.end()) {
+ const ExampleNode* en = i.value();
+ if (demos) {
+ if (!en->name().startsWith("demos")) {
+ ++i;
+ continue;
+ }
+ }
+ else if (en->name().startsWith("demos")) {
+ ++i;
+ continue;
+ }
+ // attributes that are always written for the element
+ usedAttributes.clear();
+ usedAttributes << "name" << "docUrl" << "projectPath";
+
+ writer.writeStartElement(element);
+ writer.writeAttribute("name", en->title());
+ QString docUrl = manifestDir + fileBase(en) + ".html";
+ writer.writeAttribute("docUrl", docUrl);
+ QStringList proFiles;
+ foreach (const Node* child, en->childNodes()) {
+ if (child->docSubtype() == Node::File) {
+ QString file = child->name();
+ if (file.endsWith(".pro") || file.endsWith(".qmlproject")) {
+ proFiles << file;
+ }
+ }
+ }
+ if (!proFiles.isEmpty()) {
+ if (proFiles.size() == 1) {
+ writer.writeAttribute("projectPath", examplesPath + proFiles[0]);
+ }
+ else {
+ QString exampleName = en->name().split('/').last();
+ bool proWithExampleNameFound = false;
+ for (int j = 0; j < proFiles.size(); j++)
+ {
+ if (proFiles[j].endsWith(QStringLiteral("%1/%1.pro").arg(exampleName))
+ || proFiles[j].endsWith(QStringLiteral("%1/%1.qmlproject").arg(exampleName))) {
+ writer.writeAttribute("projectPath", examplesPath + proFiles[j]);
+ proWithExampleNameFound = true;
+ break;
+ }
+ }
+ if (!proWithExampleNameFound)
+ writer.writeAttribute("projectPath", examplesPath + proFiles[0]);
+ }
+ }
+ if (!en->imageFileName().isEmpty()) {
+ writer.writeAttribute("imageUrl", manifestDir + en->imageFileName());
+ usedAttributes << "imageUrl";
+ }
+
+ QString fullName = project + QLatin1Char('/') + en->title();
+ QSet<QString> tags;
+ for (int idx=0; idx < manifestMetaContent.size(); ++idx) {
+ foreach (const QString &name, manifestMetaContent[idx].names) {
+ bool match = false;
+ int wildcard = name.indexOf(QChar('*'));
+ switch (wildcard) {
+ case -1: // no wildcard, exact match
+ match = (fullName == name);
+ break;
+ case 0: // '*' matches all
+ match = true;
+ break;
+ default: // match with wildcard at the end
+ match = fullName.startsWith(name.left(wildcard));
+ }
+ if (match) {
+ tags += manifestMetaContent[idx].tags;
+ foreach (const QString &attr, manifestMetaContent[idx].attributes) {
+ QLatin1Char div(':');
+ QStringList attrList = attr.split(div);
+ if (attrList.count() == 1)
+ attrList.append(QStringLiteral("true"));
+ QString attrName = attrList.takeFirst();
+ if (!usedAttributes.contains(attrName)) {
+ writer.writeAttribute(attrName, attrList.join(div));
+ usedAttributes << attrName;
+ }
+ }
+ }
+ }
+ }
+
+ writer.writeStartElement("description");
+ Text brief = en->doc().briefText();
+ if (!brief.isEmpty())
+ writer.writeCDATA(brief.toString());
+ else
+ writer.writeCDATA(QString("No description available"));
+ writer.writeEndElement(); // description
+
+ // Add words from module name as tags
+ // QtQuickControls -> qt,quick,controls
+ // QtOpenGL -> qt,opengl
+ QRegExp re("([A-Z]+[a-z0-9]*(3D|GL)?)");
+ int pos = 0;
+ while ((pos = re.indexIn(project, pos)) != -1) {
+ tags << re.cap(1).toLower();
+ pos += re.matchedLength();
+ }
+ tags += QSet<QString>::fromList(en->title().toLower().split(QLatin1Char(' ')));
+
+ // Clean up tags, exclude invalid and common words
+ QSet<QString>::iterator tag_it = tags.begin();
+ QSet<QString> modified;
+ while (tag_it != tags.end()) {
+ QString s = *tag_it;
+ if (s.at(0) == '(')
+ s.remove(0, 1).chop(1);
+ if (s.endsWith(QLatin1Char(':')))
+ s.chop(1);
+
+ if (s.length() < 2
+ || s.at(0).isDigit()
+ || s.at(0) == '-'
+ || s == QLatin1String("qt")
+ || s == QLatin1String("the")
+ || s == QLatin1String("and")
+ || s.startsWith(QLatin1String("example"))
+ || s.startsWith(QLatin1String("chapter")))
+ tag_it = tags.erase(tag_it);
+ else if (s != *tag_it) {
+ modified << s;
+ tag_it = tags.erase(tag_it);
+ }
+ else
+ ++tag_it;
+ }
+ tags += modified;
+
+ if (!tags.isEmpty()) {
+ writer.writeStartElement("tags");
+ bool wrote_one = false;
+ foreach (const QString &tag, tags) {
+ if (wrote_one)
+ writer.writeCharacters(",");
+ writer.writeCharacters(tag);
+ wrote_one = true;
+ }
+ writer.writeEndElement(); // tags
+ }
+
+ QString ename = en->name().mid(en->name().lastIndexOf('/')+1);
+ QMap<int, const Node*> filesToOpen;
+ foreach (const Node* child, en->childNodes()) {
+ if (child->docSubtype() == Node::File) {
+ QFileInfo fileInfo(child->name());
+ QString fileName = fileInfo.fileName().toLower();
+ // open .qml, .cpp and .h files with a
+ // basename matching the example (project) name
+ // QMap key indicates the priority -
+ // the lowest value will be the top-most file
+ if ((fileInfo.baseName().compare(ename, Qt::CaseInsensitive) == 0)) {
+ if (fileName.endsWith(".qml"))
+ filesToOpen.insert(0, child);
+ else if (fileName.endsWith(".cpp"))
+ filesToOpen.insert(1, child);
+ else if (fileName.endsWith(".h"))
+ filesToOpen.insert(2, child);
+ }
+ // main.qml takes precedence over main.cpp
+ else if (fileName.endsWith("main.qml")) {
+ filesToOpen.insert(3, child);
+ }
+ else if (fileName.endsWith("main.cpp")) {
+ filesToOpen.insert(4, child);
+ }
+ }
+ }
+
+ QMap<int, const Node*>::const_iterator it = filesToOpen.constEnd();
+ while (it != filesToOpen.constBegin()) {
+ writer.writeStartElement("fileToOpen");
+ if (--it == filesToOpen.constBegin()) {
+ writer.writeAttribute(QStringLiteral("mainFile"), QStringLiteral("true"));
+ }
+ writer.writeCharacters(examplesPath + it.value()->name());
+ writer.writeEndElement();
+ }
+
+ writer.writeEndElement(); // example
+ ++i;
+ }
+
+ writer.writeEndElement(); // examples
+ writer.writeEndElement(); // instructionals
+ writer.writeEndDocument();
+ file.close();
+}
+
+/*!
+ Reads metacontent - additional attributes and tags to apply
+ when generating manifest files, read from config. Takes the
+ configuration class \a config as a parameter.
+
+ The manifest metacontent map is cleared immediately after
+ the manifest files have been generated.
+ */
+void HtmlGenerator::readManifestMetaContent(const Config &config)
+{
+ QStringList names = config.getStringList(CONFIG_MANIFESTMETA + Config::dot + QStringLiteral("filters"));
+
+ foreach (const QString &manifest, names) {
+ ManifestMetaFilter filter;
+ QString prefix = CONFIG_MANIFESTMETA + Config::dot + manifest + Config::dot;
+ filter.names = config.getStringSet(prefix + QStringLiteral("names"));
+ filter.attributes = config.getStringSet(prefix + QStringLiteral("attributes"));
+ filter.tags = config.getStringSet(prefix + QStringLiteral("tags"));
+ manifestMetaContent.append(filter);
+ }
+}
+
+/*!
+ Find global entities that have documentation but no
+ \e{relates} comand. Report these as errors if they
+ are not also marked \e {internal}.
+
+ type: Class
+ type: Namespace
+
+ subtype: Example
+ subtype: External page
+ subtype: Group
+ subtype: Header file
+ subtype: Module
+ subtype: Page
+ subtype: QML basic type
+ subtype: QML class
+ subtype: QML module
+ */
+void HtmlGenerator::reportOrphans(const Aggregate* parent)
+{
+ const NodeList& children = parent->childNodes();
+ if (children.size() == 0)
+ return;
+
+ bool related;
+ QString message;
+ for (int i=0; i<children.size(); ++i) {
+ Node* child = children[i];
+ if (!child || child->isInternal() || child->doc().isEmpty())
+ continue;
+ if (child->relates()) {
+ related = true;
+ message = child->relates()->name();
+ }
+ else {
+ related = false;
+ message = "has documentation but no \\relates command";
+ }
+ switch (child->type()) {
+ case Node::Namespace:
+ break;
+ case Node::Class:
+ break;
+ case Node::QmlType:
+ break;
+ case Node::QmlBasicType:
+ break;
+ case Node::Group:
+ break;
+ case Node::Module:
+ break;
+ case Node::QmlModule:
+ break;
+ case Node::Document:
+ switch (child->docSubtype()) {
+ case Node::Example:
+ break;
+ case Node::HeaderFile:
+ break;
+ case Node::File:
+ break;
+ case Node::Image:
+ break;
+ case Node::Page:
+ break;
+ case Node::ExternalPage:
+ break;
+ default:
+ break;
+ }
+ break;
+ case Node::Enum:
+ if (!related)
+ child->location().warning(tr("Global enum, %1, %2").arg(child->name()).arg(message));
+ break;
+ case Node::Typedef:
+ if (!related)
+ child->location().warning(tr("Global typedef, %1, %2").arg(child->name()).arg(message));
+ break;
+ case Node::Function:
+ if (!related) {
+ const FunctionNode* fn = static_cast<const FunctionNode*>(child);
+ if (fn->isMacro())
+ child->location().warning(tr("Global macro, %1, %2").arg(child->name()).arg(message));
+ else
+ child->location().warning(tr("Global function, %1(), %2").arg(child->name()).arg(message));
+ }
+ break;
+ case Node::Property:
+ break;
+ case Node::Variable:
+ if (!related)
+ child->location().warning(tr("Global variable, %1, %2").arg(child->name()).arg(message));
+ break;
+ case Node::QmlPropertyGroup:
+ break;
+ case Node::QmlProperty:
+ if (!related)
+ child->location().warning(tr("Global QML property, %1, %2").arg(child->name()).arg(message));
+ break;
+ case Node::QmlSignal:
+ if (!related)
+ child->location().warning(tr("Global QML, signal, %1 %2").arg(child->name()).arg(message));
+ break;
+ case Node::QmlSignalHandler:
+ if (!related)
+ child->location().warning(tr("Global QML signal handler, %1, %2").arg(child->name()).arg(message));
+ break;
+ case Node::QmlMethod:
+ if (!related)
+ child->location().warning(tr("Global QML method, %1, %2").arg(child->name()).arg(message));
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+/*!
+ Returns a reference to the XML stream writer currently in use.
+ There is one XML stream writer open for each XML file being
+ written, and they are kept on a stack. The one on top of the
+ stack is the one being written to at the moment. In the HTML
+ output generator, it is perhaps impossible for there to ever
+ be more than one writer open.
+ */
+QXmlStreamWriter& HtmlGenerator::xmlWriter()
+{
+ return *xmlWriterStack.top();
+}
+
+/*!
+ This function is only called for writing ditamaps.
+
+ Calls beginSubPage() in the base class to open the file.
+ Then creates a new XML stream writer using the IO device
+ from opened file and pushes the XML writer onto a stackj.
+ Creates the file named \a fileName in the output directory.
+ Attaches a QTextStream to the created file, which is written
+ to all over the place using out(). Finally, it sets some
+ parameters in the XML writer and calls writeStartDocument().
+
+ It also ensures that a GUID map is created for the output file.
+ */
+void HtmlGenerator::beginDitamapPage(const Aggregate* node, const QString& fileName)
+{
+ Generator::beginSubPage(node,fileName);
+ QXmlStreamWriter* writer = new QXmlStreamWriter(out().device());
+ xmlWriterStack.push(writer);
+ writer->setAutoFormatting(true);
+ writer->setAutoFormattingIndent(4);
+ writer->writeStartDocument();
+}
+
+/*!
+ This function is only called for writing ditamaps.
+
+ Calls writeEndDocument() and then pops the XML stream writer
+ off the stack and deletes it. Then it calls endSubPage() in
+ the base class to close the device.
+ */
+void HtmlGenerator::endDitamapPage()
+{
+ xmlWriter().writeEndDocument();
+ delete xmlWriterStack.pop();
+ Generator::endSubPage();
+}
+
+/*!
+ This function is only called for writing ditamaps.
+
+ Creates the DITA map from the topicrefs in \a node,
+ which is a DitaMapNode.
+ */
+void HtmlGenerator::writeDitaMap(const DitaMapNode* node)
+{
+ beginDitamapPage(node,node->name());
+
+ QString doctype = "<!DOCTYPE map PUBLIC \"-//OASIS//DTD DITA Map//EN\" \"map.dtd\">";
+
+ xmlWriter().writeDTD(doctype);
+ xmlWriter().writeStartElement("map");
+ xmlWriter().writeStartElement("topicmeta");
+ xmlWriter().writeStartElement("shortdesc");
+ xmlWriter().writeCharacters(node->title());
+ xmlWriter().writeEndElement(); // </shortdesc>
+ xmlWriter().writeEndElement(); // </topicmeta>
+ DitaRefList map = node->map();
+ writeDitaRefs(map);
+ endDitamapPage();
+}
+
+/*!
+ Write the \a ditarefs to the current output file.
+ */
+void HtmlGenerator::writeDitaRefs(const DitaRefList& ditarefs)
+{
+ foreach (DitaRef* t, ditarefs) {
+ if (t->isMapRef())
+ xmlWriter().writeStartElement("mapref");
+ else
+ xmlWriter().writeStartElement("topicref");
+ xmlWriter().writeAttribute("navtitle",t->navtitle());
+ if (t->href().isEmpty()) {
+ const DocumentNode* dn = qdb_->findDocumentNodeByTitle(t->navtitle());
+ if (dn)
+ xmlWriter().writeAttribute("href",fileName(dn));
+ }
+ else
+ xmlWriter().writeAttribute("href",t->href());
+ if (t->subrefs() && !t->subrefs()->isEmpty())
+ writeDitaRefs(*(t->subrefs()));
+ xmlWriter().writeEndElement(); // </topicref> or </mapref>
+ }
+}
+
+/*!
+ Generates bold Note lines that explain how function \a fn
+ is associated with each of its associated properties.
+ */
+void HtmlGenerator::generateAssociatedPropertyNotes(const FunctionNode* fn)
+{
+ if (fn->hasAssociatedProperties()) {
+ out() << "<p><b>Note:</b> ";
+ foreach (const PropertyNode* pn, fn->associatedProperties()) {
+ QString msg;
+ switch (pn->role(fn)) {
+ case PropertyNode::Getter:
+ msg = QStringLiteral("Getter function ");
+ break;
+ case PropertyNode::Setter:
+ msg = QStringLiteral("Setter function ");
+ break;
+ case PropertyNode::Resetter:
+ msg = QStringLiteral("Resetter function ");
+ break;
+ case PropertyNode::Notifier:
+ msg = QStringLiteral("Notifier signal ");
+ break;
+ default:
+ break;
+ }
+ QString link = linkForNode(pn, 0);
+ out() << msg << "for property <a href=\"" << link << "\">" << pn->name() << "</a>. ";
+ }
+ out() << "</p>";
+ }
+}
+
+QT_END_NAMESPACE
diff --git a/src/qdoc/htmlgenerator.h b/src/qdoc/htmlgenerator.h
new file mode 100644
index 000000000..0cf367b43
--- /dev/null
+++ b/src/qdoc/htmlgenerator.h
@@ -0,0 +1,292 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL21$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** 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 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** As a special exception, The Qt Company gives you certain additional
+** rights. These rights are described in The Qt Company LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*
+ htmlgenerator.h
+*/
+
+#ifndef HTMLGENERATOR_H
+#define HTMLGENERATOR_H
+
+#include <qhash.h>
+#include <qregexp.h>
+#include <qxmlstream.h>
+#include "codemarker.h"
+#include "config.h"
+#include "generator.h"
+
+QT_BEGIN_NAMESPACE
+
+class HelpProjectWriter;
+
+class HtmlGenerator : public Generator
+{
+ Q_DECLARE_TR_FUNCTIONS(QDoc::HtmlGenerator)
+
+public:
+ enum SinceType {
+ Namespace,
+ Class,
+ MemberFunction,
+ NamespaceFunction,
+ GlobalFunction,
+ Macro,
+ Enum,
+ Typedef,
+ Property,
+ Variable,
+ QmlClass,
+ QmlProperty,
+ QmlSignal,
+ QmlSignalHandler,
+ QmlMethod,
+ LastSinceType
+ };
+
+public:
+ HtmlGenerator();
+ ~HtmlGenerator();
+
+ virtual void initializeGenerator(const Config& config) Q_DECL_OVERRIDE;
+ virtual void terminateGenerator() Q_DECL_OVERRIDE;
+ virtual QString format() Q_DECL_OVERRIDE;
+ virtual void generateDocs() Q_DECL_OVERRIDE;
+ void generateManifestFiles();
+
+ QString protectEnc(const QString &string);
+ static QString protect(const QString &string, const QString &encoding = "ISO-8859-1");
+ static QString sinceTitle(int i) { return sinceTitles[i]; }
+
+protected:
+ virtual void generateQAPage() Q_DECL_OVERRIDE;
+ QString generateLinksToLinksPage(const QString& module, CodeMarker* marker);
+ QString generateLinksToBrokenLinksPage(CodeMarker* marker, int& count);
+ virtual int generateAtom(const Atom *atom,
+ const Node *relative,
+ CodeMarker *marker) Q_DECL_OVERRIDE;
+ virtual void generateClassLikeNode(Aggregate* inner, CodeMarker* marker) Q_DECL_OVERRIDE;
+ virtual void generateQmlTypePage(QmlTypeNode* qcn, CodeMarker* marker) Q_DECL_OVERRIDE;
+ virtual void generateQmlBasicTypePage(QmlBasicTypeNode* qbtn, CodeMarker* marker) Q_DECL_OVERRIDE;
+ virtual void generateDocumentNode(DocumentNode* dn, CodeMarker* marker) Q_DECL_OVERRIDE;
+ virtual void generateCollectionNode(CollectionNode* cn, CodeMarker* marker) Q_DECL_OVERRIDE;
+ virtual QString fileExtension() const Q_DECL_OVERRIDE;
+ virtual QString refForNode(const Node *node);
+ virtual QString linkForNode(const Node *node, const Node *relative);
+
+ void generateManifestFile(const QString &manifest, const QString &element);
+ void readManifestMetaContent(const Config &config);
+ void generateKeywordAnchors(const Node* node);
+ void generateAssociatedPropertyNotes(const FunctionNode* fn);
+
+private:
+ enum SubTitleSize { SmallSubTitle, LargeSubTitle };
+ enum ExtractionMarkType {
+ BriefMark,
+ DetailedDescriptionMark,
+ MemberMark,
+ EndMark
+ };
+
+ struct ManifestMetaFilter
+ {
+ QSet<QString> names;
+ QSet<QString> attributes;
+ QSet<QString> tags;
+ };
+
+ const QPair<QString,QString> anchorForNode(const Node *node);
+ void generateNavigationBar(const QString &title,
+ const Node *node,
+ CodeMarker *marker,
+ const QString &buildversion,
+ bool tableItems = false);
+ void generateHeader(const QString& title,
+ const Node *node = 0,
+ CodeMarker *marker = 0);
+ void generateTitle(const QString& title,
+ const Text &subTitle,
+ SubTitleSize subTitleSize,
+ const Node *relative,
+ CodeMarker *marker);
+ void generateFooter(const Node *node = 0);
+ void generateRequisites(Aggregate *inner,
+ CodeMarker *marker);
+ void generateQmlRequisites(QmlTypeNode *qcn,
+ CodeMarker *marker);
+ void generateBrief(const Node *node,
+ CodeMarker *marker,
+ const Node *relative = 0);
+ void generateIncludes(const Aggregate *inner, CodeMarker *marker);
+ void generateTableOfContents(const Node *node,
+ CodeMarker *marker,
+ QList<Section>* sections = 0);
+ void generateSidebar();
+ QString generateListOfAllMemberFile(const Aggregate *inner,
+ CodeMarker *marker);
+ QString generateAllQmlMembersFile(QmlTypeNode* qml_cn, CodeMarker* marker);
+ QString generateLowStatusMemberFile(Aggregate *inner,
+ CodeMarker *marker,
+ CodeMarker::Status status);
+ QString generateQmlMemberFile(QmlTypeNode* qcn,
+ CodeMarker *marker,
+ CodeMarker::Status status);
+ void generateClassHierarchy(const Node *relative, NodeMap &classMap);
+ void generateAnnotatedList(const Node* relative, CodeMarker* marker, const NodeMultiMap& nodeMap);
+ void generateAnnotatedList(const Node* relative, CodeMarker* marker, const NodeList& nodes);
+ void generateCompactList(ListType listType,
+ const Node *relative,
+ const NodeMultiMap &classMap,
+ bool includeAlphabet,
+ QString commonPrefix);
+ void generateFunctionIndex(const Node *relative);
+ void generateLegaleseList(const Node *relative, CodeMarker *marker);
+ void generateList(const Node* relative, CodeMarker* marker, const QString& selector);
+ void generateSectionList(const Section& section,
+ const Node *relative,
+ CodeMarker *marker,
+ CodeMarker::SynopsisStyle style);
+ void generateQmlSummary(const Section& section,
+ const Node *relative,
+ CodeMarker *marker);
+ void generateQmlItem(const Node *node,
+ const Node *relative,
+ CodeMarker *marker,
+ bool summary);
+ void generateDetailedQmlMember(Node *node,
+ const Aggregate *relative,
+ CodeMarker *marker);
+ void generateQmlInherits(QmlTypeNode* qcn, CodeMarker* marker) Q_DECL_OVERRIDE;
+ void generateQmlInstantiates(QmlTypeNode* qcn, CodeMarker* marker);
+ void generateInstantiatedBy(ClassNode* cn, CodeMarker* marker);
+
+ void generateSection(const NodeList& nl,
+ const Node *relative,
+ CodeMarker *marker,
+ CodeMarker::SynopsisStyle style);
+ void generateSynopsis(const Node *node,
+ const Node *relative,
+ CodeMarker *marker,
+ CodeMarker::SynopsisStyle style,
+ bool alignNames = false,
+ const QString* prefix = 0);
+ void generateSectionInheritedList(const Section& section, const Node *relative);
+ QString highlightedCode(const QString& markedCode,
+ const Node* relative,
+ bool alignNames = false);
+
+ void generateFullName(const Node *apparentNode, const Node *relative, const Node *actualNode = 0);
+ void generateDetailedMember(const Node *node,
+ const Aggregate *relative,
+ CodeMarker *marker);
+ void generateLink(const Atom *atom, CodeMarker *marker);
+ void generateStatus(const Node *node, CodeMarker *marker);
+
+ QString getLink(const Atom *atom, const Node *relative, const Node** node);
+ QString getAutoLink(const Atom *atom, const Node *relative, const Node** node);
+
+ QString registerRef(const QString& ref);
+ virtual QString fileBase(const Node *node) const Q_DECL_OVERRIDE;
+ QString fileName(const Node *node);
+ static int hOffset(const Node *node);
+ static bool isThreeColumnEnumValueTable(const Atom *atom);
+#ifdef GENERATE_MAC_REFS
+ void generateMacRef(const Node *node, CodeMarker *marker);
+#endif
+ void beginLink(const QString &link, const Node *node, const Node *relative);
+ void endLink();
+ void generateExtractionMark(const Node *node, ExtractionMarkType markType);
+ void reportOrphans(const Aggregate* parent);
+
+ void beginDitamapPage(const Aggregate* node, const QString& fileName);
+ void endDitamapPage();
+ void writeDitaMap(const DitaMapNode* node);
+ void writeDitaRefs(const DitaRefList& ditarefs);
+ QXmlStreamWriter& xmlWriter();
+
+ QHash<QString, QString> refMap;
+ int codeIndent;
+ QString codePrefix;
+ QString codeSuffix;
+ HelpProjectWriter *helpProjectWriter;
+ bool inObsoleteLink;
+ QRegExp funcLeftParen;
+ QString style;
+ QString headerScripts;
+ QString headerStyles;
+ QString endHeader;
+ QString postHeader;
+ QString postPostHeader;
+ QString prologue;
+ QString footer;
+ QString address;
+ bool pleaseGenerateMacRef;
+ bool noNavigationBar;
+ QString project;
+ QString projectDescription;
+ QString projectUrl;
+ QString navigationLinks;
+ QString manifestDir;
+ QString examplesPath;
+ QStringList stylesheets;
+ QStringList customHeadElements;
+ bool obsoleteLinks;
+ QStack<QXmlStreamWriter*> xmlWriterStack;
+ static int id;
+ QList<ManifestMetaFilter> manifestMetaContent;
+ QString homepage;
+ QString landingpage;
+ QString cppclassespage;
+ QString qmltypespage;
+ QString buildversion;
+ QString qflagsHref_;
+ int tocDepth;
+
+public:
+ static bool debugging_on;
+ static QString divNavTop;
+};
+
+#define HTMLGENERATOR_ADDRESS "address"
+#define HTMLGENERATOR_FOOTER "footer"
+#define HTMLGENERATOR_GENERATEMACREFS "generatemacrefs" // ### document me
+#define HTMLGENERATOR_POSTHEADER "postheader"
+#define HTMLGENERATOR_POSTPOSTHEADER "postpostheader"
+#define HTMLGENERATOR_PROLOGUE "prologue"
+#define HTMLGENERATOR_NONAVIGATIONBAR "nonavigationbar"
+#define HTMLGENERATOR_NOSUBDIRS "nosubdirs"
+#define HTMLGENERATOR_TOCDEPTH "tocdepth"
+
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/qdoc/jscodemarker.cpp b/src/qdoc/jscodemarker.cpp
new file mode 100644
index 000000000..669040da0
--- /dev/null
+++ b/src/qdoc/jscodemarker.cpp
@@ -0,0 +1,142 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL21$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** 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 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** As a special exception, The Qt Company gives you certain additional
+** rights. These rights are described in The Qt Company LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*
+ jscodemarker.cpp
+*/
+
+#include "jscodemarker.h"
+
+#include "atom.h"
+#include "node.h"
+#include "qmlmarkupvisitor.h"
+#include "text.h"
+#include "tree.h"
+#include "generator.h"
+
+#include <private/qqmljsast_p.h>
+#include <private/qqmljsengine_p.h>
+#include <private/qqmljslexer_p.h>
+#include <private/qqmljsparser_p.h>
+
+QT_BEGIN_NAMESPACE
+
+JsCodeMarker::JsCodeMarker()
+{
+}
+
+JsCodeMarker::~JsCodeMarker()
+{
+}
+
+/*!
+ Returns \c true if the \a code is recognized by the parser.
+ */
+bool JsCodeMarker::recognizeCode(const QString &code)
+{
+ QQmlJS::Engine engine;
+ QQmlJS::Lexer lexer(&engine);
+ QQmlJS::Parser parser(&engine);
+
+ QString newCode = code;
+ QList<QQmlJS::AST::SourceLocation> pragmas = extractPragmas(newCode);
+ lexer.setCode(newCode, 1);
+
+ return parser.parseProgram();
+}
+
+/*!
+ Returns \c true if \a ext is any of a list of file extensions
+ for the QML language.
+ */
+bool JsCodeMarker::recognizeExtension(const QString &ext)
+{
+ return ext == "js" || ext == "json";
+}
+
+/*!
+ Returns \c true if the \a language is recognized. We recognize JavaScript,
+ ECMAScript and JSON.
+ */
+bool JsCodeMarker::recognizeLanguage(const QString &language)
+{
+ return language == "JavaScript" || language == "ECMAScript" || language == "JSON";
+}
+
+/*!
+ Returns the type of atom used to represent JavaScript code in the documentation.
+*/
+Atom::AtomType JsCodeMarker::atomType() const
+{
+ return Atom::JavaScript;
+}
+
+QString JsCodeMarker::markedUpCode(const QString &code,
+ const Node *relative,
+ const Location &location)
+{
+ return addMarkUp(code, relative, location);
+}
+
+QString JsCodeMarker::addMarkUp(const QString &code,
+ const Node * /* relative */,
+ const Location &location)
+{
+ QQmlJS::Engine engine;
+ QQmlJS::Lexer lexer(&engine);
+
+ QString newCode = code;
+ QList<QQmlJS::AST::SourceLocation> pragmas = extractPragmas(newCode);
+ lexer.setCode(newCode, 1);
+
+ QQmlJS::Parser parser(&engine);
+ QString output;
+
+ if (parser.parseProgram()) {
+ QQmlJS::AST::Node *ast = parser.rootNode();
+ // Pass the unmodified code to the visitor so that pragmas and other
+ // unhandled source text can be output.
+ QmlMarkupVisitor visitor(code, pragmas, &engine);
+ QQmlJS::AST::Node::accept(ast, &visitor);
+ output = visitor.markedUpCode();
+ } else {
+ location.warning(location.fileName() +
+ tr("Unable to parse JavaScript: \"%1\" at line %2, column %3").arg(
+ parser.errorMessage()).arg(parser.errorLineNumber()).arg(
+ parser.errorColumnNumber()));
+ output = protect(code);
+ }
+ return output;
+}
+
+QT_END_NAMESPACE
diff --git a/src/qdoc/jscodemarker.h b/src/qdoc/jscodemarker.h
new file mode 100644
index 000000000..ccba2ca4c
--- /dev/null
+++ b/src/qdoc/jscodemarker.h
@@ -0,0 +1,69 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL21$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** 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 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** As a special exception, The Qt Company gives you certain additional
+** rights. These rights are described in The Qt Company LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*
+ jscodemarker.h
+*/
+
+#ifndef JSCODEMARKER_H
+#define JSCODEMARKER_H
+
+#include "qmlcodemarker.h"
+
+QT_BEGIN_NAMESPACE
+
+class JsCodeMarker : public QmlCodeMarker
+{
+ Q_DECLARE_TR_FUNCTIONS(QDoc::JsCodeMarker)
+
+public:
+ JsCodeMarker();
+ ~JsCodeMarker();
+
+ virtual bool recognizeCode(const QString &code) Q_DECL_OVERRIDE;
+ virtual bool recognizeExtension(const QString &ext) Q_DECL_OVERRIDE;
+ virtual bool recognizeLanguage(const QString &language) Q_DECL_OVERRIDE;
+ virtual Atom::AtomType atomType() const Q_DECL_OVERRIDE;
+
+ virtual QString markedUpCode(const QString &code,
+ const Node *relative,
+ const Location &location) Q_DECL_OVERRIDE;
+
+private:
+ QString addMarkUp(const QString &code, const Node *relative,
+ const Location &location);
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/qdoc/location.cpp b/src/qdoc/location.cpp
new file mode 100644
index 000000000..98b63fd03
--- /dev/null
+++ b/src/qdoc/location.cpp
@@ -0,0 +1,448 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL21$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** 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 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** As a special exception, The Qt Company gives you certain additional
+** rights. These rights are described in The Qt Company LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <qdebug.h>
+#include "config.h"
+#include "location.h"
+#include "generator.h"
+#include <qdir.h>
+#include <qregexp.h>
+#include <stdlib.h>
+#include <limits.h>
+
+#include <stdio.h>
+
+QT_BEGIN_NAMESPACE
+
+const Location Location::null;
+
+int Location::tabSize;
+QString Location::programName;
+QRegExp *Location::spuriousRegExp = 0;
+bool Location::logProgress_ = false;
+
+/*!
+ \class Location
+
+ \brief The Location class provides a way to mark a location 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 \c true if there is no file name set yet; returns \c 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. If the Location is
+ empty, the returned string is null.
+
+ \sa lineNo(), columnNo()
+ */
+
+/*!
+ Returns the file name part of the file path, ie the current
+ file. Returns an empty string if the file path is empty.
+ */
+QString Location::fileName() const
+{
+ QString fp = filePath();
+ return (fp.isEmpty() ? fp : fp.mid(fp.lastIndexOf('/') + 1));
+}
+
+
+/*!
+ Returns the suffix of the file name. Returns an empty string
+ if the file path is empty.
+ */
+QString Location::fileSuffix() const
+{
+ QString fp = filePath();
+ return (fp.isEmpty() ? fp : fp.mid(fp.lastIndexOf('.') + 1));
+}
+
+/*!
+ \brief Returns \a path which is canonicalized and relative to the config file.
+
+ QDir::relativeFilePath does not canonicalize the paths, so
+ if the config file is located at qtbase\src\widgets\doc\qtwidgets.qdocconf
+ and it has a reference to any ancestor folder (e.g. ".." or even "../doc")
+ */
+QString Location::canonicalRelativePath(const QString &path)
+{
+ QDir configFileDir(QDir::current());
+ QDir dir(path);
+ const QString canon = dir.canonicalPath();
+ return configFileDir.relativeFilePath(canon);
+}
+
+/*! \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. Does not write the message if qdoc is in
+ the Prepare phase.
+ */
+void Location::warning(const QString& message, const QString& details) const
+{
+ if (!Generator::preparing() || Generator::singleExec())
+ emitMessage(Warning, message, details);
+}
+
+/*!
+ Writes \a message and \a detals to stderr as a formatted
+ error message. Does not write the message if qdoc is in
+ the Prepare phase.
+ */
+void Location::error(const QString& message, const QString& details) const
+{
+ if (!Generator::preparing() || Generator::singleExec())
+ emitMessage(Error, message, details);
+}
+
+/*!
+ Writes \a message and \a detals to stderr as a formatted
+ error message and then exits the program. qdoc prints fatal
+ errors in either phase (Prepare or Generate).
+ */
+void Location::fatal(const QString& message, const QString& details) const
+{
+ emitMessage(Error, message, details);
+ information(message);
+ information(details);
+ information("Aborting");
+ exit(EXIT_FAILURE);
+}
+
+/*!
+ Writes \a message and \a detals to stderr as a formatted
+ report message.
+ */
+void Location::report(const QString& message, const QString& details) const
+{
+ emitMessage(Report, message, details);
+}
+
+/*!
+ 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);
+}
+
+/*!
+ Prints \a message to \c stderr followed by a \c{'\n'},
+ but only if the -log-progress option is set.
+ */
+void Location::logToStdErr(const QString& message)
+{
+ if (logProgress_) {
+ fprintf(stderr, "LOG: %s\n", message.toLatin1().data());
+ fflush(stderr);
+ }
+}
+
+/*!
+ 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 + QLatin1Char(']');
+ result.replace("\n", "\n ");
+ if (type == Error)
+ result.prepend(tr(": error: "));
+ else if (type == Warning)
+ result.prepend(tr(": warning: "));
+ if (type != Report)
+ 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();
+ }
+ return str;
+}
+
+QString Location::top() const
+{
+ QString str = filePath();
+ if (!QDir::isAbsolutePath(str)) {
+ QDir path(str);
+ str = path.absolutePath();
+ }
+ if (lineNo() >= 1) {
+ str += QLatin1Char(':');
+ str += QString::number(lineNo());
+ }
+ if (etc())
+ str += QLatin1String(" (etc.)");
+ return str;
+}
+
+QT_END_NAMESPACE
diff --git a/src/qdoc/location.h b/src/qdoc/location.h
new file mode 100644
index 000000000..1cb8e3aec
--- /dev/null
+++ b/src/qdoc/location.h
@@ -0,0 +1,133 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL21$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** 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 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** As a special exception, The Qt Company gives you certain additional
+** rights. These rights are described in The Qt Company LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*
+ location.h
+*/
+
+#ifndef LOCATION_H
+#define LOCATION_H
+
+#include <qstack.h>
+#include <qcoreapplication.h>
+
+QT_BEGIN_NAMESPACE
+
+class Config;
+class QRegExp;
+
+class Location
+{
+ Q_DECLARE_TR_FUNCTIONS(QDoc::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;
+ QString fileSuffix() 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;
+ void report(const QString& message,
+ const QString& details = QString()) const;
+
+ 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);
+ static void logToStdErr(const QString& message);
+ static void startLoggingProgress() { logProgress_ = true; }
+ static void stopLoggingProgress() { logProgress_ = false; }
+ static QString canonicalRelativePath(const QString &path);
+
+private:
+ enum MessageType { Warning, Error, Report };
+
+ struct StackEntry
+ {
+ QString filePath;
+ int lineNo;
+ int columnNo;
+ };
+ friend class QTypeInfo<StackEntry>;
+
+ 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;
+ static bool logProgress_;
+};
+Q_DECLARE_TYPEINFO(Location::StackEntry, Q_MOVABLE_TYPE);
+Q_DECLARE_TYPEINFO(Location, Q_COMPLEX_TYPE); // stkTop = &stkBottom
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/qdoc/main.cpp b/src/qdoc/main.cpp
new file mode 100644
index 000000000..d6e00b483
--- /dev/null
+++ b/src/qdoc/main.cpp
@@ -0,0 +1,794 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL21$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** 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 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** As a special exception, The Qt Company gives you certain additional
+** rights. These rights are described in The Qt Company LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <qglobal.h>
+#include <stdlib.h>
+#include "codemarker.h"
+#include "codeparser.h"
+#include "config.h"
+#include "cppcodemarker.h"
+#include "cppcodeparser.h"
+#include "doc.h"
+#include "htmlgenerator.h"
+#include "location.h"
+#include "plaincodemarker.h"
+#include "puredocparser.h"
+#include "tokenizer.h"
+#include "tree.h"
+#include "qdocdatabase.h"
+#include "jscodemarker.h"
+#include "qmlcodemarker.h"
+#include "qmlcodeparser.h"
+#include <qdatetime.h>
+#include <qdebug.h>
+#include "qtranslator.h"
+#ifndef QT_BOOTSTRAPPED
+# include "qcoreapplication.h"
+#endif
+#include "qcommandlineoption.h"
+#include "qcommandlineparser.h"
+
+#include <algorithm>
+
+QT_BEGIN_NAMESPACE
+
+bool creationTimeBefore(const QFileInfo &fi1, const QFileInfo &fi2)
+{
+ return fi1.lastModified() < fi2.lastModified();
+}
+
+static bool highlighting = false;
+static bool showInternal = false;
+static bool singleExec = false;
+static bool writeQaPages = false;
+static bool redirectDocumentationToDevNull = false;
+static bool noLinkErrors = false;
+static bool autolinkErrors = false;
+static bool obsoleteLinks = false;
+static QStringList defines;
+static QStringList dependModules;
+static QStringList indexDirs;
+static QString currentDir;
+static QString prevCurrentDir;
+static QHash<QString,QString> defaults;
+#ifndef QT_NO_TRANSLATION
+typedef QPair<QString, QTranslator*> Translator;
+static QList<Translator> translators;
+#endif
+
+/*!
+ Read some XML indexes containing definitions from other
+ documentation sets. \a config contains a variable that
+ lists directories where index files can bge found. It also
+ contains the \c depends variable, which lists the modules
+ that the current module depends on.
+*/
+static void loadIndexFiles(Config& config)
+{
+ QDocDatabase* qdb = QDocDatabase::qdocDB();
+ QStringList indexFiles;
+ QStringList configIndexes = config.getStringList(CONFIG_INDEXES);
+ foreach (const QString &index, configIndexes) {
+ QFileInfo fi(index);
+ if (fi.exists() && fi.isFile())
+ indexFiles << index;
+ else
+ Location::null.warning(QString("Index file not found: %1").arg(index));
+ }
+
+ dependModules += config.getStringList(CONFIG_DEPENDS);
+ dependModules.removeDuplicates();
+
+ bool noOutputSubdirs = false;
+ QString singleOutputSubdir;
+ if (config.getBool(QString("HTML.nosubdirs"))) {
+ noOutputSubdirs = true;
+ singleOutputSubdir = config.getString("HTML.outputsubdir");
+ if (singleOutputSubdir.isEmpty())
+ singleOutputSubdir = "html";
+ }
+
+ if (dependModules.size() > 0) {
+ if (indexDirs.size() > 0) {
+ for (int i = 0; i < indexDirs.size(); i++) {
+ if (indexDirs[i].startsWith("..")) {
+ const QString prefix(QDir(currentDir).relativeFilePath(prevCurrentDir));
+ if (!prefix.isEmpty())
+ indexDirs[i].prepend(prefix + QLatin1Char('/'));
+ }
+ }
+ /*
+ Add all subdirectories of the indexdirs as dependModules,
+ when an asterisk is used in the 'depends' list.
+ */
+ if (dependModules.contains("*")) {
+ dependModules.removeOne("*");
+ for (int i = 0; i < indexDirs.size(); i++) {
+ QDir scanDir = QDir(indexDirs[i]);
+ scanDir.setFilter(QDir::Dirs | QDir::NoDotAndDotDot);
+ QFileInfoList dirList = scanDir.entryInfoList();
+ for (int j = 0; j < dirList.size(); j++) {
+ if (dirList[j].fileName().toLower() != config.getString(CONFIG_PROJECT).toLower())
+ dependModules.append(dirList[j].fileName());
+ }
+ }
+ }
+ for (int i = 0; i < dependModules.size(); i++) {
+ QString indexToAdd;
+ QList<QFileInfo> foundIndices;
+ for (int j = 0; j < indexDirs.size(); j++) {
+ QString fileToLookFor = indexDirs[j] + QLatin1Char('/');
+ if (noOutputSubdirs)
+ fileToLookFor += singleOutputSubdir + QLatin1Char('/');
+ else
+ fileToLookFor += dependModules[i] + QLatin1Char('/');
+ fileToLookFor += dependModules[i] + QLatin1String(".index");
+ if (QFile::exists(fileToLookFor)) {
+ QFileInfo tempFileInfo(fileToLookFor);
+ if (!foundIndices.contains(tempFileInfo))
+ foundIndices.append(tempFileInfo);
+ }
+ }
+ std::sort(foundIndices.begin(), foundIndices.end(), creationTimeBefore);
+ if (foundIndices.size() > 1) {
+ /*
+ QDoc should always use the last entry in the multimap when there are
+ multiple index files for a module, since the last modified file has the
+ highest UNIX timestamp.
+ */
+ QStringList indexPaths;
+ for (int k = 0; k < foundIndices.size(); k++)
+ indexPaths << foundIndices[k].absoluteFilePath();
+ Location::null.warning(QString("Multiple index files found for dependency \"%1\":\n%2").arg(
+ dependModules[i], indexPaths.join('\n')));
+ Location::null.warning(QString("Using %1 as index file for dependency \"%2\"").arg(
+ foundIndices[foundIndices.size() - 1].absoluteFilePath(),
+ dependModules[i]));
+ indexToAdd = foundIndices[foundIndices.size() - 1].absoluteFilePath();
+ }
+ else if (foundIndices.size() == 1) {
+ indexToAdd = foundIndices[0].absoluteFilePath();
+ }
+ if (!indexToAdd.isEmpty()) {
+ if (!indexFiles.contains(indexToAdd))
+ indexFiles << indexToAdd;
+ }
+ else {
+ Location::null.warning(QString("\"%1\" Cannot locate index file for dependency \"%2\"").arg(
+ config.getString(CONFIG_PROJECT), dependModules[i]));
+ }
+ }
+ }
+ else {
+ Location::null.warning(QLatin1String("Dependent modules specified, but no index directories were set. There will probably be errors for missing links."));
+ }
+ }
+ qdb->readIndexes(indexFiles);
+}
+
+/*!
+ Processes the qdoc config file \a fileName. This is the
+ controller for all of qdoc.
+ */
+static void processQdocconfFile(const QString &fileName)
+{
+ /*
+ The Config instance represents the configuration data for qdoc.
+ All the other classes are initialized with the config. Below, we
+ initialize the configuration with some default values.
+
+ I don't think the call to translate() does anything here. For one
+ thing, the translators haven't been installed at this point. And
+ I doubt any translator would translate QDoc anyway. But I left it
+ here because it does no harm.
+ */
+ Config config(QCoreApplication::translate("QDoc", "qdoc"));
+
+ QHash<QString,QString>::iterator iter;
+ for (iter = defaults.begin(); iter != defaults.end(); ++iter)
+ config.setStringList(iter.key(), QStringList() << iter.value());
+
+ config.setStringList(CONFIG_SYNTAXHIGHLIGHTING, QStringList(highlighting ? "true" : "false"));
+ config.setStringList(CONFIG_SHOWINTERNAL, QStringList(showInternal ? "true" : "false"));
+ config.setStringList(CONFIG_SINGLEEXEC, QStringList(singleExec ? "true" : "false"));
+ config.setStringList(CONFIG_WRITEQAPAGES, QStringList(writeQaPages ? "true" : "false"));
+ config.setStringList(CONFIG_REDIRECTDOCUMENTATIONTODEVNULL, QStringList(redirectDocumentationToDevNull ? "true" : "false"));
+ config.setStringList(CONFIG_NOLINKERRORS, QStringList(noLinkErrors ? "true" : "false"));
+ config.setStringList(CONFIG_AUTOLINKERRORS, QStringList(autolinkErrors ? "true" : "false"));
+ config.setStringList(CONFIG_OBSOLETELINKS, QStringList(obsoleteLinks ? "true" : "false"));
+
+ prevCurrentDir = QDir::currentPath();
+
+ /*
+ 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);
+ QString project = config.getString(CONFIG_PROJECT);
+ //qDebug() << "Start project:" << project;
+ /*
+ Add the defines to the configuration variables.
+ */
+ QStringList defs = defines + config.getStringList(CONFIG_DEFINES);
+ config.setStringList(CONFIG_DEFINES,defs);
+ Location::terminate();
+
+ currentDir = QFileInfo(fileName).path();
+ if (!currentDir.isEmpty())
+ QDir::setCurrent(currentDir);
+
+ QString phase = " in -";
+ if (Generator::singleExec())
+ phase += "single exec mode, ";
+ else
+ phase += "separate exec mode, ";
+ if (Generator::preparing())
+ phase += "prepare phase ";
+ else if (Generator::generating())
+ phase += "generate phase ";
+ QString msg = "Running qdoc for " + config.getString(CONFIG_PROJECT) + phase;
+ Location::logToStdErr(msg);
+
+ /*
+ Initialize all the classes and data structures with the
+ qdoc configuration. This is safe to do for each qdocconf
+ file processed, because all the data structures created
+ are either cleared after they have been used, or they
+ are cleared in the terminate() functions below.
+ */
+ Location::initialize(config);
+ Tokenizer::initialize(config);
+ Doc::initialize(config);
+ CodeMarker::initialize(config);
+ CodeParser::initialize(config);
+ Generator::initialize(config);
+
+#ifndef QT_NO_TRANSLATION
+ /*
+ Load the language translators, if the configuration specifies any,
+ but only if they haven't already been loaded. This works in both
+ -prepare/-generate mode and -singleexec mode.
+ */
+ QStringList fileNames = config.getStringList(CONFIG_TRANSLATORS);
+ QStringList::ConstIterator fn = fileNames.constBegin();
+ while (fn != fileNames.constEnd()) {
+ bool found = false;
+ if (!translators.isEmpty()) {
+ for (int i=0; i<translators.size(); ++i) {
+ if (translators.at(i).first == *fn) {
+ found = true;
+ break;
+ }
+ }
+ }
+ if (!found) {
+ QTranslator *translator = new QTranslator(0);
+ if (!translator->load(*fn)) {
+ config.lastLocation().error(QCoreApplication::translate("QDoc", "Cannot load translator '%1'").arg(*fn));
+ }
+ else {
+ QCoreApplication::instance()->installTranslator(translator);
+ translators.append(Translator(*fn, translator));
+ }
+ }
+ ++fn;
+ }
+#endif
+
+ //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();
+
+ /*
+ Initialize the qdoc database, where all the parsed source files
+ will be stored. The database includes a tree of nodes, which gets
+ built as the source files are parsed. The documentation output is
+ generated by traversing that tree.
+
+ Note: qdocDB() allocates a new instance only if no instance exists.
+ So it is safe to call qdocDB() any time.
+ */
+ QDocDatabase* qdb = QDocDatabase::qdocDB();
+ qdb->setVersion(config.getString(CONFIG_VERSION));
+ qdb->setShowInternal(config.getBool(CONFIG_SHOWINTERNAL));
+ qdb->setSingleExec(config.getBool(CONFIG_SINGLEEXEC));
+ /*
+ By default, the only output format is HTML.
+ */
+ QSet<QString> outputFormats = config.getOutputFormats();
+ Location outputFormatsLocation = config.lastLocation();
+
+ qdb->clearSearchOrder();
+ if (!Generator::singleExec()) {
+ if (!Generator::preparing()) {
+ Generator::debug(" loading index files");
+ loadIndexFiles(config);
+ Generator::debug(" done loading index files");
+ }
+ qdb->newPrimaryTree(project);
+ }
+ else if (Generator::preparing())
+ qdb->newPrimaryTree(project);
+ else
+ qdb->setPrimaryTree(project);
+
+ dependModules = config.getStringList(CONFIG_DEPENDS);
+ dependModules.removeDuplicates();
+ qdb->setSearchOrder(dependModules);
+
+ QSet<QString> excludedDirs;
+ QSet<QString> excludedFiles;
+ QStringList excludedDirsList;
+ QStringList excludedFilesList;
+
+ if (!Generator::singleExec() || !Generator::generating()) {
+ QStringList headerList;
+ QStringList sourceList;
+
+ Generator::debug("Reading excludedirs");
+ excludedDirsList = config.getCanonicalPathList(CONFIG_EXCLUDEDIRS);
+ foreach (const QString &excludeDir, excludedDirsList) {
+ QString p = QDir::fromNativeSeparators(excludeDir);
+ QDir tmp(p);
+ if (tmp.exists())
+ excludedDirs.insert(p);
+ }
+
+ Generator::debug("Reading excludefiles");
+ excludedFilesList = config.getCanonicalPathList(CONFIG_EXCLUDEFILES);
+ foreach (const QString& excludeFile, excludedFilesList) {
+ QString p = QDir::fromNativeSeparators(excludeFile);
+ excludedFiles.insert(p);
+ }
+
+ Generator::debug("Reading headerdirs");
+ headerList = config.getAllFiles(CONFIG_HEADERS,CONFIG_HEADERDIRS,excludedDirs,excludedFiles);
+ QMap<QString,QString> headers;
+ QMultiMap<QString,QString> headerFileNames;
+ for (int i=0; i<headerList.size(); ++i) {
+ if (headerList[i].contains(QString("doc/snippets")))
+ continue;
+ if (headers.contains(headerList[i]))
+ continue;
+ headers.insert(headerList[i],headerList[i]);
+ QString t = headerList[i].mid(headerList[i].lastIndexOf('/')+1);
+ headerFileNames.insert(t,t);
+ }
+
+ Generator::debug("Reading sourcedirs");
+ sourceList = config.getAllFiles(CONFIG_SOURCES,CONFIG_SOURCEDIRS,excludedDirs,excludedFiles);
+ QMap<QString,QString> sources;
+ QMultiMap<QString,QString> sourceFileNames;
+ for (int i=0; i<sourceList.size(); ++i) {
+ if (sourceList[i].contains(QString("doc/snippets")))
+ continue;
+ if (sources.contains(sourceList[i]))
+ continue;
+ sources.insert(sourceList[i],sourceList[i]);
+ QString t = sourceList[i].mid(sourceList[i].lastIndexOf('/')+1);
+ sourceFileNames.insert(t,t);
+ }
+ /*
+ Find all the qdoc files in the example dirs, and add
+ them to the source files to be parsed.
+ */
+ Generator::debug("Reading exampledirs");
+ QStringList exampleQdocList = config.getExampleQdocFiles(excludedDirs, excludedFiles);
+ for (int i=0; i<exampleQdocList.size(); ++i) {
+ if (!sources.contains(exampleQdocList[i])) {
+ sources.insert(exampleQdocList[i],exampleQdocList[i]);
+ QString t = exampleQdocList[i].mid(exampleQdocList[i].lastIndexOf('/')+1);
+ sourceFileNames.insert(t,t);
+ }
+ }
+
+ Generator::debug("Adding doc/image dirs found in exampledirs to imagedirs");
+ QSet<QString> exampleImageDirs;
+ QStringList exampleImageList = config.getExampleImageFiles(excludedDirs, excludedFiles);
+ for (int i=0; i<exampleImageList.size(); ++i) {
+ if (exampleImageList[i].contains("doc/images")) {
+ QString t = exampleImageList[i].left(exampleImageList[i].lastIndexOf("doc/images")+10);
+ if (!exampleImageDirs.contains(t)) {
+ exampleImageDirs.insert(t);
+ }
+ }
+ }
+ Generator::augmentImageDirs(exampleImageDirs);
+
+ /*
+ Parse each header file in the set using the appropriate parser and add it
+ to the big tree.
+ */
+ QSet<CodeParser *> usedParsers;
+
+ Generator::debug("Parsing header files");
+ int parsed = 0;
+ QMap<QString,QString>::ConstIterator h = headers.constBegin();
+ while (h != headers.constEnd()) {
+ CodeParser *codeParser = CodeParser::parserForHeaderFile(h.key());
+ if (codeParser) {
+ ++parsed;
+ Generator::debug(QString("Parsing " + h.key()));
+ codeParser->parseHeaderFile(config.location(), h.key());
+ usedParsers.insert(codeParser);
+ }
+ ++h;
+ }
+
+ foreach (CodeParser *codeParser, usedParsers)
+ codeParser->doneParsingHeaderFiles();
+
+ usedParsers.clear();
+ qdb->resolveInheritance();
+
+ /*
+ Parse each source text file in the set using the appropriate parser and
+ add it to the big tree.
+ */
+ parsed = 0;
+ Generator::debug("Parsing source files");
+ QMap<QString,QString>::ConstIterator s = sources.constBegin();
+ while (s != sources.constEnd()) {
+ CodeParser *codeParser = CodeParser::parserForSourceFile(s.key());
+ if (codeParser) {
+ ++parsed;
+ Generator::debug(QString("Parsing " + s.key()));
+ codeParser->parseSourceFile(config.location(), s.key());
+ usedParsers.insert(codeParser);
+ }
+ ++s;
+ }
+ Generator::debug(QString("Parsing done."));
+
+ /*
+ Currently these doneParsingSourceFiles() calls do nothing.
+ */
+ foreach (CodeParser *codeParser, usedParsers)
+ codeParser->doneParsingSourceFiles();
+
+ /*
+ Now the primary 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.
+ */
+ Generator::debug("Resolving stuff prior to generating docs");
+ qdb->resolveIssues();
+ }
+ else {
+ Generator::debug("Reading excludedirs");
+ excludedDirsList = config.getCanonicalPathList(CONFIG_EXCLUDEDIRS);
+ foreach (const QString &excludeDir, excludedDirsList) {
+ QString p = QDir::fromNativeSeparators(excludeDir);
+ QDir tmp(p);
+ if (tmp.exists())
+ excludedDirs.insert(p);
+ }
+
+ Generator::debug("Reading excludefiles");
+ excludedFilesList = config.getCanonicalPathList(CONFIG_EXCLUDEFILES);
+ foreach (const QString& excludeFile, excludedFilesList) {
+ QString p = QDir::fromNativeSeparators(excludeFile);
+ excludedFiles.insert(p);
+ }
+
+ Generator::debug("Adding doc/image dirs found in exampledirs to imagedirs");
+ QSet<QString> exampleImageDirs;
+ QStringList exampleImageList = config.getExampleImageFiles(excludedDirs, excludedFiles);
+ for (int i=0; i<exampleImageList.size(); ++i) {
+ if (exampleImageList[i].contains("doc/images")) {
+ QString t = exampleImageList[i].left(exampleImageList[i].lastIndexOf("doc/images")+10);
+ if (!exampleImageDirs.contains(t)) {
+ exampleImageDirs.insert(t);
+ }
+ }
+ }
+ Generator::augmentImageDirs(exampleImageDirs);
+ qdb->resolveStuff();
+ }
+
+ /*
+ The primary tree is built and all the stuff that needed
+ resolving has been resolved. Now traverse the tree and
+ generate the documentation output. More than one output
+ format can be requested. The tree is traversed for each
+ one.
+ */
+ Generator::debug("Generating docs");
+ QSet<QString>::ConstIterator of = outputFormats.constBegin();
+ while (of != outputFormats.constEnd()) {
+ Generator* generator = Generator::generatorForFormat(*of);
+ if (generator == 0)
+ outputFormatsLocation.fatal(QCoreApplication::translate("QDoc",
+ "Unknown output format '%1'").arg(*of));
+ generator->generateDocs();
+ ++of;
+ }
+#if 0
+ if (Generator::generating() && Generator::writeQaPages())
+ qdb->printLinkCounts(project);
+#endif
+ qdb->clearLinkCounts();
+
+ Generator::debug("Terminating qdoc classes");
+ if (Generator::debugging())
+ Generator::stopDebugging(project);
+
+ QDocDatabase::qdocDB()->setVersion(QString());
+ Generator::terminate();
+ CodeParser::terminate();
+ CodeMarker::terminate();
+ Doc::terminate();
+ Tokenizer::terminate();
+ Location::terminate();
+ QDir::setCurrent(prevCurrentDir);
+
+ Generator::debug("qdoc classes terminated");
+}
+
+extern Q_CORE_EXPORT QBasicAtomicInt qt_qhash_seed;
+QT_END_NAMESPACE
+
+int main(int argc, char **argv)
+{
+ QT_USE_NAMESPACE
+
+#ifndef QT_BOOTSTRAPPED
+ qt_qhash_seed.testAndSetRelaxed(-1, 0); // set the hash seed to 0 if it wasn't set yet
+#endif
+ QCoreApplication app(argc, argv);
+ app.setApplicationVersion(QStringLiteral(QT_VERSION_STR));
+
+ /*
+ Create code parsers for the languages to be parsed,
+ and create a tree for C++.
+ */
+ CppCodeParser cppParser;
+ QmlCodeParser qmlParser;
+ PureDocParser docParser;
+
+ /*
+ Create code markers for plain text, C++,
+ javascript, and QML.
+ */
+ PlainCodeMarker plainMarker;
+ CppCodeMarker cppMarker;
+ JsCodeMarker jsMarker;
+ QmlCodeMarker qmlMarker;
+
+ HtmlGenerator htmlGenerator;
+
+ QCommandLineParser parser;
+ parser.setApplicationDescription(QCoreApplication::translate("qdoc", "Qt documentation generator"));
+ parser.addHelpOption();
+ parser.addVersionOption();
+
+ parser.setSingleDashWordOptionMode(QCommandLineParser::ParseAsLongOptions);
+
+ parser.addPositionalArgument("file1.qdocconf ...", QCoreApplication::translate("qdoc", "Input files"));
+
+ QCommandLineOption defineOption(QStringList() << QStringLiteral("D"));
+ defineOption.setDescription(QCoreApplication::translate("qdoc", "Define the argument as a macro while parsing sources"));
+ defineOption.setValueName(QStringLiteral("macro[=def]"));
+ parser.addOption(defineOption);
+
+ QCommandLineOption dependsOption(QStringList() << QStringLiteral("depends"));
+ dependsOption.setDescription(QCoreApplication::translate("qdoc", "Specify dependent modules"));
+ dependsOption.setValueName(QStringLiteral("module"));
+ parser.addOption(dependsOption);
+
+ QCommandLineOption highlightingOption(QStringList() << QStringLiteral("highlighting"));
+ highlightingOption.setDescription(QCoreApplication::translate("qdoc", "Turn on syntax highlighting (makes qdoc run slower)"));
+ parser.addOption(highlightingOption);
+
+ QCommandLineOption showInternalOption(QStringList() << QStringLiteral("showinternal"));
+ showInternalOption.setDescription(QCoreApplication::translate("qdoc", "Include content marked internal"));
+ parser.addOption(showInternalOption);
+
+ QCommandLineOption redirectDocumentationToDevNullOption(QStringList() << QStringLiteral("redirect-documentation-to-dev-null"));
+ redirectDocumentationToDevNullOption.setDescription(QCoreApplication::translate("qdoc", "Save all documentation content to /dev/null. Useful if someone is interested in qdoc errors only."));
+ parser.addOption(redirectDocumentationToDevNullOption);
+
+ QCommandLineOption noExamplesOption(QStringList() << QStringLiteral("no-examples"));
+ noExamplesOption.setDescription(QCoreApplication::translate("qdoc", "Do not generate documentation for examples"));
+ parser.addOption(noExamplesOption);
+
+ QCommandLineOption indexDirOption(QStringList() << QStringLiteral("indexdir"));
+ indexDirOption.setDescription(QCoreApplication::translate("qdoc", "Specify a directory where QDoc should search for index files to load"));
+ indexDirOption.setValueName(QStringLiteral("dir"));
+ parser.addOption(indexDirOption);
+
+ QCommandLineOption installDirOption(QStringList() << QStringLiteral("installdir"));
+ installDirOption.setDescription(QCoreApplication::translate("qdoc", "Specify the directory where the output will be after running \"make install\""));
+ installDirOption.setValueName(QStringLiteral("dir"));
+ parser.addOption(installDirOption);
+
+ QCommandLineOption obsoleteLinksOption(QStringList() << QStringLiteral("obsoletelinks"));
+ obsoleteLinksOption.setDescription(QCoreApplication::translate("qdoc", "Report links from obsolete items to non-obsolete items"));
+ parser.addOption(obsoleteLinksOption);
+
+ QCommandLineOption outputDirOption(QStringList() << QStringLiteral("outputdir"));
+ outputDirOption.setDescription(QCoreApplication::translate("qdoc", "Specify output directory, overrides setting in qdocconf file"));
+ outputDirOption.setValueName(QStringLiteral("dir"));
+ parser.addOption(outputDirOption);
+
+ QCommandLineOption outputFormatOption(QStringList() << QStringLiteral("outputformat"));
+ outputFormatOption.setDescription(QCoreApplication::translate("qdoc", "Specify output format, overrides setting in qdocconf file"));
+ outputFormatOption.setValueName(QStringLiteral("format"));
+ parser.addOption(outputFormatOption);
+
+ QCommandLineOption noLinkErrorsOption(QStringList() << QStringLiteral("no-link-errors"));
+ noLinkErrorsOption.setDescription(QCoreApplication::translate("qdoc", "Do not print link errors (i.e. missing targets)"));
+ parser.addOption(noLinkErrorsOption);
+
+ QCommandLineOption autoLinkErrorsOption(QStringList() << QStringLiteral("autolink-errors"));
+ autoLinkErrorsOption.setDescription(QCoreApplication::translate("qdoc", "Show errors when automatic linking fails"));
+ parser.addOption(autoLinkErrorsOption);
+
+ QCommandLineOption debugOption(QStringList() << QStringLiteral("debug"));
+ debugOption.setDescription(QCoreApplication::translate("qdoc", "Enable debug output"));
+ parser.addOption(debugOption);
+
+ QCommandLineOption prepareOption(QStringList() << QStringLiteral("prepare"));
+ prepareOption.setDescription(QCoreApplication::translate("qdoc", "Run qdoc only to generate an index file, not the docs"));
+ parser.addOption(prepareOption);
+
+ QCommandLineOption generateOption(QStringList() << QStringLiteral("generate"));
+ generateOption.setDescription(QCoreApplication::translate("qdoc", "Run qdoc to read the index files and generate the docs"));
+ parser.addOption(generateOption);
+
+ QCommandLineOption logProgressOption(QStringList() << QStringLiteral("log-progress"));
+ logProgressOption.setDescription(QCoreApplication::translate("qdoc", "Log progress on stderr."));
+ parser.addOption(logProgressOption);
+
+ QCommandLineOption singleExecOption(QStringList() << QStringLiteral("single-exec"));
+ singleExecOption.setDescription(QCoreApplication::translate("qdoc", "Run qdoc once over all the qdoc conf files."));
+ parser.addOption(singleExecOption);
+
+ QCommandLineOption writeQaPagesOption(QStringList() << QStringLiteral("write-qa-pages"));
+ writeQaPagesOption.setDescription(QCoreApplication::translate("qdoc", "Write QA pages."));
+ parser.addOption(writeQaPagesOption);
+
+ parser.process(app);
+
+ defines += parser.values(defineOption);
+ dependModules += parser.values(dependsOption);
+ highlighting = parser.isSet(highlightingOption);
+ showInternal = parser.isSet(showInternalOption);
+ singleExec = parser.isSet(singleExecOption);
+ writeQaPages = parser.isSet(writeQaPagesOption);
+ redirectDocumentationToDevNull = parser.isSet(redirectDocumentationToDevNullOption);
+ Config::generateExamples = !parser.isSet(noExamplesOption);
+ foreach (const QString &indexDir, parser.values(indexDirOption)) {
+ if (QFile::exists(indexDir))
+ indexDirs += indexDir;
+ else
+ qDebug() << "Cannot find index directory" << indexDir;
+ }
+ if (parser.isSet(installDirOption))
+ Config::installDir = parser.value(installDirOption);
+ obsoleteLinks = parser.isSet(obsoleteLinksOption);
+ if (parser.isSet(outputDirOption))
+ Config::overrideOutputDir = parser.value(outputDirOption);
+ foreach (const QString &format, parser.values(outputFormatOption))
+ Config::overrideOutputFormats.insert(format);
+ noLinkErrors = parser.isSet(noLinkErrorsOption);
+ autolinkErrors = parser.isSet(autoLinkErrorsOption);
+ if (parser.isSet(debugOption))
+ Generator::startDebugging(QString("command line"));
+ if (parser.isSet(prepareOption))
+ Generator::setQDocPass(Generator::Prepare);
+ if (parser.isSet(generateOption))
+ Generator::setQDocPass(Generator::Generate);
+ if (parser.isSet(singleExecOption))
+ Generator::setSingleExec();
+ if (parser.isSet(writeQaPagesOption))
+ Generator::setWriteQaPages();
+ if (parser.isSet(logProgressOption))
+ Location::startLoggingProgress();
+
+ /*
+ The default indent for code is 0.
+ The default value for false is 0.
+ The default supported file extensions are cpp, h, qdoc and qml.
+ 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.
+ */
+ if (defaults.isEmpty()) {
+ defaults.insert(CONFIG_CODEINDENT, QLatin1String("0"));
+ defaults.insert(CONFIG_FALSEHOODS, QLatin1String("0"));
+ defaults.insert(CONFIG_FILEEXTENSIONS, QLatin1String("*.cpp *.h *.qdoc *.qml"));
+ defaults.insert(CONFIG_LANGUAGE, QLatin1String("Cpp"));
+ defaults.insert(CONFIG_OUTPUTFORMATS, QLatin1String("HTML"));
+ defaults.insert(CONFIG_TABSIZE, QLatin1String("8"));
+ }
+
+ QStringList qdocFiles = parser.positionalArguments();
+ if (qdocFiles.isEmpty())
+ parser.showHelp();
+
+ if (singleExec)
+ qdocFiles = Config::loadMaster(qdocFiles.at(0));
+
+ /*
+ Main loop is now modified to handle single exec mode.
+ */
+ if (Generator::singleExec())
+ Generator::setQDocPass(Generator::Prepare);
+ foreach (const QString &qf, qdocFiles) {
+ dependModules.clear();
+ processQdocconfFile(qf);
+ }
+ if (Generator::singleExec()) {
+ Generator::setQDocPass(Generator::Generate);
+ QDocDatabase* qdb = QDocDatabase::qdocDB();
+ qdb->processForest();
+ foreach (const QString &qf, qdocFiles) {
+ dependModules.clear();
+ processQdocconfFile(qf);
+ }
+ }
+
+#ifndef QT_NO_TRANSLATION
+ if (!translators.isEmpty()) {
+ for (int i=0; i<translators.size(); ++i) {
+ delete translators.at(i).second;
+ }
+ }
+ translators.clear();
+#endif
+ QmlTypeNode::terminate();
+
+#ifdef DEBUG_SHUTDOWN_CRASH
+ qDebug() << "main(): Delete qdoc database";
+#endif
+ QDocDatabase::destroyQdocDB();
+#ifdef DEBUG_SHUTDOWN_CRASH
+ qDebug() << "main(): qdoc database deleted";
+#endif
+
+ return EXIT_SUCCESS;
+}
diff --git a/src/qdoc/node.cpp b/src/qdoc/node.cpp
new file mode 100644
index 000000000..c1cf076f4
--- /dev/null
+++ b/src/qdoc/node.cpp
@@ -0,0 +1,3038 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL21$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** 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 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** As a special exception, The Qt Company gives you certain additional
+** rights. These rights are described in The Qt Company LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "node.h"
+#include "tree.h"
+#include "codemarker.h"
+#include "cppcodeparser.h"
+#include <quuid.h>
+#include "qdocdatabase.h"
+#include <qdebug.h>
+#include "generator.h"
+#include "tokenizer.h"
+
+QT_BEGIN_NAMESPACE
+
+int Node::propertyGroupCount_ = 0;
+QStringMap Node::operators_;
+QMap<QString,Node::NodeType> Node::goals_;
+
+/*!
+ Initialize the map of search goals. This is called once
+ by QDocDatabase::initializeDB(). The map key is a string
+ representing a value in the enum Node::NodeType. The map value
+ is the enum value.
+
+ There should be an entry in the map for each value in the
+ NodeType enum.
+ */
+void Node::initialize()
+{
+ goals_.insert("class", Node::Class);
+ goals_.insert("qmltype", Node::QmlType);
+ goals_.insert("page", Node::Document);
+ goals_.insert("function", Node::Function);
+ goals_.insert("property", Node::Property);
+ goals_.insert("variable", Node::Variable);
+ goals_.insert("group", Node::Group);
+ goals_.insert("module", Node::Module);
+ goals_.insert("qmlmodule", Node::QmlModule);
+ goals_.insert("qmppropertygroup", Node::QmlPropertyGroup);
+ goals_.insert("qmlproperty", Node::QmlProperty);
+ goals_.insert("qmlsignal", Node::QmlSignal);
+ goals_.insert("qmlsignalhandler", Node::QmlSignalHandler);
+ goals_.insert("qmlmethod", Node::QmlMethod);
+ goals_.insert("qmlbasictype", Node::QmlBasicType);
+ goals_.insert("enum", Node::Enum);
+ goals_.insert("typedef", Node::Typedef);
+ goals_.insert("namespace", Node::Namespace);
+}
+
+/*!
+ Increment the number of property groups seen in the current
+ file, and return the new value.
+ */
+int Node::incPropertyGroupCount() { return ++propertyGroupCount_; }
+
+/*!
+ Reset the number of property groups seen in the current file
+ to 0, because we are starting a new file.
+ */
+void Node::clearPropertyGroupCount() { propertyGroupCount_ = 0; }
+
+/*!
+ \class Node
+ \brief The Node class is a node in the Tree.
+
+ A Node represents a class or function or something else
+ from the source code..
+ */
+
+/*!
+ When this Node is destroyed, if it has a parent Node, it
+ removes itself from the parent node's child list.
+ */
+Node::~Node()
+{
+ if (parent_)
+ parent_->removeChild(this);
+
+ if (relatesTo_)
+ removeRelates();
+}
+
+/*!
+ Removes this node from the aggregate's list of related
+ nodes, or if this node has created a dummy "relates"
+ aggregate, deletes it.
+*/
+void Node::removeRelates()
+{
+ if (!relatesTo_)
+ return;
+
+ if (relatesTo_->isDocumentNode() && !relatesTo_->parent()) {
+ delete relatesTo_;
+ relatesTo_ = 0;
+ } else {
+ relatesTo_->removeRelated(this);
+ }
+}
+
+/*!
+ Returns this node's name member. Appends "()" to the returned
+ name, if this node is a function node.
+ */
+QString Node::plainName() const
+{
+ if (type() == Node::Function)
+ return name_ + QLatin1String("()");
+ return name_;
+}
+
+/*!
+ Constructs and returns the node's fully qualified name by
+ recursively ascending the parent links and prepending each
+ parent name + "::". Breaks out when the parent pointer is
+ \a relative. Almost all calls to this function pass 0 for
+ \a relative.
+ */
+QString Node::plainFullName(const Node* relative) const
+{
+ if (name_.isEmpty())
+ return QLatin1String("global");
+
+ QString fullName;
+ const Node* node = this;
+ while (node) {
+ fullName.prepend(node->plainName());
+ if (node->parent() == relative || node->parent()->name().isEmpty())
+ break;
+ fullName.prepend(QLatin1String("::"));
+ node = node->parent();
+ }
+ return fullName;
+}
+
+/*!
+ Constructs and returns this node's full name.
+ */
+QString Node::fullName(const Node* relative) const
+{
+ if ((isDocumentNode() || isGroup()) && !title().isEmpty())
+ return title();
+ return plainFullName(relative);
+}
+
+/*!
+ Try to match this node's type and subtype with one of the
+ pairs in \a types. If a match is found, return true. If no
+ match is found, return false.
+
+ \a types is a list of type/subtype pairs, where the first
+ value in the pair is a Node::NodeType, and the second value is
+ a Node::DocSubtype. The second value is used in the match if
+ this node's type is Node::Document.
+ */
+bool Node::match(const NodeTypeList& types) const
+{
+ for (int i=0; i<types.size(); ++i) {
+ if (type() == types.at(i).first) {
+ if (type() == Node::Document) {
+ if (docSubtype() == types.at(i).second)
+ return true;
+ }
+ else
+ return true;
+ }
+ }
+ return false;
+}
+
+/*!
+ Sets this Node's Doc to \a doc. If \a replace is false and
+ this Node already has a Doc, a warning is reported that the
+ Doc is being overridden, and it reports where the previous
+ Doc was found. If \a replace is true, the Doc is replaced
+ silently.
+ */
+void Node::setDoc(const Doc& doc, bool replace)
+{
+ if (!doc_.isEmpty() && !replace) {
+ doc.location().warning(tr("Overrides a previous doc"));
+ doc_.location().warning(tr("(The previous doc is here)"));
+ }
+ doc_ = doc;
+}
+
+/*!
+ Construct a node with the given \a type and having the
+ given \a parent and \a name. The new node is added to the
+ parent's child list.
+ */
+Node::Node(NodeType type, Aggregate *parent, const QString& name)
+ : nodeType_((unsigned char) type),
+ access_((unsigned char) Public),
+ safeness_((unsigned char) UnspecifiedSafeness),
+ pageType_((unsigned char) NoPageType),
+ status_((unsigned char) Active),
+ indexNodeFlag_(false),
+ parent_(parent),
+ relatesTo_(0),
+ name_(name)
+{
+ if (parent_)
+ parent_->addChild(this);
+ outSubDir_ = Generator::outputSubdir();
+ if (operators_.isEmpty()) {
+ operators_.insert("++","inc");
+ operators_.insert("--","dec");
+ operators_.insert("==","eq");
+ operators_.insert("!=","ne");
+ operators_.insert("<<","lt-lt");
+ operators_.insert(">>","gt-gt");
+ operators_.insert("+=","plus-assign");
+ operators_.insert("-=","minus-assign");
+ operators_.insert("*=","mult-assign");
+ operators_.insert("/=","div-assign");
+ operators_.insert("%=","mod-assign");
+ operators_.insert("&=","bitwise-and-assign");
+ operators_.insert("|=","bitwise-or-assign");
+ operators_.insert("^=","bitwise-xor-assign");
+ operators_.insert("<<=","bitwise-left-shift-assign");
+ operators_.insert(">>=","bitwise-right-shift-assign");
+ operators_.insert("||","logical-or");
+ operators_.insert("&&","logical-and");
+ operators_.insert("()","call");
+ operators_.insert("[]","subscript");
+ operators_.insert("->","pointer");
+ operators_.insert("->*","pointer-star");
+ operators_.insert("+","plus");
+ operators_.insert("-","minus");
+ operators_.insert("*","mult");
+ operators_.insert("/","div");
+ operators_.insert("%","mod");
+ operators_.insert("|","bitwise-or");
+ operators_.insert("&","bitwise-and");
+ operators_.insert("^","bitwise-xor");
+ operators_.insert("!","not");
+ operators_.insert("~","bitwise-not");
+ operators_.insert("<=","lt-eq");
+ operators_.insert(">=","gt-eq");
+ operators_.insert("<","lt");
+ operators_.insert(">","gt");
+ operators_.insert("=","assign");
+ operators_.insert(",","comma");
+ operators_.insert("delete[]","delete-array");
+ operators_.insert("delete","delete");
+ operators_.insert("new[]","new-array");
+ operators_.insert("new","new");
+ }
+}
+
+/*! \fn QString Node::url() const
+ Returns the node's URL.
+ */
+
+/*! \fn void Node::setUrl(const QString &url)
+ Sets the node's URL to \a url
+ */
+
+/*!
+ Returns this node's page type as a string, for use as an
+ attribute value in XML or HTML.
+ */
+QString Node::pageTypeString() const
+{
+ return pageTypeString((PageType) pageType_);
+}
+
+/*!
+ Returns the page type \a t as a string, for use as an
+ attribute value in XML or HTML.
+ */
+QString Node::pageTypeString(unsigned char t)
+{
+ switch ((PageType)t) {
+ case Node::ApiPage:
+ return "api";
+ case Node::ArticlePage:
+ return "article";
+ case Node::ExamplePage:
+ return "example";
+ case Node::HowToPage:
+ return "howto";
+ case Node::OverviewPage:
+ return "overview";
+ case Node::TutorialPage:
+ return "tutorial";
+ case Node::FAQPage:
+ return "faq";
+ case Node::DitaMapPage:
+ return "ditamap";
+ default:
+ return "article";
+ }
+}
+
+/*!
+ Returns this node's type as a string for use as an
+ attribute value in XML or HTML.
+ */
+QString Node::nodeTypeString() const
+{
+ return nodeTypeString(type());
+}
+
+/*!
+ Returns the node type \a t as a string for use as an
+ attribute value in XML or HTML.
+ */
+QString Node::nodeTypeString(unsigned char t)
+{
+ switch ((NodeType)t) {
+ case Namespace:
+ return "namespace";
+ case Class:
+ return "class";
+ case Document:
+ return "document";
+ case Enum:
+ return "enum";
+ case Typedef:
+ return "typedef";
+ case Function:
+ return "function";
+ case Property:
+ return "property";
+ case Variable:
+ return "variable";
+ case Group:
+ return "group";
+ case Module:
+ return "module";
+ case QmlType:
+ return "QML type";
+ case QmlBasicType:
+ return "QML basic type";
+ case QmlModule:
+ return "QML module";
+ case QmlProperty:
+ return "QML property";
+ case QmlPropertyGroup:
+ return "QML property group";
+ case QmlSignal:
+ return "QML signal";
+ case QmlSignalHandler:
+ return "QML signal handler";
+ case QmlMethod:
+ return "QML method";
+ default:
+ break;
+ }
+ return QString();
+}
+
+/*!
+ Returns this node's subtype as a string for use as an
+ attribute value in XML or HTML. This is only useful
+ in the case where the node type is Document.
+ */
+QString Node::nodeSubtypeString() const
+{
+ return nodeSubtypeString(docSubtype());
+}
+
+/*!
+ Returns the node subtype \a t as a string for use as an
+ attribute value in XML or HTML. This is only useful
+ in the case where the node type is Document.
+ */
+QString Node::nodeSubtypeString(unsigned char t)
+{
+ switch ((DocSubtype)t) {
+ case Example:
+ return "example";
+ case HeaderFile:
+ return "header file";
+ case File:
+ return "file";
+ case Image:
+ return "image";
+ case Page:
+ return "page";
+ case ExternalPage:
+ return "external page";
+ case DitaMap:
+ return "ditamap";
+ case NoSubtype:
+ default:
+ break;
+ }
+ return QString();
+}
+
+/*!
+ Set the page type according to the string \a t.
+ */
+void Node::setPageType(const QString& t)
+{
+ if ((t == "API") || (t == "api"))
+ pageType_ = (unsigned char) ApiPage;
+ else if (t == "howto")
+ pageType_ = (unsigned char) HowToPage;
+ else if (t == "overview")
+ pageType_ = (unsigned char) OverviewPage;
+ else if (t == "tutorial")
+ pageType_ = (unsigned char) TutorialPage;
+ else if (t == "faq")
+ pageType_ = (unsigned char) FAQPage;
+ else if (t == "article")
+ pageType_ = (unsigned char) ArticlePage;
+ else if (t == "example")
+ pageType_ = (unsigned char) ExamplePage;
+ else if (t == "ditamap")
+ pageType_ = (unsigned char) DitaMapPage;
+}
+
+/*! Converts the boolean value \a b to an enum representation
+ of the boolean type, which includes an enum value for the
+ \e {default value} of the item, i.e. true, false, or default.
+ */
+Node::FlagValue Node::toFlagValue(bool b)
+{
+ return b ? FlagValueTrue : FlagValueFalse;
+}
+
+/*!
+ Converts the enum \a fv back to a boolean value.
+ If \a fv is neither the true enum value nor the
+ false enum value, the boolean value returned is
+ \a defaultValue.
+
+ Note that runtimeDesignabilityFunction() should be called
+ first. If that function returns the name of a function, it
+ means the function must be called at runtime to determine
+ whether the property is Designable.
+ */
+bool Node::fromFlagValue(FlagValue fv, bool defaultValue)
+{
+ switch (fv) {
+ case FlagValueTrue:
+ return true;
+ case FlagValueFalse:
+ return false;
+ default:
+ return defaultValue;
+ }
+}
+
+/*!
+ Sets the pointer to the node that this node relates to.
+ */
+void Node::setRelates(Aggregate *pseudoParent)
+{
+ if (pseudoParent == parent())
+ return;
+
+ removeRelates();
+ relatesTo_ = pseudoParent;
+ pseudoParent->addRelated(this);
+}
+
+/*!
+ Sets the (unresolved) entity \a name that this node relates to.
+ */
+void Node::setRelates(const QString& name)
+{
+ removeRelates();
+ // Create a dummy aggregate for writing the name into the index
+ relatesTo_ = new DocumentNode(0, name, Node::NoSubtype, Node::NoPageType);
+}
+
+/*!
+ This function creates a pair that describes a link.
+ The pair is composed from \a link and \a desc. The
+ \a linkType is the map index the pair is filed under.
+ */
+void Node::setLink(LinkType linkType, const QString &link, const QString &desc)
+{
+ QPair<QString,QString> linkPair;
+ linkPair.first = link;
+ linkPair.second = desc;
+ linkMap_[linkType] = linkPair;
+}
+
+/*!
+ Sets the information about the project and version a node was introduced
+ in. The string is simplified, removing excess whitespace before being
+ stored.
+*/
+void Node::setSince(const QString &since)
+{
+ since_ = since.simplified();
+}
+
+/*!
+ Returns a string representing the access specifier.
+ */
+QString Node::accessString() const
+{
+ switch ((Access) access_) {
+ case Protected:
+ return "protected";
+ case Private:
+ return "private";
+ case Public:
+ default:
+ break;
+ }
+ return "public";
+}
+
+/*!
+ Extract a class name from the type \a string and return it.
+ */
+QString Node::extractClassName(const QString &string) const
+{
+ QString result;
+ 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(':')) {
+ result += ch;
+ }
+ else if (!result.isEmpty()) {
+ if (result != QLatin1String("const"))
+ return result;
+ result.clear();
+ }
+ }
+ return result;
+}
+
+/*!
+ Returns a string representing the access specifier.
+ */
+QString RelatedClass::accessString() const
+{
+ switch (access_) {
+ case Node::Protected:
+ return "protected";
+ case Node::Private:
+ return "private";
+ case Node::Public:
+ default:
+ break;
+ }
+ return "public";
+}
+
+/*!
+ Returns the inheritance status.
+ */
+Node::Status Node::inheritedStatus() const
+{
+ Status parentStatus = Active;
+ if (parent_)
+ parentStatus = parent_->inheritedStatus();
+ return (Status)qMin((int)status_, (int)parentStatus);
+}
+
+/*!
+ Returns the thread safeness value for whatever this node
+ represents. But if this node has a parent and the thread
+ safeness value of the parent is the same as the thread
+ safeness value of this node, what is returned is the
+ value \c{UnspecifiedSafeness}. Why?
+ */
+Node::ThreadSafeness Node::threadSafeness() const
+{
+ if (parent_ && (ThreadSafeness) safeness_ == parent_->inheritedThreadSafeness())
+ return UnspecifiedSafeness;
+ return (ThreadSafeness) safeness_;
+}
+
+/*!
+ If this node has a parent, the parent's thread safeness
+ value is returned. Otherwise, this node's thread safeness
+ value is returned. Why?
+ */
+Node::ThreadSafeness Node::inheritedThreadSafeness() const
+{
+ if (parent_ && (ThreadSafeness) safeness_ == UnspecifiedSafeness)
+ return parent_->inheritedThreadSafeness();
+ return (ThreadSafeness) safeness_;
+}
+
+#if 0
+/*!
+ Returns the sanitized file name without the path.
+ If the file is an html file, the html suffix
+ is removed. Why?
+ */
+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(QLatin1Char(' '), QLatin1Char('-'));
+ return base.toLower();
+}
+/*!
+ Returns this node's Universally Unique IDentifier as a
+ QString. Creates the UUID first, if it has not been created.
+ */
+QString Node::guid() const
+{
+ if (uuid_.isEmpty())
+ uuid_ = idForNode();
+ return uuid_;
+}
+#endif
+
+/*!
+ If this node is a QML or JS type node, return a pointer to
+ it. If it is a child of a QML or JS type node, return the
+ pointer to its parent QMLor JS type node. Otherwise return
+ 0;
+ */
+QmlTypeNode* Node::qmlTypeNode()
+{
+ if (isQmlNode() || isJsNode()) {
+ Node* n = this;
+ while (n && !(n->isQmlType() || n->isJsType()))
+ n = n->parent();
+ if (n && (n->isQmlType() || n->isJsType()))
+ return static_cast<QmlTypeNode*>(n);
+ }
+ return 0;
+}
+
+/*!
+ If this node is a QML node, find its QML class node,
+ and return a pointer to the C++ class node from the
+ QML class node. That pointer will be null if the QML
+ class node is a component. It will be non-null if
+ the QML class node is a QML element.
+ */
+ClassNode* Node::declarativeCppNode()
+{
+ QmlTypeNode* qcn = qmlTypeNode();
+ if (qcn)
+ return qcn->classNode();
+ return 0;
+}
+
+/*!
+ Returns \c true if the node's status is Internal, or if its
+ parent is a class with internal status.
+ */
+bool Node::isInternal() const
+{
+ if (status() == Internal)
+ return true;
+ if (parent() && parent()->status() == Internal)
+ return true;
+ if (relates() && relates()->status() == Internal)
+ return true;
+ return false;
+}
+
+/*!
+ Returns a pointer to the Tree this node is in.
+ */
+Tree* Node::tree() const
+{
+ return (parent() ? parent()->tree() : 0);
+}
+
+/*!
+ Returns a pointer to the root of the Tree this node is in.
+ */
+const Node* Node::root() const
+{
+ return (parent() ? parent()->root() : this);
+}
+
+/*!
+ Sets the node's declaration location, its definition
+ location, or both, depending on the suffix of the file
+ name from the file path in location \a t.
+ */
+void Node::setLocation(const Location& t)
+{
+ QString suffix = t.fileSuffix();
+ if (suffix == "h")
+ declLocation_ = t;
+ else if (suffix == "cpp")
+ defLocation_ = t;
+ else {
+ declLocation_ = t;
+ defLocation_ = t;
+ }
+}
+
+/*!
+ \class Aggregate
+ */
+
+/*!
+ The inner node destructor deletes the children and removes
+ this node from its related nodes.
+ */
+Aggregate::~Aggregate()
+{
+ removeFromRelated();
+ deleteChildren();
+}
+
+/*!
+ If \a genus is \c{Node::DontCare}, find the first node in
+ this node's child list that has the given \a name. If this
+ node is a QML type, be sure to also look in the children
+ of its property group nodes. Return the matching node or 0.
+
+ If \a genus is either \c{Node::CPP} or \c {Node::QML}, then
+ find all this node's children that have the given \a name,
+ and return the one that satisfies the \a genus requirement.
+ */
+Node *Aggregate::findChildNode(const QString& name, Node::Genus genus) const
+{
+ if (genus == Node::DontCare) {
+ Node *node = childMap_.value(name);
+ if (node && !node->isQmlPropertyGroup()) // mws asks: Why not property group?
+ return node;
+ if (isQmlType() || isJsType()) {
+ for (int i=0; i<children_.size(); ++i) {
+ Node* n = children_.at(i);
+ if (n->isQmlPropertyGroup() || isJsPropertyGroup()) {
+ node = static_cast<Aggregate*>(n)->findChildNode(name, genus);
+ if (node)
+ return node;
+ }
+ }
+ }
+ }
+ else {
+ NodeList nodes = childMap_.values(name);
+ if (!nodes.isEmpty()) {
+ for (int i=0; i<nodes.size(); ++i) {
+ Node* node = nodes.at(i);
+ if (genus == node->genus())
+ return node;
+ }
+ }
+ }
+ return primaryFunctionMap_.value(name);
+}
+
+/*!
+ Find all the child nodes of this node that are named
+ \a name and return them in \a nodes.
+ */
+void Aggregate::findChildren(const QString& name, NodeList& nodes) const
+{
+ nodes = childMap_.values(name);
+ Node* n = primaryFunctionMap_.value(name);
+ if (n) {
+ nodes.append(n);
+ NodeList t = secondaryFunctionMap_.value(name);
+ if (!t.isEmpty())
+ nodes.append(t);
+ }
+ if (!nodes.isEmpty() || !(isQmlNode() || isJsNode()))
+ return;
+ int i = name.indexOf(QChar('.'));
+ if (i < 0)
+ return;
+ QString qmlPropGroup = name.left(i);
+ NodeList t = childMap_.values(qmlPropGroup);
+ if (t.isEmpty())
+ return;
+ foreach (Node* n, t) {
+ if (n->isQmlPropertyGroup() || n->isJsPropertyGroup()) {
+ n->findChildren(name, nodes);
+ if (!nodes.isEmpty())
+ break;
+ }
+ }
+}
+
+/*!
+ This function is like findChildNode(), but if a node
+ with the specified \a name is found but it is not of the
+ specified \a type, 0 is returned.
+ */
+Node* Aggregate::findChildNode(const QString& name, NodeType type)
+{
+ if (type == Function)
+ return primaryFunctionMap_.value(name);
+ else {
+ NodeList nodes = childMap_.values(name);
+ for (int i=0; i<nodes.size(); ++i) {
+ Node* node = nodes.at(i);
+ if (node->type() == type)
+ return node;
+ }
+ }
+ return 0;
+}
+
+/*!
+ Find a function node that is a child of this nose, such
+ that the function node has the specified \a name.
+ */
+FunctionNode *Aggregate::findFunctionNode(const QString& name, const QString& params) const
+{
+ FunctionNode* pfn = static_cast<FunctionNode*>(primaryFunctionMap_.value(name));
+ FunctionNode* fn = pfn;
+ if (fn) {
+ const QVector<Parameter>* funcParams = &(fn->parameters());
+ if (params.isEmpty() && funcParams->isEmpty())
+ return fn;
+ bool isQPrivateSignal = false; // Not used in the search
+ QVector<Parameter> testParams;
+ if (!params.isEmpty()) {
+ CppCodeParser* cppParser = CppCodeParser::cppParser();
+ cppParser->parseParameters(params, testParams, isQPrivateSignal);
+ }
+ NodeList funcs = secondaryFunctionMap_.value(name);
+ int i = -1;
+ while (fn) {
+ if (testParams.size() == funcParams->size()) {
+ if (testParams.isEmpty())
+ return fn;
+ bool different = false;
+ for (int j=0; j<testParams.size(); j++) {
+ if (testParams.at(j).dataType() != funcParams->at(j).dataType()) {
+ different = true;
+ break;
+ }
+ }
+ if (!different)
+ return fn;
+ }
+ if (++i < funcs.size()) {
+ fn = static_cast<FunctionNode*>(funcs.at(i));
+ funcParams = &(fn->parameters());
+ }
+ else
+ fn = 0;
+ }
+ if (!fn && !testParams.empty())
+ return 0;
+ }
+ /*
+ Most \l commands that link to functions don't include
+ the parameter declarations in the function signature,
+ so if the \l is meant to go to a function that does
+ have parameters, the algorithm above won't find it.
+ Therefore we must return the pointer to the function
+ in the primary function map in the cases where the
+ parameters should have been specified in the \l command.
+ */
+ return (fn ? fn : pfn);
+}
+
+/*!
+ Find the function node that is a child of this node, such
+ that the function has the same name and signature as the
+ \a clone node.
+ */
+FunctionNode *Aggregate::findFunctionNode(const FunctionNode *clone) const
+{
+ QMap<QString,Node*>::ConstIterator c = primaryFunctionMap_.constFind(clone->name());
+ if (c != primaryFunctionMap_.constEnd()) {
+ if (isSameSignature(clone, (FunctionNode *) *c)) {
+ return (FunctionNode *) *c;
+ }
+ else if (secondaryFunctionMap_.contains(clone->name())) {
+ const NodeList& secs = secondaryFunctionMap_[clone->name()];
+ NodeList::ConstIterator s = secs.constBegin();
+ while (s != secs.constEnd()) {
+ if (isSameSignature(clone, (FunctionNode *) *s))
+ return (FunctionNode *) *s;
+ ++s;
+ }
+ }
+ }
+ return 0;
+}
+
+/*!
+ Returns the list of keys from the primary function map.
+ */
+QStringList Aggregate::primaryKeys()
+{
+ QStringList t;
+ QMap<QString, Node*>::iterator i = primaryFunctionMap_.begin();
+ while (i != primaryFunctionMap_.end()) {
+ t.append(i.key());
+ ++i;
+ }
+ return t;
+}
+
+/*!
+ Returns the list of keys from the secondary function map.
+ */
+QStringList Aggregate::secondaryKeys()
+{
+ QStringList t;
+ QMap<QString, NodeList>::iterator i = secondaryFunctionMap_.begin();
+ while (i != secondaryFunctionMap_.end()) {
+ t.append(i.key());
+ ++i;
+ }
+ return t;
+}
+
+/*!
+ Mark all child nodes that have no documentation as having
+ private access and internal status. qdoc will then ignore
+ them for documentation purposes. Some nodes have an
+ Intermediate status, meaning that they should be ignored,
+ but not their children.
+ */
+void Aggregate::makeUndocumentedChildrenInternal()
+{
+ foreach (Node *child, childNodes()) {
+ if (child->doc().isEmpty() && child->status() != Node::Intermediate) {
+ child->setAccess(Node::Private);
+ child->setStatus(Node::Internal);
+ }
+ }
+}
+
+/*!
+ This is where we set the overload numbers for function nodes.
+ \note Overload numbers for related non-members are handled
+ separately.
+ */
+void Aggregate::normalizeOverloads()
+{
+ QMap<QString, Node *>::Iterator p1 = primaryFunctionMap_.begin();
+ while (p1 != primaryFunctionMap_.end()) {
+ FunctionNode *primaryFunc = (FunctionNode *) *p1;
+ if (primaryFunc->status() != Active || primaryFunc->access() == Private) {
+ if (secondaryFunctionMap_.contains(primaryFunc->name())) {
+ /*
+ Either the primary function is not active or it is private.
+ It therefore can't be the primary function. Search the list
+ of overloads to find one that can be the primary function.
+ */
+ NodeList& overloads = secondaryFunctionMap_[primaryFunc->name()];
+ NodeList::ConstIterator s = overloads.constBegin();
+ while (s != overloads.constEnd()) {
+ FunctionNode *overloadFunc = (FunctionNode *) *s;
+ /*
+ Any non-obsolete, non-private function (i.e., visible function)
+ is preferable to the current primary function. Swap the primary
+ and overload functions.
+ */
+ if (overloadFunc->status() == Active && overloadFunc->access() != Private) {
+ primaryFunc->setOverloadNumber(overloadFunc->overloadNumber());
+ overloads.replace(overloads.indexOf(overloadFunc), primaryFunc);
+ *p1 = overloadFunc;
+ overloadFunc->setOverloadFlag(false);
+ overloadFunc->setOverloadNumber(0);
+ break;
+ }
+ ++s;
+ }
+ }
+ }
+ ++p1;
+ }
+ /*
+ Ensure that none of the primary functions is marked with \overload.
+ */
+ QMap<QString, Node *>::Iterator p = primaryFunctionMap_.begin();
+ while (p != primaryFunctionMap_.end()) {
+ FunctionNode *primaryFunc = (FunctionNode *) *p;
+ if (primaryFunc->isOverload()) {
+ if (secondaryFunctionMap_.contains(primaryFunc->name())) {
+ /*
+ The primary function is marked with \overload. Find an
+ overload in the secondary function map that is not marked
+ with \overload but that is active and not private. Then
+ swap it with the primary function.
+ */
+ NodeList& overloads = secondaryFunctionMap_[primaryFunc->name()];
+ NodeList::ConstIterator s = overloads.constBegin();
+ while (s != overloads.constEnd()) {
+ FunctionNode *overloadFunc = (FunctionNode *) *s;
+ if (!overloadFunc->isOverload()) {
+ if (overloadFunc->status() == Active && overloadFunc->access() != Private) {
+ primaryFunc->setOverloadNumber(overloadFunc->overloadNumber());
+ overloads.replace(overloads.indexOf(overloadFunc), primaryFunc);
+ *p = overloadFunc;
+ overloadFunc->setOverloadFlag(false);
+ overloadFunc->setOverloadNumber(0);
+ break;
+ }
+ }
+ ++s;
+ }
+ }
+ }
+ ++p;
+ }
+ /*
+ Recursive part.
+ */
+ NodeList::ConstIterator c = childNodes().constBegin();
+ while (c != childNodes().constEnd()) {
+ if ((*c)->isAggregate())
+ ((Aggregate *) *c)->normalizeOverloads();
+ ++c;
+ }
+}
+
+/*!
+ */
+void Aggregate::removeFromRelated()
+{
+ while (!related_.isEmpty()) {
+ Node *p = static_cast<Node *>(related_.takeFirst());
+
+ if (p != 0 && p->relates() == this) p->clearRelated();
+ }
+}
+
+/*!
+ Deletes all this node's children.
+ */
+void Aggregate::deleteChildren()
+{
+ NodeList childrenCopy = children_;
+ // Clear internal collections before deleting child nodes
+ children_.clear();
+ childMap_.clear();
+ enumChildren_.clear();
+ primaryFunctionMap_.clear();
+ secondaryFunctionMap_.clear();
+ qDeleteAll(childrenCopy);
+}
+
+/*! \fn bool Aggregate::isAggregate() const
+ Returns \c true because this is an inner node.
+ */
+
+/*!
+ Returns \c true if the node is a class node or a QML type node
+ that is marked as being a wrapper class or QML type, or if
+ it is a member of a wrapper class or type.
+ */
+bool Node::isWrapper() const
+{
+ return (parent_ ? parent_->isWrapper() : false);
+}
+
+/*!
+ Finds the enum type node that has \a enumValue as one of
+ its enum values and returns a pointer to it. Returns 0 if
+ no enum type node is found that has \a enumValue as one
+ of its values.
+ */
+const EnumNode *Aggregate::findEnumNodeForValue(const QString &enumValue) const
+{
+ foreach (const Node *node, enumChildren_) {
+ const EnumNode *en = static_cast<const EnumNode *>(node);
+ if (en->hasItem(enumValue))
+ return en;
+ }
+ return 0;
+}
+
+/*!
+ Returns a node list containing all the member functions of
+ some class such that the functions overload the name \a funcName.
+ */
+NodeList Aggregate::overloads(const QString &funcName) const
+{
+ NodeList result;
+ Node *primary = primaryFunctionMap_.value(funcName);
+ if (primary) {
+ result << primary;
+ result += secondaryFunctionMap_[funcName];
+ }
+ return result;
+}
+
+/*!
+ Construct an inner node (i.e., not a leaf node) of the
+ given \a type and having the given \a parent and \a name.
+ */
+Aggregate::Aggregate(NodeType type, Aggregate *parent, const QString& name)
+ : Node(type, parent, name)
+{
+ switch (type) {
+ case Class:
+ case QmlType:
+ case Namespace:
+ setPageType(ApiPage);
+ break;
+ default:
+ break;
+ }
+}
+
+/*!
+ Appends an \a include file to the list of include files.
+ */
+void Aggregate::addInclude(const QString& include)
+{
+ includes_.append(include);
+}
+
+/*!
+ Sets the list of include files to \a includes.
+ */
+void Aggregate::setIncludes(const QStringList& includes)
+{
+ includes_ = includes;
+}
+
+/*!
+ f1 is always the clone
+ */
+bool Aggregate::isSameSignature(const FunctionNode *f1, const FunctionNode *f2)
+{
+ if (f1->parameters().size() != f2->parameters().size())
+ return false;
+ if (f1->isConst() != f2->isConst())
+ return false;
+
+ QVector<Parameter>::ConstIterator p1 = f1->parameters().constBegin();
+ QVector<Parameter>::ConstIterator p2 = f2->parameters().constBegin();
+ while (p2 != f2->parameters().constEnd()) {
+ if ((*p1).hasType() && (*p2).hasType()) {
+ if ((*p1).rightType() != (*p2).rightType())
+ return false;
+
+ QString t1 = p1->dataType();
+ QString t2 = p2->dataType();
+
+ 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;
+}
+
+/*!
+ Adds the \a child to this node's child list. It might also
+ be necessary to update this node's internal collections and
+ the child's parent pointer and output subdirectory.
+ */
+void Aggregate::addChild(Node *child)
+{
+ children_.append(child);
+ if (child->type() == Function
+ || child->type() == QmlMethod
+ || child->type() == QmlSignal) {
+ FunctionNode *func = static_cast<FunctionNode*>(child);
+ QString name = func->name();
+ if (!primaryFunctionMap_.contains(name)) {
+ primaryFunctionMap_.insert(name, func);
+ func->setOverloadNumber(0);
+ }
+ else {
+ NodeList &overloads = secondaryFunctionMap_[name];
+ overloads.append(func);
+ func->setOverloadNumber(overloads.size());
+ }
+ }
+ else {
+ if (child->type() == Enum)
+ enumChildren_.append(child);
+ childMap_.insertMulti(child->name(), child);
+ }
+ if (child->parent() == 0) {
+ child->setParent(this);
+ child->setOutputSubdirectory(this->outputSubdirectory());
+ }
+}
+
+/*!
+ Adds the \a child to this node's child map using \a title
+ as the key. The \a child is not added to the child list
+ again, because it is presumed to already be there. We just
+ want to be able to find the child by its \a title.
+ */
+void Aggregate::addChild(Node* child, const QString& title)
+{
+ childMap_.insertMulti(title, child);
+}
+
+/*!
+ The \a child is removed from this node's child list and
+ from this node's internal collections. The child's parent
+ pointer is set to 0, but its output subdirectory is not
+ changed.
+ */
+void Aggregate::removeChild(Node *child)
+{
+ children_.removeAll(child);
+ enumChildren_.removeAll(child);
+ if (child->type() == Function
+ || child->type() == QmlMethod
+ || child->type() == QmlSignal) {
+ QMap<QString, Node *>::Iterator primary = primaryFunctionMap_.find(child->name());
+ NodeList& overloads = secondaryFunctionMap_[child->name()];
+ if (primary != primaryFunctionMap_.end() && *primary == child) {
+ primaryFunctionMap_.erase(primary);
+ if (!overloads.isEmpty()) {
+ FunctionNode* fn = static_cast<FunctionNode*>(overloads.takeFirst());
+ fn->setOverloadNumber(0);
+ primaryFunctionMap_.insert(child->name(), fn);
+ }
+ }
+ else
+ overloads.removeAll(child);
+ }
+ QMap<QString, Node *>::Iterator ent = childMap_.find(child->name());
+ while (ent != childMap_.end() && ent.key() == child->name()) {
+ if (*ent == child) {
+ childMap_.erase(ent);
+ break;
+ }
+ ++ent;
+ }
+ if (child->title().isEmpty())
+ return;
+ ent = childMap_.find(child->title());
+ while (ent != childMap_.end() && ent.key() == child->title()) {
+ if (*ent == child) {
+ childMap_.erase(ent);
+ break;
+ }
+ ++ent;
+ }
+ child->setParent(0);
+}
+
+/*!
+ Recursively sets the output subdirectory for children
+ */
+void Aggregate::setOutputSubdirectory(const QString &t)
+{
+ Node::setOutputSubdirectory(t);
+ for (int i = 0; i < childNodes().size(); ++i)
+ childNodes().at(i)->setOutputSubdirectory(t);
+}
+
+/*!
+ Find the module (Qt Core, Qt GUI, 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::physicalModuleName() const
+{
+ if (!physicalModuleName_.isEmpty())
+ return physicalModuleName_;
+
+ QString path = location().filePath();
+ QString pattern = QString("src") + QDir::separator();
+ int start = path.lastIndexOf(pattern);
+
+ if (start == -1)
+ return QString();
+
+ QString moduleDir = path.mid(start + pattern.size());
+ int finish = moduleDir.indexOf(QDir::separator());
+
+ if (finish == -1)
+ return QString();
+
+ QString physicalModuleName = moduleDir.left(finish);
+
+ if (physicalModuleName == "corelib")
+ return "QtCore";
+ else if (physicalModuleName == "uitools")
+ return "QtUiTools";
+ else if (physicalModuleName == "gui")
+ return "QtGui";
+ else if (physicalModuleName == "network")
+ return "QtNetwork";
+ else if (physicalModuleName == "opengl")
+ return "QtOpenGL";
+ else if (physicalModuleName == "svg")
+ return "QtSvg";
+ else if (physicalModuleName == "sql")
+ return "QtSql";
+ else if (physicalModuleName == "qtestlib")
+ return "QtTest";
+ else if (moduleDir.contains("webkit"))
+ return "QtWebKit";
+ else if (physicalModuleName == "xml")
+ return "QtXml";
+ else
+ return QString();
+}
+
+/*!
+ Removes a node from the list of nodes related to this one.
+ If it is a function node, also remove from the primary/
+ secondary function maps.
+ */
+void Aggregate::removeRelated(Node *pseudoChild)
+{
+ related_.removeAll(pseudoChild);
+
+ if (pseudoChild->isFunction()) {
+ QMap<QString, Node *>::Iterator p = primaryFunctionMap_.find(pseudoChild->name());
+ while (p != primaryFunctionMap_.end()) {
+ if (p.value() == pseudoChild) {
+ primaryFunctionMap_.erase(p);
+ break;
+ }
+ ++p;
+ }
+ NodeList& overloads = secondaryFunctionMap_[pseudoChild->name()];
+ overloads.removeAll(pseudoChild);
+ }
+}
+
+/*!
+ Adds \a pseudoChild to the list of nodes related to this one. Resolve a correct
+ overload number for a related non-member function.
+ */
+void Aggregate::addRelated(Node *pseudoChild)
+{
+ related_.append(pseudoChild);
+
+ if (pseudoChild->isFunction()) {
+ FunctionNode* fn = static_cast<FunctionNode*>(pseudoChild);
+ if (primaryFunctionMap_.contains(pseudoChild->name())) {
+ secondaryFunctionMap_[pseudoChild->name()].append(pseudoChild);
+ fn->setOverloadNumber(secondaryFunctionMap_[pseudoChild->name()].size());
+ fn->setOverloadFlag(true);
+ }
+ else {
+ primaryFunctionMap_.insert(pseudoChild->name(), pseudoChild);
+ fn->setOverloadNumber(0);
+ fn->setOverloadFlag(false);
+ }
+ }
+}
+
+/*!
+ If this node has a child that is a QML property named \a n,
+ return the pointer to that child.
+ */
+QmlPropertyNode* Aggregate::hasQmlProperty(const QString& n) const
+{
+ foreach (Node* child, childNodes()) {
+ if (child->type() == Node::QmlProperty) {
+ if (child->name() == n)
+ return static_cast<QmlPropertyNode*>(child);
+ }
+ else if (child->isQmlPropertyGroup()) {
+ QmlPropertyNode* t = child->hasQmlProperty(n);
+ if (t)
+ return t;
+ }
+ }
+ return 0;
+}
+
+/*!
+ If this node has a child that is a QML property named \a n
+ whose type (attached or normal property) matches \a attached,
+ return the pointer to that child.
+ */
+QmlPropertyNode* Aggregate::hasQmlProperty(const QString& n, bool attached) const
+{
+ foreach (Node* child, childNodes()) {
+ if (child->type() == Node::QmlProperty) {
+ if (child->name() == n && child->isAttached() == attached)
+ return static_cast<QmlPropertyNode*>(child);
+ }
+ else if (child->isQmlPropertyGroup()) {
+ QmlPropertyNode* t = child->hasQmlProperty(n, attached);
+ if (t)
+ return t;
+ }
+ }
+ return 0;
+}
+
+/*!
+ \class LeafNode
+ */
+
+/*! \fn bool LeafNode::isAggregate() const
+ Returns \c false because this is a LeafNode.
+ */
+
+/*!
+ Constructs a leaf node named \a name of the specified
+ \a type. The new leaf node becomes a child of \a parent.
+ */
+LeafNode::LeafNode(NodeType type, Aggregate *parent, const QString& name)
+ : Node(type, parent, name)
+{
+ switch (type) {
+ case Enum:
+ case Function:
+ case Typedef:
+ case Variable:
+ case QmlProperty:
+ case QmlSignal:
+ case QmlSignalHandler:
+ case QmlMethod:
+ case QmlBasicType:
+ setPageType(ApiPage);
+ break;
+ default:
+ break;
+ }
+}
+
+/*!
+ This constructor should only be used when this node's parent
+ is meant to be \a parent, but this node is not to be listed
+ as a child of \a parent. It is currently only used for the
+ documentation case where a \e{qmlproperty} command is used
+ to override the QML definition of a QML property.
+ */
+LeafNode::LeafNode(Aggregate* parent, NodeType type, const QString& name)
+ : Node(type, 0, name)
+{
+ setParent(parent);
+ switch (type) {
+ case Enum:
+ case Function:
+ case Typedef:
+ case Variable:
+ case QmlProperty:
+ case QmlSignal:
+ case QmlSignalHandler:
+ case QmlMethod:
+ setPageType(ApiPage);
+ break;
+ default:
+ break;
+ }
+}
+
+
+/*!
+ \class NamespaceNode
+ */
+
+/*!
+ Constructs a namespace node.
+ */
+NamespaceNode::NamespaceNode(Aggregate *parent, const QString& name)
+ : Aggregate(Namespace, parent, name), seen_(false), tree_(0)
+{
+ setGenus(Node::CPP);
+ setPageType(ApiPage);
+}
+
+/*!
+ \class ClassNode
+ \brief This class represents a C++ class.
+ */
+
+/*!
+ Constructs a class node. A class node will generate an API page.
+ */
+ClassNode::ClassNode(Aggregate *parent, const QString& name)
+ : Aggregate(Class, parent, name)
+{
+ abstract_ = false;
+ wrapper_ = false;
+ qmlelement = 0;
+ setGenus(Node::CPP);
+ setPageType(ApiPage);
+}
+
+/*!
+ Adds the base class \a node to this class's list of base
+ classes. The base class has the specified \a access. This
+ is a resolved base class.
+ */
+void ClassNode::addResolvedBaseClass(Access access, ClassNode* node)
+{
+ bases_.append(RelatedClass(access, node));
+ node->derived_.append(RelatedClass(access, this));
+}
+
+/*!
+ Adds the derived class \a node to this class's list of derived
+ classes. The derived class inherits this class with \a access.
+ */
+void ClassNode::addDerivedClass(Access access, ClassNode* node)
+{
+ derived_.append(RelatedClass(access, node));
+}
+
+/*!
+ Add an unresolved base class to this class node's list of
+ base classes. The unresolved base class will be resolved
+ before the generate phase of qdoc. In an unresolved base
+ class, the pointer to the base class node is 0.
+ */
+void ClassNode::addUnresolvedBaseClass(Access access,
+ const QStringList& path,
+ const QString& signature)
+{
+ bases_.append(RelatedClass(access, path, signature));
+}
+
+/*!
+ Add an unresolved \c using clause to this class node's list
+ of \c using clauses. The unresolved \c using clause will be
+ resolved before the generate phase of qdoc. In an unresolved
+ \c using clause, the pointer to the function node is 0.
+ */
+void ClassNode::addUnresolvedUsingClause(const QString& signature)
+{
+ usingClauses_.append(UsingClause(signature));
+}
+
+/*!
+ */
+void ClassNode::fixBaseClasses()
+{
+ int i;
+ i = 0;
+ QSet<ClassNode *> found;
+
+ // Remove private and duplicate base classes.
+ while (i < bases_.size()) {
+ ClassNode* bc = bases_.at(i).node_;
+ if (bc && (bc->access() == Node::Private || found.contains(bc))) {
+ RelatedClass rc = bases_.at(i);
+ bases_.removeAt(i);
+ ignoredBases_.append(rc);
+ const QList<RelatedClass> &bb = bc->baseClasses();
+ for (int j = bb.size() - 1; j >= 0; --j)
+ bases_.insert(i, bb.at(j));
+ }
+ else {
+ ++i;
+ }
+ found.insert(bc);
+ }
+
+ i = 0;
+ while (i < derived_.size()) {
+ ClassNode* dc = derived_.at(i).node_;
+ if (dc && dc->access() == Node::Private) {
+ derived_.removeAt(i);
+ const QList<RelatedClass> &dd = dc->derivedClasses();
+ for (int j = dd.size() - 1; j >= 0; --j)
+ derived_.insert(i, dd.at(j));
+ }
+ else {
+ ++i;
+ }
+ }
+}
+
+/*!
+ Not sure why this is needed.
+ */
+void ClassNode::fixPropertyUsingBaseClasses(PropertyNode* pn)
+{
+ QList<RelatedClass>::const_iterator bc = baseClasses().constBegin();
+ while (bc != baseClasses().constEnd()) {
+ ClassNode* cn = bc->node_;
+ if (cn) {
+ Node* n = cn->findChildNode(pn->name(), Node::Property);
+ if (n) {
+ PropertyNode* baseProperty = static_cast<PropertyNode*>(n);
+ cn->fixPropertyUsingBaseClasses(baseProperty);
+ pn->setOverriddenFrom(baseProperty);
+ }
+ else
+ cn->fixPropertyUsingBaseClasses(pn);
+ }
+ ++bc;
+ }
+}
+
+/*!
+ Search the child list to find the property node with the
+ specified \a name.
+ */
+PropertyNode* ClassNode::findPropertyNode(const QString& name)
+{
+ Node* n = findChildNode(name, Node::Property);
+
+ if (n)
+ return static_cast<PropertyNode*>(n);
+
+ PropertyNode* pn = 0;
+
+ const QList<RelatedClass> &bases = baseClasses();
+ if (!bases.isEmpty()) {
+ for (int i = 0; i < bases.size(); ++i) {
+ ClassNode* cn = bases[i].node_;
+ if (cn) {
+ pn = cn->findPropertyNode(name);
+ if (pn)
+ break;
+ }
+ }
+ }
+ const QList<RelatedClass>& ignoredBases = ignoredBaseClasses();
+ if (!ignoredBases.isEmpty()) {
+ for (int i = 0; i < ignoredBases.size(); ++i) {
+ ClassNode* cn = ignoredBases[i].node_;
+ if (cn) {
+ pn = cn->findPropertyNode(name);
+ if (pn)
+ break;
+ }
+ }
+ }
+
+ return pn;
+}
+
+/*!
+ This function does a recursive search of this class node's
+ base classes looking for one that has a QML element. If it
+ finds one, it returns the pointer to that QML element. If
+ it doesn't find one, it returns null.
+ */
+QmlTypeNode* ClassNode::findQmlBaseNode()
+{
+ QmlTypeNode* result = 0;
+ const QList<RelatedClass>& bases = baseClasses();
+
+ if (!bases.isEmpty()) {
+ for (int i = 0; i < bases.size(); ++i) {
+ ClassNode* cn = bases[i].node_;
+ if (cn && cn->qmlElement()) {
+ return cn->qmlElement();
+ }
+ }
+ for (int i = 0; i < bases.size(); ++i) {
+ ClassNode* cn = bases[i].node_;
+ if (cn) {
+ result = cn->findQmlBaseNode();
+ if (result != 0) {
+ return result;
+ }
+ }
+ }
+ }
+ return result;
+}
+
+/*!
+ \class DocumentNode
+ */
+
+/*!
+ The type of a DocumentNode is Document, and it has a \a subtype,
+ which specifies the type of DocumentNode. The page type for
+ the page index is set here.
+ */
+DocumentNode::DocumentNode(Aggregate* parent, const QString& name, DocSubtype subtype, Node::PageType ptype)
+ : Aggregate(Document, parent, name), nodeSubtype_(subtype)
+{
+ setGenus(Node::DOC);
+ switch (subtype) {
+ case Page:
+ setPageType(ptype);
+ break;
+ case DitaMap:
+ setPageType(ptype);
+ break;
+ case Example:
+ setPageType(ExamplePage);
+ break;
+ default:
+ break;
+ }
+}
+
+/*! \fn QString DocumentNode::title() const
+ Returns the document node's title. This is used for the page title.
+*/
+
+/*!
+ Sets the document node's \a title. This is used for the page title.
+ */
+void DocumentNode::setTitle(const QString &title)
+{
+ title_ = title;
+ parent()->addChild(this, title);
+}
+
+/*!
+ Returns the document node's full title, which is usually
+ just title(), but for some DocSubtype values is different
+ from title()
+ */
+QString DocumentNode::fullTitle() const
+{
+ if (nodeSubtype_ == File) {
+ if (title().isEmpty())
+ return name().mid(name().lastIndexOf('/') + 1) + " Example File";
+ else
+ return title();
+ }
+ else if (nodeSubtype_ == Image) {
+ if (title().isEmpty())
+ return name().mid(name().lastIndexOf('/') + 1) + " Image File";
+ else
+ return title();
+ }
+ else if (nodeSubtype_ == HeaderFile) {
+ if (title().isEmpty())
+ return name();
+ else
+ return name() + " - " + title();
+ }
+ else {
+ return title();
+ }
+}
+
+/*!
+ Returns the subtitle.
+ */
+QString DocumentNode::subTitle() const
+{
+ if (!subtitle_.isEmpty())
+ return subtitle_;
+
+ if ((nodeSubtype_ == File) || (nodeSubtype_ == Image)) {
+ if (title().isEmpty() && name().contains(QLatin1Char('/')))
+ return name();
+ }
+ return QString();
+}
+
+/*!
+ \class EnumNode
+ */
+
+/*!
+ The constructor for the node representing an enum type
+ has a \a parent class and an enum type \a name.
+ */
+EnumNode::EnumNode(Aggregate *parent, const QString& name)
+ : LeafNode(Enum, parent, name), flagsType_(0)
+{
+ setGenus(Node::CPP);
+}
+
+/*!
+ Add \a item to the enum type's item list.
+ */
+void EnumNode::addItem(const EnumItem& item)
+{
+ items_.append(item);
+ names_.insert(item.name());
+}
+
+/*!
+ Returns the access level of the enumeration item named \a name.
+ Apparently it is private if it has been omitted by qdoc's
+ omitvalue command. Otherwise it is public.
+ */
+Node::Access EnumNode::itemAccess(const QString &name) const
+{
+ if (doc().omitEnumItemNames().contains(name))
+ return Private;
+ return Public;
+}
+
+/*!
+ Returns the enum value associated with the enum \a name.
+ */
+QString EnumNode::itemValue(const QString &name) const
+{
+ foreach (const EnumItem &item, items_) {
+ if (item.name() == name)
+ return item.value();
+ }
+ return QString();
+}
+
+/*!
+ \class TypedefNode
+ */
+
+/*!
+ */
+TypedefNode::TypedefNode(Aggregate *parent, const QString& name)
+ : LeafNode(Typedef, parent, name), associatedEnum_(0)
+{
+ setGenus(Node::CPP);
+}
+
+/*!
+ */
+void TypedefNode::setAssociatedEnum(const EnumNode *enume)
+{
+ associatedEnum_ = enume;
+}
+
+/*!
+ \class Parameter
+ \brief The class Parameter contains one parameter.
+
+ A parameter can be a function parameter or a macro
+ parameter.
+ */
+
+/*!
+ Constructs this parameter from the left and right types
+ \a dataType and rightType, the parameter \a name, and the
+ \a defaultValue. In practice, \a rightType is not used,
+ and I don't know what is was meant for.
+ */
+Parameter::Parameter(const QString& dataType,
+ const QString& rightType,
+ const QString& name,
+ const QString& defaultValue)
+ : dataType_(dataType),
+ rightType_(rightType),
+ name_(name),
+ defaultValue_(defaultValue)
+{
+ // nothing.
+}
+
+/*!
+ Standard copy constructor copies \p.
+ */
+Parameter::Parameter(const Parameter& p)
+ : dataType_(p.dataType_),
+ rightType_(p.rightType_),
+ name_(p.name_),
+ defaultValue_(p.defaultValue_)
+{
+ // nothing.
+}
+
+/*!
+ standard assignment operator assigns \p.
+ */
+Parameter& Parameter::operator=(const Parameter& p)
+{
+ dataType_ = p.dataType_;
+ rightType_ = p.rightType_;
+ name_ = p.name_;
+ defaultValue_ = p.defaultValue_;
+ return *this;
+}
+
+/*!
+ Reconstructs the text describing the parameter and
+ returns it. If \a value is true, the default value
+ will be included, if there is one.
+ */
+QString Parameter::reconstruct(bool value) const
+{
+ QString p = dataType_ + rightType_;
+ if (!p.endsWith(QChar('*')) && !p.endsWith(QChar('&')) && !p.endsWith(QChar(' ')))
+ p += QLatin1Char(' ');
+ p += name_;
+ if (value && !defaultValue_.isEmpty())
+ p += " = " + defaultValue_;
+ return p;
+}
+
+
+/*!
+ \class FunctionNode
+ */
+
+/*!
+ Construct a function node for a C++ function. It's parent
+ is \a parent, and it's name is \a name.
+
+ Do not set overloadNumber_ in the initializer list because it
+ is set by addChild() in the Node base class.
+ */
+FunctionNode::FunctionNode(Aggregate *parent, const QString& name)
+ : LeafNode(Function, parent, name),
+ metaness_(Plain),
+ virtualness_(NonVirtual),
+ const_(false),
+ static_(false),
+ reimplemented_(false),
+ attached_(false),
+ privateSignal_(false),
+ overload_(false),
+ reimplementedFrom_(0)
+{
+ setGenus(Node::CPP);
+}
+
+/*!
+ Construct a function node for a QML method or signal, specified
+ by \a type. It's parent is \a parent, and it's name is \a name.
+ If \a attached is true, it is an attached method or signal.
+
+ Do not set overloadNumber_ in the initializer list because it
+ is set by addChild() in the Node base class.
+ */
+FunctionNode::FunctionNode(NodeType type, Aggregate *parent, const QString& name, bool attached)
+ : LeafNode(type, parent, name),
+ metaness_(Plain),
+ virtualness_(NonVirtual),
+ const_(false),
+ static_(false),
+ reimplemented_(false),
+ attached_(attached),
+ privateSignal_(false),
+ overload_(false),
+ reimplementedFrom_(0)
+{
+ setGenus(Node::QML);
+ if (type == QmlMethod || type == QmlSignal) {
+ if (name.startsWith("__"))
+ setStatus(Internal);
+ }
+ else if (type == Function)
+ setGenus(Node::CPP);
+}
+
+/*!
+ Sets the \a virtualness of this function. If the \a virtualness
+ is PureVirtual, and if the parent() is a ClassNode, set the parent's
+ \e abstract flag to true.
+ */
+void FunctionNode::setVirtualness(Virtualness v)
+{
+ virtualness_ = v;
+ if ((v == PureVirtual) && parent() && (parent()->type() == Node::Class))
+ parent()->setAbstract(true);
+}
+
+/*! \fn void FunctionNode::setOverloadFlag(bool b)
+ Sets this function node's overload flag to \a b.
+ It does not set the overload number.
+ */
+
+/*! \fn void FunctionNode::setOverloadNumber(unsigned char n)
+ Sets this function node's overload number to \a n.
+ It does not set the overload flag.
+ */
+
+/*!
+ Sets the function node's reimplementation flag to \a b.
+ When \a b is true, it is supposed to mean that this function
+ is a reimplementation of a virtual function in a base class,
+ but it really just means the \e {\\reimp} command was seen in
+ the qdoc comment.
+ */
+void FunctionNode::setReimplemented(bool b)
+{
+ reimplemented_ = b;
+}
+
+/*!
+ Append \a parameter to the parameter list.
+ */
+void FunctionNode::addParameter(const Parameter& parameter)
+{
+ parameters_.append(parameter);
+}
+
+/*!
+ */
+void FunctionNode::borrowParameterNames(const FunctionNode *source)
+{
+ QVector<Parameter>::Iterator t = parameters_.begin();
+ QVector<Parameter>::ConstIterator s = source->parameters_.constBegin();
+ while (s != source->parameters_.constEnd() && t != parameters_.end()) {
+ if (!(*s).name().isEmpty())
+ (*t).setName((*s).name());
+ ++s;
+ ++t;
+ }
+}
+
+/*!
+ If this function is a reimplementation, \a from points
+ to the FunctionNode of the function being reimplemented.
+ */
+void FunctionNode::setReimplementedFrom(FunctionNode *f)
+{
+ reimplementedFrom_ = f;
+ f->reimplementedBy_.append(this);
+}
+
+/*!
+ Adds the "associated" property \a p to this function node.
+ The function might be the setter or getter for a property,
+ for example.
+ */
+void FunctionNode::addAssociatedProperty(PropertyNode *p)
+{
+ associatedProperties_.append(p);
+}
+
+/*!
+ Returns true if this function has at least one property
+ that is active, i.e. at least one property that is not
+ obsolete.
+ */
+bool FunctionNode::hasActiveAssociatedProperty() const
+{
+ if (associatedProperties_.isEmpty())
+ return false;
+ foreach (const PropertyNode* p, associatedProperties_) {
+ if (!p->isObsolete())
+ return true;
+ }
+ return false;
+}
+
+/*! \fn unsigned char FunctionNode::overloadNumber() const
+ Returns the overload number for this function.
+ */
+
+/*!
+ Returns the list of parameter names.
+ */
+QStringList FunctionNode::parameterNames() const
+{
+ QStringList names;
+ QVector<Parameter>::ConstIterator p = parameters().constBegin();
+ while (p != parameters().constEnd()) {
+ names << (*p).name();
+ ++p;
+ }
+ return names;
+}
+
+/*!
+ Returns a raw list of parameters. If \a names is true, the
+ names are included. If \a values is true, the default values
+ are included, if any are present.
+ */
+QString FunctionNode::rawParameters(bool names, bool values) const
+{
+ QString raw;
+ foreach (const Parameter &parameter, parameters()) {
+ raw += parameter.dataType() + parameter.rightType();
+ if (names)
+ raw += parameter.name();
+ if (values)
+ raw += parameter.defaultValue();
+ }
+ return raw;
+}
+
+/*!
+ Returns the list of reconstructed parameters. If \a values
+ is true, the default values are included, if any are present.
+ */
+QStringList FunctionNode::reconstructParameters(bool values) const
+{
+ QStringList reconstructedParameters;
+ QVector<Parameter>::ConstIterator p = parameters().constBegin();
+ while (p != parameters().constEnd()) {
+ reconstructedParameters << (*p).reconstruct(values);
+ ++p;
+ }
+ return reconstructedParameters;
+}
+
+/*!
+ Reconstructs and returns the function's signature. If \a values
+ is true, the default values of the parameters are included, if
+ present.
+ */
+QString FunctionNode::signature(bool values) const
+{
+ QString s;
+ if (!returnType().isEmpty())
+ s = returnType() + QLatin1Char(' ');
+ s += name() + QLatin1Char('(');
+ QStringList reconstructedParameters = reconstructParameters(values);
+ int p = reconstructedParameters.size();
+ if (p > 0) {
+ for (int i=0; i<p; i++) {
+ s += reconstructedParameters[i];
+ if (i < (p-1))
+ s += ", ";
+ }
+ }
+ s += QLatin1Char(')');
+ return s;
+}
+
+/*!
+ Returns true if function \a fn has role \a r for this
+ property.
+ */
+PropertyNode::FunctionRole PropertyNode::role(const FunctionNode* fn) const
+{
+ for (int i=0; i<4; i++) {
+ if (functions_[i].contains(const_cast<FunctionNode*>(fn)))
+ return (FunctionRole) i;
+ }
+ return Notifier;
+}
+
+/*!
+ Print some debugging stuff.
+ */
+void FunctionNode::debug() const
+{
+ qDebug("QML METHOD %s returnType_ %s parentPath_ %s",
+ qPrintable(name()), qPrintable(returnType_), qPrintable(parentPath_.join(' ')));
+}
+
+/*!
+ \class PropertyNode
+
+ This class describes one instance of using the Q_PROPERTY macro.
+ */
+
+/*!
+ The constructor sets the \a parent and the \a name, but
+ everything else is set to default values.
+ */
+PropertyNode::PropertyNode(Aggregate *parent, const QString& name)
+ : LeafNode(Property, parent, name),
+ stored_(FlagValueDefault),
+ designable_(FlagValueDefault),
+ scriptable_(FlagValueDefault),
+ writable_(FlagValueDefault),
+ user_(FlagValueDefault),
+ const_(false),
+ final_(false),
+ revision_(-1),
+ overrides_(0)
+{
+ setGenus(Node::CPP);
+}
+
+/*!
+ Sets this property's \e {overridden from} property to
+ \a baseProperty, which indicates that this property
+ overrides \a baseProperty. To begin with, all the values
+ in this property are set to the corresponding values in
+ \a baseProperty.
+
+ We probably should ensure that the constant and final
+ attributes are not being overridden improperly.
+ */
+void PropertyNode::setOverriddenFrom(const PropertyNode* baseProperty)
+{
+ for (int i = 0; i < NumFunctionRoles; ++i) {
+ if (functions_[i].isEmpty())
+ functions_[i] = baseProperty->functions_[i];
+ }
+ if (stored_ == FlagValueDefault)
+ stored_ = baseProperty->stored_;
+ if (designable_ == FlagValueDefault)
+ designable_ = baseProperty->designable_;
+ if (scriptable_ == FlagValueDefault)
+ scriptable_ = baseProperty->scriptable_;
+ if (writable_ == FlagValueDefault)
+ writable_ = baseProperty->writable_;
+ if (user_ == FlagValueDefault)
+ user_ = baseProperty->user_;
+ overrides_ = baseProperty;
+}
+
+/*!
+ */
+QString PropertyNode::qualifiedDataType() const
+{
+ if (setters().isEmpty() && resetters().isEmpty()) {
+ if (type_.contains(QLatin1Char('*')) || type_.contains(QLatin1Char('&'))) {
+ // 'QWidget *' becomes 'QWidget *' const
+ return type_ + " const";
+ }
+ else {
+ /*
+ 'int' becomes 'const int' ('int const' is
+ correct C++, but looks wrong)
+ */
+ return "const " + type_;
+ }
+ }
+ else {
+ return type_;
+ }
+}
+
+bool QmlTypeNode::qmlOnly = false;
+QMultiMap<QString,Node*> QmlTypeNode::inheritedBy;
+
+/*!
+ Constructs a Qml class node. The new node has the given
+ \a parent and \a name.
+ */
+QmlTypeNode::QmlTypeNode(Aggregate *parent, const QString& name)
+ : Aggregate(QmlType, parent, name),
+ abstract_(false),
+ cnodeRequired_(false),
+ wrapper_(false),
+ cnode_(0),
+ logicalModule_(0),
+ qmlBaseNode_(0)
+{
+ int i = 0;
+ if (name.startsWith("QML:")) {
+ qDebug() << "BOGUS QML qualifier:" << name;
+ i = 4;
+ }
+ setTitle(name.mid(i));
+ setPageType(Node::ApiPage);
+ setGenus(Node::QML);
+}
+
+/*!
+ Needed for printing a debug messages.
+ */
+QmlTypeNode::~QmlTypeNode()
+{
+ // nothing.
+}
+
+/*!
+ Clear the static maps so that subsequent runs don't try to use
+ contents from a previous run.
+ */
+void QmlTypeNode::terminate()
+{
+ inheritedBy.clear();
+}
+
+/*!
+ Record the fact that QML class \a base is inherited by
+ QML class \a sub.
+ */
+void QmlTypeNode::addInheritedBy(const QString& base, Node* sub)
+{
+ if (sub->isInternal())
+ return;
+ if (inheritedBy.constFind(base,sub) == inheritedBy.constEnd())
+ inheritedBy.insert(base,sub);
+}
+
+/*!
+ Loads the list \a subs with the nodes of all the subclasses of \a base.
+ */
+void QmlTypeNode::subclasses(const QString& base, NodeList& subs)
+{
+ subs.clear();
+ if (inheritedBy.count(base) > 0) {
+ subs = inheritedBy.values(base);
+ }
+}
+
+QmlTypeNode* QmlTypeNode::qmlBaseNode()
+{
+ if (!qmlBaseNode_ && !qmlBaseName_.isEmpty()) {
+ qmlBaseNode_ = QDocDatabase::qdocDB()->findQmlType(qmlBaseName_);
+ }
+ return qmlBaseNode_;
+}
+
+/*!
+ If this QML type node has a base type node,
+ return the fully qualified name of that QML
+ type, i.e. <QML-module-name>::<QML-type-name>.
+ */
+QString QmlTypeNode::qmlFullBaseName() const
+{
+ QString result;
+ if (qmlBaseNode_) {
+ result = qmlBaseNode_->logicalModuleName() + "::" + qmlBaseNode_->name();
+ }
+ return result;
+}
+
+/*!
+ If the QML type's QML module pointer is set, return the QML
+ module name from the QML module node. Otherwise, return the
+ empty string.
+ */
+QString QmlTypeNode::logicalModuleName() const
+{
+ return (logicalModule_ ? logicalModule_->logicalModuleName() : QString());
+}
+
+/*!
+ If the QML type's QML module pointer is set, return the QML
+ module version from the QML module node. Otherwise, return
+ the empty string.
+ */
+QString QmlTypeNode::logicalModuleVersion() const
+{
+ return (logicalModule_ ? logicalModule_->logicalModuleVersion() : QString());
+}
+
+/*!
+ If the QML type's QML module pointer is set, return the QML
+ module identifier from the QML module node. Otherwise, return
+ the empty string.
+ */
+QString QmlTypeNode::logicalModuleIdentifier() const
+{
+ return (logicalModule_ ? logicalModule_->logicalModuleIdentifier() : QString());
+}
+
+/*!
+ Constructs a Qml basic type node. The new node has the given
+ \a parent and \a name.
+ */
+QmlBasicTypeNode::QmlBasicTypeNode(Aggregate *parent,
+ const QString& name)
+ : Aggregate(QmlBasicType, parent, name)
+{
+ setTitle(name);
+ setGenus(Node::QML);
+}
+
+/*!
+ Constructor for the Qml property group node. \a parent is
+ always a QmlTypeNode.
+ */
+QmlPropertyGroupNode::QmlPropertyGroupNode(QmlTypeNode* parent, const QString& name)
+ : Aggregate(QmlPropertyGroup, parent, name)
+{
+ idNumber_ = -1;
+ setGenus(Node::QML);
+}
+
+/*!
+ Return the property group node's id number for use in
+ constructing an id attribute for the property group.
+ If the id number is currently -1, increment the global
+ property group count and set the id number to the new
+ value.
+ */
+QString QmlPropertyGroupNode::idNumber()
+{
+ if (idNumber_ == -1)
+ idNumber_ = incPropertyGroupCount();
+ return QString().setNum(idNumber_);
+}
+
+/*!
+ Constructor for the QML property node.
+ */
+QmlPropertyNode::QmlPropertyNode(Aggregate* parent,
+ const QString& name,
+ const QString& type,
+ bool attached)
+ : LeafNode(QmlProperty, parent, name),
+ type_(type),
+ stored_(FlagValueDefault),
+ designable_(FlagValueDefault),
+ isAlias_(false),
+ isdefault_(false),
+ attached_(attached),
+ readOnly_(FlagValueDefault)
+{
+ setPageType(ApiPage);
+ if (type_ == QString("alias"))
+ isAlias_ = true;
+ if (name.startsWith("__"))
+ setStatus(Internal);
+ setGenus(Node::QML);
+}
+
+/*!
+ Returns \c true if a QML property or attached property is
+ not read-only. The algorithm for figuring this out is long
+ amd tedious and almost certainly will break. It currently
+ doesn't work for the qmlproperty:
+
+ \code
+ bool PropertyChanges::explicit,
+ \endcode
+
+ ...because the tokenizer gets confused on \e{explicit}.
+ */
+bool QmlPropertyNode::isWritable()
+{
+ if (readOnly_ != FlagValueDefault)
+ return !fromFlagValue(readOnly_, false);
+
+ QmlTypeNode* qcn = qmlTypeNode();
+ if (qcn) {
+ if (qcn->cppClassRequired()) {
+ if (qcn->classNode()) {
+ PropertyNode* pn = findCorrespondingCppProperty();
+ if (pn)
+ return pn->isWritable();
+ else
+ defLocation().warning(tr("No Q_PROPERTY for QML property %1::%2::%3 "
+ "in C++ class documented as QML type: "
+ "(property not found in the C++ class or its base classes)")
+ .arg(logicalModuleName()).arg(qmlTypeName()).arg(name()));
+ }
+ else
+ defLocation().warning(tr("No Q_PROPERTY for QML property %1::%2::%3 "
+ "in C++ class documented as QML type: "
+ "(C++ class not specified or not found).")
+ .arg(logicalModuleName()).arg(qmlTypeName()).arg(name()));
+ }
+ }
+ return true;
+}
+
+/*!
+ Returns a pointer this QML property's corresponding C++
+ property, if it has one.
+ */
+PropertyNode* QmlPropertyNode::findCorrespondingCppProperty()
+{
+ PropertyNode* pn;
+ Node* n = parent();
+ while (n && !(n->isQmlType() || n->isJsType()))
+ n = n->parent();
+ if (n) {
+ QmlTypeNode* qcn = static_cast<QmlTypeNode*>(n);
+ ClassNode* cn = qcn->classNode();
+ if (cn) {
+ /*
+ If there is a dot in the property name, first
+ find the C++ property corresponding to the QML
+ property group.
+ */
+ QStringList dotSplit = name().split(QChar('.'));
+ pn = cn->findPropertyNode(dotSplit[0]);
+ if (pn) {
+ /*
+ Now find the C++ property corresponding to
+ the QML property in the QML property group,
+ <group>.<property>.
+ */
+ if (dotSplit.size() > 1) {
+ QStringList path(extractClassName(pn->qualifiedDataType()));
+ Node* nn = QDocDatabase::qdocDB()->findClassNode(path);
+ if (nn) {
+ ClassNode* cn = static_cast<ClassNode*>(nn);
+ PropertyNode *pn2 = cn->findPropertyNode(dotSplit[1]);
+ /*
+ If found, return the C++ property
+ corresponding to the QML property.
+ Otherwise, return the C++ property
+ corresponding to the QML property
+ group.
+ */
+ return (pn2 ? pn2 : pn);
+ }
+ }
+ else
+ return pn;
+ }
+ }
+ }
+ return 0;
+}
+
+/*!
+ This returns the name of the owning QML type.
+ */
+QString QmlPropertyNode::element() const
+{
+ if (parent()->isQmlPropertyGroup())
+ return parent()->element();
+ return parent()->name();
+}
+
+/*!
+ Construct the full document name for this node and return it.
+ */
+QString Node::fullDocumentName() const
+{
+ QStringList pieces;
+ const Node* n = this;
+
+ do {
+ if (!n->name().isEmpty() && !n->isQmlPropertyGroup())
+ pieces.insert(0, n->name());
+
+ if ((n->isQmlType() || n->isJsType()) && !n->logicalModuleName().isEmpty()) {
+ pieces.insert(0, n->logicalModuleName());
+ break;
+ }
+
+ if (n->isDocumentNode())
+ 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.
+ QString concatenator = "::";
+ if (n->isQmlType() || n->isJsType())
+ concatenator = QLatin1Char('.');
+
+ if (n->isDocumentNode())
+ concatenator = QLatin1Char('#');
+
+ return pieces.join(concatenator);
+}
+
+/*!
+ Returns the \a str as an NCName, which means the name can
+ be used as the value of an \e id attribute. Search for NCName
+ on the internet for details of what can be an NCName.
+ */
+QString Node::cleanId(const QString &str)
+{
+ QString clean;
+ QString name = str.simplified();
+
+ if (name.isEmpty())
+ return clean;
+
+ name = name.replace("::","-");
+ name = name.replace(QLatin1Char(' '), QLatin1Char('-'));
+ name = name.replace("()","-call");
+
+ clean.reserve(name.size() + 20);
+ if (!str.startsWith("id-"))
+ clean = "id-";
+ const QChar c = name[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 += QLatin1Char('a');
+ }
+
+ for (int i = 1; i < (int) name.length(); i++) {
+ const QChar c = name[i];
+ const uint u = c.unicode();
+ if ((u >= 'a' && u <= 'z') ||
+ (u >= 'A' && u <= 'Z') ||
+ (u >= '0' && u <= '9') || u == '-' ||
+ u == '_' || u == '.') {
+ clean += c;
+ }
+ else if (c.isSpace() || u == ':' ) {
+ clean += QLatin1Char('-');
+ }
+ 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 += "-hash";
+ }
+ else if (u == '(') {
+ clean += QLatin1Char('-');
+ }
+ else if (u == ')') {
+ clean += QLatin1Char('-');
+ }
+ else {
+ clean += QLatin1Char('-');
+ clean += QString::number((int)u, 16);
+ }
+ }
+ return clean;
+}
+
+#if 0
+/*!
+ Creates a string that can be used as a UUID for the node,
+ depending on the type and subtype of the node. Uniquenss
+ is not guaranteed, but it is expected that strings created
+ here will be unique within an XML document. Hence, the
+ returned string can be used as the value of an \e id
+ attribute.
+ */
+QString Node::idForNode() const
+{
+ const FunctionNode* func;
+ const TypedefNode* tdn;
+ QString str;
+
+ switch (type()) {
+ case Node::Namespace:
+ str = "namespace-" + fullDocumentName();
+ break;
+ case Node::Class:
+ str = "class-" + fullDocumentName();
+ break;
+ case Node::Enum:
+ str = "enum-" + name();
+ break;
+ case Node::Typedef:
+ tdn = static_cast<const TypedefNode*>(this);
+ if (tdn->associatedEnum()) {
+ return tdn->associatedEnum()->idForNode();
+ }
+ else {
+ str = "typedef-" + name();
+ }
+ break;
+ case Node::Function:
+ func = static_cast<const FunctionNode*>(this);
+ if (func->associatedProperty()) {
+ return func->associatedProperty()->idForNode();
+ }
+ else {
+ if (func->name().startsWith("operator")) {
+ str.clear();
+ /*
+ The test below should probably apply to all
+ functions, but for now, overloaded operators
+ are the only ones that produce duplicate id
+ attributes in the DITA XML files.
+ */
+ if (relatesTo_)
+ str = "nonmember-";
+ QString op = func->name().mid(8);
+ if (!op.isEmpty()) {
+ int i = 0;
+ while (i<op.size() && op.at(i) == ' ')
+ ++i;
+ if (i>0 && i<op.size()) {
+ op = op.mid(i);
+ }
+ if (!op.isEmpty()) {
+ i = 0;
+ while (i < op.size()) {
+ const QChar c = op.at(i);
+ const uint u = c.unicode();
+ if ((u >= 'a' && u <= 'z') ||
+ (u >= 'A' && u <= 'Z') ||
+ (u >= '0' && u <= '9'))
+ break;
+ ++i;
+ }
+ str += "operator-";
+ if (i>0) {
+ QString tail = op.mid(i);
+ op = op.left(i);
+ if (operators_.contains(op)) {
+ str += operators_.value(op);
+ if (!tail.isEmpty())
+ str += QLatin1Char('-') + tail;
+ }
+ else
+ qDebug() << "qdoc internal error: Operator missing from operators_ map:" << op;
+ }
+ else {
+ str += op;
+ }
+ }
+ }
+ }
+ else if (parent_) {
+ if (parent_->isClass())
+ str = "class-member-" + func->name();
+ else if (parent_->isNamespace())
+ str = "namespace-member-" + func->name();
+ else if (parent_->isQmlType())
+ str = "qml-method-" + parent_->name().toLower() + "-" + func->name();
+ else if (parent_->isJsType())
+ str = "js-method-" + parent_->name().toLower() + "-" + func->name();
+ else if (parent_->type() == Document) {
+ qDebug() << "qdoc internal error: Node subtype not handled:"
+ << parent_->docSubtype() << func->name();
+ }
+ else
+ qDebug() << "qdoc internal error: Node type not handled:"
+ << parent_->type() << func->name();
+
+ }
+ if (func->overloadNumber() != 0)
+ str += QLatin1Char('-') + QString::number(func->overloadNumber());
+ }
+ break;
+ case Node::QmlType:
+ if (genus() == QML)
+ str = "qml-class-" + name();
+ else
+ str = "js-type-" + name();
+ break;
+ case Node::QmlBasicType:
+ if (genus() == QML)
+ str = "qml-basic-type-" + name();
+ else
+ str = "js-basic-type-" + name();
+ break;
+ case Node::Document:
+ {
+ switch (docSubtype()) {
+ case Node::Page:
+ case Node::HeaderFile:
+ str = title();
+ if (str.isEmpty()) {
+ str = name();
+ if (str.endsWith(".html"))
+ str.remove(str.size()-5,5);
+ }
+ str.replace(QLatin1Char('/'), QLatin1Char('-'));
+ break;
+ case Node::File:
+ str = name();
+ str.replace(QLatin1Char('/'), QLatin1Char('-'));
+ break;
+ case Node::Example:
+ str = name();
+ str.replace(QLatin1Char('/'), QLatin1Char('-'));
+ break;
+ default:
+ qDebug() << "ERROR: A case was not handled in Node::idForNode():"
+ << "docSubtype():" << docSubtype() << "type():" << type();
+ break;
+ }
+ }
+ break;
+ case Node::Group:
+ case Node::Module:
+ str = title();
+ if (str.isEmpty()) {
+ str = name();
+ if (str.endsWith(".html"))
+ str.remove(str.size()-5,5);
+ }
+ str.replace(QLatin1Char('/'), QLatin1Char('-'));
+ break;
+ case Node::QmlModule:
+ if (genus() == QML)
+ str = "qml-module-" + name();
+ else
+ str = "js-module-" + name();
+ break;
+ case Node::QmlProperty:
+ if (genus() == QML)
+ str = "qml-";
+ else
+ str = "js-";
+ if (isAttached())
+ str += "attached-property-" + name();
+ else
+ str += "property-" + name();
+ break;
+ case Node::QmlPropertyGroup:
+ {
+ Node* n = const_cast<Node*>(this);
+ if (genus() == QML)
+ str = "qml-propertygroup-" + n->name();
+ else
+ str = "js-propertygroup-" + n->name();
+ }
+ break;
+ case Node::Property:
+ str = "property-" + name();
+ break;
+ case Node::QmlSignal:
+ if (genus() == QML)
+ str = "qml-signal-" + name();
+ else
+ str = "js-signal-" + name();
+ break;
+ case Node::QmlSignalHandler:
+ if (genus() == QML)
+ str = "qml-signal-handler-" + name();
+ else
+ str = "js-signal-handler-" + name();
+ break;
+ case Node::QmlMethod:
+ func = static_cast<const FunctionNode*>(this);
+ if (genus() == QML)
+ str = "qml-method-";
+ else
+ str = "js-method-";
+ str += parent_->name().toLower() + "-" + func->name();
+ if (func->overloadNumber() != 0)
+ str += QLatin1Char('-') + QString::number(func->overloadNumber());
+ break;
+ case Node::Variable:
+ str = "var-" + name();
+ break;
+ default:
+ qDebug() << "ERROR: A case was not handled in Node::idForNode():"
+ << "type():" << type() << "docSubtype():" << docSubtype();
+ break;
+ }
+ if (str.isEmpty()) {
+ qDebug() << "ERROR: A link text was empty in Node::idForNode():"
+ << "type():" << type() << "docSubtype():" << docSubtype()
+ << "name():" << name()
+ << "title():" << title();
+ }
+ else {
+ str = cleanId(str);
+ }
+ return str;
+}
+#endif
+
+/*!
+ Prints the inner node's list of children.
+ For debugging only.
+ */
+void Aggregate::printChildren(const QString& title)
+{
+ qDebug() << title << name() << children_.size();
+ if (children_.size() > 0) {
+ for (int i=0; i<children_.size(); ++i) {
+ Node* n = children_.at(i);
+ qDebug() << " CHILD:" << n->name() << n->nodeTypeString() << n->nodeSubtypeString();
+ }
+ }
+}
+
+/*!
+ Returns \c true if the collection node's member list is
+ not empty.
+ */
+bool CollectionNode::hasMembers() const
+{
+ return !members_.isEmpty();
+}
+
+/*!
+ Appends \a node to the collection node's member list, if
+ and only if it isn't already in the member list.
+ */
+void CollectionNode::addMember(Node* node)
+{
+ if (!members_.contains(node))
+ members_.append(node);
+}
+
+/*!
+ Returns \c true if this collection node contains at least
+ one namespace node.
+ */
+bool CollectionNode::hasNamespaces() const
+{
+ if (!members_.isEmpty()) {
+ NodeList::const_iterator i = members_.begin();
+ while (i != members_.end()) {
+ if ((*i)->isNamespace())
+ return true;
+ ++i;
+ }
+ }
+ return false;
+}
+
+/*!
+ Returns \c true if this collection node contains at least
+ one class node.
+ */
+bool CollectionNode::hasClasses() const
+{
+ if (!members_.isEmpty()) {
+ NodeList::const_iterator i = members_.cbegin();
+ while (i != members_.cend()) {
+ if ((*i)->isClass())
+ return true;
+ ++i;
+ }
+ }
+ return false;
+}
+
+/*!
+ Loads \a out with all this collection node's members that
+ are namespace nodes.
+ */
+void CollectionNode::getMemberNamespaces(NodeMap& out)
+{
+ out.clear();
+ NodeList::const_iterator i = members_.cbegin();
+ while (i != members_.cend()) {
+ if ((*i)->isNamespace())
+ out.insert((*i)->name(),(*i));
+ ++i;
+ }
+}
+
+/*!
+ Loads \a out with all this collection node's members that
+ are class nodes.
+ */
+void CollectionNode::getMemberClasses(NodeMap& out) const
+{
+ out.clear();
+ NodeList::const_iterator i = members_.cbegin();
+ while (i != members_.cend()) {
+ if ((*i)->isClass())
+ out.insert((*i)->name(),(*i));
+ ++i;
+ }
+}
+
+/*!
+ Prints the collection node's list of members.
+ For debugging only.
+ */
+void CollectionNode::printMembers(const QString& title)
+{
+ qDebug() << title << name() << members_.size();
+ if (members_.size() > 0) {
+ for (int i=0; i<members_.size(); ++i) {
+ Node* n = members_.at(i);
+ qDebug() << " MEMBER:" << n->name() << n->nodeTypeString() << n->nodeSubtypeString();
+ }
+ }
+}
+
+/*!
+ Sets the document node's \a title. This is used for the page title.
+ */
+void CollectionNode::setTitle(const QString& title)
+{
+ title_ = title;
+ parent()->addChild(this, title);
+}
+
+/*!
+ This function splits \a arg on the blank character to get a
+ logical module name and version number. If the version number
+ is present, it spilts the version number on the '.' character
+ to get a major version number and a minor vrsion number. If
+ the version number is present, both the major and minor version
+ numbers should be there, but the minor version number is not
+ absolutely necessary.
+ */
+void CollectionNode::setLogicalModuleInfo(const QString& arg)
+{
+ QStringList blankSplit = arg.split(QLatin1Char(' '));
+ logicalModuleName_ = blankSplit[0];
+ if (blankSplit.size() > 1) {
+ QStringList dotSplit = blankSplit[1].split(QLatin1Char('.'));
+ logicalModuleVersionMajor_ = dotSplit[0];
+ if (dotSplit.size() > 1)
+ logicalModuleVersionMinor_ = dotSplit[1];
+ else
+ logicalModuleVersionMinor_ = "0";
+ }
+}
+
+/*!
+ This function accepts the logical module \a info as a string
+ list. If the logical module info contains the version number,
+ it spilts the version number on the '.' character to get the
+ major and minor vrsion numbers. Both major and minor version
+ numbers should be provided, but the minor version number is
+ not strictly necessary.
+ */
+void CollectionNode::setLogicalModuleInfo(const QStringList& info)
+{
+ logicalModuleName_ = info[0];
+ if (info.size() > 1) {
+ QStringList dotSplit = info[1].split(QLatin1Char('.'));
+ logicalModuleVersionMajor_ = dotSplit[0];
+ if (dotSplit.size() > 1)
+ logicalModuleVersionMinor_ = dotSplit[1];
+ else
+ logicalModuleVersionMinor_ = "0";
+ }
+}
+
+QT_END_NAMESPACE
diff --git a/src/qdoc/node.h b/src/qdoc/node.h
new file mode 100644
index 000000000..6b1032805
--- /dev/null
+++ b/src/qdoc/node.h
@@ -0,0 +1,1167 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL21$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** 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 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** As a special exception, The Qt Company gives you certain additional
+** rights. These rights are described in The Qt Company LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef NODE_H
+#define NODE_H
+
+#include <qdir.h>
+#include <qmap.h>
+#include <qpair.h>
+#include <qstringlist.h>
+
+#include "codechunk.h"
+#include "doc.h"
+
+QT_BEGIN_NAMESPACE
+
+class Node;
+class Tree;
+class EnumNode;
+class ClassNode;
+class Aggregate;
+class ExampleNode;
+class TypedefNode;
+class QmlTypeNode;
+class QDocDatabase;
+class FunctionNode;
+class PropertyNode;
+class CollectionNode;
+class QmlPropertyNode;
+
+typedef QList<Node*> NodeList;
+typedef QList<PropertyNode*> PropNodeList;
+typedef QMap<QString, Node*> NodeMap;
+typedef QMultiMap<QString, Node*> NodeMultiMap;
+typedef QPair<int, int> NodeTypePair;
+typedef QList<NodeTypePair> NodeTypeList;
+typedef QMap<QString, CollectionNode*> CNMap;
+typedef QMultiMap<QString, CollectionNode*> CNMultiMap;
+
+class Node
+{
+ Q_DECLARE_TR_FUNCTIONS(QDoc::Node)
+
+public:
+ enum NodeType {
+ NoType,
+ Namespace,
+ Class,
+ Document,
+ Enum,
+ Typedef,
+ Function,
+ Property,
+ Variable,
+ Group,
+ Module,
+ QmlType,
+ QmlModule,
+ QmlPropertyGroup,
+ QmlProperty,
+ QmlSignal,
+ QmlSignalHandler,
+ QmlMethod,
+ QmlBasicType,
+ LastType
+ };
+
+ enum DocSubtype {
+ NoSubtype,
+ Example,
+ HeaderFile,
+ File,
+ Image,
+ Page,
+ ExternalPage,
+ DitaMap,
+ LastSubtype
+ };
+
+ enum Genus { DontCare, CPP, JS, QML, DOC };
+
+ enum Access { Public, Protected, Private };
+
+ enum Status {
+ Compat,
+ Obsolete,
+ Deprecated,
+ Preliminary,
+ Active,
+ Internal,
+ Intermediate
+ }; // don't reorder this enum
+
+ enum ThreadSafeness {
+ UnspecifiedSafeness,
+ NonReentrant,
+ Reentrant,
+ ThreadSafe
+ };
+
+ enum LinkType {
+ StartLink,
+ NextLink,
+ PreviousLink,
+ ContentsLink,
+ IndexLink
+ };
+
+ enum PageType {
+ NoPageType,
+ ApiPage,
+ ArticlePage,
+ ExamplePage,
+ HowToPage,
+ OverviewPage,
+ TutorialPage,
+ FAQPage,
+ DitaMapPage,
+ OnBeyondZebra
+ };
+
+ enum FlagValue {
+ FlagValueDefault = -1,
+ FlagValueFalse = 0,
+ FlagValueTrue = 1
+ };
+
+ virtual ~Node();
+
+ QString plainName() const;
+ QString plainFullName(const Node* relative = 0) const;
+ QString fullName(const Node* relative=0) const;
+
+ const QString& fileNameBase() const { return fileNameBase_; }
+ bool hasFileNameBase() const { return !fileNameBase_.isEmpty(); }
+ void setFileNameBase(const QString& t) { fileNameBase_ = t; }
+ Node::Genus genus() const { return (Genus) genus_; }
+ void setGenus(Genus t) { genus_ = (unsigned char) t; }
+
+ void setAccess(Access t) { access_ = (unsigned char) t; }
+ void setLocation(const Location& t);
+ void setDoc(const Doc& doc, bool replace = false);
+ void setStatus(Status t) {
+ if (status_ == (unsigned char) Obsolete && t == Deprecated)
+ return;
+ status_ = (unsigned char) t;
+ }
+ void setThreadSafeness(ThreadSafeness t) { safeness_ = (unsigned char) t; }
+ void setSince(const QString &since);
+ void setRelates(Aggregate* pseudoParent);
+ void setRelates(const QString &name);
+ void setPhysicalModuleName(const QString &name) { physicalModuleName_ = name; }
+ void setUrl(const QString& url) { url_ = url; }
+ void setTemplateStuff(const QString &t) { templateStuff_ = t; }
+ void setReconstitutedBrief(const QString &t) { reconstitutedBrief_ = t; }
+ void setPageType(PageType t) { pageType_ = (unsigned char) t; }
+ void setPageType(const QString& t);
+ void setParent(Aggregate* n) { parent_ = n; }
+ void setIndexNodeFlag() { indexNodeFlag_ = true; }
+ virtual void setOutputFileName(const QString& ) { }
+
+ bool isQmlNode() const { return genus() == QML; }
+ bool isJsNode() const { return genus() == JS; }
+ bool isCppNode() const { return genus() == CPP; }
+
+ virtual bool isAggregate() const = 0;
+ virtual bool isCollectionNode() const { return false; }
+ virtual bool isDocumentNode() const { return false; }
+ virtual bool isGroup() const { return false; }
+ virtual bool isModule() const { return false; }
+ virtual bool isQmlModule() const { return false; }
+ virtual bool isJsModule() const { return false; }
+ virtual bool isQmlType() const { return false; }
+ virtual bool isJsType() const { return false; }
+ virtual bool isQmlBasicType() const { return false; }
+ virtual bool isJsBasicType() const { return false; }
+ virtual bool isEnumType() const { return false; }
+ virtual bool isExample() const { return false; }
+ virtual bool isExampleFile() const { return false; }
+ virtual bool isHeaderFile() const { return false; }
+ virtual bool isLeaf() const { return false; }
+ virtual bool isReimplemented() const { return false; }
+ virtual bool isFunction() const { return false; }
+ virtual bool isNamespace() const { return false; }
+ virtual bool isClass() const { return false; }
+ virtual bool isQtQuickNode() const { return false; }
+ virtual bool isAbstract() const { return false; }
+ virtual bool isProperty() const { return false; }
+ virtual bool isQmlProperty() const { return false; }
+ virtual bool isJsProperty() const { return false; }
+ virtual bool isQmlPropertyGroup() const { return false; }
+ virtual bool isJsPropertyGroup() const { return false; }
+ virtual bool isQmlSignal() const { return false; }
+ virtual bool isJsSignal() const { return false; }
+ virtual bool isQmlSignalHandler() const { return false; }
+ virtual bool isJsSignalHandler() const { return false; }
+ virtual bool isQmlMethod() const { return false; }
+ virtual bool isJsMethod() const { return false; }
+ virtual bool isAttached() const { return false; }
+ virtual bool isAlias() const { return false; }
+ virtual bool isWrapper() const;
+ virtual bool isReadOnly() const { return false; }
+ virtual bool isDefault() const { return false; }
+ virtual bool isExternalPage() const { return false; }
+ virtual void addMember(Node* ) { }
+ virtual bool hasMembers() const { return false; }
+ virtual bool hasNamespaces() const { return false; }
+ virtual bool hasClasses() const { return false; }
+ virtual void setAbstract(bool ) { }
+ virtual void setWrapper() { }
+ virtual QString title() const { return name(); }
+ virtual QString fullTitle() const { return name(); }
+ virtual QString subTitle() const { return QString(); }
+ virtual void setTitle(const QString& ) { }
+ virtual void setSubTitle(const QString& ) { }
+ virtual QmlPropertyNode* hasQmlProperty(const QString& ) const { return 0; }
+ virtual QmlPropertyNode* hasQmlProperty(const QString&, bool ) const { return 0; }
+ virtual void getMemberNamespaces(NodeMap& ) { }
+ virtual void getMemberClasses(NodeMap& ) const { }
+ virtual bool isInternal() const;
+ virtual void setDataType(const QString& ) { }
+ virtual void setReadOnly(bool ) { }
+ virtual Node* disambiguate(NodeType , DocSubtype ) { return this; }
+ virtual bool wasSeen() const { return false; }
+ virtual void appendGroupName(const QString& ) { }
+ virtual QString element() const { return QString(); }
+ virtual Tree* tree() const;
+ virtual void findChildren(const QString& , NodeList& nodes) const { nodes.clear(); }
+ virtual void setNoAutoList(bool ) { }
+ bool isIndexNode() const { return indexNodeFlag_; }
+ NodeType type() const { return (NodeType) nodeType_; }
+ virtual DocSubtype docSubtype() const { return NoSubtype; }
+ bool match(const NodeTypeList& types) const;
+ Aggregate* parent() const { return parent_; }
+ const Node* root() const;
+ Aggregate* relates() const { return relatesTo_; }
+ const QString& name() const { return name_; }
+ QString physicalModuleName() const;
+ QString url() const { return url_; }
+ virtual QString nameForLists() const { return name_; }
+ virtual QString outputFileName() const { return QString(); }
+ virtual QString obsoleteLink() const { return QString(); }
+ virtual void setObsoleteLink(const QString& ) { };
+ virtual void setQtVariable(const QString& ) { }
+ virtual QString qtVariable() const { return QString(); }
+
+ const QMap<LinkType, QPair<QString,QString> >& links() const { return linkMap_; }
+ void setLink(LinkType linkType, const QString &link, const QString &desc);
+
+ Access access() const { return (Access) access_; }
+ bool isPrivate() const { return (Access) access_ == Private; }
+ QString accessString() const;
+ const Location& declLocation() const { return declLocation_; }
+ const Location& defLocation() const { return defLocation_; }
+ const Location& location() const { return (defLocation_.isEmpty() ? declLocation_ : defLocation_); }
+ const Doc& doc() const { return doc_; }
+ bool hasDoc() const { return !doc_.isEmpty(); }
+ Status status() const { return (Status) status_; }
+ Status inheritedStatus() const;
+ bool isObsolete() const { return (status_ == (unsigned char) Obsolete); }
+ ThreadSafeness threadSafeness() const;
+ ThreadSafeness inheritedThreadSafeness() const;
+ QString since() const { return since_; }
+ QString templateStuff() const { return templateStuff_; }
+ const QString& reconstitutedBrief() const { return reconstitutedBrief_; }
+ PageType pageType() const { return (PageType) pageType_; }
+ QString pageTypeString() const;
+ QString nodeTypeString() const;
+ QString nodeSubtypeString() const;
+ virtual void addPageKeywords(const QString& ) { }
+
+ void clearRelated() { relatesTo_ = 0; }
+
+ //QString guid() const;
+ QString extractClassName(const QString &string) const;
+ virtual QString qmlTypeName() const { return name_; }
+ virtual QString qmlFullBaseName() const { return QString(); }
+ virtual QString logicalModuleName() const { return QString(); }
+ virtual QString logicalModuleVersion() const { return QString(); }
+ virtual QString logicalModuleIdentifier() const { return QString(); }
+ virtual void setLogicalModuleInfo(const QString& ) { }
+ virtual void setLogicalModuleInfo(const QStringList& ) { }
+ virtual CollectionNode* logicalModule() const { return 0; }
+ virtual void setQmlModule(CollectionNode* ) { }
+ virtual ClassNode* classNode() { return 0; }
+ virtual void setClassNode(ClassNode* ) { }
+ virtual const Node* applyModuleName(const Node* ) const { return 0; }
+ virtual QString idNumber() { return "0"; }
+ QmlTypeNode* qmlTypeNode();
+ ClassNode* declarativeCppNode();
+ const QString& outputSubdirectory() const { return outSubDir_; }
+ virtual void setOutputSubdirectory(const QString& t) { outSubDir_ = t; }
+ QString fullDocumentName() const;
+ static QString cleanId(const QString &str);
+ //QString idForNode() const;
+
+ static FlagValue toFlagValue(bool b);
+ static bool fromFlagValue(FlagValue fv, bool defaultValue);
+
+ static QString pageTypeString(unsigned char t);
+ static QString nodeTypeString(unsigned char t);
+ static QString nodeSubtypeString(unsigned char t);
+ static int incPropertyGroupCount();
+ static void clearPropertyGroupCount();
+ static void initialize();
+ static NodeType goal(const QString& t) { return goals_.value(t); }
+
+protected:
+ Node(NodeType type, Aggregate* parent, const QString& name);
+ void removeRelates();
+
+private:
+
+ unsigned char nodeType_;
+ unsigned char genus_;
+ unsigned char access_;
+ unsigned char safeness_;
+ unsigned char pageType_;
+ unsigned char status_;
+ bool indexNodeFlag_;
+
+ Aggregate* parent_;
+ Aggregate* relatesTo_;
+ QString name_;
+ Location declLocation_;
+ Location defLocation_;
+ Doc doc_;
+ QMap<LinkType, QPair<QString, QString> > linkMap_;
+ QString fileNameBase_;
+ QString physicalModuleName_;
+ QString url_;
+ QString since_;
+ QString templateStuff_;
+ QString reconstitutedBrief_;
+ //mutable QString uuid_;
+ QString outSubDir_;
+ static QStringMap operators_;
+ static int propertyGroupCount_;
+ static QMap<QString,Node::NodeType> goals_;
+};
+Q_DECLARE_TYPEINFO(Node::DocSubtype, Q_PRIMITIVE_TYPE);
+
+class Aggregate : public Node
+{
+public:
+ virtual ~Aggregate();
+
+ Node* findChildNode(const QString& name, Node::Genus genus) const;
+ Node* findChildNode(const QString& name, NodeType type);
+ virtual void findChildren(const QString& name, NodeList& nodes) const Q_DECL_OVERRIDE;
+ FunctionNode* findFunctionNode(const QString& name, const QString& params) const;
+ FunctionNode* findFunctionNode(const FunctionNode* clone) const;
+ void addInclude(const QString &include);
+ void setIncludes(const QStringList &includes);
+ void normalizeOverloads();
+ void makeUndocumentedChildrenInternal();
+ void deleteChildren();
+ void removeFromRelated();
+
+ virtual bool isAggregate() const Q_DECL_OVERRIDE { return true; }
+ virtual bool isLeaf() const Q_DECL_OVERRIDE { return false; }
+ const EnumNode* findEnumNodeForValue(const QString &enumValue) const;
+ const NodeList & childNodes() const { return children_; }
+ const NodeList & relatedNodes() const { return related_; }
+
+ int count() const { return children_.size(); }
+ NodeList overloads(const QString &funcName) const;
+ const QStringList& includes() const { return includes_; }
+
+ QStringList primaryKeys();
+ QStringList secondaryKeys();
+ const QStringList& pageKeywords() const { return pageKeywds; }
+ virtual void addPageKeywords(const QString& t) Q_DECL_OVERRIDE { pageKeywds << t; }
+ virtual void setOutputFileName(const QString& f) Q_DECL_OVERRIDE { outputFileName_ = f; }
+ virtual QString outputFileName() const Q_DECL_OVERRIDE { return outputFileName_; }
+ virtual QmlPropertyNode* hasQmlProperty(const QString& ) const Q_DECL_OVERRIDE;
+ virtual QmlPropertyNode* hasQmlProperty(const QString&, bool attached) const Q_DECL_OVERRIDE;
+ void addChild(Node* child, const QString& title);
+ const QStringList& groupNames() const { return groupNames_; }
+ virtual void appendGroupName(const QString& t) Q_DECL_OVERRIDE { groupNames_.append(t); }
+ void printChildren(const QString& title);
+ void addChild(Node* child);
+ void removeChild(Node* child);
+ void setOutputSubdirectory(const QString& t) Q_DECL_OVERRIDE;
+ const NodeMap& primaryFunctionMap() { return primaryFunctionMap_; }
+ const QMap<QString, NodeList>& secondaryFunctionMap() { return secondaryFunctionMap_; }
+
+protected:
+ Aggregate(NodeType type, Aggregate* parent, const QString& name);
+
+private:
+ friend class Node;
+
+ static bool isSameSignature(const FunctionNode* f1, const FunctionNode* f2);
+ void removeRelated(Node* pseudoChild);
+ void addRelated(Node* pseudoChild);
+
+ QString outputFileName_;
+ QStringList pageKeywds;
+ QStringList includes_;
+ QStringList groupNames_;
+ NodeList children_;
+ NodeList enumChildren_;
+ NodeList related_;
+ NodeMap childMap_;
+ NodeMap primaryFunctionMap_;
+ QMap<QString, NodeList> secondaryFunctionMap_;
+};
+
+class LeafNode : public Node
+{
+public:
+ LeafNode();
+ virtual ~LeafNode() { }
+
+ virtual bool isAggregate() const Q_DECL_OVERRIDE { return false; }
+ virtual bool isLeaf() const Q_DECL_OVERRIDE { return true; }
+
+protected:
+ LeafNode(NodeType type, Aggregate* parent, const QString& name);
+ LeafNode(Aggregate* parent, NodeType type, const QString& name);
+};
+
+class NamespaceNode : public Aggregate
+{
+public:
+ NamespaceNode(Aggregate* parent, const QString& name);
+ virtual ~NamespaceNode() { }
+ virtual bool isNamespace() const Q_DECL_OVERRIDE { return true; }
+ virtual Tree* tree() const Q_DECL_OVERRIDE { return (parent() ? parent()->tree() : tree_); }
+ virtual bool wasSeen() const Q_DECL_OVERRIDE { return seen_; }
+
+ void markSeen() { seen_ = true; }
+ void markNotSeen() { seen_ = false; }
+ void setTree(Tree* t) { tree_ = t; }
+ const NodeList& orphans() const { return orphans_; }
+ void addOrphan(Node* child) { orphans_.append(child); }
+
+ private:
+ bool seen_;
+ Tree* tree_;
+ NodeList orphans_;
+};
+
+struct RelatedClass
+{
+ RelatedClass() { }
+ // constructor for resolved base class
+ RelatedClass(Node::Access access, ClassNode* node)
+ : access_(access), node_(node) { }
+ // constructor for unresolved base class
+ RelatedClass(Node::Access access, const QStringList& path, const QString& signature)
+ : access_(access), node_(0), path_(path), signature_(signature) { }
+ QString accessString() const;
+ bool isPrivate() const { return (access_ == Node::Private); }
+
+ Node::Access access_;
+ ClassNode* node_;
+ QStringList path_;
+ QString signature_;
+};
+
+struct UsingClause
+{
+ UsingClause() { }
+ UsingClause(const QString& signature) : node_(0), signature_(signature) { }
+ const QString& signature() const { return signature_; }
+ const Node* node() { return node_; }
+ void setNode(const Node* n) { node_ = n; }
+
+ const Node* node_;
+ QString signature_;
+};
+
+class ClassNode : public Aggregate
+{
+public:
+ ClassNode(Aggregate* parent, const QString& name);
+ virtual ~ClassNode() { }
+ virtual bool isClass() const Q_DECL_OVERRIDE { return true; }
+ virtual bool isWrapper() const Q_DECL_OVERRIDE { return wrapper_; }
+ virtual QString obsoleteLink() const Q_DECL_OVERRIDE { return obsoleteLink_; }
+ virtual void setObsoleteLink(const QString& t) Q_DECL_OVERRIDE { obsoleteLink_ = t; }
+ virtual void setWrapper() Q_DECL_OVERRIDE { wrapper_ = true; }
+
+ void addResolvedBaseClass(Access access, ClassNode* node);
+ void addDerivedClass(Access access, ClassNode* node);
+ void addUnresolvedBaseClass(Access access, const QStringList& path, const QString& signature);
+ void addUnresolvedUsingClause(const QString& signature);
+ void fixBaseClasses();
+ void fixPropertyUsingBaseClasses(PropertyNode* pn);
+
+ QList<RelatedClass>& baseClasses() { return bases_; }
+ QList<RelatedClass>& derivedClasses() { return derived_; }
+ QList<RelatedClass>& ignoredBaseClasses() { return ignoredBases_; }
+ QList<UsingClause>& usingClauses() { return usingClauses_; }
+
+ const QList<RelatedClass> &baseClasses() const { return bases_; }
+ const QList<RelatedClass> &derivedClasses() const { return derived_; }
+ const QList<RelatedClass> &ignoredBaseClasses() const { return ignoredBases_; }
+ const QList<UsingClause>& usingClauses() const { return usingClauses_; }
+
+ QmlTypeNode* qmlElement() { return qmlelement; }
+ void setQmlElement(QmlTypeNode* qcn) { qmlelement = qcn; }
+ virtual bool isAbstract() const Q_DECL_OVERRIDE { return abstract_; }
+ virtual void setAbstract(bool b) Q_DECL_OVERRIDE { abstract_ = b; }
+ PropertyNode* findPropertyNode(const QString& name);
+ QmlTypeNode* findQmlBaseNode();
+
+private:
+ QList<RelatedClass> bases_;
+ QList<RelatedClass> derived_;
+ QList<RelatedClass> ignoredBases_;
+ QList<UsingClause> usingClauses_;
+ bool abstract_;
+ bool wrapper_;
+ QString obsoleteLink_;
+ QmlTypeNode* qmlelement;
+};
+
+class DocumentNode : public Aggregate
+{
+public:
+
+ DocumentNode(Aggregate* parent,
+ const QString& name,
+ DocSubtype docSubtype,
+ PageType ptype);
+ virtual ~DocumentNode() { }
+
+ virtual bool isDocumentNode() const Q_DECL_OVERRIDE { return true; }
+ virtual void setTitle(const QString &title) Q_DECL_OVERRIDE;
+ virtual void setSubTitle(const QString &subTitle) Q_DECL_OVERRIDE { subtitle_ = subTitle; }
+
+ DocSubtype docSubtype() const Q_DECL_OVERRIDE { return nodeSubtype_; }
+ virtual QString title() const Q_DECL_OVERRIDE { return title_; }
+ virtual QString fullTitle() const Q_DECL_OVERRIDE;
+ virtual QString subTitle() const Q_DECL_OVERRIDE;
+ virtual QString imageFileName() const { return QString(); }
+ virtual QString nameForLists() const Q_DECL_OVERRIDE { return title(); }
+ virtual void setImageFileName(const QString& ) { }
+
+ virtual bool isHeaderFile() const Q_DECL_OVERRIDE { return (docSubtype() == Node::HeaderFile); }
+ virtual bool isExample() const Q_DECL_OVERRIDE { return (docSubtype() == Node::Example); }
+ virtual bool isExampleFile() const Q_DECL_OVERRIDE { return (parent() && parent()->isExample()); }
+ virtual bool isExternalPage() const Q_DECL_OVERRIDE { return nodeSubtype_ == ExternalPage; }
+
+protected:
+ DocSubtype nodeSubtype_;
+ QString title_;
+ QString subtitle_;
+};
+
+class ExampleNode : public DocumentNode
+{
+public:
+ ExampleNode(Aggregate* parent, const QString& name)
+ : DocumentNode(parent, name, Node::Example, Node::ExamplePage) { }
+ virtual ~ExampleNode() { }
+ virtual QString imageFileName() const Q_DECL_OVERRIDE { return imageFileName_; }
+ virtual void setImageFileName(const QString& ifn) Q_DECL_OVERRIDE { imageFileName_ = ifn; }
+
+private:
+ QString imageFileName_;
+};
+
+struct ImportRec {
+ QString name_; // module name
+ QString version_; // <major> . <minor>
+ QString importId_; // "as" name
+ QString importUri_; // subdirectory of module directory
+
+ ImportRec(const QString& name,
+ const QString& version,
+ const QString& importId,
+ const QString& importUri)
+ : name_(name), version_(version), importId_(importId), importUri_(importUri) { }
+ QString& name() { return name_; }
+ QString& version() { return version_; }
+ QString& importId() { return importId_; }
+ QString& importUri() { return importUri_; }
+ bool isEmpty() const { return name_.isEmpty(); }
+};
+
+typedef QList<ImportRec> ImportList;
+
+class QmlTypeNode : public Aggregate
+{
+public:
+ QmlTypeNode(Aggregate* parent, const QString& name);
+ virtual ~QmlTypeNode();
+ virtual bool isQmlType() const Q_DECL_OVERRIDE { return genus() == Node::QML; }
+ virtual bool isJsType() const Q_DECL_OVERRIDE { return genus() == Node::JS; }
+ virtual bool isQtQuickNode() const Q_DECL_OVERRIDE {
+ return (logicalModuleName() == QLatin1String("QtQuick"));
+ }
+ virtual ClassNode* classNode() Q_DECL_OVERRIDE { return cnode_; }
+ virtual void setClassNode(ClassNode* cn) Q_DECL_OVERRIDE { cnode_ = cn; }
+ virtual bool isAbstract() const Q_DECL_OVERRIDE { return abstract_; }
+ virtual bool isWrapper() const Q_DECL_OVERRIDE { return wrapper_; }
+ virtual void setAbstract(bool b) Q_DECL_OVERRIDE { abstract_ = b; }
+ virtual void setWrapper() Q_DECL_OVERRIDE { wrapper_ = true; }
+ virtual bool isInternal() const Q_DECL_OVERRIDE { return (status() == Internal); }
+ virtual QString qmlFullBaseName() const Q_DECL_OVERRIDE;
+ virtual QString obsoleteLink() const Q_DECL_OVERRIDE { return obsoleteLink_; }
+ virtual void setObsoleteLink(const QString& t) Q_DECL_OVERRIDE { obsoleteLink_ = t; };
+ virtual QString logicalModuleName() const Q_DECL_OVERRIDE;
+ virtual QString logicalModuleVersion() const Q_DECL_OVERRIDE;
+ virtual QString logicalModuleIdentifier() const Q_DECL_OVERRIDE;
+ virtual CollectionNode* logicalModule() const Q_DECL_OVERRIDE { return logicalModule_; }
+ virtual void setQmlModule(CollectionNode* t) Q_DECL_OVERRIDE { logicalModule_ = t; }
+
+ const ImportList& importList() const { return importList_; }
+ void setImportList(const ImportList& il) { importList_ = il; }
+ const QString& qmlBaseName() const { return qmlBaseName_; }
+ void setQmlBaseName(const QString& name) { qmlBaseName_ = name; }
+ bool qmlBaseNodeNotSet() const { return (qmlBaseNode_ == 0); }
+ QmlTypeNode* qmlBaseNode();
+ void setQmlBaseNode(QmlTypeNode* b) { qmlBaseNode_ = b; }
+ void requireCppClass() { cnodeRequired_ = true; }
+ bool cppClassRequired() const { return cnodeRequired_; }
+ static void addInheritedBy(const QString& base, Node* sub);
+ static void subclasses(const QString& base, NodeList& subs);
+ static void terminate();
+
+public:
+ static bool qmlOnly;
+ static QMultiMap<QString,Node*> inheritedBy;
+
+private:
+ bool abstract_;
+ bool cnodeRequired_;
+ bool wrapper_;
+ ClassNode* cnode_;
+ QString qmlBaseName_;
+ QString obsoleteLink_;
+ CollectionNode* logicalModule_;
+ QmlTypeNode* qmlBaseNode_;
+ ImportList importList_;
+};
+
+class QmlBasicTypeNode : public Aggregate
+{
+public:
+ QmlBasicTypeNode(Aggregate* parent,
+ const QString& name);
+ virtual ~QmlBasicTypeNode() { }
+ virtual bool isQmlBasicType() const Q_DECL_OVERRIDE { return (genus() == Node::QML); }
+ virtual bool isJsBasicType() const Q_DECL_OVERRIDE { return (genus() == Node::JS); }
+};
+
+class QmlPropertyGroupNode : public Aggregate
+{
+public:
+ QmlPropertyGroupNode(QmlTypeNode* parent, const QString& name);
+ virtual ~QmlPropertyGroupNode() { }
+ virtual bool isQtQuickNode() const Q_DECL_OVERRIDE { return parent()->isQtQuickNode(); }
+ virtual QString qmlTypeName() const Q_DECL_OVERRIDE { return parent()->qmlTypeName(); }
+ virtual QString logicalModuleName() const Q_DECL_OVERRIDE {
+ return parent()->logicalModuleName();
+ }
+ virtual QString logicalModuleVersion() const Q_DECL_OVERRIDE {
+ return parent()->logicalModuleVersion();
+ }
+ virtual QString logicalModuleIdentifier() const Q_DECL_OVERRIDE {
+ return parent()->logicalModuleIdentifier();
+ }
+ virtual QString idNumber() Q_DECL_OVERRIDE;
+ virtual bool isQmlPropertyGroup() const Q_DECL_OVERRIDE { return genus() == Node::QML; }
+ virtual bool isJsPropertyGroup() const Q_DECL_OVERRIDE { return genus() == Node::JS; }
+ virtual QString element() const Q_DECL_OVERRIDE { return parent()->name(); }
+
+ private:
+ int idNumber_;
+};
+
+class QmlPropertyNode : public LeafNode
+{
+ Q_DECLARE_TR_FUNCTIONS(QDoc::QmlPropertyNode)
+
+public:
+ QmlPropertyNode(Aggregate *parent,
+ const QString& name,
+ const QString& type,
+ bool attached);
+ virtual ~QmlPropertyNode() { }
+
+ virtual void setDataType(const QString& dataType) Q_DECL_OVERRIDE { type_ = dataType; }
+ void setStored(bool stored) { stored_ = toFlagValue(stored); }
+ void setDesignable(bool designable) { designable_ = toFlagValue(designable); }
+ virtual void setReadOnly(bool ro) Q_DECL_OVERRIDE { readOnly_ = toFlagValue(ro); }
+ void setDefault() { isdefault_ = true; }
+
+ const QString &dataType() const { return type_; }
+ QString qualifiedDataType() const { return type_; }
+ bool isReadOnlySet() const { return (readOnly_ != FlagValueDefault); }
+ bool isStored() const { return fromFlagValue(stored_,true); }
+ bool isDesignable() const { return fromFlagValue(designable_,false); }
+ bool isWritable();
+ virtual bool isQmlProperty() const Q_DECL_OVERRIDE { return genus() == QML; }
+ virtual bool isJsProperty() const Q_DECL_OVERRIDE { return genus() == JS; }
+ virtual bool isDefault() const Q_DECL_OVERRIDE { return isdefault_; }
+ virtual bool isReadOnly() const Q_DECL_OVERRIDE { return fromFlagValue(readOnly_,false); }
+ virtual bool isAlias() const Q_DECL_OVERRIDE { return isAlias_; }
+ virtual bool isAttached() const Q_DECL_OVERRIDE { return attached_; }
+ virtual bool isQtQuickNode() const Q_DECL_OVERRIDE { return parent()->isQtQuickNode(); }
+ virtual QString qmlTypeName() const Q_DECL_OVERRIDE { return parent()->qmlTypeName(); }
+ virtual QString logicalModuleName() const Q_DECL_OVERRIDE {
+ return parent()->logicalModuleName();
+ }
+ virtual QString logicalModuleVersion() const Q_DECL_OVERRIDE {
+ return parent()->logicalModuleVersion();
+ }
+ virtual QString logicalModuleIdentifier() const Q_DECL_OVERRIDE {
+ return parent()->logicalModuleIdentifier();
+ }
+ virtual QString element() const Q_DECL_OVERRIDE;
+
+ private:
+ PropertyNode* findCorrespondingCppProperty();
+
+private:
+ QString type_;
+ FlagValue stored_;
+ FlagValue designable_;
+ bool isAlias_;
+ bool isdefault_;
+ bool attached_;
+ FlagValue readOnly_;
+};
+
+class EnumItem
+{
+public:
+ EnumItem() { }
+ EnumItem(const QString& name, const QString& value)
+ : name_(name), value_(value) { }
+
+ const QString& name() const { return name_; }
+ const QString& value() const { return value_; }
+
+private:
+ QString name_;
+ QString value_;
+};
+
+class EnumNode : public LeafNode
+{
+public:
+ EnumNode(Aggregate* parent, const QString& name);
+ virtual ~EnumNode() { }
+
+ void addItem(const EnumItem& item);
+ void setFlagsType(TypedefNode* typedeff);
+ bool hasItem(const QString &name) const { return names_.contains(name); }
+ virtual bool isEnumType() const Q_DECL_OVERRIDE { return true; }
+
+ const QList<EnumItem>& items() const { return items_; }
+ Access itemAccess(const QString& name) const;
+ const TypedefNode* flagsType() const { return flagsType_; }
+ QString itemValue(const QString &name) const;
+
+private:
+ QList<EnumItem> items_;
+ QSet<QString> names_;
+ const TypedefNode* flagsType_;
+};
+
+class TypedefNode : public LeafNode
+{
+public:
+ TypedefNode(Aggregate* parent, const QString& name);
+ virtual ~TypedefNode() { }
+
+ const EnumNode* associatedEnum() const { return associatedEnum_; }
+
+private:
+ void setAssociatedEnum(const EnumNode* t);
+
+ friend class EnumNode;
+
+ const EnumNode* associatedEnum_;
+};
+
+inline void EnumNode::setFlagsType(TypedefNode* t)
+{
+ flagsType_ = t;
+ t->setAssociatedEnum(this);
+}
+
+class Parameter
+{
+public:
+ Parameter() {}
+ Parameter(const QString& dataType,
+ const QString& rightType = QString(),
+ const QString& name = QString(),
+ const QString& defaultValue = QString());
+ Parameter(const Parameter& p);
+
+ Parameter& operator=(const Parameter& p);
+
+ void setName(const QString& name) { name_ = name; }
+
+ bool hasType() const { return dataType_.length() + rightType_.length() > 0; }
+ const QString& dataType() const { return dataType_; }
+ const QString& rightType() const { return rightType_; }
+ const QString& name() const { return name_; }
+ const QString& defaultValue() const { return defaultValue_; }
+
+ QString reconstruct(bool value = false) const;
+
+ public:
+ QString dataType_;
+ QString rightType_; // mws says remove this 04/08/2015
+ QString name_;
+ QString defaultValue_;
+};
+
+//friend class QTypeInfo<Parameter>;
+//Q_DECLARE_TYPEINFO(Parameter, Q_MOVABLE_TYPE);
+
+class FunctionNode : public LeafNode
+{
+public:
+ enum Metaness {
+ Plain,
+ Signal,
+ Slot,
+ Ctor,
+ Dtor,
+ MacroWithParams,
+ MacroWithoutParams,
+ Native };
+ enum Virtualness { NonVirtual, NormalVirtual, PureVirtual };
+
+ FunctionNode(Aggregate* parent, const QString &name);
+ FunctionNode(NodeType type, Aggregate* parent, const QString &name, bool attached);
+ virtual ~FunctionNode() { }
+
+ void setReturnType(const QString& t) { returnType_ = t; }
+ void setParentPath(const QStringList& p) { parentPath_ = p; }
+ void setMetaness(Metaness t) { metaness_ = t; }
+ void setVirtualness(Virtualness v);
+ void setConst(bool b) { const_ = b; }
+ void setStatic(bool b) { static_ = b; }
+ unsigned char overloadNumber() const { return overloadNumber_; }
+ void setOverloadFlag(bool b) { overload_ = b; }
+ void setOverloadNumber(unsigned char n) { overloadNumber_ = n; }
+ void setReimplemented(bool b);
+ void addParameter(const Parameter& parameter);
+ inline void setParameters(const QVector<Parameter>& parameters);
+ void borrowParameterNames(const FunctionNode* source);
+ void setReimplementedFrom(FunctionNode* from);
+
+ const QString& returnType() const { return returnType_; }
+ Metaness metaness() const { return metaness_; }
+ bool isMacro() const {
+ return metaness_ == MacroWithParams || metaness_ == MacroWithoutParams;
+ }
+ Virtualness virtualness() const { return virtualness_; }
+ bool isConst() const { return const_; }
+ bool isStatic() const { return static_; }
+ bool isOverload() const { return overload_; }
+ bool isReimplemented() const Q_DECL_OVERRIDE { return reimplemented_; }
+ bool isFunction() const Q_DECL_OVERRIDE { return true; }
+ virtual bool isQmlSignal() const Q_DECL_OVERRIDE {
+ return (type() == Node::QmlSignal) && (genus() == Node::QML);
+ }
+ virtual bool isJsSignal() const Q_DECL_OVERRIDE {
+ return (type() == Node::QmlSignal) && (genus() == Node::JS);
+ }
+ virtual bool isQmlSignalHandler() const Q_DECL_OVERRIDE {
+ return (type() == Node::QmlSignalHandler) && (genus() == Node::QML);
+ }
+ virtual bool isJsSignalHandler() const Q_DECL_OVERRIDE {
+ return (type() == Node::QmlSignalHandler) && (genus() == Node::JS);
+ }
+ virtual bool isQmlMethod() const Q_DECL_OVERRIDE {
+ return (type() == Node::QmlMethod) && (genus() == Node::QML);
+ }
+ virtual bool isJsMethod() const Q_DECL_OVERRIDE {
+ return (type() == Node::QmlMethod) && (genus() == Node::JS);
+ }
+ const QVector<Parameter>& parameters() const { return parameters_; }
+ void clearParams() { parameters_.clear(); }
+ QStringList parameterNames() const;
+ QString rawParameters(bool names = false, bool values = false) const;
+ const FunctionNode* reimplementedFrom() const { return reimplementedFrom_; }
+ const QList<FunctionNode*> &reimplementedBy() const { return reimplementedBy_; }
+ const PropNodeList& associatedProperties() const { return associatedProperties_; }
+ const QStringList& parentPath() const { return parentPath_; }
+ bool hasAssociatedProperties() const { return !associatedProperties_.isEmpty(); }
+ bool hasOneAssociatedProperty() const { return (associatedProperties_.size() == 1); }
+ PropertyNode* firstAssociatedProperty() const { return associatedProperties_[0]; }
+ bool hasActiveAssociatedProperty() const;
+
+ QStringList reconstructParameters(bool values = false) const;
+ QString signature(bool values = false) const;
+ virtual QString element() const Q_DECL_OVERRIDE { return parent()->name(); }
+ virtual bool isAttached() const Q_DECL_OVERRIDE { return attached_; }
+ virtual bool isQtQuickNode() const Q_DECL_OVERRIDE { return parent()->isQtQuickNode(); }
+ virtual QString qmlTypeName() const Q_DECL_OVERRIDE { return parent()->qmlTypeName(); }
+ virtual QString logicalModuleName() const Q_DECL_OVERRIDE {
+ return parent()->logicalModuleName();
+ }
+ virtual QString logicalModuleVersion() const Q_DECL_OVERRIDE {
+ return parent()->logicalModuleVersion();
+ }
+ virtual QString logicalModuleIdentifier() const Q_DECL_OVERRIDE {
+ return parent()->logicalModuleIdentifier();
+ }
+ bool isPrivateSignal() const { return privateSignal_; }
+ void setPrivateSignal() { privateSignal_ = true; }
+
+ void debug() const;
+
+private:
+ void addAssociatedProperty(PropertyNode* property);
+
+ friend class Aggregate;
+ friend class PropertyNode;
+
+ QString returnType_;
+ QStringList parentPath_;
+ Metaness metaness_;
+ Virtualness virtualness_;
+ bool const_ : 1;
+ bool static_ : 1;
+ bool reimplemented_: 1;
+ bool attached_: 1;
+ bool privateSignal_: 1;
+ bool overload_ : 1;
+ unsigned char overloadNumber_;
+ QVector<Parameter> parameters_;
+ const FunctionNode* reimplementedFrom_;
+ PropNodeList associatedProperties_;
+ QList<FunctionNode*> reimplementedBy_;
+};
+
+class PropertyNode : public LeafNode
+{
+public:
+ enum FunctionRole { Getter, Setter, Resetter, Notifier };
+ enum { NumFunctionRoles = Notifier + 1 };
+
+ PropertyNode(Aggregate* parent, const QString& name);
+ virtual ~PropertyNode() { }
+
+ virtual void setDataType(const QString& dataType) Q_DECL_OVERRIDE { type_ = dataType; }
+ virtual bool isProperty() const Q_DECL_OVERRIDE { return true; }
+ void addFunction(FunctionNode* function, FunctionRole role);
+ void addSignal(FunctionNode* function, FunctionRole role);
+ void setStored(bool stored) { stored_ = toFlagValue(stored); }
+ void setDesignable(bool designable) { designable_ = toFlagValue(designable); }
+ void setScriptable(bool scriptable) { scriptable_ = toFlagValue(scriptable); }
+ void setWritable(bool writable) { writable_ = toFlagValue(writable); }
+ void setUser(bool user) { user_ = toFlagValue(user); }
+ void setOverriddenFrom(const PropertyNode* baseProperty);
+ void setRuntimeDesFunc(const QString& rdf) { runtimeDesFunc_ = rdf; }
+ void setRuntimeScrFunc(const QString& scrf) { runtimeScrFunc_ = scrf; }
+ void setConstant() { const_ = true; }
+ void setFinal() { final_ = true; }
+ void setRevision(int revision) { revision_ = revision; }
+
+ const QString &dataType() const { return type_; }
+ QString qualifiedDataType() const;
+ NodeList functions() const;
+ NodeList functions(FunctionRole role) const { return functions_[(int)role]; }
+ NodeList getters() const { return functions(Getter); }
+ NodeList setters() const { return functions(Setter); }
+ NodeList resetters() const { return functions(Resetter); }
+ NodeList notifiers() const { return functions(Notifier); }
+ FunctionRole role(const FunctionNode* fn) const;
+ bool isStored() const { return fromFlagValue(stored_, storedDefault()); }
+ bool isDesignable() const { return fromFlagValue(designable_, designableDefault()); }
+ bool isScriptable() const { return fromFlagValue(scriptable_, scriptableDefault()); }
+ const QString& runtimeDesignabilityFunction() const { return runtimeDesFunc_; }
+ const QString& runtimeScriptabilityFunction() const { return runtimeScrFunc_; }
+ bool isWritable() const { return fromFlagValue(writable_, writableDefault()); }
+ bool isUser() const { return fromFlagValue(user_, userDefault()); }
+ bool isConstant() const { return const_; }
+ bool isFinal() const { return final_; }
+ const PropertyNode* overriddenFrom() const { return overrides_; }
+
+ bool storedDefault() const { return true; }
+ bool userDefault() const { return false; }
+ bool designableDefault() const { return !setters().isEmpty(); }
+ bool scriptableDefault() const { return true; }
+ bool writableDefault() const { return !setters().isEmpty(); }
+
+private:
+ QString type_;
+ QString runtimeDesFunc_;
+ QString runtimeScrFunc_;
+ NodeList functions_[NumFunctionRoles];
+ FlagValue stored_;
+ FlagValue designable_;
+ FlagValue scriptable_;
+ FlagValue writable_;
+ FlagValue user_;
+ bool const_;
+ bool final_;
+ int revision_;
+ const PropertyNode* overrides_;
+};
+
+inline void FunctionNode::setParameters(const QVector<Parameter> &p)
+{
+ parameters_ = p;
+}
+
+inline void PropertyNode::addFunction(FunctionNode* function, FunctionRole role)
+{
+ functions_[(int)role].append(function);
+ function->addAssociatedProperty(this);
+}
+
+inline void PropertyNode::addSignal(FunctionNode* function, FunctionRole role)
+{
+ functions_[(int)role].append(function);
+ function->addAssociatedProperty(this);
+}
+
+inline NodeList PropertyNode::functions() const
+{
+ NodeList list;
+ for (int i = 0; i < NumFunctionRoles; ++i)
+ list += functions_[i];
+ return list;
+}
+
+class VariableNode : public LeafNode
+{
+public:
+ VariableNode(Aggregate* parent, const QString &name);
+ virtual ~VariableNode() { }
+
+ void setLeftType(const QString &leftType) { lrftType_ = leftType; }
+ void setRightType(const QString &rightType) { rightType_ = rightType; }
+ void setStatic(bool b) { static_ = b; }
+
+ const QString &leftType() const { return lrftType_; }
+ const QString &rightType() const { return rightType_; }
+ QString dataType() const { return lrftType_ + rightType_; }
+ bool isStatic() const { return static_; }
+
+private:
+ QString lrftType_;
+ QString rightType_;
+ bool static_;
+};
+
+inline VariableNode::VariableNode(Aggregate* parent, const QString &name)
+ : LeafNode(Variable, parent, name), static_(false)
+{
+ setGenus(Node::CPP);
+}
+
+class DitaMapNode : public DocumentNode
+{
+public:
+ DitaMapNode(Aggregate* parent, const QString& name)
+ : DocumentNode(parent, name, Node::Page, Node::DitaMapPage) { }
+ virtual ~DitaMapNode() { }
+
+ const DitaRefList& map() const { return doc().ditamap(); }
+};
+
+class CollectionNode : public Aggregate
+{
+ public:
+ CollectionNode(NodeType type,
+ Aggregate* parent,
+ const QString& name,
+ Genus genus)
+ : Aggregate(type, parent, name), seen_(false), noAutoList_(false)
+ {
+ setPageType(Node::OverviewPage);
+ setGenus(genus);
+ }
+ virtual ~CollectionNode() { }
+
+ virtual bool isCollectionNode() const Q_DECL_OVERRIDE { return true; }
+ virtual bool isGroup() const Q_DECL_OVERRIDE { return genus() == Node::DOC; }
+ virtual bool isModule() const Q_DECL_OVERRIDE { return genus() == Node::CPP; }
+ virtual bool isQmlModule() const Q_DECL_OVERRIDE { return genus() == Node::QML; }
+ virtual bool isJsModule() const Q_DECL_OVERRIDE { return genus() == Node::JS; }
+ virtual QString qtVariable() const Q_DECL_OVERRIDE { return qtVariable_; }
+ virtual void setQtVariable(const QString& v) Q_DECL_OVERRIDE { qtVariable_ = v; }
+ virtual void addMember(Node* node) Q_DECL_OVERRIDE;
+ virtual bool hasMembers() const Q_DECL_OVERRIDE;
+ virtual bool hasNamespaces() const Q_DECL_OVERRIDE;
+ virtual bool hasClasses() const Q_DECL_OVERRIDE;
+ virtual void getMemberNamespaces(NodeMap& out) Q_DECL_OVERRIDE;
+ virtual void getMemberClasses(NodeMap& out) const Q_DECL_OVERRIDE;
+ virtual bool wasSeen() const Q_DECL_OVERRIDE { return seen_; }
+ virtual QString title() const Q_DECL_OVERRIDE { return title_; }
+ virtual QString subTitle() const Q_DECL_OVERRIDE { return subtitle_; }
+ virtual QString fullTitle() const Q_DECL_OVERRIDE { return title_; }
+ virtual QString nameForLists() const Q_DECL_OVERRIDE { return title_; }
+ virtual void setTitle(const QString &title) Q_DECL_OVERRIDE;
+ virtual void setSubTitle(const QString &subTitle) Q_DECL_OVERRIDE { subtitle_ = subTitle; }
+
+ virtual QString logicalModuleName() const Q_DECL_OVERRIDE { return logicalModuleName_; }
+ virtual QString logicalModuleVersion() const Q_DECL_OVERRIDE {
+ return logicalModuleVersionMajor_ + QLatin1Char('.') + logicalModuleVersionMinor_;
+ }
+ virtual QString logicalModuleIdentifier() const Q_DECL_OVERRIDE {
+ return logicalModuleName_ + logicalModuleVersionMajor_;
+ }
+ virtual void setLogicalModuleInfo(const QString& arg) Q_DECL_OVERRIDE;
+ virtual void setLogicalModuleInfo(const QStringList& info) Q_DECL_OVERRIDE;
+
+ const NodeList& members() const { return members_; }
+ void printMembers(const QString& title);
+
+ void markSeen() { seen_ = true; }
+ void markNotSeen() { seen_ = false; }
+ bool noAutoList() const { return noAutoList_; }
+ virtual void setNoAutoList(bool b) Q_DECL_OVERRIDE { noAutoList_ = b; }
+
+ private:
+ bool seen_;
+ bool noAutoList_;
+ QString title_;
+ QString subtitle_;
+ NodeList members_;
+ QString logicalModuleName_;
+ QString logicalModuleVersionMajor_;
+ QString logicalModuleVersionMinor_;
+ QString qtVariable_;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/qdoc/openedlist.cpp b/src/qdoc/openedlist.cpp
new file mode 100644
index 000000000..6bb4a270e
--- /dev/null
+++ b/src/qdoc/openedlist.cpp
@@ -0,0 +1,220 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL21$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** 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 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** As a special exception, The Qt Company gives you certain additional
+** rights. These rights are described in The Qt Company LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** $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/src/qdoc/openedlist.h b/src/qdoc/openedlist.h
new file mode 100644
index 000000000..3a564b018
--- /dev/null
+++ b/src/qdoc/openedlist.h
@@ -0,0 +1,86 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL21$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** 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 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** As a special exception, The Qt Company gives you certain additional
+** rights. These rights are described in The Qt Company LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*
+ openedlist.h
+*/
+
+#ifndef OPENEDLIST_H
+#define OPENEDLIST_H
+
+#include <qstring.h>
+
+#include "location.h"
+
+QT_BEGIN_NAMESPACE
+
+class OpenedList
+{
+ Q_DECLARE_TR_FUNCTIONS(QDoc::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;
+};
+Q_DECLARE_TYPEINFO(OpenedList, Q_MOVABLE_TYPE);
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/qdoc/plaincodemarker.cpp b/src/qdoc/plaincodemarker.cpp
new file mode 100644
index 000000000..bf2a55b2d
--- /dev/null
+++ b/src/qdoc/plaincodemarker.cpp
@@ -0,0 +1,119 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL21$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** 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 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** As a special exception, The Qt Company gives you certain additional
+** rights. These rights are described in The Qt Company LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** $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;
+}
+
+Atom::AtomType PlainCodeMarker::atomType() const
+{
+ return Atom::Code;
+}
+
+QString PlainCodeMarker::markedUpCode( const QString& code,
+ const Node * /* relative */,
+ const Location & /* location */ )
+{
+ return protect( code );
+}
+
+QString PlainCodeMarker::markedUpSynopsis( const Node * /* node */,
+ const Node * /* relative */,
+ SynopsisStyle /* style */ )
+{
+ return "foo";
+}
+
+QString PlainCodeMarker::markedUpName( const Node * /* node */ )
+{
+ return QString();
+}
+
+QString PlainCodeMarker::markedUpFullName( const Node * /* node */,
+ const Node * /* relative */ )
+{
+ return QString();
+}
+
+QString PlainCodeMarker::markedUpEnumValue(const QString & /* enumValue */,
+ const Node * /* relative */)
+{
+ return QString();
+}
+
+QString PlainCodeMarker::markedUpIncludes( const QStringList& /* includes */ )
+{
+ return QString();
+}
+
+QString PlainCodeMarker::functionBeginRegExp( const QString& /* funcName */ )
+{
+ return QString();
+}
+
+QString PlainCodeMarker::functionEndRegExp( const QString& /* funcName */ )
+{
+ return QString();
+}
+
+QList<Section> PlainCodeMarker::sections(const Aggregate * /* innerNode */,
+ SynopsisStyle /* style */,
+ Status /* status */)
+{
+ return QList<Section>();
+}
+
+QT_END_NAMESPACE
diff --git a/src/qdoc/plaincodemarker.h b/src/qdoc/plaincodemarker.h
new file mode 100644
index 000000000..b83a3e469
--- /dev/null
+++ b/src/qdoc/plaincodemarker.h
@@ -0,0 +1,69 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL21$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** 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 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** As a special exception, The Qt Company gives you certain additional
+** rights. These rights are described in The Qt Company LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** $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 ) Q_DECL_OVERRIDE;
+ bool recognizeExtension( const QString& ext ) Q_DECL_OVERRIDE;
+ bool recognizeLanguage( const QString& lang ) Q_DECL_OVERRIDE;
+ Atom::AtomType atomType() const Q_DECL_OVERRIDE;
+ QString markedUpCode( const QString& code, const Node *relative, const Location &location ) Q_DECL_OVERRIDE;
+ QString markedUpSynopsis( const Node *node, const Node *relative,
+ SynopsisStyle style ) Q_DECL_OVERRIDE;
+ QString markedUpName( const Node *node ) Q_DECL_OVERRIDE;
+ QString markedUpFullName( const Node *node, const Node *relative ) Q_DECL_OVERRIDE;
+ QString markedUpEnumValue(const QString &enumValue, const Node *relative) Q_DECL_OVERRIDE;
+ QString markedUpIncludes( const QStringList& includes ) Q_DECL_OVERRIDE;
+ QString functionBeginRegExp( const QString& funcName ) Q_DECL_OVERRIDE;
+ QString functionEndRegExp( const QString& funcName ) Q_DECL_OVERRIDE;
+ QList<Section> sections(const Aggregate *innerNode, SynopsisStyle style, Status status) Q_DECL_OVERRIDE;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/qdoc/puredocparser.cpp b/src/qdoc/puredocparser.cpp
new file mode 100644
index 000000000..b47f1fc45
--- /dev/null
+++ b/src/qdoc/puredocparser.cpp
@@ -0,0 +1,225 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL21$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** 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 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** As a special exception, The Qt Company gives you certain additional
+** rights. These rights are described in The Qt Company LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*
+ puredocparser.cpp
+*/
+
+#include <qfile.h>
+#include <stdio.h>
+#include <errno.h>
+#include "codechunk.h"
+#include "config.h"
+#include "tokenizer.h"
+#include <qdebug.h>
+#include "qdocdatabase.h"
+#include "puredocparser.h"
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ Constructs the pure doc parser.
+*/
+PureDocParser::PureDocParser()
+{
+}
+
+/*!
+ Destroys the pure doc parser.
+ */
+PureDocParser::~PureDocParser()
+{
+}
+
+/*!
+ Returns a list of the kinds of files that the pure doc
+ parser is meant to parse. The elements of the list are
+ file suffixes.
+ */
+QStringList PureDocParser::sourceFileNameFilter()
+{
+ return QStringList() << "*.qdoc" << "*.qtx" << "*.qtt" << "*.js";
+}
+
+/*!
+ Parses the source file identified by \a filePath and adds its
+ parsed contents to the database. The \a location is used for
+ reporting errors.
+ */
+void PureDocParser::parseSourceFile(const Location& location, const QString& filePath)
+{
+ QFile in(filePath);
+ currentFile_ = filePath;
+ if (!in.open(QIODevice::ReadOnly)) {
+ location.error(tr("Can't open source file '%1' (%2)").arg(filePath).arg(strerror(errno)));
+ currentFile_.clear();
+ return;
+ }
+
+ reset();
+ Location fileLocation(filePath);
+ Tokenizer fileTokenizer(fileLocation, in);
+ tokenizer = &fileTokenizer;
+ readToken();
+
+ /*
+ The set of open namespaces is cleared before parsing
+ each source file. The word "source" here means cpp file.
+ */
+ qdb_->clearOpenNamespaces();
+
+ processQdocComments();
+ in.close();
+ currentFile_.clear();
+}
+
+/*!
+ This is called by parseSourceFile() to do the actual parsing
+ and tree building. It only processes qdoc comments. It skips
+ everything else.
+ */
+bool PureDocParser::processQdocComments()
+{
+ const QSet<QString>& topicCommandsAllowed = topicCommands();
+ const QSet<QString>& otherMetacommandsAllowed = otherMetaCommands();
+ const 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);
+ Location end_loc(location());
+
+ /*
+ Doc parses the comment.
+ */
+ Doc doc(start_loc, end_loc, comment, metacommandsAllowed, topicCommandsAllowed);
+
+ QString topic;
+ bool isQmlPropertyTopic = false;
+ bool isJsPropertyTopic = false;
+
+ const TopicList& topics = doc.topicsUsed();
+ if (!topics.isEmpty()) {
+ topic = topics[0].topic;
+ if ((topic == COMMAND_QMLPROPERTY) ||
+ (topic == COMMAND_QMLPROPERTYGROUP) ||
+ (topic == COMMAND_QMLATTACHEDPROPERTY)) {
+ isQmlPropertyTopic = true;
+ }
+ else if ((topic == COMMAND_JSPROPERTY) ||
+ (topic == COMMAND_JSPROPERTYGROUP) ||
+ (topic == COMMAND_JSATTACHEDPROPERTY)) {
+ isJsPropertyTopic = true;
+ }
+ }
+
+ NodeList nodes;
+ DocList docs;
+
+ if (topic.isEmpty()) {
+ doc.location().warning(tr("This qdoc comment contains no topic command "
+ "(e.g., '\\%1', '\\%2').")
+ .arg(COMMAND_MODULE).arg(COMMAND_PAGE));
+ }
+ else if (isQmlPropertyTopic || isJsPropertyTopic) {
+ Doc nodeDoc = doc;
+ processQmlProperties(nodeDoc, nodes, docs, isJsPropertyTopic);
+ }
+ else {
+ ArgList args;
+ QSet<QString> topicCommandsUsed = topicCommandsAllowed & doc.metaCommandsUsed();
+ if (topicCommandsUsed.count() > 0) {
+ topic = *topicCommandsUsed.begin();
+ args = doc.metaCommandArgs(topic);
+ }
+ if (topicCommandsUsed.count() > 1) {
+ QString topics;
+ QSet<QString>::ConstIterator t = topicCommandsUsed.constBegin();
+ while (t != topicCommandsUsed.constEnd()) {
+ topics += " \\" + *t + QLatin1Char(',');
+ ++t;
+ }
+ topics[topics.lastIndexOf(',')] = '.';
+ int i = topics.lastIndexOf(',');
+ topics[i] = ' ';
+ topics.insert(i+1,"and");
+ doc.location().warning(tr("Multiple topic commands found in comment: %1").arg(topics));
+ }
+ ArgList::ConstIterator a = args.cbegin();
+ while (a != args.cend()) {
+ Doc nodeDoc = doc;
+ Node* node = processTopicCommand(nodeDoc,topic,*a);
+ if (node != 0) {
+ nodes.append(node);
+ docs.append(nodeDoc);
+ }
+ ++a;
+ }
+ }
+
+ Node* treeRoot = QDocDatabase::qdocDB()->primaryTreeRoot();
+ NodeList::Iterator n = nodes.begin();
+ QList<Doc>::Iterator d = docs.begin();
+ while (n != nodes.end()) {
+ processOtherMetaCommands(*d, *n);
+ (*n)->setDoc(*d);
+ checkModuleInclusion(*n);
+ if ((*n)->isAggregate() && ((Aggregate *)*n)->includes().isEmpty()) {
+ Aggregate *m = static_cast<Aggregate *>(*n);
+ while (m->parent() && m->parent() != treeRoot)
+ m = m->parent();
+ if (m == *n)
+ ((Aggregate *)*n)->addInclude((*n)->name());
+ else
+ ((Aggregate *)*n)->setIncludes(m->includes());
+ }
+ ++d;
+ ++n;
+ }
+ }
+ else {
+ readToken();
+ }
+ }
+ return true;
+}
+
+
+QT_END_NAMESPACE
diff --git a/src/qdoc/puredocparser.h b/src/qdoc/puredocparser.h
new file mode 100644
index 000000000..20748c882
--- /dev/null
+++ b/src/qdoc/puredocparser.h
@@ -0,0 +1,69 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL21$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** 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 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** As a special exception, The Qt Company gives you certain additional
+** rights. These rights are described in The Qt Company LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*
+ puredocparser.h
+*/
+
+#ifndef PUREDOCPARSER_H
+#define PUREDOCPARSER_H
+
+#include <qset.h>
+
+#include "cppcodeparser.h"
+#include "location.h"
+
+QT_BEGIN_NAMESPACE
+
+class Config;
+class Node;
+class QString;
+
+class PureDocParser : public CppCodeParser
+{
+ Q_DECLARE_TR_FUNCTIONS(QDoc::PureDocParser)
+
+public:
+ PureDocParser();
+ virtual ~PureDocParser();
+
+ virtual QStringList sourceFileNameFilter() Q_DECL_OVERRIDE;
+ virtual void parseSourceFile(const Location& location, const QString& filePath) Q_DECL_OVERRIDE;
+
+ private:
+ bool processQdocComments();
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/qdoc/qdoc.pro b/src/qdoc/qdoc.pro
new file mode 100644
index 000000000..6333a046e
--- /dev/null
+++ b/src/qdoc/qdoc.pro
@@ -0,0 +1,96 @@
+!force_bootstrap {
+ load(qfeatures)
+ requires(!contains(QT_DISABLED_FEATURES, xmlstreamwriter))
+}
+
+option(host_build)
+QT = core qmldevtools-private
+
+DEFINES += \
+ QDOC2_COMPAT
+
+INCLUDEPATH += $$QT_SOURCE_TREE/src/tools/qdoc \
+ $$QT_SOURCE_TREE/src/tools/qdoc/qmlparser
+
+# Increase the stack size on MSVC to 4M to avoid a stack overflow
+win32-msvc*:{
+ QMAKE_LFLAGS += /STACK:4194304
+}
+
+HEADERS += atom.h \
+ codechunk.h \
+ codemarker.h \
+ codeparser.h \
+ config.h \
+ cppcodemarker.h \
+ cppcodeparser.h \
+ doc.h \
+ editdistance.h \
+ generator.h \
+ helpprojectwriter.h \
+ htmlgenerator.h \
+ location.h \
+ node.h \
+ openedlist.h \
+ plaincodemarker.h \
+ puredocparser.h \
+ qdocdatabase.h \
+ qdoctagfiles.h \
+ qdocindexfiles.h \
+ quoter.h \
+ separator.h \
+ text.h \
+ tokenizer.h \
+ tree.h
+SOURCES += atom.cpp \
+ codechunk.cpp \
+ codemarker.cpp \
+ codeparser.cpp \
+ config.cpp \
+ cppcodemarker.cpp \
+ cppcodeparser.cpp \
+ doc.cpp \
+ editdistance.cpp \
+ generator.cpp \
+ helpprojectwriter.cpp \
+ htmlgenerator.cpp \
+ location.cpp \
+ main.cpp \
+ node.cpp \
+ openedlist.cpp \
+ plaincodemarker.cpp \
+ puredocparser.cpp \
+ qdocdatabase.cpp \
+ qdoctagfiles.cpp \
+ qdocindexfiles.cpp \
+ quoter.cpp \
+ separator.cpp \
+ text.cpp \
+ tokenizer.cpp \
+ tree.cpp \
+ yyindent.cpp
+
+### QML/JS Parser ###
+
+HEADERS += jscodemarker.h \
+ qmlcodemarker.h \
+ qmlcodeparser.h \
+ qmlmarkupvisitor.h \
+ qmlvisitor.h
+
+SOURCES += jscodemarker.cpp \
+ qmlcodemarker.cpp \
+ qmlcodeparser.cpp \
+ qmlmarkupvisitor.cpp \
+ qmlvisitor.cpp
+
+### Documentation for qdoc ###
+
+qtPrepareTool(QDOC, qdoc)
+qtPrepareTool(QHELPGENERATOR, qhelpgenerator)
+
+QMAKE_DOCS = $$PWD/doc/config/qdoc.qdocconf
+
+load(qt_tool)
+
+TR_EXCLUDE += $$PWD/*
diff --git a/src/qdoc/qdocdatabase.cpp b/src/qdoc/qdocdatabase.cpp
new file mode 100644
index 000000000..28373bd3b
--- /dev/null
+++ b/src/qdoc/qdocdatabase.cpp
@@ -0,0 +1,1744 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL21$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** 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 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** As a special exception, The Qt Company gives you certain additional
+** rights. These rights are described in The Qt Company LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "generator.h"
+#include "atom.h"
+#include "tree.h"
+#include "qdocdatabase.h"
+#include "qdoctagfiles.h"
+#include "qdocindexfiles.h"
+#include <qdebug.h>
+
+QT_BEGIN_NAMESPACE
+
+static NodeMap emptyNodeMap_;
+static NodeMultiMap emptyNodeMultiMap_;
+bool QDocDatabase::debug = false;
+
+/*! \class QDocForest
+ This class manages a collection of trees. Each tree is an
+ instance of class Tree, which is a private class.
+
+ The forest is populated as each index file is loaded.
+ Each index file adds a tree to the forest. Each tree
+ is named with the name of the module it represents.
+
+ The search order is created by searchOrder(), if it has
+ not already been created. The search order and module
+ names arrays have parallel structure, i.e. modulNames_[i]
+ is the module name of the Tree at searchOrder_[i].
+ */
+
+/*!
+ Destroys the qdoc forest. This requires deleting
+ each Tree in the forest. Note that the forest has
+ been transferred into the search order array, so
+ what is really being used to destroy the forest
+ is the search order array.
+ */
+QDocForest::~QDocForest()
+{
+ for (int i=0; i<searchOrder_.size(); ++i)
+ delete searchOrder_.at(i);
+ forest_.clear();
+ searchOrder_.clear();
+ indexSearchOrder_.clear();
+ moduleNames_.clear();
+ primaryTree_ = 0;
+}
+
+/*!
+ Initializes the forest prior to a traversal and
+ returns a pointer to the root node of the primary
+ tree. If the forest is empty, it return 0
+ */
+NamespaceNode* QDocForest::firstRoot()
+{
+ currentIndex_ = 0;
+ return (!searchOrder().isEmpty() ? searchOrder()[0]->root() : 0);
+}
+
+/*!
+ Increments the forest's current tree index. If the current
+ tree index is still within the forest, the function returns
+ the root node of the current tree. Otherwise it returns 0.
+ */
+NamespaceNode* QDocForest::nextRoot()
+{
+ ++currentIndex_;
+ return (currentIndex_ < searchOrder().size() ? searchOrder()[currentIndex_]->root() : 0);
+}
+
+/*!
+ Initializes the forest prior to a traversal and
+ returns a pointer to the primary tree. If the
+ forest is empty, it returns 0.
+ */
+Tree* QDocForest::firstTree()
+{
+ currentIndex_ = 0;
+ return (!searchOrder().isEmpty() ? searchOrder()[0] : 0);
+}
+
+/*!
+ Increments the forest's current tree index. If the current
+ tree index is still within the forest, the function returns
+ the pointer to the current tree. Otherwise it returns 0.
+ */
+Tree* QDocForest::nextTree()
+{
+ ++currentIndex_;
+ return (currentIndex_ < searchOrder().size() ? searchOrder()[currentIndex_] : 0);
+}
+
+/*!
+ \fn Tree* QDocForest::primaryTree()
+
+ Returns the pointer to the primary tree.
+ */
+
+/*!
+ Finds the tree for module \a t in the forest and
+ sets the primary tree to be that tree. After the
+ primary tree is set, that tree is removed from the
+ forest.
+
+ \node It gets re-inserted into the forest after the
+ search order is built.
+ */
+void QDocForest::setPrimaryTree(const QString& t)
+{
+ QString T = t.toLower();
+ primaryTree_ = findTree(T);
+ forest_.remove(T);
+ if (!primaryTree_)
+ qDebug() << "ERROR: Could not set primary tree to:" << t;
+}
+
+/*!
+ If the search order array is empty, create the search order.
+ If the search order array is not empty, do nothing.
+ */
+void QDocForest::setSearchOrder(QStringList& t)
+{
+ if (!searchOrder_.isEmpty())
+ return;
+
+ /* Allocate space for the search order. */
+ searchOrder_.reserve(forest_.size()+1);
+ searchOrder_.clear();
+ moduleNames_.reserve(forest_.size()+1);
+ moduleNames_.clear();
+
+ /* The primary tree is always first in the search order. */
+ QString primaryName = primaryTree()->physicalModuleName();
+ searchOrder_.append(primaryTree_);
+ moduleNames_.append(primaryName);
+ forest_.remove(primaryName);
+
+ QMap<QString, Tree*>::iterator i;
+ foreach (const QString &m, t) {
+ if (primaryName != m) {
+ i = forest_.find(m);
+ if (i != forest_.end()) {
+ searchOrder_.append(i.value());
+ moduleNames_.append(m);
+ forest_.remove(m);
+ }
+ }
+ }
+ /*
+ If any trees remain in the forest, just add them
+ to the search order sequentially, because we don't
+ know any better at this point.
+ */
+ if (!forest_.isEmpty()) {
+ i = forest_.begin();
+ while (i != forest_.end()) {
+ searchOrder_.append(i.value());
+ moduleNames_.append(i.key());
+ ++i;
+ }
+ forest_.clear();
+ }
+
+ /*
+ Rebuild the forest after constructing the search order.
+ It was destroyed during construction of the search order,
+ but it is needed for module-specific searches.
+
+ Note that this loop also inserts the primary tree into the
+ forrest. That is a requirement.
+ */
+ for (int i=0; i<searchOrder_.size(); ++i) {
+ if (!forest_.contains(moduleNames_.at(i))) {
+ forest_.insert(moduleNames_.at(i), searchOrder_.at(i));
+ }
+ }
+#if 0
+ qDebug() << " SEARCH ORDER:";
+ for (int i=0; i<moduleNames_.size(); ++i)
+ qDebug() << " " << i+1 << "." << moduleNames_.at(i);
+ qDebug() << " FOREST:" << forest_.keys();
+ qDebug() << "SEARCH ORDER:" << moduleNames_;
+#endif
+}
+
+/*!
+ Returns an ordered array of Tree pointers that represents
+ the order in which the trees should be searched. The first
+ Tree in the array is the tree for the current module, i.e.
+ the module for which qdoc is generating documentation.
+
+ The other Tree pointers in the array represent the index
+ files that were loaded in preparation for generating this
+ module's documentation. Each Tree pointer represents one
+ index file. The index file Tree points have been ordered
+ heuristically to, hopefully, minimize searching. Thr order
+ will probably be changed.
+
+ If the search order array is empty, this function calls
+ indexSearchOrder(). The search order array is empty while
+ the index files are being loaded, but some searches must
+ be performed during this time, notably searches for base
+ class nodes. These searches require a temporary search
+ order. The temporary order changes throughout the loading
+ of the index files, but it is always the tree for the
+ current index file first, followed by the trees for the
+ index files that have already been loaded. The only
+ ordering required in this temporary search order is that
+ the current tree must be searched first.
+ */
+const QVector<Tree*>& QDocForest::searchOrder()
+{
+ if (searchOrder_.isEmpty())
+ return indexSearchOrder();
+ return searchOrder_;
+}
+
+/*!
+ There are two search orders used by qdoc when searching for
+ things. The normal search order is returned by searchOrder(),
+ but this normal search order is not known until all the index
+ files have been read. At that point, setSearchOrder() is
+ called.
+
+ During the reading of the index files, the vector holding
+ the normal search order remains empty. Whenever the search
+ order is requested, if that vector is empty, this function
+ is called to return a temporary search order, which includes
+ all the index files that have been read so far, plus the
+ one being read now. That one is prepended to the front of
+ the vector.
+ */
+const QVector<Tree*>& QDocForest::indexSearchOrder()
+{
+ if (forest_.size() > indexSearchOrder_.size())
+ indexSearchOrder_.prepend(primaryTree_);
+ return indexSearchOrder_;
+}
+
+/*!
+ Create a new Tree for the index file for the specified
+ \a module and add it to the forest. Return the pointer
+ to its root.
+ */
+NamespaceNode* QDocForest::newIndexTree(const QString& module)
+{
+ primaryTree_ = new Tree(module, qdb_);
+ forest_.insert(module.toLower(), primaryTree_);
+ return primaryTree_->root();
+}
+
+/*!
+ Create a new Tree for use as the primary tree. This tree
+ will represent the primary module. \a module is camel case.
+ */
+void QDocForest::newPrimaryTree(const QString& module)
+{
+ primaryTree_ = new Tree(module, qdb_);
+}
+
+/*!
+ Searches through the forest for a node named \a targetPath
+ and returns a pointer to it if found. The \a relative node
+ is the starting point. It only makes sense for the primary
+ tree, which is searched first. After the primary tree has
+ been searched, \a relative is set to 0 for searching the
+ other trees, which are all index trees. With relative set
+ to 0, the starting point for each index tree is the root
+ of the index tree.
+ */
+const Node* QDocForest::findNodeForTarget(QStringList& targetPath,
+ const Node* relative,
+ Node::Genus genus,
+ QString& ref)
+{
+ int flags = SearchBaseClasses | SearchEnumValues;
+
+ QString entity = targetPath.takeFirst();
+ QStringList entityPath = entity.split("::");
+
+ QString target;
+ if (!targetPath.isEmpty())
+ target = targetPath.takeFirst();
+
+ foreach (Tree* t, searchOrder()) {
+ const Node* n = t->findNodeForTarget(entityPath, target, relative, flags, genus, ref);
+ if (n)
+ return n;
+ relative = 0;
+ }
+ return 0;
+}
+
+/*!
+ Print the list of module names ordered according
+ to how many successful searches each tree had.
+ */
+void QDocForest::printLinkCounts(const QString& project)
+{
+ Location::null.report(QString("%1: Link Counts").arg(project));
+ QMultiMap<int, QString> m;
+ foreach (Tree* t, searchOrder()) {
+ if (t->linkCount() < 0)
+ m.insert(t->linkCount(), t->physicalModuleName());
+ }
+ QString depends = "depends +=";
+ QString module = project.toLower();
+ QMultiMap<int, QString>::iterator i = m.begin();
+ while (i != m.end()) {
+ QString line = " " + i.value();
+ if (i.value() != module)
+ depends += QLatin1Char(' ') + i.value();
+ int pad = 30 - line.length();
+ for (int k=0; k<pad; ++k)
+ line += QLatin1Char(' ');
+ line += "%1";
+ Location::null.report(line.arg(-(i.key())));
+ ++i;
+ }
+ Location::null.report("Optimal depends variable:");
+ Location::null.report(depends);
+}
+
+/*!
+ Print the list of module names ordered according
+ to how many successful searches each tree had.
+ */
+QString QDocForest::getLinkCounts(QStringList& strings, QVector<int>& counts)
+{
+ QMultiMap<int, QString> m;
+ foreach (Tree* t, searchOrder()) {
+ if (t->linkCount() < 0)
+ m.insert(t->linkCount(), t->physicalModuleName());
+ }
+ QString depends = "depends +=";
+ QString module = Generator::defaultModuleName().toLower();
+ QMultiMap<int, QString>::iterator i = m.begin();
+ while (i != m.end()) {
+ if (i.value() != module) {
+ counts.append(-(i.key()));
+ strings.append(i.value());
+ depends += QLatin1Char(' ') + i.value();
+ }
+ ++i;
+ }
+ return depends;
+}
+
+/*!
+ */
+const Node* QDocForest::findFunctionNode(const QString& target,
+ const Node* relative,
+ Node::Genus genus)
+{
+ QString function, params;
+ int length = target.length();
+ if (target.endsWith(QChar(')'))) {
+ int position = target.lastIndexOf(QChar('('));
+ params = target.mid(position+1, length-position-2);
+ function = target.left(position);
+ }
+ else
+ function = target;
+ foreach (Tree* t, searchOrder()) {
+ const Node* n = t->findFunctionNode(function, params, relative, genus);
+ if (n)
+ return n;
+ relative = 0;
+ }
+ return 0;
+}
+
+/*! \class QDocDatabase
+ This class provides exclusive access to the qdoc database,
+ which consists of a forrest of trees and a lot of maps and
+ other useful data structures.
+ */
+
+QDocDatabase* QDocDatabase::qdocDB_ = NULL;
+NodeMap QDocDatabase::typeNodeMap_;
+
+/*!
+ Constructs the singleton qdoc database object. The singleton
+ constructs the \a forest_ object, which is also a singleton.
+ \a showInternal_ is normally false. If it is true, qdoc will
+ write documentation for nodes marked \c internal.
+
+ \a singleExec_ is false when qdoc is being used in the standard
+ way of running qdoc twices for each module, first with -prepare
+ and then with -generate. First the -prepare phase is run for
+ each module, then the -generate phase is run for each module.
+
+ When \a singleExec_ is true, qdoc is run only once. During the
+ single execution, qdoc processes the qdocconf files for all the
+ modules sequentially in a loop. Each source file for each module
+ is read exactly once.
+ */
+QDocDatabase::QDocDatabase()
+ : showInternal_(false), singleExec_(false), forest_(this)
+{
+ // nothing
+}
+
+/*!
+ Destroys the qdoc database object. This requires destroying
+ the forest object, which contains an array of tree pointers.
+ Each tree is deleted.
+ */
+QDocDatabase::~QDocDatabase()
+{
+ // nothing.
+}
+
+/*!
+ Creates the singleton. Allows only one instance of the class
+ to be created. Returns a pointer to the singleton.
+*/
+QDocDatabase* QDocDatabase::qdocDB()
+{
+ if (!qdocDB_) {
+ qdocDB_ = new QDocDatabase;
+ initializeDB();
+ }
+ return qdocDB_;
+}
+
+/*!
+ Destroys the singleton.
+ */
+void QDocDatabase::destroyQdocDB()
+{
+ if (qdocDB_) {
+ delete qdocDB_;
+ qdocDB_ = 0;
+ }
+}
+
+/*!
+ Initialize data structures in the singleton qdoc database.
+
+ In particular, the type node map is initialized with a lot
+ type names that don't refer to documented types. For example,
+ the C++ standard types are included. These might be documented
+ here at some point, but for now they are not. Other examples
+ include \c array and \c data, which are just generic names
+ used as place holders in function signatures that appear in
+ the documentation.
+
+ Also calls Node::initialize() to initialize the search goal map.
+ */
+void QDocDatabase::initializeDB()
+{
+ Node::initialize();
+ typeNodeMap_.insert( "accepted", 0);
+ typeNodeMap_.insert( "actionPerformed", 0);
+ typeNodeMap_.insert( "activated", 0);
+ typeNodeMap_.insert( "alias", 0);
+ typeNodeMap_.insert( "anchors", 0);
+ typeNodeMap_.insert( "any", 0);
+ typeNodeMap_.insert( "array", 0);
+ typeNodeMap_.insert( "autoSearch", 0);
+ typeNodeMap_.insert( "axis", 0);
+ typeNodeMap_.insert( "backClicked", 0);
+ typeNodeMap_.insert( "bool", 0);
+ typeNodeMap_.insert( "boomTime", 0);
+ typeNodeMap_.insert( "border", 0);
+ typeNodeMap_.insert( "buttonClicked", 0);
+ typeNodeMap_.insert( "callback", 0);
+ typeNodeMap_.insert( "char", 0);
+ typeNodeMap_.insert( "clicked", 0);
+ typeNodeMap_.insert( "close", 0);
+ typeNodeMap_.insert( "closed", 0);
+ typeNodeMap_.insert( "color", 0);
+ typeNodeMap_.insert( "cond", 0);
+ typeNodeMap_.insert( "data", 0);
+ typeNodeMap_.insert( "dataReady", 0);
+ typeNodeMap_.insert( "dateString", 0);
+ typeNodeMap_.insert( "dateTimeString", 0);
+ typeNodeMap_.insert( "datetime", 0);
+ typeNodeMap_.insert( "day", 0);
+ typeNodeMap_.insert( "deactivated", 0);
+ typeNodeMap_.insert( "double", 0);
+ typeNodeMap_.insert( "drag", 0);
+ typeNodeMap_.insert( "easing", 0);
+ typeNodeMap_.insert( "enumeration", 0);
+ typeNodeMap_.insert( "error", 0);
+ typeNodeMap_.insert( "exposure", 0);
+ typeNodeMap_.insert( "fatalError", 0);
+ typeNodeMap_.insert( "fileSelected", 0);
+ typeNodeMap_.insert( "flags", 0);
+ typeNodeMap_.insert( "float", 0);
+ typeNodeMap_.insert( "focus", 0);
+ typeNodeMap_.insert( "focusZone", 0);
+ typeNodeMap_.insert( "format", 0);
+ typeNodeMap_.insert( "framePainted", 0);
+ typeNodeMap_.insert( "from", 0);
+ typeNodeMap_.insert( "frontClicked", 0);
+ typeNodeMap_.insert( "function", 0);
+ typeNodeMap_.insert( "hasOpened", 0);
+ typeNodeMap_.insert( "hovered", 0);
+ typeNodeMap_.insert( "hoveredTitle", 0);
+ typeNodeMap_.insert( "hoveredUrl", 0);
+ typeNodeMap_.insert( "imageCapture", 0);
+ typeNodeMap_.insert( "imageProcessing", 0);
+ typeNodeMap_.insert( "index", 0);
+ typeNodeMap_.insert( "initialized", 0);
+ typeNodeMap_.insert( "int", 0);
+ typeNodeMap_.insert( "isLoaded", 0);
+ typeNodeMap_.insert( "item", 0);
+ typeNodeMap_.insert( "jsdict", 0);
+ typeNodeMap_.insert( "jsobject", 0);
+ typeNodeMap_.insert( "key", 0);
+ typeNodeMap_.insert( "keysequence", 0);
+ typeNodeMap_.insert( "list", 0);
+ typeNodeMap_.insert( "listViewClicked", 0);
+ typeNodeMap_.insert( "loadRequest", 0);
+ typeNodeMap_.insert( "locale", 0);
+ typeNodeMap_.insert( "location", 0);
+ typeNodeMap_.insert( "long", 0);
+ typeNodeMap_.insert( "message", 0);
+ typeNodeMap_.insert( "messageReceived", 0);
+ typeNodeMap_.insert( "mode", 0);
+ typeNodeMap_.insert( "month", 0);
+ typeNodeMap_.insert( "name", 0);
+ typeNodeMap_.insert( "number", 0);
+ typeNodeMap_.insert( "object", 0);
+ typeNodeMap_.insert( "offset", 0);
+ typeNodeMap_.insert( "ok", 0);
+ typeNodeMap_.insert( "openCamera", 0);
+ typeNodeMap_.insert( "openImage", 0);
+ typeNodeMap_.insert( "openVideo", 0);
+ typeNodeMap_.insert( "padding", 0);
+ typeNodeMap_.insert( "parent", 0);
+ typeNodeMap_.insert( "path", 0);
+ typeNodeMap_.insert( "photoModeSelected", 0);
+ typeNodeMap_.insert( "position", 0);
+ typeNodeMap_.insert( "precision", 0);
+ typeNodeMap_.insert( "presetClicked", 0);
+ typeNodeMap_.insert( "preview", 0);
+ typeNodeMap_.insert( "previewSelected", 0);
+ typeNodeMap_.insert( "progress", 0);
+ typeNodeMap_.insert( "puzzleLost", 0);
+ typeNodeMap_.insert( "qmlSignal", 0);
+ typeNodeMap_.insert( "real", 0);
+ typeNodeMap_.insert( "rectangle", 0);
+ typeNodeMap_.insert( "request", 0);
+ typeNodeMap_.insert( "requestId", 0);
+ typeNodeMap_.insert( "section", 0);
+ typeNodeMap_.insert( "selected", 0);
+ typeNodeMap_.insert( "send", 0);
+ typeNodeMap_.insert( "settingsClicked", 0);
+ typeNodeMap_.insert( "shoe", 0);
+ typeNodeMap_.insert( "short", 0);
+ typeNodeMap_.insert( "signed", 0);
+ typeNodeMap_.insert( "sizeChanged", 0);
+ typeNodeMap_.insert( "size_t", 0);
+ typeNodeMap_.insert( "sockaddr", 0);
+ typeNodeMap_.insert( "someOtherSignal", 0);
+ typeNodeMap_.insert( "sourceSize", 0);
+ typeNodeMap_.insert( "startButtonClicked", 0);
+ typeNodeMap_.insert( "state", 0);
+ typeNodeMap_.insert( "std::initializer_list", 0);
+ typeNodeMap_.insert( "std::list", 0);
+ typeNodeMap_.insert( "std::map", 0);
+ typeNodeMap_.insert( "std::pair", 0);
+ typeNodeMap_.insert( "std::string", 0);
+ typeNodeMap_.insert( "std::vector", 0);
+ typeNodeMap_.insert( "string", 0);
+ typeNodeMap_.insert( "stringlist", 0);
+ typeNodeMap_.insert( "swapPlayers", 0);
+ typeNodeMap_.insert( "symbol", 0);
+ typeNodeMap_.insert( "t", 0);
+ typeNodeMap_.insert( "T", 0);
+ typeNodeMap_.insert( "tagChanged", 0);
+ typeNodeMap_.insert( "timeString", 0);
+ typeNodeMap_.insert( "timeout", 0);
+ typeNodeMap_.insert( "to", 0);
+ typeNodeMap_.insert( "toggled", 0);
+ typeNodeMap_.insert( "type", 0);
+ typeNodeMap_.insert( "unsigned", 0);
+ typeNodeMap_.insert( "urllist", 0);
+ typeNodeMap_.insert( "va_list", 0);
+ typeNodeMap_.insert( "value", 0);
+ typeNodeMap_.insert( "valueEmitted", 0);
+ typeNodeMap_.insert( "videoFramePainted", 0);
+ typeNodeMap_.insert( "videoModeSelected", 0);
+ typeNodeMap_.insert( "videoRecorder", 0);
+ typeNodeMap_.insert( "void", 0);
+ typeNodeMap_.insert( "volatile", 0);
+ typeNodeMap_.insert( "wchar_t", 0);
+ typeNodeMap_.insert( "x", 0);
+ typeNodeMap_.insert( "y", 0);
+ typeNodeMap_.insert( "zoom", 0);
+ typeNodeMap_.insert( "zoomTo", 0);
+}
+
+/*! \fn NamespaceNode* QDocDatabase::primaryTreeRoot()
+ Returns a pointer to the root node of the primary tree.
+ */
+
+/*!
+ \fn const CNMap& QDocDatabase::groups()
+ Returns a const reference to the collection of all
+ group nodes in the primary tree.
+*/
+
+/*!
+ \fn const CNMap& QDocDatabase::modules()
+ Returns a const reference to the collection of all
+ module nodes in the primary tree.
+*/
+
+/*!
+ \fn const CNMap& QDocDatabase::qmlModules()
+ Returns a const reference to the collection of all
+ QML module nodes in the primary tree.
+*/
+
+/*!
+ \fn const CNMap& QDocDatabase::jsModules()
+ Returns a const reference to the collection of all
+ JovaScript module nodes in the primary tree.
+*/
+
+/*! \fn CollectionNode* QDocDatabase::findGroup(const QString& name)
+ Find the group node named \a name and return a pointer
+ to it. If a matching node is not found, add a new group
+ node named \a name and return a pointer to that one.
+
+ If a new group node is added, its parent is the tree root,
+ and the new group node is marked \e{not seen}.
+ */
+
+/*! \fn CollectionNode* QDocDatabase::findModule(const QString& name)
+ Find the module node named \a name and return a pointer
+ to it. If a matching node is not found, add a new module
+ node named \a name and return a pointer to that one.
+
+ If a new module node is added, its parent is the tree root,
+ and the new module node is marked \e{not seen}.
+ */
+
+/*! \fn CollectionNode* QDocDatabase::findQmlModule(const QString& name, bool javaScript)
+ Find the QML module node named \a name and return a pointer
+ to it. If a matching node is not found, add a new QML module
+ node named \a name and return a pointer to that one.
+
+ If \a javaScript is set, the return collection must be a
+ JavaScript module.
+
+ If a new QML or JavaScript module node is added, its parent
+ is the tree root, and the new node is marked \e{not seen}.
+ */
+
+/*! \fn CollectionNode* QDocDatabase::addGroup(const QString& name)
+ Looks up the group named \a name in the primary tree. If
+ a match is found, a pointer to the node is returned.
+ Otherwise, a new group node named \a name is created and
+ inserted into the collection, and the pointer to that node
+ is returned.
+ */
+
+/*! \fn CollectionNode* QDocDatabase::addModule(const QString& name)
+ Looks up the module named \a name in the primary tree. If
+ a match is found, a pointer to the node is returned.
+ Otherwise, a new module node named \a name is created and
+ inserted into the collection, and the pointer to that node
+ is returned.
+ */
+
+/*! \fn CollectionNode* QDocDatabase::addQmlModule(const QString& name)
+ Looks up the QML module named \a name in the primary tree.
+ If a match is found, a pointer to the node is returned.
+ Otherwise, a new QML module node named \a name is created
+ and inserted into the collection, and the pointer to that
+ node is returned.
+ */
+
+/*! \fn CollectionNode* QDocDatabase::addJsModule(const QString& name)
+ Looks up the JavaScript module named \a name in the primary
+ tree. If a match is found, a pointer to the node is returned.
+ Otherwise, a new JavaScript module node named \a name is
+ created and inserted into the collection, and the pointer to
+ that node is returned.
+ */
+
+/*! \fn CollectionNode* QDocDatabase::addToGroup(const QString& name, Node* node)
+ Looks up the group node named \a name in the collection
+ of all group nodes. If a match is not found, a new group
+ node named \a name is created and inserted into the collection.
+ Then append \a node to the group's members list, and append the
+ group node to the member list of the \a node. The parent of the
+ \a node is not changed by this function. Returns a pointer to
+ the group node.
+ */
+
+/*! \fn CollectionNode* QDocDatabase::addToModule(const QString& name, Node* node)
+ Looks up the module node named \a name in the collection
+ of all module nodes. If a match is not found, a new module
+ node named \a name is created and inserted into the collection.
+ Then append \a node to the module's members list. The parent of
+ \a node is not changed by this function. Returns the module node.
+ */
+
+/*! \fn Collection* QDocDatabase::addToQmlModule(const QString& name, Node* node)
+ Looks up the QML module named \a name. If it isn't there,
+ create it. Then append \a node to the QML module's member
+ list. The parent of \a node is not changed by this function.
+ */
+
+/*! \fn Collection* QDocDatabase::addToJsModule(const QString& name, Node* node)
+ Looks up the JavaScript module named \a name. If it isn't there,
+ create it. Then append \a node to the JavaScript module's member
+ list. The parent of \a node is not changed by this function.
+ */
+
+/*!
+ Looks up the QML type node identified by the qualified Qml
+ type \a name and returns a pointer to the QML type node.
+ */
+QmlTypeNode* QDocDatabase::findQmlType(const QString& name)
+{
+ QmlTypeNode* qcn = forest_.lookupQmlType(name);
+ if (qcn)
+ return qcn;
+ return 0;
+}
+
+/*!
+ Looks up the QML type node identified by the Qml module id
+ \a qmid and QML type \a name and returns a pointer to the
+ QML type node. The key is \a qmid + "::" + \a name.
+
+ If the QML module id is empty, it looks up the QML type by
+ \a name only.
+ */
+QmlTypeNode* QDocDatabase::findQmlType(const QString& qmid, const QString& name)
+{
+ if (!qmid.isEmpty()) {
+ QString t = qmid + "::" + name;
+ QmlTypeNode* qcn = forest_.lookupQmlType(t);
+ if (qcn)
+ return qcn;
+ }
+
+ QStringList path(name);
+ Node* n = forest_.findNodeByNameAndType(path, Node::QmlType);
+ if (n && (n->isQmlType() || n->isJsType()))
+ return static_cast<QmlTypeNode*>(n);
+ return 0;
+}
+
+/*!
+ Looks up the QML type node identified by the Qml module id
+ constructed from the strings in the \a import record and the
+ QML type \a name and returns a pointer to the QML type node.
+ If a QML type node is not found, 0 is returned.
+ */
+QmlTypeNode* QDocDatabase::findQmlType(const ImportRec& import, const QString& name)
+{
+ if (!import.isEmpty()) {
+ QStringList dotSplit;
+ dotSplit = name.split(QLatin1Char('.'));
+ QString qmName;
+ if (import.importUri_.isEmpty())
+ qmName = import.name_;
+ else
+ qmName = import.importUri_;
+ for (int i=0; i<dotSplit.size(); ++i) {
+ QString qualifiedName = qmName + "::" + dotSplit[i];
+ QmlTypeNode* qcn = forest_.lookupQmlType(qualifiedName);
+ if (qcn)
+ return qcn;
+ }
+ }
+ return 0;
+}
+
+/*!
+ This function calls a set of functions for each tree in the
+ forest that has not already been analyzed. In this way, when
+ running qdoc in \e singleExec mode, each tree is analyzed in
+ turn, and its classes and types are added to the appropriate
+ node maps.
+ */
+void QDocDatabase::processForest()
+{
+ Tree* t = forest_.firstTree();
+ while (t) {
+ findAllClasses(t->root());
+ findAllFunctions(t->root());
+ findAllObsoleteThings(t->root());
+ findAllLegaleseTexts(t->root());
+ findAllSince(t->root());
+ t->setTreeHasBeenAnalyzed();
+ t = forest_.nextTree();
+ }
+ resolveNamespaces();
+}
+
+/*!
+ This function calls \a func for each tree in the forest,
+ but only if Tree::treeHasBeenAnalyzed() returns false for
+ the tree. In this way, when running qdoc in \e singleExec
+ mode, each tree is analyzed in turn, and its classes and
+ types are added to the appropriate node maps.
+ */
+void QDocDatabase::processForest(void (QDocDatabase::*func) (Aggregate*))
+{
+ Tree* t = forest_.firstTree();
+ while (t) {
+ if (!t->treeHasBeenAnalyzed()) {
+ (this->*(func))(t->root());
+ }
+ t = forest_.nextTree();
+ }
+}
+
+/*!
+ Constructs the collection of legalese texts, if it has not
+ already been constructed and returns a reference to it.
+ */
+TextToNodeMap& QDocDatabase::getLegaleseTexts()
+{
+ if (legaleseTexts_.isEmpty())
+ processForest(&QDocDatabase::findAllLegaleseTexts);
+ return legaleseTexts_;
+}
+
+/*!
+ Construct the data structures for obsolete things, if they
+ have not already been constructed. Returns a reference to
+ the map of C++ classes with obsolete members.
+ */
+NodeMultiMap& QDocDatabase::getClassesWithObsoleteMembers()
+{
+ if (obsoleteClasses_.isEmpty() && obsoleteQmlTypes_.isEmpty())
+ processForest(&QDocDatabase::findAllObsoleteThings);
+ return classesWithObsoleteMembers_;
+}
+
+/*!
+ Construct the data structures for obsolete things, if they
+ have not already been constructed. Returns a reference to
+ the map of obsolete QML types.
+ */
+NodeMultiMap& QDocDatabase::getObsoleteQmlTypes()
+{
+ if (obsoleteClasses_.isEmpty() && obsoleteQmlTypes_.isEmpty())
+ processForest(&QDocDatabase::findAllObsoleteThings);
+ return obsoleteQmlTypes_;
+}
+
+/*!
+ Construct the data structures for obsolete things, if they
+ have not already been constructed. Returns a reference to
+ the map of QML types with obsolete members.
+ */
+NodeMultiMap& QDocDatabase::getQmlTypesWithObsoleteMembers()
+{
+ if (obsoleteClasses_.isEmpty() && obsoleteQmlTypes_.isEmpty())
+ processForest(&QDocDatabase::findAllObsoleteThings);
+ return qmlTypesWithObsoleteMembers_;
+}
+
+/*! \fn NodeMultiMap& QDocDatabase::getNamespaces()
+ Returns a reference to the map of all namespace nodes.
+ This function must not be called in the -prepare phase.
+ */
+
+/*!
+ Construct the data structures for QML basic types, if they
+ have not already been constructed. Returns a reference to
+ the map of QML basic types.
+ */
+NodeMultiMap& QDocDatabase::getQmlBasicTypes()
+{
+ if (cppClasses_.isEmpty() && qmlBasicTypes_.isEmpty())
+ processForest(&QDocDatabase::findAllClasses);
+ return qmlBasicTypes_;
+}
+
+/*!
+ Construct the data structures for obsolete things, if they
+ have not already been constructed. Returns a reference to
+ the multimap of QML types.
+ */
+NodeMultiMap& QDocDatabase::getQmlTypes()
+{
+ if (cppClasses_.isEmpty() && qmlTypes_.isEmpty())
+ processForest(&QDocDatabase::findAllClasses);
+ return qmlTypes_;
+}
+
+/*!
+ Construct the data structures for obsolete things, if they
+ have not already been constructed. Returns a reference to
+ the map of obsolete C++ clases.
+ */
+NodeMultiMap& QDocDatabase::getObsoleteClasses()
+{
+ if (obsoleteClasses_.isEmpty() && obsoleteQmlTypes_.isEmpty())
+ processForest(&QDocDatabase::findAllObsoleteThings);
+ return obsoleteClasses_;
+}
+
+/*!
+ Construct the C++ class data structures, if they have not
+ already been constructed. Returns a reference to the map
+ of all C++ classes.
+ */
+NodeMultiMap& QDocDatabase::getCppClasses()
+{
+ if (cppClasses_.isEmpty() && qmlTypes_.isEmpty())
+ processForest(&QDocDatabase::findAllClasses);
+ return cppClasses_;
+}
+
+/*!
+ Finds all the C++ class nodes and QML type nodes and
+ sorts them into maps.
+ */
+void QDocDatabase::findAllClasses(Aggregate* node)
+{
+ NodeList::const_iterator c = node->childNodes().constBegin();
+ while (c != node->childNodes().constEnd()) {
+ if ((*c)->access() != Node::Private && (!(*c)->isInternal() || showInternal_) &&
+ (*c)->tree()->camelCaseModuleName() != QString("QDoc")) {
+ 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;
+
+ cppClasses_.insert(className, *c);
+ }
+ else if (((*c)->isQmlType() || (*c)->isQmlBasicType() ||
+ (*c)->isJsType() || (*c)->isJsBasicType()) && !(*c)->doc().isEmpty()) {
+ QString qmlTypeName = (*c)->name();
+ if (qmlTypeName.startsWith(QLatin1String("QML:")))
+ qmlTypes_.insert(qmlTypeName.mid(4),*c);
+ else
+ qmlTypes_.insert(qmlTypeName,*c);
+
+ //also add to the QML basic type map
+ if ((*c)->isQmlBasicType() || (*c)->isJsType())
+ qmlBasicTypes_.insert(qmlTypeName,*c);
+ }
+ else if ((*c)->isAggregate()) {
+ findAllClasses(static_cast<Aggregate*>(*c));
+ }
+ }
+ ++c;
+ }
+}
+
+/*!
+ Construct the function index data structure and return it.
+ This data structure is used to output the function index page.
+ */
+NodeMapMap& QDocDatabase::getFunctionIndex()
+{
+ processForest(&QDocDatabase::findAllFunctions);
+ return funcIndex_;
+}
+
+/*!
+ Finds all the function nodes
+ */
+void QDocDatabase::findAllFunctions(Aggregate* node)
+{
+ NodeList::ConstIterator c = node->childNodes().constBegin();
+ while (c != node->childNodes().constEnd()) {
+ if ((*c)->access() != Node::Private) {
+ if ((*c)->isAggregate()) {
+ findAllFunctions(static_cast<Aggregate*>(*c));
+ }
+ else if ((*c)->type() == Node::Function) {
+ const FunctionNode* func = static_cast<const FunctionNode*>(*c);
+ if ((func->status() > Node::Obsolete) &&
+ !func->isInternal() &&
+ (func->metaness() != FunctionNode::Ctor) &&
+ (func->metaness() != FunctionNode::Dtor)) {
+ funcIndex_[(*c)->name()].insert((*c)->parent()->fullDocumentName(), *c);
+ }
+ }
+ }
+ ++c;
+ }
+}
+
+/*!
+ Finds all the nodes containing legalese text and puts them
+ in a map.
+ */
+void QDocDatabase::findAllLegaleseTexts(Aggregate* node)
+{
+ NodeList::ConstIterator c = node->childNodes().constBegin();
+ while (c != node->childNodes().constEnd()) {
+ if ((*c)->access() != Node::Private) {
+ if (!(*c)->doc().legaleseText().isEmpty())
+ legaleseTexts_.insertMulti((*c)->doc().legaleseText(), *c);
+ if ((*c)->isAggregate())
+ findAllLegaleseTexts(static_cast<Aggregate *>(*c));
+ }
+ ++c;
+ }
+}
+
+/*!
+ Finds all the namespace nodes and puts them in an index.
+ */
+void QDocDatabase::findAllNamespaces(Aggregate* node)
+{
+ NodeList::ConstIterator c = node->childNodes().constBegin();
+ while (c != node->childNodes().constEnd()) {
+ if ((*c)->access() != Node::Private || (*c)->isNamespace()) {
+ if ((*c)->isAggregate()) {
+ findAllNamespaces(static_cast<Aggregate *>(*c));
+ if ((*c)->isNamespace()) {
+ // Ensure that the namespace's name is not empty (the root
+ // namespace has no name).
+ if (!(*c)->name().isEmpty()) {
+ nmm_.insert((*c)->name(), *c);
+ }
+ }
+ }
+ }
+ ++c;
+ }
+}
+
+/*!
+ Finds all nodes with status = Obsolete and sorts them into
+ maps. They can be C++ classes, QML types, or they can be
+ functions, enum types, typedefs, methods, etc.
+ */
+void QDocDatabase::findAllObsoleteThings(Aggregate* node)
+{
+ NodeList::const_iterator c = node->childNodes().constBegin();
+ while (c != node->childNodes().constEnd()) {
+ if ((*c)->access() != Node::Private) {
+ QString name = (*c)->name();
+ if ((*c)->status() == Node::Obsolete) {
+ if ((*c)->type() == Node::Class) {
+ if ((*c)->parent() && (*c)->parent()->type() == Node::Namespace &&
+ !(*c)->parent()->name().isEmpty())
+ name = (*c)->parent()->name() + "::" + name;
+ obsoleteClasses_.insert(name, *c);
+ }
+ else if ((*c)->isQmlType() || (*c)->isJsType()) {
+ if (name.startsWith(QLatin1String("QML:")))
+ name = name.mid(4);
+ name = (*c)->logicalModuleName() + "::" + name;
+ obsoleteQmlTypes_.insert(name,*c);
+ }
+ }
+ else if ((*c)->type() == Node::Class) {
+ Aggregate* n = static_cast<Aggregate*>(*c);
+ bool inserted = false;
+ NodeList::const_iterator p = n->childNodes().constBegin();
+ while (p != n->childNodes().constEnd()) {
+ if ((*p)->access() != Node::Private) {
+ switch ((*p)->type()) {
+ case Node::Enum:
+ case Node::Typedef:
+ case Node::Function:
+ case Node::Property:
+ case Node::Variable:
+ if ((*p)->status() == Node::Obsolete) {
+ if ((*c)->parent() && (*c)->parent()->type() == Node::Namespace &&
+ !(*c)->parent()->name().isEmpty())
+ name = (*c)->parent()->name() + "::" + name;
+ classesWithObsoleteMembers_.insert(name, *c);
+ inserted = true;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ if (inserted)
+ break;
+ ++p;
+ }
+ }
+ else if ((*c)->isQmlType() || (*c)->isJsType()) {
+ Aggregate* n = static_cast<Aggregate*>(*c);
+ bool inserted = false;
+ NodeList::const_iterator p = n->childNodes().constBegin();
+ while (p != n->childNodes().constEnd()) {
+ if ((*p)->access() != Node::Private) {
+ switch ((*c)->type()) {
+ case Node::QmlProperty:
+ case Node::QmlSignal:
+ case Node::QmlSignalHandler:
+ case Node::QmlMethod:
+ if ((*c)->parent()) {
+ Node* parent = (*c)->parent();
+ if ((parent->isQmlPropertyGroup() ||
+ parent->isJsPropertyGroup()) && parent->parent())
+ parent = parent->parent();
+ if (parent && (parent->isQmlType() || parent->isJsType()) &&
+ !parent->name().isEmpty())
+ name = parent->name() + "::" + name;
+ }
+ qmlTypesWithObsoleteMembers_.insert(name,*c);
+ inserted = true;
+ break;
+ default:
+ break;
+ }
+ }
+ if (inserted)
+ break;
+ ++p;
+ }
+ }
+ else if ((*c)->isAggregate()) {
+ findAllObsoleteThings(static_cast<Aggregate*>(*c));
+ }
+ }
+ ++c;
+ }
+}
+
+/*!
+ Finds all the nodes where a \e{since} command appeared in the
+ qdoc comment and sorts them into maps according to the kind of
+ node.
+
+ This function is used for generating the "New Classes... in x.y"
+ section on the \e{What's New in Qt x.y} page.
+ */
+void QDocDatabase::findAllSince(Aggregate* node)
+{
+ NodeList::const_iterator child = node->childNodes().constBegin();
+ while (child != node->childNodes().constEnd()) {
+ QString sinceString = (*child)->since();
+ // Insert a new entry into each map for each new since string found.
+ if (((*child)->access() != Node::Private) && !sinceString.isEmpty()) {
+ NodeMultiMapMap::iterator nsmap = newSinceMaps_.find(sinceString);
+ if (nsmap == newSinceMaps_.end())
+ nsmap = newSinceMaps_.insert(sinceString,NodeMultiMap());
+
+ NodeMapMap::iterator ncmap = newClassMaps_.find(sinceString);
+ if (ncmap == newClassMaps_.end())
+ ncmap = newClassMaps_.insert(sinceString,NodeMap());
+
+ NodeMapMap::iterator nqcmap = newQmlTypeMaps_.find(sinceString);
+ if (nqcmap == newQmlTypeMaps_.end())
+ nqcmap = newQmlTypeMaps_.insert(sinceString,NodeMap());
+
+ if ((*child)->type() == Node::Function) {
+ // Insert functions into the general since map.
+ FunctionNode *func = static_cast<FunctionNode *>(*child);
+ if ((func->status() > Node::Obsolete) &&
+ (func->metaness() != FunctionNode::Ctor) &&
+ (func->metaness() != FunctionNode::Dtor)) {
+ nsmap.value().insert(func->name(),(*child));
+ }
+ }
+ else {
+ if ((*child)->type() == Node::Class) {
+ // Insert classes into the since and class maps.
+ QString className = (*child)->name();
+ if ((*child)->parent() && !(*child)->parent()->name().isEmpty()) {
+ className = (*child)->parent()->name()+"::"+className;
+ }
+ nsmap.value().insert(className,(*child));
+ ncmap.value().insert(className,(*child));
+ }
+ else if ((*child)->isQmlType() || (*child)->isJsType()) {
+ // Insert QML elements into the since and element maps.
+ QString className = (*child)->name();
+ if ((*child)->parent() && !(*child)->parent()->name().isEmpty()) {
+ className = (*child)->parent()->name()+"::"+className;
+ }
+ nsmap.value().insert(className,(*child));
+ nqcmap.value().insert(className,(*child));
+ }
+ else if ((*child)->isQmlProperty() || (*child)->isJsProperty()) {
+ // Insert QML properties into the since map.
+ QString propertyName = (*child)->name();
+ nsmap.value().insert(propertyName,(*child));
+ }
+ else {
+ // Insert external documents into the general since map.
+ QString name = (*child)->name();
+ if ((*child)->parent() && !(*child)->parent()->name().isEmpty()) {
+ name = (*child)->parent()->name()+"::"+name;
+ }
+ nsmap.value().insert(name,(*child));
+ }
+ }
+ }
+ // Recursively find child nodes with since commands.
+ if ((*child)->isAggregate())
+ findAllSince(static_cast<Aggregate *>(*child));
+
+ ++child;
+ }
+}
+
+/*!
+ Find the \a key in the map of new class maps, and return a
+ reference to the value, which is a NodeMap. If \a key is not
+ found, return a reference to an empty NodeMap.
+ */
+const NodeMap& QDocDatabase::getClassMap(const QString& key)
+{
+ if (newSinceMaps_.isEmpty() && newClassMaps_.isEmpty() && newQmlTypeMaps_.isEmpty())
+ processForest(&QDocDatabase::findAllSince);
+ NodeMapMap::const_iterator i = newClassMaps_.constFind(key);
+ if (i != newClassMaps_.constEnd())
+ return i.value();
+ return emptyNodeMap_;
+}
+
+/*!
+ Find the \a key in the map of new QML type maps, and return a
+ reference to the value, which is a NodeMap. If the \a key is not
+ found, return a reference to an empty NodeMap.
+ */
+const NodeMap& QDocDatabase::getQmlTypeMap(const QString& key)
+{
+ if (newSinceMaps_.isEmpty() && newClassMaps_.isEmpty() && newQmlTypeMaps_.isEmpty())
+ processForest(&QDocDatabase::findAllSince);
+ NodeMapMap::const_iterator i = newQmlTypeMaps_.constFind(key);
+ if (i != newQmlTypeMaps_.constEnd())
+ return i.value();
+ return emptyNodeMap_;
+}
+
+/*!
+ Find the \a key in the map of new \e {since} maps, and return
+ a reference to the value, which is a NodeMultiMap. If \a key
+ is not found, return a reference to an empty NodeMultiMap.
+ */
+const NodeMap& QDocDatabase::getSinceMap(const QString& key)
+{
+ if (newSinceMaps_.isEmpty() && newClassMaps_.isEmpty() && newQmlTypeMaps_.isEmpty())
+ processForest(&QDocDatabase::findAllSince);
+ NodeMultiMapMap::const_iterator i = newSinceMaps_.constFind(key);
+ if (i != newSinceMaps_.constEnd())
+ return i.value();
+ return emptyNodeMultiMap_;
+}
+
+/*!
+ Performs several housekeeping algorithms that create
+ certain data structures and resolve lots of links, prior
+ to generating documentation.
+ */
+void QDocDatabase::resolveIssues() {
+ primaryTreeRoot()->normalizeOverloads();
+ fixInheritance();
+ resolveProperties();
+ primaryTreeRoot()->makeUndocumentedChildrenInternal();
+ resolveQmlInheritance(primaryTreeRoot());
+ primaryTree()->resolveTargets(primaryTreeRoot());
+ primaryTree()->resolveCppToQmlLinks();
+ if (!Generator::singleExec()) {
+ QDocIndexFiles::qdocIndexFiles()->resolveRelates();
+ QDocIndexFiles::destroyQDocIndexFiles();
+ }
+ if (Generator::generating())
+ resolveNamespaces();
+}
+
+void QDocDatabase::resolveStuff()
+{
+ primaryTree()->resolveInheritance();
+ resolveQmlInheritance(primaryTreeRoot());
+ //primaryTree()->resolveTargets(primaryTreeRoot());
+ primaryTree()->resolveCppToQmlLinks();
+ primaryTree()->resolveUsingClauses();
+ resolveNamespaces();
+}
+
+/*!
+ */
+void QDocDatabase::resolveNamespaces()
+{
+ if (!namespaceIndex_.isEmpty())
+ return;
+ Tree* t = forest_.firstTree();
+ while (t) {
+ findAllNamespaces(t->root());
+ t = forest_.nextTree();
+ }
+ QList<QString> keys = nmm_.uniqueKeys();
+ foreach (const QString &s, keys) {
+ NamespaceNode* ns = 0;
+ QList<Node*> nodes = nmm_.values(s);
+ int count = nmm_.remove(s);
+ if (count > 1) {
+ foreach (Node* n, nodes) {
+ // Treat public namespaces from index trees as 'seen'
+ if (n->isNamespace() && (n->wasSeen() || (n->isIndexNode() && n->access() == Node::Public))) {
+ ns = static_cast<NamespaceNode*>(n);
+ ns->markSeen();
+ break;
+ }
+ }
+ }
+ else if (count == 1)
+ ns = static_cast<NamespaceNode*>(nodes.at(0));
+ if (ns && ns->wasSeen()) {
+ if (count >1) {
+ foreach (Node* n, nodes) {
+ if (n->isNamespace()) {
+ NamespaceNode* NS = static_cast<NamespaceNode*>(n);
+ if ((NS != ns) && !NS->childNodes().isEmpty()) {
+ const NodeList& children = NS->childNodes();
+ int i = children.size() - 1;
+ while (i >= 0) {
+ Node* child = children.at(i--);
+ if (!child)
+ continue;
+ if (!child->isClass()
+ && !child->isQmlType()
+ && !child->isNamespace()) {
+ NS->removeChild(child);
+ ns->addChild(child);
+ }
+ else {
+ NS->setStatus(Node::Intermediate);
+ NS->setAccess(Node::Public);
+ ns->addOrphan(child);
+ }
+ }
+ }
+ }
+ }
+ }
+ namespaceIndex_.insert(ns->name(), ns);
+ }
+ }
+}
+#if 0
+/*!
+ */
+const Node* QDocDatabase::findFunctionNode(const QString& target,
+ const Node* relative,
+ Node::Genus genus)
+{
+ return forest_.findFunctionNode(target, relative, genus);
+}
+#endif
+/*!
+ This function is called for autolinking to a \a type,
+ which could be a function return type or a parameter
+ type. The tree node that represents the \a type is
+ returned. All the trees are searched until a match is
+ found. When searching the primary tree, the search
+ begins at \a relative and proceeds up the parent chain.
+ When searching the index trees, the search begins at the
+ root.
+ */
+const Node* QDocDatabase::findTypeNode(const QString& type, const Node* relative)
+{
+ QStringList path = type.split("::");
+ if ((path.size() == 1) && (path.at(0)[0].isLower() || path.at(0) == QString("T"))) {
+ NodeMap::iterator i = typeNodeMap_.find(path.at(0));
+ if (i != typeNodeMap_.end())
+ return i.value();
+ }
+ return forest_.findTypeNode(path, relative);
+}
+
+/*!
+ Finds the node that will generate the documentation that
+ contains the \a target and returns a pointer to it.
+
+ Can this be improved by using the target map in Tree?
+ */
+const Node* QDocDatabase::findNodeForTarget(const QString& target, const Node* relative)
+{
+ const Node* node = 0;
+ if (target.isEmpty())
+ node = relative;
+ else if (target.endsWith(".html"))
+ node = findNodeByNameAndType(QStringList(target), Node::Document);
+ else {
+ QStringList path = target.split("::");
+ int flags = SearchBaseClasses | SearchEnumValues; // | NonFunction;
+ foreach (Tree* t, searchOrder()) {
+ const Node* n = t->findNode(path, relative, flags, Node::DontCare);
+ if (n)
+ return n;
+ relative = 0;
+ }
+ node = findDocumentNodeByTitle(target);
+ }
+ return node;
+}
+
+/*!
+ For each QML Type node in the tree beginning at \a root,
+ if it has a QML base type name but its QML base type node
+ pointer is 0, use the QML base type name to look up the
+ base type node. If the node is found in the tree, set the
+ node's QML base type node pointer.
+ */
+void QDocDatabase::resolveQmlInheritance(Aggregate* root)
+{
+ NodeMap previousSearches;
+ // Do we need recursion?
+ foreach (Node* child, root->childNodes()) {
+ if (child->isQmlType() || child->isJsType()) {
+ QmlTypeNode* qcn = static_cast<QmlTypeNode*>(child);
+ if (qcn->qmlBaseNodeNotSet() && !qcn->qmlBaseName().isEmpty()) {
+ QmlTypeNode* bqcn = static_cast<QmlTypeNode*>(previousSearches.value(qcn->qmlBaseName()));
+ if (bqcn)
+ qcn->setQmlBaseNode(bqcn);
+ else {
+ if (!qcn->importList().isEmpty()) {
+ const ImportList& imports = qcn->importList();
+ for (int i=0; i<imports.size(); ++i) {
+ bqcn = findQmlType(imports[i], qcn->qmlBaseName());
+ if (bqcn)
+ break;
+ }
+ }
+ if (bqcn == 0) {
+ bqcn = findQmlType(QString(), qcn->qmlBaseName());
+ }
+ if (bqcn) {
+ qcn->setQmlBaseNode(bqcn);
+ previousSearches.insert(qcn->qmlBaseName(), bqcn);
+ }
+#if 0
+ else {
+ qDebug() << "Temporary error message (ignore): UNABLE to resolve QML base type:"
+ << qcn->qmlBaseName() << "for QML type:" << qcn->name();
+ }
+#endif
+ }
+ }
+ }
+ }
+}
+
+/*!
+ Generates a tag file and writes it to \a name.
+ */
+void QDocDatabase::generateTagFile(const QString& name, Generator* g)
+{
+ if (!name.isEmpty()) {
+ QDocTagFiles::qdocTagFiles()->generateTagFile(name, g);
+ QDocTagFiles::destroyQDocTagFiles();
+ }
+}
+
+/*!
+ Reads and parses the qdoc index files listed in \a t.
+ */
+void QDocDatabase::readIndexes(const QStringList& t)
+{
+ QStringList indexFiles;
+ foreach (const QString& f, t) {
+ QString fn = f.mid(f.lastIndexOf(QChar('/'))+1);
+ if (!isLoaded(fn))
+ indexFiles << f;
+ else
+ qDebug() << "This index file is already in memory:" << f;
+ }
+ QDocIndexFiles::qdocIndexFiles()->readIndexes(indexFiles);
+}
+
+/*!
+ Generates a qdoc index file and write it to \a fileName. The
+ index file is generated with the parameters \a url, \a title,
+ \a g, and \a generateInternalNodes.
+ */
+void QDocDatabase::generateIndex(const QString& fileName,
+ const QString& url,
+ const QString& title,
+ Generator* g,
+ bool generateInternalNodes)
+{
+ QString t = fileName.mid(fileName.lastIndexOf(QChar('/'))+1);
+ primaryTree()->setIndexFileName(t);
+ QDocIndexFiles::qdocIndexFiles()->generateIndex(fileName, url, title, g, generateInternalNodes);
+ QDocIndexFiles::destroyQDocIndexFiles();
+}
+
+/*!
+ If there are open namespaces, search for the function node
+ having the same function name as the \a clone node in each
+ open namespace. The \a parentPath is a portion of the path
+ name provided with the function name at the point of
+ reference. \a parentPath is usually a class name. Return
+ the pointer to the function node if one is found in an
+ open namespace. Otherwise return 0.
+
+ This open namespace concept is of dubious value and might
+ be removed.
+ */
+FunctionNode* QDocDatabase::findNodeInOpenNamespace(const QStringList& parentPath,
+ const FunctionNode* clone)
+{
+ FunctionNode* fn = 0;
+ if (!openNamespaces_.isEmpty()) {
+ foreach (const QString& t, openNamespaces_) {
+ QStringList path = t.split("::") + parentPath;
+ fn = findFunctionNode(path, clone);
+ if (fn)
+ break;
+ }
+ }
+ return fn;
+}
+
+/*!
+ Find a node of the specified \a type that is reached with
+ the specified \a path qualified with the name of one of the
+ open namespaces (might not be any open ones). If the node
+ is found in an open namespace, prefix \a path with the name
+ of the open namespace and "::" and return a pointer to the
+ node. Othewrwise return 0.
+
+ This function only searches in the current primary tree.
+ */
+Node* QDocDatabase::findNodeInOpenNamespace(QStringList& path, Node::NodeType type)
+{
+ if (path.isEmpty())
+ return 0;
+ Node* n = 0;
+ if (!openNamespaces_.isEmpty()) {
+ foreach (const QString& t, openNamespaces_) {
+ QStringList p;
+ if (t != path[0])
+ p = t.split("::") + path;
+ else
+ p = path;
+ n = primaryTree()->findNodeByNameAndType(p, type);
+ if (n) {
+ path = p;
+ break;
+ }
+ }
+ }
+ return n;
+}
+
+/*!
+ Finds all the collection nodes of the specified \a genus
+ into the collection node map \a cnm. Nodes that match the
+ \a relative node are not included.
+ */
+void QDocDatabase::mergeCollections(Node::Genus genus, CNMap& cnm, const Node* relative)
+{
+ cnm.clear();
+ CNMultiMap cnmm;
+ foreach (Tree* t, searchOrder()) {
+ CNMap* m = t->getCollectionMap(genus);
+ if (m && !m->isEmpty()) {
+ CNMap::const_iterator i = m->cbegin();
+ while (i != m->cend()) {
+ if (!i.value()->isInternal())
+ cnmm.insert(i.key(), i.value());
+ ++i;
+ }
+ }
+ }
+ if (cnmm.isEmpty())
+ return;
+ QRegExp singleDigit("\\b([0-9])\\b");
+ QStringList keys = cnmm.uniqueKeys();
+ foreach (const QString &key, keys) {
+ QList<CollectionNode*> values = cnmm.values(key);
+ CollectionNode* n = 0;
+ foreach (CollectionNode* v, values) {
+ if (v && v->wasSeen() && (v != relative)) {
+ n = v;
+ break;
+ }
+ }
+ if (n) {
+ if (values.size() > 1) {
+ foreach (CollectionNode* v, values) {
+ if (v != n) {
+ // Allow multiple (major) versions of QML/JS modules
+ if (n->type() == Node::QmlModule
+ && n->logicalModuleIdentifier() != v->logicalModuleIdentifier()) {
+ if (v->wasSeen() && v != relative && !v->members().isEmpty())
+ cnm.insert(v->fullTitle().toLower(), v);
+ continue;
+ }
+ foreach (Node* t, v->members())
+ n->addMember(t);
+ }
+ }
+ }
+ if (!n->members().isEmpty()) {
+ QString sortKey = n->fullTitle().toLower();
+ if (sortKey.startsWith("the "))
+ sortKey.remove(0, 4);
+ sortKey.replace(singleDigit, "0\\1");
+ cnm.insert(sortKey, n);
+ }
+ }
+ }
+}
+
+/*!
+ Finds all the collection nodes with the same name
+ and genus as \a c and merges their members into the
+ members list of \a c.
+
+ For QML and JS modules, the merge is done only if
+ the module identifier matches between the nodes, to avoid
+ merging modules with different (major) versions.
+ */
+void QDocDatabase::mergeCollections(CollectionNode* c)
+{
+ foreach (Tree* t, searchOrder()) {
+ CollectionNode* cn = t->getCollection(c->name(), c->genus());
+ if (cn && cn != c) {
+ if (cn->type() == Node::QmlModule
+ && cn->logicalModuleIdentifier() != c->logicalModuleIdentifier())
+ continue;
+ foreach (Node* n, cn->members())
+ c->addMember(n);
+ }
+ }
+}
+
+/*!
+ Searches for the node that matches the path in \a atom. The
+ \a relative node is used if the first leg of the path is
+ empty, i.e. if the path begins with a hashtag. The function
+ also sets \a ref if there remains an unused leg in the path
+ after the node is found. The node is returned as well as the
+ \a ref. If the returned node pointer is null, \a ref is not
+ valid.
+ */
+const Node* QDocDatabase::findNodeForAtom(const Atom* a, const Node* relative, QString& ref)
+{
+ const Node* node = 0;
+
+ Atom* atom = const_cast<Atom*>(a);
+ QStringList targetPath = atom->string().split(QLatin1Char('#'));
+ QString first = targetPath.first().trimmed();
+ if (Generator::debugging())
+ qDebug() << " first:" << first;
+
+ Tree* domain = 0;
+ Node::Genus genus = Node::DontCare;
+ // Reserved for future use
+ //Node::NodeType goal = Node::NoType;
+
+ if (atom->isLinkAtom()) {
+ domain = atom->domain();
+ genus = atom->genus();
+ // Reserved for future use
+ //goal = atom->goal();
+ }
+
+ if (first.isEmpty())
+ node = relative; // search for a target on the current page.
+ else if (domain) {
+ if (first.endsWith(".html"))
+ node = domain->findNodeByNameAndType(QStringList(first), Node::Document);
+ else if (first.endsWith(QChar(')'))) {
+ QString function, params;
+ int length = first.length();
+ int position = first.lastIndexOf(QChar('('));
+ params = first.mid(position+1, length-position-2);
+ function = first.left(position);
+ node = domain->findFunctionNode(function, params, 0, genus);
+ }
+ if (!node) {
+ int flags = SearchBaseClasses | SearchEnumValues;
+ QStringList nodePath = first.split("::");
+ QString target;
+ targetPath.removeFirst();
+ if (!targetPath.isEmpty())
+ target = targetPath.takeFirst();
+ if (relative && relative->tree()->physicalModuleName() != domain->physicalModuleName())
+ relative = 0;
+ return domain->findNodeForTarget(nodePath, target, relative, flags, genus, ref);
+ }
+ }
+ else {
+ if (first.endsWith(".html"))
+ node = findNodeByNameAndType(QStringList(first), Node::Document);
+ else if (first.endsWith(QChar(')'))) {
+ node = findFunctionNode(first, relative, genus);
+ if (Generator::debugging())
+ qDebug() << " node:" << node;
+ }
+ if (!node)
+ return findNodeForTarget(targetPath, relative, genus, ref);
+ }
+
+ if (node && ref.isEmpty()) {
+ if (!node->url().isEmpty())
+ return node;
+ targetPath.removeFirst();
+ if (!targetPath.isEmpty()) {
+ ref = node->root()->tree()->getRef(targetPath.first(), node);
+ if (ref.isEmpty())
+ node = 0;
+ }
+ }
+ return node;
+}
+
+QT_END_NAMESPACE
diff --git a/src/qdoc/qdocdatabase.h b/src/qdoc/qdocdatabase.h
new file mode 100644
index 000000000..80a92af4e
--- /dev/null
+++ b/src/qdoc/qdocdatabase.h
@@ -0,0 +1,455 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL21$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** 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 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** As a special exception, The Qt Company gives you certain additional
+** rights. These rights are described in The Qt Company LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QDOCDATABASE_H
+#define QDOCDATABASE_H
+
+#include <qstring.h>
+#include <qmap.h>
+#include "tree.h"
+#include "config.h"
+#include "text.h"
+#include <qdebug.h>
+
+QT_BEGIN_NAMESPACE
+
+typedef QMap<QString, NodeMap> NodeMapMap;
+typedef QMap<QString, NodeMultiMap> NodeMultiMapMap;
+typedef QMultiMap<QString, Node*> QDocMultiMap;
+typedef QMap<Text, const Node*> TextToNodeMap;
+typedef QList<CollectionNode*> CollectionList;
+
+class Atom;
+class Generator;
+class QDocDatabase;
+
+enum FindFlag {
+ SearchBaseClasses = 0x1,
+ SearchEnumValues = 0x2,
+ NonFunction = 0x4
+};
+
+class QDocForest
+{
+ public:
+ friend class QDocDatabase;
+ QDocForest(QDocDatabase* qdb)
+ : qdb_(qdb), primaryTree_(0), currentIndex_(0) { }
+ ~QDocForest();
+
+ NamespaceNode* firstRoot();
+ NamespaceNode* nextRoot();
+ Tree* firstTree();
+ Tree* nextTree();
+ Tree* primaryTree() { return primaryTree_; }
+ Tree* findTree(const QString& t) { return forest_.value(t); }
+ QStringList keys() {
+ return forest_.keys();
+ }
+ NamespaceNode* primaryTreeRoot() { return (primaryTree_ ? primaryTree_->root() : 0); }
+ bool isEmpty() { return searchOrder().isEmpty(); }
+ bool done() { return (currentIndex_ >= searchOrder().size()); }
+ const QVector<Tree*>& searchOrder();
+ const QVector<Tree*>& indexSearchOrder();
+ void setSearchOrder(QStringList& t);
+ bool isLoaded(const QString& fn) {
+ foreach (Tree* t, searchOrder()) {
+ if (fn == t->indexFileName())
+ return true;
+ }
+ return false;
+ }
+
+ const Node* findNode(const QStringList& path,
+ const Node* relative,
+ int findFlags,
+ Node::Genus genus) {
+ foreach (Tree* t, searchOrder()) {
+ const Node* n = t->findNode(path, relative, findFlags, genus);
+ if (n)
+ return n;
+ relative = 0;
+ }
+ return 0;
+ }
+
+ Node* findNodeByNameAndType(const QStringList& path, Node::NodeType type) {
+ foreach (Tree* t, searchOrder()) {
+ Node* n = t->findNodeByNameAndType(path, type);
+ if (n)
+ return n;
+ }
+ return 0;
+ }
+
+ ClassNode* findClassNode(const QStringList& path) {
+ foreach (Tree* t, searchOrder()) {
+ ClassNode* n = t->findClassNode(path);
+ if (n)
+ return n;
+ }
+ return 0;
+ }
+
+ Node* findNodeForInclude(const QStringList& path) {
+ foreach (Tree* t, searchOrder()) {
+ Node* n = t->findNodeForInclude(path);
+ if (n)
+ return n;
+ }
+ return 0;
+ }
+
+ Aggregate* findRelatesNode(const QStringList& path) {
+ foreach (Tree* t, searchOrder()) {
+ Aggregate* n = t->findRelatesNode(path);
+ if (n)
+ return n;
+ }
+ return 0;
+ }
+
+ const Node* findFunctionNode(const QString& target,
+ const Node* relative,
+ Node::Genus genus);
+
+ const Node* findNodeForTarget(QStringList& targetPath,
+ const Node* relative,
+ Node::Genus genus,
+ QString& ref);
+
+ const Node* findTypeNode(const QStringList& path, const Node* relative)
+ {
+ foreach (Tree* t, searchOrder()) {
+ int flags = SearchBaseClasses | SearchEnumValues | NonFunction;
+ const Node* n = t->findNode(path, relative, flags, Node::DontCare);
+ if (n)
+ return n;
+ relative = 0;
+ }
+ return 0;
+ }
+
+ const DocumentNode* findDocumentNodeByTitle(const QString& title)
+ {
+ foreach (Tree* t, searchOrder()) {
+ const DocumentNode* n = t->findDocumentNodeByTitle(title);
+ if (n)
+ return n;
+ }
+ return 0;
+ }
+
+ const CollectionNode* getCollectionNode(const QString& name, Node::Genus genus)
+ {
+ foreach (Tree* t, searchOrder()) {
+ const CollectionNode* cn = t->getCollection(name, genus);
+ if (cn)
+ return cn;
+ }
+ return 0;
+ }
+
+ QmlTypeNode* lookupQmlType(const QString& name)
+ {
+ foreach (Tree* t, searchOrder()) {
+ QmlTypeNode* qcn = t->lookupQmlType(name);
+ if (qcn)
+ return qcn;
+ }
+ return 0;
+ }
+ void clearSearchOrder() { searchOrder_.clear(); }
+ void clearLinkCounts()
+ {
+ foreach (Tree* t, searchOrder())
+ t->clearLinkCount();
+ }
+ void printLinkCounts(const QString& project);
+ QString getLinkCounts(QStringList& strings, QVector<int>& counts);
+
+ private:
+ void newPrimaryTree(const QString& module);
+ void setPrimaryTree(const QString& t);
+ NamespaceNode* newIndexTree(const QString& module);
+
+ private:
+ QDocDatabase* qdb_;
+ Tree* primaryTree_;
+ int currentIndex_;
+ QMap<QString, Tree*> forest_;
+ QVector<Tree*> searchOrder_;
+ QVector<Tree*> indexSearchOrder_;
+ QVector<QString> moduleNames_;
+};
+
+class QDocDatabase
+{
+ Q_DECLARE_TR_FUNCTIONS(QDoc::QDocDatabase)
+
+ public:
+ static QDocDatabase* qdocDB();
+ static void destroyQdocDB();
+ ~QDocDatabase();
+
+ Tree* findTree(const QString& t) { return forest_.findTree(t); }
+
+ const CNMap& groups() { return primaryTree()->groups(); }
+ const CNMap& modules() { return primaryTree()->modules(); }
+ const CNMap& qmlModules() { return primaryTree()->qmlModules(); }
+ const CNMap& jsModules() { return primaryTree()->jsModules(); }
+
+ CollectionNode* addGroup(const QString& name) { return primaryTree()->addGroup(name); }
+ CollectionNode* addModule(const QString& name) { return primaryTree()->addModule(name); }
+ CollectionNode* addQmlModule(const QString& name) { return primaryTree()->addQmlModule(name); }
+ CollectionNode* addJsModule(const QString& name) { return primaryTree()->addJsModule(name); }
+
+ CollectionNode* addToGroup(const QString& name, Node* node) {
+ return primaryTree()->addToGroup(name, node);
+ }
+ CollectionNode* addToModule(const QString& name, Node* node) {
+ return primaryTree()->addToModule(name, node);
+ }
+ CollectionNode* addToQmlModule(const QString& name, Node* node) {
+ return primaryTree()->addToQmlModule(name, node);
+ }
+ CollectionNode* addToJsModule(const QString& name, Node* node) {
+ return primaryTree()->addToJsModule(name, node);
+ }
+
+ void addExampleNode(ExampleNode* n) { primaryTree()->addExampleNode(n); }
+ ExampleNodeMap& exampleNodeMap() { return primaryTree()->exampleNodeMap(); }
+
+ QmlTypeNode* findQmlType(const QString& name);
+ QmlTypeNode* findQmlType(const QString& qmid, const QString& name);
+ QmlTypeNode* findQmlType(const ImportRec& import, const QString& name);
+
+ private:
+ void findAllClasses(Aggregate *node);
+ void findAllFunctions(Aggregate *node);
+ void findAllLegaleseTexts(Aggregate *node);
+ void findAllNamespaces(Aggregate *node);
+ void findAllObsoleteThings(Aggregate* node);
+ void findAllSince(Aggregate *node);
+
+ public:
+ /*******************************************************************
+ special collection access functions
+ ********************************************************************/
+ NodeMultiMap& getCppClasses();
+ NodeMultiMap& getObsoleteClasses();
+ NodeMultiMap& getClassesWithObsoleteMembers();
+ NodeMultiMap& getObsoleteQmlTypes();
+ NodeMultiMap& getQmlTypesWithObsoleteMembers();
+ NodeMultiMap& getNamespaces() { resolveNamespaces(); return namespaceIndex_; }
+ NodeMultiMap& getQmlBasicTypes();
+ NodeMultiMap& getQmlTypes();
+ NodeMapMap& getFunctionIndex();
+ TextToNodeMap& getLegaleseTexts();
+ const NodeMap& getClassMap(const QString& key);
+ const NodeMap& getQmlTypeMap(const QString& key);
+ const NodeMap& getSinceMap(const QString& key);
+
+ /*******************************************************************
+ Many of these will be either eliminated or replaced.
+ ********************************************************************/
+ void resolveInheritance() { primaryTree()->resolveInheritance(); }
+ void resolveQmlInheritance(Aggregate* root);
+ void resolveIssues();
+ void resolveStuff();
+ void fixInheritance() { primaryTree()->fixInheritance(); }
+ void resolveProperties() { primaryTree()->resolveProperties(); }
+
+ void insertTarget(const QString& name,
+ const QString& title,
+ TargetRec::TargetType type,
+ Node* node,
+ int priority) {
+ primaryTree()->insertTarget(name, title, type, node, priority);
+ }
+
+ /*******************************************************************
+ The functions declared below are called for the current tree only.
+ ********************************************************************/
+ FunctionNode* findFunctionNode(const QStringList& parentPath, const FunctionNode* clone) {
+ return primaryTree()->findFunctionNode(parentPath, clone);
+ }
+ FunctionNode* findNodeInOpenNamespace(const QStringList& parentPath, const FunctionNode* clone);
+ Node* findNodeInOpenNamespace(QStringList& path, Node::NodeType type);
+ const Node* checkForCollision(const QString& name) {
+ return primaryTree()->checkForCollision(name);
+ }
+ /*******************************************************************/
+
+ /*******************************************************************
+ The functions declared below handle the parameters in '[' ']'.
+ ********************************************************************/
+ const Node* findNodeForAtom(const Atom* atom, const Node* relative, QString& ref);
+ /*******************************************************************/
+
+ /*******************************************************************
+ The functions declared below are called for all trees.
+ ********************************************************************/
+ ClassNode* findClassNode(const QStringList& path) { return forest_.findClassNode(path); }
+ Node* findNodeForInclude(const QStringList& path) { return forest_.findNodeForInclude(path); }
+ Aggregate* findRelatesNode(const QStringList& path) { return forest_.findRelatesNode(path); }
+ const Node* findFunctionNode(const QString& target, const Node* relative, Node::Genus genus) {
+ return forest_.findFunctionNode(target, relative, genus);
+ }
+ const Node* findTypeNode(const QString& type, const Node* relative);
+ const Node* findNodeForTarget(const QString& target, const Node* relative);
+ const DocumentNode* findDocumentNodeByTitle(const QString& title) {
+ return forest_.findDocumentNodeByTitle(title);
+ }
+ Node* findNodeByNameAndType(const QStringList& path, Node::NodeType type) {
+ return forest_.findNodeByNameAndType(path, type);
+ }
+ const CollectionNode* getCollectionNode(const QString& name, Node::Genus genus) {
+ return forest_.getCollectionNode(name, genus);
+ }
+
+ private:
+ const Node* findNodeForTarget(QStringList& targetPath,
+ const Node* relative,
+ Node::Genus genus,
+ QString& ref) {
+ return forest_.findNodeForTarget(targetPath, relative, genus, ref);
+ }
+
+ /*******************************************************************/
+ public:
+ void addPropertyFunction(PropertyNode* property,
+ const QString& funcName,
+ PropertyNode::FunctionRole funcRole) {
+ primaryTree()->addPropertyFunction(property, funcName, funcRole);
+ }
+
+ void setVersion(const QString& v) { version_ = v; }
+ QString version() const { return version_; }
+
+ void generateTagFile(const QString& name, Generator* g);
+ void readIndexes(const QStringList& indexFiles);
+ void generateIndex(const QString& fileName,
+ const QString& url,
+ const QString& title,
+ Generator* g,
+ bool generateInternalNodes = false);
+
+ void clearOpenNamespaces() { openNamespaces_.clear(); }
+ void insertOpenNamespace(const QString& path) { openNamespaces_.insert(path); }
+ void setShowInternal(bool value) { showInternal_ = value; }
+ void setSingleExec(bool value) { singleExec_ = value; }
+ void processForest();
+
+ // Try to make this function private.
+ QDocForest& forest() { return forest_; }
+ NamespaceNode* primaryTreeRoot() { return forest_.primaryTreeRoot(); }
+ void newPrimaryTree(const QString& module) { forest_.newPrimaryTree(module); }
+ void setPrimaryTree(const QString& t) { forest_.setPrimaryTree(t); }
+ NamespaceNode* newIndexTree(const QString& module) { return forest_.newIndexTree(module); }
+ const QVector<Tree*>& searchOrder() { return forest_.searchOrder(); }
+ void setLocalSearch() { forest_.searchOrder_ = QVector<Tree*>(1, primaryTree()); }
+ void setSearchOrder(const QVector<Tree*>& searchOrder) { forest_.searchOrder_ = searchOrder; }
+ void setSearchOrder(QStringList& t) { forest_.setSearchOrder(t); }
+ void mergeCollections(Node::Genus genus, CNMap& cnm, const Node* relative);
+ void mergeCollections(CollectionNode* c);
+ void clearSearchOrder() { forest_.clearSearchOrder(); }
+ void incrementLinkCount(const Node* t) { t->tree()->incrementLinkCount(); }
+ void clearLinkCounts() { forest_.clearLinkCounts(); }
+ void printLinkCounts(const QString& t) { forest_.printLinkCounts(t); }
+ QString getLinkCounts(QStringList& strings, QVector<int>& counts) {
+ return forest_.getLinkCounts(strings, counts);
+ }
+ QString getNewLinkTarget(const Node* locNode,
+ const Node* t,
+ const QString& fileName,
+ QString& text,
+ bool broken = false) {
+ return primaryTree()->getNewLinkTarget(locNode, t, fileName, text, broken);
+ }
+ TargetList* getTargetList(const QString& t) { return primaryTree()->getTargetList(t); }
+ QStringList getTargetListKeys() { return primaryTree()->getTargetListKeys(); }
+ QStringList keys() { return forest_.keys(); }
+ void resolveNamespaces();
+
+ private:
+ friend class QDocIndexFiles;
+ friend class QDocTagFiles;
+
+ const Node* findNode(const QStringList& path,
+ const Node* relative,
+ int findFlags,
+ Node::Genus genus) {
+ return forest_.findNode(path, relative, findFlags, genus);
+ }
+ void processForest(void (QDocDatabase::*) (Aggregate*));
+ bool isLoaded(const QString& t) { return forest_.isLoaded(t); }
+ static void initializeDB();
+
+ private:
+ QDocDatabase();
+ QDocDatabase(QDocDatabase const& ) : showInternal_(false), forest_(this) { }
+ QDocDatabase& operator=(QDocDatabase const& );
+ Tree* primaryTree() { return forest_.primaryTree(); }
+
+ public:
+ static bool debug;
+
+ private:
+ static QDocDatabase* qdocDB_;
+ static NodeMap typeNodeMap_;
+ bool showInternal_;
+ bool singleExec_;
+ QString version_;
+ QDocForest forest_;
+
+ NodeMultiMap cppClasses_;
+ NodeMultiMap obsoleteClasses_;
+ NodeMultiMap classesWithObsoleteMembers_;
+ NodeMultiMap obsoleteQmlTypes_;
+ NodeMultiMap qmlTypesWithObsoleteMembers_;
+ NodeMultiMap namespaceIndex_;
+ NodeMultiMap nmm_;
+ NodeMultiMap qmlBasicTypes_;
+ NodeMultiMap qmlTypes_;
+ NodeMapMap newClassMaps_;
+ NodeMapMap newQmlTypeMaps_;
+ NodeMultiMapMap newSinceMaps_;
+ NodeMapMap funcIndex_;
+ TextToNodeMap legaleseTexts_;
+ QSet<QString> openNamespaces_;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/qdoc/qdocindexfiles.cpp b/src/qdoc/qdocindexfiles.cpp
new file mode 100644
index 000000000..b466f5981
--- /dev/null
+++ b/src/qdoc/qdocindexfiles.cpp
@@ -0,0 +1,1628 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL21$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** 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 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** As a special exception, The Qt Company gives you certain additional
+** rights. These rights are described in The Qt Company LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qxmlstream.h"
+#include "qdocindexfiles.h"
+#include "qdoctagfiles.h"
+#include "config.h"
+#include "qdocdatabase.h"
+#include "location.h"
+#include "atom.h"
+#include "generator.h"
+#include <qdebug.h>
+
+#include <algorithm>
+
+QT_BEGIN_NAMESPACE
+
+static Node* top = 0;
+
+/*!
+ \class QDocIndexFiles
+
+ This class handles qdoc index files.
+ */
+
+QDocIndexFiles* QDocIndexFiles::qdocIndexFiles_ = NULL;
+
+/*!
+ Constructs the singleton QDocIndexFiles.
+ */
+QDocIndexFiles::QDocIndexFiles()
+ : gen_( 0 )
+{
+ qdb_ = QDocDatabase::qdocDB();
+}
+
+/*!
+ Destroys the singleton QDocIndexFiles.
+ */
+QDocIndexFiles::~QDocIndexFiles()
+{
+ qdb_ = 0;
+ gen_ = 0;
+}
+
+/*!
+ Creates the singleton. Allows only one instance of the class
+ to be created. Returns a pointer to the singleton.
+ */
+QDocIndexFiles* QDocIndexFiles::qdocIndexFiles()
+{
+ if (!qdocIndexFiles_)
+ qdocIndexFiles_ = new QDocIndexFiles;
+ return qdocIndexFiles_;
+}
+
+/*!
+ Destroys the singleton.
+ */
+void QDocIndexFiles::destroyQDocIndexFiles()
+{
+ if (qdocIndexFiles_) {
+ delete qdocIndexFiles_;
+ qdocIndexFiles_ = 0;
+ }
+}
+
+/*!
+ Reads and parses the list of index files in \a indexFiles.
+ */
+void QDocIndexFiles::readIndexes(const QStringList& indexFiles)
+{
+ relatedList_.clear();
+ foreach (const QString& indexFile, indexFiles) {
+ QString msg = "Loading index file: " + indexFile;
+ Location::logToStdErr(msg);
+ //qDebug() << msg;
+ readIndexFile(indexFile);
+ }
+}
+
+static bool readingRoot = true;
+
+/*!
+ Reads and parses the index file at \a path.
+ */
+void QDocIndexFiles::readIndexFile(const QString& path)
+{
+ QFile file(path);
+ if (!file.open(QFile::ReadOnly)) {
+ qWarning() << "Could not read index file" << path;
+ return;
+ }
+
+ QXmlStreamReader reader(&file);
+ reader.setNamespaceProcessing(false);
+
+ if (!reader.readNextStartElement())
+ return;
+
+ if (reader.name() != QLatin1String("INDEX"))
+ return;
+
+ QXmlStreamAttributes attrs = reader.attributes();
+
+ // Generate a relative URL between the install dir and the index file
+ // when the -installdir command line option is set.
+ QString indexUrl;
+ if (Config::installDir.isEmpty()) {
+ indexUrl = attrs.value(QLatin1String("url")).toString();
+ }
+ else {
+ // Use a fake directory, since we will copy the output to a sub directory of
+ // installDir when using "make install". This is just for a proper relative path.
+ //QDir installDir(path.section('/', 0, -3) + "/outputdir");
+ QDir installDir(path.section('/', 0, -3) + '/' + Generator::outputSubdir());
+ indexUrl = installDir.relativeFilePath(path).section('/', 0, -2);
+ }
+ project_ = attrs.value(QLatin1String("project")).toString();
+ basesList_.clear();
+
+ NamespaceNode* root = qdb_->newIndexTree(project_);
+
+ // Scan all elements in the XML file, constructing a map that contains
+ // base classes for each class found.
+ while (reader.readNextStartElement()) {
+ readingRoot = true;
+ readIndexSection(reader, root, indexUrl);
+ }
+
+ // Now that all the base classes have been found for this index,
+ // arrange them into an inheritance hierarchy.
+ resolveIndex();
+}
+
+/*!
+ Read a <section> element from the index file and create the
+ appropriate node(s).
+ */
+void QDocIndexFiles::readIndexSection(QXmlStreamReader& reader,
+ Node* current,
+ const QString& indexUrl)
+{
+ QXmlStreamAttributes attributes = reader.attributes();
+ QStringRef elementName = reader.name();
+
+ QString name = attributes.value(QLatin1String("name")).toString();
+ QString href = attributes.value(QLatin1String("href")).toString();
+ Node* node;
+ Location location;
+ Aggregate* parent = 0;
+
+ bool hasReadChildren = false;
+
+ if (current->isAggregate())
+ parent = static_cast<Aggregate*>(current);
+
+ QString filePath;
+ int lineNo = 0;
+ if (attributes.hasAttribute(QLatin1String("filepath"))) {
+ filePath = attributes.value(QLatin1String("filepath")).toString();
+ lineNo = attributes.value("lineno").toInt();
+ }
+ if (elementName == QLatin1String("namespace")) {
+ node = new NamespaceNode(parent, name);
+
+ if (!indexUrl.isEmpty())
+ location = Location(indexUrl + QLatin1Char('/') + name.toLower() + ".html");
+ else if (!indexUrl.isNull())
+ location = Location(name.toLower() + ".html");
+
+ }
+ else if (elementName == QLatin1String("class")) {
+ node = new ClassNode(parent, name);
+ if (attributes.hasAttribute(QLatin1String("bases"))) {
+ QString bases = attributes.value(QLatin1String("bases")).toString();
+ if (!bases.isEmpty())
+ basesList_.append(QPair<ClassNode*,QString>(static_cast<ClassNode*>(node), bases));
+ }
+ if (!indexUrl.isEmpty())
+ location = Location(indexUrl + QLatin1Char('/') + name.toLower() + ".html");
+ else if (!indexUrl.isNull())
+ location = Location(name.toLower() + ".html");
+ bool abstract = false;
+ if (attributes.value(QLatin1String("abstract")) == QLatin1String("true"))
+ abstract = true;
+ node->setAbstract(abstract);
+ }
+ else if (elementName == QLatin1String("qmlclass")) {
+ QmlTypeNode* qcn = new QmlTypeNode(parent, name);
+ qcn->setTitle(attributes.value(QLatin1String("title")).toString());
+ QString logicalModuleName = attributes.value(QLatin1String("qml-module-name")).toString();
+ if (!logicalModuleName.isEmpty())
+ qdb_->addToQmlModule(logicalModuleName, qcn);
+ bool abstract = false;
+ if (attributes.value(QLatin1String("abstract")) == QLatin1String("true"))
+ abstract = true;
+ qcn->setAbstract(abstract);
+ QString qmlFullBaseName = attributes.value(QLatin1String("qml-base-type")).toString();
+ if (!qmlFullBaseName.isEmpty()) {
+ qcn->setQmlBaseName(qmlFullBaseName);
+ }
+ if (attributes.hasAttribute(QLatin1String("location")))
+ name = attributes.value("location").toString();
+ if (!indexUrl.isEmpty())
+ location = Location(indexUrl + QLatin1Char('/') + name);
+ else if (!indexUrl.isNull())
+ location = Location(name);
+ node = qcn;
+ }
+ else if (elementName == QLatin1String("jstype")) {
+ QmlTypeNode* qcn = new QmlTypeNode(parent, name);
+ qcn->setGenus(Node::JS);
+ qcn->setTitle(attributes.value(QLatin1String("title")).toString());
+ QString logicalModuleName = attributes.value(QLatin1String("js-module-name")).toString();
+ if (!logicalModuleName.isEmpty())
+ qdb_->addToQmlModule(logicalModuleName, qcn);
+ bool abstract = false;
+ if (attributes.value(QLatin1String("abstract")) == QLatin1String("true"))
+ abstract = true;
+ qcn->setAbstract(abstract);
+ QString qmlFullBaseName = attributes.value(QLatin1String("js-base-type")).toString();
+ if (!qmlFullBaseName.isEmpty()) {
+ qcn->setQmlBaseName(qmlFullBaseName);
+ }
+ if (attributes.hasAttribute(QLatin1String("location")))
+ name = attributes.value("location").toString();
+ if (!indexUrl.isEmpty())
+ location = Location(indexUrl + QLatin1Char('/') + name);
+ else if (!indexUrl.isNull())
+ location = Location(name);
+ node = qcn;
+ }
+ else if (elementName == QLatin1String("qmlbasictype")) {
+ QmlBasicTypeNode* qbtn = new QmlBasicTypeNode(parent, name);
+ qbtn->setTitle(attributes.value(QLatin1String("title")).toString());
+ if (attributes.hasAttribute(QLatin1String("location")))
+ name = attributes.value("location").toString();
+ if (!indexUrl.isEmpty())
+ location = Location(indexUrl + QLatin1Char('/') + name);
+ else if (!indexUrl.isNull())
+ location = Location(name);
+ node = qbtn;
+ }
+ else if (elementName == QLatin1String("jsbasictype")) {
+ QmlBasicTypeNode* qbtn = new QmlBasicTypeNode(parent, name);
+ qbtn->setGenus(Node::JS);
+ qbtn->setTitle(attributes.value(QLatin1String("title")).toString());
+ if (attributes.hasAttribute(QLatin1String("location")))
+ name = attributes.value("location").toString();
+ if (!indexUrl.isEmpty())
+ location = Location(indexUrl + QLatin1Char('/') + name);
+ else if (!indexUrl.isNull())
+ location = Location(name);
+ node = qbtn;
+ }
+ else if (elementName == QLatin1String("qmlpropertygroup")) {
+ QmlTypeNode* qcn = static_cast<QmlTypeNode*>(parent);
+ QmlPropertyGroupNode* qpgn = new QmlPropertyGroupNode(qcn, name);
+ if (attributes.hasAttribute(QLatin1String("location")))
+ name = attributes.value("location").toString();
+ if (!indexUrl.isEmpty())
+ location = Location(indexUrl + QLatin1Char('/') + name);
+ else if (!indexUrl.isNull())
+ location = Location(name);
+ node = qpgn;
+ }
+ else if (elementName == QLatin1String("jspropertygroup")) {
+ QmlTypeNode* qcn = static_cast<QmlTypeNode*>(parent);
+ QmlPropertyGroupNode* qpgn = new QmlPropertyGroupNode(qcn, name);
+ qpgn->setGenus(Node::JS);
+ if (attributes.hasAttribute(QLatin1String("location")))
+ name = attributes.value("location").toString();
+ if (!indexUrl.isEmpty())
+ location = Location(indexUrl + QLatin1Char('/') + name);
+ else if (!indexUrl.isNull())
+ location = Location(name);
+ node = qpgn;
+ }
+ else if (elementName == QLatin1String("qmlproperty")) {
+ QString type = attributes.value(QLatin1String("type")).toString();
+ bool attached = false;
+ if (attributes.value(QLatin1String("attached")) == QLatin1String("true"))
+ attached = true;
+ bool readonly = false;
+ if (attributes.value(QLatin1String("writable")) == QLatin1String("false"))
+ readonly = true;
+ QmlPropertyNode* qpn = new QmlPropertyNode(parent, name, type, attached);
+ qpn->setReadOnly(readonly);
+ node = qpn;
+ }
+ else if (elementName == QLatin1String("jsproperty")) {
+ QString type = attributes.value(QLatin1String("type")).toString();
+ bool attached = false;
+ if (attributes.value(QLatin1String("attached")) == QLatin1String("true"))
+ attached = true;
+ bool readonly = false;
+ if (attributes.value(QLatin1String("writable")) == QLatin1String("false"))
+ readonly = true;
+ QmlPropertyNode* qpn = new QmlPropertyNode(parent, name, type, attached);
+ qpn->setGenus(Node::JS);
+ qpn->setReadOnly(readonly);
+ node = qpn;
+ }
+ else if ((elementName == QLatin1String("qmlmethod")) ||
+ (elementName == QLatin1String("qmlsignal")) ||
+ (elementName == QLatin1String("qmlsignalhandler"))) {
+ Node::NodeType t = Node::QmlMethod;
+ if (elementName == QLatin1String("qmlsignal"))
+ t = Node::QmlSignal;
+ else if (elementName == QLatin1String("qmlsignalhandler"))
+ t = Node::QmlSignalHandler;
+ bool attached = false;
+ FunctionNode* fn = new FunctionNode(t, parent, name, attached);
+ node = fn;
+ }
+ else if ((elementName == QLatin1String("jsmethod")) ||
+ (elementName == QLatin1String("jssignal")) ||
+ (elementName == QLatin1String("jssignalhandler"))) {
+ Node::NodeType t = Node::QmlMethod;
+ if (elementName == QLatin1String("jssignal"))
+ t = Node::QmlSignal;
+ else if (elementName == QLatin1String("jssignalhandler"))
+ t = Node::QmlSignalHandler;
+ bool attached = false;
+ FunctionNode* fn = new FunctionNode(t, parent, name, attached);
+ fn->setGenus(Node::JS);
+ node = fn;
+ }
+ else if (elementName == QLatin1String("group")) {
+ CollectionNode* cn = qdb_->addGroup(name);
+ cn->setTitle(attributes.value(QLatin1String("title")).toString());
+ cn->setSubTitle(attributes.value(QLatin1String("subtitle")).toString());
+ if (attributes.value(QLatin1String("seen")) == QLatin1String("true"))
+ cn->markSeen();
+ node = cn;
+ }
+ else if (elementName == QLatin1String("module")) {
+ CollectionNode* cn = qdb_->addModule(name);
+ cn->setTitle(attributes.value(QLatin1String("title")).toString());
+ cn->setSubTitle(attributes.value(QLatin1String("subtitle")).toString());
+ if (attributes.value(QLatin1String("seen")) == QLatin1String("true"))
+ cn->markSeen();
+ node = cn;
+ }
+ else if (elementName == QLatin1String("qmlmodule")) {
+ QString t = attributes.value(QLatin1String("qml-module-name")).toString();
+ CollectionNode* cn = qdb_->addQmlModule(t);
+ QStringList info;
+ info << t << attributes.value(QLatin1String("qml-module-version")).toString();
+ cn->setLogicalModuleInfo(info);
+ cn->setTitle(attributes.value(QLatin1String("title")).toString());
+ cn->setSubTitle(attributes.value(QLatin1String("subtitle")).toString());
+ if (attributes.value(QLatin1String("seen")) == QLatin1String("true"))
+ cn->markSeen();
+ node = cn;
+ }
+ else if (elementName == QLatin1String("jsmodule")) {
+ QString t = attributes.value(QLatin1String("js-module-name")).toString();
+ CollectionNode* cn = qdb_->addJsModule(t);
+ QStringList info;
+ info << t << attributes.value(QLatin1String("js-module-version")).toString();
+ cn->setLogicalModuleInfo(info);
+ cn->setTitle(attributes.value(QLatin1String("title")).toString());
+ cn->setSubTitle(attributes.value(QLatin1String("subtitle")).toString());
+ if (attributes.value(QLatin1String("seen")) == QLatin1String("true"))
+ cn->markSeen();
+ node = cn;
+ }
+ else if (elementName == QLatin1String("page")) {
+ Node::DocSubtype subtype;
+ Node::PageType ptype = Node::NoPageType;
+ QString attr = attributes.value(QLatin1String("subtype")).toString();
+ if (attr == QLatin1String("example")) {
+ subtype = Node::Example;
+ ptype = Node::ExamplePage;
+ }
+ else if (attr == QLatin1String("header")) {
+ subtype = Node::HeaderFile;
+ ptype = Node::ApiPage;
+ }
+ else if (attr == QLatin1String("file")) {
+ subtype = Node::File;
+ ptype = Node::NoPageType;
+ }
+ else if (attr == QLatin1String("page")) {
+ subtype = Node::Page;
+ ptype = Node::ArticlePage;
+ }
+ else if (attr == QLatin1String("externalpage")) {
+ subtype = Node::ExternalPage;
+ ptype = Node::ArticlePage;
+ }
+ else
+ goto done;
+
+ DocumentNode* docNode = new DocumentNode(parent, name, subtype, ptype);
+ docNode->setTitle(attributes.value(QLatin1String("title")).toString());
+
+ if (attributes.hasAttribute(QLatin1String("location")))
+ name = attributes.value(QLatin1String("location")).toString();
+
+ if (!indexUrl.isEmpty())
+ location = Location(indexUrl + QLatin1Char('/') + name);
+ else if (!indexUrl.isNull())
+ location = Location(name);
+
+ node = docNode;
+
+ }
+ else if (elementName == QLatin1String("enum")) {
+ EnumNode* enumNode = new EnumNode(parent, name);
+
+ if (!indexUrl.isEmpty())
+ location = Location(indexUrl + QLatin1Char('/') + parent->name().toLower() + ".html");
+ else if (!indexUrl.isNull())
+ location = Location(parent->name().toLower() + ".html");
+
+ while (reader.readNextStartElement()) {
+ QXmlStreamAttributes childAttributes = reader.attributes();
+ if (reader.name() == QLatin1String("value")) {
+
+ EnumItem item(childAttributes.value(QLatin1String("name")).toString(), childAttributes.value(QLatin1String("value")).toString());
+ enumNode->addItem(item);
+ } else if (reader.name() == QLatin1String("keyword")) {
+ insertTarget(TargetRec::Keyword, childAttributes, enumNode);
+ } else if (reader.name() == QLatin1String("target")) {
+ insertTarget(TargetRec::Target, childAttributes, enumNode);
+ }
+ reader.skipCurrentElement();
+ }
+
+ node = enumNode;
+
+ hasReadChildren = true;
+ }
+ else if (elementName == QLatin1String("typedef")) {
+ node = new TypedefNode(parent, name);
+
+ if (!indexUrl.isEmpty())
+ location = Location(indexUrl + QLatin1Char('/') + parent->name().toLower() + ".html");
+ else if (!indexUrl.isNull())
+ location = Location(parent->name().toLower() + ".html");
+
+ }
+ else if (elementName == QLatin1String("property")) {
+ node = new PropertyNode(parent, name);
+
+ if (!indexUrl.isEmpty())
+ location = Location(indexUrl + QLatin1Char('/') + parent->name().toLower() + ".html");
+ else if (!indexUrl.isNull())
+ location = Location(parent->name().toLower() + ".html");
+
+ }
+ else if (elementName == QLatin1String("function")) {
+ FunctionNode::Virtualness virt;
+ QString t = attributes.value(QLatin1String("virtual")).toString();
+ if (t == QLatin1String("non"))
+ virt = FunctionNode::NonVirtual;
+ else if (t == QLatin1String("virtual"))
+ virt = FunctionNode::NormalVirtual;
+ else if (t == QLatin1String("pure"))
+ virt = FunctionNode::PureVirtual;
+ else
+ goto done;
+
+ t = attributes.value(QLatin1String("meta")).toString();
+ FunctionNode::Metaness meta;
+ if (t == QLatin1String("plain"))
+ meta = FunctionNode::Plain;
+ else if (t == QLatin1String("signal"))
+ meta = FunctionNode::Signal;
+ else if (t == QLatin1String("slot"))
+ meta = FunctionNode::Slot;
+ else if (t == QLatin1String("constructor"))
+ meta = FunctionNode::Ctor;
+ else if (t == QLatin1String("destructor"))
+ meta = FunctionNode::Dtor;
+ else if (t == QLatin1String("macro"))
+ meta = FunctionNode::MacroWithParams;
+ else if (t == QLatin1String("macrowithparams"))
+ meta = FunctionNode::MacroWithParams;
+ else if (t == QLatin1String("macrowithoutparams"))
+ meta = FunctionNode::MacroWithoutParams;
+ else
+ goto done;
+
+ FunctionNode* functionNode = new FunctionNode(parent, name);
+ functionNode->setReturnType(attributes.value(QLatin1String("return")).toString());
+ functionNode->setVirtualness(virt);
+ functionNode->setMetaness(meta);
+ functionNode->setConst(attributes.value(QLatin1String("const")) == QLatin1String("true"));
+ functionNode->setStatic(attributes.value(QLatin1String("static")) == QLatin1String("true"));
+ if (attributes.value(QLatin1String("overload")) == QLatin1String("true")) {
+ functionNode->setOverloadFlag(true);
+ functionNode->setOverloadNumber(attributes.value(QLatin1String("overload-number")).toUInt());
+ }
+ else {
+ functionNode->setOverloadFlag(false);
+ functionNode->setOverloadNumber(0);
+ }
+ if (attributes.hasAttribute(QLatin1String("relates"))
+ && attributes.value(QLatin1String("relates")) != parent->name()) {
+ relatedList_.append(
+ QPair<FunctionNode*,QString>(functionNode,
+ attributes.value(QLatin1String("relates")).toString()));
+ }
+ /*
+ Note: The "signature" attribute was written to the
+ index file, but it is not read back in. Is that ok?
+ */
+
+ while (reader.readNextStartElement()) {
+ QXmlStreamAttributes childAttributes = reader.attributes();
+ if (reader.name() == QLatin1String("parameter")) {
+ // Do not use the default value for the parameter; it is not
+ // required, and has been known to cause problems.
+ Parameter parameter(childAttributes.value(QLatin1String("left")).toString(),
+ childAttributes.value(QLatin1String("right")).toString(),
+ childAttributes.value(QLatin1String("name")).toString(),
+ QString()); // childAttributes.value(QLatin1String("default"))
+ functionNode->addParameter(parameter);
+ } else if (reader.name() == QLatin1String("keyword")) {
+ insertTarget(TargetRec::Keyword, childAttributes, functionNode);
+ } else if (reader.name() == QLatin1String("target")) {
+ insertTarget(TargetRec::Target, childAttributes, functionNode);
+ }
+ reader.skipCurrentElement();
+ }
+
+ node = functionNode;
+ if (!indexUrl.isEmpty())
+ location = Location(indexUrl + QLatin1Char('/') + parent->name().toLower() + ".html");
+ else if (!indexUrl.isNull())
+ location = Location(parent->name().toLower() + ".html");
+
+ hasReadChildren = true;
+ }
+ else if (elementName == QLatin1String("variable")) {
+ node = new VariableNode(parent, name);
+ if (!indexUrl.isEmpty())
+ location = Location(indexUrl + QLatin1Char('/') + parent->name().toLower() + ".html");
+ else if (!indexUrl.isNull())
+ location = Location(parent->name().toLower() + ".html");
+ }
+ else if (elementName == QLatin1String("keyword")) {
+ insertTarget(TargetRec::Keyword, attributes, current);
+ goto done;
+ }
+ else if (elementName == QLatin1String("target")) {
+ insertTarget(TargetRec::Target, attributes, current);
+ goto done;
+ }
+ else if (elementName == QLatin1String("contents")) {
+ insertTarget(TargetRec::Contents, attributes, current);
+ goto done;
+ }
+ else
+ goto done;
+
+ {
+ QString access = attributes.value(QLatin1String("access")).toString();
+ if (access == "public")
+ node->setAccess(Node::Public);
+ else if (access == "protected")
+ node->setAccess(Node::Protected);
+ else if ((access == "private") || (access == "internal"))
+ node->setAccess(Node::Private);
+ else
+ node->setAccess(Node::Public);
+
+ if ((elementName != QLatin1String("page")) &&
+ (elementName != QLatin1String("qmlclass")) &&
+ (elementName != QLatin1String("qmlbasictype")) &&
+ (elementName != QLatin1String("jstype")) &&
+ (elementName != QLatin1String("jsbasictype"))) {
+ QString threadSafety = attributes.value(QLatin1String("threadsafety")).toString();
+ if (threadSafety == QLatin1String("non-reentrant"))
+ node->setThreadSafeness(Node::NonReentrant);
+ else if (threadSafety == QLatin1String("reentrant"))
+ node->setThreadSafeness(Node::Reentrant);
+ else if (threadSafety == QLatin1String("thread safe"))
+ node->setThreadSafeness(Node::ThreadSafe);
+ else
+ node->setThreadSafeness(Node::UnspecifiedSafeness);
+ }
+ else
+ node->setThreadSafeness(Node::UnspecifiedSafeness);
+
+ QString status = attributes.value(QLatin1String("status")).toString();
+ if (status == QLatin1String("compat"))
+ node->setStatus(Node::Compat);
+ else if (status == QLatin1String("obsolete"))
+ node->setStatus(Node::Obsolete);
+ else if (status == QLatin1String("deprecated"))
+ node->setStatus(Node::Obsolete);
+ else if (status == QLatin1String("preliminary"))
+ node->setStatus(Node::Preliminary);
+ else if (status == QLatin1String("active"))
+ node->setStatus(Node::Active);
+ else if (status == QLatin1String("internal"))
+ node->setStatus(Node::Internal);
+ else
+ node->setStatus(Node::Active);
+
+ QString physicalModuleName = attributes.value(QLatin1String("module")).toString();
+ if (!physicalModuleName.isEmpty())
+ qdb_->addToModule(physicalModuleName, node);
+ if (!href.isEmpty()) {
+ if (node->isExternalPage())
+ node->setUrl(href);
+ else if (!indexUrl.isEmpty())
+ node->setUrl(indexUrl + QLatin1Char('/') + href);
+ }
+
+ QString since = attributes.value(QLatin1String("since")).toString();
+ if (!since.isEmpty()) {
+ node->setSince(since);
+ }
+
+ QString groupsAttr = attributes.value(QLatin1String("groups")).toString();
+ if (!groupsAttr.isEmpty()) {
+ QStringList groupNames = groupsAttr.split(QLatin1Char(','));
+ foreach (const QString &name, groupNames) {
+ qdb_->addToGroup(name, node);
+ }
+ }
+
+ // Create some content for the node.
+ QSet<QString> emptySet;
+ Location t(filePath);
+ if (!filePath.isEmpty()) {
+ t.setLineNo(lineNo);
+ node->setLocation(t);
+ location = t;
+ }
+ Doc doc(location, location, " ", emptySet, emptySet); // placeholder
+ node->setDoc(doc);
+ node->setIndexNodeFlag();
+ node->setOutputSubdirectory(project_.toLower());
+ QString briefAttr = attributes.value(QLatin1String("brief")).toString();
+ if (!briefAttr.isEmpty()) {
+ node->setReconstitutedBrief(briefAttr);
+ }
+
+ if (!hasReadChildren) {
+ bool useParent = (elementName == QLatin1String("namespace") && name.isEmpty());
+ while (reader.readNextStartElement()) {
+ if (useParent)
+ readIndexSection(reader, parent, indexUrl);
+ else
+ readIndexSection(reader, node, indexUrl);
+ }
+ }
+ }
+
+ done:
+ while (!reader.isEndElement()) {
+ if (reader.readNext() == QXmlStreamReader::Invalid) {
+ break;
+ }
+ }
+}
+
+void QDocIndexFiles::insertTarget(TargetRec::TargetType type,
+ const QXmlStreamAttributes &attributes,
+ Node *node)
+{
+ int priority;
+ switch (type) {
+ case TargetRec::Keyword:
+ priority = 1;
+ break;
+ case TargetRec::Target:
+ priority = 2;
+ break;
+ case TargetRec::Contents:
+ priority = 3;
+ break;
+ default:
+ return;
+ }
+
+ QString name = attributes.value(QLatin1String("name")).toString();
+ QString title = attributes.value(QLatin1String("title")).toString();
+ qdb_->insertTarget(name, title, type, node, priority);
+}
+
+/*!
+ This function tries to resolve class inheritance immediately
+ after the index file is read. It is not always possible to
+ resolve a class inheritance at this point, because the base
+ class might be in an index file that hasn't been read yet, or
+ it might be in one of the header files that will be read for
+ the current module. These cases will be resolved after all
+ the index files and header and source files have been read,
+ just prior to beginning the generate phase for the current
+ module.
+
+ I don't think this is completely correct because it always
+ sets the access to public.
+ */
+void QDocIndexFiles::resolveIndex()
+{
+ QPair<ClassNode*,QString> pair;
+ foreach (pair, basesList_) {
+ foreach (const QString& base, pair.second.split(QLatin1Char(','))) {
+ QStringList basePath = base.split(QString("::"));
+ Node* n = qdb_->findClassNode(basePath);
+ if (n)
+ pair.first->addResolvedBaseClass(Node::Public, static_cast<ClassNode*>(n));
+ else
+ pair.first->addUnresolvedBaseClass(Node::Public, basePath, QString());
+ }
+ }
+ // No longer needed.
+ basesList_.clear();
+}
+
+/*
+ Goes though the list of nodes that are related to other aggregates
+ that were read from all index files, and tries to find the aggregate
+ nodes from the database. Calls the node's setRelates() for each
+ aggregate that is found in the local module (primary tree).
+
+ This function is meant to be called before starting the doc generation,
+ after all the index files are read.
+ */
+void QDocIndexFiles::resolveRelates()
+{
+ if (relatedList_.isEmpty())
+ return;
+
+ // Restrict searching only to the local (primary) tree
+ QVector<Tree*> searchOrder = qdb_->searchOrder();
+ qdb_->setLocalSearch();
+
+ QPair<FunctionNode*,QString> relatedPair;
+ foreach (relatedPair, relatedList_) {
+ QStringList path = relatedPair.second.split("::");
+ Node* n = qdb_->findRelatesNode(path);
+ if (n)
+ relatedPair.first->setRelates(static_cast<Aggregate*>(n));
+ }
+ // Restore original search order
+ qdb_->setSearchOrder(searchOrder);
+ relatedList_.clear();
+}
+
+/*!
+ 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 QDocIndexFiles::generateIndexSection(QXmlStreamWriter& writer,
+ Node* node,
+ bool generateInternalNodes)
+{
+ /*
+ Don't include index nodes in a new index file. Or DITA map nodes.
+ */
+ if (node->isIndexNode() || node->docSubtype() == Node::DitaMap)
+ return false;
+
+ QString nodeName;
+ QString logicalModuleName;
+ QString logicalModuleVersion;
+ QString qmlFullBaseName;
+ switch (node->type()) {
+ case Node::Namespace:
+ nodeName = "namespace";
+ break;
+ case Node::Class:
+ nodeName = "class";
+ break;
+ case Node::QmlType:
+ {
+ if (node->isQmlNode())
+ nodeName = "qmlclass";
+ else
+ nodeName = "jstype";
+ CollectionNode* cn = node->logicalModule();
+ if (cn)
+ logicalModuleName = cn->logicalModuleName();
+ qmlFullBaseName = node->qmlFullBaseName();
+ }
+ break;
+ case Node::QmlBasicType:
+ if (node->isQmlNode())
+ nodeName = "qmlbasictype";
+ else
+ nodeName = "jsbasictype";
+ break;
+ case Node::Document:
+ nodeName = "page";
+ break;
+ case Node::Group:
+ nodeName = "group";
+ break;
+ case Node::Module:
+ nodeName = "module";
+ break;
+ case Node::QmlModule:
+ if (node->isQmlNode())
+ nodeName = "qmlmodule";
+ else
+ nodeName = "jsmodule";
+ 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::QmlProperty:
+ if (node->isQmlNode())
+ nodeName = "qmlproperty";
+ else
+ nodeName = "jsProperty";
+ break;
+ case Node::QmlPropertyGroup:
+ if (node->isQmlNode())
+ nodeName = "qmlpropertygroup";
+ else
+ nodeName = "jspropertygroup";
+ break;
+ case Node::QmlSignal:
+ if (node->isQmlNode())
+ nodeName = "qmlsignal";
+ else
+ nodeName = "jssignal";
+ break;
+ case Node::QmlSignalHandler:
+ if (node->isQmlNode())
+ nodeName = "qmlsignalhandler";
+ else
+ nodeName = "jssignalhandler";
+ break;
+ case Node::QmlMethod:
+ if (node->isQmlNode())
+ nodeName = "qmlmethod";
+ else
+ nodeName = "jsmethod";
+ break;
+ default:
+ return false;
+ }
+
+ QString access;
+ switch (node->access()) {
+ case Node::Public:
+ access = "public";
+ break;
+ case Node::Protected:
+ access = "protected";
+ break;
+ case Node::Private:
+ {
+ access = "private";
+ bool b = generateInternalNodes;
+ if (b)
+ b = false;
+ }
+ break;
+ default:
+ return false;
+ }
+
+ QString objName = node->name();
+ // Special case: only the root node should have an empty name.
+ if (objName.isEmpty() && node != qdb_->primaryTreeRoot())
+ return false;
+
+ writer.writeStartElement(nodeName);
+
+ QXmlStreamAttributes attributes;
+
+ if (!node->isDocumentNode() && !node->isCollectionNode()) {
+ 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 = "obsolete";
+ break;
+ case Node::Preliminary:
+ status = "preliminary";
+ break;
+ case Node::Active:
+ status = "active";
+ break;
+ case Node::Internal:
+ status = "internal";
+ break;
+ default:
+ status = "main";
+ break;
+ }
+
+ writer.writeAttribute("name", objName);
+
+ // Write module and base type info for QML/JS types
+ if (node->type() == Node::QmlType || node->type() == Node::QmlModule) {
+ QString baseNameAttr("qml-base-type");
+ QString moduleNameAttr("qml-module-name");
+ QString moduleVerAttr("qml-module-version");
+ if (node->isJsNode()) {
+ baseNameAttr = "js-base-type";
+ moduleNameAttr = "js-module-name";
+ moduleVerAttr = "js-module-version";
+ }
+ if (node->type() == Node::QmlModule) {
+ logicalModuleName = node->logicalModuleName();
+ logicalModuleVersion = node->logicalModuleVersion();
+ }
+ if (!logicalModuleName.isEmpty())
+ writer.writeAttribute(moduleNameAttr, logicalModuleName);
+ if (!logicalModuleVersion.isEmpty())
+ writer.writeAttribute(moduleVerAttr, logicalModuleVersion);
+ if (!qmlFullBaseName.isEmpty())
+ writer.writeAttribute(baseNameAttr, qmlFullBaseName);
+ }
+
+ QString href;
+ if (!node->isExternalPage()) {
+ QString fullName = node->fullDocumentName();
+ if (fullName != objName)
+ writer.writeAttribute("fullname", fullName);
+#if 0
+ if (Generator::useOutputSubdirs())
+ href = node->outputSubdirectory();
+ if (!href.isEmpty())
+ href.append(QLatin1Char('/'));
+ href.append(gen_->fullDocumentLocation(node));
+#endif
+ href = gen_->fullDocumentLocation(node);
+ }
+ else
+ href = node->name();
+ if (node->isQmlNode() || node->isJsNode()) {
+ Aggregate* p = node->parent();
+ if (p) {
+ if (p->isQmlPropertyGroup() || p->isJsPropertyGroup())
+ p = p->parent();
+ if (p && (p->isQmlType() || p->isJsType()) && p->isAbstract())
+ href.clear();
+ }
+ }
+ if (!href.isEmpty())
+ writer.writeAttribute("href", href);
+
+ writer.writeAttribute("status", status);
+ if (!node->isDocumentNode() && !node->isCollectionNode()) {
+ writer.writeAttribute("access", access);
+ if (node->isAbstract())
+ writer.writeAttribute("abstract", "true");
+ }
+ const Location& declLocation = node->declLocation();
+ if (!declLocation.fileName().isEmpty())
+ writer.writeAttribute("location", declLocation.fileName());
+ if (!declLocation.filePath().isEmpty()) {
+ writer.writeAttribute("filepath", declLocation.filePath());
+ writer.writeAttribute("lineno", QString("%1").arg(declLocation.lineNo()));
+ }
+
+ if (!node->since().isEmpty()) {
+ writer.writeAttribute("since", node->since());
+ }
+
+ QString brief = node->doc().trimmedBriefText(node->name()).toString();
+ 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* n = related.node_;
+ if (n)
+ baseStrings.insert(n->fullName());
+ }
+ if (!baseStrings.isEmpty())
+ writer.writeAttribute("bases", QStringList(baseStrings.toList()).join(QLatin1Char(',')));
+ if (!node->physicalModuleName().isEmpty())
+ writer.writeAttribute("module", node->physicalModuleName());
+ if (!classNode->groupNames().isEmpty())
+ writer.writeAttribute("groups", classNode->groupNames().join(QLatin1Char(',')));
+ if (!brief.isEmpty())
+ writer.writeAttribute("brief", brief);
+ }
+ break;
+ case Node::Namespace:
+ {
+ const NamespaceNode* namespaceNode = static_cast<const NamespaceNode*>(node);
+ if (!namespaceNode->physicalModuleName().isEmpty())
+ writer.writeAttribute("module", namespaceNode->physicalModuleName());
+ if (!namespaceNode->groupNames().isEmpty())
+ writer.writeAttribute("groups", namespaceNode->groupNames().join(QLatin1Char(',')));
+ if (!brief.isEmpty())
+ writer.writeAttribute("brief", brief);
+ }
+ break;
+ case Node::QmlType:
+ {
+ const QmlTypeNode* qcn = static_cast<const QmlTypeNode*>(node);
+ writer.writeAttribute("title", qcn->title());
+ writer.writeAttribute("fulltitle", qcn->fullTitle());
+ writer.writeAttribute("subtitle", qcn->subTitle());
+ if (!qcn->groupNames().isEmpty())
+ writer.writeAttribute("groups", qcn->groupNames().join(QLatin1Char(',')));
+ if (!brief.isEmpty())
+ writer.writeAttribute("brief", brief);
+ }
+ break;
+ case Node::Document:
+ {
+ /*
+ Document nodes (such as manual pages) have a subtype,
+ a title, and other attributes.
+ */
+ bool writeModuleName = false;
+ const DocumentNode* docNode = static_cast<const DocumentNode*>(node);
+ switch (docNode->docSubtype()) {
+ case Node::Example:
+ writer.writeAttribute("subtype", "example");
+ writeModuleName = true;
+ break;
+ case Node::HeaderFile:
+ writer.writeAttribute("subtype", "header");
+ writeModuleName = true;
+ break;
+ case Node::File:
+ writer.writeAttribute("subtype", "file");
+ break;
+ case Node::Page:
+ writer.writeAttribute("subtype", "page");
+ writeModuleName = true;
+ break;
+ case Node::ExternalPage:
+ writer.writeAttribute("subtype", "externalpage");
+ break;
+ default:
+ break;
+ }
+ writer.writeAttribute("title", docNode->title());
+ writer.writeAttribute("fulltitle", docNode->fullTitle());
+ writer.writeAttribute("subtitle", docNode->subTitle());
+ if (!node->physicalModuleName().isEmpty() && writeModuleName) {
+ writer.writeAttribute("module", node->physicalModuleName());
+ }
+ if (!docNode->groupNames().isEmpty())
+ writer.writeAttribute("groups", docNode->groupNames().join(QLatin1Char(',')));
+ if (!brief.isEmpty())
+ writer.writeAttribute("brief", brief);
+ }
+ break;
+ case Node::Group:
+ {
+ const CollectionNode* cn = static_cast<const CollectionNode*>(node);
+ writer.writeAttribute("seen", cn->wasSeen() ? "true" : "false");
+ writer.writeAttribute("title", cn->title());
+ if (!cn->subTitle().isEmpty())
+ writer.writeAttribute("subtitle", cn->subTitle());
+ if (!cn->physicalModuleName().isEmpty())
+ writer.writeAttribute("module", cn->physicalModuleName());
+ if (!cn->groupNames().isEmpty())
+ writer.writeAttribute("groups", cn->groupNames().join(QLatin1Char(',')));
+ /*
+ This is not read back in, so it probably
+ shouldn't be written out in the first place.
+ */
+ if (!cn->members().isEmpty()) {
+ QStringList names;
+ foreach (const Node* member, cn->members())
+ names.append(member->name());
+ writer.writeAttribute("members", names.join(QLatin1Char(',')));
+ }
+ if (!brief.isEmpty())
+ writer.writeAttribute("brief", brief);
+ }
+ break;
+ case Node::Module:
+ {
+ const CollectionNode* cn = static_cast<const CollectionNode*>(node);
+ writer.writeAttribute("seen", cn->wasSeen() ? "true" : "false");
+ writer.writeAttribute("title", cn->title());
+ if (!cn->subTitle().isEmpty())
+ writer.writeAttribute("subtitle", cn->subTitle());
+ if (!cn->physicalModuleName().isEmpty())
+ writer.writeAttribute("module", cn->physicalModuleName());
+ if (!cn->groupNames().isEmpty())
+ writer.writeAttribute("groups", cn->groupNames().join(QLatin1Char(',')));
+ /*
+ This is not read back in, so it probably
+ shouldn't be written out in the first place.
+ */
+ if (!cn->members().isEmpty()) {
+ QStringList names;
+ foreach (const Node* member, cn->members())
+ names.append(member->name());
+ writer.writeAttribute("members", names.join(QLatin1Char(',')));
+ }
+ if (!brief.isEmpty())
+ writer.writeAttribute("brief", brief);
+ }
+ break;
+ case Node::QmlModule:
+ {
+ const CollectionNode* cn = static_cast<const CollectionNode*>(node);
+ writer.writeAttribute("seen", cn->wasSeen() ? "true" : "false");
+ writer.writeAttribute("title", cn->title());
+ if (!cn->subTitle().isEmpty())
+ writer.writeAttribute("subtitle", cn->subTitle());
+ if (!cn->physicalModuleName().isEmpty())
+ writer.writeAttribute("module", cn->physicalModuleName());
+ if (!cn->groupNames().isEmpty())
+ writer.writeAttribute("groups", cn->groupNames().join(QLatin1Char(',')));
+ /*
+ This is not read back in, so it probably
+ shouldn't be written out in the first place.
+ */
+ if (!cn->members().isEmpty()) {
+ QStringList names;
+ foreach (const Node* member, cn->members())
+ names.append(member->name());
+ writer.writeAttribute("members", names.join(QLatin1Char(',')));
+ }
+ if (!brief.isEmpty())
+ writer.writeAttribute("brief", brief);
+ }
+ 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::NormalVirtual:
+ writer.writeAttribute("virtual", "virtual");
+ 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());
+ }
+ if (functionNode->hasAssociatedProperties()) {
+ QString associatedProperties;
+ foreach (PropertyNode* pn, functionNode->associatedProperties()) {
+ if (!associatedProperties.isEmpty())
+ associatedProperties += QLatin1String(", ");
+ associatedProperties += pn->name();
+ }
+ writer.writeAttribute("associated-property", associatedProperties);
+ }
+ writer.writeAttribute("type", functionNode->returnType());
+ if (!brief.isEmpty())
+ writer.writeAttribute("brief", brief);
+
+ /*
+ Note: The "signature" attribute is written to the
+ index file, but it is not read back in. Is that ok?
+ */
+ QString signature = functionNode->signature();
+ 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", parameter.dataType());
+ writer.writeAttribute("right", parameter.rightType());
+ writer.writeAttribute("name", parameter.name());
+ writer.writeAttribute("default", parameter.defaultValue());
+ writer.writeEndElement(); // parameter
+ }
+ }
+ break;
+ case Node::QmlProperty:
+ {
+ QmlPropertyNode* qpn = static_cast<QmlPropertyNode*>(node);
+ writer.writeAttribute("type", qpn->dataType());
+ writer.writeAttribute("attached", qpn->isAttached() ? "true" : "false");
+ writer.writeAttribute("writable", qpn->isWritable() ? "true" : "false");
+ if (!brief.isEmpty())
+ writer.writeAttribute("brief", brief);
+ }
+ break;
+ case Node::QmlPropertyGroup:
+ {
+ if (!brief.isEmpty())
+ writer.writeAttribute("brief", brief);
+ }
+ break;
+ case Node::Property:
+ {
+ const PropertyNode* propertyNode = static_cast<const PropertyNode*>(node);
+ writer.writeAttribute("type", propertyNode->dataType());
+ if (!brief.isEmpty())
+ writer.writeAttribute("brief", brief);
+ 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(); // setter
+ }
+ }
+ 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(); // resetter
+ }
+ }
+ foreach (const Node* fnNode, propertyNode->notifiers()) {
+ if (fnNode) {
+ const FunctionNode* functionNode = static_cast<const FunctionNode*>(fnNode);
+ writer.writeStartElement("notifier");
+ writer.writeAttribute("name", functionNode->name());
+ writer.writeEndElement(); // notifier
+ }
+ }
+ }
+ break;
+ case Node::Variable:
+ {
+ const VariableNode* variableNode = static_cast<const VariableNode*>(node);
+ writer.writeAttribute("type", variableNode->dataType());
+ writer.writeAttribute("static", variableNode->isStatic() ? "true" : "false");
+ if (!brief.isEmpty())
+ writer.writeAttribute("brief", brief);
+ }
+ break;
+ case Node::Enum:
+ {
+ const EnumNode* enumNode = static_cast<const EnumNode*>(node);
+ if (enumNode->flagsType()) {
+ writer.writeAttribute("typedef",enumNode->flagsType()->fullDocumentName());
+ }
+ foreach (const EnumItem& item, enumNode->items()) {
+ writer.writeStartElement("value");
+ writer.writeAttribute("name", item.name());
+ writer.writeAttribute("value", item.value());
+ writer.writeEndElement(); // value
+ }
+ }
+ break;
+ case Node::Typedef:
+ {
+ const TypedefNode* typedefNode = static_cast<const TypedefNode*>(node);
+ if (typedefNode->associatedEnum()) {
+ writer.writeAttribute("enum",typedefNode->associatedEnum()->fullDocumentName());
+ }
+ }
+ break;
+ default:
+ break;
+ }
+
+ /*
+ For our 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.
+
+ targets and keywords are now allowed in any node, not just inner nodes.
+ */
+
+ if (node->doc().hasTargets()) {
+ bool external = false;
+ if (node->type() == Node::Document) {
+ const DocumentNode* docNode = static_cast<const DocumentNode*>(node);
+ if (docNode->docSubtype() == Node::ExternalPage)
+ external = true;
+ }
+ foreach (const Atom* target, node->doc().targets()) {
+ QString title = target->string();
+ QString name = Doc::canonicalTitle(title);
+ writer.writeStartElement("target");
+ if (!external)
+ writer.writeAttribute("name", name);
+ else
+ writer.writeAttribute("name", title);
+ if (name != title)
+ writer.writeAttribute("title", title);
+ writer.writeEndElement(); // target
+ }
+ }
+ if (node->doc().hasKeywords()) {
+ foreach (const Atom* keyword, node->doc().keywords()) {
+ QString title = keyword->string();
+ QString name = Doc::canonicalTitle(title);
+ writer.writeStartElement("keyword");
+ writer.writeAttribute("name", name);
+ if (name != title)
+ writer.writeAttribute("title", title);
+ writer.writeEndElement(); // keyword
+ }
+ }
+
+ // 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->isAggregate()) {
+ const Aggregate* inner = static_cast<const Aggregate*>(node);
+
+ 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
+ }
+ }
+ }
+ return true;
+}
+
+/*!
+ Returns \c true if the node \a n1 is less than node \a n2. The
+ comparison is performed by comparing properties of the nodes
+ in order of increasing complexity.
+*/
+bool compareNodes(const Node* n1, const Node* n2)
+{
+ // Private nodes can occur in any order since they won't normally be
+ // written to the index.
+ if (n1->access() == Node::Private && n2->access() == Node::Private)
+ return false;
+
+ if (n1->location().filePath() < n2->location().filePath())
+ return true;
+ else if (n1->location().filePath() > n2->location().filePath())
+ return false;
+
+ if (n1->type() < n2->type())
+ return true;
+ else if (n1->type() > n2->type())
+ return false;
+
+ if (n1->name() < n2->name())
+ return true;
+ else if (n1->name() > n2->name())
+ return false;
+
+ if (n1->access() < n2->access())
+ return true;
+ else if (n1->access() > n2->access())
+ return false;
+
+ if (n1->type() == Node::Function && n2->type() == Node::Function) {
+ const FunctionNode* f1 = static_cast<const FunctionNode*>(n1);
+ const FunctionNode* f2 = static_cast<const FunctionNode*>(n2);
+
+ if (f1->isConst() < f2->isConst())
+ return true;
+ else if (f1->isConst() > f2->isConst())
+ return false;
+
+ if (f1->signature() < f2->signature())
+ return true;
+ else if (f1->signature() > f2->signature())
+ return false;
+ }
+
+ if (n1->isDocumentNode() && n2->isDocumentNode()) {
+ const DocumentNode* f1 = static_cast<const DocumentNode*>(n1);
+ const DocumentNode* f2 = static_cast<const DocumentNode*>(n2);
+ if (f1->fullTitle() < f2->fullTitle())
+ return true;
+ else if (f1->fullTitle() > f2->fullTitle())
+ return false;
+ }
+ else if (n1->isCollectionNode() && n2->isCollectionNode()) {
+ const CollectionNode* f1 = static_cast<const CollectionNode*>(n1);
+ const CollectionNode* f2 = static_cast<const CollectionNode*>(n2);
+ if (f1->fullTitle() < f2->fullTitle())
+ return true;
+ else if (f1->fullTitle() > f2->fullTitle())
+ return false;
+ }
+
+ return false;
+}
+
+/*!
+ Generate index sections for the child nodes of the given \a node
+ using the \a writer specified. If \a generateInternalNodes is true,
+ nodes marked as internal will be included in the index; otherwise,
+ they will be omitted.
+*/
+void QDocIndexFiles::generateIndexSections(QXmlStreamWriter& writer,
+ Node* node,
+ bool generateInternalNodes)
+{
+ /*
+ Note that groups, modules, and QML modules are written
+ after all the other nodes.
+ */
+ if (node->isGroup() || node->isModule() || node->isQmlModule() || node->isJsModule())
+ return;
+
+ if (generateIndexSection(writer, node, generateInternalNodes)) {
+ if (node->isAggregate()) {
+ const Aggregate* inner = static_cast<const Aggregate*>(node);
+
+ NodeList cnodes = inner->childNodes();
+ std::sort(cnodes.begin(), cnodes.end(), compareNodes);
+
+ foreach (Node* child, cnodes) {
+ generateIndexSections(writer, child, generateInternalNodes);
+ }
+ }
+
+ if (node == top) {
+ /*
+ We wait until the end of the index file to output the group, module,
+ and QML module elements. By outputting them at the end, when we read
+ the index file back in, all the group, module, and QML module member
+ elements will have already been created. It is then only necessary to
+ create the group, module, or QML module element and add each member to
+ its member list.
+ */
+ const CNMap& groups = qdb_->groups();
+ if (!groups.isEmpty()) {
+ CNMap::ConstIterator g = groups.constBegin();
+ while (g != groups.constEnd()) {
+ if (generateIndexSection(writer, g.value(), generateInternalNodes))
+ writer.writeEndElement();
+ ++g;
+ }
+ }
+
+ const CNMap& modules = qdb_->modules();
+ if (!modules.isEmpty()) {
+ CNMap::ConstIterator g = modules.constBegin();
+ while (g != modules.constEnd()) {
+ if (generateIndexSection(writer, g.value(), generateInternalNodes))
+ writer.writeEndElement();
+ ++g;
+ }
+ }
+
+ const CNMap& qmlModules = qdb_->qmlModules();
+ if (!qmlModules.isEmpty()) {
+ CNMap::ConstIterator g = qmlModules.constBegin();
+ while (g != qmlModules.constEnd()) {
+ if (generateIndexSection(writer, g.value(), generateInternalNodes))
+ writer.writeEndElement();
+ ++g;
+ }
+ }
+
+ const CNMap& jsModules = qdb_->jsModules();
+ if (!jsModules.isEmpty()) {
+ CNMap::ConstIterator g = jsModules.constBegin();
+ while (g != jsModules.constEnd()) {
+ if (generateIndexSection(writer, g.value(), generateInternalNodes))
+ writer.writeEndElement();
+ ++g;
+ }
+ }
+ }
+
+ writer.writeEndElement();
+ }
+}
+
+/*!
+ Outputs an index file.
+ */
+void QDocIndexFiles::generateIndex(const QString& fileName,
+ const QString& url,
+ const QString& title,
+ Generator* g,
+ bool generateInternalNodes)
+{
+ QFile file(fileName);
+ if (!file.open(QFile::WriteOnly | QFile::Text))
+ return;
+
+ QString msg = "Writing index file: " + fileName;
+ Location::logToStdErr(msg);
+
+ gen_ = g;
+ 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", qdb_->version());
+ writer.writeAttribute("project", g->config()->getString(CONFIG_PROJECT));
+
+ top = qdb_->primaryTreeRoot();
+ generateIndexSections(writer, top, generateInternalNodes);
+
+ writer.writeEndElement(); // INDEX
+ writer.writeEndElement(); // QDOCINDEX
+ writer.writeEndDocument();
+ file.close();
+}
+
+QT_END_NAMESPACE
diff --git a/src/qdoc/qdocindexfiles.h b/src/qdoc/qdocindexfiles.h
new file mode 100644
index 000000000..9873322b3
--- /dev/null
+++ b/src/qdoc/qdocindexfiles.h
@@ -0,0 +1,86 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL21$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** 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 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** As a special exception, The Qt Company gives you certain additional
+** rights. These rights are described in The Qt Company LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QDOCINDEXFILES_H
+#define QDOCINDEXFILES_H
+
+#include "node.h"
+#include "tree.h"
+
+QT_BEGIN_NAMESPACE
+
+class Atom;
+class Generator;
+class QStringList;
+class QDocDatabase;
+class QXmlStreamWriter;
+class QXmlStreamAttributes;
+
+class QDocIndexFiles
+{
+ friend class QDocDatabase;
+
+ private:
+ static QDocIndexFiles* qdocIndexFiles();
+ static void destroyQDocIndexFiles();
+
+ QDocIndexFiles();
+ ~QDocIndexFiles();
+
+ void readIndexes(const QStringList& indexFiles);
+ void generateIndex(const QString& fileName,
+ const QString& url,
+ const QString& title,
+ Generator* g,
+ bool generateInternalNodes = false);
+
+ void readIndexFile(const QString& path);
+ void readIndexSection(QXmlStreamReader &reader, Node* current, const QString& indexUrl);
+ void insertTarget(TargetRec::TargetType type, const QXmlStreamAttributes &attributes, Node *node);
+ void resolveIndex();
+ void resolveRelates();
+ bool generateIndexSection(QXmlStreamWriter& writer, Node* node, bool generateInternalNodes = false);
+ void generateIndexSections(QXmlStreamWriter& writer, Node* node, bool generateInternalNodes = false);
+
+ private:
+ static QDocIndexFiles* qdocIndexFiles_;
+ QDocDatabase* qdb_;
+ Generator* gen_;
+ QString project_;
+ QVector<QPair<ClassNode*,QString> > basesList_;
+ QVector<QPair<FunctionNode*,QString> > relatedList_;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/qdoc/qdoctagfiles.cpp b/src/qdoc/qdoctagfiles.cpp
new file mode 100644
index 000000000..1210ac7c3
--- /dev/null
+++ b/src/qdoc/qdoctagfiles.cpp
@@ -0,0 +1,385 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL21$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** 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 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** As a special exception, The Qt Company gives you certain additional
+** rights. These rights are described in The Qt Company LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "node.h"
+#include "qdoctagfiles.h"
+#include "qdocdatabase.h"
+
+#include "atom.h"
+#include "doc.h"
+#include "htmlgenerator.h"
+#include "location.h"
+#include "node.h"
+#include "text.h"
+#include <limits.h>
+#include <qdebug.h>
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class QDocTagFiles
+
+ This class handles the generation of the qdoc tag file.
+ */
+
+QDocTagFiles* QDocTagFiles::qdocTagFiles_ = NULL;
+
+/*!
+ Constructs the singleton. \a qdb is the pointer to the
+ qdoc database that is used when reading and writing the
+ index files.
+ */
+QDocTagFiles::QDocTagFiles()
+{
+ qdb_ = QDocDatabase::qdocDB();
+}
+
+/*!
+ Destroys the singleton QDocTagFiles.
+ */
+QDocTagFiles::~QDocTagFiles()
+{
+ qdb_ = 0;
+}
+
+/*!
+ Creates the singleton. Allows only one instance of the class
+ to be created. Returns a pointer to the singleton.
+ */
+QDocTagFiles* QDocTagFiles::qdocTagFiles()
+{
+ if (!qdocTagFiles_)
+ qdocTagFiles_ = new QDocTagFiles;
+ return qdocTagFiles_;
+}
+
+/*!
+ Destroys the singleton.
+ */
+void QDocTagFiles::destroyQDocTagFiles()
+{
+ if (qdocTagFiles_) {
+ delete qdocTagFiles_;
+ qdocTagFiles_ = 0;
+ }
+}
+
+/*!
+ 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 QDocTagFiles::generateTagFileCompounds(QXmlStreamWriter& writer, const Aggregate* inner)
+{
+ 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:
+ case Node::QmlType:
+ kind = "class";
+ break;
+ case Node::Enum:
+ case Node::Typedef:
+ case Node::Property:
+ case Node::Function:
+ case Node::Variable:
+ 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 != qdb_->primaryTreeRoot())
+ continue;
+
+ // *** Write the starting tag for the element here. ***
+ writer.writeStartElement("compound");
+ writer.writeAttribute("kind", kind);
+
+ if (node->type() == Node::Class) {
+ writer.writeTextElement("name", node->fullDocumentName());
+ writer.writeTextElement("filename", gen_->fullDocumentLocation(node, false));
+
+ // 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* n = related.node_;
+ if (n)
+ writer.writeTextElement("base", n->name());
+ }
+
+ // Recurse to write all members.
+ generateTagFileMembers(writer, static_cast<const Aggregate*>(node));
+ writer.writeEndElement();
+
+ // Recurse to write all compounds.
+ generateTagFileCompounds(writer, static_cast<const Aggregate*>(node));
+ }
+ else {
+ writer.writeTextElement("name", node->fullDocumentName());
+ writer.writeTextElement("filename", gen_->fullDocumentLocation(node, false));
+
+ // Recurse to write all members.
+ generateTagFileMembers(writer, static_cast<const Aggregate*>(node));
+ writer.writeEndElement();
+
+ // Recurse to write all compounds.
+ generateTagFileCompounds(writer, static_cast<const Aggregate*>(node));
+ }
+ }
+}
+
+/*!
+ Writes all the members of the \a inner node with the \a writer.
+ The node represents a C++ class, namespace, etc.
+ */
+void QDocTagFiles::generateTagFileMembers(QXmlStreamWriter& writer, const Aggregate* inner)
+{
+ 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:
+ 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 != qdb_->primaryTreeRoot())
+ 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(node->fullDocumentName());
+ writer.writeEndElement();
+ break;
+ case Node::Namespace:
+ writer.writeCharacters(node->fullDocumentName());
+ 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::NormalVirtual:
+ 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 = gen_->fullDocumentLocation(node, false).split(QLatin1Char('#'));
+ writer.writeTextElement("anchorfile", pieces[0]);
+ writer.writeTextElement("anchor", pieces[1]);
+ QString signature = functionNode->signature();
+ signature = signature.mid(signature.indexOf(QChar('('))).trimmed();
+ 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 = gen_->fullDocumentLocation(node, false).split(QLatin1Char('#'));
+ writer.writeTextElement("anchorfile", pieces[0]);
+ writer.writeTextElement("anchor", pieces[1]);
+ writer.writeTextElement("arglist", QString());
+ }
+ writer.writeEndElement(); // member
+ break;
+ case Node::Enum:
+ {
+ const EnumNode* enumNode = static_cast<const EnumNode*>(node);
+ writer.writeTextElement("name", objName);
+ QStringList pieces = gen_->fullDocumentLocation(node, false).split(QLatin1Char('#'));
+ writer.writeTextElement("anchor", pieces[1]);
+ writer.writeTextElement("arglist", QString());
+ 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", QString());
+ writer.writeEndElement(); // member
+ }
+ }
+ break;
+ case Node::Typedef:
+ {
+ const TypedefNode* typedefNode = static_cast<const TypedefNode*>(node);
+ if (typedefNode->associatedEnum())
+ writer.writeAttribute("type", typedefNode->associatedEnum()->fullDocumentName());
+ else
+ writer.writeAttribute("type", QString());
+ writer.writeTextElement("name", objName);
+ QStringList pieces = gen_->fullDocumentLocation(node, false).split(QLatin1Char('#'));
+ writer.writeTextElement("anchorfile", pieces[0]);
+ writer.writeTextElement("anchor", pieces[1]);
+ writer.writeTextElement("arglist", QString());
+ }
+ writer.writeEndElement(); // member
+ break;
+
+ case Node::Variable:
+ default:
+ break;
+ }
+ }
+}
+
+/*!
+ Writes a tag file named \a fileName.
+ */
+void QDocTagFiles::generateTagFile(const QString& fileName, Generator* g)
+{
+ QFile file(fileName);
+ QFileInfo fileInfo(fileName);
+
+ // If no path was specified or it doesn't exist,
+ // default to the output directory
+ if (fileInfo.fileName() == fileName || !fileInfo.dir().exists())
+ file.setFileName(gen_->outputDir() + QLatin1Char('/') +
+ fileInfo.fileName());
+
+ if (!file.open(QFile::WriteOnly | QFile::Text)) {
+ Location::null.warning(
+ QString("Failed to open %1 for writing.").arg(file.fileName()));
+ return;
+ }
+
+ gen_ = g;
+ QXmlStreamWriter writer(&file);
+ writer.setAutoFormatting(true);
+ writer.writeStartDocument();
+ writer.writeStartElement("tagfile");
+ generateTagFileCompounds(writer, qdb_->primaryTreeRoot());
+ writer.writeEndElement(); // tagfile
+ writer.writeEndDocument();
+ file.close();
+}
+
+QT_END_NAMESPACE
diff --git a/src/qdoc/qdoctagfiles.h b/src/qdoc/qdoctagfiles.h
new file mode 100644
index 000000000..ad1d383bf
--- /dev/null
+++ b/src/qdoc/qdoctagfiles.h
@@ -0,0 +1,68 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL21$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** 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 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** As a special exception, The Qt Company gives you certain additional
+** rights. These rights are described in The Qt Company LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QDOCTAGFILES_H
+#define QDOCTAGFILES_H
+
+#include "qxmlstream.h"
+
+QT_BEGIN_NAMESPACE
+
+class Aggregate;
+class QDocDatabase;
+class Generator;
+
+class QDocTagFiles
+{
+ friend class QDocDatabase;
+
+ private:
+ static QDocTagFiles* qdocTagFiles();
+ static void destroyQDocTagFiles();
+
+ QDocTagFiles();
+ ~QDocTagFiles();
+
+ void generateTagFileCompounds(QXmlStreamWriter& writer, const Aggregate* inner);
+ void generateTagFileMembers(QXmlStreamWriter& writer, const Aggregate* inner);
+ void generateTagFile(const QString& fileName, Generator* g);
+
+ private:
+ static QDocTagFiles* qdocTagFiles_;
+ QDocDatabase* qdb_;
+ Generator* gen_;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/qdoc/qmlcodemarker.cpp b/src/qdoc/qmlcodemarker.cpp
new file mode 100644
index 000000000..089e1d1c8
--- /dev/null
+++ b/src/qdoc/qmlcodemarker.cpp
@@ -0,0 +1,274 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL21$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** 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 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** As a special exception, The Qt Company gives you certain additional
+** rights. These rights are described in The Qt Company LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*
+ qmlcodemarker.cpp
+*/
+
+#include "qmlcodemarker.h"
+
+#include "atom.h"
+#include "node.h"
+#include "qmlmarkupvisitor.h"
+#include "text.h"
+#include "tree.h"
+#include "generator.h"
+
+#include <private/qqmljsast_p.h>
+#include <private/qqmljsastfwd_p.h>
+#include <private/qqmljsengine_p.h>
+#include <private/qqmljslexer_p.h>
+#include <private/qqmljsparser_p.h>
+
+QT_BEGIN_NAMESPACE
+
+QmlCodeMarker::QmlCodeMarker()
+{
+}
+
+QmlCodeMarker::~QmlCodeMarker()
+{
+}
+
+/*!
+ Returns \c true if the \a code is recognized by the parser.
+ */
+bool QmlCodeMarker::recognizeCode(const QString &code)
+{
+ QQmlJS::Engine engine;
+ QQmlJS::Lexer lexer(&engine);
+ QQmlJS::Parser parser(&engine);
+
+ QString newCode = code;
+ extractPragmas(newCode);
+ lexer.setCode(newCode, 1);
+
+ return parser.parse();
+}
+
+/*!
+ Returns \c true if \a ext is any of a list of file extensions
+ for the QML language.
+ */
+bool QmlCodeMarker::recognizeExtension(const QString &ext)
+{
+ return ext == "qml";
+}
+
+/*!
+ Returns \c true if the \a language is recognized. Only "QML" is
+ recognized by this marker.
+ */
+bool QmlCodeMarker::recognizeLanguage(const QString &language)
+{
+ return language == "QML";
+}
+
+/*!
+ Returns the type of atom used to represent QML code in the documentation.
+*/
+Atom::AtomType QmlCodeMarker::atomType() const
+{
+ return Atom::Qml;
+}
+
+QString QmlCodeMarker::markedUpCode(const QString &code,
+ const Node *relative,
+ const Location &location)
+{
+ return addMarkUp(code, relative, location);
+}
+
+QString QmlCodeMarker::markedUpName(const Node *node)
+{
+ QString name = linkTag(node, taggedNode(node));
+ if (node->type() == Node::QmlMethod)
+ name += "()";
+ return name;
+}
+
+QString QmlCodeMarker::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 QmlCodeMarker::markedUpIncludes(const QStringList& includes)
+{
+ QString code;
+
+ QStringList::ConstIterator inc = includes.constBegin();
+ while (inc != includes.constEnd()) {
+ code += "import " + *inc + QLatin1Char('\n');
+ ++inc;
+ }
+ Location location;
+ return addMarkUp(code, 0, location);
+}
+
+QString QmlCodeMarker::functionBeginRegExp(const QString& funcName)
+{
+ return QLatin1Char('^') + QRegExp::escape("function " + funcName) + QLatin1Char('$');
+
+}
+
+QString QmlCodeMarker::functionEndRegExp(const QString& /* funcName */)
+{
+ return "^\\}$";
+}
+
+QString QmlCodeMarker::addMarkUp(const QString &code,
+ const Node * /* relative */,
+ const Location &location)
+{
+ QQmlJS::Engine engine;
+ QQmlJS::Lexer lexer(&engine);
+
+ QString newCode = code;
+ QList<QQmlJS::AST::SourceLocation> pragmas = extractPragmas(newCode);
+ lexer.setCode(newCode, 1);
+
+ QQmlJS::Parser parser(&engine);
+ QString output;
+
+ if (parser.parse()) {
+ QQmlJS::AST::UiProgram *ast = parser.ast();
+ // Pass the unmodified code to the visitor so that pragmas and other
+ // unhandled source text can be output.
+ QmlMarkupVisitor visitor(code, pragmas, &engine);
+ QQmlJS::AST::Node::accept(ast, &visitor);
+ output = visitor.markedUpCode();
+ } else {
+ location.warning(tr("Unable to parse QML snippet: \"%1\" at line %2, column %3").arg(
+ parser.errorMessage()).arg(parser.errorLineNumber()).arg(
+ parser.errorColumnNumber()));
+ output = protect(code);
+ }
+
+ return output;
+}
+
+/*
+ Copied and pasted from
+ src/declarative/qml/qqmlscriptparser.cpp.
+*/
+static void replaceWithSpace(QString &str, int idx, int n)
+{
+ QChar *data = str.data() + idx;
+ const QChar space(QLatin1Char(' '));
+ for (int ii = 0; ii < n; ++ii)
+ *data++ = space;
+}
+
+/*
+ Copied and pasted from
+ src/declarative/qml/qqmlscriptparser.cpp then modified to
+ return a list of removed pragmas.
+
+ Searches for ".pragma <value>" or ".import <stuff>" declarations
+ in \a script. Currently supported pragmas are: library
+*/
+QList<QQmlJS::AST::SourceLocation> QmlCodeMarker::extractPragmas(QString &script)
+{
+ const QString pragma(QLatin1String("pragma"));
+ const QString library(QLatin1String("library"));
+ QList<QQmlJS::AST::SourceLocation> removed;
+
+ QQmlJS::Lexer l(0);
+ l.setCode(script, 0);
+
+ int token = l.lex();
+
+ while (true) {
+ if (token != QQmlJSGrammar::T_DOT)
+ return removed;
+
+ int startOffset = l.tokenOffset();
+ int startLine = l.tokenStartLine();
+ int startColumn = l.tokenStartColumn();
+
+ token = l.lex();
+
+ if (token != QQmlJSGrammar::T_PRAGMA && token != QQmlJSGrammar::T_IMPORT)
+ return removed;
+ int endOffset = 0;
+ while (startLine == l.tokenStartLine()) {
+ endOffset = l.tokenLength() + l.tokenOffset();
+ token = l.lex();
+ }
+ replaceWithSpace(script, startOffset, endOffset - startOffset);
+ removed.append(QQmlJS::AST::SourceLocation(startOffset,
+ endOffset - startOffset,
+ startLine,
+ startColumn));
+#if 0
+ token = l.lex();
+ if (Generator::debugging())
+ qDebug() << " third token";
+ if (token != QQmlJSGrammar::T_IDENTIFIER ||
+ l.tokenStartLine() != startLine)
+ return removed;
+
+ QString pragmaValue = script.mid(l.tokenOffset(), l.tokenLength());
+ int endOffset = l.tokenLength() + l.tokenOffset();
+
+ token = l.lex();
+ if (l.tokenStartLine() == startLine)
+ return removed;
+
+ if (pragmaValue == QLatin1String("library")) {
+ replaceWithSpace(script, startOffset, endOffset - startOffset);
+ removed.append(
+ QQmlJS::AST::SourceLocation(
+ startOffset, endOffset - startOffset,
+ startLine, startColumn));
+ } else
+ return removed;
+#endif
+ }
+ return removed;
+}
+
+QT_END_NAMESPACE
diff --git a/src/qdoc/qmlcodemarker.h b/src/qdoc/qmlcodemarker.h
new file mode 100644
index 000000000..96353ebe5
--- /dev/null
+++ b/src/qdoc/qmlcodemarker.h
@@ -0,0 +1,79 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL21$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** 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 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** As a special exception, The Qt Company gives you certain additional
+** rights. These rights are described in The Qt Company LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*
+ qmlcodemarker.h
+*/
+
+#ifndef QMLCODEMARKER_H
+#define QMLCODEMARKER_H
+
+#include "cppcodemarker.h"
+
+#include <private/qqmljsastfwd_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QmlCodeMarker : public CppCodeMarker
+{
+ Q_DECLARE_TR_FUNCTIONS(QDoc::QmlCodeMarker)
+
+public:
+ QmlCodeMarker();
+ ~QmlCodeMarker();
+
+ virtual bool recognizeCode(const QString &code) Q_DECL_OVERRIDE;
+ virtual bool recognizeExtension(const QString &ext) Q_DECL_OVERRIDE;
+ virtual bool recognizeLanguage(const QString &language) Q_DECL_OVERRIDE;
+ virtual Atom::AtomType atomType() const Q_DECL_OVERRIDE;
+ virtual QString markedUpCode(const QString &code,
+ const Node *relative,
+ const Location &location) Q_DECL_OVERRIDE;
+
+ virtual QString markedUpName(const Node *node) Q_DECL_OVERRIDE;
+ virtual QString markedUpFullName(const Node *node, const Node *relative) Q_DECL_OVERRIDE;
+ virtual QString markedUpIncludes(const QStringList &includes) Q_DECL_OVERRIDE;
+ virtual QString functionBeginRegExp(const QString &funcName) Q_DECL_OVERRIDE;
+ virtual QString functionEndRegExp(const QString &funcName) Q_DECL_OVERRIDE;
+
+ /* Copied from src/declarative/qml/qdeclarativescriptparser.cpp */
+ QList<QQmlJS::AST::SourceLocation> extractPragmas(QString &script);
+
+private:
+ QString addMarkUp(const QString &code, const Node *relative,
+ const Location &location);
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/qdoc/qmlcodeparser.cpp b/src/qdoc/qmlcodeparser.cpp
new file mode 100644
index 000000000..e94df4753
--- /dev/null
+++ b/src/qdoc/qmlcodeparser.cpp
@@ -0,0 +1,333 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL21$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** 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 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** As a special exception, The Qt Company gives you certain additional
+** rights. These rights are described in The Qt Company LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*
+ qmlcodeparser.cpp
+*/
+
+#include "qmlcodeparser.h"
+
+#include "node.h"
+#include "config.h"
+#include "qmlvisitor.h"
+
+#include <private/qqmljsast_p.h>
+#include <private/qqmljsastvisitor_p.h>
+#include <qdebug.h>
+
+QT_BEGIN_NAMESPACE
+
+#define COMMAND_STARTPAGE Doc::alias("startpage")
+#define COMMAND_VARIABLE Doc::alias("variable")
+
+#define COMMAND_DEPRECATED Doc::alias("deprecated")
+#define COMMAND_INGROUP Doc::alias("ingroup")
+#define COMMAND_INTERNAL Doc::alias("internal")
+#define COMMAND_OBSOLETE Doc::alias("obsolete")
+#define COMMAND_PAGEKEYWORDS Doc::alias("pagekeywords")
+#define COMMAND_PRELIMINARY Doc::alias("preliminary")
+#define COMMAND_SINCE Doc::alias("since")
+#define COMMAND_WRAPPER Doc::alias("wrapper")
+#define COMMAND_NOAUTOLIST Doc::alias("noautolist")
+
+#define COMMAND_ABSTRACT Doc::alias("abstract")
+#define COMMAND_QMLABSTRACT Doc::alias("qmlabstract")
+#define COMMAND_QMLCLASS Doc::alias("qmlclass")
+#define COMMAND_QMLTYPE Doc::alias("qmltype")
+#define COMMAND_QMLMODULE Doc::alias("qmlmodule")
+#define COMMAND_QMLPROPERTY Doc::alias("qmlproperty")
+#define COMMAND_QMLPROPERTYGROUP Doc::alias("qmlpropertygroup")
+#define COMMAND_QMLATTACHEDPROPERTY Doc::alias("qmlattachedproperty")
+#define COMMAND_QMLINHERITS Doc::alias("inherits")
+#define COMMAND_QMLINSTANTIATES Doc::alias("instantiates")
+#define COMMAND_INQMLMODULE Doc::alias("inqmlmodule")
+#define COMMAND_QMLSIGNAL Doc::alias("qmlsignal")
+#define COMMAND_QMLATTACHEDSIGNAL Doc::alias("qmlattachedsignal")
+#define COMMAND_QMLMETHOD Doc::alias("qmlmethod")
+#define COMMAND_QMLATTACHEDMETHOD Doc::alias("qmlattachedmethod")
+#define COMMAND_QMLDEFAULT Doc::alias("default")
+#define COMMAND_QMLREADONLY Doc::alias("readonly")
+#define COMMAND_QMLBASICTYPE Doc::alias("qmlbasictype")
+#define COMMAND_QMLMODULE Doc::alias("qmlmodule")
+
+#define COMMAND_JSTYPE Doc::alias("jstype")
+#define COMMAND_JSMODULE Doc::alias("jsmodule")
+#define COMMAND_JSPROPERTY Doc::alias("jsproperty")
+#define COMMAND_JSPROPERTYGROUP Doc::alias("jspropertygroup")
+#define COMMAND_JSATTACHEDPROPERTY Doc::alias("jsattachedproperty")
+#define COMMAND_INJSMODULE Doc::alias("injsmodule")
+#define COMMAND_JSSIGNAL Doc::alias("jssignal")
+#define COMMAND_JSATTACHEDSIGNAL Doc::alias("jsattachedsignal")
+#define COMMAND_JSMETHOD Doc::alias("jsmethod")
+#define COMMAND_JSATTACHEDMETHOD Doc::alias("jsattachedmethod")
+#define COMMAND_JSBASICTYPE Doc::alias("jsbasictype")
+#define COMMAND_JSMODULE Doc::alias("jsmodule")
+
+/*!
+ Constructs the QML code parser.
+ */
+QmlCodeParser::QmlCodeParser()
+ : lexer( 0 ),
+ parser( 0 )
+{
+}
+
+/*!
+ Destroys the QML code parser.
+ */
+QmlCodeParser::~QmlCodeParser()
+{
+}
+
+/*!
+ Initializes the code parser base class. The \a config argument
+ is passed to the initialization functions in the base class.
+
+ Also creates a lexer and parser from QQmlJS.
+ */
+void QmlCodeParser::initializeParser(const Config &config)
+{
+ CodeParser::initializeParser(config);
+
+ lexer = new QQmlJS::Lexer(&engine);
+ parser = new QQmlJS::Parser(&engine);
+}
+
+/*!
+ Terminates the QML code parser. Deletes the lexer and parser
+ created by the constructor.
+ */
+void QmlCodeParser::terminateParser()
+{
+ delete lexer;
+ delete parser;
+}
+
+/*!
+ Returns "QML".
+ */
+QString QmlCodeParser::language()
+{
+ return "QML";
+}
+
+/*!
+ Returns a string list containing "*.qml". This is the only
+ file type parsed by the QMLN parser.
+ */
+QStringList QmlCodeParser::sourceFileNameFilter()
+{
+ return QStringList() << "*.qml";
+}
+
+/*!
+ Parses the source file at \a filePath and inserts the contents
+ into the database. The \a location is used for error reporting.
+
+ If it can't open the file at \a filePath, it reports an error
+ and returns without doing anything.
+ */
+void QmlCodeParser::parseSourceFile(const Location& location, const QString& filePath)
+{
+ QFile in(filePath);
+ currentFile_ = filePath;
+ if (!in.open(QIODevice::ReadOnly)) {
+ location.error(tr("Cannot open QML file '%1'").arg(filePath));
+ currentFile_.clear();
+ return;
+ }
+
+ QString document = in.readAll();
+ in.close();
+
+ Location fileLocation(filePath);
+
+ QString newCode = document;
+ extractPragmas(newCode);
+ lexer->setCode(newCode, 1);
+
+ const QSet<QString>& topicCommandsAllowed = topicCommands();
+ const QSet<QString>& otherMetacommandsAllowed = otherMetaCommands();
+ const QSet<QString>& metacommandsAllowed = topicCommandsAllowed + otherMetacommandsAllowed;
+
+ if (parser->parse()) {
+ QQmlJS::AST::UiProgram *ast = parser->ast();
+ QmlDocVisitor visitor(filePath,
+ newCode,
+ &engine,
+ metacommandsAllowed,
+ topicCommandsAllowed);
+ QQmlJS::AST::Node::accept(ast, &visitor);
+ }
+ foreach (const QQmlJS::DiagnosticMessage &msg, parser->diagnosticMessages()) {
+ qDebug().nospace() << qPrintable(filePath) << ':' << msg.loc.startLine
+ << ": QML syntax error at col " << msg.loc.startColumn
+ << ": " << qPrintable(msg.message);
+ }
+ currentFile_.clear();
+}
+
+/*!
+ Performs cleanup after qdoc is done parsing all the QML files.
+ Currently, no cleanup is required.
+ */
+void QmlCodeParser::doneParsingSourceFiles()
+{
+}
+
+static QSet<QString> topicCommands_;
+/*!
+ Returns the set of strings representing the topic commands.
+ */
+const QSet<QString>& QmlCodeParser::topicCommands()
+{
+ if (topicCommands_.isEmpty()) {
+ topicCommands_ << COMMAND_VARIABLE
+ << COMMAND_QMLCLASS
+ << COMMAND_QMLTYPE
+ << COMMAND_QMLPROPERTY
+ << COMMAND_QMLPROPERTYGROUP
+ << COMMAND_QMLATTACHEDPROPERTY
+ << COMMAND_QMLSIGNAL
+ << COMMAND_QMLATTACHEDSIGNAL
+ << COMMAND_QMLMETHOD
+ << COMMAND_QMLATTACHEDMETHOD
+ << COMMAND_QMLBASICTYPE
+ << COMMAND_JSTYPE
+ << COMMAND_JSPROPERTY
+ << COMMAND_JSPROPERTYGROUP
+ << COMMAND_JSATTACHEDPROPERTY
+ << COMMAND_JSSIGNAL
+ << COMMAND_JSATTACHEDSIGNAL
+ << COMMAND_JSMETHOD
+ << COMMAND_JSATTACHEDMETHOD
+ << COMMAND_JSBASICTYPE;
+ }
+ return topicCommands_;
+}
+
+static QSet<QString> otherMetaCommands_;
+/*!
+ Returns the set of strings representing the common metacommands
+ plus some other metacommands.
+ */
+const QSet<QString>& QmlCodeParser::otherMetaCommands()
+{
+ if (otherMetaCommands_.isEmpty()) {
+ otherMetaCommands_ = commonMetaCommands();
+ otherMetaCommands_ << COMMAND_STARTPAGE
+ << COMMAND_QMLINHERITS
+ << COMMAND_QMLDEFAULT
+ << COMMAND_QMLREADONLY
+ << COMMAND_DEPRECATED
+ << COMMAND_INGROUP
+ << COMMAND_INTERNAL
+ << COMMAND_OBSOLETE
+ << COMMAND_PRELIMINARY
+ << COMMAND_SINCE
+ << COMMAND_ABSTRACT
+ << COMMAND_QMLABSTRACT
+ << COMMAND_INQMLMODULE
+ << COMMAND_INJSMODULE
+ << COMMAND_WRAPPER
+ << COMMAND_NOAUTOLIST;
+ }
+ return otherMetaCommands_;
+}
+
+/*!
+ Copy and paste from src/declarative/qml/qdeclarativescriptparser.cpp.
+ This function blanks out the section of the \a str beginning at \a idx
+ and running for \a n characters.
+*/
+static void replaceWithSpace(QString &str, int idx, int n)
+{
+ QChar *data = str.data() + idx;
+ const QChar space(QLatin1Char(' '));
+ for (int ii = 0; ii < n; ++ii)
+ *data++ = space;
+}
+
+/*!
+ Copy & paste from src/declarative/qml/qdeclarativescriptparser.cpp,
+ then modified to return no values.
+
+ Searches for ".pragma <value>" declarations within \a script.
+ Currently supported pragmas are: library
+*/
+void QmlCodeParser::extractPragmas(QString &script)
+{
+ const QString pragma(QLatin1String("pragma"));
+ const QString library(QLatin1String("library"));
+
+ QQmlJS::Lexer l(0);
+ l.setCode(script, 0);
+
+ int token = l.lex();
+
+ while (true) {
+ if (token != QQmlJSGrammar::T_DOT)
+ return;
+
+ int startOffset = l.tokenOffset();
+ int startLine = l.tokenStartLine();
+
+ token = l.lex();
+
+ if (token != QQmlJSGrammar::T_IDENTIFIER ||
+ l.tokenStartLine() != startLine ||
+ script.mid(l.tokenOffset(), l.tokenLength()) != pragma)
+ return;
+
+ token = l.lex();
+
+ if (token != QQmlJSGrammar::T_IDENTIFIER ||
+ l.tokenStartLine() != startLine)
+ return;
+
+ QString pragmaValue = script.mid(l.tokenOffset(), l.tokenLength());
+ int endOffset = l.tokenLength() + l.tokenOffset();
+
+ token = l.lex();
+ if (l.tokenStartLine() == startLine)
+ return;
+
+ if (pragmaValue == QLatin1String("library"))
+ replaceWithSpace(script, startOffset, endOffset - startOffset);
+ else
+ return;
+ }
+ return;
+}
+
+QT_END_NAMESPACE
diff --git a/src/qdoc/qmlcodeparser.h b/src/qdoc/qmlcodeparser.h
new file mode 100644
index 000000000..8b5667532
--- /dev/null
+++ b/src/qdoc/qmlcodeparser.h
@@ -0,0 +1,84 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL21$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** 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 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** As a special exception, The Qt Company gives you certain additional
+** rights. These rights are described in The Qt Company LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*
+ qmlcodeparser.h
+*/
+
+#ifndef QMLCODEPARSER_H
+#define QMLCODEPARSER_H
+
+#include "codeparser.h"
+
+#include <qset.h>
+#include <private/qqmljsengine_p.h>
+#include <private/qqmljslexer_p.h>
+#include <private/qqmljsparser_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class Config;
+class Node;
+class QString;
+
+class QmlCodeParser : public CodeParser
+{
+ Q_DECLARE_TR_FUNCTIONS(QDoc::QmlCodeParser)
+
+public:
+ QmlCodeParser();
+ virtual ~QmlCodeParser();
+
+ virtual void initializeParser(const Config& config) Q_DECL_OVERRIDE;
+ virtual void terminateParser() Q_DECL_OVERRIDE;
+ virtual QString language() Q_DECL_OVERRIDE;
+ virtual QStringList sourceFileNameFilter() Q_DECL_OVERRIDE;
+ virtual void parseSourceFile(const Location& location, const QString& filePath) Q_DECL_OVERRIDE;
+ virtual void doneParsingSourceFiles() Q_DECL_OVERRIDE;
+
+ /* Copied from src/declarative/qml/qdeclarativescriptparser.cpp */
+ void extractPragmas(QString &script);
+
+protected:
+ const QSet<QString>& topicCommands();
+ const QSet<QString>& otherMetaCommands();
+
+private:
+ QQmlJS::Engine engine;
+ QQmlJS::Lexer *lexer;
+ QQmlJS::Parser *parser;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/qdoc/qmlmarkupvisitor.cpp b/src/qdoc/qmlmarkupvisitor.cpp
new file mode 100644
index 000000000..17dd99325
--- /dev/null
+++ b/src/qdoc/qmlmarkupvisitor.cpp
@@ -0,0 +1,844 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL21$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** 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 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** As a special exception, The Qt Company gives you certain additional
+** rights. These rights are described in The Qt Company LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qmlmarkupvisitor.h"
+
+#include <qstringlist.h>
+#include <qglobal.h>
+#include <private/qqmljsast_p.h>
+#include <private/qqmljsastfwd_p.h>
+#include <private/qqmljsengine_p.h>
+
+QT_BEGIN_NAMESPACE
+
+QmlMarkupVisitor::QmlMarkupVisitor(const QString &source,
+ const QList<QQmlJS::AST::SourceLocation> &pragmas,
+ QQmlJS::Engine *engine)
+{
+ this->source = source;
+ this->engine = engine;
+
+ cursor = 0;
+ extraIndex = 0;
+
+ // Merge the lists of locations of pragmas and comments in the source code.
+ int i = 0;
+ int j = 0;
+ const QList<QQmlJS::AST::SourceLocation> comments = engine->comments();
+ while (i < comments.size() && j < pragmas.length()) {
+ if (comments[i].offset < pragmas[j].offset) {
+ extraTypes.append(Comment);
+ extraLocations.append(comments[i]);
+ ++i;
+ } else {
+ extraTypes.append(Pragma);
+ extraLocations.append(comments[j]);
+ ++j;
+ }
+ }
+
+ while (i < comments.size()) {
+ extraTypes.append(Comment);
+ extraLocations.append(comments[i]);
+ ++i;
+ }
+
+ while (j < pragmas.length()) {
+ extraTypes.append(Pragma);
+ extraLocations.append(pragmas[j]);
+ ++j;
+ }
+}
+
+QmlMarkupVisitor::~QmlMarkupVisitor()
+{
+}
+
+// The protect() function is a copy of the one from CppCodeMarker.
+
+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 QmlMarkupVisitor::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 QmlMarkupVisitor::markedUpCode()
+{
+ if (int(cursor) < source.length())
+ addExtra(cursor, source.length());
+
+ return output;
+}
+
+void QmlMarkupVisitor::addExtra(quint32 start, quint32 finish)
+{
+ if (extraIndex >= extraLocations.length()) {
+ QString extra = source.mid(start, finish - start);
+ if (extra.trimmed().isEmpty())
+ output += extra;
+ else
+ output += protect(extra); // text that should probably have been caught by the parser
+
+ cursor = finish;
+ return;
+ }
+
+ while (extraIndex < extraLocations.length()) {
+ if (extraTypes[extraIndex] == Comment) {
+ if (extraLocations[extraIndex].offset - 2 >= start)
+ break;
+ } else {
+ if (extraLocations[extraIndex].offset >= start)
+ break;
+ }
+ extraIndex++;
+ }
+
+ quint32 i = start;
+ while (i < finish && extraIndex < extraLocations.length()) {
+ quint32 j = extraLocations[extraIndex].offset - 2;
+ if (i <= j && j < finish) {
+ if (i < j)
+ output += protect(source.mid(i, j - i));
+
+ quint32 l = extraLocations[extraIndex].length;
+ if (extraTypes[extraIndex] == Comment) {
+ if (source.mid(j, 2) == QLatin1String("/*"))
+ l += 4;
+ else
+ l += 2;
+ output += QLatin1String("<@comment>");
+ output += protect(source.mid(j, l));
+ output += QLatin1String("</@comment>");
+ } else
+ output += protect(source.mid(j, l));
+
+ extraIndex++;
+ i = j + l;
+ } else
+ break;
+ }
+
+ QString extra = source.mid(i, finish - i);
+ if (extra.trimmed().isEmpty())
+ output += extra;
+ else
+ output += protect(extra); // text that should probably have been caught by the parser
+
+ cursor = finish;
+}
+
+void QmlMarkupVisitor::addMarkedUpToken(
+ QQmlJS::AST::SourceLocation &location, const QString &tagName,
+ const QHash<QString, QString> &attributes)
+{
+ if (!location.isValid())
+ return;
+
+ if (cursor < location.offset)
+ addExtra(cursor, location.offset);
+ else if (cursor > location.offset)
+ return;
+
+ output += QString(QLatin1String("<@%1")).arg(tagName);
+ foreach (const QString &key, attributes)
+ output += QString(QLatin1String(" %1=\"%2\"")).arg(key).arg(attributes[key]);
+ output += QString(QLatin1String(">%2</@%3>")).arg(protect(sourceText(location)), tagName);
+ cursor += location.length;
+}
+
+QString QmlMarkupVisitor::sourceText(QQmlJS::AST::SourceLocation &location)
+{
+ return source.mid(location.offset, location.length);
+}
+
+void QmlMarkupVisitor::addVerbatim(QQmlJS::AST::SourceLocation first,
+ QQmlJS::AST::SourceLocation last)
+{
+ if (!first.isValid())
+ return;
+
+ quint32 start = first.begin();
+ quint32 finish;
+ if (last.isValid())
+ finish = last.end();
+ else
+ finish = first.end();
+
+ if (cursor < start)
+ addExtra(cursor, start);
+ else if (cursor > start)
+ return;
+
+ QString text = source.mid(start, finish - start);
+ output += protect(text);
+ cursor = finish;
+}
+
+bool QmlMarkupVisitor::visit(QQmlJS::AST::UiImport *uiimport)
+{
+ addVerbatim(uiimport->importToken);
+ if (!uiimport->importUri)
+ addMarkedUpToken(uiimport->fileNameToken, QLatin1String("headerfile"));
+ return false;
+}
+
+void QmlMarkupVisitor::endVisit(QQmlJS::AST::UiImport *uiimport)
+{
+ addVerbatim(uiimport->versionToken);
+ addVerbatim(uiimport->asToken);
+ addMarkedUpToken(uiimport->importIdToken, QLatin1String("headerfile"));
+ addVerbatim(uiimport->semicolonToken);
+}
+
+bool QmlMarkupVisitor::visit(QQmlJS::AST::UiPublicMember *member)
+{
+ if (member->type == QQmlJS::AST::UiPublicMember::Property) {
+ addVerbatim(member->defaultToken);
+ addVerbatim(member->readonlyToken);
+ addVerbatim(member->propertyToken);
+ addVerbatim(member->typeModifierToken);
+ addMarkedUpToken(member->typeToken, QLatin1String("type"));
+ addMarkedUpToken(member->identifierToken, QLatin1String("name"));
+ addVerbatim(member->colonToken);
+ if (member->binding)
+ QQmlJS::AST::Node::accept(member->binding, this);
+ else if (member->statement)
+ QQmlJS::AST::Node::accept(member->statement, this);
+ } else {
+ addVerbatim(member->propertyToken);
+ addVerbatim(member->typeModifierToken);
+ addMarkedUpToken(member->typeToken, QLatin1String("type"));
+ //addVerbatim(member->identifierToken);
+ QQmlJS::AST::Node::accept(member->parameters, this);
+ }
+ addVerbatim(member->semicolonToken);
+ return false;
+}
+
+bool QmlMarkupVisitor::visit(QQmlJS::AST::UiObjectInitializer *initializer)
+{
+ addVerbatim(initializer->lbraceToken, initializer->lbraceToken);
+ return true;
+}
+
+void QmlMarkupVisitor::endVisit(QQmlJS::AST::UiObjectInitializer *initializer)
+{
+ addVerbatim(initializer->rbraceToken, initializer->rbraceToken);
+}
+
+bool QmlMarkupVisitor::visit(QQmlJS::AST::UiObjectBinding *binding)
+{
+ QQmlJS::AST::Node::accept(binding->qualifiedId, this);
+ addVerbatim(binding->colonToken);
+ QQmlJS::AST::Node::accept(binding->qualifiedTypeNameId, this);
+ QQmlJS::AST::Node::accept(binding->initializer, this);
+ return false;
+}
+
+bool QmlMarkupVisitor::visit(QQmlJS::AST::UiScriptBinding *binding)
+{
+ QQmlJS::AST::Node::accept(binding->qualifiedId, this);
+ addVerbatim(binding->colonToken);
+ QQmlJS::AST::Node::accept(binding->statement, this);
+ return false;
+}
+
+bool QmlMarkupVisitor::visit(QQmlJS::AST::UiArrayBinding *binding)
+{
+ QQmlJS::AST::Node::accept(binding->qualifiedId, this);
+ addVerbatim(binding->colonToken);
+ addVerbatim(binding->lbracketToken);
+ QQmlJS::AST::Node::accept(binding->members, this);
+ addVerbatim(binding->rbracketToken);
+ return false;
+}
+
+bool QmlMarkupVisitor::visit(QQmlJS::AST::UiArrayMemberList *list)
+{
+ for (QQmlJS::AST::UiArrayMemberList *it = list; it; it = it->next) {
+ QQmlJS::AST::Node::accept(it->member, this);
+ //addVerbatim(it->commaToken);
+ }
+ return false;
+}
+
+bool QmlMarkupVisitor::visit(QQmlJS::AST::UiQualifiedId *id)
+{
+ addMarkedUpToken(id->identifierToken, QLatin1String("name"));
+ return false;
+}
+
+bool QmlMarkupVisitor::visit(QQmlJS::AST::ThisExpression *expression)
+{
+ addVerbatim(expression->thisToken);
+ return true;
+}
+
+bool QmlMarkupVisitor::visit(QQmlJS::AST::IdentifierExpression *identifier)
+{
+ addMarkedUpToken(identifier->identifierToken, QLatin1String("name"));
+ return false;
+}
+
+bool QmlMarkupVisitor::visit(QQmlJS::AST::NullExpression *null)
+{
+ addMarkedUpToken(null->nullToken, QLatin1String("number"));
+ return true;
+}
+
+bool QmlMarkupVisitor::visit(QQmlJS::AST::TrueLiteral *literal)
+{
+ addMarkedUpToken(literal->trueToken, QLatin1String("number"));
+ return true;
+}
+
+bool QmlMarkupVisitor::visit(QQmlJS::AST::FalseLiteral *literal)
+{
+ addMarkedUpToken(literal->falseToken, QLatin1String("number"));
+ return true;
+}
+
+bool QmlMarkupVisitor::visit(QQmlJS::AST::NumericLiteral *literal)
+{
+ addMarkedUpToken(literal->literalToken, QLatin1String("number"));
+ return false;
+}
+
+bool QmlMarkupVisitor::visit(QQmlJS::AST::StringLiteral *literal)
+{
+ addMarkedUpToken(literal->literalToken, QLatin1String("string"));
+ return true;
+}
+
+bool QmlMarkupVisitor::visit(QQmlJS::AST::RegExpLiteral *literal)
+{
+ addVerbatim(literal->literalToken);
+ return true;
+}
+
+bool QmlMarkupVisitor::visit(QQmlJS::AST::ArrayLiteral *literal)
+{
+ addVerbatim(literal->lbracketToken);
+ QQmlJS::AST::Node::accept(literal->elements, this);
+ addVerbatim(literal->rbracketToken);
+ return false;
+}
+
+bool QmlMarkupVisitor::visit(QQmlJS::AST::ObjectLiteral *literal)
+{
+ addVerbatim(literal->lbraceToken);
+ return true;
+}
+
+void QmlMarkupVisitor::endVisit(QQmlJS::AST::ObjectLiteral *literal)
+{
+ addVerbatim(literal->rbraceToken);
+}
+
+
+bool QmlMarkupVisitor::visit(QQmlJS::AST::ElementList *list)
+{
+ for (QQmlJS::AST::ElementList *it = list; it; it = it->next) {
+ QQmlJS::AST::Node::accept(it->expression, this);
+ //addVerbatim(it->commaToken);
+ }
+ QQmlJS::AST::Node::accept(list->elision, this);
+ return false;
+}
+
+bool QmlMarkupVisitor::visit(QQmlJS::AST::Elision *elision)
+{
+ addVerbatim(elision->commaToken, elision->commaToken);
+ return true;
+}
+
+bool QmlMarkupVisitor::visit(QQmlJS::AST::PropertyNameAndValue *list)
+{
+ QQmlJS::AST::Node::accept(list->name, this);
+ addVerbatim(list->colonToken, list->colonToken);
+ QQmlJS::AST::Node::accept(list->value, this);
+ addVerbatim(list->commaToken, list->commaToken);
+ return false;
+}
+
+bool QmlMarkupVisitor::visit(QQmlJS::AST::ArrayMemberExpression *expression)
+{
+ QQmlJS::AST::Node::accept(expression->base, this);
+ addVerbatim(expression->lbracketToken);
+ QQmlJS::AST::Node::accept(expression->expression, this);
+ addVerbatim(expression->rbracketToken);
+ return false;
+}
+
+bool QmlMarkupVisitor::visit(QQmlJS::AST::FieldMemberExpression *expression)
+{
+ QQmlJS::AST::Node::accept(expression->base, this);
+ addVerbatim(expression->dotToken);
+ addMarkedUpToken(expression->identifierToken, QLatin1String("name"));
+ return false;
+}
+
+bool QmlMarkupVisitor::visit(QQmlJS::AST::NewMemberExpression *expression)
+{
+ addVerbatim(expression->newToken);
+ QQmlJS::AST::Node::accept(expression->base, this);
+ addVerbatim(expression->lparenToken);
+ QQmlJS::AST::Node::accept(expression->arguments, this);
+ addVerbatim(expression->rparenToken);
+ return false;
+}
+
+bool QmlMarkupVisitor::visit(QQmlJS::AST::NewExpression *expression)
+{
+ addVerbatim(expression->newToken);
+ return true;
+}
+
+bool QmlMarkupVisitor::visit(QQmlJS::AST::ArgumentList *list)
+{
+ addVerbatim(list->commaToken, list->commaToken);
+ return true;
+}
+
+bool QmlMarkupVisitor::visit(QQmlJS::AST::PostIncrementExpression *expression)
+{
+ addVerbatim(expression->incrementToken);
+ return true;
+}
+
+bool QmlMarkupVisitor::visit(QQmlJS::AST::PostDecrementExpression *expression)
+{
+ addVerbatim(expression->decrementToken);
+ return true;
+}
+
+bool QmlMarkupVisitor::visit(QQmlJS::AST::DeleteExpression *expression)
+{
+ addVerbatim(expression->deleteToken);
+ return true;
+}
+
+bool QmlMarkupVisitor::visit(QQmlJS::AST::VoidExpression *expression)
+{
+ addVerbatim(expression->voidToken);
+ return true;
+}
+
+bool QmlMarkupVisitor::visit(QQmlJS::AST::TypeOfExpression *expression)
+{
+ addVerbatim(expression->typeofToken);
+ return true;
+}
+
+bool QmlMarkupVisitor::visit(QQmlJS::AST::PreIncrementExpression *expression)
+{
+ addVerbatim(expression->incrementToken);
+ return true;
+}
+
+bool QmlMarkupVisitor::visit(QQmlJS::AST::PreDecrementExpression *expression)
+{
+ addVerbatim(expression->decrementToken);
+ return true;
+}
+
+bool QmlMarkupVisitor::visit(QQmlJS::AST::UnaryPlusExpression *expression)
+{
+ addVerbatim(expression->plusToken);
+ return true;
+}
+
+bool QmlMarkupVisitor::visit(QQmlJS::AST::UnaryMinusExpression *expression)
+{
+ addVerbatim(expression->minusToken);
+ return true;
+}
+
+bool QmlMarkupVisitor::visit(QQmlJS::AST::TildeExpression *expression)
+{
+ addVerbatim(expression->tildeToken);
+ return true;
+}
+
+bool QmlMarkupVisitor::visit(QQmlJS::AST::NotExpression *expression)
+{
+ addVerbatim(expression->notToken);
+ return true;
+}
+
+bool QmlMarkupVisitor::visit(QQmlJS::AST::BinaryExpression *expression)
+{
+ QQmlJS::AST::Node::accept(expression->left, this);
+ addMarkedUpToken(expression->operatorToken, QLatin1String("op"));
+ QQmlJS::AST::Node::accept(expression->right, this);
+ return false;
+}
+
+bool QmlMarkupVisitor::visit(QQmlJS::AST::ConditionalExpression *expression)
+{
+ QQmlJS::AST::Node::accept(expression->expression, this);
+ addVerbatim(expression->questionToken);
+ QQmlJS::AST::Node::accept(expression->ok, this);
+ addVerbatim(expression->colonToken);
+ QQmlJS::AST::Node::accept(expression->ko, this);
+ return false;
+}
+
+bool QmlMarkupVisitor::visit(QQmlJS::AST::Expression *expression)
+{
+ QQmlJS::AST::Node::accept(expression->left, this);
+ addVerbatim(expression->commaToken);
+ QQmlJS::AST::Node::accept(expression->right, this);
+ return false;
+}
+
+bool QmlMarkupVisitor::visit(QQmlJS::AST::Block *block)
+{
+ addVerbatim(block->lbraceToken);
+ return true;
+}
+
+void QmlMarkupVisitor::endVisit(QQmlJS::AST::Block *block)
+{
+ addVerbatim(block->rbraceToken);
+}
+
+bool QmlMarkupVisitor::visit(QQmlJS::AST::VariableStatement *statement)
+{
+ addVerbatim(statement->declarationKindToken);
+ QQmlJS::AST::Node::accept(statement->declarations, this);
+ addVerbatim(statement->semicolonToken);
+ return false;
+}
+
+bool QmlMarkupVisitor::visit(QQmlJS::AST::VariableDeclarationList *list)
+{
+ for (QQmlJS::AST::VariableDeclarationList *it = list; it; it = it->next) {
+ QQmlJS::AST::Node::accept(it->declaration, this);
+ addVerbatim(it->commaToken);
+ }
+ return false;
+}
+
+bool QmlMarkupVisitor::visit(QQmlJS::AST::VariableDeclaration *declaration)
+{
+ addMarkedUpToken(declaration->identifierToken, QLatin1String("name"));
+ QQmlJS::AST::Node::accept(declaration->expression, this);
+ return false;
+}
+
+bool QmlMarkupVisitor::visit(QQmlJS::AST::EmptyStatement *statement)
+{
+ addVerbatim(statement->semicolonToken);
+ return true;
+}
+
+bool QmlMarkupVisitor::visit(QQmlJS::AST::ExpressionStatement *statement)
+{
+ QQmlJS::AST::Node::accept(statement->expression, this);
+ addVerbatim(statement->semicolonToken);
+ return false;
+}
+
+bool QmlMarkupVisitor::visit(QQmlJS::AST::IfStatement *statement)
+{
+ addMarkedUpToken(statement->ifToken, QLatin1String("keyword"));
+ addVerbatim(statement->lparenToken);
+ QQmlJS::AST::Node::accept(statement->expression, this);
+ addVerbatim(statement->rparenToken);
+ QQmlJS::AST::Node::accept(statement->ok, this);
+ if (statement->ko) {
+ addMarkedUpToken(statement->elseToken, QLatin1String("keyword"));
+ QQmlJS::AST::Node::accept(statement->ko, this);
+ }
+ return false;
+}
+
+bool QmlMarkupVisitor::visit(QQmlJS::AST::DoWhileStatement *statement)
+{
+ addMarkedUpToken(statement->doToken, QLatin1String("keyword"));
+ QQmlJS::AST::Node::accept(statement->statement, this);
+ addMarkedUpToken(statement->whileToken, QLatin1String("keyword"));
+ addVerbatim(statement->lparenToken);
+ QQmlJS::AST::Node::accept(statement->expression, this);
+ addVerbatim(statement->rparenToken);
+ addVerbatim(statement->semicolonToken);
+ return false;
+}
+
+bool QmlMarkupVisitor::visit(QQmlJS::AST::WhileStatement *statement)
+{
+ addMarkedUpToken(statement->whileToken, QLatin1String("keyword"));
+ addVerbatim(statement->lparenToken);
+ QQmlJS::AST::Node::accept(statement->expression, this);
+ addVerbatim(statement->rparenToken);
+ QQmlJS::AST::Node::accept(statement->statement, this);
+ return false;
+}
+
+bool QmlMarkupVisitor::visit(QQmlJS::AST::ForStatement *statement)
+{
+ addMarkedUpToken(statement->forToken, QLatin1String("keyword"));
+ addVerbatim(statement->lparenToken);
+ QQmlJS::AST::Node::accept(statement->initialiser, this);
+ addVerbatim(statement->firstSemicolonToken);
+ QQmlJS::AST::Node::accept(statement->condition, this);
+ addVerbatim(statement->secondSemicolonToken);
+ QQmlJS::AST::Node::accept(statement->expression, this);
+ addVerbatim(statement->rparenToken);
+ QQmlJS::AST::Node::accept(statement->statement, this);
+ return false;
+}
+
+bool QmlMarkupVisitor::visit(QQmlJS::AST::LocalForStatement *statement)
+{
+ addMarkedUpToken(statement->forToken, QLatin1String("keyword"));
+ addVerbatim(statement->lparenToken);
+ addMarkedUpToken(statement->varToken, QLatin1String("keyword"));
+ QQmlJS::AST::Node::accept(statement->declarations, this);
+ addVerbatim(statement->firstSemicolonToken);
+ QQmlJS::AST::Node::accept(statement->condition, this);
+ addVerbatim(statement->secondSemicolonToken);
+ QQmlJS::AST::Node::accept(statement->expression, this);
+ addVerbatim(statement->rparenToken);
+ QQmlJS::AST::Node::accept(statement->statement, this);
+ return false;
+}
+
+bool QmlMarkupVisitor::visit(QQmlJS::AST::ForEachStatement *statement)
+{
+ addMarkedUpToken(statement->forToken, QLatin1String("keyword"));
+ addVerbatim(statement->lparenToken);
+ QQmlJS::AST::Node::accept(statement->initialiser, this);
+ addVerbatim(statement->inToken);
+ QQmlJS::AST::Node::accept(statement->expression, this);
+ addVerbatim(statement->rparenToken);
+ QQmlJS::AST::Node::accept(statement->statement, this);
+ return false;
+}
+
+bool QmlMarkupVisitor::visit(QQmlJS::AST::LocalForEachStatement *statement)
+{
+ addMarkedUpToken(statement->forToken, QLatin1String("keyword"));
+ addVerbatim(statement->lparenToken);
+ addMarkedUpToken(statement->varToken, QLatin1String("keyword"));
+ QQmlJS::AST::Node::accept(statement->declaration, this);
+ addVerbatim(statement->inToken);
+ QQmlJS::AST::Node::accept(statement->expression, this);
+ addVerbatim(statement->rparenToken);
+ QQmlJS::AST::Node::accept(statement->statement, this);
+ return false;
+}
+
+bool QmlMarkupVisitor::visit(QQmlJS::AST::ContinueStatement *statement)
+{
+ addMarkedUpToken(statement->continueToken, QLatin1String("keyword"));
+ addMarkedUpToken(statement->identifierToken, QLatin1String("name"));
+ addVerbatim(statement->semicolonToken);
+ return false;
+}
+
+bool QmlMarkupVisitor::visit(QQmlJS::AST::BreakStatement *statement)
+{
+ addMarkedUpToken(statement->breakToken, QLatin1String("keyword"));
+ addMarkedUpToken(statement->identifierToken, QLatin1String("name"));
+ addVerbatim(statement->semicolonToken);
+ return false;
+}
+
+bool QmlMarkupVisitor::visit(QQmlJS::AST::ReturnStatement *statement)
+{
+ addMarkedUpToken(statement->returnToken, QLatin1String("keyword"));
+ QQmlJS::AST::Node::accept(statement->expression, this);
+ addVerbatim(statement->semicolonToken);
+ return false;
+}
+
+bool QmlMarkupVisitor::visit(QQmlJS::AST::WithStatement *statement)
+{
+ addMarkedUpToken(statement->withToken, QLatin1String("keyword"));
+ addVerbatim(statement->lparenToken);
+ QQmlJS::AST::Node::accept(statement->expression, this);
+ addVerbatim(statement->rparenToken);
+ QQmlJS::AST::Node::accept(statement->statement, this);
+ return false;
+}
+
+bool QmlMarkupVisitor::visit(QQmlJS::AST::CaseBlock *block)
+{
+ addVerbatim(block->lbraceToken);
+ return true;
+}
+
+void QmlMarkupVisitor::endVisit(QQmlJS::AST::CaseBlock *block)
+{
+ addVerbatim(block->rbraceToken, block->rbraceToken);
+}
+
+
+bool QmlMarkupVisitor::visit(QQmlJS::AST::SwitchStatement *statement)
+{
+ addMarkedUpToken(statement->switchToken, QLatin1String("keyword"));
+ addVerbatim(statement->lparenToken);
+ QQmlJS::AST::Node::accept(statement->expression, this);
+ addVerbatim(statement->rparenToken);
+ QQmlJS::AST::Node::accept(statement->block, this);
+ return false;
+}
+
+bool QmlMarkupVisitor::visit(QQmlJS::AST::CaseClause *clause)
+{
+ addMarkedUpToken(clause->caseToken, QLatin1String("keyword"));
+ QQmlJS::AST::Node::accept(clause->expression, this);
+ addVerbatim(clause->colonToken);
+ QQmlJS::AST::Node::accept(clause->statements, this);
+ return false;
+}
+
+bool QmlMarkupVisitor::visit(QQmlJS::AST::DefaultClause *clause)
+{
+ addMarkedUpToken(clause->defaultToken, QLatin1String("keyword"));
+ addVerbatim(clause->colonToken, clause->colonToken);
+ return true;
+}
+
+bool QmlMarkupVisitor::visit(QQmlJS::AST::LabelledStatement *statement)
+{
+ addMarkedUpToken(statement->identifierToken, QLatin1String("name"));
+ addVerbatim(statement->colonToken);
+ QQmlJS::AST::Node::accept(statement->statement, this);
+ return false;
+}
+
+bool QmlMarkupVisitor::visit(QQmlJS::AST::ThrowStatement *statement)
+{
+ addMarkedUpToken(statement->throwToken, QLatin1String("keyword"));
+ QQmlJS::AST::Node::accept(statement->expression, this);
+ addVerbatim(statement->semicolonToken);
+ return false;
+}
+
+bool QmlMarkupVisitor::visit(QQmlJS::AST::Catch *c)
+{
+ addMarkedUpToken(c->catchToken, QLatin1String("keyword"));
+ addVerbatim(c->lparenToken);
+ addMarkedUpToken(c->identifierToken, QLatin1String("name"));
+ addVerbatim(c->rparenToken);
+ return false;
+}
+
+bool QmlMarkupVisitor::visit(QQmlJS::AST::Finally *f)
+{
+ addMarkedUpToken(f->finallyToken, QLatin1String("keyword"));
+ QQmlJS::AST::Node::accept(f->statement, this);
+ return false;
+}
+
+bool QmlMarkupVisitor::visit(QQmlJS::AST::TryStatement *statement)
+{
+ addMarkedUpToken(statement->tryToken, QLatin1String("keyword"));
+ QQmlJS::AST::Node::accept(statement->statement, this);
+ QQmlJS::AST::Node::accept(statement->catchExpression, this);
+ QQmlJS::AST::Node::accept(statement->finallyExpression, this);
+ return false;
+}
+
+bool QmlMarkupVisitor::visit(QQmlJS::AST::FunctionExpression *expression)
+{
+ addMarkedUpToken(expression->functionToken, QLatin1String("keyword"));
+ addMarkedUpToken(expression->identifierToken, QLatin1String("name"));
+ addVerbatim(expression->lparenToken);
+ QQmlJS::AST::Node::accept(expression->formals, this);
+ addVerbatim(expression->rparenToken);
+ addVerbatim(expression->lbraceToken);
+ QQmlJS::AST::Node::accept(expression->body, this);
+ addVerbatim(expression->rbraceToken);
+ return false;
+}
+
+bool QmlMarkupVisitor::visit(QQmlJS::AST::FunctionDeclaration *declaration)
+{
+ addMarkedUpToken(declaration->functionToken, QLatin1String("keyword"));
+ addMarkedUpToken(declaration->identifierToken, QLatin1String("name"));
+ addVerbatim(declaration->lparenToken);
+ QQmlJS::AST::Node::accept(declaration->formals, this);
+ addVerbatim(declaration->rparenToken);
+ addVerbatim(declaration->lbraceToken);
+ QQmlJS::AST::Node::accept(declaration->body, this);
+ addVerbatim(declaration->rbraceToken);
+ return false;
+}
+
+bool QmlMarkupVisitor::visit(QQmlJS::AST::FormalParameterList *list)
+{
+ addVerbatim(list->commaToken);
+ addMarkedUpToken(list->identifierToken, QLatin1String("name"));
+ return false;
+}
+
+bool QmlMarkupVisitor::visit(QQmlJS::AST::DebuggerStatement *statement)
+{
+ addVerbatim(statement->debuggerToken);
+ addVerbatim(statement->semicolonToken);
+ return true;
+}
+
+// Elements and items are represented by UiObjectDefinition nodes.
+
+bool QmlMarkupVisitor::visit(QQmlJS::AST::UiObjectDefinition *definition)
+{
+ QHash<QString, QString> attributes;
+ addMarkedUpToken(definition->qualifiedTypeNameId->identifierToken, QLatin1String("type"));
+ QQmlJS::AST::Node::accept(definition->initializer, this);
+ return false;
+}
+
+QT_END_NAMESPACE
diff --git a/src/qdoc/qmlmarkupvisitor.h b/src/qdoc/qmlmarkupvisitor.h
new file mode 100644
index 000000000..ab1180fd4
--- /dev/null
+++ b/src/qdoc/qmlmarkupvisitor.h
@@ -0,0 +1,173 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL21$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** 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 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** As a special exception, The Qt Company gives you certain additional
+** rights. These rights are described in The Qt Company LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QMLMARKUPVISITOR_H
+#define QMLMARKUPVISITOR_H
+
+#include "node.h"
+#include "tree.h"
+
+#include <qstring.h>
+#include <private/qqmljsastvisitor_p.h>
+#include <private/qqmljsengine_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QmlMarkupVisitor : public QQmlJS::AST::Visitor
+{
+public:
+ enum ExtraType{
+ Comment,
+ Pragma
+ };
+
+ QmlMarkupVisitor(const QString &code,
+ const QList<QQmlJS::AST::SourceLocation> &pragmas,
+ QQmlJS::Engine *engine);
+ virtual ~QmlMarkupVisitor();
+
+ QString markedUpCode();
+
+ virtual bool visit(QQmlJS::AST::UiImport *) Q_DECL_OVERRIDE;
+ virtual void endVisit(QQmlJS::AST::UiImport *) Q_DECL_OVERRIDE;
+
+ virtual bool visit(QQmlJS::AST::UiPublicMember *) Q_DECL_OVERRIDE;
+ virtual bool visit(QQmlJS::AST::UiObjectDefinition *) Q_DECL_OVERRIDE;
+
+ virtual bool visit(QQmlJS::AST::UiObjectInitializer *) Q_DECL_OVERRIDE;
+ virtual void endVisit(QQmlJS::AST::UiObjectInitializer *) Q_DECL_OVERRIDE;
+
+ virtual bool visit(QQmlJS::AST::UiObjectBinding *) Q_DECL_OVERRIDE;
+ virtual bool visit(QQmlJS::AST::UiScriptBinding *) Q_DECL_OVERRIDE;
+ virtual bool visit(QQmlJS::AST::UiArrayBinding *) Q_DECL_OVERRIDE;
+ virtual bool visit(QQmlJS::AST::UiArrayMemberList *) Q_DECL_OVERRIDE;
+ virtual bool visit(QQmlJS::AST::UiQualifiedId *) Q_DECL_OVERRIDE;
+
+ virtual bool visit(QQmlJS::AST::ThisExpression *) Q_DECL_OVERRIDE;
+ virtual bool visit(QQmlJS::AST::IdentifierExpression *) Q_DECL_OVERRIDE;
+ virtual bool visit(QQmlJS::AST::NullExpression *) Q_DECL_OVERRIDE;
+ virtual bool visit(QQmlJS::AST::TrueLiteral *) Q_DECL_OVERRIDE;
+ virtual bool visit(QQmlJS::AST::FalseLiteral *) Q_DECL_OVERRIDE;
+ virtual bool visit(QQmlJS::AST::NumericLiteral *) Q_DECL_OVERRIDE;
+ virtual bool visit(QQmlJS::AST::StringLiteral *) Q_DECL_OVERRIDE;
+ virtual bool visit(QQmlJS::AST::RegExpLiteral *) Q_DECL_OVERRIDE;
+ virtual bool visit(QQmlJS::AST::ArrayLiteral *) Q_DECL_OVERRIDE;
+
+ virtual bool visit(QQmlJS::AST::ObjectLiteral *) Q_DECL_OVERRIDE;
+ virtual void endVisit(QQmlJS::AST::ObjectLiteral *) Q_DECL_OVERRIDE;
+
+ virtual bool visit(QQmlJS::AST::ElementList *) Q_DECL_OVERRIDE;
+ virtual bool visit(QQmlJS::AST::Elision *) Q_DECL_OVERRIDE;
+ virtual bool visit(QQmlJS::AST::PropertyNameAndValue *) Q_DECL_OVERRIDE;
+ virtual bool visit(QQmlJS::AST::ArrayMemberExpression *) Q_DECL_OVERRIDE;
+ virtual bool visit(QQmlJS::AST::FieldMemberExpression *) Q_DECL_OVERRIDE;
+ virtual bool visit(QQmlJS::AST::NewMemberExpression *) Q_DECL_OVERRIDE;
+ virtual bool visit(QQmlJS::AST::NewExpression *) Q_DECL_OVERRIDE;
+ virtual bool visit(QQmlJS::AST::ArgumentList *) Q_DECL_OVERRIDE;
+ virtual bool visit(QQmlJS::AST::PostIncrementExpression *) Q_DECL_OVERRIDE;
+ virtual bool visit(QQmlJS::AST::PostDecrementExpression *) Q_DECL_OVERRIDE;
+ virtual bool visit(QQmlJS::AST::DeleteExpression *) Q_DECL_OVERRIDE;
+ virtual bool visit(QQmlJS::AST::VoidExpression *) Q_DECL_OVERRIDE;
+ virtual bool visit(QQmlJS::AST::TypeOfExpression *) Q_DECL_OVERRIDE;
+ virtual bool visit(QQmlJS::AST::PreIncrementExpression *) Q_DECL_OVERRIDE;
+ virtual bool visit(QQmlJS::AST::PreDecrementExpression *) Q_DECL_OVERRIDE;
+ virtual bool visit(QQmlJS::AST::UnaryPlusExpression *) Q_DECL_OVERRIDE;
+ virtual bool visit(QQmlJS::AST::UnaryMinusExpression *) Q_DECL_OVERRIDE;
+ virtual bool visit(QQmlJS::AST::TildeExpression *) Q_DECL_OVERRIDE;
+ virtual bool visit(QQmlJS::AST::NotExpression *) Q_DECL_OVERRIDE;
+ virtual bool visit(QQmlJS::AST::BinaryExpression *) Q_DECL_OVERRIDE;
+ virtual bool visit(QQmlJS::AST::ConditionalExpression *) Q_DECL_OVERRIDE;
+ virtual bool visit(QQmlJS::AST::Expression *) Q_DECL_OVERRIDE;
+
+ virtual bool visit(QQmlJS::AST::Block *) Q_DECL_OVERRIDE;
+ virtual void endVisit(QQmlJS::AST::Block *) Q_DECL_OVERRIDE;
+
+ virtual bool visit(QQmlJS::AST::VariableStatement *) Q_DECL_OVERRIDE;
+ virtual bool visit(QQmlJS::AST::VariableDeclarationList *) Q_DECL_OVERRIDE;
+ virtual bool visit(QQmlJS::AST::VariableDeclaration *) Q_DECL_OVERRIDE;
+ virtual bool visit(QQmlJS::AST::EmptyStatement *) Q_DECL_OVERRIDE;
+ virtual bool visit(QQmlJS::AST::ExpressionStatement *) Q_DECL_OVERRIDE;
+ virtual bool visit(QQmlJS::AST::IfStatement *) Q_DECL_OVERRIDE;
+ virtual bool visit(QQmlJS::AST::DoWhileStatement *) Q_DECL_OVERRIDE;
+ virtual bool visit(QQmlJS::AST::WhileStatement *) Q_DECL_OVERRIDE;
+ virtual bool visit(QQmlJS::AST::ForStatement *) Q_DECL_OVERRIDE;
+ virtual bool visit(QQmlJS::AST::LocalForStatement *) Q_DECL_OVERRIDE;
+ virtual bool visit(QQmlJS::AST::ForEachStatement *) Q_DECL_OVERRIDE;
+ virtual bool visit(QQmlJS::AST::LocalForEachStatement *) Q_DECL_OVERRIDE;
+ virtual bool visit(QQmlJS::AST::ContinueStatement *) Q_DECL_OVERRIDE;
+ virtual bool visit(QQmlJS::AST::BreakStatement *) Q_DECL_OVERRIDE;
+ virtual bool visit(QQmlJS::AST::ReturnStatement *) Q_DECL_OVERRIDE;
+ virtual bool visit(QQmlJS::AST::WithStatement *) Q_DECL_OVERRIDE;
+
+ virtual bool visit(QQmlJS::AST::CaseBlock *) Q_DECL_OVERRIDE;
+ virtual void endVisit(QQmlJS::AST::CaseBlock *) Q_DECL_OVERRIDE;
+
+ virtual bool visit(QQmlJS::AST::SwitchStatement *) Q_DECL_OVERRIDE;
+ virtual bool visit(QQmlJS::AST::CaseClause *) Q_DECL_OVERRIDE;
+ virtual bool visit(QQmlJS::AST::DefaultClause *) Q_DECL_OVERRIDE;
+ virtual bool visit(QQmlJS::AST::LabelledStatement *) Q_DECL_OVERRIDE;
+ virtual bool visit(QQmlJS::AST::ThrowStatement *) Q_DECL_OVERRIDE;
+ virtual bool visit(QQmlJS::AST::TryStatement *) Q_DECL_OVERRIDE;
+ virtual bool visit(QQmlJS::AST::Catch *) Q_DECL_OVERRIDE;
+ virtual bool visit(QQmlJS::AST::Finally *) Q_DECL_OVERRIDE;
+ virtual bool visit(QQmlJS::AST::FunctionDeclaration *) Q_DECL_OVERRIDE;
+ virtual bool visit(QQmlJS::AST::FunctionExpression *) Q_DECL_OVERRIDE;
+ virtual bool visit(QQmlJS::AST::FormalParameterList *) Q_DECL_OVERRIDE;
+ virtual bool visit(QQmlJS::AST::DebuggerStatement *) Q_DECL_OVERRIDE;
+
+protected:
+ QString protect(const QString &string);
+
+private:
+ typedef QHash<QString, QString> StringHash;
+ void addExtra(quint32 start, quint32 finish);
+ void addMarkedUpToken(QQmlJS::AST::SourceLocation &location,
+ const QString &text,
+ const StringHash &attributes = StringHash());
+ void addVerbatim(QQmlJS::AST::SourceLocation first,
+ QQmlJS::AST::SourceLocation last = QQmlJS::AST::SourceLocation());
+ QString sourceText(QQmlJS::AST::SourceLocation &location);
+
+ QQmlJS::Engine *engine;
+ QVector<ExtraType> extraTypes;
+ QList<QQmlJS::AST::SourceLocation> extraLocations;
+ QString source;
+ QString output;
+ quint32 cursor;
+ int extraIndex;
+};
+Q_DECLARE_TYPEINFO(QmlMarkupVisitor::ExtraType, Q_PRIMITIVE_TYPE);
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/qdoc/qmlvisitor.cpp b/src/qdoc/qmlvisitor.cpp
new file mode 100644
index 000000000..654922929
--- /dev/null
+++ b/src/qdoc/qmlvisitor.cpp
@@ -0,0 +1,827 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL21$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** 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 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** As a special exception, The Qt Company gives you certain additional
+** rights. These rights are described in The Qt Company LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qmlvisitor.h"
+
+#include "node.h"
+#include "codeparser.h"
+#include "qdocdatabase.h"
+#include "tokenizer.h"
+
+#include <qfileinfo.h>
+#include <qstringlist.h>
+#include <qglobal.h>
+#include <qdebug.h>
+#include <private/qqmljsast_p.h>
+#include <private/qqmljsastfwd_p.h>
+#include <private/qqmljsengine_p.h>
+
+QT_BEGIN_NAMESPACE
+
+#define COMMAND_DEPRECATED Doc::alias(QLatin1String("deprecated"))
+#define COMMAND_INGROUP Doc::alias(QLatin1String("ingroup"))
+#define COMMAND_INTERNAL Doc::alias(QLatin1String("internal"))
+#define COMMAND_OBSOLETE Doc::alias(QLatin1String("obsolete"))
+#define COMMAND_PAGEKEYWORDS Doc::alias(QLatin1String("pagekeywords"))
+#define COMMAND_PRELIMINARY Doc::alias(QLatin1String("preliminary"))
+#define COMMAND_SINCE Doc::alias(QLatin1String("since"))
+#define COMMAND_WRAPPER Doc::alias(QLatin1String("wrapper"))
+#define COMMAND_NOAUTOLIST Doc::alias(QLatin1String("noautolist"))
+
+#define COMMAND_ABSTRACT Doc::alias(QLatin1String("abstract"))
+#define COMMAND_QMLABSTRACT Doc::alias(QLatin1String("qmlabstract"))
+#define COMMAND_QMLCLASS Doc::alias(QLatin1String("qmlclass"))
+#define COMMAND_QMLTYPE Doc::alias(QLatin1String("qmltype"))
+#define COMMAND_QMLMODULE Doc::alias(QLatin1String("qmlmodule"))
+#define COMMAND_QMLPROPERTY Doc::alias(QLatin1String("qmlproperty"))
+#define COMMAND_QMLPROPERTYGROUP Doc::alias(QLatin1String("qmlpropertygroup"))
+#define COMMAND_QMLATTACHEDPROPERTY Doc::alias(QLatin1String("qmlattachedproperty"))
+#define COMMAND_QMLINHERITS Doc::alias(QLatin1String("inherits"))
+#define COMMAND_QMLINSTANTIATES Doc::alias(QLatin1String("instantiates"))
+#define COMMAND_INQMLMODULE Doc::alias(QLatin1String("inqmlmodule"))
+#define COMMAND_QMLSIGNAL Doc::alias(QLatin1String("qmlsignal"))
+#define COMMAND_QMLATTACHEDSIGNAL Doc::alias(QLatin1String("qmlattachedsignal"))
+#define COMMAND_QMLMETHOD Doc::alias(QLatin1String("qmlmethod"))
+#define COMMAND_QMLATTACHEDMETHOD Doc::alias(QLatin1String("qmlattachedmethod"))
+#define COMMAND_QMLDEFAULT Doc::alias(QLatin1String("default"))
+#define COMMAND_QMLREADONLY Doc::alias(QLatin1String("readonly"))
+#define COMMAND_QMLBASICTYPE Doc::alias(QLatin1String("qmlbasictype"))
+
+#define COMMAND_JSTYPE Doc::alias(QLatin1String("jstype"))
+#define COMMAND_JSMODULE Doc::alias(QLatin1String("jsmodule"))
+#define COMMAND_JSPROPERTY Doc::alias(QLatin1String("jsproperty"))
+#define COMMAND_JSPROPERTYGROUP Doc::alias(QLatin1String("jspropertygroup"))
+#define COMMAND_JSATTACHEDPROPERTY Doc::alias(QLatin1String("jsattachedproperty"))
+#define COMMAND_INJSMODULE Doc::alias(QLatin1String("injsmodule"))
+#define COMMAND_JSSIGNAL Doc::alias(QLatin1String("jssignal"))
+#define COMMAND_JSATTACHEDSIGNAL Doc::alias(QLatin1String("jsattachedsignal"))
+#define COMMAND_JSMETHOD Doc::alias(QLatin1String("jsmethod"))
+#define COMMAND_JSATTACHEDMETHOD Doc::alias(QLatin1String("jsattachedmethod"))
+#define COMMAND_JSBASICTYPE Doc::alias(QLatin1String("jsbasictype"))
+
+/*!
+ The constructor stores all the parameters in local data members.
+ */
+QmlDocVisitor::QmlDocVisitor(const QString &filePath,
+ const QString &code,
+ QQmlJS::Engine *engine,
+ const QSet<QString> &commands,
+ const QSet<QString> &topics)
+ : nestingLevel(0)
+{
+ lastEndOffset = 0;
+ this->filePath_ = filePath;
+ this->name = QFileInfo(filePath).baseName();
+ document = code;
+ this->engine = engine;
+ this->commands_ = commands;
+ this->topics_ = topics;
+ current = QDocDatabase::qdocDB()->primaryTreeRoot();
+}
+
+/*!
+ The destructor does nothing.
+ */
+QmlDocVisitor::~QmlDocVisitor()
+{
+ // nothing.
+}
+
+/*!
+ Returns the location of the nearest comment above the \a offset.
+ */
+QQmlJS::AST::SourceLocation QmlDocVisitor::precedingComment(quint32 offset) const
+{
+ QListIterator<QQmlJS::AST::SourceLocation> it(engine->comments());
+ it.toBack();
+
+ while (it.hasPrevious()) {
+
+ QQmlJS::AST::SourceLocation loc = it.previous();
+
+ if (loc.begin() <= lastEndOffset) {
+ // Return if we reach the end of the preceding structure.
+ break;
+ }
+ else if (usedComments.contains(loc.begin())) {
+ // Return if we encounter a previously used comment.
+ break;
+ }
+ else if (loc.begin() > lastEndOffset && loc.end() < offset) {
+ // Only examine multiline comments in order to avoid snippet markers.
+ if (document.at(loc.offset - 1) == QLatin1Char('*')) {
+ QString comment = document.mid(loc.offset, loc.length);
+ if (comment.startsWith(QLatin1Char('!')) || comment.startsWith(QLatin1Char('*'))) {
+ return loc;
+ }
+ }
+ }
+ }
+
+ return QQmlJS::AST::SourceLocation();
+}
+
+class QmlSignatureParser
+{
+ public:
+ QmlSignatureParser(FunctionNode* func, const QString& signature, const Location& loc);
+ void readToken() { tok_ = tokenizer_->getToken(); }
+ QString lexeme() { return tokenizer_->lexeme(); }
+ QString previousLexeme() { return tokenizer_->previousLexeme(); }
+
+ bool match(int target);
+ bool matchDataType(CodeChunk* dataType, QString* var);
+ bool matchParameter();
+ bool matchFunctionDecl();
+
+ private:
+ QString signature_;
+ QStringList names_;
+ QString funcName_;
+ Tokenizer* tokenizer_;
+ int tok_;
+ FunctionNode* func_;
+ const Location& location_;
+};
+
+/*!
+ Finds the nearest unused qdoc comment above the QML entity
+ represented by the \a node and processes the qdoc commands
+ in that comment. The processed documentation is stored in
+ the \a node.
+
+ If a qdoc comment is found for \a location, true is returned.
+ If a comment is not found there, false is returned.
+ */
+bool QmlDocVisitor::applyDocumentation(QQmlJS::AST::SourceLocation location, Node* node)
+{
+ QQmlJS::AST::SourceLocation loc = precedingComment(location.begin());
+
+ if (loc.isValid()) {
+ QString source = document.mid(loc.offset, loc.length);
+ Location start(filePath_);
+ start.setLineNo(loc.startLine);
+ start.setColumnNo(loc.startColumn);
+ Location finish(filePath_);
+ finish.setLineNo(loc.startLine);
+ finish.setColumnNo(loc.startColumn);
+
+ Doc doc(start, finish, source.mid(1), commands_, topics_);
+ const TopicList& topicsUsed = doc.topicsUsed();
+ NodeList nodes;
+ Node* nodePassedIn = node;
+ Aggregate* parent = nodePassedIn->parent();
+ node->setDoc(doc);
+ nodes.append(node);
+ if (topicsUsed.size() > 0) {
+ for (int i=0; i<topicsUsed.size(); ++i) {
+ QString topic = topicsUsed.at(i).topic;
+ QString args = topicsUsed.at(i).args;
+ if ((topic == COMMAND_QMLPROPERTY) || (topic == COMMAND_QMLATTACHEDPROPERTY) ||
+ (topic == COMMAND_JSPROPERTY) || (topic == COMMAND_JSATTACHEDPROPERTY)) {
+ QmlPropArgs qpa;
+ if (splitQmlPropertyArg(doc, args, qpa)) {
+ if (qpa.name_ == nodePassedIn->name()) {
+ if (nodePassedIn->isAlias())
+ nodePassedIn->setDataType(qpa.type_);
+ }
+ else {
+ bool isAttached = (topic == COMMAND_QMLATTACHEDPROPERTY) ||
+ (topic == COMMAND_JSATTACHEDPROPERTY);
+ QmlPropertyNode* n = parent->hasQmlProperty(qpa.name_, isAttached);
+ if (n == 0)
+ n = new QmlPropertyNode(parent, qpa.name_, qpa.type_, isAttached);
+ n->setLocation(doc.location());
+ n->setDoc(doc);
+ n->setReadOnly(nodePassedIn->isReadOnly());
+ if (nodePassedIn->isDefault())
+ n->setDefault();
+ if (isAttached)
+ n->setReadOnly(0);
+ if ((topic == COMMAND_JSPROPERTY) ||
+ (topic == COMMAND_JSATTACHEDPROPERTY))
+ n->setGenus(Node::JS);
+ nodes.append(n);
+ }
+ }
+ else
+ qDebug() << " FAILED TO PARSE QML OR JS PROPERTY:" << topic << args;
+ }
+ else if ((topic == COMMAND_QMLMETHOD) || (topic == COMMAND_QMLATTACHEDMETHOD) ||
+ (topic == COMMAND_JSMETHOD) || (topic == COMMAND_JSATTACHEDMETHOD)) {
+ if (node->isFunction()) {
+ FunctionNode* fn = static_cast<FunctionNode*>(node);
+ QmlSignatureParser qsp(fn, args, doc.location());
+ }
+ }
+ }
+ }
+ for (int i=0; i<nodes.size(); ++i)
+ applyMetacommands(loc, nodes.at(i), doc);
+ usedComments.insert(loc.offset);
+ if (doc.isEmpty()) {
+ return false;
+ }
+ return true;
+ }
+ Location codeLoc(filePath_);
+ codeLoc.setLineNo(location.startLine);
+ node->setLocation(codeLoc);
+ return false;
+}
+
+QmlSignatureParser::QmlSignatureParser(FunctionNode* func, const QString& signature, const Location& loc)
+ : signature_(signature), func_(func), location_(loc)
+{
+ QByteArray latin1 = signature.toLatin1();
+ Tokenizer stringTokenizer(location_, latin1);
+ stringTokenizer.setParsingFnOrMacro(true);
+ tokenizer_ = &stringTokenizer;
+ readToken();
+ matchFunctionDecl();
+}
+
+/*!
+ If the current token matches \a target, read the next
+ token and return true. Otherwise, don't read the next
+ token, and return false.
+ */
+bool QmlSignatureParser::match(int target)
+{
+ if (tok_ == target) {
+ readToken();
+ return true;
+ }
+ return false;
+}
+
+/*!
+ Parse a QML data type into \a dataType and an optional
+ variable name into \a var.
+ */
+bool QmlSignatureParser::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) {
+ while (match(Tok_signed) ||
+ match(Tok_unsigned) ||
+ match(Tok_short) ||
+ match(Tok_long) ||
+ match(Tok_int64)) {
+ dataType->append(previousLexeme());
+ virgin = false;
+ }
+ }
+
+ 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());
+ }
+
+ 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());
+
+ /*
+ The usual case: Look for an optional identifier, then for
+ some array brackets.
+ */
+ dataType->appendHotspot();
+
+ if ((var != 0) && match(Tok_Ident))
+ *var = previousLexeme();
+
+ 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 QmlSignatureParser::matchParameter()
+{
+ QString name;
+ CodeChunk dataType;
+ CodeChunk defaultValue;
+
+ bool result = matchDataType(&dataType, &name);
+ if (name.isEmpty()) {
+ name = dataType.toString();
+ dataType.clear();
+ }
+
+ if (!result)
+ return false;
+ 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 QmlSignatureParser::matchFunctionDecl()
+{
+ CodeChunk returnType;
+
+ int firstBlank = signature_.indexOf(QChar(' '));
+ int leftParen = signature_.indexOf(QChar('('));
+ if ((firstBlank > 0) && (leftParen - firstBlank) > 1) {
+ if (!matchDataType(&returnType, 0))
+ return false;
+ }
+
+ while (match(Tok_Ident)) {
+ names_.append(previousLexeme());
+ if (!match(Tok_Gulbrandsen)) {
+ funcName_ = previousLexeme();
+ names_.pop_back();
+ break;
+ }
+ }
+
+ if (tok_ != Tok_LeftParen)
+ return false;
+
+ readToken();
+
+ func_->setLocation(location_);
+ func_->setReturnType(returnType.toString());
+
+ if (tok_ != Tok_RightParen) {
+ func_->clearParams();
+ do {
+ if (!matchParameter())
+ return false;
+ } while (match(Tok_Comma));
+ }
+ if (!match(Tok_RightParen))
+ return false;
+ return true;
+}
+
+/*!
+ A QML property argument has the form...
+
+ <type> <component>::<name>
+ <type> <QML-module>::<component>::<name>
+
+ This function splits the argument into one of those
+ two forms. The three part form is the old form, which
+ was used before the creation of QtQuick 2 and Qt
+ Components. A <QML-module> is the QML equivalent of a
+ C++ namespace. So this function splits \a arg on "::"
+ and stores the parts in the \e {type}, \e {module},
+ \e {component}, and \a {name}, fields of \a qpa. If it
+ is successful, it returns \c true. If not enough parts
+ are found, a qdoc warning is emitted and false is
+ returned.
+ */
+bool QmlDocVisitor::splitQmlPropertyArg(const Doc& doc,
+ const QString& arg,
+ QmlPropArgs& qpa)
+{
+ qpa.clear();
+ QStringList blankSplit = arg.split(QLatin1Char(' '));
+ if (blankSplit.size() > 1) {
+ qpa.type_ = blankSplit[0];
+ QStringList colonSplit(blankSplit[1].split("::"));
+ if (colonSplit.size() == 3) {
+ qpa.module_ = colonSplit[0];
+ qpa.component_ = colonSplit[1];
+ qpa.name_ = colonSplit[2];
+ return true;
+ }
+ else if (colonSplit.size() == 2) {
+ qpa.component_ = colonSplit[0];
+ qpa.name_ = colonSplit[1];
+ return true;
+ }
+ else if (colonSplit.size() == 1) {
+ qpa.name_ = colonSplit[0];
+ return true;
+ }
+ QString msg = "Unrecognizable QML module/component qualifier for " + arg;
+ doc.location().warning(tr(msg.toLatin1().data()));
+ }
+ else {
+ QString msg = "Missing property type for " + arg;
+ doc.location().warning(tr(msg.toLatin1().data()));
+ }
+ return false;
+}
+
+/*!
+ Applies the metacommands found in the comment.
+ */
+void QmlDocVisitor::applyMetacommands(QQmlJS::AST::SourceLocation,
+ Node* node,
+ Doc& doc)
+{
+ QDocDatabase* qdb = QDocDatabase::qdocDB();
+ QSet<QString> metacommands = doc.metaCommandsUsed();
+ if (metacommands.count() > 0) {
+ metacommands.subtract(topics_);
+ QSet<QString>::iterator i = metacommands.begin();
+ while (i != metacommands.end()) {
+ QString command = *i;
+ ArgList args = doc.metaCommandArgs(command);
+ if ((command == COMMAND_QMLABSTRACT) || (command == COMMAND_ABSTRACT)) {
+ if (node->isQmlType() || node->isJsType()) {
+ node->setAbstract(true);
+ }
+ }
+ else if (command == COMMAND_DEPRECATED) {
+ node->setStatus(Node::Obsolete);
+ }
+ else if ((command == COMMAND_INQMLMODULE) || (command == COMMAND_INJSMODULE)) {
+ qdb->addToQmlModule(args[0].first,node);
+ }
+ else if (command == COMMAND_QMLINHERITS) {
+ if (node->name() == args[0].first)
+ doc.location().warning(tr("%1 tries to inherit itself").arg(args[0].first));
+ else if (node->isQmlType() || node->isJsType()) {
+ QmlTypeNode *qmlType = static_cast<QmlTypeNode*>(node);
+ qmlType->setQmlBaseName(args[0].first);
+ QmlTypeNode::addInheritedBy(args[0].first,node);
+ }
+ }
+ else if (command == COMMAND_QMLDEFAULT) {
+ if (node->isQmlProperty() || node->isJsProperty()) {
+ QmlPropertyNode* qpn = static_cast<QmlPropertyNode*>(node);
+ qpn->setDefault();
+ }
+ }
+ else if (command == COMMAND_QMLREADONLY) {
+ if (node->isQmlProperty() || node->isJsProperty()) {
+ QmlPropertyNode* qpn = static_cast<QmlPropertyNode*>(node);
+ qpn->setReadOnly(1);
+ }
+ }
+ else if ((command == COMMAND_INGROUP) && !args.isEmpty()) {
+ ArgList::ConstIterator argsIter = args.constBegin();
+ while (argsIter != args.constEnd()) {
+ QDocDatabase::qdocDB()->addToGroup(argsIter->first, node);
+ ++argsIter;
+ }
+ }
+ else if (command == COMMAND_INTERNAL) {
+ node->setStatus(Node::Internal);
+ }
+ else if (command == COMMAND_OBSOLETE) {
+ node->setStatus(Node::Obsolete);
+ }
+ else if (command == COMMAND_PAGEKEYWORDS) {
+ // Not done yet. Do we need this?
+ }
+ else if (command == COMMAND_PRELIMINARY) {
+ node->setStatus(Node::Preliminary);
+ }
+ else if (command == COMMAND_SINCE) {
+ QString arg = args[0].first; //.join(' ');
+ node->setSince(arg);
+ }
+ else if (command == COMMAND_WRAPPER) {
+ node->setWrapper();
+ }
+ else {
+ doc.location().warning(tr("The \\%1 command is ignored in QML files").arg(command));
+ }
+ ++i;
+ }
+ }
+}
+
+/*!
+ Reconstruct the qualified \a id using dot notation
+ and return the fully qualified string.
+ */
+QString QmlDocVisitor::getFullyQualifiedId(QQmlJS::AST::UiQualifiedId *id)
+{
+ QString result;
+ if (id) {
+ result = id->name.toString();
+ id = id->next;
+ while (id != 0) {
+ result += QChar('.') + id->name.toString();
+ id = id->next;
+ }
+ }
+ return result;
+}
+
+/*!
+ Begin the visit of the object \a definition, recording it in the
+ qdoc database. Increment the object nesting level, which is used
+ to test whether we are at the public API level. The public level
+ is level 1.
+*/
+bool QmlDocVisitor::visit(QQmlJS::AST::UiObjectDefinition *definition)
+{
+ QString type = getFullyQualifiedId(definition->qualifiedTypeNameId);
+ nestingLevel++;
+
+ if (current->type() == Node::Namespace) {
+ QmlTypeNode *component = new QmlTypeNode(current, name);
+ component->setTitle(name);
+ component->setImportList(importList);
+ importList.clear();
+ if (applyDocumentation(definition->firstSourceLocation(), component)) {
+ QmlTypeNode::addInheritedBy(type, component);
+ component->setQmlBaseName(type);
+ }
+ current = component;
+ }
+
+ return true;
+}
+
+/*!
+ End the visit of the object \a definition. In particular,
+ decrement the object nesting level, which is used to test
+ whether we are at the public API level. The public API
+ level is level 1. It won't decrement below 0.
+ */
+void QmlDocVisitor::endVisit(QQmlJS::AST::UiObjectDefinition *definition)
+{
+ if (nestingLevel > 0) {
+ --nestingLevel;
+ }
+ lastEndOffset = definition->lastSourceLocation().end();
+}
+
+bool QmlDocVisitor::visit(QQmlJS::AST::UiImport *import)
+{
+ QString name = document.mid(import->fileNameToken.offset, import->fileNameToken.length);
+ if (name[0] == '\"')
+ name = name.mid(1, name.length()-2);
+ QString version = document.mid(import->versionToken.offset, import->versionToken.length);
+ QString importId = document.mid(import->importIdToken.offset, import->importIdToken.length);
+ QString importUri = getFullyQualifiedId(import->importUri);
+ QString reconstructed = importUri + QLatin1Char(' ') + version;
+ importList.append(ImportRec(name, version, importId, importUri));
+
+ return true;
+}
+
+void QmlDocVisitor::endVisit(QQmlJS::AST::UiImport *definition)
+{
+ lastEndOffset = definition->lastSourceLocation().end();
+}
+
+bool QmlDocVisitor::visit(QQmlJS::AST::UiObjectBinding *)
+{
+ ++nestingLevel;
+ return true;
+}
+
+void QmlDocVisitor::endVisit(QQmlJS::AST::UiObjectBinding *)
+{
+ --nestingLevel;
+}
+
+bool QmlDocVisitor::visit(QQmlJS::AST::UiArrayBinding *)
+{
+ return true;
+}
+
+void QmlDocVisitor::endVisit(QQmlJS::AST::UiArrayBinding *)
+{
+}
+
+/*!
+ Visits the public \a member declaration, which can be a
+ signal or a property. It is a custom signal or property.
+ Only visit the \a member if the nestingLevel is 1.
+*/
+bool QmlDocVisitor::visit(QQmlJS::AST::UiPublicMember *member)
+{
+ if (nestingLevel > 1) {
+ return true;
+ }
+ switch (member->type) {
+ case QQmlJS::AST::UiPublicMember::Signal:
+ {
+ if (current->isQmlType() || current->isJsType()) {
+ QmlTypeNode *qmlType = static_cast<QmlTypeNode *>(current);
+ if (qmlType) {
+
+ QString name = member->name.toString();
+ FunctionNode *qmlSignal = new FunctionNode(Node::QmlSignal, current, name, false);
+
+ QVector<Parameter> parameters;
+ for (QQmlJS::AST::UiParameterList *it = member->parameters; it; it = it->next) {
+ if (!it->type.isEmpty() && !it->name.isEmpty())
+ parameters.append(Parameter(it->type.toString(), QString(), it->name.toString()));
+ }
+
+ qmlSignal->setParameters(parameters);
+ applyDocumentation(member->firstSourceLocation(), qmlSignal);
+ }
+ }
+ break;
+ }
+ case QQmlJS::AST::UiPublicMember::Property:
+ {
+ QString type = member->memberType.toString();
+ QString name = member->name.toString();
+ if (current->isQmlType() || current->isJsType()) {
+ QmlTypeNode *qmlType = static_cast<QmlTypeNode *>(current);
+ if (qmlType) {
+ QString name = member->name.toString();
+ QmlPropertyNode* qmlPropNode = qmlType->hasQmlProperty(name);
+ if (qmlPropNode == 0) {
+ qmlPropNode = new QmlPropertyNode(qmlType, name, type, false);
+ if (current->isJsType())
+ qmlPropNode->setGenus(Node::JS);
+ }
+ qmlPropNode->setReadOnly(member->isReadonlyMember);
+ if (member->isDefaultMember)
+ qmlPropNode->setDefault();
+ applyDocumentation(member->firstSourceLocation(), qmlPropNode);
+ }
+ }
+ break;
+ }
+ default:
+ return false;
+ }
+
+ return true;
+}
+
+/*!
+ End the visit of the \a member.
+ */
+void QmlDocVisitor::endVisit(QQmlJS::AST::UiPublicMember* member)
+{
+ lastEndOffset = member->lastSourceLocation().end();
+}
+
+bool QmlDocVisitor::visit(QQmlJS::AST::IdentifierPropertyName *)
+{
+ return true;
+}
+
+/*!
+ Begin the visit of the function declaration \a fd, but only
+ if the nesting level is 1.
+ */
+bool QmlDocVisitor::visit(QQmlJS::AST::FunctionDeclaration* fd)
+{
+ if (nestingLevel > 1) {
+ return true;
+ }
+ if (current->isQmlType() || current->isJsType()) {
+ QmlTypeNode* qmlType = static_cast<QmlTypeNode*>(current);
+ if (qmlType) {
+ QString name = fd->name.toString();
+ FunctionNode* qmlMethod = new FunctionNode(Node::QmlMethod, current, name, false);
+ if (current->isJsType())
+ qmlMethod->setGenus(Node::JS);
+ int overloads = 0;
+ NodeList::ConstIterator i = current->childNodes().constBegin();
+ while (i != current->childNodes().constEnd()) {
+ if ((*i)->name() == name)
+ overloads++;
+ i++;
+ }
+ if (overloads > 1)
+ qmlMethod->setOverloadFlag(true);
+ QVector<Parameter> parameters;
+ QQmlJS::AST::FormalParameterList* formals = fd->formals;
+ if (formals) {
+ QQmlJS::AST::FormalParameterList* fpl = formals;
+ do {
+ parameters.append(Parameter(QString(), QString(), fpl->name.toString()));
+ fpl = fpl->next;
+ } while (fpl && fpl != formals);
+ qmlMethod->setParameters(parameters);
+ }
+ applyDocumentation(fd->firstSourceLocation(), qmlMethod);
+ }
+ }
+ return true;
+}
+
+/*!
+ End the visit of the function declaration, \a fd.
+ */
+void QmlDocVisitor::endVisit(QQmlJS::AST::FunctionDeclaration* fd)
+{
+ lastEndOffset = fd->lastSourceLocation().end();
+}
+
+/*!
+ Begin the visit of the signal handler declaration \a sb, but only
+ if the nesting level is 1.
+
+ This visit is now deprecated. It has been decided to document
+ public signals. If a signal handler must be discussed in the
+ documentation, that discussion must take place in the comment
+ for the signal.
+ */
+bool QmlDocVisitor::visit(QQmlJS::AST::UiScriptBinding* )
+{
+#if 0
+ if (nestingLevel > 1) {
+ return true;
+ }
+ if (current->isQmlType() || current->isJsType()) {
+ QString handler = sb->qualifiedId->name.toString();
+ if (handler.length() > 2 && handler.startsWith("on") && handler.at(2).isUpper()) {
+ QmlTypeNode* qmlType = static_cast<QmlTypeNode*>(current);
+ if (qmlType) {
+ FunctionNode* qmlSH = new FunctionNode(Node::QmlSignalHandler,current,handler,false);
+ applyDocumentation(sb->firstSourceLocation(), qmlSH);
+ }
+ }
+ }
+#endif
+ return true;
+}
+
+void QmlDocVisitor::endVisit(QQmlJS::AST::UiScriptBinding* sb)
+{
+ lastEndOffset = sb->lastSourceLocation().end();
+}
+
+bool QmlDocVisitor::visit(QQmlJS::AST::UiQualifiedId* )
+{
+ return true;
+}
+
+void QmlDocVisitor::endVisit(QQmlJS::AST::UiQualifiedId* )
+{
+ // nothing.
+}
+
+QT_END_NAMESPACE
diff --git a/src/qdoc/qmlvisitor.h b/src/qdoc/qmlvisitor.h
new file mode 100644
index 000000000..8a23eef75
--- /dev/null
+++ b/src/qdoc/qmlvisitor.h
@@ -0,0 +1,121 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL21$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** 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 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** As a special exception, The Qt Company gives you certain additional
+** rights. These rights are described in The Qt Company LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QMLVISITOR_H
+#define QMLVISITOR_H
+
+#include "node.h"
+
+#include <qstring.h>
+#include <private/qqmljsastvisitor_p.h>
+#include <private/qqmljsengine_p.h>
+
+QT_BEGIN_NAMESPACE
+
+struct QmlPropArgs
+{
+ QString type_;
+ QString module_;
+ QString component_;
+ QString name_;
+
+ void clear() {
+ type_.clear();
+ module_.clear();
+ component_.clear();
+ name_.clear();
+ }
+};
+
+class QmlDocVisitor : public QQmlJS::AST::Visitor
+{
+ Q_DECLARE_TR_FUNCTIONS(QDoc::QmlDocVisitor)
+
+public:
+ QmlDocVisitor(const QString &filePath,
+ const QString &code,
+ QQmlJS::Engine *engine,
+ const QSet<QString> &commands,
+ const QSet<QString> &topics);
+ virtual ~QmlDocVisitor();
+
+ bool visit(QQmlJS::AST::UiImport *import) Q_DECL_OVERRIDE;
+ void endVisit(QQmlJS::AST::UiImport *definition) Q_DECL_OVERRIDE;
+
+ bool visit(QQmlJS::AST::UiObjectDefinition *definition) Q_DECL_OVERRIDE;
+ void endVisit(QQmlJS::AST::UiObjectDefinition *definition) Q_DECL_OVERRIDE;
+
+ bool visit(QQmlJS::AST::UiPublicMember *member) Q_DECL_OVERRIDE;
+ void endVisit(QQmlJS::AST::UiPublicMember *definition) Q_DECL_OVERRIDE;
+
+ virtual bool visit(QQmlJS::AST::UiObjectBinding *) Q_DECL_OVERRIDE;
+ virtual void endVisit(QQmlJS::AST::UiObjectBinding *) Q_DECL_OVERRIDE;
+ virtual void endVisit(QQmlJS::AST::UiArrayBinding *) Q_DECL_OVERRIDE;
+ virtual bool visit(QQmlJS::AST::UiArrayBinding *) Q_DECL_OVERRIDE;
+
+ bool visit(QQmlJS::AST::IdentifierPropertyName *idproperty) Q_DECL_OVERRIDE;
+
+ bool visit(QQmlJS::AST::FunctionDeclaration *) Q_DECL_OVERRIDE;
+ void endVisit(QQmlJS::AST::FunctionDeclaration *) Q_DECL_OVERRIDE;
+
+ bool visit(QQmlJS::AST::UiScriptBinding *) Q_DECL_OVERRIDE;
+ void endVisit(QQmlJS::AST::UiScriptBinding *) Q_DECL_OVERRIDE;
+
+ bool visit(QQmlJS::AST::UiQualifiedId *) Q_DECL_OVERRIDE;
+ void endVisit(QQmlJS::AST::UiQualifiedId *) Q_DECL_OVERRIDE;
+
+private:
+ QString getFullyQualifiedId(QQmlJS::AST::UiQualifiedId *id);
+ QQmlJS::AST::SourceLocation precedingComment(quint32 offset) const;
+ bool applyDocumentation(QQmlJS::AST::SourceLocation location, Node *node);
+ void applyMetacommands(QQmlJS::AST::SourceLocation location, Node* node, Doc& doc);
+ bool splitQmlPropertyArg(const Doc& doc,
+ const QString& arg,
+ QmlPropArgs& qpa);
+
+ QQmlJS::Engine *engine;
+ quint32 lastEndOffset;
+ quint32 nestingLevel;
+ QString filePath_;
+ QString name;
+ QString document;
+ ImportList importList;
+ QSet<QString> commands_;
+ QSet<QString> topics_;
+ QSet<quint32> usedComments;
+ Aggregate *current;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/qdoc/quoter.cpp b/src/qdoc/quoter.cpp
new file mode 100644
index 000000000..25cf27f73
--- /dev/null
+++ b/src/qdoc/quoter.cpp
@@ -0,0 +1,374 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL21$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** 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 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** As a special exception, The Qt Company gives you certain additional
+** rights. These rights are described in The Qt Company LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <qfileinfo.h>
+#include <qregexp.h>
+#include <qdebug.h>
+
+#include "quoter.h"
+
+QT_BEGIN_NAMESPACE
+
+QHash<QString,QString> Quoter::commentHash;
+
+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
+QStringList Quoter::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, .py files:
+ #! [<id>]
+ * .html, .qrc, .ui, .xq, .xml .dita files:
+ <!-- [<id>] -->
+ */
+ if (!commentHash.size()) {
+ commentHash["pro"] = "#!";
+ commentHash["py"] = "#!";
+ commentHash["html"] = "<!--";
+ commentHash["qrc"] = "<!--";
+ commentHash["ui"] = "<!--";
+ commentHash["xml"] = "<!--";
+ commentHash["dita"] = "<!--";
+ 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;
+ int indent = 0;
+
+ while (!plainLines.isEmpty()) {
+ if (match(docLocation, delimiter, plainLines.first())) {
+ QString startLine = getLine();
+ while (indent < startLine.length() && startLine[indent] == QLatin1Char(' '))
+ indent++;
+ break;
+ }
+ getLine();
+ }
+ while (!plainLines.isEmpty()) {
+ QString line = plainLines.first();
+ if (match(docLocation, delimiter, line)) {
+ QString lastLine = getLine(indent);
+ int dIndex = lastLine.indexOf(delimiter);
+ if (dIndex > 0) {
+ // The delimiter might be preceded on the line by other
+ // delimeters, so look for the first comment on the line.
+ QString leading = lastLine.left(dIndex);
+ dIndex = leading.indexOf(comment);
+ if (dIndex != -1)
+ leading = leading.left(dIndex);
+ if (leading.endsWith(QLatin1String("<@comment>")))
+ leading.chop(10);
+ if (!leading.trimmed().isEmpty())
+ t += leading;
+ }
+ return t;
+ }
+
+ t += removeSpecialLines(line, comment, indent);
+ }
+ 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();
+ t += removeSpecialLines(line, comment);
+ }
+ } 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(int unindent)
+{
+ if ( plainLines.isEmpty() )
+ return QString();
+
+ plainLines.removeFirst();
+
+ QString t = markedLines.takeFirst();
+ int i = 0;
+ while (i < unindent && i < t.length() && t[i] == QLatin1Char(' '))
+ i++;
+
+ t = t.mid(i);
+ 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, "//!");
+}
+
+QString Quoter::removeSpecialLines(const QString &line, const QString &comment, int unindent)
+{
+ QString t;
+
+ // Remove special macros to support Qt namespacing.
+ QString trimmed = line.trimmed();
+ if (trimmed.startsWith("QT_BEGIN_NAMESPACE")) {
+ getLine();
+ } else if (trimmed.startsWith("QT_END_NAMESPACE")) {
+ getLine();
+ t += QLatin1Char('\n');
+ } else if (!trimmed.startsWith(comment)) {
+ // Ordinary code
+ t += getLine(unindent);
+ } else {
+ // Comments
+ if (line.contains(QLatin1Char('\n')))
+ t += QLatin1Char('\n');
+ getLine();
+ }
+ return t;
+}
+
+QT_END_NAMESPACE
diff --git a/src/qdoc/quoter.h b/src/qdoc/quoter.h
new file mode 100644
index 000000000..7877eda69
--- /dev/null
+++ b/src/qdoc/quoter.h
@@ -0,0 +1,86 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL21$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** 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 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** As a special exception, The Qt Company gives you certain additional
+** rights. These rights are described in The Qt Company LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** $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
+{
+ Q_DECLARE_TR_FUNCTIONS(QDoc::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);
+
+ static QStringList splitLines(const QString &line);
+
+private:
+ QString getLine(int unindent = 0);
+ void failedAtEnd( const Location& docLocation, const QString& command );
+ bool match( const Location& docLocation, const QString& pattern,
+ const QString& line );
+ QString commentForCode() const;
+ QString removeSpecialLines(const QString &line, const QString &comment,
+ int unindent = 0);
+
+ bool silent;
+ QStringList plainLines;
+ QStringList markedLines;
+ Location codeLocation;
+ static QHash<QString,QString> commentHash;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/qdoc/separator.cpp b/src/qdoc/separator.cpp
new file mode 100644
index 000000000..3cbcb6930
--- /dev/null
+++ b/src/qdoc/separator.cpp
@@ -0,0 +1,69 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL21$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** 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 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** As a special exception, The Qt Company gives you certain additional
+** rights. These rights are described in The Qt Company LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*
+ separator.cpp
+*/
+
+#include "separator.h"
+#include <qcoreapplication.h>
+
+QT_BEGIN_NAMESPACE
+
+QString separator(int index, int count)
+{
+ if (index == count - 1)
+ return QCoreApplication::translate("QDoc", ".", "terminator");
+ if (count == 2)
+ return QCoreApplication::translate("QDoc", " and ", "separator when N = 2");
+ if (index == 0)
+ return QCoreApplication::translate("QDoc", ", ", "first separator when N > 2");
+ if (index < count - 2)
+ return QCoreApplication::translate("QDoc", ", ", "general separator when N > 2");
+ return QCoreApplication::translate("QDoc", ", and ", "last separator when N > 2");
+}
+
+QString comma(int index, int count)
+{
+ if (index == count - 1)
+ return QString();
+ if (count == 2)
+ return QCoreApplication::translate("QDoc", " and ", "separator when N = 2");
+ if (index == 0)
+ return QCoreApplication::translate("QDoc", ", ", "first separator when N > 2");
+ if (index < count - 2)
+ return QCoreApplication::translate("QDoc", ", ", "general separator when N > 2");
+ return QCoreApplication::translate("QDoc", ", and ", "last separator when N > 2");
+}
+
+QT_END_NAMESPACE
diff --git a/src/qdoc/separator.h b/src/qdoc/separator.h
new file mode 100644
index 000000000..281d95994
--- /dev/null
+++ b/src/qdoc/separator.h
@@ -0,0 +1,50 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL21$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** 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 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** As a special exception, The Qt Company gives you certain additional
+** rights. These rights are described in The Qt Company LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*
+ separator.h
+*/
+
+#ifndef SEPARATOR_H
+#define SEPARATOR_H
+
+#include <qstring.h>
+
+QT_BEGIN_NAMESPACE
+
+QString separator( int index, int count );
+QString comma( int index, int count );
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/qdoc/text.cpp b/src/qdoc/text.cpp
new file mode 100644
index 000000000..fa105a834
--- /dev/null
+++ b/src/qdoc/text.cpp
@@ -0,0 +1,300 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL21$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** 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 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** As a special exception, The Qt Company gives you certain additional
+** rights. These rights are described in The Qt Company LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** $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::AtomType atomType)
+{
+ return operator<<(Atom(atomType));
+}
+
+Text& Text::operator<<(const QString& string)
+{
+ return operator<<(Atom(Atom::String, string));
+}
+
+Text& Text::operator<<(const Atom& atom)
+{
+ if (atom.count() < 2) {
+ if (first == 0) {
+ first = new Atom(atom.type(), atom.string());
+ last = first;
+ }
+ else
+ last = new Atom(last, atom.type(), atom.string());
+ }
+ else {
+ if (first == 0) {
+ first = new Atom(atom.type(), atom.string(), atom.string(1));
+ last = first;
+ }
+ else
+ last = new Atom(last, atom.type(), atom.string(), atom.string(1));
+ }
+ return *this;
+}
+
+/*!
+ Special output operator for LinkAtom. It makes a copy of
+ the LinkAtom \a atom and connects the cop;y to the list
+ in this Text.
+ */
+Text& Text::operator<<(const LinkAtom& atom)
+{
+ if (first == 0) {
+ first = new LinkAtom(atom);
+ last = first;
+ }
+ else
+ last = new LinkAtom(last, atom);
+ 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;
+ }
+}
+
+/*!
+ This function traverses the atom list of the Text object,
+ extracting all the string parts. It concatenates them to
+ a result string and returns it.
+ */
+QString Text::toString() const
+{
+ QString str;
+ const Atom* atom = firstAtom();
+ while (atom != 0) {
+ if (atom->type() == Atom::String ||
+ atom->type() == Atom::AutoLink ||
+ atom->type() == Atom::C ||
+ atom->type() == Atom::GuidLink)
+ str += atom->string();
+ atom = atom->next();
+ }
+ return str;
+}
+
+Text Text::subText(Atom::AtomType left, Atom::AtomType right, const Atom* from, bool inclusive) const
+{
+ const Atom* begin = from ? from : firstAtom();
+ const Atom* end;
+
+ while (begin != 0 && begin->type() != left)
+ begin = begin->next();
+ if (begin != 0) {
+ if (!inclusive)
+ begin = begin->next();
+ }
+
+ end = begin;
+ while (end != 0 && end->type() != right)
+ end = end->next();
+ if (end == 0)
+ begin = 0;
+ else if (inclusive)
+ end = end->next();
+ 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 + QLatin1Char('"');
+ 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/src/qdoc/text.h b/src/qdoc/text.h
new file mode 100644
index 000000000..40ecf3edd
--- /dev/null
+++ b/src/qdoc/text.h
@@ -0,0 +1,99 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL21$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** 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 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** As a special exception, The Qt Company gives you certain additional
+** rights. These rights are described in The Qt Company LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** $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::AtomType atomType);
+ Text& operator<<(const QString& string);
+ Text& operator<<(const Atom& atom);
+ Text& operator<<(const LinkAtom& 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::AtomType left, Atom::AtomType right, const Atom *from = 0, bool inclusive = false) const;
+ void dump() const;
+ void clear();
+
+ 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:
+
+ 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/src/qdoc/tokenizer.cpp b/src/qdoc/tokenizer.cpp
new file mode 100644
index 000000000..987fff548
--- /dev/null
+++ b/src/qdoc/tokenizer.cpp
@@ -0,0 +1,799 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL21$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** 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 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** As a special exception, The Qt Company gives you certain additional
+** rights. These rights are described in The Qt Company LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "config.h"
+#include "tokenizer.h"
+#include "generator.h"
+
+#include <qfile.h>
+#include <qhash.h>
+#include <qregexp.h>
+#include <qstring.h>
+#include <qtextcodec.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_PRIVATE_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",
+ "QPrivateSignal"
+};
+
+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;
+
+#ifndef QT_NO_TEXTCODEC
+static QTextCodec *sourceCodec = 0;
+#endif
+
+/*
+ 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, int(strlen(s)));
+ while (kwordHashTable[k]) {
+ if (++k == KwordHashTableSize)
+ k = 0;
+ }
+ kwordHashTable[k] = number;
+}
+
+Tokenizer::Tokenizer(const Location& loc, QFile &in)
+{
+ init();
+ yyIn = in.readAll();
+ 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, int(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_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();
+ /*
+ Removed check for '&&', only interpret '&=' as an operator.
+ '&&' is also used for an rvalue reference. QTBUG-32675
+ */
+ if (yyCh == '=') {
+ yyCh = getChar();
+ return Tok_SomeOperator;
+ }
+ else {
+ return Tok_Ampersand;
+ }
+ case '\'':
+ yyCh = getChar();
+ /*
+ Allow empty character literal. QTBUG-25775
+ */
+ if (yyCh == '\'') {
+ yyCh = getChar();
+ break;
+ }
+ 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.fileName().endsWith(".js"))) {
+ 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);
+
+ QString sourceEncoding = config.getString(CONFIG_SOURCEENCODING);
+ if (sourceEncoding.isEmpty())
+ sourceEncoding = QLatin1String("ISO-8859-1");
+#ifndef QT_NO_TEXTCODEC
+ sourceCodec = QTextCodec::codecForName(sourceEncoding.toLocal8Bit());
+#endif
+
+ 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('|'));
+
+ /*
+ The keyword hash table is always cleared before any words are inserted.
+ */
+ 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.toLatin1();
+ 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.toLatin1();
+ ignoredTokensAndDirectives->insert(db, true);
+ insertKwordIntoHash(db.data(), -1);
+ }
+}
+
+/*!
+ The heap allocated variables are freed here. The keyword
+ hash table is not cleared here, but it is cleared in the
+ initialize() function, before any keywords are inserted.
+ */
+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();
+ if (yyCh == '\r')
+ yyCh = getChar();
+ }
+ condition += yyCh;
+ yyCh = getChar();
+ }
+ condition.remove(*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 && tok != Tok_Eoi);
+ 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 \c 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.endsWith(QChar(')')))
+ return isTrue(t.mid(1, t.length() - 2));
+
+ if (definedX->exactMatch(t))
+ return defines->exactMatch(definedX->cap(1));
+ else
+ return !falsehoods->exactMatch(t);
+}
+
+QString Tokenizer::lexeme() const
+{
+#ifndef QT_NO_TEXTCODEC
+ return sourceCodec->toUnicode(yyLex);
+#else
+ return QString::fromUtf8(yyLex);
+#endif
+}
+
+QString Tokenizer::previousLexeme() const
+{
+#ifndef QT_NO_TEXTCODEC
+ return sourceCodec->toUnicode(yyPrevLex);
+#else
+ return QString::fromUtf8(yyPrevLex);
+#endif
+}
+
+QT_END_NAMESPACE
diff --git a/src/qdoc/tokenizer.h b/src/qdoc/tokenizer.h
new file mode 100644
index 000000000..41a3ffd93
--- /dev/null
+++ b/src/qdoc/tokenizer.h
@@ -0,0 +1,177 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL21$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** 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 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** As a special exception, The Qt Company gives you certain additional
+** rights. These rights are described in The Qt Company LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*
+ tokenizer.h
+*/
+
+#ifndef TOKENIZER_H
+#define TOKENIZER_H
+
+#include <qfile.h>
+#include <qstack.h>
+#include <qstring.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_PRIVATE_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_QPrivateSignal,
+ Tok_FirstKeyword = Tok_char, Tok_LastKeyword = Tok_QPrivateSignal };
+
+/*
+ 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
+{
+ Q_DECLARE_TR_FUNCTIONS(QDoc::Tokenizer)
+
+public:
+ Tokenizer(const Location& loc, const QByteArray &in);
+ Tokenizer(const Location& loc, QFile &file);
+
+ ~Tokenizer();
+
+ int getToken();
+ void setParsingFnOrMacro(bool macro) { parsingMacro = macro; }
+ bool parsingFnOrMacro() const { return parsingMacro; }
+
+ const Location &location() const { return yyTokLoc; }
+ QString previousLexeme() const;
+ QString lexeme() const;
+ QString version() const { return yyVersion; }
+ int braceDepth() const { return yyBraceDepth; }
+ int parenDepth() const { return yyParenDepth; }
+ int bracketDepth() const { return yyBracketDepth; }
+ Location& tokenLocation() { return yyTokLoc; }
+
+ 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 explicitly 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/src/qdoc/tree.cpp b/src/qdoc/tree.cpp
new file mode 100644
index 000000000..f8f88e6b9
--- /dev/null
+++ b/src/qdoc/tree.cpp
@@ -0,0 +1,1525 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL21$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** 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 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** As a special exception, The Qt Company gives you certain additional
+** rights. These rights are described in The Qt Company LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "doc.h"
+#include "htmlgenerator.h"
+#include "location.h"
+#include "node.h"
+#include "text.h"
+#include "tree.h"
+#include "qdocdatabase.h"
+#include <limits.h>
+#include <qdebug.h>
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class Tree
+
+ This class constructs and maintains a tree of instances of
+ the subclasses of Node.
+
+ This class is now private. Only class QDocDatabase has access.
+ Please don't change this. If you must access class Tree, do it
+ though the pointer to the singleton QDocDatabase.
+
+ Tree is being converted to a forest. A static member provides a
+ map of Tree* values with the module names as the keys. There is
+ one Tree in the map for each index file read, and there is one
+ tree that is not in the map for the module whose documentation
+ is being generated.
+ */
+
+/*!
+ Constructs a Tree. \a qdb is the pointer to the singleton
+ qdoc database that is constructing the tree. This might not
+ be necessary, and it might be removed later.
+
+ \a camelCaseModuleName is the project name for this tree,
+ which was obtained from the qdocconf file via the Config
+ singleton.
+ */
+Tree::Tree(const QString& camelCaseModuleName, QDocDatabase* qdb)
+ : treeHasBeenAnalyzed_(false),
+ docsHaveBeenGenerated_(false),
+ linkCount_(0),
+ camelCaseModuleName_(camelCaseModuleName),
+ physicalModuleName_(camelCaseModuleName.toLower()),
+ qdb_(qdb),
+ root_(0, QString()),
+ targetListMap_(0)
+{
+ root_.setPhysicalModuleName(physicalModuleName_);
+ root_.setTree(this);
+ if (Generator::writeQaPages()) {
+ targetListMap_ = new TargetListMap;
+ }
+}
+
+/*!
+ Destroys the Tree. The root node is a data member
+ of this object, so its destructor is called. The
+ destructor of each child node is called, and these
+ destructors are recursive. Thus the entire tree is
+ destroyed.
+
+ There are two maps of targets, keywords, and contents.
+ One map is indexed by ref, the other by title. The ref
+ is just the canonical form of the title. Both maps
+ use the same set of TargetRec objects as the values,
+ so the destructor only deletes the values from one of
+ the maps. Then it clears both maps.
+ */
+Tree::~Tree()
+{
+ TargetMap::iterator i = nodesByTargetRef_.begin();
+ while (i != nodesByTargetRef_.end()) {
+ delete i.value();
+ ++i;
+ }
+ nodesByTargetRef_.clear();
+ nodesByTargetTitle_.clear();
+ if (Generator::writeQaPages() && targetListMap_) {
+ TargetListMap::iterator i = targetListMap_->begin();
+ while (i != targetListMap_->end()) {
+ TargetList* tlist = i.value();
+ if (tlist) {
+ foreach (TargetLoc* tloc, *tlist)
+ delete tloc;
+ }
+ delete tlist;
+ ++i;
+ }
+ }
+}
+
+/* API members */
+
+/*!
+ Calls findClassNode() first with \a path and \a start. If
+ it finds a node, the node is returned. If not, it calls
+ findNamespaceNode() with the same parameters. The result
+ is returned.
+ */
+Node* Tree::findNodeForInclude(const QStringList& path) const
+{
+ Node* n = findClassNode(path);
+ if (!n)
+ n = findNamespaceNode(path);
+ return n;
+}
+
+/*!
+ Find the C++ class node named \a path. Begin the search at the
+ \a start node. If the \a start node is 0, begin the search
+ at the root of the tree. Only a C++ class node named \a path is
+ acceptible. If one is not found, 0 is returned.
+ */
+ClassNode* Tree::findClassNode(const QStringList& path, const Node* start) const
+{
+ if (!start)
+ start = const_cast<NamespaceNode*>(root());
+ return static_cast<ClassNode*>(findNodeRecursive(path, 0, start, Node::Class));
+}
+
+/*!
+ Find the Namespace node named \a path. Begin the search at
+ the root of the tree. Only a Namespace node named \a path
+ is acceptible. If one is not found, 0 is returned.
+ */
+NamespaceNode* Tree::findNamespaceNode(const QStringList& path) const
+{
+ Node* start = const_cast<NamespaceNode*>(root());
+ return static_cast<NamespaceNode*>(findNodeRecursive(path, 0, start, Node::Namespace));
+}
+
+/*!
+ This function first ignores the \a clone node and searches
+ for the parent node with \a parentPath. If that search is
+ successful, it searches for a child node of the parent that
+ matches the \a clone node. If it finds a node that is just
+ like the \a clone, it returns a pointer to the found node.
+
+ Apparently the search order is important here. Don't change
+ it unless you know what you are doing, or you will introduce
+ qdoc warnings.
+ */
+FunctionNode* Tree::findFunctionNode(const QStringList& parentPath, const FunctionNode* clone)
+{
+ const Node* parent = findNamespaceNode(parentPath);
+ if (parent == 0)
+ parent = findClassNode(parentPath, 0);
+ if (parent == 0)
+ parent = findNode(parentPath, 0, 0, Node::DontCare);
+ if (parent == 0 || !parent->isAggregate())
+ return 0;
+ return ((const Aggregate*)parent)->findFunctionNode(clone);
+}
+
+
+/*!
+ Find the Qml type node named \a path. Begin the search at the
+ \a start node. If the \a start node is 0, begin the search
+ at the root of the tree. Only a Qml type node named <\a path is
+ acceptible. If one is not found, 0 is returned.
+ */
+QmlTypeNode* Tree::findQmlTypeNode(const QStringList& path)
+{
+ /*
+ If the path contains one or two double colons ("::"),
+ check first to see if the first two path strings refer
+ to a QML element. If they do, path[0] will be the QML
+ module identifier, and path[1] will be the QML type.
+ If the anser is yes, the reference identifies a QML
+ class node.
+ */
+ if (path.size() >= 2 && !path[0].isEmpty()) {
+ QmlTypeNode* qcn = qdb_->findQmlType(path[0], path[1]);
+ if (qcn)
+ return qcn;
+ }
+ return static_cast<QmlTypeNode*>(findNodeRecursive(path, 0, root(), Node::QmlType));
+}
+
+/*!
+ This function begins searching the tree at \a relative for
+ the \l {FunctionNode} {function node} identified by \a path.
+ The \a findFlags are used to restrict the search. If a node
+ that matches the \a path is found, it is returned. Otherwise,
+ 0 is returned. If \a relative is 0, the root of the tree is
+ used as the starting point.
+ */
+const FunctionNode* Tree::findFunctionNode(const QStringList& path,
+ const QString& params,
+ const Node* relative,
+ int findFlags,
+ Node::Genus genus) const
+{
+ if (path.size() == 3 && !path[0].isEmpty() &&
+ ((genus == Node::QML) || (genus == Node::DontCare))) {
+ QmlTypeNode* qcn = lookupQmlType(QString(path[0] + "::" + path[1]));
+ if (!qcn) {
+ QStringList p(path[1]);
+ Node* n = findNodeByNameAndType(p, Node::QmlType);
+ if (n && (n->isQmlType() || n->isJsType()))
+ qcn = static_cast<QmlTypeNode*>(n);
+ }
+ if (qcn)
+ return static_cast<const FunctionNode*>(qcn->findFunctionNode(path[2], params));
+ }
+
+ if (!relative)
+ relative = root();
+ else if (genus != Node::DontCare) {
+ if (genus != relative->genus())
+ relative = root();
+ }
+
+ do {
+ const Node* node = relative;
+ int i;
+
+ for (i = 0; i < path.size(); ++i) {
+ if (node == 0 || !node->isAggregate())
+ break;
+
+ const Node* next;
+ if (i == path.size() - 1)
+ next = ((const Aggregate*) node)->findFunctionNode(path.at(i), params);
+ else
+ next = ((const Aggregate*) node)->findChildNode(path.at(i), genus);
+
+ if (!next && node->isClass() && (findFlags & SearchBaseClasses)) {
+ NodeList baseClasses = allBaseClasses(static_cast<const ClassNode*>(node));
+ foreach (const Node* baseClass, baseClasses) {
+ if (i == path.size() - 1)
+ next = static_cast<const Aggregate*>(baseClass)->findFunctionNode(path.at(i), params);
+ else
+ next = static_cast<const Aggregate*>(baseClass)->findChildNode(path.at(i), genus);
+
+ if (next)
+ break;
+ }
+ }
+
+ node = next;
+ }
+ if (node && i == path.size() && node->isFunction()) {
+ // 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;
+}
+
+static NodeTypeList t;
+static const NodeTypeList& relatesTypes()
+{
+ if (t.isEmpty()) {
+ t.reserve(3);
+ t.append(NodeTypePair(Node::Class, Node::NoSubtype));
+ t.append(NodeTypePair(Node::Namespace, Node::NoSubtype));
+ t.append(NodeTypePair(Node::Document, Node::HeaderFile));
+ }
+ return t;
+}
+
+/*!
+ This function searches for the node specified by \a path.
+ The matching node can be one of several different types
+ including a C++ class, a C++ namespace, or a C++ header
+ file.
+
+ I'm not sure if it can be a QML type, but if that is a
+ possibility, the code can easily accommodate it.
+
+ If a matching node is found, a pointer to it is returned.
+ Otherwise 0 is returned.
+ */
+Aggregate* Tree::findRelatesNode(const QStringList& path)
+{
+ Node* n = findNodeRecursive(path, 0, root(), relatesTypes());
+ return ((n && n->isAggregate()) ? static_cast<Aggregate*>(n) : 0);
+}
+
+/*!
+ Inserts function name \a funcName and function role \a funcRole into
+ the property function map for the specified \a property.
+ */
+void Tree::addPropertyFunction(PropertyNode* property,
+ const QString& funcName,
+ PropertyNode::FunctionRole funcRole)
+{
+ unresolvedPropertyMap[property].insert(funcRole, funcName);
+}
+
+/*!
+ This function resolves C++ inheritance and reimplementation
+ settings for each C++ class node found in the tree beginning
+ at \a n. It also calls itself recursively for each C++ class
+ node or namespace node it encounters. For each child of \a n
+ that is a class node, it calls resolveInheritanceHelper().
+
+ This function does not resolve QML inheritance.
+ */
+void Tree::resolveInheritance(Aggregate* n)
+{
+ if (!n)
+ n = root();
+
+ for (int pass = 0; pass < 2; pass++) {
+ NodeList::ConstIterator c = n->childNodes().constBegin();
+ while (c != n->childNodes().constEnd()) {
+ if ((*c)->type() == Node::Class) {
+ resolveInheritanceHelper(pass, (ClassNode*)*c);
+ resolveInheritance((ClassNode*)*c);
+ }
+ else if ((*c)->type() == Node::Namespace) {
+ NamespaceNode* ns = static_cast<NamespaceNode*>(*c);
+ resolveInheritance(ns);
+ }
+ ++c;
+ }
+ }
+}
+
+/*!
+ This function is run twice for eachclass node \a cn in the
+ tree. First it is run with \a pass set to 0 for each
+ class node \a cn. Then it is run with \a pass set to 1 for
+ eachclass node \a cn.
+
+ In \a pass 0, all the base classes ofclass node \a cn are
+ found and added to the base class list forclass node \a cn.
+
+ In \a pass 1, each child ofclass node \a cn that is a function
+ that is reimplemented from one of the base classes is marked
+ as being reimplemented from that class.
+
+ Some property node fixing up is also done in \a pass 1.
+ */
+void Tree::resolveInheritanceHelper(int pass, ClassNode* cn)
+{
+ if (pass == 0) {
+ QList<RelatedClass>& bases = cn->baseClasses();
+ QList<RelatedClass>::iterator b = bases.begin();
+ while (b != bases.end()) {
+ if (!(*b).node_) {
+ Node* n = qdb_->findClassNode((*b).path_);
+ /*
+ If the node for the base class was not found,
+ the reason might be that the subclass is in a
+ namespace and the base class is in the same
+ namespace, but the base class name was not
+ qualified with the namespace name. That is the
+ case most of the time. Then restart the search
+ at the parent of the subclass node (the namespace
+ node) using the unqualified base class name.
+ */
+ if (!n) {
+ Aggregate* parent = cn->parent();
+ if (parent)
+ // Exclude the root namespace
+ if (parent->isNamespace() && !parent->name().isEmpty())
+ n = findClassNode((*b).path_, parent);
+ }
+ if (n) {
+ ClassNode* bcn = static_cast<ClassNode*>(n);
+ (*b).node_ = bcn;
+ bcn->addDerivedClass((*b).access_, cn);
+ }
+ }
+ ++b;
+ }
+ }
+ else {
+ NodeList::ConstIterator c = cn->childNodes().constBegin();
+ while (c != cn->childNodes().constEnd()) {
+ if ((*c)->type() == Node::Function) {
+ FunctionNode* func = (FunctionNode*)* c;
+ FunctionNode* from = findVirtualFunctionInBaseClasses(cn, func);
+ if (from != 0) {
+ if (func->virtualness() == FunctionNode::NonVirtual)
+ func->setVirtualness(FunctionNode::NormalVirtual);
+ func->setReimplementedFrom(from);
+ }
+ }
+ else if ((*c)->type() == Node::Property)
+ cn->fixPropertyUsingBaseClasses(static_cast<PropertyNode*>(*c));
+ ++c;
+ }
+ }
+}
+
+/*!
+ */
+void Tree::resolveProperties()
+{
+ PropertyMap::ConstIterator propEntry;
+
+ propEntry = unresolvedPropertyMap.constBegin();
+ while (propEntry != unresolvedPropertyMap.constEnd()) {
+ PropertyNode* property = propEntry.key();
+ Aggregate* parent = property->parent();
+ QString getterName = (*propEntry)[PropertyNode::Getter];
+ QString setterName = (*propEntry)[PropertyNode::Setter];
+ QString resetterName = (*propEntry)[PropertyNode::Resetter];
+ QString notifierName = (*propEntry)[PropertyNode::Notifier];
+
+ NodeList::ConstIterator c = parent->childNodes().constBegin();
+ while (c != parent->childNodes().constEnd()) {
+ 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);
+ }
+ else if (function->name() == notifierName) {
+ property->addSignal(function, PropertyNode::Notifier);
+ }
+ }
+ }
+ ++c;
+ }
+ ++propEntry;
+ }
+
+ propEntry = unresolvedPropertyMap.constBegin();
+ while (propEntry != unresolvedPropertyMap.constEnd()) {
+ PropertyNode* property = propEntry.key();
+ // redo it to set the property functions
+ if (property->overriddenFrom())
+ property->setOverriddenFrom(property->overriddenFrom());
+ ++propEntry;
+ }
+
+ unresolvedPropertyMap.clear();
+}
+
+/*!
+ For each QML class node that points to a C++ class node,
+ follow its C++ class node pointer and set the C++ class
+ node's QML class node pointer back to the QML class node.
+ */
+void Tree::resolveCppToQmlLinks()
+{
+
+ foreach (Node* child, root_.childNodes()) {
+ if (child->isQmlType() || child->isJsType()) {
+ QmlTypeNode* qcn = static_cast<QmlTypeNode*>(child);
+ ClassNode* cn = const_cast<ClassNode*>(qcn->classNode());
+ if (cn)
+ cn->setQmlElement(qcn);
+ }
+ }
+}
+
+/*!
+ For each C++ class node, resolve any \c using clauses
+ that appeared in the class declaration.
+ */
+void Tree::resolveUsingClauses()
+{
+ foreach (Node* child, root_.childNodes()) {
+ if (child->isClass()) {
+ ClassNode* cn = static_cast<ClassNode*>(child);
+ QList<UsingClause>& usingClauses = cn->usingClauses();
+ QList<UsingClause>::iterator uc = usingClauses.begin();
+ while (uc != usingClauses.end()) {
+ if (!(*uc).node()) {
+ const Node* n = qdb_->findFunctionNode((*uc).signature(), cn, Node::CPP);
+ if (n)
+ (*uc).setNode(n);
+ }
+ ++uc;
+ }
+ }
+ }
+}
+
+/*!
+ */
+void Tree::fixInheritance(NamespaceNode* rootNode)
+{
+ if (!rootNode)
+ rootNode = root();
+
+ NodeList::ConstIterator c = rootNode->childNodes().constBegin();
+ while (c != rootNode->childNodes().constEnd()) {
+ 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* cn, FunctionNode* clone)
+{
+ const QList<RelatedClass>& rc = cn->baseClasses();
+ QList<RelatedClass>::ConstIterator r = rc.constBegin();
+ while (r != rc.constEnd()) {
+ FunctionNode* func;
+ if ((*r).node_) {
+ if (((func = findVirtualFunctionInBaseClasses((*r).node_, clone)) != 0 ||
+ (func = (*r).node_->findFunctionNode(clone)) != 0)) {
+ if (func->virtualness() != FunctionNode::NonVirtual)
+ return func;
+ }
+ }
+ ++r;
+ }
+ return 0;
+}
+
+/*!
+ */
+NodeList Tree::allBaseClasses(const ClassNode* classNode) const
+{
+ NodeList result;
+ foreach (const RelatedClass& r, classNode->baseClasses()) {
+ if (r.node_) {
+ result += r.node_;
+ result += allBaseClasses(r.node_);
+ }
+ }
+ return result;
+}
+
+/*!
+ Find the node with the specified \a path name that is of
+ the specified \a type and \a subtype. Begin the search at
+ the \a start node. If the \a start node is 0, begin the
+ search at the tree root. \a subtype is not used unless
+ \a type is \c{Document}.
+ */
+Node* Tree::findNodeByNameAndType(const QStringList& path, Node::NodeType type) const
+{
+ return findNodeRecursive(path, 0, root(), type);
+}
+
+/*!
+ Recursive search for a node identified by \a path. Each
+ path element is a name. \a pathIndex specifies the index
+ of the name in \a path to try to match. \a start is the
+ node whose children shoulod be searched for one that has
+ that name. Each time a match is found, increment the
+ \a pathIndex and call this function recursively.
+
+ If the end of the path is reached (i.e. if a matching
+ node is found for each name in the \a path), the \a type
+ must match the type of the last matching node, and if the
+ type is \e{Document}, the \a subtype must match as well.
+
+ If the algorithm is successful, the pointer to the final
+ node is returned. Otherwise 0 is returned.
+ */
+Node* Tree::findNodeRecursive(const QStringList& path,
+ int pathIndex,
+ const Node* start,
+ Node::NodeType type) const
+{
+ if (!start || path.isEmpty())
+ return 0; // no place to start, or nothing to search for.
+ Node* node = const_cast<Node*>(start);
+ if (start->isLeaf()) {
+ if (pathIndex >= path.size())
+ return node; // found a match.
+ return 0; // premature leaf
+ }
+
+ Aggregate* current = static_cast<Aggregate*>(node);
+ const NodeList& children = current->childNodes();
+ const QString& name = path.at(pathIndex);
+ for (int i=0; i<children.size(); ++i) {
+ Node* n = children.at(i);
+ if (!n)
+ continue;
+ if (n->isQmlPropertyGroup()) {
+ if (type == Node::QmlProperty) {
+ n = findNodeRecursive(path, pathIndex, n, type);
+ if (n)
+ return n;
+ }
+ }
+ else if (n->name() == name) {
+ if (pathIndex+1 >= path.size()) {
+ if ((n->type() == type) || (type == Node::NoType))
+ return n;
+ continue;
+ }
+ else { // Search the children of n for the next name in the path.
+ n = findNodeRecursive(path, pathIndex+1, n, type);
+ if (n)
+ return n;
+ }
+ }
+ }
+ return 0;
+}
+
+/*!
+ Recursive search for a node identified by \a path. Each
+ path element is a name. \a pathIndex specifies the index
+ of the name in \a path to try to match. \a start is the
+ node whose children shoulod be searched for one that has
+ that name. Each time a name match is found, increment the
+ \a pathIndex and call this function recursively.
+
+ If the end of the path is reached (i.e. if a matching
+ node is found for each name in the \a path), test the
+ matching node's type and subtype values against the ones
+ listed in \a types. If a match is found there, return the
+ pointer to the final node. Otherwise return 0.
+ */
+Node* Tree::findNodeRecursive(const QStringList& path,
+ int pathIndex,
+ Node* start,
+ const NodeTypeList& types) const
+{
+ if (!start || path.isEmpty())
+ return 0;
+ if (start->isLeaf())
+ return ((pathIndex >= path.size()) ? start : 0);
+ if (pathIndex >= path.size())
+ return 0;
+
+ Aggregate* current = static_cast<Aggregate*>(start);
+ const NodeList& children = current->childNodes();
+ for (int i=0; i<children.size(); ++i) {
+ Node* n = children.at(i);
+ if (n && n->name() == path.at(pathIndex)) {
+ if (pathIndex+1 >= path.size()) {
+ if (n->match(types))
+ return n;
+ }
+ else if (!n->isLeaf()) {
+ n = findNodeRecursive(path, pathIndex+1, n, types);
+ if (n)
+ return n;
+ }
+ }
+ }
+ return 0;
+}
+
+/*!
+ Searches the tree for a node that matches the \a path plus
+ the \a target. The search begins at \a start and moves up
+ the parent chain from there, or, if \a start is 0, the search
+ begins at the root.
+
+ The \a flags can indicate whether to search base classes and/or
+ the enum values in enum types. \a genus can be a further restriction
+ on what kind of node is an acceptible match, i.e. CPP or QML.
+
+ If a matching node is found, \a ref is an output parameter that
+ is set to the HTML reference to use for the link.
+ */
+const Node* Tree::findNodeForTarget(const QStringList& path,
+ const QString& target,
+ const Node* start,
+ int flags,
+ Node::Genus genus,
+ QString& ref) const
+{
+ const Node* node = 0;
+ if ((genus == Node::DontCare) || (genus == Node::DOC)) {
+ node = findDocumentNodeByTitle(path.at(0));
+ if (node) {
+ if (!target.isEmpty()) {
+ ref = getRef(target, node);
+ if (ref.isEmpty())
+ node = 0;
+ }
+ if (node)
+ return node;
+ }
+ }
+
+ node = findUnambiguousTarget(path.join(QStringLiteral("::")), ref);
+ if (node) {
+ if (!target.isEmpty()) {
+ ref = getRef(target, node);
+ if (ref.isEmpty())
+ node = 0;
+ }
+ if (node)
+ return node;
+ }
+
+ const Node* current = start;
+ if (!current)
+ current = root();
+
+ /*
+ If the path contains one or two double colons ("::"),
+ check first to see if the first two path strings refer
+ to a QML element. If they do, path[0] will be the QML
+ module identifier, and path[1] will be the QML type.
+ If the answer is yes, the reference identifies a QML
+ type node.
+ */
+ int path_idx = 0;
+ if (((genus == Node::QML) || (genus == Node::DontCare)) &&
+ (path.size() >= 2) && !path[0].isEmpty()) {
+ QmlTypeNode* qcn = lookupQmlType(QString(path[0] + "::" + path[1]));
+ if (qcn) {
+ current = qcn;
+ if (path.size() == 2) {
+ if (!target.isEmpty()) {
+ ref = getRef(target, current);
+ if (!ref.isEmpty())
+ return current;
+ return 0;
+ }
+ else
+ return current;
+ }
+ path_idx = 2;
+ }
+ }
+
+ while (current) {
+ if (current->isAggregate()) {
+ const Node* node = matchPathAndTarget(path, path_idx, target, current, flags, genus, ref);
+ if (node)
+ return node;
+ }
+ current = current->parent();
+ path_idx = 0;
+ }
+ return 0;
+}
+
+/*!
+ First, the \a path is used to find a node. The \a path
+ matches some part of the node's fully quallified name.
+ If the \a target is not empty, it must match a target
+ in the matching node. If the matching of the \a path
+ and the \a target (if present) is successful, \a ref
+ is set from the \a target, and the pointer to the
+ matching node is returned. \a idx is the index into the
+ \a path where to begin the matching. The function is
+ recursive with idx being incremented for each recursive
+ call.
+
+ The matching node must be of the correct \a genus, i.e.
+ either QML or C++, but \a genus can be set to \c DontCare.
+ \a flags indicates whether to search base classes and
+ whether to search for an enum value. \a node points to
+ the node where the search should begin, assuming the
+ \a path is a not a fully-qualified name. \a node is
+ most often the root of this Tree.
+ */
+const Node* Tree::matchPathAndTarget(const QStringList& path,
+ int idx,
+ const QString& target,
+ const Node* node,
+ int flags,
+ Node::Genus genus,
+ QString& ref) const
+{
+ /*
+ If the path has been matched, then if there is a target,
+ try to match the target. If there is a target, but you
+ can't match it at the end of the path, give up; return 0.
+ */
+ if (idx == path.size()) {
+ if (!target.isEmpty()) {
+ ref = getRef(target, node);
+ if (ref.isEmpty())
+ return 0;
+ }
+ if (node->isFunction() && node->name() == node->parent()->name())
+ node = node->parent();
+ return node;
+ }
+
+ const Node* t = 0;
+ QString name = path.at(idx);
+ QList<Node*> nodes;
+ node->findChildren(name, nodes);
+
+ foreach (const Node* n, nodes) {
+ if (genus != Node::DontCare) {
+ if (n->genus() != genus)
+ continue;
+ }
+ t = matchPathAndTarget(path, idx+1, target, n, flags, genus, ref);
+ if (t && !t->isPrivate())
+ return t;
+ }
+ if (target.isEmpty()) {
+ if ((idx) == (path.size()-1) && node->isAggregate() && (flags & SearchEnumValues)) {
+ t = static_cast<const Aggregate*>(node)->findEnumNodeForValue(path.at(idx));
+ if (t)
+ return t;
+ }
+ }
+ if (((genus == Node::CPP) || (genus == Node::DontCare)) &&
+ node->isClass() && (flags & SearchBaseClasses)) {
+ NodeList baseClasses = allBaseClasses(static_cast<const ClassNode*>(node));
+ foreach (const Node* bc, baseClasses) {
+ t = matchPathAndTarget(path, idx, target, bc, flags, genus, ref);
+ if (t && ! t->isPrivate())
+ return t;
+ if (target.isEmpty()) {
+ if ((idx) == (path.size()-1) && (flags & SearchEnumValues)) {
+ t = static_cast<const Aggregate*>(bc)->findEnumNodeForValue(path.at(idx));
+ if (t)
+ return t;
+ }
+ }
+ }
+ }
+ return 0;
+}
+
+/*!
+ Searches the tree for a node that matches the \a path. The
+ search begins at \a start but can move up the parent chain
+ recursively if no match is found.
+
+ This findNode() callse the other findNode(), which is not
+ called anywhere else.
+ */
+const Node* Tree::findNode(const QStringList& path,
+ const Node* start,
+ int findFlags,
+ Node::Genus genus) const
+{
+ const Node* current = start;
+ if (!current)
+ current = root();
+
+ do {
+ const Node* node = current;
+ int i;
+ int start_idx = 0;
+
+ /*
+ If the path contains one or two double colons ("::"),
+ check first to see if the first two path strings refer
+ to a QML element. If they do, path[0] will be the QML
+ module identifier, and path[1] will be the QML type.
+ If the answer is yes, the reference identifies a QML
+ type node.
+ */
+ if (((genus == Node::QML) || (genus == Node::DontCare)) &&
+ (path.size() >= 2) && !path[0].isEmpty()) {
+ QmlTypeNode* qcn = lookupQmlType(QString(path[0] + "::" + path[1]));
+ if (qcn) {
+ node = qcn;
+ if (path.size() == 2)
+ return node;
+ start_idx = 2;
+ }
+ }
+
+ for (i = start_idx; i < path.size(); ++i) {
+ if (node == 0 || !node->isAggregate())
+ break;
+
+ const Node* next = static_cast<const Aggregate*>(node)->findChildNode(path.at(i), genus);
+ if (!next && (findFlags & SearchEnumValues) && i == path.size()-1) {
+ next = static_cast<const Aggregate*>(node)->findEnumNodeForValue(path.at(i));
+ }
+ if (!next && ((genus == Node::CPP) || (genus == Node::DontCare)) &&
+ node->isClass() && (findFlags & SearchBaseClasses)) {
+ NodeList baseClasses = allBaseClasses(static_cast<const ClassNode*>(node));
+ foreach (const Node* baseClass, baseClasses) {
+ next = static_cast<const Aggregate*>(baseClass)->findChildNode(path.at(i), genus);
+ if (!next && (findFlags & SearchEnumValues) && i == path.size() - 1)
+ next = static_cast<const Aggregate*>(baseClass)->findEnumNodeForValue(path.at(i));
+ if (next) {
+ break;
+ }
+ }
+ }
+ node = next;
+ }
+ if (node && i == path.size())
+ return node;
+ current = current->parent();
+ } while (current);
+
+ return 0;
+}
+
+/*!
+ This function searches for a node with a canonical title
+ constructed from \a target. If the node it finds is \a node,
+ it returns the ref from that node. Otherwise it returns an
+ empty string.
+ */
+QString Tree::getRef(const QString& target, const Node* node) const
+{
+ TargetMap::const_iterator i = nodesByTargetTitle_.constFind(target);
+ if (i != nodesByTargetTitle_.constEnd()) {
+ do {
+ if (i.value()->node_ == node)
+ return i.value()->ref_;
+ ++i;
+ } while (i != nodesByTargetTitle_.constEnd() && i.key() == target);
+ }
+ QString key = Doc::canonicalTitle(target);
+ i = nodesByTargetRef_.constFind(key);
+ if (i != nodesByTargetRef_.constEnd()) {
+ do {
+ if (i.value()->node_ == node)
+ return i.value()->ref_;
+ ++i;
+ } while (i != nodesByTargetRef_.constEnd() && i.key() == key);
+ }
+ return QString();
+}
+
+/*!
+ Inserts a new target into the target table. \a name is the
+ key. The target record contains the \a type, a pointer to
+ the \a node, the \a priority. and a canonicalized form of
+ the \a name, which is later used.
+ */
+void Tree::insertTarget(const QString& name,
+ const QString& title,
+ TargetRec::TargetType type,
+ Node* node,
+ int priority)
+{
+ TargetRec* target = new TargetRec(name, title, type, node, priority);
+ nodesByTargetRef_.insert(name, target);
+ nodesByTargetTitle_.insert(title, target);
+}
+
+/*!
+ */
+void Tree::resolveTargets(Aggregate* root)
+{
+ foreach (Node* child, root->childNodes()) {
+ if (child->type() == Node::Document) {
+ DocumentNode* node = static_cast<DocumentNode*>(child);
+ QString key = node->title();
+ if (!key.isEmpty()) {
+ if (key.contains(QChar(' ')))
+ key = Doc::canonicalTitle(key);
+ QList<DocumentNode*> nodes = docNodesByTitle_.values(key);
+ bool alreadyThere = false;
+ if (!nodes.empty()) {
+ for (int i=0; i< nodes.size(); ++i) {
+ if (nodes[i]->docSubtype() == Node::ExternalPage) {
+ if (node->name() == nodes[i]->name()) {
+ alreadyThere = true;
+ break;
+ }
+ }
+ }
+ }
+ if (!alreadyThere)
+ docNodesByTitle_.insert(key, node);
+ }
+ }
+
+ if (child->doc().hasTableOfContents()) {
+ const QList<Atom*>& toc = child->doc().tableOfContents();
+ for (int i = 0; i < toc.size(); ++i) {
+ QString ref = refForAtom(toc.at(i));
+ QString title = Text::sectionHeading(toc.at(i)).toString();
+ if (!ref.isEmpty() && !title.isEmpty()) {
+ QString key = Doc::canonicalTitle(title);
+ TargetRec* target = new TargetRec(ref, title, TargetRec::Contents, child, 3);
+ nodesByTargetRef_.insert(key, target);
+ nodesByTargetTitle_.insert(title, target);
+ }
+ }
+ }
+ if (child->doc().hasKeywords()) {
+ const QList<Atom*>& keywords = child->doc().keywords();
+ for (int i = 0; i < keywords.size(); ++i) {
+ QString ref = refForAtom(keywords.at(i));
+ QString title = keywords.at(i)->string();
+ if (!ref.isEmpty() && !title.isEmpty()) {
+ TargetRec* target = new TargetRec(ref, title, TargetRec::Keyword, child, 1);
+ nodesByTargetRef_.insert(Doc::canonicalTitle(title), target);
+ nodesByTargetTitle_.insert(title, target);
+ }
+ }
+ }
+ if (child->doc().hasTargets()) {
+ const QList<Atom*>& targets = child->doc().targets();
+ for (int i = 0; i < targets.size(); ++i) {
+ QString ref = refForAtom(targets.at(i));
+ QString title = targets.at(i)->string();
+ if (!ref.isEmpty() && !title.isEmpty()) {
+ QString key = Doc::canonicalTitle(title);
+ TargetRec* target = new TargetRec(ref, title, TargetRec::Target, child, 2);
+ nodesByTargetRef_.insert(key, target);
+ nodesByTargetTitle_.insert(title, target);
+ }
+ }
+ }
+ if (child->isAggregate())
+ resolveTargets(static_cast<Aggregate*>(child));
+ }
+}
+
+/*!
+ This function searches for a \a target anchor node. If it
+ finds one, it sets \a ref and returns the found node.
+ */
+const Node*
+Tree::findUnambiguousTarget(const QString& target, QString& ref) const
+{
+ int numBestTargets = 0;
+ TargetRec* bestTarget = 0;
+ QList<TargetRec*> bestTargetList;
+
+ QString key = target;
+ TargetMap::const_iterator i = nodesByTargetTitle_.find(key);
+ while (i != nodesByTargetTitle_.constEnd()) {
+ if (i.key() != key)
+ break;
+ TargetRec* candidate = i.value();
+ if (!bestTarget || (candidate->priority_ < bestTarget->priority_)) {
+ bestTarget = candidate;
+ bestTargetList.clear();
+ bestTargetList.append(candidate);
+ numBestTargets = 1;
+ } else if (candidate->priority_ == bestTarget->priority_) {
+ bestTargetList.append(candidate);
+ ++numBestTargets;
+ }
+ ++i;
+ }
+ if (bestTarget) {
+ ref = bestTarget->ref_;
+ return bestTarget->node_;
+ }
+
+ numBestTargets = 0;
+ bestTarget = 0;
+ key = Doc::canonicalTitle(target);
+ i = nodesByTargetRef_.find(key);
+ while (i != nodesByTargetRef_.constEnd()) {
+ if (i.key() != key)
+ break;
+ TargetRec* candidate = i.value();
+ if (!bestTarget || (candidate->priority_ < bestTarget->priority_)) {
+ bestTarget = candidate;
+ bestTargetList.clear();
+ bestTargetList.append(candidate);
+ numBestTargets = 1;
+ } else if (candidate->priority_ == bestTarget->priority_) {
+ bestTargetList.append(candidate);
+ ++numBestTargets;
+ }
+ ++i;
+ }
+ if (bestTarget) {
+ ref = bestTarget->ref_;
+ return bestTarget->node_;
+ }
+
+ ref.clear();
+ return 0;
+}
+
+/*!
+ This function searches for a node with the specified \a title.
+ */
+const DocumentNode* Tree::findDocumentNodeByTitle(const QString& title) const
+{
+ DocumentNodeMultiMap::const_iterator i;
+ if (title.contains(QChar(' ')))
+ i = docNodesByTitle_.constFind(Doc::canonicalTitle(title));
+ else
+ i = docNodesByTitle_.constFind(title);
+ if (i != docNodesByTitle_.constEnd()) {
+ /*
+ Reporting all these duplicate section titles is probably
+ overkill. We should report the duplicate file and let
+ that suffice.
+ */
+ DocumentNodeMultiMap::const_iterator j = i;
+ ++j;
+ if (j != docNodesByTitle_.constEnd() && j.key() == i.key()) {
+ while (j != docNodesByTitle_.constEnd()) {
+ if (j.key() == i.key() && j.value()->url().isEmpty()) {
+ break; // Just report one duplicate for now.
+ }
+ ++j;
+ }
+ if (j != docNodesByTitle_.cend()) {
+ i.value()->location().warning("This page title exists in more than one file: " + title);
+ j.value()->location().warning("[It also exists here]");
+ }
+ }
+ return i.value();
+ }
+ return 0;
+}
+
+/*!
+ Returns a canonical title for the \a atom, if the \a atom
+ is a SectionLeft or a Target.
+ */
+QString Tree::refForAtom(const Atom* atom)
+{
+ if (atom) {
+ if (atom->type() == Atom::SectionLeft)
+ return Doc::canonicalTitle(Text::sectionHeading(atom).toString());
+ if ((atom->type() == Atom::Target) || (atom->type() == Atom::Keyword))
+ return Doc::canonicalTitle(atom->string());
+ }
+ return QString();
+}
+
+/*!
+ \fn const CNMap& Tree::groups() const
+ Returns a const reference to the collection of all
+ group nodes.
+*/
+
+/*!
+ \fn const ModuleMap& Tree::modules() const
+ Returns a const reference to the collection of all
+ module nodes.
+*/
+
+/*!
+ \fn const QmlModuleMap& Tree::qmlModules() const
+ Returns a const reference to the collection of all
+ QML module nodes.
+*/
+
+/*!
+ Returns a pointer to the collection map specified by \a genus.
+ Returns null if \a genus is not specified.
+ */
+CNMap* Tree::getCollectionMap(Node::Genus genus)
+{
+ switch (genus) {
+ case Node::DOC:
+ return &groups_;
+ case Node::CPP:
+ return &modules_;
+ case Node::QML:
+ return &qmlModules_;
+ case Node::JS:
+ return &jsModules_;
+ default:
+ break;
+ }
+ return 0;
+}
+
+/*!
+ Returns a pointer to the collection named \a name of the
+ specified \a genus in this tree. If there is no matching
+ collection in this tree, 0 is returned.
+ */
+CollectionNode* Tree::getCollection(const QString& name, Node::Genus genus)
+{
+ CNMap* m = getCollectionMap(genus);
+ if (m) {
+ CNMap::const_iterator i = m->constFind(name);
+ if (i != m->cend())
+ return i.value();
+ }
+ return 0;
+}
+
+/*!
+ Find the group, module, QML module, or JavaScript module
+ named \a name and return a pointer to that collection node.
+ \a genus specifies which kind of collection node you want.
+ If a collection node with the specified \a name and \a genus
+ is not found, a new one is created, and the pointer to the
+ new one is returned.
+
+ If a new collection node is created, its parent is the tree
+ root, and the new collection node is marked \e{not seen}.
+
+ \a genus must be specified, i.e. it must not be \c{DontCare}.
+ If it is \c{DontCare}, 0 is returned, which is a programming
+ error.
+ */
+CollectionNode* Tree::findCollection(const QString& name, Node::Genus genus)
+{
+ CNMap* m = getCollectionMap(genus);
+ if (!m) // error
+ return 0;
+ CNMap::const_iterator i = m->constFind(name);
+ if (i != m->cend())
+ return i.value();
+ Node::NodeType t = Node::NoType;
+ switch (genus) {
+ case Node::DOC:
+ t = Node::Group;
+ break;
+ case Node::CPP:
+ t = Node::Module;
+ break;
+ case Node::QML:
+ t = Node::QmlModule;
+ break;
+ case Node::JS:
+ t = Node::QmlModule;
+ break;
+ default:
+ break;
+ }
+ CollectionNode* cn = new CollectionNode(t, root(), name, genus);
+ cn->markNotSeen();
+ m->insert(name, cn);
+ return cn;
+}
+
+/*! \fn CollectionNode* Tree::findGroup(const QString& name)
+ Find the group node named \a name and return a pointer
+ to it. If the group node is not found, add a new group
+ node named \a name and return a pointer to the new one.
+
+ If a new group node is added, its parent is the tree root,
+ and the new group node is marked \e{not seen}.
+ */
+
+/*! \fn CollectionNode* Tree::findModule(const QString& name)
+ Find the module node named \a name and return a pointer
+ to it. If a matching node is not found, add a new module
+ node named \a name and return a pointer to that one.
+
+ If a new module node is added, its parent is the tree root,
+ and the new module node is marked \e{not seen}.
+ */
+
+/*! \fn CollectionNode* Tree::findQmlModule(const QString& name)
+ Find the QML module node named \a name and return a pointer
+ to it. If a matching node is not found, add a new QML module
+ node named \a name and return a pointer to that one.
+
+ If a new QML module node is added, its parent is the tree root,
+ and the new node is marked \e{not seen}.
+ */
+
+/*! \fn CollectionNode* Tree::findJsModule(const QString& name)
+ Find the JavaScript module named \a name and return a pointer
+ to it. If a matching node is not found, add a new JavaScript
+ module node named \a name and return a pointer to that one.
+
+ If a new JavaScript module node is added, its parent is the
+ tree root, and the new node is marked \e{not seen}.
+ */
+
+/*! \fn CollectionNode* Tree::addGroup(const QString& name)
+ Looks up the group node named \a name in the collection
+ of all group nodes. If a match is found, a pointer to the
+ node is returned. Otherwise, a new group node named \a name
+ is created and inserted into the collection, and the pointer
+ to that node is returned.
+ */
+
+/*! \fn CollectionNode* Tree::addModule(const QString& name)
+ Looks up the module node named \a name in the collection
+ of all module nodes. If a match is found, a pointer to the
+ node is returned. Otherwise, a new module node named \a name
+ is created and inserted into the collection, and the pointer
+ to that node is returned.
+ */
+
+/*! \fn CollectionNode* Tree::addQmlModule(const QString& name)
+ Looks up the QML module node named \a name in the collection
+ of all QML module nodes. If a match is found, a pointer to the
+ node is returned. Otherwise, a new QML module node named \a name
+ is created and inserted into the collection, and the pointer
+ to that node is returned.
+ */
+
+/*! \fn CollectionNode* Tree::addJsModule(const QString& name)
+ Looks up the JavaScript module node named \a name in the collection
+ of all JavaScript module nodes. If a match is found, a pointer to the
+ node is returned. Otherwise, a new JavaScrpt module node named \a name
+ is created and inserted into the collection, and the pointer
+ to that node is returned.
+ */
+
+/*!
+ Looks up the group node named \a name in the collection
+ of all group nodes. If a match is not found, a new group
+ node named \a name is created and inserted into the collection.
+ Then append \a node to the group's members list, and append the
+ group name to the list of group names in \a node. The parent of
+ \a node is not changed by this function. Returns a pointer to
+ the group node.
+ */
+CollectionNode* Tree::addToGroup(const QString& name, Node* node)
+{
+ CollectionNode* cn = findGroup(name);
+ if (!node->isInternal()) {
+ cn->addMember(node);
+ node->appendGroupName(name);
+ }
+ return cn;
+}
+
+/*!
+ Looks up the module node named \a name in the collection
+ of all module nodes. If a match is not found, a new module
+ node named \a name is created and inserted into the collection.
+ Then append \a node to the module's members list. The parent of
+ \a node is not changed by this function. Returns the module node.
+ */
+CollectionNode* Tree::addToModule(const QString& name, Node* node)
+{
+ CollectionNode* cn = findModule(name);
+ cn->addMember(node);
+ node->setPhysicalModuleName(name);
+ return cn;
+}
+
+/*!
+ Looks up the QML module named \a name. If it isn't there,
+ create it. Then append \a node to the QML module's member
+ list. The parent of \a node is not changed by this function.
+ Returns the pointer to the QML module node.
+ */
+CollectionNode* Tree::addToQmlModule(const QString& name, Node* node)
+{
+ QStringList qmid;
+ QStringList dotSplit;
+ QStringList blankSplit = name.split(QLatin1Char(' '));
+ qmid.append(blankSplit[0]);
+ if (blankSplit.size() > 1) {
+ qmid.append(blankSplit[0] + blankSplit[1]);
+ dotSplit = blankSplit[1].split(QLatin1Char('.'));
+ qmid.append(blankSplit[0] + dotSplit[0]);
+ }
+
+ CollectionNode* cn = findQmlModule(blankSplit[0]);
+ cn->addMember(node);
+ node->setQmlModule(cn);
+ if (node->isQmlType()) {
+ QmlTypeNode* n = static_cast<QmlTypeNode*>(node);
+ for (int i=0; i<qmid.size(); ++i) {
+ QString key = qmid[i] + "::" + node->name();
+ insertQmlType(key, n);
+ }
+ }
+ return cn;
+}
+
+/*!
+ Looks up the QML module named \a name. If it isn't there,
+ create it. Then append \a node to the QML module's member
+ list. The parent of \a node is not changed by this function.
+ Returns the pointer to the QML module node.
+ */
+CollectionNode* Tree::addToJsModule(const QString& name, Node* node)
+{
+ QStringList qmid;
+ QStringList dotSplit;
+ QStringList blankSplit = name.split(QLatin1Char(' '));
+ qmid.append(blankSplit[0]);
+ if (blankSplit.size() > 1) {
+ qmid.append(blankSplit[0] + blankSplit[1]);
+ dotSplit = blankSplit[1].split(QLatin1Char('.'));
+ qmid.append(blankSplit[0] + dotSplit[0]);
+ }
+
+ CollectionNode* cn = findJsModule(blankSplit[0]);
+ cn->addMember(node);
+ node->setQmlModule(cn);
+ if (node->isJsType()) {
+ QmlTypeNode* n = static_cast<QmlTypeNode*>(node);
+ for (int i=0; i<qmid.size(); ++i) {
+ QString key = qmid[i] + "::" + node->name();
+ insertQmlType(key, n);
+ }
+ }
+ return cn;
+}
+
+/*!
+ If the QML type map does not contain \a key, insert node
+ \a n with the specified \a key.
+ */
+void Tree::insertQmlType(const QString& key, QmlTypeNode* n)
+{
+ if (!qmlTypeMap_.contains(key))
+ qmlTypeMap_.insert(key,n);
+}
+
+/*!
+ Split \a target on "::" and find the function node with that
+ path.
+ */
+const Node* Tree::findFunctionNode(const QString& target,
+ const QString& params,
+ const Node* relative,
+ Node::Genus genus) const
+{
+ QString t = target;
+ if (t.endsWith("()")) {
+ t.chop(2);
+ }
+ QStringList path = t.split("::");
+ const FunctionNode* fn = findFunctionNode(path, params, relative, SearchBaseClasses, genus);
+ if (fn && fn->metaness() != FunctionNode::MacroWithoutParams)
+ return fn;
+ return 0;
+}
+
+/*!
+ Search for a node that is identified by \a name.
+ Return a pointer to a matching node, or 0.
+*/
+const Node* Tree::checkForCollision(const QString& name)
+{
+ return findNode(QStringList(name), 0, 0, Node::DontCare);
+}
+
+/*!
+ Generate a target of the form link-nnn, where the nnn is
+ the current link count for this tree. This target string
+ is returned. It will be output as an HTML anchor just before
+ an HTML link to the node \a t.
+
+ The node \a t
+ */
+QString Tree::getNewLinkTarget(const Node* locNode,
+ const Node* t,
+ const QString& fileName,
+ QString& text,
+ bool broken)
+{
+ QString physicalModuleName;
+ if (t && !broken) {
+ Tree* tree = t->tree();
+ if (tree != this)
+ tree->incrementLinkCount();
+ physicalModuleName = tree->physicalModuleName();
+ }
+ else
+ physicalModuleName = "broken";
+ incrementLinkCount();
+ QString target = QString("qa-target-%1").arg(-(linkCount()));
+ TargetLoc* tloc = new TargetLoc(locNode, target, fileName, text, broken);
+ TargetList* tList = 0;
+ TargetListMap::iterator i = targetListMap_->find(physicalModuleName);
+ if (i == targetListMap_->end()) {
+ tList = new TargetList;
+ i = targetListMap_->insert(physicalModuleName, tList);
+ }
+ else
+ tList = i.value();
+ tList->append(tloc);
+ return target;
+}
+
+/*!
+ Look up the target list for the specified \a module
+ and return a pointer to it.
+ */
+TargetList* Tree::getTargetList(const QString& module)
+{
+ return targetListMap_->value(module);
+}
+
+QT_END_NAMESPACE
diff --git a/src/qdoc/tree.h b/src/qdoc/tree.h
new file mode 100644
index 000000000..1fef15bc6
--- /dev/null
+++ b/src/qdoc/tree.h
@@ -0,0 +1,255 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL21$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** 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 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** As a special exception, The Qt Company gives you certain additional
+** rights. These rights are described in The Qt Company LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*
+ tree.h
+*/
+
+#ifndef TREE_H
+#define TREE_H
+
+#include <QtCore/qstack.h>
+#include "node.h"
+
+QT_BEGIN_NAMESPACE
+
+class QStringList;
+class QDocDatabase;
+
+struct TargetRec
+{
+ public:
+ enum TargetType { Unknown, Target, Keyword, Contents, Class, Function, Page, Subtitle };
+
+ TargetRec(const QString& name,
+ const QString& title,
+ TargetRec::TargetType type,
+ Node* node,
+ int priority)
+ : node_(node), ref_(name), title_(title), priority_(priority), type_(type) {
+ // Discard the dedicated ref for keywords - they always
+ // link to the top of the QDoc comment they appear in
+ if (type == Keyword)
+ ref_.clear();
+ }
+
+ bool isEmpty() const { return ref_.isEmpty(); }
+
+ Node* node_;
+ QString ref_;
+ QString title_;
+ int priority_;
+ TargetType type_;
+};
+
+struct TargetLoc
+{
+ public:
+ TargetLoc(const Node* loc, const QString& t, const QString& fileName, const QString& text, bool broken)
+ : loc_(loc), target_(t), fileName_(fileName), text_(text), broken_(broken) { }
+ const Node* loc_;
+ QString target_;
+ QString fileName_;
+ QString text_;
+ bool broken_;
+};
+
+typedef QMultiMap<QString, TargetRec*> TargetMap;
+typedef QMultiMap<QString, DocumentNode*> DocumentNodeMultiMap;
+typedef QMap<QString, QmlTypeNode*> QmlTypeMap;
+typedef QMultiMap<QString, const ExampleNode*> ExampleNodeMap;
+typedef QVector<TargetLoc*> TargetList;
+typedef QMap<QString, TargetList*> TargetListMap;
+
+class Tree
+{
+ private:
+ friend class QDocForest;
+ friend class QDocDatabase;
+
+ typedef QMap<PropertyNode::FunctionRole, QString> RoleMap;
+ typedef QMap<PropertyNode*, RoleMap> PropertyMap;
+
+ Tree(const QString& camelCaseModuleName, QDocDatabase* qdb);
+ ~Tree();
+
+ Node* findNodeForInclude(const QStringList& path) const;
+ ClassNode* findClassNode(const QStringList& path, const Node* start = 0) const;
+ NamespaceNode* findNamespaceNode(const QStringList& path) const;
+ FunctionNode* findFunctionNode(const QStringList& parentPath, const FunctionNode* clone);
+ const Node* findFunctionNode(const QString& target,
+ const QString& params,
+ const Node* relative,
+ Node::Genus genus) const;
+
+ Node* findNodeRecursive(const QStringList& path,
+ int pathIndex,
+ const Node* start,
+ Node::NodeType type) const;
+ Node* findNodeRecursive(const QStringList& path,
+ int pathIndex,
+ Node* start,
+ const NodeTypeList& types) const;
+
+ const Node* findNodeForTarget(const QStringList& path,
+ const QString& target,
+ const Node* node,
+ int flags,
+ Node::Genus genus,
+ QString& ref) const;
+ const Node* matchPathAndTarget(const QStringList& path,
+ int idx,
+ const QString& target,
+ const Node* node,
+ int flags,
+ Node::Genus genus,
+ QString& ref) const;
+
+ const Node* findNode(const QStringList &path,
+ const Node* relative, // = 0,
+ int findFlags, // = 0,
+ Node::Genus genus) const; // = Node::DontCare) const;
+
+ QmlTypeNode* findQmlTypeNode(const QStringList& path);
+
+ Node* findNodeByNameAndType(const QStringList& path, Node::NodeType type) const;
+ Aggregate* findRelatesNode(const QStringList& path);
+ QString getRef(const QString& target, const Node* node) const;
+ void insertTarget(const QString& name,
+ const QString& title,
+ TargetRec::TargetType type,
+ Node* node,
+ int priority);
+ void resolveTargets(Aggregate* root);
+ const Node* findUnambiguousTarget(const QString& target, QString& ref) const;
+ const DocumentNode* findDocumentNodeByTitle(const QString& title) const;
+
+ void addPropertyFunction(PropertyNode *property,
+ const QString &funcName,
+ PropertyNode::FunctionRole funcRole);
+ void resolveInheritance(Aggregate* n = 0);
+ void resolveInheritanceHelper(int pass, ClassNode* cn);
+ void resolveProperties();
+ void resolveCppToQmlLinks();
+ void resolveUsingClauses();
+ void fixInheritance(NamespaceNode *rootNode = 0);
+ NamespaceNode *root() { return &root_; }
+
+ const FunctionNode *findFunctionNode(const QStringList &path,
+ const QString& params,
+ const Node *relative = 0,
+ int findFlags = 0,
+ Node::Genus genus = Node::DontCare) const;
+ const NamespaceNode *root() const { return &root_; }
+
+ FunctionNode *findVirtualFunctionInBaseClasses(ClassNode *classe,
+ FunctionNode *clone);
+ NodeList allBaseClasses(const ClassNode *classe) const;
+ QString refForAtom(const Atom* atom);
+
+ CNMap* getCollectionMap(Node::Genus genus);
+ const CNMap& groups() const { return groups_; }
+ const CNMap& modules() const { return modules_; }
+ const CNMap& qmlModules() const { return qmlModules_; }
+ const CNMap& jsModules() const { return jsModules_; }
+
+ CollectionNode* getCollection(const QString& name, Node::Genus genus);
+ CollectionNode* findCollection(const QString& name, Node::Genus genus);
+
+ CollectionNode* findGroup(const QString& name) { return findCollection(name, Node::DOC); }
+ CollectionNode* findModule(const QString& name) { return findCollection(name, Node::CPP); }
+ CollectionNode* findQmlModule(const QString& name) { return findCollection(name, Node::QML); }
+ CollectionNode* findJsModule(const QString& name) { return findCollection(name, Node::JS); }
+
+ CollectionNode* addGroup(const QString& name) { return findGroup(name); }
+ CollectionNode* addModule(const QString& name) { return findModule(name); }
+ CollectionNode* addQmlModule(const QString& name) { return findQmlModule(name); }
+ CollectionNode* addJsModule(const QString& name) { return findJsModule(name); }
+
+ CollectionNode* addToGroup(const QString& name, Node* node);
+ CollectionNode* addToModule(const QString& name, Node* node);
+ CollectionNode* addToQmlModule(const QString& name, Node* node);
+ CollectionNode* addToJsModule(const QString& name, Node* node);
+
+ QmlTypeNode* lookupQmlType(const QString& name) const { return qmlTypeMap_.value(name); }
+ void insertQmlType(const QString& key, QmlTypeNode* n);
+ void addExampleNode(ExampleNode* n) { exampleNodeMap_.insert(n->title(), n); }
+ ExampleNodeMap& exampleNodeMap() { return exampleNodeMap_; }
+ const Node* checkForCollision(const QString& name);
+ void setIndexFileName(const QString& t) { indexFileName_ = t; }
+
+ bool treeHasBeenAnalyzed() const { return treeHasBeenAnalyzed_; }
+ bool docsHaveBeenGenerated() const { return docsHaveBeenGenerated_; }
+ void setTreeHasBeenAnalyzed() { treeHasBeenAnalyzed_ = true; }
+ void setdocsHaveBeenGenerated() { docsHaveBeenGenerated_ = true; }
+ QString getNewLinkTarget(const Node* locNode,
+ const Node* t,
+ const QString& fileName,
+ QString& text,
+ bool broken);
+ TargetList* getTargetList(const QString& module);
+ QStringList getTargetListKeys() { return targetListMap_->keys(); }
+
+ public:
+ const QString& camelCaseModuleName() const { return camelCaseModuleName_; }
+ const QString& physicalModuleName() const { return physicalModuleName_; }
+ const QString& indexFileName() const { return indexFileName_; }
+ long incrementLinkCount() { return --linkCount_; }
+ void clearLinkCount() { linkCount_ = 0; }
+ long linkCount() const { return linkCount_; }
+
+private:
+ bool treeHasBeenAnalyzed_;
+ bool docsHaveBeenGenerated_;
+ long linkCount_;
+ QString camelCaseModuleName_;
+ QString physicalModuleName_;
+ QString indexFileName_;
+ QDocDatabase* qdb_;
+ NamespaceNode root_;
+ PropertyMap unresolvedPropertyMap;
+ DocumentNodeMultiMap docNodesByTitle_;
+ TargetMap nodesByTargetRef_;
+ TargetMap nodesByTargetTitle_;
+ CNMap groups_;
+ CNMap modules_;
+ CNMap qmlModules_;
+ CNMap jsModules_;
+ QmlTypeMap qmlTypeMap_;
+ ExampleNodeMap exampleNodeMap_;
+ TargetListMap* targetListMap_;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/qdoc/yyindent.cpp b/src/qdoc/yyindent.cpp
new file mode 100644
index 000000000..e48a84708
--- /dev/null
+++ b/src/qdoc/yyindent.cpp
@@ -0,0 +1,1182 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL21$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** 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 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** As a special exception, The Qt Company gives you certain additional
+** rights. These rights are described in The Qt Company LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** $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 \c true if string t is made only of white space; otherwise
+ returns \c 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 \c true if typedIn the same as okayCh or is null; otherwise
+ returns \c 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->constBegin() ) {
+ 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->constEnd();
+ --yyLinizerState->iter;
+ yyLinizerState->line = *yyLinizerState->iter;
+ readLine();
+}
+
+/*
+ Returns \c true if the start of the bottom line of yyProgram (and
+ potentially the whole line) is part of a C-style comment;
+ otherwise returns \c 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->constEnd();
+ --p; // skip bottom line
+
+ for ( int i = 0; i < BigRoof; i++ ) {
+ if ( p == yyProgram->constBegin() )
+ 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 \c 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 \c true if the current line (and upwards) forms a braceless
+ control statement; otherwise returns \c 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(QLatin1Char(')')) )
+ 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 \c 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(QLatin1Char(';')) &&
+ lastParen(*yyLine) == QChar('(') ) {
+ /*
+ Exception:
+
+ for ( int i = 1;
+ i < 10;
+ */
+ unf = true;
+ }
+ }
+
+ YY_RESTORE();
+ return unf;
+}
+
+/*
+ Returns \c 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 Qt'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(QLatin1Char(',')) &&
+ (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(QLatin1Char(',')) ) {
+ /*
+ 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(QLatin1Char(';')) || 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.constBegin();
+ while ( line != program.constEnd() ) {
+ p.push_back( *line );
+ QChar typedIn = firstNonWhiteSpace( *line );
+ if ( p.last().endsWith(QLatin1Char(':')) )
+ typedIn = ':';
+
+ int indent = indentForBottomLine( p, typedIn );
+
+ if ( !(*line).trimmed().isEmpty() ) {
+ for ( int j = 0; j < indent; j++ )
+ out += QLatin1Char(' ');
+ out += (*line).trimmed();
+ }
+ out += QLatin1Char('\n');
+ ++line;
+ }
+
+ while ( out.endsWith(QLatin1Char('\n')) )
+ out.truncate( out.length() - 1 );
+
+ printf( "%s\n", out.toLatin1().data() );
+ return 0;
+}
+
+#endif // Q_TEST_YYINDENT
diff --git a/src/src.pro b/src/src.pro
index 494898fee..387d54f70 100644
--- a/src/src.pro
+++ b/src/src.pro
@@ -14,6 +14,7 @@ qtHaveModule(widgets) {
}
SUBDIRS += linguist \
+ qdoc \
qtplugininfo
if(!android|android_app):!ios: SUBDIRS += qtpaths