lms-audio/src/cuetracklistwidget.cpp
2024-07-09 20:48:20 +02:00

604 lines
21 KiB
C++

#include <algorithm>
#include <iostream>
#include <string>
#include <fstream>
#include <QWidget>
#include <QScrollArea>
#include <QEvent>
#include <QKeyEvent>
#include <QShortcut>
#include <QVBoxLayout>
#include <QTableWidget>
#include <QTableWidgetItem>
#include <QFile>
#include <QXmlStreamReader>
#include <QMessageBox>
#include "cuetracklistwidget.h"
CueTrackListWidget::CueTrackListWidget(QWidget *parent)
{
layout = new QVBoxLayout(this);
tableWidget = new QTableWidget();
tableWidget->setSortingEnabled(false);
layout->addWidget(tableWidget);
setLayout(layout);
QShortcut *shortcut_up = new QShortcut(QKeySequence("Up"), parent);
QObject::connect(shortcut_up, SIGNAL(activated()), this, SLOT(key_up()));
QShortcut *shortcut_down = new QShortcut(QKeySequence("Down"), parent);
QObject::connect(shortcut_down, SIGNAL(activated()), this, SLOT(key_down()));
QShortcut *shortcut_copy = new QShortcut(QKeySequence("Ctrl+C"), parent);
QObject::connect(shortcut_copy, SIGNAL(activated()), this, SLOT(copyCueTrack()));
QShortcut *shortcut_cut = new QShortcut(QKeySequence("Ctrl+X"), parent);
QObject::connect(shortcut_cut, SIGNAL(activated()), this, SLOT(cutCueTrack()));
QShortcut *shortcut_paste = new QShortcut(QKeySequence("Ctrl+V"), parent);
QObject::connect(shortcut_paste, SIGNAL(activated()), this, SLOT(pasteCueTrack()));
tableWidget->installEventFilter(this);
QShortcut *shortcut_delete = new QShortcut(QKeySequence("Delete"), parent);
QObject::connect(shortcut_delete, SIGNAL(activated()), this, SLOT(deleteCueTrack()));
QShortcut *shortcut_insert = new QShortcut(QKeySequence("Insert"), parent);
QObject::connect(shortcut_insert, SIGNAL(activated()), this, SLOT(createNewCueTrack()));
QShortcut *shortcut_edit = new QShortcut(QKeySequence("e"), parent);
QObject::connect(shortcut_edit, SIGNAL(activated()), this, SLOT(editCueTrack()));
}
void CueTrackListWidget::addCueTrack(CueTrack* cue) {
cueTracks.push_back(cue);
displayCueTrackInTable(cue, -1);
m_size++;
}
void CueTrackListWidget::removeCueTrack(int index) {
if (index >= m_size) return;
cueTracks.erase(cueTracks.begin() + index);
tableWidget->removeRow(index);
m_size--;
}
CueTrack* CueTrackListWidget::getTrackAtIndex(int index) {
if (index < m_size) {
return cueTracks.at(index);
}
return nullptr;
}
CueTrack* CueTrackListWidget::getSelectedTrack(bool advance) {
CueTrack* track = getTrackAtIndex(selectedIndex);
if (advance)
key_down();
return track;
}
void CueTrackListWidget::key_up() {
if (m_size > 0 && selectedIndex > 0) {
updateSelectedCueTrack(false);
selectedIndex--;
updateSelectedCueTrack(true);
tableWidget->scrollToItem(tableWidget->item(selectedIndex, 0));
}
emit changeSelectedIndex(selectedIndex);
}
void CueTrackListWidget::key_down() {
if (selectedIndex < m_size - 1 && m_size > 0) {
updateSelectedCueTrack(false);
selectedIndex++;
updateSelectedCueTrack(true);
tableWidget->scrollToItem(tableWidget->item(selectedIndex, 0));
}
emit changeSelectedIndex(selectedIndex);
}
void CueTrackListWidget::displayCueTrackInTable(CueTrack *cueTrack, int index) {
if (tableWidget->columnCount() == 0) {
tableWidget->setColumnCount(7);
QStringList headers = {"Active", "Number","Audio Channel", "Name", "Volume", "Status", "File"};
tableWidget->setHorizontalHeaderLabels(headers);
}
if (index == -1)
index = tableWidget->rowCount();
tableWidget->insertRow(index);
tableWidget->setItem(index, 0, new QTableWidgetItem(cueTrack->active ? "Yes" : "No"));
tableWidget->setItem(index, 1, new QTableWidgetItem(QStringLiteral("%1").arg(cueTrack->userNumber, 3, 10, QLatin1Char('0'))));
tableWidget->setItem(index, 2, new QTableWidgetItem(QString::number(cueTrack->audioLayer)));
tableWidget->setItem(index, 3, new QTableWidgetItem(cueTrack->name.data()));
tableWidget->setItem(index, 4, new QTableWidgetItem(QString::number(cueTrack->volume)));
QString statusStr;
statusStr = statusToString(cueTrack->status);
tableWidget->setItem(index, 5, new QTableWidgetItem(statusStr));
tableWidget->setItem(index, 6, new QTableWidgetItem(*getFileName(cueTrack->filePath)));
}
void CueTrackListWidget::updateSelectedCueTrack(bool highlightRow) {
if (selectedIndex >= 0 && selectedIndex < tableWidget->rowCount()) {
QBrush backgroundBrush(highlightRow ? QColor(248, 248, 200) : Qt::white);
for (int column = 0; column < tableWidget->columnCount(); ++column) {
QTableWidgetItem *item = tableWidget->item(selectedIndex, column);
if (!item) {
item = new QTableWidgetItem();
tableWidget->setItem(selectedIndex, column, item);
}
item->setBackground(backgroundBrush);
}
tableWidget->resizeColumnsToContents();
tableWidget->resizeRowsToContents();
}
}
void CueTrackListWidget::cueTrackLoadDefaults(CueTrack * t)
{
t->active = false;
t->audioLayer = 0;
t->bus1 = 100;
t->bus2 = 100;
t->entryPoint = 0;
t->exitPoint = 255;
t->fadeIn = 2;
t->fadeOut = 2;
t->waitIn = 0;
t->waitOut = 0;
t->pan = 0;
t->pitch = 1;
t->status = Status::PlayingOnce;
lastUserCueNumber += 10;
t->userNumber = lastUserCueNumber;
t->volume = 80;
t->stopAtEnd = true;
t->filePath = "";
t->duration = 0;
t->multi = false;
}
void CueTrackListWidget::createNewCueTrack()
{
CueTrack *t = new CueTrack;
cueTrackLoadDefaults(t);
EditCueTrackWidget dialog(t, this);
if (dialog.exec() == QDialog::Accepted) {
t->active = false;
addCueTrack(t);
if (m_size == 1)
{
updateSelectedCueTrack(true);
emit changeSelectedIndex(0);
} else
redrawCueTrackList();
if (lastUserCueNumber < t->userNumber)
lastUserCueNumber = t->userNumber;
} else
delete (t);
}
void CueTrackListWidget::editCueTrack()
{
CueTrack *current = getTrackAtIndex(selectedIndex);
EditCueTrackWidget dialog(current, this);
if (dialog.exec() == QDialog::Accepted) {
copyCueTrack(dialog.cueTrack, current);
redrawCueTrackList();
emit changeSelectedIndex(selectedIndex);
}
if (lastUserCueNumber < current->userNumber)
lastUserCueNumber = current->userNumber;
}
void CueTrackListWidget::deleteCueTrack()
{
QMessageBox::StandardButton reply;
reply = QMessageBox::question(this, "Delete Cue Track", "Are you sure you want to delete this cue track?",
QMessageBox::Yes | QMessageBox::No);
if (reply == QMessageBox::Yes) {
removeCueTrack(selectedIndex);
if (selectedIndex >= m_size)
{
selectedIndex = m_size - 1;
emit changeSelectedIndex(selectedIndex);
}
redrawCueTrackList();
}
}
void CueTrackListWidget::copyCueTrack(CueTrack *src, CueTrack *dst)
{
dst->active = src->active;
dst->audioLayer = src->audioLayer;
dst->bus1 = src->bus1;
dst->bus2 = src->bus2;
dst->entryPoint = src->entryPoint;
dst->exitPoint = src->exitPoint;
dst->fadeIn = src->fadeIn;
dst->fadeOut = src->fadeOut;
dst->waitIn = src->waitIn;
dst->waitOut = src->waitOut;
dst->pan = src->pan;
dst->pitch = src->pitch;
dst->status = src->status;
dst->userNumber = src->userNumber;
dst->volume = src->volume;
dst->stopAtEnd = src->stopAtEnd;
dst->name = src->name;
dst->description = src->description;
dst->filePath = src->filePath;
dst->duration = src->duration;
dst->multi = src->multi;
}
QString *CueTrackListWidget::getFileName(std::string s)
{
size_t bar = s.rfind("/");
std::string tmp = s.substr(bar + 1, strlen(s.data()));
QString *ret = new QString(tmp.data());
ret->truncate(64);
return ret;
}
void CueTrackListWidget::sortCueTrackList()
{
std::sort(cueTracks.begin(), cueTracks.end(), [](CueTrack *a, CueTrack *b) {
return a->userNumber < b->userNumber;
});
}
void CueTrackListWidget::redrawCueTrackList()
{
if (!m_size)
return;
int selected = cueTracks.at(selectedIndex)->userNumber;
clearTableWidget();
tableWidget->setColumnCount(7);
QStringList headers = {"Active", "Number","Channel", "Name", "Volume", "Status", "File"};
tableWidget->setHorizontalHeaderLabels(headers);
sortCueTrackList();
selectedIndex = 0;
for (int i = 0; i < m_size; i++)
{
displayCueTrackInTable(cueTracks.at(i), i);
}
selectedIndex = 0;
while (cueTracks.at(selectedIndex)->userNumber != selected)
{
selectedIndex++;
}
updateSelectedCueTrack(true);
tableWidget->resizeColumnsToContents();
tableWidget->resizeRowsToContents();
tableWidget->scrollToItem(tableWidget->item(selectedIndex, 0));
tableWidget->blockSignals(false);
emit changeSelectedIndex(selectedIndex);
}
void CueTrackListWidget::loadCueTrackList(std::string filename)
{
qDebug() << "loading cue list from " << filename.data();
QFile file(QString::fromStdString(filename));
if (!file.open(QIODevice::ReadOnly | QIODevice::Text))
{
std::cerr << "No se pudo abrir el archivo para lectura" << std::endl;
return;
}
QXmlStreamReader xmlReader(&file);
clearCueTrackList();
lastUserCueNumber = 0;
while (!xmlReader.atEnd() && !xmlReader.hasError())
{
QXmlStreamReader::TokenType token = xmlReader.readNext();
if (token == QXmlStreamReader::StartElement)
{
if (xmlReader.name() == "CueTrack")
{
CueTrack *t = new CueTrack;
while (!(xmlReader.tokenType() == QXmlStreamReader::EndElement && xmlReader.name() == "CueTrack"))
{
if (xmlReader.tokenType() == QXmlStreamReader::StartElement)
{
QString elementName = xmlReader.name().toString();
xmlReader.readNext();
if (xmlReader.tokenType() == QXmlStreamReader::Characters)
{
if (elementName == "filePath") {
t->filePath = xmlReader.text().toString().toStdString();
}
else if (elementName == "volume") {
t->volume = xmlReader.text().toInt();
}
else if (elementName == "pan") {
t->pan = xmlReader.text().toInt();
}
else if (elementName == "pitch") {
t->pitch = xmlReader.text().toInt();
}
else if (elementName == "bus1") {
t->bus1 = xmlReader.text().toInt();
}
else if (elementName == "bus2") {
t->bus2 = xmlReader.text().toInt();
}
else if (elementName == "status") {
QString tmp = xmlReader.text().toString();
t->status = stringToStatus(&tmp);
}
else if (elementName == "fadeOut") {
t->fadeOut = xmlReader.text().toInt();
}
else if (elementName == "fadeIn") {
t->fadeIn = xmlReader.text().toInt();
}
else if (elementName == "waitIn") {
t->waitIn = xmlReader.text().toInt();
}
else if (elementName == "waitOut") {
t->waitOut = xmlReader.text().toInt();
}
else if (elementName == "stopAtEnd") {
QString tmp = xmlReader.text().toString().toLower();
if (tmp.compare("false"))
t->stopAtEnd = true;
else {
t->stopAtEnd = false;
}
}
else if (elementName == "name") {
t->name = xmlReader.text().toString().toStdString();
}
else if (elementName == "description") {
t->description = xmlReader.text().toString().toStdString();
}
else if (elementName == "userNumber") {
t->userNumber = xmlReader.text().toInt();
if (t->userNumber > lastUserCueNumber)
{
lastUserCueNumber = t->userNumber;
}
}
else if (elementName == "entryPoint") {
t->entryPoint = xmlReader.text().toInt();
}
else if (elementName == "exitPoint") {
t->exitPoint = xmlReader.text().toInt();
}
else if (elementName == "audioLayer") {
t->audioLayer = xmlReader.text().toInt();
}
else if (elementName == "duration") {
t->duration = xmlReader.text().toInt();
}
else if (elementName == "multi") {
QString tmp = xmlReader.text().toString().toLower();
if (tmp.compare("false"))
t->multi = true;
else {
t->multi = false;
}
}
t->active = false;
}
}
xmlReader.readNext();
}
addCueTrack(t);
}
}
}
if (xmlReader.hasError())
{
std::cerr << "Error al leer el archivo XML: " << xmlReader.errorString().toStdString() << std::endl;
}
file.close();
redrawCueTrackList();
}
void CueTrackListWidget::saveCueTrackList(std::string filename)
{
qDebug() << "saving cue list to " << filename.data();
std::ofstream file(filename);
if (!file.is_open())
{
return;
}
file << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
file << "<CueTrackList>\n";
for (int i = 0; i < m_size; i++)
{
file << cueTrackToXml(*cueTracks.at(i)) << std::endl;
}
file << "</CueTrackList>\n";
file.close();
}
std::string CueTrackListWidget::cueTrackToXml(const CueTrack& cueTrack)
{
std::string xml = " <CueTrack>\n";
xml += " <filePath>" + cueTrack.filePath + "</filePath>\n";
xml += " <volume>" + std::to_string(cueTrack.volume) + "</volume>\n";
xml += " <pan>" + std::to_string(cueTrack.pan) + "</pan>\n";
xml += " <pitch>" + std::to_string(cueTrack.pitch) + "</pitch>\n";
xml += " <bus1>" + std::to_string(cueTrack.bus1) + "</bus1>\n";
xml += " <bus2>" + std::to_string(cueTrack.bus2) + "</bus2>\n";
xml += " <status>";
xml += statusToString(cueTrack.status);
xml += "</status>\n";
xml += " <fadeOut>" + std::to_string(cueTrack.fadeOut) + "</fadeOut>\n";
xml += " <fadeIn>" + std::to_string(cueTrack.fadeIn) + "</fadeIn>\n";
xml += " <waitIn>" + std::to_string(cueTrack.waitIn) + "</waitIn>\n";
xml += " <waitOut>" + std::to_string(cueTrack.waitOut) + "</waitOut>\n";
xml += " <stopAtEnd>";
xml += (cueTrack.stopAtEnd ? "true" : "false");
xml += "</stopAtEnd>\n";
xml += " <name>" + cueTrack.name + "</name>\n";
xml += " <description>" + cueTrack.description + "</description>\n";
xml += " <userNumber>" + std::to_string(cueTrack.userNumber) + "</userNumber>\n";
xml += " <entryPoint>" + std::to_string(cueTrack.entryPoint) + "</entryPoint>\n";
xml += " <exitPoint>" + std::to_string(cueTrack.exitPoint) + "</exitPoint>\n";
xml += " <audioLayer>" + std::to_string(cueTrack.audioLayer) + "</audioLayer>\n";
xml += " <duration>" + std::to_string(cueTrack.duration) + "</duration>\n";
xml += " <multi>";
xml += (cueTrack.multi ? "true" : "false");
xml += "</multi>\n";
xml += " </CueTrack>\n";
return xml;
}
void CueTrackListWidget::clearCueTrackList()
{
for (int i = 0; i < m_size; i++)
{
delete cueTracks.at(i);
}
cueTracks.clear();
m_size = 0;
selectedIndex = 0;
}
#include <QFile>
#include <QXmlStreamWriter>
void saveCueTrackToXml(const CueTrack& cueTrack, const QString& filename) {
QFile file(filename);
if (!file.open(QIODevice::WriteOnly)) {
qInfo() << "Can not open file " << filename << "\n";
return;
}
QXmlStreamWriter xmlWriter(&file);
xmlWriter.setAutoFormatting(true);
xmlWriter.writeStartDocument();
xmlWriter.writeStartElement("CueTrack");
xmlWriter.writeTextElement("filePath", QString::fromStdString(cueTrack.filePath));
xmlWriter.writeTextElement("volume", QString::number(cueTrack.volume));
xmlWriter.writeTextElement("pan", QString::number(cueTrack.pan));
xmlWriter.writeTextElement("pitch", QString::number(cueTrack.pitch));
xmlWriter.writeTextElement("bus1", QString::number(cueTrack.bus1));
xmlWriter.writeTextElement("bus2", QString::number(cueTrack.bus2));
xmlWriter.writeTextElement("status", statusToString(cueTrack.status));
xmlWriter.writeTextElement("fadeOut", QString::number(cueTrack.fadeOut));
xmlWriter.writeTextElement("fadeIn", QString::number(cueTrack.fadeIn));
xmlWriter.writeTextElement("waitIn", QString::number(cueTrack.waitIn));
xmlWriter.writeTextElement("waitOut", QString::number(cueTrack.waitOut));
xmlWriter.writeTextElement("stopAtEnd", cueTrack.stopAtEnd ? "true" : "false");
xmlWriter.writeTextElement("name", QString::fromStdString(cueTrack.name));
xmlWriter.writeTextElement("description", QString::fromStdString(cueTrack.description));
xmlWriter.writeTextElement("userNumber", QString::number(cueTrack.userNumber));
xmlWriter.writeTextElement("entryPoint", QString::number(cueTrack.entryPoint));
xmlWriter.writeTextElement("exitPoint", QString::number(cueTrack.exitPoint));
xmlWriter.writeTextElement("audioLayer", QString::number(cueTrack.audioLayer));
xmlWriter.writeEndElement();
xmlWriter.writeEndDocument();
file.close();
}
#include <QFile>
#include <QXmlStreamReader>
#include <QString>
#include <string>
CueTrack loadCueTrackFromXml(const QString& filename) {
QFile file(filename);
if (!file.open(QIODevice::ReadOnly)) {
throw std::runtime_error("No se pudo abrir el archivo");
}
QXmlStreamReader xmlReader(&file);
CueTrack cueTrack;
while (!xmlReader.atEnd() && !xmlReader.hasError()) {
QXmlStreamReader::TokenType token = xmlReader.readNext();
if (token == QXmlStreamReader::StartElement) {
const QString elementName = xmlReader.name().toString();
if (elementName == "filePath") {
cueTrack.filePath = xmlReader.readElementText().toStdString();
} else if (elementName == "volume") {
cueTrack.volume = xmlReader.readElementText().toInt();
} else if (elementName == "pan") {
cueTrack.pan = xmlReader.readElementText().toInt();
} else if (elementName == "pitch") {
cueTrack.pitch = xmlReader.readElementText().toInt();
} else if (elementName == "bus1") {
cueTrack.bus1 = xmlReader.readElementText().toInt();
} else if (elementName == "bus2") {
cueTrack.bus2 = xmlReader.readElementText().toInt();
} else if (elementName == "status") {
QString tmp = xmlReader.readElementText();
cueTrack.status = stringToStatus(&tmp);
} else if (elementName == "fadeOut") {
cueTrack.fadeOut = xmlReader.readElementText().toInt();
} else if (elementName == "fadeIn") {
cueTrack.fadeIn = xmlReader.readElementText().toInt();
} else if (elementName == "waitIn") {
cueTrack.waitIn = xmlReader.readElementText().toInt();
} else if (elementName == "waitOut") {
cueTrack.waitOut = xmlReader.readElementText().toInt();
} else if (elementName == "stopAtEnd") {
cueTrack.stopAtEnd = xmlReader.readElementText() == "true";
} else if (elementName == "name") {
cueTrack.name = xmlReader.readElementText().toStdString();
} else if (elementName == "description") {
cueTrack.description = xmlReader.readElementText().toStdString();
} else if (elementName == "userNumber") {
cueTrack.userNumber = xmlReader.readElementText().toInt();
} else if (elementName == "entryPoint") {
cueTrack.entryPoint = xmlReader.readElementText().toInt();
} else if (elementName == "exitPoint") {
cueTrack.exitPoint = xmlReader.readElementText().toInt();
} else if (elementName == "audioLayer") {
cueTrack.audioLayer = xmlReader.readElementText().toInt();
} else if (elementName == "duration") {
cueTrack.duration = xmlReader.readElementText().toInt();
}
}
}
if (xmlReader.hasError()) {
throw std::runtime_error("Error al parsear el archivo XML");
}
file.close();
return cueTrack;
}
void CueTrackListWidget::clearTableWidget()
{
for (int i = 0; i < m_size; i++)
{
tableWidget->removeRow(i);
}
tableWidget->clear();
tableWidget->setRowCount(0);
}
void CueTrackListWidget::cueTrackAtEnd(int layer)
{
for (int i = 0; i < m_size; i++)
{
CueTrack * cur = cueTracks.at(i);
if (cur->audioLayer == layer)
{
cur->active = false;
}
}
redrawCueTrackList();
}
void CueTrackListWidget::copyCueTrack() {
if (selectedIndex >= 0 && selectedIndex < m_size) {
delete copiedCue;
copiedCue = new CueTrack(*cueTracks.at(selectedIndex));
}
}
void CueTrackListWidget::cutCueTrack() {
if (selectedIndex >= 0 && selectedIndex < m_size) {
delete copiedCue;
copiedCue = new CueTrack(*cueTracks.at(selectedIndex));
removeCueTrack(selectedIndex);
}
}
void CueTrackListWidget::pasteCueTrack() {
if (copiedCue != nullptr) {
CueTrack* newCue = new CueTrack(*copiedCue);
addCueTrack(newCue);
}
}