229 lines
7 KiB
C++
229 lines
7 KiB
C++
|
|
#include "ui_DemoParser.h"
|
||
|
|
#include "sfizz/parser/Parser.h"
|
||
|
|
#include <QApplication>
|
||
|
|
#include <QTimer>
|
||
|
|
#include <QTemporaryFile>
|
||
|
|
#include <QFileInfo>
|
||
|
|
#include <QDir>
|
||
|
|
#include <QTextBlock>
|
||
|
|
#include <QDebug>
|
||
|
|
|
||
|
|
class Application : public QApplication, public sfz::Parser::Listener {
|
||
|
|
public:
|
||
|
|
Application(int& argc, char *argv[]) : QApplication(argc, argv) {}
|
||
|
|
void init();
|
||
|
|
|
||
|
|
private:
|
||
|
|
void requestParseCheck();
|
||
|
|
void runParseCheck();
|
||
|
|
|
||
|
|
private:
|
||
|
|
void onParseBegin() override;
|
||
|
|
void onParseEnd() override;
|
||
|
|
void onParseHeader(const sfz::SourceRange& range, const std::string& header) override;
|
||
|
|
void onParseOpcode(const sfz::SourceRange& rangeOpcode, const sfz::SourceRange& rangeValue, const std::string& name, const std::string& value) override;
|
||
|
|
void onParseError(const sfz::SourceRange& range, const std::string& message) override;
|
||
|
|
void onParseWarning(const sfz::SourceRange& range, const std::string& message) override;
|
||
|
|
|
||
|
|
static QTextCursor selectSourceRange(QTextDocument* doc, const sfz::SourceRange& range);
|
||
|
|
|
||
|
|
private:
|
||
|
|
sfz::Parser _parser;
|
||
|
|
Ui::MainWindow _ui;
|
||
|
|
QTimer* _recheckTimer = nullptr;
|
||
|
|
bool _blockTextChanged = false;
|
||
|
|
};
|
||
|
|
|
||
|
|
static const char defaultSfzText[] = R"SFZ(
|
||
|
|
//----------------------------------------------------------------------------//
|
||
|
|
// This is a SFZ test file with many problems. //
|
||
|
|
//----------------------------------------------------------------------------//
|
||
|
|
|
||
|
|
// opcode without header
|
||
|
|
not_in_header=on // warning
|
||
|
|
|
||
|
|
// invalid headers
|
||
|
|
<> // empty
|
||
|
|
<ab@cd> // bad identifier
|
||
|
|
|
||
|
|
<region>
|
||
|
|
sample=*sine key=69
|
||
|
|
sample=My Directory/My Wave.wav // path with spaces
|
||
|
|
sample=My Directory/My Wave.wav key=69 // path with spaces, and other opcode following
|
||
|
|
sample=Foo=Bar.wav // path invalid: it cannot contain the '=' sign
|
||
|
|
|
||
|
|
#include "FileWhichDoesNotExist.sfz"
|
||
|
|
|
||
|
|
// malformed includes
|
||
|
|
#include "MyFileWhichDoesNotExist1.sfz
|
||
|
|
#include MyFileWhichDoesNotExist1.sfz"
|
||
|
|
|
||
|
|
// #define with some bad variable names
|
||
|
|
#define $foo 1234
|
||
|
|
#define Foo 1234
|
||
|
|
#define $ 1234
|
||
|
|
|
||
|
|
// #define with empty expansion, accepted
|
||
|
|
#define $foo
|
||
|
|
|
||
|
|
// expansion
|
||
|
|
abc$foo=1
|
||
|
|
abcdef=$foo
|
||
|
|
|
||
|
|
// expansion of undefined variables
|
||
|
|
abc$toto=1
|
||
|
|
abcdef=$tata
|
||
|
|
|
||
|
|
// opcode name which expands to invalid identifier
|
||
|
|
$titi=1
|
||
|
|
)SFZ";
|
||
|
|
|
||
|
|
void Application::init()
|
||
|
|
{
|
||
|
|
QMainWindow* window = new QMainWindow;
|
||
|
|
_ui.setupUi(window);
|
||
|
|
|
||
|
|
_recheckTimer = new QTimer;
|
||
|
|
_recheckTimer->setSingleShot(true);
|
||
|
|
_recheckTimer->setInterval(200);
|
||
|
|
connect(
|
||
|
|
_recheckTimer, &QTimer::timeout,
|
||
|
|
this, [this]() { runParseCheck(); });
|
||
|
|
|
||
|
|
_ui.sfzEdit->setFont(QFontDatabase::systemFont(QFontDatabase::FixedFont));
|
||
|
|
_ui.sfzEdit->setPlainText(QString::fromLatin1(defaultSfzText));
|
||
|
|
connect(
|
||
|
|
_ui.sfzEdit, &QTextEdit::textChanged,
|
||
|
|
this, [this]() { if (!_blockTextChanged) requestParseCheck(); });
|
||
|
|
|
||
|
|
_ui.messageTable->setColumnCount(3);
|
||
|
|
_ui.messageTable->setHorizontalHeaderLabels(QStringList() << tr("Type") << tr("Line") << tr("Message"));
|
||
|
|
_ui.messageTable->horizontalHeader()->setStretchLastSection(true);
|
||
|
|
|
||
|
|
window->show();
|
||
|
|
|
||
|
|
_parser.setListener(this);
|
||
|
|
requestParseCheck();
|
||
|
|
}
|
||
|
|
|
||
|
|
void Application::requestParseCheck()
|
||
|
|
{
|
||
|
|
_recheckTimer->start();
|
||
|
|
}
|
||
|
|
|
||
|
|
void Application::runParseCheck()
|
||
|
|
{
|
||
|
|
QByteArray code = _ui.sfzEdit->toPlainText().toLatin1();
|
||
|
|
|
||
|
|
QTemporaryFile temp(QDir::tempPath() + "/parseXXXXXX.sfz");
|
||
|
|
temp.open();
|
||
|
|
temp.write(code);
|
||
|
|
temp.flush();
|
||
|
|
|
||
|
|
_blockTextChanged = true;
|
||
|
|
_parser.parseFile(temp.fileName().toStdString());
|
||
|
|
_blockTextChanged = false;
|
||
|
|
}
|
||
|
|
|
||
|
|
void Application::onParseBegin()
|
||
|
|
{
|
||
|
|
QTextDocument* doc = _ui.sfzEdit->document();
|
||
|
|
QTextCursor cur(doc);
|
||
|
|
cur.movePosition(QTextCursor::Start);
|
||
|
|
cur.movePosition(QTextCursor::End, QTextCursor::KeepAnchor);
|
||
|
|
QTextCharFormat cfmt;
|
||
|
|
cur.setCharFormat(cfmt);
|
||
|
|
|
||
|
|
QTableWidget* t = _ui.messageTable;
|
||
|
|
while (t->rowCount() > 0)
|
||
|
|
t->removeRow(t->rowCount() - 1);
|
||
|
|
}
|
||
|
|
|
||
|
|
void Application::onParseEnd()
|
||
|
|
{
|
||
|
|
}
|
||
|
|
|
||
|
|
void Application::onParseHeader(const sfz::SourceRange& range, const std::string& header)
|
||
|
|
{
|
||
|
|
(void)header;
|
||
|
|
|
||
|
|
QTextDocument* doc = _ui.sfzEdit->document();
|
||
|
|
QTextCursor cur = selectSourceRange(doc, range);
|
||
|
|
QTextCharFormat cfmt;
|
||
|
|
cfmt.setForeground(QColor::fromRgb(0x4E9A06));
|
||
|
|
cur.mergeCharFormat(cfmt);
|
||
|
|
}
|
||
|
|
|
||
|
|
void Application::onParseOpcode(const sfz::SourceRange& rangeOpcode, const sfz::SourceRange& rangeValue, const std::string& name, const std::string& value)
|
||
|
|
{
|
||
|
|
(void)name;
|
||
|
|
(void)value;
|
||
|
|
|
||
|
|
QTextDocument* doc = _ui.sfzEdit->document();
|
||
|
|
QTextCursor curOpcode = selectSourceRange(doc, rangeOpcode);
|
||
|
|
QTextCursor curValue = selectSourceRange(doc, rangeValue);
|
||
|
|
QTextCharFormat cfmtOpcode;
|
||
|
|
cfmtOpcode.setForeground(QColor::fromRgb(0x75507B));
|
||
|
|
QTextCharFormat cfmtValue;
|
||
|
|
cfmtValue.setForeground(QColor::fromRgb(0x3465A4));
|
||
|
|
curOpcode.mergeCharFormat(cfmtOpcode);
|
||
|
|
curValue.mergeCharFormat(cfmtValue);
|
||
|
|
}
|
||
|
|
|
||
|
|
void Application::onParseError(const sfz::SourceRange& range, const std::string& message)
|
||
|
|
{
|
||
|
|
QTableWidget* t = _ui.messageTable;
|
||
|
|
int row = t->rowCount();
|
||
|
|
t->insertRow(row);
|
||
|
|
|
||
|
|
// QString path = QString::fromStdString(*range.start.filePath);
|
||
|
|
// QString file = QFileInfo(path).fileName();
|
||
|
|
t->setItem(row, 0, new QTableWidgetItem(tr("Error")));
|
||
|
|
t->setItem(row, 1, new QTableWidgetItem(QString::number(range.start.lineNumber)));
|
||
|
|
t->setItem(row, 2, new QTableWidgetItem(QString::fromStdString(message)));
|
||
|
|
|
||
|
|
QTextDocument* doc = _ui.sfzEdit->document();
|
||
|
|
QTextCursor cur = selectSourceRange(doc, range);
|
||
|
|
QTextCharFormat cfmt;
|
||
|
|
cfmt.setUnderlineStyle(QTextCharFormat::SingleUnderline);
|
||
|
|
cfmt.setUnderlineColor(Qt::red);
|
||
|
|
cur.mergeCharFormat(cfmt);
|
||
|
|
}
|
||
|
|
|
||
|
|
void Application::onParseWarning(const sfz::SourceRange& range, const std::string& message)
|
||
|
|
{
|
||
|
|
QTableWidget* t = _ui.messageTable;
|
||
|
|
int row = t->rowCount();
|
||
|
|
t->insertRow(row);
|
||
|
|
|
||
|
|
// QString path = QString::fromStdString(*range.start.filePath);
|
||
|
|
// QString file = QFileInfo(path).fileName();
|
||
|
|
t->setItem(row, 0, new QTableWidgetItem(tr("Warning")));
|
||
|
|
t->setItem(row, 1, new QTableWidgetItem(QString::number(range.start.lineNumber)));
|
||
|
|
t->setItem(row, 2, new QTableWidgetItem(QString::fromStdString(message)));
|
||
|
|
|
||
|
|
QTextDocument* doc = _ui.sfzEdit->document();
|
||
|
|
QTextCursor cur = selectSourceRange(doc, range);
|
||
|
|
QTextCharFormat cfmt;
|
||
|
|
cfmt.setUnderlineStyle(QTextCharFormat::SingleUnderline);
|
||
|
|
cfmt.setUnderlineColor(Qt::gray);
|
||
|
|
cur.mergeCharFormat(cfmt);
|
||
|
|
}
|
||
|
|
|
||
|
|
QTextCursor Application::selectSourceRange(QTextDocument* doc, const sfz::SourceRange& range)
|
||
|
|
{
|
||
|
|
QTextCursor cur(doc->findBlockByLineNumber(range.start.lineNumber));
|
||
|
|
cur.movePosition(QTextCursor::NextCharacter, QTextCursor::MoveAnchor, range.start.columnNumber);
|
||
|
|
cur.movePosition(QTextCursor::Down, QTextCursor::KeepAnchor, range.end.lineNumber - range.start.lineNumber);
|
||
|
|
cur.movePosition(QTextCursor::StartOfLine, QTextCursor::KeepAnchor);
|
||
|
|
cur.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor, range.end.columnNumber);
|
||
|
|
return cur;
|
||
|
|
}
|
||
|
|
|
||
|
|
int main(int argc, char* argv[])
|
||
|
|
{
|
||
|
|
Application app(argc, argv);
|
||
|
|
app.init();
|
||
|
|
return app.exec();
|
||
|
|
}
|