Index: trunk/tools/qdoc3/helpprojectwriter.cpp
===================================================================
--- trunk/tools/qdoc3/helpprojectwriter.cpp	(revision 651)
+++ trunk/tools/qdoc3/helpprojectwriter.cpp	(revision 846)
@@ -1,5 +1,5 @@
 /****************************************************************************
 **
-** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
 ** All rights reserved.
 ** Contact: Nokia Corporation (qt-info@nokia.com)
@@ -40,7 +40,7 @@
 ****************************************************************************/
 
-#include <QtXml>
 #include <QHash>
 #include <QMap>
+//#include <qdebug.h>
 
 #include "atom.h"
@@ -50,4 +50,5 @@
 #include "node.h"
 #include "tree.h"
+#include <qdebug.h>
 
 QT_BEGIN_NAMESPACE
@@ -92,4 +93,5 @@
             subproject.indexTitle = config.getString(subprefix + "indexTitle");
             subproject.sortPages = config.getBool(subprefix + "sortPages");
+            subproject.type = config.getString(subprefix + "type");
             readSelectors(subproject, config.getStringList(subprefix + "selectors"));
             project.subprojects[name] = subproject;
@@ -118,4 +120,9 @@
     typeHash["variable"] = Node::Variable;
     typeHash["target"] = Node::Target;
+#ifdef QDOC_QML
+    typeHash["qmlproperty"] = Node::QmlProperty;
+    typeHash["qmlsignal"] = Node::QmlSignal;
+    typeHash["qmlmethod"] = Node::QmlMethod;
+#endif
 
     QHash<QString, Node::SubType> subTypeHash;
@@ -129,4 +136,6 @@
 #ifdef QDOC_QML
     subTypeHash["qmlclass"] = Node::QmlClass;
+    subTypeHash["qmlpropertygroup"] = Node::QmlPropertyGroup;
+    subTypeHash["qmlbasictype"] = Node::QmlBasicType;
 #endif
 
@@ -178,5 +187,11 @@
     QStringList details;
 
-    if (node->parent() && !node->parent()->name().isEmpty()) {
+    if (node->type() == Node::QmlProperty) {
+        // "name"
+        details << node->name();
+        // "id"
+        details << node->parent()->parent()->name()+"::"+node->name();
+    }
+    else if (node->parent() && !node->parent()->name().isEmpty()) {
         // "name"
         if (node->type() == Node::Enum || node->type() == Node::Typedef)
@@ -186,27 +201,27 @@
         // "id"
         details << node->parent()->name()+"::"+node->name();
-    } else if (node->type() == Node::Fake) {
+    }
+    else if (node->type() == Node::Fake) {
         const FakeNode *fake = static_cast<const FakeNode *>(node);
-#ifdef QDOC_QML
         if (fake->subType() == Node::QmlClass) {
             details << (QmlClassNode::qmlOnly ? fake->name() : fake->fullTitle());
             details << "QML." + fake->name();
-        } else
-#endif
-        {
+        }
+        else {
             details << fake->fullTitle();
             details << fake->fullTitle();
         }
-    } else {
+    }
+    else {
         details << node->name();
         details << node->name();
     }
     details << tree->fullDocumentLocation(node);
-
     return details;
 }
 
 bool HelpProjectWriter::generateSection(HelpProject &project,
-                        QXmlStreamWriter & /* writer */, const Node *node)
+                                        QXmlStreamWriter & /* writer */,
+                                        const Node *node)
 {
     if (!node->url().isEmpty())
@@ -227,7 +242,8 @@
         const FakeNode *fake = static_cast<const FakeNode *>(node);
         objName = fake->fullTitle();
-    } else
+    }
+    else
         objName = tree->fullDocumentName(node);
-
+    
     // Only add nodes to the set for each subproject if they match a selector.
     // Those that match will be listed in the table of contents.
@@ -236,6 +252,7 @@
         SubProject subproject = project.subprojects[name];
         // No selectors: accept all nodes.
