MuseScore/manual/genManual.cpp
2012-08-08 11:16:55 +02:00

418 lines
14 KiB
C++

//=============================================================================
// MuseScore
// Music Composition & Notation
//
// Copyright (C) 2002-2011 Werner Schweer
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License version 2
// as published by the Free Software Foundation and appearing in
// the file LICENCE.GPL
//=============================================================================
#include <stdio.h>
QString srcPath;
QString dstPath;
//---------------------------------------------------------
// Prop
//---------------------------------------------------------
struct Prop {
QString name;
QString type;
QString description;
};
//---------------------------------------------------------
// Proc
//---------------------------------------------------------
struct Proc {
QString name;
QString type;
QStringList description;
};
//---------------------------------------------------------
// Class
//---------------------------------------------------------
struct Class {
QString name;
QStringList description;
QString parent;
QList<Prop> props;
QList<Proc> procs;
bool operator<(const Class& c) const {
return name < c.name;
}
};
static QList<Class> classes;
//---------------------------------------------------------
// addHeader
//---------------------------------------------------------
static void addHeader(QString& out)
{
out += "<!DOCTYPE html>\n"
"<html>\n"
"<head>\n"
" <meta charset=\"utf-8\">\n"
" <link rel=\"stylesheet\" type=\"text/css\" href=\"manual.css\"/>\n"
" </head>\n"
"<body>\n";
}
//---------------------------------------------------------
// addFooter
//---------------------------------------------------------
static void addFooter(QString& out)
{
out += "</body>\n"
"</html>\n";
}
//---------------------------------------------------------
// parseClass
//---------------------------------------------------------
static void parseClass(const QString& name, const QString& in)
{
Class cl;
cl.name = name;
QStringList sl = in.split("\n");
QStringList methodDescription;
QRegExp re("@P ([^\\s]+)\\s+([^\\s]+)(.*)");
// matches Q_INVOKABLE void mops(int a); // comment
QRegExp re1("Q_INVOKABLE +([^ ]+) +([^;]+); */*(.*)");
QRegExp re2("Q_INVOKABLE +([^ ]+) +([^\\{]+)\\{");
QRegExp re3("Q_INVOKABLE +([^ ]+) +(\\w+\\([^\\)]*\\))\\s+const\\s*([^\\{]*)\\{");
QRegExp reD("//@ (.*)");
QRegExp re4 ("class +(\\w+) *: *public +(\\w+) *\\{");
QRegExp re4b("class +(\\w+) *: *public +(\\w+), *public");
if (!re1.isValid() || !re2.isValid() || !re3.isValid())
abort();
bool parseClassDescription = true;
foreach(const QString& s, sl) {
if (re.indexIn(s, 0) != -1) { //@P
parseClassDescription = false;
Prop p;
p.name = re.cap(1);
p.type = re.cap(2);
p.description = re.cap(3);
cl.props.append(p);
}
else if (re2.indexIn(s, 0) != -1) {
parseClassDescription = false;
Proc p;
p.type = re2.cap(1);
p.name = re2.cap(2);
p.description = methodDescription;
methodDescription.clear();
cl.procs.append(p);
}
else if (re1.indexIn(s, 0) != -1) {
parseClassDescription = false;
Proc p;
p.type = re1.cap(1);
p.name = re1.cap(2);
p.description = methodDescription;
methodDescription.clear();
cl.procs.append(p);
}
else if (re3.indexIn(s, 0) != -1) {
parseClassDescription = false;
Proc p;
p.type = re3.cap(1);
p.name = re3.cap(2);
p.description = methodDescription;
methodDescription.clear();
cl.procs.append(p);
}
else if ((reD.indexIn(s, 0) != -1)) {
if (parseClassDescription)
cl.description.append(reD.cap(1));
else
methodDescription.append(reD.cap(1));
}
else if (s.startsWith("///")) {
QString ss = s.mid(3);
if (parseClassDescription)
cl.description.append(ss);
else
methodDescription.append(ss);
}
else if (re4.indexIn(s, 0) != -1) {
parseClassDescription = false;
QString parent = re4.cap(2).simplified();
if (name == re4.cap(1).simplified()) {
cl.parent = parent;
}
else {
printf("?<%s>!=<%s> derived from <%s>\n",
qPrintable(name), qPrintable(re4.cap(1).simplified()), qPrintable(parent));
}
}
else if (re4b.indexIn(s, 0) != -1) {
parseClassDescription = false;
QString parent = re4b.cap(2).simplified();
if (name == re4b.cap(1).simplified()) {
cl.parent = parent;
}
else {
printf("?<%s>!=<%s> derived from <%s>\n",
qPrintable(name), qPrintable(re4b.cap(1).simplified()), qPrintable(parent));
}
}
}
classes.append(cl);
}
//---------------------------------------------------------
// scanFile
//---------------------------------------------------------
static void scanFile(const QString& in)
{
QList<Prop> props;
QList<Proc> procs;
QRegExp re("@@ ([^\\n]*)");
int gpos = 0;
QString className;
for (;;) {
int rv = re.indexIn(in, gpos);
if (rv == -1) {
if (!className.isEmpty())
parseClass(className, in.mid(gpos, in.size() - gpos));
break;
}
int next = rv + re.matchedLength();
if (gpos)
parseClass(className, in.mid(gpos, next-gpos));
className = re.cap(1).simplified();
gpos = next;
}
}
//---------------------------------------------------------
// writeOutput
//---------------------------------------------------------
static void writeOutput()
{
foreach(const Class& cl, classes) {
QString out;
addHeader(out);
out += QString("<h3>%1</h3>\n").arg(cl.name);
if (!cl.parent.isEmpty()) {
// show parent only if its part of the exported classes
foreach(const Class& lcl, classes) {
if (lcl.name == cl.parent) {
QString path = cl.parent.toLower();
out += QString("inherits <a href=\"%1.html\">%2</a><br/><br/>\n").arg(path).arg(cl.parent);
break;
}
}
}
if (!cl.description.isEmpty()) {
out += "<div class=\"class-description\">\n";
foreach(const QString& s, cl.description) {
out += s.simplified().replace("\\brief ", "");
out += "<br/>\n";
}
out += "</div>\n";
}
if (!cl.procs.isEmpty()) {
out += "<h4>Methods</h4>\n";
out += "<div class=\"methods\">\n";
foreach(const Proc& p, cl.procs) {
out += "<div class=\"method\">\n";
QString type(p.type);
bool found = false;
if (type.endsWith("*")) {
type = type.left(type.size()-1);
foreach(const Class& cl, classes) {
if (cl.name == type) {
found = true;
break;
}
}
}
if (found)
out += QString("<code><a href=\"%1.html\">%2</a> ")
.arg(type.toLower()).arg(type);
else
out += QString("<code>%1 ").arg(type);
QRegExp re("([^(]+)\\(([^)]*)\\)");
if (re.indexIn(p.name, 0) != -1) {
out += QString("<b>%2</b>(%3)</code>\n") .arg(re.cap(1)).arg(re.cap(2));
}
else {
out += QString("<b>%2</b></code>\n").arg(p.name);
}
out += "</div>\n";
if (!p.description.isEmpty()) {
out += "<div class=\"method-description\">\n";
foreach(const QString& s, p.description) {
out += s.simplified();
out += "<br/>\n";
}
out += "</div>\n";
}
}
out += "</div>\n";
}
if (!cl.props.isEmpty()) {
out += "<h4>Properties</h4>\n";
out += "<div class=\"properties\">\n";
out += "<table cellpadding=\"0\" cellspacing=\"0\">\n";
foreach(const Prop& m, cl.props) {
out += "<tr>";
out += QString("<td>%1</td>"
"<td>%2</td>"
"<td>%3</td>")
.arg(m.name).arg(m.type).arg(m.description);
out += "</tr>\n";
}
out += "</table></div>\n";
}
addFooter(out);
QString ofile = dstPath + "/plugins/" + cl.name.toLower() + ".html";
QFile of(ofile);
if (!of.open(QIODevice::WriteOnly)) {
printf("open <%s> failed: %s\n", qPrintable(ofile), qPrintable(of.errorString()));
exit(-4);
}
of.write(out.toUtf8());
of.close();
}
//
// write index
//
QString out;
addHeader(out);
out += "<h2>Score Elements</h2>\n"
"<ul>\n";
qSort(classes);
foreach(const Class& s, classes) {
out += QString("<li><a href=\"%1\">%2</a></li>\n")
.arg(s.name.toLower() + ".html").arg(s.name);
}
addFooter(out);
QString ofile = dstPath + "/plugins/plugins.html";
QFile of(ofile);
if (!of.open(QIODevice::WriteOnly)) {
printf("open <%s> failed\n", qPrintable(ofile));
exit(-4);
}
of.write(out.toUtf8());
of.close();
}
//---------------------------------------------------------
// usage
//---------------------------------------------------------
static void usage(const char* program, const char* hint)
{
fprintf(stderr, "%s: %s\n", program, hint);
fprintf(stderr, "usage: %s [options] srcPath dstPath\n", program);
fprintf(stderr, "options: -v print version\n"
);
}
//---------------------------------------------------------
// printVersion
//---------------------------------------------------------
static void printVersion(const char* program)
{
printf("this is %s, version 0.1\n", program);
}
//---------------------------------------------------------
// main
//---------------------------------------------------------
int main(int argc, char* argv[])
{
char* prog = argv[0];
int c;
while ((c = getopt(argc, argv, "v")) != EOF) {
switch (c) {
case 'v':
printVersion(argv[0]);
return 0;
default:
usage(prog, "bad argument");
return -1;
}
}
argc -= optind;
argv += optind;
if (argc != 2) {
usage(prog, "bad arguments");
return -1;
}
srcPath = argv[0];
dstPath = argv[1];
QStringList files;
files << "mscore/plugins.h";
QDir libdir(srcPath + "/libmscore");
QStringList filter;
filter << "*.h";
QStringList fl = libdir.entryList(filter, QDir::Files);
foreach(QString f, fl)
files << "libmscore/" + f;
foreach(const QString& s, files) {
QString infile = srcPath + "/" + s;
QFile inFile(infile);
if (!inFile.open(QIODevice::ReadOnly)) {
fprintf(stderr, "%s: cannot open input file <%s>\n",
argv[0], qPrintable(infile));
return -2;
}
printf("ScanFile %s\n", qPrintable(infile));
QString in = inFile.readAll();
scanFile(in);
inFile.close();
}
QDir dir;
QString opath = dstPath + "/plugins";
if (!dir.mkpath(opath)) {
fprintf(stderr, "%s: cannot create destination path: <%s>\n",
argv[0], qPrintable(dstPath));
return -3;
}
writeOutput();
return 0;
}