diff options
Diffstat (limited to 'src/tools')
27 files changed, 1261 insertions, 611 deletions
diff --git a/src/tools/bootstrap/bootstrap.pro b/src/tools/bootstrap/bootstrap.pro index 527a932c4d..e0f5546966 100644 --- a/src/tools/bootstrap/bootstrap.pro +++ b/src/tools/bootstrap/bootstrap.pro @@ -130,9 +130,15 @@ win32:SOURCES += ../../corelib/io/qfilesystemengine_win.cpp \ ../../corelib/plugin/qsystemlibrary.cpp \ mac { - SOURCES += ../../corelib/kernel/qcoreapplication_mac.cpp \ - ../../corelib/kernel/qcore_mac.cpp - LIBS += -framework CoreServices -framework Foundation + SOURCES += \ + ../../corelib/kernel/qcoreapplication_mac.cpp \ + ../../corelib/kernel/qcore_mac.cpp + OBJECTIVE_SOURCES += \ + ../../corelib/kernel/qcore_mac_objc.mm + + LIBS += -framework Foundation + osx: LIBS_PRIVATE += -framework CoreServices + ios: LIBS_PRIVATE += -framework UIKit } macx { diff --git a/src/tools/moc/generator.cpp b/src/tools/moc/generator.cpp index 6c5e772e8c..912fa995fa 100644 --- a/src/tools/moc/generator.cpp +++ b/src/tools/moc/generator.cpp @@ -359,7 +359,13 @@ void Generator::generateCode() fprintf(out, " %4d, %4d, // constructors\n", isConstructible ? cdef->constructorList.count() : 0, isConstructible ? index : 0); - fprintf(out, " %4d, // flags\n", 0); + int flags = 0; + if (cdef->hasQGadget) { + // Ideally, all the classes could have that flag. But this broke classes generated + // by qdbusxml2cpp which generate code that require that we call qt_metacall for properties + flags |= PropertyAccessInStaticMetaCall; + } + fprintf(out, " %4d, // flags\n", flags); fprintf(out, " %4d, // signalCount\n", cdef->signalList.count()); @@ -425,7 +431,9 @@ void Generator::generateCode() // // Generate internal qt_static_metacall() function // - if (cdef->hasQObject && !isQt) + const bool hasStaticMetaCall = !isQt && + (cdef->hasQObject || !cdef->methodList.isEmpty() || !cdef->propertyList.isEmpty()); + if (hasStaticMetaCall) generateStaticMetacall(); // @@ -513,7 +521,7 @@ void Generator::generateCode() fprintf(out, "qt_meta_stringdata_%s.data,\n" " qt_meta_data_%s, ", qualifiedClassNameIdentifier.constData(), qualifiedClassNameIdentifier.constData()); - if (cdef->hasQObject && !isQt) + if (hasStaticMetaCall) fprintf(out, " qt_static_metacall, "); else fprintf(out, " Q_NULLPTR, "); @@ -919,10 +927,6 @@ void Generator::generateMetacall() } if (cdef->propertyList.size()) { - bool needGet = false; - bool needTempVarForGet = false; - bool needSet = false; - bool needReset = false; bool needDesignable = false; bool needScriptable = false; bool needStored = false; @@ -930,132 +934,21 @@ void Generator::generateMetacall() bool needUser = false; for (int i = 0; i < cdef->propertyList.size(); ++i) { const PropertyDef &p = cdef->propertyList.at(i); - needGet |= !p.read.isEmpty() || !p.member.isEmpty(); - if (!p.read.isEmpty() || !p.member.isEmpty()) - needTempVarForGet |= (p.gspec != PropertyDef::PointerSpec - && p.gspec != PropertyDef::ReferenceSpec); - - needSet |= !p.write.isEmpty() || (!p.member.isEmpty() && !p.constant); - needReset |= !p.reset.isEmpty(); needDesignable |= p.designable.endsWith(')'); needScriptable |= p.scriptable.endsWith(')'); needStored |= p.stored.endsWith(')'); needEditable |= p.editable.endsWith(')'); needUser |= p.user.endsWith(')'); } - fprintf(out, "\n#ifndef QT_NO_PROPERTIES\n "); + fprintf(out, "\n#ifndef QT_NO_PROPERTIES\n "); if (needElse) - fprintf(out, " else "); - fprintf(out, "if (_c == QMetaObject::ReadProperty) {\n"); - if (needGet) { - if (needTempVarForGet) - fprintf(out, " void *_v = _a[0];\n"); - fprintf(out, " switch (_id) {\n"); - for (int propindex = 0; propindex < cdef->propertyList.size(); ++propindex) { - const PropertyDef &p = cdef->propertyList.at(propindex); - if (p.read.isEmpty() && p.member.isEmpty()) - continue; - QByteArray prefix; - if (p.inPrivateClass.size()) { - prefix = p.inPrivateClass; - prefix.append("->"); - } - if (p.gspec == PropertyDef::PointerSpec) - fprintf(out, " case %d: _a[0] = const_cast<void*>(reinterpret_cast<const void*>(%s%s())); break;\n", - propindex, prefix.constData(), p.read.constData()); - else if (p.gspec == PropertyDef::ReferenceSpec) - fprintf(out, " case %d: _a[0] = const_cast<void*>(reinterpret_cast<const void*>(&%s%s())); break;\n", - propindex, prefix.constData(), p.read.constData()); - else if (cdef->enumDeclarations.value(p.type, false)) - fprintf(out, " case %d: *reinterpret_cast<int*>(_v) = QFlag(%s%s()); break;\n", - propindex, prefix.constData(), p.read.constData()); - else if (!p.read.isEmpty()) - fprintf(out, " case %d: *reinterpret_cast< %s*>(_v) = %s%s(); break;\n", - propindex, p.type.constData(), prefix.constData(), p.read.constData()); - else - fprintf(out, " case %d: *reinterpret_cast< %s*>(_v) = %s%s; break;\n", - propindex, p.type.constData(), prefix.constData(), p.member.constData()); - } - fprintf(out, " default: break;\n"); - fprintf(out, " }\n"); - } - - fprintf(out, - " _id -= %d;\n" - " }", cdef->propertyList.count()); - - fprintf(out, " else "); - fprintf(out, "if (_c == QMetaObject::WriteProperty) {\n"); - - if (needSet) { - fprintf(out, " void *_v = _a[0];\n"); - fprintf(out, " switch (_id) {\n"); - for (int propindex = 0; propindex < cdef->propertyList.size(); ++propindex) { - const PropertyDef &p = cdef->propertyList.at(propindex); - if (p.constant) - continue; - if (p.write.isEmpty() && p.member.isEmpty()) - continue; - QByteArray prefix; - if (p.inPrivateClass.size()) { - prefix = p.inPrivateClass; - prefix.append("->"); - } - if (cdef->enumDeclarations.value(p.type, false)) { - fprintf(out, " case %d: %s%s(QFlag(*reinterpret_cast<int*>(_v))); break;\n", - propindex, prefix.constData(), p.write.constData()); - } else if (!p.write.isEmpty()) { - fprintf(out, " case %d: %s%s(*reinterpret_cast< %s*>(_v)); break;\n", - propindex, prefix.constData(), p.write.constData(), p.type.constData()); - } else { - fprintf(out, " case %d:\n", propindex); - fprintf(out, " if (%s%s != *reinterpret_cast< %s*>(_v)) {\n", - prefix.constData(), p.member.constData(), p.type.constData()); - fprintf(out, " %s%s = *reinterpret_cast< %s*>(_v);\n", - prefix.constData(), p.member.constData(), p.type.constData()); - if (!p.notify.isEmpty() && p.notifyId != -1) { - const FunctionDef &f = cdef->signalList.at(p.notifyId); - if (f.arguments.size() == 0) - fprintf(out, " Q_EMIT %s();\n", p.notify.constData()); - else if (f.arguments.size() == 1 && f.arguments.at(0).normalizedType == p.type) - fprintf(out, " Q_EMIT %s(%s%s);\n", - p.notify.constData(), prefix.constData(), p.member.constData()); - } - fprintf(out, " }\n"); - fprintf(out, " break;\n"); - } - } - fprintf(out, " default: break;\n"); - fprintf(out, " }\n"); - } - - fprintf(out, - " _id -= %d;\n" - " }", cdef->propertyList.count()); - - fprintf(out, " else "); - fprintf(out, "if (_c == QMetaObject::ResetProperty) {\n"); - if (needReset) { - fprintf(out, " switch (_id) {\n"); - for (int propindex = 0; propindex < cdef->propertyList.size(); ++propindex) { - const PropertyDef &p = cdef->propertyList.at(propindex); - if (!p.reset.endsWith(')')) - continue; - QByteArray prefix; - if (p.inPrivateClass.size()) { - prefix = p.inPrivateClass; - prefix.append("->"); - } - fprintf(out, " case %d: %s%s; break;\n", - propindex, prefix.constData(), p.reset.constData()); - } - fprintf(out, " default: break;\n"); - fprintf(out, " }\n"); - } + fprintf(out, "else "); fprintf(out, - " _id -= %d;\n" - " }", cdef->propertyList.count()); + "if (_c == QMetaObject::ReadProperty || _c == QMetaObject::WriteProperty\n" + " || _c == QMetaObject::ResetProperty || _c == QMetaObject::RegisterPropertyMetaType) {\n" + " qt_static_metacall(this, _c, _id, _a);\n" + " _id -= %d;\n }", cdef->propertyList.count()); fprintf(out, " else "); fprintf(out, "if (_c == QMetaObject::QueryPropertyDesignable) {\n"); @@ -1153,16 +1046,6 @@ void Generator::generateMetacall() " _id -= %d;\n" " }", cdef->propertyList.count()); - fprintf(out, " else "); - fprintf(out, "if (_c == QMetaObject::RegisterPropertyMetaType) {\n"); - fprintf(out, " if (_id < %d)\n", cdef->propertyList.size()); - - if (automaticPropertyMetaTypesHelper().isEmpty()) - fprintf(out, " *reinterpret_cast<int*>(_a[0]) = -1;\n"); - else - fprintf(out, " qt_static_metacall(this, _c, _id, _a);\n"); - fprintf(out, " _id -= %d;\n }", cdef->propertyList.size()); - fprintf(out, "\n#endif // QT_NO_PROPERTIES"); } if (methodList.size() || cdef->signalList.size() || cdef->propertyList.size()) @@ -1246,10 +1129,14 @@ void Generator::generateStaticMetacall() else fprintf(out, " "); fprintf(out, "if (_c == QMetaObject::InvokeMetaMethod) {\n"); + if (cdef->hasQObject) { #ifndef QT_NO_DEBUG - fprintf(out, " Q_ASSERT(staticMetaObject.cast(_o));\n"); + fprintf(out, " Q_ASSERT(staticMetaObject.cast(_o));\n"); #endif - fprintf(out, " %s *_t = static_cast<%s *>(_o);\n", cdef->classname.constData(), cdef->classname.constData()); + fprintf(out, " %s *_t = static_cast<%s *>(_o);\n", cdef->classname.constData(), cdef->classname.constData()); + } else { + fprintf(out, " %s *_t = reinterpret_cast<%s *>(_o);\n", cdef->classname.constData(), cdef->classname.constData()); + } fprintf(out, " switch (_id) {\n"); for (int methodindex = 0; methodindex < methodList.size(); ++methodindex) { const FunctionDef &f = methodList.at(methodindex); @@ -1377,6 +1264,153 @@ void Generator::generateStaticMetacall() needElse = true; } + if (!cdef->propertyList.empty()) { + bool needGet = false; + bool needTempVarForGet = false; + bool needSet = false; + bool needReset = false; + for (int i = 0; i < cdef->propertyList.size(); ++i) { + const PropertyDef &p = cdef->propertyList.at(i); + needGet |= !p.read.isEmpty() || !p.member.isEmpty(); + if (!p.read.isEmpty() || !p.member.isEmpty()) + needTempVarForGet |= (p.gspec != PropertyDef::PointerSpec + && p.gspec != PropertyDef::ReferenceSpec); + + needSet |= !p.write.isEmpty() || (!p.member.isEmpty() && !p.constant); + needReset |= !p.reset.isEmpty(); + } + fprintf(out, "\n#ifndef QT_NO_PROPERTIES\n "); + + if (needElse) + fprintf(out, "else "); + fprintf(out, "if (_c == QMetaObject::ReadProperty) {\n"); + if (needGet) { + if (cdef->hasQObject) { +#ifndef QT_NO_DEBUG + fprintf(out, " Q_ASSERT(staticMetaObject.cast(_o));\n"); +#endif + fprintf(out, " %s *_t = static_cast<%s *>(_o);\n", cdef->classname.constData(), cdef->classname.constData()); + } else { + fprintf(out, " %s *_t = reinterpret_cast<%s *>(_o);\n", cdef->classname.constData(), cdef->classname.constData()); + } + if (needTempVarForGet) + fprintf(out, " void *_v = _a[0];\n"); + fprintf(out, " switch (_id) {\n"); + for (int propindex = 0; propindex < cdef->propertyList.size(); ++propindex) { + const PropertyDef &p = cdef->propertyList.at(propindex); + if (p.read.isEmpty() && p.member.isEmpty()) + continue; + QByteArray prefix = "_t->"; + if (p.inPrivateClass.size()) { + prefix += p.inPrivateClass + "->"; + } + if (p.gspec == PropertyDef::PointerSpec) + fprintf(out, " case %d: _a[0] = const_cast<void*>(reinterpret_cast<const void*>(%s%s())); break;\n", + propindex, prefix.constData(), p.read.constData()); + else if (p.gspec == PropertyDef::ReferenceSpec) + fprintf(out, " case %d: _a[0] = const_cast<void*>(reinterpret_cast<const void*>(&%s%s())); break;\n", + propindex, prefix.constData(), p.read.constData()); + else if (cdef->enumDeclarations.value(p.type, false)) + fprintf(out, " case %d: *reinterpret_cast<int*>(_v) = QFlag(%s%s()); break;\n", + propindex, prefix.constData(), p.read.constData()); + else if (!p.read.isEmpty()) + fprintf(out, " case %d: *reinterpret_cast< %s*>(_v) = %s%s(); break;\n", + propindex, p.type.constData(), prefix.constData(), p.read.constData()); + else + fprintf(out, " case %d: *reinterpret_cast< %s*>(_v) = %s%s; break;\n", + propindex, p.type.constData(), prefix.constData(), p.member.constData()); + } + fprintf(out, " default: break;\n"); + fprintf(out, " }\n"); + } + + fprintf(out, " }"); + + fprintf(out, " else "); + fprintf(out, "if (_c == QMetaObject::WriteProperty) {\n"); + + if (needSet) { + if (cdef->hasQObject) { +#ifndef QT_NO_DEBUG + fprintf(out, " Q_ASSERT(staticMetaObject.cast(_o));\n"); +#endif + fprintf(out, " %s *_t = static_cast<%s *>(_o);\n", cdef->classname.constData(), cdef->classname.constData()); + } else { + fprintf(out, " %s *_t = reinterpret_cast<%s *>(_o);\n", cdef->classname.constData(), cdef->classname.constData()); + } + fprintf(out, " void *_v = _a[0];\n"); + fprintf(out, " switch (_id) {\n"); + for (int propindex = 0; propindex < cdef->propertyList.size(); ++propindex) { + const PropertyDef &p = cdef->propertyList.at(propindex); + if (p.constant) + continue; + if (p.write.isEmpty() && p.member.isEmpty()) + continue; + QByteArray prefix = "_t->"; + if (p.inPrivateClass.size()) { + prefix += p.inPrivateClass + "->"; + } + if (cdef->enumDeclarations.value(p.type, false)) { + fprintf(out, " case %d: %s%s(QFlag(*reinterpret_cast<int*>(_v))); break;\n", + propindex, prefix.constData(), p.write.constData()); + } else if (!p.write.isEmpty()) { + fprintf(out, " case %d: %s%s(*reinterpret_cast< %s*>(_v)); break;\n", + propindex, prefix.constData(), p.write.constData(), p.type.constData()); + } else { + fprintf(out, " case %d:\n", propindex); + fprintf(out, " if (%s%s != *reinterpret_cast< %s*>(_v)) {\n", + prefix.constData(), p.member.constData(), p.type.constData()); + fprintf(out, " %s%s = *reinterpret_cast< %s*>(_v);\n", + prefix.constData(), p.member.constData(), p.type.constData()); + if (!p.notify.isEmpty() && p.notifyId != -1) { + const FunctionDef &f = cdef->signalList.at(p.notifyId); + if (f.arguments.size() == 0) + fprintf(out, " Q_EMIT _t->%s();\n", p.notify.constData()); + else if (f.arguments.size() == 1 && f.arguments.at(0).normalizedType == p.type) + fprintf(out, " Q_EMIT _t->%s(%s%s);\n", + p.notify.constData(), prefix.constData(), p.member.constData()); + } + fprintf(out, " }\n"); + fprintf(out, " break;\n"); + } + } + fprintf(out, " default: break;\n"); + fprintf(out, " }\n"); + } + + fprintf(out, " }"); + + fprintf(out, " else "); + fprintf(out, "if (_c == QMetaObject::ResetProperty) {\n"); + if (needReset) { + if (cdef->hasQObject) { +#ifndef QT_NO_DEBUG + fprintf(out, " Q_ASSERT(staticMetaObject.cast(_o));\n"); +#endif + fprintf(out, " %s *_t = static_cast<%s *>(_o);\n", cdef->classname.constData(), cdef->classname.constData()); + } else { + fprintf(out, " %s *_t = reinterpret_cast<%s *>(_o);\n", cdef->classname.constData(), cdef->classname.constData()); + } + fprintf(out, " switch (_id) {\n"); + for (int propindex = 0; propindex < cdef->propertyList.size(); ++propindex) { + const PropertyDef &p = cdef->propertyList.at(propindex); + if (!p.reset.endsWith(')')) + continue; + QByteArray prefix = "_t->"; + if (p.inPrivateClass.size()) { + prefix += p.inPrivateClass + "->"; + } + fprintf(out, " case %d: %s%s; break;\n", + propindex, prefix.constData(), p.reset.constData()); + } + fprintf(out, " default: break;\n"); + fprintf(out, " }\n"); + } + fprintf(out, " }"); + fprintf(out, "\n#endif // QT_NO_PROPERTIES"); + needElse = true; + } + if (needElse) fprintf(out, "\n"); diff --git a/src/tools/moc/preprocessor.cpp b/src/tools/moc/preprocessor.cpp index c5d6b58412..687c4f8474 100644 --- a/src/tools/moc/preprocessor.cpp +++ b/src/tools/moc/preprocessor.cpp @@ -52,11 +52,12 @@ static QByteArray cleaned(const QByteArray &input) QByteArray result; result.reserve(input.size()); const char *data = input.constData(); + const char *end = input.constData() + input.size(); char *output = result.data(); int newlines = 0; - while (*data) { - while (*data && is_space(*data)) + while (data != end) { + while (data != end && is_space(*data)) ++data; bool takeLine = (*data == '#'); if (*data == '%' && *(data+1) == ':') { @@ -66,15 +67,15 @@ static QByteArray cleaned(const QByteArray &input) if (takeLine) { *output = '#'; ++output; - do ++data; while (*data && is_space(*data)); + do ++data; while (data != end && is_space(*data)); } - while (*data) { + while (data != end) { // handle \\\n, \\\r\n and \\\r if (*data == '\\') { if (*(data + 1) == '\r') { ++data; } - if (*data && (*(data + 1) == '\n' || (*data) == '\r')) { + if (data != end && (*(data + 1) == '\n' || (*data) == '\r')) { ++newlines; data += 1; if (*data != '\r') @@ -201,7 +202,7 @@ Symbols Preprocessor::tokenize(const QByteArray& input, int lineNum, Preprocesso data = skipQuote(data); token = STRING_LITERAL; // concatenate multi-line strings for easier - // STRING_LITERAAL handling in moc + // STRING_LITERAL handling in moc if (!Preprocessor::preprocessOnly && !symbols.isEmpty() && symbols.last().token == STRING_LITERAL) { @@ -964,6 +965,43 @@ int Preprocessor::evaluateCondition() return expression.value(); } +static QByteArray readOrMapFile(QFile *file) +{ + const qint64 size = file->size(); + char *rawInput = reinterpret_cast<char*>(file->map(0, size)); + return rawInput ? QByteArray::fromRawData(rawInput, size) : file->readAll(); +} + +static void mergeStringLiterals(Symbols *_symbols) +{ + Symbols &symbols = *_symbols; + for (Symbols::iterator i = symbols.begin(); i != symbols.end(); ++i) { + if (i->token == STRING_LITERAL) { + Symbols::Iterator mergeSymbol = i; + int literalsLength = mergeSymbol->len; + while (++i != symbols.end() && i->token == STRING_LITERAL) + literalsLength += i->len - 2; // no quotes + + if (literalsLength != mergeSymbol->len) { + QByteArray mergeSymbolOriginalLexem = mergeSymbol->unquotedLexem(); + QByteArray &mergeSymbolLexem = mergeSymbol->lex; + mergeSymbolLexem.resize(0); + mergeSymbolLexem.reserve(literalsLength); + mergeSymbolLexem.append('"'); + mergeSymbolLexem.append(mergeSymbolOriginalLexem); + for (Symbols::const_iterator j = mergeSymbol + 1; j != i; ++j) + mergeSymbolLexem.append(j->lex.constData() + j->from + 1, j->len - 2); // append j->unquotedLexem() + mergeSymbolLexem.append('"'); + mergeSymbol->len = mergeSymbol->lex.length(); + mergeSymbol->from = 0; + i = symbols.erase(mergeSymbol + 1, i); + } + if (i == symbols.end()) + break; + } + } +} + void Preprocessor::preprocess(const QByteArray &filename, Symbols &preprocessed) { currentFilenames.push(filename); @@ -1020,7 +1058,8 @@ void Preprocessor::preprocess(const QByteArray &filename, Symbols &preprocessed) if (!file.open(QFile::ReadOnly)) continue; - QByteArray input = file.readAll(); + QByteArray input = readOrMapFile(&file); + file.close(); if (input.isEmpty()) continue; @@ -1153,9 +1192,10 @@ void Preprocessor::preprocess(const QByteArray &filename, Symbols &preprocessed) currentFilenames.pop(); } -Symbols Preprocessor::preprocessed(const QByteArray &filename, QIODevice *file) +Symbols Preprocessor::preprocessed(const QByteArray &filename, QFile *file) { - QByteArray input = file->readAll(); + QByteArray input = readOrMapFile(file); + if (input.isEmpty()) return symbols; @@ -1176,6 +1216,7 @@ Symbols Preprocessor::preprocessed(const QByteArray &filename, QIODevice *file) // phase 3: preprocess conditions and substitute macros Symbols result; preprocess(filename, result); + mergeStringLiterals(&result); #if 0 for (int j = 0; j < result.size(); ++j) diff --git a/src/tools/moc/preprocessor.h b/src/tools/moc/preprocessor.h index fc86781be8..77f3a30ac0 100644 --- a/src/tools/moc/preprocessor.h +++ b/src/tools/moc/preprocessor.h @@ -57,7 +57,7 @@ typedef SubArray MacroName; #endif typedef QHash<MacroName, Macro> Macros; -class QIODevice; +class QFile; class Preprocessor : public Parser { @@ -67,7 +67,7 @@ public: QList<QByteArray> frameworks; QSet<QByteArray> preprocessedIncludes; Macros macros; - Symbols preprocessed(const QByteArray &filename, QIODevice *device); + Symbols preprocessed(const QByteArray &filename, QFile *device); void parseDefineArguments(Macro *m); diff --git a/src/tools/qdoc/atom.cpp b/src/tools/qdoc/atom.cpp index 1af31afedd..9aaa1af319 100644 --- a/src/tools/qdoc/atom.cpp +++ b/src/tools/qdoc/atom.cpp @@ -97,6 +97,9 @@ QT_BEGIN_NAMESPACE \value ImageText \value ImportantNote \value InlineImage + \value JavaScript + \value EndJavaScript + \value Keyword \value LineBreak \value Link \value LinkNode @@ -107,6 +110,8 @@ QT_BEGIN_NAMESPACE \value ListItemLeft \value ListItemRight \value ListRight + \value NavAutoLink + \value NavLink \value Nop \value Note \value ParaLeft @@ -181,6 +186,7 @@ static const struct { { "InlineImage", Atom::InlineImage }, { "JavaScript", Atom::JavaScript }, { "EndJavaScript", Atom::EndJavaScript }, + { "Keyword", Atom::Keyword }, { "LegaleseLeft", Atom::LegaleseLeft }, { "LegaleseRight", Atom::LegaleseRight }, { "LineBreak", Atom::LineBreak }, @@ -193,6 +199,8 @@ static const struct { { "ListItemLeft", Atom::ListItemLeft }, { "ListItemRight", Atom::ListItemRight }, { "ListRight", Atom::ListRight }, + { "NavAutoLink", Atom::NavAutoLink }, + { "NavLink", Atom::NavLink }, { "Nop", Atom::Nop }, { "NoteLeft", Atom::NoteLeft }, { "NoteRight", Atom::NoteRight }, diff --git a/src/tools/qdoc/atom.h b/src/tools/qdoc/atom.h index 3a7e992f7b..2eb34a042b 100644 --- a/src/tools/qdoc/atom.h +++ b/src/tools/qdoc/atom.h @@ -57,7 +57,7 @@ public: BriefRight, C, CaptionLeft, - CaptionRight, // 10 + CaptionRight, Code, CodeBad, CodeNew, @@ -67,7 +67,7 @@ public: DivLeft, DivRight, EndQmlText, - FootnoteLeft, // 20 + FootnoteLeft, FootnoteRight, FormatElse, FormatEndif, @@ -77,17 +77,18 @@ public: GeneratedList, GuidLink, HR, - Image, // 30 + Image, ImageText, ImportantLeft, ImportantRight, InlineImage, JavaScript, EndJavaScript, + Keyword, LegaleseLeft, LegaleseRight, LineBreak, - Link, // 40 + Link, LinkNode, ListLeft, ListItemNumber, @@ -96,8 +97,10 @@ public: ListItemLeft, ListItemRight, ListRight, + NavAutoLink, + NavLink, Nop, - NoteLeft, // 50 + NoteLeft, NoteRight, ParaLeft, ParaRight, @@ -107,7 +110,7 @@ public: QuotationRight, RawString, SectionLeft, - SectionRight, // 60 + SectionRight, SectionHeadingLeft, SectionHeadingRight, SidebarLeft, @@ -117,7 +120,7 @@ public: SnippetIdentifier, SnippetLocation, String, - TableLeft, // 70 + TableLeft, TableRight, TableHeaderLeft, TableHeaderRight, @@ -127,7 +130,7 @@ public: TableItemRight, TableOfContents, Target, - UnhandledFormat, // 80 + UnhandledFormat, UnknownCommand, Last = UnknownCommand }; diff --git a/src/tools/qdoc/codeparser.cpp b/src/tools/qdoc/codeparser.cpp index 00341940da..acb297d5f9 100644 --- a/src/tools/qdoc/codeparser.cpp +++ b/src/tools/qdoc/codeparser.cpp @@ -65,9 +65,9 @@ QT_BEGIN_NAMESPACE #define COMMAND_TITLE Doc::alias(QLatin1String("title")) #define COMMAND_WRAPPER Doc::alias(QLatin1String("wrapper")) -QString CodeParser::currentSubDir_; QList<CodeParser *> CodeParser::parsers; -bool CodeParser::showInternal = false; +bool CodeParser::showInternal_ = false; +bool CodeParser::singleExec_ = false; /*! The constructor adds this code parser to the static @@ -93,7 +93,8 @@ CodeParser::~CodeParser() */ void CodeParser::initializeParser(const Config& config) { - showInternal = config.getBool(CONFIG_SHOWINTERNAL); + showInternal_ = config.getBool(CONFIG_SHOWINTERNAL); + singleExec_ = config.getBool(CONFIG_SINGLEEXEC); } /*! @@ -262,7 +263,7 @@ void CodeParser::processCommonMetaCommand(const Location& location, node->setStatus(Node::Preliminary); } else if (command == COMMAND_INTERNAL) { - if (!showInternal) { + if (!showInternal_) { node->setAccess(Node::Private); node->setStatus(Node::Internal); if (node->type() == Node::QmlPropertyGroup) { diff --git a/src/tools/qdoc/codeparser.h b/src/tools/qdoc/codeparser.h index 5b3b1192f3..c9a9b746b1 100644 --- a/src/tools/qdoc/codeparser.h +++ b/src/tools/qdoc/codeparser.h @@ -74,7 +74,6 @@ public: static CodeParser *parserForHeaderFile(const QString &filePath); static CodeParser *parserForSourceFile(const QString &filePath); static void setLink(Node* node, Node::LinkType linkType, const QString& arg); - static const QString& currentOutputSubdirectory() { return currentSubDir_; } protected: const QSet<QString>& commonMetaCommands(); @@ -89,9 +88,9 @@ protected: QDocDatabase* qdb_; private: - static QString currentSubDir_; static QList<CodeParser *> parsers; - static bool showInternal; + static bool showInternal_; + static bool singleExec_; }; QT_END_NAMESPACE diff --git a/src/tools/qdoc/config.cpp b/src/tools/qdoc/config.cpp index 51ab341869..f73ac147b8 100644 --- a/src/tools/qdoc/config.cpp +++ b/src/tools/qdoc/config.cpp @@ -98,6 +98,7 @@ QString ConfigStrings::QUOTINGINFORMATION = QStringLiteral("quotinginformation") QString ConfigStrings::SCRIPTDIRS = QStringLiteral("scriptdirs"); QString ConfigStrings::SCRIPTS = QStringLiteral("scripts"); QString ConfigStrings::SHOWINTERNAL = QStringLiteral("showinternal"); +QString ConfigStrings::SINGLEEXEC = QStringLiteral("singleexec"); QString ConfigStrings::SOURCEDIRS = QStringLiteral("sourcedirs"); QString ConfigStrings::SOURCEENCODING = QStringLiteral("sourceencoding"); QString ConfigStrings::SOURCES = QStringLiteral("sources"); @@ -118,6 +119,7 @@ QString ConfigStrings::FILEEXTENSIONS = QStringLiteral("fileextensions"); QString ConfigStrings::IMAGEEXTENSIONS = QStringLiteral("imageextensions"); QString ConfigStrings::QMLONLY = QStringLiteral("qmlonly"); QString ConfigStrings::QMLTYPESPAGE = QStringLiteral("qmltypespage"); +QString ConfigStrings::WRITEQAPAGES = QStringLiteral("writeqapages"); /*! An entry in a stack, where each entry is a list @@ -350,6 +352,10 @@ QString Config::getOutputDir() const t = getString(CONFIG_OUTPUTDIR); else t = overrideOutputDir; + if (Generator::singleExec()) { + QString project = getString(CONFIG_PROJECT); + t += QLatin1Char('/') + project.toLower(); + } if (!Generator::useOutputSubdirs()) { t = t.left(t.lastIndexOf('/')); QString singleOutputSubdir = getString("HTML.outputsubdir"); @@ -869,6 +875,36 @@ bool Config::isMetaKeyChar(QChar ch) } /*! + \a fileName is a master qdocconf file. It contains a list of + qdocconf files and nothing else. Read the list and return it. + */ +QStringList Config::loadMaster(const QString& fileName) +{ + Location location = Location::null; + QFile fin(fileName); + if (!fin.open(QFile::ReadOnly | QFile::Text)) { + if (!Config::installDir.isEmpty()) { + int prefix = location.filePath().length() - location.fileName().length(); + fin.setFileName(Config::installDir + "/" + fileName.right(fileName.length() - prefix)); + } + if (!fin.open(QFile::ReadOnly | QFile::Text)) + location.fatal(tr("Cannot open master qdocconf file '%1': %2").arg(fileName).arg(fin.errorString())); + } + QTextStream stream(&fin); +#ifndef QT_NO_TEXTCODEC + stream.setCodec("UTF-8"); +#endif + QStringList qdocFiles; + QString line = stream.readLine(); + while (!line.isNull()) { + qdocFiles.append(line); + line = stream.readLine(); + } + fin.close(); + return qdocFiles; +} + +/*! Load, parse, and process a qdoc configuration file. This function is only called by the other load() function, but this one is recursive, i.e., it calls itself when it sees diff --git a/src/tools/qdoc/config.h b/src/tools/qdoc/config.h index 70b5adfd68..a836448719 100644 --- a/src/tools/qdoc/config.h +++ b/src/tools/qdoc/config.h @@ -108,6 +108,7 @@ public: QStringList getExampleQdocFiles(const QSet<QString> &excludedDirs, const QSet<QString> &excludedFiles); QStringList getExampleImageFiles(const QSet<QString> &excludedDirs, const QSet<QString> &excludedFiles); + static QStringList loadMaster(const QString& fileName); static QStringList getFilesHere(const QString& dir, const QString& nameFilter, const Location &location = Location(), @@ -209,6 +210,7 @@ struct ConfigStrings static QString SCRIPTDIRS; static QString SCRIPTS; static QString SHOWINTERNAL; + static QString SINGLEEXEC; static QString SOURCEDIRS; static QString SOURCEENCODING; static QString SOURCES; @@ -229,6 +231,7 @@ struct ConfigStrings static QString IMAGEEXTENSIONS; static QString QMLONLY; static QString QMLTYPESPAGE; + static QString WRITEQAPAGES; }; #define CONFIG_ALIAS ConfigStrings::ALIAS @@ -282,6 +285,7 @@ struct ConfigStrings #define CONFIG_SCRIPTDIRS ConfigStrings::SCRIPTDIRS #define CONFIG_SCRIPTS ConfigStrings::SCRIPTS #define CONFIG_SHOWINTERNAL ConfigStrings::SHOWINTERNAL +#define CONFIG_SINGLEEXEC ConfigStrings::SINGLEEXEC #define CONFIG_SOURCEDIRS ConfigStrings::SOURCEDIRS #define CONFIG_SOURCEENCODING ConfigStrings::SOURCEENCODING #define CONFIG_SOURCES ConfigStrings::SOURCES @@ -302,6 +306,7 @@ struct ConfigStrings #define CONFIG_IMAGEEXTENSIONS ConfigStrings::IMAGEEXTENSIONS #define CONFIG_QMLONLY ConfigStrings::QMLONLY #define CONFIG_QMLTYPESPAGE ConfigStrings::QMLTYPESPAGE +#define CONFIG_WRITEQAPAGES ConfigStrings::WRITEQAPAGES QT_END_NAMESPACE diff --git a/src/tools/qdoc/ditaxmlgenerator.cpp b/src/tools/qdoc/ditaxmlgenerator.cpp index d9cf56769b..54b358e170 100644 --- a/src/tools/qdoc/ditaxmlgenerator.cpp +++ b/src/tools/qdoc/ditaxmlgenerator.cpp @@ -663,10 +663,10 @@ GuidMap* DitaXmlGenerator::lookupGuidMap(const QString& fileName) */ void DitaXmlGenerator::generateDocs() { - if (!runPrepareOnly()) + if (!preparing()) Generator::generateDocs(); - if (!runGenerateOnly()) { + if (!generating()) { QString fileBase = project.toLower().simplified().replace(QLatin1Char(' '), QLatin1Char('-')); qdb_->generateIndex(outputDir() + QLatin1Char('/') + fileBase + ".index", projectUrl, @@ -675,7 +675,7 @@ void DitaXmlGenerator::generateDocs() true); } - if (!runPrepareOnly()) { + if (!preparing()) { writeDitaMap(); /* Generate the XML tag file, if it was requested. diff --git a/src/tools/qdoc/doc.cpp b/src/tools/qdoc/doc.cpp index 5a3ad959d2..c10e3b4669 100644 --- a/src/tools/qdoc/doc.cpp +++ b/src/tools/qdoc/doc.cpp @@ -1611,12 +1611,14 @@ void DocParser::parse(const QString& source, QString word = in.mid(startPos, pos - startPos); // is word a C++ symbol or an English word? if ((numInternalUppercase >= 1 && numLowercase >= 2) - || numStrangeSymbols >= 1) { - append(Atom::AutoLink, word); + || numStrangeSymbols > 0) { + if (word.startsWith(QString("__"))) + appendWord(word); + else + append(Atom::AutoLink, word); } - else { + else appendWord(word); - } } } } @@ -1690,12 +1692,15 @@ void DocParser::insertTarget(const QString &target, bool keyword) } else { targetMap_.insert(target, location()); - append(Atom::Target, target); priv->constructExtra(); - if (keyword) + if (keyword) { + append(Atom::Keyword, target); priv->extra->keywords_.append(priv->text.lastAtom()); - else + } + else { + append(Atom::Target, target); priv->extra->targets_.append(priv->text.lastAtom()); + } } } @@ -1991,7 +1996,7 @@ void DocParser::append(const QString &string) Atom::Type lastType = priv->text.lastAtom()->type(); if ((lastType == Atom::Code) && priv->text.lastAtom()->string().endsWith(QLatin1String("\n\n"))) priv->text.lastAtom()->chopString(); - priv->text << Atom(string); + priv->text << Atom(string); // The Atom type is Link. } void DocParser::append(Atom::Type type, const QString& p1, const QString& p2) @@ -2008,7 +2013,7 @@ void DocParser::append(const QString& p1, const QString& p2) if ((lastType == Atom::Code) && priv->text.lastAtom()->string().endsWith(QLatin1String("\n\n"))) priv->text.lastAtom()->chopString(); if (p2.isEmpty()) - priv->text << Atom(p1); + priv->text << Atom(p1); // The Atom type is Link. else priv->text << LinkAtom(p1, p2); } @@ -2319,7 +2324,10 @@ QString DocParser::getBracedArgument(bool verbatim) } break; default: - arg += in[pos]; + if (in[pos].isSpace() && !verbatim) + arg += QChar(' '); + else + arg += in[pos]; pos++; } } @@ -2816,18 +2824,6 @@ QString DocParser::slashed(const QString& str) #define COMMAND_BRIEF Doc::alias("brief") #define COMMAND_QMLBRIEF Doc::alias("qmlbrief") -#if 0 -Doc::Doc(const Location& start_loc, - const Location& end_loc, - const QString& source, - const QSet<QString>& metaCommandSet) -{ - priv = new DocPrivate(start_loc,end_loc,source); - DocParser parser; - parser.parse(source,priv,metaCommandSet,QSet<QString>()); -} -#endif - /*! Parse the qdoc comment \a source. Build up a list of all the topic commands found including their arguments. This constructor is used @@ -3234,6 +3230,9 @@ void Doc::initialize(const Config& config) } } +/*! + All the heap allocated variables are deleted. + */ void Doc::terminate() { DocParser::exampleFiles.clear(); diff --git a/src/tools/qdoc/generator.cpp b/src/tools/qdoc/generator.cpp index 5aff19e121..db531e3936 100644 --- a/src/tools/qdoc/generator.cpp +++ b/src/tools/qdoc/generator.cpp @@ -64,7 +64,7 @@ QString Generator::outSubdir_; QStringList Generator::outFileNames_; QSet<QString> Generator::outputFormats; QHash<QString, QString> Generator::outputPrefixes; -QString Generator::project; +QString Generator::project_; QStringList Generator::scriptDirs; QStringList Generator::scriptFiles; QString Generator::sinceTitles[] = @@ -92,7 +92,9 @@ bool Generator::debugging_ = false; bool Generator::noLinkErrors_ = false; bool Generator::autolinkErrors_ = false; bool Generator::redirectDocumentationToDevNull_ = false; -Generator::Passes Generator::qdocPass_ = Both; +Generator::QDocPass Generator::qdocPass_ = Generator::Neither; +bool Generator::qdocSingleExec_ = false; +bool Generator::qdocWriteQaPages_ = false; bool Generator::useOutputSubdirs_ = true; void Generator::startDebugging(const QString& message) @@ -134,6 +136,7 @@ Generator::Generator() inTableHeader_(false), threeColumnEnumValueTable_(true), showInternal_(false), + singleExec_(false), numTableRows_(0) { qdb_ = QDocDatabase::qdocDB(); @@ -259,7 +262,8 @@ void Generator::writeOutFileNames() void Generator::beginSubPage(const InnerNode* node, const QString& fileName) { QString path = outputDir() + QLatin1Char('/'); - if (Generator::useOutputSubdirs() && !node->outputSubdirectory().isEmpty()) + if (Generator::useOutputSubdirs() && !node->outputSubdirectory().isEmpty() && + !outputDir().endsWith(node->outputSubdirectory())) path += node->outputSubdirectory() + QLatin1Char('/'); path += fileName; @@ -314,7 +318,7 @@ QString Generator::fileBase(const Node *node) const if (node->isExample() || node->isExampleFile()) { QString modPrefix(node->moduleName()); if (modPrefix.isEmpty()) { - modPrefix = project; + modPrefix = project_; } base.prepend(modPrefix.toLower() + QLatin1Char('-')); } @@ -1529,7 +1533,7 @@ void Generator::initialize(const Config &config) QDir dirInfo; if (dirInfo.exists(outDir_)) { - if (!runGenerateOnly() && Generator::useOutputSubdirs()) { + if (!generating() && Generator::useOutputSubdirs()) { if (!Config::removeDirContents(outDir_)) config.lastLocation().error(tr("Cannot empty output directory '%1'").arg(outDir_)); } @@ -1643,7 +1647,7 @@ void Generator::initialize(const Config &config) ++n; } - project = config.getString(CONFIG_PROJECT); + project_ = config.getString(CONFIG_PROJECT); QStringList prefixes = config.getStringList(CONFIG_OUTPUTPREFIXES); if (!prefixes.isEmpty()) { @@ -1678,6 +1682,7 @@ void Generator::initializeGenerator(const Config& config) { config_ = &config; showInternal_ = config.getBool(CONFIG_SHOWINTERNAL); + singleExec_ = config.getBool(CONFIG_SINGLEEXEC); } bool Generator::matchAhead(const Atom *atom, Atom::Type expectedAtomType) diff --git a/src/tools/qdoc/generator.h b/src/tools/qdoc/generator.h index 110a8d9e73..b1faf02ae9 100644 --- a/src/tools/qdoc/generator.h +++ b/src/tools/qdoc/generator.h @@ -61,7 +61,7 @@ class Generator Q_DECLARE_TR_FUNCTIONS(QDoc::Generator) public: - enum Passes { Both, Prepare, Generate }; + enum QDocPass { Neither, Prepare, Generate }; enum ListType { Generic, Obsolete }; Generator(); @@ -91,10 +91,14 @@ public: static bool debugging() { return debugging_; } static bool noLinkErrors() { return noLinkErrors_; } static bool autolinkErrors() { return autolinkErrors_; } - static void setQDocPass(Passes pass) { qdocPass_ = pass; } - static bool runPrepareOnly() { return (qdocPass_ == Prepare); } - static bool runGenerateOnly() { return (qdocPass_ == Generate); } - static QString defaultModuleName() { return project; } + static void setQDocPass(QDocPass t) { qdocPass_ = t; } + static bool preparing() { return (qdocPass_ == Prepare); } + static bool generating() { return (qdocPass_ == Generate); } + static bool singleExec() { return qdocSingleExec_; } + static bool writeQaPages() { return qdocWriteQaPages_; } + static void setSingleExec() { qdocSingleExec_ = true; } + static void setWriteQaPages() { qdocWriteQaPages_ = true; } + static QString defaultModuleName() { return project_; } static void resetUseOutputSubdirs() { useOutputSubdirs_ = false; } static bool useOutputSubdirs() { return useOutputSubdirs_; } @@ -103,6 +107,7 @@ protected: virtual void endSubPage(); virtual QString fileBase(const Node* node) const; virtual QString fileExtension() const = 0; + virtual void generateQAPage() { } virtual void generateAlsoList(const Node *node, CodeMarker *marker); virtual int generateAtom(const Atom *atom, const Node *relative, CodeMarker *marker); virtual void generateBody(const Node *node, CodeMarker *marker); @@ -198,7 +203,7 @@ private: static QStringList imageDirs; static QStringList imageFiles; static QMap<QString, QStringList> imgFileExts; - static QString project; + static QString project_; static QString outDir_; static QString outSubdir_; static QStringList outFileNames_; @@ -212,7 +217,9 @@ private: static bool noLinkErrors_; static bool autolinkErrors_; static bool redirectDocumentationToDevNull_; - static Passes qdocPass_; + static QDocPass qdocPass_; + static bool qdocSingleExec_; + static bool qdocWriteQaPages_; static bool useOutputSubdirs_; void generateReimplementedFrom(const FunctionNode *func, CodeMarker *marker); @@ -232,6 +239,7 @@ private: bool inTableHeader_; bool threeColumnEnumValueTable_; bool showInternal_; + bool singleExec_; int numTableRows_; QString link_; QString sectionNumber_; diff --git a/src/tools/qdoc/htmlgenerator.cpp b/src/tools/qdoc/htmlgenerator.cpp index 0f5bf26e71..bc79d5ce7f 100644 --- a/src/tools/qdoc/htmlgenerator.cpp +++ b/src/tools/qdoc/htmlgenerator.cpp @@ -100,8 +100,10 @@ HtmlGenerator::HtmlGenerator() */ HtmlGenerator::~HtmlGenerator() { - if (helpProjectWriter) + if (helpProjectWriter) { delete helpProjectWriter; + helpProjectWriter = 0; + } } /*! @@ -130,6 +132,11 @@ void HtmlGenerator::initializeGenerator(const Config &config) Generator::initializeGenerator(config); obsoleteLinks = config.getBool(CONFIG_OBSOLETELINKS); setImageFileExtensions(QStringList() << "png" << "jpg" << "jpeg" << "gif"); + + /* + The formatting maps are owned by Generator. They are cleared in + Generator::terminate(). + */ int i = 0; while (defaults[i].key) { formattingLeftMap().insert(defaults[i].key, defaults[i].left); @@ -215,7 +222,12 @@ void HtmlGenerator::initializeGenerator(const Config &config) // The following line was changed to fix QTBUG-27798 //codeIndent = config.getInt(CONFIG_CODEINDENT); - helpProjectWriter = new HelpProjectWriter(config, project.toLower() + ".qhp", this); + /* + The help file write should be allocated once and only once + per qdoc execution. + */ + if (helpProjectWriter == 0) + helpProjectWriter = new HelpProjectWriter(config, project.toLower() + ".qhp", this); // Documentation template handling headerScripts = config.getString(HtmlGenerator::format() + Config::dot + CONFIG_HEADERSCRIPTS); @@ -263,6 +275,20 @@ QString HtmlGenerator::format() } /*! + Generate targets for any \keyword commands that were seen + in the qdoc comment for the \a node. + */ +void HtmlGenerator::generateKeywordAnchors(const Node* node) +{ + if (!node->doc().isEmpty()) { + const QList<Atom*>& keywords = node->doc().keywords(); + foreach (Atom* a, keywords) { + out() << "<a name=\"" << Doc::canonicalTitle(a->string()) << "\"></a>"; + } + } +} + +/*! Traverses the current tree generating all the HTML documentation. */ void HtmlGenerator::generateDocs() @@ -270,10 +296,12 @@ void HtmlGenerator::generateDocs() Node* qflags = qdb_->findClassNode(QStringList("QFlags")); if (qflags) qflagsHref_ = linkForNode(qflags,0); - if (!runPrepareOnly()) + if (!preparing()) Generator::generateDocs(); + if (Generator::generating() && Generator::writeQaPages()) + generateQAPage(); - if (!runGenerateOnly()) { + if (!generating()) { QString fileBase = project.toLower().simplified().replace(QLatin1Char(' '), QLatin1Char('-')); qdb_->generateIndex(outputDir() + QLatin1Char('/') + fileBase + ".index", projectUrl, @@ -282,7 +310,7 @@ void HtmlGenerator::generateDocs() true); } - if (!runPrepareOnly()) { + if (!preparing()) { helpProjectWriter->generate(); generateManifestFiles(); /* @@ -293,6 +321,144 @@ void HtmlGenerator::generateDocs() } /*! + Output the module's Quality Assurance page. + */ +void HtmlGenerator::generateQAPage() +{ + NamespaceNode* node = qdb_->primaryTreeRoot(); + beginSubPage(node, "aaa-" + defaultModuleName().toLower() + "-qa-page.html"); + CodeMarker* marker = CodeMarker::markerForFileName(node->location().filePath()); + QString title = "Quality Assurance Page for " + defaultModuleName(); + QString t = "Quality assurance information for checking the " + defaultModuleName() + " documentation."; + generateHeader(title, node, marker); + generateTitle(title, Text() << t, LargeSubTitle, node, marker); + + QStringList strings; + QVector<int> counts; + QString depends = qdb_->getLinkCounts(strings, counts); + if (!strings.isEmpty()) { + t = "Intermodule Link Counts"; + QString ref = registerRef(t); + out() << "<a name=\"" << ref << "\"></a>" << divNavTop << '\n'; + out() << "<h2 id=\"" << ref << "\">" << protectEnc(t) << "</h2>\n"; + out() << "<table class=\"valuelist\"><tr valign=\"top\" " + << "class=\"even\"><th class=\"tblConst\">Destination Module</th>" + << "<th class=\"tblval\">Link Count</th></tr>\n"; + QString fileName; + for (int i = 0; i< strings.size(); ++i) { + fileName = generateLinksToLinksPage(strings.at(i), marker); + out() << "<tr><td class=\"topAlign\"><tt>" + << "<a href=\"" << fileName << "\">" + << strings.at(i) << "</a>" + << "</tt></td><td class=\"topAlign\"><tt>" << counts.at(i) + << "</tt></td></tr>\n"; + } + int count = 0; + fileName = generateLinksToBrokenLinksPage(marker, count); + if (count != 0) { + out() << "<tr><td class=\"topAlign\"><tt>" + << "<a href=\"" << fileName << "\">" + << "Broken Links" << "</a>" + << "</tt></td><td class=\"topAlign\"><tt>" << count + << "</tt></td></tr>\n"; + + } + + out() << "</table>\n"; + t = "The Optimal \"depends\" Variable"; + out() << "<h2>" << protectEnc(t) << "</h2>\n"; + t = "Consider replacing the depends variable in " + defaultModuleName().toLower() + + ".qdocconf with this one, if the two are not identical:"; + out() << "<p>" << protectEnc(t) << "</p>\n"; + out() << "<p>" << protectEnc(depends) << "</p>\n"; + } + generateFooter(); + endSubPage(); +} + +/*! + This function writes an html file containing a list of + links to links that originate in the current module and + go to targets in the specified \a module. The \a marker + is used for the same thing the marker is always used for. + */ +QString HtmlGenerator::generateLinksToLinksPage(const QString& module, CodeMarker* marker) +{ + NamespaceNode* node = qdb_->primaryTreeRoot(); + QString fileName = "aaa-links-to-" + module + ".html"; + beginSubPage(node, fileName); + QString title = "Links from " + defaultModuleName() + " to " + module; + generateHeader(title, node, marker); + generateTitle(title, Text(), SmallSubTitle, node, marker); + out() << "<p>This is a list of links from " << defaultModuleName() + << " to " << module << ". "; + out() << "Click on a link to go to the location of the link. The link is marked "; + out() << "with red asterisks. "; + out() << "Click on the marked link to see if it goes to the right place.</p>\n"; + TargetList* tlist = qdb_->getTargetList(module); + if (tlist) { + out() << "<table class=\"valuelist\"><tr valign=\"top\" class=\"odd\"><th class=\"tblConst\">Link to link...</th><th class=\"tblval\">In file...</th><th class=\"tbldscr\">Somewhere after line number...</th></tr>\n"; + foreach (TargetLoc* t, *tlist) { + // e.g.: <a name="link-8421"></a><a href="layout.html">Layout Management</a> + out() << "<tr><td class=\"topAlign\">"; + out() << "<a href=\"" << t->fileName_ << "#" << t->target_ << "\">"; + out() << t->text_ << "</a></td>"; + out() << "<td class=\"topAlign\">"; + QString f = t->loc_->doc().location().filePath(); + out() << f << "</td>"; + out() << "<td class=\"topAlign\">"; + out() << t->loc_->doc().location().lineNo() << "</td></tr>\n"; + } + out() << "</table>\n"; + } + generateFooter(); + endSubPage(); + return fileName; +} + +/*! + This function writes an html file containing a list of + links to broken links that originate in the current + module and go nowwhere. It returns the name of the file + it generates, and it sets \a count to the number of + broken links that were found. The \a marker is used for + the same thing the marker is always used for. + */ +QString HtmlGenerator::generateLinksToBrokenLinksPage(CodeMarker* marker, int& count) +{ + QString fileName; + NamespaceNode* node = qdb_->primaryTreeRoot(); + TargetList* tlist = qdb_->getTargetList("broken"); + if (tlist && !tlist->isEmpty()) { + count = tlist->size(); + fileName = "aaa-links-to-broken-links.html"; + beginSubPage(node, fileName); + QString title = "Broken links in " + defaultModuleName(); + generateHeader(title, node, marker); + generateTitle(title, Text(), SmallSubTitle, node, marker); + out() << "<p>This is a list of broken links in " << defaultModuleName() << ". "; + out() << "Click on a link to go to the broken link. "; + out() << "The link's target could not be found.</p>\n"; + out() << "<table class=\"valuelist\"><tr valign=\"top\" class=\"odd\"><th class=\"tblConst\">Link to broken link...</th><th class=\"tblval\">In file...</th><th class=\"tbldscr\">Somewhere after line number...</th></tr>\n"; + foreach (TargetLoc* t, *tlist) { + // e.g.: <a name="link-8421"></a><a href="layout.html">Layout Management</a> + out() << "<tr><td class=\"topAlign\">"; + out() << "<a href=\"" << t->fileName_ << "#" << t->target_ << "\">"; + out() << t->text_ << "</a></td>"; + out() << "<td class=\"topAlign\">"; + QString f = t->loc_->doc().location().filePath(); + out() << f << "</td>"; + out() << "<td class=\"topAlign\">"; + out() << t->loc_->doc().location().lineNo() << "</td></tr>\n"; + } + out() << "</table>\n"; + generateFooter(); + endSubPage(); + } + return fileName; +} + +/*! Generate html from an instance of Atom. */ int HtmlGenerator::generateAtom(const Atom *atom, const Node *relative, CodeMarker *marker) @@ -310,6 +476,7 @@ int HtmlGenerator::generateAtom(const Atom *atom, const Node *relative, CodeMark case Atom::AbstractRight: break; case Atom::AutoLink: + case Atom::NavAutoLink: if (!inLink_ && !inContents_ && !inSectionHeading_) { const Node *node = 0; QString link = getAutoLink(atom, relative, &node); @@ -321,9 +488,15 @@ int HtmlGenerator::generateAtom(const Atom *atom, const Node *relative, CodeMark if ((relative->parent() != node) && !relative->isObsolete()) link.clear(); } - if (link.isEmpty()) + if (link.isEmpty()) { out() << protectEnc(atom->string()); + } else { + if (Generator::writeQaPages() && node && (atom->type() != Atom::NavAutoLink)) { + QString text = atom->string(); + QString target = qdb_->getNewLinkTarget(relative, node, outFileName(), text); + out() << "<a id=\"" << Doc::canonicalTitle(target) << "\" class=\"qa-mark\"></a>"; + } beginLink(link, node, relative); generateLink(atom, marker); endLink(); @@ -806,14 +979,31 @@ int HtmlGenerator::generateAtom(const Atom *atom, const Node *relative, CodeMark out() << "<br/>"; break; case Atom::Link: + case Atom::NavLink: { inObsoleteLink = false; const Node *node = 0; QString link = getLink(atom, relative, &node); if (link.isEmpty() && (node != relative) && !noLinkErrors()) { relative->doc().location().warning(tr("Can't link to '%1'").arg(atom->string())); + if (Generator::writeQaPages() && (atom->type() != Atom::NavAutoLink)) { + QString text = atom->next()->next()->string(); + QString target = qdb_->getNewLinkTarget(relative, node, outFileName(), text, true); + out() << "<a id=\"" << Doc::canonicalTitle(target) << "\" class=\"qa-mark\"></a>"; + } } else { + if (Generator::writeQaPages() && node && (atom->type() != Atom::NavLink)) { + QString text = atom->next()->next()->string(); + QString target = qdb_->getNewLinkTarget(relative, node, outFileName(), text); + out() << "<a id=\"" << Doc::canonicalTitle(target) << "\" class=\"qa-mark\"></a>"; + } + /* + mws saw this on 17/10/2014. + Is this correct? Setting node to 0 means the + following test always fails. Did we decide to + no longer warn about linking to obsolete things? + */ node = 0; if (node && node->status() == Node::Obsolete) { if ((relative->parent() != node) && !relative->isObsolete()) { @@ -1126,6 +1316,8 @@ int HtmlGenerator::generateAtom(const Atom *atom, const Node *relative, CodeMark break; case Atom::TableOfContents: break; + case Atom::Keyword: + break; case Atom::Target: out() << "<a name=\"" << Doc::canonicalTitle(atom->string()) << "\"></a>"; break; @@ -1173,8 +1365,10 @@ void HtmlGenerator::generateClassLikeNode(InnerNode* inner, CodeMarker* marker) subtitleText << "(" << Atom(Atom::AutoLink, fullTitle) << ")" << Atom(Atom::LineBreak); generateHeader(title, inner, marker); + sections = marker->sections(inner, CodeMarker::Summary, CodeMarker::Okay); generateTableOfContents(inner,marker,§ions); + generateKeywordAnchors(inner); generateTitle(title, subtitleText, SmallSubTitle, inner, marker); generateBrief(inner, marker); generateRequisites(inner, marker); @@ -1347,6 +1541,7 @@ void HtmlGenerator::generateQmlTypePage(QmlClassNode* qcn, CodeMarker* marker) QList<Section> sections = marker->qmlSections(qcn, CodeMarker::Summary); generateTableOfContents(qcn, marker, §ions); marker = CodeMarker::markerForLanguage(QLatin1String("QML")); + generateKeywordAnchors(qcn); generateTitle(htmlTitle, Text() << qcn->subTitle(), subTitleSize, qcn, marker); generateBrief(qcn, marker); generateQmlRequisites(qcn, marker); @@ -1418,6 +1613,7 @@ void HtmlGenerator::generateQmlBasicTypePage(QmlBasicTypeNode* qbtn, CodeMarker* generateHeader(htmlTitle, qbtn, marker); QList<Section> sections = marker->sections(qbtn, CodeMarker::Summary, CodeMarker::Okay); generateTableOfContents(qbtn,marker,§ions); + generateKeywordAnchors(qbtn); generateTitle(htmlTitle, Text() << qbtn->subTitle(), subTitleSize, @@ -1463,6 +1659,7 @@ void HtmlGenerator::generateDocNode(DocNode* dn, CodeMarker* marker) if ((dn->name() != QStringLiteral("index.html"))) generateTableOfContents(dn,marker,0); + generateKeywordAnchors(dn); generateTitle(fullTitle, Text() << dn->subTitle(), subTitleSize, @@ -1547,6 +1744,7 @@ void HtmlGenerator::generateCollectionNode(CollectionNode* cn, CodeMarker* marke generateHeader(fullTitle, cn, marker); generateTableOfContents(cn,marker,0); + generateKeywordAnchors(cn); generateTitle(fullTitle, Text() << cn->subTitle(), subTitleSize, cn, marker); if (cn->isModule()) { @@ -1647,11 +1845,11 @@ void HtmlGenerator::generateNavigationBar(const QString &title, return; if (!homepage.isEmpty()) navigationbar << Atom(Atom::ListItemLeft) - << Atom(Atom::AutoLink, homepage) + << Atom(Atom::NavAutoLink, homepage) << Atom(Atom::ListItemRight); if (!landingpage.isEmpty() && landingpage != title) navigationbar << Atom(Atom::ListItemLeft) - << Atom(Atom::AutoLink, landingpage) + << Atom(Atom::NavAutoLink, landingpage) << Atom(Atom::ListItemRight); if (node->isClass()) { @@ -1660,7 +1858,7 @@ void HtmlGenerator::generateNavigationBar(const QString &title, if (!cppclassespage.isEmpty()) navigationbar << Atom(Atom::ListItemLeft) - << Atom(Atom::Link, cppclassespage) + << Atom(Atom::NavLink, cppclassespage) << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK) << Atom(Atom::String, QLatin1String("C++ Classes")) << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK) @@ -1674,7 +1872,7 @@ void HtmlGenerator::generateNavigationBar(const QString &title, else if (node->isQmlType() || node->isQmlBasicType()) { if (!qmltypespage.isEmpty()) navigationbar << Atom(Atom::ListItemLeft) - << Atom(Atom::Link, qmltypespage) + << Atom(Atom::NavLink, qmltypespage) << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK) << Atom(Atom::String, QLatin1String("QML Types")) << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK) @@ -1686,7 +1884,7 @@ void HtmlGenerator::generateNavigationBar(const QString &title, else { if (node->isExampleFile()) { navigationbar << Atom(Atom::ListItemLeft) - << Atom(Atom::Link, node->parent()->name()) + << Atom(Atom::NavLink, node->parent()->name()) << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK) << Atom(Atom::String, node->parent()->title()) << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK) @@ -2321,7 +2519,7 @@ QString HtmlGenerator::generateListOfAllMemberFile(const InnerNode *inner, out() << ", including inherited members.</p>\n"; Section section = sections.first(); - generateSectionList(section, 0, marker, CodeMarker::Subpage); + generateSectionList(section, inner, marker, CodeMarker::Subpage); generateFooter(); endSubPage(); @@ -2378,7 +2576,7 @@ QString HtmlGenerator::generateAllQmlMembersFile(QmlClassNode* qml_cn, CodeMarke prefix = keys.at(j).mid(1); prefix = prefix.left(keys.at(j).indexOf("::")+1); } - generateQmlItem(nodes[j], qcn, marker, true); + generateQmlItem(nodes[j], qml_cn, marker, true); if (nodes[j]->isAttached()) out() << " [attached]"; //generateSynopsis(nodes[j], qcn, marker, CodeMarker::Subpage, false, &prefix); @@ -2807,8 +3005,9 @@ void HtmlGenerator::generateCompactList(ListType listType, else if (listType == Obsolete) { QString fileName = fileBase(it.value()) + "-obsolete." + fileExtension(); QString link; - if (useOutputSubdirs()) + if (useOutputSubdirs()) { link = QString("../" + it.value()->outputSubdirectory() + QLatin1Char('/')); + } link += fileName; out() << "<a href=\"" << link << "\">"; } @@ -2848,7 +3047,7 @@ void HtmlGenerator::generateFunctionIndex(const Node *relative) char currentLetter; out() << "<ul>\n"; - NodeMapMap funcIndex = qdb_->getFunctionIndex(); + NodeMapMap& funcIndex = qdb_->getFunctionIndex(); QMap<QString, NodeMap >::ConstIterator f = funcIndex.constBegin(); while (f != funcIndex.constEnd()) { out() << "<li>"; @@ -3667,34 +3866,7 @@ QString HtmlGenerator::getLink(const Atom *atom, const Node *relative, const Nod if (t.startsWith("mailto:")) return t; } - - QString ref; - - *node = qdb_->findNodeForAtom(atom, relative, ref); - if (!(*node)) - return QString(); - - QString url = (*node)->url(); - if (!url.isEmpty()) { - if (ref.isEmpty()) - return url; - int hashtag = url.lastIndexOf(QChar('#')); - if (hashtag != -1) - url.truncate(hashtag); - return url + "#" + ref; - } - /* - Given that *node is not null, we now cconstruct a link - to the page that *node represents, and then if we found - a target on that page, we connect the target to the link - with '#'. - */ - QString link = linkForNode(*node, relative); - if (*node && (*node)->subType() == Node::Image) - link = "images/used-in-examples/" + link; - if (!ref.isEmpty()) - link += QLatin1Char('#') + ref; - return link; + return getAutoLink(atom, relative, node); } /*! @@ -3712,29 +3884,28 @@ QString HtmlGenerator::getLink(const Atom *atom, const Node *relative, const Nod QString HtmlGenerator::getAutoLink(const Atom *atom, const Node *relative, const Node** node) { QString ref; - QString link; *node = qdb_->findNodeForAtom(atom, relative, ref); if (!(*node)) return QString(); - QString url = (*node)->url(); - if (!url.isEmpty()) { - if (ref.isEmpty()) - return url; - int hashtag = url.lastIndexOf(QChar('#')); + QString link = (*node)->url(); + if (link.isEmpty()) { + link = linkForNode(*node, relative); + if ((*node)->subType() == Node::Image) + link = "images/used-in-examples/" + link; + if (!ref.isEmpty()) + link += QLatin1Char('#') + ref; + } + else if (!ref.isEmpty()) { + int hashtag = link.lastIndexOf(QChar('#')); if (hashtag != -1) - url.truncate(hashtag); - return url + "#" + ref; + link.truncate(hashtag); + link += "#" + ref; } - - link = linkForNode(*node, relative); - if (!ref.isEmpty()) - link += QLatin1Char('#') + ref; return link; } - /*! Construct the link string for the \a node and return it. The \a relative node is use to decide the link we are @@ -3789,7 +3960,12 @@ QString HtmlGenerator::linkForNode(const Node *node, const Node *relative) if (node && relative && (node != relative)) { if (useOutputSubdirs() && !node->isExternalPage() && node->outputSubdirectory() != relative->outputSubdirectory()) { - link.prepend(QString("../" + node->outputSubdirectory() + QLatin1Char('/'))); + if (link.startsWith(QString(node->outputSubdirectory() + QLatin1Char('/')))) { + link.prepend(QString("../")); + } + else { + link.prepend(QString("../" + node->outputSubdirectory() + QLatin1Char('/'))); + } } } return link; @@ -3827,6 +4003,7 @@ void HtmlGenerator::generateDetailedMember(const Node *node, generateMacRef(node, marker); #endif generateExtractionMark(node, MemberMark); + generateKeywordAnchors(node); QString nodeRef = refForNode(node); if (node->type() == Node::Enum && (enume = static_cast<const EnumNode *>(node))->flagsType()) { @@ -4063,6 +4240,7 @@ void HtmlGenerator::generateDetailedQmlMember(Node *node, generateMacRef(node, marker); #endif generateExtractionMark(node, MemberMark); + generateKeywordAnchors(node); out() << "<div class=\"qmlitem\">"; QString nodeRef = refForNode(node); if (node->type() == Node::QmlPropertyGroup) { @@ -4516,6 +4694,9 @@ void HtmlGenerator::generateManifestFile(QString manifest, QString element) Reads metacontent - additional attributes and tags to apply when generating manifest files, read from config. Takes the configuration class \a config as a parameter. + + The manifest metacontent map is cleared immediately after + the manifest files have been generated. */ void HtmlGenerator::readManifestMetaContent(const Config &config) { diff --git a/src/tools/qdoc/htmlgenerator.h b/src/tools/qdoc/htmlgenerator.h index 40360da02e..f7625113f0 100644 --- a/src/tools/qdoc/htmlgenerator.h +++ b/src/tools/qdoc/htmlgenerator.h @@ -89,6 +89,9 @@ public: static QString sinceTitle(int i) { return sinceTitles[i]; } protected: + virtual void generateQAPage(); + QString generateLinksToLinksPage(const QString& module, CodeMarker* marker); + QString generateLinksToBrokenLinksPage(CodeMarker* marker, int& count); virtual int generateAtom(const Atom *atom, const Node *relative, CodeMarker *marker); @@ -103,6 +106,7 @@ protected: void generateManifestFile(QString manifest, QString element); void readManifestMetaContent(const Config &config); + void generateKeywordAnchors(const Node* node); private: enum SubTitleSize { SmallSubTitle, LargeSubTitle }; diff --git a/src/tools/qdoc/location.cpp b/src/tools/qdoc/location.cpp index 040dd0cd88..923901fc34 100644 --- a/src/tools/qdoc/location.cpp +++ b/src/tools/qdoc/location.cpp @@ -256,7 +256,7 @@ QString Location::canonicalRelativePath(const QString &path) */ void Location::warning(const QString& message, const QString& details) const { - if (!Generator::runPrepareOnly()) + if (!Generator::preparing()) emitMessage(Warning, message, details); } @@ -267,7 +267,7 @@ void Location::warning(const QString& message, const QString& details) const */ void Location::error(const QString& message, const QString& details) const { - if (!Generator::runPrepareOnly()) + if (!Generator::preparing()) emitMessage(Error, message, details); } @@ -286,6 +286,15 @@ void Location::fatal(const QString& message, const QString& details) const } /*! + Writes \a message and \a detals to stderr as a formatted + report message. + */ +void Location::report(const QString& message, const QString& details) const +{ + emitMessage(Report, message, details); +} + +/*! Gets several parameters from the \a config, including tab size, program name, and a regular expression that appears to be used for matching certain error messages @@ -371,7 +380,8 @@ void Location::emitMessage(MessageType type, result.prepend(tr(": error: ")); else if (type == Warning) result.prepend(tr(": warning: ")); - result.prepend(toString()); + if (type != Report) + result.prepend(toString()); fprintf(stderr, "%s\n", result.toLatin1().data()); fflush(stderr); } diff --git a/src/tools/qdoc/location.h b/src/tools/qdoc/location.h index eb4dbbae98..b6589fd6ab 100644 --- a/src/tools/qdoc/location.h +++ b/src/tools/qdoc/location.h @@ -81,6 +81,8 @@ public: const QString& details = QString()) const; void fatal(const QString& message, const QString& details = QString()) const; + void report(const QString& message, + const QString& details = QString()) const; static const Location null; @@ -94,7 +96,7 @@ public: static QString canonicalRelativePath(const QString &path); private: - enum MessageType { Warning, Error }; + enum MessageType { Warning, Error, Report }; struct StackEntry { diff --git a/src/tools/qdoc/main.cpp b/src/tools/qdoc/main.cpp index 118c206f16..186fd3195b 100644 --- a/src/tools/qdoc/main.cpp +++ b/src/tools/qdoc/main.cpp @@ -63,7 +63,6 @@ QT_BEGIN_NAMESPACE - bool creationTimeBefore(const QFileInfo &fi1, const QFileInfo &fi2) { return fi1.lastModified() < fi2.lastModified(); @@ -71,6 +70,8 @@ bool creationTimeBefore(const QFileInfo &fi1, const QFileInfo &fi2) static bool highlighting = false; static bool showInternal = false; +static bool singleExec = false; +static bool writeQaPages = false; static bool redirectDocumentationToDevNull = false; static bool noLinkErrors = false; static bool autolinkErrors = false; @@ -80,14 +81,22 @@ static QStringList dependModules; static QStringList indexDirs; static QString currentDir; static QString prevCurrentDir; +static QHash<QString,QString> defaults; +#ifndef QT_NO_TRANSLATION +typedef QPair<QString, QTranslator*> Translator; +static QList<Translator> translators; +#endif - +/*! + Read some XML indexes containing definitions from other + documentation sets. \a config contains a variable that + lists directories where index files can bge found. It also + contains the \c depends variable, which lists the modules + that the current module depends on. +*/ static void loadIndexFiles(Config& config) { QDocDatabase* qdb = QDocDatabase::qdocDB(); - /* - Read some XML indexes containing definitions from other documentation sets. - */ QStringList indexFiles; QStringList configIndexes = config.getStringList(CONFIG_INDEXES); foreach (const QString &index, configIndexes) { @@ -194,35 +203,17 @@ static void loadIndexFiles(Config& config) */ static void processQdocconfFile(const QString &fileName) { -#ifndef QT_NO_TRANSLATION - QList<QTranslator *> translators; -#endif - /* The Config instance represents the configuration data for qdoc. - All the other classes are initialized with the config. Here we + All the other classes are initialized with the config. Below, we initialize the configuration with some default values. - */ - Config config(QCoreApplication::translate("QDoc", "qdoc")); - /* - The default indent for code is 4. - The default value for false is 0. - The default supported file extensions are cpp, h, qdoc and qml. - The default language is c++. - The default output format is html. - The default tab size is 8. - And those are all the default values for configuration variables. + I don't think the call to translate() does anything here. For one + thing, the translators haven't been installed at this point. And + I doubt any translator would translate QDoc anyway. But I left it + here because it does no harm. */ - static QHash<QString,QString> defaults; - if (defaults.isEmpty()) { - defaults.insert(CONFIG_CODEINDENT, QLatin1String("4")); - defaults.insert(CONFIG_FALSEHOODS, QLatin1String("0")); - defaults.insert(CONFIG_FILEEXTENSIONS, QLatin1String("*.cpp *.h *.qdoc *.qml")); - defaults.insert(CONFIG_LANGUAGE, QLatin1String("Cpp")); - defaults.insert(CONFIG_OUTPUTFORMATS, QLatin1String("HTML")); - defaults.insert(CONFIG_TABSIZE, QLatin1String("8")); - } + Config config(QCoreApplication::translate("QDoc", "qdoc")); QHash<QString,QString>::iterator iter; for (iter = defaults.begin(); iter != defaults.end(); ++iter) @@ -230,6 +221,8 @@ static void processQdocconfFile(const QString &fileName) config.setStringList(CONFIG_SYNTAXHIGHLIGHTING, QStringList(highlighting ? "true" : "false")); config.setStringList(CONFIG_SHOWINTERNAL, QStringList(showInternal ? "true" : "false")); + config.setStringList(CONFIG_SINGLEEXEC, QStringList(singleExec ? "true" : "false")); + config.setStringList(CONFIG_WRITEQAPAGES, QStringList(writeQaPages ? "true" : "false")); config.setStringList(CONFIG_REDIRECTDOCUMENTATIONTODEVNULL, QStringList(redirectDocumentationToDevNull ? "true" : "false")); config.setStringList(CONFIG_NOLINKERRORS, QStringList(noLinkErrors ? "true" : "false")); config.setStringList(CONFIG_AUTOLINKERRORS, QStringList(autolinkErrors ? "true" : "false")); @@ -247,8 +240,8 @@ static void processQdocconfFile(const QString &fileName) currentDir = QFileInfo(fileName).path(); Location::initialize(config); config.load(fileName); - QString project = config.getString(CONFIG_PROJECT).toLower(); - //qDebug() << "\nStart project:" << project; + QString project = config.getString(CONFIG_PROJECT); + //qDebug() << "Start project:" << project; /* Add the defines to the configuration variables. */ @@ -261,17 +254,24 @@ static void processQdocconfFile(const QString &fileName) if (!currentDir.isEmpty()) QDir::setCurrent(currentDir); - QString phase; - if (Generator::runPrepareOnly()) - phase = " in -prepare mode "; - else if (Generator::runGenerateOnly()) - phase = " in -generate mode "; + QString phase = " in -"; + if (Generator::singleExec()) + phase += "single exec mode, "; + else + phase += "separate exec mode, "; + if (Generator::preparing()) + phase += "prepare phase "; + else if (Generator::generating()) + phase += "generate phase "; QString msg = "Running qdoc for " + config.getString(CONFIG_PROJECT) + phase; Location::logToStdErr(msg); /* Initialize all the classes and data structures with the - qdoc configuration. + qdoc configuration. This is safe to do for each qdocconf + file processed, because all the data structures created + are either cleared after they have been used, or they + are cleared in the terminate() functions below. */ Location::initialize(config); Tokenizer::initialize(config); @@ -282,16 +282,32 @@ static void processQdocconfFile(const QString &fileName) #ifndef QT_NO_TRANSLATION /* - Load the language translators, if the configuration specifies any. + Load the language translators, if the configuration specifies any, + but only if they haven't already been loaded. This works in both + -prepare/-generate mode and -singleexec mode. */ QStringList fileNames = config.getStringList(CONFIG_TRANSLATORS); QStringList::ConstIterator fn = fileNames.constBegin(); while (fn != fileNames.constEnd()) { - QTranslator *translator = new QTranslator(0); - if (!translator->load(*fn)) - config.lastLocation().error(QCoreApplication::translate("QDoc", "Cannot load translator '%1'").arg(*fn)); - QCoreApplication::instance()->installTranslator(translator); - translators.append(translator); + bool found = false; + if (!translators.isEmpty()) { + for (int i=0; i<translators.size(); ++i) { + if (translators.at(i).first == *fn) { + found = true; + break; + } + } + } + if (!found) { + QTranslator *translator = new QTranslator(0); + if (!translator->load(*fn)) { + config.lastLocation().error(QCoreApplication::translate("QDoc", "Cannot load translator '%1'").arg(*fn)); + } + else { + QCoreApplication::instance()->installTranslator(translator); + translators.append(Translator(*fn, translator)); + } + } ++fn; } #endif @@ -311,175 +327,228 @@ static void processQdocconfFile(const QString &fileName) will be stored. The database includes a tree of nodes, which gets built as the source files are parsed. The documentation output is generated by traversing that tree. + + Note: qdocDB() allocates a new instance only if no instance exists. + So it is safe to call qdocDB() any time. */ QDocDatabase* qdb = QDocDatabase::qdocDB(); qdb->setVersion(config.getString(CONFIG_VERSION)); qdb->setShowInternal(config.getBool(CONFIG_SHOWINTERNAL)); + qdb->setSingleExec(config.getBool(CONFIG_SINGLEEXEC)); /* By default, the only output format is HTML. */ QSet<QString> outputFormats = config.getOutputFormats(); Location outputFormatsLocation = config.lastLocation(); - //if (!Generator::runPrepareOnly()) - Generator::debug(" loading index files"); - loadIndexFiles(config); - qdb->newPrimaryTree(config.getString(CONFIG_PROJECT)); - qdb->setSearchOrder(); - Generator::debug(" done loading index files"); + qdb->clearSearchOrder(); + QString p = config.getString(CONFIG_PROJECT).toLower(); + if (!Generator::singleExec()) { + Generator::debug(" loading index files"); + loadIndexFiles(config); + Generator::debug(" done loading index files"); + qdb->newPrimaryTree(p); + } + else if (Generator::preparing()) + qdb->newPrimaryTree(p); + else + qdb->setPrimaryTree(p); + + dependModules = config.getStringList(CONFIG_DEPENDS); + dependModules.removeDuplicates(); + qdb->setSearchOrder(dependModules); QSet<QString> excludedDirs; QSet<QString> excludedFiles; - QStringList headerList; - QStringList sourceList; QStringList excludedDirsList; QStringList excludedFilesList; - Generator::debug("Reading excludedirs"); - excludedDirsList = config.getCanonicalPathList(CONFIG_EXCLUDEDIRS); - foreach (const QString &excludeDir, excludedDirsList) { - QString p = QDir::fromNativeSeparators(excludeDir); - QDir tmp(p); - if (tmp.exists()) - excludedDirs.insert(p); - } + if (!Generator::singleExec() || !Generator::generating()) { + QStringList headerList; + QStringList sourceList; + + Generator::debug("Reading excludedirs"); + excludedDirsList = config.getCanonicalPathList(CONFIG_EXCLUDEDIRS); + foreach (const QString &excludeDir, excludedDirsList) { + QString p = QDir::fromNativeSeparators(excludeDir); + QDir tmp(p); + if (tmp.exists()) + excludedDirs.insert(p); + } - Generator::debug("Reading excludefiles"); - excludedFilesList = config.getCanonicalPathList(CONFIG_EXCLUDEFILES); - foreach (const QString& excludeFile, excludedFilesList) { - QString p = QDir::fromNativeSeparators(excludeFile); - excludedFiles.insert(p); - } + Generator::debug("Reading excludefiles"); + excludedFilesList = config.getCanonicalPathList(CONFIG_EXCLUDEFILES); + foreach (const QString& excludeFile, excludedFilesList) { + QString p = QDir::fromNativeSeparators(excludeFile); + excludedFiles.insert(p); + } - Generator::debug("Reading headerdirs"); - headerList = config.getAllFiles(CONFIG_HEADERS,CONFIG_HEADERDIRS,excludedDirs,excludedFiles); - QMap<QString,QString> headers; - QMultiMap<QString,QString> headerFileNames; - for (int i=0; i<headerList.size(); ++i) { - if (headerList[i].contains(QString("doc/snippets"))) - continue; - if (headers.contains(headerList[i])) - continue; - headers.insert(headerList[i],headerList[i]); - QString t = headerList[i].mid(headerList[i].lastIndexOf('/')+1); - headerFileNames.insert(t,t); - } + Generator::debug("Reading headerdirs"); + headerList = config.getAllFiles(CONFIG_HEADERS,CONFIG_HEADERDIRS,excludedDirs,excludedFiles); + QMap<QString,QString> headers; + QMultiMap<QString,QString> headerFileNames; + for (int i=0; i<headerList.size(); ++i) { + if (headerList[i].contains(QString("doc/snippets"))) + continue; + if (headers.contains(headerList[i])) + continue; + headers.insert(headerList[i],headerList[i]); + QString t = headerList[i].mid(headerList[i].lastIndexOf('/')+1); + headerFileNames.insert(t,t); + } - Generator::debug("Reading sourcedirs"); - sourceList = config.getAllFiles(CONFIG_SOURCES,CONFIG_SOURCEDIRS,excludedDirs,excludedFiles); - QMap<QString,QString> sources; - QMultiMap<QString,QString> sourceFileNames; - for (int i=0; i<sourceList.size(); ++i) { - if (sourceList[i].contains(QString("doc/snippets"))) - continue; - if (sources.contains(sourceList[i])) - continue; - sources.insert(sourceList[i],sourceList[i]); - QString t = sourceList[i].mid(sourceList[i].lastIndexOf('/')+1); - sourceFileNames.insert(t,t); - } - /* - Find all the qdoc files in the example dirs, and add - them to the source files to be parsed. - */ - Generator::debug("Reading exampledirs"); - QStringList exampleQdocList = config.getExampleQdocFiles(excludedDirs, excludedFiles); - for (int i=0; i<exampleQdocList.size(); ++i) { - if (!sources.contains(exampleQdocList[i])) { - sources.insert(exampleQdocList[i],exampleQdocList[i]); - QString t = exampleQdocList[i].mid(exampleQdocList[i].lastIndexOf('/')+1); + Generator::debug("Reading sourcedirs"); + sourceList = config.getAllFiles(CONFIG_SOURCES,CONFIG_SOURCEDIRS,excludedDirs,excludedFiles); + QMap<QString,QString> sources; + QMultiMap<QString,QString> sourceFileNames; + for (int i=0; i<sourceList.size(); ++i) { + if (sourceList[i].contains(QString("doc/snippets"))) + continue; + if (sources.contains(sourceList[i])) + continue; + sources.insert(sourceList[i],sourceList[i]); + QString t = sourceList[i].mid(sourceList[i].lastIndexOf('/')+1); sourceFileNames.insert(t,t); } - } + /* + Find all the qdoc files in the example dirs, and add + them to the source files to be parsed. + */ + Generator::debug("Reading exampledirs"); + QStringList exampleQdocList = config.getExampleQdocFiles(excludedDirs, excludedFiles); + for (int i=0; i<exampleQdocList.size(); ++i) { + if (!sources.contains(exampleQdocList[i])) { + sources.insert(exampleQdocList[i],exampleQdocList[i]); + QString t = exampleQdocList[i].mid(exampleQdocList[i].lastIndexOf('/')+1); + sourceFileNames.insert(t,t); + } + } - Generator::debug("Adding doc/image dirs found in exampledirs to imagedirs"); - QSet<QString> exampleImageDirs; - QStringList exampleImageList = config.getExampleImageFiles(excludedDirs, excludedFiles); - for (int i=0; i<exampleImageList.size(); ++i) { - if (exampleImageList[i].contains("doc/images")) { - QString t = exampleImageList[i].left(exampleImageList[i].lastIndexOf("doc/images")+10); - if (!exampleImageDirs.contains(t)) { - exampleImageDirs.insert(t); + Generator::debug("Adding doc/image dirs found in exampledirs to imagedirs"); + QSet<QString> exampleImageDirs; + QStringList exampleImageList = config.getExampleImageFiles(excludedDirs, excludedFiles); + for (int i=0; i<exampleImageList.size(); ++i) { + if (exampleImageList[i].contains("doc/images")) { + QString t = exampleImageList[i].left(exampleImageList[i].lastIndexOf("doc/images")+10); + if (!exampleImageDirs.contains(t)) { + exampleImageDirs.insert(t); + } } } - } - Generator::augmentImageDirs(exampleImageDirs); + Generator::augmentImageDirs(exampleImageDirs); + + /* + Parse each header file in the set using the appropriate parser and add it + to the big tree. + */ + QSet<CodeParser *> usedParsers; + + Generator::debug("Parsing header files"); + int parsed = 0; + QMap<QString,QString>::ConstIterator h = headers.constBegin(); + while (h != headers.constEnd()) { + CodeParser *codeParser = CodeParser::parserForHeaderFile(h.key()); + if (codeParser) { + ++parsed; + Generator::debug(QString("Parsing " + h.key())); + codeParser->parseHeaderFile(config.location(), h.key()); + usedParsers.insert(codeParser); + } + ++h; + } - /* - Parse each header file in the set using the appropriate parser and add it - to the big tree. - */ - QSet<CodeParser *> usedParsers; - - Generator::debug("Parsing header files"); - int parsed = 0; - QMap<QString,QString>::ConstIterator h = headers.constBegin(); - while (h != headers.constEnd()) { - CodeParser *codeParser = CodeParser::parserForHeaderFile(h.key()); - if (codeParser) { - ++parsed; - Generator::debug(QString("Parsing " + h.key())); - codeParser->parseHeaderFile(config.location(), h.key()); - usedParsers.insert(codeParser); + foreach (CodeParser *codeParser, usedParsers) + codeParser->doneParsingHeaderFiles(); + + usedParsers.clear(); + qdb->resolveInheritance(); + + /* + Parse each source text file in the set using the appropriate parser and + add it to the big tree. + */ + parsed = 0; + Generator::debug("Parsing source files"); + QMap<QString,QString>::ConstIterator s = sources.constBegin(); + while (s != sources.constEnd()) { + CodeParser *codeParser = CodeParser::parserForSourceFile(s.key()); + if (codeParser) { + ++parsed; + Generator::debug(QString("Parsing " + s.key())); + codeParser->parseSourceFile(config.location(), s.key()); + usedParsers.insert(codeParser); + } + ++s; } - ++h; + Generator::debug(QString("Parsing done.")); + + foreach (CodeParser *codeParser, usedParsers) + codeParser->doneParsingSourceFiles(); + + /* + Now the primary tree has been built from all the header and + source files. Resolve all the class names, function names, + targets, URLs, links, and other stuff that needs resolving. + */ + Generator::debug("Resolving stuff prior to generating docs"); + qdb->resolveIssues(); } + else { + Generator::debug("Reading excludedirs"); + excludedDirsList = config.getCanonicalPathList(CONFIG_EXCLUDEDIRS); + foreach (const QString &excludeDir, excludedDirsList) { + QString p = QDir::fromNativeSeparators(excludeDir); + QDir tmp(p); + if (tmp.exists()) + excludedDirs.insert(p); + } - foreach (CodeParser *codeParser, usedParsers) - codeParser->doneParsingHeaderFiles(); - - usedParsers.clear(); - qdb->resolveInheritance(); + Generator::debug("Reading excludefiles"); + excludedFilesList = config.getCanonicalPathList(CONFIG_EXCLUDEFILES); + foreach (const QString& excludeFile, excludedFilesList) { + QString p = QDir::fromNativeSeparators(excludeFile); + excludedFiles.insert(p); + } - /* - Parse each source text file in the set using the appropriate parser and - add it to the big tree. - */ - parsed = 0; - Generator::debug("Parsing source files"); - QMap<QString,QString>::ConstIterator s = sources.constBegin(); - while (s != sources.constEnd()) { - CodeParser *codeParser = CodeParser::parserForSourceFile(s.key()); - if (codeParser) { - ++parsed; - Generator::debug(QString("Parsing " + s.key())); - codeParser->parseSourceFile(config.location(), s.key()); - usedParsers.insert(codeParser); + Generator::debug("Adding doc/image dirs found in exampledirs to imagedirs"); + QSet<QString> exampleImageDirs; + QStringList exampleImageList = config.getExampleImageFiles(excludedDirs, excludedFiles); + for (int i=0; i<exampleImageList.size(); ++i) { + if (exampleImageList[i].contains("doc/images")) { + QString t = exampleImageList[i].left(exampleImageList[i].lastIndexOf("doc/images")+10); + if (!exampleImageDirs.contains(t)) { + exampleImageDirs.insert(t); + } + } } - ++s; + Generator::augmentImageDirs(exampleImageDirs); + qdb->resolveStuff(); } - Generator::debug(QString("Parsing done.")); - - foreach (CodeParser *codeParser, usedParsers) - codeParser->doneParsingSourceFiles(); /* - Now the big tree has been built from all the header and - source files. Resolve all the class names, function names, - targets, URLs, links, and other stuff that needs resolving. - */ - Generator::debug("Resolving stuff prior to generating docs"); - qdb->resolveIssues(); - - /* - The tree is built and all the stuff that needed resolving - has been resolved. Now traverse the tree and generate the - documentation output. More than one output format can be - requested. The tree is traversed for each one. + The primary tree is built and all the stuff that needed + resolving has been resolved. Now traverse the tree and + generate the documentation output. More than one output + format can be requested. The tree is traversed for each + one. */ Generator::debug("Generating docs"); QSet<QString>::ConstIterator of = outputFormats.constBegin(); while (of != outputFormats.constEnd()) { Generator* generator = Generator::generatorForFormat(*of); if (generator == 0) - outputFormatsLocation.fatal(QCoreApplication::translate("QDoc", "Unknown output format '%1'").arg(*of)); + outputFormatsLocation.fatal(QCoreApplication::translate("QDoc", + "Unknown output format '%1'").arg(*of)); generator->generateDocs(); ++of; } +#if 0 + if (Generator::generating() && Generator::writeQaPages()) + qdb->printLinkCounts(project); +#endif + qdb->clearLinkCounts(); - - //Generator::writeOutFileNames(); - Generator::debug("Shutting down qdoc"); + Generator::debug("Terminating qdoc classes"); if (Generator::debugging()) Generator::stopDebugging(project); @@ -492,17 +561,7 @@ static void processQdocconfFile(const QString &fileName) Location::terminate(); QDir::setCurrent(prevCurrentDir); -#ifndef QT_NO_TRANSLATION - qDeleteAll(translators); -#endif -#ifdef DEBUG_SHUTDOWN_CRASH - qDebug() << "main(): Delete qdoc database"; -#endif - QDocDatabase::destroyQdocDB(); -#ifdef DEBUG_SHUTDOWN_CRASH - qDebug() << "main(): qdoc database deleted"; -#endif - Generator::debug("qdoc finished!"); + Generator::debug("qdoc classes terminated"); } extern Q_CORE_EXPORT QBasicAtomicInt qt_qhash_seed; @@ -621,12 +680,22 @@ int main(int argc, char **argv) logProgressOption.setDescription(QCoreApplication::translate("qdoc", "Log progress on stderr.")); parser.addOption(logProgressOption); + QCommandLineOption singleExecOption(QStringList() << QStringLiteral("single-exec")); + singleExecOption.setDescription(QCoreApplication::translate("qdoc", "Run qdoc once over all the qdoc conf files.")); + parser.addOption(singleExecOption); + + QCommandLineOption writeQaPagesOption(QStringList() << QStringLiteral("write-qa-pages")); + writeQaPagesOption.setDescription(QCoreApplication::translate("qdoc", "Write QA pages.")); + parser.addOption(writeQaPagesOption); + parser.process(app); defines += parser.values(defineOption); dependModules += parser.values(dependsOption); highlighting = parser.isSet(highlightingOption); showInternal = parser.isSet(showInternalOption); + singleExec = parser.isSet(singleExecOption); + writeQaPages = parser.isSet(writeQaPagesOption); redirectDocumentationToDevNull = parser.isSet(redirectDocumentationToDevNullOption); Config::generateExamples = !parser.isSet(noExamplesOption); foreach (const QString &indexDir, parser.values(indexDirOption)) { @@ -650,21 +719,73 @@ int main(int argc, char **argv) Generator::setQDocPass(Generator::Prepare); if (parser.isSet(generateOption)) Generator::setQDocPass(Generator::Generate); + if (parser.isSet(singleExecOption)) + Generator::setSingleExec(); + if (parser.isSet(writeQaPagesOption)) + Generator::setWriteQaPages(); if (parser.isSet(logProgressOption)) Location::startLoggingProgress(); - const QStringList qdocFiles = parser.positionalArguments(); + /* + The default indent for code is 4. + The default value for false is 0. + The default supported file extensions are cpp, h, qdoc and qml. + The default language is c++. + The default output format is html. + The default tab size is 8. + And those are all the default values for configuration variables. + */ + if (defaults.isEmpty()) { + defaults.insert(CONFIG_CODEINDENT, QLatin1String("4")); + defaults.insert(CONFIG_FALSEHOODS, QLatin1String("0")); + defaults.insert(CONFIG_FILEEXTENSIONS, QLatin1String("*.cpp *.h *.qdoc *.qml")); + defaults.insert(CONFIG_LANGUAGE, QLatin1String("Cpp")); + defaults.insert(CONFIG_OUTPUTFORMATS, QLatin1String("HTML")); + defaults.insert(CONFIG_TABSIZE, QLatin1String("8")); + } + + QStringList qdocFiles = parser.positionalArguments(); if (qdocFiles.isEmpty()) parser.showHelp(); + if (singleExec) + qdocFiles = Config::loadMaster(qdocFiles.at(0)); + /* - Main loop. + Main loop is now modified to handle single exec mode. */ + if (Generator::singleExec()) + Generator::setQDocPass(Generator::Prepare); foreach (const QString &qf, qdocFiles) { - //qDebug() << "PROCESSING:" << qf; + dependModules.clear(); processQdocconfFile(qf); } + if (Generator::singleExec()) { + Generator::setQDocPass(Generator::Generate); + QDocDatabase* qdb = QDocDatabase::qdocDB(); + qdb->processForest(); + foreach (const QString &qf, qdocFiles) { + dependModules.clear(); + processQdocconfFile(qf); + } + } + +#ifndef QT_NO_TRANSLATION + if (!translators.isEmpty()) { + for (int i=0; i<translators.size(); ++i) { + delete translators.at(i).second; + } + } + translators.clear(); +#endif + +#ifdef DEBUG_SHUTDOWN_CRASH + qDebug() << "main(): Delete qdoc database"; +#endif + QDocDatabase::destroyQdocDB(); +#ifdef DEBUG_SHUTDOWN_CRASH + qDebug() << "main(): qdoc database deleted"; +#endif return EXIT_SUCCESS; } - diff --git a/src/tools/qdoc/node.cpp b/src/tools/qdoc/node.cpp index ce655efef6..f012aae87a 100644 --- a/src/tools/qdoc/node.cpp +++ b/src/tools/qdoc/node.cpp @@ -218,7 +218,7 @@ Node::Node(Type type, InnerNode *parent, const QString& name) { if (parent_) parent_->addChild(this); - outSubDir_ = CodeParser::currentOutputSubdirectory(); + outSubDir_ = Generator::outputSubdir(); if (operators_.isEmpty()) { operators_.insert("++","inc"); operators_.insert("--","dec"); diff --git a/src/tools/qdoc/qdocdatabase.cpp b/src/tools/qdoc/qdocdatabase.cpp index 36dd05bb92..51908bc9e3 100644 --- a/src/tools/qdoc/qdocdatabase.cpp +++ b/src/tools/qdoc/qdocdatabase.cpp @@ -128,142 +128,54 @@ Tree* QDocForest::nextTree() */ /*! + Finds the tree for module \a t in the forest and + sets the primary tree to be that tree. After the + primary tree is set, that tree is removed from the + forest. + + \node It gets re-inserted into the forest after the + search order is built. + */ +void QDocForest::setPrimaryTree(const QString& t) +{ + primaryTree_ = findTree(t); + forest_.remove(t); + if (!primaryTree_) + qDebug() << "ERROR: Could not set primary tree to:" << t; +} + +/*! If the search order array is empty, create the search order. If the search order array is not empty, do nothing. */ -void QDocForest::setSearchOrder() +void QDocForest::setSearchOrder(QStringList& t) { if (!searchOrder_.isEmpty()) return; - QString primaryName = primaryTree()->moduleName(); - searchOrder_.clear(); + + /* Allocate space for the search order. */ searchOrder_.reserve(forest_.size()+1); + searchOrder_.clear(); moduleNames_.reserve(forest_.size()+1); + moduleNames_.clear(); + + /* The primary tree is always first in the search order. */ + QString primaryName = primaryTree()->moduleName(); searchOrder_.append(primaryTree_); moduleNames_.append(primaryName); + forest_.remove(primaryName); + QMap<QString, Tree*>::iterator i; - if (primaryName != "QtCore") { - i = forest_.find("QtCore"); - if (i != forest_.end()) { - searchOrder_.append(i.value()); - moduleNames_.append("QtCore"); - forest_.erase(i); - } - } - if (primaryName != "QtGui") { - i = forest_.find("QtGui"); - if (i != forest_.end()) { - searchOrder_.append(i.value()); - moduleNames_.append("QtGui"); - forest_.erase(i); - } - } - if (primaryName != "QtNetwork") { - i = forest_.find("QtNetwork"); - if (i != forest_.end()) { - searchOrder_.append(i.value()); - moduleNames_.append("QtNetwork"); - forest_.erase(i); - } - } - if (primaryName != "QtOpenGL") { - i = forest_.find("QtOpenGL"); - if (i != forest_.end()) { - searchOrder_.append(i.value()); - moduleNames_.append("QtOpenGL"); - forest_.erase(i); - } - } - if (primaryName != "QtWidgets") { - i = forest_.find("QtWidgets"); - if (i != forest_.end()) { - searchOrder_.append(i.value()); - moduleNames_.append("QtWidgets"); - forest_.erase(i); - } - } - if (primaryName != "QtSql") { - i = forest_.find("QtSql"); - if (i != forest_.end()) { - searchOrder_.append(i.value()); - moduleNames_.append("QtSql"); - forest_.erase(i); - } - } - if (primaryName != "QtXml") { - i = forest_.find("QtXml"); - if (i != forest_.end()) { - searchOrder_.append(i.value()); - moduleNames_.append("QtXml"); - forest_.erase(i); - } - } - if (primaryName != "QtSvg") { - i = forest_.find("QtSvg"); - if (i != forest_.end()) { - searchOrder_.append(i.value()); - moduleNames_.append("QtSvg"); - forest_.erase(i); - } - } - if (primaryName != "QtDoc") { - i = forest_.find("QtDoc"); - if (i != forest_.end()) { - searchOrder_.append(i.value()); - moduleNames_.append("QtDoc"); - forest_.erase(i); - } - } - if (primaryName != "QtQuick") { - i = forest_.find("QtQuick"); - if (i != forest_.end()) { - searchOrder_.append(i.value()); - moduleNames_.append("QtQuick"); - forest_.erase(i); - } - } - if (primaryName != "QtQml") { - i = forest_.find("QtQml"); - if (i != forest_.end()) { - searchOrder_.append(i.value()); - moduleNames_.append("QtQml"); - forest_.erase(i); - } - } - if (primaryName != "QtPrintSupport") { - i = forest_.find("QtPrintSupport"); - if (i != forest_.end()) { - searchOrder_.append(i.value()); - moduleNames_.append("QtPrintSupport"); - forest_.erase(i); - } - } - if (primaryName != "QtGraphicalEffects") { - i = forest_.find("QtGraphicalEffects"); - if (i != forest_.end()) { - searchOrder_.append(i.value()); - moduleNames_.append("QtGraphicalEffects"); - forest_.erase(i); - } - } - if (primaryName != "QtConcurrent") { - i = forest_.find("QtConcurrent"); - if (i != forest_.end()) { - searchOrder_.append(i.value()); - moduleNames_.append("QtConcurrent"); - forest_.erase(i); - } - } -#if 0 - if (primaryName != "zzz") { - i = forest_.find("zzz"); - if (i != forest_.end()) { - searchOrder_.append(i.value()); - moduleNames_.append("zzz"); - forest_.erase(i); + foreach (QString m, t) { + if (primaryName != m) { + i = forest_.find(m); + if (i != forest_.end()) { + searchOrder_.append(i.value()); + moduleNames_.append(m); + forest_.remove(m); + } } } -#endif /* If any trees remain in the forest, just add them to the search order sequentially, because we don't @@ -283,15 +195,21 @@ void QDocForest::setSearchOrder() Rebuild the forest after constructing the search order. It was destroyed during construction of the search order, but it is needed for module-specific searches. + + Note that this loop also inserts the primary tree into the + forrest. That is a requirement. */ for (int i=0; i<searchOrder_.size(); ++i) { - forest_.insert(moduleNames_.at(i).toLower(), searchOrder_.at(i)); + if (!forest_.contains(moduleNames_.at(i))) { + forest_.insert(moduleNames_.at(i), searchOrder_.at(i)); + } } - #if 0 - qDebug() << " SEARCH ORDER:"; + qDebug() << " SEARCH ORDER:"; for (int i=0; i<moduleNames_.size(); ++i) - qDebug() << " " << i+1 << "." << moduleNames_.at(i); + qDebug() << " " << i+1 << "." << moduleNames_.at(i); + qDebug() << " FOREST:" << forest_.keys(); + qDebug() << "SEARCH ORDER:" << moduleNames_; #endif } @@ -363,7 +281,7 @@ NamespaceNode* QDocForest::newIndexTree(const QString& module) /*! Create a new Tree for use as the primary tree. This tree - will represent the primary module. + will represent the primary module. \a module is camel case. */ void QDocForest::newPrimaryTree(const QString& module) { @@ -431,6 +349,61 @@ void QDocForest::mergeCollectionMaps(Node::Type nt, CNMultiMap& cnmm) } } +/*! + Print the list of module names ordered according + to how many successful searches each tree had. + */ +void QDocForest::printLinkCounts(const QString& project) +{ + Location::null.report(QString("%1: Link Counts").arg(project)); + QMultiMap<int, QString> m; + foreach (Tree* t, searchOrder()) { + if (t->linkCount() < 0) + m.insert(t->linkCount(), t->moduleName()); + } + QString depends = "depends +="; + QString module = project.toLower(); + QMultiMap<int, QString>::iterator i = m.begin(); + while (i != m.end()) { + QString line = " " + i.value(); + if (i.value() != module) + depends += " " + i.value(); + int pad = 30 - line.length(); + for (int k=0; k<pad; ++k) + line += " "; + line += "%1"; + Location::null.report(line.arg(-(i.key()))); + ++i; + } + Location::null.report("Optimal depends variable:"); + Location::null.report(depends); +} + +/*! + Print the list of module names ordered according + to how many successful searches each tree had. + */ +QString QDocForest::getLinkCounts(QStringList& strings, QVector<int>& counts) +{ + QMultiMap<int, QString> m; + foreach (Tree* t, searchOrder()) { + if (t->linkCount() < 0) + m.insert(t->linkCount(), t->moduleName()); + } + QString depends = "depends +="; + QString module = Generator::defaultModuleName().toLower(); + QMultiMap<int, QString>::iterator i = m.begin(); + while (i != m.end()) { + if (i.value() != module) { + counts.append(-(i.key())); + strings.append(i.value()); + depends += " " + i.value(); + } + ++i; + } + return depends; +} + /*! \class QDocDatabase This class provides exclusive access to the qdoc database, which consists of a forrest of trees and a lot of maps and @@ -445,8 +418,19 @@ NodeMap QDocDatabase::typeNodeMap_; constructs the \a forest_ object, which is also a singleton. \a showInternal_ is normally false. If it is true, qdoc will write documentation for nodes marked \c internal. + + \a singleExec_ is false when qdoc is being used in the standard + way of running qdoc twices for each module, first with -prepare + and then with -generate. First the -prepare phase is run for + each module, then the -generate phase is run for each module. + + When \a singleExec_ is true, qdoc is run only once. During the + single execution, qdoc processes the qdocconf files for all the + modules sequentially in a loop. Each source file for each module + is read exactly once. */ -QDocDatabase::QDocDatabase() : showInternal_(false), forest_(this) +QDocDatabase::QDocDatabase() + : showInternal_(false), singleExec_(false), forest_(this) { // nothing } @@ -809,13 +793,41 @@ QmlClassNode* QDocDatabase::findQmlType(const ImportRec& import, const QString& } /*! - This function calls \a func for each tree in the forest. + This function calls a set of functions for each tree in the + forest that has not already been analyzed. In this way, when + running qdoc in \e singleExec mode, each tree is analyzed in + turn, and its classes and types are added to the appropriate + node maps. + */ +void QDocDatabase::processForest() +{ + Tree* t = forest_.firstTree(); + while (t) { + findAllNamespaces(t->root()); + findAllClasses(t->root()); + findAllFunctions(t->root()); + findAllObsoleteThings(t->root()); + findAllLegaleseTexts(t->root()); + findAllSince(t->root()); + t->setTreeHasBeenAnalyzed(); + t = forest_.nextTree(); + } +} + +/*! + This function calls \a func for each tree in the forest, + but only if Tree::treeHasBeenAnalyzed() returns false for + the tree. In this way, when running qdoc in \e singleExec + mode, each tree is analyzed in turn, and its classes and + types are added to the appropriate node maps. */ void QDocDatabase::processForest(void (QDocDatabase::*func) (InnerNode*)) { Tree* t = forest_.firstTree(); while (t) { - (this->*(func))(t->root()); + if (!t->treeHasBeenAnalyzed()) { + (this->*(func))(t->root()); + } t = forest_.nextTree(); } } @@ -887,7 +899,7 @@ NodeMap& QDocDatabase::getNamespaces() */ NodeMap& QDocDatabase::getServiceClasses() { - if (nonCompatClasses_.isEmpty() && qmlClasses_.isEmpty()) + if (cppClasses_.isEmpty() && qmlTypes_.isEmpty()) processForest(&QDocDatabase::findAllClasses); return serviceClasses_; } @@ -899,7 +911,7 @@ NodeMap& QDocDatabase::getServiceClasses() */ NodeMap& QDocDatabase::getQmlBasicTypes() { - if (nonCompatClasses_.isEmpty() && qmlBasicTypes_.isEmpty()) + if (cppClasses_.isEmpty() && qmlBasicTypes_.isEmpty()) processForest(&QDocDatabase::findAllClasses); return qmlBasicTypes_; } @@ -911,9 +923,9 @@ NodeMap& QDocDatabase::getQmlBasicTypes() */ NodeMap& QDocDatabase::getQmlTypes() { - if (nonCompatClasses_.isEmpty() && qmlClasses_.isEmpty()) + if (cppClasses_.isEmpty() && qmlTypes_.isEmpty()) processForest(&QDocDatabase::findAllClasses); - return qmlClasses_; + return qmlTypes_; } /*! @@ -935,7 +947,7 @@ NodeMap& QDocDatabase::getObsoleteClasses() */ NodeMap& QDocDatabase::getCompatibilityClasses() { - if (nonCompatClasses_.isEmpty() && qmlClasses_.isEmpty()) + if (cppClasses_.isEmpty() && qmlTypes_.isEmpty()) processForest(&QDocDatabase::findAllClasses); return compatClasses_; } @@ -950,7 +962,7 @@ NodeMap& QDocDatabase::getCompatibilityClasses() */ NodeMap& QDocDatabase::getMainClasses() { - if (nonCompatClasses_.isEmpty() && qmlClasses_.isEmpty()) + if (cppClasses_.isEmpty() && qmlTypes_.isEmpty()) processForest(&QDocDatabase::findAllClasses); return mainClasses_; } @@ -962,9 +974,9 @@ NodeMap& QDocDatabase::getMainClasses() */ NodeMap& QDocDatabase::getCppClasses() { - if (nonCompatClasses_.isEmpty() && qmlClasses_.isEmpty()) + if (cppClasses_.isEmpty() && qmlTypes_.isEmpty()) processForest(&QDocDatabase::findAllClasses); - return nonCompatClasses_; + return cppClasses_; } /*! @@ -987,7 +999,7 @@ void QDocDatabase::findAllClasses(InnerNode* node) compatClasses_.insert(className, *c); } else { - nonCompatClasses_.insert(className, *c); + cppClasses_.insert(className, *c); if ((*c)->status() == Node::Main) mainClasses_.insert(className, *c); } @@ -1000,9 +1012,9 @@ void QDocDatabase::findAllClasses(InnerNode* node) else if (((*c)->isQmlType() || (*c)->isQmlBasicType())&& !(*c)->doc().isEmpty()) { QString qmlTypeName = (*c)->name(); if (qmlTypeName.startsWith(QLatin1String("QML:"))) - qmlClasses_.insert(qmlTypeName.mid(4),*c); + qmlTypes_.insert(qmlTypeName.mid(4),*c); else - qmlClasses_.insert(qmlTypeName,*c); + qmlTypes_.insert(qmlTypeName,*c); //also add to the QML basic type map if ((*c)->isQmlBasicType()) @@ -1022,7 +1034,6 @@ void QDocDatabase::findAllClasses(InnerNode* node) */ NodeMapMap& QDocDatabase::getFunctionIndex() { - funcIndex_.clear(); processForest(&QDocDatabase::findAllFunctions); return funcIndex_; } @@ -1314,7 +1325,15 @@ const NodeMultiMap& QDocDatabase::getSinceMap(const QString& key) */ void QDocDatabase::resolveIssues() { resolveQmlInheritance(primaryTreeRoot()); - resolveTargets(); + primaryTree()->resolveTargets(primaryTreeRoot()); + primaryTree()->resolveCppToQmlLinks(); +} + +void QDocDatabase::resolveStuff() +{ + primaryTree()->resolveInheritance(); + resolveQmlInheritance(primaryTreeRoot()); + //primaryTree()->resolveTargets(primaryTreeRoot()); primaryTree()->resolveCppToQmlLinks(); } @@ -1424,10 +1443,18 @@ void QDocDatabase::generateTagFile(const QString& name, Generator* g) } /*! - Reads and parses the qdoc index files listed in \a indexFiles. + Reads and parses the qdoc index files listed in \a t. */ -void QDocDatabase::readIndexes(const QStringList& indexFiles) +void QDocDatabase::readIndexes(const QStringList& t) { + QStringList indexFiles; + foreach (const QString& f, t) { + QString fn = f.mid(f.lastIndexOf(QChar('/'))+1); + if (!isLoaded(fn)) + indexFiles << f; + else + qDebug() << "This index file is already in memory:" << f; + } QDocIndexFiles::qdocIndexFiles()->readIndexes(indexFiles); QDocIndexFiles::destroyQDocIndexFiles(); } @@ -1443,6 +1470,8 @@ void QDocDatabase::generateIndex(const QString& fileName, Generator* g, bool generateInternalNodes) { + QString t = fileName.mid(fileName.lastIndexOf(QChar('/'))+1); + primaryTree()->setIndexFileName(t); QDocIndexFiles::qdocIndexFiles()->generateIndex(fileName, url, title, g, generateInternalNodes); QDocIndexFiles::destroyQDocIndexFiles(); } diff --git a/src/tools/qdoc/qdocdatabase.h b/src/tools/qdoc/qdocdatabase.h index 8b67aca971..9b4d7019ad 100644 --- a/src/tools/qdoc/qdocdatabase.h +++ b/src/tools/qdoc/qdocdatabase.h @@ -78,7 +78,14 @@ class QDocForest bool done() { return (currentIndex_ >= searchOrder().size()); } const QVector<Tree*>& searchOrder(); const QVector<Tree*>& indexSearchOrder(); - void setSearchOrder(); + void setSearchOrder(QStringList& t); + bool isLoaded(const QString& fn) { + foreach (Tree* t, searchOrder()) { + if (fn == t->indexFileName()) + return true; + } + return false; + } const Node* findNode(const QStringList& path, const Node* relative, @@ -186,8 +193,18 @@ class QDocForest } } + void clearSearchOrder() { searchOrder_.clear(); } + void clearLinkCounts() + { + foreach (Tree* t, searchOrder()) + t->clearLinkCount(); + } + void printLinkCounts(const QString& project); + QString getLinkCounts(QStringList& strings, QVector<int>& counts); + private: void newPrimaryTree(const QString& module); + void setPrimaryTree(const QString& t); NamespaceNode* newIndexTree(const QString& module); private: @@ -274,12 +291,10 @@ class QDocDatabase void resolveInheritance() { primaryTree()->resolveInheritance(); } void resolveQmlInheritance(InnerNode* root); void resolveIssues(); + void resolveStuff(); void fixInheritance() { primaryTree()->fixInheritance(); } void resolveProperties() { primaryTree()->resolveProperties(); } - void resolveTargets() { - primaryTree()->resolveTargets(primaryTreeRoot()); - } void insertTarget(const QString& name, const QString& title, TargetRec::Type type, @@ -355,18 +370,37 @@ class QDocDatabase void clearOpenNamespaces() { openNamespaces_.clear(); } void insertOpenNamespace(const QString& path) { openNamespaces_.insert(path); } void setShowInternal(bool value) { showInternal_ = value; } + void setSingleExec(bool value) { singleExec_ = value; } + void processForest(); // Try to make this function private. QDocForest& forest() { return forest_; } NamespaceNode* primaryTreeRoot() { return forest_.primaryTreeRoot(); } void newPrimaryTree(const QString& module) { forest_.newPrimaryTree(module); } + void setPrimaryTree(const QString& t) { forest_.setPrimaryTree(t); } NamespaceNode* newIndexTree(const QString& module) { return forest_.newIndexTree(module); } const QVector<Tree*>& searchOrder() { return forest_.searchOrder(); } void setLocalSearch() { forest_.searchOrder_ = QVector<Tree*>(1, primaryTree()); } void setSearchOrder(const QVector<Tree*>& searchOrder) { forest_.searchOrder_ = searchOrder; } - void setSearchOrder() { forest_.setSearchOrder(); } + void setSearchOrder(QStringList& t) { forest_.setSearchOrder(t); } void mergeCollections(Node::Type nt, CNMap& cnm, const Node* relative); void mergeCollections(CollectionNode* cn); + void clearSearchOrder() { forest_.clearSearchOrder(); } + void incrementLinkCount(const Node* t) { t->tree()->incrementLinkCount(); } + void clearLinkCounts() { forest_.clearLinkCounts(); } + void printLinkCounts(const QString& t) { forest_.printLinkCounts(t); } + QString getLinkCounts(QStringList& strings, QVector<int>& counts) { + return forest_.getLinkCounts(strings, counts); + } + QString getNewLinkTarget(const Node* locNode, + const Node* t, + const QString& fileName, + QString& text, + bool broken = false) { + return primaryTree()->getNewLinkTarget(locNode, t, fileName, text, broken); + } + TargetList* getTargetList(const QString& t) { return primaryTree()->getTargetList(t); } + QStringList getTargetListKeys() { return primaryTree()->getTargetListKeys(); } private: friend class QDocIndexFiles; @@ -379,6 +413,7 @@ class QDocDatabase return forest_.findNode(path, relative, findFlags, genus); } void processForest(void (QDocDatabase::*) (InnerNode*)); + bool isLoaded(const QString& t) { return forest_.isLoaded(t); } static void initializeDB(); private: @@ -394,20 +429,21 @@ class QDocDatabase static QDocDatabase* qdocDB_; static NodeMap typeNodeMap_; bool showInternal_; + bool singleExec_; QString version_; QDocForest forest_; - NodeMap nonCompatClasses_; - NodeMap mainClasses_; + NodeMap cppClasses_; + NodeMap mainClasses_; // MWS: not needed, should be delete NodeMap compatClasses_; NodeMap obsoleteClasses_; NodeMap classesWithObsoleteMembers_; NodeMap obsoleteQmlTypes_; NodeMap qmlTypesWithObsoleteMembers_; NodeMap namespaceIndex_; - NodeMap serviceClasses_; + NodeMap serviceClasses_; // MWS: not needed, should be deleted NodeMap qmlBasicTypes_; - NodeMap qmlClasses_; + NodeMap qmlTypes_; NodeMapMap newClassMaps_; NodeMapMap newQmlTypeMaps_; NodeMultiMapMap newSinceMaps_; diff --git a/src/tools/qdoc/qdocindexfiles.cpp b/src/tools/qdoc/qdocindexfiles.cpp index 00041b2b65..e152b04ead 100644 --- a/src/tools/qdoc/qdocindexfiles.cpp +++ b/src/tools/qdoc/qdocindexfiles.cpp @@ -564,7 +564,6 @@ void QDocIndexFiles::readIndexSection(const QDomElement& element, node->setReconstitutedBrief(briefAttr); } - // zzz bool useParent = (element.nodeName() == "namespace" && name.isEmpty()); if (element.hasChildNodes()) { QDomElement child = element.firstChildElement(); @@ -806,11 +805,14 @@ bool QDocIndexFiles::generateIndexSection(QXmlStreamWriter& writer, QString fullName = node->fullDocumentName(); if (fullName != objName) writer.writeAttribute("fullname", fullName); +#if 0 if (Generator::useOutputSubdirs()) href = node->outputSubdirectory(); if (!href.isEmpty()) href.append(QLatin1Char('/')); href.append(gen_->fullDocumentLocation(node)); +#endif + href = gen_->fullDocumentLocation(node); } else href = node->name(); diff --git a/src/tools/qdoc/tokenizer.cpp b/src/tools/qdoc/tokenizer.cpp index 7c9e9f338a..dca1e9bb55 100644 --- a/src/tools/qdoc/tokenizer.cpp +++ b/src/tools/qdoc/tokenizer.cpp @@ -511,6 +511,9 @@ void Tokenizer::initialize(const Config &config) defines = new QRegExp(d.join('|')); falsehoods = new QRegExp(config.getStringList(CONFIG_FALSEHOODS).join('|')); + /* + The keyword hash table is always cleared before any words are inserted. + */ memset(kwordHashTable, 0, sizeof(kwordHashTable)); for (int i = 0; i < Tok_LastKeyword - Tok_FirstKeyword + 1; i++) insertKwordIntoHash(kwords[i], i + 1); @@ -533,6 +536,11 @@ void Tokenizer::initialize(const Config &config) } } +/*! + The heap allocated variables are freed here. The keyword + hash table is not cleared here, but it is cleared in the + initialize() function, before any keywords are inserted. + */ void Tokenizer::terminate() { delete comment; diff --git a/src/tools/qdoc/tree.cpp b/src/tools/qdoc/tree.cpp index 2e327a5ac8..a4b8d8cd8a 100644 --- a/src/tools/qdoc/tree.cpp +++ b/src/tools/qdoc/tree.cpp @@ -66,10 +66,19 @@ QT_BEGIN_NAMESPACE be necessary, and it might be removed later. */ Tree::Tree(const QString& module, QDocDatabase* qdb) - : module_(module), qdb_(qdb), root_(0, QString()) + : treeHasBeenAnalyzed_(false), + docsHaveBeenGenerated_(false), + linkCount_(0), + module_(module), + qdb_(qdb), + root_(0, QString()), + targetListMap_(0) { root_.setModuleName(module_); root_.setTree(this); + if (Generator::writeQaPages()) { + targetListMap_ = new TargetListMap; + } } /*! @@ -95,6 +104,18 @@ Tree::~Tree() } nodesByTargetRef_.clear(); nodesByTargetTitle_.clear(); + if (Generator::writeQaPages() && targetListMap_) { + TargetListMap::iterator i = targetListMap_->begin(); + while (i != targetListMap_->end()) { + TargetList* tlist = i.value(); + if (tlist) { + foreach (TargetLoc* tloc, *tlist) + delete tloc; + } + delete tlist; + ++i; + } + } } /* API members */ @@ -1114,7 +1135,7 @@ QString Tree::refForAtom(const Atom* atom) if (atom) { if (atom->type() == Atom::SectionLeft) return Doc::canonicalTitle(Text::sectionHeading(atom).toString()); - if (atom->type() == Atom::Target) + if ((atom->type() == Atom::Target) || (atom->type() == Atom::Keyword)) return Doc::canonicalTitle(atom->string()); } return QString(); @@ -1394,4 +1415,51 @@ const Node* Tree::checkForCollision(const QString& name) return findNode(QStringList(name), 0, 0, Node::DontCare); } +/*! + Generate a target of the form link-nnn, where the nnn is + the current link count for this tree. This target string + is returned. It will be output as an HTML anchor just before + an HTML link to the node \a t. + + The node \a t + */ +QString Tree::getNewLinkTarget(const Node* locNode, + const Node* t, + const QString& fileName, + QString& text, + bool broken) +{ + QString moduleName; + if (t && !broken) { + Tree* tree = t->tree(); + if (tree != this) + tree->incrementLinkCount(); + moduleName = tree->moduleName(); + } + else + moduleName = "broken"; + incrementLinkCount(); + QString target = QString("qa-target-%1").arg(-(linkCount())); + TargetLoc* tloc = new TargetLoc(locNode, target, fileName, text, broken); + TargetList* tList = 0; + TargetListMap::iterator i = targetListMap_->find(moduleName); + if (i == targetListMap_->end()) { + tList = new TargetList; + i = targetListMap_->insert(moduleName, tList); + } + else + tList = i.value(); + tList->append(tloc); + return target; +} + +/*! + Look up the target list for the specified \a module + and return a pointer to it. + */ +TargetList* Tree::getTargetList(const QString& module) +{ + return targetListMap_->value(module); +} + QT_END_NAMESPACE diff --git a/src/tools/qdoc/tree.h b/src/tools/qdoc/tree.h index 6bb13ee327..6ccf85371b 100644 --- a/src/tools/qdoc/tree.h +++ b/src/tools/qdoc/tree.h @@ -67,10 +67,24 @@ struct TargetRec Type type_; }; +struct TargetLoc +{ + public: + TargetLoc(const Node* loc, const QString& t, const QString& fileName, const QString& text, bool broken) + : loc_(loc), target_(t), fileName_(fileName), text_(text), broken_(broken) { } + const Node* loc_; + QString target_; + QString fileName_; + QString text_; + bool broken_; +}; + typedef QMultiMap<QString, TargetRec*> TargetMap; typedef QMultiMap<QString, DocNode*> DocNodeMultiMap; typedef QMap<QString, QmlClassNode*> QmlTypeMap; typedef QMultiMap<QString, const ExampleNode*> ExampleNodeMap; +typedef QVector<TargetLoc*> TargetList; +typedef QMap<QString, TargetList*> TargetListMap; class Tree { @@ -187,12 +201,33 @@ class Tree void addExampleNode(ExampleNode* n) { exampleNodeMap_.insert(n->title(), n); } ExampleNodeMap& exampleNodeMap() { return exampleNodeMap_; } const Node* checkForCollision(const QString& name); + void setIndexFileName(const QString& t) { indexFileName_ = t; } + + bool treeHasBeenAnalyzed() const { return treeHasBeenAnalyzed_; } + bool docsHaveBeenGenerated() const { return docsHaveBeenGenerated_; } + void setTreeHasBeenAnalyzed() { treeHasBeenAnalyzed_ = true; } + void setdocsHaveBeenGenerated() { docsHaveBeenGenerated_ = true; } + QString getNewLinkTarget(const Node* locNode, + const Node* t, + const QString& fileName, + QString& text, + bool broken); + TargetList* getTargetList(const QString& module); + QStringList getTargetListKeys() { return targetListMap_->keys(); } public: const QString& moduleName() const { return module_; } + const QString& indexFileName() const { return indexFileName_; } + long incrementLinkCount() { return --linkCount_; } + void clearLinkCount() { linkCount_ = 0; } + long linkCount() const { return linkCount_; } private: + bool treeHasBeenAnalyzed_; + bool docsHaveBeenGenerated_; + long linkCount_; QString module_; + QString indexFileName_; QDocDatabase* qdb_; NamespaceNode root_; PropertyMap unresolvedPropertyMap; @@ -204,6 +239,7 @@ private: CNMap qmlModules_; QmlTypeMap qmlTypeMap_; ExampleNodeMap exampleNodeMap_; + TargetListMap* targetListMap_; }; QT_END_NAMESPACE diff --git a/src/tools/uic/cpp/cppwriteinitialization.cpp b/src/tools/uic/cpp/cppwriteinitialization.cpp index 5a1c1850d4..2627c4cf5c 100644 --- a/src/tools/uic/cpp/cppwriteinitialization.cpp +++ b/src/tools/uic/cpp/cppwriteinitialization.cpp @@ -913,6 +913,7 @@ void WriteInitialization::acceptLayout(DomLayout *node) m_layoutMarginType = SubLayoutMargin; DomPropertyList propList = node->elementProperty(); + DomPropertyList newPropList; if (m_layoutWidget) { bool left, top, right, bottom; left = top = right = bottom = false; @@ -932,31 +933,38 @@ void WriteInitialization::acceptLayout(DomLayout *node) DomProperty *p = new DomProperty(); p->setAttributeName(QLatin1String("leftMargin")); p->setElementNumber(0); - propList.append(p); + newPropList.append(p); } if (!top) { DomProperty *p = new DomProperty(); p->setAttributeName(QLatin1String("topMargin")); p->setElementNumber(0); - propList.append(p); + newPropList.append(p); } if (!right) { DomProperty *p = new DomProperty(); p->setAttributeName(QLatin1String("rightMargin")); p->setElementNumber(0); - propList.append(p); + newPropList.append(p); } if (!bottom) { DomProperty *p = new DomProperty(); p->setAttributeName(QLatin1String("bottomMargin")); p->setElementNumber(0); - propList.append(p); + newPropList.append(p); } m_layoutWidget = false; } + propList.append(newPropList); + writeProperties(varName, className, propList, WritePropertyIgnoreMargin|WritePropertyIgnoreSpacing); + // Clean up again: + propList.clear(); + qDeleteAll(newPropList); + newPropList.clear(); + m_layoutChain.push(node); TreeWalker::acceptLayout(node); m_layoutChain.pop(); |