tools/qdoc3/htmlgenerator.cpp
changeset 0 1918ee327afb
child 3 41300fa6a67c
equal deleted inserted replaced
-1:000000000000 0:1918ee327afb
       
     1 /****************************************************************************
       
     2 **
       
     3 ** Copyright (C) 2009 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                 if ((currentParagraphNo[i] < NumParagraphs) &&
       
  2305                     !paragraphName[currentParagraphNo[i]].isEmpty()) {
       
  2306                     NodeMap::Iterator it;
       
  2307                     it = paragraph[currentParagraphNo[i]].begin();
       
  2308                     for (j = 0; j < currentOffsetInParagraph[i]; j++)
       
  2309                         ++it;
       
  2310 
       
  2311                     out() << "<td>";
       
  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                     out() << "</td>\n";
       
  2326                  }
       
  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     }
       
  3433     return PageGenerator::fileName(node);
       
  3434 }
       
  3435 
       
  3436 QString HtmlGenerator::refForNode(const Node *node)
       
  3437 {
       
  3438     const FunctionNode *func;
       
  3439     const TypedefNode *typedeffe;
       
  3440     QString ref;
       
  3441 
       
  3442     switch (node->type()) {
       
  3443     case Node::Namespace:
       
  3444     case Node::Class:
       
  3445     default:
       
  3446         break;
       
  3447     case Node::Enum:
       
  3448         ref = node->name() + "-enum";
       
  3449         break;
       
  3450     case Node::Typedef:
       
  3451         typedeffe = static_cast<const TypedefNode *>(node);
       
  3452         if (typedeffe->associatedEnum()) {
       
  3453             return refForNode(typedeffe->associatedEnum());
       
  3454         }
       
  3455         else {
       
  3456             ref = node->name() + "-typedef";
       
  3457         }
       
  3458         break;
       
  3459     case Node::Function:
       
  3460         func = static_cast<const FunctionNode *>(node);
       
  3461         if (func->associatedProperty()) {
       
  3462             return refForNode(func->associatedProperty());
       
  3463         }
       
  3464         else {
       
  3465             ref = func->name();
       
  3466             if (func->overloadNumber() != 1)
       
  3467                 ref += "-" + QString::number(func->overloadNumber());
       
  3468         }
       
  3469         break;
       
  3470     case Node::Property:
       
  3471 #ifdef QDOC_QML
       
  3472     case Node::QmlProperty:
       
  3473 #endif        
       
  3474         ref = node->name() + "-prop";
       
  3475         break;
       
  3476 #ifdef QDOC_QML
       
  3477     case Node::QmlSignal:
       
  3478         ref = node->name() + "-signal";
       
  3479         break;
       
  3480     case Node::QmlMethod:
       
  3481         ref = node->name() + "-method";
       
  3482         break;
       
  3483 #endif        
       
  3484     case Node::Variable:
       
  3485         ref = node->name() + "-var";
       
  3486         break;
       
  3487     case Node::Target:
       
  3488         return protect(node->name());
       
  3489     }
       
  3490     return registerRef(ref);
       
  3491 }
       
  3492 
       
  3493 QString HtmlGenerator::linkForNode(const Node *node, const Node *relative)
       
  3494 {
       
  3495     QString link;
       
  3496     QString fn;
       
  3497     QString ref;
       
  3498 
       
  3499     if (node == 0 || node == relative)
       
  3500         return QString();
       
  3501     if (!node->url().isEmpty())
       
  3502         return node->url();
       
  3503     if (fileBase(node).isEmpty())
       
  3504         return QString();
       
  3505     if (node->access() == Node::Private)
       
  3506         return QString();
       
  3507 
       
  3508     fn = fileName(node);
       
  3509 /*    if (!node->url().isEmpty())
       
  3510         return fn;*/
       
  3511 #if 0
       
  3512     // ### reintroduce this test, without breaking .dcf files
       
  3513     if (fn != outFileName())
       
  3514 #endif
       
  3515         link += fn;
       
  3516 
       
  3517     if (!node->isInnerNode()) {
       
  3518         ref = refForNode(node);
       
  3519         if (relative && fn == fileName(relative) && ref == refForNode(relative))
       
  3520             return QString();
       
  3521 
       
  3522         link += "#";
       
  3523         link += ref;
       
  3524     }
       
  3525     return link;
       
  3526 }
       
  3527 
       
  3528 QString HtmlGenerator::refForAtom(Atom *atom, const Node * /* node */)
       
  3529 {
       
  3530     if (atom->type() == Atom::SectionLeft) {
       
  3531         return Doc::canonicalTitle(Text::sectionHeading(atom).toString());
       
  3532     }
       
  3533     else if (atom->type() == Atom::Target) {
       
  3534         return Doc::canonicalTitle(atom->string());
       
  3535     }
       
  3536     else {
       
  3537         return QString();
       
  3538     }
       
  3539 }
       
  3540 
       
  3541 void HtmlGenerator::generateFullName(const Node *apparentNode,
       
  3542                                      const Node *relative,
       
  3543                                      CodeMarker *marker,
       
  3544                                      const Node *actualNode)
       
  3545 {
       
  3546     if (actualNode == 0)
       
  3547         actualNode = apparentNode;
       
  3548     out() << "<a href=\"" << linkForNode(actualNode, relative);
       
  3549     if (true || relative == 0 || relative->status() != actualNode->status()) {
       
  3550         switch (actualNode->status()) {
       
  3551         case Node::Obsolete:
       
  3552             out() << "\" class=\"obsolete";
       
  3553             break;
       
  3554         case Node::Compat:
       
  3555             out() << "\" class=\"compat";
       
  3556             break;
       
  3557         default:
       
  3558             ;
       
  3559         }
       
  3560     }
       
  3561     out() << "\">";
       
  3562     out() << protect(fullName(apparentNode, relative, marker));
       
  3563     out() << "</a>";
       
  3564 }
       
  3565 
       
  3566 void HtmlGenerator::generateDetailedMember(const Node *node,
       
  3567                                            const InnerNode *relative,
       
  3568                                            CodeMarker *marker)
       
  3569 {
       
  3570     const EnumNode *enume;
       
  3571 
       
  3572     generateMacRef(node, marker);
       
  3573     if (node->type() == Node::Enum
       
  3574             && (enume = static_cast<const EnumNode *>(node))->flagsType()) {
       
  3575         generateMacRef(enume->flagsType(), marker);
       
  3576         out() << "<h3 class=\"flags\">";
       
  3577         out() << "<a name=\"" + refForNode(node) + "\"></a>";
       
  3578         generateSynopsis(enume, relative, marker, CodeMarker::Detailed);
       
  3579         out() << "<br />";
       
  3580         generateSynopsis(enume->flagsType(),
       
  3581                          relative,
       
  3582                          marker,
       
  3583                          CodeMarker::Detailed);
       
  3584         out() << "</h3>\n";
       
  3585     }
       
  3586     else {
       
  3587         out() << "<h3 class=\"fn\">";
       
  3588         out() << "<a name=\"" + refForNode(node) + "\"></a>";
       
  3589         generateSynopsis(node, relative, marker, CodeMarker::Detailed);
       
  3590         out() << "</h3>\n";
       
  3591     }
       
  3592 
       
  3593     generateStatus(node, marker);
       
  3594     generateBody(node, marker);
       
  3595     generateThreadSafeness(node, marker);
       
  3596     generateSince(node, marker);
       
  3597 
       
  3598     if (node->type() == Node::Property) {
       
  3599         const PropertyNode *property = static_cast<const PropertyNode *>(node);
       
  3600         Section section;
       
  3601 
       
  3602         section.members += property->getters();
       
  3603         section.members += property->setters();
       
  3604         section.members += property->resetters();
       
  3605 
       
  3606         if (!section.members.isEmpty()) {
       
  3607             out() << "<p><b>Access functions:</b></p>\n";
       
  3608             generateSectionList(section, node, marker, CodeMarker::Accessors);
       
  3609         }
       
  3610 
       
  3611         Section notifiers;
       
  3612         notifiers.members += property->notifiers();
       
  3613         
       
  3614         if (!notifiers.members.isEmpty()) {
       
  3615             out() << "<p><b>Notifier signal:</b></p>\n";
       
  3616             //out() << "<p>This signal is emitted when the property value is changed.</p>\n";
       
  3617             generateSectionList(notifiers, node, marker, CodeMarker::Accessors);
       
  3618         }
       
  3619     }
       
  3620     else if (node->type() == Node::Enum) {
       
  3621         const EnumNode *enume = static_cast<const EnumNode *>(node);
       
  3622         if (enume->flagsType()) {
       
  3623             out() << "<p>The " << protect(enume->flagsType()->name())
       
  3624                   << " type is a typedef for "
       
  3625                   << "<a href=\"qflags.html\">QFlags</a>&lt;"
       
  3626                   << protect(enume->name())
       
  3627                   << "&gt;. It stores an OR combination of "
       
  3628                   << protect(enume->name())
       
  3629                   << " values.</p>\n";
       
  3630         }
       
  3631     }
       
  3632     generateAlsoList(node, marker);
       
  3633 }
       
  3634 
       
  3635 void HtmlGenerator::findAllClasses(const InnerNode *node)
       
  3636 {
       
  3637     NodeList::const_iterator c = node->childNodes().constBegin();
       
  3638     while (c != node->childNodes().constEnd()) {
       
  3639         if ((*c)->access() != Node::Private && (*c)->url().isEmpty()) {
       
  3640             if ((*c)->type() == Node::Class && !(*c)->doc().isEmpty()) {
       
  3641                 QString className = (*c)->name();
       
  3642                 if ((*c)->parent() &&
       
  3643                     (*c)->parent()->type() == Node::Namespace &&
       
  3644                     !(*c)->parent()->name().isEmpty())
       
  3645                     className = (*c)->parent()->name()+"::"+className;
       
  3646 
       
  3647                 if (!(static_cast<const ClassNode *>(*c))->hideFromMainList()) {
       
  3648                     if ((*c)->status() == Node::Compat) {
       
  3649                         compatClasses.insert(className, *c);
       
  3650                     }
       
  3651                     else if ((*c)->status() == Node::Obsolete) {
       
  3652                         obsoleteClasses.insert(className, *c);
       
  3653                     }
       
  3654                     else {
       
  3655                         nonCompatClasses.insert(className, *c);
       
  3656                         if ((*c)->status() == Node::Main)
       
  3657                             mainClasses.insert(className, *c);
       
  3658                     }
       
  3659                 }
       
  3660 
       
  3661                 QString moduleName = (*c)->moduleName();
       
  3662                 if (moduleName == "Qt3SupportLight") {
       
  3663                     moduleClassMap[moduleName].insert((*c)->name(), *c);
       
  3664                     moduleName = "Qt3Support";
       
  3665                 }
       
  3666                 if (!moduleName.isEmpty())
       
  3667                     moduleClassMap[moduleName].insert((*c)->name(), *c);
       
  3668 
       
  3669                 QString serviceName =
       
  3670                     (static_cast<const ClassNode *>(*c))->serviceName();
       
  3671                 if (!serviceName.isEmpty())
       
  3672                     serviceClasses.insert(serviceName, *c);
       
  3673             }
       
  3674             else if ((*c)->isInnerNode()) {
       
  3675                 findAllClasses(static_cast<InnerNode *>(*c));
       
  3676             }
       
  3677         }
       
  3678         ++c;
       
  3679     }
       
  3680 }
       
  3681 
       
  3682 /*!
       
  3683   For generating the "New Classes... in 4.6" section on the
       
  3684   What's New in 4.6" page.
       
  3685  */
       
  3686 void HtmlGenerator::findAllSince(const InnerNode *node)
       
  3687 {
       
  3688     NodeList::const_iterator child = node->childNodes().constBegin();
       
  3689     while (child != node->childNodes().constEnd()) {
       
  3690         QString sinceVersion = (*child)->since();
       
  3691         if (((*child)->access() != Node::Private) && !sinceVersion.isEmpty()) {
       
  3692             NewSinceMaps::iterator nsmap = newSinceMaps.find(sinceVersion);
       
  3693             if (nsmap == newSinceMaps.end())
       
  3694                 nsmap = newSinceMaps.insert(sinceVersion,NodeMultiMap());
       
  3695             NewClassMaps::iterator ncmap = newClassMaps.find(sinceVersion);
       
  3696             if (ncmap == newClassMaps.end())
       
  3697                 ncmap = newClassMaps.insert(sinceVersion,NodeMap());
       
  3698  
       
  3699             if ((*child)->type() == Node::Function) {
       
  3700                 FunctionNode *func = static_cast<FunctionNode *>(*child);
       
  3701                 if ((func->status() > Node::Obsolete) &&
       
  3702                     (func->metaness() != FunctionNode::Ctor) &&
       
  3703                     (func->metaness() != FunctionNode::Dtor)) {
       
  3704                     nsmap.value().insert(func->name(),(*child));
       
  3705                 }
       
  3706             }
       
  3707             else if ((*child)->url().isEmpty()) {
       
  3708                 if ((*child)->type() == Node::Class && !(*child)->doc().isEmpty()) {
       
  3709                     QString className = (*child)->name();
       
  3710                     if ((*child)->parent() &&
       
  3711                         (*child)->parent()->type() == Node::Namespace &&
       
  3712                         !(*child)->parent()->name().isEmpty())
       
  3713                         className = (*child)->parent()->name()+"::"+className;
       
  3714                     nsmap.value().insert(className,(*child));
       
  3715                     ncmap.value().insert(className,(*child));
       
  3716                 }
       
  3717             }
       
  3718             else {
       
  3719                 QString name = (*child)->name();
       
  3720                 if ((*child)->parent() &&
       
  3721                     (*child)->parent()->type() == Node::Namespace &&
       
  3722                     !(*child)->parent()->name().isEmpty())
       
  3723                     name = (*child)->parent()->name()+"::"+name;
       
  3724                 nsmap.value().insert(name,(*child));
       
  3725             }
       
  3726             if ((*child)->isInnerNode()) {
       
  3727                 findAllSince(static_cast<InnerNode *>(*child));
       
  3728             }
       
  3729         }
       
  3730         ++child;
       
  3731     }
       
  3732 }
       
  3733 
       
  3734 #if 0
       
  3735     const QRegExp versionSeparator("[\\-\\.]");
       
  3736     const int minorIndex = version.indexOf(versionSeparator);
       
  3737     const int patchIndex = version.indexOf(versionSeparator, minorIndex+1);
       
  3738     version = version.left(patchIndex);
       
  3739 #endif
       
  3740 
       
  3741 void HtmlGenerator::findAllFunctions(const InnerNode *node)
       
  3742 {
       
  3743     NodeList::ConstIterator c = node->childNodes().begin();
       
  3744     while (c != node->childNodes().end()) {
       
  3745         if ((*c)->access() != Node::Private) {
       
  3746             if ((*c)->isInnerNode() && (*c)->url().isEmpty()) {
       
  3747                 findAllFunctions(static_cast<const InnerNode *>(*c));
       
  3748             }
       
  3749             else if ((*c)->type() == Node::Function) {
       
  3750                 const FunctionNode *func = static_cast<const FunctionNode *>(*c);
       
  3751                 if ((func->status() > Node::Obsolete) &&
       
  3752                     (func->metaness() != FunctionNode::Ctor) &&
       
  3753                     (func->metaness() != FunctionNode::Dtor)) {
       
  3754                     funcIndex[(*c)->name()].insert(myTree->fullDocumentName((*c)->parent()), *c);
       
  3755                 }
       
  3756             }
       
  3757         }
       
  3758         ++c;
       
  3759     }
       
  3760 }
       
  3761 
       
  3762 void HtmlGenerator::findAllLegaleseTexts(const InnerNode *node)
       
  3763 {
       
  3764     NodeList::ConstIterator c = node->childNodes().begin();
       
  3765     while (c != node->childNodes().end()) {
       
  3766         if ((*c)->access() != Node::Private) {
       
  3767             if (!(*c)->doc().legaleseText().isEmpty())
       
  3768                 legaleseTexts.insertMulti((*c)->doc().legaleseText(), *c);
       
  3769             if ((*c)->isInnerNode())
       
  3770                 findAllLegaleseTexts(static_cast<const InnerNode *>(*c));
       
  3771         }
       
  3772         ++c;
       
  3773     }
       
  3774 }
       
  3775 
       
  3776 void HtmlGenerator::findAllNamespaces(const InnerNode *node)
       
  3777 {
       
  3778     NodeList::ConstIterator c = node->childNodes().begin();
       
  3779     while (c != node->childNodes().end()) {
       
  3780         if ((*c)->access() != Node::Private) {
       
  3781             if ((*c)->isInnerNode() && (*c)->url().isEmpty()) {
       
  3782                 findAllNamespaces(static_cast<const InnerNode *>(*c));
       
  3783                 if ((*c)->type() == Node::Namespace) {
       
  3784                     const NamespaceNode *nspace = static_cast<const NamespaceNode *>(*c);
       
  3785                     // Ensure that the namespace's name is not empty (the root
       
  3786                     // namespace has no name).
       
  3787                     if (!nspace->name().isEmpty()) {
       
  3788                         namespaceIndex.insert(nspace->name(), *c);
       
  3789                         QString moduleName = (*c)->moduleName();
       
  3790                         if (moduleName == "Qt3SupportLight") {
       
  3791                             moduleNamespaceMap[moduleName].insert((*c)->name(), *c);
       
  3792                             moduleName = "Qt3Support";
       
  3793                         }
       
  3794                         if (!moduleName.isEmpty())
       
  3795                             moduleNamespaceMap[moduleName].insert((*c)->name(), *c);
       
  3796                     }
       
  3797                 }
       
  3798             }
       
  3799         }
       
  3800         ++c;
       
  3801     }
       
  3802 }
       
  3803 
       
  3804 #ifdef ZZZ_QDOC_QML
       
  3805 /*!
       
  3806   This function finds all the qml element nodes and
       
  3807   stores them in a map for later use.
       
  3808  */
       
  3809 void HtmlGenerator::findAllQmlClasses(const InnerNode *node)
       
  3810 {
       
  3811     NodeList::const_iterator c = node->childNodes().constBegin();
       
  3812     while (c != node->childNodes().constEnd()) {
       
  3813         if ((*c)->type() == Node::Fake) {
       
  3814             const FakeNode* fakeNode = static_cast<const FakeNode *>(*c);
       
  3815             if (fakeNode->subType() == Node::QmlClass) {
       
  3816                 const QmlClassNode* qmlNode =
       
  3817                     static_cast<const QmlClassNode*>(fakeNode);
       
  3818                 const Node* n = qmlNode->classNode();
       
  3819             }
       
  3820             qmlClasses.insert(fakeNode->name(),*c);
       
  3821         }
       
  3822         ++c;
       
  3823     }
       
  3824 }
       
  3825 #endif
       
  3826 
       
  3827 int HtmlGenerator::hOffset(const Node *node)
       
  3828 {
       
  3829     switch (node->type()) {
       
  3830     case Node::Namespace:
       
  3831     case Node::Class:
       
  3832         return 2;
       
  3833     case Node::Fake:
       
  3834         if (node->doc().briefText().isEmpty())
       
  3835             return 1;
       
  3836         else
       
  3837             return 2;
       
  3838     case Node::Enum:
       
  3839     case Node::Typedef:
       
  3840     case Node::Function:
       
  3841     case Node::Property:
       
  3842     default:
       
  3843         return 3;
       
  3844     }
       
  3845 }
       
  3846 
       
  3847 bool HtmlGenerator::isThreeColumnEnumValueTable(const Atom *atom)
       
  3848 {
       
  3849     while (atom != 0 && !(atom->type() == Atom::ListRight && atom->string() == ATOM_LIST_VALUE)) {
       
  3850         if (atom->type() == Atom::ListItemLeft && !matchAhead(atom, Atom::ListItemRight))
       
  3851             return true;
       
  3852         atom = atom->next();
       
  3853     }
       
  3854     return false;
       
  3855 }
       
  3856 
       
  3857 const Node *HtmlGenerator::findNodeForTarget(const QString &target,
       
  3858                                              const Node *relative,
       
  3859                                              CodeMarker *marker,
       
  3860                                              const Atom *atom)
       
  3861 {
       
  3862     const Node *node = 0;
       
  3863 
       
  3864     if (target.isEmpty()) {
       
  3865         node = relative;
       
  3866     }
       
  3867     else if (target.endsWith(".html")) {
       
  3868         node = myTree->root()->findNode(target, Node::Fake);
       
  3869     }
       
  3870     else if (marker) {
       
  3871         node = marker->resolveTarget(target, myTree, relative);
       
  3872         if (!node)
       
  3873             node = myTree->findFakeNodeByTitle(target);
       
  3874         if (!node && atom) {
       
  3875             node = myTree->findUnambiguousTarget(target,
       
  3876                 *const_cast<Atom**>(&atom));
       
  3877         }
       
  3878     }
       
  3879 
       
  3880     if (!node)
       
  3881         relative->doc().location().warning(tr("Cannot link to '%1'").arg(target));
       
  3882 
       
  3883     return node;
       
  3884 }
       
  3885 
       
  3886 const QPair<QString,QString> HtmlGenerator::anchorForNode(const Node *node)
       
  3887 {
       
  3888     QPair<QString,QString> anchorPair;
       
  3889 
       
  3890     anchorPair.first = PageGenerator::fileName(node);
       
  3891     if (node->type() == Node::Fake) {
       
  3892         const FakeNode *fakeNode = static_cast<const FakeNode*>(node);
       
  3893         anchorPair.second = fakeNode->title();
       
  3894     }
       
  3895 
       
  3896     return anchorPair;
       
  3897 }
       
  3898 
       
  3899 QString HtmlGenerator::getLink(const Atom *atom,
       
  3900                                const Node *relative,
       
  3901                                CodeMarker *marker,
       
  3902                                const Node** node)
       
  3903 {
       
  3904     QString link;
       
  3905     *node = 0;
       
  3906     inObsoleteLink = false;
       
  3907 
       
  3908     if (atom->string().contains(":") &&
       
  3909             (atom->string().startsWith("file:")
       
  3910              || atom->string().startsWith("http:")
       
  3911              || atom->string().startsWith("https:")
       
  3912              || atom->string().startsWith("ftp:")
       
  3913              || atom->string().startsWith("mailto:"))) {
       
  3914 
       
  3915         link = atom->string();
       
  3916     }
       
  3917     else {
       
  3918         QStringList path;
       
  3919         if (atom->string().contains('#')) {
       
  3920             path = atom->string().split('#');
       
  3921         }
       
  3922         else {
       
  3923             path.append(atom->string());
       
  3924         }
       
  3925 
       
  3926         Atom *targetAtom = 0;
       
  3927 
       
  3928         QString first = path.first().trimmed();
       
  3929         if (first.isEmpty()) {
       
  3930             *node = relative;
       
  3931         }
       
  3932         else if (first.endsWith(".html")) {
       
  3933             *node = myTree->root()->findNode(first, Node::Fake);
       
  3934         }
       
  3935         else {
       
  3936             *node = marker->resolveTarget(first, myTree, relative);
       
  3937             if (!*node)
       
  3938                 *node = myTree->findFakeNodeByTitle(first);
       
  3939             if (!*node)
       
  3940                 *node = myTree->findUnambiguousTarget(first, targetAtom);
       
  3941         }
       
  3942 
       
  3943         if (*node) {
       
  3944             if (!(*node)->url().isEmpty())
       
  3945                 return (*node)->url();
       
  3946             else
       
  3947                 path.removeFirst();
       
  3948         }
       
  3949         else {
       
  3950             *node = relative;
       
  3951         }
       
  3952 
       
  3953         if (*node) {
       
  3954             if ((*node)->status() == Node::Obsolete) {
       
  3955                 if (relative) {
       
  3956                     if (relative->parent() != *node) {
       
  3957                         if (relative->status() != Node::Obsolete) {
       
  3958                             bool porting = false;
       
  3959                             if (relative->type() == Node::Fake) {
       
  3960                                 const FakeNode* fake = static_cast<const FakeNode*>(relative);
       
  3961                                 if (fake->title().startsWith("Porting"))
       
  3962                                     porting = true;
       
  3963                             }
       
  3964                             QString name = marker->plainFullName(relative);
       
  3965                             if (!porting && !name.startsWith("Q3")) {
       
  3966                                 if (obsoleteLinks) {
       
  3967                                     relative->doc().location().warning(tr("Link to obsolete item '%1' in %2")
       
  3968                                                                        .arg(atom->string())
       
  3969                                                                        .arg(name));
       
  3970                                 }
       
  3971                                 inObsoleteLink = true;
       
  3972                             }
       
  3973                         }
       
  3974                     }
       
  3975                 }
       
  3976                 else {
       
  3977                     qDebug() << "Link to Obsolete entity"
       
  3978                              << (*node)->name() << "no relative";
       
  3979                 }
       
  3980             }
       
  3981 #if 0                    
       
  3982             else if ((*node)->status() == Node::Deprecated) {
       
  3983                 qDebug() << "Link to Deprecated entity";
       
  3984             }
       
  3985             else if ((*node)->status() == Node::Internal) {
       
  3986                 qDebug() << "Link to Internal entity";
       
  3987             }
       
  3988 #endif                
       
  3989         }
       
  3990 
       
  3991         while (!path.isEmpty()) {
       
  3992             targetAtom = myTree->findTarget(path.first(), *node);
       
  3993             if (targetAtom == 0)
       
  3994                 break;
       
  3995             path.removeFirst();
       
  3996         }
       
  3997 
       
  3998         if (path.isEmpty()) {
       
  3999             link = linkForNode(*node, relative);
       
  4000             if (targetAtom)
       
  4001                 link += "#" + refForAtom(targetAtom, *node);
       
  4002         }
       
  4003     }
       
  4004     return link;
       
  4005 }
       
  4006 
       
  4007 void HtmlGenerator::generateDcf(const QString &fileBase,
       
  4008                                 const QString &startPage,
       
  4009                                 const QString &title,
       
  4010                                 DcfSection &dcfRoot)
       
  4011 {
       
  4012     dcfRoot.ref = startPage;
       
  4013     dcfRoot.title = title;
       
  4014     generateDcfSections(dcfRoot, outputDir() + "/" + fileBase + ".dcf", fileBase + "/reference");
       
  4015 }
       
  4016 
       
  4017 void HtmlGenerator::generateIndex(const QString &fileBase,
       
  4018                                   const QString &url,
       
  4019                                   const QString &title)
       
  4020 {
       
  4021     myTree->generateIndex(outputDir() + "/" + fileBase + ".index", url, title);
       
  4022 }
       
  4023 
       
  4024 void HtmlGenerator::generateStatus(const Node *node, CodeMarker *marker)
       
  4025 {
       
  4026     Text text;
       
  4027 
       
  4028     switch (node->status()) {
       
  4029     case Node::Obsolete:
       
  4030         if (node->isInnerNode())
       
  4031 	    Generator::generateStatus(node, marker);
       
  4032         break;
       
  4033     case Node::Compat:
       
  4034         if (node->isInnerNode()) {
       
  4035             text << Atom::ParaLeft
       
  4036                  << Atom(Atom::FormattingLeft,ATOM_FORMATTING_BOLD)
       
  4037                  << "This "
       
  4038                  << typeString(node)
       
  4039                  << " is part of the Qt 3 support library."
       
  4040                  << Atom(Atom::FormattingRight, ATOM_FORMATTING_BOLD)
       
  4041                  << " It is provided to keep old source code working. "
       
  4042                  << "We strongly advise against "
       
  4043                  << "using it in new code. See ";
       
  4044 
       
  4045             const FakeNode *fakeNode = myTree->findFakeNodeByTitle("Porting To Qt 4");
       
  4046             Atom *targetAtom = 0;
       
  4047             if (fakeNode && node->type() == Node::Class) {
       
  4048                 QString oldName(node->name());
       
  4049                 targetAtom = myTree->findTarget(oldName.replace("3", ""),
       
  4050                                                 fakeNode);
       
  4051             }
       
  4052 
       
  4053             if (targetAtom) {
       
  4054                 text << Atom(Atom::Link, linkForNode(fakeNode, node) + "#" +
       
  4055                                          refForAtom(targetAtom, fakeNode));
       
  4056             }
       
  4057             else
       
  4058                 text << Atom(Atom::Link, "Porting to Qt 4");
       
  4059 
       
  4060             text << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK)
       
  4061                  << Atom(Atom::String, "Porting to Qt 4")
       
  4062                  << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK)
       
  4063                  << " for more information."
       
  4064                  << Atom::ParaRight;
       
  4065         }
       
  4066         generateText(text, node, marker);
       
  4067         break;
       
  4068     default:
       
  4069         Generator::generateStatus(node, marker);
       
  4070     }
       
  4071 }
       
  4072 
       
  4073 void HtmlGenerator::generateMacRef(const Node *node, CodeMarker *marker)
       
  4074 {
       
  4075     if (!pleaseGenerateMacRef || marker == 0)
       
  4076         return;
       
  4077 
       
  4078     QStringList macRefs = marker->macRefsForNode(node);
       
  4079     foreach (const QString &macRef, macRefs)
       
  4080         out() << "<a name=\"" << "//apple_ref/" << macRef << "\" />\n";
       
  4081 }
       
  4082 
       
  4083 void HtmlGenerator::beginLink(const QString &link,
       
  4084                               const Node *node,
       
  4085                               const Node *relative,
       
  4086                               CodeMarker *marker)
       
  4087 {
       
  4088     Q_UNUSED(marker)
       
  4089     Q_UNUSED(relative)
       
  4090 
       
  4091     this->link = link;
       
  4092     if (link.isEmpty()) {
       
  4093         if (showBrokenLinks)
       
  4094             out() << "<i>";
       
  4095     }
       
  4096     else if (node == 0 || (relative != 0 &&
       
  4097                            node->status() == relative->status())) {
       
  4098         out() << "<a href=\"" << link << "\">";
       
  4099     }
       
  4100     else {
       
  4101         switch (node->status()) {
       
  4102         case Node::Obsolete:
       
  4103             out() << "<a href=\"" << link << "\" class=\"obsolete\">";
       
  4104             break;
       
  4105         case Node::Compat:
       
  4106             out() << "<a href=\"" << link << "\" class=\"compat\">";
       
  4107             break;
       
  4108         default:
       
  4109             out() << "<a href=\"" << link << "\">";
       
  4110         }
       
  4111     }
       
  4112     inLink = true;
       
  4113 }
       
  4114 
       
  4115 void HtmlGenerator::endLink()
       
  4116 {
       
  4117     if (inLink) {
       
  4118         if (link.isEmpty()) {
       
  4119             if (showBrokenLinks)
       
  4120                 out() << "</i>";
       
  4121         }
       
  4122         else {
       
  4123             if (inObsoleteLink) {
       
  4124                 out() << "<sup>(obsolete)</sup>";
       
  4125             }
       
  4126             out() << "</a>";
       
  4127         }
       
  4128     }
       
  4129     inLink = false;
       
  4130     inObsoleteLink = false;
       
  4131 }
       
  4132 
       
  4133 QT_END_NAMESPACE
       
  4134 
       
  4135 #ifdef QDOC_QML
       
  4136 
       
  4137 /*!
       
  4138   Generates the summary for for the \a section. Only used for
       
  4139   sections of QML element documentation.
       
  4140 
       
  4141   Currently handles only the QML property group.
       
  4142  */
       
  4143 void HtmlGenerator::generateQmlSummary(const Section& section,
       
  4144                                        const Node *relative,
       
  4145                                        CodeMarker *marker)
       
  4146 {
       
  4147     if (!section.members.isEmpty()) {
       
  4148         NodeList::ConstIterator m;
       
  4149         int count = section.members.size();
       
  4150         bool twoColumn = false;
       
  4151         if (section.members.first()->type() == Node::QmlProperty) {
       
  4152             twoColumn = (count >= 5);
       
  4153         }
       
  4154         if (twoColumn)
       
  4155             out() << "<p><table width=\"100%\" border=\"0\" cellpadding=\"0\""
       
  4156                      " cellspacing=\"0\">\n"
       
  4157                   << "<tr><td width=\"45%\" valign=\"top\">";
       
  4158         out() << "<ul>\n";
       
  4159 
       
  4160         int row = 0;
       
  4161         m = section.members.begin();
       
  4162         while (m != section.members.end()) {
       
  4163             if (twoColumn && row == (int) (count + 1) / 2)
       
  4164                 out() << "</ul></td><td valign=\"top\"><ul>\n";
       
  4165             out() << "<li><div class=\"fn\"></div>";
       
  4166             generateQmlItem(*m,relative,marker,true);
       
  4167             out() << "</li>\n";
       
  4168             row++;
       
  4169             ++m;
       
  4170         }
       
  4171         out() << "</ul>\n";
       
  4172         if (twoColumn)
       
  4173             out() << "</td></tr>\n</table></p>\n";
       
  4174     }
       
  4175 }
       
  4176 
       
  4177 /*!
       
  4178   Outputs the html detailed documentation for a section
       
  4179   on a QML element reference page.
       
  4180  */
       
  4181 void HtmlGenerator::generateDetailedQmlMember(const Node *node,
       
  4182                                               const InnerNode *relative,
       
  4183                                               CodeMarker *marker)
       
  4184 {
       
  4185     const QmlPropertyNode* qpn = 0;
       
  4186     generateMacRef(node, marker);
       
  4187     out() << "<div class=\"qmlitem\">";
       
  4188     if (node->subType() == Node::QmlPropertyGroup) {
       
  4189         const QmlPropGroupNode* qpgn = static_cast<const QmlPropGroupNode*>(node);
       
  4190         NodeList::ConstIterator p = qpgn->childNodes().begin();
       
  4191         out() << "<div class=\"qmlproto\">";
       
  4192         out() << "<table class=\"qmlname\">";
       
  4193 
       
  4194         while (p != qpgn->childNodes().end()) {
       
  4195             if ((*p)->type() == Node::QmlProperty) {
       
  4196                 qpn = static_cast<const QmlPropertyNode*>(*p);
       
  4197                 out() << "<tr><td>";
       
  4198                 out() << "<a name=\"" + refForNode(qpn) + "\"></a>";
       
  4199                 generateQmlItem(qpn, relative, marker, false);
       
  4200                 out() << "</td></tr>";
       
  4201                 if (qpgn->isDefault()) {
       
  4202                     out() << "</table>"
       
  4203                           << "</div></div>"
       
  4204                           << "<div class=\"qmlitem\">"
       
  4205                           << "<div class=\"qmlproto\">"
       
  4206                           << "<table class=\"qmlname\">"
       
  4207                           << "<tr><td><font color=\"green\">"
       
  4208                           << "default</font></td></tr>";
       
  4209                 }
       
  4210             }
       
  4211             ++p;
       
  4212         }
       
  4213         out() << "</table>";
       
  4214         out() << "</div>";
       
  4215     }
       
  4216     else if (node->type() == Node::QmlSignal) {
       
  4217         const QmlSignalNode* qsn = static_cast<const QmlSignalNode*>(node);
       
  4218         out() << "<div class=\"qmlproto\">";
       
  4219         out() << "<table class=\"qmlname\">";
       
  4220         out() << "<tr><td>";
       
  4221         out() << "<a name=\"" + refForNode(qsn) + "\"></a>";
       
  4222         generateQmlItem(qsn,relative,marker,false);
       
  4223         out() << "</td></tr>";
       
  4224         out() << "</table>";
       
  4225         out() << "</div>";
       
  4226     }
       
  4227     else if (node->type() == Node::QmlMethod) {
       
  4228         const QmlMethodNode* qmn = static_cast<const QmlMethodNode*>(node);
       
  4229         out() << "<div class=\"qmlproto\">";
       
  4230         out() << "<table class=\"qmlname\">";
       
  4231         out() << "<tr><td>";
       
  4232         out() << "<a name=\"" + refForNode(qmn) + "\"></a>";
       
  4233         generateQmlItem(qmn,relative,marker,false);
       
  4234         out() << "</td></tr>";
       
  4235         out() << "</table>";
       
  4236         out() << "</div>";
       
  4237     }
       
  4238     out() << "<div class=\"qmldoc\">";
       
  4239     generateStatus(node, marker);
       
  4240     generateBody(node, marker);
       
  4241     generateThreadSafeness(node, marker);
       
  4242     generateSince(node, marker);
       
  4243     generateAlsoList(node, marker);
       
  4244     out() << "</div>";
       
  4245     out() << "</div>";
       
  4246 }
       
  4247 
       
  4248 /*!
       
  4249   Output the "Inherits" line for the QML element,
       
  4250   if there should be one.
       
  4251  */
       
  4252 void HtmlGenerator::generateQmlInherits(const QmlClassNode* cn,
       
  4253                                         CodeMarker* marker)
       
  4254 {
       
  4255     if (cn && !cn->links().empty()) {
       
  4256         if (cn->links().contains(Node::InheritsLink)) {
       
  4257             QPair<QString,QString> linkPair;
       
  4258             linkPair = cn->links()[Node::InheritsLink];
       
  4259             QStringList strList(linkPair.first);
       
  4260             const Node* n = myTree->findNode(strList,Node::Fake);
       
  4261             if (n && n->subType() == Node::QmlClass) {
       
  4262                 const QmlClassNode* qcn = static_cast<const QmlClassNode*>(n);
       
  4263                 out() << "<p style=\"text-align: center\">";
       
  4264                 Text text;
       
  4265                 text << "[Inherits ";
       
  4266                 text << Atom(Atom::LinkNode,CodeMarker::stringForNode(qcn));
       
  4267                 text << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK);
       
  4268                 text << Atom(Atom::String, linkPair.second);
       
  4269                 text << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK);
       
  4270                 text << "]";
       
  4271                 generateText(text, cn, marker);
       
  4272                 out() << "</p>";
       
  4273             }
       
  4274         }
       
  4275     }
       
  4276 }
       
  4277 
       
  4278 /*!
       
  4279   Output the "[Xxx instantiates the C++ class QFxXxx]"
       
  4280   line for the QML element, if there should be one.
       
  4281 
       
  4282   If there is no class node, or if the class node status
       
  4283   is set to Node::Internal, do nothing. 
       
  4284  */
       
  4285 void HtmlGenerator::generateQmlInstantiates(const QmlClassNode* qcn,
       
  4286                                             CodeMarker* marker)
       
  4287 {
       
  4288     const ClassNode* cn = qcn->classNode();
       
  4289     if (cn && (cn->status() != Node::Internal)) {
       
  4290         out() << "<p style=\"text-align: center\">";
       
  4291         Text text;
       
  4292         text << "[";
       
  4293         text << Atom(Atom::LinkNode,CodeMarker::stringForNode(qcn));
       
  4294         text << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK);
       
  4295         text << Atom(Atom::String, qcn->name());
       
  4296         text << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK);
       
  4297         text << " instantiates the C++ class ";
       
  4298         text << Atom(Atom::LinkNode,CodeMarker::stringForNode(cn));
       
  4299         text << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK);
       
  4300         text << Atom(Atom::String, cn->name());
       
  4301         text << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK);
       
  4302         text << "]";
       
  4303         generateText(text, qcn, marker);
       
  4304         out() << "</p>";
       
  4305     }
       
  4306 }
       
  4307 
       
  4308 /*!
       
  4309   Output the "[QFxXxx is instantiated by QML element Xxx]"
       
  4310   line for the class, if there should be one.
       
  4311 
       
  4312   If there is no QML element, or if the class node status
       
  4313   is set to Node::Internal, do nothing. 
       
  4314  */
       
  4315 void HtmlGenerator::generateInstantiatedBy(const ClassNode* cn,
       
  4316                                            CodeMarker* marker)
       
  4317 {
       
  4318     if (cn &&  cn->status() != Node::Internal && !cn->qmlElement().isEmpty()) {
       
  4319         const Node* n = myTree->root()->findNode(cn->qmlElement(),Node::Fake);
       
  4320         if (n && n->subType() == Node::QmlClass) {
       
  4321             out() << "<p style=\"text-align: center\">";
       
  4322             Text text;
       
  4323             text << "[";
       
  4324             text << Atom(Atom::LinkNode,CodeMarker::stringForNode(cn));
       
  4325             text << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK);
       
  4326             text << Atom(Atom::String, cn->name());
       
  4327             text << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK);
       
  4328             text << " is instantiated by QML element ";
       
  4329             text << Atom(Atom::LinkNode,CodeMarker::stringForNode(n));
       
  4330             text << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK);
       
  4331             text << Atom(Atom::String, n->name());
       
  4332             text << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK);
       
  4333             text << "]";
       
  4334             generateText(text, cn, marker);
       
  4335             out() << "</p>";
       
  4336         }
       
  4337     }
       
  4338 }
       
  4339 
       
  4340 #endif