62
62
static bool showBrokenLinks = false;
64
static QRegExp linkTag("(<@link node=\"([^\"]+)\">).*(</@link>)");
65
static QRegExp funcTag("(<@func target=\"([^\"]*)\">)(.*)(</@func>)");
66
static QRegExp typeTag("(<@(type|headerfile|func)(?: +[^>]*)?>)(.*)(</@\\2>)");
67
static QRegExp spanTag("</@(?:comment|preprocessor|string|char)>");
68
static QRegExp unknownTag("</?@[^>]*>");
70
bool parseArg(const QString &src,
78
#define SKIP_CHAR(c) \
80
qDebug() << "looking for " << c << " at " << QString(src.data() + i, n - i); \
81
if (i >= n || src[i] != c) { \
83
qDebug() << " char '" << c << "' not found"; \
90
while (i < n && src[i] == ' ') \
96
// assume "<@" has been parsed outside
100
if (tag != QStringRef(&src, i, tag.length())) {
102
qDebug() << "tag " << tag << " not found at " << i;
107
qDebug() << "haystack:" << src << "needle:" << tag << "i:" <<i;
112
// parse stuff like: linkTag("(<@link node=\"([^\"]+)\">).*(</@link>)");
115
// read parameter name
117
while (i < n && src[i].isLetter())
121
qDebug() << "read parameter" << QString(src.data() + j, i - j);
124
// skip parameter name
126
while (i < n && src[i] != '"')
128
*par1 = QStringRef(&src, j, i - j);
133
qDebug() << "no optional parameter found";
139
// find contents up to closing "</@tag>
142
if (i + 4 + tag.length() > n)
146
if (src[i + 1] != '/')
148
if (src[i + 2] != '@')
150
if (tag != QStringRef(&src, i + 3, tag.length()))
152
if (src[i + 3 + tag.length()] != '>')
157
*contents = QStringRef(&src, j, i - j);
159
i += tag.length() + 4;
163
qDebug() << " tag " << tag << " found: pos now: " << i;
168
static void addLink(const QString &linkTarget,
169
const QStringRef &nestedStuff,
172
if (!linkTarget.isEmpty()) {
173
*res += "<a href=\"";
64
185
HtmlGenerator::HtmlGenerator()
65
186
: helpProjectWriter(0), inLink(false), inContents(false),
66
187
inSectionHeading(false), inTableHeader(false), numTableRows(0),
67
188
threeColumnEnumValueTable(true), funcLeftParen("\\S(\\()"),
189
tre(0), slow(false), obsoleteLinks(false)
1126
1430
appendDcfSubSection(&fakeSection, compatSection);
1434
else if (fake->subType() == Node::QmlClass) {
1435
const QmlClassNode* qml_cn = static_cast<const QmlClassNode*>(fake);
1436
const ClassNode* cn = qml_cn->classNode();
1437
generateQmlInherits(qml_cn, marker);
1438
generateQmlInstantiates(qml_cn, marker);
1439
generateBrief(qml_cn, marker);
1440
sections = marker->qmlSections(qml_cn,CodeMarker::Summary);
1441
s = sections.begin();
1442
while (s != sections.end()) {
1443
out() << "<a name=\"" << registerRef((*s).name) << "\"></a>\n";
1444
out() << "<h2>" << protect((*s).name) << "</h2>\n";
1445
generateQmlSummary(*s,fake,marker);
1449
out() << "<a name=\"" << registerRef("details") << "\"></a>\n";
1450
out() << "<h2>" << "Detailed Description" << "</h2>\n";
1451
generateBody(fake, marker);
1453
generateQmlText(cn->doc().body(), cn, marker, fake->name());
1454
generateAlsoList(fake, marker);
1455
out() << "<hr />\n";
1457
sections = marker->qmlSections(qml_cn,CodeMarker::Detailed);
1458
s = sections.begin();
1459
while (s != sections.end()) {
1460
out() << "<h2>" << protect((*s).name) << "</h2>\n";
1461
NodeList::ConstIterator m = (*s).members.begin();
1462
while (m != (*s).members.end()) {
1463
generateDetailedQmlMember(*m, fake, marker);
1464
out() << "<br />\n";
1465
fakeSection.keywords += qMakePair((*m)->name(),
1471
generateFooter(fake);
1130
1476
sections = marker->sections(fake, CodeMarker::Summary, CodeMarker::Okay);
1131
1477
s = sections.begin();
1132
1478
while (s != sections.end()) {
1133
1479
out() << "<a name=\"" << registerRef((*s).name) << "\"></a>\n";
1134
out() << "<h3>" << protect((*s).name) << "</h3>\n";
1480
out() << "<h2>" << protect((*s).name) << "</h2>\n";
1135
1481
generateSectionList(*s, fake, marker, CodeMarker::Summary);
1139
1485
Text brief = fake->doc().briefText();
1140
if (fake->subType() == FakeNode::Module && !brief.isEmpty()) {
1486
if (fake->subType() == Node::Module && !brief.isEmpty()) {
1141
1487
out() << "<a name=\"" << registerRef("details") << "\"></a>\n";
1142
1488
out() << "<h2>" << "Detailed Description" << "</h2>\n";
1145
1491
generateBody(fake, marker);
1147
if (fake->subType() == FakeNode::QmlClass) {
1148
//qDebug() << "generateFakeNode(): QML CLASS" << fake->name();
1149
const QmlNode* qmlNode = static_cast<const QmlNode*>(fake);
1150
const ClassNode* cn = qmlNode->classNode();
1152
//qDebug() << " CPP CLASS" << cn->name();
1153
generateQmlText(cn->doc().body(), cn, marker);
1158
1492
generateAlsoList(fake, marker);
1160
1494
if (!fake->groupMembers().isEmpty()) {
1813
2175
firstOffset[NumColumns] = classMap.count();
1815
out() << "<p><table width=\"100%\">\n";
2177
out() << "<p><table class=\"generic\" width=\"100%\">\n";
1816
2178
for (k = 0; k < numRows; k++) {
1817
2179
out() << "<tr>\n";
1818
2180
for (i = 0; i < NumColumns; i++) {
1819
2181
if (currentOffset[i] >= firstOffset[i + 1]) {
1820
2182
// this column is finished
1821
2183
out() << "<td>\n</td>\n";
1823
while (currentOffsetInParagraph[i] == paragraph[currentParagraphNo[i]].count()) {
2186
while ((currentParagraphNo[i] < NumParagraphs) &&
2187
(currentOffsetInParagraph[i] == paragraph[currentParagraphNo[i]].count())) {
1824
2188
++currentParagraphNo[i];
1825
2189
currentOffsetInParagraph[i] = 0;
2192
if (currentParagraphNo[i] >= NumParagraphs) {
2193
qDebug() << "### Internal error ###" << __FILE__ << __LINE__
2194
<< currentParagraphNo[i] << NumParagraphs;
2195
currentParagraphNo[i] = NumParagraphs - 1;
1828
2198
out() << "<td align=\"right\">";
1829
2199
if (currentOffsetInParagraph[i] == 0) {
1830
2200
// start a new paragraph
1831
out() << "<b>" << paragraphName[currentParagraphNo[i]] << " </b>";
1836
QMap<QString, const Node *>::Iterator it;
1837
it = paragraph[currentParagraphNo[i]].begin();
1838
for (j = 0; j < currentOffsetInParagraph[i]; j++)
1842
// Previously, we used generateFullName() for this, but we
1843
// require some special formatting.
1844
out() << "<a href=\"" << linkForNode(it.value(), relative) << "\">";
1845
QStringList pieces = fullName(it.value(), relative, marker).split("::");
1846
out() << protect(pieces.last());
1848
if (pieces.size() > 1) {
1850
generateFullName(it.value()->parent(), relative, marker);
2202
<< paragraphName[currentParagraphNo[i]]
2207
if ((currentParagraphNo[i] < NumParagraphs) &&
2208
!paragraphName[currentParagraphNo[i]].isEmpty()) {
2209
QMap<QString, const Node *>::Iterator it;
2210
it = paragraph[currentParagraphNo[i]].begin();
2211
for (j = 0; j < currentOffsetInParagraph[i]; j++)
2215
// Previously, we used generateFullName() for this, but we
2216
// require some special formatting.
2217
out() << "<a href=\""
2218
<< linkForNode(it.value(), relative)
2220
QStringList pieces = fullName(it.value(), relative, marker).split("::");
2221
out() << protect(pieces.last());
2223
if (pieces.size() > 1) {
2225
generateFullName(it.value()->parent(), relative, marker);
1855
2231
currentOffset[i]++;
1856
2232
currentOffsetInParagraph[i]++;
2078
void HtmlGenerator::generateSectionList(const Section& section, const Node *relative,
2079
CodeMarker *marker, CodeMarker::SynopsisStyle style)
2081
if (!section.members.isEmpty()) {
2082
bool twoColumn = false;
2083
if (style == CodeMarker::SeparateList) {
2084
twoColumn = (section.members.count() >= 16);
2085
} else if (section.members.first()->type() == Node::Property) {
2494
#ifdef QDOC_NAME_ALIGNMENT
2495
void HtmlGenerator::generateSection(const NodeList& nl,
2496
const Node *relative,
2498
CodeMarker::SynopsisStyle style)
2500
bool name_alignment = true;
2501
if (!nl.isEmpty()) {
2502
bool twoColumn = false;
2503
if (style == CodeMarker::SeparateList) {
2504
name_alignment = false;
2505
twoColumn = (nl.count() >= 16);
2507
else if (nl.first()->type() == Node::Property) {
2508
twoColumn = (nl.count() >= 5);
2509
name_alignment = false;
2511
if (name_alignment) {
2512
out() << "<table class=\"alignedsummary\" border=\"0\" cellpadding=\"0\" "
2513
<< "cellspacing=\"0\" width=\"100%\">\n";
2517
out() << "<p><table class=\"propsummary\" width=\"100%\" "
2518
<< "border=\"0\" cellpadding=\"0\""
2519
<< " cellspacing=\"0\">\n"
2520
<< "<tr><td width=\"45%\" valign=\"top\">";
2525
NodeList::ConstIterator m = nl.begin();
2526
while (m != nl.end()) {
2527
if ((*m)->access() == Node::Private) {
2532
if (name_alignment) {
2533
out() << "<tr><td class=\"memItemLeft\" "
2534
<< "align=\"right\" valign=\"top\">";
2537
if (twoColumn && i == (int) (nl.count() + 1) / 2)
2538
out() << "</ul></td><td valign=\"top\"><ul>\n";
2539
out() << "<li><div class=\"fn\">";
2542
generateSynopsis(*m, relative, marker, style, name_alignment);
2544
out() << "</td></tr>\n";
2546
out() << "</div></li>\n";
2551
out() << "</table>\n";
2555
out() << "</td></tr>\n</table></p>\n";
2560
void HtmlGenerator::generateSectionList(const Section& section,
2561
const Node *relative,
2563
CodeMarker::SynopsisStyle style)
2565
bool name_alignment = true;
2566
if (!section.members.isEmpty()) {
2567
bool twoColumn = false;
2568
if (style == CodeMarker::SeparateList) {
2569
name_alignment = false;
2570
twoColumn = (section.members.count() >= 16);
2572
else if (section.members.first()->type() == Node::Property) {
2573
twoColumn = (section.members.count() >= 5);
2574
name_alignment = false;
2576
if (name_alignment) {
2577
out() << "<table class=\"alignedsummary\" border=\"0\" cellpadding=\"0\" "
2578
<< "cellspacing=\"0\" width=\"100%\">\n";
2582
out() << "<p><table class=\"propsummary\" width=\"100%\" "
2583
<< "border=\"0\" cellpadding=\"0\""
2584
<< " cellspacing=\"0\">\n"
2585
<< "<tr><td width=\"45%\" valign=\"top\">";
2590
NodeList::ConstIterator m = section.members.begin();
2591
while (m != section.members.end()) {
2592
if ((*m)->access() == Node::Private) {
2597
if (name_alignment) {
2598
out() << "<tr><td class=\"memItemLeft\" "
2599
<< "align=\"right\" valign=\"top\">";
2602
if (twoColumn && i == (int) (section.members.count() + 1) / 2)
2603
out() << "</ul></td><td valign=\"top\"><ul>\n";
2604
out() << "<li><div class=\"fn\">";
2607
generateSynopsis(*m, relative, marker, style, name_alignment);
2609
out() << "</td></tr>\n";
2611
out() << "</div></li>\n";
2616
out() << "</table>\n";
2620
out() << "</td></tr>\n</table></p>\n";
2624
if (style == CodeMarker::Summary && !section.inherited.isEmpty()) {
2626
generateSectionInheritedList(section, relative, marker, name_alignment);
2631
void HtmlGenerator::generateSectionInheritedList(const Section& section,
2632
const Node *relative,
2636
QList<QPair<ClassNode *, int> >::ConstIterator p = section.inherited.begin();
2637
while (p != section.inherited.end()) {
2639
out() << "<li><div bar=\"2\" class=\"fn\"></div>";
2641
out() << "<li><div class=\"fn\"></div>";
2642
out() << (*p).second << " ";
2643
if ((*p).second == 1) {
2644
out() << section.singularMember;
2647
out() << section.pluralMember;
2649
out() << " inherited from <a href=\"" << fileName((*p).first)
2650
<< "#" << HtmlGenerator::cleanRef(section.name.toLower()) << "\">"
2651
<< protect(marker->plainFullName((*p).first, relative))
2657
void HtmlGenerator::generateSynopsis(const Node *node,
2658
const Node *relative,
2660
CodeMarker::SynopsisStyle style,
2663
QString marked = marker->markedUpSynopsis(node, relative, style);
2664
QRegExp templateTag("(<[^@>]*>)");
2665
if (marked.indexOf(templateTag) != -1) {
2666
QString contents = protect(marked.mid(templateTag.pos(1),
2667
templateTag.cap(1).length()));
2668
marked.replace(templateTag.pos(1), templateTag.cap(1).length(),
2671
marked.replace(QRegExp("<@param>([a-z]+)_([1-9n])</@param>"),
2672
"<i>\\1<sub>\\2</sub></i>");
2673
marked.replace("<@param>", "<i>");
2674
marked.replace("</@param>", "</i>");
2676
if (style == CodeMarker::Summary) {
2677
marked.replace("<@name>", ""); // was "<b>"
2678
marked.replace("</@name>", ""); // was "</b>"
2681
if (style == CodeMarker::SeparateList) {
2682
QRegExp extraRegExp("<@extra>.*</@extra>");
2683
extraRegExp.setMinimal(true);
2684
marked.replace(extraRegExp, "");
2686
marked.replace("<@extra>", " <tt>");
2687
marked.replace("</@extra>", "</tt>");
2690
if (style != CodeMarker::Detailed) {
2691
marked.replace("<@type>", "");
2692
marked.replace("</@type>", "");
2694
out() << highlightedCode(marked, marker, relative, style, nameAlignment);
2697
QString HtmlGenerator::highlightedCode(const QString& markedCode,
2699
const Node *relative,
2700
CodeMarker::SynopsisStyle ,
2703
QString src = markedCode;
2708
const QChar charLangle = '<';
2709
const QChar charAt = '@';
2711
// replace all <@link> tags: "(<@link node=\"([^\"]+)\">).*(</@link>)"
2712
static const QString linkTag("link");
2714
for (int i = 0, n = src.size(); i < n;) {
2715
if (src.at(i) == charLangle && src.at(i + 1).unicode() == '@') {
2716
if (nameAlignment && !done) {// && (i != 0)) Why was this here?
2717
html += "</td><td class=\"memItemRight\" valign=\"bottom\">";
2721
if (parseArg(src, linkTag, &i, n, &arg, &par1)) {
2723
QString link = linkForNode(
2724
CodeMarker::nodeForString(par1.toString()), relative);
2725
addLink(link, arg, &html);
2734
html += src.at(i++);
2740
// is this block ever used at all?
2741
// replace all <@func> tags: "(<@func target=\"([^\"]*)\">)(.*)(</@func>)"
2744
static const QString funcTag("func");
2745
for (int i = 0, n = src.size(); i < n;) {
2746
if (src.at(i) == charLangle && src.at(i + 1) == charAt) {
2748
if (parseArg(src, funcTag, &i, n, &arg, &par1)) {
2749
QString link = linkForNode(
2750
marker->resolveTarget(par1.toString(),
2754
addLink(link, arg, &html);
2755
par1 = QStringRef();
2763
html += src.at(i++);
2768
// replace all "(<@(type|headerfile|func)(?: +[^>]*)?>)(.*)(</@\\2>)" tags
2771
static const QString typeTags[] = { "type", "headerfile", "func" };
2772
for (int i = 0, n = src.size(); i < n;) {
2773
if (src.at(i) == charLangle && src.at(i + 1) == charAt) {
2775
bool handled = false;
2776
for (int k = 0; k != 3; ++k) {
2777
if (parseArg(src, typeTags[k], &i, n, &arg, &par1)) {
2778
par1 = QStringRef();
2779
QString link = linkForNode(
2780
marker->resolveTarget(arg.toString(), tre, relative),
2782
addLink(link, arg, &html);
2793
html += src.at(i++);
2798
// "<@comment>" -> "<span class=\"comment\">";
2799
// "<@preprocessor>" -> "<span class=\"preprocessor\">";
2800
// "<@string>" -> "<span class=\"string\">";
2801
// "<@char>" -> "<span class=\"char\">";
2802
// "</@(?:comment|preprocessor|string|char)>" -> "</span>"
2805
static const QString spanTags[] = {
2806
"<@comment>", "<span class=\"comment\">",
2807
"<@preprocessor>", "<span class=\"preprocessor\">",
2808
"<@string>", "<span class=\"string\">",
2809
"<@char>", "<span class=\"char\">",
2810
"</@comment>", "</span>",
2811
"</@preprocessor>","</span>",
2812
"</@string>", "</span>",
2813
"</@char>", "</span>"
2814
// "<@char>", "<font color=blue>",
2815
// "</@char>", "</font>",
2816
// "<@func>", "<font color=green>",
2817
// "</@func>", "</font>",
2819
// "</@id>", "</i>",
2820
// "<@keyword>", "<b>",
2821
// "</@keyword>", "</b>",
2822
// "<@number>", "<font color=yellow>",
2823
// "</@number>", "</font>",
2825
// "</@op>", "</b>",
2826
// "<@param>", "<i>",
2827
// "</@param>", "</i>",
2828
// "<@string>", "<font color=green>",
2829
// "</@string>", "</font>",
2831
for (int i = 0, n = src.size(); i < n;) {
2832
if (src.at(i) == charLangle) {
2833
bool handled = false;
2834
for (int k = 0; k != 8; ++k) {
2835
const QString & tag = spanTags[2 * k];
2836
if (tag == QStringRef(&src, i, tag.length())) {
2837
html += spanTags[2 * k + 1];
2845
if (src.at(i) == charAt ||
2846
(src.at(i) == QLatin1Char('/') && src.at(i + 1) == charAt)) {
2847
// drop 'our' unknown tags (the ones still containing '@')
2848
while (i < n && src.at(i) != QLatin1Char('>'))
2853
// retain all others
2868
void HtmlGenerator::generateSectionList(const Section& section,
2869
const Node *relative,
2871
CodeMarker::SynopsisStyle style)
2873
if (!section.members.isEmpty()) {
2874
bool twoColumn = false;
2875
if (style == CodeMarker::SeparateList) {
2876
twoColumn = (section.members.count() >= 16);
2878
else if (section.members.first()->type() == Node::Property) {
2086
2879
twoColumn = (section.members.count() >= 5);
2089
out() << "<p><table width=\"100%\" border=\"0\" cellpadding=\"0\""
2090
" cellspacing=\"0\">\n"
2882
out() << "<p><table class=\"generic\" width=\"100%\" border=\"0\" "
2883
<< "cellpadding=\"0\" cellspacing=\"0\">\n"
2091
2884
<< "<tr><td width=\"45%\" valign=\"top\">";
2092
2885
out() << "<ul>\n";
2147
void HtmlGenerator::generateLink(const Atom *atom, const Node * /* relative */, CodeMarker *marker)
2941
void HtmlGenerator::generateSynopsis(const Node *node,
2942
const Node *relative,
2944
CodeMarker::SynopsisStyle style)
2946
QString marked = marker->markedUpSynopsis(node, relative, style);
2947
QRegExp templateTag("(<[^@>]*>)");
2948
if (marked.indexOf(templateTag) != -1) {
2949
QString contents = protect(marked.mid(templateTag.pos(1),
2950
templateTag.cap(1).length()));
2951
marked.replace(templateTag.pos(1), templateTag.cap(1).length(),
2954
marked.replace(QRegExp("<@param>([a-z]+)_([1-9n])</@param>"), "<i>\\1<sub>\\2</sub></i>");
2955
marked.replace("<@param>", "<i>");
2956
marked.replace("</@param>", "</i>");
2958
if (style == CodeMarker::Summary)
2959
marked.replace("@name>", "b>");
2961
if (style == CodeMarker::SeparateList) {
2962
QRegExp extraRegExp("<@extra>.*</@extra>");
2963
extraRegExp.setMinimal(true);
2964
marked.replace(extraRegExp, "");
2966
marked.replace("<@extra>", " <tt>");
2967
marked.replace("</@extra>", "</tt>");
2970
if (style != CodeMarker::Detailed) {
2971
marked.replace("<@type>", "");
2972
marked.replace("</@type>", "");
2974
out() << highlightedCode(marked, marker, relative);
2977
QString HtmlGenerator::highlightedCode(const QString& markedCode,
2979
const Node *relative)
2981
QString src = markedCode;
2986
const QChar charLangle = '<';
2987
const QChar charAt = '@';
2989
// replace all <@link> tags: "(<@link node=\"([^\"]+)\">).*(</@link>)"
2990
static const QString linkTag("link");
2991
for (int i = 0, n = src.size(); i < n;) {
2992
if (src.at(i) == charLangle && src.at(i + 1) == charAt) {
2994
if (parseArg(src, linkTag, &i, n, &arg, &par1)) {
2995
const Node* node = CodeMarker::nodeForString(par1.toString());
2996
QString link = linkForNode(node, relative);
2997
addLink(link, arg, &html);
3005
html += src.at(i++);
3010
// is this block ever used at all?
3011
// replace all <@func> tags: "(<@func target=\"([^\"]*)\">)(.*)(</@func>)"
3014
static const QString funcTag("func");
3015
for (int i = 0, n = src.size(); i < n;) {
3016
if (src.at(i) == charLangle && src.at(i + 1) == charAt) {
3018
if (parseArg(src, funcTag, &i, n, &arg, &par1)) {
3019
QString link = linkForNode(
3020
marker->resolveTarget(par1.toString(),
3024
addLink(link, arg, &html);
3025
par1 = QStringRef();
3033
html += src.at(i++);
3038
// replace all "(<@(type|headerfile|func)(?: +[^>]*)?>)(.*)(</@\\2>)" tags
3041
static const QString typeTags[] = { "type", "headerfile", "func" };
3042
for (int i = 0, n = src.size(); i < n;) {
3043
if (src.at(i) == charLangle && src.at(i + 1) == charAt) {
3045
bool handled = false;
3046
for (int k = 0; k != 3; ++k) {
3047
if (parseArg(src, typeTags[k], &i, n, &arg, &par1)) {
3048
par1 = QStringRef();
3049
QString link = linkForNode(
3050
marker->resolveTarget(arg.toString(), tre, relative),
3052
addLink(link, arg, &html);
3063
html += src.at(i++);
3068
// "<@comment>" -> "<span class=\"comment\">";
3069
// "<@preprocessor>" -> "<span class=\"preprocessor\">";
3070
// "<@string>" -> "<span class=\"string\">";
3071
// "<@char>" -> "<span class=\"char\">";
3072
// "</@(?:comment|preprocessor|string|char)>" -> "</span>"
3075
static const QString spanTags[] = {
3076
"<@comment>", "<span class=\"comment\">",
3077
"<@preprocessor>", "<span class=\"preprocessor\">",
3078
"<@string>", "<span class=\"string\">",
3079
"<@char>", "<span class=\"char\">",
3080
"</@comment>", "</span>",
3081
"</@preprocessor>","</span>",
3082
"</@string>", "</span>",
3083
"</@char>", "</span>"
3084
// "<@char>", "<font color=blue>",
3085
// "</@char>", "</font>",
3086
// "<@func>", "<font color=green>",
3087
// "</@func>", "</font>",
3089
// "</@id>", "</i>",
3090
// "<@keyword>", "<b>",
3091
// "</@keyword>", "</b>",
3092
// "<@number>", "<font color=yellow>",
3093
// "</@number>", "</font>",
3095
// "</@op>", "</b>",
3096
// "<@param>", "<i>",
3097
// "</@param>", "</i>",
3098
// "<@string>", "<font color=green>",
3099
// "</@string>", "</font>",
3101
for (int i = 0, n = src.size(); i < n;) {
3102
if (src.at(i) == charLangle) {
3103
bool handled = false;
3104
for (int k = 0; k != 8; ++k) {
3105
const QString & tag = spanTags[2 * k];
3106
if (tag == QStringRef(&src, i, tag.length())) {
3107
html += spanTags[2 * k + 1];
3115
if (src.at(i) == charAt ||
3116
(src.at(i) == QLatin1Char('/') && src.at(i + 1) == charAt)) {
3117
// drop 'our' unknown tags (the ones still containing '@')
3118
while (i < n && src.at(i) != QLatin1Char('>'))
3123
// retain all others
3138
void HtmlGenerator::generateLink(const Atom* atom,
3139
const Node* /* relative */,
2149
3142
static QRegExp camelCase("[A-Z][A-Z][a-z]|[a-z][A-Z0-9]|_");
2293
static QRegExp linkTag("(<@link node=\"([^\"]+)\">).*(</@link>)");
2294
static QRegExp funcTag("(<@func target=\"([^\"]*)\">)(.*)(</@func>)");
2295
static QRegExp typeTag("(<@(type|headerfile|func)(?: +[^>]*)?>)(.*)(</@\\2>)");
2296
static QRegExp spanTag("</@(?:comment|preprocessor|string|char)>");
2297
static QRegExp unknownTag("</?@[^>]*>");
2299
bool parseArg(const QString &src,
2303
QStringRef *contents,
2304
QStringRef *par1 = 0,
2307
#define SKIP_CHAR(c) \
2309
qDebug() << "looking for " << c << " at " << QString(src.data() + i, n - i); \
2310
if (i >= n || src[i] != c) { \
2312
qDebug() << " char '" << c << "' not found"; \
2318
#define SKIP_SPACE \
2319
while (i < n && src[i] == ' ') \
2325
// assume "<@" has been parsed outside
2329
if (tag != QStringRef(&src, i, tag.length())) {
2331
qDebug() << "tag " << tag << " not found at " << i;
2336
qDebug() << "haystack:" << src << "needle:" << tag << "i:" <<i;
2341
// parse stuff like: linkTag("(<@link node=\"([^\"]+)\">).*(</@link>)");
2344
// read parameter name
2346
while (i < n && src[i].isLetter())
2348
if (src[i] == '=') {
2350
qDebug() << "read parameter" << QString(src.data() + j, i - j);
2353
// skip parameter name
2355
while (i < n && src[i] != '"')
2357
*par1 = QStringRef(&src, j, i - j);
2362
qDebug() << "no optional parameter found";
2368
// find contents up to closing "</@tag>
2371
if (i + 4 + tag.length() > n)
2375
if (src[i + 1] != '/')
2377
if (src[i + 2] != '@')
2379
if (tag != QStringRef(&src, i + 3, tag.length()))
2381
if (src[i + 3 + tag.length()] != '>')
2386
*contents = QStringRef(&src, j, i - j);
2388
i += tag.length() + 4;
2392
qDebug() << " tag " << tag << " found: pos now: " << i;
2397
static void addLink(const QString &linkTarget,
2398
const QStringRef &nestedStuff,
2401
if (!linkTarget.isEmpty()) {
2402
*res += "<a href=\"";
2405
*res += nestedStuff;
2409
*res += nestedStuff;
2413
QString HtmlGenerator::highlightedCode(const QString& markedCode,
2415
const Node *relative)
2417
QString src = markedCode;
2422
const QChar charLangle = '<';
2423
const QChar charAt = '@';
2425
// replace all <@link> tags: "(<@link node=\"([^\"]+)\">).*(</@link>)"
2426
static const QString linkTag("link");
2427
for (int i = 0, n = src.size(); i < n;) {
2428
if (src.at(i) == charLangle && src.at(i + 1) == charAt) {
2430
if (parseArg(src, linkTag, &i, n, &arg, &par1)) {
2431
QString link = linkForNode(
2432
CodeMarker::nodeForString(par1.toString()), relative);
2433
addLink(link, arg, &html);
2441
html += src.at(i++);
2447
// is this block ever used at all?
2448
// replace all <@func> tags: "(<@func target=\"([^\"]*)\">)(.*)(</@func>)"
2451
static const QString funcTag("func");
2452
for (int i = 0, n = src.size(); i < n;) {
2453
if (src.at(i) == charLangle && src.at(i + 1) == charAt) {
2455
if (parseArg(src, funcTag, &i, n, &arg, &par1)) {
2456
QString link = linkForNode(
2457
marker->resolveTarget(par1.toString(),
2461
addLink(link, arg, &html);
2462
par1 = QStringRef();
2470
html += src.at(i++);
2475
// replace all "(<@(type|headerfile|func)(?: +[^>]*)?>)(.*)(</@\\2>)" tags
2478
static const QString typeTags[] = { "type", "headerfile", "func" };
2479
for (int i = 0, n = src.size(); i < n;) {
2480
if (src.at(i) == charLangle && src.at(i + 1) == charAt) {
2482
bool handled = false;
2483
for (int k = 0; k != 3; ++k) {
2484
if (parseArg(src, typeTags[k], &i, n, &arg, &par1)) {
2485
par1 = QStringRef();
2486
QString link = linkForNode(
2487
marker->resolveTarget(arg.toString(), tre, relative),
2489
addLink(link, arg, &html);
2500
html += src.at(i++);
2505
// "<@comment>" -> "<span class=\"comment\">";
2506
// "<@preprocessor>" -> "<span class=\"preprocessor\">";
2507
// "<@string>" -> "<span class=\"string\">";
2508
// "<@char>" -> "<span class=\"char\">";
2509
// "</@(?:comment|preprocessor|string|char)>" -> "</span>"
2512
static const QString spanTags[] = {
2513
"<@comment>", "<span class=\"comment\">",
2514
"<@preprocessor>", "<span class=\"preprocessor\">",
2515
"<@string>", "<span class=\"string\">",
2516
"<@char>", "<span class=\"char\">",
2517
"</@comment>", "</span>",
2518
"</@preprocessor>","</span>",
2519
"</@string>", "</span>",
2520
"</@char>", "</span>"
2521
// "<@char>", "<font color=blue>",
2522
// "</@char>", "</font>",
2523
// "<@func>", "<font color=green>",
2524
// "</@func>", "</font>",
2526
// "</@id>", "</i>",
2527
// "<@keyword>", "<b>",
2528
// "</@keyword>", "</b>",
2529
// "<@number>", "<font color=yellow>",
2530
// "</@number>", "</font>",
2532
// "</@op>", "</b>",
2533
// "<@param>", "<i>",
2534
// "</@param>", "</i>",
2535
// "<@string>", "<font color=green>",
2536
// "</@string>", "</font>",
2538
for (int i = 0, n = src.size(); i < n;) {
2539
if (src.at(i) == charLangle) {
2540
bool handled = false;
2541
for (int k = 0; k != 8; ++k) {
2542
const QString & tag = spanTags[2 * k];
2543
if (tag == QStringRef(&src, i, tag.length())) {
2544
html += spanTags[2 * k + 1];
2552
if (src.at(i) == charAt ||
2553
(src.at(i) == QLatin1Char('/') && src.at(i + 1) == charAt)) {
2554
// drop 'our' unknown tags (the ones still containing '@')
2555
while (i < n && src.at(i) != QLatin1Char('>'))
2560
// retain all others
2574
3286
QString HtmlGenerator::fileBase(const Node *node)
2576
3288
QString result;
3035
3825
QString first = path.first().trimmed();
3036
3826
if (first.isEmpty()) {
3039
3829
else if (first.endsWith(".html")) {
3040
node = tre->root()->findNode(first, Node::Fake);
3830
*node = tre->root()->findNode(first, Node::Fake);
3043
node = marker->resolveTarget(first, tre, relative);
3045
node = tre->findFakeNodeByTitle(first);
3047
node = tre->findUnambiguousTarget(first, targetAtom);
3833
*node = marker->resolveTarget(first, tre, relative);
3835
*node = tre->findFakeNodeByTitle(first);
3837
*node = tre->findUnambiguousTarget(first, targetAtom);
3051
if (!node->url().isEmpty())
3841
if (!(*node)->url().isEmpty())
3842
return (*node)->url();
3054
3844
path.removeFirst();
3851
if ((*node)->status() == Node::Obsolete) {
3853
if (relative->parent() != *node) {
3854
if (relative->status() != Node::Obsolete) {
3855
bool porting = false;
3856
if (relative->type() == Node::Fake) {
3857
const FakeNode* fake = static_cast<const FakeNode*>(relative);
3858
if (fake->title().startsWith("Porting"))
3861
QString name = marker->plainFullName(relative);
3862
if (!porting && !name.startsWith("Q3")) {
3863
if (obsoleteLinks) {
3864
relative->doc().location().warning(tr("Link to obsolete item '%1' in %2")
3865
.arg(atom->string())
3868
inObsoleteLink = true;
3874
qDebug() << "Link to Obsolete entity"
3875
<< (*node)->name() << "no relative";
3879
else if ((*node)->status() == Node::Deprecated) {
3880
qDebug() << "Link to Deprecated entity";
3882
else if ((*node)->status() == Node::Internal) {
3883
qDebug() << "Link to Internal entity";
3060
3888
while (!path.isEmpty()) {
3061
targetAtom = tre->findTarget(path.first(), node);
3889
targetAtom = tre->findTarget(path.first(), *node);
3062
3890
if (targetAtom == 0)
3064
3892
path.removeFirst();
3067
3895
if (path.isEmpty()) {
3068
link = linkForNode(node, relative);
3896
link = linkForNode(*node, relative);
3069
3897
if (targetAtom)
3070
link += "#" + refForAtom(targetAtom, node);
3898
link += "#" + refForAtom(targetAtom, *node);
3186
4014
out() << "</i>";
4017
if (inObsoleteLink) {
4018
out() << "<sup>(obsolete)</sup>";
3189
4020
out() << "</a>";
3192
4023
inLink = false;
4024
inObsoleteLink = false;
3195
4027
QT_END_NAMESPACE
4032
Generates the summary for for the \a section. Only used for
4033
sections of QML element documentation.
4035
Currently handles only the QML property group.
4037
void HtmlGenerator::generateQmlSummary(const Section& section,
4038
const Node *relative,
4041
if (!section.members.isEmpty()) {
4042
NodeList::ConstIterator m;
4043
int count = section.members.size();
4044
bool twoColumn = false;
4045
if (section.members.first()->type() == Node::QmlProperty) {
4046
twoColumn = (count >= 5);
4049
out() << "<p><table width=\"100%\" border=\"0\" cellpadding=\"0\""
4050
" cellspacing=\"0\">\n"
4051
<< "<tr><td width=\"45%\" valign=\"top\">";
4055
m = section.members.begin();
4056
while (m != section.members.end()) {
4057
if (twoColumn && row == (int) (count + 1) / 2)
4058
out() << "</ul></td><td valign=\"top\"><ul>\n";
4059
out() << "<li><div class=\"fn\"></div>";
4060
generateQmlItem(*m,relative,marker,true);
4067
out() << "</td></tr>\n</table></p>\n";
4072
Outputs the html detailed documentation for a section
4073
on a QML element reference page.
4075
void HtmlGenerator::generateDetailedQmlMember(const Node *node,
4076
const InnerNode *relative,
4079
const QmlPropertyNode* qpn = 0;
4080
generateMacRef(node, marker);
4081
out() << "<div class=\"qmlitem\">";
4082
if (node->subType() == Node::QmlPropertyGroup) {
4083
const QmlPropGroupNode* qpgn = static_cast<const QmlPropGroupNode*>(node);
4084
NodeList::ConstIterator p = qpgn->childNodes().begin();
4085
out() << "<div class=\"qmlproto\">";
4086
out() << "<table class=\"qmlname\">";
4088
while (p != qpgn->childNodes().end()) {
4089
if ((*p)->type() == Node::QmlProperty) {
4090
qpn = static_cast<const QmlPropertyNode*>(*p);
4091
out() << "<tr><td>";
4092
out() << "<a name=\"" + refForNode(qpn) + "\"></a>";
4093
generateQmlItem(qpn, relative, marker, false);
4094
out() << "</td></tr>";
4095
if (qpgn->isDefault()) {
4098
<< "<div class=\"qmlitem\">"
4099
<< "<div class=\"qmlproto\">"
4100
<< "<table class=\"qmlname\">"
4101
<< "<tr><td><font color=\"green\">"
4102
<< "default</font></td></tr>";
4107
out() << "</table>";
4110
else if (node->type() == Node::QmlSignal) {
4111
const QmlSignalNode* qsn = static_cast<const QmlSignalNode*>(node);
4112
out() << "<div class=\"qmlproto\">";
4113
out() << "<table class=\"qmlname\">";
4114
out() << "<tr><td>";
4115
out() << "<a name=\"" + refForNode(qsn) + "\"></a>";
4116
generateQmlItem(qsn,relative,marker,false);
4117
out() << "</td></tr>";
4118
out() << "</table>";
4121
else if (node->type() == Node::QmlMethod) {
4122
const QmlMethodNode* qmn = static_cast<const QmlMethodNode*>(node);
4123
out() << "<div class=\"qmlproto\">";
4124
out() << "<table class=\"qmlname\">";
4125
out() << "<tr><td>";
4126
out() << "<a name=\"" + refForNode(qmn) + "\"></a>";
4127
generateQmlItem(qmn,relative,marker,false);
4128
out() << "</td></tr>";
4129
out() << "</table>";
4132
out() << "<div class=\"qmldoc\">";
4133
generateStatus(node, marker);
4134
generateBody(node, marker);
4135
generateThreadSafeness(node, marker);
4136
generateSince(node, marker);
4137
generateAlsoList(node, marker);
4143
Output the "Inherits" line for the QML element,
4144
if there should be one.
4146
void HtmlGenerator::generateQmlInherits(const QmlClassNode* cn,
4149
if (cn && !cn->links().empty()) {
4150
if (cn->links().contains(Node::InheritsLink)) {
4151
QPair<QString,QString> linkPair;
4152
linkPair = cn->links()[Node::InheritsLink];
4153
QStringList strList(linkPair.first);
4154
const Node* n = tre->findNode(strList,Node::Fake);
4155
if (n && n->subType() == Node::QmlClass) {
4156
const QmlClassNode* qcn = static_cast<const QmlClassNode*>(n);
4157
out() << "<p style=\"text-align: center\">";
4159
text << "[Inherits ";
4160
text << Atom(Atom::LinkNode,CodeMarker::stringForNode(qcn));
4161
text << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK);
4162
text << Atom(Atom::String, linkPair.second);
4163
text << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK);
4165
generateText(text, cn, marker);
4173
Output the "[Xxx instantiates the C++ class QFxXxx]"
4174
line for the QML element, if there should be one.
4176
If there is no class node, or if the class node status
4177
is set to Node::Internal, do nothing.
4179
void HtmlGenerator::generateQmlInstantiates(const QmlClassNode* qcn,
4182
const ClassNode* cn = qcn->classNode();
4183
if (cn && (cn->status() != Node::Internal)) {
4184
out() << "<p style=\"text-align: center\">";
4187
text << Atom(Atom::LinkNode,CodeMarker::stringForNode(qcn));
4188
text << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK);
4189
text << Atom(Atom::String, qcn->name());
4190
text << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK);
4191
text << " instantiates the C++ class ";
4192
text << Atom(Atom::LinkNode,CodeMarker::stringForNode(cn));
4193
text << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK);
4194
text << Atom(Atom::String, cn->name());
4195
text << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK);
4197
generateText(text, qcn, marker);
4203
Output the "[QFxXxx is instantiated by QML element Xxx]"
4204
line for the class, if there should be one.
4206
If there is no QML element, or if the class node status
4207
is set to Node::Internal, do nothing.
4209
void HtmlGenerator::generateInstantiatedBy(const ClassNode* cn,
4212
if (cn && cn->status() != Node::Internal && !cn->qmlElement().isEmpty()) {
4213
const Node* n = tre->root()->findNode(cn->qmlElement(),Node::Fake);
4214
if (n && n->subType() == Node::QmlClass) {
4215
out() << "<p style=\"text-align: center\">";
4218
text << Atom(Atom::LinkNode,CodeMarker::stringForNode(cn));
4219
text << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK);
4220
text << Atom(Atom::String, cn->name());
4221
text << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK);
4222
text << " is instantiated by QML element ";
4223
text << Atom(Atom::LinkNode,CodeMarker::stringForNode(n));
4224
text << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK);
4225
text << Atom(Atom::String, n->name());
4226
text << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK);
4228
generateText(text, cn, marker);