/****************************************************************************
**
** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** This file is part of the tools applications of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** No Commercial Usage
** This file contains pre-release code and may not be distributed.
** You may use this file in accordance with the terms and conditions
** contained in the Technology Preview License Agreement accompanying
** this package.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Nokia gives you certain additional
** rights. These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** If you have questions regarding the use of this file, please contact
** Nokia at qt-info@nokia.com.
**
**
**
**
**
**
**
**
** $QT_END_LICENSE$
**
****************************************************************************/
/*
htmlgenerator.cpp
*/
#include "codemarker.h"
#include "codeparser.h"
#include "helpprojectwriter.h"
#include "htmlgenerator.h"
#include "node.h"
#include "separator.h"
#include "tree.h"
#include
#include
#include
#include
#include
QT_BEGIN_NAMESPACE
#define COMMAND_VERSION Doc::alias("version")
int HtmlGenerator::id = 0;
bool HtmlGenerator::debugging_on = false;
#if 0
QString HtmlGenerator::divNavTop = "
";
#endif
QString HtmlGenerator::divNavTop = "";
QString HtmlGenerator::sinceTitles[] =
{
" New Namespaces",
" New Classes",
" New Member Functions",
" New Functions in Namespaces",
" New Global Functions",
" New Macros",
" New Enum Types",
" New Typedefs",
" New Properties",
" New Variables",
" New QML Elements",
" New Qml Properties",
" New Qml Signals",
" New Qml Methods",
""
};
static bool showBrokenLinks = false;
static QRegExp linkTag("(<@link node=\"([^\"]+)\">).*(@link>)");
static QRegExp funcTag("(<@func target=\"([^\"]*)\">)(.*)(@func>)");
static QRegExp typeTag("(<@(type|headerfile|func)(?: +[^>]*)?>)(.*)(@\\2>)");
static QRegExp spanTag("@(?:comment|preprocessor|string|char)>");
static QRegExp unknownTag("?@[^>]*>");
bool parseArg(const QString &src,
const QString &tag,
int *pos,
int n,
QStringRef *contents,
QStringRef *par1 = 0,
bool debug = false)
{
#define SKIP_CHAR(c) \
if (debug) \
qDebug() << "looking for " << c << " at " << QString(src.data() + i, n - i); \
if (i >= n || src[i] != c) { \
if (debug) \
qDebug() << " char '" << c << "' not found"; \
return false; \
} \
++i;
#define SKIP_SPACE \
while (i < n && src[i] == ' ') \
++i;
int i = *pos;
int j = i;
// assume "<@" has been parsed outside
//SKIP_CHAR('<');
//SKIP_CHAR('@');
if (tag != QStringRef(&src, i, tag.length())) {
if (0 && debug)
qDebug() << "tag " << tag << " not found at " << i;
return false;
}
if (debug)
qDebug() << "haystack:" << src << "needle:" << tag << "i:" <).*(@link>)");
if (par1) {
SKIP_SPACE;
// read parameter name
j = i;
while (i < n && src[i].isLetter())
++i;
if (src[i] == '=') {
if (debug)
qDebug() << "read parameter" << QString(src.data() + j, i - j);
SKIP_CHAR('=');
SKIP_CHAR('"');
// skip parameter name
j = i;
while (i < n && src[i] != '"')
++i;
*par1 = QStringRef(&src, j, i - j);
SKIP_CHAR('"');
SKIP_SPACE;
} else {
if (debug)
qDebug() << "no optional parameter found";
}
}
SKIP_SPACE;
SKIP_CHAR('>');
// find contents up to closing "@tag>
j = i;
for (; true; ++i) {
if (i + 4 + tag.length() > n)
return false;
if (src[i] != '<')
continue;
if (src[i + 1] != '/')
continue;
if (src[i + 2] != '@')
continue;
if (tag != QStringRef(&src, i + 3, tag.length()))
continue;
if (src[i + 3 + tag.length()] != '>')
continue;
break;
}
*contents = QStringRef(&src, j, i - j);
i += tag.length() + 4;
*pos = i;
if (debug)
qDebug() << " tag " << tag << " found: pos now: " << i;
return true;
#undef SKIP_CHAR
}
static void addLink(const QString &linkTarget,
const QStringRef &nestedStuff,
QString *res)
{
if (!linkTarget.isEmpty()) {
*res += "";
*res += nestedStuff;
*res += "";
}
else {
*res += nestedStuff;
}
}
HtmlGenerator::HtmlGenerator()
: helpProjectWriter(0),
inLink(false),
inContents(false),
inSectionHeading(false),
inTableHeader(false),
numTableRows(0),
threeColumnEnumValueTable(true),
offlineDocs(true),
creatorDocs(true),
funcLeftParen("\\S(\\()"),
myTree(0),
slow(false),
obsoleteLinks(false)
{
}
HtmlGenerator::~HtmlGenerator()
{
if (helpProjectWriter)
delete helpProjectWriter;
}
void HtmlGenerator::initializeGenerator(const Config &config)
{
static const struct {
const char *key;
const char *left;
const char *right;
} defaults[] = {
{ ATOM_FORMATTING_BOLD, "", "" },
{ ATOM_FORMATTING_INDEX, "" },
{ ATOM_FORMATTING_ITALIC, "", "" },
{ ATOM_FORMATTING_PARAMETER, "", "" },
{ ATOM_FORMATTING_SUBSCRIPT, "", "" },
{ ATOM_FORMATTING_SUPERSCRIPT, "", "" },
{ ATOM_FORMATTING_TELETYPE, "", "" },
{ ATOM_FORMATTING_UNDERLINE, "", "" },
{ 0, 0, 0 }
};
Generator::initializeGenerator(config);
obsoleteLinks = config.getBool(QLatin1String(CONFIG_OBSOLETELINKS));
setImageFileExtensions(QStringList() << "png" << "jpg" << "jpeg" << "gif");
int i = 0;
while (defaults[i].key) {
formattingLeftMap().insert(defaults[i].key, defaults[i].left);
formattingRightMap().insert(defaults[i].key, defaults[i].right);
i++;
}
style = config.getString(HtmlGenerator::format() +
Config::dot +
HTMLGENERATOR_STYLE);
postHeader = config.getString(HtmlGenerator::format() +
Config::dot +
HTMLGENERATOR_POSTHEADER);
postPostHeader = config.getString(HtmlGenerator::format() +
Config::dot +
HTMLGENERATOR_POSTPOSTHEADER);
footer = config.getString(HtmlGenerator::format() +
Config::dot +
HTMLGENERATOR_FOOTER);
address = config.getString(HtmlGenerator::format() +
Config::dot +
HTMLGENERATOR_ADDRESS);
pleaseGenerateMacRef = config.getBool(HtmlGenerator::format() +
Config::dot +
HTMLGENERATOR_GENERATEMACREFS);
project = config.getString(CONFIG_PROJECT);
offlineDocs = !config.getBool(CONFIG_ONLINE);
creatorDocs = false; //!config.getBool(CONFIG_CREATOR);
projectDescription = config.getString(CONFIG_DESCRIPTION);
if (projectDescription.isEmpty() && !project.isEmpty())
projectDescription = project + " Reference Documentation";
projectUrl = config.getString(CONFIG_URL);
outputEncoding = config.getString(CONFIG_OUTPUTENCODING);
if (outputEncoding.isEmpty())
outputEncoding = QLatin1String("ISO-8859-1");
outputCodec = QTextCodec::codecForName(outputEncoding.toLocal8Bit());
naturalLanguage = config.getString(CONFIG_NATURALLANGUAGE);
if (naturalLanguage.isEmpty())
naturalLanguage = QLatin1String("en");
QSet editionNames = config.subVars(CONFIG_EDITION);
QSet::ConstIterator edition = editionNames.begin();
while (edition != editionNames.end()) {
QString editionName = *edition;
QStringList editionModules = config.getStringList(CONFIG_EDITION +
Config::dot +
editionName +
Config::dot +
"modules");
QStringList editionGroups = config.getStringList(CONFIG_EDITION +
Config::dot +
editionName +
Config::dot +
"groups");
if (!editionModules.isEmpty())
editionModuleMap[editionName] = editionModules;
if (!editionGroups.isEmpty())
editionGroupMap[editionName] = editionGroups;
++edition;
}
slow = config.getBool(CONFIG_SLOW);
stylesheets = config.getStringList(HtmlGenerator::format() +
Config::dot +
HTMLGENERATOR_STYLESHEETS);
customHeadElements = config.getStringList(HtmlGenerator::format() +
Config::dot +
HTMLGENERATOR_CUSTOMHEADELEMENTS);
codeIndent = config.getInt(CONFIG_CODEINDENT);
helpProjectWriter = new HelpProjectWriter(config,
project.toLower() +
".qhp");
}
void HtmlGenerator::terminateGenerator()
{
Generator::terminateGenerator();
}
QString HtmlGenerator::format()
{
return "HTML";
}
/*!
This is where the html files and dcf files are written.
\note The html file generation is done in the base class,
PageGenerator::generateTree().
*/
void HtmlGenerator::generateTree(const Tree *tree, CodeMarker *marker)
{
myTree = tree;
nonCompatClasses.clear();
mainClasses.clear();
compatClasses.clear();
obsoleteClasses.clear();
moduleClassMap.clear();
moduleNamespaceMap.clear();
funcIndex.clear();
legaleseTexts.clear();
serviceClasses.clear();
findAllClasses(tree->root());
findAllFunctions(tree->root());
findAllLegaleseTexts(tree->root());
findAllNamespaces(tree->root());
findAllSince(tree->root());
PageGenerator::generateTree(tree, marker);
dcfClassesRoot.ref = "classes.html";
dcfClassesRoot.title = "Classes";
qSort(dcfClassesRoot.subsections);
dcfOverviewsRoot.ref = "overviews.html";
dcfOverviewsRoot.title = "Overviews";
qSort(dcfOverviewsRoot.subsections);
dcfExamplesRoot.ref = "examples.html";
dcfExamplesRoot.title = "Tutorial & Examples";
qSort(dcfExamplesRoot.subsections);
DcfSection qtRoot;
appendDcfSubSection(&qtRoot, dcfClassesRoot);
appendDcfSubSection(&qtRoot, dcfOverviewsRoot);
appendDcfSubSection(&qtRoot, dcfExamplesRoot);
generateDcf(project.toLower().simplified().replace(" ", "-"),
"index.html",
projectDescription, qtRoot);
generateDcf("designer",
"designer-manual.html",
"Qt Designer Manual",
dcfDesignerRoot);
generateDcf("linguist",
"linguist-manual.html",
"Qt Linguist Manual",
dcfLinguistRoot);
generateDcf("assistant",
"assistant-manual.html",
"Qt Assistant Manual",
dcfAssistantRoot);
generateDcf("qmake",
"qmake-manual.html",
"qmake Manual",
dcfQmakeRoot);
QString fileBase = project.toLower().simplified().replace(" ", "-");
generateIndex(fileBase, projectUrl, projectDescription);
generatePageIndex(outputDir() + "/" + fileBase + ".pageindex", marker);
helpProjectWriter->generate(myTree);
}
void HtmlGenerator::startText(const Node * /* relative */,
CodeMarker * /* marker */)
{
inLink = false;
inContents = false;
inSectionHeading = false;
inTableHeader = false;
numTableRows = 0;
threeColumnEnumValueTable = true;
link.clear();
sectionNumber.clear();
}
/*!
Generate html from an instance of Atom.
*/
int HtmlGenerator::generateAtom(const Atom *atom,
const Node *relative,
CodeMarker *marker)
{
int skipAhead = 0;
static bool in_para = false;
switch (atom->type()) {
case Atom::AbstractLeft:
break;
case Atom::AbstractRight:
break;
case Atom::AutoLink:
if (!inLink && !inContents && !inSectionHeading) {
const Node *node = 0;
QString link = getLink(atom, relative, marker, &node);
if (!link.isEmpty()) {
beginLink(link, node, relative, marker);
generateLink(atom, relative, marker);
endLink();
}
else {
out() << protectEnc(atom->string());
}
}
else {
out() << protectEnc(atom->string());
}
break;
case Atom::BaseName:
break;
case Atom::BriefLeft:
if (relative->type() == Node::Fake) {
skipAhead = skipAtoms(atom, Atom::BriefRight);
break;
}
out() << "
\n";
bool needOtherSection = false;
/*
sections is built above for the call to generateTableOfContents().
*/
s = sections.begin();
while (s != sections.end()) {
if (s->members.isEmpty() && s->reimpMembers.isEmpty()) {
if (!s->inherited.isEmpty())
needOtherSection = true;
}
else {
if (!s->members.isEmpty()) {
// out() << "\n";
out() << "" << divNavTop << "\n";
out() << "