util/tools/qdoc3/htmlgenerator.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   htmlgenerator.cpp
       
    44 */
       
    45 
       
    46 #include "codemarker.h"
       
    47 #include "helpprojectwriter.h"
       
    48 #include "htmlgenerator.h"
       
    49 #include "node.h"
       
    50 #include "separator.h"
       
    51 #include "tree.h"
       
    52 #include <ctype.h>
       
    53 
       
    54 #include <qdebug.h>
       
    55 #include <qlist.h>
       
    56 #include <qiterator.h>
       
    57 
       
    58 QT_BEGIN_NAMESPACE
       
    59 
       
    60 #define COMMAND_VERSION                 Doc::alias("version")
       
    61 
       
    62 QString HtmlGenerator::sinceTitles[] =
       
    63     {
       
    64         "    New Namespaces",
       
    65         "    New Classes",
       
    66         "    New Member Functions",
       
    67         "    New Functions in Namespaces",
       
    68         "    New Global Functions",
       
    69         "    New Macros",
       
    70         "    New Enum Types",
       
    71         "    New Typedefs",
       
    72         "    New Properties",
       
    73         "    New Variables",
       
    74         "    New Qml Properties",
       
    75         "    New Qml Signals",
       
    76         "    New Qml Methods",
       
    77         ""
       
    78     };
       
    79 
       
    80 static bool showBrokenLinks = false;
       
    81 
       
    82 static QRegExp linkTag("(<@link node=\"([^\"]+)\">).*(</@link>)");
       
    83 static QRegExp funcTag("(<@func target=\"([^\"]*)\">)(.*)(</@func>)");
       
    84 static QRegExp typeTag("(<@(type|headerfile|func)(?: +[^>]*)?>)(.*)(</@\\2>)");
       
    85 static QRegExp spanTag("</@(?:comment|preprocessor|string|char)>");
       
    86 static QRegExp unknownTag("</?@[^>]*>");
       
    87 
       
    88 bool parseArg(const QString &src,
       
    89               const QString &tag,
       
    90               int *pos,
       
    91               int n,
       
    92               QStringRef *contents,
       
    93               QStringRef *par1 = 0,
       
    94               bool debug = false)
       
    95 {
       
    96 #define SKIP_CHAR(c) \
       
    97     if (debug) \
       
    98         qDebug() << "looking for " << c << " at " << QString(src.data() + i, n - i); \
       
    99     if (i >= n || src[i] != c) { \
       
   100         if (debug) \
       
   101             qDebug() << " char '" << c << "' not found"; \
       
   102         return false; \
       
   103     } \
       
   104     ++i;
       
   105 
       
   106 
       
   107 #define SKIP_SPACE \
       
   108     while (i < n && src[i] == ' ') \
       
   109         ++i;
       
   110 
       
   111     int i = *pos;
       
   112     int j = i;
       
   113 
       
   114     // assume "<@" has been parsed outside
       
   115     //SKIP_CHAR('<');
       
   116     //SKIP_CHAR('@');
       
   117 
       
   118     if (tag != QStringRef(&src, i, tag.length())) {
       
   119         if (0 && debug)
       
   120             qDebug() << "tag " << tag << " not found at " << i;
       
   121         return false;
       
   122     }
       
   123 
       
   124     if (debug)
       
   125         qDebug() << "haystack:" << src << "needle:" << tag << "i:" <<i;
       
   126 
       
   127     // skip tag
       
   128     i += tag.length();
       
   129 
       
   130     // parse stuff like:  linkTag("(<@link node=\"([^\"]+)\">).*(</@link>)");
       
   131     if (par1) {
       
   132         SKIP_SPACE;
       
   133         // read parameter name
       
   134         j = i;
       
   135         while (i < n && src[i].isLetter())
       
   136             ++i;
       
   137         if (src[i] == '=') {
       
   138             if (debug)
       
   139                 qDebug() << "read parameter" << QString(src.data() + j, i - j);
       
   140             SKIP_CHAR('=');
       
   141             SKIP_CHAR('"');
       
   142             // skip parameter name
       
   143             j = i;
       
   144             while (i < n && src[i] != '"')
       
   145                 ++i;
       
   146             *par1 = QStringRef(&src, j, i - j);
       
   147             SKIP_CHAR('"');
       
   148             SKIP_SPACE;
       
   149         } else {
       
   150             if (debug)
       
   151                 qDebug() << "no optional parameter found";
       
   152         }
       
   153     }
       
   154     SKIP_SPACE;
       
   155     SKIP_CHAR('>');
       
   156 
       
   157     // find contents up to closing "</@tag>
       
   158     j = i;
       
   159     for (; true; ++i) {
       
   160         if (i + 4 + tag.length() > n)
       
   161             return false;
       
   162         if (src[i] != '<')
       
   163             continue;
       
   164         if (src[i + 1] != '/')
       
   165             continue;
       
   166         if (src[i + 2] != '@')
       
   167             continue;
       
   168         if (tag != QStringRef(&src, i + 3, tag.length()))
       
   169             continue;
       
   170         if (src[i + 3 + tag.length()] != '>')
       
   171             continue;
       
   172         break;
       
   173     }
       
   174 
       
   175     *contents = QStringRef(&src, j, i - j);
       
   176 
       
   177     i += tag.length() + 4;
       
   178 
       
   179     *pos = i;
       
   180     if (debug)
       
   181         qDebug() << " tag " << tag << " found: pos now: " << i;
       
   182     return true;
       
   183 #undef SKIP_CHAR
       
   184 }
       
   185 
       
   186 static void addLink(const QString &linkTarget,
       
   187                     const QStringRef &nestedStuff,
       
   188                     QString *res)
       
   189 {
       
   190     if (!linkTarget.isEmpty()) {
       
   191         *res += "<a href=\"";
       
   192         *res += linkTarget;
       
   193         *res += "\">";
       
   194         *res += nestedStuff;
       
   195         *res += "</a>";
       
   196     }
       
   197     else {
       
   198         *res += nestedStuff;
       
   199     }
       
   200 }
       
   201 
       
   202 
       
   203 HtmlGenerator::HtmlGenerator()
       
   204     : helpProjectWriter(0), inLink(false), inContents(false),
       
   205       inSectionHeading(false), inTableHeader(false), numTableRows(0),
       
   206       threeColumnEnumValueTable(true), funcLeftParen("\\S(\\()"),
       
   207       myTree(0), slow(false), obsoleteLinks(false)
       
   208 {
       
   209 }
       
   210 
       
   211 HtmlGenerator::~HtmlGenerator()
       
   212 {
       
   213     if (helpProjectWriter)
       
   214         delete helpProjectWriter;
       
   215 }
       
   216 
       
   217 void HtmlGenerator::initializeGenerator(const Config &config)
       
   218 {
       
   219     static const struct {
       
   220         const char *key;
       
   221         const char *left;
       
   222         const char *right;
       
   223     } defaults[] = {
       
   224         { ATOM_FORMATTING_BOLD, "<b>", "</b>" },
       
   225         { ATOM_FORMATTING_INDEX, "<!--", "-->" },
       
   226         { ATOM_FORMATTING_ITALIC, "<i>", "</i>" },
       
   227         { ATOM_FORMATTING_PARAMETER, "<i>", "</i>" },
       
   228         { ATOM_FORMATTING_SUBSCRIPT, "<sub>", "</sub>" },
       
   229         { ATOM_FORMATTING_SUPERSCRIPT, "<sup>", "</sup>" },
       
   230         { ATOM_FORMATTING_TELETYPE, "<tt>", "</tt>" },
       
   231         { ATOM_FORMATTING_UNDERLINE, "<u>", "</u>" },
       
   232         { 0, 0, 0 }
       
   233     };
       
   234 
       
   235     Generator::initializeGenerator(config);
       
   236     obsoleteLinks = config.getBool(QLatin1String(CONFIG_OBSOLETELINKS));
       
   237     setImageFileExtensions(QStringList() << "png" << "jpg" << "jpeg" << "gif");
       
   238     int i = 0;
       
   239     while (defaults[i].key) {
       
   240         formattingLeftMap().insert(defaults[i].key, defaults[i].left);
       
   241         formattingRightMap().insert(defaults[i].key, defaults[i].right);
       
   242         i++;
       
   243     }
       
   244 
       
   245     style = config.getString(HtmlGenerator::format() +
       
   246                              Config::dot +
       
   247                              HTMLGENERATOR_STYLE);
       
   248     postHeader = config.getString(HtmlGenerator::format() +
       
   249                                   Config::dot +
       
   250                                   HTMLGENERATOR_POSTHEADER);
       
   251     footer = config.getString(HtmlGenerator::format() +
       
   252                               Config::dot +
       
   253                               HTMLGENERATOR_FOOTER);
       
   254     address = config.getString(HtmlGenerator::format() +
       
   255                                Config::dot +
       
   256                                HTMLGENERATOR_ADDRESS);
       
   257     pleaseGenerateMacRef = config.getBool(HtmlGenerator::format() +
       
   258                                           Config::dot +
       
   259                                           HTMLGENERATOR_GENERATEMACREFS);
       
   260 
       
   261     project = config.getString(CONFIG_PROJECT);
       
   262 
       
   263     projectDescription = config.getString(CONFIG_DESCRIPTION);
       
   264     if (projectDescription.isEmpty() && !project.isEmpty())
       
   265         projectDescription = project + " Reference Documentation";
       
   266 
       
   267     projectUrl = config.getString(CONFIG_URL);
       
   268 
       
   269     QSet<QString> editionNames = config.subVars(CONFIG_EDITION);
       
   270     QSet<QString>::ConstIterator edition = editionNames.begin();
       
   271     while (edition != editionNames.end()) {
       
   272         QString editionName = *edition;
       
   273         QStringList editionModules = config.getStringList(CONFIG_EDITION +
       
   274                                                           Config::dot +
       
   275                                                           editionName +
       
   276                                                           Config::dot +
       
   277                                                           "modules");
       
   278         QStringList editionGroups = config.getStringList(CONFIG_EDITION +
       
   279                                                          Config::dot +
       
   280                                                          editionName +
       
   281                                                          Config::dot +
       
   282                                                          "groups");
       
   283 
       
   284         if (!editionModules.isEmpty())
       
   285             editionModuleMap[editionName] = editionModules;
       
   286         if (!editionGroups.isEmpty())
       
   287             editionGroupMap[editionName] = editionGroups;
       
   288 
       
   289         ++edition;
       
   290     }
       
   291 
       
   292     slow = config.getBool(CONFIG_SLOW);
       
   293 
       
   294     stylesheets = config.getStringList(HtmlGenerator::format() +
       
   295                                        Config::dot +
       
   296                                        HTMLGENERATOR_STYLESHEETS);
       
   297     customHeadElements = config.getStringList(HtmlGenerator::format() +
       
   298                                               Config::dot +
       
   299                                               HTMLGENERATOR_CUSTOMHEADELEMENTS);
       
   300     codeIndent = config.getInt(CONFIG_CODEINDENT);
       
   301 
       
   302     helpProjectWriter = new HelpProjectWriter(config,
       
   303                                               project.toLower() +
       
   304                                               ".qhp");
       
   305 }
       
   306 
       
   307 void HtmlGenerator::terminateGenerator()
       
   308 {
       
   309     Generator::terminateGenerator();
       
   310 }
       
   311 
       
   312 QString HtmlGenerator::format()
       
   313 {
       
   314     return "HTML";
       
   315 }
       
   316 
       
   317 /*!
       
   318   This is where the html files and dcf files are written.
       
   319   \note The html file generation is done in the base class,
       
   320   PageGenerator::generateTree().
       
   321  */
       
   322 void HtmlGenerator::generateTree(const Tree *tree, CodeMarker *marker)
       
   323 {
       
   324     // Copy the stylesheets from the directory containing the qdocconf file.
       
   325     // ### This should be changed to use a special directory in doc/src.
       
   326     QStringList::ConstIterator styleIter = stylesheets.begin();
       
   327     QDir configPath = QDir::current();
       
   328     while (styleIter != stylesheets.end()) {
       
   329         QString filePath = configPath.absoluteFilePath(*styleIter);
       
   330         Config::copyFile(Location(), filePath, filePath, outputDir());
       
   331         ++styleIter;
       
   332     }
       
   333 
       
   334     myTree = tree;
       
   335     nonCompatClasses.clear();
       
   336     mainClasses.clear();
       
   337     compatClasses.clear();
       
   338     obsoleteClasses.clear();
       
   339     moduleClassMap.clear();
       
   340     moduleNamespaceMap.clear();
       
   341     funcIndex.clear();
       
   342     legaleseTexts.clear();
       
   343     serviceClasses.clear();
       
   344     findAllClasses(tree->root());
       
   345     findAllFunctions(tree->root());
       
   346     findAllLegaleseTexts(tree->root());
       
   347     findAllNamespaces(tree->root());
       
   348 #ifdef ZZZ_QDOC_QML
       
   349     findAllQmlClasses(tree->root());
       
   350 #endif
       
   351     findAllSince(tree->root());
       
   352 
       
   353     PageGenerator::generateTree(tree, marker);
       
   354 
       
   355     dcfClassesRoot.ref = "classes.html";
       
   356     dcfClassesRoot.title = "Classes";
       
   357     qSort(dcfClassesRoot.subsections);
       
   358 
       
   359     dcfOverviewsRoot.ref = "overviews.html";
       
   360     dcfOverviewsRoot.title = "Overviews";
       
   361     qSort(dcfOverviewsRoot.subsections);
       
   362 
       
   363     dcfExamplesRoot.ref = "examples.html";
       
   364     dcfExamplesRoot.title = "Tutorial & Examples";
       
   365     qSort(dcfExamplesRoot.subsections);
       
   366 
       
   367     DcfSection qtRoot;
       
   368     appendDcfSubSection(&qtRoot, dcfClassesRoot);
       
   369     appendDcfSubSection(&qtRoot, dcfOverviewsRoot);
       
   370     appendDcfSubSection(&qtRoot, dcfExamplesRoot);
       
   371 
       
   372     generateDcf(project.toLower().simplified().replace(" ", "-"),
       
   373                 "index.html",
       
   374                 projectDescription, qtRoot);
       
   375     generateDcf("designer",
       
   376                 "designer-manual.html",
       
   377                 "Qt Designer Manual",
       
   378                 dcfDesignerRoot);
       
   379     generateDcf("linguist",
       
   380                 "linguist-manual.html",
       
   381                 "Qt Linguist Manual",
       
   382                 dcfLinguistRoot);
       
   383     generateDcf("assistant",
       
   384                 "assistant-manual.html",
       
   385                 "Qt Assistant Manual",
       
   386                 dcfAssistantRoot);
       
   387     generateDcf("qmake",
       
   388                 "qmake-manual.html",
       
   389                 "qmake Manual",
       
   390                 dcfQmakeRoot);
       
   391 
       
   392     generateIndex(project.toLower().simplified().replace(" ", "-"),
       
   393                   projectUrl,
       
   394                   projectDescription);
       
   395 
       
   396     helpProjectWriter->generate(myTree);
       
   397 }
       
   398 
       
   399 void HtmlGenerator::startText(const Node * /* relative */,
       
   400                               CodeMarker * /* marker */)
       
   401 {
       
   402     inLink = false;
       
   403     inContents = false;
       
   404     inSectionHeading = false;
       
   405     inTableHeader = false;
       
   406     numTableRows = 0;
       
   407     threeColumnEnumValueTable = true;
       
   408     link.clear();
       
   409     sectionNumber.clear();
       
   410 }
       
   411 
       
   412 int HtmlGenerator::generateAtom(const Atom *atom,
       
   413                                 const Node *relative,
       
   414                                 CodeMarker *marker)
       
   415 {
       
   416     int skipAhead = 0;
       
   417     static bool in_para = false;
       
   418 
       
   419     switch (atom->type()) {
       
   420     case Atom::AbstractLeft:
       
   421         break;
       
   422     case Atom::AbstractRight:
       
   423         break;
       
   424     case Atom::AutoLink:
       
   425         if (!inLink && !inContents && !inSectionHeading) {
       
   426             const Node *node = 0;
       
   427             QString link = getLink(atom, relative, marker, &node);
       
   428             if (!link.isEmpty()) {
       
   429                 beginLink(link, node, relative, marker);
       
   430                 generateLink(atom, relative, marker);
       
   431                 endLink();
       
   432             }
       
   433             else {
       
   434                 out() << protect(atom->string());
       
   435             }
       
   436         }
       
   437         else {
       
   438             out() << protect(atom->string());
       
   439         }
       
   440         break;
       
   441     case Atom::BaseName:
       
   442         break;
       
   443     case Atom::BriefLeft:
       
   444         if (relative->type() == Node::Fake) {
       
   445             skipAhead = skipAtoms(atom, Atom::BriefRight);
       
   446             break;
       
   447         }
       
   448 
       
   449         out() << "<p>";
       
   450         if (relative->type() == Node::Property ||
       
   451             relative->type() == Node::Variable) {
       
   452             QString str;
       
   453             atom = atom->next();
       
   454             while (atom != 0 && atom->type() != Atom::BriefRight) {
       
   455                 if (atom->type() == Atom::String ||
       
   456                     atom->type() == Atom::AutoLink)
       
   457                     str += atom->string();
       
   458                 skipAhead++;
       
   459                 atom = atom->next();
       
   460             }
       
   461             str[0] = str[0].toLower();
       
   462             if (str.right(1) == ".")
       
   463                 str.truncate(str.length() - 1);
       
   464             out() << "This ";
       
   465             if (relative->type() == Node::Property)
       
   466                 out() << "property";
       
   467             else
       
   468                 out() << "variable";
       
   469             QStringList words = str.split(" ");
       
   470             if (!(words.first() == "contains" || words.first() == "specifies"
       
   471                 || words.first() == "describes" || words.first() == "defines"
       
   472                 || words.first() == "holds" || words.first() == "determines"))
       
   473                 out() << " holds ";
       
   474             else
       
   475                 out() << " ";
       
   476             out() << str << ".";
       
   477         }
       
   478         break;
       
   479     case Atom::BriefRight:
       
   480         if (relative->type() != Node::Fake)
       
   481             out() << "</p>\n";
       
   482         break;
       
   483     case Atom::C:
       
   484         out() << formattingLeftMap()[ATOM_FORMATTING_TELETYPE];
       
   485         if (inLink) {
       
   486             out() << protect(plainCode(atom->string()));
       
   487         }
       
   488         else {
       
   489             out() << highlightedCode(atom->string(), marker, relative);
       
   490         }
       
   491         out() << formattingRightMap()[ATOM_FORMATTING_TELETYPE];
       
   492         break;
       
   493     case Atom::Code:
       
   494 	out() << "<pre>"
       
   495               << trimmedTrailing(highlightedCode(indent(codeIndent,atom->string()),
       
   496                                                  marker,relative))
       
   497               << "</pre>\n";
       
   498 	break;
       
   499 #ifdef QDOC_QML
       
   500     case Atom::Qml:
       
   501 	out() << "<pre>"
       
   502               << trimmedTrailing(highlightedCode(indent(codeIndent,atom->string()),
       
   503                                                  marker,relative))
       
   504               << "</pre>\n";
       
   505 	break;
       
   506 #endif
       
   507     case Atom::CodeNew:
       
   508         out() << "<p>you can rewrite it as</p>\n"
       
   509               << "<pre>"
       
   510               << trimmedTrailing(highlightedCode(indent(codeIndent,atom->string()),
       
   511                                                  marker,relative))
       
   512               << "</pre>\n";
       
   513         break;
       
   514     case Atom::CodeOld:
       
   515         out() << "<p>For example, if you have code like</p>\n";
       
   516         // fallthrough
       
   517     case Atom::CodeBad:
       
   518         out() << "<pre><font color=\"#404040\">"
       
   519               << trimmedTrailing(protect(plainCode(indent(codeIndent,atom->string()))))
       
   520               << "</font></pre>\n";
       
   521 	break;
       
   522     case Atom::FootnoteLeft:
       
   523         // ### For now
       
   524         if (in_para) {
       
   525             out() << "</p>\n";
       
   526             in_para = false;
       
   527         }
       
   528         out() << "<!-- ";
       
   529         break;
       
   530     case Atom::FootnoteRight:
       
   531         // ### For now
       
   532         out() << "-->";
       
   533         break;
       
   534     case Atom::FormatElse:
       
   535     case Atom::FormatEndif:
       
   536     case Atom::FormatIf:
       
   537         break;
       
   538     case Atom::FormattingLeft:
       
   539         out() << formattingLeftMap()[atom->string()];
       
   540         if (atom->string() == ATOM_FORMATTING_PARAMETER) {
       
   541             if (atom->next() != 0 && atom->next()->type() == Atom::String) {
       
   542                 QRegExp subscriptRegExp("([a-z]+)_([0-9n])");
       
   543                 if (subscriptRegExp.exactMatch(atom->next()->string())) {
       
   544                     out() << subscriptRegExp.cap(1) << "<sub>"
       
   545                           << subscriptRegExp.cap(2) << "</sub>";
       
   546                     skipAhead = 1;
       
   547                 }
       
   548             }
       
   549         }
       
   550         break;
       
   551     case Atom::FormattingRight:
       
   552         if (atom->string() == ATOM_FORMATTING_LINK) {
       
   553             endLink();
       
   554         }
       
   555         else {
       
   556             out() << formattingRightMap()[atom->string()];
       
   557         }
       
   558         break;
       
   559     case Atom::AnnotatedList:
       
   560         {
       
   561             QList<Node*> values = myTree->groups().values(atom->string());
       
   562             NodeMap nodeMap;
       
   563             for (int i = 0; i < values.size(); ++i) {
       
   564                 const Node* n = values.at(i);
       
   565                 if ((n->status() != Node::Internal) && (n->access() != Node::Private)) {
       
   566                     nodeMap.insert(n->nameForLists(),n);
       
   567                 }
       
   568             }
       
   569             generateAnnotatedList(relative, marker, nodeMap);
       
   570         }
       
   571         break;
       
   572     case Atom::GeneratedList:
       
   573         if (atom->string() == "annotatedclasses") {
       
   574             generateAnnotatedList(relative, marker, nonCompatClasses);
       
   575         }
       
   576         else if (atom->string() == "classes") {
       
   577             generateCompactList(relative, marker, nonCompatClasses);
       
   578         }
       
   579         else if (atom->string().contains("classesbymodule")) {
       
   580             QString arg = atom->string().trimmed();
       
   581             QString moduleName = atom->string().mid(atom->string().indexOf(
       
   582                 "classesbymodule") + 15).trimmed();
       
   583             if (moduleClassMap.contains(moduleName))
       
   584                 generateAnnotatedList(relative, marker, moduleClassMap[moduleName]);
       
   585         }
       
   586         else if (atom->string().contains("classesbyedition")) {
       
   587 
       
   588             QString arg = atom->string().trimmed();
       
   589             QString editionName = atom->string().mid(atom->string().indexOf(
       
   590                 "classesbyedition") + 16).trimmed();
       
   591 
       
   592             if (editionModuleMap.contains(editionName)) {
       
   593 
       
   594                 // Add all classes in the modules listed for that edition.
       
   595                 NodeMap editionClasses;
       
   596                 foreach (const QString &moduleName, editionModuleMap[editionName]) {
       
   597                     if (moduleClassMap.contains(moduleName))
       
   598                         editionClasses.unite(moduleClassMap[moduleName]);
       
   599                 }
       
   600 
       
   601                 // Add additional groups and remove groups of classes that
       
   602                 // should be excluded from the edition.
       
   603 
       
   604                 QMultiMap <QString, Node *> groups = myTree->groups();
       
   605                 foreach (const QString &groupName, editionGroupMap[editionName]) {
       
   606                     QList<Node *> groupClasses;
       
   607                     if (groupName.startsWith("-")) {
       
   608                         groupClasses = groups.values(groupName.mid(1));
       
   609                         foreach (const Node *node, groupClasses)
       
   610                             editionClasses.remove(node->name());
       
   611                     }
       
   612                     else {
       
   613                         groupClasses = groups.values(groupName);
       
   614                         foreach (const Node *node, groupClasses)
       
   615                             editionClasses.insert(node->name(), node);
       
   616                     }
       
   617                 }
       
   618                 generateAnnotatedList(relative, marker, editionClasses);
       
   619             }
       
   620         }
       
   621         else if (atom->string() == "classhierarchy") {
       
   622             generateClassHierarchy(relative, marker, nonCompatClasses);
       
   623         }
       
   624         else if (atom->string() == "compatclasses") {
       
   625             generateCompactList(relative, marker, compatClasses);
       
   626         }
       
   627         else if (atom->string() == "obsoleteclasses") {
       
   628             generateCompactList(relative, marker, obsoleteClasses);
       
   629         }
       
   630         else if (atom->string() == "functionindex") {
       
   631             generateFunctionIndex(relative, marker);
       
   632         }
       
   633         else if (atom->string() == "legalese") {
       
   634             generateLegaleseList(relative, marker);
       
   635         }
       
   636         else if (atom->string() == "mainclasses") {
       
   637             generateCompactList(relative, marker, mainClasses);
       
   638         }
       
   639         else if (atom->string() == "services") {
       
   640             generateCompactList(relative, marker, serviceClasses);
       
   641         }
       
   642         else if (atom->string() == "overviews") {
       
   643             generateOverviewList(relative, marker);
       
   644         }
       
   645         else if (atom->string() == "namespaces") {
       
   646             generateAnnotatedList(relative, marker, namespaceIndex);
       
   647         }
       
   648         else if (atom->string() == "related") {
       
   649             const FakeNode *fake = static_cast<const FakeNode *>(relative);
       
   650             if (fake && !fake->groupMembers().isEmpty()) {
       
   651                 NodeMap groupMembersMap;
       
   652                 foreach (const Node *node, fake->groupMembers()) {
       
   653                     if (node->type() == Node::Fake)
       
   654                         groupMembersMap[fullName(node, relative, marker)] = node;
       
   655                 }
       
   656                 generateAnnotatedList(fake, marker, groupMembersMap);
       
   657             }
       
   658         }
       
   659         else if (atom->string() == "relatedinline") {
       
   660             const FakeNode *fake = static_cast<const FakeNode *>(relative);
       
   661             if (fake && !fake->groupMembers().isEmpty()) {
       
   662                 // Reverse the list into the original scan order.
       
   663                 // Should be sorted.  But on what?  It may not be a
       
   664                 // regular class or page definition.
       
   665                 QList<const Node *> list;
       
   666                 foreach (const Node *node, fake->groupMembers())
       
   667                     list.prepend(node);
       
   668                 foreach (const Node *node, list)
       
   669                     generateBody(node, marker);
       
   670             }
       
   671         }
       
   672         break;
       
   673     case Atom::SinceList:
       
   674         {
       
   675             NewSinceMaps::const_iterator nsmap;
       
   676             nsmap = newSinceMaps.find(atom->string());
       
   677             NewClassMaps::const_iterator ncmap;
       
   678             ncmap = newClassMaps.find(atom->string());
       
   679             if ((nsmap != newSinceMaps.constEnd()) && !nsmap.value().isEmpty()) {
       
   680                 QList<Section> sections;
       
   681                 QList<Section>::ConstIterator s;
       
   682                 for (int i=0; i<LastSinceType; ++i)
       
   683                     sections.append(Section(sinceTitle(i),QString(),QString()));
       
   684 
       
   685                 NodeMultiMap::const_iterator n = nsmap.value().constBegin();
       
   686                 while (n != nsmap.value().constEnd()) {
       
   687                     const Node* node = n.value();
       
   688                     switch (node->type()) {
       
   689                       case Node::Namespace:
       
   690                           sections[Namespace].appendMember((Node*)node);
       
   691                           break;
       
   692                       case Node::Class: 
       
   693                           sections[Class].appendMember((Node*)node);
       
   694                           break;
       
   695                       case Node::Enum: 
       
   696                           sections[Enum].appendMember((Node*)node);
       
   697                           break;
       
   698                       case Node::Typedef: 
       
   699                           sections[Typedef].appendMember((Node*)node);
       
   700                           break;
       
   701                       case Node::Function: {
       
   702                           const FunctionNode* fn = static_cast<const FunctionNode*>(node);
       
   703                           if (fn->isMacro())
       
   704                               sections[Macro].appendMember((Node*)node);
       
   705                           else {
       
   706                               Node* p = fn->parent();
       
   707                               if (p) {
       
   708                                   if (p->type() == Node::Class)
       
   709                                       sections[MemberFunction].appendMember((Node*)node);
       
   710                                   else if (p->type() == Node::Namespace) {
       
   711                                       if (p->name().isEmpty())
       
   712                                           sections[GlobalFunction].appendMember((Node*)node);
       
   713                                       else
       
   714                                           sections[NamespaceFunction].appendMember((Node*)node);
       
   715                                   }
       
   716                                   else
       
   717                                       sections[GlobalFunction].appendMember((Node*)node);
       
   718                               }
       
   719                               else
       
   720                                   sections[GlobalFunction].appendMember((Node*)node);
       
   721                           }
       
   722                           break;
       
   723                       }
       
   724                       case Node::Property:
       
   725                           sections[Property].appendMember((Node*)node);
       
   726                           break;
       
   727                       case Node::Variable: 
       
   728                           sections[Variable].appendMember((Node*)node);
       
   729                           break;
       
   730                       case Node::QmlProperty:
       
   731                           sections[QmlProperty].appendMember((Node*)node);
       
   732                           break;
       
   733                       case Node::QmlSignal:
       
   734                           sections[QmlSignal].appendMember((Node*)node);
       
   735                           break;
       
   736                       case Node::QmlMethod:
       
   737                           sections[QmlMethod].appendMember((Node*)node);
       
   738                           break;
       
   739                       default:
       
   740                           break;
       
   741                     }
       
   742                     ++n;
       
   743                 }
       
   744 
       
   745                 /*
       
   746                   First generate the table of contents.
       
   747                  */
       
   748                 out() << "<ul>\n";
       
   749                 s = sections.constBegin();
       
   750                 while (s != sections.constEnd()) {
       
   751                     if (!(*s).members.isEmpty()) {
       
   752 
       
   753                         out() << "<li>"
       
   754                               << "<a href=\"#"
       
   755                               << Doc::canonicalTitle((*s).name)
       
   756                               << "\">"
       
   757                               << (*s).name
       
   758                               << "</a></li>\n";
       
   759                     }
       
   760                     ++s;
       
   761                 }
       
   762                 out() << "</ul>\n";
       
   763 
       
   764                 int idx = 0;
       
   765                 s = sections.constBegin();
       
   766                 while (s != sections.constEnd()) {
       
   767                     if (!(*s).members.isEmpty()) {
       
   768                         out() << "<a name=\""
       
   769                               << Doc::canonicalTitle((*s).name)
       
   770                               << "\"></a>\n";
       
   771                         out() << "<h3>" << protect((*s).name) << "</h3>\n";
       
   772                         if (idx == Class)
       
   773                             generateCompactList(0, marker, ncmap.value(), QString("Q"));
       
   774                         else if (idx == MemberFunction) {
       
   775                             ParentMaps parentmaps;
       
   776                             ParentMaps::iterator pmap;
       
   777                             NodeList::const_iterator i = s->members.constBegin();
       
   778                             while (i != s->members.constEnd()) {
       
   779                                 Node* p = (*i)->parent();
       
   780                                 pmap = parentmaps.find(p);
       
   781                                 if (pmap == parentmaps.end())
       
   782                                     pmap = parentmaps.insert(p,NodeMultiMap());
       
   783                                 pmap->insert((*i)->name(),(*i));
       
   784                                 ++i;
       
   785                             }
       
   786                             pmap = parentmaps.begin();
       
   787                             while (pmap != parentmaps.end()) {
       
   788                                 NodeList nlist = pmap->values();
       
   789                                 out() << "<p>Class ";
       
   790 
       
   791                                 out() << "<a href=\""
       
   792                                       << linkForNode(pmap.key(), 0)
       
   793                                       << "\">";
       
   794                                 QStringList pieces = fullName(pmap.key(), 0, marker).split("::");
       
   795                                 out() << protect(pieces.last());
       
   796                                 out() << "</a>"  << ":</p>\n";
       
   797 
       
   798                                 generateSection(nlist, 0, marker, CodeMarker::Summary);
       
   799                                 out() << "<br />";
       
   800                                 ++pmap;
       
   801                             }
       
   802                         }
       
   803                         else
       
   804                             generateSection(s->members, 0, marker, CodeMarker::Summary);
       
   805                      }
       
   806                     ++idx;
       
   807                     ++s;
       
   808                 }
       
   809             }
       
   810         }
       
   811         break;
       
   812     case Atom::Image:
       
   813     case Atom::InlineImage:
       
   814         {
       
   815             QString fileName = imageFileName(relative, atom->string());
       
   816             QString text;
       
   817             if (atom->next() != 0)
       
   818                 text = atom->next()->string();
       
   819             if (atom->type() == Atom::Image)
       
   820                 out() << "<p align=\"center\">";
       
   821             if (fileName.isEmpty()) {
       
   822                 out() << "<font color=\"red\">[Missing image "
       
   823                       << protect(atom->string()) << "]</font>";
       
   824             }
       
   825             else {
       
   826                 out() << "<img src=\"" << protect(fileName) << "\"";
       
   827                 if (!text.isEmpty())
       
   828                     out() << " alt=\"" << protect(text) << "\"";
       
   829                 out() << " />";
       
   830                 helpProjectWriter->addExtraFile(fileName);
       
   831             }
       
   832             if (atom->type() == Atom::Image)
       
   833                 out() << "</p>";
       
   834         }
       
   835         break;
       
   836     case Atom::ImageText:
       
   837         break;
       
   838     case Atom::LegaleseLeft:
       
   839         out() << "<div style=\"padding: 0.5em; background: #e0e0e0; color: black\">";
       
   840         break;
       
   841     case Atom::LegaleseRight:
       
   842         out() << "</div>";
       
   843         break;
       
   844     case Atom::LineBreak:
       
   845         out() << "<br />";
       
   846         break;
       
   847     case Atom::Link:
       
   848         {
       
   849             const Node *node = 0;
       
   850             QString myLink = getLink(atom, relative, marker, &node);
       
   851             if (myLink.isEmpty()) {
       
   852                 relative->doc().location().warning(tr("Cannot link to '%1' in %2")
       
   853                         .arg(atom->string())
       
   854                         .arg(marker->plainFullName(relative)));
       
   855             }
       
   856             beginLink(myLink, node, relative, marker);
       
   857             skipAhead = 1;
       
   858         }
       
   859         break;
       
   860     case Atom::LinkNode:
       
   861         {
       
   862             const Node *node = CodeMarker::nodeForString(atom->string());
       
   863             beginLink(linkForNode(node, relative), node, relative, marker);
       
   864             skipAhead = 1;
       
   865         }
       
   866         break;
       
   867     case Atom::ListLeft:
       
   868         if (in_para) {
       
   869             out() << "</p>\n";
       
   870             in_para = false;
       
   871         }
       
   872         if (atom->string() == ATOM_LIST_BULLET) {
       
   873             out() << "<ul>\n";
       
   874         }
       
   875         else if (atom->string() == ATOM_LIST_TAG) {
       
   876             out() << "<dl>\n";
       
   877         }
       
   878         else if (atom->string() == ATOM_LIST_VALUE) {
       
   879             threeColumnEnumValueTable = isThreeColumnEnumValueTable(atom);
       
   880             if (threeColumnEnumValueTable) {
       
   881                 out() << "<p><table class=\"valuelist\" border=\"1\" cellpadding=\"2\" "
       
   882                       << "cellspacing=\"1\" width=\"100%\">\n"
       
   883                       << "<tr><th width=\"25%\">Constant</th>"
       
   884                       << "<th width=\"15%\">Value</th>"
       
   885                       << "<th width=\"60%\">Description</th></tr>\n";
       
   886             }
       
   887             else {
       
   888                 out() << "<p><table  class=\"valuelist\" border=\"1\" cellpadding=\"2\" "
       
   889                       << "cellspacing=\"1\" width=\"40%\">\n"
       
   890                       << "<tr><th width=\"60%\">Constant</th><th "
       
   891                       << "width=\"40%\">Value</th></tr>\n";
       
   892             }
       
   893         }
       
   894         else {
       
   895             out() << "<ol type=";
       
   896             if (atom->string() == ATOM_LIST_UPPERALPHA) {
       
   897                 out() << "\"A\"";
       
   898             }
       
   899             else if (atom->string() == ATOM_LIST_LOWERALPHA) {
       
   900                 out() << "\"a\"";
       
   901             }
       
   902             else if (atom->string() == ATOM_LIST_UPPERROMAN) {
       
   903                 out() << "\"I\"";
       
   904             }
       
   905             else if (atom->string() == ATOM_LIST_LOWERROMAN) {
       
   906                 out() << "\"i\"";
       
   907             }
       
   908             else { // (atom->string() == ATOM_LIST_NUMERIC)
       
   909                 out() << "\"1\"";
       
   910             }
       
   911             if (atom->next() != 0 && atom->next()->string().toInt() != 1)
       
   912                 out() << " start=\"" << atom->next()->string() << "\"";
       
   913             out() << ">\n";
       
   914         }
       
   915         break;
       
   916     case Atom::ListItemNumber:
       
   917         break;
       
   918     case Atom::ListTagLeft:
       
   919         if (atom->string() == ATOM_LIST_TAG) {
       
   920             out() << "<dt>";
       
   921         }
       
   922         else { // (atom->string() == ATOM_LIST_VALUE)
       
   923             // ### Trenton
       
   924 
       
   925             out() << "<tr><td valign=\"top\"><tt>"
       
   926                   << protect(plainCode(marker->markedUpEnumValue(atom->next()->string(),
       
   927                                                                  relative)))
       
   928                   << "</tt></td><td align=\"center\" valign=\"top\">";
       
   929 
       
   930             QString itemValue;
       
   931             if (relative->type() == Node::Enum) {
       
   932                 const EnumNode *enume = static_cast<const EnumNode *>(relative);
       
   933                 itemValue = enume->itemValue(atom->next()->string());
       
   934             }
       
   935 
       
   936             if (itemValue.isEmpty())
       
   937                 out() << "?";
       
   938             else
       
   939                 out() << "<tt>" << protect(itemValue) << "</tt>";
       
   940 
       
   941             skipAhead = 1;
       
   942         }
       
   943         break;
       
   944     case Atom::ListTagRight:
       
   945         if (atom->string() == ATOM_LIST_TAG)
       
   946             out() << "</dt>\n";
       
   947         break;
       
   948     case Atom::ListItemLeft:
       
   949         if (atom->string() == ATOM_LIST_TAG) {
       
   950             out() << "<dd>";
       
   951         }
       
   952         else if (atom->string() == ATOM_LIST_VALUE) {
       
   953             if (threeColumnEnumValueTable) {
       
   954                 out() << "</td><td valign=\"top\">";
       
   955                 if (matchAhead(atom, Atom::ListItemRight))
       
   956                     out() << "&nbsp;";
       
   957             }
       
   958         }
       
   959         else {
       
   960             out() << "<li>";
       
   961         }
       
   962         if (matchAhead(atom, Atom::ParaLeft))
       
   963             skipAhead = 1;
       
   964         break;
       
   965     case Atom::ListItemRight:
       
   966         if (atom->string() == ATOM_LIST_TAG) {
       
   967             out() << "</dd>\n";
       
   968         }
       
   969         else if (atom->string() == ATOM_LIST_VALUE) {
       
   970             out() << "</td></tr>\n";
       
   971         }
       
   972         else {
       
   973             out() << "</li>\n";
       
   974         }
       
   975         break;
       
   976     case Atom::ListRight:
       
   977         if (atom->string() == ATOM_LIST_BULLET) {
       
   978             out() << "</ul>\n";
       
   979         }
       
   980         else if (atom->string() == ATOM_LIST_TAG) {
       
   981             out() << "</dl>\n";
       
   982         }
       
   983         else if (atom->string() == ATOM_LIST_VALUE) {
       
   984             out() << "</table></p>\n";
       
   985         }
       
   986         else {
       
   987             out() << "</ol>\n";
       
   988         }
       
   989         break;
       
   990     case Atom::Nop:
       
   991         break;
       
   992     case Atom::ParaLeft:
       
   993         out() << "<p>";
       
   994         in_para = true;
       
   995         break;
       
   996     case Atom::ParaRight:
       
   997         endLink();
       
   998         if (in_para) {
       
   999             out() << "</p>\n";
       
  1000             in_para = false;
       
  1001         }
       
  1002         //if (!matchAhead(atom, Atom::ListItemRight) && !matchAhead(atom, Atom::TableItemRight))
       
  1003         //    out() << "</p>\n";
       
  1004         break;
       
  1005     case Atom::QuotationLeft:
       
  1006         out() << "<blockquote>";
       
  1007         break;
       
  1008     case Atom::QuotationRight:
       
  1009         out() << "</blockquote>\n";
       
  1010         break;
       
  1011     case Atom::RawString:
       
  1012         out() << atom->string();
       
  1013         break;
       
  1014     case Atom::SectionLeft:
       
  1015 #if 0
       
  1016         {
       
  1017             int nextLevel = atom->string().toInt();
       
  1018             if (sectionNumber.size() < nextLevel) {
       
  1019                 do {
       
  1020                     sectionNumber.append("1");
       
  1021                 } while (sectionNumber.size() < nextLevel);
       
  1022             }
       
  1023             else {
       
  1024                 while (sectionNumber.size() > nextLevel) {
       
  1025                     sectionNumber.removeLast();
       
  1026                 }
       
  1027                 sectionNumber.last() = QString::number(sectionNumber.last().toInt() + 1);
       
  1028             }
       
  1029             out() << "<a name=\"sec-" << sectionNumber.join("-") << "\"></a>\n";
       
  1030         }
       
  1031 #else
       
  1032         out() << "<a name=\"" << Doc::canonicalTitle(Text::sectionHeading(atom).toString())
       
  1033               << "\"></a>\n";
       
  1034 #endif
       
  1035         break;
       
  1036     case Atom::SectionRight:
       
  1037         break;
       
  1038     case Atom::SectionHeadingLeft:
       
  1039         out() << "<h" + QString::number(atom->string().toInt() + hOffset(relative)) + ">";
       
  1040         inSectionHeading = true;
       
  1041         break;
       
  1042     case Atom::SectionHeadingRight:
       
  1043         out() << "</h" + QString::number(atom->string().toInt() + hOffset(relative)) + ">\n";
       
  1044         inSectionHeading = false;
       
  1045         break;
       
  1046     case Atom::SidebarLeft:
       
  1047         break;
       
  1048     case Atom::SidebarRight:
       
  1049         break;
       
  1050     case Atom::String:
       
  1051         if (inLink && !inContents && !inSectionHeading) {
       
  1052             generateLink(atom, relative, marker);
       
  1053         }
       
  1054         else {
       
  1055             out() << protect(atom->string());
       
  1056         }
       
  1057         break;
       
  1058     case Atom::TableLeft:
       
  1059         if (in_para) {
       
  1060             out() << "</p>\n";
       
  1061             in_para = false;
       
  1062         }
       
  1063         if (!atom->string().isEmpty()) {
       
  1064             if (atom->string().contains("%"))
       
  1065                 out() << "<p><table class=\"generic\" width=\"" << atom->string() << "\" "
       
  1066                       << "align=\"center\" cellpadding=\"2\" "
       
  1067                       << "cellspacing=\"1\" border=\"0\">\n";
       
  1068             else {
       
  1069                 out() << "<p><table class=\"generic\" align=\"center\" cellpadding=\"2\" "
       
  1070                       << "cellspacing=\"1\" border=\"0\">\n";
       
  1071             }
       
  1072         }
       
  1073         else {
       
  1074             out() << "<p><table class=\"generic\" align=\"center\" cellpadding=\"2\" "
       
  1075                   << "cellspacing=\"1\" border=\"0\">\n";
       
  1076         }
       
  1077         numTableRows = 0;
       
  1078         break;
       
  1079     case Atom::TableRight:
       
  1080         out() << "</table></p>\n";
       
  1081         break;
       
  1082     case Atom::TableHeaderLeft:
       
  1083         out() << "<thead><tr valign=\"top\" class=\"qt-style\">";
       
  1084         inTableHeader = true;
       
  1085         break;
       
  1086     case Atom::TableHeaderRight:
       
  1087         out() << "</tr>";
       
  1088         if (matchAhead(atom, Atom::TableHeaderLeft)) {
       
  1089             skipAhead = 1;
       
  1090             out() << "\n<tr valign=\"top\" class=\"qt-style\">";
       
  1091         }
       
  1092         else {
       
  1093             out() << "</thead>\n";
       
  1094             inTableHeader = false;
       
  1095         }
       
  1096         break;
       
  1097     case Atom::TableRowLeft:
       
  1098         if (++numTableRows % 2 == 1)
       
  1099             out() << "<tr valign=\"top\" class=\"odd\">";
       
  1100         else
       
  1101             out() << "<tr valign=\"top\" class=\"even\">";
       
  1102         break;
       
  1103     case Atom::TableRowRight:
       
  1104         out() << "</tr>\n";
       
  1105         break;
       
  1106     case Atom::TableItemLeft:
       
  1107         {
       
  1108             if (inTableHeader)
       
  1109                 out() << "<th";
       
  1110             else
       
  1111                 out() << "<td";
       
  1112 
       
  1113             QStringList spans = atom->string().split(",");
       
  1114             if (spans.size() == 2) {
       
  1115                 if (spans.at(0) != "1")
       
  1116                     out() << " colspan=\"" << spans.at(0) << "\"";
       
  1117                 if (spans.at(1) != "1")
       
  1118                     out() << " rowspan=\"" << spans.at(1) << "\"";
       
  1119                 out() << ">";
       
  1120             }
       
  1121             if (matchAhead(atom, Atom::ParaLeft))
       
  1122                 skipAhead = 1;
       
  1123         }
       
  1124         break;
       
  1125     case Atom::TableItemRight:
       
  1126         if (inTableHeader)
       
  1127             out() << "</th>";
       
  1128         else
       
  1129             out() << "</td>";
       
  1130         if (matchAhead(atom, Atom::ParaLeft))
       
  1131             skipAhead = 1;
       
  1132         break;
       
  1133     case Atom::TableOfContents:
       
  1134         {
       
  1135             int numColumns = 1;
       
  1136             const Node *node = relative;
       
  1137 
       
  1138             Doc::SectioningUnit sectioningUnit = Doc::Section4;
       
  1139             QStringList params = atom->string().split(",");
       
  1140             QString columnText = params.at(0);
       
  1141             QStringList pieces = columnText.split(" ", QString::SkipEmptyParts);
       
  1142             if (pieces.size() >= 2) {
       
  1143                 columnText = pieces.at(0);
       
  1144                 pieces.pop_front();
       
  1145                 QString path = pieces.join(" ").trimmed();
       
  1146                 node = findNodeForTarget(path, relative, marker, atom);
       
  1147             }
       
  1148 
       
  1149             if (params.size() == 2) {
       
  1150                 numColumns = qMax(columnText.toInt(), numColumns);
       
  1151                 sectioningUnit = (Doc::SectioningUnit)params.at(1).toInt();
       
  1152             }
       
  1153 
       
  1154             if (node)
       
  1155                 generateTableOfContents(node,
       
  1156                                         marker,
       
  1157                                         sectioningUnit,
       
  1158                                         numColumns,
       
  1159                                         relative);
       
  1160         }
       
  1161         break;
       
  1162     case Atom::Target:
       
  1163         out() << "<a name=\"" << Doc::canonicalTitle(atom->string()) << "\"></a>";
       
  1164         break;
       
  1165     case Atom::UnhandledFormat:
       
  1166         out() << "<font color=\"red\"><b>&lt;Missing HTML&gt;</b></font>";
       
  1167         break;
       
  1168     case Atom::UnknownCommand:
       
  1169         out() << "<font color=\"red\"><b><code>\\" << protect(atom->string())
       
  1170               << "</code></b></font>";
       
  1171         break;
       
  1172 #ifdef QDOC_QML
       
  1173     case Atom::QmlText:
       
  1174     case Atom::EndQmlText:
       
  1175         // don't do anything with these. They are just tags.
       
  1176         break;
       
  1177 #endif
       
  1178     default:
       
  1179         unknownAtom(atom);
       
  1180     }
       
  1181     return skipAhead;
       
  1182 }
       
  1183 
       
  1184 void HtmlGenerator::generateClassLikeNode(const InnerNode *inner,
       
  1185                                           CodeMarker *marker)
       
  1186 {
       
  1187     QList<Section> sections;
       
  1188     QList<Section>::ConstIterator s;
       
  1189 
       
  1190     const ClassNode *classe = 0;
       
  1191     const NamespaceNode *namespasse = 0;
       
  1192 
       
  1193     QString title;
       
  1194     QString rawTitle;
       
  1195     QString fullTitle;
       
  1196     if (inner->type() == Node::Namespace) {
       
  1197         namespasse = static_cast<const NamespaceNode *>(inner);
       
  1198         rawTitle = marker->plainName(inner);
       
  1199         fullTitle = marker->plainFullName(inner);
       
  1200         title = rawTitle + " Namespace Reference";
       
  1201     }
       
  1202     else if (inner->type() == Node::Class) {
       
  1203         classe = static_cast<const ClassNode *>(inner);
       
  1204         rawTitle = marker->plainName(inner);
       
  1205         fullTitle = marker->plainFullName(inner);
       
  1206         title = rawTitle + " Class Reference";
       
  1207     }
       
  1208 
       
  1209     DcfSection classSection;
       
  1210     classSection.title = title;
       
  1211     classSection.ref = linkForNode(inner, 0);
       
  1212     classSection.keywords += qMakePair(inner->name(), classSection.ref);
       
  1213 
       
  1214     Text subtitleText;
       
  1215     if (rawTitle != fullTitle)
       
  1216         subtitleText << "(" << Atom(Atom::AutoLink, fullTitle) << ")"
       
  1217                      << Atom(Atom::LineBreak);
       
  1218 
       
  1219     QString fixedModule = inner->moduleName();
       
  1220     if (fixedModule == "Qt3SupportLight")
       
  1221         fixedModule = "Qt3Support";
       
  1222     if (!fixedModule.isEmpty())
       
  1223         subtitleText << "[" << Atom(Atom::AutoLink, fixedModule) << " module]";
       
  1224 
       
  1225     if (fixedModule.isEmpty()) {
       
  1226         QMultiMap<QString, QString> publicGroups = myTree->publicGroups();
       
  1227         QList<QString> groupNames = publicGroups.values(inner->name());
       
  1228         if (!groupNames.isEmpty()) {
       
  1229             qSort(groupNames.begin(), groupNames.end());
       
  1230             subtitleText << "[";
       
  1231             for (int j=0; j<groupNames.count(); j++) {
       
  1232                 subtitleText <<  Atom(Atom::AutoLink, groupNames[j]);
       
  1233                 if (j<groupNames.count()-1)
       
  1234                     subtitleText <<", ";
       
  1235             }
       
  1236             subtitleText << "]";
       
  1237         }
       
  1238     }
       
  1239 
       
  1240     generateHeader(title, inner, marker, true);
       
  1241     generateTitle(title, subtitleText, SmallSubTitle, inner, marker);
       
  1242 
       
  1243 #ifdef QDOC_QML
       
  1244     if (classe && !classe->qmlElement().isEmpty()) {
       
  1245         generateInstantiatedBy(classe,marker);
       
  1246     }
       
  1247 #endif
       
  1248     
       
  1249     generateBrief(inner, marker);
       
  1250     generateIncludes(inner, marker);
       
  1251     generateStatus(inner, marker);
       
  1252     if (classe) {
       
  1253         generateModuleWarning(classe, marker);
       
  1254         generateInherits(classe, marker);
       
  1255         generateInheritedBy(classe, marker);
       
  1256     }
       
  1257     generateThreadSafeness(inner, marker);
       
  1258     generateSince(inner, marker);
       
  1259 
       
  1260     out() << "<ul>\n";
       
  1261 
       
  1262     QString membersLink = generateListOfAllMemberFile(inner, marker);
       
  1263     if (!membersLink.isEmpty())
       
  1264         out() << "<li><a href=\"" << membersLink << "\">"
       
  1265               << "List of all members, including inherited members</a></li>\n";
       
  1266 
       
  1267     QString obsoleteLink = generateLowStatusMemberFile(inner,
       
  1268                                                        marker,
       
  1269                                                        CodeMarker::Obsolete);
       
  1270     if (!obsoleteLink.isEmpty())
       
  1271         out() << "<li><a href=\"" << obsoleteLink << "\">"
       
  1272               << "Obsolete members</a></li>\n";
       
  1273 
       
  1274     QString compatLink = generateLowStatusMemberFile(inner,
       
  1275                                                      marker,
       
  1276                                                      CodeMarker::Compat);
       
  1277     if (!compatLink.isEmpty())
       
  1278         out() << "<li><a href=\"" << compatLink << "\">"
       
  1279               << "Qt 3 support members</a></li>\n";
       
  1280 
       
  1281     out() << "</ul>\n";
       
  1282 
       
  1283     bool needOtherSection = false;
       
  1284 
       
  1285     sections = marker->sections(inner, CodeMarker::Summary, CodeMarker::Okay);
       
  1286     s = sections.begin();
       
  1287     while (s != sections.end()) {
       
  1288         if (s->members.isEmpty() && s->reimpMembers.isEmpty()) {
       
  1289             if (!s->inherited.isEmpty())
       
  1290                 needOtherSection = true;
       
  1291         }
       
  1292         else {
       
  1293             if (!s->members.isEmpty()) {
       
  1294                 out() << "<hr />\n";
       
  1295                 out() << "<a name=\""
       
  1296                       << registerRef((*s).name.toLower())
       
  1297                       << "\"></a>\n";
       
  1298                 out() << "<h2>" << protect((*s).name) << "</h2>\n";
       
  1299                 generateSection(s->members, inner, marker, CodeMarker::Summary);
       
  1300             }
       
  1301             if (!s->reimpMembers.isEmpty()) {
       
  1302                 QString name = QString("Reimplemented ") + (*s).name;
       
  1303                 out() << "<hr />\n";
       
  1304                 out() << "<a name=\""
       
  1305                       << registerRef(name.toLower())
       
  1306                       << "\"></a>\n";
       
  1307                 out() << "<h2>" << protect(name) << "</h2>\n";
       
  1308                 generateSection(s->reimpMembers, inner, marker, CodeMarker::Summary);
       
  1309             }
       
  1310 
       
  1311             if (!s->inherited.isEmpty()) {
       
  1312                 out() << "<ul>\n";
       
  1313                 generateSectionInheritedList(*s, inner, marker, true);
       
  1314                 out() << "</ul>\n";
       
  1315             }
       
  1316         }
       
  1317         ++s;
       
  1318     }
       
  1319 
       
  1320     if (needOtherSection) {
       
  1321         out() << "<h3>Additional Inherited Members</h3>\n"
       
  1322                  "<ul>\n";
       
  1323 
       
  1324         s = sections.begin();
       
  1325         while (s != sections.end()) {
       
  1326             if (s->members.isEmpty() && !s->inherited.isEmpty())
       
  1327                 generateSectionInheritedList(*s, inner, marker);
       
  1328             ++s;
       
  1329         }
       
  1330         out() << "</ul>\n";
       
  1331     }
       
  1332 
       
  1333     out() << "<a name=\"" << registerRef("details") << "\"></a>\n";
       
  1334 
       
  1335     if (!inner->doc().isEmpty()) {
       
  1336         out() << "<hr />\n"
       
  1337               << "<h2>" << "Detailed Description" << "</h2>\n";
       
  1338         generateBody(inner, marker);
       
  1339         generateAlsoList(inner, marker);
       
  1340     }
       
  1341 
       
  1342     sections = marker->sections(inner, CodeMarker::Detailed, CodeMarker::Okay);
       
  1343     s = sections.begin();
       
  1344     while (s != sections.end()) {
       
  1345         out() << "<hr />\n";
       
  1346         out() << "<h2>" << protect((*s).name) << "</h2>\n";
       
  1347 
       
  1348         NodeList::ConstIterator m = (*s).members.begin();
       
  1349         while (m != (*s).members.end()) {
       
  1350             if ((*m)->access() != Node::Private) { // ### check necessary?
       
  1351                 if ((*m)->type() != Node::Class)
       
  1352                     generateDetailedMember(*m, inner, marker);
       
  1353                 else {
       
  1354                     out() << "<h3> class ";
       
  1355                     generateFullName(*m, inner, marker);
       
  1356                     out() << "</h3>";
       
  1357                     generateBrief(*m, marker, inner);
       
  1358                 }
       
  1359 
       
  1360                 QStringList names;
       
  1361                 names << (*m)->name();
       
  1362                 if ((*m)->type() == Node::Function) {
       
  1363                     const FunctionNode *func = reinterpret_cast<const FunctionNode *>(*m);
       
  1364                     if (func->metaness() == FunctionNode::Ctor ||
       
  1365                         func->metaness() == FunctionNode::Dtor ||
       
  1366                         func->overloadNumber() != 1)
       
  1367                         names.clear();
       
  1368                 }
       
  1369                 else if ((*m)->type() == Node::Property) {
       
  1370                     const PropertyNode *prop = reinterpret_cast<const PropertyNode *>(*m);
       
  1371                     if (!prop->getters().isEmpty() &&
       
  1372                         !names.contains(prop->getters().first()->name()))
       
  1373                         names << prop->getters().first()->name();
       
  1374                     if (!prop->setters().isEmpty())
       
  1375                         names << prop->setters().first()->name();
       
  1376                     if (!prop->resetters().isEmpty())
       
  1377                         names << prop->resetters().first()->name();
       
  1378                 }
       
  1379                 else if ((*m)->type() == Node::Enum) {
       
  1380                     const EnumNode *enume = reinterpret_cast<const EnumNode*>(*m);
       
  1381                     if (enume->flagsType())
       
  1382                         names << enume->flagsType()->name();
       
  1383 
       
  1384                     foreach (const QString &enumName,
       
  1385                              enume->doc().enumItemNames().toSet() -
       
  1386                              enume->doc().omitEnumItemNames().toSet())
       
  1387                         names << plainCode(marker->markedUpEnumValue(enumName,
       
  1388                                                                      enume));
       
  1389                 }
       
  1390                 foreach (const QString &name, names)
       
  1391                     classSection.keywords += qMakePair(name,linkForNode(*m,0));
       
  1392             }
       
  1393             ++m;
       
  1394         }
       
  1395         ++s;
       
  1396     }
       
  1397     generateFooter(inner);
       
  1398 
       
  1399     if (!membersLink.isEmpty()) {
       
  1400         DcfSection membersSection;
       
  1401         membersSection.title = "List of all members";
       
  1402         membersSection.ref = membersLink;
       
  1403         appendDcfSubSection(&classSection, membersSection);
       
  1404     }
       
  1405     if (!obsoleteLink.isEmpty()) {
       
  1406         DcfSection obsoleteSection;
       
  1407         obsoleteSection.title = "Obsolete members";
       
  1408         obsoleteSection.ref = obsoleteLink;
       
  1409         appendDcfSubSection(&classSection, obsoleteSection);
       
  1410     }
       
  1411     if (!compatLink.isEmpty()) {
       
  1412         DcfSection compatSection;
       
  1413         compatSection.title = "Qt 3 support members";
       
  1414         compatSection.ref = compatLink;
       
  1415         appendDcfSubSection(&classSection, compatSection);
       
  1416     }
       
  1417 
       
  1418     appendDcfSubSection(&dcfClassesRoot, classSection);
       
  1419 }
       
  1420 
       
  1421 void HtmlGenerator::generateFakeNode(const FakeNode *fake, CodeMarker *marker)
       
  1422 {
       
  1423     SubTitleSize subTitleSize = LargeSubTitle;
       
  1424     DcfSection fakeSection;
       
  1425     fakeSection.title = fake->fullTitle();
       
  1426     fakeSection.ref = linkForNode(fake, 0);
       
  1427 
       
  1428     QList<Section> sections;
       
  1429     QList<Section>::const_iterator s;
       
  1430 
       
  1431     QString htmlTitle = fake->fullTitle();
       
  1432     if (fake->subType() == Node::File && !fake->subTitle().isEmpty()) {
       
  1433         subTitleSize = SmallSubTitle;
       
  1434         htmlTitle += " (" + fake->subTitle() + ")";
       
  1435     }
       
  1436 
       
  1437     generateHeader(htmlTitle, fake, marker, true);
       
  1438     generateTitle(fake->fullTitle(),
       
  1439                   Text() << fake->subTitle(),
       
  1440                   subTitleSize,
       
  1441                   fake,
       
  1442                   marker);
       
  1443 
       
  1444     if (fake->subType() == Node::Module) {
       
  1445         // Generate brief text and status for modules.
       
  1446         generateBrief(fake, marker);
       
  1447         generateStatus(fake, marker);
       
  1448 
       
  1449         if (moduleNamespaceMap.contains(fake->name())) {
       
  1450             out() << "<h2>Namespaces</h2>\n";
       
  1451             generateAnnotatedList(fake, marker, moduleNamespaceMap[fake->name()]);
       
  1452         }
       
  1453         if (moduleClassMap.contains(fake->name())) {
       
  1454             out() << "<h2>Classes</h2>\n";
       
  1455             generateAnnotatedList(fake, marker, moduleClassMap[fake->name()]);
       
  1456         }
       
  1457     }
       
  1458     else if (fake->subType() == Node::HeaderFile) {
       
  1459         // Generate brief text and status for modules.
       
  1460         generateBrief(fake, marker);
       
  1461         generateStatus(fake, marker);
       
  1462 
       
  1463         out() << "<ul>\n";
       
  1464 
       
  1465         QString membersLink = generateListOfAllMemberFile(fake, marker);
       
  1466         if (!membersLink.isEmpty())
       
  1467             out() << "<li><a href=\"" << membersLink << "\">"
       
  1468                   << "List of all members, including inherited members</a></li>\n";
       
  1469 
       
  1470         QString obsoleteLink = generateLowStatusMemberFile(fake,
       
  1471                                                            marker,
       
  1472                                                            CodeMarker::Obsolete);
       
  1473         if (!obsoleteLink.isEmpty())
       
  1474             out() << "<li><a href=\"" << obsoleteLink << "\">"
       
  1475                   << "Obsolete members</a></li>\n";
       
  1476 
       
  1477         QString compatLink = generateLowStatusMemberFile(fake,
       
  1478                                                          marker,
       
  1479                                                          CodeMarker::Compat);
       
  1480         if (!compatLink.isEmpty())
       
  1481             out() << "<li><a href=\"" << compatLink << "\">"
       
  1482                   << "Qt 3 support members</a></li>\n";
       
  1483 
       
  1484         out() << "</ul>\n";
       
  1485 
       
  1486         if (!membersLink.isEmpty()) {
       
  1487             DcfSection membersSection;
       
  1488             membersSection.title = "List of all members";
       
  1489             membersSection.ref = membersLink;
       
  1490             appendDcfSubSection(&fakeSection, membersSection);
       
  1491         }
       
  1492         if (!obsoleteLink.isEmpty()) {
       
  1493             DcfSection obsoleteSection;
       
  1494             obsoleteSection.title = "Obsolete members";
       
  1495             obsoleteSection.ref = obsoleteLink;
       
  1496             appendDcfSubSection(&fakeSection, obsoleteSection);
       
  1497         }
       
  1498         if (!compatLink.isEmpty()) {
       
  1499             DcfSection compatSection;
       
  1500             compatSection.title = "Qt 3 support members";
       
  1501             compatSection.ref = compatLink;
       
  1502             appendDcfSubSection(&fakeSection, compatSection);
       
  1503         }
       
  1504     }
       
  1505 #ifdef QDOC_QML
       
  1506     else if (fake->subType() == Node::QmlClass) {
       
  1507         const QmlClassNode* qml_cn = static_cast<const QmlClassNode*>(fake);
       
  1508         const ClassNode* cn = qml_cn->classNode();
       
  1509         generateQmlInherits(qml_cn, marker);
       
  1510         generateQmlInstantiates(qml_cn, marker);
       
  1511         generateBrief(qml_cn, marker);
       
  1512         sections = marker->qmlSections(qml_cn,CodeMarker::Summary);
       
  1513         s = sections.begin();
       
  1514         while (s != sections.end()) {
       
  1515             out() << "<a name=\"" << registerRef((*s).name) << "\"></a>\n";
       
  1516             out() << "<h2>" << protect((*s).name) << "</h2>\n";
       
  1517             generateQmlSummary(*s,fake,marker);
       
  1518             ++s;
       
  1519         }
       
  1520 
       
  1521         out() << "<a name=\"" << registerRef("details") << "\"></a>\n";
       
  1522         out() << "<h2>" << "Detailed Description" << "</h2>\n";
       
  1523         generateBody(fake, marker);
       
  1524         if (cn)
       
  1525             generateQmlText(cn->doc().body(), cn, marker, fake->name());
       
  1526         generateAlsoList(fake, marker);
       
  1527         out() << "<hr />\n";
       
  1528 
       
  1529         sections = marker->qmlSections(qml_cn,CodeMarker::Detailed);
       
  1530         s = sections.begin();
       
  1531         while (s != sections.end()) {
       
  1532             out() << "<h2>" << protect((*s).name) << "</h2>\n";
       
  1533             NodeList::ConstIterator m = (*s).members.begin();
       
  1534             while (m != (*s).members.end()) {
       
  1535                 generateDetailedQmlMember(*m, fake, marker);
       
  1536                 out() << "<br />\n";
       
  1537                 fakeSection.keywords += qMakePair((*m)->name(),
       
  1538                                                   linkForNode(*m,0));
       
  1539                 ++m;
       
  1540             }
       
  1541             ++s;
       
  1542         }
       
  1543         generateFooter(fake);
       
  1544         return;
       
  1545     }
       
  1546 #endif
       
  1547     
       
  1548     sections = marker->sections(fake, CodeMarker::Summary, CodeMarker::Okay);
       
  1549     s = sections.begin();
       
  1550     while (s != sections.end()) {
       
  1551         out() << "<a name=\"" << registerRef((*s).name) << "\"></a>\n";
       
  1552         out() << "<h2>" << protect((*s).name) << "</h2>\n";
       
  1553         generateSectionList(*s, fake, marker, CodeMarker::Summary);
       
  1554         ++s;
       
  1555     }
       
  1556 
       
  1557     Text brief = fake->doc().briefText();
       
  1558     if (fake->subType() == Node::Module && !brief.isEmpty()) {
       
  1559         out() << "<a name=\"" << registerRef("details") << "\"></a>\n";
       
  1560         out() << "<h2>" << "Detailed Description" << "</h2>\n";
       
  1561     }
       
  1562 
       
  1563     generateBody(fake, marker);
       
  1564     generateAlsoList(fake, marker);
       
  1565 
       
  1566     if (!fake->groupMembers().isEmpty()) {
       
  1567         NodeMap groupMembersMap;
       
  1568         foreach (const Node *node, fake->groupMembers()) {
       
  1569             if (node->type() == Node::Class || node->type() == Node::Namespace)
       
  1570                 groupMembersMap[node->name()] = node;
       
  1571         }
       
  1572         generateAnnotatedList(fake, marker, groupMembersMap);
       
  1573     }
       
  1574 
       
  1575     fakeSection.keywords += qMakePair(fakeSection.title, fakeSection.ref);
       
  1576 
       
  1577     sections = marker->sections(fake, CodeMarker::Detailed, CodeMarker::Okay);
       
  1578     s = sections.begin();
       
  1579     while (s != sections.end()) {
       
  1580         out() << "<hr />\n";
       
  1581         out() << "<h2>" << protect((*s).name) << "</h2>\n";
       
  1582 
       
  1583         NodeList::ConstIterator m = (*s).members.begin();
       
  1584         while (m != (*s).members.end()) {
       
  1585             generateDetailedMember(*m, fake, marker);
       
  1586             fakeSection.keywords += qMakePair((*m)->name(), linkForNode(*m, 0));
       
  1587             ++m;
       
  1588         }
       
  1589         ++s;
       
  1590     }
       
  1591     generateFooter(fake);
       
  1592 
       
  1593     if (fake->subType() == Node::Example) {
       
  1594         appendDcfSubSection(&dcfExamplesRoot, fakeSection);
       
  1595     }
       
  1596     else if (fake->subType() != Node::File) {
       
  1597         QString contentsPage = fake->links().value(Node::ContentsLink).first;
       
  1598 
       
  1599         if (contentsPage == "Qt Designer Manual") {
       
  1600             appendDcfSubSection(&dcfDesignerRoot, fakeSection);
       
  1601         }
       
  1602         else if (contentsPage == "Qt Linguist Manual") {
       
  1603             appendDcfSubSection(&dcfLinguistRoot, fakeSection);
       
  1604         }
       
  1605         else if (contentsPage == "Qt Assistant Manual") {
       
  1606             appendDcfSubSection(&dcfAssistantRoot, fakeSection);
       
  1607         }
       
  1608         else if (contentsPage == "qmake Manual") {
       
  1609             appendDcfSubSection(&dcfQmakeRoot, fakeSection);
       
  1610         }
       
  1611         else {
       
  1612             appendDcfSubSection(&dcfOverviewsRoot, fakeSection);
       
  1613         }
       
  1614     }
       
  1615 }
       
  1616 
       
  1617 QString HtmlGenerator::fileExtension(const Node * /* node */)
       
  1618 {
       
  1619     return "html";
       
  1620 }
       
  1621 
       
  1622 void HtmlGenerator::generateHeader(const QString& title,
       
  1623                                    const Node *node,
       
  1624                                    CodeMarker *marker,
       
  1625                                    bool mainPage)
       
  1626 {
       
  1627     out() << "<?xml version=\"1.0\" encoding=\"iso-8859-1\"?>\n";
       
  1628 
       
  1629     out() << "<!DOCTYPE html\n"
       
  1630              "    PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"DTD/xhtml1-strict.dtd\">\n"
       
  1631              "<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\" lang=\"en\">\n";
       
  1632 
       
  1633     QString shortVersion;
       
  1634     if ((project != "Qtopia") && (project != "Qt Extended")) {
       
  1635         shortVersion = project + " " + shortVersion + ": ";
       
  1636         if (node && !node->doc().location().isEmpty())
       
  1637             out() << "<!-- " << node->doc().location().fileName() << " -->\n";
       
  1638 
       
  1639         shortVersion = myTree->version();
       
  1640         if (shortVersion.count(QChar('.')) == 2)
       
  1641             shortVersion.truncate(shortVersion.lastIndexOf(QChar('.')));
       
  1642         if (!shortVersion.isEmpty()) {
       
  1643             if (project == "QSA")
       
  1644                 shortVersion = "QSA " + shortVersion + ": ";
       
  1645             else
       
  1646                 shortVersion = "Qt " + shortVersion + ": ";
       
  1647         }
       
  1648     }
       
  1649 
       
  1650     out() << "<head>\n"
       
  1651              "  <title>" << shortVersion << protect(title) << "</title>\n";
       
  1652     if (!style.isEmpty())
       
  1653         out() << "    <style type=\"text/css\">" << style << "</style>\n";
       
  1654 
       
  1655     const QMap<QString, QString> &metaMap = node->doc().metaTagMap();
       
  1656     if (!metaMap.isEmpty()) {
       
  1657         QMapIterator<QString, QString> i(metaMap);
       
  1658         while (i.hasNext()) {
       
  1659             i.next();
       
  1660             out() << "    <meta name=\"" << protect(i.key()) << "\" contents=\""
       
  1661                   << protect(i.value()) << "\" />\n";
       
  1662         }
       
  1663     }
       
  1664 
       
  1665     navigationLinks.clear();
       
  1666 
       
  1667     if (node && !node->links().empty()) {
       
  1668         QPair<QString,QString> linkPair;
       
  1669         QPair<QString,QString> anchorPair;
       
  1670         const Node *linkNode;
       
  1671 
       
  1672         if (node->links().contains(Node::PreviousLink)) {
       
  1673             linkPair = node->links()[Node::PreviousLink];
       
  1674             linkNode = findNodeForTarget(linkPair.first, node, marker);
       
  1675             if (!linkNode || linkNode == node)
       
  1676                 anchorPair = linkPair;
       
  1677             else
       
  1678                 anchorPair = anchorForNode(linkNode);
       
  1679 
       
  1680             out() << "  <link rel=\"prev\" href=\""
       
  1681                   << anchorPair.first << "\" />\n";
       
  1682 
       
  1683             navigationLinks += "[Previous: <a href=\"" + anchorPair.first + "\">";
       
  1684             if (linkPair.first == linkPair.second && !anchorPair.second.isEmpty())
       
  1685                 navigationLinks += protect(anchorPair.second);
       
  1686             else
       
  1687                 navigationLinks += protect(linkPair.second);
       
  1688             navigationLinks += "</a>]\n";
       
  1689         }
       
  1690         if (node->links().contains(Node::ContentsLink)) {
       
  1691             linkPair = node->links()[Node::ContentsLink];
       
  1692             linkNode = findNodeForTarget(linkPair.first, node, marker);
       
  1693             if (!linkNode || linkNode == node)
       
  1694                 anchorPair = linkPair;
       
  1695             else
       
  1696                 anchorPair = anchorForNode(linkNode);
       
  1697 
       
  1698             out() << "  <link rel=\"contents\" href=\""
       
  1699                   << anchorPair.first << "\" />\n";
       
  1700 
       
  1701             navigationLinks += "[<a href=\"" + anchorPair.first + "\">";
       
  1702             if (linkPair.first == linkPair.second && !anchorPair.second.isEmpty())
       
  1703                 navigationLinks += protect(anchorPair.second);
       
  1704             else
       
  1705                 navigationLinks += protect(linkPair.second);
       
  1706             navigationLinks += "</a>]\n";
       
  1707         }
       
  1708         if (node->links().contains(Node::NextLink)) {
       
  1709             linkPair = node->links()[Node::NextLink];
       
  1710             linkNode = findNodeForTarget(linkPair.first, node, marker);
       
  1711             if (!linkNode || linkNode == node)
       
  1712                 anchorPair = linkPair;
       
  1713             else
       
  1714                 anchorPair = anchorForNode(linkNode);
       
  1715 
       
  1716             out() << "  <link rel=\"next\" href=\""
       
  1717                   << anchorPair.first << "\" />\n";
       
  1718 
       
  1719             navigationLinks += "[Next: <a href=\"" + anchorPair.first + "\">";
       
  1720             if (linkPair.first == linkPair.second && !anchorPair.second.isEmpty())
       
  1721                 navigationLinks += protect(anchorPair.second);
       
  1722             else
       
  1723                 navigationLinks += protect(linkPair.second);
       
  1724             navigationLinks += "</a>]\n";
       
  1725         }
       
  1726         if (node->links().contains(Node::IndexLink)) {
       
  1727             linkPair = node->links()[Node::IndexLink];
       
  1728             linkNode = findNodeForTarget(linkPair.first, node, marker);
       
  1729             if (!linkNode || linkNode == node)
       
  1730                 anchorPair = linkPair;
       
  1731             else
       
  1732                 anchorPair = anchorForNode(linkNode);
       
  1733             out() << "  <link rel=\"index\" href=\""
       
  1734                   << anchorPair.first << "\" />\n";
       
  1735         }
       
  1736         if (node->links().contains(Node::StartLink)) {
       
  1737             linkPair = node->links()[Node::StartLink];
       
  1738             linkNode = findNodeForTarget(linkPair.first, node, marker);
       
  1739             if (!linkNode || linkNode == node)
       
  1740                 anchorPair = linkPair;
       
  1741             else
       
  1742                 anchorPair = anchorForNode(linkNode);
       
  1743             out() << "  <link rel=\"start\" href=\""
       
  1744                   << anchorPair.first << "\" />\n";
       
  1745         }
       
  1746     }
       
  1747 
       
  1748     foreach (const QString &stylesheet, stylesheets) {
       
  1749         out() << "  <link href=\"" << stylesheet << "\" rel=\"stylesheet\" "
       
  1750               << "type=\"text/css\" />\n";
       
  1751     }
       
  1752 
       
  1753     foreach (const QString &customHeadElement, customHeadElements) {
       
  1754         out() << "  " << customHeadElement << "\n";
       
  1755     }
       
  1756 
       
  1757     out() << "</head>\n"
       
  1758              "<body>\n";
       
  1759     if (mainPage)
       
  1760         generateMacRef(node, marker);
       
  1761     out() << QString(postHeader).replace("\\" + COMMAND_VERSION, myTree->version());
       
  1762 
       
  1763 
       
  1764     if (node && !node->links().empty())
       
  1765         out() << "<p>\n" << navigationLinks << "</p>\n";
       
  1766 }
       
  1767 
       
  1768 void HtmlGenerator::generateTitle(const QString& title,
       
  1769                                   const Text &subTitle,
       
  1770                                   SubTitleSize subTitleSize,
       
  1771                                   const Node *relative,
       
  1772                                   CodeMarker *marker)
       
  1773 {
       
  1774     out() << "<h1 class=\"title\">" << protect(title);
       
  1775     if (!subTitle.isEmpty()) {
       
  1776         out() << "<br />";
       
  1777         if (subTitleSize == SmallSubTitle)
       
  1778             out() << "<span class=\"small-subtitle\">";
       
  1779         else
       
  1780             out() << "<span class=\"subtitle\">";
       
  1781         generateText(subTitle, relative, marker);
       
  1782         out() << "</span>\n";
       
  1783     }
       
  1784     out() << "</h1>\n";
       
  1785 }
       
  1786 
       
  1787 void HtmlGenerator::generateFooter(const Node *node)
       
  1788 {
       
  1789     if (node && !node->links().empty())
       
  1790         out() << "<p>\n" << navigationLinks << "</p>\n";
       
  1791 
       
  1792     out() << QString(footer).replace("\\" + COMMAND_VERSION, myTree->version())
       
  1793           << QString(address).replace("\\" + COMMAND_VERSION, myTree->version())
       
  1794           << "</body>\n"
       
  1795              "</html>\n";
       
  1796 }
       
  1797 
       
  1798 void HtmlGenerator::generateBrief(const Node *node, CodeMarker *marker,
       
  1799                                   const Node *relative)
       
  1800 {
       
  1801     Text brief = node->doc().briefText();
       
  1802     if (!brief.isEmpty()) {
       
  1803         out() << "<p>";
       
  1804         generateText(brief, node, marker);
       
  1805         if (!relative || node == relative)
       
  1806             out() << " <a href=\"#";
       
  1807         else
       
  1808             out() << " <a href=\"" << linkForNode(node, relative) << "#";
       
  1809         out() << registerRef("details") << "\">More...</a></p>\n";
       
  1810     }
       
  1811 }
       
  1812 
       
  1813 void HtmlGenerator::generateIncludes(const InnerNode *inner, CodeMarker *marker)
       
  1814 {
       
  1815     if (!inner->includes().isEmpty()) {
       
  1816         out() << "<pre>"
       
  1817               << trimmedTrailing(highlightedCode(indent(codeIndent,
       
  1818                                                         marker->markedUpIncludes(inner->includes())),
       
  1819                                                  marker,inner))
       
  1820               << "</pre>";
       
  1821     }
       
  1822 }
       
  1823 
       
  1824 void HtmlGenerator::generateTableOfContents(const Node *node,
       
  1825                                             CodeMarker *marker,
       
  1826                                             Doc::SectioningUnit sectioningUnit,
       
  1827                                             int numColumns,
       
  1828                                             const Node *relative)
       
  1829 
       
  1830 {
       
  1831     if (!node->doc().hasTableOfContents())
       
  1832         return;
       
  1833     QList<Atom *> toc = node->doc().tableOfContents();
       
  1834     if (toc.isEmpty())
       
  1835         return;
       
  1836 
       
  1837     QString nodeName = "";
       
  1838     if (node != relative)
       
  1839         nodeName = node->name();
       
  1840 
       
  1841     QStringList sectionNumber;
       
  1842     int columnSize = 0;
       
  1843 
       
  1844     QString tdTag;
       
  1845     if (numColumns > 1) {
       
  1846         tdTag = "<td width=\"" + QString::number((100 + numColumns - 1) / numColumns) + "%\">";
       
  1847         out() << "<p><table class=\"toc\" width=\"100%\">\n<tr valign=\"top\">"
       
  1848               << tdTag << "\n";
       
  1849     }
       
  1850 
       
  1851     // disable nested links in table of contents
       
  1852     inContents = true;
       
  1853     inLink = true;
       
  1854 
       
  1855     for (int i = 0; i < toc.size(); ++i) {
       
  1856         Atom *atom = toc.at(i);
       
  1857 
       
  1858         int nextLevel = atom->string().toInt();
       
  1859         if (nextLevel > (int)sectioningUnit)
       
  1860             continue;
       
  1861 
       
  1862         if (sectionNumber.size() < nextLevel) {
       
  1863             do {
       
  1864                 out() << "<ul>";
       
  1865                 sectionNumber.append("1");
       
  1866             } while (sectionNumber.size() < nextLevel);
       
  1867         }
       
  1868         else {
       
  1869             while (sectionNumber.size() > nextLevel) {
       
  1870                 out() << "</ul>\n";
       
  1871                 sectionNumber.removeLast();
       
  1872             }
       
  1873             sectionNumber.last() = QString::number(sectionNumber.last().toInt() + 1);
       
  1874         }
       
  1875         int numAtoms;
       
  1876         Text headingText = Text::sectionHeading(atom);
       
  1877 
       
  1878         if (sectionNumber.size() == 1 && columnSize > toc.size() / numColumns) {
       
  1879             out() << "</ul></td>" << tdTag << "<ul>\n";
       
  1880             columnSize = 0;
       
  1881         }
       
  1882         out() << "<li>";
       
  1883         out() << "<a href=\""
       
  1884               << nodeName
       
  1885               << "#"
       
  1886               << Doc::canonicalTitle(headingText.toString())
       
  1887               << "\">";
       
  1888         generateAtomList(headingText.firstAtom(), node, marker, true, numAtoms);
       
  1889         out() << "</a></li>\n";
       
  1890 
       
  1891         ++columnSize;
       
  1892     }
       
  1893     while (!sectionNumber.isEmpty()) {
       
  1894         out() << "</ul>\n";
       
  1895         sectionNumber.removeLast();
       
  1896     }
       
  1897 
       
  1898     if (numColumns > 1)
       
  1899         out() << "</td></tr></table></p>\n";
       
  1900 
       
  1901     inContents = false;
       
  1902     inLink = false;
       
  1903 }
       
  1904 
       
  1905 #if 0
       
  1906 void HtmlGenerator::generateNavigationBar(const NavigationBar& bar,
       
  1907                                            const Node *node,
       
  1908                                            CodeMarker *marker)
       
  1909 {
       
  1910     if (bar.prev.begin() != 0 || bar.current.begin() != 0 ||
       
  1911          bar.next.begin() != 0) {
       
  1912         out() << "<p align=\"right\">";
       
  1913         if (bar.prev.begin() != 0) {
       
  1914 #if 0
       
  1915             out() << "[<a href=\"" << section.previousBaseName()
       
  1916                   << ".html\">Prev: ";
       
  1917             generateText(section.previousHeading(), node, marker);
       
  1918             out() << "</a>]\n";
       
  1919 #endif
       
  1920         }
       
  1921         if (bar.current.begin() != 0) {
       
  1922             out() << "[<a href=\"" << "home"
       
  1923                   << ".html\">Home</a>]\n";
       
  1924         }
       
  1925         if (bar.next.begin() != 0) {
       
  1926             out() << "[<a href=\"" << fileBase(node, bar.next)
       
  1927                   << ".html\">Next: ";
       
  1928             generateText(Text::sectionHeading(bar.next.begin()), node, marker);
       
  1929             out() << "</a>]\n";
       
  1930         }
       
  1931         out() << "</p>\n";
       
  1932     }
       
  1933 }
       
  1934 #endif
       
  1935 
       
  1936 QString HtmlGenerator::generateListOfAllMemberFile(const InnerNode *inner,
       
  1937                                                    CodeMarker *marker)
       
  1938 {
       
  1939     QList<Section> sections;
       
  1940     QList<Section>::ConstIterator s;
       
  1941 
       
  1942     sections = marker->sections(inner,
       
  1943                                 CodeMarker::SeparateList,
       
  1944                                 CodeMarker::Okay);
       
  1945     if (sections.isEmpty())
       
  1946         return QString();
       
  1947 
       
  1948     QString fileName = fileBase(inner) + "-members." + fileExtension(inner);
       
  1949     beginSubPage(inner->location(), fileName);
       
  1950     QString title = "List of All Members for " + inner->name();
       
  1951     generateHeader(title, inner, marker, false);
       
  1952     generateTitle(title, Text(), SmallSubTitle, inner, marker);
       
  1953     out() << "<p>This is the complete list of members for ";
       
  1954     generateFullName(inner, 0, marker);
       
  1955     out() << ", including inherited members.</p>\n";
       
  1956 
       
  1957     Section section = sections.first();
       
  1958     generateSectionList(section, 0, marker, CodeMarker::SeparateList);
       
  1959 
       
  1960     generateFooter();
       
  1961     endSubPage();
       
  1962     return fileName;
       
  1963 }
       
  1964 
       
  1965 QString HtmlGenerator::generateLowStatusMemberFile(const InnerNode *inner,
       
  1966                                                    CodeMarker *marker,
       
  1967                                                    CodeMarker::Status status)
       
  1968 {
       
  1969     QList<Section> sections = marker->sections(inner,
       
  1970                                                CodeMarker::Summary,
       
  1971                                                status);
       
  1972     QMutableListIterator<Section> j(sections);
       
  1973     while (j.hasNext()) {
       
  1974         if (j.next().members.size() == 0)
       
  1975             j.remove();
       
  1976     }
       
  1977     if (sections.isEmpty())
       
  1978         return QString();
       
  1979 
       
  1980     int i;
       
  1981 
       
  1982     QString title;
       
  1983     QString fileName;
       
  1984 
       
  1985     if (status == CodeMarker::Compat) {
       
  1986         title = "Qt 3 Support Members for " + inner->name();
       
  1987         fileName = fileBase(inner) + "-qt3." + fileExtension(inner);
       
  1988     }
       
  1989     else {
       
  1990         title = "Obsolete Members for " + inner->name();
       
  1991         fileName = fileBase(inner) + "-obsolete." + fileExtension(inner);
       
  1992     }
       
  1993 
       
  1994     beginSubPage(inner->location(), fileName);
       
  1995     generateHeader(title, inner, marker, false);
       
  1996     generateTitle(title, Text(), SmallSubTitle, inner, marker);
       
  1997 
       
  1998     if (status == CodeMarker::Compat) {
       
  1999         out() << "<p><b>The following class members are part of the "
       
  2000                  "<a href=\"qt3support.html\">Qt 3 support layer</a>.</b> "
       
  2001                  "They are provided to help you port old code to Qt 4. We advise against "
       
  2002                  "using them in new code.</p>\n";
       
  2003     }
       
  2004     else {
       
  2005         out() << "<p><b>The following class members are obsolete.</b> "
       
  2006               << "They are provided to keep old source code working. "
       
  2007               << "We strongly advise against using them in new code.</p>\n";
       
  2008     }
       
  2009 
       
  2010     out() << "<p><ul><li><a href=\""
       
  2011           << linkForNode(inner, 0) << "\">"
       
  2012           << protect(inner->name())
       
  2013           << " class reference</a></li></ul></p>\n";
       
  2014 
       
  2015     for (i = 0; i < sections.size(); ++i) {
       
  2016         out() << "<h2>" << protect(sections.at(i).name) << "</h2>\n";
       
  2017         generateSectionList(sections.at(i), inner, marker, CodeMarker::Summary);
       
  2018     }
       
  2019 
       
  2020     sections = marker->sections(inner, CodeMarker::Detailed, status);
       
  2021     for (i = 0; i < sections.size(); ++i) {
       
  2022         out() << "<hr />\n";
       
  2023         out() << "<h2>" << protect(sections.at(i).name) << "</h2>\n";
       
  2024 
       
  2025         NodeList::ConstIterator m = sections.at(i).members.begin();
       
  2026         while (m != sections.at(i).members.end()) {
       
  2027             if ((*m)->access() != Node::Private)
       
  2028                 generateDetailedMember(*m, inner, marker);
       
  2029             ++m;
       
  2030         }
       
  2031     }
       
  2032 
       
  2033     generateFooter();
       
  2034     endSubPage();
       
  2035     return fileName;
       
  2036 }
       
  2037 
       
  2038 void HtmlGenerator::generateClassHierarchy(const Node *relative,
       
  2039                                            CodeMarker *marker,
       
  2040                                            const QMap<QString,const Node*> &classMap)
       
  2041 {
       
  2042     if (classMap.isEmpty())
       
  2043         return;
       
  2044 
       
  2045     NodeMap topLevel;
       
  2046     NodeMap::ConstIterator c = classMap.begin();
       
  2047     while (c != classMap.end()) {
       
  2048         const ClassNode *classe = static_cast<const ClassNode *>(*c);
       
  2049         if (classe->baseClasses().isEmpty())
       
  2050             topLevel.insert(classe->name(), classe);
       
  2051         ++c;
       
  2052     }
       
  2053 
       
  2054     QStack<NodeMap > stack;
       
  2055     stack.push(topLevel);
       
  2056 
       
  2057     out() << "<ul>\n";
       
  2058     while (!stack.isEmpty()) {
       
  2059         if (stack.top().isEmpty()) {
       
  2060             stack.pop();
       
  2061             out() << "</ul>\n";
       
  2062         }
       
  2063         else {
       
  2064             const ClassNode *child =
       
  2065                 static_cast<const ClassNode *>(*stack.top().begin());
       
  2066             out() << "<li>";
       
  2067             generateFullName(child, relative, marker);
       
  2068             out() << "</li>\n";
       
  2069             stack.top().erase(stack.top().begin());
       
  2070 
       
  2071             NodeMap newTop;
       
  2072             foreach (const RelatedClass &d, child->derivedClasses()) {
       
  2073                 if (d.access != Node::Private)
       
  2074                     newTop.insert(d.node->name(), d.node);
       
  2075             }
       
  2076             if (!newTop.isEmpty()) {
       
  2077                 stack.push(newTop);
       
  2078                 out() << "<ul>\n";
       
  2079             }
       
  2080         }
       
  2081     }
       
  2082 }
       
  2083 
       
  2084 void HtmlGenerator::generateAnnotatedList(const Node *relative,
       
  2085                                           CodeMarker *marker,
       
  2086                                           const NodeMap &nodeMap)
       
  2087 {
       
  2088     out() << "<p><table width=\"100%\" class=\"annotated\" cellpadding=\"2\" "
       
  2089           << "cellspacing=\"1\" border=\"0\">\n";
       
  2090 
       
  2091     int row = 0;
       
  2092     foreach (const QString &name, nodeMap.keys()) {
       
  2093         const Node *node = nodeMap[name];
       
  2094 
       
  2095         if (node->status() == Node::Obsolete)
       
  2096             continue;
       
  2097 
       
  2098         if (++row % 2 == 1)
       
  2099             out() << "<tr valign=\"top\" class=\"odd\">";
       
  2100         else
       
  2101             out() << "<tr valign=\"top\" class=\"even\">";
       
  2102         out() << "<th>";
       
  2103         generateFullName(node, relative, marker);
       
  2104         out() << "</th>";
       
  2105 
       
  2106         if (!(node->type() == Node::Fake)) {
       
  2107             Text brief = node->doc().trimmedBriefText(name);
       
  2108             if (!brief.isEmpty()) {
       
  2109                 out() << "<td>";
       
  2110                 generateText(brief, node, marker);
       
  2111                 out() << "</td>";
       
  2112             }
       
  2113         }
       
  2114         else {
       
  2115             out() << "<td>";
       
  2116             out() << protect(node->doc().briefText().toString());
       
  2117             out() << "</td>";
       
  2118         }
       
  2119         out() << "</tr>\n";
       
  2120     }
       
  2121     out() << "</table></p>\n";
       
  2122 }
       
  2123 
       
  2124 /*!
       
  2125   This function finds the common prefix of the names of all
       
  2126   the classes in \a classMap and then generates a compact
       
  2127   list of the class names alphabetized on the part of the
       
  2128   name not including the common prefix. You can tell the
       
  2129   function to use \a comonPrefix as the common prefix, but
       
  2130   normally you let it figure it out itself by looking at
       
  2131   the name of the first and last classes in \a classMap.
       
  2132  */
       
  2133 void HtmlGenerator::generateCompactList(const Node *relative,
       
  2134                                         CodeMarker *marker,
       
  2135                                         const NodeMap &classMap,
       
  2136                                         QString commonPrefix)
       
  2137 {
       
  2138     const int NumParagraphs = 37; // '0' to '9', 'A' to 'Z', '_'
       
  2139     const int NumColumns = 4; // number of columns in the result
       
  2140 
       
  2141     if (classMap.isEmpty())
       
  2142         return;
       
  2143 
       
  2144     /*
       
  2145       If commonPrefix is not empty, then the caller knows what
       
  2146       the common prefix is and has passed it in, so just use that
       
  2147       one.
       
  2148      */
       
  2149     int commonPrefixLen = commonPrefix.length();
       
  2150     if (commonPrefixLen == 0) {
       
  2151         QString first;
       
  2152         QString last;
       
  2153         
       
  2154         /*
       
  2155           The caller didn't pass in a common prefix, so get the common
       
  2156           prefix by looking at the class names of the first and last
       
  2157           classes in the class map. Discard any namespace names and
       
  2158           just use the bare class names. For Qt, the prefix is "Q".
       
  2159 
       
  2160           Note that the algorithm used here to derive the common prefix
       
  2161           from the first and last classes in alphabetical order (QAccel
       
  2162           and QXtWidget in Qt 2.1), fails if either class name does not
       
  2163           begin with Q.
       
  2164         */
       
  2165 
       
  2166         NodeMap::const_iterator iter = classMap.begin();
       
  2167         while (iter != classMap.end()) {
       
  2168             if (!iter.key().contains("::")) {
       
  2169                 first = iter.key();
       
  2170                 break;
       
  2171             }
       
  2172             ++iter;
       
  2173         }
       
  2174 
       
  2175         if (first.isEmpty())
       
  2176             first = classMap.begin().key();
       
  2177 
       
  2178         iter = classMap.end();
       
  2179         while (iter != classMap.begin()) {
       
  2180             --iter;
       
  2181             if (!iter.key().contains("::")) {
       
  2182                 last = iter.key();
       
  2183                 break;
       
  2184             }
       
  2185         }
       
  2186 
       
  2187         if (last.isEmpty())
       
  2188             last = classMap.begin().key();
       
  2189 
       
  2190         if (classMap.size() > 1) {
       
  2191             while (commonPrefixLen < first.length() + 1 &&
       
  2192                    commonPrefixLen < last.length() + 1 &&
       
  2193                    first[commonPrefixLen] == last[commonPrefixLen])
       
  2194                 ++commonPrefixLen;
       
  2195         }
       
  2196 
       
  2197         commonPrefix = first.left(commonPrefixLen);
       
  2198     }
       
  2199 
       
  2200     /*
       
  2201       Divide the data into 37 paragraphs: 0, ..., 9, A, ..., Z,
       
  2202       underscore (_). QAccel will fall in paragraph 10 (A) and
       
  2203       QXtWidget in paragraph 33 (X). This is the only place where we
       
  2204       assume that NumParagraphs is 37. Each paragraph is a NodeMap.
       
  2205     */
       
  2206     NodeMap paragraph[NumParagraphs+1];
       
  2207     QString paragraphName[NumParagraphs+1];
       
  2208 
       
  2209     NodeMap::ConstIterator c = classMap.begin();
       
  2210     while (c != classMap.end()) {
       
  2211         QStringList pieces = c.key().split("::");
       
  2212         QString key;
       
  2213         int idx = commonPrefixLen;
       
  2214         if (!pieces.last().startsWith(commonPrefix))
       
  2215             idx = 0;
       
  2216         if (pieces.size() == 1)
       
  2217             key = pieces.last().mid(idx).toLower();
       
  2218         else
       
  2219             key = pieces.last().toLower();
       
  2220 
       
  2221         int paragraphNo = NumParagraphs - 1;
       
  2222 
       
  2223         if (key[0].digitValue() != -1) {
       
  2224             paragraphNo = key[0].digitValue();
       
  2225         }
       
  2226         else if (key[0] >= QLatin1Char('a') && key[0] <= QLatin1Char('z')) {
       
  2227             paragraphNo = 10 + key[0].unicode() - 'a';
       
  2228         }
       
  2229 
       
  2230         paragraphName[paragraphNo] = key[0].toUpper();
       
  2231         paragraph[paragraphNo].insert(key, c.value());
       
  2232         ++c;
       
  2233     }
       
  2234 
       
  2235     /*
       
  2236       Each paragraph j has a size: paragraph[j].count(). In the
       
  2237       discussion, we will assume paragraphs 0 to 5 will have sizes
       
  2238       3, 1, 4, 1, 5, 9.
       
  2239 
       
  2240       We now want to compute the paragraph offset. Paragraphs 0 to 6
       
  2241       start at offsets 0, 3, 4, 8, 9, 14, 23.
       
  2242     */
       
  2243     int paragraphOffset[NumParagraphs + 1];     // 37 + 1
       
  2244     int i, j, k;
       
  2245 
       
  2246     paragraphOffset[0] = 0;
       
  2247     for (j = 0; j < NumParagraphs; j++)         // j = 0..36
       
  2248         paragraphOffset[j + 1] = paragraphOffset[j] + paragraph[j].count();
       
  2249 
       
  2250     int firstOffset[NumColumns + 1];            // 4 + 1
       
  2251     int currentOffset[NumColumns];              // 4
       
  2252     int currentParagraphNo[NumColumns];         // 4
       
  2253     int currentOffsetInParagraph[NumColumns];   // 4
       
  2254 
       
  2255     int numRows = (classMap.count() + NumColumns - 1) / NumColumns;
       
  2256     int curParagNo = 0;
       
  2257 
       
  2258     for (i = 0; i < NumColumns; i++) {          // i = 0..3
       
  2259         firstOffset[i] = qMin(i * numRows, classMap.size());
       
  2260         currentOffset[i] = firstOffset[i];
       
  2261 
       
  2262         for (j = curParagNo; j < NumParagraphs; j++) {
       
  2263             if (paragraphOffset[j] > firstOffset[i])
       
  2264                 break;
       
  2265             if (paragraphOffset[j] <= firstOffset[i])
       
  2266                 curParagNo = j;
       
  2267         }
       
  2268         currentParagraphNo[i] = curParagNo;
       
  2269         currentOffsetInParagraph[i] = firstOffset[i] -
       
  2270                                       paragraphOffset[curParagNo];
       
  2271     }
       
  2272     firstOffset[NumColumns] = classMap.count();
       
  2273 
       
  2274     out() << "<p><table class=\"generic\" width=\"100%\">\n";
       
  2275     for (k = 0; k < numRows; k++) {
       
  2276         out() << "<tr>\n";
       
  2277         for (i = 0; i < NumColumns; i++) {
       
  2278             if (currentOffset[i] >= firstOffset[i + 1]) {
       
  2279                 // this column is finished
       
  2280                 out() << "<td>\n</td>\n";
       
  2281             }
       
  2282             else {
       
  2283                 while ((currentParagraphNo[i] < NumParagraphs) &&
       
  2284                        (currentOffsetInParagraph[i] == paragraph[currentParagraphNo[i]].count())) {
       
  2285                     ++currentParagraphNo[i];
       
  2286                     currentOffsetInParagraph[i] = 0;
       
  2287                 }
       
  2288 #if 0
       
  2289                 if (currentParagraphNo[i] >= NumParagraphs) {
       
  2290                     qDebug() << "### Internal error ###" << __FILE__ << __LINE__
       
  2291                              << currentParagraphNo[i] << NumParagraphs;
       
  2292                     currentParagraphNo[i] = NumParagraphs - 1;
       
  2293                 }
       
  2294 #endif
       
  2295                 out() << "<td align=\"right\">";
       
  2296                 if (currentOffsetInParagraph[i] == 0) {
       
  2297                     // start a new paragraph
       
  2298                     out() << "<b>"
       
  2299                           << paragraphName[currentParagraphNo[i]]
       
  2300                           << "&nbsp;</b>";
       
  2301                 }
       
  2302                 out() << "</td>\n";
       
  2303 
       
  2304                 out() << "<td>";
       
  2305                 if ((currentParagraphNo[i] < NumParagraphs) &&
       
  2306                     !paragraphName[currentParagraphNo[i]].isEmpty()) {
       
  2307                     NodeMap::Iterator it;
       
  2308                     it = paragraph[currentParagraphNo[i]].begin();
       
  2309                     for (j = 0; j < currentOffsetInParagraph[i]; j++)
       
  2310                         ++it;
       
  2311 
       
  2312                     // Previously, we used generateFullName() for this, but we
       
  2313                     // require some special formatting.
       
  2314                     out() << "<a href=\""
       
  2315                         << linkForNode(it.value(), relative)
       
  2316                         << "\">";
       
  2317                     QStringList pieces = fullName(it.value(), relative, marker).split("::");
       
  2318                     out() << protect(pieces.last());
       
  2319                     out() << "</a>";
       
  2320                     if (pieces.size() > 1) {
       
  2321                         out() << " (";
       
  2322                         generateFullName(it.value()->parent(), relative, marker);
       
  2323                         out() << ")";
       
  2324                     }
       
  2325                 }
       
  2326                 out() << "</td>\n";
       
  2327 
       
  2328                 currentOffset[i]++;
       
  2329                 currentOffsetInParagraph[i]++;
       
  2330             }
       
  2331         }
       
  2332         out() << "</tr>\n";
       
  2333     }
       
  2334     out() << "</table></p>\n";
       
  2335 }
       
  2336 
       
  2337 void HtmlGenerator::generateFunctionIndex(const Node *relative,
       
  2338                                           CodeMarker *marker)
       
  2339 {
       
  2340     out() << "<p align=\"center\"><font size=\"+1\"><b>";
       
  2341     for (int i = 0; i < 26; i++) {
       
  2342         QChar ch('a' + i);
       
  2343         out() << QString("<a href=\"#%1\">%2</a>&nbsp;").arg(ch).arg(ch.toUpper());
       
  2344     }
       
  2345     out() << "</b></font></p>\n";
       
  2346 
       
  2347     char nextLetter = 'a';
       
  2348     char currentLetter;
       
  2349 
       
  2350 #if 1
       
  2351     out() << "<ul>\n";
       
  2352 #endif
       
  2353     QMap<QString, NodeMap >::ConstIterator f = funcIndex.begin();
       
  2354     while (f != funcIndex.end()) {
       
  2355 #if 1
       
  2356         out() << "<li>";
       
  2357 #else
       
  2358         out() << "<p>";
       
  2359 #endif
       
  2360         out() << protect(f.key()) << ":";
       
  2361 
       
  2362         currentLetter = f.key()[0].unicode();
       
  2363         while (islower(currentLetter) && currentLetter >= nextLetter) {
       
  2364             out() << QString("<a name=\"%1\"></a>").arg(nextLetter);
       
  2365             nextLetter++;
       
  2366         }
       
  2367 
       
  2368         NodeMap::ConstIterator s = (*f).begin();
       
  2369         while (s != (*f).end()) {
       
  2370             out() << " ";
       
  2371             generateFullName((*s)->parent(), relative, marker, *s);
       
  2372             ++s;
       
  2373         }
       
  2374 #if 1
       
  2375         out() << "</li>";
       
  2376 #else
       
  2377         out() << "</p>";
       
  2378 #endif
       
  2379         out() << "\n";
       
  2380         ++f;
       
  2381     }
       
  2382 #if 1
       
  2383     out() << "</ul>\n";
       
  2384 #endif
       
  2385 }
       
  2386 
       
  2387 void HtmlGenerator::generateLegaleseList(const Node *relative,
       
  2388                                          CodeMarker *marker)
       
  2389 {
       
  2390     QMap<Text, const Node *>::ConstIterator it = legaleseTexts.begin();
       
  2391     while (it != legaleseTexts.end()) {
       
  2392         Text text = it.key();
       
  2393         out() << "<hr />\n";
       
  2394         generateText(text, relative, marker);
       
  2395         out() << "<ul>\n";
       
  2396         do {
       
  2397             out() << "<li>";
       
  2398             generateFullName(it.value(), relative, marker);
       
  2399             out() << "</li>\n";
       
  2400             ++it;
       
  2401         } while (it != legaleseTexts.end() && it.key() == text);
       
  2402         out() << "</ul>\n";
       
  2403     }
       
  2404 }
       
  2405 
       
  2406 /*void HtmlGenerator::generateSynopsis(const Node *node,
       
  2407                                      const Node *relative,
       
  2408                                      CodeMarker *marker,
       
  2409                                      CodeMarker::SynopsisStyle style)
       
  2410 {
       
  2411     QString marked = marker->markedUpSynopsis(node, relative, style);
       
  2412     QRegExp templateTag("(<[^@>]*>)");
       
  2413     if (marked.indexOf(templateTag) != -1) {
       
  2414         QString contents = protect(marked.mid(templateTag.pos(1),
       
  2415                                               templateTag.cap(1).length()));
       
  2416         marked.replace(templateTag.pos(1), templateTag.cap(1).length(),
       
  2417                         contents);
       
  2418     }
       
  2419     marked.replace(QRegExp("<@param>([a-z]+)_([1-9n])</@param>"),
       
  2420                    "<i>\\1<sub>\\2</sub></i>");
       
  2421     marked.replace("<@param>", "<i>");
       
  2422     marked.replace("</@param>", "</i>");
       
  2423 
       
  2424     if (style == CodeMarker::Summary)
       
  2425         marked.replace("@name>", "b>");
       
  2426 
       
  2427     if (style == CodeMarker::SeparateList) {
       
  2428         QRegExp extraRegExp("<@extra>.*</@extra>");
       
  2429         extraRegExp.setMinimal(true);
       
  2430         marked.replace(extraRegExp, "");
       
  2431     }
       
  2432     else {
       
  2433         marked.replace("<@extra>", "&nbsp;&nbsp;<tt>");
       
  2434         marked.replace("</@extra>", "</tt>");
       
  2435     }
       
  2436 
       
  2437     if (style != CodeMarker::Detailed) {
       
  2438         marked.replace("<@type>", "");
       
  2439         marked.replace("</@type>", "");
       
  2440     }
       
  2441     out() << highlightedCode(marked, marker, relative);
       
  2442 }*/
       
  2443 
       
  2444 #ifdef QDOC_QML
       
  2445 void HtmlGenerator::generateQmlItem(const Node *node,
       
  2446                                     const Node *relative,
       
  2447                                     CodeMarker *marker,
       
  2448                                     bool summary)
       
  2449 { 
       
  2450     QString marked = marker->markedUpQmlItem(node,summary);
       
  2451     QRegExp templateTag("(<[^@>]*>)");
       
  2452     if (marked.indexOf(templateTag) != -1) {
       
  2453         QString contents = protect(marked.mid(templateTag.pos(1),
       
  2454                                               templateTag.cap(1).length()));
       
  2455         marked.replace(templateTag.pos(1), templateTag.cap(1).length(),
       
  2456                         contents);
       
  2457     }
       
  2458     marked.replace(QRegExp("<@param>([a-z]+)_([1-9n])</@param>"),
       
  2459                    "<i>\\1<sub>\\2</sub></i>");
       
  2460     marked.replace("<@param>", "<i>");
       
  2461     marked.replace("</@param>", "</i>");
       
  2462 
       
  2463     if (summary)
       
  2464         marked.replace("@name>", "b>");
       
  2465 
       
  2466     marked.replace("<@extra>", "&nbsp;&nbsp;<tt>");
       
  2467     marked.replace("</@extra>", "</tt>");
       
  2468 
       
  2469     if (summary) {
       
  2470         marked.replace("<@type>", "");
       
  2471         marked.replace("</@type>", "");
       
  2472     }
       
  2473     out() << highlightedCode(marked, marker, relative);
       
  2474 }
       
  2475 #endif
       
  2476 
       
  2477 void HtmlGenerator::generateOverviewList(const Node *relative, CodeMarker * /* marker */)
       
  2478 {
       
  2479     QMap<const FakeNode *, QMap<QString, FakeNode *> > fakeNodeMap;
       
  2480     QMap<QString, const FakeNode *> groupTitlesMap;
       
  2481     QMap<QString, FakeNode *> uncategorizedNodeMap;
       
  2482     QRegExp singleDigit("\\b([0-9])\\b");
       
  2483 
       
  2484     const NodeList children = myTree->root()->childNodes();
       
  2485     foreach (Node *child, children) {
       
  2486         if (child->type() == Node::Fake && child != relative) {
       
  2487             FakeNode *fakeNode = static_cast<FakeNode *>(child);
       
  2488 
       
  2489             // Check whether the page is part of a group or is the group
       
  2490             // definition page.
       
  2491             QString group;
       
  2492             bool isGroupPage = false;
       
  2493             if (fakeNode->doc().metaCommandsUsed().contains("group")) {
       
  2494                 group = fakeNode->doc().metaCommandArgs("group")[0];
       
  2495                 isGroupPage = true;
       
  2496             }
       
  2497 
       
  2498             // there are too many examples; they would clutter the list
       
  2499             if (fakeNode->subType() == Node::Example)
       
  2500                 continue;
       
  2501 
       
  2502             // not interested either in individual (Qt Designer etc.) manual chapters
       
  2503             if (fakeNode->links().contains(Node::ContentsLink))
       
  2504                 continue;
       
  2505 
       
  2506             // Discard external nodes.
       
  2507             if (fakeNode->subType() == Node::ExternalPage)
       
  2508                 continue;
       
  2509 
       
  2510             QString sortKey = fakeNode->fullTitle().toLower();
       
  2511             if (sortKey.startsWith("the "))
       
  2512                 sortKey.remove(0, 4);
       
  2513             sortKey.replace(singleDigit, "0\\1");
       
  2514 
       
  2515             if (!group.isEmpty()) {
       
  2516                 if (isGroupPage) {
       
  2517                     // If we encounter a group definition page, we add all
       
  2518                     // the pages in that group to the list for that group.
       
  2519                     foreach (Node *member, fakeNode->groupMembers()) {
       
  2520                         if (member->type() != Node::Fake)
       
  2521                             continue;
       
  2522                         FakeNode *page = static_cast<FakeNode *>(member);
       
  2523                         if (page) {
       
  2524                             QString sortKey = page->fullTitle().toLower();
       
  2525                             if (sortKey.startsWith("the "))
       
  2526                                 sortKey.remove(0, 4);
       
  2527                             sortKey.replace(singleDigit, "0\\1");
       
  2528                             fakeNodeMap[const_cast<const FakeNode *>(fakeNode)].insert(sortKey, page);
       
  2529                             groupTitlesMap[fakeNode->fullTitle()] = const_cast<const FakeNode *>(fakeNode);
       
  2530                         }
       
  2531                     }
       
  2532                 }
       
  2533                 else if (!isGroupPage) {
       
  2534                     // If we encounter a page that belongs to a group then
       
  2535                     // we add that page to the list for that group.
       
  2536                     const FakeNode *groupNode = static_cast<const FakeNode *>(myTree->root()->findNode(group, Node::Fake));
       
  2537                     if (groupNode)
       
  2538                         fakeNodeMap[groupNode].insert(sortKey, fakeNode);
       
  2539                     //else
       
  2540                     //    uncategorizedNodeMap.insert(sortKey, fakeNode);
       
  2541                 }// else
       
  2542                 //    uncategorizedNodeMap.insert(sortKey, fakeNode);
       
  2543             }// else
       
  2544             //    uncategorizedNodeMap.insert(sortKey, fakeNode);
       
  2545         }
       
  2546     }
       
  2547 
       
  2548     // We now list all the pages found that belong to groups.
       
  2549     // If only certain pages were found for a group, but the definition page
       
  2550     // for that group wasn't listed, the list of pages will be intentionally
       
  2551     // incomplete. However, if the group definition page was listed, all the
       
  2552     // pages in that group are listed for completeness.
       
  2553 
       
  2554     if (!fakeNodeMap.isEmpty()) {
       
  2555         foreach (const QString &groupTitle, groupTitlesMap.keys()) {
       
  2556             const FakeNode *groupNode = groupTitlesMap[groupTitle];
       
  2557             out() << QString("<h3><a href=\"%1\">%2</a></h3>\n").arg(
       
  2558                         linkForNode(groupNode, relative)).arg(
       
  2559                         protect(groupNode->fullTitle()));
       
  2560 
       
  2561             if (fakeNodeMap[groupNode].count() == 0)
       
  2562                 continue;
       
  2563 
       
  2564             out() << "<ul>\n";
       
  2565 
       
  2566             foreach (const FakeNode *fakeNode, fakeNodeMap[groupNode]) {
       
  2567                 QString title = fakeNode->fullTitle();
       
  2568                 if (title.startsWith("The "))
       
  2569                     title.remove(0, 4);
       
  2570                 out() << "<li><a href=\"" << linkForNode(fakeNode, relative) << "\">"
       
  2571                       << protect(title) << "</a></li>\n";
       
  2572             }
       
  2573             out() << "</ul>\n";
       
  2574         }
       
  2575     }
       
  2576 
       
  2577     if (!uncategorizedNodeMap.isEmpty()) {
       
  2578         out() << QString("<h3>Miscellaneous</h3>\n");
       
  2579         out() << "<ul>\n";
       
  2580         foreach (const FakeNode *fakeNode, uncategorizedNodeMap) {
       
  2581             QString title = fakeNode->fullTitle();
       
  2582             if (title.startsWith("The "))
       
  2583                 title.remove(0, 4);
       
  2584             out() << "<li><a href=\"" << linkForNode(fakeNode, relative) << "\">"
       
  2585                   << protect(title) << "</a></li>\n";
       
  2586         }
       
  2587         out() << "</ul>\n";
       
  2588     }
       
  2589 }
       
  2590 
       
  2591 #ifdef QDOC_NAME_ALIGNMENT
       
  2592 void HtmlGenerator::generateSection(const NodeList& nl,
       
  2593                                     const Node *relative,
       
  2594                                     CodeMarker *marker,
       
  2595                                     CodeMarker::SynopsisStyle style)
       
  2596 {
       
  2597     bool name_alignment = true;
       
  2598     if (!nl.isEmpty()) {
       
  2599         bool twoColumn = false;
       
  2600         if (style == CodeMarker::SeparateList) {
       
  2601             name_alignment = false;
       
  2602             twoColumn = (nl.count() >= 16);
       
  2603         }
       
  2604         else if (nl.first()->type() == Node::Property) {
       
  2605             twoColumn = (nl.count() >= 5);
       
  2606             name_alignment = false;
       
  2607         }
       
  2608         if (name_alignment) {
       
  2609             out() << "<table class=\"alignedsummary\" border=\"0\" cellpadding=\"0\" "
       
  2610                   << "cellspacing=\"0\" width=\"100%\">\n";
       
  2611         }
       
  2612         else {
       
  2613             if (twoColumn)
       
  2614                 out() << "<p><table class=\"propsummary\" width=\"100%\" "
       
  2615                       << "border=\"0\" cellpadding=\"0\""
       
  2616                       << " cellspacing=\"0\">\n"
       
  2617                       << "<tr><td width=\"45%\" valign=\"top\">";
       
  2618             out() << "<ul>\n";
       
  2619         }
       
  2620 
       
  2621         int i = 0;
       
  2622         NodeList::ConstIterator m = nl.begin();
       
  2623         while (m != nl.end()) {
       
  2624             if ((*m)->access() == Node::Private) {
       
  2625                 ++m;
       
  2626                 continue;
       
  2627             }
       
  2628 
       
  2629             if (name_alignment) {
       
  2630                 out() << "<tr><td class=\"memItemLeft\" "
       
  2631                       << "align=\"right\" valign=\"top\">";
       
  2632             }
       
  2633             else {
       
  2634                 if (twoColumn && i == (int) (nl.count() + 1) / 2)
       
  2635                     out() << "</ul></td><td valign=\"top\"><ul>\n";
       
  2636                 out() << "<li><div class=\"fn\">";
       
  2637             }
       
  2638 
       
  2639             generateSynopsis(*m, relative, marker, style, name_alignment);
       
  2640             if (name_alignment)
       
  2641                 out() << "</td></tr>\n";
       
  2642             else
       
  2643                 out() << "</div></li>\n";
       
  2644             i++;
       
  2645             ++m;
       
  2646         }
       
  2647         if (name_alignment)
       
  2648             out() << "</table>\n";
       
  2649         else {
       
  2650             out() << "</ul>\n";
       
  2651             if (twoColumn)
       
  2652                 out() << "</td></tr>\n</table></p>\n";
       
  2653         }
       
  2654     }
       
  2655 }
       
  2656 
       
  2657 void HtmlGenerator::generateSectionList(const Section& section,
       
  2658                                         const Node *relative,
       
  2659                                         CodeMarker *marker,
       
  2660                                         CodeMarker::SynopsisStyle style)
       
  2661 {
       
  2662     bool name_alignment = true;
       
  2663     if (!section.members.isEmpty()) {
       
  2664         bool twoColumn = false;
       
  2665         if (style == CodeMarker::SeparateList) {
       
  2666             name_alignment = false;
       
  2667             twoColumn = (section.members.count() >= 16);
       
  2668         }
       
  2669         else if (section.members.first()->type() == Node::Property) {
       
  2670             twoColumn = (section.members.count() >= 5);
       
  2671             name_alignment = false;
       
  2672         }
       
  2673         if (name_alignment) {
       
  2674             out() << "<table class=\"alignedsummary\" border=\"0\" cellpadding=\"0\" "
       
  2675                   << "cellspacing=\"0\" width=\"100%\">\n";
       
  2676         }
       
  2677         else {
       
  2678             if (twoColumn)
       
  2679                 out() << "<p><table class=\"propsummary\" width=\"100%\" "
       
  2680                       << "border=\"0\" cellpadding=\"0\""
       
  2681                       << " cellspacing=\"0\">\n"
       
  2682                       << "<tr><td width=\"45%\" valign=\"top\">";
       
  2683             out() << "<ul>\n";
       
  2684         }
       
  2685 
       
  2686         int i = 0;
       
  2687         NodeList::ConstIterator m = section.members.begin();
       
  2688         while (m != section.members.end()) {
       
  2689             if ((*m)->access() == Node::Private) {
       
  2690                 ++m;
       
  2691                 continue;
       
  2692             }
       
  2693 
       
  2694             if (name_alignment) {
       
  2695                 out() << "<tr><td class=\"memItemLeft\" "
       
  2696                       << "align=\"right\" valign=\"top\">";
       
  2697             }
       
  2698             else {
       
  2699                 if (twoColumn && i == (int) (section.members.count() + 1) / 2)
       
  2700                     out() << "</ul></td><td valign=\"top\"><ul>\n";
       
  2701                 out() << "<li><div class=\"fn\">";
       
  2702             }
       
  2703 
       
  2704             generateSynopsis(*m, relative, marker, style, name_alignment);
       
  2705             if (name_alignment)
       
  2706                 out() << "</td></tr>\n";
       
  2707             else
       
  2708                 out() << "</div></li>\n";
       
  2709             i++;
       
  2710             ++m;
       
  2711         }
       
  2712         if (name_alignment)
       
  2713             out() << "</table>\n";
       
  2714         else {
       
  2715             out() << "</ul>\n";
       
  2716             if (twoColumn)
       
  2717                 out() << "</td></tr>\n</table></p>\n";
       
  2718         }
       
  2719     }
       
  2720 
       
  2721     if (style == CodeMarker::Summary && !section.inherited.isEmpty()) {
       
  2722         out() << "<ul>\n";
       
  2723         generateSectionInheritedList(section, relative, marker, name_alignment);
       
  2724         out() << "</ul>\n";
       
  2725     }
       
  2726 }
       
  2727 
       
  2728 void HtmlGenerator::generateSectionInheritedList(const Section& section,
       
  2729                                                  const Node *relative,
       
  2730                                                  CodeMarker *marker,
       
  2731                                                  bool nameAlignment)
       
  2732 {
       
  2733     QList<QPair<ClassNode *, int> >::ConstIterator p = section.inherited.begin();
       
  2734     while (p != section.inherited.end()) {
       
  2735         if (nameAlignment)
       
  2736             out() << "<li><div bar=\"2\" class=\"fn\"></div>";
       
  2737         else
       
  2738             out() << "<li><div class=\"fn\"></div>";
       
  2739         out() << (*p).second << " ";
       
  2740         if ((*p).second == 1) {
       
  2741             out() << section.singularMember;
       
  2742         }
       
  2743         else {
       
  2744             out() << section.pluralMember;
       
  2745         }
       
  2746         out() << " inherited from <a href=\"" << fileName((*p).first)
       
  2747               << "#" << HtmlGenerator::cleanRef(section.name.toLower()) << "\">"
       
  2748               << protect(marker->plainFullName((*p).first, relative))
       
  2749               << "</a></li>\n";
       
  2750         ++p;
       
  2751     }
       
  2752 }
       
  2753 
       
  2754 void HtmlGenerator::generateSynopsis(const Node *node,
       
  2755                                      const Node *relative,
       
  2756                                      CodeMarker *marker,
       
  2757                                      CodeMarker::SynopsisStyle style,
       
  2758                                      bool nameAlignment)
       
  2759 {
       
  2760     QString marked = marker->markedUpSynopsis(node, relative, style);
       
  2761     QRegExp templateTag("(<[^@>]*>)");
       
  2762     if (marked.indexOf(templateTag) != -1) {
       
  2763         QString contents = protect(marked.mid(templateTag.pos(1),
       
  2764                                               templateTag.cap(1).length()));
       
  2765         marked.replace(templateTag.pos(1), templateTag.cap(1).length(),
       
  2766                         contents);
       
  2767     }
       
  2768     marked.replace(QRegExp("<@param>([a-z]+)_([1-9n])</@param>"),
       
  2769                    "<i>\\1<sub>\\2</sub></i>");
       
  2770     marked.replace("<@param>", "<i>");
       
  2771     marked.replace("</@param>", "</i>");
       
  2772 
       
  2773     if (style == CodeMarker::Summary) {
       
  2774         marked.replace("<@name>", "");   // was "<b>"
       
  2775         marked.replace("</@name>", "");  // was "</b>"
       
  2776     }
       
  2777 
       
  2778     if (style == CodeMarker::SeparateList) {
       
  2779         QRegExp extraRegExp("<@extra>.*</@extra>");
       
  2780         extraRegExp.setMinimal(true);
       
  2781         marked.replace(extraRegExp, "");
       
  2782     } else {
       
  2783         marked.replace("<@extra>", "&nbsp;&nbsp;<tt>");
       
  2784         marked.replace("</@extra>", "</tt>");
       
  2785     }
       
  2786 
       
  2787     if (style != CodeMarker::Detailed) {
       
  2788         marked.replace("<@type>", "");
       
  2789         marked.replace("</@type>", "");
       
  2790     }
       
  2791     out() << highlightedCode(marked, marker, relative, style, nameAlignment);
       
  2792 }
       
  2793 
       
  2794 QString HtmlGenerator::highlightedCode(const QString& markedCode,
       
  2795                                        CodeMarker *marker,
       
  2796                                        const Node *relative,
       
  2797                                        CodeMarker::SynopsisStyle ,
       
  2798                                        bool nameAlignment)
       
  2799 {
       
  2800     QString src = markedCode;
       
  2801     QString html;
       
  2802     QStringRef arg;
       
  2803     QStringRef par1;
       
  2804 
       
  2805     const QChar charLangle = '<';
       
  2806     const QChar charAt = '@';
       
  2807 
       
  2808     // replace all <@link> tags: "(<@link node=\"([^\"]+)\">).*(</@link>)"
       
  2809     static const QString linkTag("link");
       
  2810     bool done = false;
       
  2811     for (int i = 0, n = src.size(); i < n;) {
       
  2812         if (src.at(i) == charLangle && src.at(i + 1).unicode() == '@') {
       
  2813             if (nameAlignment && !done) {// && (i != 0)) Why was this here?
       
  2814                 html += "</td><td class=\"memItemRight\" valign=\"bottom\">";
       
  2815                 done = true;
       
  2816             }
       
  2817             i += 2;
       
  2818             if (parseArg(src, linkTag, &i, n, &arg, &par1)) {
       
  2819                 html += "<b>";
       
  2820                 QString link = linkForNode(
       
  2821                     CodeMarker::nodeForString(par1.toString()), relative);
       
  2822                 addLink(link, arg, &html);
       
  2823                 html += "</b>";
       
  2824             }
       
  2825             else {
       
  2826                 html += charLangle;
       
  2827                 html += charAt;
       
  2828             }
       
  2829         }
       
  2830         else {
       
  2831             html += src.at(i++);
       
  2832         }
       
  2833     }
       
  2834 
       
  2835 
       
  2836     if (slow) {
       
  2837         // is this block ever used at all?
       
  2838         // replace all <@func> tags: "(<@func target=\"([^\"]*)\">)(.*)(</@func>)"
       
  2839         src = html;
       
  2840         html = QString();
       
  2841         static const QString funcTag("func");
       
  2842         for (int i = 0, n = src.size(); i < n;) {
       
  2843             if (src.at(i) == charLangle && src.at(i + 1) == charAt) {
       
  2844                 i += 2;
       
  2845                 if (parseArg(src, funcTag, &i, n, &arg, &par1)) {
       
  2846                     QString link = linkForNode(
       
  2847                             marker->resolveTarget(par1.toString(),
       
  2848                                                   myTree,
       
  2849                                                   relative),
       
  2850                             relative);
       
  2851                     addLink(link, arg, &html);
       
  2852                     par1 = QStringRef();
       
  2853                 }
       
  2854                 else {
       
  2855                     html += charLangle;
       
  2856                     html += charAt;
       
  2857                 }
       
  2858             }
       
  2859             else {
       
  2860                 html += src.at(i++);
       
  2861             }
       
  2862         }
       
  2863     }
       
  2864 
       
  2865     // replace all "(<@(type|headerfile|func)(?: +[^>]*)?>)(.*)(</@\\2>)" tags
       
  2866     src = html;
       
  2867     html = QString();
       
  2868     static const QString typeTags[] = { "type", "headerfile", "func" };
       
  2869     for (int i = 0, n = src.size(); i < n;) {
       
  2870         if (src.at(i) == charLangle && src.at(i + 1) == charAt) {
       
  2871             i += 2;
       
  2872             bool handled = false;
       
  2873             for (int k = 0; k != 3; ++k) {
       
  2874                 if (parseArg(src, typeTags[k], &i, n, &arg, &par1)) {
       
  2875                     par1 = QStringRef();
       
  2876                     QString link = linkForNode(
       
  2877                             marker->resolveTarget(arg.toString(), myTree, relative),
       
  2878                             relative);
       
  2879                     addLink(link, arg, &html);
       
  2880                     handled = true;
       
  2881                     break;
       
  2882                 }
       
  2883             }
       
  2884             if (!handled) {
       
  2885                 html += charLangle;
       
  2886                 html += charAt;
       
  2887             }
       
  2888         }
       
  2889         else {
       
  2890             html += src.at(i++);
       
  2891         }
       
  2892     }
       
  2893 
       
  2894     // replace all
       
  2895     // "<@comment>" -> "<span class=\"comment\">";
       
  2896     // "<@preprocessor>" -> "<span class=\"preprocessor\">";
       
  2897     // "<@string>" -> "<span class=\"string\">";
       
  2898     // "<@char>" -> "<span class=\"char\">";
       
  2899     // "</@(?:comment|preprocessor|string|char)>" -> "</span>"
       
  2900     src = html;
       
  2901     html = QString();
       
  2902     static const QString spanTags[] = {
       
  2903         "<@comment>",      "<span class=\"comment\">",
       
  2904         "<@preprocessor>", "<span class=\"preprocessor\">",
       
  2905         "<@string>",       "<span class=\"string\">",
       
  2906         "<@char>",         "<span class=\"char\">",
       
  2907         "</@comment>",     "</span>",
       
  2908         "</@preprocessor>","</span>",
       
  2909         "</@string>",      "</span>",
       
  2910         "</@char>",        "</span>"
       
  2911         // "<@char>",      "<font color=blue>",
       
  2912         // "</@char>",     "</font>",
       
  2913         // "<@func>",      "<font color=green>",
       
  2914         // "</@func>",     "</font>",
       
  2915         // "<@id>",        "<i>",
       
  2916         // "</@id>",       "</i>",
       
  2917         // "<@keyword>",   "<b>",
       
  2918         // "</@keyword>",  "</b>",
       
  2919         // "<@number>",    "<font color=yellow>",
       
  2920         // "</@number>",   "</font>",
       
  2921         // "<@op>",        "<b>",
       
  2922         // "</@op>",       "</b>",
       
  2923         // "<@param>",     "<i>",
       
  2924         // "</@param>",    "</i>",
       
  2925         // "<@string>",    "<font color=green>",
       
  2926         // "</@string>",  "</font>",
       
  2927     };
       
  2928     for (int i = 0, n = src.size(); i < n;) {
       
  2929         if (src.at(i) == charLangle) {
       
  2930             bool handled = false;
       
  2931             for (int k = 0; k != 8; ++k) {
       
  2932                 const QString & tag = spanTags[2 * k];
       
  2933                 if (tag == QStringRef(&src, i, tag.length())) {
       
  2934                     html += spanTags[2 * k + 1];
       
  2935                     i += tag.length();
       
  2936                     handled = true;
       
  2937                     break;
       
  2938                 }
       
  2939             }
       
  2940             if (!handled) {
       
  2941                 ++i;
       
  2942                 if (src.at(i) == charAt ||
       
  2943                     (src.at(i) == QLatin1Char('/') && src.at(i + 1) == charAt)) {
       
  2944                     // drop 'our' unknown tags (the ones still containing '@')
       
  2945                     while (i < n && src.at(i) != QLatin1Char('>'))
       
  2946                         ++i;
       
  2947                     ++i;
       
  2948                 }
       
  2949                 else {
       
  2950                     // retain all others
       
  2951                     html += charLangle;
       
  2952                 }
       
  2953             }
       
  2954         }
       
  2955         else {
       
  2956             html += src.at(i);
       
  2957             ++i;
       
  2958         }
       
  2959     }
       
  2960 
       
  2961     return html;
       
  2962 }
       
  2963 
       
  2964 #else
       
  2965 void HtmlGenerator::generateSectionList(const Section& section,
       
  2966                                         const Node *relative,
       
  2967                                         CodeMarker *marker,
       
  2968                                         CodeMarker::SynopsisStyle style)
       
  2969 {
       
  2970     if (!section.members.isEmpty()) {
       
  2971         bool twoColumn = false;
       
  2972         if (style == CodeMarker::SeparateList) {
       
  2973             twoColumn = (section.members.count() >= 16);
       
  2974         }
       
  2975         else if (section.members.first()->type() == Node::Property) {
       
  2976             twoColumn = (section.members.count() >= 5);
       
  2977         }
       
  2978         if (twoColumn)
       
  2979             out() << "<p><table class=\"generic\" width=\"100%\" border=\"0\" "
       
  2980                   << "cellpadding=\"0\" cellspacing=\"0\">\n"
       
  2981                   << "<tr><td width=\"45%\" valign=\"top\">";
       
  2982         out() << "<ul>\n";
       
  2983 
       
  2984         int i = 0;
       
  2985         NodeList::ConstIterator m = section.members.begin();
       
  2986         while (m != section.members.end()) {
       
  2987             if ((*m)->access() == Node::Private) {
       
  2988                 ++m;
       
  2989                 continue;
       
  2990             }
       
  2991 
       
  2992             if (twoColumn && i == (int) (section.members.count() + 1) / 2)
       
  2993                 out() << "</ul></td><td valign=\"top\"><ul>\n";
       
  2994 
       
  2995             out() << "<li><div class=\"fn\"></div>";
       
  2996             if (style == CodeMarker::Accessors)
       
  2997                 out() << "<b>";
       
  2998             generateSynopsis(*m, relative, marker, style);
       
  2999             if (style == CodeMarker::Accessors)
       
  3000                 out() << "</b>";
       
  3001             out() << "</li>\n";
       
  3002             i++;
       
  3003             ++m;
       
  3004         }
       
  3005         out() << "</ul>\n";
       
  3006         if (twoColumn)
       
  3007             out() << "</td></tr>\n</table></p>\n";
       
  3008     }
       
  3009 
       
  3010     if (style == CodeMarker::Summary && !section.inherited.isEmpty()) {
       
  3011         out() << "<ul>\n";
       
  3012         generateSectionInheritedList(section, relative, marker);
       
  3013         out() << "</ul>\n";
       
  3014     }
       
  3015 }
       
  3016 
       
  3017 void HtmlGenerator::generateSectionInheritedList(const Section& section,
       
  3018                                                  const Node *relative,
       
  3019                                                  CodeMarker *marker)
       
  3020 {
       
  3021     QList<QPair<ClassNode *, int> >::ConstIterator p = section.inherited.begin();
       
  3022     while (p != section.inherited.end()) {
       
  3023         out() << "<li><div bar=\"2\" class=\"fn\"></div>";
       
  3024         out() << (*p).second << " ";
       
  3025         if ((*p).second == 1) {
       
  3026             out() << section.singularMember;
       
  3027         } else {
       
  3028             out() << section.pluralMember;
       
  3029         }
       
  3030         out() << " inherited from <a href=\"" << fileName((*p).first)
       
  3031               << "#" << HtmlGenerator::cleanRef(section.name.toLower()) << "\">"
       
  3032               << protect(marker->plainFullName((*p).first, relative))
       
  3033               << "</a></li>\n";
       
  3034         ++p;
       
  3035     }
       
  3036 }
       
  3037 
       
  3038 void HtmlGenerator::generateSynopsis(const Node *node,
       
  3039                                      const Node *relative,
       
  3040                                      CodeMarker *marker,
       
  3041                                      CodeMarker::SynopsisStyle style)
       
  3042 {
       
  3043     QString marked = marker->markedUpSynopsis(node, relative, style);
       
  3044     QRegExp templateTag("(<[^@>]*>)");
       
  3045     if (marked.indexOf(templateTag) != -1) {
       
  3046         QString contents = protect(marked.mid(templateTag.pos(1),
       
  3047                                               templateTag.cap(1).length()));
       
  3048         marked.replace(templateTag.pos(1), templateTag.cap(1).length(),
       
  3049                         contents);
       
  3050     }
       
  3051     marked.replace(QRegExp("<@param>([a-z]+)_([1-9n])</@param>"), "<i>\\1<sub>\\2</sub></i>");
       
  3052     marked.replace("<@param>", "<i>");
       
  3053     marked.replace("</@param>", "</i>");
       
  3054 
       
  3055     if (style == CodeMarker::Summary)
       
  3056         marked.replace("@name>", "b>");
       
  3057 
       
  3058     if (style == CodeMarker::SeparateList) {
       
  3059         QRegExp extraRegExp("<@extra>.*</@extra>");
       
  3060         extraRegExp.setMinimal(true);
       
  3061         marked.replace(extraRegExp, "");
       
  3062     } else {
       
  3063         marked.replace("<@extra>", "&nbsp;&nbsp;<tt>");
       
  3064         marked.replace("</@extra>", "</tt>");
       
  3065     }
       
  3066 
       
  3067     if (style != CodeMarker::Detailed) {
       
  3068         marked.replace("<@type>", "");
       
  3069         marked.replace("</@type>", "");
       
  3070     }
       
  3071     out() << highlightedCode(marked, marker, relative);
       
  3072 }
       
  3073 
       
  3074 QString HtmlGenerator::highlightedCode(const QString& markedCode,
       
  3075                                        CodeMarker *marker,
       
  3076                                        const Node *relative)
       
  3077 {
       
  3078     QString src = markedCode;
       
  3079     QString html;
       
  3080     QStringRef arg;
       
  3081     QStringRef par1;
       
  3082 
       
  3083     const QChar charLangle = '<';
       
  3084     const QChar charAt = '@';
       
  3085 
       
  3086     // replace all <@link> tags: "(<@link node=\"([^\"]+)\">).*(</@link>)"
       
  3087     static const QString linkTag("link");
       
  3088     for (int i = 0, n = src.size(); i < n;) {
       
  3089         if (src.at(i) == charLangle && src.at(i + 1) == charAt) {
       
  3090             i += 2;
       
  3091             if (parseArg(src, linkTag, &i, n, &arg, &par1)) {
       
  3092                 const Node* node = CodeMarker::nodeForString(par1.toString());
       
  3093                 QString link = linkForNode(node, relative);
       
  3094                 addLink(link, arg, &html);
       
  3095             }
       
  3096             else {
       
  3097                 html += charLangle;
       
  3098                 html += charAt;
       
  3099             }
       
  3100         }
       
  3101         else {
       
  3102             html += src.at(i++);
       
  3103         }
       
  3104     }
       
  3105 
       
  3106     if (slow) {
       
  3107         // is this block ever used at all?
       
  3108         // replace all <@func> tags: "(<@func target=\"([^\"]*)\">)(.*)(</@func>)"
       
  3109         src = html;
       
  3110         html = QString();
       
  3111         static const QString funcTag("func");
       
  3112         for (int i = 0, n = src.size(); i < n;) {
       
  3113             if (src.at(i) == charLangle && src.at(i + 1) == charAt) {
       
  3114                 i += 2;
       
  3115                 if (parseArg(src, funcTag, &i, n, &arg, &par1)) {
       
  3116                     QString link = linkForNode(
       
  3117                             marker->resolveTarget(par1.toString(),
       
  3118                                                   myTree,
       
  3119                                                   relative),
       
  3120                             relative);
       
  3121                     addLink(link, arg, &html);
       
  3122                     par1 = QStringRef();
       
  3123                 }
       
  3124                 else {
       
  3125                     html += charLangle;
       
  3126                     html += charAt;
       
  3127                 }
       
  3128             }
       
  3129             else {
       
  3130                 html += src.at(i++);
       
  3131             }
       
  3132         }
       
  3133     }
       
  3134 
       
  3135     // replace all "(<@(type|headerfile|func)(?: +[^>]*)?>)(.*)(</@\\2>)" tags
       
  3136     src = html;
       
  3137     html = QString();
       
  3138     static const QString typeTags[] = { "type", "headerfile", "func" };
       
  3139     for (int i = 0, n = src.size(); i < n;) {
       
  3140         if (src.at(i) == charLangle && src.at(i + 1) == charAt) {
       
  3141             i += 2;
       
  3142             bool handled = false;
       
  3143             for (int k = 0; k != 3; ++k) {
       
  3144                 if (parseArg(src, typeTags[k], &i, n, &arg, &par1)) {
       
  3145                     par1 = QStringRef();
       
  3146                     QString link = linkForNode(
       
  3147                             marker->resolveTarget(arg.toString(), myTree, relative),
       
  3148                             relative);
       
  3149                     addLink(link, arg, &html);
       
  3150                     handled = true;
       
  3151                     break;
       
  3152                 }
       
  3153             }
       
  3154             if (!handled) {
       
  3155                 html += charLangle;
       
  3156                 html += charAt;
       
  3157             }
       
  3158         }
       
  3159         else {
       
  3160             html += src.at(i++);
       
  3161         }
       
  3162     }
       
  3163 
       
  3164     // replace all
       
  3165     // "<@comment>" -> "<span class=\"comment\">";
       
  3166     // "<@preprocessor>" -> "<span class=\"preprocessor\">";
       
  3167     // "<@string>" -> "<span class=\"string\">";
       
  3168     // "<@char>" -> "<span class=\"char\">";
       
  3169     // "</@(?:comment|preprocessor|string|char)>" -> "</span>"
       
  3170     src = html;
       
  3171     html = QString();
       
  3172     static const QString spanTags[] = {
       
  3173         "<@comment>",      "<span class=\"comment\">",
       
  3174         "<@preprocessor>", "<span class=\"preprocessor\">",
       
  3175         "<@string>",       "<span class=\"string\">",
       
  3176         "<@char>",         "<span class=\"char\">",
       
  3177         "</@comment>",     "</span>",
       
  3178         "</@preprocessor>","</span>",
       
  3179         "</@string>",      "</span>",
       
  3180         "</@char>",        "</span>"
       
  3181         // "<@char>",      "<font color=blue>",
       
  3182         // "</@char>",     "</font>",
       
  3183         // "<@func>",      "<font color=green>",
       
  3184         // "</@func>",     "</font>",
       
  3185         // "<@id>",        "<i>",
       
  3186         // "</@id>",       "</i>",
       
  3187         // "<@keyword>",   "<b>",
       
  3188         // "</@keyword>",  "</b>",
       
  3189         // "<@number>",    "<font color=yellow>",
       
  3190         // "</@number>",   "</font>",
       
  3191         // "<@op>",        "<b>",
       
  3192         // "</@op>",       "</b>",
       
  3193         // "<@param>",     "<i>",
       
  3194         // "</@param>",    "</i>",
       
  3195         // "<@string>",    "<font color=green>",
       
  3196         // "</@string>",  "</font>",
       
  3197     };
       
  3198     for (int i = 0, n = src.size(); i < n;) {
       
  3199         if (src.at(i) == charLangle) {
       
  3200             bool handled = false;
       
  3201             for (int k = 0; k != 8; ++k) {
       
  3202                 const QString & tag = spanTags[2 * k];
       
  3203                 if (tag == QStringRef(&src, i, tag.length())) {
       
  3204                     html += spanTags[2 * k + 1];
       
  3205                     i += tag.length();
       
  3206                     handled = true;
       
  3207                     break;
       
  3208                 }
       
  3209             }
       
  3210             if (!handled) {
       
  3211                 ++i;
       
  3212                 if (src.at(i) == charAt ||
       
  3213                     (src.at(i) == QLatin1Char('/') && src.at(i + 1) == charAt)) {
       
  3214                     // drop 'our' unknown tags (the ones still containing '@')
       
  3215                     while (i < n && src.at(i) != QLatin1Char('>'))
       
  3216                         ++i;
       
  3217                     ++i;
       
  3218                 }
       
  3219                 else {
       
  3220                     // retain all others
       
  3221                     html += charLangle;
       
  3222                 }
       
  3223             }
       
  3224         }
       
  3225         else {
       
  3226             html += src.at(i);
       
  3227             ++i;
       
  3228         }
       
  3229     }
       
  3230 
       
  3231     return html;
       
  3232 }
       
  3233 #endif
       
  3234 
       
  3235 void HtmlGenerator::generateLink(const Atom* atom,
       
  3236                                  const Node* /* relative */,
       
  3237                                  CodeMarker* marker)
       
  3238 {
       
  3239     static QRegExp camelCase("[A-Z][A-Z][a-z]|[a-z][A-Z0-9]|_");
       
  3240 
       
  3241     if (funcLeftParen.indexIn(atom->string()) != -1 && marker->recognizeLanguage("Cpp")) {
       
  3242         // hack for C++: move () outside of link
       
  3243         int k = funcLeftParen.pos(1);
       
  3244         out() << protect(atom->string().left(k));
       
  3245         if (link.isEmpty()) {
       
  3246             if (showBrokenLinks)
       
  3247                 out() << "</i>";
       
  3248         } else {
       
  3249             out() << "</a>";
       
  3250         }
       
  3251         inLink = false;
       
  3252         out() << protect(atom->string().mid(k));
       
  3253     } else if (marker->recognizeLanguage("Java")) {
       
  3254 	// hack for Java: remove () and use <tt> when appropriate
       
  3255         bool func = atom->string().endsWith("()");
       
  3256         bool tt = (func || atom->string().contains(camelCase));
       
  3257         if (tt)
       
  3258             out() << "<tt>";
       
  3259         if (func) {
       
  3260             out() << protect(atom->string().left(atom->string().length() - 2));
       
  3261         } else {
       
  3262             out() << protect(atom->string());
       
  3263         }
       
  3264         out() << "</tt>";
       
  3265     } else {
       
  3266         out() << protect(atom->string());
       
  3267     }
       
  3268 }
       
  3269 
       
  3270 QString HtmlGenerator::cleanRef(const QString& ref)
       
  3271 {
       
  3272     QString clean;
       
  3273 
       
  3274     if (ref.isEmpty())
       
  3275         return clean;
       
  3276 
       
  3277     clean.reserve(ref.size() + 20);
       
  3278     const QChar c = ref[0];
       
  3279     const uint u = c.unicode();
       
  3280 
       
  3281     if ((u >= 'a' && u <= 'z') ||
       
  3282          (u >= 'A' && u <= 'Z') ||
       
  3283          (u >= '0' && u <= '9')) {
       
  3284         clean += c;
       
  3285     } else if (u == '~') {
       
  3286         clean += "dtor.";
       
  3287     } else if (u == '_') {
       
  3288         clean += "underscore.";
       
  3289     } else {
       
  3290         clean += "A";
       
  3291     }
       
  3292 
       
  3293     for (int i = 1; i < (int) ref.length(); i++) {
       
  3294         const QChar c = ref[i];
       
  3295         const uint u = c.unicode();
       
  3296         if ((u >= 'a' && u <= 'z') ||
       
  3297              (u >= 'A' && u <= 'Z') ||
       
  3298              (u >= '0' && u <= '9') || u == '-' ||
       
  3299              u == '_' || u == ':' || u == '.') {
       
  3300             clean += c;
       
  3301         } else if (c.isSpace()) {
       
  3302             clean += "-";
       
  3303         } else if (u == '!') {
       
  3304             clean += "-not";
       
  3305         } else if (u == '&') {
       
  3306             clean += "-and";
       
  3307         } else if (u == '<') {
       
  3308             clean += "-lt";
       
  3309         } else if (u == '=') {
       
  3310             clean += "-eq";
       
  3311         } else if (u == '>') {
       
  3312             clean += "-gt";
       
  3313         } else if (u == '#') {
       
  3314             clean += "#";
       
  3315         } else {
       
  3316             clean += "-";
       
  3317             clean += QString::number((int)u, 16);
       
  3318         }
       
  3319     }
       
  3320     return clean;
       
  3321 }
       
  3322 
       
  3323 QString HtmlGenerator::registerRef(const QString& ref)
       
  3324 {
       
  3325     QString clean = HtmlGenerator::cleanRef(ref);
       
  3326 
       
  3327     for (;;) {
       
  3328         QString& prevRef = refMap[clean.toLower()];
       
  3329         if (prevRef.isEmpty()) {
       
  3330             prevRef = ref;
       
  3331             break;
       
  3332         } else if (prevRef == ref) {
       
  3333             break;
       
  3334         }
       
  3335         clean += "x";
       
  3336     }
       
  3337     return clean;
       
  3338 }
       
  3339 
       
  3340 QString HtmlGenerator::protect(const QString& string)
       
  3341 {
       
  3342 #define APPEND(x) \
       
  3343     if (html.isEmpty()) { \
       
  3344         html = string; \
       
  3345         html.truncate(i); \
       
  3346     } \
       
  3347     html += (x);
       
  3348 
       
  3349     QString html;
       
  3350     int n = string.length();
       
  3351 
       
  3352     for (int i = 0; i < n; ++i) {
       
  3353         QChar ch = string.at(i);
       
  3354 
       
  3355         if (ch == QLatin1Char('&')) {
       
  3356             APPEND("&amp;");
       
  3357         } else if (ch == QLatin1Char('<')) {
       
  3358             APPEND("&lt;");
       
  3359         } else if (ch == QLatin1Char('>')) {
       
  3360             APPEND("&gt;");
       
  3361         } else if (ch == QLatin1Char('"')) {
       
  3362             APPEND("&quot;");
       
  3363         } else if (ch.unicode() > 0x007F
       
  3364                    || (ch == QLatin1Char('*') && i + 1 < n && string.at(i) == QLatin1Char('/'))
       
  3365                    || (ch == QLatin1Char('.') && i > 2 && string.at(i - 2) == QLatin1Char('.'))) {
       
  3366             // we escape '*/' and the last dot in 'e.g.' and 'i.e.' for the Javadoc generator
       
  3367             APPEND("&#x");
       
  3368             html += QString::number(ch.unicode(), 16);
       
  3369             html += QLatin1Char(';');
       
  3370         } else {
       
  3371             if (!html.isEmpty())
       
  3372                 html += ch;
       
  3373         }
       
  3374     }
       
  3375 
       
  3376     if (!html.isEmpty())
       
  3377         return html;
       
  3378     return string;
       
  3379 
       
  3380 #undef APPEND
       
  3381 }
       
  3382 
       
  3383 QString HtmlGenerator::fileBase(const Node *node)
       
  3384 {
       
  3385     QString result;
       
  3386 
       
  3387     result = PageGenerator::fileBase(node);
       
  3388 
       
  3389     if (!node->isInnerNode()) {
       
  3390         switch (node->status()) {
       
  3391         case Node::Compat:
       
  3392             result += "-qt3";
       
  3393             break;
       
  3394         case Node::Obsolete:
       
  3395             result += "-obsolete";
       
  3396             break;
       
  3397         default:
       
  3398             ;
       
  3399         }
       
  3400     }
       
  3401     return result;
       
  3402 }
       
  3403 
       
  3404 #if 0
       
  3405 QString HtmlGenerator::fileBase(const Node *node,
       
  3406                                 const SectionIterator& section)
       
  3407 {
       
  3408     QStringList::ConstIterator s = section.sectionNumber().end();
       
  3409     QStringList::ConstIterator b = section.baseNameStack().end();
       
  3410 
       
  3411     QString suffix;
       
  3412     QString base = fileBase(node);
       
  3413 
       
  3414     while (s != section.sectionNumber().begin()) {
       
  3415         --s;
       
  3416         --b;
       
  3417         if (!(*b).isEmpty()) {
       
  3418             base = *b;
       
  3419             break;
       
  3420         }
       
  3421         suffix.prepend("-" + *s);
       
  3422     }
       
  3423     return base + suffix;
       
  3424 }
       
  3425 #endif
       
  3426 
       
  3427 QString HtmlGenerator::fileName(const Node *node)
       
  3428 {
       
  3429     if (node->type() == Node::Fake) {
       
  3430         if (static_cast<const FakeNode *>(node)->subType() == Node::ExternalPage)
       
  3431             return node->name();
       
  3432         if (static_cast<const FakeNode *>(node)->subType() == Node::Image)
       
  3433             return node->name();
       
  3434     }
       
  3435     return PageGenerator::fileName(node);
       
  3436 }
       
  3437 
       
  3438 QString HtmlGenerator::refForNode(const Node *node)
       
  3439 {
       
  3440     const FunctionNode *func;
       
  3441     const TypedefNode *typedeffe;
       
  3442     QString ref;
       
  3443 
       
  3444     switch (node->type()) {
       
  3445     case Node::Namespace:
       
  3446     case Node::Class:
       
  3447     default:
       
  3448         break;
       
  3449     case Node::Enum:
       
  3450         ref = node->name() + "-enum";
       
  3451         break;
       
  3452     case Node::Typedef:
       
  3453         typedeffe = static_cast<const TypedefNode *>(node);
       
  3454         if (typedeffe->associatedEnum()) {
       
  3455             return refForNode(typedeffe->associatedEnum());
       
  3456         }
       
  3457         else {
       
  3458             ref = node->name() + "-typedef";
       
  3459         }
       
  3460         break;
       
  3461     case Node::Function:
       
  3462         func = static_cast<const FunctionNode *>(node);
       
  3463         if (func->associatedProperty()) {
       
  3464             return refForNode(func->associatedProperty());
       
  3465         }
       
  3466         else {
       
  3467             ref = func->name();
       
  3468             if (func->overloadNumber() != 1)
       
  3469                 ref += "-" + QString::number(func->overloadNumber());
       
  3470         }
       
  3471         break;
       
  3472 #ifdef QDOC_QML        
       
  3473     case Node::Fake:
       
  3474         if (node->subType() != Node::QmlPropertyGroup)
       
  3475             break;
       
  3476     case Node::QmlProperty:
       
  3477 #endif        
       
  3478     case Node::Property:
       
  3479         ref = node->name() + "-prop";
       
  3480         break;
       
  3481 #ifdef QDOC_QML
       
  3482     case Node::QmlSignal:
       
  3483         ref = node->name() + "-signal";
       
  3484         break;
       
  3485     case Node::QmlMethod:
       
  3486         ref = node->name() + "-method";
       
  3487         break;
       
  3488 #endif        
       
  3489     case Node::Variable:
       
  3490         ref = node->name() + "-var";
       
  3491         break;
       
  3492     case Node::Target:
       
  3493         return protect(node->name());
       
  3494     }
       
  3495     return registerRef(ref);
       
  3496 }
       
  3497 
       
  3498 QString HtmlGenerator::linkForNode(const Node *node, const Node *relative)
       
  3499 {
       
  3500     QString link;
       
  3501     QString fn;
       
  3502     QString ref;
       
  3503 
       
  3504     if (node == 0 || node == relative)
       
  3505         return QString();
       
  3506     if (!node->url().isEmpty())
       
  3507         return node->url();
       
  3508     if (fileBase(node).isEmpty())
       
  3509         return QString();
       
  3510     if (node->access() == Node::Private)
       
  3511         return QString();
       
  3512 
       
  3513     fn = fileName(node);
       
  3514 /*    if (!node->url().isEmpty())
       
  3515         return fn;*/
       
  3516 #if 0
       
  3517     // ### reintroduce this test, without breaking .dcf files
       
  3518     if (fn != outFileName())
       
  3519 #endif
       
  3520     link += fn;
       
  3521 
       
  3522     if (!node->isInnerNode() || node->subType() == Node::QmlPropertyGroup) {
       
  3523         ref = refForNode(node);
       
  3524         if (relative && fn == fileName(relative) && ref == refForNode(relative))
       
  3525             return QString();
       
  3526 
       
  3527         link += "#";
       
  3528         link += ref;
       
  3529     }
       
  3530     return link;
       
  3531 }
       
  3532 
       
  3533 QString HtmlGenerator::refForAtom(Atom *atom, const Node * /* node */)
       
  3534 {
       
  3535     if (atom->type() == Atom::SectionLeft) {
       
  3536         return Doc::canonicalTitle(Text::sectionHeading(atom).toString());
       
  3537     }
       
  3538     else if (atom->type() == Atom::Target) {
       
  3539         return Doc::canonicalTitle(atom->string());
       
  3540     }
       
  3541     else {
       
  3542         return QString();
       
  3543     }
       
  3544 }
       
  3545 
       
  3546 void HtmlGenerator::generateFullName(const Node *apparentNode,
       
  3547                                      const Node *relative,
       
  3548                                      CodeMarker *marker,
       
  3549                                      const Node *actualNode)
       
  3550 {
       
  3551     if (actualNode == 0)
       
  3552         actualNode = apparentNode;
       
  3553     out() << "<a href=\"" << linkForNode(actualNode, relative);
       
  3554     if (true || relative == 0 || relative->status() != actualNode->status()) {
       
  3555         switch (actualNode->status()) {
       
  3556         case Node::Obsolete:
       
  3557             out() << "\" class=\"obsolete";
       
  3558             break;
       
  3559         case Node::Compat:
       
  3560             out() << "\" class=\"compat";
       
  3561             break;
       
  3562         default:
       
  3563             ;
       
  3564         }
       
  3565     }
       
  3566     out() << "\">";
       
  3567     out() << protect(fullName(apparentNode, relative, marker));
       
  3568     out() << "</a>";
       
  3569 }
       
  3570 
       
  3571 void HtmlGenerator::generateDetailedMember(const Node *node,
       
  3572                                            const InnerNode *relative,
       
  3573                                            CodeMarker *marker)
       
  3574 {
       
  3575     const EnumNode *enume;
       
  3576 
       
  3577     generateMacRef(node, marker);
       
  3578     if (node->type() == Node::Enum
       
  3579             && (enume = static_cast<const EnumNode *>(node))->flagsType()) {
       
  3580         generateMacRef(enume->flagsType(), marker);
       
  3581         out() << "<h3 class=\"flags\">";
       
  3582         out() << "<a name=\"" + refForNode(node) + "\"></a>";
       
  3583         generateSynopsis(enume, relative, marker, CodeMarker::Detailed);
       
  3584         out() << "<br />";
       
  3585         generateSynopsis(enume->flagsType(),
       
  3586                          relative,
       
  3587                          marker,
       
  3588                          CodeMarker::Detailed);
       
  3589         out() << "</h3>\n";
       
  3590     }
       
  3591     else {
       
  3592         out() << "<h3 class=\"fn\">";
       
  3593         out() << "<a name=\"" + refForNode(node) + "\"></a>";
       
  3594         generateSynopsis(node, relative, marker, CodeMarker::Detailed);
       
  3595         out() << "</h3>\n";
       
  3596     }
       
  3597 
       
  3598     generateStatus(node, marker);
       
  3599     generateBody(node, marker);
       
  3600     generateThreadSafeness(node, marker);
       
  3601     generateSince(node, marker);
       
  3602 
       
  3603     if (node->type() == Node::Property) {
       
  3604         const PropertyNode *property = static_cast<const PropertyNode *>(node);
       
  3605         Section section;
       
  3606 
       
  3607         section.members += property->getters();
       
  3608         section.members += property->setters();
       
  3609         section.members += property->resetters();
       
  3610 
       
  3611         if (!section.members.isEmpty()) {
       
  3612             out() << "<p><b>Access functions:</b></p>\n";
       
  3613             generateSectionList(section, node, marker, CodeMarker::Accessors);
       
  3614         }
       
  3615 
       
  3616         Section notifiers;
       
  3617         notifiers.members += property->notifiers();
       
  3618         
       
  3619         if (!notifiers.members.isEmpty()) {
       
  3620             out() << "<p><b>Notifier signal:</b></p>\n";
       
  3621             //out() << "<p>This signal is emitted when the property value is changed.</p>\n";
       
  3622             generateSectionList(notifiers, node, marker, CodeMarker::Accessors);
       
  3623         }
       
  3624     }
       
  3625     else if (node->type() == Node::Enum) {
       
  3626         const EnumNode *enume = static_cast<const EnumNode *>(node);
       
  3627         if (enume->flagsType()) {
       
  3628             out() << "<p>The " << protect(enume->flagsType()->name())
       
  3629                   << " type is a typedef for "
       
  3630                   << "<a href=\"qflags.html\">QFlags</a>&lt;"
       
  3631                   << protect(enume->name())
       
  3632                   << "&gt;. It stores an OR combination of "
       
  3633                   << protect(enume->name())
       
  3634                   << " values.</p>\n";
       
  3635         }
       
  3636     }
       
  3637     generateAlsoList(node, marker);
       
  3638 }
       
  3639 
       
  3640 void HtmlGenerator::findAllClasses(const InnerNode *node)
       
  3641 {
       
  3642     NodeList::const_iterator c = node->childNodes().constBegin();
       
  3643     while (c != node->childNodes().constEnd()) {
       
  3644         if ((*c)->access() != Node::Private && (*c)->url().isEmpty()) {
       
  3645             if ((*c)->type() == Node::Class && !(*c)->doc().isEmpty()) {
       
  3646                 QString className = (*c)->name();
       
  3647                 if ((*c)->parent() &&
       
  3648                     (*c)->parent()->type() == Node::Namespace &&
       
  3649                     !(*c)->parent()->name().isEmpty())
       
  3650                     className = (*c)->parent()->name()+"::"+className;
       
  3651 
       
  3652                 if (!(static_cast<const ClassNode *>(*c))->hideFromMainList()) {
       
  3653                     if ((*c)->status() == Node::Compat) {
       
  3654                         compatClasses.insert(className, *c);
       
  3655                     }
       
  3656                     else if ((*c)->status() == Node::Obsolete) {
       
  3657                         obsoleteClasses.insert(className, *c);
       
  3658                     }
       
  3659                     else {
       
  3660                         nonCompatClasses.insert(className, *c);
       
  3661                         if ((*c)->status() == Node::Main)
       
  3662                             mainClasses.insert(className, *c);
       
  3663                     }
       
  3664                 }
       
  3665 
       
  3666                 QString moduleName = (*c)->moduleName();
       
  3667                 if (moduleName == "Qt3SupportLight") {
       
  3668                     moduleClassMap[moduleName].insert((*c)->name(), *c);
       
  3669                     moduleName = "Qt3Support";
       
  3670                 }
       
  3671                 if (!moduleName.isEmpty())
       
  3672                     moduleClassMap[moduleName].insert((*c)->name(), *c);
       
  3673 
       
  3674                 QString serviceName =
       
  3675                     (static_cast<const ClassNode *>(*c))->serviceName();
       
  3676                 if (!serviceName.isEmpty())
       
  3677                     serviceClasses.insert(serviceName, *c);
       
  3678             }
       
  3679             else if ((*c)->isInnerNode()) {
       
  3680                 findAllClasses(static_cast<InnerNode *>(*c));
       
  3681             }
       
  3682         }
       
  3683         ++c;
       
  3684     }
       
  3685 }
       
  3686 
       
  3687 /*!
       
  3688   For generating the "New Classes... in 4.6" section on the
       
  3689   What's New in 4.6" page.
       
  3690  */
       
  3691 void HtmlGenerator::findAllSince(const InnerNode *node)
       
  3692 {
       
  3693     NodeList::const_iterator child = node->childNodes().constBegin();
       
  3694     while (child != node->childNodes().constEnd()) {
       
  3695         QString sinceVersion = (*child)->since();
       
  3696         if (((*child)->access() != Node::Private) && !sinceVersion.isEmpty()) {
       
  3697             NewSinceMaps::iterator nsmap = newSinceMaps.find(sinceVersion);
       
  3698             if (nsmap == newSinceMaps.end())
       
  3699                 nsmap = newSinceMaps.insert(sinceVersion,NodeMultiMap());
       
  3700             NewClassMaps::iterator ncmap = newClassMaps.find(sinceVersion);
       
  3701             if (ncmap == newClassMaps.end())
       
  3702                 ncmap = newClassMaps.insert(sinceVersion,NodeMap());
       
  3703  
       
  3704             if ((*child)->type() == Node::Function) {
       
  3705                 FunctionNode *func = static_cast<FunctionNode *>(*child);
       
  3706                 if ((func->status() > Node::Obsolete) &&
       
  3707                     (func->metaness() != FunctionNode::Ctor) &&
       
  3708                     (func->metaness() != FunctionNode::Dtor)) {
       
  3709                     nsmap.value().insert(func->name(),(*child));
       
  3710                 }
       
  3711             }
       
  3712             else if ((*child)->url().isEmpty()) {
       
  3713                 if ((*child)->type() == Node::Class && !(*child)->doc().isEmpty()) {
       
  3714                     QString className = (*child)->name();
       
  3715                     if ((*child)->parent() &&
       
  3716                         (*child)->parent()->type() == Node::Namespace &&
       
  3717                         !(*child)->parent()->name().isEmpty())
       
  3718                         className = (*child)->parent()->name()+"::"+className;
       
  3719                     nsmap.value().insert(className,(*child));
       
  3720                     ncmap.value().insert(className,(*child));
       
  3721                 }
       
  3722             }
       
  3723             else {
       
  3724                 QString name = (*child)->name();
       
  3725                 if ((*child)->parent() &&
       
  3726                     (*child)->parent()->type() == Node::Namespace &&
       
  3727                     !(*child)->parent()->name().isEmpty())
       
  3728                     name = (*child)->parent()->name()+"::"+name;
       
  3729                 nsmap.value().insert(name,(*child));
       
  3730             }
       
  3731             if ((*child)->isInnerNode()) {
       
  3732                 findAllSince(static_cast<InnerNode *>(*child));
       
  3733             }
       
  3734         }
       
  3735         ++child;
       
  3736     }
       
  3737 }
       
  3738 
       
  3739 #if 0
       
  3740     const QRegExp versionSeparator("[\\-\\.]");
       
  3741     const int minorIndex = version.indexOf(versionSeparator);
       
  3742     const int patchIndex = version.indexOf(versionSeparator, minorIndex+1);
       
  3743     version = version.left(patchIndex);
       
  3744 #endif
       
  3745 
       
  3746 void HtmlGenerator::findAllFunctions(const InnerNode *node)
       
  3747 {
       
  3748     NodeList::ConstIterator c = node->childNodes().begin();
       
  3749     while (c != node->childNodes().end()) {
       
  3750         if ((*c)->access() != Node::Private) {
       
  3751             if ((*c)->isInnerNode() && (*c)->url().isEmpty()) {
       
  3752                 findAllFunctions(static_cast<const InnerNode *>(*c));
       
  3753             }
       
  3754             else if ((*c)->type() == Node::Function) {
       
  3755                 const FunctionNode *func = static_cast<const FunctionNode *>(*c);
       
  3756                 if ((func->status() > Node::Obsolete) &&
       
  3757                     (func->metaness() != FunctionNode::Ctor) &&
       
  3758                     (func->metaness() != FunctionNode::Dtor)) {
       
  3759                     funcIndex[(*c)->name()].insert(myTree->fullDocumentName((*c)->parent()), *c);
       
  3760                 }
       
  3761             }
       
  3762         }
       
  3763         ++c;
       
  3764     }
       
  3765 }
       
  3766 
       
  3767 void HtmlGenerator::findAllLegaleseTexts(const InnerNode *node)
       
  3768 {
       
  3769     NodeList::ConstIterator c = node->childNodes().begin();
       
  3770     while (c != node->childNodes().end()) {
       
  3771         if ((*c)->access() != Node::Private) {
       
  3772             if (!(*c)->doc().legaleseText().isEmpty())
       
  3773                 legaleseTexts.insertMulti((*c)->doc().legaleseText(), *c);
       
  3774             if ((*c)->isInnerNode())
       
  3775                 findAllLegaleseTexts(static_cast<const InnerNode *>(*c));
       
  3776         }
       
  3777         ++c;
       
  3778     }
       
  3779 }
       
  3780 
       
  3781 void HtmlGenerator::findAllNamespaces(const InnerNode *node)
       
  3782 {
       
  3783     NodeList::ConstIterator c = node->childNodes().begin();
       
  3784     while (c != node->childNodes().end()) {
       
  3785         if ((*c)->access() != Node::Private) {
       
  3786             if ((*c)->isInnerNode() && (*c)->url().isEmpty()) {
       
  3787                 findAllNamespaces(static_cast<const InnerNode *>(*c));
       
  3788                 if ((*c)->type() == Node::Namespace) {
       
  3789                     const NamespaceNode *nspace = static_cast<const NamespaceNode *>(*c);
       
  3790                     // Ensure that the namespace's name is not empty (the root
       
  3791                     // namespace has no name).
       
  3792                     if (!nspace->name().isEmpty()) {
       
  3793                         namespaceIndex.insert(nspace->name(), *c);
       
  3794                         QString moduleName = (*c)->moduleName();
       
  3795                         if (moduleName == "Qt3SupportLight") {
       
  3796                             moduleNamespaceMap[moduleName].insert((*c)->name(), *c);
       
  3797                             moduleName = "Qt3Support";
       
  3798                         }
       
  3799                         if (!moduleName.isEmpty())
       
  3800                             moduleNamespaceMap[moduleName].insert((*c)->name(), *c);
       
  3801                     }
       
  3802                 }
       
  3803             }
       
  3804         }
       
  3805         ++c;
       
  3806     }
       
  3807 }
       
  3808 
       
  3809 #ifdef ZZZ_QDOC_QML
       
  3810 /*!
       
  3811   This function finds all the qml element nodes and
       
  3812   stores them in a map for later use.
       
  3813  */
       
  3814 void HtmlGenerator::findAllQmlClasses(const InnerNode *node)
       
  3815 {
       
  3816     NodeList::const_iterator c = node->childNodes().constBegin();
       
  3817     while (c != node->childNodes().constEnd()) {
       
  3818         if ((*c)->type() == Node::Fake) {
       
  3819             const FakeNode* fakeNode = static_cast<const FakeNode *>(*c);
       
  3820             if (fakeNode->subType() == Node::QmlClass) {
       
  3821                 const QmlClassNode* qmlNode =
       
  3822                     static_cast<const QmlClassNode*>(fakeNode);
       
  3823                 const Node* n = qmlNode->classNode();
       
  3824             }
       
  3825             qmlClasses.insert(fakeNode->name(),*c);
       
  3826         }
       
  3827         ++c;
       
  3828     }
       
  3829 }
       
  3830 #endif
       
  3831 
       
  3832 int HtmlGenerator::hOffset(const Node *node)
       
  3833 {
       
  3834     switch (node->type()) {
       
  3835     case Node::Namespace:
       
  3836     case Node::Class:
       
  3837         return 2;
       
  3838     case Node::Fake:
       
  3839         if (node->doc().briefText().isEmpty())
       
  3840             return 1;
       
  3841         else
       
  3842             return 2;
       
  3843     case Node::Enum:
       
  3844     case Node::Typedef:
       
  3845     case Node::Function:
       
  3846     case Node::Property:
       
  3847     default:
       
  3848         return 3;
       
  3849     }
       
  3850 }
       
  3851 
       
  3852 bool HtmlGenerator::isThreeColumnEnumValueTable(const Atom *atom)
       
  3853 {
       
  3854     while (atom != 0 && !(atom->type() == Atom::ListRight && atom->string() == ATOM_LIST_VALUE)) {
       
  3855         if (atom->type() == Atom::ListItemLeft && !matchAhead(atom, Atom::ListItemRight))
       
  3856             return true;
       
  3857         atom = atom->next();
       
  3858     }
       
  3859     return false;
       
  3860 }
       
  3861 
       
  3862 const Node *HtmlGenerator::findNodeForTarget(const QString &target,
       
  3863                                              const Node *relative,
       
  3864                                              CodeMarker *marker,
       
  3865                                              const Atom *atom)
       
  3866 {
       
  3867     const Node *node = 0;
       
  3868 
       
  3869     if (target.isEmpty()) {
       
  3870         node = relative;
       
  3871     }
       
  3872     else if (target.endsWith(".html")) {
       
  3873         node = myTree->root()->findNode(target, Node::Fake);
       
  3874     }
       
  3875     else if (marker) {
       
  3876         node = marker->resolveTarget(target, myTree, relative);
       
  3877         if (!node)
       
  3878             node = myTree->findFakeNodeByTitle(target);
       
  3879         if (!node && atom) {
       
  3880             node = myTree->findUnambiguousTarget(target,
       
  3881                 *const_cast<Atom**>(&atom));
       
  3882         }
       
  3883     }
       
  3884 
       
  3885     if (!node)
       
  3886         relative->doc().location().warning(tr("Cannot link to '%1'").arg(target));
       
  3887 
       
  3888     return node;
       
  3889 }
       
  3890 
       
  3891 const QPair<QString,QString> HtmlGenerator::anchorForNode(const Node *node)
       
  3892 {
       
  3893     QPair<QString,QString> anchorPair;
       
  3894 
       
  3895     anchorPair.first = PageGenerator::fileName(node);
       
  3896     if (node->type() == Node::Fake) {
       
  3897         const FakeNode *fakeNode = static_cast<const FakeNode*>(node);
       
  3898         anchorPair.second = fakeNode->title();
       
  3899     }
       
  3900 
       
  3901     return anchorPair;
       
  3902 }
       
  3903 
       
  3904 QString HtmlGenerator::getLink(const Atom *atom,
       
  3905                                const Node *relative,
       
  3906                                CodeMarker *marker,
       
  3907                                const Node** node)
       
  3908 {
       
  3909     QString link;
       
  3910     *node = 0;
       
  3911     inObsoleteLink = false;
       
  3912 
       
  3913     if (atom->string().contains(":") &&
       
  3914             (atom->string().startsWith("file:")
       
  3915              || atom->string().startsWith("http:")
       
  3916              || atom->string().startsWith("https:")
       
  3917              || atom->string().startsWith("ftp:")
       
  3918              || atom->string().startsWith("mailto:"))) {
       
  3919 
       
  3920         link = atom->string();
       
  3921     }
       
  3922     else {
       
  3923         QStringList path;
       
  3924         if (atom->string().contains('#')) {
       
  3925             path = atom->string().split('#');
       
  3926         }
       
  3927         else {
       
  3928             path.append(atom->string());
       
  3929         }
       
  3930 
       
  3931         Atom *targetAtom = 0;
       
  3932 
       
  3933         QString first = path.first().trimmed();
       
  3934         if (first.isEmpty()) {
       
  3935             *node = relative;
       
  3936         }
       
  3937         else if (first.endsWith(".html")) {
       
  3938             *node = myTree->root()->findNode(first, Node::Fake);
       
  3939         }
       
  3940         else {
       
  3941             *node = marker->resolveTarget(first, myTree, relative);
       
  3942             if (!*node)
       
  3943                 *node = myTree->findFakeNodeByTitle(first);
       
  3944             if (!*node)
       
  3945                 *node = myTree->findUnambiguousTarget(first, targetAtom);
       
  3946         }
       
  3947 
       
  3948         if (*node) {
       
  3949             if (!(*node)->url().isEmpty())
       
  3950                 return (*node)->url();
       
  3951             else
       
  3952                 path.removeFirst();
       
  3953         }
       
  3954         else {
       
  3955             *node = relative;
       
  3956         }
       
  3957 
       
  3958         if (*node) {
       
  3959             if ((*node)->status() == Node::Obsolete) {
       
  3960                 if (relative) {
       
  3961                     if (relative->parent() != *node) {
       
  3962                         if (relative->status() != Node::Obsolete) {
       
  3963                             bool porting = false;
       
  3964                             if (relative->type() == Node::Fake) {
       
  3965                                 const FakeNode* fake = static_cast<const FakeNode*>(relative);
       
  3966                                 if (fake->title().startsWith("Porting"))
       
  3967                                     porting = true;
       
  3968                             }
       
  3969                             QString name = marker->plainFullName(relative);
       
  3970                             if (!porting && !name.startsWith("Q3")) {
       
  3971                                 if (obsoleteLinks) {
       
  3972                                     relative->doc().location().warning(tr("Link to obsolete item '%1' in %2")
       
  3973                                                                        .arg(atom->string())
       
  3974                                                                        .arg(name));
       
  3975                                 }
       
  3976                                 inObsoleteLink = true;
       
  3977                             }
       
  3978                         }
       
  3979                     }
       
  3980                 }
       
  3981                 else {
       
  3982                     qDebug() << "Link to Obsolete entity"
       
  3983                              << (*node)->name() << "no relative";
       
  3984                 }
       
  3985             }
       
  3986 #if 0                    
       
  3987             else if ((*node)->status() == Node::Deprecated) {
       
  3988                 qDebug() << "Link to Deprecated entity";
       
  3989             }
       
  3990             else if ((*node)->status() == Node::Internal) {
       
  3991                 qDebug() << "Link to Internal entity";
       
  3992             }
       
  3993 #endif                
       
  3994         }
       
  3995 
       
  3996         while (!path.isEmpty()) {
       
  3997             targetAtom = myTree->findTarget(path.first(), *node);
       
  3998             if (targetAtom == 0)
       
  3999                 break;
       
  4000             path.removeFirst();
       
  4001         }
       
  4002 
       
  4003         if (path.isEmpty()) {
       
  4004             link = linkForNode(*node, relative);
       
  4005             if (*node && (*node)->subType() == Node::Image)
       
  4006                 link = "images/used-in-examples/" + link;
       
  4007             if (targetAtom)
       
  4008                 link += "#" + refForAtom(targetAtom, *node);
       
  4009         }
       
  4010     }
       
  4011     return link;
       
  4012 }
       
  4013 
       
  4014 void HtmlGenerator::generateDcf(const QString &fileBase,
       
  4015                                 const QString &startPage,
       
  4016                                 const QString &title,
       
  4017                                 DcfSection &dcfRoot)
       
  4018 {
       
  4019     dcfRoot.ref = startPage;
       
  4020     dcfRoot.title = title;
       
  4021     generateDcfSections(dcfRoot, outputDir() + "/" + fileBase + ".dcf", fileBase + "/reference");
       
  4022 }
       
  4023 
       
  4024 void HtmlGenerator::generateIndex(const QString &fileBase,
       
  4025                                   const QString &url,
       
  4026                                   const QString &title)
       
  4027 {
       
  4028     myTree->generateIndex(outputDir() + "/" + fileBase + ".index", url, title);
       
  4029 }
       
  4030 
       
  4031 void HtmlGenerator::generateStatus(const Node *node, CodeMarker *marker)
       
  4032 {
       
  4033     Text text;
       
  4034 
       
  4035     switch (node->status()) {
       
  4036     case Node::Obsolete:
       
  4037         if (node->isInnerNode())
       
  4038 	    Generator::generateStatus(node, marker);
       
  4039         break;
       
  4040     case Node::Compat:
       
  4041         if (node->isInnerNode()) {
       
  4042             text << Atom::ParaLeft
       
  4043                  << Atom(Atom::FormattingLeft,ATOM_FORMATTING_BOLD)
       
  4044                  << "This "
       
  4045                  << typeString(node)
       
  4046                  << " is part of the Qt 3 support library."
       
  4047                  << Atom(Atom::FormattingRight, ATOM_FORMATTING_BOLD)
       
  4048                  << " It is provided to keep old source code working. "
       
  4049                  << "We strongly advise against "
       
  4050                  << "using it in new code. See ";
       
  4051 
       
  4052             const FakeNode *fakeNode = myTree->findFakeNodeByTitle("Porting To Qt 4");
       
  4053             Atom *targetAtom = 0;
       
  4054             if (fakeNode && node->type() == Node::Class) {
       
  4055                 QString oldName(node->name());
       
  4056                 targetAtom = myTree->findTarget(oldName.replace("3", ""),
       
  4057                                                 fakeNode);
       
  4058             }
       
  4059 
       
  4060             if (targetAtom) {
       
  4061                 text << Atom(Atom::Link, linkForNode(fakeNode, node) + "#" +
       
  4062                                          refForAtom(targetAtom, fakeNode));
       
  4063             }
       
  4064             else
       
  4065                 text << Atom(Atom::Link, "Porting to Qt 4");
       
  4066 
       
  4067             text << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK)
       
  4068                  << Atom(Atom::String, "Porting to Qt 4")
       
  4069                  << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK)
       
  4070                  << " for more information."
       
  4071                  << Atom::ParaRight;
       
  4072         }
       
  4073         generateText(text, node, marker);
       
  4074         break;
       
  4075     default:
       
  4076         Generator::generateStatus(node, marker);
       
  4077     }
       
  4078 }
       
  4079 
       
  4080 void HtmlGenerator::generateMacRef(const Node *node, CodeMarker *marker)
       
  4081 {
       
  4082     if (!pleaseGenerateMacRef || marker == 0)
       
  4083         return;
       
  4084 
       
  4085     QStringList macRefs = marker->macRefsForNode(node);
       
  4086     foreach (const QString &macRef, macRefs)
       
  4087         out() << "<a name=\"" << "//apple_ref/" << macRef << "\"></a>\n";
       
  4088 }
       
  4089 
       
  4090 void HtmlGenerator::beginLink(const QString &link,
       
  4091                               const Node *node,
       
  4092                               const Node *relative,
       
  4093                               CodeMarker *marker)
       
  4094 {
       
  4095     Q_UNUSED(marker)
       
  4096     Q_UNUSED(relative)
       
  4097 
       
  4098     this->link = link;
       
  4099     if (link.isEmpty()) {
       
  4100         if (showBrokenLinks)
       
  4101             out() << "<i>";
       
  4102     }
       
  4103     else if (node == 0 || (relative != 0 &&
       
  4104                            node->status() == relative->status())) {
       
  4105         out() << "<a href=\"" << link << "\">";
       
  4106     }
       
  4107     else {
       
  4108         switch (node->status()) {
       
  4109         case Node::Obsolete:
       
  4110             out() << "<a href=\"" << link << "\" class=\"obsolete\">";
       
  4111             break;
       
  4112         case Node::Compat:
       
  4113             out() << "<a href=\"" << link << "\" class=\"compat\">";
       
  4114             break;
       
  4115         default:
       
  4116             out() << "<a href=\"" << link << "\">";
       
  4117         }
       
  4118     }
       
  4119     inLink = true;
       
  4120 }
       
  4121 
       
  4122 void HtmlGenerator::endLink()
       
  4123 {
       
  4124     if (inLink) {
       
  4125         if (link.isEmpty()) {
       
  4126             if (showBrokenLinks)
       
  4127                 out() << "</i>";
       
  4128         }
       
  4129         else {
       
  4130             if (inObsoleteLink) {
       
  4131                 out() << "<sup>(obsolete)</sup>";
       
  4132             }
       
  4133             out() << "</a>";
       
  4134         }
       
  4135     }
       
  4136     inLink = false;
       
  4137     inObsoleteLink = false;
       
  4138 }
       
  4139 
       
  4140 QT_END_NAMESPACE
       
  4141 
       
  4142 #ifdef QDOC_QML
       
  4143 
       
  4144 /*!
       
  4145   Generates the summary for for the \a section. Only used for
       
  4146   sections of QML element documentation.
       
  4147 
       
  4148   Currently handles only the QML property group.
       
  4149  */
       
  4150 void HtmlGenerator::generateQmlSummary(const Section& section,
       
  4151                                        const Node *relative,
       
  4152                                        CodeMarker *marker)
       
  4153 {
       
  4154     if (!section.members.isEmpty()) {
       
  4155         NodeList::ConstIterator m;
       
  4156         int count = section.members.size();
       
  4157         bool twoColumn = false;
       
  4158         if (section.members.first()->type() == Node::QmlProperty) {
       
  4159             twoColumn = (count >= 5);
       
  4160         }
       
  4161         if (twoColumn)
       
  4162             out() << "<p><table width=\"100%\" border=\"0\" cellpadding=\"0\""
       
  4163                      " cellspacing=\"0\">\n"
       
  4164                   << "<tr><td width=\"45%\" valign=\"top\">";
       
  4165         out() << "<ul>\n";
       
  4166 
       
  4167         int row = 0;
       
  4168         m = section.members.begin();
       
  4169         while (m != section.members.end()) {
       
  4170             if (twoColumn && row == (int) (count + 1) / 2)
       
  4171                 out() << "</ul></td><td valign=\"top\"><ul>\n";
       
  4172             out() << "<li><div class=\"fn\"></div>";
       
  4173             generateQmlItem(*m,relative,marker,true);
       
  4174             out() << "</li>\n";
       
  4175             row++;
       
  4176             ++m;
       
  4177         }
       
  4178         out() << "</ul>\n";
       
  4179         if (twoColumn)
       
  4180             out() << "</td></tr>\n</table></p>\n";
       
  4181     }
       
  4182 }
       
  4183 
       
  4184 /*!
       
  4185   Outputs the html detailed documentation for a section
       
  4186   on a QML element reference page.
       
  4187  */
       
  4188 void HtmlGenerator::generateDetailedQmlMember(const Node *node,
       
  4189                                               const InnerNode *relative,
       
  4190                                               CodeMarker *marker)
       
  4191 {
       
  4192     const QmlPropertyNode* qpn = 0;
       
  4193     generateMacRef(node, marker);
       
  4194     out() << "<div class=\"qmlitem\">";
       
  4195     if (node->subType() == Node::QmlPropertyGroup) {
       
  4196         const QmlPropGroupNode* qpgn = static_cast<const QmlPropGroupNode*>(node);
       
  4197         NodeList::ConstIterator p = qpgn->childNodes().begin();
       
  4198         out() << "<div class=\"qmlproto\">";
       
  4199         out() << "<table width=\"100%\" class=\"qmlname\">";
       
  4200 
       
  4201         while (p != qpgn->childNodes().end()) {
       
  4202             if ((*p)->type() == Node::QmlProperty) {
       
  4203                 qpn = static_cast<const QmlPropertyNode*>(*p);
       
  4204                 out() << "<tr><td>";
       
  4205                 out() << "<a name=\"" + refForNode(qpn) + "\"></a>";
       
  4206                 if (!qpn->isWritable())
       
  4207                     out() << "<span class=\"qmlreadonly\">read-only</span>";
       
  4208                 generateQmlItem(qpn, relative, marker, false);
       
  4209                 out() << "</td></tr>";
       
  4210                 if (qpgn->isDefault()) {
       
  4211                     out() << "</table>"
       
  4212                           << "</div></div>"
       
  4213                           << "<div class=\"qmlitem\">"
       
  4214                           << "<div class=\"qmlproto\">"
       
  4215                           << "<table class=\"qmlname\">"
       
  4216                           << "<tr><td><font color=\"green\">"
       
  4217                           << "default</font></td></tr>";
       
  4218                 }
       
  4219             }
       
  4220             ++p;
       
  4221         }
       
  4222         out() << "</table>";
       
  4223         out() << "</div>";
       
  4224     }
       
  4225     else if (node->type() == Node::QmlSignal) {
       
  4226         const FunctionNode* qsn = static_cast<const FunctionNode*>(node);
       
  4227         out() << "<div class=\"qmlproto\">";
       
  4228         out() << "<table class=\"qmlname\">";
       
  4229         out() << "<tr><td>";
       
  4230         out() << "<a name=\"" + refForNode(qsn) + "\"></a>";
       
  4231         generateSynopsis(qsn,relative,marker,CodeMarker::Detailed,false);
       
  4232         //generateQmlItem(qsn,relative,marker,false);
       
  4233         out() << "</td></tr>";
       
  4234         out() << "</table>";
       
  4235         out() << "</div>";
       
  4236     }
       
  4237     else if (node->type() == Node::QmlMethod) {
       
  4238         const FunctionNode* qmn = static_cast<const FunctionNode*>(node);
       
  4239         out() << "<div class=\"qmlproto\">";
       
  4240         out() << "<table class=\"qmlname\">";
       
  4241         out() << "<tr><td>";
       
  4242         out() << "<a name=\"" + refForNode(qmn) + "\"></a>";
       
  4243         generateSynopsis(qmn,relative,marker,CodeMarker::Detailed,false);
       
  4244         out() << "</td></tr>";
       
  4245         out() << "</table>";
       
  4246         out() << "</div>";
       
  4247     }
       
  4248     out() << "<div class=\"qmldoc\">";
       
  4249     generateStatus(node, marker);
       
  4250     generateBody(node, marker);
       
  4251     generateThreadSafeness(node, marker);
       
  4252     generateSince(node, marker);
       
  4253     generateAlsoList(node, marker);
       
  4254     out() << "</div>";
       
  4255     out() << "</div>";
       
  4256 }
       
  4257 
       
  4258 /*!
       
  4259   Output the "Inherits" line for the QML element,
       
  4260   if there should be one.
       
  4261  */
       
  4262 void HtmlGenerator::generateQmlInherits(const QmlClassNode* cn,
       
  4263                                         CodeMarker* marker)
       
  4264 {
       
  4265     if (cn && !cn->links().empty()) {
       
  4266         if (cn->links().contains(Node::InheritsLink)) {
       
  4267             QPair<QString,QString> linkPair;
       
  4268             linkPair = cn->links()[Node::InheritsLink];
       
  4269             QStringList strList(linkPair.first);
       
  4270             const Node* n = myTree->findNode(strList,Node::Fake);
       
  4271             if (n && n->subType() == Node::QmlClass) {
       
  4272                 const QmlClassNode* qcn = static_cast<const QmlClassNode*>(n);
       
  4273                 out() << "<p style=\"text-align: center\">";
       
  4274                 Text text;
       
  4275                 text << "[Inherits ";
       
  4276                 text << Atom(Atom::LinkNode,CodeMarker::stringForNode(qcn));
       
  4277                 text << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK);
       
  4278                 text << Atom(Atom::String, linkPair.second);
       
  4279                 text << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK);
       
  4280                 text << "]";
       
  4281                 generateText(text, cn, marker);
       
  4282                 out() << "</p>";
       
  4283             }
       
  4284         }
       
  4285     }
       
  4286 }
       
  4287 
       
  4288 /*!
       
  4289   Output the "[Xxx instantiates the C++ class QmlGraphicsXxx]"
       
  4290   line for the QML element, if there should be one.
       
  4291 
       
  4292   If there is no class node, or if the class node status
       
  4293   is set to Node::Internal, do nothing. 
       
  4294  */
       
  4295 void HtmlGenerator::generateQmlInstantiates(const QmlClassNode* qcn,
       
  4296                                             CodeMarker* marker)
       
  4297 {
       
  4298     const ClassNode* cn = qcn->classNode();
       
  4299     if (cn && (cn->status() != Node::Internal)) {
       
  4300         out() << "<p style=\"text-align: center\">";
       
  4301         Text text;
       
  4302         text << "[";
       
  4303         text << Atom(Atom::LinkNode,CodeMarker::stringForNode(qcn));
       
  4304         text << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK);
       
  4305         text << Atom(Atom::String, qcn->name());
       
  4306         text << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK);
       
  4307         text << " instantiates the C++ class ";
       
  4308         text << Atom(Atom::LinkNode,CodeMarker::stringForNode(cn));
       
  4309         text << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK);
       
  4310         text << Atom(Atom::String, cn->name());
       
  4311         text << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK);
       
  4312         text << "]";
       
  4313         generateText(text, qcn, marker);
       
  4314         out() << "</p>";
       
  4315     }
       
  4316 }
       
  4317 
       
  4318 /*!
       
  4319   Output the "[QmlGraphicsXxx is instantiated by QML element Xxx]"
       
  4320   line for the class, if there should be one.
       
  4321 
       
  4322   If there is no QML element, or if the class node status
       
  4323   is set to Node::Internal, do nothing. 
       
  4324  */
       
  4325 void HtmlGenerator::generateInstantiatedBy(const ClassNode* cn,
       
  4326                                            CodeMarker* marker)
       
  4327 {
       
  4328     if (cn &&  cn->status() != Node::Internal && !cn->qmlElement().isEmpty()) {
       
  4329         const Node* n = myTree->root()->findNode(cn->qmlElement(),Node::Fake);
       
  4330         if (n && n->subType() == Node::QmlClass) {
       
  4331             out() << "<p style=\"text-align: center\">";
       
  4332             Text text;
       
  4333             text << "[";
       
  4334             text << Atom(Atom::LinkNode,CodeMarker::stringForNode(cn));
       
  4335             text << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK);
       
  4336             text << Atom(Atom::String, cn->name());
       
  4337             text << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK);
       
  4338             text << " is instantiated by QML element ";
       
  4339             text << Atom(Atom::LinkNode,CodeMarker::stringForNode(n));
       
  4340             text << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK);
       
  4341             text << Atom(Atom::String, n->name());
       
  4342             text << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK);
       
  4343             text << "]";
       
  4344             generateText(text, cn, marker);
       
  4345             out() << "</p>";
       
  4346         }
       
  4347     }
       
  4348 }
       
  4349 
       
  4350 #endif