-        if (subproject.selectors.isEmpty())
+        if (subproject.selectors.isEmpty()) {
             project.subprojects[name].nodes[objName] = node;
+        }
         else if (subproject.selectors.contains(node->type())) {
             // Accept only the node types in the selectors hash.
@@ -248,7 +265,8 @@
                 if (subproject.selectors[node->type()].contains(fakeNode->subType()) &&
                     fakeNode->subType() != Node::ExternalPage &&
-                    !fakeNode->fullTitle().isEmpty())
+                    !fakeNode->fullTitle().isEmpty()) {
 
                     project.subprojects[name].nodes[objName] = node;
+                }
             }
         }
@@ -291,4 +309,7 @@
 
         case Node::Property:
+        case Node::QmlProperty:
+        case Node::QmlSignal:
+        case Node::QmlMethod:
             project.keywords.append(keywordDetails(node));
             break;
@@ -401,5 +422,5 @@
     if (!generateSection(project, writer, node))
         return;
-
+    
     if (node->isInnerNode()) {
         const InnerNode *inner = static_cast<const InnerNode *>(node);
@@ -410,6 +431,21 @@
             if (node->access() == Node::Private)
                 continue;
-            if (node->type() == Node::Fake)
-                childMap[static_cast<const FakeNode *>(node)->fullTitle()] = node;
+            if (node->type() == Node::Fake) {
+                /*
+                  Don't visit QML property group nodes,
+                  but visit their children, which are all
+                  QML property nodes.
+                 */
+                if (node->subType() == Node::QmlPropertyGroup) {
+                    const InnerNode* inner = static_cast<const InnerNode*>(node);
+                    foreach (const Node* n, inner->childNodes()) {
+                        if (n->access() == Node::Private)
+                            continue;
+                        childMap[tree->fullDocumentName(n)] = n;
+                    }
+                }
+                else
+                    childMap[static_cast<const FakeNode *>(node)->fullTitle()] = node;
+            }
             else {
                 if (node->type() == Node::Function) {
@@ -495,11 +531,9 @@
             writer.writeAttribute("ref", href);
             writer.writeAttribute("title", fakeNode->fullTitle());
-            //            qDebug() << "Title:" << fakeNode->fullTitle();
             
-            if (fakeNode->subType() == Node::HeaderFile) {
-
+            if ((fakeNode->subType() == Node::HeaderFile) || (fakeNode->subType() == Node::QmlClass)) {
                 // Write subsections for all members, obsolete members and Qt 3
                 // members.
-                if (!project.memberStatus[node].isEmpty()) {
+                if (!project.memberStatus[node].isEmpty() || (fakeNode->subType() == Node::QmlClass)) {
                     QString membersPath = href.left(href.size()-5) + "-members.html";
                     writer.writeStartElement("section");
@@ -594,42 +628,98 @@
         SubProject subproject = project.subprojects[name];
 
-        if (!name.isEmpty()) {
-            writer.writeStartElement("section");
-            QString indexPath = tree->fullDocumentLocation(tree->findFakeNodeByTitle(subproject.indexTitle));
-            writer.writeAttribute("ref", HtmlGenerator::cleanRef(indexPath));
-            writer.writeAttribute("title", subproject.title);
-            project.files.insert(indexPath);
-        }
-        if (subproject.sortPages) {
-            QStringList titles = subproject.nodes.keys();
-            titles.sort();
-            foreach (const QString &title, titles)
-                writeNode(project, writer, subproject.nodes[title]);
+        if (subproject.type == QLatin1String("manual")) {
+
+            const FakeNode *indexPage = tree->findFakeNodeByTitle(subproject.indexTitle);
+            if (indexPage) {
+                Text indexBody = indexPage->doc().body();
+                const Atom *atom = indexBody.firstAtom();
+                QStack<int> sectionStack;
+                bool inItem = false;
+
+                while (atom) {
+                    switch (atom->type()) {
+                    case Atom::ListLeft:
+                        sectionStack.push(0);
+                        break;
+                    case Atom::ListRight:
+                        if (sectionStack.pop() > 0)
+                            writer.writeEndElement(); // section
+                        break;
+                    case Atom::ListItemLeft:
+                        inItem = true;
+                        break;
+                    case Atom::ListItemRight:
+                        inItem = false;
+                        break;
+                    case Atom::Link:
+                        if (inItem) {
+                            if (sectionStack.top() > 0)
+                                writer.writeEndElement(); // section
+
+                            const FakeNode *page = tree->findFakeNodeByTitle(atom->string());
+                            writer.writeStartElement("section");
+                            QString indexPath = tree->fullDocumentLocation(page);
+                            writer.writeAttribute("ref", HtmlGenerator::cleanRef(indexPath));
+                            writer.writeAttribute("title", atom->string());
+                            project.files.insert(indexPath);
+
+                            sectionStack.top() += 1;
+                        }
+                        break;
+                    default:
+                        ;
+                    }
+
+                    if (atom == indexBody.lastAtom())
+                        break;
+                    atom = atom->next();
+                }
+            } else
+                rootNode->doc().location().warning(
+                    tr("Failed to find index: %1").arg(subproject.indexTitle)
+                    );
+
         } else {
-            // Find a contents node and navigate from there, using the NextLink values.
-            foreach (const Node *node, subproject.nodes) {
-                QString nextTitle = node->links().value(Node::NextLink).first;
-                if (!nextTitle.isEmpty() &&
-                    node->links().value(Node::ContentsLink).first.isEmpty()) {
-
-                    FakeNode *nextPage = const_cast<FakeNode *>(tree->findFakeNodeByTitle(nextTitle));
-
-                    // Write the contents node.
-                    writeNode(project, writer, node);
-
-                    while (nextPage) {
-                        writeNode(project, writer, nextPage);
-                        nextTitle = nextPage->links().value(Node::NextLink).first;
-                        if(nextTitle.isEmpty())
-                            break;
-                        nextPage = const_cast<FakeNode *>(tree->findFakeNodeByTitle(nextTitle));
+
+            if (!name.isEmpty()) {
+                writer.writeStartElement("section");
+                QString indexPath = tree->fullDocumentLocation(tree->findFakeNodeByTitle(subproject.indexTitle));
+                writer.writeAttribute("ref", HtmlGenerator::cleanRef(indexPath));
+                writer.writeAttribute("title", subproject.title);
+                project.files.insert(indexPath);
+            }
+            if (subproject.sortPages) {
+                QStringList titles = subproject.nodes.keys();
+                titles.sort();
+                foreach (const QString &title, titles) {
+                    writeNode(project, writer, subproject.nodes[title]);
+                }
+            } else {
+                // Find a contents node and navigate from there, using the NextLink values.
+                foreach (const Node *node, subproject.nodes) {
+                    QString nextTitle = node->links().value(Node::NextLink).first;
+                    if (!nextTitle.isEmpty() &&
+                        node->links().value(Node::ContentsLink).first.isEmpty()) {
+
+                        FakeNode *nextPage = const_cast<FakeNode *>(tree->findFakeNodeByTitle(nextTitle));
+
+                        // Write the contents node.
+                        writeNode(project, writer, node);
+
+                        while (nextPage) {
+                            writeNode(project, writer, nextPage);
+                            nextTitle = nextPage->links().value(Node::NextLink).first;
+                            if(nextTitle.isEmpty())
+                                break;
+                            nextPage = const_cast<FakeNode *>(tree->findFakeNodeByTitle(nextTitle));
+                        }
+                        break;
                     }
-                    break;
-                }
-            }
-        }
-
-        if (!name.isEmpty())
-            writer.writeEndElement(); // section
+                }
+            }
+
+            if (!name.isEmpty())
+                writer.writeEndElement(); // section
+        }
     }
 
