Ignore:
Timestamp:
Feb 11, 2010, 11:19:06 PM (15 years ago)
Author:
Dmitry A. Kuminov
Message:

trunk: Merged in qt 4.6.1 sources.

Location:
trunk
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • trunk

  • trunk/tools/qdoc3/htmlgenerator.cpp

    r2 r561  
    22**
    33** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
    4 ** Contact: Qt Software Information (qt-info@nokia.com)
     4** All rights reserved.
     5** Contact: Nokia Corporation (qt-info@nokia.com)
    56**
    67** This file is part of the tools applications of the Qt Toolkit.
     
    2122** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
    2223**
    23 ** In addition, as a special exception, Nokia gives you certain
    24 ** additional rights. These rights are described in the Nokia Qt LGPL
    25 ** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
    26 ** package.
     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.
    2727**
    2828** GNU General Public License Usage
     
    3434** met: http://www.gnu.org/copyleft/gpl.html.
    3535**
    36 ** If you are unsure which license is appropriate for your use, please
    37 ** contact the sales department at qt-sales@nokia.com.
     36** If you have questions regarding the use of this file, please contact
     37** Nokia at qt-info@nokia.com.
    3838** $QT_END_LICENSE$
    3939**
     
    6060#define COMMAND_VERSION                 Doc::alias("version")
    6161
     62QString 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
    6280static bool showBrokenLinks = false;
    63 
    64 HtmlGenerator::HtmlGenerator()
    65     : helpProjectWriter(0), inLink(false), inContents(false),
    66       inSectionHeading(false), inTableHeader(false), numTableRows(0),
    67       threeColumnEnumValueTable(true), funcLeftParen("\\S(\\()"),
    68       tre(0), slow(false)
    69 {
    70 }
    71 
    72 HtmlGenerator::~HtmlGenerator()
    73 {
    74     if (helpProjectWriter)
    75         delete helpProjectWriter;
    76 }
    77 
    78 void HtmlGenerator::initializeGenerator(const Config &config)
    79 {
    80     static const struct {
    81         const char *key;
    82         const char *left;
    83         const char *right;
    84     } defaults[] = {
    85         { ATOM_FORMATTING_BOLD, "<b>", "</b>" },
    86         { ATOM_FORMATTING_INDEX, "<!--", "-->" },
    87         { ATOM_FORMATTING_ITALIC, "<i>", "</i>" },
    88         { ATOM_FORMATTING_PARAMETER, "<i>", "</i>" },
    89         { ATOM_FORMATTING_SUBSCRIPT, "<sub>", "</sub>" },
    90         { ATOM_FORMATTING_SUPERSCRIPT, "<sup>", "</sup>" },
    91         { ATOM_FORMATTING_TELETYPE, "<tt>", "</tt>" },
    92         { ATOM_FORMATTING_UNDERLINE, "<u>", "</u>" },
    93         { 0, 0, 0 }
    94     };
    95 
    96     Generator::initializeGenerator(config);
    97     setImageFileExtensions(QStringList() << "png" << "jpg" << "jpeg" << "gif");
    98     int i = 0;
    99     while (defaults[i].key) {
    100         formattingLeftMap().insert(defaults[i].key, defaults[i].left);
    101         formattingRightMap().insert(defaults[i].key, defaults[i].right);
    102         i++;
    103     }
    104 
    105     style = config.getString(HtmlGenerator::format() + Config::dot + HTMLGENERATOR_STYLE);
    106     postHeader = config.getString(HtmlGenerator::format() + Config::dot + HTMLGENERATOR_POSTHEADER);
    107     footer = config.getString(HtmlGenerator::format() + Config::dot + HTMLGENERATOR_FOOTER);
    108     address = config.getString(HtmlGenerator::format() + Config::dot + HTMLGENERATOR_ADDRESS);
    109     pleaseGenerateMacRef = config.getBool(HtmlGenerator::format() + Config::dot + HTMLGENERATOR_GENERATEMACREFS);
    110 
    111     project = config.getString(CONFIG_PROJECT);
    112 
    113     projectDescription = config.getString(CONFIG_DESCRIPTION);
    114     if (projectDescription.isEmpty() && !project.isEmpty())
    115         projectDescription = project + " Reference Documentation";
    116 
    117     projectUrl = config.getString(CONFIG_URL);
    118 
    119     QSet<QString> editionNames = config.subVars(CONFIG_EDITION);
    120     QSet<QString>::ConstIterator edition = editionNames.begin();
    121     while (edition != editionNames.end()) {
    122         QString editionName = *edition;
    123         QStringList editionModules = config.getStringList(
    124             CONFIG_EDITION + Config::dot + editionName + Config::dot + "modules");
    125         QStringList editionGroups = config.getStringList(
    126             CONFIG_EDITION + Config::dot + editionName + Config::dot + "groups");
    127 
    128         if (!editionModules.isEmpty())
    129             editionModuleMap[editionName] = editionModules;
    130         if (!editionGroups.isEmpty())
    131             editionGroupMap[editionName] = editionGroups;
    132 
    133         ++edition;
    134     }
    135 
    136     slow = config.getBool(CONFIG_SLOW);
    137 
    138     stylesheets = config.getStringList(HtmlGenerator::format() + Config::dot + HTMLGENERATOR_STYLESHEETS);
    139     customHeadElements = config.getStringList(HtmlGenerator::format() + Config::dot + HTMLGENERATOR_CUSTOMHEADELEMENTS);
    140     codeIndent = config.getInt(CONFIG_CODEINDENT);
    141 
    142     helpProjectWriter = new HelpProjectWriter(config, project.toLower() + ".qhp");
    143 }
    144 
    145 void HtmlGenerator::terminateGenerator()
    146 {
    147     Generator::terminateGenerator();
    148 }
    149 
    150 QString HtmlGenerator::format()
    151 {
    152     return "HTML";
    153 }
    154 
    155 /*!
    156   This is where the html files and dcf files are written.
    157   \note The html file generation is done in the base class,
    158   PageGenerator::generateTree().
    159  */
    160 void HtmlGenerator::generateTree(const Tree *tree, CodeMarker *marker)
    161 {
    162     // Copy the stylesheets from the directory containing the qdocconf file.
    163     // ### This should be changed to use a special directory in doc/src.
    164     QStringList::ConstIterator styleIter = stylesheets.begin();
    165     QDir configPath = QDir::current();
    166     while (styleIter != stylesheets.end()) {
    167         QString filePath = configPath.absoluteFilePath(*styleIter);
    168         Config::copyFile(Location(), filePath, filePath, outputDir());
    169         ++styleIter;
    170     }
    171 
    172     tre = tree;
    173     nonCompatClasses.clear();
    174     mainClasses.clear();
    175     compatClasses.clear();
    176     moduleClassMap.clear();
    177     moduleNamespaceMap.clear();
    178     funcIndex.clear();
    179     legaleseTexts.clear();
    180     serviceClasses.clear();
    181     findAllClasses(tree->root());
    182     findAllFunctions(tree->root());
    183     findAllLegaleseTexts(tree->root());
    184     findAllNamespaces(tree->root());
    185 #ifdef ZZZ_QDOC_QML   
    186     findAllQmlClasses(tree->root());
    187 #endif   
    188 
    189     PageGenerator::generateTree(tree, marker);
    190 
    191     dcfClassesRoot.ref = "classes.html";
    192     dcfClassesRoot.title = "Classes";
    193     qSort(dcfClassesRoot.subsections);
    194 
    195     dcfOverviewsRoot.ref = "overviews.html";
    196     dcfOverviewsRoot.title = "Overviews";
    197     qSort(dcfOverviewsRoot.subsections);
    198 
    199     dcfExamplesRoot.ref = "examples.html";
    200     dcfExamplesRoot.title = "Tutorial & Examples";
    201     qSort(dcfExamplesRoot.subsections);
    202 
    203     DcfSection qtRoot;
    204     appendDcfSubSection(&qtRoot, dcfClassesRoot);
    205     appendDcfSubSection(&qtRoot, dcfOverviewsRoot);
    206     appendDcfSubSection(&qtRoot, dcfExamplesRoot);
    207 
    208     generateDcf(project.toLower().simplified().replace(" ", "-"),
    209                 "index.html",
    210                 projectDescription, qtRoot);
    211     generateDcf("designer",
    212                 "designer-manual.html",
    213                 "Qt Designer Manual",
    214                 dcfDesignerRoot);
    215     generateDcf("linguist",
    216                 "linguist-manual.html",
    217                 "Qt Linguist Manual",
    218                 dcfLinguistRoot);
    219     generateDcf("assistant",
    220                 "assistant-manual.html",
    221                 "Qt Assistant Manual",
    222                 dcfAssistantRoot);
    223     generateDcf("qmake",
    224                 "qmake-manual.html",
    225                 "qmake Manual",
    226                 dcfQmakeRoot);
    227 
    228     generateIndex(project.toLower().simplified().replace(" ", "-"),
    229                   projectUrl,
    230                   projectDescription);
    231 
    232     helpProjectWriter->generate(tre);
    233 }
    234 
    235 void HtmlGenerator::startText(const Node * /* relative */,
    236                               CodeMarker * /* marker */)
    237 {
    238     inLink = false;
    239     inContents = false;
    240     inSectionHeading = false;
    241     inTableHeader = false;
    242     numTableRows = 0;
    243     threeColumnEnumValueTable = true;
    244     link.clear();
    245     sectionNumber.clear();
    246 }
    247 
    248 int HtmlGenerator::generateAtom(const Atom *atom,
    249                                 const Node *relative,
    250                                 CodeMarker *marker)
    251 {
    252     int skipAhead = 0;
    253     static bool in_para = false;
    254 
    255     switch (atom->type()) {
    256     case Atom::AbstractLeft:
    257         break;
    258     case Atom::AbstractRight:
    259         break;
    260     case Atom::AutoLink:
    261         if (!inLink && !inContents && !inSectionHeading) {
    262             const Node *node = 0;
    263             QString link = getLink(atom, relative, marker, node);
    264             if (!link.isEmpty()) {
    265                 beginLink(link, node, relative, marker);
    266                 generateLink(atom, relative, marker);
    267                 endLink();
    268             }
    269             else {
    270                 out() << protect(atom->string());
    271             }
    272         }
    273         else {
    274             out() << protect(atom->string());
    275         }
    276         break;
    277     case Atom::BaseName:
    278         break;
    279     case Atom::BriefLeft:
    280         if (relative->type() == Node::Fake) {
    281             skipAhead = skipAtoms(atom, Atom::BriefRight);
    282             break;
    283         }
    284 
    285         out() << "<p>";
    286         if (relative->type() == Node::Property ||
    287             relative->type() == Node::Variable) {
    288             QString str;
    289             atom = atom->next();
    290             while (atom != 0 && atom->type() != Atom::BriefRight) {
    291                 if (atom->type() == Atom::String || atom->type() == Atom::AutoLink)
    292                     str += atom->string();
    293                 skipAhead++;
    294                 atom = atom->next();
    295             }
    296             str[0] = str[0].toLower();
    297             if (str.right(1) == ".")
    298                 str.truncate(str.length() - 1);
    299             out() << "This ";
    300             if (relative->type() == Node::Property)
    301                 out() << "property";
    302             else
    303                 out() << "variable";
    304             QStringList words = str.split(" ");
    305             if (!(words.first() == "contains" || words.first() == "specifies"
    306                 || words.first() == "describes" || words.first() == "defines"
    307                 || words.first() == "holds" || words.first() == "determines"))
    308                 out() << " holds ";
    309             else
    310                 out() << " ";
    311             out() << str << ".";
    312         }
    313         break;
    314     case Atom::BriefRight:
    315         if (relative->type() != Node::Fake)
    316             out() << "</p>\n";
    317         break;
    318     case Atom::C:
    319         out() << formattingLeftMap()[ATOM_FORMATTING_TELETYPE];
    320         if (inLink) {
    321             out() << protect(plainCode(atom->string()));
    322         }
    323         else {
    324             out() << highlightedCode(atom->string(), marker, relative);
    325         }
    326         out() << formattingRightMap()[ATOM_FORMATTING_TELETYPE];
    327         break;
    328     case Atom::Code:
    329         out() << "<pre>" << trimmedTrailing(highlightedCode(indent(codeIndent, atom->string()),
    330                                                             marker, relative))
    331               << "</pre>\n";
    332         break;
    333 #ifdef QDOC_QML       
    334     case Atom::Qml:
    335         out() << "<pre>" << trimmedTrailing(highlightedCode(indent(codeIndent, atom->string()),
    336                                                             marker, relative))
    337               << "</pre>\n";
    338         break;
    339 #endif       
    340     case Atom::CodeNew:
    341         out() << "<p>you can rewrite it as</p>\n"
    342               << "<pre>" << trimmedTrailing(highlightedCode(indent(codeIndent, atom->string()),
    343                                                             marker, relative))
    344               << "</pre>\n";
    345         break;
    346     case Atom::CodeOld:
    347         out() << "<p>For example, if you have code like</p>\n";
    348         // fallthrough
    349     case Atom::CodeBad:
    350         out() << "<pre><font color=\"#404040\">"
    351               << trimmedTrailing(protect(plainCode(indent(codeIndent, atom->string()))))
    352               << "</font></pre>\n";
    353         break;
    354     case Atom::FootnoteLeft:
    355         // ### For now
    356         if (in_para) {
    357             out() << "</p>\n";
    358             in_para = false;
    359         }
    360         out() << "<!-- ";
    361         break;
    362     case Atom::FootnoteRight:
    363         // ### For now
    364         out() << "-->";
    365         break;
    366     case Atom::FormatElse:
    367     case Atom::FormatEndif:
    368     case Atom::FormatIf:
    369         break;
    370     case Atom::FormattingLeft:
    371         out() << formattingLeftMap()[atom->string()];
    372         if (atom->string() == ATOM_FORMATTING_PARAMETER) {
    373             if (atom->next() != 0 && atom->next()->type() == Atom::String) {
    374                 QRegExp subscriptRegExp("([a-z]+)_([0-9n])");
    375                 if (subscriptRegExp.exactMatch(atom->next()->string())) {
    376                     out() << subscriptRegExp.cap(1) << "<sub>"
    377                           << subscriptRegExp.cap(2) << "</sub>";
    378                     skipAhead = 1;
    379                 }
    380             }
    381         }
    382         break;
    383     case Atom::FormattingRight:
    384         if (atom->string() == ATOM_FORMATTING_LINK) {
    385             endLink();
    386         }
    387         else {
    388             out() << formattingRightMap()[atom->string()];
    389         }
    390         break;
    391     case Atom::GeneratedList:
    392         if (atom->string() == "annotatedclasses") {
    393             generateAnnotatedList(relative, marker, nonCompatClasses);
    394         }
    395         else if (atom->string() == "classes") {
    396             generateCompactList(relative, marker, nonCompatClasses);
    397         }
    398         else if (atom->string().contains("classesbymodule")) {
    399             QString arg = atom->string().trimmed();
    400             QString moduleName = atom->string().mid(atom->string().indexOf(
    401                 "classesbymodule") + 15).trimmed();
    402             if (moduleClassMap.contains(moduleName))
    403                 generateAnnotatedList(relative, marker, moduleClassMap[moduleName]);
    404         }
    405         else if (atom->string().contains("classesbyedition")) {
    406 
    407             QString arg = atom->string().trimmed();
    408             QString editionName = atom->string().mid(atom->string().indexOf(
    409                 "classesbyedition") + 16).trimmed();
    410 
    411             if (editionModuleMap.contains(editionName)) {
    412 
    413                 // Add all classes in the modules listed for that edition.
    414                 QMap<QString, const Node *> editionClasses;
    415                 foreach (const QString &moduleName, editionModuleMap[editionName]) {
    416                     if (moduleClassMap.contains(moduleName))
    417                         editionClasses.unite(moduleClassMap[moduleName]);
    418                 }
    419 
    420                 // Add additional groups and remove groups of classes that
    421                 // should be excluded from the edition.
    422 
    423                 QMultiMap <QString, Node *> groups = tre->groups();
    424                 foreach (const QString &groupName, editionGroupMap[editionName]) {
    425                     QList<Node *> groupClasses;
    426                     if (groupName.startsWith("-")) {
    427                         groupClasses = groups.values(groupName.mid(1));
    428                         foreach (const Node *node, groupClasses)
    429                             editionClasses.remove(node->name());
    430                     }
    431                     else {
    432                         groupClasses = groups.values(groupName);
    433                         foreach (const Node *node, groupClasses)
    434                             editionClasses.insert(node->name(), node);
    435                     }
    436                 }
    437                 generateAnnotatedList(relative, marker, editionClasses);
    438             }
    439         }
    440         else if (atom->string() == "classhierarchy") {
    441             generateClassHierarchy(relative, marker, nonCompatClasses);
    442         }
    443         else if (atom->string() == "compatclasses") {
    444             generateCompactList(relative, marker, compatClasses);
    445         }
    446         else if (atom->string() == "functionindex") {
    447             generateFunctionIndex(relative, marker);
    448         }
    449         else if (atom->string() == "legalese") {
    450             generateLegaleseList(relative, marker);
    451         }
    452         else if (atom->string() == "mainclasses") {
    453             generateCompactList(relative, marker, mainClasses);
    454         }
    455         else if (atom->string() == "services") {
    456             generateCompactList(relative, marker, serviceClasses);
    457         }
    458         else if (atom->string() == "overviews") {
    459             generateOverviewList(relative, marker);
    460         }
    461         else if (atom->string() == "namespaces") {
    462             generateAnnotatedList(relative, marker, namespaceIndex);
    463         }
    464         else if (atom->string() == "related") {
    465             const FakeNode *fake = static_cast<const FakeNode *>(relative);
    466             if (fake && !fake->groupMembers().isEmpty()) {
    467                 QMap<QString, const Node *> groupMembersMap;
    468                 foreach (const Node *node, fake->groupMembers()) {
    469                     if (node->type() == Node::Fake)
    470                         groupMembersMap[fullName(node, relative, marker)] = node;
    471                 }
    472                 generateAnnotatedList(fake, marker, groupMembersMap);
    473             }
    474         }
    475         else if (atom->string() == "relatedinline") {
    476             const FakeNode *fake = static_cast<const FakeNode *>(relative);
    477             if (fake && !fake->groupMembers().isEmpty()) {
    478                 // Reverse the list into the original scan order.
    479                 // Should be sorted.  But on what?  It may not be a
    480                 // regular class or page definition.
    481                 QList<const Node *> list;
    482                 foreach (const Node *node, fake->groupMembers())
    483                     list.prepend(node);
    484                 foreach (const Node *node, list)
    485                     generateBody(node, marker);
    486             }
    487         }
    488         break;
    489     case Atom::Image:
    490     case Atom::InlineImage:
    491         {
    492             QString fileName = imageFileName(relative, atom->string());
    493             QString text;
    494             if (atom->next() != 0)
    495                 text = atom->next()->string();
    496             if (atom->type() == Atom::Image)
    497                 out() << "<p align=\"center\">";
    498             if (fileName.isEmpty()) {
    499                 out() << "<font color=\"red\">[Missing image "
    500                       << protect(atom->string()) << "]</font>";
    501             }
    502             else {
    503                 out() << "<img src=\"" << protect(fileName) << "\"";
    504                 if (!text.isEmpty())
    505                     out() << " alt=\"" << protect(text) << "\"";
    506                 out() << " />";
    507                 helpProjectWriter->addExtraFile(fileName);
    508             }
    509             if (atom->type() == Atom::Image)
    510                 out() << "</p>";
    511         }
    512         break;
    513     case Atom::ImageText:
    514         break;
    515     case Atom::LegaleseLeft:
    516         out() << "<div style=\"padding: 0.5em; background: #e0e0e0; color: black\">";
    517         break;
    518     case Atom::LegaleseRight:
    519         out() << "</div>";
    520         break;
    521     case Atom::LineBreak:
    522         out() << "<br />";
    523         break;
    524     case Atom::Link:
    525         {
    526             const Node *node = 0;
    527             QString myLink = getLink(atom, relative, marker, node);
    528             if (myLink.isEmpty())
    529                 relative->doc().location().warning(tr("Cannot link to '%1' in %2")
    530                         .arg(atom->string())
    531                         .arg(marker->plainFullName(relative)));
    532             beginLink(myLink, node, relative, marker);
    533             skipAhead = 1;
    534         }
    535         break;
    536     case Atom::LinkNode:
    537         {
    538             const Node *node = CodeMarker::nodeForString(atom->string());
    539             beginLink(linkForNode(node, relative), node, relative, marker);
    540             skipAhead = 1;
    541         }
    542         break;
    543     case Atom::ListLeft:
    544         if (in_para) {
    545             out() << "</p>\n";
    546             in_para = false;
    547         }
    548         if (atom->string() == ATOM_LIST_BULLET) {
    549             out() << "<ul>\n";
    550         }
    551         else if (atom->string() == ATOM_LIST_TAG) {
    552             out() << "<dl>\n";
    553         }
    554         else if (atom->string() == ATOM_LIST_VALUE) {
    555             threeColumnEnumValueTable = isThreeColumnEnumValueTable(atom);
    556             if (threeColumnEnumValueTable) {
    557                 out() << "<p><table border=\"1\" cellpadding=\"2\" cellspacing=\"1\" width=\"100%\">\n"
    558                          "<tr><th width=\"25%\">Constant</th><th width=\"15%\">Value</th>"
    559                          "<th width=\"60%\">Description</th></tr>\n";
    560             }
    561             else {
    562                 out() << "<p><table border=\"1\" cellpadding=\"2\" cellspacing=\"1\" width=\"40%\">\n"
    563                       << "<tr><th width=\"60%\">Constant</th><th width=\"40%\">Value</th></tr>\n";
    564             }
    565         }
    566         else {
    567             out() << "<ol type=";
    568             if (atom->string() == ATOM_LIST_UPPERALPHA) {
    569                 out() << "\"A\"";
    570             }
    571             else if (atom->string() == ATOM_LIST_LOWERALPHA) {
    572                 out() << "\"a\"";
    573             }
    574             else if (atom->string() == ATOM_LIST_UPPERROMAN) {
    575                 out() << "\"I\"";
    576             }
    577             else if (atom->string() == ATOM_LIST_LOWERROMAN) {
    578                 out() << "\"i\"";
    579             }
    580             else { // (atom->string() == ATOM_LIST_NUMERIC)
    581                 out() << "\"1\"";
    582             }
    583             if (atom->next() != 0 && atom->next()->string().toInt() != 1)
    584                 out() << " start=\"" << atom->next()->string() << "\"";
    585             out() << ">\n";
    586         }
    587         break;
    588     case Atom::ListItemNumber:
    589         break;
    590     case Atom::ListTagLeft:
    591         if (atom->string() == ATOM_LIST_TAG) {
    592             out() << "<dt>";
    593         }
    594         else { // (atom->string() == ATOM_LIST_VALUE)
    595             // ### Trenton
    596 
    597             out() << "<tr><td valign=\"top\"><tt>"
    598                   << protect(plainCode(marker->markedUpEnumValue(atom->next()->string(),
    599                                                                  relative)))
    600                   << "</tt></td><td align=\"center\" valign=\"top\">";
    601 
    602             QString itemValue;
    603             if (relative->type() == Node::Enum) {
    604                 const EnumNode *enume = static_cast<const EnumNode *>(relative);
    605                 itemValue = enume->itemValue(atom->next()->string());
    606             }
    607 
    608             if (itemValue.isEmpty())
    609                 out() << "?";
    610             else
    611                 out() << "<tt>" << protect(itemValue) << "</tt>";
    612 
    613             skipAhead = 1;
    614         }
    615         break;
    616     case Atom::ListTagRight:
    617         if (atom->string() == ATOM_LIST_TAG)
    618             out() << "</dt>\n";
    619         break;
    620     case Atom::ListItemLeft:
    621         if (atom->string() == ATOM_LIST_TAG) {
    622             out() << "<dd>";
    623         }
    624         else if (atom->string() == ATOM_LIST_VALUE) {
    625             if (threeColumnEnumValueTable) {
    626                 out() << "</td><td valign=\"top\">";
    627                 if (matchAhead(atom, Atom::ListItemRight))
    628                     out() << "&nbsp;";
    629             }
    630         }
    631         else {
    632             out() << "<li>";
    633         }
    634         if (matchAhead(atom, Atom::ParaLeft))
    635             skipAhead = 1;
    636         break;
    637     case Atom::ListItemRight:
    638         if (atom->string() == ATOM_LIST_TAG) {
    639             out() << "</dd>\n";
    640         }
    641         else if (atom->string() == ATOM_LIST_VALUE) {
    642             out() << "</td></tr>\n";
    643         }
    644         else {
    645             out() << "</li>\n";
    646         }
    647         break;
    648     case Atom::ListRight:
    649         if (atom->string() == ATOM_LIST_BULLET) {
    650             out() << "</ul>\n";
    651         }
    652         else if (atom->string() == ATOM_LIST_TAG) {
    653             out() << "</dl>\n";
    654         }
    655         else if (atom->string() == ATOM_LIST_VALUE) {
    656             out() << "</table></p>\n";
    657         }
    658         else {
    659             out() << "</ol>\n";
    660         }
    661         break;
    662     case Atom::Nop:
    663         break;
    664     case Atom::ParaLeft:
    665         out() << "<p>";
    666         in_para = true;
    667         break;
    668     case Atom::ParaRight:
    669         endLink();
    670         if (in_para) {
    671             out() << "</p>\n";
    672             in_para = false;
    673         }
    674         //if (!matchAhead(atom, Atom::ListItemRight) && !matchAhead(atom, Atom::TableItemRight))
    675         //    out() << "</p>\n";
    676         break;
    677     case Atom::QuotationLeft:
    678         out() << "<blockquote>";
    679         break;
    680     case Atom::QuotationRight:
    681         out() << "</blockquote>\n";
    682         break;
    683     case Atom::RawString:
    684         out() << atom->string();
    685         break;
    686     case Atom::SectionLeft:
    687 #if 0
    688         {
    689             int nextLevel = atom->string().toInt();
    690             if (sectionNumber.size() < nextLevel) {
    691                 do {
    692                     sectionNumber.append("1");
    693                 } while (sectionNumber.size() < nextLevel);
    694             }
    695             else {
    696                 while (sectionNumber.size() > nextLevel) {
    697                     sectionNumber.removeLast();
    698                 }
    699                 sectionNumber.last() = QString::number(sectionNumber.last().toInt() + 1);
    700             }
    701             out() << "<a name=\"sec-" << sectionNumber.join("-") << "\"></a>\n";
    702         }
    703 #else
    704         out() << "<a name=\"" << Doc::canonicalTitle(Text::sectionHeading(atom).toString())
    705               << "\"></a>\n";
    706 #endif
    707         break;
    708     case Atom::SectionRight:
    709         break;
    710     case Atom::SectionHeadingLeft:
    711         out() << "<h" + QString::number(atom->string().toInt() + hOffset(relative)) + ">";
    712         inSectionHeading = true;
    713         break;
    714     case Atom::SectionHeadingRight:
    715         out() << "</h" + QString::number(atom->string().toInt() + hOffset(relative)) + ">\n";
    716         inSectionHeading = false;
    717         break;
    718     case Atom::SidebarLeft:
    719         break;
    720     case Atom::SidebarRight:
    721         break;
    722     case Atom::String:
    723         if (inLink && !inContents && !inSectionHeading) {
    724             generateLink(atom, relative, marker);
    725         }
    726         else {
    727             out() << protect(atom->string());
    728         }
    729         break;
    730     case Atom::TableLeft:
    731         if (in_para) {
    732             out() << "</p>\n";
    733             in_para = false;
    734         }
    735         if (!atom->string().isEmpty()) {
    736             if (atom->string().contains("%"))
    737                 out() << "<p><table width=\"" << atom->string() << "\" "
    738                       << "align=\"center\" cellpadding=\"2\" "
    739                       << "cellspacing=\"1\" border=\"0\">\n";
    740             else
    741                 out() << "<p><table align=\"center\" cellpadding=\"2\" cellspacing=\"1\" border=\"0\">\n";
    742         }
    743         else {
    744             out() << "<p><table align=\"center\" cellpadding=\"2\" cellspacing=\"1\" border=\"0\">\n";
    745         }
    746         numTableRows = 0;
    747         break;
    748     case Atom::TableRight:
    749         out() << "</table></p>\n";
    750         break;
    751     case Atom::TableHeaderLeft:
    752         out() << "<thead><tr valign=\"top\" class=\"qt-style\">";
    753         inTableHeader = true;
    754         break;
    755     case Atom::TableHeaderRight:
    756         out() << "</tr>";
    757         if (matchAhead(atom, Atom::TableHeaderLeft)) {
    758             skipAhead = 1;
    759             out() << "\n<tr valign=\"top\" class=\"qt-style\">";
    760         }
    761         else {
    762             out() << "</thead>\n";
    763             inTableHeader = false;
    764         }
    765         break;
    766     case Atom::TableRowLeft:
    767         if (++numTableRows % 2 == 1)
    768             out() << "<tr valign=\"top\" class=\"odd\">";
    769         else
    770             out() << "<tr valign=\"top\" class=\"even\">";
    771         break;
    772     case Atom::TableRowRight:
    773         out() << "</tr>\n";
    774         break;
    775     case Atom::TableItemLeft:
    776         {
    777             if (inTableHeader)
    778                 out() << "<th";
    779             else
    780                 out() << "<td";
    781 
    782             QStringList spans = atom->string().split(",");
    783             if (spans.size() == 2) {
    784                 if (spans.at(0) != "1")
    785                     out() << " colspan=\"" << spans.at(0) << "\"";
    786                 if (spans.at(1) != "1")
    787                     out() << " rowspan=\"" << spans.at(1) << "\"";
    788                 out() << ">";
    789             }
    790             if (matchAhead(atom, Atom::ParaLeft))
    791                 skipAhead = 1;
    792         }
    793         break;
    794     case Atom::TableItemRight:
    795         if (inTableHeader)
    796             out() << "</th>";
    797         else
    798             out() << "</td>";
    799         if (matchAhead(atom, Atom::ParaLeft))
    800             skipAhead = 1;
    801         break;
    802     case Atom::TableOfContents:
    803         {
    804             int numColumns = 1;
    805             const Node *node = relative;
    806 
    807             Doc::SectioningUnit sectioningUnit = Doc::Section4;
    808             QStringList params = atom->string().split(",");
    809             QString columnText = params.at(0);
    810             QStringList pieces = columnText.split(" ", QString::SkipEmptyParts);
    811             if (pieces.size() >= 2) {
    812                 columnText = pieces.at(0);
    813                 pieces.pop_front();
    814                 QString path = pieces.join(" ").trimmed();
    815                 node = findNodeForTarget(path, relative, marker, atom);
    816             }
    817 
    818             if (params.size() == 2) {
    819                 numColumns = qMax(columnText.toInt(), numColumns);
    820                 sectioningUnit = (Doc::SectioningUnit)params.at(1).toInt();
    821             }
    822 
    823             if (node)
    824                 generateTableOfContents(node, marker, sectioningUnit, numColumns,
    825                                         relative);
    826         }
    827         break;
    828     case Atom::Target:
    829         out() << "<a name=\"" << Doc::canonicalTitle(atom->string()) << "\"></a>";
    830         break;
    831     case Atom::UnhandledFormat:
    832         out() << "<font color=\"red\"><b>&lt;Missing HTML&gt;</b></font>";
    833         break;
    834     case Atom::UnknownCommand:
    835         out() << "<font color=\"red\"><b><code>\\" << protect(atom->string())
    836               << "</code></b></font>";
    837         break;
    838 #ifdef QDOC_QML
    839     case Atom::QmlText:
    840     case Atom::EndQmlText:
    841         // don't do anything with these. They are just tags.
    842         break;
    843 #endif       
    844     default:
    845         unknownAtom(atom);
    846     }
    847     return skipAhead;
    848 }
    849 
    850 void HtmlGenerator::generateClassLikeNode(const InnerNode *inner,
    851                                           CodeMarker *marker)
    852 {
    853     QList<Section> sections;
    854     QList<Section>::ConstIterator s;
    855 
    856     const ClassNode *classe = 0;
    857     const NamespaceNode *namespasse = 0;
    858 
    859     QString title;
    860     QString rawTitle;
    861     QString fullTitle;
    862     if (inner->type() == Node::Namespace) {
    863         namespasse = static_cast<const NamespaceNode *>(inner);
    864         rawTitle = marker->plainName(inner);
    865         fullTitle = marker->plainFullName(inner);
    866         title = rawTitle + " Namespace Reference";
    867     }
    868     else if (inner->type() == Node::Class) {
    869         classe = static_cast<const ClassNode *>(inner);
    870         rawTitle = marker->plainName(inner);
    871         fullTitle = marker->plainFullName(inner);
    872         title = rawTitle + " Class Reference";
    873     }
    874 
    875     DcfSection classSection;
    876     classSection.title = title;
    877     classSection.ref = linkForNode(inner, 0);
    878     classSection.keywords += qMakePair(inner->name(), classSection.ref);
    879 
    880     Text subtitleText;
    881     if (rawTitle != fullTitle)
    882         subtitleText << "(" << Atom(Atom::AutoLink, fullTitle) << ")"
    883                      << Atom(Atom::LineBreak);
    884 
    885     QString fixedModule = inner->moduleName();
    886     if (fixedModule == "Qt3SupportLight")
    887         fixedModule = "Qt3Support";
    888     if (!fixedModule.isEmpty())
    889         subtitleText << "[" << Atom(Atom::AutoLink, fixedModule) << " module]";
    890 
    891     if (fixedModule.isEmpty()) {
    892         QMultiMap<QString, QString> publicGroups = tre->publicGroups();
    893         QList<QString> groupNames = publicGroups.values(inner->name());
    894         if (!groupNames.isEmpty()) {
    895             qSort(groupNames.begin(), groupNames.end());
    896             subtitleText << "[";
    897             for (int j=0; j<groupNames.count(); j++) {
    898                 subtitleText <<  Atom(Atom::AutoLink, groupNames[j]);
    899                 if (j<groupNames.count()-1)
    900                     subtitleText <<", ";
    901             }
    902             subtitleText << "]";
    903         }
    904     }
    905 
    906     generateHeader(title, inner, marker, true);
    907     generateTitle(title, subtitleText, SmallSubTitle, inner, marker);
    908 
    909     generateBrief(inner, marker);
    910     generateIncludes(inner, marker);
    911     generateStatus(inner, marker);
    912     if (classe) {
    913         generateModuleWarning(classe, marker);
    914         generateInherits(classe, marker);
    915         generateInheritedBy(classe, marker);
    916     }
    917     generateThreadSafeness(inner, marker);
    918     generateSince(inner, marker);
    919 
    920     out() << "<ul>\n";
    921 
    922     QString membersLink = generateListOfAllMemberFile(inner, marker);
    923     if (!membersLink.isEmpty())
    924         out() << "<li><a href=\"" << membersLink << "\">"
    925               << "List of all members, including inherited members</a></li>\n";
    926 
    927     QString obsoleteLink = generateLowStatusMemberFile(inner, marker, CodeMarker::Obsolete);
    928     if (!obsoleteLink.isEmpty())
    929         out() << "<li><a href=\"" << obsoleteLink << "\">"
    930               << "Obsolete members</a></li>\n";
    931 
    932     QString compatLink = generateLowStatusMemberFile(inner, marker, CodeMarker::Compat);
    933     if (!compatLink.isEmpty())
    934         out() << "<li><a href=\"" << compatLink << "\">"
    935               << "Qt 3 support members</a></li>\n";
    936 
    937     out() << "</ul>\n";
    938 
    939     bool needOtherSection = false;
    940 
    941     sections = marker->sections(inner, CodeMarker::Summary, CodeMarker::Okay);
    942     s = sections.begin();
    943     while (s != sections.end()) {
    944         if (s->members.isEmpty()) {
    945             if (!s->inherited.isEmpty())
    946                 needOtherSection = true;
    947         } else {
    948             out() << "<a name=\"" << registerRef((*s).name.toLower()) << "\"></a>\n";
    949             out() << "<h3>" << protect((*s).name) << "</h3>\n";
    950 
    951             generateSectionList(*s, inner, marker, CodeMarker::Summary);
    952         }
    953         ++s;
    954     }
    955 
    956     if (needOtherSection) {
    957         out() << "<h3>Additional Inherited Members</h3>\n"
    958                  "<ul>\n";
    959 
    960         s = sections.begin();
    961         while (s != sections.end()) {
    962             if (s->members.isEmpty() && !s->inherited.isEmpty())
    963                 generateSectionInheritedList(*s, inner, marker);
    964             ++s;
    965         }
    966         out() << "</ul>\n";
    967     }
    968 
    969     out() << "<a name=\"" << registerRef("details") << "\"></a>\n";
    970 
    971     if (!inner->doc().isEmpty()) {
    972         out() << "<hr />\n"
    973               << "<h2>" << "Detailed Description" << "</h2>\n";
    974         generateBody(inner, marker);
    975         generateAlsoList(inner, marker);
    976     }
    977 
    978     sections = marker->sections(inner, CodeMarker::Detailed, CodeMarker::Okay);
    979     s = sections.begin();
    980     while (s != sections.end()) {
    981         out() << "<hr />\n";
    982         out() << "<h2>" << protect((*s).name) << "</h2>\n";
    983 
    984         NodeList::ConstIterator m = (*s).members.begin();
    985         while (m != (*s).members.end()) {
    986             if ((*m)->access() != Node::Private) { // ### check necessary?
    987                 if ((*m)->type() != Node::Class)
    988                     generateDetailedMember(*m, inner, marker);
    989                 else {
    990                     out() << "<h3> class ";
    991                     generateFullName(*m, inner, marker);
    992                     out() << "</h3>";
    993                     generateBrief(*m, marker, inner);
    994                 }
    995 
    996                 QStringList names;
    997                 names << (*m)->name();
    998                 if ((*m)->type() == Node::Function) {
    999                     const FunctionNode *func = reinterpret_cast<const FunctionNode *>(*m);
    1000                     if (func->metaness() == FunctionNode::Ctor || func->metaness() == FunctionNode::Dtor
    1001                             || func->overloadNumber() != 1)
    1002                         names.clear();
    1003                 } else if ((*m)->type() == Node::Property) {
    1004                     const PropertyNode *prop = reinterpret_cast<const PropertyNode *>(*m);
    1005                     if (!prop->getters().isEmpty() && !names.contains(prop->getters().first()->name()))
    1006                         names << prop->getters().first()->name();
    1007                     if (!prop->setters().isEmpty())
    1008                         names << prop->setters().first()->name();
    1009                     if (!prop->resetters().isEmpty())
    1010                         names << prop->resetters().first()->name();
    1011                 } else if ((*m)->type() == Node::Enum) {
    1012                     const EnumNode *enume = reinterpret_cast<const EnumNode *>(*m);
    1013                     if (enume->flagsType())
    1014                         names << enume->flagsType()->name();
    1015 
    1016                     foreach (const QString &enumName,
    1017                              enume->doc().enumItemNames().toSet()
    1018                              - enume->doc().omitEnumItemNames().toSet())
    1019                         names << plainCode(marker->markedUpEnumValue(enumName, enume));
    1020                 }
    1021                 foreach (const QString &name, names)
    1022                     classSection.keywords += qMakePair(name, linkForNode(*m, 0));
    1023             }
    1024             ++m;
    1025         }
    1026         ++s;
    1027     }
    1028     generateFooter(inner);
    1029 
    1030     if (!membersLink.isEmpty()) {
    1031         DcfSection membersSection;
    1032         membersSection.title = "List of all members";
    1033         membersSection.ref = membersLink;
    1034         appendDcfSubSection(&classSection, membersSection);
    1035     }
    1036     if (!obsoleteLink.isEmpty()) {
    1037         DcfSection obsoleteSection;
    1038         obsoleteSection.title = "Obsolete members";
    1039         obsoleteSection.ref = obsoleteLink;
    1040         appendDcfSubSection(&classSection, obsoleteSection);
    1041     }
    1042     if (!compatLink.isEmpty()) {
    1043         DcfSection compatSection;
    1044         compatSection.title = "Qt 3 support members";
    1045         compatSection.ref = compatLink;
    1046         appendDcfSubSection(&classSection, compatSection);
    1047     }
    1048 
    1049     appendDcfSubSection(&dcfClassesRoot, classSection);
    1050 }
    1051 
    1052 void HtmlGenerator::generateFakeNode(const FakeNode *fake, CodeMarker *marker)
    1053 {
    1054     SubTitleSize subTitleSize = LargeSubTitle;
    1055     DcfSection fakeSection;
    1056     fakeSection.title = fake->fullTitle();
    1057     fakeSection.ref = linkForNode(fake, 0);
    1058 
    1059     QList<Section> sections;
    1060     QList<Section>::const_iterator s;
    1061 
    1062     QString htmlTitle = fake->fullTitle();
    1063     if (fake->subType() == FakeNode::File && !fake->subTitle().isEmpty()) {
    1064         subTitleSize = SmallSubTitle;
    1065         htmlTitle += " (" + fake->subTitle() + ")";
    1066     }
    1067 
    1068     generateHeader(htmlTitle, fake, marker, true);
    1069     generateTitle(fake->fullTitle(), Text() << fake->subTitle(), subTitleSize,
    1070                   fake, marker);
    1071 
    1072     if (fake->subType() == FakeNode::Module) {
    1073         // Generate brief text and status for modules.
    1074         generateBrief(fake, marker);
    1075         generateStatus(fake, marker);
    1076 
    1077         if (moduleNamespaceMap.contains(fake->name())) {
    1078             out() << "<h2>Namespaces</h2>\n";
    1079             generateAnnotatedList(fake, marker, moduleNamespaceMap[fake->name()]);
    1080         }
    1081         if (moduleClassMap.contains(fake->name())) {
    1082             out() << "<h2>Classes</h2>\n";
    1083             generateAnnotatedList(fake, marker, moduleClassMap[fake->name()]);
    1084         }
    1085     }
    1086     else if (fake->subType() == FakeNode::HeaderFile) {
    1087         // Generate brief text and status for modules.
    1088         generateBrief(fake, marker);
    1089         generateStatus(fake, marker);
    1090 
    1091         out() << "<ul>\n";
    1092 
    1093         QString membersLink = generateListOfAllMemberFile(fake, marker);
    1094         if (!membersLink.isEmpty())
    1095             out() << "<li><a href=\"" << membersLink << "\">"
    1096                   << "List of all members, including inherited members</a></li>\n";
    1097 
    1098         QString obsoleteLink = generateLowStatusMemberFile(fake, marker, CodeMarker::Obsolete);
    1099         if (!obsoleteLink.isEmpty())
    1100             out() << "<li><a href=\"" << obsoleteLink << "\">"
    1101                   << "Obsolete members</a></li>\n";
    1102 
    1103         QString compatLink = generateLowStatusMemberFile(fake, marker, CodeMarker::Compat);
    1104         if (!compatLink.isEmpty())
    1105             out() << "<li><a href=\"" << compatLink << "\">"
    1106                   << "Qt 3 support members</a></li>\n";
    1107 
    1108         out() << "</ul>\n";
    1109 
    1110         if (!membersLink.isEmpty()) {
    1111             DcfSection membersSection;
    1112             membersSection.title = "List of all members";
    1113             membersSection.ref = membersLink;
    1114             appendDcfSubSection(&fakeSection, membersSection);
    1115         }
    1116         if (!obsoleteLink.isEmpty()) {
    1117             DcfSection obsoleteSection;
    1118             obsoleteSection.title = "Obsolete members";
    1119             obsoleteSection.ref = obsoleteLink;
    1120             appendDcfSubSection(&fakeSection, obsoleteSection);
    1121         }
    1122         if (!compatLink.isEmpty()) {
    1123             DcfSection compatSection;
    1124             compatSection.title = "Qt 3 support members";
    1125             compatSection.ref = compatLink;
    1126             appendDcfSubSection(&fakeSection, compatSection);
    1127         }
    1128     }
    1129 
    1130     sections = marker->sections(fake, CodeMarker::Summary, CodeMarker::Okay);
    1131     s = sections.begin();
    1132     while (s != sections.end()) {
    1133         out() << "<a name=\"" << registerRef((*s).name) << "\"></a>\n";
    1134         out() << "<h3>" << protect((*s).name) << "</h3>\n";
    1135         generateSectionList(*s, fake, marker, CodeMarker::Summary);
    1136         ++s;
    1137     }
    1138 
    1139     Text brief = fake->doc().briefText();
    1140     if (fake->subType() == FakeNode::Module && !brief.isEmpty()) {
    1141         out() << "<a name=\"" << registerRef("details") << "\"></a>\n";
    1142         out() << "<h2>" << "Detailed Description" << "</h2>\n";
    1143     }
    1144 
    1145     generateBody(fake, marker);
    1146 #ifdef QDOC_QML   
    1147     if (fake->subType() == FakeNode::QmlClass) {
    1148         //qDebug() << "generateFakeNode(): QML CLASS" << fake->name();
    1149         const QmlNode* qmlNode = static_cast<const QmlNode*>(fake);
    1150         const ClassNode* cn = qmlNode->classNode();
    1151         if (cn) {
    1152             //qDebug() << "  CPP CLASS" << cn->name();
    1153             generateQmlText(cn->doc().body(), cn, marker);
    1154         }
    1155     }
    1156 #endif   
    1157 
    1158     generateAlsoList(fake, marker);
    1159 
    1160     if (!fake->groupMembers().isEmpty()) {
    1161         QMap<QString, const Node *> groupMembersMap;
    1162         foreach (const Node *node, fake->groupMembers()) {
    1163             if (node->type() == Node::Class || node->type() == Node::Namespace)
    1164                 groupMembersMap[node->name()] = node;
    1165         }
    1166         generateAnnotatedList(fake, marker, groupMembersMap);
    1167     }
    1168 
    1169     fakeSection.keywords += qMakePair(fakeSection.title, fakeSection.ref);
    1170 
    1171     sections = marker->sections(fake, CodeMarker::Detailed, CodeMarker::Okay);
    1172     s = sections.begin();
    1173     while (s != sections.end()) {
    1174         out() << "<hr />\n";
    1175         out() << "<h2>" << protect((*s).name) << "</h2>\n";
    1176 
    1177         NodeList::ConstIterator m = (*s).members.begin();
    1178         while (m != (*s).members.end()) {
    1179             generateDetailedMember(*m, fake, marker);
    1180             fakeSection.keywords += qMakePair((*m)->name(), linkForNode(*m, 0));
    1181             ++m;
    1182         }
    1183         ++s;
    1184     }
    1185     generateFooter(fake);
    1186 
    1187     if (fake->subType() == FakeNode::Example) {
    1188         appendDcfSubSection(&dcfExamplesRoot, fakeSection);
    1189     }
    1190     else if (fake->subType() != FakeNode::File) {
    1191         QString contentsPage = fake->links().value(Node::ContentsLink).first;
    1192 
    1193         if (contentsPage == "Qt Designer Manual") {
    1194             appendDcfSubSection(&dcfDesignerRoot, fakeSection);
    1195         }
    1196         else if (contentsPage == "Qt Linguist Manual") {
    1197             appendDcfSubSection(&dcfLinguistRoot, fakeSection);
    1198         }
    1199         else if (contentsPage == "Qt Assistant Manual") {
    1200             appendDcfSubSection(&dcfAssistantRoot, fakeSection);
    1201         }
    1202         else if (contentsPage == "qmake Manual") {
    1203             appendDcfSubSection(&dcfQmakeRoot, fakeSection);
    1204         }
    1205         else {
    1206             appendDcfSubSection(&dcfOverviewsRoot, fakeSection);
    1207         }
    1208     }
    1209 }
    1210 
    1211 QString HtmlGenerator::fileExtension(const Node * /* node */)
    1212 {
    1213     return "html";
    1214 }
    1215 
    1216 void HtmlGenerator::generateHeader(const QString& title,
    1217                                    const Node *node,
    1218                                    CodeMarker *marker,
    1219                                    bool mainPage)
    1220 {
    1221     out() << "<?xml version=\"1.0\" encoding=\"iso-8859-1\"?>\n";
    1222 
    1223     out() << "<!DOCTYPE html\n"
    1224              "    PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"DTD/xhtml1-strict.dtd\">\n"
    1225              "<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\" lang=\"en\">\n";
    1226 
    1227     QString shortVersion;
    1228     if ((project != "Qtopia") && (project != "Qt Extended")) {
    1229         shortVersion = project + " " + shortVersion + ": ";
    1230         if (node && !node->doc().location().isEmpty())
    1231             out() << "<!-- " << node->doc().location().filePath() << " -->\n";
    1232 
    1233         shortVersion = tre->version();
    1234         if (shortVersion.count(QChar('.')) == 2)
    1235             shortVersion.truncate(shortVersion.lastIndexOf(QChar('.')));
    1236         if (!shortVersion.isEmpty()) {
    1237             if (project == "QSA")
    1238                 shortVersion = "QSA " + shortVersion + ": ";
    1239             else
    1240                 shortVersion = "Qt " + shortVersion + ": ";
    1241         }
    1242     }
    1243 
    1244     out() << "<head>\n"
    1245              "  <title>" << shortVersion << protect(title) << "</title>\n";
    1246     if (!style.isEmpty())
    1247         out() << "    <style type=\"text/css\">" << style << "</style>\n";
    1248 
    1249     const QMap<QString, QString> &metaMap = node->doc().metaTagMap();
    1250     if (!metaMap.isEmpty()) {
    1251         QMapIterator<QString, QString> i(metaMap);
    1252         while (i.hasNext()) {
    1253             i.next();
    1254             out() << "    <meta name=\"" << protect(i.key()) << "\" contents=\""
    1255                   << protect(i.value()) << "\" />\n";
    1256         }
    1257     }
    1258 
    1259     navigationLinks.clear();
    1260 
    1261     if (node && !node->links().empty()) {
    1262         QPair<QString,QString> linkPair;
    1263         QPair<QString,QString> anchorPair;
    1264         const Node *linkNode;
    1265 
    1266         if (node->links().contains(Node::PreviousLink)) {
    1267             linkPair = node->links()[Node::PreviousLink];
    1268             linkNode = findNodeForTarget(linkPair.first, node, marker);
    1269             if (!linkNode || linkNode == node)
    1270                 anchorPair = linkPair;
    1271             else
    1272                 anchorPair = anchorForNode(linkNode);
    1273 
    1274             out() << "  <link rel=\"prev\" href=\""
    1275                   << anchorPair.first << "\" />\n";
    1276 
    1277             navigationLinks += "[Previous: <a href=\"" + anchorPair.first + "\">";
    1278             if (linkPair.first == linkPair.second && !anchorPair.second.isEmpty())
    1279                 navigationLinks += protect(anchorPair.second);
    1280             else
    1281                 navigationLinks += protect(linkPair.second);
    1282             navigationLinks += "</a>]\n";
    1283         }
    1284         if (node->links().contains(Node::ContentsLink)) {
    1285             linkPair = node->links()[Node::ContentsLink];
    1286             linkNode = findNodeForTarget(linkPair.first, node, marker);
    1287             if (!linkNode || linkNode == node)
    1288                 anchorPair = linkPair;
    1289             else
    1290                 anchorPair = anchorForNode(linkNode);
    1291 
    1292             out() << "  <link rel=\"contents\" href=\""
    1293                   << anchorPair.first << "\" />\n";
    1294 
    1295             navigationLinks += "[<a href=\"" + anchorPair.first + "\">";
    1296             if (linkPair.first == linkPair.second && !anchorPair.second.isEmpty())
    1297                 navigationLinks += protect(anchorPair.second);
    1298             else
    1299                 navigationLinks += protect(linkPair.second);
    1300             navigationLinks += "</a>]\n";
    1301         }
    1302         if (node->links().contains(Node::NextLink)) {
    1303             linkPair = node->links()[Node::NextLink];
    1304             linkNode = findNodeForTarget(linkPair.first, node, marker);
    1305             if (!linkNode || linkNode == node)
    1306                 anchorPair = linkPair;
    1307             else
    1308                 anchorPair = anchorForNode(linkNode);
    1309 
    1310             out() << "  <link rel=\"next\" href=\""
    1311                   << anchorPair.first << "\" />\n";
    1312 
    1313             navigationLinks += "[Next: <a href=\"" + anchorPair.first + "\">";
    1314             if (linkPair.first == linkPair.second && !anchorPair.second.isEmpty())
    1315                 navigationLinks += protect(anchorPair.second);
    1316             else
    1317                 navigationLinks += protect(linkPair.second);
    1318             navigationLinks += "</a>]\n";
    1319         }
    1320         if (node->links().contains(Node::IndexLink)) {
    1321             linkPair = node->links()[Node::IndexLink];
    1322             linkNode = findNodeForTarget(linkPair.first, node, marker);
    1323             if (!linkNode || linkNode == node)
    1324                 anchorPair = linkPair;
    1325             else
    1326                 anchorPair = anchorForNode(linkNode);
    1327             out() << "  <link rel=\"index\" href=\""
    1328                   << anchorPair.first << "\" />\n";
    1329         }
    1330         if (node->links().contains(Node::StartLink)) {
    1331             linkPair = node->links()[Node::StartLink];
    1332             linkNode = findNodeForTarget(linkPair.first, node, marker);
    1333             if (!linkNode || linkNode == node)
    1334                 anchorPair = linkPair;
    1335             else
    1336                 anchorPair = anchorForNode(linkNode);
    1337             out() << "  <link rel=\"start\" href=\""
    1338                   << anchorPair.first << "\" />\n";
    1339         }
    1340     }
    1341 
    1342     foreach (const QString &stylesheet, stylesheets) {
    1343         out() << "  <link href=\"" << stylesheet << "\" rel=\"stylesheet\" "
    1344               << "type=\"text/css\" />\n";
    1345     }
    1346 
    1347     foreach (const QString &customHeadElement, customHeadElements) {
    1348         out() << "  " << customHeadElement << "\n";
    1349     }
    1350 
    1351     out() << "</head>\n"
    1352              "<body>\n";
    1353     if (mainPage)
    1354         generateMacRef(node, marker);
    1355     out() << QString(postHeader).replace("\\" + COMMAND_VERSION, tre->version());
    1356 
    1357 
    1358     if (node && !node->links().empty())
    1359         out() << "<p>\n" << navigationLinks << "</p>\n";
    1360 }
    1361 
    1362 void HtmlGenerator::generateTitle(const QString& title,
    1363                                   const Text &subTitle,
    1364                                   SubTitleSize subTitleSize,
    1365                                   const Node *relative,
    1366                                   CodeMarker *marker)
    1367 {
    1368     out() << "<h1 class=\"title\">" << protect(title);
    1369     if (!subTitle.isEmpty()) {
    1370         out() << "<br />";
    1371         if (subTitleSize == SmallSubTitle)
    1372             out() << "<span class=\"small-subtitle\">";
    1373         else
    1374             out() << "<span class=\"subtitle\">";
    1375         generateText(subTitle, relative, marker);
    1376         out() << "</span>\n";
    1377     }
    1378     out() << "</h1>\n";
    1379 }
    1380 
    1381 void HtmlGenerator::generateFooter(const Node *node)
    1382 {
    1383     if (node && !node->links().empty())
    1384         out() << "<p>\n" << navigationLinks << "</p>\n";
    1385 
    1386     out() << QString(footer).replace("\\" + COMMAND_VERSION, tre->version())
    1387           << QString(address).replace("\\" + COMMAND_VERSION, tre->version())
    1388           << "</body>\n"
    1389              "</html>\n";
    1390 }
    1391 
    1392 void HtmlGenerator::generateBrief(const Node *node, CodeMarker *marker,
    1393                                   const Node *relative)
    1394 {
    1395     Text brief = node->doc().briefText();
    1396     if (!brief.isEmpty()) {
    1397         out() << "<p>";
    1398         generateText(brief, node, marker);
    1399         if (!relative || node == relative)
    1400             out() << " <a href=\"#";
    1401         else
    1402             out() << " <a href=\"" << linkForNode(node, relative) << "#";
    1403         out() << registerRef("details") << "\">More...</a></p>\n";
    1404     }
    1405 }
    1406 
    1407 void HtmlGenerator::generateIncludes(const InnerNode *inner, CodeMarker *marker)
    1408 {
    1409     if (!inner->includes().isEmpty()) {
    1410         out() << "<pre>" << trimmedTrailing(highlightedCode(indent(codeIndent,
    1411                                                                    marker->markedUpIncludes(
    1412                                                                         inner->includes())),
    1413                                                                         marker, inner))
    1414               << "</pre>";
    1415     }
    1416 }
    1417 
    1418 void HtmlGenerator::generateTableOfContents(const Node *node, CodeMarker *marker,
    1419                                             Doc::SectioningUnit sectioningUnit,
    1420                                             int numColumns, const Node *relative)
    1421 
    1422 {
    1423     if (!node->doc().hasTableOfContents())
    1424         return;
    1425     QList<Atom *> toc = node->doc().tableOfContents();
    1426     if (toc.isEmpty())
    1427         return;
    1428 
    1429     QString nodeName = "";
    1430     if (node != relative)
    1431         nodeName = node->name();
    1432 
    1433     QStringList sectionNumber;
    1434     int columnSize = 0;
    1435 
    1436     QString tdTag;
    1437     if (numColumns > 1) {
    1438         tdTag = "<td width=\"" + QString::number((100 + numColumns - 1) / numColumns) + "%\">";
    1439         out() << "<p><table width=\"100%\">\n<tr valign=\"top\">" << tdTag << "\n";
    1440     }
    1441 
    1442     // disable nested links in table of contents
    1443     inContents = true;
    1444     inLink = true;
    1445 
    1446     for (int i = 0; i < toc.size(); ++i) {
    1447         Atom *atom = toc.at(i);
    1448 
    1449         int nextLevel = atom->string().toInt();
    1450         if (nextLevel > (int)sectioningUnit)
    1451             continue;
    1452 
    1453         if (sectionNumber.size() < nextLevel) {
    1454             do {
    1455                 out() << "<ul>";
    1456                 sectionNumber.append("1");
    1457             } while (sectionNumber.size() < nextLevel);
    1458         } else {
    1459             while (sectionNumber.size() > nextLevel) {
    1460                 out() << "</ul>\n";
    1461                 sectionNumber.removeLast();
    1462             }
    1463             sectionNumber.last() = QString::number(sectionNumber.last().toInt() + 1);
    1464         }
    1465         int numAtoms;
    1466         Text headingText = Text::sectionHeading(atom);
    1467 
    1468         if (sectionNumber.size() == 1 && columnSize > toc.size() / numColumns) {
    1469             out() << "</ul></td>" << tdTag << "<ul>\n";
    1470             columnSize = 0;
    1471         }
    1472         out() << "<li>";
    1473         out() << "<a href=\"" << nodeName << "#" << Doc::canonicalTitle(headingText.toString())
    1474               << "\">";
    1475         generateAtomList(headingText.firstAtom(), node, marker, true, numAtoms);
    1476         out() << "</a></li>\n";
    1477 
    1478         ++columnSize;
    1479     }
    1480     while (!sectionNumber.isEmpty()) {
    1481         out() << "</ul>\n";
    1482         sectionNumber.removeLast();
    1483     }
    1484 
    1485     if (numColumns > 1)
    1486         out() << "</td></tr></table></p>\n";
    1487 
    1488     inContents = false;
    1489     inLink = false;
    1490 }
    1491 
    1492 #if 0
    1493 void HtmlGenerator::generateNavigationBar(const NavigationBar& bar,
    1494                                            const Node *node,
    1495                                            CodeMarker *marker)
    1496 {
    1497     if (bar.prev.begin() != 0 || bar.current.begin() != 0 ||
    1498          bar.next.begin() != 0) {
    1499         out() << "<p align=\"right\">";
    1500         if (bar.prev.begin() != 0) {
    1501 #if 0
    1502             out() << "[<a href=\"" << section.previousBaseName()
    1503                   << ".html\">Prev: ";
    1504             generateText(section.previousHeading(), node, marker);
    1505             out() << "</a>]\n";
    1506 #endif
    1507         }
    1508         if (bar.current.begin() != 0) {
    1509             out() << "[<a href=\"" << "home"
    1510                   << ".html\">Home</a>]\n";
    1511         }
    1512         if (bar.next.begin() != 0) {
    1513             out() << "[<a href=\"" << fileBase(node, bar.next)
    1514                   << ".html\">Next: ";
    1515             generateText(Text::sectionHeading(bar.next.begin()), node, marker);
    1516             out() << "</a>]\n";
    1517         }
    1518         out() << "</p>\n";
    1519     }
    1520 }
    1521 #endif
    1522 
    1523 QString HtmlGenerator::generateListOfAllMemberFile(const InnerNode *inner, CodeMarker *marker)
    1524 {
    1525     QList<Section> sections;
    1526     QList<Section>::ConstIterator s;
    1527 
    1528     sections = marker->sections(inner, CodeMarker::SeparateList, CodeMarker::Okay);
    1529     if (sections.isEmpty())
    1530         return QString();
    1531 
    1532     QString fileName = fileBase(inner) + "-members." + fileExtension(inner);
    1533     beginSubPage(inner->location(), fileName);
    1534     QString title = "List of All Members for " + inner->name();
    1535     generateHeader(title, inner, marker, false);
    1536     generateTitle(title, Text(), SmallSubTitle, inner, marker);
    1537     out() << "<p>This is the complete list of members for ";
    1538     generateFullName(inner, 0, marker);
    1539     out() << ", including inherited members.</p>\n";
    1540 
    1541     Section section = sections.first();
    1542     generateSectionList(section, 0, marker, CodeMarker::SeparateList);
    1543 
    1544     generateFooter();
    1545     endSubPage();
    1546     return fileName;
    1547 }
    1548 
    1549 QString HtmlGenerator::generateLowStatusMemberFile(const InnerNode *inner, CodeMarker *marker,
    1550                                                    CodeMarker::Status status)
    1551 {
    1552     QList<Section> sections = marker->sections(inner, CodeMarker::Summary, status);
    1553     QMutableListIterator<Section> j(sections);
    1554     while (j.hasNext()) {
    1555         if (j.next().members.size() == 0)
    1556             j.remove();
    1557     }
    1558     if (sections.isEmpty())
    1559         return QString();
    1560 
    1561     int i;
    1562 
    1563     QString title;
    1564     QString fileName;
    1565 
    1566     if (status == CodeMarker::Compat) {
    1567         title = "Qt 3 Support Members for " + inner->name();
    1568         fileName = fileBase(inner) + "-qt3." + fileExtension(inner);
    1569     } else {
    1570         title = "Obsolete Members for " + inner->name();
    1571         fileName = fileBase(inner) + "-obsolete." + fileExtension(inner);
    1572     }
    1573 
    1574     beginSubPage(inner->location(), fileName);
    1575     generateHeader(title, inner, marker, false);
    1576     generateTitle(title, Text(), SmallSubTitle, inner, marker);
    1577 
    1578     if (status == CodeMarker::Compat) {
    1579         out() << "<p><b>The following class members are part of the "
    1580                  "<a href=\"qt3support.html\">Qt 3 support layer</a>.</b> "
    1581                  "They are provided to help you port old code to Qt 4. We advise against "
    1582                  "using them in new code.</p>\n";
    1583     } else {
    1584         out() << "<p><b>The following class members are obsolete.</b> They are provided to keep "
    1585                  "old source code working. We strongly advise against using them in new "
    1586                  "code.</p>\n";
    1587     }
    1588 
    1589     out() << "<p><ul><li><a href=\"" << linkForNode(inner, 0) << "\">" << protect(inner->name())
    1590           << " class reference</a></li></ul></p>\n";
    1591 
    1592     for (i = 0; i < sections.size(); ++i) {
    1593         out() << "<h3>" << protect(sections.at(i).name) << "</h3>\n";
    1594 
    1595         generateSectionList(sections.at(i), inner, marker, CodeMarker::Summary);
    1596     }
    1597 
    1598     sections = marker->sections(inner, CodeMarker::Detailed, status);
    1599     for (i = 0; i < sections.size(); ++i) {
    1600         out() << "<hr />\n";
    1601         out() << "<h2>" << protect(sections.at(i).name) << "</h2>\n";
    1602 
    1603         NodeList::ConstIterator m = sections.at(i).members.begin();
    1604         while (m != sections.at(i).members.end()) {
    1605             if ((*m)->access() != Node::Private)
    1606                 generateDetailedMember(*m, inner, marker);
    1607             ++m;
    1608         }
    1609     }
    1610 
    1611     generateFooter();
    1612     endSubPage();
    1613     return fileName;
    1614 }
    1615 
    1616 void HtmlGenerator::generateClassHierarchy(const Node *relative, CodeMarker *marker,
    1617                                            const QMap<QString, const Node *> &classMap)
    1618 {
    1619     if (classMap.isEmpty())
    1620         return;
    1621 
    1622     QMap<QString, const Node *> topLevel;
    1623     QMap<QString, const Node *>::ConstIterator c = classMap.begin();
    1624     while (c != classMap.end()) {
    1625         const ClassNode *classe = static_cast<const ClassNode *>(*c);
    1626         if (classe->baseClasses().isEmpty())
    1627             topLevel.insert(classe->name(), classe);
    1628         ++c;
    1629     }
    1630 
    1631     QStack<QMap<QString, const Node *> > stack;
    1632     stack.push(topLevel);
    1633 
    1634     out() << "<ul>\n";
    1635     while (!stack.isEmpty()) {
    1636         if (stack.top().isEmpty()) {
    1637             stack.pop();
    1638             out() << "</ul>\n";
    1639         } else {
    1640             const ClassNode *child = static_cast<const ClassNode *>(*stack.top().begin());
    1641             out() << "<li>";
    1642             generateFullName(child, relative, marker);
    1643             out() << "</li>\n";
    1644             stack.top().erase(stack.top().begin());
    1645 
    1646             QMap<QString, const Node *> newTop;
    1647             foreach (const RelatedClass &d, child->derivedClasses()) {
    1648                 if (d.access != Node::Private)
    1649                     newTop.insert(d.node->name(), d.node);
    1650             }
    1651             if (!newTop.isEmpty()) {
    1652                 stack.push(newTop);
    1653                 out() << "<ul>\n";
    1654             }
    1655         }
    1656     }
    1657 }
    1658 
    1659 void HtmlGenerator::generateAnnotatedList(const Node *relative, CodeMarker *marker,
    1660                     const QMap<QString, const Node *> &nodeMap)
    1661 {
    1662     out() << "<p><table width=\"100%\" class=\"annotated\" cellpadding=\"2\" cellspacing=\"1\" border=\"0\">\n";
    1663 
    1664     int row = 0;
    1665     foreach (const QString &name, nodeMap.keys()) {
    1666         const Node *node = nodeMap[name];
    1667 
    1668         if (++row % 2 == 1)
    1669             out() << "<tr valign=\"top\" class=\"odd\">";
    1670         else
    1671             out() << "<tr valign=\"top\" class=\"even\">";
    1672         out() << "<th>";
    1673         generateFullName(node, relative, marker);
    1674         out() << "</th>";
    1675 
    1676         if (!(node->type() == Node::Fake)) {
    1677             Text brief = node->doc().trimmedBriefText(name);
    1678             if (!brief.isEmpty()) {
    1679                 out() << "<td>";
    1680                 generateText(brief, node, marker);
    1681                 out() << "</td>";
    1682             }
    1683         } else {
    1684             out() << "<td>";
    1685             out() << protect(node->doc().briefText().toString());
    1686             out() << "</td>";
    1687         }
    1688         out() << "</tr>\n";
    1689     }
    1690     out() << "</table></p>\n";
    1691 }
    1692 
    1693 void HtmlGenerator::generateCompactList(const Node *relative, CodeMarker *marker,
    1694                                         const QMap<QString, const Node *> &classMap)
    1695 {
    1696     const int NumParagraphs = 37; // '0' to '9', 'A' to 'Z', '_'
    1697     const int NumColumns = 4; // number of columns in the result
    1698 
    1699     if (classMap.isEmpty())
    1700         return;
    1701 
    1702     /*
    1703       First, find out the common prefix of all non-namespaced classes.
    1704       For Qt, the prefix is Q. It can easily be derived from the first
    1705       and last classes in alphabetical order (QAccel and QXtWidget in Qt 2.1).
    1706     */
    1707     int commonPrefixLen = 0;
    1708     QString commonPrefix;
    1709     QString first;
    1710     QString last;
    1711 
    1712     QMap<QString, const Node *>::const_iterator iter = classMap.begin();
    1713     while (iter != classMap.end()) {
    1714         if (!iter.key().contains("::")) {
    1715             first = iter.key();
    1716             break;
    1717         }
    1718         ++iter;
    1719     }
    1720 
    1721     if (first.isEmpty())
    1722         first = classMap.begin().key();
    1723 
    1724     iter = classMap.end();
    1725     while (iter != classMap.begin()) {
    1726         --iter;
    1727         if (!iter.key().contains("::")) {
    1728             last = iter.key();
    1729             break;
    1730         }
    1731     }
    1732 
    1733     if (last.isEmpty())
    1734         last = classMap.begin().key();
    1735 
    1736     if (classMap.size() > 1) {
    1737         while (commonPrefixLen < first.length() + 1 && commonPrefixLen < last.length() + 1
    1738                && first[commonPrefixLen] == last[commonPrefixLen])
    1739             ++commonPrefixLen;
    1740     }
    1741 
    1742     commonPrefix = first.left(commonPrefixLen);
    1743 
    1744     /*
    1745       Divide the data into 37 paragraphs: 0, ..., 9, A, ..., Z,
    1746       underscore (_). QAccel will fall in paragraph 10 (A) and
    1747       QXtWidget in paragraph 33 (X). This is the only place where we
    1748       assume that NumParagraphs is 37. Each paragraph is a
    1749       QMap<QString, const Node *>.
    1750     */
    1751     QMap<QString, const Node *> paragraph[NumParagraphs];
    1752     QString paragraphName[NumParagraphs];
    1753 
    1754     QMap<QString, const Node *>::ConstIterator c = classMap.begin();
    1755     while (c != classMap.end()) {
    1756         QStringList pieces = c.key().split("::");
    1757         QString key;
    1758         if (pieces.size() == 1)
    1759             key = pieces.last().mid(commonPrefixLen).toLower();
    1760         else
    1761             key = pieces.last().toLower();
    1762 
    1763         int paragraphNo = NumParagraphs - 1;
    1764 
    1765         if (key[0].digitValue() != -1) {
    1766             paragraphNo = key[0].digitValue();
    1767         } else if (key[0] >= QLatin1Char('a') && key[0] <= QLatin1Char('z')) {
    1768             paragraphNo = 10 + key[0].unicode() - 'a';
    1769         }
    1770 
    1771         paragraphName[paragraphNo] = key[0].toUpper();
    1772         paragraph[paragraphNo].insert(key, c.value());
    1773         ++c;
    1774     }
    1775 
    1776     /*
    1777       Each paragraph j has a size: paragraph[j].count(). In the
    1778       discussion, we will assume paragraphs 0 to 5 will have sizes
    1779       3, 1, 4, 1, 5, 9.
    1780 
    1781       We now want to compute the paragraph offset. Paragraphs 0 to 6
    1782       start at offsets 0, 3, 4, 8, 9, 14, 23.
    1783     */
    1784     int paragraphOffset[NumParagraphs + 1];
    1785     int i, j, k;
    1786 
    1787     paragraphOffset[0] = 0;
    1788     for (j = 0; j < NumParagraphs; j++)
    1789         paragraphOffset[j + 1] = paragraphOffset[j] + paragraph[j].count();
    1790 
    1791     int firstOffset[NumColumns + 1];
    1792     int currentOffset[NumColumns];
    1793     int currentParagraphNo[NumColumns];
    1794     int currentOffsetInParagraph[NumColumns];
    1795 
    1796     int numRows = (classMap.count() + NumColumns - 1) / NumColumns;
    1797     int curParagNo = 0;
    1798 
    1799     for (i = 0; i < NumColumns; i++) {
    1800         firstOffset[i] = qMin(i * numRows, classMap.size());
    1801         currentOffset[i] = firstOffset[i];
    1802 
    1803         for (j = curParagNo; j < NumParagraphs; j++) {
    1804             if (paragraphOffset[j] > firstOffset[i])
    1805                 break;
    1806             if (paragraphOffset[j] <= firstOffset[i])
    1807                 curParagNo = j;
    1808         }
    1809         currentParagraphNo[i] = curParagNo;
    1810         currentOffsetInParagraph[i] = firstOffset[i] -
    1811                                       paragraphOffset[curParagNo];
    1812     }
    1813     firstOffset[NumColumns] = classMap.count();
    1814 
    1815     out() << "<p><table width=\"100%\">\n";
    1816     for (k = 0; k < numRows; k++) {
    1817         out() << "<tr>\n";
    1818         for (i = 0; i < NumColumns; i++) {
    1819             if (currentOffset[i] >= firstOffset[i + 1]) {
    1820                 // this column is finished
    1821                 out() << "<td>\n</td>\n";
    1822             } else {
    1823                 while (currentOffsetInParagraph[i] == paragraph[currentParagraphNo[i]].count()) {
    1824                     ++currentParagraphNo[i];
    1825                     currentOffsetInParagraph[i] = 0;
    1826                 }
    1827 
    1828                 out() << "<td align=\"right\">";
    1829                 if (currentOffsetInParagraph[i] == 0) {
    1830                     // start a new paragraph
    1831                     out() << "<b>" << paragraphName[currentParagraphNo[i]] << "&nbsp;</b>";
    1832                 }
    1833                 out() << "</td>\n";
    1834 
    1835                 // bad loop
    1836                 QMap<QString, const Node *>::Iterator it;
    1837                 it = paragraph[currentParagraphNo[i]].begin();
    1838                 for (j = 0; j < currentOffsetInParagraph[i]; j++)
    1839                     ++it;
    1840 
    1841                 out() << "<td>";
    1842                 // Previously, we used generateFullName() for this, but we
    1843                 // require some special formatting.
    1844                 out() << "<a href=\"" << linkForNode(it.value(), relative) << "\">";
    1845                 QStringList pieces = fullName(it.value(), relative, marker).split("::");
    1846                 out() << protect(pieces.last());
    1847                 out() << "</a>";
    1848                 if (pieces.size() > 1) {
    1849                     out() << " (";
    1850                     generateFullName(it.value()->parent(), relative, marker);
    1851                     out() << ")";
    1852                 }
    1853                 out() << "</td>\n";
    1854 
    1855                 currentOffset[i]++;
    1856                 currentOffsetInParagraph[i]++;
    1857             }
    1858         }
    1859         out() << "</tr>\n";
    1860     }
    1861     out() << "</table></p>\n";
    1862 }
    1863 
    1864 void HtmlGenerator::generateFunctionIndex(const Node *relative, CodeMarker *marker)
    1865 {
    1866     out() << "<p align=\"center\"><font size=\"+1\"><b>";
    1867     for (int i = 0; i < 26; i++) {
    1868         QChar ch('a' + i);
    1869         out() << QString("<a href=\"#%1\">%2</a>&nbsp;").arg(ch).arg(ch.toUpper());
    1870     }
    1871     out() << "</b></font></p>\n";
    1872 
    1873     char nextLetter = 'a';
    1874     char currentLetter;
    1875 
    1876 #if 1
    1877     out() << "<ul>\n";
    1878 #endif
    1879     QMap<QString, QMap<QString, const Node *> >::ConstIterator f = funcIndex.begin();
    1880     while (f != funcIndex.end()) {
    1881 #if 1
    1882         out() << "<li>";
    1883 #else
    1884         out() << "<p>";
    1885 #endif
    1886         out() << protect(f.key()) << ":";
    1887 
    1888         currentLetter = f.key()[0].unicode();
    1889         while (islower(currentLetter) && currentLetter >= nextLetter) {
    1890             out() << QString("<a name=\"%1\"></a>").arg(nextLetter);
    1891             nextLetter++;
    1892         }
    1893 
    1894         QMap<QString, const Node *>::ConstIterator s = (*f).begin();
    1895         while (s != (*f).end()) {
    1896             out() << " ";
    1897             generateFullName((*s)->parent(), relative, marker, *s);
    1898             ++s;
    1899         }
    1900 #if 1
    1901         out() << "</li>";
    1902 #else
    1903         out() << "</p>";
    1904 #endif
    1905         out() << "\n";
    1906         ++f;
    1907     }
    1908 #if 1
    1909     out() << "</ul>\n";
    1910 #endif
    1911 }
    1912 
    1913 void HtmlGenerator::generateLegaleseList(const Node *relative, CodeMarker *marker)
    1914 {
    1915     QMap<Text, const Node *>::ConstIterator it = legaleseTexts.begin();
    1916     while (it != legaleseTexts.end()) {
    1917         Text text = it.key();
    1918         out() << "<hr />\n";
    1919         generateText(text, relative, marker);
    1920         out() << "<ul>\n";
    1921         do {
    1922             out() << "<li>";
    1923             generateFullName(it.value(), relative, marker);
    1924             out() << "</li>\n";
    1925             ++it;
    1926         } while (it != legaleseTexts.end() && it.key() == text);
    1927         out() << "</ul>\n";
    1928     }
    1929 }
    1930 
    1931 void HtmlGenerator::generateSynopsis(const Node *node, const Node *relative,
    1932                                      CodeMarker *marker, CodeMarker::SynopsisStyle style)
    1933 {
    1934     QString marked = marker->markedUpSynopsis(node, relative, style);
    1935     QRegExp templateTag("(<[^@>]*>)");
    1936     if (marked.indexOf(templateTag) != -1) {
    1937         QString contents = protect(marked.mid(templateTag.pos(1),
    1938                                               templateTag.cap(1).length()));
    1939         marked.replace(templateTag.pos(1), templateTag.cap(1).length(),
    1940                         contents);
    1941     }
    1942     marked.replace(QRegExp("<@param>([a-z]+)_([1-9n])</@param>"), "<i>\\1<sub>\\2</sub></i>");
    1943     marked.replace("<@param>", "<i>");
    1944     marked.replace("</@param>", "</i>");
    1945 
    1946     if (style == CodeMarker::Summary)
    1947         marked.replace("@name>", "b>");
    1948 
    1949     if (style == CodeMarker::SeparateList) {
    1950         QRegExp extraRegExp("<@extra>.*</@extra>");
    1951         extraRegExp.setMinimal(true);
    1952         marked.replace(extraRegExp, "");
    1953     } else {
    1954         marked.replace("<@extra>", "&nbsp;&nbsp;<tt>");
    1955         marked.replace("</@extra>", "</tt>");
    1956     }
    1957 
    1958     if (style != CodeMarker::Detailed) {
    1959         marked.replace("<@type>", "");
    1960         marked.replace("</@type>", "");
    1961     }
    1962     out() << highlightedCode(marked, marker, relative);
    1963 }
    1964 
    1965 void HtmlGenerator::generateOverviewList(const Node *relative, CodeMarker * /* marker */)
    1966 {
    1967     QMap<const FakeNode *, QMap<QString, FakeNode *> > fakeNodeMap;
    1968     QMap<QString, const FakeNode *> groupTitlesMap;
    1969     QMap<QString, FakeNode *> uncategorizedNodeMap;
    1970     QRegExp singleDigit("\\b([0-9])\\b");
    1971 
    1972     const NodeList children = tre->root()->childNodes();
    1973     foreach (Node *child, children) {
    1974         if (child->type() == Node::Fake && child != relative) {
    1975             FakeNode *fakeNode = static_cast<FakeNode *>(child);
    1976 
    1977             // Check whether the page is part of a group or is the group
    1978             // definition page.
    1979             QString group;
    1980             bool isGroupPage = false;
    1981             if (fakeNode->doc().metaCommandsUsed().contains("group")) {
    1982                 group = fakeNode->doc().metaCommandArgs("group")[0];
    1983                 isGroupPage = true;
    1984             }
    1985 
    1986             // there are too many examples; they would clutter the list
    1987             if (fakeNode->subType() == FakeNode::Example)
    1988                 continue;
    1989 
    1990             // not interested either in individual (Qt Designer etc.) manual chapters
    1991             if (fakeNode->links().contains(Node::ContentsLink))
    1992                 continue;
    1993 
    1994             // Discard external nodes.
    1995             if (fakeNode->subType() == FakeNode::ExternalPage)
    1996                 continue;
    1997 
    1998             QString sortKey = fakeNode->fullTitle().toLower();
    1999             if (sortKey.startsWith("the "))
    2000                 sortKey.remove(0, 4);
    2001             sortKey.replace(singleDigit, "0\\1");
    2002 
    2003             if (!group.isEmpty()) {
    2004                 if (isGroupPage) {
    2005                     // If we encounter a group definition page, we add all
    2006                     // the pages in that group to the list for that group.
    2007                     foreach (Node *member, fakeNode->groupMembers()) {
    2008                         if (member->type() != Node::Fake)
    2009                             continue;
    2010                         FakeNode *page = static_cast<FakeNode *>(member);
    2011                         if (page) {
    2012                             QString sortKey = page->fullTitle().toLower();
    2013                             if (sortKey.startsWith("the "))
    2014                                 sortKey.remove(0, 4);
    2015                             sortKey.replace(singleDigit, "0\\1");
    2016                             fakeNodeMap[const_cast<const FakeNode *>(fakeNode)].insert(sortKey, page);
    2017                             groupTitlesMap[fakeNode->fullTitle()] = const_cast<const FakeNode *>(fakeNode);
    2018                         }
    2019                     }
    2020                 } else if (!isGroupPage) {
    2021                     // If we encounter a page that belongs to a group then
    2022                     // we add that page to the list for that group.
    2023                     const FakeNode *groupNode = static_cast<const FakeNode *>(tre->root()->findNode(group, Node::Fake));
    2024                     if (groupNode)
    2025                         fakeNodeMap[groupNode].insert(sortKey, fakeNode);
    2026                     //else
    2027                     //    uncategorizedNodeMap.insert(sortKey, fakeNode);
    2028                 }// else
    2029                 //    uncategorizedNodeMap.insert(sortKey, fakeNode);
    2030             }// else
    2031             //    uncategorizedNodeMap.insert(sortKey, fakeNode);
    2032         }
    2033     }
    2034 
    2035     // We now list all the pages found that belong to groups.
    2036     // If only certain pages were found for a group, but the definition page
    2037     // for that group wasn't listed, the list of pages will be intentionally
    2038     // incomplete. However, if the group definition page was listed, all the
    2039     // pages in that group are listed for completeness.
    2040 
    2041     if (!fakeNodeMap.isEmpty()) {
    2042         foreach (const QString &groupTitle, groupTitlesMap.keys()) {
    2043             const FakeNode *groupNode = groupTitlesMap[groupTitle];
    2044             out() << QString("<h3><a href=\"%1\">%2</a></h3>\n").arg(
    2045                         linkForNode(groupNode, relative)).arg(
    2046                         protect(groupNode->fullTitle()));
    2047 
    2048             if (fakeNodeMap[groupNode].count() == 0)
    2049                 continue;
    2050 
    2051             out() << "<ul>\n";
    2052 
    2053             foreach (const FakeNode *fakeNode, fakeNodeMap[groupNode]) {
    2054                 QString title = fakeNode->fullTitle();
    2055                 if (title.startsWith("The "))
    2056                     title.remove(0, 4);
    2057                 out() << "<li><a href=\"" << linkForNode(fakeNode, relative) << "\">"
    2058                       << protect(title) << "</a></li>\n";
    2059             }
    2060             out() << "</ul>\n";
    2061         }
    2062     }
    2063 
    2064     if (!uncategorizedNodeMap.isEmpty()) {
    2065         out() << QString("<h3>Miscellaneous</h3>\n");
    2066         out() << "<ul>\n";
    2067         foreach (const FakeNode *fakeNode, uncategorizedNodeMap) {
    2068             QString title = fakeNode->fullTitle();
    2069             if (title.startsWith("The "))
    2070                 title.remove(0, 4);
    2071             out() << "<li><a href=\"" << linkForNode(fakeNode, relative) << "\">"
    2072                   << protect(title) << "</a></li>\n";
    2073         }
    2074         out() << "</ul>\n";
    2075     }
    2076 }
    2077 
    2078 void HtmlGenerator::generateSectionList(const Section& section, const Node *relative,
    2079                                         CodeMarker *marker, CodeMarker::SynopsisStyle style)
    2080 {
    2081     if (!section.members.isEmpty()) {
    2082         bool twoColumn = false;
    2083         if (style == CodeMarker::SeparateList) {
    2084             twoColumn = (section.members.count() >= 16);
    2085         } else if (section.members.first()->type() == Node::Property) {
    2086             twoColumn = (section.members.count() >= 5);
    2087         }
    2088         if (twoColumn)
    2089             out() << "<p><table width=\"100%\" border=\"0\" cellpadding=\"0\""
    2090                      " cellspacing=\"0\">\n"
    2091                   << "<tr><td width=\"45%\" valign=\"top\">";
    2092         out() << "<ul>\n";
    2093 
    2094         int i = 0;
    2095         NodeList::ConstIterator m = section.members.begin();
    2096         while (m != section.members.end()) {
    2097             if ((*m)->access() == Node::Private) {
    2098                 ++m;
    2099                 continue;
    2100             }
    2101 
    2102             if (twoColumn && i == (int) (section.members.count() + 1) / 2)
    2103                 out() << "</ul></td><td valign=\"top\"><ul>\n";
    2104 
    2105             out() << "<li><div class=\"fn\"></div>";
    2106             if (style == CodeMarker::Accessors)
    2107                 out() << "<b>";
    2108             generateSynopsis(*m, relative, marker, style);
    2109             if (style == CodeMarker::Accessors)
    2110                 out() << "</b>";
    2111             out() << "</li>\n";
    2112             i++;
    2113             ++m;
    2114         }
    2115         out() << "</ul>\n";
    2116         if (twoColumn)
    2117             out() << "</td></tr>\n</table></p>\n";
    2118     }
    2119 
    2120     if (style == CodeMarker::Summary && !section.inherited.isEmpty()) {
    2121         out() << "<ul>\n";
    2122         generateSectionInheritedList(section, relative, marker);
    2123         out() << "</ul>\n";
    2124     }
    2125 }
    2126 
    2127 void HtmlGenerator::generateSectionInheritedList(const Section& section, const Node *relative,
    2128                                                  CodeMarker *marker)
    2129 {
    2130     QList<QPair<ClassNode *, int> >::ConstIterator p = section.inherited.begin();
    2131     while (p != section.inherited.end()) {
    2132         out() << "<li><div class=\"fn\"></div>";
    2133         out() << (*p).second << " ";
    2134         if ((*p).second == 1) {
    2135             out() << section.singularMember;
    2136         } else {
    2137             out() << section.pluralMember;
    2138         }
    2139         out() << " inherited from <a href=\"" << fileName((*p).first)
    2140               << "#" << HtmlGenerator::cleanRef(section.name.toLower()) << "\">"
    2141               << protect(marker->plainFullName((*p).first, relative))
    2142               << "</a></li>\n";
    2143         ++p;
    2144     }
    2145 }
    2146 
    2147 void HtmlGenerator::generateLink(const Atom *atom, const Node * /* relative */, CodeMarker *marker)
    2148 {
    2149     static QRegExp camelCase("[A-Z][A-Z][a-z]|[a-z][A-Z0-9]|_");
    2150 
    2151     if (funcLeftParen.indexIn(atom->string()) != -1 && marker->recognizeLanguage("Cpp")) {
    2152         // hack for C++: move () outside of link
    2153         int k = funcLeftParen.pos(1);
    2154         out() << protect(atom->string().left(k));
    2155         if (link.isEmpty()) {
    2156             if (showBrokenLinks)
    2157                 out() << "</i>";
    2158         } else {
    2159             out() << "</a>";
    2160         }
    2161         inLink = false;
    2162         out() << protect(atom->string().mid(k));
    2163     } else if (marker->recognizeLanguage("Java")) {
    2164         // hack for Java: remove () and use <tt> when appropriate
    2165         bool func = atom->string().endsWith("()");
    2166         bool tt = (func || atom->string().contains(camelCase));
    2167         if (tt)
    2168             out() << "<tt>";
    2169         if (func) {
    2170             out() << protect(atom->string().left(atom->string().length() - 2));
    2171         } else {
    2172             out() << protect(atom->string());
    2173         }
    2174         out() << "</tt>";
    2175     } else {
    2176         out() << protect(atom->string());
    2177     }
    2178 }
    2179 
    2180 QString HtmlGenerator::cleanRef(const QString& ref)
    2181 {
    2182     QString clean;
    2183 
    2184     if (ref.isEmpty())
    2185         return clean;
    2186 
    2187     clean.reserve(ref.size() + 20);
    2188     const QChar c = ref[0];
    2189     const uint u = c.unicode();
    2190 
    2191     if ((u >= 'a' && u <= 'z') ||
    2192          (u >= 'A' && u <= 'Z') ||
    2193          (u >= '0' && u <= '9')) {
    2194         clean += c;
    2195     } else if (u == '~') {
    2196         clean += "dtor.";
    2197     } else if (u == '_') {
    2198         clean += "underscore.";
    2199     } else {
    2200         clean += "A";
    2201     }
    2202 
    2203     for (int i = 1; i < (int) ref.length(); i++) {
    2204         const QChar c = ref[i];
    2205         const uint u = c.unicode();
    2206         if ((u >= 'a' && u <= 'z') ||
    2207              (u >= 'A' && u <= 'Z') ||
    2208              (u >= '0' && u <= '9') || u == '-' ||
    2209              u == '_' || u == ':' || u == '.') {
    2210             clean += c;
    2211         } else if (c.isSpace()) {
    2212             clean += "-";
    2213         } else if (u == '!') {
    2214             clean += "-not";
    2215         } else if (u == '&') {
    2216             clean += "-and";
    2217         } else if (u == '<') {
    2218             clean += "-lt";
    2219         } else if (u == '=') {
    2220             clean += "-eq";
    2221         } else if (u == '>') {
    2222             clean += "-gt";
    2223         } else if (u == '#') {
    2224             clean += "#";
    2225         } else {
    2226             clean += "-";
    2227             clean += QString::number((int)u, 16);
    2228         }
    2229     }
    2230     return clean;
    2231 }
    2232 
    2233 QString HtmlGenerator::registerRef(const QString& ref)
    2234 {
    2235     QString clean = HtmlGenerator::cleanRef(ref);
    2236 
    2237     for (;;) {
    2238         QString& prevRef = refMap[clean.toLower()];
    2239         if (prevRef.isEmpty()) {
    2240             prevRef = ref;
    2241             break;
    2242         } else if (prevRef == ref) {
    2243             break;
    2244         }
    2245         clean += "x";
    2246     }
    2247     return clean;
    2248 }
    2249 
    2250 QString HtmlGenerator::protect(const QString& string)
    2251 {
    2252 #define APPEND(x) \
    2253     if (html.isEmpty()) { \
    2254         html = string; \
    2255         html.truncate(i); \
    2256     } \
    2257     html += (x);
    2258 
    2259     QString html;
    2260     int n = string.length();
    2261 
    2262     for (int i = 0; i < n; ++i) {
    2263         QChar ch = string.at(i);
    2264 
    2265         if (ch == QLatin1Char('&')) {
    2266             APPEND("&amp;");
    2267         } else if (ch == QLatin1Char('<')) {
    2268             APPEND("&lt;");
    2269         } else if (ch == QLatin1Char('>')) {
    2270             APPEND("&gt;");
    2271         } else if (ch == QLatin1Char('"')) {
    2272             APPEND("&quot;");
    2273         } else if (ch.unicode() > 0x007F
    2274                    || (ch == QLatin1Char('*') && i + 1 < n && string.at(i) == QLatin1Char('/'))
    2275                    || (ch == QLatin1Char('.') && i > 2 && string.at(i - 2) == QLatin1Char('.'))) {
    2276             // we escape '*/' and the last dot in 'e.g.' and 'i.e.' for the Javadoc generator
    2277             APPEND("&#x");
    2278             html += QString::number(ch.unicode(), 16);
    2279             html += QLatin1Char(';');
    2280         } else {
    2281             if (!html.isEmpty())
    2282                 html += ch;
    2283         }
    2284     }
    2285 
    2286     if (!html.isEmpty())
    2287         return html;
    2288     return string;
    2289 
    2290 #undef APPEND
    2291 }
    229281
    229382static QRegExp linkTag("(<@link node=\"([^\"]+)\">).*(</@link>)");
     
    2411200}
    2412201
     202
     203HtmlGenerator::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
     211HtmlGenerator::~HtmlGenerator()
     212{
     213    if (helpProjectWriter)
     214        delete helpProjectWriter;
     215}
     216
     217void 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
     307void HtmlGenerator::terminateGenerator()
     308{
     309    Generator::terminateGenerator();
     310}
     311
     312QString 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 */
     322void 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
     399void 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
     412int 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
     1184void 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
     1421void 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
     1617QString HtmlGenerator::fileExtension(const Node * /* node */)
     1618{
     1619    return "html";
     1620}
     1621
     1622void 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
     1768void 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
     1787void 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
     1798void 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
     1813void 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
     1824void 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
     1906void 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
     1936QString 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
     1965QString 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
     2038void 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
     2084void 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 */
     2133void HtmlGenerator::generateCompactList(const Node *relative,
     2134                                        CodeMarker *marker,
     2135                                        const NodeMap &classMap,
     2136                                        QString commonPrefix)
     2137{
     2138    const int NumParagraphs = 37; // '0' to '9', 'A' to 'Z', '_'
     2139    const int NumColumns = 4; // number of columns in the result
     2140
     2141    if (classMap.isEmpty())
     2142        return;
     2143
     2144    /*
     2145      If commonPrefix is not empty, then the caller knows what
     2146      the common prefix is and has passed it in, so just use that
     2147      one.
     2148     */
     2149    int commonPrefixLen = commonPrefix.length();
     2150    if (commonPrefixLen == 0) {
     2151        QString first;
     2152        QString last;
     2153       
     2154        /*
     2155          The caller didn't pass in a common prefix, so get the common
     2156          prefix by looking at the class names of the first and last
     2157          classes in the class map. Discard any namespace names and
     2158          just use the bare class names. For Qt, the prefix is "Q".
     2159
     2160          Note that the algorithm used here to derive the common prefix
     2161          from the first and last classes in alphabetical order (QAccel
     2162          and QXtWidget in Qt 2.1), fails if either class name does not
     2163          begin with Q.
     2164        */
     2165
     2166        NodeMap::const_iterator iter = classMap.begin();
     2167        while (iter != classMap.end()) {
     2168            if (!iter.key().contains("::")) {
     2169                first = iter.key();
     2170                break;
     2171            }
     2172            ++iter;
     2173        }
     2174
     2175        if (first.isEmpty())
     2176            first = classMap.begin().key();
     2177
     2178        iter = classMap.end();
     2179        while (iter != classMap.begin()) {
     2180            --iter;
     2181            if (!iter.key().contains("::")) {
     2182                last = iter.key();
     2183                break;
     2184            }
     2185        }
     2186
     2187        if (last.isEmpty())
     2188            last = classMap.begin().key();
     2189
     2190        if (classMap.size() > 1) {
     2191            while (commonPrefixLen < first.length() + 1 &&
     2192                   commonPrefixLen < last.length() + 1 &&
     2193                   first[commonPrefixLen] == last[commonPrefixLen])
     2194                ++commonPrefixLen;
     2195        }
     2196
     2197        commonPrefix = first.left(commonPrefixLen);
     2198    }
     2199
     2200    /*
     2201      Divide the data into 37 paragraphs: 0, ..., 9, A, ..., Z,
     2202      underscore (_). QAccel will fall in paragraph 10 (A) and
     2203      QXtWidget in paragraph 33 (X). This is the only place where we
     2204      assume that NumParagraphs is 37. Each paragraph is a NodeMap.
     2205    */
     2206    NodeMap paragraph[NumParagraphs+1];
     2207    QString paragraphName[NumParagraphs+1];
     2208
     2209    NodeMap::ConstIterator c = classMap.begin();
     2210    while (c != classMap.end()) {
     2211        QStringList pieces = c.key().split("::");
     2212        QString key;
     2213        int idx = commonPrefixLen;
     2214        if (!pieces.last().startsWith(commonPrefix))
     2215            idx = 0;
     2216        if (pieces.size() == 1)
     2217            key = pieces.last().mid(idx).toLower();
     2218        else
     2219            key = pieces.last().toLower();
     2220
     2221        int paragraphNo = NumParagraphs - 1;
     2222
     2223        if (key[0].digitValue() != -1) {
     2224            paragraphNo = key[0].digitValue();
     2225        }
     2226        else if (key[0] >= QLatin1Char('a') && key[0] <= QLatin1Char('z')) {
     2227            paragraphNo = 10 + key[0].unicode() - 'a';
     2228        }
     2229
     2230        paragraphName[paragraphNo] = key[0].toUpper();
     2231        paragraph[paragraphNo].insert(key, c.value());
     2232        ++c;
     2233    }
     2234
     2235    /*
     2236      Each paragraph j has a size: paragraph[j].count(). In the
     2237      discussion, we will assume paragraphs 0 to 5 will have sizes
     2238      3, 1, 4, 1, 5, 9.
     2239
     2240      We now want to compute the paragraph offset. Paragraphs 0 to 6
     2241      start at offsets 0, 3, 4, 8, 9, 14, 23.
     2242    */
     2243    int paragraphOffset[NumParagraphs + 1];     // 37 + 1
     2244    int i, j, k;
     2245
     2246    paragraphOffset[0] = 0;
     2247    for (j = 0; j < NumParagraphs; j++)         // j = 0..36
     2248        paragraphOffset[j + 1] = paragraphOffset[j] + paragraph[j].count();
     2249
     2250    int firstOffset[NumColumns + 1];            // 4 + 1
     2251    int currentOffset[NumColumns];              // 4
     2252    int currentParagraphNo[NumColumns];         // 4
     2253    int currentOffsetInParagraph[NumColumns];   // 4
     2254
     2255    int numRows = (classMap.count() + NumColumns - 1) / NumColumns;
     2256    int curParagNo = 0;
     2257
     2258    for (i = 0; i < NumColumns; i++) {          // i = 0..3
     2259        firstOffset[i] = qMin(i * numRows, classMap.size());
     2260        currentOffset[i] = firstOffset[i];
     2261
     2262        for (j = curParagNo; j < NumParagraphs; j++) {
     2263            if (paragraphOffset[j] > firstOffset[i])
     2264                break;
     2265            if (paragraphOffset[j] <= firstOffset[i])
     2266                curParagNo = j;
     2267        }
     2268        currentParagraphNo[i] = curParagNo;
     2269        currentOffsetInParagraph[i] = firstOffset[i] -
     2270                                      paragraphOffset[curParagNo];
     2271    }
     2272    firstOffset[NumColumns] = classMap.count();
     2273
     2274    out() << "<p><table class=\"generic\" width=\"100%\">\n";
     2275    for (k = 0; k < numRows; k++) {
     2276        out() << "<tr>\n";
     2277        for (i = 0; i < NumColumns; i++) {
     2278            if (currentOffset[i] >= firstOffset[i + 1]) {
     2279                // this column is finished
     2280                out() << "<td>\n</td>\n";
     2281            }
     2282            else {
     2283                while ((currentParagraphNo[i] < NumParagraphs) &&
     2284                       (currentOffsetInParagraph[i] == paragraph[currentParagraphNo[i]].count())) {
     2285                    ++currentParagraphNo[i];
     2286                    currentOffsetInParagraph[i] = 0;
     2287                }
     2288#if 0
     2289                if (currentParagraphNo[i] >= NumParagraphs) {
     2290                    qDebug() << "### Internal error ###" << __FILE__ << __LINE__
     2291                             << currentParagraphNo[i] << NumParagraphs;
     2292                    currentParagraphNo[i] = NumParagraphs - 1;
     2293                }
     2294#endif
     2295                out() << "<td align=\"right\">";
     2296                if (currentOffsetInParagraph[i] == 0) {
     2297                    // start a new paragraph
     2298                    out() << "<b>"
     2299                          << paragraphName[currentParagraphNo[i]]
     2300                          << "&nbsp;</b>";
     2301                }
     2302                out() << "</td>\n";
     2303
     2304                out() << "<td>";
     2305                if ((currentParagraphNo[i] < NumParagraphs) &&
     2306                    !paragraphName[currentParagraphNo[i]].isEmpty()) {
     2307                    NodeMap::Iterator it;
     2308                    it = paragraph[currentParagraphNo[i]].begin();
     2309                    for (j = 0; j < currentOffsetInParagraph[i]; j++)
     2310                        ++it;
     2311
     2312                    // Previously, we used generateFullName() for this, but we
     2313                    // require some special formatting.
     2314                    out() << "<a href=\""
     2315                        << linkForNode(it.value(), relative)
     2316                        << "\">";
     2317                    QStringList pieces = fullName(it.value(), relative, marker).split("::");
     2318                    out() << protect(pieces.last());
     2319                    out() << "</a>";
     2320                    if (pieces.size() > 1) {
     2321                        out() << " (";
     2322                        generateFullName(it.value()->parent(), relative, marker);
     2323                        out() << ")";
     2324                    }
     2325                }
     2326                out() << "</td>\n";
     2327
     2328                currentOffset[i]++;
     2329                currentOffsetInParagraph[i]++;
     2330            }
     2331        }
     2332        out() << "</tr>\n";
     2333    }
     2334    out() << "</table></p>\n";
     2335}
     2336
     2337void 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
     2387void 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
     2445void 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
     2477void 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
     2592void 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
     2657void 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
     2728void 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
     2754void 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
    24132794QString HtmlGenerator::highlightedCode(const QString& markedCode,
    24142795                                       CodeMarker *marker,
    2415                                        const Node *relative)
     2796                                       const Node *relative,
     2797                                       CodeMarker::SynopsisStyle ,
     2798                                       bool nameAlignment)
    24162799{
    24172800    QString src = markedCode;
     
    24252808    // replace all <@link> tags: "(<@link node=\"([^\"]+)\">).*(</@link>)"
    24262809    static const QString linkTag("link");
     2810    bool done = false;
    24272811    for (int i = 0, n = src.size(); i < n;) {
    2428         if (src.at(i) == charLangle && src.at(i + 1) == charAt) {
     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            }
    24292817            i += 2;
    24302818            if (parseArg(src, linkTag, &i, n, &arg, &par1)) {
     2819                html += "<b>";
    24312820                QString link = linkForNode(
    24322821                    CodeMarker::nodeForString(par1.toString()), relative);
    24332822                addLink(link, arg, &html);
     2823                html += "</b>";
    24342824            }
    24352825            else {
     
    24562846                    QString link = linkForNode(
    24572847                            marker->resolveTarget(par1.toString(),
    2458                                                   tre,
     2848                                                  myTree,
    24592849                                                  relative),
    24602850                            relative);
     
    24852875                    par1 = QStringRef();
    24862876                    QString link = linkForNode(
    2487                             marker->resolveTarget(arg.toString(), tre, relative),
     2877                            marker->resolveTarget(arg.toString(), myTree, relative),
    24882878                            relative);
    24892879                    addLink(link, arg, &html);
     
    25722962}
    25732963
     2964#else
     2965void 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
     3017void 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
     3038void 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
     3074QString 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
     3235void 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
     3270QString 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
     3323QString 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
     3340QString 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
    25743383QString HtmlGenerator::fileBase(const Node *node)
    25753384{
     
    26193428{
    26203429    if (node->type() == Node::Fake) {
    2621         if (static_cast<const FakeNode *>(node)->subType() == FakeNode::ExternalPage)
     3430        if (static_cast<const FakeNode *>(node)->subType() == Node::ExternalPage)
    26223431            return node->name();
    2623     }
    2624 
     3432        if (static_cast<const FakeNode *>(node)->subType() == Node::Image)
     3433            return node->name();
     3434    }
    26253435    return PageGenerator::fileName(node);
    26263436}
     
    26443454        if (typedeffe->associatedEnum()) {
    26453455            return refForNode(typedeffe->associatedEnum());
    2646         } else {
     3456        }
     3457        else {
    26473458            ref = node->name() + "-typedef";
    26483459        }
     
    26523463        if (func->associatedProperty()) {
    26533464            return refForNode(func->associatedProperty());
    2654         } else {
     3465        }
     3466        else {
    26553467            ref = func->name();
    26563468            if (func->overloadNumber() != 1)
     
    26583470        }
    26593471        break;
     3472#ifdef QDOC_QML       
     3473    case Node::Fake:
     3474        if (node->subType() != Node::QmlPropertyGroup)
     3475            break;
     3476    case Node::QmlProperty:
     3477#endif       
    26603478    case Node::Property:
    26613479        ref = node->name() + "-prop";
    26623480        break;
     3481#ifdef QDOC_QML
     3482    case Node::QmlSignal:
     3483        ref = node->name() + "-signal";
     3484        break;
     3485    case Node::QmlMethod:
     3486        ref = node->name() + "-method";
     3487        break;
     3488#endif       
    26633489    case Node::Variable:
    26643490        ref = node->name() + "-var";
     
    26923518    if (fn != outFileName())
    26933519#endif
    2694         link += fn;
    2695 
    2696     if (!node->isInnerNode()) {
     3520    link += fn;
     3521
     3522    if (!node->isInnerNode() || node->subType() == Node::QmlPropertyGroup) {
    26973523        ref = refForNode(node);
    26983524        if (relative && fn == fileName(relative) && ref == refForNode(relative))
     
    27093535    if (atom->type() == Atom::SectionLeft) {
    27103536        return Doc::canonicalTitle(Text::sectionHeading(atom).toString());
    2711     } else if (atom->type() == Atom::Target) {
     3537    }
     3538    else if (atom->type() == Atom::Target) {
    27123539        return Doc::canonicalTitle(atom->string());
    2713     } else {
     3540    }
     3541    else {
    27143542        return QString();
    27153543    }
     
    27553583        generateSynopsis(enume, relative, marker, CodeMarker::Detailed);
    27563584        out() << "<br />";
    2757         generateSynopsis(enume->flagsType(), relative, marker, CodeMarker::Detailed);
     3585        generateSynopsis(enume->flagsType(),
     3586                         relative,
     3587                         marker,
     3588                         CodeMarker::Detailed);
    27583589        out() << "</h3>\n";
    27593590    }
     
    27793610
    27803611        if (!section.members.isEmpty()) {
    2781             out() << "<p>Access functions:</p>\n";
     3612            out() << "<p><b>Access functions:</b></p>\n";
    27823613            generateSectionList(section, node, marker, CodeMarker::Accessors);
     3614        }
     3615
     3616        Section notifiers;
     3617        notifiers.members += property->notifiers();
     3618       
     3619        if (!notifiers.members.isEmpty()) {
     3620            out() << "<p><b>Notifier signal:</b></p>\n";
     3621            //out() << "<p>This signal is emitted when the property value is changed.</p>\n";
     3622            generateSectionList(notifiers, node, marker, CodeMarker::Accessors);
    27833623        }
    27843624    }
     
    27903630                  << "<a href=\"qflags.html\">QFlags</a>&lt;"
    27913631                  << protect(enume->name())
    2792                   << "&gt;. It stores an OR combination of " << protect(enume->name())
     3632                  << "&gt;. It stores an OR combination of "
     3633                  << protect(enume->name())
    27933634                  << " values.</p>\n";
    27943635        }
     
    28043645            if ((*c)->type() == Node::Class && !(*c)->doc().isEmpty()) {
    28053646                QString className = (*c)->name();
    2806                 if ((*c)->parent() && (*c)->parent()->type() == Node::Namespace &&
     3647                if ((*c)->parent() &&
     3648                    (*c)->parent()->type() == Node::Namespace &&
    28073649                    !(*c)->parent()->name().isEmpty())
    28083650                    className = (*c)->parent()->name()+"::"+className;
     
    28113653                    if ((*c)->status() == Node::Compat) {
    28123654                        compatClasses.insert(className, *c);
     3655                    }
     3656                    else if ((*c)->status() == Node::Obsolete) {
     3657                        obsoleteClasses.insert(className, *c);
    28133658                    }
    28143659                    else {
     
    28403685}
    28413686
     3687/*!
     3688  For generating the "New Classes... in 4.6" section on the
     3689  What's New in 4.6" page.
     3690 */
     3691void HtmlGenerator::findAllSince(const InnerNode *node)
     3692{
     3693    NodeList::const_iterator child = node->childNodes().constBegin();
     3694    while (child != node->childNodes().constEnd()) {
     3695        QString sinceVersion = (*child)->since();
     3696        if (((*child)->access() != Node::Private) && !sinceVersion.isEmpty()) {
     3697            NewSinceMaps::iterator nsmap = newSinceMaps.find(sinceVersion);
     3698            if (nsmap == newSinceMaps.end())
     3699                nsmap = newSinceMaps.insert(sinceVersion,NodeMultiMap());
     3700            NewClassMaps::iterator ncmap = newClassMaps.find(sinceVersion);
     3701            if (ncmap == newClassMaps.end())
     3702                ncmap = newClassMaps.insert(sinceVersion,NodeMap());
     3703 
     3704            if ((*child)->type() == Node::Function) {
     3705                FunctionNode *func = static_cast<FunctionNode *>(*child);
     3706                if ((func->status() > Node::Obsolete) &&
     3707                    (func->metaness() != FunctionNode::Ctor) &&
     3708                    (func->metaness() != FunctionNode::Dtor)) {
     3709                    nsmap.value().insert(func->name(),(*child));
     3710                }
     3711            }
     3712            else if ((*child)->url().isEmpty()) {
     3713                if ((*child)->type() == Node::Class && !(*child)->doc().isEmpty()) {
     3714                    QString className = (*child)->name();
     3715                    if ((*child)->parent() &&
     3716                        (*child)->parent()->type() == Node::Namespace &&
     3717                        !(*child)->parent()->name().isEmpty())
     3718                        className = (*child)->parent()->name()+"::"+className;
     3719                    nsmap.value().insert(className,(*child));
     3720                    ncmap.value().insert(className,(*child));
     3721                }
     3722            }
     3723            else {
     3724                QString name = (*child)->name();
     3725                if ((*child)->parent() &&
     3726                    (*child)->parent()->type() == Node::Namespace &&
     3727                    !(*child)->parent()->name().isEmpty())
     3728                    name = (*child)->parent()->name()+"::"+name;
     3729                nsmap.value().insert(name,(*child));
     3730            }
     3731            if ((*child)->isInnerNode()) {
     3732                findAllSince(static_cast<InnerNode *>(*child));
     3733            }
     3734        }
     3735        ++child;
     3736    }
     3737}
     3738
     3739#if 0
     3740    const QRegExp versionSeparator("[\\-\\.]");
     3741    const int minorIndex = version.indexOf(versionSeparator);
     3742    const int patchIndex = version.indexOf(versionSeparator, minorIndex+1);
     3743    version = version.left(patchIndex);
     3744#endif
     3745
    28423746void HtmlGenerator::findAllFunctions(const InnerNode *node)
    28433747{
     
    28503754            else if ((*c)->type() == Node::Function) {
    28513755                const FunctionNode *func = static_cast<const FunctionNode *>(*c);
    2852                 if (func->status() > Node::Obsolete && func->metaness() != FunctionNode::Ctor
    2853                         && func->metaness() != FunctionNode::Dtor) {
    2854                     funcIndex[(*c)->name()].insert((*c)->parent()->name(), *c);
     3756                if ((func->status() > Node::Obsolete) &&
     3757                    (func->metaness() != FunctionNode::Ctor) &&
     3758                    (func->metaness() != FunctionNode::Dtor)) {
     3759                    funcIndex[(*c)->name()].insert(myTree->fullDocumentName((*c)->parent()), *c);
    28553760                }
    28563761            }
     
    29023807}
    29033808
    2904 #ifdef ZZZ_QDOC_QML   
     3809#ifdef ZZZ_QDOC_QML
    29053810/*!
    29063811  This function finds all the qml element nodes and
     
    29133818        if ((*c)->type() == Node::Fake) {
    29143819            const FakeNode* fakeNode = static_cast<const FakeNode *>(*c);
    2915             if (fakeNode->subType() == FakeNode::QmlClass) {
    2916                 const QmlNode* qmlNode = static_cast<const QmlNode*>(fakeNode);
    2917                 //qDebug() << "HtmlGenerator: QML CLASS" << qmlNode->name();
     3820            if (fakeNode->subType() == Node::QmlClass) {
     3821                const QmlClassNode* qmlNode =
     3822                    static_cast<const QmlClassNode*>(fakeNode);
    29183823                const Node* n = qmlNode->classNode();
    2919                 if (n)
    2920                     //qDebug() << "  FOUND IT!" << n->name();
    29213824            }
    29223825            qmlClasses.insert(fakeNode->name(),*c);
     
    29253828    }
    29263829}
    2927 #endif   
    2928 
    2929 #if 0
    2930             else if ((*c)->isInnerNode()) {
    2931                 findAllClasses(static_cast<InnerNode *>(*c));
    2932             }
    29333830#endif
    29343831
     
    29743871    }
    29753872    else if (target.endsWith(".html")) {
    2976         node = tre->root()->findNode(target, Node::Fake);
     3873        node = myTree->root()->findNode(target, Node::Fake);
    29773874    }
    29783875    else if (marker) {
    2979         node = marker->resolveTarget(target, tre, relative);
     3876        node = marker->resolveTarget(target, myTree, relative);
    29803877        if (!node)
    2981             node = tre->findFakeNodeByTitle(target);
     3878            node = myTree->findFakeNodeByTitle(target);
    29823879        if (!node && atom) {
    2983             node = tre->findUnambiguousTarget(target,
     3880            node = myTree->findUnambiguousTarget(target,
    29843881                *const_cast<Atom**>(&atom));
    29853882        }
     
    30083905                               const Node *relative,
    30093906                               CodeMarker *marker,
    3010                                const Node *node)
     3907                               const Node** node)
    30113908{
    30123909    QString link;
    3013     node = 0;
     3910    *node = 0;
     3911    inObsoleteLink = false;
    30143912
    30153913    if (atom->string().contains(":") &&
     
    30353933        QString first = path.first().trimmed();
    30363934        if (first.isEmpty()) {
    3037             node = relative;
     3935            *node = relative;
    30383936        }
    30393937        else if (first.endsWith(".html")) {
    3040             node = tre->root()->findNode(first, Node::Fake);
     3938            *node = myTree->root()->findNode(first, Node::Fake);
    30413939        }
    30423940        else {
    3043             node = marker->resolveTarget(first, tre, relative);
    3044             if (!node)
    3045                 node = tre->findFakeNodeByTitle(first);
    3046             if (!node)
    3047                 node = tre->findUnambiguousTarget(first, targetAtom);
    3048         }
    3049 
    3050         if (node) {
    3051             if (!node->url().isEmpty())
    3052                 return node->url();
     3941            *node = marker->resolveTarget(first, myTree, relative);
     3942            if (!*node)
     3943                *node = myTree->findFakeNodeByTitle(first);
     3944            if (!*node)
     3945                *node = myTree->findUnambiguousTarget(first, targetAtom);
     3946        }
     3947
     3948        if (*node) {
     3949            if (!(*node)->url().isEmpty())
     3950                return (*node)->url();
    30533951            else
    30543952                path.removeFirst();
    30553953        }
    30563954        else {
    3057             node = relative;
     3955            *node = relative;
     3956        }
     3957
     3958        if (*node) {
     3959            if ((*node)->status() == Node::Obsolete) {
     3960                if (relative) {
     3961                    if (relative->parent() != *node) {
     3962                        if (relative->status() != Node::Obsolete) {
     3963                            bool porting = false;
     3964                            if (relative->type() == Node::Fake) {
     3965                                const FakeNode* fake = static_cast<const FakeNode*>(relative);
     3966                                if (fake->title().startsWith("Porting"))
     3967                                    porting = true;
     3968                            }
     3969                            QString name = marker->plainFullName(relative);
     3970                            if (!porting && !name.startsWith("Q3")) {
     3971                                if (obsoleteLinks) {
     3972                                    relative->doc().location().warning(tr("Link to obsolete item '%1' in %2")
     3973                                                                       .arg(atom->string())
     3974                                                                       .arg(name));
     3975                                }
     3976                                inObsoleteLink = true;
     3977                            }
     3978                        }
     3979                    }
     3980                }
     3981                else {
     3982                    qDebug() << "Link to Obsolete entity"
     3983                             << (*node)->name() << "no relative";
     3984                }
     3985            }
     3986#if 0                   
     3987            else if ((*node)->status() == Node::Deprecated) {
     3988                qDebug() << "Link to Deprecated entity";
     3989            }
     3990            else if ((*node)->status() == Node::Internal) {
     3991                qDebug() << "Link to Internal entity";
     3992            }
     3993#endif               
    30583994        }
    30593995
    30603996        while (!path.isEmpty()) {
    3061             targetAtom = tre->findTarget(path.first(), node);
     3997            targetAtom = myTree->findTarget(path.first(), *node);
    30623998            if (targetAtom == 0)
    30633999                break;
     
    30664002
    30674003        if (path.isEmpty()) {
    3068             link = linkForNode(node, relative);
     4004            link = linkForNode(*node, relative);
     4005            if (*node && (*node)->subType() == Node::Image)
     4006                link = "images/used-in-examples/" + link;
    30694007            if (targetAtom)
    3070                 link += "#" + refForAtom(targetAtom, node);
     4008                link += "#" + refForAtom(targetAtom, *node);
    30714009        }
    30724010    }
     
    30884026                                  const QString &title)
    30894027{
    3090     tre->generateIndex(outputDir() + "/" + fileBase + ".index", url, title);
     4028    myTree->generateIndex(outputDir() + "/" + fileBase + ".index", url, title);
    30914029}
    30924030
     
    31034041        if (node->isInnerNode()) {
    31044042            text << Atom::ParaLeft
    3105                  << Atom(Atom::FormattingLeft,ATOM_FORMATTING_BOLD) << "This "
    3106                  << typeString(node) << " is part of the Qt 3 support library."
     4043                 << Atom(Atom::FormattingLeft,ATOM_FORMATTING_BOLD)
     4044                 << "This "
     4045                 << typeString(node)
     4046                 << " is part of the Qt 3 support library."
    31074047                 << Atom(Atom::FormattingRight, ATOM_FORMATTING_BOLD)
    3108                  << " It is provided to keep old source code working. We strongly advise against "
     4048                 << " It is provided to keep old source code working. "
     4049                 << "We strongly advise against "
    31094050                 << "using it in new code. See ";
    31104051
    3111             const FakeNode *fakeNode = tre->findFakeNodeByTitle("Porting To Qt 4");
     4052            const FakeNode *fakeNode = myTree->findFakeNodeByTitle("Porting To Qt 4");
    31124053            Atom *targetAtom = 0;
    31134054            if (fakeNode && node->type() == Node::Class) {
    31144055                QString oldName(node->name());
    3115                 targetAtom = tre->findTarget(oldName.replace("3", ""),
    3116                                              fakeNode);
     4056                targetAtom = myTree->findTarget(oldName.replace("3", ""),
     4057                                                fakeNode);
    31174058            }
    31184059
     
    31444085    QStringList macRefs = marker->macRefsForNode(node);
    31454086    foreach (const QString &macRef, macRefs)
    3146         out() << "<a name=\"" << "//apple_ref/" << macRef << "\" />\n";
     4087        out() << "<a name=\"" << "//apple_ref/" << macRef << "\"></a>\n";
    31474088}
    31484089
     
    31874128        }
    31884129        else {
     4130            if (inObsoleteLink) {
     4131                out() << "<sup>(obsolete)</sup>";
     4132            }
    31894133            out() << "</a>";
    31904134        }
    31914135    }
    31924136    inLink = false;
     4137    inObsoleteLink = false;
    31934138}
    31944139
    31954140QT_END_NAMESPACE
     4141
     4142#ifdef QDOC_QML
     4143
     4144/*!
     4145  Generates the summary for for the \a section. Only used for
     4146  sections of QML element documentation.
     4147
     4148  Currently handles only the QML property group.
     4149 */
     4150void HtmlGenerator::generateQmlSummary(const Section& section,
     4151                                       const Node *relative,
     4152                                       CodeMarker *marker)
     4153{
     4154    if (!section.members.isEmpty()) {
     4155        NodeList::ConstIterator m;
     4156        int count = section.members.size();
     4157        bool twoColumn = false;
     4158        if (section.members.first()->type() == Node::QmlProperty) {
     4159            twoColumn = (count >= 5);
     4160        }
     4161        if (twoColumn)
     4162            out() << "<p><table width=\"100%\" border=\"0\" cellpadding=\"0\""
     4163                     " cellspacing=\"0\">\n"
     4164                  << "<tr><td width=\"45%\" valign=\"top\">";
     4165        out() << "<ul>\n";
     4166
     4167        int row = 0;
     4168        m = section.members.begin();
     4169        while (m != section.members.end()) {
     4170            if (twoColumn && row == (int) (count + 1) / 2)
     4171                out() << "</ul></td><td valign=\"top\"><ul>\n";
     4172            out() << "<li><div class=\"fn\"></div>";
     4173            generateQmlItem(*m,relative,marker,true);
     4174            out() << "</li>\n";
     4175            row++;
     4176            ++m;
     4177        }
     4178        out() << "</ul>\n";
     4179        if (twoColumn)
     4180            out() << "</td></tr>\n</table></p>\n";
     4181    }
     4182}
     4183
     4184/*!
     4185  Outputs the html detailed documentation for a section
     4186  on a QML element reference page.
     4187 */
     4188void HtmlGenerator::generateDetailedQmlMember(const Node *node,
     4189                                              const InnerNode *relative,
     4190                                              CodeMarker *marker)
     4191{
     4192    const QmlPropertyNode* qpn = 0;
     4193    generateMacRef(node, marker);
     4194    out() << "<div class=\"qmlitem\">";
     4195    if (node->subType() == Node::QmlPropertyGroup) {
     4196        const QmlPropGroupNode* qpgn = static_cast<const QmlPropGroupNode*>(node);
     4197        NodeList::ConstIterator p = qpgn->childNodes().begin();
     4198        out() << "<div class=\"qmlproto\">";
     4199        out() << "<table width=\"100%\" class=\"qmlname\">";
     4200
     4201        while (p != qpgn->childNodes().end()) {
     4202            if ((*p)->type() == Node::QmlProperty) {
     4203                qpn = static_cast<const QmlPropertyNode*>(*p);
     4204                out() << "<tr><td>";
     4205                out() << "<a name=\"" + refForNode(qpn) + "\"></a>";
     4206                if (!qpn->isWritable())
     4207                    out() << "<span class=\"qmlreadonly\">read-only</span>";
     4208                generateQmlItem(qpn, relative, marker, false);
     4209                out() << "</td></tr>";
     4210                if (qpgn->isDefault()) {
     4211                    out() << "</table>"
     4212                          << "</div></div>"
     4213                          << "<div class=\"qmlitem\">"
     4214                          << "<div class=\"qmlproto\">"
     4215                          << "<table class=\"qmlname\">"
     4216                          << "<tr><td><font color=\"green\">"
     4217                          << "default</font></td></tr>";
     4218                }
     4219            }
     4220            ++p;
     4221        }
     4222        out() << "</table>";
     4223        out() << "</div>";
     4224    }
     4225    else if (node->type() == Node::QmlSignal) {
     4226        const FunctionNode* qsn = static_cast<const FunctionNode*>(node);
     4227        out() << "<div class=\"qmlproto\">";
     4228        out() << "<table class=\"qmlname\">";
     4229        out() << "<tr><td>";
     4230        out() << "<a name=\"" + refForNode(qsn) + "\"></a>";
     4231        generateSynopsis(qsn,relative,marker,CodeMarker::Detailed,false);
     4232        //generateQmlItem(qsn,relative,marker,false);
     4233        out() << "</td></tr>";
     4234        out() << "</table>";
     4235        out() << "</div>";
     4236    }
     4237    else if (node->type() == Node::QmlMethod) {
     4238        const FunctionNode* qmn = static_cast<const FunctionNode*>(node);
     4239        out() << "<div class=\"qmlproto\">";
     4240        out() << "<table class=\"qmlname\">";
     4241        out() << "<tr><td>";
     4242        out() << "<a name=\"" + refForNode(qmn) + "\"></a>";
     4243        generateSynopsis(qmn,relative,marker,CodeMarker::Detailed,false);
     4244        out() << "</td></tr>";
     4245        out() << "</table>";
     4246        out() << "</div>";
     4247    }
     4248    out() << "<div class=\"qmldoc\">";
     4249    generateStatus(node, marker);
     4250    generateBody(node, marker);
     4251    generateThreadSafeness(node, marker);
     4252    generateSince(node, marker);
     4253    generateAlsoList(node, marker);
     4254    out() << "</div>";
     4255    out() << "</div>";
     4256}
     4257
     4258/*!
     4259  Output the "Inherits" line for the QML element,
     4260  if there should be one.
     4261 */
     4262void HtmlGenerator::generateQmlInherits(const QmlClassNode* cn,
     4263                                        CodeMarker* marker)
     4264{
     4265    if (cn && !cn->links().empty()) {
     4266        if (cn->links().contains(Node::InheritsLink)) {
     4267            QPair<QString,QString> linkPair;
     4268            linkPair = cn->links()[Node::InheritsLink];
     4269            QStringList strList(linkPair.first);
     4270            const Node* n = myTree->findNode(strList,Node::Fake);
     4271            if (n && n->subType() == Node::QmlClass) {
     4272                const QmlClassNode* qcn = static_cast<const QmlClassNode*>(n);
     4273                out() << "<p style=\"text-align: center\">";
     4274                Text text;
     4275                text << "[Inherits ";
     4276                text << Atom(Atom::LinkNode,CodeMarker::stringForNode(qcn));
     4277                text << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK);
     4278                text << Atom(Atom::String, linkPair.second);
     4279                text << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK);
     4280                text << "]";
     4281                generateText(text, cn, marker);
     4282                out() << "</p>";
     4283            }
     4284        }
     4285    }
     4286}
     4287
     4288/*!
     4289  Output the "[Xxx instantiates the C++ class QmlGraphicsXxx]"
     4290  line for the QML element, if there should be one.
     4291
     4292  If there is no class node, or if the class node status
     4293  is set to Node::Internal, do nothing.
     4294 */
     4295void HtmlGenerator::generateQmlInstantiates(const QmlClassNode* qcn,
     4296                                            CodeMarker* marker)
     4297{
     4298    const ClassNode* cn = qcn->classNode();
     4299    if (cn && (cn->status() != Node::Internal)) {
     4300        out() << "<p style=\"text-align: center\">";
     4301        Text text;
     4302        text << "[";
     4303        text << Atom(Atom::LinkNode,CodeMarker::stringForNode(qcn));
     4304        text << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK);
     4305        text << Atom(Atom::String, qcn->name());
     4306        text << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK);
     4307        text << " instantiates the C++ class ";
     4308        text << Atom(Atom::LinkNode,CodeMarker::stringForNode(cn));
     4309        text << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK);
     4310        text << Atom(Atom::String, cn->name());
     4311        text << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK);
     4312        text << "]";
     4313        generateText(text, qcn, marker);
     4314        out() << "</p>";
     4315    }
     4316}
     4317
     4318/*!
     4319  Output the "[QmlGraphicsXxx is instantiated by QML element Xxx]"
     4320  line for the class, if there should be one.
     4321
     4322  If there is no QML element, or if the class node status
     4323  is set to Node::Internal, do nothing.
     4324 */
     4325void HtmlGenerator::generateInstantiatedBy(const ClassNode* cn,
     4326                                           CodeMarker* marker)
     4327{
     4328    if (cn &&  cn->status() != Node::Internal && !cn->qmlElement().isEmpty()) {
     4329        const Node* n = myTree->root()->findNode(cn->qmlElement(),Node::Fake);
     4330        if (n && n->subType() == Node::QmlClass) {
     4331            out() << "<p style=\"text-align: center\">";
     4332            Text text;
     4333            text << "[";
     4334            text << Atom(Atom::LinkNode,CodeMarker::stringForNode(cn));
     4335            text << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK);
     4336            text << Atom(Atom::String, cn->name());
     4337            text << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK);
     4338            text << " is instantiated by QML element ";
     4339            text << Atom(Atom::LinkNode,CodeMarker::stringForNode(n));
     4340            text << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK);
     4341            text << Atom(Atom::String, n->name());
     4342            text << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK);
     4343            text << "]";
     4344            generateText(text, cn, marker);
     4345            out() << "</p>";
     4346        }
     4347    }
     4348}
     4349
     4350#endif
Note: See TracChangeset for help on using the changeset viewer.