util/tools/qdoc3/generator.cpp
changeset 7 f7bc934e204c
equal deleted inserted replaced
3:41300fa6a67c 7:f7bc934e204c
       
     1 /****************************************************************************
       
     2 **
       
     3 ** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
       
     4 ** All rights reserved.
       
     5 ** Contact: Nokia Corporation (qt-info@nokia.com)
       
     6 **
       
     7 ** This file is part of the tools applications of the Qt Toolkit.
       
     8 **
       
     9 ** $QT_BEGIN_LICENSE:LGPL$
       
    10 ** No Commercial Usage
       
    11 ** This file contains pre-release code and may not be distributed.
       
    12 ** You may use this file in accordance with the terms and conditions
       
    13 ** contained in the Technology Preview License Agreement accompanying
       
    14 ** this package.
       
    15 **
       
    16 ** GNU Lesser General Public License Usage
       
    17 ** Alternatively, this file may be used under the terms of the GNU Lesser
       
    18 ** General Public License version 2.1 as published by the Free Software
       
    19 ** Foundation and appearing in the file LICENSE.LGPL included in the
       
    20 ** packaging of this file.  Please review the following information to
       
    21 ** ensure the GNU Lesser General Public License version 2.1 requirements
       
    22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
       
    23 **
       
    24 ** In addition, as a special exception, Nokia gives you certain additional
       
    25 ** rights.  These rights are described in the Nokia Qt LGPL Exception
       
    26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
       
    27 **
       
    28 ** If you have questions regarding the use of this file, please contact
       
    29 ** Nokia at qt-info@nokia.com.
       
    30 **
       
    31 **
       
    32 **
       
    33 **
       
    34 **
       
    35 **
       
    36 **
       
    37 **
       
    38 ** $QT_END_LICENSE$
       
    39 **
       
    40 ****************************************************************************/
       
    41 
       
    42 /*
       
    43   generator.cpp
       
    44 */
       
    45 #include <QtCore>
       
    46 #include <qdir.h>
       
    47 #include <qdebug.h>
       
    48 #include "codemarker.h"
       
    49 #include "config.h"
       
    50 #include "doc.h"
       
    51 #include "editdistance.h"
       
    52 #include "generator.h"
       
    53 #include "node.h"
       
    54 #include "openedlist.h"
       
    55 #include "quoter.h"
       
    56 #include "separator.h"
       
    57 #include "tokenizer.h"
       
    58 
       
    59 QT_BEGIN_NAMESPACE
       
    60 
       
    61 QList<Generator *> Generator::generators;
       
    62 QMap<QString, QMap<QString, QString> > Generator::fmtLeftMaps;
       
    63 QMap<QString, QMap<QString, QString> > Generator::fmtRightMaps;
       
    64 QMap<QString, QStringList> Generator::imgFileExts;
       
    65 QSet<QString> Generator::outputFormats;
       
    66 QStringList Generator::imageFiles;
       
    67 QStringList Generator::imageDirs;
       
    68 QStringList Generator::exampleDirs;
       
    69 QStringList Generator::exampleImgExts;
       
    70 QString Generator::outDir;
       
    71 QString Generator::project;
       
    72 
       
    73 static void singularPlural(Text& text, const NodeList& nodes)
       
    74 {
       
    75     if (nodes.count() == 1)
       
    76         text << " is";
       
    77     else
       
    78         text << " are";
       
    79 }
       
    80 
       
    81 Generator::Generator()
       
    82     : amp("&amp;"),
       
    83       lt("&lt;"),
       
    84       gt("&gt;"),
       
    85       quot("&quot;"),
       
    86       tag("</?@[^>]*>")
       
    87 {
       
    88     generators.prepend(this);
       
    89 }
       
    90 
       
    91 Generator::~Generator()
       
    92 {
       
    93     generators.removeAll(this);
       
    94 }
       
    95 
       
    96 void Generator::initializeGenerator(const Config & /* config */)
       
    97 {
       
    98 }
       
    99 
       
   100 void Generator::terminateGenerator()
       
   101 {
       
   102 }
       
   103 
       
   104 void Generator::initialize(const Config &config)
       
   105 {
       
   106     outputFormats = config.getStringSet(CONFIG_OUTPUTFORMATS);
       
   107     if (!outputFormats.isEmpty()) {
       
   108         outDir = config.getString(CONFIG_OUTPUTDIR);
       
   109         if (outDir.isEmpty())
       
   110             config.lastLocation().fatal(tr("No output directory specified in configuration file"));
       
   111 
       
   112         QDir dirInfo;
       
   113         if (dirInfo.exists(outDir)) {
       
   114             if (!Config::removeDirContents(outDir))
       
   115                 config.lastLocation().error(tr("Cannot empty output directory '%1'").arg(outDir));
       
   116         }
       
   117         else {
       
   118             if (!dirInfo.mkpath(outDir))
       
   119                 config.lastLocation().fatal(tr("Cannot create output directory '%1'").arg(outDir));
       
   120         }
       
   121 
       
   122         if (!dirInfo.mkdir(outDir + "/images"))
       
   123             config.lastLocation().fatal(tr("Cannot create output directory '%1'")
       
   124                                         .arg(outDir + "/images"));
       
   125         if (!dirInfo.mkdir(outDir + "/images/used-in-examples"))
       
   126             config.lastLocation().fatal(tr("Cannot create output directory '%1'")
       
   127                                         .arg(outDir + "/images/used-in-examples"));
       
   128     }
       
   129 
       
   130     imageFiles = config.getStringList(CONFIG_IMAGES);
       
   131     imageDirs = config.getStringList(CONFIG_IMAGEDIRS);
       
   132     exampleDirs = config.getStringList(CONFIG_EXAMPLEDIRS);
       
   133     exampleImgExts = config.getStringList(CONFIG_EXAMPLES + Config::dot +
       
   134                                           CONFIG_IMAGEEXTENSIONS);
       
   135 
       
   136     QString imagesDotFileExtensions =
       
   137         CONFIG_IMAGES + Config::dot + CONFIG_FILEEXTENSIONS;
       
   138     QSet<QString> formats = config.subVars(imagesDotFileExtensions);
       
   139     QSet<QString>::ConstIterator f = formats.begin();
       
   140     while (f != formats.end()) {
       
   141         imgFileExts[*f] = config.getStringList(imagesDotFileExtensions +
       
   142                                                Config::dot + *f);
       
   143         ++f;
       
   144     }
       
   145 
       
   146     QList<Generator *>::ConstIterator g = generators.begin();
       
   147     while (g != generators.end()) {
       
   148         if (outputFormats.contains((*g)->format())) {
       
   149             (*g)->initializeGenerator(config);
       
   150             QStringList extraImages =
       
   151                 config.getStringList(CONFIG_EXTRAIMAGES+Config::dot+(*g)->format());
       
   152             QStringList::ConstIterator e = extraImages.begin();
       
   153             while (e != extraImages.end()) {
       
   154                 QString userFriendlyFilePath;
       
   155                 QString filePath = Config::findFile(config.lastLocation(),
       
   156                                                     imageFiles,
       
   157                                                     imageDirs,
       
   158                                                     *e,
       
   159                                                     imgFileExts[(*g)->format()],
       
   160                                                     userFriendlyFilePath);
       
   161                 if (!filePath.isEmpty())
       
   162                     Config::copyFile(config.lastLocation(),
       
   163                                      filePath,
       
   164                                      userFriendlyFilePath,
       
   165                                      (*g)->outputDir() +
       
   166                                      "/images");
       
   167                 ++e;
       
   168             }
       
   169         }
       
   170         ++g;
       
   171     }
       
   172 
       
   173     QRegExp secondParamAndAbove("[\2-\7]");
       
   174     QSet<QString> formattingNames = config.subVars(CONFIG_FORMATTING);
       
   175     QSet<QString>::ConstIterator n = formattingNames.begin();
       
   176     while (n != formattingNames.end()) {
       
   177         QString formattingDotName = CONFIG_FORMATTING + Config::dot + *n;
       
   178 
       
   179         QSet<QString> formats = config.subVars(formattingDotName);
       
   180         QSet<QString>::ConstIterator f = formats.begin();
       
   181         while (f != formats.end()) {
       
   182             QString def = config.getString(formattingDotName +
       
   183                                            Config::dot + *f);
       
   184             if (!def.isEmpty()) {
       
   185                 int numParams = Config::numParams(def);
       
   186                 int numOccs = def.count("\1");
       
   187 
       
   188                 if (numParams != 1) {
       
   189                     config.lastLocation().warning(tr("Formatting '%1' must "
       
   190                                                      "have exactly one "
       
   191                                                      "parameter (found %2)")
       
   192                                                   .arg(*n).arg(numParams));
       
   193                 }
       
   194                 else if (numOccs > 1) {
       
   195                     config.lastLocation().fatal(tr("Formatting '%1' must "
       
   196                                                    "contain exactly one "
       
   197                                                    "occurrence of '\\1' "
       
   198                                                    "(found %2)")
       
   199                                                 .arg(*n).arg(numOccs));
       
   200                 }
       
   201                 else {
       
   202                     int paramPos = def.indexOf("\1");
       
   203                     fmtLeftMaps[*f].insert(*n, def.left(paramPos));
       
   204                     fmtRightMaps[*f].insert(*n, def.mid(paramPos + 1));
       
   205                 }
       
   206             }
       
   207             ++f;
       
   208         }
       
   209         ++n;
       
   210     }
       
   211 
       
   212     project = config.getString(CONFIG_PROJECT);
       
   213 }
       
   214 
       
   215 void Generator::terminate()
       
   216 {
       
   217     QList<Generator *>::ConstIterator g = generators.begin();
       
   218     while (g != generators.end()) {
       
   219         if (outputFormats.contains((*g)->format()))
       
   220             (*g)->terminateGenerator();
       
   221         ++g;
       
   222     }
       
   223 
       
   224     fmtLeftMaps.clear();
       
   225     fmtRightMaps.clear();
       
   226     imgFileExts.clear();
       
   227     imageFiles.clear();
       
   228     imageDirs.clear();
       
   229     outDir = "";
       
   230 }
       
   231 
       
   232 Generator *Generator::generatorForFormat(const QString& format)
       
   233 {
       
   234     QList<Generator *>::ConstIterator g = generators.begin();
       
   235     while (g != generators.end()) {
       
   236         if ((*g)->format() == format)
       
   237             return *g;
       
   238         ++g;
       
   239     }
       
   240     return 0;
       
   241 }
       
   242 
       
   243 void Generator::startText(const Node * /* relative */,
       
   244                           CodeMarker * /* marker */)
       
   245 {
       
   246 }
       
   247 
       
   248 void Generator::endText(const Node * /* relative */,
       
   249                         CodeMarker * /* marker */)
       
   250 {
       
   251 }
       
   252 
       
   253 int Generator::generateAtom(const Atom * /* atom */,
       
   254                             const Node * /* relative */,
       
   255                             CodeMarker * /* marker */)
       
   256 {
       
   257     return 0;
       
   258 }
       
   259 
       
   260 void Generator::generateClassLikeNode(const InnerNode * /* classe */,
       
   261                                       CodeMarker * /* marker */)
       
   262 {
       
   263 }
       
   264 
       
   265 void Generator::generateFakeNode(const FakeNode * /* fake */,
       
   266                                  CodeMarker * /* marker */)
       
   267 {
       
   268 }
       
   269 
       
   270 bool Generator::generateText(const Text& text,
       
   271                              const Node *relative,
       
   272                              CodeMarker *marker)
       
   273 {
       
   274     if (text.firstAtom() != 0) {
       
   275         int numAtoms = 0;
       
   276         startText(relative, marker);
       
   277         generateAtomList(text.firstAtom(),
       
   278                          relative,
       
   279                          marker,
       
   280                          true,
       
   281                          numAtoms);
       
   282         endText(relative, marker);
       
   283         return true;
       
   284     }
       
   285     return false;
       
   286 }
       
   287 
       
   288 #ifdef QDOC_QML
       
   289 /*!
       
   290   Extract sections of markup text surrounded by \e qmltext
       
   291   and \e endqmltext and output them.
       
   292  */
       
   293 bool Generator::generateQmlText(const Text& text,
       
   294                                 const Node *relative,
       
   295                                 CodeMarker *marker,
       
   296                                 const QString& /* qmlName */ )
       
   297 {
       
   298     const Atom* atom = text.firstAtom();
       
   299     if (atom == 0)
       
   300         return false;
       
   301 
       
   302     startText(relative, marker);
       
   303     while (atom) {
       
   304         if (atom->type() != Atom::QmlText)
       
   305             atom = atom->next();
       
   306         else {
       
   307             atom = atom->next();
       
   308             while (atom && (atom->type() != Atom::EndQmlText)) {
       
   309                 int n = 1 + generateAtom(atom, relative, marker);
       
   310                 while (n-- > 0)
       
   311                     atom = atom->next();
       
   312             }
       
   313         }
       
   314     }
       
   315     endText(relative, marker);
       
   316     return true;
       
   317 }
       
   318 #endif
       
   319 
       
   320 void Generator::generateBody(const Node *node, CodeMarker *marker)
       
   321 {
       
   322     bool quiet = false;
       
   323 
       
   324     if (node->type() == Node::Function) {
       
   325 #if 0        
       
   326         const FunctionNode *func = (const FunctionNode *) node;
       
   327         if (func->isOverload() && func->metaness() != FunctionNode::Ctor)
       
   328             generateOverload(node, marker);
       
   329 #endif        
       
   330     }
       
   331     else if (node->type() == Node::Fake) {
       
   332         const FakeNode *fake = static_cast<const FakeNode *>(node);
       
   333         if (fake->subType() == Node::Example)
       
   334             generateExampleFiles(fake, marker);
       
   335         else if ((fake->subType() == Node::File) || (fake->subType() == Node::Image))
       
   336             quiet = true;
       
   337     }
       
   338 
       
   339     if (node->doc().isEmpty()) {
       
   340         if (!quiet && !node->isReimp()) // ### might be unnecessary
       
   341             node->location().warning(tr("No documentation for '%1'")
       
   342                             .arg(marker->plainFullName(node)));
       
   343     }
       
   344     else {
       
   345         if (node->type() == Node::Function) {
       
   346             const FunctionNode *func = static_cast<const FunctionNode *>(node);
       
   347             if (func->reimplementedFrom() != 0)
       
   348                 generateReimplementedFrom(func, marker);
       
   349         }
       
   350         
       
   351         if (!generateText(node->doc().body(), node, marker))
       
   352             if (node->isReimp())
       
   353                 return;
       
   354 
       
   355         if (node->type() == Node::Enum) {
       
   356             const EnumNode *enume = (const EnumNode *) node;
       
   357 
       
   358             QSet<QString> definedItems;
       
   359             QList<EnumItem>::ConstIterator it = enume->items().begin();
       
   360             while (it != enume->items().end()) {
       
   361                 definedItems.insert((*it).name());
       
   362                 ++it;
       
   363             }
       
   364 
       
   365             QSet<QString> documentedItems = enume->doc().enumItemNames().toSet();
       
   366             QSet<QString> allItems = definedItems + documentedItems;
       
   367             if (allItems.count() > definedItems.count() ||
       
   368                  allItems.count() > documentedItems.count()) {
       
   369                 QSet<QString>::ConstIterator a = allItems.begin();
       
   370                 while (a != allItems.end()) {
       
   371                     if (!definedItems.contains(*a)) {
       
   372                         QString details;
       
   373                         QString best = nearestName(*a, definedItems);
       
   374                         if (!best.isEmpty() && !documentedItems.contains(best))
       
   375                             details = tr("Maybe you meant '%1'?").arg(best);
       
   376 
       
   377                         node->doc().location().warning(
       
   378                             tr("No such enum item '%1' in %2").arg(*a).arg(marker->plainFullName(node)),
       
   379                             details);
       
   380                     }
       
   381                     else if (!documentedItems.contains(*a)) {
       
   382                         node->doc().location().warning(
       
   383                             tr("Undocumented enum item '%1' in %2").arg(*a).arg(marker->plainFullName(node)));
       
   384                     }
       
   385                     ++a;
       
   386                 }
       
   387             }
       
   388         }
       
   389         else if (node->type() == Node::Function) {
       
   390             const FunctionNode *func = static_cast<const FunctionNode *>(node);
       
   391             QSet<QString> definedParams;
       
   392             QList<Parameter>::ConstIterator p = func->parameters().begin();
       
   393             while (p != func->parameters().end()) {
       
   394                 if ((*p).name().isEmpty() && (*p).leftType() != QLatin1String("...")
       
   395                         && func->name() != QLatin1String("operator++")
       
   396                         && func->name() != QLatin1String("operator--")) {
       
   397                     node->doc().location().warning(tr("Missing parameter name"));
       
   398                 }
       
   399                 else {
       
   400                     definedParams.insert((*p).name());
       
   401                 }
       
   402                 ++p;
       
   403             }
       
   404 
       
   405             QSet<QString> documentedParams = func->doc().parameterNames();
       
   406             QSet<QString> allParams = definedParams + documentedParams;
       
   407             if (allParams.count() > definedParams.count()
       
   408                     || allParams.count() > documentedParams.count()) {
       
   409                 QSet<QString>::ConstIterator a = allParams.begin();
       
   410                 while (a != allParams.end()) {
       
   411                     if (!definedParams.contains(*a)) {
       
   412                         QString details;
       
   413                         QString best = nearestName(*a, definedParams);
       
   414                         if (!best.isEmpty())
       
   415                             details = tr("Maybe you meant '%1'?").arg(best);
       
   416 
       
   417                         node->doc().location().warning(
       
   418                             tr("No such parameter '%1' in %2").arg(*a).arg(marker->plainFullName(node)),
       
   419                             details);
       
   420                     }
       
   421                     else if (!(*a).isEmpty() && !documentedParams.contains(*a)) {
       
   422                         bool needWarning = (func->status() > Node::Obsolete);
       
   423                         if (func->overloadNumber() > 1) {
       
   424                             FunctionNode *primaryFunc =
       
   425                                     func->parent()->findFunctionNode(func->name());
       
   426                             if (primaryFunc) {
       
   427                                 foreach (const Parameter &param,
       
   428                                          primaryFunc->parameters()) {
       
   429                                     if (param.name() == *a) {
       
   430                                         needWarning = false;
       
   431                                         break;
       
   432                                     }
       
   433                                 }
       
   434                             }
       
   435                         }
       
   436                         if (needWarning && !func->isReimp())
       
   437                             node->doc().location().warning(
       
   438                                 tr("Undocumented parameter '%1' in %2")
       
   439                                 .arg(*a).arg(marker->plainFullName(node)));
       
   440                     }
       
   441                     ++a;
       
   442                 }
       
   443             }
       
   444 /* Something like this return value check should be implemented at some point. */
       
   445             if (func->status() > Node::Obsolete && func->returnType() == "bool"
       
   446                     && func->reimplementedFrom() == 0 && !func->isOverload()) {
       
   447                 QString body = func->doc().body().toString();
       
   448                 if (!body.contains("return", Qt::CaseInsensitive))
       
   449                     node->doc().location().warning(tr("Undocumented return value"));
       
   450             }
       
   451 #if 0
       
   452             // Now we put this at the top, before the other text.
       
   453             if (func->reimplementedFrom() != 0)
       
   454                 generateReimplementedFrom(func, marker);
       
   455 #endif            
       
   456         }
       
   457     }
       
   458 
       
   459     if (node->type() == Node::Fake) {
       
   460         const FakeNode *fake = static_cast<const FakeNode *>(node);
       
   461         if (fake->subType() == Node::File) {
       
   462             Text text;
       
   463             Quoter quoter;
       
   464             Doc::quoteFromFile(fake->doc().location(), quoter, fake->name());
       
   465             QString code = quoter.quoteTo(fake->location(), "", "");
       
   466             text << Atom(Atom::Code, code);
       
   467             generateText(text, fake, marker);
       
   468         }
       
   469     }
       
   470 }
       
   471 
       
   472 void Generator::generateAlsoList(const Node *node, CodeMarker *marker)
       
   473 {
       
   474     QList<Text> alsoList = node->doc().alsoList();
       
   475     supplementAlsoList(node, alsoList);
       
   476 
       
   477     if (!alsoList.isEmpty()) {
       
   478         Text text;
       
   479         text << Atom::ParaLeft << "See also ";
       
   480 
       
   481         for (int i = 0; i < alsoList.size(); ++i)
       
   482             text << alsoList.at(i) << separator(i, alsoList.size());
       
   483 
       
   484         text << Atom::ParaRight;
       
   485         generateText(text, node, marker);
       
   486     }
       
   487 }
       
   488 
       
   489 void Generator::generateInherits(const ClassNode *classe, CodeMarker *marker)
       
   490 {
       
   491     QList<RelatedClass>::ConstIterator r;
       
   492     int index;
       
   493 
       
   494     if (!classe->baseClasses().isEmpty()) {
       
   495         Text text;
       
   496         text << Atom::ParaLeft << "Inherits ";
       
   497 
       
   498         r = classe->baseClasses().begin();
       
   499         index = 0;
       
   500         while (r != classe->baseClasses().end()) {
       
   501             text << Atom(Atom::LinkNode, CodeMarker::stringForNode((*r).node))
       
   502                  << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK)
       
   503                  << Atom(Atom::String, (*r).dataTypeWithTemplateArgs)
       
   504                  << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK);
       
   505 
       
   506             if ((*r).access == Node::Protected) {
       
   507                 text << " (protected)";
       
   508             }
       
   509             else if ((*r).access == Node::Private) {
       
   510                 text << " (private)";
       
   511             }
       
   512             text << separator(index++, classe->baseClasses().count());
       
   513             ++r;
       
   514         }
       
   515         text << Atom::ParaRight;
       
   516         generateText(text, classe, marker);
       
   517     }
       
   518 }
       
   519 
       
   520 #ifdef QDOC_QML
       
   521 /*!
       
   522  */
       
   523 void Generator::generateQmlInherits(const QmlClassNode* , CodeMarker* )
       
   524 {
       
   525     // stub.
       
   526 }
       
   527 #endif
       
   528 
       
   529 void Generator::generateInheritedBy(const ClassNode *classe,
       
   530                                     CodeMarker *marker)
       
   531 {
       
   532     if (!classe->derivedClasses().isEmpty()) {
       
   533         Text text;
       
   534         text << Atom::ParaLeft << "Inherited by ";
       
   535 
       
   536         appendSortedNames(text, classe, classe->derivedClasses(), marker);
       
   537         text << Atom::ParaRight;
       
   538         generateText(text, classe, marker);
       
   539     }
       
   540 }
       
   541 
       
   542 /*!
       
   543   This function is called when the documentation for an
       
   544   example is being formatted. It outputs the list of source
       
   545   files comprising the example, and the list of images used
       
   546   by the example. The images are copied into a subtree of
       
   547   \c{...doc/html/images/used-in-examples/...} 
       
   548  */
       
   549 void Generator::generateFileList(const FakeNode* fake,
       
   550                                  CodeMarker* marker,
       
   551                                  Node::SubType subtype,
       
   552                                  const QString& tag)
       
   553 {
       
   554     int count = 0;
       
   555     Text text;
       
   556     OpenedList openedList(OpenedList::Bullet);
       
   557 
       
   558     text << Atom::ParaLeft << tag << Atom::ParaRight
       
   559          << Atom(Atom::ListLeft, openedList.styleString());
       
   560 
       
   561     foreach (const Node* child, fake->childNodes()) {
       
   562         if (child->subType() == subtype) {
       
   563             ++count;
       
   564             QString file = child->name();
       
   565             if (subtype == Node::Image) {
       
   566                 if (!file.isEmpty()) {
       
   567                     QDir dirInfo;
       
   568                     QString userFriendlyFilePath;
       
   569                     QString srcPath = Config::findFile(fake->location(),
       
   570                                                        QStringList(),
       
   571                                                        exampleDirs,
       
   572                                                        file,
       
   573                                                        exampleImgExts,
       
   574                                                        userFriendlyFilePath);
       
   575                     userFriendlyFilePath.truncate(userFriendlyFilePath.lastIndexOf('/'));
       
   576 
       
   577                     QString imgOutDir = outDir + "/images/used-in-examples/" + userFriendlyFilePath;
       
   578                     if (!dirInfo.mkpath(imgOutDir))
       
   579                         fake->location().fatal(tr("Cannot create output directory '%1'")
       
   580                                                .arg(imgOutDir));
       
   581 
       
   582                     QString imgOutName = Config::copyFile(fake->location(),
       
   583                                                           srcPath,
       
   584                                                           file,
       
   585                                                           imgOutDir);
       
   586                 }
       
   587 
       
   588             }
       
   589 
       
   590             openedList.next();
       
   591             text << Atom(Atom::ListItemNumber, openedList.numberString())
       
   592                  << Atom(Atom::ListItemLeft, openedList.styleString())
       
   593                  << Atom::ParaLeft
       
   594                  << Atom(Atom::Link, file)
       
   595                  << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK)
       
   596                  << file
       
   597                  << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK)
       
   598                  << Atom::ParaRight
       
   599                  << Atom(Atom::ListItemRight, openedList.styleString());
       
   600         }
       
   601     }
       
   602     text << Atom(Atom::ListRight, openedList.styleString());
       
   603     if (count > 0)
       
   604         generateText(text, fake, marker);
       
   605 }
       
   606 
       
   607 void Generator::generateExampleFiles(const FakeNode *fake, CodeMarker *marker)
       
   608 {
       
   609     if (fake->childNodes().isEmpty())
       
   610         return;
       
   611     generateFileList(fake, marker, Node::File, QString("Files:"));
       
   612     generateFileList(fake, marker, Node::Image, QString("Images:"));
       
   613 }
       
   614 
       
   615 #if 0
       
   616     QList<Generator *>::ConstIterator g = generators.begin();
       
   617     while (g != generators.end()) {
       
   618         if (outputFormats.contains((*g)->format())) {
       
   619             (*g)->initializeGenerator(config);
       
   620             QStringList extraImages =
       
   621                 config.getStringList(CONFIG_EXTRAIMAGES+Config::dot+(*g)->format());
       
   622             QStringList::ConstIterator e = extraImages.begin();
       
   623             while (e != extraImages.end()) {
       
   624                 QString userFriendlyFilePath;
       
   625                 QString filePath = Config::findFile(config.lastLocation(),
       
   626                                                     imageFiles,
       
   627                                                     imageDirs,
       
   628                                                     *e,
       
   629                                                     imgFileExts[(*g)->format()],
       
   630                                                     userFriendlyFilePath);
       
   631                 if (!filePath.isEmpty())
       
   632                     Config::copyFile(config.lastLocation(),
       
   633                                      filePath,
       
   634                                      userFriendlyFilePath,
       
   635                                      (*g)->outputDir() +
       
   636                                      "/images");
       
   637                 ++e;
       
   638             }
       
   639         }
       
   640         ++g;
       
   641     }
       
   642 #endif
       
   643 
       
   644 void Generator::generateModuleWarning(const ClassNode *classe,
       
   645                                       CodeMarker *marker)
       
   646 {
       
   647     QString module = classe->moduleName();
       
   648     if (!module.isEmpty()) {
       
   649         Text text;
       
   650         if (!editionModuleMap["DesktopLight"].contains(module)) {
       
   651             text << Atom::ParaLeft
       
   652                  << Atom(Atom::FormattingLeft, ATOM_FORMATTING_BOLD)
       
   653                  << "This class is not part of the Qt GUI Framework Edition."
       
   654                  << Atom(Atom::FormattingRight, ATOM_FORMATTING_BOLD)
       
   655                  << Atom::ParaRight;
       
   656         }
       
   657         else if (module == "Qt3Support") {
       
   658             text << Atom::ParaLeft
       
   659                  << Atom(Atom::FormattingLeft, ATOM_FORMATTING_BOLD)
       
   660                  << "Note to Qt GUI Framework Edition users:"
       
   661                  << Atom(Atom::FormattingRight, ATOM_FORMATTING_BOLD)
       
   662                  << " This class is only available in the "
       
   663                  << Atom(Atom::AutoLink, "Qt Full Framework Edition")
       
   664                  << "." << Atom::ParaRight;
       
   665         }
       
   666 
       
   667         generateText(text, classe, marker);
       
   668     }
       
   669 }
       
   670 
       
   671 QString Generator::indent(int level, const QString& markedCode)
       
   672 {
       
   673     if (level == 0)
       
   674         return markedCode;
       
   675 
       
   676     QString t;
       
   677     int column = 0;
       
   678 
       
   679     int i = 0;
       
   680     while (i < (int) markedCode.length()) {
       
   681         if (markedCode.at(i) == QLatin1Char('<')) {
       
   682             while (i < (int) markedCode.length()) {
       
   683                 t += markedCode.at(i++);
       
   684                 if (markedCode.at(i - 1) == QLatin1Char('>'))
       
   685                     break;
       
   686             }
       
   687         }
       
   688         else {
       
   689             if (markedCode.at(i) == QLatin1Char('\n')) {
       
   690                 column = 0;
       
   691             }
       
   692             else {
       
   693                 if (column == 0) {
       
   694                     for (int j = 0; j < level; j++)
       
   695                         t += QLatin1Char(' ');
       
   696                 }
       
   697                 column++;
       
   698             }
       
   699             t += markedCode.at(i++);
       
   700         }
       
   701     }
       
   702     return t;
       
   703 }
       
   704 
       
   705 QString Generator::plainCode(const QString& markedCode)
       
   706 {
       
   707     QString t = markedCode;
       
   708     t.replace(tag, QString());
       
   709     t.replace(quot, QLatin1String("\""));
       
   710     t.replace(gt, QLatin1String(">"));
       
   711     t.replace(lt, QLatin1String("<"));
       
   712     t.replace(amp, QLatin1String("&"));
       
   713     return t;
       
   714 }
       
   715 
       
   716 QString Generator::typeString(const Node *node)
       
   717 {
       
   718     switch (node->type()) {
       
   719     case Node::Namespace:
       
   720         return "namespace";
       
   721     case Node::Class:
       
   722         return "class";
       
   723     case Node::Fake:
       
   724     default:
       
   725         return "documentation";
       
   726     case Node::Enum:
       
   727         return "enum";
       
   728     case Node::Typedef:
       
   729         return "typedef";
       
   730     case Node::Function:
       
   731         return "function";
       
   732     case Node::Property:
       
   733         return "property";
       
   734     }
       
   735 }
       
   736 
       
   737 QString Generator::imageFileName(const Node *relative, const QString& fileBase)
       
   738 {
       
   739     QString userFriendlyFilePath;
       
   740     QString filePath = Config::findFile(
       
   741         relative->doc().location(), imageFiles, imageDirs, fileBase,
       
   742         imgFileExts[format()], userFriendlyFilePath);
       
   743 
       
   744     if (filePath.isEmpty())
       
   745         return QString();
       
   746 
       
   747     return QLatin1String("images/")
       
   748            + Config::copyFile(relative->doc().location(),
       
   749                               filePath, userFriendlyFilePath,
       
   750                               outputDir() + QLatin1String("/images"));
       
   751 }
       
   752 
       
   753 void Generator::setImageFileExtensions(const QStringList& extensions)
       
   754 {
       
   755     imgFileExts[format()] = extensions;
       
   756 }
       
   757 
       
   758 void Generator::unknownAtom(const Atom *atom)
       
   759 {
       
   760     Location::internalError(tr("unknown atom type '%1' in %2 generator")
       
   761                             .arg(atom->typeString()).arg(format()));
       
   762 }
       
   763 
       
   764 bool Generator::matchAhead(const Atom *atom, Atom::Type expectedAtomType)
       
   765 {
       
   766     return atom->next() != 0 && atom->next()->type() == expectedAtomType;
       
   767 }
       
   768 
       
   769 void Generator::supplementAlsoList(const Node *node, QList<Text> &alsoList)
       
   770 {
       
   771     if (node->type() == Node::Function) {
       
   772         const FunctionNode *func = static_cast<const FunctionNode *>(node);
       
   773         if (func->overloadNumber() == 1) {
       
   774             QString alternateName;
       
   775             const FunctionNode *alternateFunc = 0;
       
   776 
       
   777             if (func->name().startsWith("set") && func->name().size() >= 4) {
       
   778                 alternateName = func->name()[3].toLower();
       
   779                 alternateName += func->name().mid(4);
       
   780                 alternateFunc = func->parent()->findFunctionNode(alternateName);
       
   781 
       
   782                 if (!alternateFunc) {
       
   783                     alternateName = "is" + func->name().mid(3);
       
   784                     alternateFunc = func->parent()->findFunctionNode(alternateName);
       
   785                     if (!alternateFunc) {
       
   786                         alternateName = "has" + func->name().mid(3);
       
   787                         alternateFunc = func->parent()->findFunctionNode(alternateName);
       
   788                     }
       
   789                 }
       
   790             }
       
   791             else if (!func->name().isEmpty()) {
       
   792                 alternateName = "set";
       
   793                 alternateName += func->name()[0].toUpper();
       
   794                 alternateName += func->name().mid(1);
       
   795                 alternateFunc = func->parent()->findFunctionNode(alternateName);
       
   796             }
       
   797 
       
   798             if (alternateFunc && alternateFunc->access() != Node::Private) {
       
   799                 int i;
       
   800                 for (i = 0; i < alsoList.size(); ++i) {
       
   801                     if (alsoList.at(i).toString().contains(alternateName))
       
   802                         break;
       
   803                 }
       
   804 
       
   805                 if (i == alsoList.size()) {
       
   806                     alternateName += "()";
       
   807 
       
   808                     Text also;
       
   809                     also << Atom(Atom::Link, alternateName)
       
   810                          << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK)
       
   811                          << alternateName
       
   812                          << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK);
       
   813                     alsoList.prepend(also);
       
   814                 }
       
   815             }
       
   816         }
       
   817     }
       
   818 }
       
   819 
       
   820 QMap<QString, QString>& Generator::formattingLeftMap()
       
   821 {
       
   822     return fmtLeftMaps[format()];
       
   823 }
       
   824 
       
   825 QMap<QString, QString>& Generator::formattingRightMap()
       
   826 {
       
   827     return fmtRightMaps[format()];
       
   828 }
       
   829 
       
   830 QString Generator::trimmedTrailing(const QString &string)
       
   831 {
       
   832     QString trimmed = string;
       
   833     while (trimmed.length() > 0 && trimmed[trimmed.length() - 1].isSpace())
       
   834         trimmed.truncate(trimmed.length() - 1);
       
   835     return trimmed;
       
   836 }
       
   837 
       
   838 void Generator::generateStatus(const Node *node, CodeMarker *marker)
       
   839 {
       
   840     Text text;
       
   841 
       
   842     switch (node->status()) {
       
   843     case Node::Commendable:
       
   844     case Node::Main:
       
   845         break;
       
   846     case Node::Preliminary:
       
   847         text << Atom::ParaLeft
       
   848              << Atom(Atom::FormattingLeft, ATOM_FORMATTING_BOLD)
       
   849              << "This "
       
   850              << typeString(node)
       
   851              << " is under development and is subject to change."
       
   852              << Atom(Atom::FormattingRight, ATOM_FORMATTING_BOLD)
       
   853              << Atom::ParaRight;
       
   854         break;
       
   855     case Node::Deprecated:
       
   856         text << Atom::ParaLeft;
       
   857         if (node->isInnerNode())
       
   858             text << Atom(Atom::FormattingLeft, ATOM_FORMATTING_BOLD);
       
   859         text << "This " << typeString(node) << " is deprecated.";
       
   860         if (node->isInnerNode())
       
   861             text << Atom(Atom::FormattingRight, ATOM_FORMATTING_BOLD);
       
   862         text << Atom::ParaRight;
       
   863         break;
       
   864     case Node::Obsolete:
       
   865         text << Atom::ParaLeft;
       
   866         if (node->isInnerNode())
       
   867             text << Atom(Atom::FormattingLeft, ATOM_FORMATTING_BOLD);
       
   868         text << "This " << typeString(node) << " is obsolete.";
       
   869         if (node->isInnerNode())
       
   870             text << Atom(Atom::FormattingRight, ATOM_FORMATTING_BOLD);
       
   871         text << " It is provided to keep old source code working. "
       
   872              << "We strongly advise against "
       
   873              << "using it in new code." << Atom::ParaRight;
       
   874         break;
       
   875     case Node::Compat:
       
   876         // reimplemented in HtmlGenerator subclass
       
   877         if (node->isInnerNode()) {
       
   878             text << Atom::ParaLeft
       
   879                  << Atom(Atom::FormattingLeft, ATOM_FORMATTING_BOLD)
       
   880                  << "This "
       
   881                  << typeString(node)
       
   882                  << " is part of the Qt 3 compatibility layer."
       
   883                  << Atom(Atom::FormattingRight, ATOM_FORMATTING_BOLD)
       
   884                  << " It is provided to keep old source code working. "
       
   885                  << "We strongly advise against "
       
   886                  << "using it in new code. See "
       
   887                  << Atom(Atom::AutoLink, "Porting to Qt 4")
       
   888                  << " for more information."
       
   889                  << Atom::ParaRight;
       
   890         }
       
   891         break;
       
   892     case Node::Internal:
       
   893     default:
       
   894         break;
       
   895     }
       
   896     generateText(text, node, marker);
       
   897 }
       
   898 
       
   899 void Generator::generateThreadSafeness(const Node *node, CodeMarker *marker)
       
   900 {
       
   901     Text text;
       
   902     Text theStockLink;
       
   903     Node::ThreadSafeness threadSafeness = node->threadSafeness();
       
   904 
       
   905     Text rlink;
       
   906     rlink << Atom(Atom::Link,"reentrant")
       
   907           << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK)
       
   908           << "reentrant"
       
   909           << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK);
       
   910 
       
   911     Text tlink;
       
   912     tlink << Atom(Atom::Link,"thread-safe")
       
   913           << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK)
       
   914           << "thread-safe"
       
   915           << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK);
       
   916 
       
   917     switch (threadSafeness) {
       
   918     case Node::UnspecifiedSafeness:
       
   919         break;
       
   920     case Node::NonReentrant:
       
   921         text << Atom::ParaLeft
       
   922              << Atom(Atom::FormattingLeft,ATOM_FORMATTING_BOLD)
       
   923              << "Warning:"
       
   924              << Atom(Atom::FormattingRight,ATOM_FORMATTING_BOLD)
       
   925              << " This "
       
   926              << typeString(node)
       
   927              << " is not "
       
   928              << rlink
       
   929              << "."
       
   930              << Atom::ParaRight;
       
   931         break;
       
   932     case Node::Reentrant:
       
   933     case Node::ThreadSafe:
       
   934         text << Atom::ParaLeft
       
   935              << Atom(Atom::FormattingLeft,ATOM_FORMATTING_BOLD)
       
   936              << "Note:"
       
   937              << Atom(Atom::FormattingRight,ATOM_FORMATTING_BOLD)
       
   938              << " ";
       
   939 
       
   940         if (node->isInnerNode()) {
       
   941             const InnerNode* innerNode = static_cast<const InnerNode*>(node);
       
   942             text << "All functions in this "
       
   943                  << typeString(node)
       
   944                  << " are ";
       
   945             if (threadSafeness == Node::ThreadSafe)
       
   946                 text << tlink;
       
   947             else
       
   948                 text << rlink;
       
   949 
       
   950             bool exceptions = false;
       
   951             NodeList reentrant;
       
   952             NodeList threadsafe;
       
   953             NodeList nonreentrant;
       
   954             NodeList::ConstIterator c = innerNode->childNodes().begin();
       
   955             while (c != innerNode->childNodes().end()) {
       
   956                 switch ((*c)->threadSafeness()) {
       
   957                 case Node::Reentrant:
       
   958                     reentrant.append(*c);
       
   959                     if (threadSafeness == Node::ThreadSafe)
       
   960                         exceptions = true;
       
   961                     break;
       
   962                 case Node::ThreadSafe:
       
   963                     threadsafe.append(*c);
       
   964                     if (threadSafeness == Node::Reentrant)
       
   965                         exceptions = true;
       
   966                     break;
       
   967                 case Node::NonReentrant:
       
   968                     nonreentrant.append(*c);
       
   969                     exceptions = true;
       
   970                     break;
       
   971                 default:
       
   972                     break;
       
   973                 }
       
   974                 ++c;
       
   975             }
       
   976             if (!exceptions) 
       
   977                 text << ".";
       
   978             else if (threadSafeness == Node::Reentrant) {
       
   979                 if (nonreentrant.isEmpty()) {
       
   980                     if (!threadsafe.isEmpty()) {
       
   981                         text << ", but ";
       
   982                         appendFullNames(text,threadsafe,innerNode,marker);
       
   983                         singularPlural(text,threadsafe);
       
   984                         text << " also " << tlink << ".";
       
   985                     }
       
   986                     else
       
   987                         text << ".";
       
   988                 }
       
   989                 else {
       
   990                     text << ", except for ";
       
   991                     appendFullNames(text,nonreentrant,innerNode,marker);
       
   992                     text << ", which";
       
   993                     singularPlural(text,nonreentrant);
       
   994                     text << " nonreentrant.";
       
   995                     if (!threadsafe.isEmpty()) {
       
   996                         text << " ";
       
   997                         appendFullNames(text,threadsafe,innerNode,marker);
       
   998                         singularPlural(text,threadsafe);
       
   999                         text << " " << tlink << ".";
       
  1000                     }
       
  1001                 }
       
  1002             }
       
  1003             else { // thread-safe
       
  1004                 if (!nonreentrant.isEmpty() || !reentrant.isEmpty()) {
       
  1005                     text << ", except for ";
       
  1006                     if (!reentrant.isEmpty()) {
       
  1007                         appendFullNames(text,reentrant,innerNode,marker);
       
  1008                         text << ", which";
       
  1009                         singularPlural(text,reentrant);
       
  1010                         text << " only " << rlink;
       
  1011                         if (!nonreentrant.isEmpty())
       
  1012                             text << ", and ";
       
  1013                     }
       
  1014                     if (!nonreentrant.isEmpty()) {
       
  1015                         appendFullNames(text,nonreentrant,innerNode,marker);
       
  1016                         text << ", which";
       
  1017                         singularPlural(text,nonreentrant);
       
  1018                         text << " nonreentrant.";
       
  1019                     }
       
  1020                     text << ".";
       
  1021                 }
       
  1022             }
       
  1023         }
       
  1024         else {
       
  1025             text << "This " << typeString(node) << " is ";
       
  1026             if (threadSafeness == Node::ThreadSafe)
       
  1027                 text << tlink;
       
  1028             else
       
  1029                 text << rlink;
       
  1030             text << ".";
       
  1031         }
       
  1032         text << Atom::ParaRight;
       
  1033     }
       
  1034     generateText(text,node,marker);
       
  1035 }
       
  1036 
       
  1037 void Generator::generateSince(const Node *node, CodeMarker *marker)
       
  1038 {
       
  1039     if (!node->since().isEmpty()) {
       
  1040         Text text;
       
  1041         text << Atom::ParaLeft
       
  1042              << "This "
       
  1043              << typeString(node)
       
  1044              << " was introduced in ";
       
  1045         if (project.isEmpty())
       
  1046              text << "version";
       
  1047         else
       
  1048              text << project;
       
  1049         text << " " << node->since() << "." << Atom::ParaRight;
       
  1050         generateText(text, node, marker);
       
  1051     }
       
  1052 }
       
  1053 
       
  1054 /*!
       
  1055   No longer in use.
       
  1056  */
       
  1057 void Generator::generateOverload(const Node *node, CodeMarker *marker)
       
  1058 {
       
  1059     Text text;
       
  1060     text << Atom::ParaLeft
       
  1061          << "This function overloads ";
       
  1062     QString t = node->name() + "()";
       
  1063     text << Atom::AutoLink << t 
       
  1064          << Atom::ParaRight;
       
  1065     generateText(text, node, marker);
       
  1066 }
       
  1067 
       
  1068 void Generator::generateReimplementedFrom(const FunctionNode *func,
       
  1069                                           CodeMarker *marker)
       
  1070 {
       
  1071     if (func->reimplementedFrom() != 0) {
       
  1072         const FunctionNode *from = func->reimplementedFrom();
       
  1073         if (from->access() != Node::Private &&
       
  1074             from->parent()->access() != Node::Private) {
       
  1075             Text text;
       
  1076             text << Atom::ParaLeft << "Reimplemented from ";
       
  1077             QString fullName =  from->parent()->name() + "::" + from->name() + "()";
       
  1078             appendFullName(text, from->parent(), fullName, from);
       
  1079             text << "." << Atom::ParaRight;
       
  1080             generateText(text, func, marker);
       
  1081         }
       
  1082     }
       
  1083 }
       
  1084 
       
  1085 const Atom *Generator::generateAtomList(const Atom *atom,
       
  1086                                         const Node *relative,
       
  1087                                         CodeMarker *marker,
       
  1088                                         bool generate,
       
  1089                                         int &numAtoms)
       
  1090 {
       
  1091     while (atom) {
       
  1092         if (atom->type() == Atom::FormatIf) {
       
  1093             int numAtoms0 = numAtoms;
       
  1094             bool rightFormat = canHandleFormat(atom->string());
       
  1095             atom = generateAtomList(atom->next(),
       
  1096                                     relative,
       
  1097                                     marker,
       
  1098                                     generate && rightFormat,
       
  1099                                     numAtoms);
       
  1100             if (!atom)
       
  1101                 return 0;
       
  1102 
       
  1103             if (atom->type() == Atom::FormatElse) {
       
  1104                 ++numAtoms;
       
  1105                 atom = generateAtomList(atom->next(),
       
  1106                                         relative,
       
  1107                                         marker,
       
  1108                                         generate && !rightFormat,
       
  1109                                         numAtoms);
       
  1110                 if (!atom)
       
  1111                     return 0;
       
  1112             }
       
  1113 
       
  1114             if (atom->type() == Atom::FormatEndif) {
       
  1115                 if (generate && numAtoms0 == numAtoms) {
       
  1116                     relative->location().warning(tr("Output format %1 not handled")
       
  1117                                                  .arg(format()));
       
  1118                     Atom unhandledFormatAtom(Atom::UnhandledFormat, format());
       
  1119                     generateAtomList(&unhandledFormatAtom,
       
  1120                                      relative,
       
  1121                                      marker,
       
  1122                                      generate,
       
  1123                                      numAtoms);
       
  1124                 }
       
  1125                 atom = atom->next();
       
  1126             }
       
  1127         }
       
  1128         else if (atom->type() == Atom::FormatElse ||
       
  1129                  atom->type() == Atom::FormatEndif) {
       
  1130             return atom;
       
  1131         }
       
  1132         else {
       
  1133             int n = 1;
       
  1134             if (generate) {
       
  1135                 n += generateAtom(atom, relative, marker);
       
  1136                 numAtoms += n;
       
  1137             }
       
  1138             while (n-- > 0)
       
  1139                 atom = atom->next();
       
  1140         }
       
  1141     }
       
  1142     return 0;
       
  1143 }
       
  1144 
       
  1145 void Generator::appendFullName(Text& text,
       
  1146                                const Node *apparentNode,
       
  1147                                const Node *relative,
       
  1148                                CodeMarker *marker,
       
  1149                                const Node *actualNode)
       
  1150 {
       
  1151     if (actualNode == 0)
       
  1152         actualNode = apparentNode;
       
  1153     text << Atom(Atom::LinkNode, CodeMarker::stringForNode(actualNode))
       
  1154          << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK)
       
  1155          << Atom(Atom::String, marker->plainFullName(apparentNode, relative))
       
  1156          << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK);
       
  1157 }
       
  1158 
       
  1159 void Generator::appendFullName(Text& text,
       
  1160                                const Node *apparentNode,
       
  1161                                const QString& fullName,
       
  1162                                const Node *actualNode)
       
  1163 {
       
  1164     if (actualNode == 0)
       
  1165         actualNode = apparentNode;
       
  1166     text << Atom(Atom::LinkNode, CodeMarker::stringForNode(actualNode))
       
  1167          << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK)
       
  1168          << Atom(Atom::String, fullName)
       
  1169          << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK);
       
  1170 }
       
  1171 
       
  1172 void Generator::appendFullNames(Text& text,
       
  1173                                 const NodeList& nodes,
       
  1174                                 const Node* relative,
       
  1175                                 CodeMarker* marker)
       
  1176 {
       
  1177     NodeList::ConstIterator n = nodes.begin();
       
  1178     int index = 0;
       
  1179     while (n != nodes.end()) {
       
  1180         appendFullName(text,*n,relative,marker);
       
  1181         text << comma(index++,nodes.count());
       
  1182         ++n;
       
  1183     }
       
  1184 }
       
  1185 
       
  1186 void Generator::appendSortedNames(Text& text,
       
  1187                                   const ClassNode *classe,
       
  1188                                   const QList<RelatedClass> &classes,
       
  1189                                   CodeMarker *marker)
       
  1190 {
       
  1191     QList<RelatedClass>::ConstIterator r;
       
  1192     QMap<QString,Text> classMap;
       
  1193     int index = 0;
       
  1194 
       
  1195     r = classes.begin();
       
  1196     while (r != classes.end()) {
       
  1197         if ((*r).node->access() == Node::Public &&
       
  1198             (*r).node->status() != Node::Internal
       
  1199             && !(*r).node->doc().isEmpty()) {
       
  1200             Text className;
       
  1201             appendFullName(className, (*r).node, classe, marker);
       
  1202             classMap[className.toString().toLower()] = className;
       
  1203         }
       
  1204         ++r;
       
  1205     }
       
  1206 
       
  1207     QStringList classNames = classMap.keys();
       
  1208     classNames.sort();
       
  1209 
       
  1210     foreach (const QString &className, classNames) {
       
  1211         text << classMap[className];
       
  1212         text << separator(index++, classNames.count());
       
  1213     }
       
  1214 }
       
  1215 
       
  1216 int Generator::skipAtoms(const Atom *atom, Atom::Type type) const
       
  1217 {
       
  1218     int skipAhead = 0;
       
  1219     atom = atom->next();
       
  1220     while (atom != 0 && atom->type() != type) {
       
  1221         skipAhead++;
       
  1222         atom = atom->next();
       
  1223     }
       
  1224     return skipAhead;
       
  1225 }
       
  1226 
       
  1227 QString Generator::fullName(const Node *node,
       
  1228                             const Node *relative,
       
  1229                             CodeMarker *marker) const
       
  1230 {
       
  1231     if (node->type() == Node::Fake)
       
  1232         return static_cast<const FakeNode *>(node)->title();
       
  1233     else if (node->type() == Node::Class &&
       
  1234         !(static_cast<const ClassNode *>(node))->serviceName().isEmpty())
       
  1235         return (static_cast<const ClassNode *>(node))->serviceName();
       
  1236     else
       
  1237         return marker->plainFullName(node, relative);
       
  1238 }
       
  1239 
       
  1240 QT_END_NAMESPACE