diff options
Diffstat (limited to 'src/tools')
-rw-r--r-- | src/tools/qdoc/ditaxmlgenerator.cpp | 142 | ||||
-rw-r--r-- | src/tools/qdoc/ditaxmlgenerator.h | 6 | ||||
-rw-r--r-- | src/tools/qdoc/doc/config/qt-defines.qdocconf | 1 | ||||
-rw-r--r-- | src/tools/qdoc/doc/examples/componentset/uicomponents.qdoc | 7 | ||||
-rw-r--r-- | src/tools/qdoc/doc/examples/examples.qdoc | 24 | ||||
-rw-r--r-- | src/tools/qdoc/doc/qdoc-guide.qdoc | 38 | ||||
-rw-r--r-- | src/tools/qdoc/doc/qdoc-manual.qdoc | 56 | ||||
-rw-r--r-- | src/tools/qdoc/generator.cpp | 1916 | ||||
-rw-r--r-- | src/tools/qdoc/generator.h | 176 | ||||
-rw-r--r-- | src/tools/qdoc/helpprojectwriter.cpp | 8 | ||||
-rw-r--r-- | src/tools/qdoc/htmlgenerator.cpp | 270 | ||||
-rw-r--r-- | src/tools/qdoc/htmlgenerator.h | 16 | ||||
-rw-r--r-- | src/tools/qdoc/node.cpp | 2 | ||||
-rw-r--r-- | src/tools/qdoc/node.h | 1 | ||||
-rw-r--r-- | src/tools/qdoc/pagegenerator.cpp | 389 | ||||
-rw-r--r-- | src/tools/qdoc/pagegenerator.h | 99 | ||||
-rw-r--r-- | src/tools/qdoc/qdoc.pro | 2 | ||||
-rw-r--r-- | src/tools/qdoc/qmlvisitor.cpp | 10 | ||||
-rw-r--r-- | src/tools/qdoc/tree.cpp | 12 |
19 files changed, 1389 insertions, 1786 deletions
diff --git a/src/tools/qdoc/ditaxmlgenerator.cpp b/src/tools/qdoc/ditaxmlgenerator.cpp index f19f902c6b..25c92fb44f 100644 --- a/src/tools/qdoc/ditaxmlgenerator.cpp +++ b/src/tools/qdoc/ditaxmlgenerator.cpp @@ -43,6 +43,11 @@ ditaxmlgenerator.cpp */ +#include <QDebug> +#include <QList> +#include <qiterator.h> +#include <QTextCodec> +#include <QUuid> #include "codemarker.h" #include "codeparser.h" #include "ditaxmlgenerator.h" @@ -51,11 +56,6 @@ #include "separator.h" #include "tree.h" #include <ctype.h> -#include <qdebug.h> -#include <qlist.h> -#include <qiterator.h> -#include <qtextcodec.h> -#include <QUuid> QT_BEGIN_NAMESPACE @@ -328,8 +328,7 @@ DitaXmlGenerator::DitaTag DitaXmlGenerator::currentTag() /*! Write the start tag \c{<apiDesc>}. if \a title is not empty, generate a GUID from it and write the GUID as the - value of the \e{id} attribute. Then write \a title as - the value of the \e {spectitle} attribute. + value of the \e{id} attribute. Then if \a outputclass is not empty, write it as the value of the \a outputclass attribute. @@ -341,7 +340,8 @@ int DitaXmlGenerator::enterApiDesc(const QString& outputclass, const QString& ti writeStartTag(DT_apiDesc); if (!title.isEmpty()) { writeGuidAttribute(title); - xmlWriter().writeAttribute("spectitle",title); + //Are there cases where the spectitle is required? + //xmlWriter().writeAttribute("spectitle",title); } if (!outputclass.isEmpty()) xmlWriter().writeAttribute("outputclass",outputclass); @@ -636,7 +636,7 @@ void DitaXmlGenerator::generateTree(const Tree *tree) findAllNamespaces(tree->root()); findAllSince(tree->root()); - PageGenerator::generateTree(tree); + Generator::generateTree(tree); writeDitaMap(tree); } @@ -1770,13 +1770,15 @@ DitaXmlGenerator::generateClassLikeNode(const InnerNode* inner, CodeMarker* mark writeEndTag(); // <cxxClassDefinition> enterApiDesc(QString(),title); +#if 0 + // To be removed, if really not needed. Text brief = nsn->doc().briefText(); // zzz if (!brief.isEmpty()) { writeStartTag(DT_p); generateText(brief, nsn, marker); writeEndTag(); // </p> } - generateIncludes(nsn, marker); +#endif generateStatus(nsn, marker); generateThreadSafeness(nsn, marker); generateSince(nsn, marker); @@ -1904,13 +1906,15 @@ DitaXmlGenerator::generateClassLikeNode(const InnerNode* inner, CodeMarker* mark writeEndTag(); // <cxxClassDefinition> enterApiDesc(QString(),title); +#if 0 + // To be removed, if really not needed. Text brief = cn->doc().briefText(); // zzz if (!brief.isEmpty()) { writeStartTag(DT_p); generateText(brief, cn, marker); writeEndTag(); // </p> } - generateIncludes(cn, marker); +#endif generateStatus(cn, marker); generateInherits(cn, marker); generateInheritedBy(cn, marker); @@ -2028,13 +2032,15 @@ DitaXmlGenerator::generateClassLikeNode(const InnerNode* inner, CodeMarker* mark writeStartTag(DT_cxxClassDetail); enterApiDesc(QString(),title); +#if 0 + // To be removed, if really not needed. Text brief = fn->doc().briefText(); // zzz if (!brief.isEmpty()) { writeStartTag(DT_p); generateText(brief, fn, marker); writeEndTag(); // </p> } - generateIncludes(fn, marker); +#endif generateStatus(fn, marker); generateThreadSafeness(fn, marker); generateSince(fn, marker); @@ -2148,12 +2154,15 @@ DitaXmlGenerator::generateClassLikeNode(const InnerNode* inner, CodeMarker* mark writeStartTag(DT_cxxClassDetail); enterApiDesc(QString(),title); +#if 0 + // To be removed, if really not needed. Text brief = qcn->doc().briefText(); // zzz if (!brief.isEmpty()) { writeStartTag(DT_p); generateText(brief, qcn, marker); writeEndTag(); // </p> } +#endif generateQmlInstantiates(qcn, marker); generateQmlInherits(qcn, marker); generateQmlInheritedBy(qcn, marker); @@ -2517,19 +2526,6 @@ void DitaXmlGenerator::generateBrief(const Node* node, CodeMarker* marker) } /*! - Writes the \c {#include ...} required to include the class - or namespace in a compilation. - */ -void DitaXmlGenerator::generateIncludes(const InnerNode* inner, CodeMarker* marker) -{ - if (!inner->includes().isEmpty()) { - writeStartTag(DT_codeblock); - writeText(marker->markedUpIncludes(inner->includes()), marker, inner); - writeEndTag(); // </codeblock> - } -} - -/*! zzz Generates a table of contents beginning at \a node. Currently just returns without writing anything. @@ -3487,15 +3483,16 @@ QString DitaXmlGenerator::getMarkedUpSynopsis(const Node* node, } /*! - Renamed from highlightedCode() in the html generator. Writes - the \a markedCode to the current XML stream. + Renamed from highlightedCode() in the html generator. Gets the text + from \a markedCode , and then the text is written to the current XML + stream. */ void DitaXmlGenerator::writeText(const QString& markedCode, CodeMarker* marker, const Node* relative) { QString src = markedCode; - QString html; + QString text; QStringRef arg; QStringRef par1; @@ -3528,7 +3525,7 @@ void DitaXmlGenerator::writeText(const QString& markedCode, for (int k = 0; k != 13; ++k) { const QString & tag = spanTags[2 * k]; if (tag == QStringRef(&src, i, tag.length())) { - html += spanTags[2 * k + 1]; + text += spanTags[2 * k + 1]; i += tag.length(); handled = true; break; @@ -3545,20 +3542,20 @@ void DitaXmlGenerator::writeText(const QString& markedCode, } else { // retain all others - html += charLangle; + text += charLangle; } } } else { - html += src.at(i); + text += src.at(i); ++i; } } // replace all <@link> tags: "(<@link node=\"([^\"]+)\">).*(</@link>)" // replace all "(<@(type|headerfile|func)(?: +[^>]*)?>)(.*)(</@\\2>)" tags - src = html; - html = QString(); + src = text; + text = QString(); static const QString markTags[] = { // 0 1 2 3 4 5 "link", "type", "headerfile", "func", "param", "extra" @@ -3571,18 +3568,18 @@ void DitaXmlGenerator::writeText(const QString& markedCode, if (parseArg(src, markTags[k], &i, n, &arg, &par1)) { const Node* n = 0; if (k == 0) { // <@link> - if (!html.isEmpty()) { - writeCharacters(html); - html.clear(); + if (!text.isEmpty()) { + writeCharacters(text); + text.clear(); } n = CodeMarker::nodeForString(par1.toString()); QString link = linkForNode(n, relative); addLink(link, arg); } else if (k == 4) { // <@param> - if (!html.isEmpty()) { - writeCharacters(html); - html.clear(); + if (!text.isEmpty()) { + writeCharacters(text); + text.clear(); } writeStartTag(DT_i); //writeCharacters(" " + arg.toString()); @@ -3590,18 +3587,18 @@ void DitaXmlGenerator::writeText(const QString& markedCode, writeEndTag(); // </i> } else if (k == 5) { // <@extra> - if (!html.isEmpty()) { - writeCharacters(html); - html.clear(); + if (!text.isEmpty()) { + writeCharacters(text); + text.clear(); } writeStartTag(DT_tt); writeCharacters(arg.toString()); writeEndTag(); // </tt> } else { - if (!html.isEmpty()) { - writeCharacters(html); - html.clear(); + if (!text.isEmpty()) { + writeCharacters(text); + text.clear(); } par1 = QStringRef(); QString link; @@ -3626,12 +3623,11 @@ void DitaXmlGenerator::writeText(const QString& markedCode, } } else { - html += src.at(i++); + text += src.at(i++); } } - - if (!html.isEmpty()) { - writeCharacters(html); + if (!text.isEmpty()) { + writeCharacters(text); } } @@ -3816,7 +3812,7 @@ QString DitaXmlGenerator::protect(const QString& string, const QString& ) //outp QString DitaXmlGenerator::fileBase(const Node* node) const { QString result; - result = PageGenerator::fileBase(node); + result = Generator::fileBase(node); #if 0 if (!node->isInnerNode()) { switch (node->status()) { @@ -3898,7 +3894,7 @@ QString DitaXmlGenerator::fileName(const Node* node) if (static_cast<const FakeNode*>(node)->subType() == Node::Image) return node->name(); } - return PageGenerator::fileName(node); + return Generator::fileName(node); } QString DitaXmlGenerator::linkForNode(const Node* node, const Node* relative) @@ -4162,7 +4158,7 @@ const Node* DitaXmlGenerator::findNodeForTarget(const QString& target, const QPair<QString,QString> DitaXmlGenerator::anchorForNode(const Node* node) { QPair<QString,QString> anchorPair; - anchorPair.first = PageGenerator::fileName(node); + anchorPair.first = Generator::fileName(node); if (node->type() == Node::Fake) { const FakeNode *fakeNode = static_cast<const FakeNode*>(node); anchorPair.second = fakeNode->title(); @@ -4594,26 +4590,6 @@ void DitaXmlGenerator::generateQmlInherits(const QmlClassNode* qcn, CodeMarker* } /*! - Output the "Inherit by" list for the QML element, - if it is inherited by any other elements. - */ -void DitaXmlGenerator::generateQmlInheritedBy(const QmlClassNode* qcn, - CodeMarker* marker) -{ - if (qcn) { - NodeList subs; - QmlClassNode::subclasses(qcn->name(),subs); - if (!subs.isEmpty()) { - Text text; - text << Atom::ParaLeft << "Inherited by "; - appendSortedQmlNames(text,qcn,subs,marker); - 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. @@ -5559,7 +5535,7 @@ void DitaXmlGenerator::writePropertyParameter(const QString& tag, const NodeList void DitaXmlGenerator::beginSubPage(const InnerNode* node, const QString& fileName) { - PageGenerator::beginSubPage(node,fileName); + Generator::beginSubPage(node,fileName); (void) lookupGuidMap(fileName); QXmlStreamWriter* writer = new QXmlStreamWriter(out().device()); xmlWriterStack.push(writer); @@ -5580,7 +5556,7 @@ void DitaXmlGenerator::endSubPage() qDebug() << "Missing </section> in" << outFileName() << sectionNestingLevel; xmlWriter().writeEndDocument(); delete xmlWriterStack.pop(); - PageGenerator::endSubPage(); + Generator::endSubPage(); } /*! @@ -6353,6 +6329,24 @@ DitaXmlGenerator::writeProlog(const InnerNode* inner) xmlWriter().writeAttribute("content",i.value()); writeEndTag(); // </othermeta> } + if ((tagStack.first() == DT_cxxClass && !inner->includes().isEmpty()) || + (inner->type() == Node::Fake && inner->subType() == Node::HeaderFile)) { + writeStartTag(DT_othermeta); + xmlWriter().writeAttribute("name","includeFile"); + QString text; + QStringList::ConstIterator i = inner->includes().begin(); + while (i != inner->includes().end()) { + if ((*i).startsWith("<") && (*i).endsWith(">")) + text += *i; + else + text += "<" + *i + ">"; + ++i; + if (i != inner->includes().end()) + text += "\n"; + } + xmlWriter().writeAttribute("content",text); + writeEndTag(); // </othermeta> + } writeEndTag(); // </metadata> writeEndTag(); // </prolog> } diff --git a/src/tools/qdoc/ditaxmlgenerator.h b/src/tools/qdoc/ditaxmlgenerator.h index b3da02bce1..a4fc890dde 100644 --- a/src/tools/qdoc/ditaxmlgenerator.h +++ b/src/tools/qdoc/ditaxmlgenerator.h @@ -47,14 +47,14 @@ #include <QXmlStreamWriter> #include "codemarker.h" #include "config.h" -#include "pagegenerator.h" +#include "generator.h" QT_BEGIN_NAMESPACE typedef QMap<QString, QString> GuidMap; typedef QMap<QString, GuidMap*> GuidMaps; -class DitaXmlGenerator : public PageGenerator +class DitaXmlGenerator : public Generator { public: enum SinceType { @@ -340,7 +340,6 @@ private: const QString& name, bool subpage = false); void generateBrief(const Node* node, CodeMarker* marker); - void generateIncludes(const InnerNode* inner, CodeMarker* marker); void generateTableOfContents(const Node* node, CodeMarker* marker, Doc::Sections sectioningUnit, @@ -381,7 +380,6 @@ private: const InnerNode* relative, CodeMarker* marker); void generateQmlInherits(const QmlClassNode* qcn, CodeMarker* marker); - void generateQmlInheritedBy(const QmlClassNode* qcn, CodeMarker* marker); void generateQmlInstantiates(const QmlClassNode* qcn, CodeMarker* marker); void generateInstantiatedBy(const ClassNode* cn, CodeMarker* marker); diff --git a/src/tools/qdoc/doc/config/qt-defines.qdocconf b/src/tools/qdoc/doc/config/qt-defines.qdocconf index 50a355f04c..e11b32dc4c 100644 --- a/src/tools/qdoc/doc/config/qt-defines.qdocconf +++ b/src/tools/qdoc/doc/config/qt-defines.qdocconf @@ -3,7 +3,6 @@ defines = Q_QDOC \ QT_.*_LIB \ QT_COMPAT \ QT_KEYPAD_NAVIGATION \ - QT_NO_EGL \ QT3_SUPPORT \ Q_WS_.* \ Q_OS_.* \ diff --git a/src/tools/qdoc/doc/examples/componentset/uicomponents.qdoc b/src/tools/qdoc/doc/examples/componentset/uicomponents.qdoc index 10c23c7c0f..ad61daa3ca 100644 --- a/src/tools/qdoc/doc/examples/componentset/uicomponents.qdoc +++ b/src/tools/qdoc/doc/examples/componentset/uicomponents.qdoc @@ -28,10 +28,11 @@ /*! \qmlmodule UIComponents 1.0 \title UI Components - \brief Basic set of QML Components + \brief Basic set of UI components - This is a listing of a list of QML components. These files are available - for general import and they are based off the \l{Qt Quick Code Samples}. + 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/tools/qdoc/doc/examples/examples.qdoc b/src/tools/qdoc/doc/examples/examples.qdoc index 800589b9ac..236e7ae74a 100644 --- a/src/tools/qdoc/doc/examples/examples.qdoc +++ b/src/tools/qdoc/doc/examples/examples.qdoc @@ -29,11 +29,11 @@ \example componentset \title QML Documentation Example - This example demonstrates one of the ways to document QML components. + This example demonstrates one of the ways to document QML types. - In particular, there are sample components that are documented with QDoc - commands comments. There are documentation comments for the QML components - and their public interfaces. The components are grouped into a module, the + 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}{uicomponents.qdoc} file generates @@ -43,18 +43,18 @@ \section1 QML Class - The components use the \l{qmlclass-command}{\\qmlclass} to document the - component. In addition, they have the \l{inmodule-command}{\\inmodule} + The QML types use the \l{qmlclass-command}{\\qmlclass} 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 component. + description when listing the types. \section1 Properties, Signals, Handlers, and Methods - The components have their 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 components, therefore, you only need to place the + 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 @@ -75,10 +75,10 @@ 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 Components with C++ Implementation + \section1 QML Types with C++ Implementation - This example only demonstrates the documentation for components in QML + 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 component. + inside C++ classes to define the public API of the QML type. */ diff --git a/src/tools/qdoc/doc/qdoc-guide.qdoc b/src/tools/qdoc/doc/qdoc-guide.qdoc index 33cc5c67fd..a875c48770 100644 --- a/src/tools/qdoc/doc/qdoc-guide.qdoc +++ b/src/tools/qdoc/doc/qdoc-guide.qdoc @@ -48,7 +48,7 @@ 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 a QML component documentation. + documentation and an example of a QML type documentation. For specific QDoc information, consult the \l{Table of Contents}{QDoc Manual}. @@ -246,13 +246,13 @@ configuration file. \code outputprefixes = QML - outputprefixes.QML = qml-components- + outputprefixes.QML = uicomponents- \endcode - The outputprefixes will, for example, prefix QML components HTML filenames. + The outputprefixes will, for example, prefix QML type HTML filenames. \code files: - qml-components-button.html - qml-components-scrollbar.html + uicomponents-button.html + uicomponents-scrollbar.html \endcode */ @@ -295,7 +295,7 @@ \list \li \l{enum-command}{\\enum} - for enumeration documentation \li \l{class-command}{\\class} - for C++ class documentation - \li \l{qmlclass-command}{\\qmlclass} - for QML component documentation + \li \l{qmlclass-command}{\\qmlclass} - for QML type documentation \li \l{page-command}{\\page} - for creating a page. \endlist @@ -411,7 +411,7 @@ \li Article \li FAQ (Frequently Asked Questions) \li C++ API Documentation - \li QML Component Documentation + \li QML Type Documentation \li Code Example \endlist @@ -440,7 +440,7 @@ not properly process QDoc comments in header files. \keyword qml-documentation - \section2 Documenting QML Components + \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. @@ -453,7 +453,7 @@ \li \l{qmlattachedproperty-command}{\\qmlattachedproperty} \li \l{qmlattachedsignal-command}{\\qmlattachedsignal} \li \l{qmlbasictype-command}{\\qmlbasictype} - \li \l{qmlclass-command}{\\qmlclass} - creates a QML component documentation + \li \l{qmlclass-command}{\\qmlclass} - creates a QML type documentation \li \l{qmlmethod-command}{\\qmlmethod} \li \l{qmlproperty-command}{\\qmlproperty} \li \l{qmlsignal-command}{\\qmlsignal} @@ -511,25 +511,25 @@ \section3 QML Modules - A component belongs to a component \e set or a \e module. The module - may include all the related components for a platform or contain a certain + 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 \l{QML Elements} belong to the QtQuick2 module while there is also a QtQuick1 module for the older - elements introduced in Qt 4. + types introduced in Qt 4. - Modules affect the way Qdoc link and relate the components. The + Modules affect the way Qdoc link and relate the types. The \l{qmlclass-command}{\\qmlclass} topic command must have an \l{inqmlmodule-command}{\\inqmlmodule} context command to relate the - component to a module. Similarly, a \l{qmlmodule-command}{\\qmlmodule} topic + type to a 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 related components. + for the module. The overview page will list the related types. - The links to the components, must therefore, also contain the module name. - For example, if a component called \c TabWidget is in the \c UIComponents + 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 components and QML modules. + QDoc commands to document QML types and QML modules. \section3 Read-only and Internal QML Properties @@ -539,7 +539,7 @@ \code readonly property int sampleReadOnlyProperty: 0 \endcode - For example, the example \l{TabWidget} component has a fictitious read-only + 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. diff --git a/src/tools/qdoc/doc/qdoc-manual.qdoc b/src/tools/qdoc/doc/qdoc-manual.qdoc index e6bdf5af27..342cabc432 100644 --- a/src/tools/qdoc/doc/qdoc-manual.qdoc +++ b/src/tools/qdoc/doc/qdoc-manual.qdoc @@ -5202,7 +5202,7 @@ \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 element references, etc. You should never use this one for + and QML type references, etc. You should never use this one for the pages you write, because this one is reserved for qdoc. \endlist @@ -5351,13 +5351,13 @@ \section1 \\qmlattachedproperty The \\qmlattachedproperty command is for documenting a QML - property that will be attached to some QML element type. See + property that will be attached to some QML type. See \l{http://doc.qt.nokia.com/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 element \c ListView, + attached property named \c isCurrentItem in QML \c ListView, and the property has type \c {bool}, the \\qmlattachedproperty for it would look like this: @@ -5389,7 +5389,7 @@ the \l{qmlsignal-command} {\\qmlsignal} command. The argument is the rest of the line. It should be the name of the - QML element where the signal handler is declared, the \c{::} + QML type where the signal handler is declared, the \c{::} qualifier, and finally the signal handler name. If we have a QML attached signal handler named \c onAdd() in the \c GridView element, the \\qmlattachedsignal for it would look like this: @@ -5447,10 +5447,10 @@ \target qmlclass-command \section1 \\qmlclass - The \\qmlclass command is for documenting a QML element that is + 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 element. The second argument - is the name of the C++ class that instantiates the QML element. + 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 / *! @@ -5481,12 +5481,12 @@ \endcode This example generates the - \l {http://doc.qt.nokia.com/4.7/qml-transform.html} {QML Transform - Element} page. The \\qmlclass comment should include the \l - {since-command} {\\since} command, because all QML elements are + \l {http://doc.qt.nokia.com/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. And since every QML element is a member of a group of QML - elements, it should also include one or more \l{ingroup-command} + command. And 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 @@ -5521,9 +5521,9 @@ 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 element name, the \c{::} + 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 element \c Translate, and the property + 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 @@ -5544,7 +5544,7 @@ The \\qmlsignal command is for documenting a \l{http://doc.qt.nokia.com/4.7/qdeclarativeintroduction.html#signal-handlers} {signal handler}. - The argument is the rest of the line. It should be the QML element where the + The argument is the rest of the line. It should be the QML type where the signal handler is declared, the \c{::} qualifier, and finally the signal handler name. If we have a QML signal handler named \c onAdd() in QML element \c MouseArea, the \\qmlsignal for it would look like this: @@ -5571,7 +5571,7 @@ \section1 \\qmlmodule Insert the \c{\\qmlmodule} command to create a \c QML module page. A QML - module is a collection of QML components or any related material. This + 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 @@ -5594,7 +5594,7 @@ \endcode The \l{componentset}{UIComponents} example demonstrates proper usage of - QDoc commands to document QML components and QML modules. + QDoc commands to document QML types and QML modules. \target inqmlmodule-command \section1 \\inqmlmodule @@ -5615,7 +5615,7 @@ \c{\l ClickableComponents::ClickableButton} format. The \l{componentset}{UIComponents} example demonstrates proper usage of - QDoc commands to document QML components and QML modules. + QDoc commands to document QML types and QML modules. \target typedef-command \section1 \\typedef @@ -6154,7 +6154,7 @@ See how QDoc renders this property on the reference page for the \l {http://doc.qt.nokia.com/4.7/qml-state.html#changes-prop} {State} - element. + type. \target obsolete-command \section1 \\obsolete @@ -6538,18 +6538,18 @@ is an overload of another function, or this function is a reimplementation of another function, or this typedef is \e related to some class or header file. There is also a command - for documenting that a QML element inherits some other QML - element. + 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 element - inherits some other QML element. It must be included in the + 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{qmlclass-command}{\\qmlclass} comment. - The argument is the name of the inherited QML element. + The argument is the name of the inherited QML type. \code / *! @@ -7957,12 +7957,12 @@ \code outputprefixes = QML - outputprefixes.QML = qt-components- + outputprefixes.QML = uicomponents- \endcode - By default, files containing the API documentation for QML elements - or components are prefixed with "qml-". In the above example, the - prefix "qt-components-" is used instead. + By default, files containing the API documentation for QML types + are prefixed with "qml-". In the above example, the + prefix \c "uicomponents" is used instead. \target qhp-variable \section1 qhp diff --git a/src/tools/qdoc/generator.cpp b/src/tools/qdoc/generator.cpp index c990413234..97ab5652ae 100644 --- a/src/tools/qdoc/generator.cpp +++ b/src/tools/qdoc/generator.cpp @@ -42,13 +42,11 @@ /* generator.cpp */ -#include <qdir.h> -#ifdef DEBUG_MULTIPLE_QDOCCONF_FILES -#include <qdebug.h> -#endif -#include <qdebug.h> +#include <QDir> +#include <QDebug> #include "codemarker.h" #include "config.h" +#include "ditaxmlgenerator.h" #include "doc.h" #include "editdistance.h" #include "generator.h" @@ -57,28 +55,25 @@ #include "quoter.h" #include "separator.h" #include "tokenizer.h" -#include "ditaxmlgenerator.h" +#include "tree.h" QT_BEGIN_NAMESPACE -QList<Generator *> Generator::generators; +QString Generator::baseDir_; +QStringList Generator::exampleDirs; +QStringList Generator::exampleImgExts; QMap<QString, QMap<QString, QString> > Generator::fmtLeftMaps; QMap<QString, QMap<QString, QString> > Generator::fmtRightMaps; -QMap<QString, QStringList> Generator::imgFileExts; -QSet<QString> Generator::outputFormats; -QStringList Generator::imageFiles; +QList<Generator *> Generator::generators; QStringList Generator::imageDirs; -QStringList Generator::exampleDirs; -QStringList Generator::exampleImgExts; -QStringList Generator::scriptFiles; -QStringList Generator::scriptDirs; -QStringList Generator::styleFiles; -QStringList Generator::styleDirs; +QStringList Generator::imageFiles; +QMap<QString, QStringList> Generator::imgFileExts; QString Generator::outDir_; -QString Generator::baseDir_; -QString Generator::project; +QSet<QString> Generator::outputFormats; QHash<QString, QString> Generator::outputPrefixes; - +QString Generator::project; +QStringList Generator::scriptDirs; +QStringList Generator::scriptFiles; QString Generator::sinceTitles[] = { " New Namespaces", @@ -98,19 +93,14 @@ QString Generator::sinceTitles[] = " New QML Methods", "" }; +QStringList Generator::styleDirs; +QStringList Generator::styleFiles; -static void singularPlural(Text& text, const NodeList& nodes) -{ - if (nodes.count() == 1) - text << " is"; - else - text << " are"; -} Generator::Generator() : amp("&"), - lt("<"), gt(">"), + lt("<"), quot("""), tag("</?@[^>]*>") { @@ -122,233 +112,371 @@ Generator::~Generator() generators.removeAll(this); } -void Generator::initializeGenerator(const Config & /* config */) +void Generator::appendFullName(Text& text, + const Node *apparentNode, + const Node *relative, + CodeMarker *marker, + const Node *actualNode) { + if (actualNode == 0) + actualNode = apparentNode; + text << Atom(Atom::LinkNode, CodeMarker::stringForNode(actualNode)) + << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK) + << Atom(Atom::String, marker->plainFullName(apparentNode, relative)) + << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK); } -void Generator::terminateGenerator() +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::initialize(const Config &config) +void Generator::appendFullNames(Text& text, + const NodeList& nodes, + const Node* relative, + CodeMarker* marker) { - outputFormats = config.getOutputFormats(); - if (!outputFormats.isEmpty()) { - outDir_ = config.getOutputDir(); - baseDir_ = config.getString(CONFIG_BASEDIR); - if (!baseDir_.isEmpty()) - config.location().warning(tr("\"basedir\" specified in config file. " - "All output will be in module directories of the output directory")); - if (outDir_.isEmpty()) - config.lastLocation().fatal(tr("No output directory specified in configuration file or on the command line")); + NodeList::ConstIterator n = nodes.begin(); + int index = 0; + while (n != nodes.end()) { + appendFullName(text,*n,relative,marker); + text << comma(index++,nodes.count()); + ++n; + } +} - QDir dirInfo; - if (dirInfo.exists(outDir_)) { - if (!Config::removeDirContents(outDir_)) - config.lastLocation().error(tr("Cannot empty output directory '%1'").arg(outDir_)); - } - else { - if (!dirInfo.mkpath(outDir_)) - config.lastLocation().fatal(tr("Cannot create output directory '%1'").arg(outDir_)); +void Generator::appendSortedNames(Text& text, + const ClassNode *classe, + const QList<RelatedClass> &classes, + CodeMarker *marker) +{ + QList<RelatedClass>::ConstIterator r; + QMap<QString,Text> classMap; + int index = 0; + + r = classes.begin(); + while (r != classes.end()) { + if ((*r).node->access() == Node::Public && + (*r).node->status() != Node::Internal + && !(*r).node->doc().isEmpty()) { + Text className; + appendFullName(className, (*r).node, classe, marker); + classMap[className.toString().toLower()] = className; } + ++r; + } - if (!dirInfo.mkdir(outDir_ + "/images")) - config.lastLocation().fatal(tr("Cannot create output directory '%1'") - .arg(outDir_ + "/images")); - if (!dirInfo.mkdir(outDir_ + "/images/used-in-examples")) - config.lastLocation().fatal(tr("Cannot create output directory '%1'") - .arg(outDir_ + "/images/used-in-examples")); - if (!dirInfo.mkdir(outDir_ + "/scripts")) - config.lastLocation().fatal(tr("Cannot create output directory '%1'") - .arg(outDir_ + "/scripts")); - if (!dirInfo.mkdir(outDir_ + "/style")) - config.lastLocation().fatal(tr("Cannot create output directory '%1'") - .arg(outDir_ + "/style")); + QStringList classNames = classMap.keys(); + classNames.sort(); + + foreach (const QString &className, classNames) { + text << classMap[className]; + text << separator(index++, classNames.count()); } +} - imageFiles = config.getCleanPathList(CONFIG_IMAGES); - imageDirs = config.getCleanPathList(CONFIG_IMAGEDIRS); - scriptFiles = config.getCleanPathList(CONFIG_SCRIPTS); - scriptDirs = config.getCleanPathList(CONFIG_SCRIPTDIRS); - styleFiles = config.getCleanPathList(CONFIG_STYLES); - styleDirs = config.getCleanPathList(CONFIG_STYLEDIRS); - exampleDirs = config.getCleanPathList(CONFIG_EXAMPLEDIRS); - exampleImgExts = config.getStringList(CONFIG_EXAMPLES + Config::dot + - CONFIG_IMAGEEXTENSIONS); +void Generator::appendSortedQmlNames(Text& text, + const Node* base, + const NodeList& subs, + CodeMarker *marker) +{ + QMap<QString,Text> classMap; + int index = 0; - QString imagesDotFileExtensions = - CONFIG_IMAGES + Config::dot + CONFIG_FILEEXTENSIONS; - QSet<QString> formats = config.subVars(imagesDotFileExtensions); - QSet<QString>::ConstIterator f = formats.begin(); - while (f != formats.end()) { - imgFileExts[*f] = config.getStringList(imagesDotFileExtensions + - Config::dot + *f); - ++f; + for (int i = 0; i < subs.size(); ++i) { + Text t; + if (!base->isQtQuickNode() || !subs[i]->isQtQuickNode() || + (base->qmlModuleIdentifier() == subs[i]->qmlModuleIdentifier())) { + appendFullName(t, subs[i], base, marker); + classMap[t.toString().toLower()] = t; + } } - QList<Generator *>::ConstIterator g = generators.begin(); - while (g != generators.end()) { - if (outputFormats.contains((*g)->format())) { - (*g)->initializeGenerator(config); - QStringList extraImages = - config.getCleanPathList(CONFIG_EXTRAIMAGES+Config::dot+(*g)->format()); - QStringList::ConstIterator e = extraImages.begin(); - while (e != extraImages.end()) { - QString userFriendlyFilePath; - QString filePath = Config::findFile(config.lastLocation(), - imageFiles, - imageDirs, - *e, - imgFileExts[(*g)->format()], - userFriendlyFilePath); - if (!filePath.isEmpty()) - Config::copyFile(config.lastLocation(), - filePath, - userFriendlyFilePath, - (*g)->outputDir() + - "/images"); - ++e; - } + QStringList names = classMap.keys(); + names.sort(); - // Documentation template handling - QString templateDir = config.getString( - (*g)->format() + Config::dot + CONFIG_TEMPLATEDIR); + foreach (const QString &name, names) { + text << classMap[name]; + text << separator(index++, names.count()); + } +} - if (!templateDir.isEmpty()) { - QStringList noExts; - QStringList searchDirs = QStringList() << templateDir; - QStringList scripts = - config.getCleanPathList((*g)->format()+Config::dot+CONFIG_SCRIPTS); - e = scripts.begin(); - while (e != scripts.end()) { - QString userFriendlyFilePath; - QString filePath = Config::findFile(config.lastLocation(), - scriptFiles, - searchDirs, - *e, - noExts, - userFriendlyFilePath); - if (!filePath.isEmpty()) - Config::copyFile(config.lastLocation(), - filePath, - userFriendlyFilePath, - (*g)->outputDir() + - "/scripts"); - ++e; - } +/*! + 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 InnerNode* node, const QString& fileName) +{ + QString path = outputDir() + QLatin1Char('/'); + if (!node->outputSubdirectory().isEmpty()) + path += node->outputSubdirectory() + QLatin1Char('/'); + path += fileName; + QFile* outFile = new QFile(path); + if (!outFile->open(QFile::WriteOnly)) + node->location().fatal(tr("Cannot open output file '%1'").arg(outFile->fileName())); + QTextStream* out = new QTextStream(outFile); + + if (outputCodec) + out->setCodec(outputCodec); + outStreamStack.push(out); + const_cast<InnerNode*>(node)->setOutputFileName(fileName); +} - QStringList styles = - config.getCleanPathList((*g)->format()+Config::dot+CONFIG_STYLESHEETS); - e = styles.begin(); - while (e != styles.end()) { - QString userFriendlyFilePath; - QString filePath = Config::findFile(config.lastLocation(), - styleFiles, - searchDirs, - *e, - noExts, - userFriendlyFilePath); - if (!filePath.isEmpty()) - Config::copyFile(config.lastLocation(), - filePath, - userFriendlyFilePath, - (*g)->outputDir() + - "/style"); - ++e; - } - } +/*! + 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(); +} + +void Generator::endText(const Node * /* relative */, + CodeMarker * /* marker */) +{ +} + +QString Generator::fileBase(const Node *node) const +{ + if (node->relates()) + node = node->relates(); + else if (!node->isInnerNode()) + node = node->parent(); + if (node->subType() == Node::QmlPropertyGroup) { + node = node->parent(); + } + + QString base = node->doc().baseName(); + if (!base.isEmpty()) + return base; + + const Node *p = node; + + forever { + const Node *pp = p->parent(); + base.prepend(p->name()); + if (!p->qmlModuleIdentifier().isEmpty()) + base.prepend(p->qmlModuleIdentifier()+QChar('-')); + /* + To avoid file name conflicts in the html directory, + we prepend a prefix (by default, "qml-") to the file name of QML + element doc files. + */ + if ((p->subType() == Node::QmlClass) || + (p->subType() == Node::QmlBasicType)) { + base.prepend(outputPrefix(QLatin1String("QML"))); } - ++g; + if (!pp || pp->name().isEmpty() || pp->type() == Node::Fake) + break; + base.prepend(QLatin1Char('-')); + p = pp; + } + if (node->type() == Node::Fake) { + if (node->subType() == Node::Collision) { + const NameCollisionNode* ncn = static_cast<const NameCollisionNode*>(node); + if (ncn->currentChild()) + return fileBase(ncn->currentChild()); + base.prepend("collision-"); + } + //Was QDOC2_COMPAT, required for index.html + if (base.endsWith(".html")) + base.truncate(base.length() - 5); } - QRegExp secondParamAndAbove("[\2-\7]"); - QSet<QString> formattingNames = config.subVars(CONFIG_FORMATTING); - QSet<QString>::ConstIterator n = formattingNames.begin(); - while (n != formattingNames.end()) { - QString formattingDotName = CONFIG_FORMATTING + Config::dot + *n; + // 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); + return res; +} - QSet<QString> formats = config.subVars(formattingDotName); - QSet<QString>::ConstIterator f = formats.begin(); - while (f != formats.end()) { - QString def = config.getString(formattingDotName + - Config::dot + *f); - if (!def.isEmpty()) { - int numParams = Config::numParams(def); - int numOccs = def.count("\1"); +/*! + If 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(); - if (numParams != 1) { - config.lastLocation().warning(tr("Formatting '%1' must " - "have exactly one " - "parameter (found %2)") - .arg(*n).arg(numParams)); + QString name = fileBase(node); + name += QLatin1Char('.'); + name += fileExtension(node); + return name; +} + +/*! + For generating the "New Classes... in x.y" section on the + What's New in Qt x.y" page. + */ +void Generator::findAllSince(const InnerNode *node) +{ + NodeList::const_iterator child = node->childNodes().constBegin(); + + // Traverse the tree, starting at the node supplied. + + while (child != node->childNodes().constEnd()) { + + QString sinceString = (*child)->since(); + + if (((*child)->access() != Node::Private) && !sinceString.isEmpty()) { + + // Insert a new entry into each map for each new since string found. + NewSinceMaps::iterator nsmap = newSinceMaps.find(sinceString); + if (nsmap == newSinceMaps.end()) + nsmap = newSinceMaps.insert(sinceString,NodeMultiMap()); + + NewClassMaps::iterator ncmap = newClassMaps.find(sinceString); + if (ncmap == newClassMaps.end()) + ncmap = newClassMaps.insert(sinceString,NodeMap()); + + NewClassMaps::iterator nqcmap = newQmlClassMaps.find(sinceString); + if (nqcmap == newQmlClassMaps.end()) + nqcmap = newQmlClassMaps.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 (numOccs > 1) { - config.lastLocation().fatal(tr("Formatting '%1' must " - "contain exactly one " - "occurrence of '\\1' " - "(found %2)") - .arg(*n).arg(numOccs)); + } + else if ((*child)->url().isEmpty()) { + if ((*child)->type() == Node::Class && !(*child)->doc().isEmpty()) { + // Insert classes into the since and class maps. + QString className = (*child)->name(); + if ((*child)->parent() && + (*child)->parent()->type() == Node::Namespace && + !(*child)->parent()->name().isEmpty()) + className = (*child)->parent()->name()+"::"+className; + + nsmap.value().insert(className,(*child)); + ncmap.value().insert(className,(*child)); } - else { - int paramPos = def.indexOf("\1"); - fmtLeftMaps[*f].insert(*n, def.left(paramPos)); - fmtRightMaps[*f].insert(*n, def.mid(paramPos + 1)); + else if ((*child)->subType() == Node::QmlClass) { + // Insert QML elements into the since and element maps. + QString className = (*child)->name(); + if ((*child)->parent() && + (*child)->parent()->type() == Node::Namespace && + !(*child)->parent()->name().isEmpty()) + className = (*child)->parent()->name()+"::"+className; + + nsmap.value().insert(className,(*child)); + nqcmap.value().insert(className,(*child)); + } + else if ((*child)->type() == Node::QmlProperty) { + // Insert QML properties into the since map. + QString propertyName = (*child)->name(); + nsmap.value().insert(propertyName,(*child)); } } - ++f; - } - ++n; - } + else { + // Insert external documents into the general since map. + QString name = (*child)->name(); + if ((*child)->parent() && + (*child)->parent()->type() == Node::Namespace && + !(*child)->parent()->name().isEmpty()) + name = (*child)->parent()->name()+"::"+name; - project = config.getString(CONFIG_PROJECT); + nsmap.value().insert(name,(*child)); + } - QStringList prefixes = config.getStringList(CONFIG_OUTPUTPREFIXES); - if (!prefixes.isEmpty()) { - foreach (QString prefix, prefixes) - outputPrefixes[prefix] = config.getString( - CONFIG_OUTPUTPREFIXES + Config::dot + prefix); - } else - outputPrefixes[QLatin1String("QML")] = QLatin1String("qml-"); + // Find child nodes with since commands. + if ((*child)->isInnerNode()) { + findAllSince(static_cast<InnerNode *>(*child)); + } + } + ++child; + } } -void Generator::terminate() +QMap<QString, QString>& Generator::formattingLeftMap() { - QList<Generator *>::ConstIterator g = generators.begin(); - while (g != generators.end()) { - if (outputFormats.contains((*g)->format())) - (*g)->terminateGenerator(); - ++g; - } - - fmtLeftMaps.clear(); - fmtRightMaps.clear(); - imgFileExts.clear(); - imageFiles.clear(); - imageDirs.clear(); - outDir_ = ""; - QmlClassNode::terminate(); - ExampleNode::terminate(); + return fmtLeftMaps[format()]; } -Generator *Generator::generatorForFormat(const QString& format) +QMap<QString, QString>& Generator::formattingRightMap() { - QList<Generator *>::ConstIterator g = generators.begin(); - while (g != generators.end()) { - if ((*g)->format() == format) - return *g; - ++g; - } - return 0; + return fmtRightMaps[format()]; } -void Generator::startText(const Node * /* relative */, - CodeMarker * /* marker */) +QString Generator::fullName(const Node *node, + const Node *relative, + CodeMarker *marker) const { + if (node->type() == Node::Fake) { + const FakeNode* fn = static_cast<const FakeNode *>(node); +#if 0 + // Removed for QTBUG-22870 + if (!fn->qmlModuleIdentifier().isEmpty()) + return fn->qmlModuleIdentifier() + QLatin1Char(' ') + fn->title(); +#endif + return fn->title(); + } + else if (node->type() == Node::Class && + !(static_cast<const ClassNode *>(node))->serviceName().isEmpty()) + return (static_cast<const ClassNode *>(node))->serviceName(); + else + return marker->plainFullName(node, relative); } -void Generator::endText(const Node * /* relative */, - CodeMarker * /* marker */) +void Generator::generateAlsoList(const Node *node, CodeMarker *marker) { + QList<Text> alsoList = node->doc().alsoList(); + supplementAlsoList(node, alsoList); + + if (!alsoList.isEmpty()) { + Text text; + text << Atom::ParaLeft + << 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 */, @@ -358,68 +486,65 @@ int Generator::generateAtom(const Atom * /* atom */, return 0; } -void Generator::generateClassLikeNode(const InnerNode * /* classe */, - CodeMarker * /* marker */) -{ -} - -void Generator::generateFakeNode(const FakeNode * /* fake */, - CodeMarker * /* marker */) -{ -} - -bool Generator::generateText(const Text& text, - const Node *relative, - CodeMarker *marker) +const Atom *Generator::generateAtomList(const Atom *atom, + const Node *relative, + CodeMarker *marker, + bool generate, + int &numAtoms) { - bool result = false; - if (text.firstAtom() != 0) { - int numAtoms = 0; - startText(relative, marker); - generateAtomList(text.firstAtom(), - relative, - marker, - true, - numAtoms); - endText(relative, marker); - result = true; - } - return result; -} + 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; -#ifdef QDOC_QML -/*! - 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->type() == Atom::FormatElse) { + ++numAtoms; + atom = generateAtomList(atom->next(), + relative, + marker, + generate && !rightFormat, + numAtoms); + if (!atom) + return 0; + } - if (atom != 0) { - startText(relative, marker); - 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(); + 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(); } } - endText(relative, marker); - result = true; + 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 result; + return 0; } -#endif void Generator::generateBody(const Node *node, CodeMarker *marker) { @@ -568,105 +693,22 @@ void Generator::generateBody(const Node *node, CodeMarker *marker) } } -void Generator::generateAlsoList(const Node *node, CodeMarker *marker) -{ - QList<Text> alsoList = node->doc().alsoList(); - supplementAlsoList(node, alsoList); - - if (!alsoList.isEmpty()) { - Text text; - text << Atom::ParaLeft - << 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); - } -} - -/*! - Generate a list of maintainers in the output - */ -void Generator::generateMaintainerList(const InnerNode* 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); - } -} - -void Generator::generateInherits(const ClassNode *classe, CodeMarker *marker) +void Generator::generateClassLikeNode(const InnerNode * /* 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().begin(); - index = 0; - while (r != classe->baseClasses().end()) { - text << Atom(Atom::LinkNode, CodeMarker::stringForNode((*r).node)) - << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK) - << Atom(Atom::String, (*r).dataTypeWithTemplateArgs) - << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK); - - if ((*r).access == Node::Protected) { - text << " (protected)"; - } - else if ((*r).access == Node::Private) { - text << " (private)"; - } - text << separator(index++, classe->baseClasses().count()); - ++r; - } - text << Atom::ParaRight; - generateText(text, classe, marker); - } } -#ifdef QDOC_QML -/*! - */ -void Generator::generateQmlInherits(const QmlClassNode* , CodeMarker* ) +void Generator::generateExampleFiles(const FakeNode *fake, CodeMarker *marker) { - // stub. + if (fake->childNodes().isEmpty()) + return; + generateFileList(fake, marker, Node::File, QString("Files:")); + generateFileList(fake, marker, Node::Image, QString("Images:")); } -#endif -void Generator::generateInheritedBy(const ClassNode *classe, - CodeMarker *marker) +void Generator::generateFakeNode(const FakeNode * /* fake */, + 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(), marker); - text << Atom::ParaRight; - generateText(text, classe, marker); - } } /*! @@ -734,193 +776,250 @@ void Generator::generateFileList(const FakeNode* fake, generateText(text, fake, marker); } -void Generator::generateExampleFiles(const FakeNode *fake, CodeMarker *marker) +void Generator::generateInheritedBy(const ClassNode *classe, + CodeMarker *marker) { - if (fake->childNodes().isEmpty()) - return; - generateFileList(fake, marker, Node::File, QString("Files:")); - generateFileList(fake, marker, Node::Image, QString("Images:")); + 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(), marker); + text << Atom::ParaRight; + generateText(text, classe, marker); + } } -QString Generator::indent(int level, const QString& markedCode) +void Generator::generateInherits(const ClassNode *classe, CodeMarker *marker) { - if (level == 0) - return markedCode; + QList<RelatedClass>::ConstIterator r; + int index; - QString t; - int column = 0; + if (!classe->baseClasses().isEmpty()) { + Text text; + text << Atom::ParaLeft + << Atom(Atom::FormattingLeft,ATOM_FORMATTING_BOLD) + << "Inherits: " + << Atom(Atom::FormattingRight,ATOM_FORMATTING_BOLD); - 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(' '); + r = classe->baseClasses().begin(); + index = 0; + while (r != classe->baseClasses().end()) { + text << Atom(Atom::LinkNode, CodeMarker::stringForNode((*r).node)) + << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK) + << Atom(Atom::String, (*r).dataTypeWithTemplateArgs) + << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK); + + if ((*r).access == Node::Protected) { + text << " (protected)"; } - column++; + else if ((*r).access == Node::Private) { + text << " (private)"; + } + text << separator(index++, classe->baseClasses().count()); + ++r; } - t += markedCode.at(i++); + text << Atom::ParaRight; + generateText(text, classe, marker); } - return t; } -QString Generator::plainCode(const QString& markedCode) -{ - QString t = markedCode; - t.replace(tag, QString()); - t.replace(quot, QLatin1String("\"")); - t.replace(gt, QLatin1String(">")); - t.replace(lt, QLatin1String("<")); - t.replace(amp, QLatin1String("&")); - return t; -} +/*! + Recursive writing of HTML files from the root \a node. -QString Generator::typeString(const Node *node) + \note NameCollisionNodes are skipped here and processed + later. See HtmlGenerator::generateDisambiguationPages() + for more on this. + */ +void Generator::generateInnerNode(const InnerNode* node) { - switch (node->type()) { - case Node::Namespace: - return "namespace"; - case Node::Class: - return "class"; - case Node::Fake: - { - switch (node->subType()) { - case Node::QmlClass: - return "element"; - case Node::QmlPropertyGroup: - return "property group"; - case Node::QmlBasicType: - return "type"; - default: - return "documentation"; + if (!node->url().isNull()) + return; + + if (node->type() == Node::Fake) { + const FakeNode *fakeNode = static_cast<const FakeNode *>(node); + if (fakeNode->subType() == Node::ExternalPage) + return; + if (fakeNode->subType() == Node::Image) + return; + if (fakeNode->subType() == Node::QmlPropertyGroup) + return; + if (fakeNode->subType() == Node::Page) { + if (node->count() > 0) + qDebug("PAGE %s HAS CHILDREN", qPrintable(fakeNode->title())); } } - case Node::Enum: - return "enum"; - case Node::Typedef: - return "typedef"; - case Node::Function: - return "function"; - case Node::Property: - return "property"; - default: - return "documentation"; + + /* + Obtain a code marker for the source file. + */ + CodeMarker *marker = CodeMarker::markerForFileName(node->location().filePath()); + + if (node->parent() != 0) { + /* + Skip name collision nodes here and process them + later in generateDisambiguationPages(). Each one + is appended to a list for later. + */ + if ((node->type() == Node::Fake) && (node->subType() == Node::Collision)) { + const NameCollisionNode* ncn = static_cast<const NameCollisionNode*>(node); + collisionNodes.append(const_cast<NameCollisionNode*>(ncn)); + } + else { + beginSubPage(node, fileName(node)); + if (node->type() == Node::Namespace || node->type() == Node::Class) { + generateClassLikeNode(node, marker); + } + else if (node->type() == Node::Fake) { + generateFakeNode(static_cast<const FakeNode *>(node), marker); + } + endSubPage(); + } + } + + NodeList::ConstIterator c = node->childNodes().begin(); + while (c != node->childNodes().end()) { + if ((*c)->isInnerNode() && (*c)->access() != Node::Private) { + generateInnerNode((const InnerNode *) *c); + } + ++c; } } /*! - Returns a relative path name for an image. + Generate a list of maintainers in the output */ -QString Generator::imageFileName(const Node *relative, const QString& fileBase) +void Generator::generateMaintainerList(const InnerNode* node, CodeMarker* marker) { - QString userFriendlyFilePath; - QString filePath = Config::findFile( - relative->doc().location(), imageFiles, imageDirs, fileBase, - imgFileExts[format()], userFriendlyFilePath); + QStringList sl = getMetadataElements(node,"maintainer"); - if (filePath.isEmpty()) - return QString(); + if (!sl.isEmpty()) { + Text text; + text << Atom::ParaLeft + << Atom(Atom::FormattingLeft,ATOM_FORMATTING_BOLD) + << "Maintained by: " + << Atom(Atom::FormattingRight,ATOM_FORMATTING_BOLD); - QString path = Config::copyFile(relative->doc().location(), - filePath, - userFriendlyFilePath, - outputDir() + QLatin1String("/images")); - QString images = "images"; - if (path[0] != '/') - images.append(QLatin1Char('/')); - return images + path; -} + for (int i = 0; i < sl.size(); ++i) + text << sl.at(i) << separator(i, sl.size()); -void Generator::setImageFileExtensions(const QStringList& extensions) -{ - imgFileExts[format()] = extensions; + text << Atom::ParaRight; + generateText(text, node, marker); + } } -void Generator::unknownAtom(const Atom *atom) +#ifdef QDOC_QML +/*! + Output the "Inherit by" list for the QML element, + if it is inherited by any other elements. + */ +void Generator::generateQmlInheritedBy(const QmlClassNode* qcn, + CodeMarker* marker) { - Location::internalError(tr("unknown atom type '%1' in %2 generator") - .arg(atom->typeString()).arg(format())); + if (qcn) { + NodeList subs; + QmlClassNode::subclasses(qcn->name(),subs); + if (!subs.isEmpty()) { + Text text; + text << Atom::ParaLeft << "Inherited by "; + appendSortedQmlNames(text,qcn,subs,marker); + text << Atom::ParaRight; + generateText(text, qcn, marker); + } + } } +#endif -bool Generator::matchAhead(const Atom *atom, Atom::Type expectedAtomType) +#ifdef QDOC_QML +/*! + */ +void Generator::generateQmlInherits(const QmlClassNode* , CodeMarker* ) { - return atom->next() != 0 && atom->next()->type() == expectedAtomType; + // stub. } +#endif -void Generator::supplementAlsoList(const Node *node, QList<Text> &alsoList) +#ifdef QDOC_QML +/*! + 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 */ ) { - if (node->type() == Node::Function) { - const FunctionNode *func = static_cast<const FunctionNode *>(node); - if (func->overloadNumber() == 1) { - QString alternateName; - const FunctionNode *alternateFunc = 0; - - if (func->name().startsWith("set") && func->name().size() >= 4) { - alternateName = func->name()[3].toLower(); - alternateName += func->name().mid(4); - alternateFunc = func->parent()->findFunctionNode(alternateName); - - if (!alternateFunc) { - alternateName = "is" + func->name().mid(3); - alternateFunc = func->parent()->findFunctionNode(alternateName); - if (!alternateFunc) { - alternateName = "has" + func->name().mid(3); - alternateFunc = func->parent()->findFunctionNode(alternateName); - } - } - } - else if (!func->name().isEmpty()) { - alternateName = "set"; - alternateName += func->name()[0].toUpper(); - alternateName += func->name().mid(1); - alternateFunc = func->parent()->findFunctionNode(alternateName); - } - - if (alternateFunc && alternateFunc->access() != Node::Private) { - int i; - for (i = 0; i < alsoList.size(); ++i) { - if (alsoList.at(i).toString().contains(alternateName)) - break; - } - - if (i == alsoList.size()) { - alternateName += "()"; + const Atom* atom = text.firstAtom(); + bool result = false; - Text also; - also << Atom(Atom::Link, alternateName) - << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK) - << alternateName - << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK); - alsoList.prepend(also); + if (atom != 0) { + startText(relative, marker); + while (atom) { + if (atom->type() != Atom::QmlText) + atom = atom->next(); + else { + atom = atom->next(); + while (atom && (atom->type() != Atom::EndQmlText)) { + int n = 1 + generateAtom(atom, relative, marker); + while (n-- > 0) + atom = atom->next(); } } } + endText(relative, marker); + result = true; } + return result; } +#endif -QMap<QString, QString>& Generator::formattingLeftMap() +void Generator::generateReimplementedFrom(const FunctionNode *func, + CodeMarker *marker) { - return fmtLeftMaps[format()]; + 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); + } + } } -QMap<QString, QString>& Generator::formattingRightMap() +void Generator::generateSince(const Node *node, CodeMarker *marker) { - return fmtRightMaps[format()]; -} + 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 "; -/* - Trims trailimng whitespace off the \a string and returns - the trimmed string. - */ -QString Generator::trimmedTrailing(const QString& string) -{ - QString trimmed = string; - while (trimmed.length() > 0 && trimmed[trimmed.length() - 1].isSpace()) - trimmed.truncate(trimmed.length() - 1); - return trimmed; + QStringList since = node->since().split(QLatin1Char(' ')); + if (since.count() == 1) { + // Handle legacy use of \since <version>. + if (project.isEmpty()) + text << "version"; + else + text << project; + text << " " << since[0]; + } else { + // Reconstruct the <project> <version> string. + text << " " << since.join(" "); + } + + text << "." << Atom::ParaRight; + generateText(text, node, marker); + } } void Generator::generateStatus(const Node *node, CodeMarker *marker) @@ -967,13 +1066,10 @@ void Generator::generateStatus(const Node *node, CodeMarker *marker) << Atom(Atom::FormattingLeft, ATOM_FORMATTING_BOLD) << "This " << typeString(node) - << " is part of the Qt 3 compatibility layer." + << " 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. See " - << Atom(Atom::AutoLink, "Porting to Qt 4") - << " for more information." + << "We strongly advise against using it in new code." << Atom::ParaRight; } break; @@ -984,10 +1080,28 @@ void Generator::generateStatus(const Node *node, CodeMarker *marker) generateText(text, node, marker); } +bool Generator::generateText(const Text& text, + const Node *relative, + CodeMarker *marker) +{ + bool result = false; + if (text.firstAtom() != 0) { + int numAtoms = 0; + startText(relative, marker); + generateAtomList(text.firstAtom(), + relative, + marker, + true, + numAtoms); + endText(relative, marker); + result = true; + } + return result; +} + void Generator::generateThreadSafeness(const Node *node, CodeMarker *marker) { Text text; - Text theStockLink; Node::ThreadSafeness threadSafeness = node->threadSafeness(); Text rlink; @@ -1125,208 +1239,438 @@ void Generator::generateThreadSafeness(const Node *node, CodeMarker *marker) generateText(text,node,marker); } -void Generator::generateSince(const Node *node, CodeMarker *marker) +/*! + This function is recursive. + */ +void Generator::generateTree(const Tree *tree) { - 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 "; + generateInnerNode(tree->root()); +} - QStringList since = node->since().split(QLatin1Char(' ')); - if (since.count() == 1) { - // Handle legacy use of \since <version>. - if (project.isEmpty()) - text << "version"; - else - text << project; - text << " " << since[0]; - } else { - // Reconstruct the <project> <version> string. - text << " " << since.join(" "); - } +Generator *Generator::generatorForFormat(const QString& format) +{ + QList<Generator *>::ConstIterator g = generators.begin(); + while (g != generators.end()) { + if ((*g)->format() == format) + return *g; + ++g; + } + return 0; +} - text << "." << Atom::ParaRight; - generateText(text, node, marker); +/*! + 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 InnerNode* 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; } -void Generator::generateReimplementedFrom(const FunctionNode *func, - CodeMarker *marker) +/*! + 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 InnerNode* inner, const QString& t) { - 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); + 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")); + QString images = "images"; + if (path[0] != '/') + images.append(QLatin1Char('/')); + return images + path; +} + +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; } -const Atom *Generator::generateAtomList(const Atom *atom, - const Node *relative, - CodeMarker *marker, - bool generate, - int &numAtoms) +void Generator::initialize(const Config &config) { - 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; + outputFormats = config.getOutputFormats(); + if (!outputFormats.isEmpty()) { + outDir_ = config.getOutputDir(); + baseDir_ = config.getString(CONFIG_BASEDIR); + if (!baseDir_.isEmpty()) + config.location().warning(tr("\"basedir\" specified in config file. " + "All output will be in module directories of the output directory")); + if (outDir_.isEmpty()) + config.lastLocation().fatal(tr("No output directory specified in configuration file or on the command line")); - if (atom->type() == Atom::FormatElse) { - ++numAtoms; - atom = generateAtomList(atom->next(), - relative, - marker, - generate && !rightFormat, - numAtoms); - if (!atom) - return 0; + QDir dirInfo; + if (dirInfo.exists(outDir_)) { + if (!Config::removeDirContents(outDir_)) + config.lastLocation().error(tr("Cannot empty output directory '%1'").arg(outDir_)); + } + else { + if (!dirInfo.mkpath(outDir_)) + config.lastLocation().fatal(tr("Cannot create output directory '%1'").arg(outDir_)); + } + + if (!dirInfo.mkdir(outDir_ + "/images")) + config.lastLocation().fatal(tr("Cannot create output directory '%1'") + .arg(outDir_ + "/images")); + if (!dirInfo.mkdir(outDir_ + "/images/used-in-examples")) + config.lastLocation().fatal(tr("Cannot create output directory '%1'") + .arg(outDir_ + "/images/used-in-examples")); + if (!dirInfo.mkdir(outDir_ + "/scripts")) + config.lastLocation().fatal(tr("Cannot create output directory '%1'") + .arg(outDir_ + "/scripts")); + if (!dirInfo.mkdir(outDir_ + "/style")) + config.lastLocation().fatal(tr("Cannot create output directory '%1'") + .arg(outDir_ + "/style")); + } + + imageFiles = config.getCleanPathList(CONFIG_IMAGES); + imageDirs = config.getCleanPathList(CONFIG_IMAGEDIRS); + scriptFiles = config.getCleanPathList(CONFIG_SCRIPTS); + scriptDirs = config.getCleanPathList(CONFIG_SCRIPTDIRS); + styleFiles = config.getCleanPathList(CONFIG_STYLES); + styleDirs = config.getCleanPathList(CONFIG_STYLEDIRS); + exampleDirs = config.getCleanPathList(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.begin(); + while (f != formats.end()) { + imgFileExts[*f] = config.getStringList(imagesDotFileExtensions + + Config::dot + *f); + ++f; + } + + QList<Generator *>::ConstIterator g = generators.begin(); + while (g != generators.end()) { + if (outputFormats.contains((*g)->format())) { + (*g)->initializeGenerator(config); + QStringList extraImages = + config.getCleanPathList(CONFIG_EXTRAIMAGES+Config::dot+(*g)->format()); + QStringList::ConstIterator e = extraImages.begin(); + while (e != extraImages.end()) { + QString userFriendlyFilePath; + QString filePath = Config::findFile(config.lastLocation(), + imageFiles, + imageDirs, + *e, + imgFileExts[(*g)->format()], + userFriendlyFilePath); + if (!filePath.isEmpty()) + Config::copyFile(config.lastLocation(), + filePath, + userFriendlyFilePath, + (*g)->outputDir() + + "/images"); + ++e; } - 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); + // Documentation template handling + QString templateDir = config.getString( + (*g)->format() + Config::dot + CONFIG_TEMPLATEDIR); + + if (!templateDir.isEmpty()) { + QStringList noExts; + QStringList searchDirs = QStringList() << templateDir; + QStringList scripts = + config.getCleanPathList((*g)->format()+Config::dot+CONFIG_SCRIPTS); + e = scripts.begin(); + while (e != scripts.end()) { + QString userFriendlyFilePath; + QString filePath = Config::findFile(config.lastLocation(), + scriptFiles, + searchDirs, + *e, + noExts, + userFriendlyFilePath); + if (!filePath.isEmpty()) + Config::copyFile(config.lastLocation(), + filePath, + userFriendlyFilePath, + (*g)->outputDir() + + "/scripts"); + ++e; + } + + QStringList styles = + config.getCleanPathList((*g)->format()+Config::dot+CONFIG_STYLESHEETS); + e = styles.begin(); + while (e != styles.end()) { + QString userFriendlyFilePath; + QString filePath = Config::findFile(config.lastLocation(), + styleFiles, + searchDirs, + *e, + noExts, + userFriendlyFilePath); + if (!filePath.isEmpty()) + Config::copyFile(config.lastLocation(), + filePath, + userFriendlyFilePath, + (*g)->outputDir() + + "/style"); + ++e; } - 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; + ++g; + } + + QRegExp secondParamAndAbove("[\2-\7]"); + QSet<QString> formattingNames = config.subVars(CONFIG_FORMATTING); + QSet<QString>::ConstIterator n = formattingNames.begin(); + while (n != formattingNames.end()) { + QString formattingDotName = CONFIG_FORMATTING + Config::dot + *n; + + QSet<QString> formats = config.subVars(formattingDotName); + QSet<QString>::ConstIterator f = formats.begin(); + while (f != formats.end()) { + QString def = config.getString(formattingDotName + + Config::dot + *f); + if (!def.isEmpty()) { + int numParams = Config::numParams(def); + int numOccs = def.count("\1"); + + if (numParams != 1) { + config.lastLocation().warning(tr("Formatting '%1' must " + "have exactly one " + "parameter (found %2)") + .arg(*n).arg(numParams)); + } + else if (numOccs > 1) { + config.lastLocation().fatal(tr("Formatting '%1' must " + "contain exactly one " + "occurrence of '\\1' " + "(found %2)") + .arg(*n).arg(numOccs)); + } + else { + int paramPos = def.indexOf("\1"); + fmtLeftMaps[*f].insert(*n, def.left(paramPos)); + fmtRightMaps[*f].insert(*n, def.mid(paramPos + 1)); + } } - while (n-- > 0) - atom = atom->next(); + ++f; } + ++n; } - return 0; + + project = config.getString(CONFIG_PROJECT); + + QStringList prefixes = config.getStringList(CONFIG_OUTPUTPREFIXES); + if (!prefixes.isEmpty()) { + foreach (QString prefix, prefixes) + outputPrefixes[prefix] = config.getString( + CONFIG_OUTPUTPREFIXES + Config::dot + prefix); + } else + outputPrefixes[QLatin1String("QML")] = QLatin1String("qml-"); } -void Generator::appendFullName(Text& text, - const Node *apparentNode, - const Node *relative, - CodeMarker *marker, - const Node *actualNode) +void Generator::initializeGenerator(const Config & /* config */) { - if (actualNode == 0) - actualNode = apparentNode; - text << Atom(Atom::LinkNode, CodeMarker::stringForNode(actualNode)) - << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK) - << Atom(Atom::String, marker->plainFullName(apparentNode, relative)) - << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK); } -void Generator::appendFullName(Text& text, - const Node *apparentNode, - const QString& fullName, - const Node *actualNode) +bool Generator::matchAhead(const Atom *atom, Atom::Type expectedAtomType) { - 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); + return atom->next() != 0 && atom->next()->type() == expectedAtomType; } -void Generator::appendFullNames(Text& text, - const NodeList& nodes, - const Node* relative, - CodeMarker* marker) +/*! + 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() { - NodeList::ConstIterator n = nodes.begin(); - int index = 0; - while (n != nodes.end()) { - appendFullName(text,*n,relative,marker); - text << comma(index++,nodes.count()); - ++n; - } + return *outStreamStack.top(); } -void Generator::appendSortedNames(Text& text, - const ClassNode *classe, - const QList<RelatedClass> &classes, - CodeMarker *marker) +QString Generator::outputPrefix(const QString &nodeType) { - QList<RelatedClass>::ConstIterator r; - QMap<QString,Text> classMap; - int index = 0; + return outputPrefixes[nodeType]; +} - r = classes.begin(); - while (r != classes.end()) { - if ((*r).node->access() == Node::Public && - (*r).node->status() != Node::Internal - && !(*r).node->doc().isEmpty()) { - Text className; - appendFullName(className, (*r).node, classe, marker); - classMap[className.toString().toLower()] = className; +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"; } - ++r; + } + 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; } - QStringList classNames = classMap.keys(); - classNames.sort(); + *contents = QStringRef(&src, j, i - j); - foreach (const QString &className, classNames) { - text << classMap[className]; - text << separator(index++, classNames.count()); - } + i += tag.length() + 4; + + *pos = i; + if (debug) + qDebug() << " tag " << tag << " found: pos now: " << i; + return true; +#undef SKIP_CHAR } -void Generator::appendSortedQmlNames(Text& text, - const Node* base, - const NodeList& subs, - CodeMarker *marker) +QString Generator::plainCode(const QString& markedCode) { - QMap<QString,Text> classMap; - int index = 0; - - for (int i = 0; i < subs.size(); ++i) { - Text t; - if (!base->isQtQuickNode() || !subs[i]->isQtQuickNode() || - (base->qmlModuleIdentifier() == subs[i]->qmlModuleIdentifier())) { - appendFullName(t, subs[i], base, marker); - classMap[t.toString().toLower()] = t; - } - } + 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; +} - QStringList names = classMap.keys(); - names.sort(); +void Generator::setImageFileExtensions(const QStringList& extensions) +{ + imgFileExts[format()] = extensions; +} - foreach (const QString &name, names) { - text << classMap[name]; - text << separator(index++, names.count()); - } +void Generator::singularPlural(Text& text, const NodeList& nodes) +{ + if (nodes.count() == 1) + text << " is"; + else + text << " are"; } int Generator::skipAtoms(const Atom *atom, Atom::Type type) const @@ -1340,156 +1684,134 @@ int Generator::skipAtoms(const Atom *atom, Atom::Type type) const return skipAhead; } -QString Generator::fullName(const Node *node, - const Node *relative, - CodeMarker *marker) const +void Generator::startText(const Node * /* relative */, + CodeMarker * /* marker */) { - if (node->type() == Node::Fake) { - const FakeNode* fn = static_cast<const FakeNode *>(node); -#if 0 - // Removed for QTBUG-22870 - if (!fn->qmlModuleIdentifier().isEmpty()) - return fn->qmlModuleIdentifier() + QLatin1Char(' ') + fn->title(); -#endif - return fn->title(); - } - else if (node->type() == Node::Class && - !(static_cast<const ClassNode *>(node))->serviceName().isEmpty()) - return (static_cast<const ClassNode *>(node))->serviceName(); - else - return marker->plainFullName(node, relative); } -QString Generator::outputPrefix(const QString &nodeType) +void Generator::supplementAlsoList(const Node *node, QList<Text> &alsoList) { - return outputPrefixes[nodeType]; -} + if (node->type() == Node::Function) { + const FunctionNode *func = static_cast<const FunctionNode *>(node); + if (func->overloadNumber() == 1) { + QString alternateName; + const FunctionNode *alternateFunc = 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. + if (func->name().startsWith("set") && func->name().size() >= 4) { + alternateName = func->name()[3].toLower(); + alternateName += func->name().mid(4); + alternateFunc = func->parent()->findFunctionNode(alternateName); - \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 InnerNode* 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); + if (!alternateFunc) { + alternateName = "is" + func->name().mid(3); + alternateFunc = func->parent()->findFunctionNode(alternateName); + if (!alternateFunc) { + alternateName = "has" + func->name().mid(3); + alternateFunc = func->parent()->findFunctionNode(alternateName); + } + } + } + else if (!func->name().isEmpty()) { + alternateName = "set"; + alternateName += func->name()[0].toUpper(); + alternateName += func->name().mid(1); + alternateFunc = func->parent()->findFunctionNode(alternateName); + } + + if (alternateFunc && alternateFunc->access() != Node::Private) { + int i; + for (i = 0; i < alsoList.size(); ++i) { + if (alsoList.at(i).toString().contains(alternateName)) + break; + } + + if (i == alsoList.size()) { + alternateName += "()"; + + Text also; + also << Atom(Atom::Link, alternateName) + << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK) + << alternateName + << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK); + alsoList.prepend(also); + } + } + } } - 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. +void Generator::terminate() +{ + QList<Generator *>::ConstIterator g = generators.begin(); + while (g != generators.end()) { + if (outputFormats.contains((*g)->format())) + (*g)->terminateGenerator(); + ++g; + } - \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 InnerNode* inner, const QString& t) + fmtLeftMaps.clear(); + fmtRightMaps.clear(); + imgFileExts.clear(); + imageFiles.clear(); + imageDirs.clear(); + outDir_ = ""; + QmlClassNode::terminate(); + ExampleNode::terminate(); +} + +void Generator::terminateGenerator() { - QStringList s; - QStringMultiMap& metaTagMap = const_cast<QStringMultiMap&>(inner->doc().metaTagMap()); - s = metaTagMap.values(t); - if (!s.isEmpty()) - metaTagMap.remove(t); - return s; } /*! - For generating the "New Classes... in 4.6" section on the - What's New in 4.6" page. + Trims trailing whitespace off the \a string and returns + the trimmed string. */ -void Generator::findAllSince(const InnerNode *node) +QString Generator::trimmedTrailing(const QString& string) { - NodeList::const_iterator child = node->childNodes().constBegin(); - - // Traverse the tree, starting at the node supplied. - - while (child != node->childNodes().constEnd()) { - - QString sinceString = (*child)->since(); - - if (((*child)->access() != Node::Private) && !sinceString.isEmpty()) { - - // Insert a new entry into each map for each new since string found. - NewSinceMaps::iterator nsmap = newSinceMaps.find(sinceString); - if (nsmap == newSinceMaps.end()) - nsmap = newSinceMaps.insert(sinceString,NodeMultiMap()); - - NewClassMaps::iterator ncmap = newClassMaps.find(sinceString); - if (ncmap == newClassMaps.end()) - ncmap = newClassMaps.insert(sinceString,NodeMap()); - - NewClassMaps::iterator nqcmap = newQmlClassMaps.find(sinceString); - if (nqcmap == newQmlClassMaps.end()) - nqcmap = newQmlClassMaps.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)->url().isEmpty()) { - if ((*child)->type() == Node::Class && !(*child)->doc().isEmpty()) { - // Insert classes into the since and class maps. - QString className = (*child)->name(); - if ((*child)->parent() && - (*child)->parent()->type() == Node::Namespace && - !(*child)->parent()->name().isEmpty()) - className = (*child)->parent()->name()+"::"+className; - - nsmap.value().insert(className,(*child)); - ncmap.value().insert(className,(*child)); - } - else if ((*child)->subType() == Node::QmlClass) { - // Insert QML elements into the since and element maps. - QString className = (*child)->name(); - if ((*child)->parent() && - (*child)->parent()->type() == Node::Namespace && - !(*child)->parent()->name().isEmpty()) - className = (*child)->parent()->name()+"::"+className; - - nsmap.value().insert(className,(*child)); - nqcmap.value().insert(className,(*child)); - } - else if ((*child)->type() == Node::QmlProperty) { - // 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()->type() == Node::Namespace && - !(*child)->parent()->name().isEmpty()) - name = (*child)->parent()->name()+"::"+name; - - nsmap.value().insert(name,(*child)); - } + QString trimmed = string; + while (trimmed.length() > 0 && trimmed[trimmed.length() - 1].isSpace()) + trimmed.truncate(trimmed.length() - 1); + return trimmed; +} - // Find child nodes with since commands. - if ((*child)->isInnerNode()) { - findAllSince(static_cast<InnerNode *>(*child)); - } +QString Generator::typeString(const Node *node) +{ + switch (node->type()) { + case Node::Namespace: + return "namespace"; + case Node::Class: + return "class"; + case Node::Fake: + { + switch (node->subType()) { + case Node::QmlClass: + return "type"; + case Node::QmlPropertyGroup: + return "property group"; + case Node::QmlBasicType: + return "type"; + default: + return "documentation"; } - ++child; } + case Node::Enum: + return "enum"; + case Node::Typedef: + return "typedef"; + case Node::Function: + return "function"; + case Node::Property: + return "property"; + 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/tools/qdoc/generator.h b/src/tools/qdoc/generator.h index 4021a85f61..bc1b12b958 100644 --- a/src/tools/qdoc/generator.h +++ b/src/tools/qdoc/generator.h @@ -46,22 +46,23 @@ #ifndef GENERATOR_H #define GENERATOR_H -#include <qlist.h> -#include <qmap.h> -#include <qregexp.h> -#include <qstring.h> -#include <qstringlist.h> +#include <QList> +#include <QMap> +#include <QRegExp> +#include <QString> +#include <QStringList> +#include <QTextStream> #include "node.h" #include "text.h" QT_BEGIN_NAMESPACE +typedef QMap<QString, NodeMap> NewClassMaps; +typedef QMap<QString, NodeMultiMap> NewSinceMaps; typedef QMap<QString, const Node*> NodeMap; typedef QMultiMap<QString, Node*> NodeMultiMap; -typedef QMap<QString, NodeMultiMap> NewSinceMaps; typedef QMap<Node*, NodeMultiMap> ParentMaps; -typedef QMap<QString, NodeMap> NewClassMaps; class ClassNode; class Config; @@ -80,87 +81,124 @@ public: Generator(); virtual ~Generator(); + virtual bool canHandleFormat(const QString &format) { return format == this->format(); } + virtual QString format() = 0; + virtual void generateTree(const Tree *tree); virtual void initializeGenerator(const Config &config); virtual void terminateGenerator(); - virtual QString format() = 0; - virtual bool canHandleFormat(const QString &format) { return format == this->format(); } - virtual void generateTree(const Tree *tree) = 0; - static void initialize(const Config& config); - static void terminate(); + static const QString& baseDir() { return baseDir_; } static Generator *generatorForFormat(const QString& format); + static void initialize(const Config& config); static const QString& outputDir() { return outDir_; } - static const QString& baseDir() { return baseDir_; } + static void terminate(); protected: - virtual void startText(const Node *relative, CodeMarker *marker); + virtual void beginSubPage(const InnerNode* node, const QString& fileName); + virtual void endSubPage(); virtual void endText(const Node *relative, CodeMarker *marker); + virtual QString fileBase(const Node* node) const; + virtual QString fileExtension(const Node* node) const = 0; + virtual QString fullName(const Node *node, + const Node *relative, + CodeMarker *marker) const; + 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(const InnerNode *inner, CodeMarker *marker); virtual void generateFakeNode(const FakeNode *fake, CodeMarker *marker); - - virtual bool generateText(const Text& text, - const Node *relative, - CodeMarker *marker); + virtual void generateInheritedBy(const ClassNode *classe, + CodeMarker *marker); + virtual void generateInherits(const ClassNode *classe, + CodeMarker *marker); + virtual void generateInnerNode(const InnerNode *node); + virtual void generateMaintainerList(const InnerNode* node, CodeMarker* marker); + virtual void generateQmlInheritedBy(const QmlClassNode* qcn, CodeMarker* marker); + virtual void generateQmlInherits(const QmlClassNode* qcn, CodeMarker* marker); virtual bool generateQmlText(const Text& text, const Node *relative, CodeMarker *marker, const QString& qmlName); - virtual void generateQmlInherits(const QmlClassNode* qcn, CodeMarker* marker); - virtual void generateBody(const Node *node, CodeMarker *marker); - virtual void generateAlsoList(const Node *node, CodeMarker *marker); - virtual void generateMaintainerList(const InnerNode* node, CodeMarker* marker); - virtual void generateInherits(const ClassNode *classe, - CodeMarker *marker); - virtual void generateInheritedBy(const ClassNode *classe, - CodeMarker *marker); + virtual bool generateText(const Text& text, + const Node *relative, + CodeMarker *marker); + virtual QString imageFileName(const Node *relative, const QString& fileBase); + virtual QString outFileName() { return QString(); } + virtual int skipAtoms(const Atom *atom, Atom::Type type) const; + virtual void startText(const Node *relative, CodeMarker *marker); + virtual QString typeString(const Node *node); - void generateThreadSafeness(const Node *node, CodeMarker *marker); - void generateSince(const Node *node, CodeMarker *marker); - void generateStatus(const Node *node, CodeMarker *marker); + static bool matchAhead(const Atom *atom, Atom::Type expectedAtomType); + static QString outputPrefix(const QString &nodeType); + static void singularPlural(Text& text, const NodeList& nodes); + static void supplementAlsoList(const Node *node, QList<Text> &alsoList); + static QString trimmedTrailing(const QString &string); + static QString sinceTitles[]; + + QString fileName(const Node* node) const; + void findAllSince(const InnerNode *node); + 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 FakeNode *fake, CodeMarker *marker); void generateFileList(const FakeNode* fake, CodeMarker* marker, Node::SubType subtype, const QString& tag); - void generateExampleFiles(const FakeNode *fake, CodeMarker *marker); - - virtual int skipAtoms(const Atom *atom, Atom::Type type) const; - virtual QString fullName(const Node *node, - const Node *relative, - CodeMarker *marker) const; - - virtual QString outFileName() { return QString(); } - + void generateSince(const Node *node, CodeMarker *marker); + void generateStatus(const Node *node, CodeMarker *marker); + void generateThreadSafeness(const Node *node, CodeMarker *marker); + QString getMetadataElement(const InnerNode* inner, const QString& t); + QStringList getMetadataElements(const InnerNode* inner, const QString& t); QString indent(int level, const QString& markedCode); + QTextStream& out(); + 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); - virtual QString typeString(const Node *node); - virtual QString imageFileName(const Node *relative, const QString& fileBase); void setImageFileExtensions(const QStringList& extensions); void unknownAtom(const Atom *atom); - QMap<QString, QString> &formattingLeftMap(); - QMap<QString, QString> &formattingRightMap(); - QMap<QString, QStringList> editionModuleMap; - QMap<QString, QStringList> editionGroupMap; - - static QString trimmedTrailing(const QString &string); - static bool matchAhead(const Atom *atom, Atom::Type expectedAtomType); - static void supplementAlsoList(const Node *node, QList<Text> &alsoList); - static QString outputPrefix(const QString &nodeType); - QString getMetadataElement(const InnerNode* inner, const QString& t); - QStringList getMetadataElements(const InnerNode* inner, const QString& t); - void findAllSince(const InnerNode *node); + QList<NameCollisionNode*> collisionNodes; + QMap<QString, QStringList> editionGroupMap; + QMap<QString, QStringList> editionModuleMap; + QString naturalLanguage; + QTextCodec* outputCodec; + QString outputEncoding; + QStack<QTextStream*> outStreamStack; + NewClassMaps newClassMaps; + NewClassMaps newQmlClassMaps; + NewSinceMaps newSinceMaps; private: - void generateReimplementedFrom(const FunctionNode *func, - CodeMarker *marker); + static QString baseDir_; + 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 QSet<QString> outputFormats; + static QHash<QString, QString> outputPrefixes; + static QStringList scriptDirs; + static QStringList scriptFiles; + static QStringList styleDirs; + static QStringList styleFiles; + void appendFullName(Text& text, const Node *apparentNode, const Node *relative, @@ -178,42 +216,18 @@ private: const ClassNode *classe, const QList<RelatedClass> &classes, CodeMarker *marker); - -protected: void appendSortedQmlNames(Text& text, const Node* base, const NodeList& subs, CodeMarker *marker); + void generateReimplementedFrom(const FunctionNode *func, + CodeMarker *marker); - static QString sinceTitles[]; - NewSinceMaps newSinceMaps; - NewClassMaps newClassMaps; - NewClassMaps newQmlClassMaps; - -private: QString amp; - QString lt; QString gt; + QString lt; QString quot; QRegExp tag; - - static QList<Generator *> generators; - static QMap<QString, QMap<QString, QString> > fmtLeftMaps; - static QMap<QString, QMap<QString, QString> > fmtRightMaps; - static QMap<QString, QStringList> imgFileExts; - static QSet<QString> outputFormats; - static QStringList imageFiles; - static QStringList imageDirs; - static QStringList exampleDirs; - static QStringList exampleImgExts; - static QStringList scriptFiles; - static QStringList scriptDirs; - static QStringList styleFiles; - static QStringList styleDirs; - static QString outDir_; - static QString baseDir_; - static QString project; - static QHash<QString, QString> outputPrefixes; }; QT_END_NAMESPACE diff --git a/src/tools/qdoc/helpprojectwriter.cpp b/src/tools/qdoc/helpprojectwriter.cpp index 096877211d..5efd6ef928 100644 --- a/src/tools/qdoc/helpprojectwriter.cpp +++ b/src/tools/qdoc/helpprojectwriter.cpp @@ -506,10 +506,10 @@ void HelpProjectWriter::writeNode(HelpProject &project, QXmlStreamWriter &writer project.files.insert(membersPath); } if (project.memberStatus[node].contains(Node::Compat)) { - QString compatPath = href.left(href.size()-5) + "-qt3.html"; + QString compatPath = href.left(href.size()-5) + "-compat.html"; writer.writeStartElement("section"); writer.writeAttribute("ref", compatPath); - writer.writeAttribute("title", tr("Qt 3 support members")); + writer.writeAttribute("title", tr("Compatibility members")); writer.writeEndElement(); // section project.files.insert(compatPath); } @@ -553,10 +553,10 @@ void HelpProjectWriter::writeNode(HelpProject &project, QXmlStreamWriter &writer project.files.insert(membersPath); } if (project.memberStatus[node].contains(Node::Compat)) { - QString compatPath = href.left(href.size()-5) + "-qt3.html"; + QString compatPath = href.left(href.size()-5) + "-compat.html"; writer.writeStartElement("section"); writer.writeAttribute("ref", compatPath); - writer.writeAttribute("title", tr("Qt 3 support members")); + writer.writeAttribute("title", tr("Compatibility members")); writer.writeEndElement(); // section project.files.insert(compatPath); } diff --git a/src/tools/qdoc/htmlgenerator.cpp b/src/tools/qdoc/htmlgenerator.cpp index 9b886564c4..b8ad119466 100644 --- a/src/tools/qdoc/htmlgenerator.cpp +++ b/src/tools/qdoc/htmlgenerator.cpp @@ -273,13 +273,12 @@ void HtmlGenerator::generateTree(const Tree *tree) findAllNamespaces(tree->root()); findAllSince(tree->root()); - PageGenerator::generateTree(tree); + Generator::generateTree(tree); reportOrphans(tree->root()); generateDisambiguationPages(); QString fileBase = project.toLower().simplified().replace(" ", "-"); generateIndex(fileBase, projectUrl, projectDescription); - generatePageIndex(outputDir() + QLatin1Char('/') + fileBase + ".pageindex"); helpProjectWriter->generate(myTree); generateManifestFiles(); @@ -1223,7 +1222,7 @@ void HtmlGenerator::generateClassLikeNode(const InnerNode *inner, CodeMarker::Compat); if (!compatLink.isEmpty()) out() << "<li><a href=\"" << compatLink << "\">" - << "Qt 3 support members</a></li>\n"; + << "Compatibility members</a></li>\n"; out() << "</ul>\n"; @@ -1331,6 +1330,8 @@ void HtmlGenerator::generateClassLikeNode(const InnerNode *inner, 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); @@ -1375,7 +1376,7 @@ void HtmlGenerator::generateDisambiguationPages() for (int i=0; i<collisionNodes.size(); ++i) { NameCollisionNode* ncn = collisionNodes.at(i); ncn->clearCurrentChild(); - beginSubPage(ncn, PageGenerator::fileName(ncn)); + beginSubPage(ncn, Generator::fileName(ncn)); QString fullTitle = "Name Collision: " + ncn->fullTitle(); QString htmlTitle = fullTitle; CodeMarker* marker = CodeMarker::markerForFileName(ncn->location().filePath()); @@ -1529,7 +1530,7 @@ void HtmlGenerator::generateFakeNode(const FakeNode *fake, CodeMarker *marker) CodeMarker::Compat); if (!compatLink.isEmpty()) out() << "<li><a href=\"" << compatLink << "\">" - << "Qt 3 support members</a></li>\n"; + << "Compatibility members</a></li>\n"; out() << "</ul>\n"; } @@ -2138,8 +2139,8 @@ QString HtmlGenerator::generateLowStatusMemberFile(const InnerNode *inner, QString fileName; if (status == CodeMarker::Compat) { - title = "Qt 3 Support Members for " + inner->name(); - fileName = fileBase(inner) + "-qt3." + fileExtension(inner); + title = "Compatibility Members for " + inner->name(); + fileName = fileBase(inner) + "-compat." + fileExtension(inner); } else { title = "Obsolete Members for " + inner->name(); @@ -2152,8 +2153,7 @@ QString HtmlGenerator::generateLowStatusMemberFile(const InnerNode *inner, if (status == CodeMarker::Compat) { out() << "<p><b>The following class members are part of the " - "<a href=\"qt3support.html\">Qt 3 support layer</a>.</b> " - "They are provided to help you port old code to Qt 4. We advise against " + "Qt compatibility layer.</b> We advise against " "using them in new code.</p>\n"; } else { @@ -3270,12 +3270,12 @@ QString HtmlGenerator::fileBase(const Node *node) const { QString result; - result = PageGenerator::fileBase(node); + result = Generator::fileBase(node); if (!node->isInnerNode()) { switch (node->status()) { case Node::Compat: - result += "-qt3"; + result += "-compat"; break; case Node::Obsolete: result += "-obsolete"; @@ -3295,7 +3295,7 @@ QString HtmlGenerator::fileName(const Node *node) if (static_cast<const FakeNode *>(node)->subType() == Node::Image) return node->name(); } - return PageGenerator::fileName(node); + return Generator::fileName(node); } QString HtmlGenerator::refForNode(const Node *node) @@ -3719,7 +3719,7 @@ const QPair<QString,QString> HtmlGenerator::anchorForNode(const Node *node) { QPair<QString,QString> anchorPair; - anchorPair.first = PageGenerator::fileName(node); + anchorPair.first = Generator::fileName(node); if (node->type() == Node::Fake) { const FakeNode *fakeNode = static_cast<const FakeNode*>(node); anchorPair.second = fakeNode->title(); @@ -4228,25 +4228,6 @@ void HtmlGenerator::generateQmlInherits(const QmlClassNode* qcn, CodeMarker* mar } /*! - Output the "Inherit by" list for the QML element, - if it is inherited by any other elements. - */ -void HtmlGenerator::generateQmlInheritedBy(const QmlClassNode* qcn, CodeMarker* marker) -{ - if (qcn) { - NodeList subs; - QmlClassNode::subclasses(qcn->name(),subs); - if (!subs.isEmpty()) { - Text text; - text << Atom::ParaLeft << "Inherited by "; - appendSortedQmlNames(text,qcn,subs,marker); - 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. @@ -4307,225 +4288,6 @@ void HtmlGenerator::generateInstantiatedBy(const ClassNode* cn, CodeMarker* mark } } -/*! - Generate the <page> element for the given \a node using the \a writer. - Return true if a <page> element was written; otherwise return false. - */ -bool HtmlGenerator::generatePageElement(QXmlStreamWriter& writer, - const Node* node, - CodeMarker* marker) const -{ - if (node->pageType() == Node::NoPageType) - return false; - if (node->name().isEmpty()) - return true; - if (node->access() == Node::Private) - return false; - - QString guid = QUuid::createUuid().toString(); - QString title; - QString rawTitle; - QString fullTitle; - QStringList pageWords; - QXmlStreamAttributes attributes; - - QString url = node->outputSubdirectory(); - if (!url.isEmpty()) - url.append(QLatin1Char('/')); - url.append(PageGenerator::fileName(node)); - - writer.writeStartElement("page"); - - if (node->isInnerNode()) { - const InnerNode* inner = static_cast<const InnerNode*>(node); - if (!inner->pageKeywords().isEmpty()) - pageWords << inner->pageKeywords(); - - switch (node->type()) { - case Node::Fake: - { - const FakeNode* fake = static_cast<const FakeNode*>(node); - title = fake->fullTitle(); - pageWords << title; - break; - } - case Node::Class: - { - title = node->name() + " Class"; - pageWords << node->name() << "class" << "reference"; - break; - } - case Node::Namespace: - { - rawTitle = marker->plainName(inner); - fullTitle = marker->plainFullName(inner); - title = rawTitle + " Namespace"; - pageWords << rawTitle << "namespace" << "reference"; - break; - } - default: - title = node->name(); - pageWords << title; - break; - } - } - else { - switch (node->type()) { - case Node::Enum: - { - title = node->name() + " Enum"; - pageWords << node->name() << "enum" << "type"; - url += QLatin1Char('#') + node->name() + "-enum"; - break; - } - case Node::Function: - { - title = node->name() + " Function"; - pageWords << node->name() << "function"; - url += QLatin1Char('#') + node->name(); - break; - } - case Node::Property: - { - title = node->name() + " Property"; - pageWords << node->name() << "property"; - url += QLatin1Char('#') + node->name() + "-prop"; - break; - } - case Node::Typedef: - { - title = node->name() + " Type"; - pageWords << node->name() << "typedef" << "type"; - url += QLatin1Char('#') + node->name(); - break; - } - default: - title = node->name(); - pageWords << title; - break; - } - - Node* parent = node->parent(); - if (parent && ((parent->type() == Node::Class) || - (parent->type() == Node::Namespace))) { - pageWords << parent->name(); - } - } - - writer.writeAttribute("id",guid); - writer.writeStartElement("pageWords"); - writer.writeCharacters(pageWords.join(" ")); - - writer.writeEndElement(); - writer.writeStartElement("pageTitle"); - writer.writeCharacters(title); - writer.writeEndElement(); - writer.writeStartElement("pageUrl"); - writer.writeCharacters(url); - writer.writeEndElement(); - writer.writeStartElement("pageType"); - QString ptype = "Article"; - switch (node->pageType()) { - case Node::ApiPage: - ptype = "APIPage"; - break; - case Node::ArticlePage: - ptype = "Article"; - break; - case Node::ExamplePage: - ptype = "Example"; - break; - case Node::HowToPage: - ptype = "HowTo"; - break; - case Node::OverviewPage: - ptype = "Overview"; - break; - case Node::TutorialPage: - ptype = "Tutorial"; - break; - case Node::FAQPage: - ptype = "FAQ"; - break; - default: - break; - } - writer.writeCharacters(ptype); - writer.writeEndElement(); - writer.writeEndElement(); - - if (node->type() == Node::Fake && node->doc().hasTableOfContents()) { - QList<Atom*> toc = node->doc().tableOfContents(); - if (!toc.isEmpty()) { - for (int i = 0; i < toc.size(); ++i) { - Text headingText = Text::sectionHeading(toc.at(i)); - QString s = headingText.toString(); - writer.writeStartElement("page"); - guid = QUuid::createUuid().toString(); - QString internalUrl = url + QLatin1Char('#') + Doc::canonicalTitle(s); - writer.writeAttribute("id",guid); - writer.writeStartElement("pageWords"); - writer.writeCharacters(pageWords.join(" ")); - writer.writeCharacters(" "); - writer.writeCharacters(s); - writer.writeEndElement(); - writer.writeStartElement("pageTitle"); - writer.writeCharacters(s); - writer.writeEndElement(); - writer.writeStartElement("pageUrl"); - writer.writeCharacters(internalUrl); - writer.writeEndElement(); - writer.writeStartElement("pageType"); - writer.writeCharacters("Article"); - writer.writeEndElement(); - writer.writeEndElement(); - } - } - } - return true; -} - -/*! - Traverse the tree recursively and generate the <keyword> - elements. - */ -void HtmlGenerator::generatePageElements(QXmlStreamWriter& writer, const Node* node, CodeMarker* marker) const -{ - if (generatePageElement(writer, node, marker)) { - - if (node->isInnerNode()) { - const InnerNode *inner = static_cast<const InnerNode *>(node); - - // Recurse to write an element for this child node and all its children. - foreach (const Node *child, inner->childNodes()) - generatePageElements(writer, child, marker); - } - } -} - -/*! - Outputs the file containing the index used for searching the html docs. - */ -void HtmlGenerator::generatePageIndex(const QString& fileName) const -{ - QFile file(fileName); - if (!file.open(QFile::WriteOnly | QFile::Text)) - return ; - - CodeMarker *marker = CodeMarker::markerForFileName(fileName); - - QXmlStreamWriter writer(&file); - writer.setAutoFormatting(true); - writer.writeStartDocument(); - writer.writeStartElement("qtPageIndex"); - - generatePageElements(writer, myTree->root(), marker); - - writer.writeEndElement(); // qtPageIndex - writer.writeEndDocument(); - file.close(); -} - void HtmlGenerator::generateExtractionMark(const Node *node, ExtractionMarkType markType) { if (markType != EndMark) { @@ -4719,7 +4481,7 @@ QString HtmlGenerator::fullDocumentLocation(const Node *node, bool subdir) if (node->type() != Node::Class && node->type() != Node::Namespace) { switch (node->status()) { case Node::Compat: - parentName.replace(".html", "-qt3.html"); + parentName.replace(".html", "-compat.html"); break; case Node::Obsolete: parentName.replace(".html", "-obsolete.html"); @@ -5042,7 +4804,7 @@ QXmlStreamWriter& HtmlGenerator::xmlWriter() */ void HtmlGenerator::beginDitamapPage(const InnerNode* node, const QString& fileName) { - PageGenerator::beginSubPage(node,fileName); + Generator::beginSubPage(node,fileName); QXmlStreamWriter* writer = new QXmlStreamWriter(out().device()); xmlWriterStack.push(writer); writer->setAutoFormatting(true); @@ -5061,7 +4823,7 @@ void HtmlGenerator::endDitamapPage() { xmlWriter().writeEndDocument(); delete xmlWriterStack.pop(); - PageGenerator::endSubPage(); + Generator::endSubPage(); } /*! diff --git a/src/tools/qdoc/htmlgenerator.h b/src/tools/qdoc/htmlgenerator.h index 24c5fb7179..1ba88f83df 100644 --- a/src/tools/qdoc/htmlgenerator.h +++ b/src/tools/qdoc/htmlgenerator.h @@ -46,18 +46,18 @@ #ifndef HTMLGENERATOR_H #define HTMLGENERATOR_H -#include <qmap.h> -#include <qregexp.h> +#include <QMap> +#include <QRegExp> #include <QXmlStreamWriter> #include "codemarker.h" #include "config.h" -#include "pagegenerator.h" +#include "generator.h" QT_BEGIN_NAMESPACE class HelpProjectWriter; -class HtmlGenerator : public PageGenerator +class HtmlGenerator : public Generator { public: enum SinceType { @@ -180,7 +180,6 @@ private: const InnerNode *relative, CodeMarker *marker); void generateQmlInherits(const QmlClassNode* qcn, CodeMarker* marker); - void generateQmlInheritedBy(const QmlClassNode* qcn, CodeMarker* marker); void generateQmlInstantiates(const QmlClassNode* qcn, CodeMarker* marker); void generateInstantiatedBy(const ClassNode* cn, CodeMarker* marker); @@ -240,13 +239,6 @@ private: const Node *relative, CodeMarker *marker); void endLink(); - bool generatePageElement(QXmlStreamWriter& writer, - const Node* node, - CodeMarker* marker) const; - void generatePageElements(QXmlStreamWriter& writer, - const Node* node, - CodeMarker* marker) const; - void generatePageIndex(const QString& fileName) const; void generateExtractionMark(const Node *node, ExtractionMarkType markType); void reportOrphans(const InnerNode* parent); diff --git a/src/tools/qdoc/node.cpp b/src/tools/qdoc/node.cpp index e4350f1d9e..fb4a3fe271 100644 --- a/src/tools/qdoc/node.cpp +++ b/src/tools/qdoc/node.cpp @@ -1171,8 +1171,6 @@ QString Node::moduleName() const return "QtNetwork"; else if (moduleName == "opengl") return "QtOpenGL"; - else if (moduleName == "qt3support") - return "Qt3Support"; else if (moduleName == "svg") return "QtSvg"; else if (moduleName == "sql") diff --git a/src/tools/qdoc/node.h b/src/tools/qdoc/node.h index 8c6eb673cd..5b1eb21113 100644 --- a/src/tools/qdoc/node.h +++ b/src/tools/qdoc/node.h @@ -907,6 +907,7 @@ inline void PropertyNode::addFunction(FunctionNode* function, FunctionRole role) inline void PropertyNode::addSignal(FunctionNode* function, FunctionRole role) { funcs[(int)role].append(function); + function->setAssociatedProperty(this); } inline NodeList PropertyNode::functions() const diff --git a/src/tools/qdoc/pagegenerator.cpp b/src/tools/qdoc/pagegenerator.cpp deleted file mode 100644 index 6a52b7755a..0000000000 --- a/src/tools/qdoc/pagegenerator.cpp +++ /dev/null @@ -1,389 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/ -** -** This file is part of the tools applications of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** GNU Lesser General Public License Usage -** This file may be used under the terms of the GNU Lesser General Public -** License version 2.1 as published by the Free Software Foundation and -** appearing in the file LICENSE.LGPL included in the packaging of this -** file. Please review the following information to ensure the GNU Lesser -** General Public License version 2.1 requirements will be met: -** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU General -** Public License version 3.0 as published by the Free Software Foundation -** and appearing in the file LICENSE.GPL included in the packaging of this -** file. Please review the following information to ensure the GNU General -** Public License version 3.0 requirements will be met: -** http://www.gnu.org/copyleft/gpl.html. -** -** Other Usage -** Alternatively, this file may be used in accordance with the terms and -** conditions contained in a signed written agreement between you and Nokia. -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -/* - pagegenerator.cpp -*/ - -#include <qfile.h> -#include <qfileinfo.h> -#include <qdebug.h> -#include "codemarker.h" -#include "pagegenerator.h" -#include "tree.h" - -QT_BEGIN_NAMESPACE - -/*! - Nothing to do in the constructor. - */ -PageGenerator::PageGenerator() - : outputCodec(0) -{ - // nothing. -} - -/*! - The destructor - */ -PageGenerator::~PageGenerator() -{ - while (!outStreamStack.isEmpty()) - endSubPage(); -} - -bool PageGenerator::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 -} - -/*! - This function is recursive. - */ -void PageGenerator::generateTree(const Tree *tree) -{ - generateInnerNode(tree->root()); -} - -QString PageGenerator::fileBase(const Node *node) const -{ - if (node->relates()) - node = node->relates(); - else if (!node->isInnerNode()) - node = node->parent(); - if (node->subType() == Node::QmlPropertyGroup) { - node = node->parent(); - } - - QString base = node->doc().baseName(); - if (!base.isEmpty()) - return base; - - const Node *p = node; - - forever { - const Node *pp = p->parent(); - base.prepend(p->name()); - if (!p->qmlModuleIdentifier().isEmpty()) - base.prepend(p->qmlModuleIdentifier()+QChar('-')); - /* - To avoid file name conflicts in the html directory, - we prepend a prefix (by default, "qml-") to the file name of QML - element doc files. - */ - if ((p->subType() == Node::QmlClass) || - (p->subType() == Node::QmlBasicType)) { - base.prepend(outputPrefix(QLatin1String("QML"))); - } - if (!pp || pp->name().isEmpty() || pp->type() == Node::Fake) - break; - base.prepend(QLatin1Char('-')); - p = pp; - } - if (node->type() == Node::Fake) { - if (node->subType() == Node::Collision) { - const NameCollisionNode* ncn = static_cast<const NameCollisionNode*>(node); - if (ncn->currentChild()) - return fileBase(ncn->currentChild()); - base.prepend("collision-"); - } -#ifdef QDOC2_COMPAT - if (base.endsWith(".html")) - base.truncate(base.length() - 5); -#endif - } - - // the code below is effectively equivalent to: - // base.replace(QRegExp("[^A-Za-z0-9]+"), " "); - // base = base.trimmed(); - // base.replace(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); - 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 PageGenerator::fileName(const Node* node) const -{ - if (!node->url().isEmpty()) - return node->url(); - - QString name = fileBase(node); - name += QLatin1Char('.'); - name += fileExtension(node); - return name; -} - -/*! - Return the current output file name. - */ -QString PageGenerator::outFileName() -{ - return QFileInfo(static_cast<QFile*>(out().device())->fileName()).fileName(); -} - -/*! - 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 PageGenerator::beginSubPage(const InnerNode* node, const QString& fileName) -{ - QString path = outputDir() + QLatin1Char('/'); - if (!node->outputSubdirectory().isEmpty()) - path += node->outputSubdirectory() + QLatin1Char('/'); - path += fileName; - QFile* outFile = new QFile(path); - if (!outFile->open(QFile::WriteOnly)) - node->location().fatal(tr("Cannot open output file '%1'").arg(outFile->fileName())); - QTextStream* out = new QTextStream(outFile); - - if (outputCodec) - out->setCodec(outputCodec); - outStreamStack.push(out); - const_cast<InnerNode*>(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 PageGenerator::endSubPage() -{ - outStreamStack.top()->flush(); - delete outStreamStack.top()->device(); - delete outStreamStack.pop(); -} - -/*! - Used for writing to the current output stream. Returns a - reference to the crrent output stream, which is then used - with the \c {<<} operator for writing. - */ -QTextStream &PageGenerator::out() -{ - return *outStreamStack.top(); -} - -/*! - Recursive writing of HTML files from the root \a node. - - \note NameCollisionNodes are skipped here and processed - later. See HtmlGenerator::generateDisambiguationPages() - for more on this. - */ -void -PageGenerator::generateInnerNode(const InnerNode* node) -{ - if (!node->url().isNull()) - return; - - if (node->type() == Node::Fake) { - const FakeNode *fakeNode = static_cast<const FakeNode *>(node); - if (fakeNode->subType() == Node::ExternalPage) - return; - if (fakeNode->subType() == Node::Image) - return; - if (fakeNode->subType() == Node::QmlPropertyGroup) - return; - if (fakeNode->subType() == Node::Page) { - if (node->count() > 0) - qDebug("PAGE %s HAS CHILDREN", qPrintable(fakeNode->title())); - } - } - - /* - Obtain a code marker for the source file. - */ - CodeMarker *marker = CodeMarker::markerForFileName(node->location().filePath()); - - if (node->parent() != 0) { - /* - Skip name collision nodes here and process them - later in generateDisambiguationPages(). Each one - is appended to a list for later. - */ - if ((node->type() == Node::Fake) && (node->subType() == Node::Collision)) { - const NameCollisionNode* ncn = static_cast<const NameCollisionNode*>(node); - collisionNodes.append(const_cast<NameCollisionNode*>(ncn)); - } - else { - beginSubPage(node, fileName(node)); - if (node->type() == Node::Namespace || node->type() == Node::Class) { - generateClassLikeNode(node, marker); - } - else if (node->type() == Node::Fake) { - generateFakeNode(static_cast<const FakeNode *>(node), marker); - } - endSubPage(); - } - } - - NodeList::ConstIterator c = node->childNodes().begin(); - while (c != node->childNodes().end()) { - if ((*c)->isInnerNode() && (*c)->access() != Node::Private) { - generateInnerNode((const InnerNode *) *c); - } - ++c; - } -} - -QT_END_NAMESPACE diff --git a/src/tools/qdoc/pagegenerator.h b/src/tools/qdoc/pagegenerator.h deleted file mode 100644 index da0d32d2ff..0000000000 --- a/src/tools/qdoc/pagegenerator.h +++ /dev/null @@ -1,99 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/ -** -** This file is part of the tools applications of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** GNU Lesser General Public License Usage -** This file may be used under the terms of the GNU Lesser General Public -** License version 2.1 as published by the Free Software Foundation and -** appearing in the file LICENSE.LGPL included in the packaging of this -** file. Please review the following information to ensure the GNU Lesser -** General Public License version 2.1 requirements will be met: -** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU General -** Public License version 3.0 as published by the Free Software Foundation -** and appearing in the file LICENSE.GPL included in the packaging of this -** file. Please review the following information to ensure the GNU General -** Public License version 3.0 requirements will be met: -** http://www.gnu.org/copyleft/gpl.html. -** -** Other Usage -** Alternatively, this file may be used in accordance with the terms and -** conditions contained in a signed written agreement between you and Nokia. -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -/* - pagegenerator.h -*/ - -#ifndef PAGEGENERATOR_H -#define PAGEGENERATOR_H - -#include <QStack> -#include <qtextstream.h> -#include "generator.h" -#include "location.h" - -QT_BEGIN_NAMESPACE - -class QTextCodec; -class ClassNode; -class InnerNode; -class NamespaceNode; -class NameCollisionNode; - -class PageGenerator : public Generator -{ -public: - PageGenerator(); - ~PageGenerator(); - - virtual void generateTree(const Tree *tree); - virtual void generateDisambiguationPages() { } - -protected: - virtual QString fileBase(const Node* node) const; - virtual QString fileExtension(const Node* node) const = 0; - QString fileName(const Node* node) const; - QString outFileName(); - virtual void beginSubPage(const InnerNode* node, const QString& fileName); - virtual void endSubPage(); - virtual void generateInnerNode(const InnerNode *node); - QTextStream& out(); - - QString naturalLanguage; - QString outputEncoding; - QTextCodec* outputCodec; - bool parseArg(const QString& src, - const QString& tag, - int* pos, - int n, - QStringRef* contents, - QStringRef* par1 = 0, - bool debug = false); - -protected: - QStack<QTextStream*> outStreamStack; - QList<NameCollisionNode*> collisionNodes; -}; - -QT_END_NAMESPACE - -#endif diff --git a/src/tools/qdoc/qdoc.pro b/src/tools/qdoc/qdoc.pro index 03de9f91df..09f9b7a0c1 100644 --- a/src/tools/qdoc/qdoc.pro +++ b/src/tools/qdoc/qdoc.pro @@ -39,7 +39,6 @@ HEADERS += atom.h \ location.h \ node.h \ openedlist.h \ - pagegenerator.h \ plaincodemarker.h \ puredocparser.h \ quoter.h \ @@ -65,7 +64,6 @@ SOURCES += atom.cpp \ main.cpp \ node.cpp \ openedlist.cpp \ - pagegenerator.cpp \ plaincodemarker.cpp \ puredocparser.cpp \ quoter.cpp \ diff --git a/src/tools/qdoc/qmlvisitor.cpp b/src/tools/qdoc/qmlvisitor.cpp index 9c934ebcd1..956f874cbf 100644 --- a/src/tools/qdoc/qmlvisitor.cpp +++ b/src/tools/qdoc/qmlvisitor.cpp @@ -266,6 +266,7 @@ void QmlDocVisitor::applyMetacommands(QQmlJS::AST::SourceLocation, if (!topic.isEmpty()) { args = doc.metaCommandArgs(topic); if (topic == COMMAND_QMLCLASS) { + // do nothing. } else if (topic == COMMAND_QMLPROPERTY) { if (node->type() == Node::QmlProperty) { @@ -320,7 +321,6 @@ void QmlDocVisitor::applyMetacommands(QQmlJS::AST::SourceLocation, if (node->name() == args[0]) doc.location().warning(tr("%1 tries to inherit itself").arg(args[0])); else { - qDebug() << "QML Component:" << node->name() << "inherits:" << args[0]; CodeParser::setLink(node, Node::InheritsLink, args[0]); if (node->subType() == Node::QmlClass) { QmlClassNode::addInheritedBy(args[0],node); @@ -339,8 +339,12 @@ void QmlDocVisitor::applyMetacommands(QQmlJS::AST::SourceLocation, qpn->setReadOnly(1); } } - else if (command == COMMAND_INGROUP) { - tree->addToGroup(node, args[0]); + else if ((command == COMMAND_INGROUP) && !args.isEmpty()) { + QStringList::ConstIterator arg = args.begin(); + while (arg != args.end()) { + tree->addToGroup(node, *arg); + ++arg; + } } else if (command == COMMAND_INTERNAL) { node->setAccess(Node::Private); diff --git a/src/tools/qdoc/tree.cpp b/src/tools/qdoc/tree.cpp index 4b2a8ba55c..450f2d777c 100644 --- a/src/tools/qdoc/tree.cpp +++ b/src/tools/qdoc/tree.cpp @@ -1739,7 +1739,7 @@ bool Tree::generateIndexSection(QXmlStreamWriter& writer, const FunctionNode* functionNode = static_cast<const FunctionNode*>(fnNode); writer.writeStartElement("setter"); writer.writeAttribute("name", functionNode->name()); - writer.writeEndElement(); // getter + writer.writeEndElement(); // setter } } foreach (const Node* fnNode, propertyNode->resetters()) { @@ -1747,7 +1747,15 @@ bool Tree::generateIndexSection(QXmlStreamWriter& writer, const FunctionNode* functionNode = static_cast<const FunctionNode*>(fnNode); writer.writeStartElement("resetter"); writer.writeAttribute("name", functionNode->name()); - writer.writeEndElement(); // getter + 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 } } } |